Skip to content

Commit

Permalink
[Editor] Add support for printing newly added Ink annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
calixteman committed Jun 21, 2022
1 parent 8d466f5 commit f27c8c4
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 91 deletions.
75 changes: 61 additions & 14 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,27 @@ class AnnotationFactory {
dependencies,
};
}

static async printNewAnnotations(evaluator, task, annotations) {
if (!annotations) {
return null;
}

const xref = evaluator.xref;
const promises = [];
for (const annotation of annotations) {
switch (annotation.annotationType) {
case AnnotationEditorType.FREETEXT:
break;
case AnnotationEditorType.INK:
promises.push(
InkAnnotation.createNewPrintAnnotation(annotation, xref)
);
}
}

return Promise.all(promises);
}
}

function getRgbColor(color, defaultColor = new Uint8ClampedArray(3)) {
Expand Down Expand Up @@ -3621,15 +3642,7 @@ class InkAnnotation extends MarkupAnnotation {
}
}

static async createNewAnnotation(
xref,
evaluator,
task,
annotation,
results,
others
) {
const inkRef = xref.getNewRef();
static createInkDict(annotation, xref, { apRef, ap }) {
const ink = new Dict(xref);
ink.set("Type", Name.get("Annot"));
ink.set("Subtype", Name.get("Ink"));
Expand All @@ -3643,6 +3656,19 @@ class InkAnnotation extends MarkupAnnotation {
ink.set("Border", [0, 0, 0]);
ink.set("Rotate", 0);

const n = new Dict(xref);
ink.set("AP", n);

if (apRef) {
n.set("N", apRef);
} else {
n.set("N", ap);
}

return ink;
}

static createNewAppearanceStream(annotation, xref) {
const [x1, y1, x2, y2] = annotation.rect;
const w = x2 - x1;
const h = y2 - y1;
Expand Down Expand Up @@ -3679,18 +3705,29 @@ class InkAnnotation extends MarkupAnnotation {
const ap = new StringStream(appearance);
ap.dict = appearanceStreamDict;

buffer.length = 0;
return ap;
}

static async createNewAnnotation(
xref,
evaluator,
task,
annotation,
results,
others
) {
const inkRef = xref.getNewRef();
const apRef = xref.getNewRef();
const ink = this.createInkDict(annotation, xref, { apRef });
const ap = this.createNewAppearanceStream(annotation, xref);

const buffer = [];
let transform = xref.encrypt
? xref.encrypt.createCipherTransform(apRef.num, apRef.gen)
: null;
writeObject(apRef, ap, buffer, transform);
others.push({ ref: apRef, data: buffer.join("") });

const n = new Dict(xref);
n.set("N", apRef);
ink.set("AP", n);

buffer.length = 0;
transform = xref.encrypt
? xref.encrypt.createCipherTransform(inkRef.num, inkRef.gen)
Expand All @@ -3699,6 +3736,16 @@ class InkAnnotation extends MarkupAnnotation {

results.push({ ref: inkRef, data: buffer.join("") });
}

static async createNewPrintAnnotation(annotation, xref) {
const ap = this.createNewAppearanceStream(annotation, xref);
const ink = this.createInkDict(annotation, xref, { ap });

return new InkAnnotation({
dict: ink,
xref,
});
}
}

class HighlightAnnotation extends MarkupAnnotation {
Expand Down
23 changes: 23 additions & 0 deletions src/core/core_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

import {
AnnotationEditorPrefix,
assert,
BaseException,
FontType,
Expand Down Expand Up @@ -548,6 +549,27 @@ function numberToString(value) {
return value.toFixed(2);
}

function getNewAnnotationsMap(annotationStorage) {
if (!annotationStorage) {
return null;
}
const newAnnotationsByPage = new Map();
// The concept of page in a XFA is very different, so
// editing is just not implemented.
for (const [key, value] of annotationStorage) {
if (!key.startsWith(AnnotationEditorPrefix)) {
continue;
}
let annotations = newAnnotationsByPage.get(value.pageIndex);
if (!annotations) {
annotations = [];
newAnnotationsByPage.set(value.pageIndex, annotations);
}
annotations.push(value);
}
return newAnnotationsByPage.size > 0 ? newAnnotationsByPage : null;
}

export {
collectActions,
DocStats,
Expand All @@ -556,6 +578,7 @@ export {
getArrayLookupTableFactory,
getInheritableProperty,
getLookupTableFactory,
getNewAnnotationsMap,
isWhiteSpace,
log2,
MissingDataException,
Expand Down
119 changes: 71 additions & 48 deletions src/core/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import {
collectActions,
getInheritableProperty,
getNewAnnotationsMap,
isWhiteSpace,
MissingDataException,
validateCSSFont,
Expand Down Expand Up @@ -312,6 +313,8 @@ class Page {
{ ref: this.ref, data: buffer.join("") },
...newData.annotations
);

this.xref.resetNewRef();
return objects;
}

Expand Down Expand Up @@ -397,6 +400,21 @@ class Page {
options: this.evaluatorOptions,
});

const newAnnotationsByPage = !this.xfaFactory
? getNewAnnotationsMap(annotationStorage)
: null;

let newAnnotationsPromise = Promise.resolve(null);
if (newAnnotationsByPage) {
const newAnnotations = newAnnotationsByPage.get(this.pageIndex);
if (newAnnotations) {
newAnnotationsPromise = AnnotationFactory.printNewAnnotations(
partialEvaluator,
task,
newAnnotations
);
}
}
const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
const pageListPromise = dataPromises.then(([contentStream]) => {
const opList = new OperatorList(intent, sink);
Expand Down Expand Up @@ -424,58 +442,63 @@ class Page {

// Fetch the page's annotations and add their operator lists to the
// page's operator list to render them.
return Promise.all([pageListPromise, this._parsedAnnotations]).then(
function ([pageOpList, annotations]) {
return Promise.all([
pageListPromise,
this._parsedAnnotations,
newAnnotationsPromise,
]).then(function ([pageOpList, annotations, newAnnotations]) {
if (newAnnotations) {
annotations = annotations.concat(newAnnotations);
}
if (
annotations.length === 0 ||
intent & RenderingIntentFlag.ANNOTATIONS_DISABLE
) {
pageOpList.flush(true);
return { length: pageOpList.totalLength };
}
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),
intentAny = !!(intent & RenderingIntentFlag.ANY),
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
intentPrint = !!(intent & RenderingIntentFlag.PRINT);

// Collect the operator list promises for the annotations. Each promise
// is resolved with the complete operator list for a single annotation.
const opListPromises = [];
for (const annotation of annotations) {
if (
annotations.length === 0 ||
intent & RenderingIntentFlag.ANNOTATIONS_DISABLE
intentAny ||
(intentDisplay && annotation.mustBeViewed(annotationStorage)) ||
(intentPrint && annotation.mustBePrinted(annotationStorage))
) {
pageOpList.flush(true);
return { length: pageOpList.totalLength };
}
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),
intentAny = !!(intent & RenderingIntentFlag.ANY),
intentDisplay = !!(intent & RenderingIntentFlag.DISPLAY),
intentPrint = !!(intent & RenderingIntentFlag.PRINT);

// Collect the operator list promises for the annotations. Each promise
// is resolved with the complete operator list for a single annotation.
const opListPromises = [];
for (const annotation of annotations) {
if (
intentAny ||
(intentDisplay && annotation.mustBeViewed(annotationStorage)) ||
(intentPrint && annotation.mustBePrinted(annotationStorage))
) {
opListPromises.push(
annotation
.getOperatorList(
partialEvaluator,
task,
intent,
renderForms,
annotationStorage
)
.catch(function (reason) {
warn(
"getOperatorList - ignoring annotation data during " +
`"${task.name}" task: "${reason}".`
);
return null;
})
);
}
opListPromises.push(
annotation
.getOperatorList(
partialEvaluator,
task,
intent,
renderForms,
annotationStorage
)
.catch(function (reason) {
warn(
"getOperatorList - ignoring annotation data during " +
`"${task.name}" task: "${reason}".`
);
return null;
})
);
}

return Promise.all(opListPromises).then(function (opLists) {
for (const opList of opLists) {
pageOpList.addOpList(opList);
}
pageOpList.flush(true);
return { length: pageOpList.totalLength };
});
}
);

return Promise.all(opListPromises).then(function (opLists) {
for (const opList of opLists) {
pageOpList.addOpList(opList);
}
pageOpList.flush(true);
return { length: pageOpList.totalLength };
});
});
}

extractTextContent({
Expand Down
46 changes: 17 additions & 29 deletions src/core/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import {
AbortException,
AnnotationEditorPrefix,
arrayByteLength,
arraysToBytes,
createPromiseCapability,
Expand All @@ -33,13 +32,13 @@ import {
warn,
} from "../shared/util.js";
import { Dict, Ref } from "./primitives.js";
import { getNewAnnotationsMap, XRefParseException } from "./core_utils.js";
import { LocalPdfManager, NetworkPdfManager } from "./pdf_manager.js";
import { clearGlobalCaches } from "./cleanup_helper.js";
import { incrementalUpdate } from "./writer.js";
import { isNodeJS } from "../shared/is_node.js";
import { MessageHandler } from "../shared/message_handler.js";
import { PDFWorkerStream } from "./worker_stream.js";
import { XRefParseException } from "./core_utils.js";

class WorkerTask {
constructor(name) {
Expand Down Expand Up @@ -558,22 +557,9 @@ class WorkerMessageHandler {
function ({ isPureXfa, numPages, annotationStorage, filename }) {
pdfManager.requestLoadedStream();

const newAnnotationsByPage = new Map();
if (!isPureXfa) {
// The concept of page in a XFA is very different, so
// editing is just not implemented.
for (const [key, value] of annotationStorage) {
if (!key.startsWith(AnnotationEditorPrefix)) {
continue;
}
let annotations = newAnnotationsByPage.get(value.pageIndex);
if (!annotations) {
annotations = [];
newAnnotationsByPage.set(value.pageIndex, annotations);
}
annotations.push(value);
}
}
const newAnnotationsByPage = !isPureXfa
? getNewAnnotationsMap(annotationStorage)
: null;

const promises = [
pdfManager.onLoadedStream(),
Expand All @@ -583,17 +569,19 @@ class WorkerMessageHandler {
pdfManager.ensureDoc("startXRef"),
];

for (const [pageIndex, annotations] of newAnnotationsByPage) {
promises.push(
pdfManager.getPage(pageIndex).then(page => {
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
return page
.saveNewAnnotations(handler, task, annotations)
.finally(function () {
finishWorkerTask(task);
});
})
);
if (newAnnotationsByPage) {
for (const [pageIndex, annotations] of newAnnotationsByPage) {
promises.push(
pdfManager.getPage(pageIndex).then(page => {
const task = new WorkerTask(`Save (editor): page ${pageIndex}`);
return page
.saveNewAnnotations(handler, task, annotations)
.finally(function () {
finishWorkerTask(task);
});
})
);
}
}

if (isPureXfa) {
Expand Down
Loading

0 comments on commit f27c8c4

Please sign in to comment.