Skip to content

Commit e348849

Browse files
committed
[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.
1 parent 21ef454 commit e348849

File tree

8 files changed

+268
-144
lines changed

8 files changed

+268
-144
lines changed

src/core/annotation.js

Lines changed: 146 additions & 129 deletions
Large diffs are not rendered by default.

src/core/core_utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ function stringToAsciiOrUTF16BE(str) {
684684
}
685685

686686
function isAscii(str) {
687-
return /^[\x00-\x7F]*$/.test(str);
687+
return !str || /^[\x00-\x7F]*$/.test(str);
688688
}
689689

690690
function stringToUTF16HexString(str) {

src/core/default_appearance.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,11 @@ class FakeUnicodeFont {
267267
get fontDescriptorRef() {
268268
if (!FakeUnicodeFont._fontDescriptorRef) {
269269
const fontDescriptor = new Dict(this.xref);
270-
fontDescriptor.set("Type", Name.get("FontDescriptor"));
270+
fontDescriptor.setName("Type", "FontDescriptor");
271271
fontDescriptor.set("FontName", this.fontName);
272272
fontDescriptor.set("FontFamily", "MyriadPro Regular");
273273
fontDescriptor.set("FontBBox", [0, 0, 0, 0]);
274-
fontDescriptor.set("FontStretch", Name.get("Normal"));
274+
fontDescriptor.setName("FontStretch", "Normal");
275275
fontDescriptor.set("FontWeight", 400);
276276
fontDescriptor.set("ItalicAngle", 0);
277277

@@ -285,9 +285,9 @@ class FakeUnicodeFont {
285285
get descendantFontRef() {
286286
const descendantFont = new Dict(this.xref);
287287
descendantFont.set("BaseFont", this.fontName);
288-
descendantFont.set("Type", Name.get("Font"));
289-
descendantFont.set("Subtype", Name.get("CIDFontType0"));
290-
descendantFont.set("CIDToGIDMap", Name.get("Identity"));
288+
descendantFont.setName("Type", "Font");
289+
descendantFont.setName("Subtype", "CIDFontType0");
290+
descendantFont.setName("CIDToGIDMap", "Identity");
291291
descendantFont.set("FirstChar", this.firstChar);
292292
descendantFont.set("LastChar", this.lastChar);
293293
descendantFont.set("FontDescriptor", this.fontDescriptorRef);
@@ -330,11 +330,11 @@ class FakeUnicodeFont {
330330
get baseFontRef() {
331331
const baseFont = new Dict(this.xref);
332332
baseFont.set("BaseFont", this.fontName);
333-
baseFont.set("Type", Name.get("Font"));
334-
baseFont.set("Subtype", Name.get("Type0"));
335-
baseFont.set("Encoding", Name.get("Identity-H"));
333+
baseFont.setName("Type", "Font");
334+
baseFont.setName("Subtype", "Type0");
335+
baseFont.setName("Encoding", "Identity-H");
336336
baseFont.set("DescendantFonts", [this.descendantFontRef]);
337-
baseFont.set("ToUnicode", Name.get("Identity-H"));
337+
baseFont.setName("ToUnicode", "Identity-H");
338338

339339
return this.xref.getNewPersistentRef(baseFont);
340340
}
@@ -463,7 +463,7 @@ class FakeUnicodeFont {
463463
const r0 = new Dict(this.xref);
464464
r0.set("ca", strokeAlpha);
465465
r0.set("CA", strokeAlpha);
466-
r0.set("Type", Name.get("ExtGState"));
466+
r0.setName("Type", "ExtGState");
467467
extGState.set("R0", r0);
468468
resources.set("ExtGState", extGState);
469469
}
@@ -476,8 +476,8 @@ class FakeUnicodeFont {
476476
const appearance = buffer.join("\n");
477477

478478
const appearanceStreamDict = new Dict(this.xref);
479-
appearanceStreamDict.set("Subtype", Name.get("Form"));
480-
appearanceStreamDict.set("Type", Name.get("XObject"));
479+
appearanceStreamDict.setName("Subtype", "Form");
480+
appearanceStreamDict.setName("Type", "XObject");
481481
appearanceStreamDict.set("BBox", [0, 0, w, h]);
482482
appearanceStreamDict.set("Length", appearance.length);
483483
appearanceStreamDict.set("Resources", resources);

src/core/primitives.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,38 @@ class Dict {
199199
this._map.set(key, value);
200200
}
201201

202+
setIfNotExists(key, value) {
203+
if (!this.has(key)) {
204+
this.set(key, value);
205+
}
206+
}
207+
208+
setNumber(key, value) {
209+
if (typeof value === "number") {
210+
this.set(key, value);
211+
}
212+
}
213+
214+
setArray(key, value) {
215+
if (Array.isArray(value) || ArrayBuffer.isView(value)) {
216+
this.set(key, value);
217+
}
218+
}
219+
220+
setSomething(key, value) {
221+
if (value) {
222+
this.set(key, value);
223+
}
224+
}
225+
226+
setName(key, value) {
227+
if (typeof value === "string") {
228+
this.set(key, Name.get(value));
229+
} else if (value instanceof Name) {
230+
this.set(key, value);
231+
}
232+
}
233+
202234
has(key) {
203235
return this._map.has(key);
204236
}

src/core/writer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ function updateXFA({ xfaData, xfaDatasetsRef, changes, xref }) {
271271
}
272272
const xfaDataStream = new StringStream(xfaData);
273273
xfaDataStream.dict = new Dict(xref);
274-
xfaDataStream.dict.set("Type", Name.get("EmbeddedFile"));
274+
xfaDataStream.dict.setName("Type", "EmbeddedFile");
275275

276276
changes.put(xfaDatasetsRef, {
277277
data: xfaDataStream,
@@ -382,7 +382,7 @@ function getTrailerDict(xrefInfo, changes, useXrefStream) {
382382
if (useXrefStream) {
383383
changes.put(refForXrefTable, { data: "" });
384384
newXref.set("Size", refForXrefTable.num + 1);
385-
newXref.set("Type", Name.get("XRef"));
385+
newXref.setName("Type", "XRef");
386386
} else {
387387
newXref.set("Size", refForXrefTable.num);
388388
}

src/shared/util.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,9 @@ function isArrayEqual(arr1, arr2) {
10921092
}
10931093

10941094
function getModificationDate(date = new Date()) {
1095+
if (!(date instanceof Date)) {
1096+
date = new Date(date);
1097+
}
10951098
const buffer = [
10961099
date.getUTCFullYear().toString(),
10971100
(date.getUTCMonth() + 1).toString().padStart(2, "0"),

test/test_manifest.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12140,5 +12140,26 @@
1214012140
"type": "eq",
1214112141
"forms": true,
1214212142
"link": true
12143+
},
12144+
{
12145+
"id": "highlights-popup",
12146+
"file": "pdfs/highlights.pdf",
12147+
"md5": "55c12c918f3e2253b39b42075cb38205",
12148+
"rounds": 1,
12149+
"type": "eq",
12150+
"lastPage": 1,
12151+
"save": true,
12152+
"annotations": true,
12153+
"annotationStorage": {
12154+
"pdfjs_internal_editor_0": {
12155+
"annotationType": 9,
12156+
"popup": {
12157+
"contents": "Hello PDF.js World"
12158+
},
12159+
"pageIndex": 0,
12160+
"date": "2013-11-12T14:15:16Z",
12161+
"id": "612R"
12162+
}
12163+
}
1214312164
}
1214412165
]

test/unit/annotation_spec.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4971,6 +4971,57 @@ describe("annotation", function () {
49714971
OPS.endAnnotation,
49724972
]);
49734973
});
4974+
4975+
it("should update an existing Highlight annotation", async function () {
4976+
const highlightDict = new Dict();
4977+
highlightDict.set("Type", Name.get("Annot"));
4978+
highlightDict.set("Subtype", Name.get("Highlight"));
4979+
highlightDict.set("Rotate", 0);
4980+
highlightDict.set("CreationDate", "D:20190423");
4981+
4982+
const highlightRef = Ref.get(143, 0);
4983+
const xref = (partialEvaluator.xref = new XRefMock([
4984+
{ ref: highlightRef, data: highlightDict },
4985+
]));
4986+
const changes = new RefSetCache();
4987+
4988+
const task = new WorkerTask("test Highlight update");
4989+
await AnnotationFactory.saveNewAnnotations(
4990+
partialEvaluator,
4991+
task,
4992+
[
4993+
{
4994+
annotationType: AnnotationEditorType.HIGHLIGHT,
4995+
rotation: 90,
4996+
popup: {
4997+
contents: "Hello PDF.js World !",
4998+
},
4999+
id: "143R",
5000+
ref: highlightRef,
5001+
oldAnnotation: highlightDict,
5002+
},
5003+
],
5004+
null,
5005+
changes
5006+
);
5007+
5008+
const data = await writeChanges(changes, xref);
5009+
5010+
const popup = data[0];
5011+
expect(popup.data).toEqual(
5012+
"1 0 obj\n" +
5013+
"<< /Type /Annot /Subtype /Popup /Open false /Parent 143 0 R>>\n" +
5014+
"endobj\n"
5015+
);
5016+
5017+
const base = data[1].data.replaceAll(/\(D:\d+\)/g, "(date)");
5018+
expect(base).toEqual(
5019+
"143 0 obj\n" +
5020+
"<< /Type /Annot /Subtype /Highlight /Rotate 90 /CreationDate (date) /M (date) " +
5021+
"/F 4 /Contents (Hello PDF.js World !) /Popup 1 0 R>>\n" +
5022+
"endobj\n"
5023+
);
5024+
});
49745025
});
49755026

49765027
describe("UnderlineAnnotation", function () {

0 commit comments

Comments
 (0)