async getOperatorList()

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 };
  }