diff --git a/web/mozPrintCallback_polyfill.js b/web/mozPrintCallback_polyfill.js new file mode 100644 index 000000000..c5eeb3548 --- /dev/null +++ b/web/mozPrintCallback_polyfill.js @@ -0,0 +1,141 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2013 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. + */ +/* globals HTMLCanvasElement */ + +'use strict'; +(function mozPrintCallbackPolyfillClosure() { + if ('mozPrintCallback' in document.createElement('canvas')) { + return; + } + // Cause positive result on feature-detection: + HTMLCanvasElement.prototype.mozPrintCallback = undefined; + + var canvases; // During print task: non-live NodeList of elements + var index; // Index of element that is being processed + + var print = window.print; + window.print = function print() { + if (canvases) { + console.warn('Ignored window.print() because of a pending print job.'); + return; + } + try { + dispatchEvent('beforeprint'); + } finally { + canvases = document.querySelectorAll('canvas'); + index = -1; + next(); + } + }; + + function dispatchEvent(eventType) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent(eventType, false, false, 'custom'); + window.dispatchEvent(event); + } + + function next() { + if (!canvases) { + return; // Print task cancelled by user (state reset in abort()) + } + + renderProgress(); + if (++index < canvases.length) { + var canvas = canvases[index]; + if (typeof canvas.mozPrintCallback === 'function') { + canvas.mozPrintCallback({ + context: canvas.getContext('2d'), + abort: abort, + done: next + }); + } else { + next(); + } + } else { + renderProgress(); + print.call(window); + setTimeout(abort, 20); // Tidy-up + } + } + + function abort() { + if (canvases) { + canvases = null; + renderProgress(); + dispatchEvent('afterprint'); + } + } + + function renderProgress() { + var progressContainer = document.getElementById('mozPrintCallback-shim'); + if (canvases) { + var progress = Math.round(100 * index / canvases.length); + var progressBar = progressContainer.querySelector('progress'); + var progressPerc = progressContainer.querySelector('.relative-progress'); + progressBar.value = progress; + progressPerc.textContent = progress + '%'; + progressContainer.removeAttribute('hidden'); + progressContainer.onclick = abort; + } else { + progressContainer.setAttribute('hidden', ''); + } + } + + var hasAttachEvent = !!document.attachEvent; + + window.addEventListener('keydown', function(event) { + if (event.keyCode === 80/*P*/ && (event.ctrlKey || event.metaKey)) { + window.print(); + if (hasAttachEvent) { + // Only attachEvent can cancel Ctrl + P dialog in IE <=10 + // attachEvent is gone in IE11, so the dialog will re-appear in IE11. + return; + } + event.preventDefault(); + if (event.stopImmediatePropagation) { + event.stopImmediatePropagation(); + } else { + event.stopPropagation(); + } + return; + } + if (event.keyCode === 27 && canvases) { // Esc + abort(); + } + }, true); + if (hasAttachEvent) { + document.attachEvent('onkeydown', function(event) { + event = event || window.event; + if (event.keyCode === 80/*P*/ && event.ctrlKey) { + event.keyCode = 0; + return false; + } + }); + } + + if ('onbeforeprint' in window) { + // Do not propagate before/afterprint events when they are not triggered + // from within this polyfill. (FF/IE). + var stopPropagationIfNeeded = function(event) { + if (event.detail !== 'custom' && event.stopImmediatePropagation) { + event.stopImmediatePropagation(); + } + }; + window.addEventListener('beforeprint', stopPropagationIfNeeded, false); + window.addEventListener('afterprint', stopPropagationIfNeeded, false); + } +})(); diff --git a/web/page_view.js b/web/page_view.js index 124998b33..3d70074fe 100644 --- a/web/page_view.js +++ b/web/page_view.js @@ -525,7 +525,11 @@ var PageView = function pageView(container, id, scale, CustomStyle.setProp('transformOrigin' , canvas, '0% 0%'); var printContainer = document.getElementById('printContainer'); - printContainer.appendChild(canvas); + var canvasWrapper = document.createElement('div'); + canvasWrapper.style.width = viewport.width + 'pt'; + canvasWrapper.style.height = viewport.height + 'pt'; + canvasWrapper.appendChild(canvas); + printContainer.appendChild(canvasWrapper); var self = this; canvas.mozPrintCallback = function(obj) { diff --git a/web/viewer-snippet-mozPrintCallback-polyfill.html b/web/viewer-snippet-mozPrintCallback-polyfill.html new file mode 100644 index 000000000..ebf2e3419 --- /dev/null +++ b/web/viewer-snippet-mozPrintCallback-polyfill.html @@ -0,0 +1,71 @@ + diff --git a/web/viewer.html b/web/viewer.html index c970b3236..3eb22ab06 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -309,5 +309,8 @@ limitations under the License.
+ + + diff --git a/web/viewer.js b/web/viewer.js index 5cb315fde..dcfd46738 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -56,6 +56,11 @@ PDFJS.imageResourcesPath = './images/'; var mozL10n = document.mozL10n || document.webL10n; //#include ui_utils.js + +//#if !(FIREFOX || MOZCENTRAL || B2G) +//#include mozPrintCallback_polyfill.js +//#endif + //#if GENERIC || CHROME //#include download_manager.js //#endif