mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-09 09:45:42 +02:00
Slightly reduce asynchronicity when parsing Annotations
Over time the amount of "document level" data potentially needed during parsing of Annotations have increased a fair bit, which means that we currently need to ensure that a bunch of data is available for each individual Annotation. Given that this data is "constant" for a PDF document we can instead create (and cache) it lazily, only when needed, *before* starting to parse the Annotations on a page. This way the parsing of individual Annotations should become slightly less asynchronous, which really cannot hurt. An additional benefit of these changes is that we can reduce the number of parameters that need to be explicitly passed around in the annotation-code, which helps overall readability in my opinion. One potential drawback of these changes is that the `AnnotationFactory.create` method no longer handles "everything" on its own, however given how few call-sites there are I don't think that's too much of a problem.
This commit is contained in:
parent
3e32d87be7
commit
df9cce39c0
5 changed files with 414 additions and 313 deletions
|
@ -435,9 +435,12 @@ class Page {
|
|||
|
||||
let newAnnotationsPromise = Promise.resolve(null);
|
||||
if (newAnnotationsByPage) {
|
||||
let imagePromises;
|
||||
const newAnnotations = newAnnotationsByPage.get(this.pageIndex);
|
||||
if (newAnnotations) {
|
||||
const annotationGlobalsPromise =
|
||||
this.pdfManager.ensureDoc("annotationGlobals");
|
||||
let imagePromises;
|
||||
|
||||
// An annotation can contain a reference to a bitmap, but this bitmap
|
||||
// is defined in another annotation. So we need to find this annotation
|
||||
// and generate the bitmap.
|
||||
|
@ -476,11 +479,21 @@ class Page {
|
|||
|
||||
deletedAnnotations = new RefSet();
|
||||
this.#replaceIdByRef(newAnnotations, deletedAnnotations, null);
|
||||
newAnnotationsPromise = AnnotationFactory.printNewAnnotations(
|
||||
partialEvaluator,
|
||||
task,
|
||||
newAnnotations,
|
||||
imagePromises
|
||||
|
||||
newAnnotationsPromise = annotationGlobalsPromise.then(
|
||||
annotationGlobals => {
|
||||
if (!annotationGlobals) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return AnnotationFactory.printNewAnnotations(
|
||||
annotationGlobals,
|
||||
partialEvaluator,
|
||||
task,
|
||||
newAnnotations,
|
||||
imagePromises
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -672,7 +685,7 @@ class Page {
|
|||
async getAnnotationsData(handler, task, intent) {
|
||||
const annotations = await this._parsedAnnotations;
|
||||
if (annotations.length === 0) {
|
||||
return [];
|
||||
return annotations;
|
||||
}
|
||||
|
||||
const annotationsData = [],
|
||||
|
@ -732,16 +745,25 @@ class Page {
|
|||
}
|
||||
|
||||
get _parsedAnnotations() {
|
||||
const parsedAnnotations = this.pdfManager
|
||||
const promise = this.pdfManager
|
||||
.ensure(this, "annotations")
|
||||
.then(() => {
|
||||
.then(async annots => {
|
||||
if (annots.length === 0) {
|
||||
return annots;
|
||||
}
|
||||
const annotationGlobals =
|
||||
await this.pdfManager.ensureDoc("annotationGlobals");
|
||||
if (!annotationGlobals) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const annotationPromises = [];
|
||||
for (const annotationRef of this.annotations) {
|
||||
for (const annotationRef of annots) {
|
||||
annotationPromises.push(
|
||||
AnnotationFactory.create(
|
||||
this.xref,
|
||||
annotationRef,
|
||||
this.pdfManager,
|
||||
annotationGlobals,
|
||||
this._localIdFactory,
|
||||
/* collectFields */ false,
|
||||
this.ref
|
||||
|
@ -752,34 +774,28 @@ class Page {
|
|||
);
|
||||
}
|
||||
|
||||
return Promise.all(annotationPromises).then(function (annotations) {
|
||||
if (annotations.length === 0) {
|
||||
return annotations;
|
||||
const sortedAnnotations = [];
|
||||
let popupAnnotations;
|
||||
// Ensure that PopupAnnotations are handled last, since they depend on
|
||||
// their parent Annotation in the display layer; fixes issue 11362.
|
||||
for (const annotation of await Promise.all(annotationPromises)) {
|
||||
if (!annotation) {
|
||||
continue;
|
||||
}
|
||||
if (annotation instanceof PopupAnnotation) {
|
||||
(popupAnnotations ||= []).push(annotation);
|
||||
continue;
|
||||
}
|
||||
sortedAnnotations.push(annotation);
|
||||
}
|
||||
if (popupAnnotations) {
|
||||
sortedAnnotations.push(...popupAnnotations);
|
||||
}
|
||||
|
||||
const sortedAnnotations = [];
|
||||
let popupAnnotations;
|
||||
// Ensure that PopupAnnotations are handled last, since they depend on
|
||||
// their parent Annotation in the display layer; fixes issue 11362.
|
||||
for (const annotation of annotations) {
|
||||
if (!annotation) {
|
||||
continue;
|
||||
}
|
||||
if (annotation instanceof PopupAnnotation) {
|
||||
(popupAnnotations ||= []).push(annotation);
|
||||
continue;
|
||||
}
|
||||
sortedAnnotations.push(annotation);
|
||||
}
|
||||
if (popupAnnotations) {
|
||||
sortedAnnotations.push(...popupAnnotations);
|
||||
}
|
||||
|
||||
return sortedAnnotations;
|
||||
});
|
||||
return sortedAnnotations;
|
||||
});
|
||||
|
||||
return shadow(this, "_parsedAnnotations", parsedAnnotations);
|
||||
return shadow(this, "_parsedAnnotations", promise);
|
||||
}
|
||||
|
||||
get jsActions() {
|
||||
|
@ -1704,10 +1720,7 @@ class PDFDocument {
|
|||
: clearGlobalCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_collectFieldObjects(name, fieldRef, promises) {
|
||||
#collectFieldObjects(name, fieldRef, promises, annotationGlobals) {
|
||||
const field = this.xref.fetchIfRef(fieldRef);
|
||||
if (field.has("T")) {
|
||||
const partName = stringToPDFString(field.get("T"));
|
||||
|
@ -1721,22 +1734,21 @@ class PDFDocument {
|
|||
AnnotationFactory.create(
|
||||
this.xref,
|
||||
fieldRef,
|
||||
this.pdfManager,
|
||||
annotationGlobals,
|
||||
this._localIdFactory,
|
||||
/* collectFields */ true,
|
||||
/* pageRef */ null
|
||||
)
|
||||
.then(annotation => annotation?.getFieldObject())
|
||||
.catch(function (reason) {
|
||||
warn(`_collectFieldObjects: "${reason}".`);
|
||||
warn(`#collectFieldObjects: "${reason}".`);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
if (field.has("Kids")) {
|
||||
const kids = field.get("Kids");
|
||||
for (const kid of kids) {
|
||||
this._collectFieldObjects(name, kid, promises);
|
||||
for (const kid of field.get("Kids")) {
|
||||
this.#collectFieldObjects(name, kid, promises, annotationGlobals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1746,29 +1758,41 @@ class PDFDocument {
|
|||
return shadow(this, "fieldObjects", Promise.resolve(null));
|
||||
}
|
||||
|
||||
const allFields = Object.create(null);
|
||||
const fieldPromises = new Map();
|
||||
for (const fieldRef of this.catalog.acroForm.get("Fields")) {
|
||||
this._collectFieldObjects("", fieldRef, fieldPromises);
|
||||
}
|
||||
const promise = this.pdfManager
|
||||
.ensureDoc("annotationGlobals")
|
||||
.then(async annotationGlobals => {
|
||||
if (!annotationGlobals) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allPromises = [];
|
||||
for (const [name, promises] of fieldPromises) {
|
||||
allPromises.push(
|
||||
Promise.all(promises).then(fields => {
|
||||
fields = fields.filter(field => !!field);
|
||||
if (fields.length > 0) {
|
||||
allFields[name] = fields;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
const allFields = Object.create(null);
|
||||
const fieldPromises = new Map();
|
||||
for (const fieldRef of this.catalog.acroForm.get("Fields")) {
|
||||
this.#collectFieldObjects(
|
||||
"",
|
||||
fieldRef,
|
||||
fieldPromises,
|
||||
annotationGlobals
|
||||
);
|
||||
}
|
||||
|
||||
return shadow(
|
||||
this,
|
||||
"fieldObjects",
|
||||
Promise.all(allPromises).then(() => allFields)
|
||||
);
|
||||
const allPromises = [];
|
||||
for (const [name, promises] of fieldPromises) {
|
||||
allPromises.push(
|
||||
Promise.all(promises).then(fields => {
|
||||
fields = fields.filter(field => !!field);
|
||||
if (fields.length > 0) {
|
||||
allFields[name] = fields;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(allPromises);
|
||||
return allFields;
|
||||
});
|
||||
|
||||
return shadow(this, "fieldObjects", promise);
|
||||
}
|
||||
|
||||
get hasJSActions() {
|
||||
|
@ -1818,6 +1842,14 @@ class PDFDocument {
|
|||
}
|
||||
return shadow(this, "calculationOrderIds", ids);
|
||||
}
|
||||
|
||||
get annotationGlobals() {
|
||||
return shadow(
|
||||
this,
|
||||
"annotationGlobals",
|
||||
AnnotationFactory.createGlobals(this.pdfManager)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { Page, PDFDocument };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue