[api-minor] Introduce a PrintAnnotationStorage
with *frozen* serializable data
Given that printing is triggered *synchronously* in browsers, it's thus possible for scripting (in PDF documents) to modify the Annotation-data while printing is currently ongoing. To work-around that we add a new printing-specific `AnnotationStorage`, where the serializable data is *frozen* upon initialization, which the viewer can thus create/utilize during printing.
This commit is contained in:
parent
c5dc082da4
commit
1cc7cecc7b
@ -13,9 +13,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { objectFromMap, unreachable } from "../shared/util.js";
|
||||||
import { AnnotationEditor } from "./editor/editor.js";
|
import { AnnotationEditor } from "./editor/editor.js";
|
||||||
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
|
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
|
||||||
import { objectFromMap } from "../shared/util.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key/value storage for annotation data in forms.
|
* Key/value storage for annotation data in forms.
|
||||||
@ -98,7 +98,7 @@ class AnnotationStorage {
|
|||||||
this._storage.set(key, value);
|
this._storage.set(key, value);
|
||||||
}
|
}
|
||||||
if (modified) {
|
if (modified) {
|
||||||
this._setModified();
|
this.#setModified();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +110,7 @@ class AnnotationStorage {
|
|||||||
return this._storage.size;
|
return this._storage.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#setModified() {
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_setModified() {
|
|
||||||
if (!this._modified) {
|
if (!this._modified) {
|
||||||
this._modified = true;
|
this._modified = true;
|
||||||
if (typeof this.onSetModified === "function") {
|
if (typeof this.onSetModified === "function") {
|
||||||
@ -131,6 +128,13 @@ class AnnotationStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {PrintAnnotationStorage}
|
||||||
|
*/
|
||||||
|
get print() {
|
||||||
|
return new PrintAnnotationStorage(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PLEASE NOTE: Only intended for usage within the API itself.
|
* PLEASE NOTE: Only intended for usage within the API itself.
|
||||||
* @ignore
|
* @ignore
|
||||||
@ -139,11 +143,10 @@ class AnnotationStorage {
|
|||||||
if (this._storage.size === 0) {
|
if (this._storage.size === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clone = new Map();
|
const clone = new Map();
|
||||||
for (const [key, value] of this._storage) {
|
|
||||||
const val = value instanceof AnnotationEditor ? value.serialize() : value;
|
for (const [key, val] of this._storage) {
|
||||||
clone.set(key, val);
|
clone.set(key, val instanceof AnnotationEditor ? val.serialize() : val);
|
||||||
}
|
}
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
@ -152,15 +155,48 @@ class AnnotationStorage {
|
|||||||
* PLEASE NOTE: Only intended for usage within the API itself.
|
* PLEASE NOTE: Only intended for usage within the API itself.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
get hash() {
|
static getHash(map) {
|
||||||
|
if (!map) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
const hash = new MurmurHash3_64();
|
const hash = new MurmurHash3_64();
|
||||||
|
|
||||||
for (const [key, value] of this._storage) {
|
for (const [key, val] of map) {
|
||||||
const val = value instanceof AnnotationEditor ? value.serialize() : value;
|
|
||||||
hash.update(`${key}:${JSON.stringify(val)}`);
|
hash.update(`${key}:${JSON.stringify(val)}`);
|
||||||
}
|
}
|
||||||
return hash.hexdigest();
|
return hash.hexdigest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AnnotationStorage };
|
/**
|
||||||
|
* A special `AnnotationStorage` for use during printing, where the serializable
|
||||||
|
* data is *frozen* upon initialization, to prevent scripting from modifying its
|
||||||
|
* contents. (Necessary since printing is triggered synchronously in browsers.)
|
||||||
|
*/
|
||||||
|
class PrintAnnotationStorage extends AnnotationStorage {
|
||||||
|
#serializable = null;
|
||||||
|
|
||||||
|
constructor(parent) {
|
||||||
|
super();
|
||||||
|
// Create a *copy* of the data, since Objects are passed by reference in JS.
|
||||||
|
this.#serializable = structuredClone(parent.serializable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {PrintAnnotationStorage}
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line getter-return
|
||||||
|
get print() {
|
||||||
|
unreachable("Should not call PrintAnnotationStorage.print");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PLEASE NOTE: Only intended for usage within the API itself.
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
get serializable() {
|
||||||
|
return this.#serializable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AnnotationStorage, PrintAnnotationStorage };
|
||||||
|
@ -37,6 +37,10 @@ import {
|
|||||||
unreachable,
|
unreachable,
|
||||||
warn,
|
warn,
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
|
import {
|
||||||
|
AnnotationStorage,
|
||||||
|
PrintAnnotationStorage,
|
||||||
|
} from "./annotation_storage.js";
|
||||||
import {
|
import {
|
||||||
deprecated,
|
deprecated,
|
||||||
DOMCanvasFactory,
|
DOMCanvasFactory,
|
||||||
@ -49,7 +53,6 @@ import {
|
|||||||
StatTimer,
|
StatTimer,
|
||||||
} from "./display_utils.js";
|
} from "./display_utils.js";
|
||||||
import { FontFaceObject, FontLoader } from "./font_loader.js";
|
import { FontFaceObject, FontLoader } from "./font_loader.js";
|
||||||
import { AnnotationStorage } from "./annotation_storage.js";
|
|
||||||
import { CanvasGraphics } from "./canvas.js";
|
import { CanvasGraphics } from "./canvas.js";
|
||||||
import { GlobalWorkerOptions } from "./worker_options.js";
|
import { GlobalWorkerOptions } from "./worker_options.js";
|
||||||
import { isNodeJS } from "../shared/is_node.js";
|
import { isNodeJS } from "../shared/is_node.js";
|
||||||
@ -1181,6 +1184,7 @@ class PDFDocumentProxy {
|
|||||||
* states set.
|
* states set.
|
||||||
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] - Map some
|
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] - Map some
|
||||||
* annotation ids with canvases used to render them.
|
* annotation ids with canvases used to render them.
|
||||||
|
* @property {PrintAnnotationStorage} [printAnnotationStorage]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1201,6 +1205,7 @@ class PDFDocumentProxy {
|
|||||||
* (as above) but where interactive form elements are updated with data
|
* (as above) but where interactive form elements are updated with data
|
||||||
* from the {@link AnnotationStorage}-instance; useful e.g. for printing.
|
* from the {@link AnnotationStorage}-instance; useful e.g. for printing.
|
||||||
* The default value is `AnnotationMode.ENABLE`.
|
* The default value is `AnnotationMode.ENABLE`.
|
||||||
|
* @property {PrintAnnotationStorage} [printAnnotationStorage]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1399,6 +1404,7 @@ class PDFPageProxy {
|
|||||||
optionalContentConfigPromise = null,
|
optionalContentConfigPromise = null,
|
||||||
annotationCanvasMap = null,
|
annotationCanvasMap = null,
|
||||||
pageColors = null,
|
pageColors = null,
|
||||||
|
printAnnotationStorage = null,
|
||||||
}) {
|
}) {
|
||||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
||||||
if (arguments[0]?.renderInteractiveForms !== undefined) {
|
if (arguments[0]?.renderInteractiveForms !== undefined) {
|
||||||
@ -1433,7 +1439,8 @@ class PDFPageProxy {
|
|||||||
|
|
||||||
const intentArgs = this._transport.getRenderingIntent(
|
const intentArgs = this._transport.getRenderingIntent(
|
||||||
intent,
|
intent,
|
||||||
annotationMode
|
annotationMode,
|
||||||
|
printAnnotationStorage
|
||||||
);
|
);
|
||||||
// If there was a pending destroy, cancel it so no cleanup happens during
|
// If there was a pending destroy, cancel it so no cleanup happens during
|
||||||
// this call to render.
|
// this call to render.
|
||||||
@ -1560,6 +1567,7 @@ class PDFPageProxy {
|
|||||||
getOperatorList({
|
getOperatorList({
|
||||||
intent = "display",
|
intent = "display",
|
||||||
annotationMode = AnnotationMode.ENABLE,
|
annotationMode = AnnotationMode.ENABLE,
|
||||||
|
printAnnotationStorage = null,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
function operatorListChanged() {
|
function operatorListChanged() {
|
||||||
if (intentState.operatorList.lastChunk) {
|
if (intentState.operatorList.lastChunk) {
|
||||||
@ -1572,6 +1580,7 @@ class PDFPageProxy {
|
|||||||
const intentArgs = this._transport.getRenderingIntent(
|
const intentArgs = this._transport.getRenderingIntent(
|
||||||
intent,
|
intent,
|
||||||
annotationMode,
|
annotationMode,
|
||||||
|
printAnnotationStorage,
|
||||||
/* isOpList = */ true
|
/* isOpList = */ true
|
||||||
);
|
);
|
||||||
let intentState = this._intentStates.get(intentArgs.cacheKey);
|
let intentState = this._intentStates.get(intentArgs.cacheKey);
|
||||||
@ -1800,7 +1809,7 @@ class PDFPageProxy {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_pumpOperatorList({ renderingIntent, cacheKey }) {
|
_pumpOperatorList({ renderingIntent, cacheKey, annotationStorageMap }) {
|
||||||
if (
|
if (
|
||||||
typeof PDFJSDev === "undefined" ||
|
typeof PDFJSDev === "undefined" ||
|
||||||
PDFJSDev.test("!PRODUCTION || TESTING")
|
PDFJSDev.test("!PRODUCTION || TESTING")
|
||||||
@ -1817,10 +1826,7 @@ class PDFPageProxy {
|
|||||||
pageIndex: this._pageIndex,
|
pageIndex: this._pageIndex,
|
||||||
intent: renderingIntent,
|
intent: renderingIntent,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
annotationStorage:
|
annotationStorage: annotationStorageMap,
|
||||||
renderingIntent & RenderingIntentFlag.ANNOTATIONS_STORAGE
|
|
||||||
? this._transport.annotationStorage.serializable
|
|
||||||
: null,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const reader = readableStream.getReader();
|
const reader = readableStream.getReader();
|
||||||
@ -2406,10 +2412,11 @@ class WorkerTransport {
|
|||||||
getRenderingIntent(
|
getRenderingIntent(
|
||||||
intent,
|
intent,
|
||||||
annotationMode = AnnotationMode.ENABLE,
|
annotationMode = AnnotationMode.ENABLE,
|
||||||
|
printAnnotationStorage = null,
|
||||||
isOpList = false
|
isOpList = false
|
||||||
) {
|
) {
|
||||||
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
|
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
|
||||||
let annotationHash = "";
|
let annotationMap = null;
|
||||||
|
|
||||||
switch (intent) {
|
switch (intent) {
|
||||||
case "any":
|
case "any":
|
||||||
@ -2436,7 +2443,13 @@ class WorkerTransport {
|
|||||||
case AnnotationMode.ENABLE_STORAGE:
|
case AnnotationMode.ENABLE_STORAGE:
|
||||||
renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE;
|
renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE;
|
||||||
|
|
||||||
annotationHash = this.annotationStorage.hash;
|
const annotationStorage =
|
||||||
|
renderingIntent & RenderingIntentFlag.PRINT &&
|
||||||
|
printAnnotationStorage instanceof PrintAnnotationStorage
|
||||||
|
? printAnnotationStorage
|
||||||
|
: this.annotationStorage;
|
||||||
|
|
||||||
|
annotationMap = annotationStorage.serializable;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);
|
warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);
|
||||||
@ -2448,7 +2461,10 @@ class WorkerTransport {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
renderingIntent,
|
renderingIntent,
|
||||||
cacheKey: `${renderingIntent}_${annotationHash}`,
|
cacheKey: `${renderingIntent}_${AnnotationStorage.getHash(
|
||||||
|
annotationMap
|
||||||
|
)}`,
|
||||||
|
annotationStorageMap: annotationMap,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ import {
|
|||||||
RenderingCancelledException,
|
RenderingCancelledException,
|
||||||
StatTimer,
|
StatTimer,
|
||||||
} from "../../src/display/display_utils.js";
|
} from "../../src/display/display_utils.js";
|
||||||
|
import { AnnotationStorage } from "../../src/display/annotation_storage.js";
|
||||||
import { AutoPrintRegExp } from "../../web/ui_utils.js";
|
import { AutoPrintRegExp } from "../../web/ui_utils.js";
|
||||||
import { GlobalImageCache } from "../../src/core/image_utils.js";
|
import { GlobalImageCache } from "../../src/core/image_utils.js";
|
||||||
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
|
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
|
||||||
@ -2826,6 +2827,75 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`)
|
|||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
firstImgData = null;
|
firstImgData = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("render for printing, with `printAnnotationStorage` set", async function () {
|
||||||
|
async function getPrintData(printAnnotationStorage = null) {
|
||||||
|
const canvasAndCtx = CanvasFactory.create(
|
||||||
|
viewport.width,
|
||||||
|
viewport.height
|
||||||
|
);
|
||||||
|
const renderTask = pdfPage.render({
|
||||||
|
canvasContext: canvasAndCtx.context,
|
||||||
|
canvasFactory: CanvasFactory,
|
||||||
|
viewport,
|
||||||
|
intent: "print",
|
||||||
|
annotationMode: AnnotationMode.ENABLE_STORAGE,
|
||||||
|
printAnnotationStorage,
|
||||||
|
});
|
||||||
|
|
||||||
|
await renderTask.promise;
|
||||||
|
const printData = canvasAndCtx.canvas.toDataURL();
|
||||||
|
CanvasFactory.destroy(canvasAndCtx);
|
||||||
|
|
||||||
|
return printData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingTask = getDocument(
|
||||||
|
buildGetDocumentParams("annotation-tx.pdf")
|
||||||
|
);
|
||||||
|
const pdfDoc = await loadingTask.promise;
|
||||||
|
const pdfPage = await pdfDoc.getPage(1);
|
||||||
|
const viewport = pdfPage.getViewport({ scale: 1 });
|
||||||
|
|
||||||
|
// Update the contents of the form-field.
|
||||||
|
const { annotationStorage } = pdfDoc;
|
||||||
|
annotationStorage.setValue("22R", { value: "Hello World" });
|
||||||
|
|
||||||
|
// Render for printing, with default parameters.
|
||||||
|
const printOriginalData = await getPrintData();
|
||||||
|
|
||||||
|
// Get the *frozen* print-storage for use during printing.
|
||||||
|
const printAnnotationStorage = annotationStorage.print;
|
||||||
|
// Update the contents of the form-field again.
|
||||||
|
annotationStorage.setValue("22R", { value: "Printing again..." });
|
||||||
|
|
||||||
|
const annotationHash = AnnotationStorage.getHash(
|
||||||
|
annotationStorage.serializable
|
||||||
|
);
|
||||||
|
const printAnnotationHash = AnnotationStorage.getHash(
|
||||||
|
printAnnotationStorage.serializable
|
||||||
|
);
|
||||||
|
// Sanity check to ensure that the print-storage didn't change,
|
||||||
|
// after the form-field was updated.
|
||||||
|
expect(printAnnotationHash).not.toEqual(annotationHash);
|
||||||
|
|
||||||
|
// Render for printing again, after updating the form-field,
|
||||||
|
// with default parameters.
|
||||||
|
const printAgainData = await getPrintData();
|
||||||
|
|
||||||
|
// Render for printing again, after updating the form-field,
|
||||||
|
// with `printAnnotationStorage` set.
|
||||||
|
const printStorageData = await getPrintData(printAnnotationStorage);
|
||||||
|
|
||||||
|
// Ensure that printing again, with default parameters,
|
||||||
|
// actually uses the "new" form-field data.
|
||||||
|
expect(printAgainData).not.toEqual(printOriginalData);
|
||||||
|
// Finally ensure that printing, with `printAnnotationStorage` set,
|
||||||
|
// still uses the "previous" form-field data.
|
||||||
|
expect(printStorageData).toEqual(printOriginalData);
|
||||||
|
|
||||||
|
await loadingTask.destroy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Multiple `getDocument` instances", function () {
|
describe("Multiple `getDocument` instances", function () {
|
||||||
|
22
web/app.js
22
web/app.js
@ -254,6 +254,7 @@ const PDFViewerApplication = {
|
|||||||
_wheelUnusedTicks: 0,
|
_wheelUnusedTicks: 0,
|
||||||
_idleCallbacks: new Set(),
|
_idleCallbacks: new Set(),
|
||||||
_PDFBug: null,
|
_PDFBug: null,
|
||||||
|
_printAnnotationStoragePromise: null,
|
||||||
|
|
||||||
// Called once when the document is loaded.
|
// Called once when the document is loaded.
|
||||||
async initialize(appConfig) {
|
async initialize(appConfig) {
|
||||||
@ -1790,9 +1791,14 @@ const PDFViewerApplication = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
beforePrint() {
|
beforePrint() {
|
||||||
// Given that the "beforeprint" browser event is synchronous, we
|
this._printAnnotationStoragePromise = this.pdfScriptingManager
|
||||||
// unfortunately cannot await the scripting event dispatching here.
|
.dispatchWillPrint()
|
||||||
this.pdfScriptingManager.dispatchWillPrint();
|
.catch(() => {
|
||||||
|
/* Avoid breaking printing; ignoring errors. */
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return this.pdfDocument?.annotationStorage.print;
|
||||||
|
});
|
||||||
|
|
||||||
if (this.printService) {
|
if (this.printService) {
|
||||||
// There is no way to suppress beforePrint/afterPrint events,
|
// There is no way to suppress beforePrint/afterPrint events,
|
||||||
@ -1830,6 +1836,7 @@ const PDFViewerApplication = {
|
|||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise,
|
optionalContentConfigPromise,
|
||||||
|
this._printAnnotationStoragePromise,
|
||||||
this.l10n
|
this.l10n
|
||||||
);
|
);
|
||||||
this.printService = printService;
|
this.printService = printService;
|
||||||
@ -1843,9 +1850,12 @@ const PDFViewerApplication = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
afterPrint() {
|
afterPrint() {
|
||||||
// Given that the "afterprint" browser event is synchronous, we
|
if (this._printAnnotationStoragePromise) {
|
||||||
// unfortunately cannot await the scripting event dispatching here.
|
this._printAnnotationStoragePromise.then(() => {
|
||||||
this.pdfScriptingManager.dispatchDidPrint();
|
this.pdfScriptingManager.dispatchDidPrint();
|
||||||
|
});
|
||||||
|
this._printAnnotationStoragePromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.printService) {
|
if (this.printService) {
|
||||||
this.printService.destroy();
|
this.printService.destroy();
|
||||||
|
@ -29,7 +29,8 @@ function composePage(
|
|||||||
size,
|
size,
|
||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStoragePromise
|
||||||
) {
|
) {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
|
|
||||||
@ -61,9 +62,12 @@ function composePage(
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
let thisRenderTask = null;
|
let thisRenderTask = null;
|
||||||
pdfDocument
|
|
||||||
.getPage(pageNumber)
|
Promise.all([
|
||||||
.then(function (pdfPage) {
|
pdfDocument.getPage(pageNumber),
|
||||||
|
printAnnotationStoragePromise,
|
||||||
|
])
|
||||||
|
.then(function ([pdfPage, printAnnotationStorage]) {
|
||||||
if (currentRenderTask) {
|
if (currentRenderTask) {
|
||||||
currentRenderTask.cancel();
|
currentRenderTask.cancel();
|
||||||
currentRenderTask = null;
|
currentRenderTask = null;
|
||||||
@ -75,6 +79,7 @@ function composePage(
|
|||||||
intent: "print",
|
intent: "print",
|
||||||
annotationMode: AnnotationMode.ENABLE_STORAGE,
|
annotationMode: AnnotationMode.ENABLE_STORAGE,
|
||||||
optionalContentConfigPromise,
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStorage,
|
||||||
};
|
};
|
||||||
currentRenderTask = thisRenderTask = pdfPage.render(renderContext);
|
currentRenderTask = thisRenderTask = pdfPage.render(renderContext);
|
||||||
return thisRenderTask.promise;
|
return thisRenderTask.promise;
|
||||||
@ -114,7 +119,8 @@ function FirefoxPrintService(
|
|||||||
pagesOverview,
|
pagesOverview,
|
||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise = null
|
optionalContentConfigPromise = null,
|
||||||
|
printAnnotationStoragePromise = null
|
||||||
) {
|
) {
|
||||||
this.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
this.pagesOverview = pagesOverview;
|
this.pagesOverview = pagesOverview;
|
||||||
@ -122,6 +128,8 @@ function FirefoxPrintService(
|
|||||||
this._printResolution = printResolution || 150;
|
this._printResolution = printResolution || 150;
|
||||||
this._optionalContentConfigPromise =
|
this._optionalContentConfigPromise =
|
||||||
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
|
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
|
||||||
|
this._optionalContentConfigPromise =
|
||||||
|
printAnnotationStoragePromise || Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
FirefoxPrintService.prototype = {
|
FirefoxPrintService.prototype = {
|
||||||
@ -132,6 +140,7 @@ FirefoxPrintService.prototype = {
|
|||||||
printContainer,
|
printContainer,
|
||||||
_printResolution,
|
_printResolution,
|
||||||
_optionalContentConfigPromise,
|
_optionalContentConfigPromise,
|
||||||
|
_printAnnotationStoragePromise,
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
const body = document.querySelector("body");
|
const body = document.querySelector("body");
|
||||||
@ -149,7 +158,8 @@ FirefoxPrintService.prototype = {
|
|||||||
pagesOverview[i],
|
pagesOverview[i],
|
||||||
printContainer,
|
printContainer,
|
||||||
_printResolution,
|
_printResolution,
|
||||||
_optionalContentConfigPromise
|
_optionalContentConfigPromise,
|
||||||
|
_printAnnotationStoragePromise
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -175,14 +185,16 @@ PDFPrintServiceFactory.instance = {
|
|||||||
pagesOverview,
|
pagesOverview,
|
||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStoragePromise
|
||||||
) {
|
) {
|
||||||
return new FirefoxPrintService(
|
return new FirefoxPrintService(
|
||||||
pdfDocument,
|
pdfDocument,
|
||||||
pagesOverview,
|
pagesOverview,
|
||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStoragePromise
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,8 @@ function renderPage(
|
|||||||
pageNumber,
|
pageNumber,
|
||||||
size,
|
size,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStoragePromise
|
||||||
) {
|
) {
|
||||||
const scratchCanvas = activeService.scratchCanvas;
|
const scratchCanvas = activeService.scratchCanvas;
|
||||||
|
|
||||||
@ -44,7 +45,10 @@ function renderPage(
|
|||||||
ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
|
ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
return pdfDocument.getPage(pageNumber).then(function (pdfPage) {
|
return Promise.all([
|
||||||
|
pdfDocument.getPage(pageNumber),
|
||||||
|
printAnnotationStoragePromise,
|
||||||
|
]).then(function ([pdfPage, printAnnotationStorage]) {
|
||||||
const renderContext = {
|
const renderContext = {
|
||||||
canvasContext: ctx,
|
canvasContext: ctx,
|
||||||
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
|
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
|
||||||
@ -52,6 +56,7 @@ function renderPage(
|
|||||||
intent: "print",
|
intent: "print",
|
||||||
annotationMode: AnnotationMode.ENABLE_STORAGE,
|
annotationMode: AnnotationMode.ENABLE_STORAGE,
|
||||||
optionalContentConfigPromise,
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStorage,
|
||||||
};
|
};
|
||||||
return pdfPage.render(renderContext).promise;
|
return pdfPage.render(renderContext).promise;
|
||||||
});
|
});
|
||||||
@ -63,6 +68,7 @@ function PDFPrintService(
|
|||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise = null,
|
optionalContentConfigPromise = null,
|
||||||
|
printAnnotationStoragePromise = null,
|
||||||
l10n
|
l10n
|
||||||
) {
|
) {
|
||||||
this.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
@ -71,6 +77,8 @@ function PDFPrintService(
|
|||||||
this._printResolution = printResolution || 150;
|
this._printResolution = printResolution || 150;
|
||||||
this._optionalContentConfigPromise =
|
this._optionalContentConfigPromise =
|
||||||
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
|
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
|
||||||
|
this._printAnnotationStoragePromise =
|
||||||
|
printAnnotationStoragePromise || Promise.resolve();
|
||||||
this.l10n = l10n;
|
this.l10n = l10n;
|
||||||
this.currentPage = -1;
|
this.currentPage = -1;
|
||||||
// The temporary canvas where renderPage paints one page at a time.
|
// The temporary canvas where renderPage paints one page at a time.
|
||||||
@ -160,7 +168,8 @@ PDFPrintService.prototype = {
|
|||||||
/* pageNumber = */ index + 1,
|
/* pageNumber = */ index + 1,
|
||||||
this.pagesOverview[index],
|
this.pagesOverview[index],
|
||||||
this._printResolution,
|
this._printResolution,
|
||||||
this._optionalContentConfigPromise
|
this._optionalContentConfigPromise,
|
||||||
|
this._printAnnotationStoragePromise
|
||||||
)
|
)
|
||||||
.then(this.useRenderedPage.bind(this))
|
.then(this.useRenderedPage.bind(this))
|
||||||
.then(function () {
|
.then(function () {
|
||||||
@ -359,6 +368,7 @@ PDFPrintServiceFactory.instance = {
|
|||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise,
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStoragePromise,
|
||||||
l10n
|
l10n
|
||||||
) {
|
) {
|
||||||
if (activeService) {
|
if (activeService) {
|
||||||
@ -370,6 +380,7 @@ PDFPrintServiceFactory.instance = {
|
|||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise,
|
optionalContentConfigPromise,
|
||||||
|
printAnnotationStoragePromise,
|
||||||
l10n
|
l10n
|
||||||
);
|
);
|
||||||
return activeService;
|
return activeService;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user