Merge pull request #19026 from calixteman/refactor_newrefs_saving
Simplify saving added/modified annotations.
This commit is contained in:
commit
13a231cd3b
@ -68,7 +68,6 @@ import { FileSpec } from "./file_spec.js";
|
|||||||
import { JpegStream } from "./jpeg_stream.js";
|
import { JpegStream } from "./jpeg_stream.js";
|
||||||
import { ObjectLoader } from "./object_loader.js";
|
import { ObjectLoader } from "./object_loader.js";
|
||||||
import { OperatorList } from "./operator_list.js";
|
import { OperatorList } from "./operator_list.js";
|
||||||
import { writeObject } from "./writer.js";
|
|
||||||
import { XFAFactory } from "./xfa/factory.js";
|
import { XFAFactory } from "./xfa/factory.js";
|
||||||
|
|
||||||
class AnnotationFactory {
|
class AnnotationFactory {
|
||||||
@ -332,10 +331,15 @@ class AnnotationFactory {
|
|||||||
return imagePromises;
|
return imagePromises;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async saveNewAnnotations(evaluator, task, annotations, imagePromises) {
|
static async saveNewAnnotations(
|
||||||
|
evaluator,
|
||||||
|
task,
|
||||||
|
annotations,
|
||||||
|
imagePromises,
|
||||||
|
changes
|
||||||
|
) {
|
||||||
const xref = evaluator.xref;
|
const xref = evaluator.xref;
|
||||||
let baseFontRef;
|
let baseFontRef;
|
||||||
const dependencies = [];
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const { isOffscreenCanvasSupported } = evaluator.options;
|
const { isOffscreenCanvasSupported } = evaluator.options;
|
||||||
|
|
||||||
@ -351,38 +355,33 @@ class AnnotationFactory {
|
|||||||
baseFont.set("Type", Name.get("Font"));
|
baseFont.set("Type", Name.get("Font"));
|
||||||
baseFont.set("Subtype", Name.get("Type1"));
|
baseFont.set("Subtype", Name.get("Type1"));
|
||||||
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
|
baseFont.set("Encoding", Name.get("WinAnsiEncoding"));
|
||||||
const buffer = [];
|
|
||||||
baseFontRef = xref.getNewTemporaryRef();
|
baseFontRef = xref.getNewTemporaryRef();
|
||||||
await writeObject(baseFontRef, baseFont, buffer, xref);
|
changes.put(baseFontRef, {
|
||||||
dependencies.push({ ref: baseFontRef, data: buffer.join("") });
|
data: baseFont,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
promises.push(
|
promises.push(
|
||||||
FreeTextAnnotation.createNewAnnotation(
|
FreeTextAnnotation.createNewAnnotation(xref, annotation, changes, {
|
||||||
xref,
|
evaluator,
|
||||||
annotation,
|
task,
|
||||||
dependencies,
|
baseFontRef,
|
||||||
{ evaluator, task, baseFontRef }
|
})
|
||||||
)
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorType.HIGHLIGHT:
|
case AnnotationEditorType.HIGHLIGHT:
|
||||||
if (annotation.quadPoints) {
|
if (annotation.quadPoints) {
|
||||||
promises.push(
|
promises.push(
|
||||||
HighlightAnnotation.createNewAnnotation(
|
HighlightAnnotation.createNewAnnotation(xref, annotation, changes)
|
||||||
xref,
|
|
||||||
annotation,
|
|
||||||
dependencies
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
promises.push(
|
promises.push(
|
||||||
InkAnnotation.createNewAnnotation(xref, annotation, dependencies)
|
InkAnnotation.createNewAnnotation(xref, annotation, changes)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorType.INK:
|
case AnnotationEditorType.INK:
|
||||||
promises.push(
|
promises.push(
|
||||||
InkAnnotation.createNewAnnotation(xref, annotation, dependencies)
|
InkAnnotation.createNewAnnotation(xref, annotation, changes)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorType.STAMP:
|
case AnnotationEditorType.STAMP:
|
||||||
@ -391,26 +390,23 @@ class AnnotationFactory {
|
|||||||
: null;
|
: null;
|
||||||
if (image?.imageStream) {
|
if (image?.imageStream) {
|
||||||
const { imageStream, smaskStream } = image;
|
const { imageStream, smaskStream } = image;
|
||||||
const buffer = [];
|
|
||||||
if (smaskStream) {
|
if (smaskStream) {
|
||||||
const smaskRef = xref.getNewTemporaryRef();
|
const smaskRef = xref.getNewTemporaryRef();
|
||||||
await writeObject(smaskRef, smaskStream, buffer, xref);
|
changes.put(smaskRef, {
|
||||||
dependencies.push({ ref: smaskRef, data: buffer.join("") });
|
data: smaskStream,
|
||||||
|
});
|
||||||
imageStream.dict.set("SMask", smaskRef);
|
imageStream.dict.set("SMask", smaskRef);
|
||||||
buffer.length = 0;
|
|
||||||
}
|
}
|
||||||
const imageRef = (image.imageRef = xref.getNewTemporaryRef());
|
const imageRef = (image.imageRef = xref.getNewTemporaryRef());
|
||||||
await writeObject(imageRef, imageStream, buffer, xref);
|
changes.put(imageRef, {
|
||||||
dependencies.push({ ref: imageRef, data: buffer.join("") });
|
data: imageStream,
|
||||||
|
});
|
||||||
image.imageStream = image.smaskStream = null;
|
image.imageStream = image.smaskStream = null;
|
||||||
}
|
}
|
||||||
promises.push(
|
promises.push(
|
||||||
StampAnnotation.createNewAnnotation(
|
StampAnnotation.createNewAnnotation(xref, annotation, changes, {
|
||||||
xref,
|
image,
|
||||||
annotation,
|
})
|
||||||
dependencies,
|
|
||||||
{ image }
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -418,7 +414,6 @@ class AnnotationFactory {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
annotations: await Promise.all(promises),
|
annotations: await Promise.all(promises),
|
||||||
dependencies,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1227,7 +1222,7 @@ class Annotation {
|
|||||||
return { opList, separateForm: false, separateCanvas: isUsingOwnCanvas };
|
return { opList, separateForm: false, separateCanvas: isUsingOwnCanvas };
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(evaluator, task, annotationStorage) {
|
async save(evaluator, task, annotationStorage, changes) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1758,14 +1753,13 @@ class MarkupAnnotation extends Annotation {
|
|||||||
this._streams.push(this.appearance, appearanceStream);
|
this._streams.push(this.appearance, appearanceStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createNewAnnotation(xref, annotation, dependencies, params) {
|
static async createNewAnnotation(xref, annotation, changes, params) {
|
||||||
if (!annotation.ref) {
|
if (!annotation.ref) {
|
||||||
annotation.ref = xref.getNewTemporaryRef();
|
annotation.ref = xref.getNewTemporaryRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotationRef = annotation.ref;
|
const annotationRef = annotation.ref;
|
||||||
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
||||||
const buffer = [];
|
|
||||||
let annotationDict;
|
let annotationDict;
|
||||||
|
|
||||||
if (ap) {
|
if (ap) {
|
||||||
@ -1773,8 +1767,9 @@ class MarkupAnnotation extends Annotation {
|
|||||||
annotationDict = this.createNewDict(annotation, xref, {
|
annotationDict = this.createNewDict(annotation, xref, {
|
||||||
apRef,
|
apRef,
|
||||||
});
|
});
|
||||||
await writeObject(apRef, ap, buffer, xref);
|
changes.put(apRef, {
|
||||||
dependencies.push({ ref: apRef, data: buffer.join("") });
|
data: ap,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
annotationDict = this.createNewDict(annotation, xref, {});
|
annotationDict = this.createNewDict(annotation, xref, {});
|
||||||
}
|
}
|
||||||
@ -1782,10 +1777,11 @@ class MarkupAnnotation extends Annotation {
|
|||||||
annotationDict.set("StructParent", annotation.parentTreeId);
|
annotationDict.set("StructParent", annotation.parentTreeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.length = 0;
|
changes.put(annotationRef, {
|
||||||
await writeObject(annotationRef, annotationDict, buffer, xref);
|
data: annotationDict,
|
||||||
|
});
|
||||||
|
|
||||||
return { ref: annotationRef, data: buffer.join("") };
|
return { ref: annotationRef };
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createNewPrintAnnotation(
|
static async createNewPrintAnnotation(
|
||||||
@ -2112,7 +2108,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
|
|
||||||
amendSavedDict(annotationStorage, dict) {}
|
amendSavedDict(annotationStorage, dict) {}
|
||||||
|
|
||||||
async save(evaluator, task, annotationStorage) {
|
async save(evaluator, task, annotationStorage, changes) {
|
||||||
const storageEntry = annotationStorage?.get(this.data.id);
|
const storageEntry = annotationStorage?.get(this.data.id);
|
||||||
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
||||||
let value = storageEntry?.value,
|
let value = storageEntry?.value,
|
||||||
@ -2123,7 +2119,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
rotation === undefined &&
|
rotation === undefined &&
|
||||||
flags === undefined
|
flags === undefined
|
||||||
) {
|
) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
value ||= this.data.fieldValue;
|
value ||= this.data.fieldValue;
|
||||||
}
|
}
|
||||||
@ -2137,7 +2133,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
isArrayEqual(value, this.data.fieldValue) &&
|
isArrayEqual(value, this.data.fieldValue) &&
|
||||||
flags === undefined
|
flags === undefined
|
||||||
) {
|
) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rotation === undefined) {
|
if (rotation === undefined) {
|
||||||
@ -2154,7 +2150,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
);
|
);
|
||||||
if (appearance === null && flags === undefined) {
|
if (appearance === null && flags === undefined) {
|
||||||
// Appearance didn't change.
|
// Appearance didn't change.
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No need to create an appearance: the pdf has the flag /NeedAppearances
|
// 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);
|
const originalDict = xref.fetchIfRef(this.ref);
|
||||||
if (!(originalDict instanceof Dict)) {
|
if (!(originalDict instanceof Dict)) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dict = new Dict(xref);
|
const dict = new Dict(xref);
|
||||||
@ -2208,12 +2204,11 @@ class WidgetAnnotation extends Annotation {
|
|||||||
dict.set("MK", maybeMK);
|
dict.set("MK", maybeMK);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = [];
|
changes.put(this.ref, {
|
||||||
const changes = [
|
data: dict,
|
||||||
// data for the original object
|
xfa,
|
||||||
// V field changed + reference for new AP
|
needAppearances,
|
||||||
{ ref: this.ref, data: "", xfa, needAppearances },
|
});
|
||||||
];
|
|
||||||
if (appearance !== null) {
|
if (appearance !== null) {
|
||||||
const newRef = xref.getNewTemporaryRef();
|
const newRef = xref.getNewTemporaryRef();
|
||||||
const AP = new Dict(xref);
|
const AP = new Dict(xref);
|
||||||
@ -2238,26 +2233,14 @@ class WidgetAnnotation extends Annotation {
|
|||||||
appearanceDict.set("Matrix", rotationMatrix);
|
appearanceDict.set("Matrix", rotationMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeObject(newRef, appearanceStream, buffer, xref);
|
changes.put(newRef, {
|
||||||
|
data: appearanceStream,
|
||||||
changes.push(
|
xfa: null,
|
||||||
// data for the new AP
|
needAppearances: false,
|
||||||
{
|
});
|
||||||
ref: newRef,
|
|
||||||
data: buffer.join(""),
|
|
||||||
xfa: null,
|
|
||||||
needAppearances: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
buffer.length = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dict.set("M", `D:${getModificationDate()}`);
|
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) {
|
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) {
|
if (this.data.checkBox) {
|
||||||
return this._saveCheckbox(evaluator, task, annotationStorage);
|
this._saveCheckbox(evaluator, task, annotationStorage, changes);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data.radioButton) {
|
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) {
|
if (!annotationStorage) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
const storageEntry = annotationStorage.get(this.data.id);
|
const storageEntry = annotationStorage.get(this.data.id);
|
||||||
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
||||||
@ -3102,18 +3083,18 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
|
|
||||||
if (rotation === undefined && flags === undefined) {
|
if (rotation === undefined && flags === undefined) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultValue = this.data.fieldValue === this.data.exportValue;
|
const defaultValue = this.data.fieldValue === this.data.exportValue;
|
||||||
if (defaultValue === value) {
|
if (defaultValue === value) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dict = evaluator.xref.fetchIfRef(this.ref);
|
let dict = evaluator.xref.fetchIfRef(this.ref);
|
||||||
if (!(dict instanceof Dict)) {
|
if (!(dict instanceof Dict)) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
dict = dict.clone();
|
dict = dict.clone();
|
||||||
|
|
||||||
@ -3142,15 +3123,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
dict.set("MK", maybeMK);
|
dict.set("MK", maybeMK);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = [];
|
changes.put(this.ref, {
|
||||||
await writeObject(this.ref, dict, buffer, evaluator.xref);
|
data: dict,
|
||||||
|
xfa,
|
||||||
return [{ ref: this.ref, data: buffer.join(""), xfa }];
|
needAppearances: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _saveRadioButton(evaluator, task, annotationStorage) {
|
async _saveRadioButton(evaluator, task, annotationStorage, changes) {
|
||||||
if (!annotationStorage) {
|
if (!annotationStorage) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
const storageEntry = annotationStorage.get(this.data.id);
|
const storageEntry = annotationStorage.get(this.data.id);
|
||||||
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
const flags = this._buildFlags(storageEntry?.noView, storageEntry?.noPrint);
|
||||||
@ -3159,18 +3141,18 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
|
|
||||||
if (rotation === undefined && flags === undefined) {
|
if (rotation === undefined && flags === undefined) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultValue = this.data.fieldValue === this.data.buttonValue;
|
const defaultValue = this.data.fieldValue === this.data.buttonValue;
|
||||||
if (defaultValue === value) {
|
if (defaultValue === value) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dict = evaluator.xref.fetchIfRef(this.ref);
|
let dict = evaluator.xref.fetchIfRef(this.ref);
|
||||||
if (!(dict instanceof Dict)) {
|
if (!(dict instanceof Dict)) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
dict = dict.clone();
|
dict = dict.clone();
|
||||||
|
|
||||||
@ -3188,16 +3170,16 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const name = Name.get(value ? this.data.buttonValue : "Off");
|
const name = Name.get(value ? this.data.buttonValue : "Off");
|
||||||
const buffer = [];
|
|
||||||
let parentData = null;
|
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
if (this.parent instanceof Ref) {
|
if (this.parent instanceof Ref) {
|
||||||
const parent = evaluator.xref.fetch(this.parent);
|
const parent = evaluator.xref.fetch(this.parent).clone();
|
||||||
parent.set("V", name);
|
parent.set("V", name);
|
||||||
await writeObject(this.parent, parent, buffer, evaluator.xref);
|
changes.put(this.parent, {
|
||||||
parentData = buffer.join("");
|
data: parent,
|
||||||
buffer.length = 0;
|
xfa: null,
|
||||||
|
needAppearances: false,
|
||||||
|
});
|
||||||
} else if (this.parent instanceof Dict) {
|
} else if (this.parent instanceof Dict) {
|
||||||
this.parent.set("V", name);
|
this.parent.set("V", name);
|
||||||
}
|
}
|
||||||
@ -3219,13 +3201,11 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
dict.set("MK", maybeMK);
|
dict.set("MK", maybeMK);
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeObject(this.ref, dict, buffer, evaluator.xref);
|
changes.put(this.ref, {
|
||||||
const newRefs = [{ ref: this.ref, data: buffer.join(""), xfa }];
|
data: dict,
|
||||||
if (parentData) {
|
xfa,
|
||||||
newRefs.push({ ref: this.parent, data: parentData, xfa: null });
|
needAppearances: false,
|
||||||
}
|
});
|
||||||
|
|
||||||
return newRefs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDefaultCheckedAppearance(params, type) {
|
_getDefaultCheckedAppearance(params, type) {
|
||||||
|
|||||||
@ -70,7 +70,6 @@ import { OperatorList } from "./operator_list.js";
|
|||||||
import { PartialEvaluator } from "./evaluator.js";
|
import { PartialEvaluator } from "./evaluator.js";
|
||||||
import { StreamsSequenceStream } from "./decode_stream.js";
|
import { StreamsSequenceStream } from "./decode_stream.js";
|
||||||
import { StructTreePage } from "./struct_tree.js";
|
import { StructTreePage } from "./struct_tree.js";
|
||||||
import { writeObject } from "./writer.js";
|
|
||||||
import { XFAFactory } from "./xfa/factory.js";
|
import { XFAFactory } from "./xfa/factory.js";
|
||||||
import { XRef } from "./xref.js";
|
import { XRef } from "./xref.js";
|
||||||
|
|
||||||
@ -314,7 +313,7 @@ class Page {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveNewAnnotations(handler, task, annotations, imagePromises) {
|
async saveNewAnnotations(handler, task, annotations, imagePromises, changes) {
|
||||||
if (this.xfaFactory) {
|
if (this.xfaFactory) {
|
||||||
throw new Error("XFA: Cannot save new annotations.");
|
throw new Error("XFA: Cannot save new annotations.");
|
||||||
}
|
}
|
||||||
@ -348,7 +347,8 @@ class Page {
|
|||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
annotations,
|
annotations,
|
||||||
imagePromises
|
imagePromises,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const { ref } of newData.annotations) {
|
for (const { ref } of newData.annotations) {
|
||||||
@ -358,27 +358,20 @@ class Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedDict = pageDict.get("Annots");
|
const dict = pageDict.clone();
|
||||||
pageDict.set("Annots", annotationsArray);
|
dict.set("Annots", annotationsArray);
|
||||||
const buffer = [];
|
changes.put(this.ref, {
|
||||||
await writeObject(this.ref, pageDict, buffer, this.xref);
|
data: dict,
|
||||||
if (savedDict) {
|
});
|
||||||
pageDict.set("Annots", savedDict);
|
|
||||||
}
|
|
||||||
|
|
||||||
const objects = newData.dependencies;
|
|
||||||
objects.push(
|
|
||||||
{ ref: this.ref, data: buffer.join("") },
|
|
||||||
...newData.annotations
|
|
||||||
);
|
|
||||||
for (const deletedRef of deletedAnnotations) {
|
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({
|
const partialEvaluator = new PartialEvaluator({
|
||||||
xref: this.xref,
|
xref: this.xref,
|
||||||
handler,
|
handler,
|
||||||
@ -395,11 +388,11 @@ class Page {
|
|||||||
// Fetch the page's annotations and save the content
|
// Fetch the page's annotations and save the content
|
||||||
// in case of interactive form fields.
|
// in case of interactive form fields.
|
||||||
return this._parsedAnnotations.then(function (annotations) {
|
return this._parsedAnnotations.then(function (annotations) {
|
||||||
const newRefsPromises = [];
|
const promises = [];
|
||||||
for (const annotation of annotations) {
|
for (const annotation of annotations) {
|
||||||
newRefsPromises.push(
|
promises.push(
|
||||||
annotation
|
annotation
|
||||||
.save(partialEvaluator, task, annotationStorage)
|
.save(partialEvaluator, task, annotationStorage, changes)
|
||||||
.catch(function (reason) {
|
.catch(function (reason) {
|
||||||
warn(
|
warn(
|
||||||
"save - ignoring annotation data during " +
|
"save - ignoring annotation data during " +
|
||||||
@ -410,9 +403,7 @@ class Page {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(newRefsPromises).then(function (newRefs) {
|
return Promise.all(promises);
|
||||||
return newRefs.filter(newRef => !!newRef);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -383,6 +383,10 @@ class RefSetCache {
|
|||||||
this._map.clear();
|
this._map.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*values() {
|
||||||
|
yield* this._map.values();
|
||||||
|
}
|
||||||
|
|
||||||
*items() {
|
*items() {
|
||||||
for (const [ref, value] of this._map) {
|
for (const [ref, value] of this._map) {
|
||||||
yield [Ref.fromString(ref), value];
|
yield [Ref.fromString(ref), value];
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import { AnnotationPrefix, stringToPDFString, warn } from "../shared/util.js";
|
|||||||
import { Dict, isName, Name, Ref, RefSetCache } from "./primitives.js";
|
import { Dict, isName, Name, Ref, RefSetCache } from "./primitives.js";
|
||||||
import { lookupNormalRect, stringToAsciiOrUTF16BE } from "./core_utils.js";
|
import { lookupNormalRect, stringToAsciiOrUTF16BE } from "./core_utils.js";
|
||||||
import { NumberTree } from "./name_number_tree.js";
|
import { NumberTree } from "./name_number_tree.js";
|
||||||
import { writeObject } from "./writer.js";
|
|
||||||
|
|
||||||
const MAX_DEPTH = 40;
|
const MAX_DEPTH = 40;
|
||||||
|
|
||||||
@ -117,7 +116,7 @@ class StructTreeRoot {
|
|||||||
xref,
|
xref,
|
||||||
catalogRef,
|
catalogRef,
|
||||||
pdfManager,
|
pdfManager,
|
||||||
newRefs,
|
changes,
|
||||||
}) {
|
}) {
|
||||||
const root = pdfManager.catalog.cloneDict();
|
const root = pdfManager.catalog.cloneDict();
|
||||||
const cache = new RefSetCache();
|
const cache = new RefSetCache();
|
||||||
@ -146,18 +145,17 @@ class StructTreeRoot {
|
|||||||
nums,
|
nums,
|
||||||
xref,
|
xref,
|
||||||
pdfManager,
|
pdfManager,
|
||||||
newRefs,
|
changes,
|
||||||
cache,
|
cache,
|
||||||
});
|
});
|
||||||
structTreeRoot.set("ParentTreeNextKey", nextKey);
|
structTreeRoot.set("ParentTreeNextKey", nextKey);
|
||||||
|
|
||||||
cache.put(parentTreeRef, parentTree);
|
cache.put(parentTreeRef, parentTree);
|
||||||
|
|
||||||
const buffer = [];
|
|
||||||
for (const [ref, obj] of cache.items()) {
|
for (const [ref, obj] of cache.items()) {
|
||||||
buffer.length = 0;
|
changes.put(ref, {
|
||||||
await writeObject(ref, obj, buffer, xref);
|
data: obj,
|
||||||
newRefs.push({ ref, data: buffer.join("") });
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +233,7 @@ class StructTreeRoot {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStructureTree({ newAnnotationsByPage, pdfManager, newRefs }) {
|
async updateStructureTree({ newAnnotationsByPage, pdfManager, changes }) {
|
||||||
const xref = this.dict.xref;
|
const xref = this.dict.xref;
|
||||||
const structTreeRoot = this.dict.clone();
|
const structTreeRoot = this.dict.clone();
|
||||||
const structTreeRootRef = this.ref;
|
const structTreeRootRef = this.ref;
|
||||||
@ -273,7 +271,7 @@ class StructTreeRoot {
|
|||||||
nums,
|
nums,
|
||||||
xref,
|
xref,
|
||||||
pdfManager,
|
pdfManager,
|
||||||
newRefs,
|
changes,
|
||||||
cache,
|
cache,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -288,11 +286,10 @@ class StructTreeRoot {
|
|||||||
cache.put(numsRef, nums);
|
cache.put(numsRef, nums);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = [];
|
|
||||||
for (const [ref, obj] of cache.items()) {
|
for (const [ref, obj] of cache.items()) {
|
||||||
buffer.length = 0;
|
changes.put(ref, {
|
||||||
await writeObject(ref, obj, buffer, xref);
|
data: obj,
|
||||||
newRefs.push({ ref, data: buffer.join("") });
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,13 +301,12 @@ class StructTreeRoot {
|
|||||||
nums,
|
nums,
|
||||||
xref,
|
xref,
|
||||||
pdfManager,
|
pdfManager,
|
||||||
newRefs,
|
changes,
|
||||||
cache,
|
cache,
|
||||||
}) {
|
}) {
|
||||||
const objr = Name.get("OBJR");
|
const objr = Name.get("OBJR");
|
||||||
let nextKey = -1;
|
let nextKey = -1;
|
||||||
let structTreePageObjs;
|
let structTreePageObjs;
|
||||||
const buffer = [];
|
|
||||||
|
|
||||||
for (const [pageIndex, elements] of newAnnotationsByPage) {
|
for (const [pageIndex, elements] of newAnnotationsByPage) {
|
||||||
const page = await pdfManager.getPage(pageIndex);
|
const page = await pdfManager.getPage(pageIndex);
|
||||||
@ -350,9 +346,9 @@ class StructTreeRoot {
|
|||||||
// We update the existing tag.
|
// We update the existing tag.
|
||||||
const tagDict = xref.fetch(objRef).clone();
|
const tagDict = xref.fetch(objRef).clone();
|
||||||
StructTreeRoot.#writeProperties(tagDict, accessibilityData);
|
StructTreeRoot.#writeProperties(tagDict, accessibilityData);
|
||||||
buffer.length = 0;
|
changes.put(objRef, {
|
||||||
await writeObject(objRef, tagDict, buffer, xref);
|
data: tagDict,
|
||||||
newRefs.push({ ref: objRef, data: buffer.join("") });
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ import {
|
|||||||
getNewAnnotationsMap,
|
getNewAnnotationsMap,
|
||||||
XRefParseException,
|
XRefParseException,
|
||||||
} from "./core_utils.js";
|
} 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 { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js";
|
||||||
import { AnnotationFactory } from "./annotation.js";
|
import { AnnotationFactory } from "./annotation.js";
|
||||||
import { clearGlobalCaches } from "./cleanup_helper.js";
|
import { clearGlobalCaches } from "./cleanup_helper.js";
|
||||||
@ -540,6 +540,7 @@ class WorkerMessageHandler {
|
|||||||
pdfManager.ensureDoc("linearization"),
|
pdfManager.ensureDoc("linearization"),
|
||||||
pdfManager.ensureCatalog("structTreeRoot"),
|
pdfManager.ensureCatalog("structTreeRoot"),
|
||||||
];
|
];
|
||||||
|
const changes = new RefSetCache();
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
const newAnnotationsByPage = !isPureXfa
|
const newAnnotationsByPage = !isPureXfa
|
||||||
@ -590,7 +591,13 @@ class WorkerMessageHandler {
|
|||||||
pdfManager.getPage(pageIndex).then(page => {
|
pdfManager.getPage(pageIndex).then(page => {
|
||||||
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
|
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
|
||||||
return page
|
return page
|
||||||
.saveNewAnnotations(handler, task, annotations, imagePromises)
|
.saveNewAnnotations(
|
||||||
|
handler,
|
||||||
|
task,
|
||||||
|
annotations,
|
||||||
|
imagePromises,
|
||||||
|
changes
|
||||||
|
)
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
finishWorkerTask(task);
|
finishWorkerTask(task);
|
||||||
});
|
});
|
||||||
@ -600,26 +607,24 @@ class WorkerMessageHandler {
|
|||||||
if (structTreeRoot === null) {
|
if (structTreeRoot === null) {
|
||||||
// No structTreeRoot exists, so we need to create one.
|
// No structTreeRoot exists, so we need to create one.
|
||||||
promises.push(
|
promises.push(
|
||||||
Promise.all(newAnnotationPromises).then(async newRefs => {
|
Promise.all(newAnnotationPromises).then(async () => {
|
||||||
await StructTreeRoot.createStructureTree({
|
await StructTreeRoot.createStructureTree({
|
||||||
newAnnotationsByPage,
|
newAnnotationsByPage,
|
||||||
xref,
|
xref,
|
||||||
catalogRef,
|
catalogRef,
|
||||||
pdfManager,
|
pdfManager,
|
||||||
newRefs,
|
changes,
|
||||||
});
|
});
|
||||||
return newRefs;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else if (structTreeRoot) {
|
} else if (structTreeRoot) {
|
||||||
promises.push(
|
promises.push(
|
||||||
Promise.all(newAnnotationPromises).then(async newRefs => {
|
Promise.all(newAnnotationPromises).then(async () => {
|
||||||
await structTreeRoot.updateStructureTree({
|
await structTreeRoot.updateStructureTree({
|
||||||
newAnnotationsByPage,
|
newAnnotationsByPage,
|
||||||
pdfManager,
|
pdfManager,
|
||||||
newRefs,
|
changes,
|
||||||
});
|
});
|
||||||
return newRefs;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -633,7 +638,7 @@ class WorkerMessageHandler {
|
|||||||
pdfManager.getPage(pageIndex).then(function (page) {
|
pdfManager.getPage(pageIndex).then(function (page) {
|
||||||
const task = new WorkerTask(`Save: page ${pageIndex}`);
|
const task = new WorkerTask(`Save: page ${pageIndex}`);
|
||||||
return page
|
return page
|
||||||
.save(handler, task, annotationStorage)
|
.save(handler, task, annotationStorage, changes)
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
finishWorkerTask(task);
|
finishWorkerTask(task);
|
||||||
});
|
});
|
||||||
@ -643,26 +648,21 @@ class WorkerMessageHandler {
|
|||||||
}
|
}
|
||||||
const refs = await Promise.all(promises);
|
const refs = await Promise.all(promises);
|
||||||
|
|
||||||
let newRefs = [];
|
|
||||||
let xfaData = null;
|
let xfaData = null;
|
||||||
if (isPureXfa) {
|
if (isPureXfa) {
|
||||||
xfaData = refs[0];
|
xfaData = refs[0];
|
||||||
if (!xfaData) {
|
if (!xfaData) {
|
||||||
return stream.bytes;
|
return stream.bytes;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (changes.size === 0) {
|
||||||
newRefs = refs.flat(2);
|
// No new refs so just return the initial bytes
|
||||||
|
return stream.bytes;
|
||||||
if (newRefs.length === 0) {
|
|
||||||
// No new refs so just return the initial bytes
|
|
||||||
return stream.bytes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const needAppearances =
|
const needAppearances =
|
||||||
acroFormRef &&
|
acroFormRef &&
|
||||||
acroForm instanceof Dict &&
|
acroForm instanceof Dict &&
|
||||||
newRefs.some(ref => ref.needAppearances);
|
changes.values().some(ref => ref.needAppearances);
|
||||||
|
|
||||||
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
|
const xfa = (acroForm instanceof Dict && acroForm.get("XFA")) || null;
|
||||||
let xfaDatasetsRef = null;
|
let xfaDatasetsRef = null;
|
||||||
@ -712,7 +712,7 @@ class WorkerMessageHandler {
|
|||||||
return incrementalUpdate({
|
return incrementalUpdate({
|
||||||
originalData: stream.bytes,
|
originalData: stream.bytes,
|
||||||
xrefInfo: newXrefInfo,
|
xrefInfo: newXrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
xref,
|
xref,
|
||||||
hasXfa: !!xfa,
|
hasXfa: !!xfa,
|
||||||
xfaDatasetsRef,
|
xfaDatasetsRef,
|
||||||
|
|||||||
@ -23,9 +23,9 @@ import {
|
|||||||
parseXFAPath,
|
parseXFAPath,
|
||||||
} from "./core_utils.js";
|
} from "./core_utils.js";
|
||||||
import { SimpleDOMNode, SimpleXMLParser } from "./xml_parser.js";
|
import { SimpleDOMNode, SimpleXMLParser } from "./xml_parser.js";
|
||||||
|
import { Stream, StringStream } from "./stream.js";
|
||||||
import { BaseStream } from "./base_stream.js";
|
import { BaseStream } from "./base_stream.js";
|
||||||
import { calculateMD5 } from "./crypto.js";
|
import { calculateMD5 } from "./crypto.js";
|
||||||
import { Stream } from "./stream.js";
|
|
||||||
|
|
||||||
async function writeObject(ref, obj, buffer, { encrypt = null }) {
|
async function writeObject(ref, obj, buffer, { encrypt = null }) {
|
||||||
const transform = encrypt?.createCipherTransform(ref.num, ref.gen);
|
const transform = encrypt?.createCipherTransform(ref.num, ref.gen);
|
||||||
@ -192,10 +192,10 @@ function computeMD5(filesize, xrefInfo) {
|
|||||||
return bytesToString(calculateMD5(array));
|
return bytesToString(calculateMD5(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeXFADataForAcroform(str, newRefs) {
|
function writeXFADataForAcroform(str, changes) {
|
||||||
const xml = new SimpleXMLParser({ hasAttributes: true }).parseFromString(str);
|
const xml = new SimpleXMLParser({ hasAttributes: true }).parseFromString(str);
|
||||||
|
|
||||||
for (const { xfa } of newRefs) {
|
for (const { xfa } of changes) {
|
||||||
if (!xfa) {
|
if (!xfa) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -230,7 +230,7 @@ async function updateAcroform({
|
|||||||
hasXfaDatasetsEntry,
|
hasXfaDatasetsEntry,
|
||||||
xfaDatasetsRef,
|
xfaDatasetsRef,
|
||||||
needAppearances,
|
needAppearances,
|
||||||
newRefs,
|
changes,
|
||||||
}) {
|
}) {
|
||||||
if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) {
|
if (hasXfa && !hasXfaDatasetsEntry && !xfaDatasetsRef) {
|
||||||
warn("XFA - Cannot save it");
|
warn("XFA - Cannot save it");
|
||||||
@ -257,33 +257,23 @@ async function updateAcroform({
|
|||||||
dict.set("NeedAppearances", true);
|
dict.set("NeedAppearances", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buffer = [];
|
changes.put(acroFormRef, {
|
||||||
await writeObject(acroFormRef, dict, buffer, xref);
|
data: dict,
|
||||||
|
});
|
||||||
newRefs.push({ ref: acroFormRef, data: buffer.join("") });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateXFA({ xfaData, xfaDatasetsRef, newRefs, xref }) {
|
function updateXFA({ xfaData, xfaDatasetsRef, changes, xref }) {
|
||||||
if (xfaData === null) {
|
if (xfaData === null) {
|
||||||
const datasets = xref.fetchIfRef(xfaDatasetsRef);
|
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;
|
changes.put(xfaDatasetsRef, {
|
||||||
if (encrypt) {
|
data: xfaDataStream,
|
||||||
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 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getXRefTable(xrefInfo, baseOffset, newRefs, newXref, buffer) {
|
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);
|
const newXref = new Dict(null);
|
||||||
newXref.set("Prev", xrefInfo.startXRef);
|
newXref.set("Prev", xrefInfo.startXRef);
|
||||||
const refForXrefTable = xrefInfo.newRef;
|
const refForXrefTable = xrefInfo.newRef;
|
||||||
if (useXrefStream) {
|
if (useXrefStream) {
|
||||||
newRefs.push({ ref: refForXrefTable, data: "" });
|
changes.put(refForXrefTable, { data: "" });
|
||||||
newXref.set("Size", refForXrefTable.num + 1);
|
newXref.set("Size", refForXrefTable.num + 1);
|
||||||
newXref.set("Type", Name.get("XRef"));
|
newXref.set("Type", Name.get("XRef"));
|
||||||
} else {
|
} else {
|
||||||
@ -406,10 +396,24 @@ function getTrailerDict(xrefInfo, newRefs, useXrefStream) {
|
|||||||
return newXref;
|
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({
|
async function incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
xref = null,
|
xref = null,
|
||||||
hasXfa = false,
|
hasXfa = false,
|
||||||
xfaDatasetsRef = null,
|
xfaDatasetsRef = null,
|
||||||
@ -428,19 +432,21 @@ async function incrementalUpdate({
|
|||||||
hasXfaDatasetsEntry,
|
hasXfaDatasetsEntry,
|
||||||
xfaDatasetsRef,
|
xfaDatasetsRef,
|
||||||
needAppearances,
|
needAppearances,
|
||||||
newRefs,
|
changes,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasXfa) {
|
if (hasXfa) {
|
||||||
updateXFA({
|
updateXFA({
|
||||||
xfaData,
|
xfaData,
|
||||||
xfaDatasetsRef,
|
xfaDatasetsRef,
|
||||||
newRefs,
|
changes,
|
||||||
xref,
|
xref,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newXref = getTrailerDict(xrefInfo, changes, useXrefStream);
|
||||||
const buffer = [];
|
const buffer = [];
|
||||||
|
const newRefs = await writeChanges(changes, xref, buffer);
|
||||||
let baseOffset = originalData.length;
|
let baseOffset = originalData.length;
|
||||||
const lastByte = originalData.at(-1);
|
const lastByte = originalData.at(-1);
|
||||||
if (lastByte !== /* \n */ 0x0a && lastByte !== /* \r */ 0x0d) {
|
if (lastByte !== /* \n */ 0x0a && lastByte !== /* \r */ 0x0d) {
|
||||||
@ -449,10 +455,6 @@ async function incrementalUpdate({
|
|||||||
baseOffset += 1;
|
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) {
|
for (const { data } of newRefs) {
|
||||||
if (data !== null) {
|
if (data !== null) {
|
||||||
buffer.push(data);
|
buffer.push(data);
|
||||||
@ -482,4 +484,4 @@ async function incrementalUpdate({
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { incrementalUpdate, writeDict, writeObject };
|
export { incrementalUpdate, writeChanges, writeDict, writeObject };
|
||||||
|
|||||||
@ -47,6 +47,7 @@ import { FlateStream } from "../../src/core/flate_stream.js";
|
|||||||
import { PartialEvaluator } from "../../src/core/evaluator.js";
|
import { PartialEvaluator } from "../../src/core/evaluator.js";
|
||||||
import { StringStream } from "../../src/core/stream.js";
|
import { StringStream } from "../../src/core/stream.js";
|
||||||
import { WorkerTask } from "../../src/core/worker.js";
|
import { WorkerTask } from "../../src/core/worker.js";
|
||||||
|
import { writeChanges } from "../../src/core/writer.js";
|
||||||
|
|
||||||
describe("annotation", function () {
|
describe("annotation", function () {
|
||||||
class PDFManagerMock {
|
class PDFManagerMock {
|
||||||
@ -2120,14 +2121,12 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: "hello world" });
|
annotationStorage.set(annotation.data.id, { value: "hello world" });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -2166,14 +2165,12 @@ describe("annotation", function () {
|
|||||||
value: "hello world",
|
value: "hello world",
|
||||||
rotation: 90,
|
rotation: 90,
|
||||||
});
|
});
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -2210,14 +2207,12 @@ describe("annotation", function () {
|
|||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
const value = "a".repeat(256);
|
const value = "a".repeat(256);
|
||||||
annotationStorage.set(annotation.data.id, { value });
|
annotationStorage.set(annotation.data.id, { value });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -2356,16 +2351,14 @@ describe("annotation", function () {
|
|||||||
annotationStorage.set(annotation.data.id, {
|
annotationStorage.set(annotation.data.id, {
|
||||||
value: "こんにちは世界の",
|
value: "こんにちは世界の",
|
||||||
});
|
});
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
const utf16String =
|
const utf16String =
|
||||||
"\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
"\x30\x53\x30\x93\x30\x6b\x30\x61\x30\x6f\x4e\x16\x75\x4c\x30\x6e";
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -2771,12 +2764,10 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: true });
|
annotationStorage.set(annotation.data.id, { value: true });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const [oldData] = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const [oldData] = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
|
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(oldData.data).toEqual(
|
expect(oldData.data).toEqual(
|
||||||
@ -2788,12 +2779,9 @@ describe("annotation", function () {
|
|||||||
|
|
||||||
annotationStorage.set(annotation.data.id, { value: false });
|
annotationStorage.set(annotation.data.id, { value: false });
|
||||||
|
|
||||||
const data = await annotation.save(
|
changes.clear();
|
||||||
partialEvaluator,
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
task,
|
expect(changes.size).toEqual(0);
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data).toEqual(null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should save rotated checkboxes", async function () {
|
it("should save rotated checkboxes", async function () {
|
||||||
@ -2822,12 +2810,10 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: true, rotation: 180 });
|
annotationStorage.set(annotation.data.id, { value: true, rotation: 180 });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const [oldData] = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const [oldData] = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
|
oldData.data = oldData.data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(oldData.data).toEqual(
|
expect(oldData.data).toEqual(
|
||||||
@ -2839,12 +2825,9 @@ describe("annotation", function () {
|
|||||||
|
|
||||||
annotationStorage.set(annotation.data.id, { value: false });
|
annotationStorage.set(annotation.data.id, { value: false });
|
||||||
|
|
||||||
const data = await annotation.save(
|
changes.clear();
|
||||||
partialEvaluator,
|
await annotation.save(partialEvaluator, task, annotationStorage);
|
||||||
task,
|
expect(changes.size).toEqual(0);
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data).toEqual(null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle radio buttons with a field value", async function () {
|
it("should handle radio buttons with a field value", async function () {
|
||||||
@ -3117,12 +3100,10 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: true });
|
annotationStorage.set(annotation.data.id, { value: true });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
let data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [radioData, parentData] = data;
|
const [radioData, parentData] = data;
|
||||||
radioData.data = radioData.data.replace(/\(D:\d+\)/, "(date)");
|
radioData.data = radioData.data.replace(/\(D:\d+\)/, "(date)");
|
||||||
@ -3140,8 +3121,9 @@ describe("annotation", function () {
|
|||||||
|
|
||||||
annotationStorage.set(annotation.data.id, { value: false });
|
annotationStorage.set(annotation.data.id, { value: false });
|
||||||
|
|
||||||
data = await annotation.save(partialEvaluator, task, annotationStorage);
|
changes.clear();
|
||||||
expect(data).toEqual(null);
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
|
expect(changes.size).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should save radio buttons without a field value", async function () {
|
it("should save radio buttons without a field value", async function () {
|
||||||
@ -3180,12 +3162,10 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: true });
|
annotationStorage.set(annotation.data.id, { value: true });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [radioData, parentData] = data;
|
const [radioData, parentData] = data;
|
||||||
radioData.data = radioData.data.replace(/\(D:\d+\)/, "(date)");
|
radioData.data = radioData.data.replace(/\(D:\d+\)/, "(date)");
|
||||||
@ -3216,13 +3196,10 @@ describe("annotation", function () {
|
|||||||
idFactoryMock
|
idFactoryMock
|
||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
expect(changes.size).toEqual(0);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data).toEqual(null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle push buttons", async function () {
|
it("should handle push buttons", async function () {
|
||||||
@ -3734,14 +3711,12 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: "C", rotation: 270 });
|
annotationStorage.set(annotation.data.id, { value: "C", rotation: 270 });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -3795,14 +3770,12 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: "C" });
|
annotationStorage.set(annotation.data.id, { value: "C" });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -3860,15 +3833,12 @@ describe("annotation", function () {
|
|||||||
);
|
);
|
||||||
const annotationStorage = new Map();
|
const annotationStorage = new Map();
|
||||||
annotationStorage.set(annotation.data.id, { value: ["B", "C"] });
|
annotationStorage.set(annotation.data.id, { value: ["B", "C"] });
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const data = await annotation.save(
|
await annotation.save(partialEvaluator, task, annotationStorage, changes);
|
||||||
partialEvaluator,
|
const data = await writeChanges(changes, xref);
|
||||||
task,
|
|
||||||
annotationStorage
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(data.length).toEqual(2);
|
expect(data.length).toEqual(2);
|
||||||
const [oldData, newData] = data;
|
const [newData, oldData] = data;
|
||||||
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
expect(oldData.ref).toEqual(Ref.get(123, 0));
|
||||||
expect(newData.ref).toEqual(Ref.get(2, 0));
|
expect(newData.ref).toEqual(Ref.get(2, 0));
|
||||||
|
|
||||||
@ -4154,9 +4124,10 @@ describe("annotation", function () {
|
|||||||
|
|
||||||
describe("FreeTextAnnotation", () => {
|
describe("FreeTextAnnotation", () => {
|
||||||
it("should create a new FreeText annotation", async () => {
|
it("should create a new FreeText annotation", async () => {
|
||||||
partialEvaluator.xref = new XRefMock();
|
const xref = (partialEvaluator.xref = new XRefMock());
|
||||||
const task = new WorkerTask("test FreeText creation");
|
const task = new WorkerTask("test FreeText creation");
|
||||||
const data = await AnnotationFactory.saveNewAnnotations(
|
const changes = new RefSetCache();
|
||||||
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
@ -4168,10 +4139,13 @@ describe("annotation", function () {
|
|||||||
color: [0, 0, 0],
|
color: [0, 0, 0],
|
||||||
value: "Hello PDF.js World!",
|
value: "Hello PDF.js World!",
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
null,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
|
||||||
const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
|
const base = data[1].data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(base).toEqual(
|
expect(base).toEqual(
|
||||||
"2 0 obj\n" +
|
"2 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /FreeText /CreationDate (date) " +
|
"<< /Type /Annot /Subtype /FreeText /CreationDate (date) " +
|
||||||
@ -4180,7 +4154,7 @@ describe("annotation", function () {
|
|||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
const font = data.dependencies[0].data;
|
const font = data[0].data;
|
||||||
expect(font).toEqual(
|
expect(font).toEqual(
|
||||||
"1 0 obj\n" +
|
"1 0 obj\n" +
|
||||||
"<< /BaseFont /Helvetica /Type /Font /Subtype /Type1 /Encoding " +
|
"<< /BaseFont /Helvetica /Type /Font /Subtype /Type1 /Encoding " +
|
||||||
@ -4188,7 +4162,7 @@ describe("annotation", function () {
|
|||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
const appearance = data.dependencies[1].data;
|
const appearance = data[2].data;
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"3 0 obj\n" +
|
"3 0 obj\n" +
|
||||||
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
|
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
|
||||||
@ -4265,12 +4239,13 @@ describe("annotation", function () {
|
|||||||
freeTextDict.set("Foo", Name.get("Bar"));
|
freeTextDict.set("Foo", Name.get("Bar"));
|
||||||
|
|
||||||
const freeTextRef = Ref.get(143, 0);
|
const freeTextRef = Ref.get(143, 0);
|
||||||
partialEvaluator.xref = new XRefMock([
|
const xref = (partialEvaluator.xref = new XRefMock([
|
||||||
{ ref: freeTextRef, data: freeTextDict },
|
{ ref: freeTextRef, data: freeTextDict },
|
||||||
]);
|
]));
|
||||||
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const task = new WorkerTask("test FreeText update");
|
const task = new WorkerTask("test FreeText update");
|
||||||
const data = await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
@ -4285,10 +4260,13 @@ describe("annotation", function () {
|
|||||||
ref: freeTextRef,
|
ref: freeTextRef,
|
||||||
oldAnnotation: freeTextDict,
|
oldAnnotation: freeTextDict,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
null,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
|
||||||
const base = data.annotations[0].data.replaceAll(/\(D:\d+\)/g, "(date)");
|
const base = data[2].data.replaceAll(/\(D:\d+\)/g, "(date)");
|
||||||
expect(base).toEqual(
|
expect(base).toEqual(
|
||||||
"143 0 obj\n" +
|
"143 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /FreeText /CreationDate (date) /Foo /Bar /M (date) " +
|
"<< /Type /Annot /Subtype /FreeText /CreationDate (date) /Foo /Bar /M (date) " +
|
||||||
@ -4379,9 +4357,10 @@ describe("annotation", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create a new Ink annotation", async function () {
|
it("should create a new Ink annotation", async function () {
|
||||||
partialEvaluator.xref = new XRefMock();
|
const xref = (partialEvaluator.xref = new XRefMock());
|
||||||
|
const changes = new RefSetCache();
|
||||||
const task = new WorkerTask("test Ink creation");
|
const task = new WorkerTask("test Ink creation");
|
||||||
const data = await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
@ -4408,10 +4387,13 @@ describe("annotation", function () {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
null,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
|
||||||
const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
|
const base = data[0].data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(base).toEqual(
|
expect(base).toEqual(
|
||||||
"1 0 obj\n" +
|
"1 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
|
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
|
||||||
@ -4420,7 +4402,7 @@ describe("annotation", function () {
|
|||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
const appearance = data.dependencies[0].data;
|
const appearance = data[1].data;
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"2 0 obj\n" +
|
"2 0 obj\n" +
|
||||||
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 129>> stream\n" +
|
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 129>> stream\n" +
|
||||||
@ -4440,9 +4422,10 @@ describe("annotation", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create a new Ink annotation with some transparency", async function () {
|
it("should create a new Ink annotation with some transparency", async function () {
|
||||||
partialEvaluator.xref = new XRefMock();
|
const xref = (partialEvaluator.xref = new XRefMock());
|
||||||
|
const changes = new RefSetCache();
|
||||||
const task = new WorkerTask("test Ink creation");
|
const task = new WorkerTask("test Ink creation");
|
||||||
const data = await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
@ -4469,10 +4452,13 @@ describe("annotation", function () {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
null,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
|
||||||
const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
|
const base = data[0].data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(base).toEqual(
|
expect(base).toEqual(
|
||||||
"1 0 obj\n" +
|
"1 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
|
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
|
||||||
@ -4481,7 +4467,7 @@ describe("annotation", function () {
|
|||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
const appearance = data.dependencies[0].data;
|
const appearance = data[1].data;
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"2 0 obj\n" +
|
"2 0 obj\n" +
|
||||||
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 136 /Resources " +
|
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] /Length 136 /Resources " +
|
||||||
@ -4627,9 +4613,10 @@ describe("annotation", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create a new Highlight annotation", async function () {
|
it("should create a new Highlight annotation", async function () {
|
||||||
partialEvaluator.xref = new XRefMock();
|
const xref = (partialEvaluator.xref = new XRefMock());
|
||||||
|
const changes = new RefSetCache();
|
||||||
const task = new WorkerTask("test Highlight creation");
|
const task = new WorkerTask("test Highlight creation");
|
||||||
const data = await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
@ -4645,10 +4632,13 @@ describe("annotation", function () {
|
|||||||
[12, 13, 14, 15],
|
[12, 13, 14, 15],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
null,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
|
||||||
const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
|
const base = data[0].data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(base).toEqual(
|
expect(base).toEqual(
|
||||||
"1 0 obj\n" +
|
"1 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /Highlight /CreationDate (date) /Rect [12 34 56 78] " +
|
"<< /Type /Annot /Subtype /Highlight /CreationDate (date) /Rect [12 34 56 78] " +
|
||||||
@ -4657,7 +4647,7 @@ describe("annotation", function () {
|
|||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
const appearance = data.dependencies[0].data;
|
const appearance = data[1].data;
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"2 0 obj\n" +
|
"2 0 obj\n" +
|
||||||
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
|
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
|
||||||
@ -4717,9 +4707,10 @@ describe("annotation", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create a new free Highlight annotation", async function () {
|
it("should create a new free Highlight annotation", async function () {
|
||||||
partialEvaluator.xref = new XRefMock();
|
const xref = (partialEvaluator.xref = new XRefMock());
|
||||||
|
const changes = new RefSetCache();
|
||||||
const task = new WorkerTask("test free Highlight creation");
|
const task = new WorkerTask("test free Highlight creation");
|
||||||
const data = await AnnotationFactory.saveNewAnnotations(
|
await AnnotationFactory.saveNewAnnotations(
|
||||||
partialEvaluator,
|
partialEvaluator,
|
||||||
task,
|
task,
|
||||||
[
|
[
|
||||||
@ -4749,10 +4740,13 @@ describe("annotation", function () {
|
|||||||
points: [Float32Array.from([16, 17, 18, 19])],
|
points: [Float32Array.from([16, 17, 18, 19])],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
null,
|
||||||
|
changes
|
||||||
);
|
);
|
||||||
|
const data = await writeChanges(changes, xref);
|
||||||
|
|
||||||
const base = data.annotations[0].data.replace(/\(D:\d+\)/, "(date)");
|
const base = data[0].data.replace(/\(D:\d+\)/, "(date)");
|
||||||
expect(base).toEqual(
|
expect(base).toEqual(
|
||||||
"1 0 obj\n" +
|
"1 0 obj\n" +
|
||||||
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
|
"<< /Type /Annot /Subtype /Ink /CreationDate (date) /Rect [12 34 56 78] " +
|
||||||
@ -4761,7 +4755,7 @@ describe("annotation", function () {
|
|||||||
"endobj\n"
|
"endobj\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
const appearance = data.dependencies[0].data;
|
const appearance = data[1].data;
|
||||||
expect(appearance).toEqual(
|
expect(appearance).toEqual(
|
||||||
"2 0 obj\n" +
|
"2 0 obj\n" +
|
||||||
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
|
"<< /FormType 1 /Subtype /Form /Type /XObject /BBox [12 34 56 78] " +
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Dict, Name, Ref } from "../../src/core/primitives.js";
|
import { Dict, Name, Ref, RefSetCache } from "../../src/core/primitives.js";
|
||||||
import { incrementalUpdate, writeDict } from "../../src/core/writer.js";
|
import { incrementalUpdate, writeDict } from "../../src/core/writer.js";
|
||||||
import { bytesToString } from "../../src/shared/util.js";
|
import { bytesToString } from "../../src/shared/util.js";
|
||||||
import { StringStream } from "../../src/core/stream.js";
|
import { StringStream } from "../../src/core/stream.js";
|
||||||
@ -22,10 +22,9 @@ describe("Writer", function () {
|
|||||||
describe("Incremental update", function () {
|
describe("Incremental update", function () {
|
||||||
it("should update a file with new objects", async function () {
|
it("should update a file with new objects", async function () {
|
||||||
const originalData = new Uint8Array();
|
const originalData = new Uint8Array();
|
||||||
const newRefs = [
|
const changes = new RefSetCache();
|
||||||
{ ref: Ref.get(123, 0x2d), data: "abc\n" },
|
changes.put(Ref.get(123, 0x2d), { data: "abc\n" });
|
||||||
{ ref: Ref.get(456, 0x4e), data: "defg\n" },
|
changes.put(Ref.get(456, 0x4e), { data: "defg\n" });
|
||||||
];
|
|
||||||
const xrefInfo = {
|
const xrefInfo = {
|
||||||
newRef: Ref.get(789, 0),
|
newRef: Ref.get(789, 0),
|
||||||
startXRef: 314,
|
startXRef: 314,
|
||||||
@ -40,7 +39,8 @@ describe("Writer", function () {
|
|||||||
let data = await incrementalUpdate({
|
let data = await incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
|
xref: {},
|
||||||
useXrefStream: true,
|
useXrefStream: true,
|
||||||
});
|
});
|
||||||
data = bytesToString(data);
|
data = bytesToString(data);
|
||||||
@ -65,7 +65,8 @@ describe("Writer", function () {
|
|||||||
data = await incrementalUpdate({
|
data = await incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
|
xref: {},
|
||||||
useXrefStream: false,
|
useXrefStream: false,
|
||||||
});
|
});
|
||||||
data = bytesToString(data);
|
data = bytesToString(data);
|
||||||
@ -91,7 +92,8 @@ describe("Writer", function () {
|
|||||||
|
|
||||||
it("should update a file, missing the /ID-entry, with new objects", async function () {
|
it("should update a file, missing the /ID-entry, with new objects", async function () {
|
||||||
const originalData = new Uint8Array();
|
const originalData = new Uint8Array();
|
||||||
const newRefs = [{ ref: Ref.get(123, 0x2d), data: "abc\n" }];
|
const changes = new RefSetCache();
|
||||||
|
changes.put(Ref.get(123, 0x2d), { data: "abc\n" });
|
||||||
const xrefInfo = {
|
const xrefInfo = {
|
||||||
newRef: Ref.get(789, 0),
|
newRef: Ref.get(789, 0),
|
||||||
startXRef: 314,
|
startXRef: 314,
|
||||||
@ -106,7 +108,8 @@ describe("Writer", function () {
|
|||||||
let data = await incrementalUpdate({
|
let data = await incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
|
xref: {},
|
||||||
useXrefStream: true,
|
useXrefStream: true,
|
||||||
});
|
});
|
||||||
data = bytesToString(data);
|
data = bytesToString(data);
|
||||||
@ -185,7 +188,7 @@ describe("Writer", function () {
|
|||||||
describe("XFA", function () {
|
describe("XFA", function () {
|
||||||
it("should update AcroForm when no datasets in XFA array", async function () {
|
it("should update AcroForm when no datasets in XFA array", async function () {
|
||||||
const originalData = new Uint8Array();
|
const originalData = new Uint8Array();
|
||||||
const newRefs = [];
|
const changes = new RefSetCache();
|
||||||
|
|
||||||
const acroForm = new Dict(null);
|
const acroForm = new Dict(null);
|
||||||
acroForm.set("XFA", [
|
acroForm.set("XFA", [
|
||||||
@ -212,7 +215,7 @@ describe("Writer", function () {
|
|||||||
let data = await incrementalUpdate({
|
let data = await incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
hasXfa: true,
|
hasXfa: true,
|
||||||
xfaDatasetsRef,
|
xfaDatasetsRef,
|
||||||
hasXfaDatasetsEntry: false,
|
hasXfaDatasetsEntry: false,
|
||||||
@ -230,8 +233,7 @@ describe("Writer", function () {
|
|||||||
"<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" +
|
"<< /XFA [(preamble) 123 0 R (datasets) 101112 0 R (postamble) 456 0 R]>>\n" +
|
||||||
"endobj\n" +
|
"endobj\n" +
|
||||||
"101112 0 obj\n" +
|
"101112 0 obj\n" +
|
||||||
"<< /Type /EmbeddedFile /Length 20>>\n" +
|
"<< /Type /EmbeddedFile /Length 20>> stream\n" +
|
||||||
"stream\n" +
|
|
||||||
"<hello>world</hello>\n" +
|
"<hello>world</hello>\n" +
|
||||||
"endstream\n" +
|
"endstream\n" +
|
||||||
"endobj\n" +
|
"endobj\n" +
|
||||||
@ -250,10 +252,9 @@ describe("Writer", function () {
|
|||||||
|
|
||||||
it("should update a file with a deleted object", async function () {
|
it("should update a file with a deleted object", async function () {
|
||||||
const originalData = new Uint8Array();
|
const originalData = new Uint8Array();
|
||||||
const newRefs = [
|
const changes = new RefSetCache();
|
||||||
{ ref: Ref.get(123, 0x2d), data: null },
|
changes.put(Ref.get(123, 0x2d), { data: null });
|
||||||
{ ref: Ref.get(456, 0x4e), data: "abc\n" },
|
changes.put(Ref.get(456, 0x4e), { data: "abc\n" });
|
||||||
];
|
|
||||||
const xrefInfo = {
|
const xrefInfo = {
|
||||||
newRef: Ref.get(789, 0),
|
newRef: Ref.get(789, 0),
|
||||||
startXRef: 314,
|
startXRef: 314,
|
||||||
@ -268,7 +269,8 @@ describe("Writer", function () {
|
|||||||
let data = await incrementalUpdate({
|
let data = await incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
|
xref: {},
|
||||||
useXrefStream: true,
|
useXrefStream: true,
|
||||||
});
|
});
|
||||||
data = bytesToString(data);
|
data = bytesToString(data);
|
||||||
@ -292,7 +294,8 @@ describe("Writer", function () {
|
|||||||
data = await incrementalUpdate({
|
data = await incrementalUpdate({
|
||||||
originalData,
|
originalData,
|
||||||
xrefInfo,
|
xrefInfo,
|
||||||
newRefs,
|
changes,
|
||||||
|
xref: {},
|
||||||
useXrefStream: false,
|
useXrefStream: false,
|
||||||
});
|
});
|
||||||
data = bytesToString(data);
|
data = bytesToString(data);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user