diff --git a/src/core/annotation.js b/src/core/annotation.js index 0ae082aead2a6..eccec71b9bff4 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -702,6 +702,7 @@ class Annotation { this.setTitle(dict.get("T")); this.setContents(dict.get("Contents")); + this.setAnnotationName(dict.get("NM")); this.setModificationDate(dict.get("M")); this.setFlags(dict.get("F")); this.setRectangle(dict.getArray("Rect")); @@ -738,6 +739,7 @@ class Annotation { contentsObj: this._contents, hasAppearance: !!this.appearance, id: params.id, + annotationName: this.annotationName, modificationDate: this.modificationDate, oc: this._oc, rect: this.rectangle, @@ -973,6 +975,37 @@ class Annotation { this._contents = this._parseStringHelper(contents); } + /** + * Set the subject. + * + * @public + * @memberof Annotation + * @param {string} subject - A short description of what the annotation is + * about, which viewers show beside its contents + */ + setSubject(subject) { + this._subject = this._parseStringHelper(subject); + } + + /** + * Set the annotation name. + * + * Unlike `id`, which identifies the object in the file, this is the name the + * document itself gives the annotation, and the one other annotations and + * external records refer to it by. + * + * @public + * @memberof Annotation + * @param {string} annotationName - The annotation name, uniquely identifying + * the annotation among those on its page + */ + setAnnotationName(annotationName) { + this.annotationName = + typeof annotationName === "string" + ? stringToPDFString(annotationName) + : null; + } + /** * Set the modification date. * @@ -1732,6 +1765,9 @@ class MarkupAnnotation extends Annotation { this.setContents(parent.get("Contents")); this.data.contentsObj = this._contents; + this.setSubject(parent.get("Subj")); + this.data.subjectObj = this._subject; + if (!parent.has("CreationDate")) { this.data.creationDate = null; } else { @@ -1758,6 +1794,9 @@ class MarkupAnnotation extends Annotation { } else { this.data.titleObj = this._title; + this.setSubject(dict.get("Subj")); + this.data.subjectObj = this._subject; + this.setCreationDate(dict.get("CreationDate")); this.data.creationDate = this.creationDate; diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 6c6c8250c08c0..11fab74a07892 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -675,6 +675,7 @@ describe("annotation", function () { annotationDict.set("Subtype", Name.get("Text")); annotationDict.set("T", "ParentTitle"); annotationDict.set("Contents", "ParentText"); + annotationDict.set("Subj", "ParentSubject"); annotationDict.set("CreationDate", "D:20180423"); annotationDict.set("M", "D:20190423"); annotationDict.set("C", [0, 0, 1]); @@ -694,6 +695,7 @@ describe("annotation", function () { replyDict.set("RT", Name.get("Group")); replyDict.set("T", "ReplyTitle"); replyDict.set("Contents", "ReplyText"); + replyDict.set("Subj", "ReplySubject"); replyDict.set("CreationDate", "D:20180523"); replyDict.set("M", "D:20190523"); replyDict.set("C", [0.4]); @@ -717,12 +719,88 @@ describe("annotation", function () { expect(data.replyType).toEqual("Group"); expect(data.titleObj).toEqual({ str: "ParentTitle", dir: "ltr" }); expect(data.contentsObj).toEqual({ str: "ParentText", dir: "ltr" }); + expect(data.subjectObj).toEqual({ str: "ParentSubject", dir: "ltr" }); expect(data.creationDate).toEqual("D:20180423"); expect(data.modificationDate).toEqual("D:20190423"); expect(data.color).toEqual(new Uint8ClampedArray([0, 0, 255])); expect(data.popupRef).toEqual("820R"); }); + it("should parse the annotation name", async function () { + const annotationDict = new Dict(); + annotationDict.set("Type", Name.get("Annot")); + annotationDict.set("Subtype", Name.get("Text")); + annotationDict.set("NM", "the-name-the-document-gives-it"); + + const annotationRef = Ref.get(1, 0); + const xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]); + + const { data } = await AnnotationFactory.create( + xref, + annotationRef, + annotationGlobalsMock, + idFactoryMock + ); + expect(data.annotationName).toEqual("the-name-the-document-gives-it"); + // The object id is a separate thing, and stays what it was. + expect(data.id).toEqual("1R"); + }); + + it("should handle a missing annotation name", async function () { + const annotationDict = new Dict(); + annotationDict.set("Type", Name.get("Annot")); + annotationDict.set("Subtype", Name.get("Text")); + + const annotationRef = Ref.get(1, 0); + const xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]); + + const { data } = await AnnotationFactory.create( + xref, + annotationRef, + annotationGlobalsMock, + idFactoryMock + ); + expect(data.annotationName).toEqual(null); + }); + + it("should parse the subject", async function () { + const annotationDict = new Dict(); + annotationDict.set("Type", Name.get("Annot")); + annotationDict.set("Subtype", Name.get("Text")); + annotationDict.set("Subj", "What this is about"); + + const annotationRef = Ref.get(1, 0); + const xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]); + + const { data } = await AnnotationFactory.create( + xref, + annotationRef, + annotationGlobalsMock, + idFactoryMock + ); + expect(data.subjectObj).toEqual({ + str: "What this is about", + dir: "ltr", + }); + }); + + it("should handle a missing subject", async function () { + const annotationDict = new Dict(); + annotationDict.set("Type", Name.get("Annot")); + annotationDict.set("Subtype", Name.get("Text")); + + const annotationRef = Ref.get(1, 0); + const xref = new XRefMock([{ ref: annotationRef, data: annotationDict }]); + + const { data } = await AnnotationFactory.create( + xref, + annotationRef, + annotationGlobalsMock, + idFactoryMock + ); + expect(data.subjectObj).toEqual({ str: "", dir: "ltr" }); + }); + it("should parse IRT/RT for a reply type", async function () { const annotationRef = Ref.get(819, 0); const annotationDict = new Dict();