Skip to content

Commit c7a18bd

Browse files
authored
Merge pull request #15047 from calixteman/editor_printing
[Editor] Add support for printing newly added Ink annotations
2 parents 8d466f5 + f27c8c4 commit c7a18bd

File tree

6 files changed

+268
-91
lines changed

6 files changed

+268
-91
lines changed

src/core/annotation.js

+61-14
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,27 @@ class AnnotationFactory {
295295
dependencies,
296296
};
297297
}
298+
299+
static async printNewAnnotations(evaluator, task, annotations) {
300+
if (!annotations) {
301+
return null;
302+
}
303+
304+
const xref = evaluator.xref;
305+
const promises = [];
306+
for (const annotation of annotations) {
307+
switch (annotation.annotationType) {
308+
case AnnotationEditorType.FREETEXT:
309+
break;
310+
case AnnotationEditorType.INK:
311+
promises.push(
312+
InkAnnotation.createNewPrintAnnotation(annotation, xref)
313+
);
314+
}
315+
}
316+
317+
return Promise.all(promises);
318+
}
298319
}
299320

300321
function getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) {
@@ -3621,15 +3642,7 @@ class InkAnnotation extends MarkupAnnotation {
36213642
}
36223643
}
36233644

3624-
static async createNewAnnotation(
3625-
xref,
3626-
evaluator,
3627-
task,
3628-
annotation,
3629-
results,
3630-
others
3631-
) {
3632-
const inkRef = xref.getNewRef();
3645+
static createInkDict(annotation, xref, { apRef, ap }) {
36333646
const ink = new Dict(xref);
36343647
ink.set("Type", Name.get("Annot"));
36353648
ink.set("Subtype", Name.get("Ink"));
@@ -3643,6 +3656,19 @@ class InkAnnotation extends MarkupAnnotation {
36433656
ink.set("Border", [0, 0, 0]);
36443657
ink.set("Rotate", 0);
36453658

3659+
const n = new Dict(xref);
3660+
ink.set("AP", n);
3661+
3662+
if (apRef) {
3663+
n.set("N", apRef);
3664+
} else {
3665+
n.set("N", ap);
3666+
}
3667+
3668+
return ink;
3669+
}
3670+
3671+
static createNewAppearanceStream(annotation, xref) {
36463672
const [x1, y1, x2, y2] = annotation.rect;
36473673
const w = x2 - x1;
36483674
const h = y2 - y1;
@@ -3679,18 +3705,29 @@ class InkAnnotation extends MarkupAnnotation {
36793705
const ap = new StringStream(appearance);
36803706
ap.dict = appearanceStreamDict;
36813707

3682-
buffer.length = 0;
3708+
return ap;
3709+
}
3710+
3711+
static async createNewAnnotation(
3712+
xref,
3713+
evaluator,
3714+
task,
3715+
annotation,
3716+
results,
3717+
others
3718+
) {
3719+
const inkRef = xref.getNewRef();
36833720
const apRef = xref.getNewRef();
3721+
const ink = this.createInkDict(annotation, xref, { apRef });
3722+
const ap = this.createNewAppearanceStream(annotation, xref);
3723+
3724+
const buffer = [];
36843725
let transform = xref.encrypt
36853726
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
36863727
: null;
36873728
writeObject(apRef, ap, buffer, transform);
36883729
others.push({ ref: apRef, data: buffer.join("") });
36893730

3690-
const n = new Dict(xref);
3691-
n.set("N", apRef);
3692-
ink.set("AP", n);
3693-
36943731
buffer.length = 0;
36953732
transform = xref.encrypt
36963733
? xref.encrypt.createCipherTransform(inkRef.num, inkRef.gen)
@@ -3699,6 +3736,16 @@ class InkAnnotation extends MarkupAnnotation {
36993736

37003737
results.push({ ref: inkRef, data: buffer.join("") });
37013738
}
3739+
3740+
static async createNewPrintAnnotation(annotation, xref) {
3741+
const ap = this.createNewAppearanceStream(annotation, xref);
3742+
const ink = this.createInkDict(annotation, xref, { ap });
3743+
3744+
return new InkAnnotation({
3745+
dict: ink,
3746+
xref,
3747+
});
3748+
}
37023749
}
37033750

37043751
class HighlightAnnotation extends MarkupAnnotation {

src/core/core_utils.js

+23
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
import {
17+
AnnotationEditorPrefix,
1718
assert,
1819
BaseException,
1920
FontType,
@@ -548,6 +549,27 @@ function numberToString(value) {
548549
return value.toFixed(2);
549550
}
550551

552+
function getNewAnnotationsMap(annotationStorage) {
553+
if (!annotationStorage) {
554+
return null;
555+
}
556+
const newAnnotationsByPage = new Map();
557+
// The concept of page in a XFA is very different, so
558+
// editing is just not implemented.
559+
for (const [key, value] of annotationStorage) {
560+
if (!key.startsWith(AnnotationEditorPrefix)) {
561+
continue;
562+
}
563+
let annotations = newAnnotationsByPage.get(value.pageIndex);
564+
if (!annotations) {
565+
annotations = [];
566+
newAnnotationsByPage.set(value.pageIndex, annotations);
567+
}
568+
annotations.push(value);
569+
}
570+
return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;
571+
}
572+
551573
export {
552574
collectActions,
553575
DocStats,
@@ -556,6 +578,7 @@ export {
556578
getArrayLookupTableFactory,
557579
getInheritableProperty,
558580
getLookupTableFactory,
581+
getNewAnnotationsMap,
559582
isWhiteSpace,
560583
log2,
561584
MissingDataException,

src/core/document.js

+71-48
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
import {
3434
collectActions,
3535
getInheritableProperty,
36+
getNewAnnotationsMap,
3637
isWhiteSpace,
3738
MissingDataException,
3839
validateCSSFont,
@@ -312,6 +313,8 @@ class Page {
312313
{ ref: this.ref, data: buffer.join("") },
313314
...newData.annotations
314315
);
316+
317+
this.xref.resetNewRef();
315318
return objects;
316319
}
317320

@@ -397,6 +400,21 @@ class Page {
397400
options: this.evaluatorOptions,
398401
});
399402

403+
const newAnnotationsByPage = !this.xfaFactory
404+
? getNewAnnotationsMap(annotationStorage)
405+
: null;
406+
407+
let newAnnotationsPromise = Promise.resolve(null);
408+
if (newAnnotationsByPage) {
409+
const newAnnotations = newAnnotationsByPage.get(this.pageIndex);
410+
if (newAnnotations) {
411+
newAnnotationsPromise = AnnotationFactory.printNewAnnotations(
412+
partialEvaluator,
413+
task,
414+
newAnnotations
415+
);
416+
}
417+
}
400418
const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
401419
const pageListPromise = dataPromises.then(([contentStream]) => {
402420
const opList = new OperatorList(intent, sink);
@@ -424,58 +442,63 @@ class Page {
424442

425443
// Fetch the page's annotations and add their operator lists to the
426444
// page's operator list to render them.
427-
return Promise.all([pageListPromise, this._parsedAnnotations]).then(
428-
function ([pageOpList, annotations]) {
445+
return Promise.all([
446+
pageListPromise,
447+
this._parsedAnnotations,
448+
newAnnotationsPromise,
449+
]).then(function ([pageOpList, annotations, newAnnotations]) {
450+
if (newAnnotations) {
451+
annotations = annotations.concat(newAnnotations);
452+
}
453+
if (
454+
annotations.length === 0 ||
455+
intent & RenderingIntentFlag.ANNOTATIONS_DISABLE
456+
) {
457+
pageOpList.flush(true);
458+
return { length: pageOpList.totalLength };
459+
}
460+
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),
461+
intentAny = !!(intent & RenderingIntentFlag.ANY),
462+
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
463+
intentPrint = !!(intent & RenderingIntentFlag.PRINT);
464+
465+
// Collect the operator list promises for the annotations. Each promise
466+
// is resolved with the complete operator list for a single annotation.
467+
const opListPromises = [];
468+
for (const annotation of annotations) {
429469
if (
430-
annotations.length === 0 ||
431-
intent & RenderingIntentFlag.ANNOTATIONS_DISABLE
470+
intentAny ||
471+
(intentDisplay && annotation.mustBeViewed(annotationStorage)) ||
472+
(intentPrint && annotation.mustBePrinted(annotationStorage))
432473
) {
433-
pageOpList.flush(true);
434-
return { length: pageOpList.totalLength };
435-
}
436-
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),
437-
intentAny = !!(intent & RenderingIntentFlag.ANY),
438-
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
439-
intentPrint = !!(intent & RenderingIntentFlag.PRINT);
440-
441-
// Collect the operator list promises for the annotations. Each promise
442-
// is resolved with the complete operator list for a single annotation.
443-
const opListPromises = [];
444-
for (const annotation of annotations) {
445-
if (
446-
intentAny ||
447-
(intentDisplay && annotation.mustBeViewed(annotationStorage)) ||
448-
(intentPrint && annotation.mustBePrinted(annotationStorage))
449-
) {
450-
opListPromises.push(
451-
annotation
452-
.getOperatorList(
453-
partialEvaluator,
454-
task,
455-
intent,
456-
renderForms,
457-
annotationStorage
458-
)
459-
.catch(function (reason) {
460-
warn(
461-
"getOperatorList - ignoring annotation data during " +
462-
`"${task.name}" task: "${reason}".`
463-
);
464-
return null;
465-
})
466-
);
467-
}
474+
opListPromises.push(
475+
annotation
476+
.getOperatorList(
477+
partialEvaluator,
478+
task,
479+
intent,
480+
renderForms,
481+
annotationStorage
482+
)
483+
.catch(function (reason) {
484+
warn(
485+
"getOperatorList - ignoring annotation data during " +
486+
`"${task.name}" task: "${reason}".`
487+
);
488+
return null;
489+
})
490+
);
468491
}
469-
470-
return Promise.all(opListPromises).then(function (opLists) {
471-
for (const opList of opLists) {
472-
pageOpList.addOpList(opList);
473-
}
474-
pageOpList.flush(true);
475-
return { length: pageOpList.totalLength };
476-
});
477492
}
478-
);
493+
494+
return Promise.all(opListPromises).then(function (opLists) {
495+
for (const opList of opLists) {
496+
pageOpList.addOpList(opList);
497+
}
498+
pageOpList.flush(true);
499+
return { length: pageOpList.totalLength };
500+
});
501+
});
479502
}
480503

481504
extractTextContent({

src/core/worker.js

+17-29
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import {
1717
AbortException,
18-
AnnotationEditorPrefix,
1918
arrayByteLength,
2019
arraysToBytes,
2120
createPromiseCapability,
@@ -33,13 +32,13 @@ import {
3332
warn,
3433
} from "../shared/util.js";
3534
import { Dict, Ref } from "./primitives.js";
35+
import { getNewAnnotationsMap, XRefParseException } from "./core_utils.js";
3636
import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js";
3737
import { clearGlobalCaches } from "./cleanup_helper.js";
3838
import { incrementalUpdate } from "./writer.js";
3939
import { isNodeJS } from "../shared/is_node.js";
4040
import { MessageHandler } from "../shared/message_handler.js";
4141
import { PDFWorkerStream } from "./worker_stream.js";
42-
import { XRefParseException } from "./core_utils.js";
4342

4443
class WorkerTask {
4544
constructor(name) {
@@ -558,22 +557,9 @@ class WorkerMessageHandler {
558557
function ({ isPureXfa, numPages, annotationStorage, filename }) {
559558
pdfManager.requestLoadedStream();
560559

561-
const newAnnotationsByPage = new Map();
562-
if (!isPureXfa) {
563-
// The concept of page in a XFA is very different, so
564-
// editing is just not implemented.
565-
for (const [key, value] of annotationStorage) {
566-
if (!key.startsWith(AnnotationEditorPrefix)) {
567-
continue;
568-
}
569-
let annotations = newAnnotationsByPage.get(value.pageIndex);
570-
if (!annotations) {
571-
annotations = [];
572-
newAnnotationsByPage.set(value.pageIndex, annotations);
573-
}
574-
annotations.push(value);
575-
}
576-
}
560+
const newAnnotationsByPage = !isPureXfa
561+
? getNewAnnotationsMap(annotationStorage)
562+
: null;
577563

578564
const promises = [
579565
pdfManager.onLoadedStream(),
@@ -583,17 +569,19 @@ class WorkerMessageHandler {
583569
pdfManager.ensureDoc("startXRef"),
584570
];
585571

586-
for (const [pageIndex, annotations] of newAnnotationsByPage) {
587-
promises.push(
588-
pdfManager.getPage(pageIndex).then(page => {
589-
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
590-
return page
591-
.saveNewAnnotations(handler, task, annotations)
592-
.finally(function () {
593-
finishWorkerTask(task);
594-
});
595-
})
596-
);
572+
if (newAnnotationsByPage) {
573+
for (const [pageIndex, annotations] of newAnnotationsByPage) {
574+
promises.push(
575+
pdfManager.getPage(pageIndex).then(page => {
576+
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
577+
return page
578+
.saveNewAnnotations(handler, task, annotations)
579+
.finally(function () {
580+
finishWorkerTask(task);
581+
});
582+
})
583+
);
584+
}
597585
}
598586

599587
if (isPureXfa) {

0 commit comments

Comments
 (0)