From 6711123f68651401af64ea9be9011ade6ffaae57 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Wed, 10 Jul 2024 17:28:26 +0200 Subject: [PATCH] [Editor] Update the freetext annotation dictionary instead of creating a new one when updating an existing freetext --- src/core/annotation.js | 29 ++++++++++++++++++++------ src/core/primitives.js | 4 ++++ test/unit/annotation_spec.js | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index 35b13776d2b63..7ac9a3eed0722 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -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); @@ -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); diff --git a/src/core/primitives.js b/src/core/primitives.js index e1bc4798b65de..a6801935de2fe 100644 --- a/src/core/primitives.js +++ b/src/core/primitives.js @@ -270,6 +270,10 @@ class Dict { } return dict; } + + delete(key) { + delete this._map[key]; + } } class Ref { diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 4b3949ceeda97..8f91754741270 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -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");