Move the transfers
computation into the AnnotationStorage
class
Rather than having to *manually* determine the potential `transfers` at various spots in the API, we can let the `AnnotationStorage.serializable` getter include this. To further simplify things, we can also let the `serializable` getter compute and include the `hash`-string as well.
This commit is contained in:
parent
88c7c8b5bf
commit
39113baa33
@ -17,6 +17,12 @@ 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";
|
||||||
|
|
||||||
|
const SerializableEmpty = Object.freeze({
|
||||||
|
map: null,
|
||||||
|
hash: "",
|
||||||
|
transfers: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key/value storage for annotation data in forms.
|
* Key/value storage for annotation data in forms.
|
||||||
*/
|
*/
|
||||||
@ -171,34 +177,27 @@ class AnnotationStorage {
|
|||||||
*/
|
*/
|
||||||
get serializable() {
|
get serializable() {
|
||||||
if (this.#storage.size === 0) {
|
if (this.#storage.size === 0) {
|
||||||
return null;
|
return SerializableEmpty;
|
||||||
}
|
}
|
||||||
const clone = new Map();
|
const map = new Map(),
|
||||||
|
hash = new MurmurHash3_64(),
|
||||||
|
transfers = [];
|
||||||
for (const [key, val] of this.#storage) {
|
for (const [key, val] of this.#storage) {
|
||||||
const serialized =
|
const serialized =
|
||||||
val instanceof AnnotationEditor ? val.serialize() : val;
|
val instanceof AnnotationEditor ? val.serialize() : val;
|
||||||
if (serialized) {
|
if (serialized) {
|
||||||
clone.set(key, serialized);
|
map.set(key, serialized);
|
||||||
|
|
||||||
|
hash.update(`${key}:${JSON.stringify(serialized)}`);
|
||||||
|
|
||||||
|
if (serialized.bitmap) {
|
||||||
|
transfers.push(serialized.bitmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return clone;
|
return map.size > 0
|
||||||
}
|
? { map, hash: hash.hexdigest(), transfers }
|
||||||
|
: SerializableEmpty;
|
||||||
/**
|
|
||||||
* PLEASE NOTE: Only intended for usage within the API itself.
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
static getHash(map) {
|
|
||||||
if (!map) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const hash = new MurmurHash3_64();
|
|
||||||
|
|
||||||
for (const [key, val] of map) {
|
|
||||||
hash.update(`${key}:${JSON.stringify(val)}`);
|
|
||||||
}
|
|
||||||
return hash.hexdigest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,12 +207,21 @@ class AnnotationStorage {
|
|||||||
* contents. (Necessary since printing is triggered synchronously in browsers.)
|
* contents. (Necessary since printing is triggered synchronously in browsers.)
|
||||||
*/
|
*/
|
||||||
class PrintAnnotationStorage extends AnnotationStorage {
|
class PrintAnnotationStorage extends AnnotationStorage {
|
||||||
#serializable = null;
|
#serializable;
|
||||||
|
|
||||||
constructor(parent) {
|
constructor(parent) {
|
||||||
super();
|
super();
|
||||||
|
const { map, hash, transfers } = parent.serializable;
|
||||||
// Create a *copy* of the data, since Objects are passed by reference in JS.
|
// Create a *copy* of the data, since Objects are passed by reference in JS.
|
||||||
this.#serializable = structuredClone(parent.serializable);
|
const clone = structuredClone(
|
||||||
|
map,
|
||||||
|
(typeof PDFJSDev === "undefined" ||
|
||||||
|
PDFJSDev.test("SKIP_BABEL || TESTING")) &&
|
||||||
|
transfers
|
||||||
|
? { transfer: transfers }
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
this.#serializable = { map: clone, hash, transfers };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,4 +241,4 @@ class PrintAnnotationStorage extends AnnotationStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AnnotationStorage, PrintAnnotationStorage };
|
export { AnnotationStorage, PrintAnnotationStorage, SerializableEmpty };
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
AnnotationStorage,
|
AnnotationStorage,
|
||||||
PrintAnnotationStorage,
|
PrintAnnotationStorage,
|
||||||
|
SerializableEmpty,
|
||||||
} from "./annotation_storage.js";
|
} from "./annotation_storage.js";
|
||||||
import {
|
import {
|
||||||
deprecated,
|
deprecated,
|
||||||
@ -1811,22 +1812,18 @@ class PDFPageProxy {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_pumpOperatorList({ renderingIntent, cacheKey, annotationStorageMap }) {
|
_pumpOperatorList({
|
||||||
|
renderingIntent,
|
||||||
|
cacheKey,
|
||||||
|
annotationStorageSerializable,
|
||||||
|
}) {
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
assert(
|
assert(
|
||||||
Number.isInteger(renderingIntent) && renderingIntent > 0,
|
Number.isInteger(renderingIntent) && renderingIntent > 0,
|
||||||
'_pumpOperatorList: Expected valid "renderingIntent" argument.'
|
'_pumpOperatorList: Expected valid "renderingIntent" argument.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const { map, transfers } = annotationStorageSerializable;
|
||||||
const transfers = [];
|
|
||||||
if (annotationStorageMap) {
|
|
||||||
for (const annotation of annotationStorageMap.values()) {
|
|
||||||
if (annotation.bitmap) {
|
|
||||||
transfers.push(annotation.bitmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const readableStream = this._transport.messageHandler.sendWithStream(
|
const readableStream = this._transport.messageHandler.sendWithStream(
|
||||||
"GetOperatorList",
|
"GetOperatorList",
|
||||||
@ -1834,7 +1831,7 @@ class PDFPageProxy {
|
|||||||
pageIndex: this._pageIndex,
|
pageIndex: this._pageIndex,
|
||||||
intent: renderingIntent,
|
intent: renderingIntent,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
annotationStorage: annotationStorageMap,
|
annotationStorage: map,
|
||||||
},
|
},
|
||||||
transfers
|
transfers
|
||||||
);
|
);
|
||||||
@ -2459,7 +2456,7 @@ class WorkerTransport {
|
|||||||
isOpList = false
|
isOpList = false
|
||||||
) {
|
) {
|
||||||
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
|
let renderingIntent = RenderingIntentFlag.DISPLAY; // Default value.
|
||||||
let annotationMap = null;
|
let annotationStorageSerializable = SerializableEmpty;
|
||||||
|
|
||||||
switch (intent) {
|
switch (intent) {
|
||||||
case "any":
|
case "any":
|
||||||
@ -2492,7 +2489,7 @@ class WorkerTransport {
|
|||||||
? printAnnotationStorage
|
? printAnnotationStorage
|
||||||
: this.annotationStorage;
|
: this.annotationStorage;
|
||||||
|
|
||||||
annotationMap = annotationStorage.serializable;
|
annotationStorageSerializable = annotationStorage.serializable;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);
|
warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);
|
||||||
@ -2504,10 +2501,8 @@ class WorkerTransport {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
renderingIntent,
|
renderingIntent,
|
||||||
cacheKey: `${renderingIntent}_${AnnotationStorage.getHash(
|
cacheKey: `${renderingIntent}_${annotationStorageSerializable.hash}`,
|
||||||
annotationMap
|
annotationStorageSerializable,
|
||||||
)}`,
|
|
||||||
annotationStorageMap: annotationMap,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2915,22 +2910,15 @@ class WorkerTransport {
|
|||||||
"please use the getData-method instead."
|
"please use the getData-method instead."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const annotationStorage = this.annotationStorage.serializable;
|
const { map, transfers } = this.annotationStorage.serializable;
|
||||||
const transfers = [];
|
|
||||||
if (annotationStorage) {
|
|
||||||
for (const annotation of annotationStorage.values()) {
|
|
||||||
if (annotation.bitmap) {
|
|
||||||
transfers.push(annotation.bitmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.messageHandler
|
return this.messageHandler
|
||||||
.sendWithPromise(
|
.sendWithPromise(
|
||||||
"SaveDocument",
|
"SaveDocument",
|
||||||
{
|
{
|
||||||
isPureXfa: !!this._htmlForXfa,
|
isPureXfa: !!this._htmlForXfa,
|
||||||
numPages: this._numPages,
|
numPages: this._numPages,
|
||||||
annotationStorage,
|
annotationStorage: map,
|
||||||
filename: this._fullReader?.filename ?? null,
|
filename: this._fullReader?.filename ?? null,
|
||||||
},
|
},
|
||||||
transfers
|
transfers
|
||||||
|
@ -688,13 +688,12 @@ describe("FreeText Editor", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const serialize = proprName =>
|
const serialize = proprName =>
|
||||||
page.evaluate(
|
page.evaluate(name => {
|
||||||
name =>
|
const { map } =
|
||||||
[
|
window.PDFViewerApplication.pdfDocument.annotationStorage
|
||||||
...window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.values(),
|
.serializable;
|
||||||
].map(x => x[name]),
|
return map ? Array.from(map.values(), x => x[name]) : [];
|
||||||
proprName
|
}, proprName);
|
||||||
);
|
|
||||||
|
|
||||||
expect(await serialize("value"))
|
expect(await serialize("value"))
|
||||||
.withContext(`In ${browserName}`)
|
.withContext(`In ${browserName}`)
|
||||||
@ -805,13 +804,12 @@ describe("FreeText Editor", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const serialize = proprName =>
|
const serialize = proprName =>
|
||||||
page.evaluate(
|
page.evaluate(name => {
|
||||||
name =>
|
const { map } =
|
||||||
[
|
window.PDFViewerApplication.pdfDocument.annotationStorage
|
||||||
...window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.values(),
|
.serializable;
|
||||||
].map(x => x[name]),
|
return map ? Array.from(map.values(), x => x[name]) : [];
|
||||||
proprName
|
}, proprName);
|
||||||
);
|
|
||||||
|
|
||||||
const rects = (await serialize("rect")).map(rect =>
|
const rects = (await serialize("rect")).map(rect =>
|
||||||
rect.slice(0, 2).map(x => Math.floor(x))
|
rect.slice(0, 2).map(x => Math.floor(x))
|
||||||
|
@ -136,9 +136,11 @@ const mockClipboard = async pages => {
|
|||||||
exports.mockClipboard = mockClipboard;
|
exports.mockClipboard = mockClipboard;
|
||||||
|
|
||||||
const getSerialized = page =>
|
const getSerialized = page =>
|
||||||
page.evaluate(() => [
|
page.evaluate(() => {
|
||||||
...window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.values(),
|
const { map } =
|
||||||
]);
|
window.PDFViewerApplication.pdfDocument.annotationStorage.serializable;
|
||||||
|
return map ? [...map.values()] : [];
|
||||||
|
});
|
||||||
exports.getSerialized = getSerialized;
|
exports.getSerialized = getSerialized;
|
||||||
|
|
||||||
function getEditors(page, kind) {
|
function getEditors(page, kind) {
|
||||||
|
@ -50,7 +50,6 @@ 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";
|
||||||
@ -3525,12 +3524,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`)
|
|||||||
// Update the contents of the form-field again.
|
// Update the contents of the form-field again.
|
||||||
annotationStorage.setValue("22R", { value: "Printing again..." });
|
annotationStorage.setValue("22R", { value: "Printing again..." });
|
||||||
|
|
||||||
const annotationHash = AnnotationStorage.getHash(
|
const { hash: annotationHash } = annotationStorage.serializable;
|
||||||
annotationStorage.serializable
|
const { hash: printAnnotationHash } = printAnnotationStorage.serializable;
|
||||||
);
|
|
||||||
const printAnnotationHash = AnnotationStorage.getHash(
|
|
||||||
printAnnotationStorage.serializable
|
|
||||||
);
|
|
||||||
// Sanity check to ensure that the print-storage didn't change,
|
// Sanity check to ensure that the print-storage didn't change,
|
||||||
// after the form-field was updated.
|
// after the form-field was updated.
|
||||||
expect(printAnnotationHash).not.toEqual(annotationHash);
|
expect(printAnnotationHash).not.toEqual(annotationHash);
|
||||||
|
Loading…
Reference in New Issue
Block a user