diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 14e9fd0b6..3e3bd2fff 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -93,9 +93,11 @@ var PDFPageView = (function PDFPageViewClosure() { this.textLayerFactory = textLayerFactory; this.annotationLayerFactory = annotationLayerFactory; - this.renderTask = null; + this.paintTask = null; + this.paintedViewport = null; this.renderingState = RenderingStates.INITIAL; this.resume = null; + this.error = null; this.onBeforeDraw = null; this.onAfterDraw = null; @@ -171,6 +173,9 @@ var PDFPageView = (function PDFPageViewClosure() { this.canvas.height = 0; delete this.canvas; } + if (!currentZoomLayerNode) { + this.paintedViewport = null; + } this.loadingIconDiv = document.createElement('div'); this.loadingIconDiv.className = 'loadingIcon'; @@ -224,9 +229,9 @@ var PDFPageView = (function PDFPageViewClosure() { }, cancelRendering: function PDFPageView_cancelRendering() { - if (this.renderTask) { - this.renderTask.cancel(); - this.renderTask = null; + if (this.paintTask) { + this.paintTask.cancel(); + this.paintTask = null; } this.renderingState = RenderingStates.INITIAL; this.resume = null; @@ -258,7 +263,8 @@ var PDFPageView = (function PDFPageViewClosure() { canvas.style.height = canvas.parentNode.style.height = div.style.height = Math.floor(height) + 'px'; // The canvas may have been originally rotated, rotate relative to that. - var relativeRotation = this.viewport.rotation - canvas._viewport.rotation; + var relativeRotation = this.viewport.rotation - + this.paintedViewport.rotation; var absRotation = Math.abs(relativeRotation); var scaleX = 1, scaleY = 1; if (absRotation === 90 || absRotation === 270) { @@ -337,6 +343,7 @@ var PDFPageView = (function PDFPageViewClosure() { this.renderingState = RenderingStates.RUNNING; + var self = this; var pdfPage = this.pdfPage; var viewport = this.viewport; var div = this.div; @@ -347,62 +354,12 @@ var PDFPageView = (function PDFPageViewClosure() { canvasWrapper.style.height = div.style.height; canvasWrapper.classList.add('canvasWrapper'); - var canvas = document.createElement('canvas'); - canvas.id = 'page' + this.id; - // Keep the canvas hidden until the first draw callback, or until drawing - // is complete when `!this.renderingQueue`, to prevent black flickering. - canvas.setAttribute('hidden', 'hidden'); - var isCanvasHidden = true; - - canvasWrapper.appendChild(canvas); if (this.annotationLayer && this.annotationLayer.div) { // annotationLayer needs to stay on top div.insertBefore(canvasWrapper, this.annotationLayer.div); } else { div.appendChild(canvasWrapper); } - this.canvas = canvas; - - if (typeof PDFJSDev === 'undefined' || - PDFJSDev.test('MOZCENTRAL || FIREFOX || GENERIC')) { - canvas.mozOpaque = true; - } - - var ctx = canvas.getContext('2d', {alpha: false}); - var outputScale = getOutputScale(ctx); - this.outputScale = outputScale; - - if (pdfjsLib.PDFJS.useOnlyCssZoom) { - var actualSizeViewport = viewport.clone({scale: CSS_UNITS}); - // Use a scale that will make the canvas be the original intended size - // of the page. - outputScale.sx *= actualSizeViewport.width / viewport.width; - outputScale.sy *= actualSizeViewport.height / viewport.height; - outputScale.scaled = true; - } - - if (pdfjsLib.PDFJS.maxCanvasPixels > 0) { - var pixelsInViewport = viewport.width * viewport.height; - var maxScale = - Math.sqrt(pdfjsLib.PDFJS.maxCanvasPixels / pixelsInViewport); - if (outputScale.sx > maxScale || outputScale.sy > maxScale) { - outputScale.sx = maxScale; - outputScale.sy = maxScale; - outputScale.scaled = true; - this.hasRestrictedScaling = true; - } else { - this.hasRestrictedScaling = false; - } - } - - var sfx = approximateFraction(outputScale.sx); - var sfy = approximateFraction(outputScale.sy); - canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]); - canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]); - canvas.style.width = roundToDivide(viewport.width, sfx[1]) + 'px'; - canvas.style.height = roundToDivide(viewport.height, sfy[1]) + 'px'; - // Add the viewport so it's known what it was originally drawn with. - canvas._viewport = viewport; var textLayerDiv = null; var textLayer = null; @@ -424,35 +381,36 @@ var PDFPageView = (function PDFPageViewClosure() { } this.textLayer = textLayer; - var resolveRenderPromise, rejectRenderPromise; - var promise = new Promise(function (resolve, reject) { - resolveRenderPromise = resolve; - rejectRenderPromise = reject; - }); - - // Rendering area + var renderContinueCallback = null; + if (this.renderingQueue) { + renderContinueCallback = function renderContinueCallback(cont) { + if (!self.renderingQueue.isHighestPriority(self)) { + self.renderingState = RenderingStates.PAUSED; + self.resume = function resumeCallback() { + self.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + }; + } - var self = this; - function pageViewDrawCallback(error) { - // The renderTask may have been replaced by a new one, so only remove - // the reference to the renderTask if it matches the one that is + var finishPaintTask = function finishPaintTask(error) { + // The paintTask may have been replaced by a new one, so only remove + // the reference to the paintTask if it matches the one that is // triggering this callback. - if (renderTask === self.renderTask) { - self.renderTask = null; + if (paintTask === self.paintTask) { + self.paintTask = null; } if (error === 'cancelled') { - rejectRenderPromise(error); + self.error = null; return; } self.renderingState = RenderingStates.FINISHED; - if (isCanvasHidden) { - self.canvas.removeAttribute('hidden'); - isCanvasHidden = false; - } - if (self.loadingIconDiv) { div.removeChild(self.loadingIconDiv); delete self.loadingIconDiv; @@ -484,33 +442,121 @@ var PDFPageView = (function PDFPageViewClosure() { pageNumber: self.id, cssTransform: false, }); + }; - if (!error) { - resolveRenderPromise(undefined); - } else { - rejectRenderPromise(error); + var paintTask = this.paintOnCanvas(canvasWrapper); + paintTask.onRenderContinue = renderContinueCallback; + this.paintTask = paintTask; + + var resultPromise = paintTask.promise.then(function () { + finishPaintTask(null); + if (textLayer) { + pdfPage.getTextContent({ + normalizeWhitespace: true, + }).then(function textContentResolved(textContent) { + textLayer.setTextContent(textContent); + textLayer.render(TEXT_LAYER_RENDER_DELAY); + }); + } + }, function (reason) { + finishPaintTask(reason); + throw reason; + }); + + if (this.annotationLayerFactory) { + if (!this.annotationLayer) { + this.annotationLayer = this.annotationLayerFactory. + createAnnotationLayerBuilder(div, pdfPage, + this.renderInteractiveForms); } + this.annotationLayer.render(this.viewport, 'display'); } + div.setAttribute('data-loaded', true); - var renderContinueCallback = null; - if (this.renderingQueue) { - renderContinueCallback = function renderContinueCallback(cont) { - if (!self.renderingQueue.isHighestPriority(self)) { - self.renderingState = RenderingStates.PAUSED; - self.resume = function resumeCallback() { - self.renderingState = RenderingStates.RUNNING; - cont(); - }; - return; - } - if (isCanvasHidden) { - self.canvas.removeAttribute('hidden'); - isCanvasHidden = false; - } + if (this.onBeforeDraw) { + this.onBeforeDraw(); + } + return resultPromise; + }, + + paintOnCanvas: function (canvasWrapper) { + var resolveRenderPromise, rejectRenderPromise; + var promise = new Promise(function (resolve, reject) { + resolveRenderPromise = resolve; + rejectRenderPromise = reject; + }); + + var result = { + promise: promise, + onRenderContinue: function (cont) { cont(); - }; + }, + cancel: function () { + renderTask.cancel(); + } + }; + + var self = this; + var pdfPage = this.pdfPage; + var viewport = this.viewport; + var canvas = document.createElement('canvas'); + canvas.id = 'page' + this.id; + // Keep the canvas hidden until the first draw callback, or until drawing + // is complete when `!this.renderingQueue`, to prevent black flickering. + canvas.setAttribute('hidden', 'hidden'); + var isCanvasHidden = true; + var showCanvas = function () { + if (isCanvasHidden) { + canvas.removeAttribute('hidden'); + isCanvasHidden = false; + } + }; + + canvasWrapper.appendChild(canvas); + this.canvas = canvas; + + if (typeof PDFJSDev === 'undefined' || + PDFJSDev.test('MOZCENTRAL || FIREFOX || GENERIC')) { + canvas.mozOpaque = true; } + var ctx = canvas.getContext('2d', {alpha: false}); + var outputScale = getOutputScale(ctx); + this.outputScale = outputScale; + + if (pdfjsLib.PDFJS.useOnlyCssZoom) { + var actualSizeViewport = viewport.clone({scale: CSS_UNITS}); + // Use a scale that will make the canvas be the original intended size + // of the page. + outputScale.sx *= actualSizeViewport.width / viewport.width; + outputScale.sy *= actualSizeViewport.height / viewport.height; + outputScale.scaled = true; + } + + if (pdfjsLib.PDFJS.maxCanvasPixels > 0) { + var pixelsInViewport = viewport.width * viewport.height; + var maxScale = + Math.sqrt(pdfjsLib.PDFJS.maxCanvasPixels / pixelsInViewport); + if (outputScale.sx > maxScale || outputScale.sy > maxScale) { + outputScale.sx = maxScale; + outputScale.sy = maxScale; + outputScale.scaled = true; + this.hasRestrictedScaling = true; + } else { + this.hasRestrictedScaling = false; + } + } + + var sfx = approximateFraction(outputScale.sx); + var sfy = approximateFraction(outputScale.sy); + canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]); + canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]); + canvas.style.width = roundToDivide(viewport.width, sfx[1]) + 'px'; + canvas.style.height = roundToDivide(viewport.height, sfy[1]) + 'px'; + // Add the viewport so it's known what it was originally drawn with. + this.paintedViewport = viewport; + + // Rendering area var transform = !outputScale.scaled ? null : [outputScale.sx, 0, 0, outputScale.sy, 0, 0]; var renderContext = { @@ -520,40 +566,28 @@ var PDFPageView = (function PDFPageViewClosure() { renderInteractiveForms: this.renderInteractiveForms, // intent: 'default', // === 'display' }; - var renderTask = this.renderTask = this.pdfPage.render(renderContext); - renderTask.onContinue = renderContinueCallback; + var renderTask = this.pdfPage.render(renderContext); + renderTask.onContinue = function (cont) { + showCanvas(); + if (result.onRenderContinue) { + result.onRenderContinue(cont); + } else { + cont(); + } + }; - this.renderTask.promise.then( + renderTask.promise.then( function pdfPageRenderCallback() { - pageViewDrawCallback(null); - if (textLayer) { - self.pdfPage.getTextContent({ - normalizeWhitespace: true, - }).then(function textContentResolved(textContent) { - textLayer.setTextContent(textContent); - textLayer.render(TEXT_LAYER_RENDER_DELAY); - }); - } + showCanvas(); + resolveRenderPromise(undefined); }, function pdfPageRenderError(error) { - pageViewDrawCallback(error); + showCanvas(); + rejectRenderPromise(error); } ); - if (this.annotationLayerFactory) { - if (!this.annotationLayer) { - this.annotationLayer = this.annotationLayerFactory. - createAnnotationLayerBuilder(div, this.pdfPage, - this.renderInteractiveForms); - } - this.annotationLayer.render(this.viewport, 'display'); - } - div.setAttribute('data-loaded', true); - - if (self.onBeforeDraw) { - self.onBeforeDraw(); - } - return promise; + return result; }, /**