mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-25 01:28:06 +02:00
[Editor] Add support for printing newly added Ink annotations
This commit is contained in:
parent
8d466f5dac
commit
f27c8c4471
6 changed files with 271 additions and 94 deletions
|
@ -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)) {
|
||||
|
@ -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"));
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
AnnotationEditorPrefix,
|
||||
assert,
|
||||
BaseException,
|
||||
FontType,
|
||||
|
@ -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,
|
||||
|
@ -556,6 +578,7 @@ export {
|
|||
getArrayLookupTableFactory,
|
||||
getInheritableProperty,
|
||||
getLookupTableFactory,
|
||||
getNewAnnotationsMap,
|
||||
isWhiteSpace,
|
||||
log2,
|
||||
MissingDataException,
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
import {
|
||||
collectActions,
|
||||
getInheritableProperty,
|
||||
getNewAnnotationsMap,
|
||||
isWhiteSpace,
|
||||
MissingDataException,
|
||||
validateCSSFont,
|
||||
|
@ -312,6 +313,8 @@ class Page {
|
|||
{ ref: this.ref, data: buffer.join("") },
|
||||
...newData.annotations
|
||||
);
|
||||
|
||||
this.xref.resetNewRef();
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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]) {
|
||||
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 (
|
||||
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;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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([
|
||||
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 (
|
||||
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;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(opListPromises).then(function (opLists) {
|
||||
for (const opList of opLists) {
|
||||
pageOpList.addOpList(opList);
|
||||
}
|
||||
pageOpList.flush(true);
|
||||
return { length: pageOpList.totalLength };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
extractTextContent({
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
import {
|
||||
AbortException,
|
||||
AnnotationEditorPrefix,
|
||||
arrayByteLength,
|
||||
arraysToBytes,
|
||||
createPromiseCapability,
|
||||
|
@ -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) {
|
||||
|
@ -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(),
|
||||
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue