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
@ -67,6 +67,35 @@ import { OperatorList } from "./operator_list.js";
|
|||||||
import { XFAFactory } from "./xfa/factory.js";
|
import { XFAFactory } from "./xfa/factory.js";
|
||||||
|
|
||||||
class AnnotationFactory {
|
class AnnotationFactory {
|
||||||
|
static createGlobals(pdfManager) {
|
||||||
|
return Promise.all([
|
||||||
|
pdfManager.ensureCatalog("acroForm"),
|
||||||
|
pdfManager.ensureDoc("xfaDatasets"),
|
||||||
|
pdfManager.ensureCatalog("structTreeRoot"),
|
||||||
|
// Only necessary to prevent the `Catalog.baseUrl`-getter, used
|
||||||
|
// with some Annotations, from throwing and thus breaking parsing:
|
||||||
|
pdfManager.ensureCatalog("baseUrl"),
|
||||||
|
// Only necessary to prevent the `Catalog.attachments`-getter, used
|
||||||
|
// with "GoToE" actions, from throwing and thus breaking parsing:
|
||||||
|
pdfManager.ensureCatalog("attachments"),
|
||||||
|
]).then(
|
||||||
|
([acroForm, xfaDatasets, structTreeRoot, baseUrl, attachments]) => {
|
||||||
|
return {
|
||||||
|
pdfManager,
|
||||||
|
acroForm: acroForm instanceof Dict ? acroForm : Dict.empty,
|
||||||
|
xfaDatasets,
|
||||||
|
structTreeRoot,
|
||||||
|
baseUrl,
|
||||||
|
attachments,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
reason => {
|
||||||
|
warn(`createGlobals: "${reason}".`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an `Annotation` object of the correct type for the given reference
|
* Create an `Annotation` object of the correct type for the given reference
|
||||||
* to an annotation dictionary. This yields a promise that is resolved when
|
* to an annotation dictionary. This yields a promise that is resolved when
|
||||||
@ -74,48 +103,33 @@ class AnnotationFactory {
|
|||||||
*
|
*
|
||||||
* @param {XRef} xref
|
* @param {XRef} xref
|
||||||
* @param {Object} ref
|
* @param {Object} ref
|
||||||
* @param {PDFManager} pdfManager
|
* @params {Object} annotationGlobals
|
||||||
* @param {Object} idFactory
|
* @param {Object} idFactory
|
||||||
* @param {boolean} collectFields
|
* @param {boolean} [collectFields]
|
||||||
* @param {Object} [pageRef]
|
* @param {Object} [pageRef]
|
||||||
* @returns {Promise} A promise that is resolved with an {Annotation}
|
* @returns {Promise} A promise that is resolved with an {Annotation}
|
||||||
* instance.
|
* instance.
|
||||||
*/
|
*/
|
||||||
static create(xref, ref, pdfManager, idFactory, collectFields, pageRef) {
|
static async create(
|
||||||
return Promise.all([
|
xref,
|
||||||
pdfManager.ensureCatalog("acroForm"),
|
ref,
|
||||||
// Only necessary to prevent the `pdfManager.docBaseUrl`-getter, used
|
annotationGlobals,
|
||||||
// with certain Annotations, from throwing and thus breaking parsing:
|
idFactory,
|
||||||
pdfManager.ensureCatalog("baseUrl"),
|
collectFields,
|
||||||
// Only necessary in the `Catalog.parseDestDictionary`-method,
|
pageRef
|
||||||
// when parsing "GoToE" actions:
|
) {
|
||||||
pdfManager.ensureCatalog("attachments"),
|
const pageIndex = collectFields
|
||||||
pdfManager.ensureDoc("xfaDatasets"),
|
? await this._getPageIndex(xref, ref, annotationGlobals.pdfManager)
|
||||||
collectFields ? this._getPageIndex(xref, ref, pdfManager) : -1,
|
: null;
|
||||||
pageRef ? pdfManager.ensureCatalog("structTreeRoot") : null,
|
|
||||||
]).then(
|
return annotationGlobals.pdfManager.ensure(this, "_create", [
|
||||||
([
|
xref,
|
||||||
acroForm,
|
ref,
|
||||||
baseUrl,
|
annotationGlobals,
|
||||||
attachments,
|
idFactory,
|
||||||
xfaDatasets,
|
pageIndex,
|
||||||
pageIndex,
|
pageRef,
|
||||||
structTreeRoot,
|
]);
|
||||||
]) =>
|
|
||||||
pdfManager.ensure(this, "_create", [
|
|
||||||
xref,
|
|
||||||
ref,
|
|
||||||
pdfManager,
|
|
||||||
idFactory,
|
|
||||||
acroForm,
|
|
||||||
attachments,
|
|
||||||
xfaDatasets,
|
|
||||||
collectFields,
|
|
||||||
pageIndex,
|
|
||||||
structTreeRoot,
|
|
||||||
pageRef,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,14 +138,9 @@ class AnnotationFactory {
|
|||||||
static _create(
|
static _create(
|
||||||
xref,
|
xref,
|
||||||
ref,
|
ref,
|
||||||
pdfManager,
|
annotationGlobals,
|
||||||
idFactory,
|
idFactory,
|
||||||
acroForm,
|
pageIndex = null,
|
||||||
attachments = null,
|
|
||||||
xfaDatasets,
|
|
||||||
collectFields,
|
|
||||||
pageIndex = -1,
|
|
||||||
structTreeRoot = null,
|
|
||||||
pageRef = null
|
pageRef = null
|
||||||
) {
|
) {
|
||||||
const dict = xref.fetchIfRef(ref);
|
const dict = xref.fetchIfRef(ref);
|
||||||
@ -139,6 +148,7 @@ class AnnotationFactory {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { acroForm, pdfManager } = annotationGlobals;
|
||||||
const id =
|
const id =
|
||||||
ref instanceof Ref ? ref.toString() : `annot_${idFactory.createObjId()}`;
|
ref instanceof Ref ? ref.toString() : `annot_${idFactory.createObjId()}`;
|
||||||
|
|
||||||
@ -146,8 +156,6 @@ class AnnotationFactory {
|
|||||||
let subtype = dict.get("Subtype");
|
let subtype = dict.get("Subtype");
|
||||||
subtype = subtype instanceof Name ? subtype.name : null;
|
subtype = subtype instanceof Name ? subtype.name : null;
|
||||||
|
|
||||||
const acroFormDict = acroForm instanceof Dict ? acroForm : Dict.empty;
|
|
||||||
|
|
||||||
// Return the right annotation object based on the subtype and field type.
|
// Return the right annotation object based on the subtype and field type.
|
||||||
const parameters = {
|
const parameters = {
|
||||||
xref,
|
xref,
|
||||||
@ -155,16 +163,11 @@ class AnnotationFactory {
|
|||||||
dict,
|
dict,
|
||||||
subtype,
|
subtype,
|
||||||
id,
|
id,
|
||||||
pdfManager,
|
annotationGlobals,
|
||||||
acroForm: acroFormDict,
|
|
||||||
attachments,
|
|
||||||
xfaDatasets,
|
|
||||||
collectFields,
|
|
||||||
needAppearances:
|
needAppearances:
|
||||||
!collectFields && acroFormDict.get("NeedAppearances") === true,
|
pageIndex === null && acroForm.get("NeedAppearances") === true,
|
||||||
pageIndex,
|
pageIndex,
|
||||||
evaluatorOptions: pdfManager.evaluatorOptions,
|
evaluatorOptions: pdfManager.evaluatorOptions,
|
||||||
structTreeRoot,
|
|
||||||
pageRef,
|
pageRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -241,7 +244,7 @@ class AnnotationFactory {
|
|||||||
return new FileAttachmentAnnotation(parameters);
|
return new FileAttachmentAnnotation(parameters);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (!collectFields) {
|
if (pageIndex === null) {
|
||||||
if (!subtype) {
|
if (!subtype) {
|
||||||
warn("Annotation is missing the required /Subtype.");
|
warn("Annotation is missing the required /Subtype.");
|
||||||
} else {
|
} else {
|
||||||
@ -404,6 +407,7 @@ class AnnotationFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async printNewAnnotations(
|
static async printNewAnnotations(
|
||||||
|
annotationGlobals,
|
||||||
evaluator,
|
evaluator,
|
||||||
task,
|
task,
|
||||||
annotations,
|
annotations,
|
||||||
@ -422,18 +426,28 @@ class AnnotationFactory {
|
|||||||
switch (annotation.annotationType) {
|
switch (annotation.annotationType) {
|
||||||
case AnnotationEditorType.FREETEXT:
|
case AnnotationEditorType.FREETEXT:
|
||||||
promises.push(
|
promises.push(
|
||||||
FreeTextAnnotation.createNewPrintAnnotation(xref, annotation, {
|
FreeTextAnnotation.createNewPrintAnnotation(
|
||||||
evaluator,
|
annotationGlobals,
|
||||||
task,
|
xref,
|
||||||
evaluatorOptions: options,
|
annotation,
|
||||||
})
|
{
|
||||||
|
evaluator,
|
||||||
|
task,
|
||||||
|
evaluatorOptions: options,
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorType.INK:
|
case AnnotationEditorType.INK:
|
||||||
promises.push(
|
promises.push(
|
||||||
InkAnnotation.createNewPrintAnnotation(xref, annotation, {
|
InkAnnotation.createNewPrintAnnotation(
|
||||||
evaluatorOptions: options,
|
annotationGlobals,
|
||||||
})
|
xref,
|
||||||
|
annotation,
|
||||||
|
{
|
||||||
|
evaluatorOptions: options,
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AnnotationEditorType.STAMP:
|
case AnnotationEditorType.STAMP:
|
||||||
@ -450,10 +464,15 @@ class AnnotationFactory {
|
|||||||
image.imageStream = image.smaskStream = null;
|
image.imageStream = image.smaskStream = null;
|
||||||
}
|
}
|
||||||
promises.push(
|
promises.push(
|
||||||
StampAnnotation.createNewPrintAnnotation(xref, annotation, {
|
StampAnnotation.createNewPrintAnnotation(
|
||||||
image,
|
annotationGlobals,
|
||||||
evaluatorOptions: options,
|
xref,
|
||||||
})
|
annotation,
|
||||||
|
{
|
||||||
|
image,
|
||||||
|
evaluatorOptions: options,
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -582,7 +601,7 @@ function getTransformMatrix(rect, bbox, matrix) {
|
|||||||
|
|
||||||
class Annotation {
|
class Annotation {
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
const { dict, xref } = params;
|
const { dict, xref, annotationGlobals } = params;
|
||||||
|
|
||||||
this.setTitle(dict.get("T"));
|
this.setTitle(dict.get("T"));
|
||||||
this.setContents(dict.get("Contents"));
|
this.setContents(dict.get("Contents"));
|
||||||
@ -610,11 +629,15 @@ class Annotation {
|
|||||||
const isLocked = !!(this.flags & AnnotationFlag.LOCKED);
|
const isLocked = !!(this.flags & AnnotationFlag.LOCKED);
|
||||||
const isContentLocked = !!(this.flags & AnnotationFlag.LOCKEDCONTENTS);
|
const isContentLocked = !!(this.flags & AnnotationFlag.LOCKEDCONTENTS);
|
||||||
|
|
||||||
if (params.structTreeRoot) {
|
if (annotationGlobals.structTreeRoot) {
|
||||||
let structParent = dict.get("StructParent");
|
let structParent = dict.get("StructParent");
|
||||||
structParent =
|
structParent =
|
||||||
Number.isInteger(structParent) && structParent >= 0 ? structParent : -1;
|
Number.isInteger(structParent) && structParent >= 0 ? structParent : -1;
|
||||||
params.structTreeRoot.addAnnotationIdToPage(params.pageRef, structParent);
|
|
||||||
|
annotationGlobals.structTreeRoot.addAnnotationIdToPage(
|
||||||
|
params.pageRef,
|
||||||
|
structParent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose public properties using a data object.
|
// Expose public properties using a data object.
|
||||||
@ -636,7 +659,7 @@ class Annotation {
|
|||||||
noHTML: isLocked && isContentLocked,
|
noHTML: isLocked && isContentLocked,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (params.collectFields) {
|
if (params.pageIndex !== null) {
|
||||||
// Fields can act as container for other fields and have
|
// Fields can act as container for other fields and have
|
||||||
// some actions even if no Annotation inherit from them.
|
// some actions even if no Annotation inherit from them.
|
||||||
// Those fields can be referenced by CO (calculation order).
|
// Those fields can be referenced by CO (calculation order).
|
||||||
@ -767,9 +790,11 @@ class Annotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDefaultAppearance(params) {
|
setDefaultAppearance(params) {
|
||||||
|
const { dict, annotationGlobals } = params;
|
||||||
|
|
||||||
const defaultAppearance =
|
const defaultAppearance =
|
||||||
getInheritableProperty({ dict: params.dict, key: "DA" }) ||
|
getInheritableProperty({ dict, key: "DA" }) ||
|
||||||
params.acroForm.get("DA");
|
annotationGlobals.acroForm.get("DA");
|
||||||
this._defaultAppearance =
|
this._defaultAppearance =
|
||||||
typeof defaultAppearance === "string" ? defaultAppearance : "";
|
typeof defaultAppearance === "string" ? defaultAppearance : "";
|
||||||
this.data.defaultAppearanceData = parseDefaultAppearance(
|
this.data.defaultAppearanceData = parseDefaultAppearance(
|
||||||
@ -1652,13 +1677,19 @@ class MarkupAnnotation extends Annotation {
|
|||||||
return { ref: annotationRef, data: buffer.join("") };
|
return { ref: annotationRef, data: buffer.join("") };
|
||||||
}
|
}
|
||||||
|
|
||||||
static async createNewPrintAnnotation(xref, annotation, params) {
|
static async createNewPrintAnnotation(
|
||||||
|
annotationGlobals,
|
||||||
|
xref,
|
||||||
|
annotation,
|
||||||
|
params
|
||||||
|
) {
|
||||||
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
const ap = await this.createNewAppearanceStream(annotation, xref, params);
|
||||||
const annotationDict = this.createNewDict(annotation, xref, { ap });
|
const annotationDict = this.createNewDict(annotation, xref, { ap });
|
||||||
|
|
||||||
const newAnnotation = new this.prototype.constructor({
|
const newAnnotation = new this.prototype.constructor({
|
||||||
dict: annotationDict,
|
dict: annotationDict,
|
||||||
xref,
|
xref,
|
||||||
|
annotationGlobals,
|
||||||
evaluatorOptions: params.evaluatorOptions,
|
evaluatorOptions: params.evaluatorOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1674,7 +1705,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
constructor(params) {
|
constructor(params) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
const { dict, xref } = params;
|
const { dict, xref, annotationGlobals } = params;
|
||||||
const data = this.data;
|
const data = this.data;
|
||||||
this._needAppearances = params.needAppearances;
|
this._needAppearances = params.needAppearances;
|
||||||
|
|
||||||
@ -1701,12 +1732,13 @@ class WidgetAnnotation extends Annotation {
|
|||||||
});
|
});
|
||||||
data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);
|
data.defaultFieldValue = this._decodeFormValue(defaultFieldValue);
|
||||||
|
|
||||||
if (fieldValue === undefined && params.xfaDatasets) {
|
if (fieldValue === undefined && annotationGlobals.xfaDatasets) {
|
||||||
// Try to figure out if we have something in the xfa dataset.
|
// Try to figure out if we have something in the xfa dataset.
|
||||||
const path = this._title.str;
|
const path = this._title.str;
|
||||||
if (path) {
|
if (path) {
|
||||||
this._hasValueFromXFA = true;
|
this._hasValueFromXFA = true;
|
||||||
data.fieldValue = fieldValue = params.xfaDatasets.getValue(path);
|
data.fieldValue = fieldValue =
|
||||||
|
annotationGlobals.xfaDatasets.getValue(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1729,7 +1761,7 @@ class WidgetAnnotation extends Annotation {
|
|||||||
data.fieldType = fieldType instanceof Name ? fieldType.name : null;
|
data.fieldType = fieldType instanceof Name ? fieldType.name : null;
|
||||||
|
|
||||||
const localResources = getInheritableProperty({ dict, key: "DR" });
|
const localResources = getInheritableProperty({ dict, key: "DR" });
|
||||||
const acroFormResources = params.acroForm.get("DR");
|
const acroFormResources = annotationGlobals.acroForm.get("DR");
|
||||||
const appearanceResources = this.appearance?.dict.get("Resources");
|
const appearanceResources = this.appearance?.dict.get("Resources");
|
||||||
|
|
||||||
this._fieldResources = {
|
this._fieldResources = {
|
||||||
@ -3268,22 +3300,20 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_processPushButton(params) {
|
_processPushButton(params) {
|
||||||
if (
|
const { dict, annotationGlobals } = params;
|
||||||
!params.dict.has("A") &&
|
|
||||||
!params.dict.has("AA") &&
|
if (!dict.has("A") && !dict.has("AA") && !this.data.alternativeText) {
|
||||||
!this.data.alternativeText
|
|
||||||
) {
|
|
||||||
warn("Push buttons without action dictionaries are not supported");
|
warn("Push buttons without action dictionaries are not supported");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data.isTooltipOnly = !params.dict.has("A") && !params.dict.has("AA");
|
this.data.isTooltipOnly = !dict.has("A") && !dict.has("AA");
|
||||||
|
|
||||||
Catalog.parseDestDictionary({
|
Catalog.parseDestDictionary({
|
||||||
destDict: params.dict,
|
destDict: dict,
|
||||||
resultObj: this.data,
|
resultObj: this.data,
|
||||||
docBaseUrl: params.pdfManager.docBaseUrl,
|
docBaseUrl: annotationGlobals.baseUrl,
|
||||||
docAttachments: params.attachments,
|
docAttachments: annotationGlobals.attachments,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3641,9 +3671,10 @@ class LinkAnnotation extends Annotation {
|
|||||||
constructor(params) {
|
constructor(params) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
|
const { dict, annotationGlobals } = params;
|
||||||
this.data.annotationType = AnnotationType.LINK;
|
this.data.annotationType = AnnotationType.LINK;
|
||||||
|
|
||||||
const quadPoints = getQuadPoints(params.dict, this.rectangle);
|
const quadPoints = getQuadPoints(dict, this.rectangle);
|
||||||
if (quadPoints) {
|
if (quadPoints) {
|
||||||
this.data.quadPoints = quadPoints;
|
this.data.quadPoints = quadPoints;
|
||||||
}
|
}
|
||||||
@ -3652,10 +3683,10 @@ class LinkAnnotation extends Annotation {
|
|||||||
this.data.borderColor ||= this.data.color;
|
this.data.borderColor ||= this.data.color;
|
||||||
|
|
||||||
Catalog.parseDestDictionary({
|
Catalog.parseDestDictionary({
|
||||||
destDict: params.dict,
|
destDict: dict,
|
||||||
resultObj: this.data,
|
resultObj: this.data,
|
||||||
docBaseUrl: params.pdfManager.docBaseUrl,
|
docBaseUrl: annotationGlobals.baseUrl,
|
||||||
docAttachments: params.attachments,
|
docAttachments: annotationGlobals.attachments,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,7 +310,7 @@ class Catalog {
|
|||||||
Catalog.parseDestDictionary({
|
Catalog.parseDestDictionary({
|
||||||
destDict: outlineDict,
|
destDict: outlineDict,
|
||||||
resultObj: data,
|
resultObj: data,
|
||||||
docBaseUrl: this.pdfManager.docBaseUrl,
|
docBaseUrl: this.baseUrl,
|
||||||
docAttachments: this.attachments,
|
docAttachments: this.attachments,
|
||||||
});
|
});
|
||||||
const title = outlineDict.get("Title");
|
const title = outlineDict.get("Title");
|
||||||
@ -1405,7 +1405,7 @@ class Catalog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return shadow(this, "baseUrl", null);
|
return shadow(this, "baseUrl", this.pdfManager.docBaseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -435,9 +435,12 @@ class Page {
|
|||||||
|
|
||||||
let newAnnotationsPromise = Promise.resolve(null);
|
let newAnnotationsPromise = Promise.resolve(null);
|
||||||
if (newAnnotationsByPage) {
|
if (newAnnotationsByPage) {
|
||||||
let imagePromises;
|
|
||||||
const newAnnotations = newAnnotationsByPage.get(this.pageIndex);
|
const newAnnotations = newAnnotationsByPage.get(this.pageIndex);
|
||||||
if (newAnnotations) {
|
if (newAnnotations) {
|
||||||
|
const annotationGlobalsPromise =
|
||||||
|
this.pdfManager.ensureDoc("annotationGlobals");
|
||||||
|
let imagePromises;
|
||||||
|
|
||||||
// An annotation can contain a reference to a bitmap, but this bitmap
|
// An annotation can contain a reference to a bitmap, but this bitmap
|
||||||
// is defined in another annotation. So we need to find this annotation
|
// is defined in another annotation. So we need to find this annotation
|
||||||
// and generate the bitmap.
|
// and generate the bitmap.
|
||||||
@ -476,11 +479,21 @@ class Page {
|
|||||||
|
|
||||||
deletedAnnotations = new RefSet();
|
deletedAnnotations = new RefSet();
|
||||||
this.#replaceIdByRef(newAnnotations, deletedAnnotations, null);
|
this.#replaceIdByRef(newAnnotations, deletedAnnotations, null);
|
||||||
newAnnotationsPromise = AnnotationFactory.printNewAnnotations(
|
|
||||||
partialEvaluator,
|
newAnnotationsPromise = annotationGlobalsPromise.then(
|
||||||
task,
|
annotationGlobals => {
|
||||||
newAnnotations,
|
if (!annotationGlobals) {
|
||||||
imagePromises
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnnotationFactory.printNewAnnotations(
|
||||||
|
annotationGlobals,
|
||||||
|
partialEvaluator,
|
||||||
|
task,
|
||||||
|
newAnnotations,
|
||||||
|
imagePromises
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,7 +685,7 @@ class Page {
|
|||||||
async getAnnotationsData(handler, task, intent) {
|
async getAnnotationsData(handler, task, intent) {
|
||||||
const annotations = await this._parsedAnnotations;
|
const annotations = await this._parsedAnnotations;
|
||||||
if (annotations.length === 0) {
|
if (annotations.length === 0) {
|
||||||
return [];
|
return annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotationsData = [],
|
const annotationsData = [],
|
||||||
@ -732,16 +745,25 @@ class Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get _parsedAnnotations() {
|
get _parsedAnnotations() {
|
||||||
const parsedAnnotations = this.pdfManager
|
const promise = this.pdfManager
|
||||||
.ensure(this, "annotations")
|
.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 = [];
|
const annotationPromises = [];
|
||||||
for (const annotationRef of this.annotations) {
|
for (const annotationRef of annots) {
|
||||||
annotationPromises.push(
|
annotationPromises.push(
|
||||||
AnnotationFactory.create(
|
AnnotationFactory.create(
|
||||||
this.xref,
|
this.xref,
|
||||||
annotationRef,
|
annotationRef,
|
||||||
this.pdfManager,
|
annotationGlobals,
|
||||||
this._localIdFactory,
|
this._localIdFactory,
|
||||||
/* collectFields */ false,
|
/* collectFields */ false,
|
||||||
this.ref
|
this.ref
|
||||||
@ -752,34 +774,28 @@ class Page {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(annotationPromises).then(function (annotations) {
|
const sortedAnnotations = [];
|
||||||
if (annotations.length === 0) {
|
let popupAnnotations;
|
||||||
return annotations;
|
// 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 = [];
|
return 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 shadow(this, "_parsedAnnotations", parsedAnnotations);
|
return shadow(this, "_parsedAnnotations", promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
get jsActions() {
|
get jsActions() {
|
||||||
@ -1704,10 +1720,7 @@ class PDFDocument {
|
|||||||
: clearGlobalCaches();
|
: clearGlobalCaches();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#collectFieldObjects(name, fieldRef, promises, annotationGlobals) {
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_collectFieldObjects(name, fieldRef, promises) {
|
|
||||||
const field = this.xref.fetchIfRef(fieldRef);
|
const field = this.xref.fetchIfRef(fieldRef);
|
||||||
if (field.has("T")) {
|
if (field.has("T")) {
|
||||||
const partName = stringToPDFString(field.get("T"));
|
const partName = stringToPDFString(field.get("T"));
|
||||||
@ -1721,22 +1734,21 @@ class PDFDocument {
|
|||||||
AnnotationFactory.create(
|
AnnotationFactory.create(
|
||||||
this.xref,
|
this.xref,
|
||||||
fieldRef,
|
fieldRef,
|
||||||
this.pdfManager,
|
annotationGlobals,
|
||||||
this._localIdFactory,
|
this._localIdFactory,
|
||||||
/* collectFields */ true,
|
/* collectFields */ true,
|
||||||
/* pageRef */ null
|
/* pageRef */ null
|
||||||
)
|
)
|
||||||
.then(annotation => annotation?.getFieldObject())
|
.then(annotation => annotation?.getFieldObject())
|
||||||
.catch(function (reason) {
|
.catch(function (reason) {
|
||||||
warn(`_collectFieldObjects: "${reason}".`);
|
warn(`#collectFieldObjects: "${reason}".`);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (field.has("Kids")) {
|
if (field.has("Kids")) {
|
||||||
const kids = field.get("Kids");
|
for (const kid of field.get("Kids")) {
|
||||||
for (const kid of kids) {
|
this.#collectFieldObjects(name, kid, promises, annotationGlobals);
|
||||||
this._collectFieldObjects(name, kid, promises);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1746,29 +1758,41 @@ class PDFDocument {
|
|||||||
return shadow(this, "fieldObjects", Promise.resolve(null));
|
return shadow(this, "fieldObjects", Promise.resolve(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
const allFields = Object.create(null);
|
const promise = this.pdfManager
|
||||||
const fieldPromises = new Map();
|
.ensureDoc("annotationGlobals")
|
||||||
for (const fieldRef of this.catalog.acroForm.get("Fields")) {
|
.then(async annotationGlobals => {
|
||||||
this._collectFieldObjects("", fieldRef, fieldPromises);
|
if (!annotationGlobals) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const allPromises = [];
|
const allFields = Object.create(null);
|
||||||
for (const [name, promises] of fieldPromises) {
|
const fieldPromises = new Map();
|
||||||
allPromises.push(
|
for (const fieldRef of this.catalog.acroForm.get("Fields")) {
|
||||||
Promise.all(promises).then(fields => {
|
this.#collectFieldObjects(
|
||||||
fields = fields.filter(field => !!field);
|
"",
|
||||||
if (fields.length > 0) {
|
fieldRef,
|
||||||
allFields[name] = fields;
|
fieldPromises,
|
||||||
}
|
annotationGlobals
|
||||||
})
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return shadow(
|
const allPromises = [];
|
||||||
this,
|
for (const [name, promises] of fieldPromises) {
|
||||||
"fieldObjects",
|
allPromises.push(
|
||||||
Promise.all(allPromises).then(() => allFields)
|
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() {
|
get hasJSActions() {
|
||||||
@ -1818,6 +1842,14 @@ class PDFDocument {
|
|||||||
}
|
}
|
||||||
return shadow(this, "calculationOrderIds", ids);
|
return shadow(this, "calculationOrderIds", ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get annotationGlobals() {
|
||||||
|
return shadow(
|
||||||
|
this,
|
||||||
|
"annotationGlobals",
|
||||||
|
AnnotationFactory.createGlobals(this.pdfManager)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Page, PDFDocument };
|
export { Page, PDFDocument };
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
import {
|
import {
|
||||||
createValidAbsoluteUrl,
|
createValidAbsoluteUrl,
|
||||||
FeatureTest,
|
FeatureTest,
|
||||||
shadow,
|
|
||||||
unreachable,
|
unreachable,
|
||||||
warn,
|
warn,
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
@ -62,8 +61,7 @@ class BasePdfManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get docBaseUrl() {
|
get docBaseUrl() {
|
||||||
const catalog = this.pdfDocument.catalog;
|
return this._docBaseUrl;
|
||||||
return shadow(this, "docBaseUrl", catalog.baseUrl || this._docBaseUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureDoc(prop, args) {
|
ensureDoc(prop, args) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user