Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify saving added/modified annotations. #19026

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 81 additions & 101 deletions src/core/annotation.js

Large diffs are not rendered by default.

41 changes: 16 additions & 25 deletions src/core/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ import { OperatorList } from "./operator_list.js";
import { PartialEvaluator } from "./evaluator.js";
import { StreamsSequenceStream } from "./decode_stream.js";
import { StructTreePage } from "./struct_tree.js";
import { writeObject } from "./writer.js";
import { XFAFactory } from "./xfa/factory.js";
import { XRef } from "./xref.js";

Expand Down Expand Up @@ -314,7 +313,7 @@ class Page {
await Promise.all(promises);
}

async saveNewAnnotations(handler, task, annotations, imagePromises) {
async saveNewAnnotations(handler, task, annotations, imagePromises, changes) {
if (this.xfaFactory) {
throw new Error("XFA: Cannot save new annotations.");
}
Expand Down Expand Up @@ -348,7 +347,8 @@ class Page {
partialEvaluator,
task,
annotations,
imagePromises
imagePromises,
changes
);

for (const { ref } of newData.annotations) {
Expand All @@ -358,27 +358,20 @@ class Page {
}
}

const savedDict = pageDict.get("Annots");
pageDict.set("Annots", annotationsArray);
const buffer = [];
await writeObject(this.ref, pageDict, buffer, this.xref);
if (savedDict) {
pageDict.set("Annots", savedDict);
}
const dict = pageDict.clone();
dict.set("Annots", annotationsArray);
changes.put(this.ref, {
data: dict,
});

const objects = newData.dependencies;
objects.push(
{ ref: this.ref, data: buffer.join("") },
...newData.annotations
);
for (const deletedRef of deletedAnnotations) {
objects.push({ ref: deletedRef, data: null });
changes.put(deletedRef, {
data: null,
});
}

return objects;
}

save(handler, task, annotationStorage) {
save(handler, task, annotationStorage, changes) {
const partialEvaluator = new PartialEvaluator({
xref: this.xref,
handler,
Expand All @@ -395,11 +388,11 @@ class Page {
// Fetch the page's annotations and save the content
// in case of interactive form fields.
return this._parsedAnnotations.then(function (annotations) {
const newRefsPromises = [];
const promises = [];
for (const annotation of annotations) {
newRefsPromises.push(
promises.push(
annotation
.save(partialEvaluator, task, annotationStorage)
.save(partialEvaluator, task, annotationStorage, changes)
.catch(function (reason) {
warn(
"save - ignoring annotation data during " +
Expand All @@ -410,9 +403,7 @@ class Page {
);
}

return Promise.all(newRefsPromises).then(function (newRefs) {
return newRefs.filter(newRef => !!newRef);
});
return Promise.all(promises);
});
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/primitives.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ class RefSetCache {
this._map.clear();
}

*values() {
yield* this._map.values();
}

*items() {
for (const [ref, value] of this._map) {
yield [Ref.fromString(ref), value];
Expand Down
32 changes: 14 additions & 18 deletions src/core/struct_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { AnnotationPrefix, stringToPDFString, warn } from "../shared/util.js";
import { Dict, isName, Name, Ref, RefSetCache } from "./primitives.js";
import { lookupNormalRect, stringToAsciiOrUTF16BE } from "./core_utils.js";
import { NumberTree } from "./name_number_tree.js";
import { writeObject } from "./writer.js";

const MAX_DEPTH = 40;

Expand Down Expand Up @@ -117,7 +116,7 @@ class StructTreeRoot {
xref,
catalogRef,
pdfManager,
newRefs,
changes,
}) {
const root = pdfManager.catalog.cloneDict();
const cache = new RefSetCache();
Expand Down Expand Up @@ -146,18 +145,17 @@ class StructTreeRoot {
nums,
xref,
pdfManager,
newRefs,
changes,
cache,
});
structTreeRoot.set("ParentTreeNextKey", nextKey);

cache.put(parentTreeRef, parentTree);

const buffer = [];
for (const [ref, obj] of cache.items()) {
buffer.length = 0;
await writeObject(ref, obj, buffer, xref);
newRefs.push({ ref, data: buffer.join("") });
changes.put(ref, {
data: obj,
});
}
}

Expand Down Expand Up @@ -235,7 +233,7 @@ class StructTreeRoot {
return true;
}

async updateStructureTree({ newAnnotationsByPage, pdfManager, newRefs }) {
async updateStructureTree({ newAnnotationsByPage, pdfManager, changes }) {
const xref = this.dict.xref;
const structTreeRoot = this.dict.clone();
const structTreeRootRef = this.ref;
Expand Down Expand Up @@ -273,7 +271,7 @@ class StructTreeRoot {
nums,
xref,
pdfManager,
newRefs,
changes,
cache,
});

Expand All @@ -288,11 +286,10 @@ class StructTreeRoot {
cache.put(numsRef, nums);
}

const buffer = [];
for (const [ref, obj] of cache.items()) {
buffer.length = 0;
await writeObject(ref, obj, buffer, xref);
newRefs.push({ ref, data: buffer.join("") });
changes.put(ref, {
data: obj,
});
}
}

Expand All @@ -304,13 +301,12 @@ class StructTreeRoot {
nums,
xref,
pdfManager,
newRefs,
changes,
cache,
}) {
const objr = Name.get("OBJR");
let nextKey = -1;
let structTreePageObjs;
const buffer = [];

for (const [pageIndex, elements] of newAnnotationsByPage) {
const page = await pdfManager.getPage(pageIndex);
Expand Down Expand Up @@ -350,9 +346,9 @@ class StructTreeRoot {
// We update the existing tag.
const tagDict = xref.fetch(objRef).clone();
StructTreeRoot.#writeProperties(tagDict, accessibilityData);
buffer.length = 0;
await writeObject(objRef, tagDict, buffer, xref);
newRefs.push({ ref: objRef, data: buffer.join("") });
changes.put(objRef, {
data: tagDict,
});
continue;
}
}
Expand Down
38 changes: 19 additions & 19 deletions src/core/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
getNewAnnotationsMap,
XRefParseException,
} from "./core_utils.js";
import { Dict, isDict, Ref } from "./primitives.js";
import { Dict, isDict, Ref, RefSetCache } from "./primitives.js";
import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js";
import { AnnotationFactory } from "./annotation.js";
import { clearGlobalCaches } from "./cleanup_helper.js";
Expand Down Expand Up @@ -540,6 +540,7 @@ class WorkerMessageHandler {
pdfManager.ensureDoc("linearization"),
pdfManager.ensureCatalog("structTreeRoot"),
];
const changes = new RefSetCache();
const promises = [];

const newAnnotationsByPage = !isPureXfa
Expand Down Expand Up @@ -590,7 +591,13 @@ class WorkerMessageHandler {
pdfManager.getPage(pageIndex).then(page => {
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
return page
.saveNewAnnotations(handler, task, annotations, imagePromises)
.saveNewAnnotations(
handler,
task,
annotations,
imagePromises,
changes
)
.finally(function () {
finishWorkerTask(task);
});
Expand All @@ -600,26 +607,24 @@ class WorkerMessageHandler {
if (structTreeRoot === null) {
// No structTreeRoot exists, so we need to create one.
promises.push(
Promise.all(newAnnotationPromises).then(async newRefs => {
Promise.all(newAnnotationPromises).then(async () => {
await StructTreeRoot.createStructureTree({
newAnnotationsByPage,
xref,
catalogRef,
pdfManager,
newRefs,
changes,
});
return newRefs;
})
);
} else if (structTreeRoot) {
promises.push(
Promise.all(newAnnotationPromises).then(async newRefs => {
Promise.all(newAnnotationPromises).then(async () => {
await structTreeRoot.updateStructureTree({
newAnnotationsByPage,
pdfManager,
newRefs,
changes,
});
return newRefs;
})
);
}
Expand All @@ -633,7 +638,7 @@ class WorkerMessageHandler {
pdfManager.getPage(pageIndex).then(function (page) {
const task = new WorkerTask(`Save: page ${pageIndex}`);
return page
.save(handler, task, annotationStorage)
.save(handler, task, annotationStorage, changes)
.finally(function () {
finishWorkerTask(task);
});
Expand All @@ -643,26 +648,21 @@ class WorkerMessageHandler {
}
const refs = await Promise.all(promises);

let newRefs = [];
let xfaData = null;
if (isPureXfa) {
xfaData = refs[0];
if (!xfaData) {
return stream.bytes;
}
} else {
newRefs = refs.flat(2);

if (newRefs.length === 0) {
// No new refs so just return the initial bytes
return stream.bytes;
}
} else if (changes.size === 0) {
// No new refs so just return the initial bytes
return stream.bytes;
}

const needAppearances =
acroFormRef &&
acroForm instanceof Dict &&
newRefs.some(ref => ref.needAppearances);
changes.values().some(ref => ref.needAppearances);

const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
let xfaDatasetsRef = null;
Expand Down Expand Up @@ -712,7 +712,7 @@ class WorkerMessageHandler {
return incrementalUpdate({
originalData: stream.bytes,
xrefInfo: newXrefInfo,
newRefs,
changes,
xref,
hasXfa: !!xfa,
xfaDatasetsRef,
Expand Down
Loading