1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-23 08:38:06 +02:00

Simplify saving added/modified annotations.

Having this map to collect the different changes will allow to know if some objects have already been modified.
This commit is contained in:
Calixte Denizet 2024-11-10 20:54:42 +01:00
parent 7a962031e9
commit 4bf7787084
8 changed files with 301 additions and 331 deletions

View file

@ -68,7 +68,6 @@ import { FileSpec } from "./file_spec.js";
import { JpegStream } from "./jpeg_stream.js";
import { ObjectLoader } from "./object_loader.js";
import { OperatorList } from "./operator_list.js";
import { writeObject } from "./writer.js";
import { XFAFactory } from "./xfa/factory.js";
class AnnotationFactory {
@ -332,10 +331,15 @@ class AnnotationFactory {
return imagePromises;
}
static async saveNewAnnotations(evaluator, task, annotations, imagePromises) {
static async saveNewAnnotations(
evaluator,
task,
annotations,
imagePromises,
changes
) {
const xref = evaluator.xref;
let baseFontRef;
const dependencies = [];
const promises = [];
const { isOffscreenCanvasSupported } = evaluator.options;
@ -351,38 +355,33 @@ class AnnotationFactory {
baseFont.set("Type", Name.get("Font"));
baseFont.set("Subtype", Name.get("Type1"));
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
const buffer = [];
baseFontRef = xref.getNewTemporaryRef();
await writeObject(baseFontRef, baseFont, buffer, xref);
dependencies.push({ ref: baseFontRef, data: buffer.join("") });
changes.put(baseFontRef, {
data: baseFont,
});
}
promises.push(
FreeTextAnnotation.createNewAnnotation(
xref,
annotation,
dependencies,
{ evaluator, task, baseFontRef }
)
FreeTextAnnotation.createNewAnnotation(xref, annotation, changes, {
evaluator,
task,
baseFontRef,
})
);
break;
case AnnotationEditorType.HIGHLIGHT:
if (annotation.quadPoints) {
promises.push(
HighlightAnnotation.createNewAnnotation(
xref,
annotation,
dependencies
)
HighlightAnnotation.createNewAnnotation(xref, annotation, changes)
);
} else {
promises.push(
InkAnnotation.createNewAnnotation(xref, annotation, dependencies)
InkAnnotation.createNewAnnotation(xref, annotation, changes)
);
}
break;
case AnnotationEditorType.INK:
promises.push(
InkAnnotation.createNewAnnotation(xref, annotation, dependencies)
InkAnnotation.createNewAnnotation(xref, annotation, changes)
);
break;
case AnnotationEditorType.STAMP:
@ -391,26 +390,23 @@ class AnnotationFactory {
: null;
if (image?.imageStream) {
const { imageStream, smaskStream } = image;
const buffer = [];
if (smaskStream) {
const smaskRef = xref.getNewTemporaryRef();
await writeObject(smaskRef, smaskStream, buffer, xref);
dependencies.push({ ref: smaskRef, data: buffer.join("") });
changes.put(smaskRef, {
data: smaskStream,
});
imageStream.dict.set("SMask", smaskRef);
buffer.length = 0;
}
const imageRef = (image.imageRef = xref.getNewTemporaryRef());
await writeObject(imageRef, imageStream, buffer, xref);
dependencies.push({ ref: imageRef, data: buffer.join("") });
changes.put(imageRef, {
data: imageStream,
});
image.imageStream = image.smaskStream = null;
}
promises.push(
StampAnnotation.createNewAnnotation(
xref,
annotation,
dependencies,
{ image }
)
StampAnnotation.createNewAnnotation(xref, annotation, changes, {
image,
})
);
break;
}
@ -418,7 +414,6 @@ class AnnotationFactory {
return {
annotations: await Promise.all(promises),
dependencies,
};
}
@ -1227,7 +1222,7 @@ class Annotation {
return { opList, separateForm: false, separateCanvas: isUsingOwnCanvas };
}
async save(evaluator, task, annotationStorage) {
async save(evaluator, task, annotationStorage, changes) {
return null;
}
@ -1758,14 +1753,13 @@ class MarkupAnnotation extends Annotation {
this._streams.push(this.appearance, appearanceStream);
}
static async createNewAnnotation(xref, annotation, dependencies, params) {
static async createNewAnnotation(xref, annotation, changes, params) {
if (!annotation.ref) {
annotation.ref = xref.getNewTemporaryRef();
}
const annotationRef = annotation.ref;
const ap = await this.createNewAppearanceStream(annotation, xref, params);
const buffer = [];
let annotationDict;
if (ap) {
@ -1773,8 +1767,9 @@ class MarkupAnnotation extends Annotation {
annotationDict = this.createNewDict(annotation, xref, {
apRef,
});
await writeObject(apRef, ap, buffer, xref);
dependencies.push({ ref: apRef, data: buffer.join("") });
changes.put(apRef, {
data: ap,
});
} else {
annotationDict = this.createNewDict(annotation, xref, {});
}
@ -1782,10 +1777,11 @@ class MarkupAnnotation extends Annotation {
annotationDict.set("StructParent", annotation.parentTreeId);
}
buffer.length = 0;
await writeObject(annotationRef, annotationDict, buffer, xref);
changes.put(annotationRef, {
data: annotationDict,
});
return { ref: annotationRef, data: buffer.join("") };
return { ref: annotationRef };
}
static async createNewPrintAnnotation(
@ -2112,7 +2108,7 @@ class WidgetAnnotation extends Annotation {
amendSavedDict(annotationStorage, dict) {}
async save(evaluator, task, annotationStorage) {
async save(evaluator, task, annotationStorage, changes) {
const storageEntry = annotationStorage?.get(this.data.id);
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
let value = storageEntry?.value,
@ -2123,7 +2119,7 @@ class WidgetAnnotation extends Annotation {
rotation === undefined &&
flags === undefined
) {
return null;
return;
}
value ||= this.data.fieldValue;
}
@ -2137,7 +2133,7 @@ class WidgetAnnotation extends Annotation {
isArrayEqual(value, this.data.fieldValue) &&
flags === undefined
) {
return null;
return;
}
if (rotation === undefined) {
@ -2154,7 +2150,7 @@ class WidgetAnnotation extends Annotation {
);
if (appearance === null && flags === undefined) {
// Appearance didn't change.
return null;
return;
}
} else {
// No need to create an appearance: the pdf has the flag /NeedAppearances
@ -2171,7 +2167,7 @@ class WidgetAnnotation extends Annotation {
const originalDict = xref.fetchIfRef(this.ref);
if (!(originalDict instanceof Dict)) {
return null;
return;
}
const dict = new Dict(xref);
@ -2208,12 +2204,11 @@ class WidgetAnnotation extends Annotation {
dict.set("MK", maybeMK);
}
const buffer = [];
const changes = [
// data for the original object
// V field changed + reference for new AP
{ ref: this.ref, data: "", xfa, needAppearances },
];
changes.put(this.ref, {
data: dict,
xfa,
needAppearances,
});
if (appearance !== null) {
const newRef = xref.getNewTemporaryRef();
const AP = new Dict(xref);
@ -2238,26 +2233,14 @@ class WidgetAnnotation extends Annotation {
appearanceDict.set("Matrix", rotationMatrix);
}
await writeObject(newRef, appearanceStream, buffer, xref);
changes.push(
// data for the new AP
{
ref: newRef,
data: buffer.join(""),
xfa: null,
needAppearances: false,
}
);
buffer.length = 0;
changes.put(newRef, {
data: appearanceStream,
xfa: null,
needAppearances: false,
});
}
dict.set("M", `D:${getModificationDate()}`);
await writeObject(this.ref, dict, buffer, xref);
changes[0].data = buffer.join("");
return changes;
}
async _getAppearance(evaluator, task, intent, annotationStorage) {
@ -3078,22 +3061,20 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
};
}
async save(evaluator, task, annotationStorage) {
async save(evaluator, task, annotationStorage, changes) {
if (this.data.checkBox) {
return this._saveCheckbox(evaluator, task, annotationStorage);
this._saveCheckbox(evaluator, task, annotationStorage, changes);
return;
}
if (this.data.radioButton) {
return this._saveRadioButton(evaluator, task, annotationStorage);
this._saveRadioButton(evaluator, task, annotationStorage, changes);
}
// Nothing to save
return null;
}
async _saveCheckbox(evaluator, task, annotationStorage) {
async _saveCheckbox(evaluator, task, annotationStorage, changes) {
if (!annotationStorage) {
return null;
return;
}
const storageEntry = annotationStorage.get(this.data.id);
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
@ -3102,18 +3083,18 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
if (rotation === undefined && flags === undefined) {
if (value === undefined) {
return null;
return;
}
const defaultValue = this.data.fieldValue === this.data.exportValue;
if (defaultValue === value) {
return null;
return;
}
}
let dict = evaluator.xref.fetchIfRef(this.ref);
if (!(dict instanceof Dict)) {
return null;
return;
}
dict = dict.clone();
@ -3142,15 +3123,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
dict.set("MK", maybeMK);
}
const buffer = [];
await writeObject(this.ref, dict, buffer, evaluator.xref);
return [{ ref: this.ref, data: buffer.join(""), xfa }];
changes.put(this.ref, {
data: dict,
xfa,
needAppearances: false,
});
}
async _saveRadioButton(evaluator, task, annotationStorage) {
async _saveRadioButton(evaluator, task, annotationStorage, changes) {
if (!annotationStorage) {
return null;
return;
}
const storageEntry = annotationStorage.get(this.data.id);
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
@ -3159,18 +3141,18 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
if (rotation === undefined && flags === undefined) {
if (value === undefined) {
return null;
return;
}
const defaultValue = this.data.fieldValue === this.data.buttonValue;
if (defaultValue === value) {
return null;
return;
}
}
let dict = evaluator.xref.fetchIfRef(this.ref);
if (!(dict instanceof Dict)) {
return null;
return;
}
dict = dict.clone();
@ -3188,16 +3170,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
};
const name = Name.get(value ? this.data.buttonValue : "Off");
const buffer = [];
let parentData = null;
if (value) {
if (this.parent instanceof Ref) {
const parent = evaluator.xref.fetch(this.parent);
const parent = evaluator.xref.fetch(this.parent).clone();
parent.set("V", name);
await writeObject(this.parent, parent, buffer, evaluator.xref);
parentData = buffer.join("");
buffer.length = 0;
changes.put(this.parent, {
data: parent,
xfa: null,
needAppearances: false,
});
} else if (this.parent instanceof Dict) {
this.parent.set("V", name);
}
@ -3219,13 +3201,11 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
dict.set("MK", maybeMK);
}
await writeObject(this.ref, dict, buffer, evaluator.xref);
const newRefs = [{ ref: this.ref, data: buffer.join(""), xfa }];
if (parentData) {
newRefs.push({ ref: this.parent, data: parentData, xfa: null });
}
return newRefs;
changes.put(this.ref, {
data: dict,
xfa,
needAppearances: false,
});
}
_getDefaultCheckedAppearance(params, type) {

View file

@ -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";
@ -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.");
}
@ -348,7 +347,8 @@ class Page {
partialEvaluator,
task,
annotations,
imagePromises
imagePromises,
changes
);
for (const { ref } of newData.annotations) {
@ -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,
@ -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 " +
@ -410,9 +403,7 @@ class Page {
);
}
return Promise.all(newRefsPromises).then(function (newRefs) {
return newRefs.filter(newRef => !!newRef);
});
return Promise.all(promises);
});
}

View file

@ -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];

View file

@ -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;
@ -117,7 +116,7 @@ class StructTreeRoot {
xref,
catalogRef,
pdfManager,
newRefs,
changes,
}) {
const root = pdfManager.catalog.cloneDict();
const cache = new RefSetCache();
@ -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,
});
}
}
@ -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;
@ -273,7 +271,7 @@ class StructTreeRoot {
nums,
xref,
pdfManager,
newRefs,
changes,
cache,
});
@ -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,
});
}
}
@ -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);
@ -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;
}
}

View file

@ -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";
@ -540,6 +540,7 @@ class WorkerMessageHandler {
pdfManager.ensureDoc("linearization"),
pdfManager.ensureCatalog("structTreeRoot"),
];
const changes = new RefSetCache();
const promises = [];
const newAnnotationsByPage = !isPureXfa
@ -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);
});
@ -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;
})
);
}
@ -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);
});
@ -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;
@ -712,7 +712,7 @@ class WorkerMessageHandler {
return incrementalUpdate({
originalData: stream.bytes,
xrefInfo: newXrefInfo,
newRefs,
changes,
xref,
hasXfa: !!xfa,
xfaDatasetsRef,

View file

@ -23,9 +23,9 @@ import {
parseXFAPath,
} from "./core_utils.js";
import { SimpleDOMNode, SimpleXMLParser } from "./xml_parser.js";
import { Stream, StringStream } from "./stream.js";
import { BaseStream } from "./base_stream.js";
import { calculateMD5 } from "./crypto.js";
import { Stream } from "./stream.js";
async function writeObject(ref, obj, buffer, { encrypt = null }) {
const transform = encrypt?.createCipherTransform(ref.num, ref.gen);
@ -192,10 +192,10 @@ function computeMD5(filesize, xrefInfo) {
return bytesToString(calculateMD5(array));
}
function writeXFADataForAcroform(str, newRefs) {
function writeXFADataForAcroform(str, changes) {
const xml = new SimpleXMLParser({ hasAttributes: true }).parseFromString(str);
for (const { xfa } of newRefs) {
for (const { xfa } of changes) {
if (!xfa) {
continue;
}
@ -230,7 +230,7 @@ async function updateAcroform({
hasXfaDatasetsEntry,
xfaDatasetsRef,
needAppearances,
newRefs,
changes,
}) {
if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) {
warn("XFA - Cannot save it");
@ -257,33 +257,23 @@ async function updateAcroform({
dict.set("NeedAppearances", true);
}
const buffer = [];
await writeObject(acroFormRef, dict, buffer, xref);
newRefs.push({ ref: acroFormRef, data: buffer.join("") });
changes.put(acroFormRef, {
data: dict,
});
}
function updateXFA({ xfaData, xfaDatasetsRef, newRefs, xref }) {
function updateXFA({ xfaData, xfaDatasetsRef, changes, xref }) {
if (xfaData === null) {
const datasets = xref.fetchIfRef(xfaDatasetsRef);
xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
xfaData = writeXFADataForAcroform(datasets.getString(), changes);
}
const xfaDataStream = new StringStream(xfaData);
xfaDataStream.dict = new Dict(xref);
xfaDataStream.dict.set("Type", Name.get("EmbeddedFile"));
const encrypt = xref.encrypt;
if (encrypt) {
const transform = encrypt.createCipherTransform(
xfaDatasetsRef.num,
xfaDatasetsRef.gen
);
xfaData = transform.encryptString(xfaData);
}
const data =
`${xfaDatasetsRef.num} ${xfaDatasetsRef.gen} obj\n` +
`<< /Type /EmbeddedFile /Length ${xfaData.length}>>\nstream\n` +
xfaData +
"\nendstream\nendobj\n";
newRefs.push({ ref: xfaDatasetsRef, data });
changes.put(xfaDatasetsRef, {
data: xfaDataStream,
});
}
async function getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer) {
@ -383,12 +373,12 @@ function computeIDs(baseOffset, xrefInfo, newXref) {
}
}
function getTrailerDict(xrefInfo, newRefs, useXrefStream) {
function getTrailerDict(xrefInfo, changes, useXrefStream) {
const newXref = new Dict(null);
newXref.set("Prev", xrefInfo.startXRef);
const refForXrefTable = xrefInfo.newRef;
if (useXrefStream) {
newRefs.push({ ref: refForXrefTable, data: "" });
changes.put(refForXrefTable, { data: "" });
newXref.set("Size", refForXrefTable.num + 1);
newXref.set("Type", Name.get("XRef"));
} else {
@ -406,10 +396,24 @@ function getTrailerDict(xrefInfo, newRefs, useXrefStream) {
return newXref;
}
async function writeChanges(changes, xref, buffer = []) {
const newRefs = [];
for (const [ref, { data }] of changes.items()) {
if (data === null || typeof data === "string") {
newRefs.push({ ref, data });
continue;
}
await writeObject(ref, data, buffer, xref);
newRefs.push({ ref, data: buffer.join("") });
buffer.length = 0;
}
return newRefs.sort((a, b) => /* compare the refs */ a.ref.num - b.ref.num);
}
async function incrementalUpdate({
originalData,
xrefInfo,
newRefs,
changes,
xref = null,
hasXfa = false,
xfaDatasetsRef = null,
@ -428,19 +432,21 @@ async function incrementalUpdate({
hasXfaDatasetsEntry,
xfaDatasetsRef,
needAppearances,
newRefs,
changes,
});
if (hasXfa) {
updateXFA({
xfaData,
xfaDatasetsRef,
newRefs,
changes,
xref,
});
}
const newXref = getTrailerDict(xrefInfo, changes, useXrefStream);
const buffer = [];
const newRefs = await writeChanges(changes, xref, buffer);
let baseOffset = originalData.length;
const lastByte = originalData.at(-1);
if (lastByte !== /* \n */ 0x0a && lastByte !== /* \r */ 0x0d) {
@ -449,10 +455,6 @@ async function incrementalUpdate({
baseOffset += 1;
}
const newXref = getTrailerDict(xrefInfo, newRefs, useXrefStream);
newRefs = newRefs.sort(
(a, b) => /* compare the refs */ a.ref.num - b.ref.num
);
for (const { data } of newRefs) {
if (data !== null) {
buffer.push(data);
@ -482,4 +484,4 @@ async function incrementalUpdate({
return array;
}
export { incrementalUpdate, writeDict, writeObject };
export { incrementalUpdate, writeChanges, writeDict, writeObject };