Preserve Attachments Used for Stamp Annotation Templates

When document operations are applied, either by calling PSPDFKit.Instance#applyOperations or using the Document Editor UI, the current instance is reloaded to reflect the new version of the document. In that process, image attachments that aren’t currently used anywhere on the document are destroyed. Thus, if you had created some image annotations and added those as stamp annotation templates, their image attachments will no longer appear.

To avoid this, you can keep a cached copy of the image attachments in memory and recreate the image attachments once the document has changed. The code below is an example where we store the image attachments used on image annotations added for stamp annotation templates whenever the current interaction mode changes to PSPDFKit.InteractionMode#DOCUMENT_EDITOR and restore that copy on document.change events:

let instance = null;

// Object that will contain our attachments.
let attachmentsCache = null;

PSPDFKit.load({
  // Your configuration.
}).then(async (_instance) => {
  instance = _instance;
  const filePaths = ["a.png", "b.png"]; // Path to your images.
  const imageBlobs = await Promise.all(
    filePaths.map((name) =>
      fetch(`/assets/${name}`).then((response) => response.blob())
    )
  );
  const attachments = await Promise.all(
    imageBlobs.map((file) => instance.createAttachment(file))
  );
  const annotations = attachments.map(
    (imageAttachmentId) =>
      new PSPDFKit.Annotations.ImageAnnotation({
        imageAttachmentId,
        contentType: "image/png",
        boundingBox: new PSPDFKit.Geometry.Rect({
          width: 300,
          height: 200,
          top: 0,
          left: 0
        })
      })
  );

  // We add the image annotations created as stamp annotation templates.
  instance.setStampAnnotationTemplates((stampAnnotationTemplates) => [
    ...annotations,
    ...stampAnnotationTemplates
  ]);

  instance.addEventListener("viewState.change", (state) => {
    if (
      state.interactionMode === PSPDFKit.InteractionMode.DOCUMENT_EDITOR
    ) {
      // As soon as the document editor is opened, we cache the
      // current attachments used on stamp annotation templates.
      const attachmentIds = instance.stampAnnotationTemplates
        .filter((annotation) => annotation.imageAttachmentId)
        .map((annotation) => annotation.imageAttachmentId);
      // We need to obtain the underlying blob
      // from the image attachment ID.
      attachmentsCache = Promise.all(
        attachmentIds.map((id) => instance.getAttachment(id))
      );
    }
  });
  instance.addEventListener("document.change", async () => {
    // "document.change" events are invoked after operations are
    // applied to the current instance.
    if (!attachmentsCache) {
      return;
    }
    const attachments = await attachmentsCache;
    await Promise.all(
      attachments.map((attachment) =>
        instance.createAttachment(attachment)
      )
    );
    // All attachments were recreated at this point.
  });
});

If document operations are applied programmatically using PSPDFKit.Instance#applyOperations, you can cache the current image attachments just before calling the method and recreate the attachments once the Promise returned by the former method resolves:

let instance = null;

// Object that will contain our attachments.
let attachmentsCache = null;

PSPDFKit.load({
  // Your configuration.
}).then(async (_instance) => {
  instance = _instance;
  const filePaths = ["a.png", "b.png"]; // Path to your images.
  const imageBlobs = await Promise.all(
    filePaths.map((name) =>
      fetch(`/assets/${name}`).then((response) => response.blob())
    )
  );
  const attachments = await Promise.all(
    imageBlobs.map((file) => instance.createAttachment(file))
  );
  const annotations = attachments.map(
    (imageAttachmentId) =>
      new PSPDFKit.Annotations.ImageAnnotation({
        imageAttachmentId,
        contentType: "image/png",
        boundingBox: new PSPDFKit.Geometry.Rect({
          width: 300,
          height: 200,
          top: 0,
          left: 0
        })
      })
  );

  // We add the image annotations created as stamp annotation templates.
  instance.setStampAnnotationTemplates((stampAnnotationTemplates) => [
    ...annotations,
    ...stampAnnotationTemplates
  ]);
  const attachmentIds = instance.stampAnnotationTemplates
    .filter((annotation) => annotation.imageAttachmentId)
    .map((annotation) => annotation.imageAttachmentId);
  // We need to obtain the underlying blob
  // from the image attachment ID.
  attachmentsCache = await Promise.all(
    attachmentIds.map((id) => instance.getAttachment(id))
  );

  const additionalPDF = await fetch(
    "assets/example.pdf"
  ).then((response) => response.blob());

  // Manually apply an "importDocument" operation.
  await instance.applyOperations([
    {
      type: "importDocument",
      afterPageIndex: instance.totalPageCount - 1,
      document: additionalPDF
    }
  ]);

  await Promise.all(
    attachmentsCache.map((attachment) =>
      instance.createAttachment(attachment)
    )
  );

  // All attachments were recreated at this point.
});

This has been tested with PSPDFKit for Web 2019.5.4.