Skip to content
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
93 changes: 78 additions & 15 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ class AnnotationFactory {
}

return {
annotations: await Promise.all(promises),
annotations: (await Promise.all(promises)).flat(),
};
}

Expand Down Expand Up @@ -1798,7 +1798,29 @@ class MarkupAnnotation extends Annotation {
data: annotationDict,
});

return { ref: annotationRef };
const retRef = { ref: annotationRef };
if (annotation.popup) {
const popup = annotation.popup;
if (popup.deleted) {
annotationDict.delete("Popup");
annotationDict.delete("Contents");
annotationDict.delete("RC");
return retRef;
}
const popupRef = (popup.ref ||= xref.getNewTemporaryRef());
popup.parent = annotationRef;
const popupDict = PopupAnnotation.createNewDict(popup, xref);
changes.put(popupRef, { data: popupDict });
annotationDict.setIfDefined(
"Contents",
stringToAsciiOrUTF16BE(popup.contents)
);
annotationDict.set("Popup", popupRef);

return [retRef, { ref: popupRef }];
}

return retRef;
}

static async createNewPrintAnnotation(
Expand Down Expand Up @@ -3880,6 +3902,22 @@ class PopupAnnotation extends Annotation {

this.data.open = !!dict.get("Open");
}

static createNewDict(annotation, xref, _params) {
const { oldAnnotation, rect, parent } = annotation;
const popup = oldAnnotation || new Dict(xref);
popup.setIfNotExists("Type", Name.get("Annot"));
popup.setIfNotExists("Subtype", Name.get("Popup"));
popup.setIfNotExists("Open", false);
popup.setIfArray("Rect", rect);
popup.set("Parent", parent);

return popup;
}

static async createNewAppearanceStream(annotation, xref, params) {
return null;
}
}

class FreeTextAnnotation extends MarkupAnnotation {
Expand Down Expand Up @@ -3947,18 +3985,27 @@ class FreeTextAnnotation extends MarkupAnnotation {
}

static createNewDict(annotation, xref, { apRef, ap }) {
const { color, fontSize, oldAnnotation, rect, rotation, user, value } =
annotation;
const {
color,
date,
fontSize,
oldAnnotation,
rect,
rotation,
user,
value,
} = annotation;
const freetext = oldAnnotation || new Dict(xref);
freetext.setIfNotExists("Type", Name.get("Annot"));
freetext.setIfNotExists("Subtype", Name.get("FreeText"));
freetext.set(
oldAnnotation ? "M" : "CreationDate",
`D:${getModificationDate(date)}`
);
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.setIfArray("Rect", rect);
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
Expand Down Expand Up @@ -4492,6 +4539,7 @@ class InkAnnotation extends MarkupAnnotation {
const {
oldAnnotation,
color,
date,
opacity,
paths,
outlines,
Expand All @@ -4503,7 +4551,10 @@ class InkAnnotation extends MarkupAnnotation {
const ink = oldAnnotation || new Dict(xref);
ink.setIfNotExists("Type", Name.get("Annot"));
ink.setIfNotExists("Subtype", Name.get("Ink"));
ink.set(oldAnnotation ? "M" : "CreationDate", `D:${getModificationDate()}`);
ink.set(
oldAnnotation ? "M" : "CreationDate",
`D:${getModificationDate(date)}`
);
ink.setIfArray("Rect", rect);
ink.setIfArray("InkList", outlines?.points || paths?.points);
ink.setIfNotExists("F", 4);
Expand Down Expand Up @@ -4730,13 +4781,23 @@ class HighlightAnnotation extends MarkupAnnotation {
}

static createNewDict(annotation, xref, { apRef, ap }) {
const { color, oldAnnotation, opacity, rect, rotation, user, quadPoints } =
annotation;
const date = `D:${getModificationDate()}`;
const {
color,
date,
oldAnnotation,
opacity,
rect,
rotation,
user,
quadPoints,
} = annotation;
const highlight = oldAnnotation || new Dict(xref);
highlight.setIfNotExists("Type", Name.get("Annot"));
highlight.setIfNotExists("Subtype", Name.get("Highlight"));
highlight.set(oldAnnotation ? "M" : "CreationDate", date);
highlight.set(
oldAnnotation ? "M" : "CreationDate",
`D:${getModificationDate(date)}`
);
highlight.setIfArray("Rect", rect);
highlight.setIfNotExists("F", 4);
highlight.setIfNotExists("Border", [0, 0, 0]);
Expand Down Expand Up @@ -5046,12 +5107,14 @@ class StampAnnotation extends MarkupAnnotation {
}

static createNewDict(annotation, xref, { apRef, ap }) {
const { oldAnnotation, rect, rotation, user } = annotation;
const date = `D:${getModificationDate(annotation.date)}`;
const { date, oldAnnotation, rect, rotation, user } = annotation;
const stamp = oldAnnotation || new Dict(xref);
stamp.setIfNotExists("Type", Name.get("Annot"));
stamp.setIfNotExists("Subtype", Name.get("Stamp"));
stamp.set(oldAnnotation ? "M" : "CreationDate", date);
stamp.set(
oldAnnotation ? "M" : "CreationDate",
`D:${getModificationDate(date)}`
);
stamp.setIfArray("Rect", rect);
stamp.setIfNotExists("F", 4);
stamp.setIfNotExists("Border", [0, 0, 0]);
Expand Down
6 changes: 6 additions & 0 deletions src/core/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ class Page {
}
continue;
}
if (annotation.popup?.deleted) {
const popupRef = Ref.fromString(annotation.popupRef);
if (popupRef) {
deletedAnnotations.put(popupRef, popupRef);
}
}
existingAnnotations?.put(ref);
annotation.ref = ref;
promises.push(
Expand Down
3 changes: 3 additions & 0 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,9 @@ function isArrayEqual(arr1, arr2) {
}

function getModificationDate(date = new Date()) {
if (!(date instanceof Date)) {
date = new Date(date);
}
const buffer = [
date.getUTCFullYear().toString(),
(date.getUTCMonth() + 1).toString().padStart(2, "0"),
Expand Down
42 changes: 42 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12171,5 +12171,47 @@
"md5": "9fa985242476c642464d94893528e40f",
"rounds": 1,
"type": "eq"
},
{
"id": "highlights-popup",
"file": "pdfs/highlights.pdf",
"md5": "55c12c918f3e2253b39b42075cb38205",
"rounds": 1,
"type": "eq",
"lastPage": 1,
"save": true,
"annotations": true,
"annotationStorage": {
"pdfjs_internal_editor_0": {
"annotationType": 9,
"popup": {
"contents": "Hello PDF.js World"
},
"pageIndex": 0,
"date": "2013-11-12T14:15:16Z",
"id": "612R"
}
}
},
{
"id": "annotation-caret-ink-popup-deleted",
"file": "pdfs/annotation-caret-ink.pdf",
"md5": "6218ca235580d1975474c979e0128c2d",
"rounds": 1,
"type": "eq",
"lastPage": 1,
"save": true,
"annotations": true,
"annotationStorage": {
"pdfjs_internal_editor_0": {
"annotationType": 15,
"popup": {
"deleted": true
},
"pageIndex": 0,
"id": "25R",
"popupRef": "27R"
}
}
}
]
51 changes: 51 additions & 0 deletions test/unit/annotation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4971,6 +4971,57 @@ describe("annotation", function () {
OPS.endAnnotation,
]);
});

it("should update an existing Highlight annotation", async function () {
const highlightDict = new Dict();
highlightDict.set("Type", Name.get("Annot"));
highlightDict.set("Subtype", Name.get("Highlight"));
highlightDict.set("Rotate", 0);
highlightDict.set("CreationDate", "D:20190423");

const highlightRef = Ref.get(143, 0);
const xref = (partialEvaluator.xref = new XRefMock([
{ ref: highlightRef, data: highlightDict },
]));
const changes = new RefSetCache();

const task = new WorkerTask("test Highlight update");
await AnnotationFactory.saveNewAnnotations(
partialEvaluator,
task,
[
{
annotationType: AnnotationEditorType.HIGHLIGHT,
rotation: 90,
popup: {
contents: "Hello PDF.js World !",
},
id: "143R",
ref: highlightRef,
oldAnnotation: highlightDict,
},
],
null,
changes
);

const data = await writeChanges(changes, xref);

const popup = data[0];
expect(popup.data).toEqual(
"1 0 obj\n" +
"<< /Type /Annot /Subtype /Popup /Open false /Parent 143 0 R>>\n" +
"endobj\n"
);

const base = data[1].data.replaceAll(/\(D:\d+\)/g, "(date)");
expect(base).toEqual(
"143 0 obj\n" +
"<< /Type /Annot /Subtype /Highlight /Rotate 90 /CreationDate (date) /M (date) " +
"/F 4 /Contents (Hello PDF.js World !) /Popup 1 0 R>>\n" +
"endobj\n"
);
});
});

describe("UnderlineAnnotation", function () {
Expand Down
1 change: 1 addition & 0 deletions test/unit/util_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ describe("util", function () {
it("should get a correctly formatted date", function () {
const date = new Date(Date.UTC(3141, 5, 9, 2, 6, 53));
expect(getModificationDate(date)).toEqual("31410609020653");
expect(getModificationDate(date.toString())).toEqual("31410609020653");
});
});

Expand Down