[Editor] Add the possibility to add a popup to an annotation when saving
When saving/printing, only update the properties which are provided and set a default value only when there is no pre-existing one.
This commit is contained in:
parent
e853a8f41a
commit
8fc51dc089
@ -428,7 +428,7 @@ class AnnotationFactory {
|
||||
}
|
||||
|
||||
return {
|
||||
annotations: await Promise.all(promises),
|
||||
annotations: (await Promise.all(promises)).flat(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -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(
|
||||
@ -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 {
|
||||
@ -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)}`;
|
||||
@ -4492,6 +4539,7 @@ class InkAnnotation extends MarkupAnnotation {
|
||||
const {
|
||||
oldAnnotation,
|
||||
color,
|
||||
date,
|
||||
opacity,
|
||||
paths,
|
||||
outlines,
|
||||
@ -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);
|
||||
@ -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]);
|
||||
@ -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]);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -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 () {
|
||||
|
||||
@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user