pdfjs-vue-print
Version:
Example Vue 3 project using pdf.js to build a simple custom PDF.js viewer and print service.
284 lines (234 loc) • 7.46 kB
JavaScript
/* Copyright 2012 Mozilla Foundation
* Copyright 2022 Drew Letcher
*
* 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";
import * as pdfjsLib from "pdfjs-dist/build/pdf.js"
import { l10n } from "pdfjs-dist/web/pdf_viewer";
import { PDFPrintServiceFactory } from "./print_service_factory.js";
import * as PrintService from "./print_service.js"
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.js";
const CMAP_URL = "../../node_modules/pdfjs-dist/cmaps/";
const CMAP_PACKED = true;
const MAX_IMAGE_SIZE = 1024 * 1024;
const ENABLE_XFA = false;
export default class PrintOnlyApp {
constructor(options) {
this.options = options;
this.printContainer = options.printContainer || null;
this.password = options.password || "";
this.onPrinted = options.onPrinted || null;
this._boundEvents = {};
this.pdfDocument = null;
this.pdfLoadingTask = null;
this.url = "";
this.documentInfo = null;
this.metadata = null;
this.pagesOverview = null;
this.enablePrintAutoRotate = true;
this.printResolution = 150;
this.printService = null;
}
// Called once when the document is loaded.
async create() {
console.log("create");
PrintService.install();
this.bindPrintEvents();
}
destroy() {
this.unbindPrintEvents();
PrintService.remove();
}
bindPrintEvents() {
const { _boundEvents } = this;
_boundEvents.beforePrint = this.beforePrint.bind(this);
_boundEvents.afterPrint = this.afterPrint.bind(this);
window.addEventListener("beforeprint", _boundEvents.beforePrint);
window.addEventListener("afterprint", _boundEvents.afterPrint);
}
unbindPrintEvents() {
const { _boundEvents } = this;
window.removeEventListener("beforeprint", _boundEvents.beforePrint);
window.removeEventListener("afterprint", _boundEvents.afterPrint);
_boundEvents.beforePrint = null;
_boundEvents.afterPrint = null;
}
/**
* Opens PDF document specified by URL.
*/
async open(params) {
console.log("open");
if (this.pdfLoadingTask) {
// We need to destroy already opened document
await this.close()
}
const url = params.url;
this.setTitleUsingUrl(url);
// Loading document
const loadingTask = pdfjsLib.getDocument({
url,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
maxImageSize: MAX_IMAGE_SIZE,
enableXfa: ENABLE_XFA,
});
this.pdfLoadingTask = loadingTask;
let pdfPassword = this.password;
loadingTask.onPassword = (callback, reason) => {
console.log("onPassword: " + reason)
callback(pdfPassword)
}
const pdfDocument = await loadingTask.promise
// Document loaded, specifying document for the viewer.
this.pdfDocument = pdfDocument;
this.setTitleUsingMetadata(pdfDocument);
await this.getPagesOverview()
}
/**
* Closes opened PDF document
* @returns {Promise} - Returns the promise, which is resolved when all
* destruction is completed.
*/
async close() {
console.log("close");
if (!this.pdfLoadingTask) {
return;
}
await this.pdfLoadingTask.destroy();
this.pdfLoadingTask = null;
if (this.pdfDocument) {
this.pdfDocument = null;
}
}
async getPagesOverview() {
function isPortraitOrientation(size) {
return size.width <= size.height;
}
this.pagesOverview = [];
if (!this.pdfDocument) {
return;
}
for (let i = 1; i <= this.pdfDocument.numPages; i++) {
const pdfPage = await this.pdfDocument.getPage(i);
const viewport = pdfPage.getViewport({ scale: 1.0 });
if (!this.enablePrintAutoRotate || isPortraitOrientation(viewport)) {
this.pagesOverview.push({
width: viewport.width,
height: viewport.height,
rotation: viewport.rotation,
});
}
else {
// Landscape orientation.
this.pagesOverview.push({
width: viewport.height,
height: viewport.width,
rotation: (viewport.rotation - 90) % 360,
});
}
}
}
beforePrint() {
console.log("beforePrint")
if (this.printService) {
// There is no way to suppress beforePrint/afterPrint events,
// but PDFPrintService may generate double events -- this will ignore
// the second event that will be coming from native window.print().
return;
}
const pagesOverview = this.pagesOverview; // this.pdfViewer.getPagesOverview();
const printContainer = this.printContainer;
const printResolution = this.printResolution;
const optionalContentConfigPromise = null; // this.pdfViewer.optionalContentConfigPromise;
console.log("create printService")
const printService = PDFPrintServiceFactory.instance.createPrintService(
this.pdfDocument,
pagesOverview,
printContainer,
printResolution,
optionalContentConfigPromise,
l10n
);
this.printService = printService;
printService.layout();
}
print() {
window.print();
}
afterPrint() {
console.log("afterPrint");
if (this.printService) {
console.log("destroy printService")
this.printService.destroy();
this.printService = null;
}
if (this.onPrinted)
this.onPrinted();
}
setTitle(title) {
document.title = title;
}
setTitleUsingUrl(url) {
this.url = url;
let title = pdfjsLib.getFilenameFromUrl(url) || url;
try {
title = decodeURIComponent(title);
} catch (e) {
// decodeURIComponent may throw URIError,
// fall back to using the unprocessed url in that case
}
this.setTitle(title);
}
setTitleUsingMetadata(pdfDocument) {
const self = this;
pdfDocument.getMetadata()
.then((data) => {
const info = data.info;
const metadata = data.metadata;
self.documentInfo = info;
self.metadata = metadata;
// Provides some basic debug information
console.log(
"PDF " +
pdfDocument.fingerprints[ 0 ] +
" [" +
info.PDFFormatVersion +
" " +
(info.Producer || "-").trim() +
" / " +
(info.Creator || "-").trim() +
"]" +
" (PDF.js: " +
(pdfjsLib.version || "-") +
")"
);
let pdfTitle;
if (metadata && metadata.has("dc:title")) {
const title = metadata.get("dc:title");
// Ghostscript sometimes returns 'Untitled', so prevent setting the
// title to 'Untitled.
if (title !== "Untitled") {
pdfTitle = title;
}
}
if (!pdfTitle && info && info.Title) {
pdfTitle = info.Title;
}
if (pdfTitle) {
self.setTitle(pdfTitle + " - " + document.title);
}
});
}
};
export { PrintOnlyApp };