Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Editor] Update the freetext annotation dictionary instead of creating a new one when updating an existing freetext #18419

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1711,18 +1711,28 @@ class MarkupAnnotation extends Annotation {
}

static async createNewAnnotation(xref, annotation, dependencies, params) {
const annotationRef = (annotation.ref ||= xref.getNewTemporaryRef());
let oldAnnotation;
if (annotation.ref) {
oldAnnotation = (await xref.fetchIfRefAsync(annotation.ref)).clone();
} else {
annotation.ref = xref.getNewTemporaryRef();
}

const annotationRef = annotation.ref;
const ap = await this.createNewAppearanceStream(annotation, xref, params);
const buffer = [];
let annotationDict;

if (ap) {
const apRef = xref.getNewTemporaryRef();
annotationDict = this.createNewDict(annotation, xref, { apRef });
annotationDict = this.createNewDict(annotation, xref, {
apRef,
oldAnnotation,
});
await writeObject(apRef, ap, buffer, xref);
dependencies.push({ ref: apRef, data: buffer.join("") });
} else {
annotationDict = this.createNewDict(annotation, xref, {});
annotationDict = this.createNewDict(annotation, xref, { oldAnnotation });
}
if (Number.isInteger(annotation.parentTreeId)) {
annotationDict.set("StructParent", annotation.parentTreeId);
Expand Down Expand Up @@ -3826,12 +3836,19 @@ class FreeTextAnnotation extends MarkupAnnotation {
return this._hasAppearance;
}

static createNewDict(annotation, xref, { apRef, ap }) {
static createNewDict(annotation, xref, { apRef, ap, oldAnnotation }) {
const { color, fontSize, rect, rotation, user, value } = annotation;
const freetext = new Dict(xref);
const freetext = oldAnnotation || new Dict(xref);
freetext.set("Type", Name.get("Annot"));
freetext.set("Subtype", Name.get("FreeText"));
freetext.set("CreationDate", `D:${getModificationDate()}`);
if (oldAnnotation) {
freetext.set("M", `D:${getModificationDate()}`);
// TODO: We should try to generate a new RC from the content we've.
// For now we can just remove it to avoid any issues.
freetext.delete("RC");
} else {
freetext.set("CreationDate", `D:${getModificationDate()}`);
}
freetext.set("Rect", rect);
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
freetext.set("DA", da);
Expand Down
4 changes: 4 additions & 0 deletions src/core/primitives.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ class Dict {
}
return dict;
}

delete(key) {
delete this._map[key];
}
}

class Ref {
Expand Down
40 changes: 40 additions & 0 deletions test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4257,6 +4257,46 @@ describe("annotation", function () {
]);
});

it("should update an existing FreeText annotation", async function () {
const freeTextDict = new Dict();
freeTextDict.set("Type", Name.get("Annot"));
freeTextDict.set("Subtype", Name.get("FreeText"));
freeTextDict.set("CreationDate", "D:20190423");
freeTextDict.set("Foo", Name.get("Bar"));

const freeTextRef = Ref.get(143, 0);
partialEvaluator.xref = new XRefMock([
{ ref: freeTextRef, data: freeTextDict },
]);

const task = new WorkerTask("test FreeText update");
const data = await AnnotationFactory.saveNewAnnotations(
partialEvaluator,
task,
[
{
annotationType: AnnotationEditorType.FREETEXT,
rect: [12, 34, 56, 78],
rotation: 0,
fontSize: 10,
color: [0, 0, 0],
value: "Hello PDF.js World !",
id: "143R",
ref: freeTextRef,
},
]
);

const base = data.annotations[0].data.replaceAll(/\(D:\d+\)/g, "(date)");
expect(base).toEqual(
"143 0 obj\n" +
"<< /Type /Annot /Subtype /FreeText /CreationDate (date) /Foo /Bar /M (date) " +
"/Rect [12 34 56 78] /DA (/Helv 10 Tf 0 g) /Contents (Hello PDF.js World !) " +
"/F 4 /Border [0 0 0] /Rotate 0 /AP << /N 2 0 R>>>>\n" +
"endobj\n"
);
});

it("should extract the text from a FreeText annotation", async function () {
partialEvaluator.xref = new XRefMock();
const task = new WorkerTask("test FreeText text extraction");
Expand Down