Merge pull request #19976 from calixteman/write_popup
[Editor] Add the possibility to add a popup to an annotation when saving
This commit is contained in:
commit
b7a0f01f40
@ -428,7 +428,7 @@ class AnnotationFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
annotations: await Promise.all(promises),
|
annotations: (await Promise.all(promises)).flat(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1798,7 +1798,29 @@ class MarkupAnnotation extends Annotation {
|
|||||||
data: annotationDict,
|
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(
|
static async createNewPrintAnnotation(
|
||||||
@ -3880,6 +3902,22 @@ class PopupAnnotation extends Annotation {
|
|||||||
|
|
||||||
this.data.open = !!dict.get("Open");
|
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 {
|
class FreeTextAnnotation extends MarkupAnnotation {
|
||||||
@ -3947,18 +3985,27 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createNewDict(annotation, xref, { apRef, ap }) {
|
static createNewDict(annotation, xref, { apRef, ap }) {
|
||||||
const { color, fontSize, oldAnnotation, rect, rotation, user, value } =
|
const {
|
||||||
annotation;
|
color,
|
||||||
|
date,
|
||||||
|
fontSize,
|
||||||
|
oldAnnotation,
|
||||||
|
rect,
|
||||||
|
rotation,
|
||||||
|
user,
|
||||||
|
value,
|
||||||
|
} = annotation;
|
||||||
const freetext = oldAnnotation || new Dict(xref);
|
const freetext = oldAnnotation || new Dict(xref);
|
||||||
freetext.setIfNotExists("Type", Name.get("Annot"));
|
freetext.setIfNotExists("Type", Name.get("Annot"));
|
||||||
freetext.setIfNotExists("Subtype", Name.get("FreeText"));
|
freetext.setIfNotExists("Subtype", Name.get("FreeText"));
|
||||||
|
freetext.set(
|
||||||
|
oldAnnotation ? "M" : "CreationDate",
|
||||||
|
`D:${getModificationDate(date)}`
|
||||||
|
);
|
||||||
if (oldAnnotation) {
|
if (oldAnnotation) {
|
||||||
freetext.set("M", `D:${getModificationDate()}`);
|
|
||||||
// TODO: We should try to generate a new RC from the content we've.
|
// 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.
|
// For now we can just remove it to avoid any issues.
|
||||||
freetext.delete("RC");
|
freetext.delete("RC");
|
||||||
} else {
|
|
||||||
freetext.set("CreationDate", `D:${getModificationDate()}`);
|
|
||||||
}
|
}
|
||||||
freetext.setIfArray("Rect", rect);
|
freetext.setIfArray("Rect", rect);
|
||||||
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
|
const da = `/Helv ${fontSize} Tf ${getPdfColor(color, /* isFill */ true)}`;
|
||||||
@ -4492,6 +4539,7 @@ class InkAnnotation extends MarkupAnnotation {
|
|||||||
const {
|
const {
|
||||||
oldAnnotation,
|
oldAnnotation,
|
||||||
color,
|
color,
|
||||||
|
date,
|
||||||
opacity,
|
opacity,
|
||||||
paths,
|
paths,
|
||||||
outlines,
|
outlines,
|
||||||
@ -4503,7 +4551,10 @@ class InkAnnotation extends MarkupAnnotation {
|
|||||||
const ink = oldAnnotation || new Dict(xref);
|
const ink = oldAnnotation || new Dict(xref);
|
||||||
ink.setIfNotExists("Type", Name.get("Annot"));
|
ink.setIfNotExists("Type", Name.get("Annot"));
|
||||||
ink.setIfNotExists("Subtype", Name.get("Ink"));
|
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("Rect", rect);
|
||||||
ink.setIfArray("InkList", outlines?.points || paths?.points);
|
ink.setIfArray("InkList", outlines?.points || paths?.points);
|
||||||
ink.setIfNotExists("F", 4);
|
ink.setIfNotExists("F", 4);
|
||||||
@ -4730,13 +4781,23 @@ class HighlightAnnotation extends MarkupAnnotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createNewDict(annotation, xref, { apRef, ap }) {
|
static createNewDict(annotation, xref, { apRef, ap }) {
|
||||||
const { color, oldAnnotation, opacity, rect, rotation, user, quadPoints } =
|
const {
|
||||||
annotation;
|
color,
|
||||||
const date = `D:${getModificationDate()}`;
|
date,
|
||||||
|
oldAnnotation,
|
||||||
|
opacity,
|
||||||
|
rect,
|
||||||
|
rotation,
|
||||||
|
user,
|
||||||
|
quadPoints,
|
||||||
|
} = annotation;
|
||||||
const highlight = oldAnnotation || new Dict(xref);
|
const highlight = oldAnnotation || new Dict(xref);
|
||||||
highlight.setIfNotExists("Type", Name.get("Annot"));
|
highlight.setIfNotExists("Type", Name.get("Annot"));
|
||||||
highlight.setIfNotExists("Subtype", Name.get("Highlight"));
|
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.setIfArray("Rect", rect);
|
||||||
highlight.setIfNotExists("F", 4);
|
highlight.setIfNotExists("F", 4);
|
||||||
highlight.setIfNotExists("Border", [0, 0, 0]);
|
highlight.setIfNotExists("Border", [0, 0, 0]);
|
||||||
@ -5046,12 +5107,14 @@ class StampAnnotation extends MarkupAnnotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static createNewDict(annotation, xref, { apRef, ap }) {
|
static createNewDict(annotation, xref, { apRef, ap }) {
|
||||||
const { oldAnnotation, rect, rotation, user } = annotation;
|
const { date, oldAnnotation, rect, rotation, user } = annotation;
|
||||||
const date = `D:${getModificationDate(annotation.date)}`;
|
|
||||||
const stamp = oldAnnotation || new Dict(xref);
|
const stamp = oldAnnotation || new Dict(xref);
|
||||||
stamp.setIfNotExists("Type", Name.get("Annot"));
|
stamp.setIfNotExists("Type", Name.get("Annot"));
|
||||||
stamp.setIfNotExists("Subtype", Name.get("Stamp"));
|
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.setIfArray("Rect", rect);
|
||||||
stamp.setIfNotExists("F", 4);
|
stamp.setIfNotExists("F", 4);
|
||||||
stamp.setIfNotExists("Border", [0, 0, 0]);
|
stamp.setIfNotExists("Border", [0, 0, 0]);
|
||||||
|
|||||||
@ -310,6 +310,12 @@ class Page {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (annotation.popup?.deleted) {
|
||||||
|
const popupRef = Ref.fromString(annotation.popupRef);
|
||||||
|
if (popupRef) {
|
||||||
|
deletedAnnotations.put(popupRef, popupRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
existingAnnotations?.put(ref);
|
existingAnnotations?.put(ref);
|
||||||
annotation.ref = ref;
|
annotation.ref = ref;
|
||||||
promises.push(
|
promises.push(
|
||||||
|
|||||||
@ -1092,6 +1092,9 @@ function isArrayEqual(arr1, arr2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getModificationDate(date = new Date()) {
|
function getModificationDate(date = new Date()) {
|
||||||
|
if (!(date instanceof Date)) {
|
||||||
|
date = new Date(date);
|
||||||
|
}
|
||||||
const buffer = [
|
const buffer = [
|
||||||
date.getUTCFullYear().toString(),
|
date.getUTCFullYear().toString(),
|
||||||
(date.getUTCMonth() + 1).toString().padStart(2, "0"),
|
(date.getUTCMonth() + 1).toString().padStart(2, "0"),
|
||||||
|
|||||||
@ -12171,5 +12171,47 @@
|
|||||||
"md5": "9fa985242476c642464d94893528e40f",
|
"md5": "9fa985242476c642464d94893528e40f",
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"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,
|
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 () {
|
describe("UnderlineAnnotation", function () {
|
||||||
|
|||||||
@ -247,6 +247,7 @@ describe("util", function () {
|
|||||||
it("should get a correctly formatted date", function () {
|
it("should get a correctly formatted date", function () {
|
||||||
const date = new Date(Date.UTC(3141, 5, 9, 2, 6, 53));
|
const date = new Date(Date.UTC(3141, 5, 9, 2, 6, 53));
|
||||||
expect(getModificationDate(date)).toEqual("31410609020653");
|
expect(getModificationDate(date)).toEqual("31410609020653");
|
||||||
|
expect(getModificationDate(date.toString())).toEqual("31410609020653");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user