diff --git a/src/core/annotation.js b/src/core/annotation.js index 4962e791a652c..632b795b7d48a 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -922,6 +922,16 @@ class Annotation { this._title = this._parseStringHelper(title); } + /** + * Set the subject. + * + * @param {string} subject - The subject of the annotation (PDF /Subj entry), + * displayed alongside the title in annotation popups. + */ + setSubject(subject) { + this._subject = this._parseStringHelper(subject); + } + /** * Set the contents. * @@ -1657,6 +1667,9 @@ class MarkupAnnotation extends Annotation { this.setTitle(parent.get("T")); this.data.titleObj = this._title; + this.setSubject(parent.get("Subj")); + this.data.subjectObj = this._subject; + this.setContents(parent.get("Contents")); this.data.contentsObj = this._contents; @@ -1686,6 +1699,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; @@ -3987,6 +4003,9 @@ class PopupAnnotation extends Annotation { this.setTitle(parentItem.get("T")); this.data.titleObj = this._title; + this.setSubject(parentItem.get("Subj")); + this.data.subjectObj = this._subject; + this.setContents(parentItem.get("Contents")); this.data.contentsObj = this._contents; diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 59cc9dfcbd092..68031e45a2f58 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -750,6 +750,7 @@ class AnnotationElement { data: { color: data.color, titleObj: data.titleObj, + subjectObj: data.subjectObj, modificationDate, contentsObj, richText: data.richText, @@ -2374,6 +2375,7 @@ class PopupAnnotationElement extends AnnotationElement { container: this.container, color: this.data.color, titleObj: this.data.titleObj, + subjectObj: this.data.subjectObj, modificationDate: this.data.modificationDate || this.data.creationDate, contentsObj: this.data.contentsObj, richText: this.data.richText, @@ -2455,6 +2457,8 @@ class PopupElement { #titleObj = null; + #subjectObj = null; + #updates = null; #wasVisible = false; @@ -2468,6 +2472,7 @@ class PopupElement { color, elements, titleObj, + subjectObj, modificationDate, contentsObj, richText, @@ -2479,6 +2484,7 @@ class PopupElement { }) { this.#container = container; this.#titleObj = titleObj; + this.#subjectObj = subjectObj; this.#contentsObj = contentsObj; this.#richText = richText; this.#parent = parent; @@ -2671,6 +2677,8 @@ class PopupElement { opacity, creationDate, modificationDate, + titleObj: this.#titleObj, + subjectObj: this.#subjectObj, }; } @@ -2783,7 +2791,12 @@ class PopupElement { const title = document.createElement("span"); title.className = "title"; header.append(title); - ({ dir: title.dir, str: title.textContent } = this.#titleObj); + if (this.#subjectObj?.str) { + title.textContent = `${this.#subjectObj.str} \u2014 ${this.#titleObj.str}`; + title.dir = this.#subjectObj.dir; + } else { + ({ dir: title.dir, str: title.textContent } = this.#titleObj); + } } popup.append(header); diff --git a/test/pdfs/annotation-popup-subject.pdf b/test/pdfs/annotation-popup-subject.pdf new file mode 100644 index 0000000000000..0c10c35547b14 Binary files /dev/null and b/test/pdfs/annotation-popup-subject.pdf differ diff --git a/test/test_manifest.json b/test/test_manifest.json index d28f0318019d7..2f3a5a64d8294 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -8235,6 +8235,15 @@ "annotations": true, "about": "Text annotation without a separate Popup annotation" }, + { + "id": "annotation-popup-subject", + "file": "pdfs/annotation-popup-subject.pdf", + "md5": "60eb42164ccf3408d2ba495aa7c9ce9c", + "rounds": 1, + "type": "eq", + "annotations": true, + "about": "Text annotation popup showing /Subj (subject) alongside /T (author) in the header" + }, { "id": "annotation-underline", "file": "pdfs/annotation-underline.pdf", diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index cf364a521bcb0..d7eed51758fb4 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -649,6 +649,7 @@ describe("annotation", function () { annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); annotationDict.set("T", "ParentTitle"); + annotationDict.set("Subj", "ParentSubject"); annotationDict.set("Contents", "ParentText"); annotationDict.set("CreationDate", "D:20180423"); annotationDict.set("M", "D:20190423"); @@ -691,6 +692,7 @@ describe("annotation", function () { expect(data.inReplyTo).toEqual(annotationRef.toString()); expect(data.replyType).toEqual("Group"); expect(data.titleObj).toEqual({ str: "ParentTitle", dir: "ltr" }); + expect(data.subjectObj).toEqual({ str: "ParentSubject", dir: "ltr" }); expect(data.contentsObj).toEqual({ str: "ParentText", dir: "ltr" }); expect(data.creationDate).toEqual("D:20180423"); expect(data.modificationDate).toEqual("D:20190423"); @@ -723,6 +725,7 @@ describe("annotation", function () { replyDict.set("IRT", annotationRef); replyDict.set("RT", Name.get("R")); replyDict.set("T", "ReplyTitle"); + replyDict.set("Subj", "ReplySubject"); replyDict.set("Contents", "ReplyText"); replyDict.set("CreationDate", "D:20180523"); replyDict.set("M", "D:20190523"); @@ -746,6 +749,7 @@ describe("annotation", function () { expect(data.inReplyTo).toEqual(annotationRef.toString()); expect(data.replyType).toEqual("R"); expect(data.titleObj).toEqual({ str: "ReplyTitle", dir: "ltr" }); + expect(data.subjectObj).toEqual({ str: "ReplySubject", dir: "ltr" }); expect(data.contentsObj).toEqual({ str: "ReplyText", dir: "ltr" }); expect(data.creationDate).toEqual("D:20180523"); expect(data.modificationDate).toEqual("D:20190523"); @@ -4142,6 +4146,7 @@ describe("annotation", function () { annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); annotationDict.set("T", "Correct Title"); + annotationDict.set("Subj", "Correct Subject"); annotationDict.set("Contents", "Correct Text"); annotationDict.set("M", "D:20190423"); annotationDict.set("C", [0, 0, 1]); @@ -4184,6 +4189,7 @@ describe("annotation", function () { idFactoryMock ); expect(data.titleObj).toEqual({ str: "Correct Title", dir: "ltr" }); + expect(data.subjectObj).toEqual({ str: "Correct Subject", dir: "ltr" }); expect(data.contentsObj).toEqual({ str: "Correct Text", dir: "ltr" }); expect(data.modificationDate).toEqual("D:20190423"); expect(data.color).toEqual(new Uint8ClampedArray([0, 0, 255])); diff --git a/web/comment_manager.js b/web/comment_manager.js index 9625c9fb90764..2226a4bd3017a 100644 --- a/web/comment_manager.js +++ b/web/comment_manager.js @@ -876,6 +876,8 @@ class CommentPopup { #text = null; + #title = null; + #time = null; #prevDragX = 0; @@ -939,6 +941,8 @@ class CommentPopup { const time = (this.#time = document.createElement("time")); time.className = "commentPopupTime"; + const title = (this.#title = document.createElement("span")); + const buttons = (this.#buttonsContainer = document.createElement("div")); buttons.className = "commentPopupButtons"; const edit = document.createElement("button"); @@ -999,7 +1003,7 @@ class CommentPopup { del.addEventListener("contextmenu", noContextMenu); buttons.append(edit, del); - top.append(time, buttons); + top.append(title, time, buttons); const separator = document.createElement("hr"); @@ -1137,9 +1141,19 @@ class CommentPopup { modificationDate, color, opacity, + titleObj, + subjectObj, } = editor.getData(); container.style.backgroundColor = (color && CommentManager._makeCommentColor(color, opacity)) || ""; + if (titleObj?.str) { + this.#title.textContent = subjectObj?.str + ? `${subjectObj.str} \u2014 ${titleObj.str}` + : titleObj.str; + this.#title.hidden = false; + } else { + this.#title.hidden = true; + } this.#text.replaceChildren(); const html = richText?.str && (!contentsObj?.str || richText.str === contentsObj.str)