Skip to content
Open
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
19 changes: 19 additions & 0 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
15 changes: 14 additions & 1 deletion src/display/annotation_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ class AnnotationElement {
data: {
color: data.color,
titleObj: data.titleObj,
subjectObj: data.subjectObj,
modificationDate,
contentsObj,
richText: data.richText,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -2455,6 +2457,8 @@ class PopupElement {

#titleObj = null;

#subjectObj = null;

#updates = null;

#wasVisible = false;
Expand All @@ -2468,6 +2472,7 @@ class PopupElement {
color,
elements,
titleObj,
subjectObj,
modificationDate,
contentsObj,
richText,
Expand All @@ -2479,6 +2484,7 @@ class PopupElement {
}) {
this.#container = container;
this.#titleObj = titleObj;
this.#subjectObj = subjectObj;
this.#contentsObj = contentsObj;
this.#richText = richText;
this.#parent = parent;
Expand Down Expand Up @@ -2671,6 +2677,8 @@ class PopupElement {
opacity,
creationDate,
modificationDate,
titleObj: this.#titleObj,
subjectObj: this.#subjectObj,
};
}

Expand Down Expand Up @@ -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}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some tests for this patch. It could be that there are already some title tests that you can extend with subject parsing, and/or add a reference test with a PDF file that contains an annotation with a subject that we render visually.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Extended the MarkupAnnotation and PopupAnnotation unit tests to verify subject parsing. I also added a new reference test PDF (annotation-popup-subject.pdf) with the /Subj field and registered it in the manifest to visually verify the popup header rendering.

title.dir = this.#subjectObj.dir;
} else {
({ dir: title.dir, str: title.textContent } = this.#titleObj);
}
}
popup.append(header);

Expand Down
Binary file added test/pdfs/annotation-popup-subject.pdf
Binary file not shown.
9 changes: 9 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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]));
Expand Down
16 changes: 15 additions & 1 deletion web/comment_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,8 @@ class CommentPopup {

#text = null;

#title = null;

#time = null;

#prevDragX = 0;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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)
Expand Down