diff --git a/src/core/annotation.js b/src/core/annotation.js index ab31be32f..fcf5e4209 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -509,7 +509,7 @@ class Annotation { }); } - getOperatorList(evaluator, task, renderForms) { + getOperatorList(evaluator, task, renderForms, annotationStorage) { if (!this.appearance) { return Promise.resolve(new OperatorList()); } @@ -877,13 +877,18 @@ class WidgetAnnotation extends Annotation { return !!(this.data.fieldFlags & flag); } - getOperatorList(evaluator, task, renderForms) { + getOperatorList(evaluator, task, renderForms, annotationStorage) { // Do not render form elements on the canvas when interactive forms are // enabled. The display layer is responsible for rendering them instead. if (renderForms) { return Promise.resolve(new OperatorList()); } - return super.getOperatorList(evaluator, task, renderForms); + return super.getOperatorList( + evaluator, + task, + renderForms, + annotationStorage + ); } } @@ -920,9 +925,14 @@ class TextWidgetAnnotation extends WidgetAnnotation { this.data.maxLen !== null; } - getOperatorList(evaluator, task, renderForms) { + getOperatorList(evaluator, task, renderForms, annotationStorage) { if (renderForms || this.appearance) { - return super.getOperatorList(evaluator, task, renderForms); + return super.getOperatorList( + evaluator, + task, + renderForms, + annotationStorage + ); } const operatorList = new OperatorList(); diff --git a/src/core/document.js b/src/core/document.js index 1e02d414c..7e93ba73a 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -238,7 +238,14 @@ class Page { }); } - getOperatorList({ handler, sink, task, intent, renderInteractiveForms }) { + getOperatorList({ + handler, + sink, + task, + intent, + renderInteractiveForms, + annotationStorage, + }) { const contentStreamPromise = this.pdfManager.ensure( this, "getContentStream" @@ -301,7 +308,12 @@ class Page { if (isAnnotationRenderable(annotation, intent)) { opListPromises.push( annotation - .getOperatorList(partialEvaluator, task, renderInteractiveForms) + .getOperatorList( + partialEvaluator, + task, + renderInteractiveForms, + annotationStorage + ) .catch(function (reason) { warn( "getOperatorList - ignoring annotation data during " + diff --git a/src/core/worker.js b/src/core/worker.js index 31bd828de..6f90cee8e 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -532,6 +532,7 @@ class WorkerMessageHandler { task, intent: data.intent, renderInteractiveForms: data.renderInteractiveForms, + annotationStorage: data.annotationStorage, }) .then( function (operatorListInfo) { diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index cee8ee77d..42b535880 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -140,6 +140,7 @@ class AnnotationElement { this.imageResourcesPath = parameters.imageResourcesPath; this.renderInteractiveForms = parameters.renderInteractiveForms; this.svgFactory = parameters.svgFactory; + this.annotationStorage = parameters.annotationStorage; if (isRenderable) { this.container = this._createContainer(ignoreBorder); @@ -1450,6 +1451,7 @@ class AnnotationLayer { imageResourcesPath: parameters.imageResourcesPath || "", renderInteractiveForms: parameters.renderInteractiveForms || false, svgFactory: new DOMSVGFactory(), + annotationStorage: parameters.annotationStorage, }); if (element.isRenderable) { parameters.div.appendChild(element.render()); diff --git a/src/display/annotation_storage.js b/src/display/annotation_storage.js new file mode 100644 index 000000000..29e7e20ff --- /dev/null +++ b/src/display/annotation_storage.js @@ -0,0 +1,57 @@ +/* Copyright 2020 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. + */ + +class AnnotationStorage { + constructor() { + this._storage = {}; + } + + /** + * Get the value for a given key if it exists + * or store and return the default value + * + * @public + * @memberof AnnotationStorage + * @param {string} key + * @param {Object} defaultValue + * @returns {Object} + */ + getOrCreateValue(key, defaultValue) { + if (key in this._storage) { + return this._storage[key]; + } + + this._storage[key] = defaultValue; + return defaultValue; + } + + /** + * Set the value for a given key + * + * @public + * @memberof AnnotationStorage + * @param {string} key + * @param {Object} value + */ + setValue(key, value) { + this._storage[key] = value; + } + + getAll() { + return this._storage; + } +} + +export { AnnotationStorage }; diff --git a/src/display/api.js b/src/display/api.js index e88618a32..b1fb4ba61 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -48,6 +48,7 @@ import { } from "./display_utils.js"; import { FontFaceObject, FontLoader } from "./font_loader.js"; import { NodeCanvasFactory, NodeCMapReaderFactory } from "./node_utils.js"; +import { AnnotationStorage } from "./annotation_storage.js"; import { apiCompatibilityParams } from "./api_compatibility.js"; import { CanvasGraphics } from "./canvas.js"; import { GlobalWorkerOptions } from "./worker_options.js"; @@ -576,6 +577,14 @@ class PDFDocumentProxy { constructor(pdfInfo, transport) { this._pdfInfo = pdfInfo; this._transport = transport; + this._annotationStorage = new AnnotationStorage(); + } + + /** + * @type {AnnotationStorage} Storage for annotation data in forms. + */ + get annotationStorage() { + return this._annotationStorage; } /** @@ -1004,6 +1013,7 @@ class PDFPageProxy { imageLayer = null, canvasFactory = null, background = null, + annotationStorage = null, }) { if (this._stats) { this._stats.time("Overall"); @@ -1048,6 +1058,7 @@ class PDFPageProxy { pageIndex: this._pageIndex, intent: renderingIntent, renderInteractiveForms: renderInteractiveForms === true, + annotationStorage, }); } diff --git a/test/unit/annotation_storage_spec.js b/test/unit/annotation_storage_spec.js new file mode 100644 index 000000000..15585548a --- /dev/null +++ b/test/unit/annotation_storage_spec.js @@ -0,0 +1,42 @@ +/* Copyright 2020 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. + */ + +import { AnnotationStorage } from "../../src/display/annotation_storage.js"; + +describe("AnnotationStorage", function () { + describe("GetOrCreateValue", function () { + it("should get and set a new value in the annotation storage", function (done) { + const annotationStorage = new AnnotationStorage(); + let value = annotationStorage.getOrCreateValue("123A", "hello world"); + expect(value).toEqual("hello world"); + + // the second argument is the default value to use + // if the key isn't in the storage + value = annotationStorage.getOrCreateValue("123A", "an other string"); + expect(value).toEqual("hello world"); + done(); + }); + }); + + describe("SetValue", function () { + it("should set a new value in the annotation storage", function (done) { + const annotationStorage = new AnnotationStorage(); + annotationStorage.setValue("123A", "an other string"); + const value = annotationStorage.getAll()["123A"]; + expect(value).toEqual("an other string"); + done(); + }); + }); +}); diff --git a/test/unit/clitests.json b/test/unit/clitests.json index 4e22992c4..20f1ba961 100644 --- a/test/unit/clitests.json +++ b/test/unit/clitests.json @@ -7,6 +7,7 @@ "spec_files": [ "annotation_spec.js", + "annotation_storage_spec.js", "api_spec.js", "bidi_spec.js", "cff_parser_spec.js", diff --git a/test/unit/jasmine-boot.js b/test/unit/jasmine-boot.js index cd6fe879f..d81704348 100644 --- a/test/unit/jasmine-boot.js +++ b/test/unit/jasmine-boot.js @@ -49,6 +49,7 @@ function initializePDFJS(callback) { "pdfjs/display/fetch_stream.js", "pdfjs/shared/is_node.js", "pdfjs-test/unit/annotation_spec.js", + "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/bidi_spec.js", "pdfjs-test/unit/cff_parser_spec.js", diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index 44691ea6c..fb532e1d8 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -38,6 +38,7 @@ class AnnotationLayerBuilder { pdfPage, linkService, downloadManager, + annotationStorage, imageResourcesPath = "", renderInteractiveForms = false, l10n = NullL10n, @@ -49,6 +50,7 @@ class AnnotationLayerBuilder { this.imageResourcesPath = imageResourcesPath; this.renderInteractiveForms = renderInteractiveForms; this.l10n = l10n; + this.annotationStorage = annotationStorage; this.div = null; this._cancelled = false; @@ -73,6 +75,7 @@ class AnnotationLayerBuilder { renderInteractiveForms: this.renderInteractiveForms, linkService: this.linkService, downloadManager: this.downloadManager, + annotationStorage: this.annotationStorage, }; if (this.div) { @@ -124,6 +127,7 @@ class DefaultAnnotationLayerFactory { createAnnotationLayerBuilder( pageDiv, pdfPage, + annotationStorage, imageResourcesPath = "", renderInteractiveForms = false, l10n = NullL10n @@ -135,6 +139,7 @@ class DefaultAnnotationLayerFactory { renderInteractiveForms, linkService: new SimpleLinkService(), l10n, + annotationStorage, }); } } diff --git a/web/base_viewer.js b/web/base_viewer.js index a1b9302da..ede70ee5f 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -1164,6 +1164,7 @@ class BaseViewer { renderInteractiveForms, linkService: this.linkService, downloadManager: this.downloadManager, + annotationStorage: this.pdfDocument.annotationStorage, l10n, }); } diff --git a/web/firefox_print_service.js b/web/firefox_print_service.js index 312a05427..d293140ad 100644 --- a/web/firefox_print_service.js +++ b/web/firefox_print_service.js @@ -53,6 +53,7 @@ function composePage(pdfDocument, pageNumber, size, printContainer) { transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0], viewport: pdfPage.getViewport({ scale: 1, rotation: size.rotation }), intent: "print", + annotationStorage: pdfDocument.annotationStorage.getAll(), }; return pdfPage.render(renderContext).promise; }) diff --git a/web/pdf_print_service.js b/web/pdf_print_service.js index d16792c7f..6d0b4a368 100644 --- a/web/pdf_print_service.js +++ b/web/pdf_print_service.js @@ -49,6 +49,7 @@ function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size) { transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0], viewport: pdfPage.getViewport({ scale: 1, rotation: size.rotation }), intent: "print", + annotationStorage: pdfDocument.annotationStorage.getAll(), }; return pdfPage.render(renderContext).promise; })