From 24a7a58da7a5144d736fb9b2bf673c386fe0bcbd Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Thu, 6 Oct 2016 16:46:30 -0500 Subject: [PATCH] Moves mozPrintCallback specific code to firefox_printservice.js --- gulpfile.js | 2 +- web/app.js | 89 ++++++------------ web/firefox_print_service.js | 155 +++++++++++++++++++++++++++++++ web/mozPrintCallback_polyfill.js | 3 + web/pdf_page_view.js | 53 ----------- web/pdf_viewer.js | 20 ++++ web/viewer.css | 6 +- web/viewer.js | 3 +- 8 files changed, 213 insertions(+), 118 deletions(-) create mode 100644 web/firefox_print_service.js diff --git a/gulpfile.js b/gulpfile.js index b13865343..bc3cb6e5c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -244,7 +244,7 @@ function createWebBundle(defines) { amdName = 'pdfjs-dist/web/viewer'; outputName = 'viewer.js'; template = 'web/viewer.js'; - files = ['app.js']; + files = ['app.js', 'firefox_print_service.js']; if (defines.FIREFOX || defines.MOZCENTRAL) { files.push('firefoxcom.js'); } else if (defines.CHROME) { diff --git a/web/app.js b/web/app.js index e9f64c231..8f3bfc58a 100644 --- a/web/app.js +++ b/web/app.js @@ -143,7 +143,7 @@ var PDFViewerApplication = { appConfig: null, pdfDocument: null, pdfLoadingTask: null, - printing: false, + printService: null, /** @type {PDFViewer} */ pdfViewer: null, /** @type {PDFThumbnailViewer} */ @@ -428,11 +428,12 @@ var PDFViewerApplication = { return this.pdfViewer.currentPageNumber; }, - get supportsPrinting() { - var canvas = document.createElement('canvas'); - var value = 'mozPrintCallback' in canvas; + get printing() { + return !!this.printService; + }, - return pdfjsLib.shadow(this, 'supportsPrinting', value); + get supportsPrinting() { + return PDFPrintServiceFactory.instance.supportsPrinting; }, get supportsFullscreen() { @@ -1106,59 +1107,23 @@ var PDFViewerApplication = { return; } - var alertNotReady = false; - var i, ii; - if (!this.pdfDocument || !this.pagesCount) { - alertNotReady = true; - } else { - for (i = 0, ii = this.pagesCount; i < ii; ++i) { - if (!this.pdfViewer.getPageView(i).pdfPage) { - alertNotReady = true; - break; - } - } - } - if (alertNotReady) { + // The beforePrint is a sync method and we need to know layout before + // returning from this method. Ensure that we can get sizes of the pages. + if (!this.pdfViewer.pageViewsReady) { var notReadyMessage = mozL10n.get('printing_not_ready', null, 'Warning: The PDF is not fully loaded for printing.'); window.alert(notReadyMessage); return; } - this.printing = true; + var pagesOverview = this.pdfViewer.getPagesOverview(); + var printContainer = this.appConfig.printContainer; + var printService = PDFPrintServiceFactory.instance.createPrintService( + this.pdfDocument, pagesOverview, printContainer); + this.printService = printService; this.forceRendering(); - var printContainer = this.appConfig.printContainer; - var body = document.querySelector('body'); - body.setAttribute('data-mozPrintCallback', true); - - if (!this.hasEqualPageSizes) { - console.warn('Not all pages have the same size. The printed result ' + - 'may be incorrect!'); - } - - // Insert a @page + size rule to make sure that the page size is correctly - // set. Note that we assume that all pages have the same size, because - // variable-size pages are not supported yet (at least in Chrome & Firefox). - // TODO(robwu): Use named pages when size calculation bugs get resolved - // (e.g. https://crbug.com/355116) AND when support for named pages is - // added (http://www.w3.org/TR/css3-page/#using-named-pages). - // In browsers where @page + size is not supported (such as Firefox, - // https://bugzil.la/851441), the next stylesheet will be ignored and the - // user has to select the correct paper size in the UI if wanted. - this.pageStyleSheet = document.createElement('style'); - var pageSize = this.pdfViewer.getPageView(0).pdfPage.getViewport(1); - this.pageStyleSheet.textContent = - // "size: " is what we need. But also add "A4" because - // Firefox incorrectly reports support for the other value. - '@supports ((size:A4) and (size:1pt 1pt)) {' + - '@page { size: ' + pageSize.width + 'pt ' + pageSize.height + 'pt;}' + - '}'; - body.appendChild(this.pageStyleSheet); - - for (i = 0, ii = this.pagesCount; i < ii; ++i) { - this.pdfViewer.getPageView(i).beforePrint(printContainer); - } + printService.layout(); //#if !PRODUCTION if (true) { @@ -1186,17 +1151,10 @@ var PDFViewerApplication = { }, afterPrint: function pdfViewSetupAfterPrint() { - var div = this.appConfig.printContainer; - while (div.hasChildNodes()) { - div.removeChild(div.lastChild); + if (this.printService) { + this.printService.destroy(); + this.printService = null; } - - if (this.pageStyleSheet && this.pageStyleSheet.parentNode) { - this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet); - this.pageStyleSheet = null; - } - - this.printing = false; this.forceRendering(); }, @@ -2330,6 +2288,17 @@ window.addEventListener('afterprint', function afterPrint(evt) { }); })(); +/* Abstract factory for the print service. */ +var PDFPrintServiceFactory = { + instance: { + supportsPrinting: false, + createPrintService: function () { + throw new Error('Not implemented: createPrintService'); + } + } +}; + exports.PDFViewerApplication = PDFViewerApplication; exports.DefaultExernalServices = DefaultExernalServices; +exports.PDFPrintServiceFactory = PDFPrintServiceFactory; })); diff --git a/web/firefox_print_service.js b/web/firefox_print_service.js new file mode 100644 index 000000000..ec6ab3a83 --- /dev/null +++ b/web/firefox_print_service.js @@ -0,0 +1,155 @@ +/* Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define('pdfjs-web/firefox_print_service', ['exports', 'pdfjs-web/ui_utils', + 'pdfjs-web/app', 'pdfjs-web/pdfjs'], factory); + } else if (typeof exports !== 'undefined') { + factory(exports, require('./ui_utils.js'), require('./app.js'), + require('./pdfjs.js')); + } else { + factory((root.pdfjsWebFirefoxPrintService = {}), root.pdfjsWebUIUtils, + root.pdfjsWebApp, root.pdfjsWebPDFJS); + } +}(this, function (exports, uiUtils, app, pdfjsLib) { + var CSS_UNITS = uiUtils.CSS_UNITS; + var PDFPrintServiceFactory = app.PDFPrintServiceFactory; + + // Creates a placeholder with div and canvas with right size for the page. + function composePage(pdfDocument, pageNumber, size, printContainer) { + var canvas = document.createElement('canvas'); + + // The size of the canvas in pixels for printing. + var PRINT_RESOLUTION = 150; + var PRINT_UNITS = PRINT_RESOLUTION / 72.0; + canvas.width = Math.floor(size.width * PRINT_UNITS); + canvas.height = Math.floor(size.height * PRINT_UNITS); + + // The physical size of the canvas as specified by the PDF document. + canvas.style.width = Math.floor(size.width * CSS_UNITS) + 'px'; + canvas.style.height = Math.floor(size.height * CSS_UNITS) + 'px'; + + var canvasWrapper = document.createElement('div'); + canvasWrapper.appendChild(canvas); + printContainer.appendChild(canvasWrapper); + + canvas.mozPrintCallback = function(obj) { + // Printing/rendering the page. + var ctx = obj.context; + + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + pdfDocument.getPage(pageNumber).then(function (pdfPage) { + var renderContext = { + canvasContext: ctx, + transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0], + viewport: pdfPage.getViewport(1), + intent: 'print' + }; + return pdfPage.render(renderContext).promise; + }).then(function() { + // Tell the printEngine that rendering this canvas/page has finished. + obj.done(); + }, function(error) { + console.error(error); + // Tell the printEngine that rendering this canvas/page has failed. + // This will make the print process stop. + if ('abort' in obj) { + obj.abort(); + } else { + obj.done(); + } + }); + }; + } + + function FirefoxPrintService(pdfDocument, pagesOverview, printContainer) { + this.pdfDocument = pdfDocument; + this.pagesOverview = pagesOverview; + this.printContainer = printContainer; + this.pageStyleSheet = null; + } + + FirefoxPrintService.prototype = { + layout: function () { + var pdfDocument = this.pdfDocument; + var printContainer = this.printContainer; + var body = document.querySelector('body'); + body.setAttribute('data-pdfjsprinting', true); + + var hasEqualPageSizes = this.pagesOverview.every(function (size) { + return size.width === this.pagesOverview[0].width && + size.height === this.pagesOverview[0].height; + }, this); + if (!hasEqualPageSizes) { + console.warn('Not all pages have the same size. The printed ' + + 'result may be incorrect!'); + } + + // Insert a @page + size rule to make sure that the page size is correctly + // set. Note that we assume that all pages have the same size, because + // variable-size pages are not supported yet (e.g. in Chrome & Firefox). + // TODO(robwu): Use named pages when size calculation bugs get resolved + // (e.g. https://crbug.com/355116) AND when support for named pages is + // added (http://www.w3.org/TR/css3-page/#using-named-pages). + // In browsers where @page + size is not supported (such as Firefox, + // https://bugzil.la/851441), the next stylesheet will be ignored and the + // user has to select the correct paper size in the UI if wanted. + this.pageStyleSheet = document.createElement('style'); + var pageSize = this.pagesOverview[0]; + this.pageStyleSheet.textContent = + // "size: " is what we need. But also add "A4" because + // Firefox incorrectly reports support for the other value. + '@supports ((size:A4) and (size:1pt 1pt)) {' + + '@page { size: ' + pageSize.width + 'pt ' + pageSize.height + 'pt;}' + + '}'; + body.appendChild(this.pageStyleSheet); + + for (var i = 0, ii = this.pagesOverview.length; i < ii; ++i) { + composePage(pdfDocument, i + 1, this.pagesOverview[i], printContainer); + } + }, + + destroy: function () { + this.printContainer.textContent = ''; + if (this.pageStyleSheet && this.pageStyleSheet.parentNode) { + this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet); + this.pageStyleSheet = null; + } + } + }; + + PDFPrintServiceFactory.instance = { + get supportsPrinting() { + var canvas = document.createElement('canvas'); + var value = 'mozPrintCallback' in canvas; + + return pdfjsLib.shadow(this, 'supportsPrinting', value); + }, + + createPrintService: function (pdfDocument, pagesOverview, printContainer) { + return new FirefoxPrintService(pdfDocument, pagesOverview, + printContainer); + } + }; + + exports.FirefoxPrintService = FirefoxPrintService; +})); diff --git a/web/mozPrintCallback_polyfill.js b/web/mozPrintCallback_polyfill.js index e8fde3c71..043206424 100644 --- a/web/mozPrintCallback_polyfill.js +++ b/web/mozPrintCallback_polyfill.js @@ -99,6 +99,9 @@ function renderProgress() { var progressContainer = document.getElementById('mozPrintCallback-shim'); + if (!progressContainer) { + return; + } if (canvases && canvases.length) { var progress = Math.round(100 * index / canvases.length); var progressBar = progressContainer.querySelector('progress'); diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 927d9b0d3..ee87bf8b2 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -540,59 +540,6 @@ var PDFPageView = (function PDFPageViewClosure() { } return promise; }, - - beforePrint: function PDFPageView_beforePrint(printContainer) { - var CustomStyle = pdfjsLib.CustomStyle; - var pdfPage = this.pdfPage; - - var viewport = pdfPage.getViewport(1); - - var canvas = document.createElement('canvas'); - - // The size of the canvas in pixels for printing. - var PRINT_RESOLUTION = 150; - var PRINT_UNITS = PRINT_RESOLUTION / 72.0; - canvas.width = Math.floor(viewport.width * PRINT_UNITS); - canvas.height = Math.floor(viewport.height * PRINT_UNITS); - - // The physical size of the canvas as specified by the PDF document. - canvas.style.width = Math.floor(viewport.width * CSS_UNITS) + 'px'; - canvas.style.height = Math.floor(viewport.height * CSS_UNITS) + 'px'; - - var canvasWrapper = document.createElement('div'); - canvasWrapper.appendChild(canvas); - printContainer.appendChild(canvasWrapper); - - canvas.mozPrintCallback = function(obj) { - var ctx = obj.context; - - ctx.save(); - ctx.fillStyle = 'rgb(255, 255, 255)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.restore(); - - var renderContext = { - canvasContext: ctx, - transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0], - viewport: viewport, - intent: 'print' - }; - - pdfPage.render(renderContext).promise.then(function() { - // Tell the printEngine that rendering this canvas/page has finished. - obj.done(); - }, function(error) { - console.error(error); - // Tell the printEngine that rendering this canvas/page has failed. - // This will make the print process stop. - if ('abort' in obj) { - obj.abort(); - } else { - obj.done(); - } - }); - }; - }, }; return PDFPageView; diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index cab205254..6b47781c3 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -161,6 +161,13 @@ var PDFViewer = (function pdfViewer() { return this._pages[index]; }, + /** + * @returns {boolean} true if all {PDFPageView} objects are initialized. + */ + get pageViewsReady() { + return this._pageViewsReady; + }, + /** * @returns {number} */ @@ -309,6 +316,7 @@ var PDFViewer = (function pdfViewer() { }); this.pagesPromise = pagesPromise; pagesPromise.then(function () { + self._pageViewsReady = true; self.eventBus.dispatch('pagesloaded', { source: self, pagesCount: pagesCount @@ -414,6 +422,7 @@ var PDFViewer = (function pdfViewer() { this._location = null; this._pagesRotation = 0; this._pagesRequests = []; + this._pageViewsReady = false; var container = this.viewer; while (container.hasChildNodes()) { @@ -877,6 +886,17 @@ var PDFViewer = (function pdfViewer() { setFindController: function (findController) { this.findController = findController; }, + + /** + * Returns sizes of the pages. + * @returns {Array} Array of objects with width/height fields. + */ + getPagesOverview: function () { + return this._pages.map(function (pageView) { + var viewport = pageView.pdfPage.getViewport(1); + return {width: viewport.width, height: viewport.height}; + }); + }, }; return PDFViewer; diff --git a/web/viewer.css b/web/viewer.css index 66e9e137d..4a0d2539b 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -1795,11 +1795,11 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * { display: none; } - /* Rules for browsers that support mozPrintCallback */ - body[data-mozPrintCallback] #outerContainer { + /* Rules for browsers that support PDF.js printing */ + body[data-pdfjsprinting] #outerContainer { display: none; } - body[data-mozPrintCallback] #printContainer { + body[data-pdfjsprinting] #printContainer { display: block; } #printContainer { diff --git a/web/viewer.js b/web/viewer.js index e955896c1..faa927d26 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -175,7 +175,8 @@ function webViewerLoad() { // Ensure that src/main_loader.js has loaded all the necessary dependencies // *before* the viewer loads, to prevent issues in browsers relying on e.g. // the Promise/URL polyfill in src/shared/util.js (fixes issue 7448). - require(['pdfjs-web/app', 'mozPrintCallback_polyfill.js'], function (web) { + require(['pdfjs-web/app', 'pdfjs-web/firefox_print_service', + 'pdfjs-web/mozPrintCallback_polyfill'], function (web) { window.PDFViewerApplication = web.PDFViewerApplication; web.PDFViewerApplication.run(config); });