diff --git a/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js b/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js new file mode 100644 index 0000000000000000000000000000000000000000..c546a88af87a3abf577c76a23a9ee7111ab48091 --- /dev/null +++ b/bigbluebutton-html5/imports/api/slides/server/methods/zoomSlide.js @@ -0,0 +1,52 @@ +import Presentations from '/imports/api/presentations'; +import Slides from '/imports/api/slides'; +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; +import RedisPubSub from '/imports/startup/server/redis'; + +export default function switchSlide(credentials, slideNumber, podId, widthRatio, heightRatio, x, y) { + const REDIS_CONFIG = Meteor.settings.private.redis; + + const CHANNEL = REDIS_CONFIG.channels.toAkkaApps; + const EVENT_NAME = 'ResizeAndMovePagePubMsg'; + + const { meetingId, requesterUserId, requesterToken } = credentials; + + check(meetingId, String); + check(requesterUserId, String); + check(requesterToken, String); + + const selector = { + meetingId, + podId, + current: true, + }; + const Presentation = Presentations.findOne(selector); + + if (!Presentation) { + throw new Meteor.Error('presentation-not-found', 'You need a presentation to be able to switch slides'); + } + + const Slide = Slides.findOne({ + meetingId, + podId, + presentationId: Presentation.id, + num: slideNumber, + }); + + if (!Slide) { + throw new Meteor.Error('slide-not-found', `Slide number ${slideNumber} not found in the current presentation`); + } + + const payload = { + podId, + presentationId: Presentation.id, + pageId: Slide.id, + xOffset: x, + yOffset: y, + widthRatio, + heightRatio, + }; + + return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload); +} diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx index e0f81ab95b4795e0adaf9433e4ef720a31beefba..f639796bb79bc540716e1fb0494a84289ea51897 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-overlay/component.jsx @@ -1,14 +1,17 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import SlideViewModel from '/imports/utils/slideViewModel'; const CURSOR_INTERVAL = 16; -const MYSTERY_NUM = 2; +const MYSTERY_NUM = 5; const HUNDRED_PERCENT = 100; export default class PresentationOverlay extends Component { constructor(props) { super(props); + this.SlideZoomData = new SlideViewModel(props.adjustedSizes.width, props.adjustedSizes.height, props.slideHeight, props.slideWidth);; + // last sent coordinates this.lastSentClientX = 0; this.lastSentClientY = 0; @@ -20,7 +23,7 @@ export default class PresentationOverlay extends Component { // id of the setInterval() this.intervalId = 0; this.state = { - zoomValue: 100, + zoom: 100, }; // Mobile Firefox has a bug where e.preventDefault on touchstart doesn't prevent @@ -89,76 +92,115 @@ export default class PresentationOverlay extends Component { return point; } + componentDidMount() { + this.SlideZoomData.viewportW = this.props.adjustedSizes.width; + this.SlideZoomData.viewportH = this.props.adjustedSizes.height; + this.SlideZoomData.pageOrigW = this.props.adjustedSizes.width; + this.SlideZoomData.pageOrigH = this.props.adjustedSizes.height; + this.SlideZoomData.parentW = document.querySelector('._imports_ui_components_presentation__styles__presentationArea').getBoundingClientRect().width; + this.SlideZoomData.parentH = document.querySelector('._imports_ui_components_presentation__styles__presentationArea').getBoundingClientRect().height; + console.error(this.SlideZoomData); + + } mouseZoomHandler(e) { // calc for sliderZoom // zoomSlide(zoom, (((slideLoader.content.width/2)*SlideViewModel.HUNDRED_PERCENT)/zoom), (((slideLoader.content.height/2)*SlideViewModel.HUNDRED_PERCENT)/zoom)); + let zoom = this.state.zoom; + console.error(this.SlideZoomData.viewportW); + + if (e.deltaY < 0) { + zoom += 5; + } + if (e.deltaY > 0) { + zoom -= 5; + } + if (zoom <= HUNDRED_PERCENT) { + zoom = HUNDRED_PERCENT; + } else if (zoom >= 400) { + zoom = 400; + } const { zoomSlide, podId, currentSlideNum, slideWidth, slideHeight, - adjustedSizes + adjustedSizes, + updateCursor, + whiteboardId, } = this.props; - + const mouseX = e.clientX; const mouseY = e.clientY; + const svgPosition = this.getTransformedSvgPoint(mouseX, mouseY); + const svgPercentage = this.svgCoordinateToPercentages(svgPosition); + console.error(this.state.zoom); + + this.SlideZoomData.onZoom(zoom, svgPosition.x, svgPosition.y); + zoomSlide( + currentSlideNum, + podId, + this.SlideZoomData.viewedRegionW, + this.SlideZoomData.viewedRegionH, + this.SlideZoomData.viewedRegionX, + this.SlideZoomData.viewedRegionY, + ); + this.setState({ zoom }); + updateCursor({ + xPercent: svgPercentage.x, + yPercent: svgPercentage.y, + whiteboardId, + }); // console.error('event', x,y); - const _pageOrigW = slideWidth; - const _pageOrigH = slideHeight; - const viewportW = adjustedSizes.width; - const viewportH = adjustedSizes.height; + // const _pageOrigW = slideWidth; + // const _pageOrigH = slideHeight; + // const viewportW = adjustedSizes.width; + // const viewportH = adjustedSizes.height; - let _calcPageW = viewportW/(100/HUNDRED_PERCENT); - let _calcPageH = viewportH/(100/HUNDRED_PERCENT); - let _calcPageX = (0/HUNDRED_PERCENT) * _calcPageW; - let _calcPageY = (0/HUNDRED_PERCENT) * _calcPageH; + // let _calcPageW = viewportW/(100/HUNDRED_PERCENT); + // let _calcPageH = viewportH/(100/HUNDRED_PERCENT); + // let _calcPageX = (0/HUNDRED_PERCENT) * _calcPageW; + // let _calcPageY = (0/HUNDRED_PERCENT) * _calcPageH; - const svgPosition = this.getTransformedSvgPoint(mouseX, mouseY); - const svgPercentage = this.svgCoordinateToPercentages(svgPosition); + // const svgPercentage = this.svgCoordinateToPercentages(svgPosition); - // console.error('svgPosition', svgPosition.x, svgPosition.y); - // console.error('svgPercentage', svgPercentage.x, svgPercentage.y); + // // console.error('svgPosition', svgPosition.x, svgPosition.y); + // // console.error('svgPercentage', svgPercentage.x, svgPercentage.y); - const relXcoordInPage = (svgPercentage.x) / 100; - const relYcoordInPage = (svgPercentage.y) / 100; + // const relXcoordInPage = (svgPercentage.x) / 100; + // const relYcoordInPage = (svgPercentage.y) / 100; - const zoomValue = this.state.zoomValue + 5; + // const zoomValue = this.state.zoomValue + 5; - console.error(zoomValue); - console.error(mouseX, mouseY); + // console.error(zoomValue); + // console.error(mouseX, mouseY); - _calcPageW = viewportW * zoomValue / HUNDRED_PERCENT; - _calcPageH = (_calcPageW / _pageOrigW) * _pageOrigH; + // _calcPageW = viewportW * zoomValue / HUNDRED_PERCENT; + // _calcPageH = (_calcPageW / _pageOrigW) * _pageOrigH; - console.error(_calcPageW, _calcPageH); + // console.error(_calcPageW, _calcPageH); - const absXcoordInPage = relXcoordInPage * _calcPageW; - const absYcoordInPage = relYcoordInPage * _calcPageH; + // const absXcoordInPage = relXcoordInPage * _calcPageW; + // const absYcoordInPage = relYcoordInPage * _calcPageH; - console.error(mouseX, mouseY); - console.error(svgPosition.x, svgPosition.y); - console.error(relXcoordInPage, relYcoordInPage); - console.error(absXcoordInPage, absYcoordInPage); - - _calcPageX = (absXcoordInPage - mouseX) / MYSTERY_NUM; - _calcPageY = (absYcoordInPage - mouseY) / MYSTERY_NUM; - - const offsetX = (_calcPageX * 100) / _calcPageW; - const offsetY = (_calcPageX * 100) / _calcPageW; - console.error(`(${_calcPageX} * 100) / ${_calcPageW}`, offsetX); - console.error(`(${_calcPageY} * 100) / ${_calcPageH}`, offsetY); - - this.setState( - { zoomValue }, - () => zoomSlide(currentSlideNum, podId, - (this.state.zoomValue - 100) - 100, - offsetX, - offsetY - ) - ); + // console.error(mouseX, mouseY); + // console.error(svgPosition.x, svgPosition.y); + // console.error(relXcoordInPage, relYcoordInPage); + // console.error(absXcoordInPage, absYcoordInPage); + + // _calcPageX = (absXcoordInPage - mouseX) / MYSTERY_NUM; + // _calcPageY = (absYcoordInPage - mouseY) / MYSTERY_NUM; + + // const offsetX = (_calcPageX * 100) / _calcPageW; + // const offsetY = (_calcPageX * 100) / _calcPageW; + // const ration = HUNDRED_PERCENT - (this.state.zoomValue - HUNDRED_PERCENT); + // console.error(`(${_calcPageX} * 100) / ${_calcPageW}`, offsetX); + // console.error(`(${_calcPageY} * 100) / ${_calcPageH}`, offsetY); + + + } diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/service.js b/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/service.js index d5870687fbe97fa018f6644a7d1cee2d6048019f..43d8bf9aee4bda7e8ea394b8366e678d4d9dbf4c 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/service.js +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/service.js @@ -40,8 +40,8 @@ const nextSlide = (currentSlideNum, numberOfSlides, podId) => { } }; -const zoomSlide = (currentSlideNum, podId, value, xOffset, yOffset) => { - makeCall('zoomSlide', currentSlideNum, podId, value, xOffset, yOffset); +const zoomSlide = (currentSlideNum, podId, widthRation, HeightRatio, xOffset, yOffset) => { + makeCall('zoomSlide', currentSlideNum, podId, widthRation, HeightRatio, xOffset, yOffset); }; diff --git a/bigbluebutton-html5/imports/utils/slideCalcUtils.js b/bigbluebutton-html5/imports/utils/slideCalcUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..d05b0d87aa6831caa26a531dc800317315cfaa4e --- /dev/null +++ b/bigbluebutton-html5/imports/utils/slideCalcUtils.js @@ -0,0 +1,63 @@ +const HUNDRED_PERCENT = 100; +const MYSTERY_NUM = 2; +export default class SlideCalcUtil { + // After lots of trial and error on why synching doesn't work properly, I found I had to + // multiply the coordinates by 2. There's something I don't understand probably on the + // canvas coordinate system. (ralam feb 22, 2012) + + /** + * Calculate the viewed region width + */ + static calcViewedRegionWidth(vpw, cpw) { + const width = (vpw / cpw) * HUNDRED_PERCENT; + if (width > HUNDRED_PERCENT) { + return HUNDRED_PERCENT; + } + return width; + } + + static calcViewedRegionHeight(vph, cph) { + const height = (vph / cph) * HUNDRED_PERCENT; + if (height > HUNDRED_PERCENT) { + return HUNDRED_PERCENT; + } + return height; + } + + static calcCalcPageSizeWidth(ftp, vpw, vrw) { + if (ftp) { + return (vpw / vrw) * HUNDRED_PERCENT; + } + return vpw; + } + + static calcCalcPageSizeHeight(ftp, vph, vrh, cpw, cph, opw, oph) { + if (ftp) { + return (vph / vrh) * HUNDRED_PERCENT; + } + return (cpw / opw) * oph; + } + + static calcViewedRegionX(cpx, cpw) { + return (cpx * HUNDRED_PERCENT) / cpw; + } + + static calcViewedRegionY(cpy, cph) { + return (cpy * HUNDRED_PERCENT) / cph; + } + + static calculateViewportX(vpw, pw) { + if (vpw == pw) { + return 0; + } + return (pw - vpw) / MYSTERY_NUM; + } + + static calculateViewportY(vph, ph) { + if (vph == ph) { + return 0; + } + return (ph - vph) / MYSTERY_NUM; + + } +} \ No newline at end of file diff --git a/bigbluebutton-html5/imports/utils/slideViewModel.js b/bigbluebutton-html5/imports/utils/slideViewModel.js new file mode 100644 index 0000000000000000000000000000000000000000..ad69c3f06240a606d9c908a59b0623a0c4e56218 --- /dev/null +++ b/bigbluebutton-html5/imports/utils/slideViewModel.js @@ -0,0 +1,289 @@ +import SlideCalcUtil from './slideCalcUtils'; + +const MAX_ZOOM_PERCENT = 400; +const HUNDRED_PERCENT = 100; +const MYSTERY_NUM = 2; + +export default class SlideViewModel { + // private static const LOGGER:ILogger = getClassLogger(SlideViewModel); + + constructor(vw, vh, sw, sh) { + this.viewportX = 0; + this.viewportY = 0; + this.viewportW = vw; + this.viewportH = vh; + + this.loaderW = 0; + this.loaderH = 0; + this.loaderX = 0; + this.loaderY = 0; + + this.viewedRegionX = 0; + this.viewedRegionY = 0; + this.viewedRegionW = HUNDRED_PERCENT; + this.viewedRegionH = HUNDRED_PERCENT; + + this.pageOrigW = sw; + this.pageOrigH = sh; + this.calcPageW = 0; + this.calcPageH = 0; + this.calcPageX = 0; + this.calcPageY = 0; + this.parentW = vw; + this.parentH = vh; + + this.fitToPage = true; + + // After lots of trial and error on why synching doesn't work properly, I found I had to + // multiply the coordinates by 2. There's something I don't understand probably on the + // canvas coordinate system. (ralam feb 22, 2012) + } + + isPortraitDoc() { + return this.pageOrigH > this.pageOrigW; + } + + reset(pageWidth, pageHeight) { + this.pageOrigW = pageWidth; + this.pageOrigH = pageHeight; + } + + resetForNewSlide(pageWidth, pageHeight) { + this.pageOrigW = pageWidth; + this.pageOrigH = pageHeight; + } + // Not used into class + // parentChange(parentW, parentH) { + // this.viewportW = this.parentW = parentW; + // this.viewportH = this.parentH = parentH; + // } + + calculateViewportXY() { + this.viewportX = SlideCalcUtil.calculateViewportX(this.viewportW, this.parentW); + this.viewportY = SlideCalcUtil.calculateViewportY(this.viewportH, this.parentH); + } + + calcViewedRegion() { + this.viewedRegionW = SlideCalcUtil.calcViewedRegionWidth(this.viewportW, this.calcPageW); + this.viewedRegionH = SlideCalcUtil.calcViewedRegionHeight(this.viewportH, this.calcPageH); + this.viewedRegionX = SlideCalcUtil.calcViewedRegionX(this.calcPageX, this.calcPageW); + this.viewedRegionY = SlideCalcUtil.calcViewedRegionY(this.calcPageY, this.calcPageH); + } + + displayPresenterView() { + this.loaderX = Math.round(this.calcPageX); + this.loaderY = Math.round(this.calcPageY); + this.loaderW = Math.round(this.calcPageW); + this.loaderH = Math.round(this.calcPageH); + } + + adjustSlideAfterParentResized() { + if (this.fitToPage) { + this.calculateViewportNeededForRegion(this.viewedRegionW, this.viewedRegionH); + this.displayViewerRegion(this.viewedRegionX, this.viewedRegionY, this.viewedRegionW, this.viewedRegionH); + this.calculateViewportXY(); + this.displayPresenterView(); + this.printViewedRegion(); + } else { + this.calculateViewportSize(); + this.calculateViewportXY(); + this.calcPageW = (this.viewportW / this.viewedRegionW) * HUNDRED_PERCENT; + this.calcPageH = (this.pageOrigH / this.pageOrigW) * this.calcPageW; + this.calcViewedRegion(); + this.doBoundsValidation(); + } + } + + switchToFitToPage(ftp) { + // LOGGER.debug("switchToFitToPage"); + + this.fitToPage = ftp; + + this.saveViewedRegion(0, 0, 100, 100); + + this.calculateViewportSize(); + this.calculateViewportXY(); + } + + doWidthBoundsDetection() { + if (this.calcPageX >= 0) { + // Don't let the left edge move inside the view. + this.calcPageX = 0; + } else if ((this.calcPageW + this.calcPageX * MYSTERY_NUM) < this.viewportW) { + // Don't let the right edge move inside the view. + this.calcPageX = (this.viewportW - this.calcPageW) / MYSTERY_NUM; + } else { + // Let the move happen. + } + } + + doHeightBoundsDetection() { + if (this.calcPageY >= 0) { + // Don't let the top edge move into the view. + this.calcPageY = 0; + } else if ((this.calcPageH + this.calcPageY * MYSTERY_NUM) < this.viewportH) { + // Don't let the bottome edge move into the view. + this.calcPageY = (this.viewportH - this.calcPageH) / MYSTERY_NUM; + } else { + // Let the move happen. + } + } + + doBoundsValidation() { + this.doWidthBoundsDetection(); + this.doHeightBoundsDetection(); + } + + /** Returns whether or not the page actually moved */ + onMove(deltaX, deltaY) { + const oldX = this.calcPageX; + const oldY = this.calcPageY; + + this.calcPageX += deltaX / MYSTERY_NUM; + this.calcPageY += deltaY / MYSTERY_NUM; + + this.doBoundsValidation(); + + if (oldX != this.calcPageX || oldY != this.calcPageY) { + this.calcViewedRegion(); + return true; + } + return false; + } + + calculateViewportSize() { + this.viewportW = this.parentW; + this.viewportH = this.parentH; + + /* + * For some reason when the viewport values are both whole numbers the clipping doesn't + * function. When the second part of the width/height pair is rounded up and then + * reduced by 0.5 the clipping always seems to happen. This was a long standing, bug + * and if you try to remove the Math.ceil and "-0.5" you better know what you're doing. + * - Chad (Aug 30, 2017) + */ + + if (this.fitToPage) { + // If the height is smaller than the width, we use the height as the base to determine the size of the slide. + if (this.parentH < this.parentW) { + this.viewportH = this.parentH; + this.viewportW = Math.ceil((this.pageOrigW * this.viewportH) / this.pageOrigH) - 0.5; + if (this.parentW < this.viewportW) { + this.viewportW = this.parentW; + this.viewportH = Math.ceil((this.pageOrigH * this.viewportW) / this.pageOrigW) - 0.5; + } + } else { + this.viewportW = this.parentW; + this.viewportH = Math.ceil((this.viewportW / this.pageOrigW) * this.pageOrigH) - 0.5; + if (this.parentH < this.viewportH) { + this.viewportH = this.parentH; + this.viewportW = Math.ceil((this.pageOrigW * this.viewportH) / this.pageOrigH) - 0.5; + } + } + } else { + this.viewportH = Math.ceil((this.viewportW / this.pageOrigW) * this.pageOrigH) - 0.5; + if (this.viewportH > this.parentH) { + this.viewportH = this.parentH; + } + } + } + + printViewedRegion() { + console.debug("Region [" + this.viewedRegionW + "," + this.viewedRegionH + "] [" + this.viewedRegionX + "," + this.viewedRegionY + "]"); + console.debug("Region [" + ((this.viewedRegionW / HUNDRED_PERCENT)*this.calcPageW) + "," + ((this.viewedRegionH/HUNDRED_PERCENT)*this.calcPageH) + + "] [" + ((this.viewedRegionX/HUNDRED_PERCENT)*this.calcPageW) + "," + ((this.viewedRegionY/HUNDRED_PERCENT)*this.calcPageH) + "]"); + } + + onZoom(zoomValue, mouseX, mouseY) { + + // Absolute x and y positions of the mouse over the (enlarged) slide: + let absXcoordInPage = Math.abs(this.calcPageX) * MYSTERY_NUM + mouseX; + let absYcoordInPage = Math.abs(this.calcPageY) * MYSTERY_NUM + mouseY; + + // Relative position of mouse over the slide. For example, if your slide is 1000 pixels wide, + // and your mouse has an absolute x-coordinate of 700, then relXcoordInPage will be 0.7 : + const relXcoordInPage = absXcoordInPage / this.calcPageW; + const relYcoordInPage = absYcoordInPage / this.calcPageH; + + // Relative position of mouse in the view port (same as above, but with the view port): + const relXcoordInViewport = mouseX / this.viewportW; + const relYcoordInViewport = mouseY / this.viewportH; + + if (this.isPortraitDoc()) { + if (this.fitToPage) { + this.calcPageH = this.viewportH * zoomValue / HUNDRED_PERCENT; + this.calcPageW = (this.pageOrigW / this.pageOrigH) * this.calcPageH; + } else { + this.calcPageW = this.viewportW * zoomValue / HUNDRED_PERCENT; + this.calcPageH = (this.calcPageW / this.pageOrigW) * this.pageOrigH; + } + } else if (this.fitToPage) { + this.calcPageW = this.viewportW * zoomValue / HUNDRED_PERCENT; + this.calcPageH = this.viewportH * zoomValue / HUNDRED_PERCENT; + } else { + this.calcPageW = this.viewportW * zoomValue / HUNDRED_PERCENT; + this.calcPageH = (this.calcPageW / this.pageOrigW) * this.pageOrigH; + } + + + absXcoordInPage = relXcoordInPage * this.calcPageW; + absYcoordInPage = relYcoordInPage * this.calcPageH; + + this.calcPageX = -((absXcoordInPage - mouseX) / MYSTERY_NUM); + this.calcPageY = -((absYcoordInPage - mouseY) / MYSTERY_NUM); + + this.doWidthBoundsDetection(); + this.doHeightBoundsDetection(); + + this.calcViewedRegion(); + this.printViewedRegion(); + } + + displayViewerRegion(x, y, regionW, regionH) { + // LogUtil.debug("** disp viewer 1 [" + regionW + "," + regionH + "][" + x + "," + y + "]"); + this.calcPageW = this.viewportW / (regionW / HUNDRED_PERCENT); + this.calcPageH = this.viewportH / (regionH / HUNDRED_PERCENT); + this.calcPageX = (x / HUNDRED_PERCENT) * this.calcPageW; + this.calcPageY = (y / HUNDRED_PERCENT) * this.calcPageH; + // LogUtil.debug("** disp viewer 2 [" + viewportW + "," + viewportH + "] + // [" +_calcPageW + "," + _calcPageH + "][" + _calcPageX + "," + _calcPageY + "]"); + } + + saveViewedRegion(x, y, regionW, regionH) { + this.viewedRegionX = x; + this.viewedRegionY = y; + this.viewedRegionW = regionW; + this.viewedRegionH = regionH; + } + + calculateViewportNeededForRegion(regionW, regionH) { + const vrwp = this.pageOrigW * (regionW / HUNDRED_PERCENT); + const vrhp = this.pageOrigH * (regionH / HUNDRED_PERCENT); + + /* + * For some reason when the viewport values are both whole numbers the clipping doesn't + * function. When the second part of the width/height pair is rounded up and then + * reduced by 0.5 the clipping always seems to happen. This was a long standing, bug + * and if you try to remove the Math.ceil and "-0.5" you better know what you're doing. + * - Chad (Aug 30, 2017) + */ + + if (this.parentW < this.parentH) { + this.viewportW = this.parentW; + this.viewportH = Math.ceil((vrhp / vrwp) * this.parentW) - 0.5; + if (this.parentH < this.viewportH) { + this.viewportH = this.parentH; + this.viewportW = Math.ceil((vrwp * this.viewportH) / vrhp) - 0.5; + } + } else { + this.viewportH = this.parentH; + this.viewportW = Math.ceil((vrwp / vrhp) * this.parentH) - 0.5; + if (this.parentW < this.viewportW) { + this.viewportW = this.parentW; + this.viewportH = Math.ceil((vrhp * this.viewportW) / vrwp) - 0.5; + } + } + // LOGGER.debug("calc viewport ***** resizing [" + viewportW + "," + viewportH + "] + // [" + parentW + "," + parentH + "," + fitToPage + "] [" + pageOrigW + "," + pageOrigH + "]"); + } +}