UNPKG

9.56 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * JavaScript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.PDFPrintService = PDFPrintService;
28
29var _pdf = require("../pdf");
30
31var _app = require("./app.js");
32
33var _print_utils = require("./print_utils.js");
34
35let activeService = null;
36let dialog = null;
37let overlayManager = null;
38
39function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size, printResolution, optionalContentConfigPromise, printAnnotationStoragePromise) {
40 const scratchCanvas = activeService.scratchCanvas;
41 const PRINT_UNITS = printResolution / _pdf.PixelsPerInch.PDF;
42 scratchCanvas.width = Math.floor(size.width * PRINT_UNITS);
43 scratchCanvas.height = Math.floor(size.height * PRINT_UNITS);
44 const ctx = scratchCanvas.getContext("2d");
45 ctx.save();
46 ctx.fillStyle = "rgb(255, 255, 255)";
47 ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
48 ctx.restore();
49 return Promise.all([pdfDocument.getPage(pageNumber), printAnnotationStoragePromise]).then(function ([pdfPage, printAnnotationStorage]) {
50 const renderContext = {
51 canvasContext: ctx,
52 transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
53 viewport: pdfPage.getViewport({
54 scale: 1,
55 rotation: size.rotation
56 }),
57 intent: "print",
58 annotationMode: _pdf.AnnotationMode.ENABLE_STORAGE,
59 optionalContentConfigPromise,
60 printAnnotationStorage
61 };
62 return pdfPage.render(renderContext).promise;
63 });
64}
65
66function PDFPrintService(pdfDocument, pagesOverview, printContainer, printResolution, optionalContentConfigPromise = null, printAnnotationStoragePromise = null, l10n) {
67 this.pdfDocument = pdfDocument;
68 this.pagesOverview = pagesOverview;
69 this.printContainer = printContainer;
70 this._printResolution = printResolution || 150;
71 this._optionalContentConfigPromise = optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
72 this._printAnnotationStoragePromise = printAnnotationStoragePromise || Promise.resolve();
73 this.l10n = l10n;
74 this.currentPage = -1;
75 this.scratchCanvas = document.createElement("canvas");
76}
77
78PDFPrintService.prototype = {
79 layout() {
80 this.throwIfInactive();
81 const body = document.querySelector("body");
82 body.setAttribute("data-pdfjsprinting", true);
83 const hasEqualPageSizes = this.pagesOverview.every(function (size) {
84 return size.width === this.pagesOverview[0].width && size.height === this.pagesOverview[0].height;
85 }, this);
86
87 if (!hasEqualPageSizes) {
88 console.warn("Not all pages have the same size. The printed " + "result may be incorrect!");
89 }
90
91 this.pageStyleSheet = document.createElement("style");
92 const pageSize = this.pagesOverview[0];
93 this.pageStyleSheet.textContent = "@page { size: " + pageSize.width + "pt " + pageSize.height + "pt;}";
94 body.append(this.pageStyleSheet);
95 },
96
97 destroy() {
98 if (activeService !== this) {
99 return;
100 }
101
102 this.printContainer.textContent = "";
103 const body = document.querySelector("body");
104 body.removeAttribute("data-pdfjsprinting");
105
106 if (this.pageStyleSheet) {
107 this.pageStyleSheet.remove();
108 this.pageStyleSheet = null;
109 }
110
111 this.scratchCanvas.width = this.scratchCanvas.height = 0;
112 this.scratchCanvas = null;
113 activeService = null;
114 ensureOverlay().then(function () {
115 if (overlayManager.active === dialog) {
116 overlayManager.close(dialog);
117 }
118 });
119 },
120
121 renderPages() {
122 if (this.pdfDocument.isPureXfa) {
123 (0, _print_utils.getXfaHtmlForPrinting)(this.printContainer, this.pdfDocument);
124 return Promise.resolve();
125 }
126
127 const pageCount = this.pagesOverview.length;
128
129 const renderNextPage = (resolve, reject) => {
130 this.throwIfInactive();
131
132 if (++this.currentPage >= pageCount) {
133 renderProgress(pageCount, pageCount, this.l10n);
134 resolve();
135 return;
136 }
137
138 const index = this.currentPage;
139 renderProgress(index, pageCount, this.l10n);
140 renderPage(this, this.pdfDocument, index + 1, this.pagesOverview[index], this._printResolution, this._optionalContentConfigPromise, this._printAnnotationStoragePromise).then(this.useRenderedPage.bind(this)).then(function () {
141 renderNextPage(resolve, reject);
142 }, reject);
143 };
144
145 return new Promise(renderNextPage);
146 },
147
148 useRenderedPage() {
149 this.throwIfInactive();
150 const img = document.createElement("img");
151 const scratchCanvas = this.scratchCanvas;
152
153 if ("toBlob" in scratchCanvas) {
154 scratchCanvas.toBlob(function (blob) {
155 img.src = URL.createObjectURL(blob);
156 });
157 } else {
158 img.src = scratchCanvas.toDataURL();
159 }
160
161 const wrapper = document.createElement("div");
162 wrapper.className = "printedPage";
163 wrapper.append(img);
164 this.printContainer.append(wrapper);
165 return new Promise(function (resolve, reject) {
166 img.onload = resolve;
167 img.onerror = reject;
168 });
169 },
170
171 performPrint() {
172 this.throwIfInactive();
173 return new Promise(resolve => {
174 setTimeout(() => {
175 if (!this.active) {
176 resolve();
177 return;
178 }
179
180 print.call(window);
181 setTimeout(resolve, 20);
182 }, 0);
183 });
184 },
185
186 get active() {
187 return this === activeService;
188 },
189
190 throwIfInactive() {
191 if (!this.active) {
192 throw new Error("This print request was cancelled or completed.");
193 }
194 }
195
196};
197const print = window.print;
198
199window.print = function () {
200 if (activeService) {
201 console.warn("Ignored window.print() because of a pending print job.");
202 return;
203 }
204
205 ensureOverlay().then(function () {
206 if (activeService) {
207 overlayManager.open(dialog);
208 }
209 });
210
211 try {
212 dispatchEvent("beforeprint");
213 } finally {
214 if (!activeService) {
215 console.error("Expected print service to be initialized.");
216 ensureOverlay().then(function () {
217 if (overlayManager.active === dialog) {
218 overlayManager.close(dialog);
219 }
220 });
221 return;
222 }
223
224 const activeServiceOnEntry = activeService;
225 activeService.renderPages().then(function () {
226 return activeServiceOnEntry.performPrint();
227 }).catch(function () {}).then(function () {
228 if (activeServiceOnEntry.active) {
229 abort();
230 }
231 });
232 }
233};
234
235function dispatchEvent(eventType) {
236 const event = document.createEvent("CustomEvent");
237 event.initCustomEvent(eventType, false, false, "custom");
238 window.dispatchEvent(event);
239}
240
241function abort() {
242 if (activeService) {
243 activeService.destroy();
244 dispatchEvent("afterprint");
245 }
246}
247
248function renderProgress(index, total, l10n) {
249 dialog ||= document.getElementById("printServiceDialog");
250 const progress = Math.round(100 * index / total);
251 const progressBar = dialog.querySelector("progress");
252 const progressPerc = dialog.querySelector(".relative-progress");
253 progressBar.value = progress;
254 l10n.get("print_progress_percent", {
255 progress
256 }).then(msg => {
257 progressPerc.textContent = msg;
258 });
259}
260
261window.addEventListener("keydown", function (event) {
262 if (event.keyCode === 80 && (event.ctrlKey || event.metaKey) && !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
263 window.print();
264 event.preventDefault();
265
266 if (event.stopImmediatePropagation) {
267 event.stopImmediatePropagation();
268 } else {
269 event.stopPropagation();
270 }
271 }
272}, true);
273
274if ("onbeforeprint" in window) {
275 const stopPropagationIfNeeded = function (event) {
276 if (event.detail !== "custom" && event.stopImmediatePropagation) {
277 event.stopImmediatePropagation();
278 }
279 };
280
281 window.addEventListener("beforeprint", stopPropagationIfNeeded);
282 window.addEventListener("afterprint", stopPropagationIfNeeded);
283}
284
285let overlayPromise;
286
287function ensureOverlay() {
288 if (!overlayPromise) {
289 overlayManager = _app.PDFViewerApplication.overlayManager;
290
291 if (!overlayManager) {
292 throw new Error("The overlay manager has not yet been initialized.");
293 }
294
295 dialog ||= document.getElementById("printServiceDialog");
296 overlayPromise = overlayManager.register(dialog, true);
297 document.getElementById("printCancel").onclick = abort;
298 dialog.addEventListener("close", abort);
299 }
300
301 return overlayPromise;
302}
303
304_app.PDFPrintServiceFactory.instance = {
305 supportsPrinting: true,
306
307 createPrintService(pdfDocument, pagesOverview, printContainer, printResolution, optionalContentConfigPromise, printAnnotationStoragePromise, l10n) {
308 if (activeService) {
309 throw new Error("The print service is created and active.");
310 }
311
312 activeService = new PDFPrintService(pdfDocument, pagesOverview, printContainer, printResolution, optionalContentConfigPromise, printAnnotationStoragePromise, l10n);
313 return activeService;
314 }
315
316};
\No newline at end of file