in src/core/document.js [430:627]
async getOperatorList({
handler,
sink,
task,
intent,
cacheKey,
annotationStorage = null,
modifiedIds = null,
}) {
const contentStreamPromise = this.getContentStream();
const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST);
const partialEvaluator = this.#createPartialEvaluator(handler);
const newAnnotsByPage = !this.xfaFactory
? getNewAnnotationsMap(annotationStorage)
: null;
const newAnnots = newAnnotsByPage?.get(this.pageIndex);
let newAnnotationsPromise = Promise.resolve(null);
let deletedAnnotations = null;
if (newAnnots) {
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.
const missingBitmaps = new Set();
for (const { bitmapId, bitmap } of newAnnots) {
if (bitmapId && !bitmap && !missingBitmaps.has(bitmapId)) {
missingBitmaps.add(bitmapId);
}
}
const { isOffscreenCanvasSupported } = this.evaluatorOptions;
if (missingBitmaps.size > 0) {
const annotationWithBitmaps = newAnnots.slice();
for (const [key, annotation] of annotationStorage) {
if (!key.startsWith(AnnotationEditorPrefix)) {
continue;
}
if (annotation.bitmap && missingBitmaps.has(annotation.bitmapId)) {
annotationWithBitmaps.push(annotation);
}
}
// The array annotationWithBitmaps cannot be empty: the check above
// makes sure to have at least one annotation containing the bitmap.
imagePromises = AnnotationFactory.generateImages(
annotationWithBitmaps,
this.xref,
isOffscreenCanvasSupported
);
} else {
imagePromises = AnnotationFactory.generateImages(
newAnnots,
this.xref,
isOffscreenCanvasSupported
);
}
deletedAnnotations = new RefSet();
newAnnotationsPromise = Promise.all([
annotationGlobalsPromise,
this.#replaceIdByRef(newAnnots, deletedAnnotations, null),
]).then(([annotationGlobals]) => {
if (!annotationGlobals) {
return null;
}
return AnnotationFactory.printNewAnnotations(
annotationGlobals,
partialEvaluator,
task,
newAnnots,
imagePromises
);
});
}
const pageListPromise = Promise.all([
contentStreamPromise,
resourcesPromise,
]).then(async ([contentStream]) => {
const resources = await this.#getMergedResources(
contentStream.dict,
RESOURCES_KEYS_OPERATOR_LIST
);
const opList = new OperatorList(intent, sink);
handler.send("StartRenderPage", {
transparency: partialEvaluator.hasBlendModes(
resources,
this.nonBlendModesSet
),
pageIndex: this.pageIndex,
cacheKey,
});
await partialEvaluator.getOperatorList({
stream: contentStream,
task,
resources,
operatorList: opList,
});
return opList;
});
// Fetch the page's annotations and add their operator lists to the
// page's operator list to render them.
// eslint-disable-next-line prefer-const
let [pageOpList, annotations, newAnnotations] = await Promise.all([
pageListPromise,
this._parsedAnnotations,
newAnnotationsPromise,
]);
if (newAnnotations) {
// Some annotations can already exist (if it has the refToReplace
// property). In this case, we replace the old annotation by the new one.
annotations = annotations.filter(
a => !(a.ref && deletedAnnotations.has(a.ref))
);
for (let i = 0, ii = newAnnotations.length; i < ii; i++) {
const newAnnotation = newAnnotations[i];
if (newAnnotation.refToReplace) {
const j = annotations.findIndex(
a => a.ref && isRefsEqual(a.ref, newAnnotation.refToReplace)
);
if (j >= 0) {
annotations.splice(j, 1, newAnnotation);
newAnnotations.splice(i--, 1);
ii--;
}
}
}
annotations = annotations.concat(newAnnotations);
}
if (
annotations.length === 0 ||
intent & RenderingIntentFlag.ANNOTATIONS_DISABLE
) {
pageOpList.flush(/* lastChunk = */ true);
return { length: pageOpList.totalLength };
}
const renderForms = !!(intent & RenderingIntentFlag.ANNOTATIONS_FORMS),
isEditing = !!(intent & RenderingIntentFlag.IS_EDITING),
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, renderForms) &&
annotation.mustBeViewedWhenEditing(isEditing, modifiedIds)) ||
(intentPrint && annotation.mustBePrinted(annotationStorage))
) {
opListPromises.push(
annotation
.getOperatorList(partialEvaluator, task, intent, annotationStorage)
.catch(function (reason) {
warn(
"getOperatorList - ignoring annotation data during " +
`"${task.name}" task: "${reason}".`
);
return {
opList: null,
separateForm: false,
separateCanvas: false,
};
})
);
}
}
const opLists = await Promise.all(opListPromises);
let form = false,
canvas = false;
for (const { opList, separateForm, separateCanvas } of opLists) {
pageOpList.addOpList(opList);
form ||= separateForm;
canvas ||= separateCanvas;
}
pageOpList.flush(
/* lastChunk = */ true,
/* separateAnnots = */ { form, canvas }
);
return { length: pageOpList.totalLength };
}