> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/annotation/custom-appearances.md).

# Custom PDF annotation appearances

Enhance PDF annotation visuals with custom appearances using WebViewer. Override default rendering with unique PDF objects or pages for a tailored look across all platforms. The Apryse Web SDK streaml

WebViewer supports adding a PDF object or page as an appearance to any annotation type. This allows an annotation to be displayed in a custom way which overrides the default rendering based on the annotation properties. This custom appearance is also compatible with the PDF specification so after downloading the file the annotation will appear identically in other PDF viewers.

The [addCustomAppearance](https://sdk.apryse.com/api/web/Core.Annotations.Annotation.html#addCustomAppearance) function is defined on all annotations and expects a [PDF Document object](https://sdk.apryse.com/api/web/Core.Document.html) to be passed in along with an optional page number or PDF object number. The normal annotation appearance will then be overridden by the PDF document's content when the file is downloaded.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  // options
}, document.getElementById('viewer'))
  .then((instance) => {
    const { Annotations, documentViewer } = instance.Core;

    documentViewer.addEventListener('documentLoaded', async () => {
      const rectangle = new Annotations.RectangleAnnotation();
      rectangle.PageNumber = 1;
      rectangle.X = 10;
      rectangle.Y = 150;
      rectangle.Width = 235;
      rectangle.Height = 200;
      rectangle.FillColor = new Annotations.Color(0, 0, 0);

      // note that if you are adding multiple appearances you should make sure they have unique file names
      const doc = await instance.Core.createDocument('https://pdftron.s3.amazonaws.com/downloads/pl/tiger.pdf', {
        useDownloader: false,
        filename: 'tiger.pdf'
      });
      rectangle.addCustomAppearance(doc, { pageNumber: 1 });

      documentViewer.getAnnotationManager().addAnnotation(rectangle);
      documentViewer.getAnnotationManager().redrawAnnotation(rectangle);
    });
  });
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  // options
}, document.getElementById('viewer'))
  .then((instance) => {
    const { Annotations, docViewer, CoreControls } = instance;

    docViewer.on('documentLoaded', async () => {
      const rectangle = new Annotations.RectangleAnnotation();
      rectangle.PageNumber = 1;
      rectangle.X = 10;
      rectangle.Y = 150;
      rectangle.Width = 235;
      rectangle.Height = 200;
      rectangle.FillColor = new Annotations.Color(0, 0, 0);

      // note that if you are adding multiple appearances you should make sure they have unique file names
      const doc = await CoreControls.createDocument('https://pdftron.s3.amazonaws.com/downloads/pl/tiger.pdf', {
        useDownloader: false,
        filename: 'tiger.pdf'
      });
      rectangle.addCustomAppearance(doc, { pageNumber: 1 });

      docViewer.getAnnotationManager().addAnnotation(rectangle);
      docViewer.getAnnotationManager().redrawAnnotation(rectangle);
    });
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

Then you can see the rectangle has the appearance of the tiger PDF document.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-9ea445b324e7da0ad817aef2d61939b79e8fa094%2F16d9beaccb07e2b344df00d09b83a1262da7b593-300x385.png?alt=media)

## Custom appearances with XFDF

If you download the PDF using [getFileData](/web/open-save-document/save.md#get-document-data-as-a-blob) then the annotation appearances will be saved with the PDF and visible in other PDF viewers.

However if you save your annotations separately from the PDF as XFDF then you'll need to use the [annotManager.setCustomAppearanceHandler](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#setCustomAppearanceHandler) API so that the appearance can be reloaded into WebViewer when you import the XFDF. The XFDF only contains a reference to the appearance name and not the entire contents of the PDF file describing the appearance.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  // options
}, document.getElementById('viewer'))
  .then((instance) => {
    const { annotationManager } = instance.Core;

    annotationManager.setCustomAppearanceHandler(async (filename) => {
      // filename is the name of the appearance

      // this is assuming that you have saved the file referenced by the appearances somewhere on your server with the same filename
      return instance.Core.createDocument(`https://pdftron.s3.amazonaws.com/downloads/pl/${filename}`, { useDownloader: false });
    });

    // later call annotationManager.importAnnotations or annotationManager.importAnnotCommand
  });
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  // options
}, document.getElementById('viewer'))
  .then((instance) => {
    const { annotManager, CoreControls } = instance;

    annotManager.setCustomAppearanceHandler(async (filename) => {
      // filename is the name of the appearance

      // this is assuming that you have saved the file referenced by the appearances somewhere on your server with the same filename
      return CoreControls.createDocument(`https://pdftron.s3.amazonaws.com/downloads/pl/${filename}`, { useDownloader: false });
    });

    // later call annotManager.importAnnotations or annotManager.importAnnotCommand
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Vector appearances

It is possible to create vector quality custom annotations using Apryse's CanvasToPDF library.

To do this, install the `@pdftron/canvas-to-pdf` npm package and import the canvasToPDF function. This function accepts a draw handler containing canvas drawing commands and outputs a blob representing a PDF with vector graphics. Convert this blob into a [PDF Document object](https://sdk.apryse.com/api/web/Core.Document.html) then pass it as a parameter to [addCustomAppearance](https://sdk.apryse.com/api/web/Core.Annotations.Annotation.html#addCustomAppearance) to create a vector appearance.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  // options
}, document.getElementById('viewer')).then((instance) => {
  const { Annotations, annotationManager, documentViewer } = instance.Core;

  const annotWidth = 600;
  const annotHeight = 600;

  documentViewer.addEventListener('documentLoaded', async () => {
    const rectangleAnnot = new Annotations.RectangleAnnotation({
      PageNumber: 1,
      // values are in page coordinates with (0, 0) in the top left
      X: 0,
      Y: 0,
      Width: annotWidth,
      Height: annotHeight,
      Author: annotationManager.getCurrentUser(),
    });

    const draw = (ctx) => {
      for (let i = 0; i < 15; i++) {
        for (let j = 0; j < 15; j++) {
          ctx.strokeStyle = `rgb( 0, ${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)})`;
          ctx.beginPath();
          ctx.arc(25 + j * 40, 25 + i * 40, 15, 0, Math.PI * 2, true);
          ctx.stroke();
        }
      }
    };

    const blob = await canvasToPDF(draw, {
      width: rectangleAnnot.Width,
      height: rectangleAnnot.Height,
    });
    const doc = await instance.Core.createDocument(blob, {
      extension: 'pdf',
    });

    rectangleAnnot.addCustomAppearance(doc, { pageNumber: 1 });

    annotationManager.addAnnotation(rectangleAnnot);
    annotationManager.redrawAnnotation(rectangleAnnot);
  });
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

Then you can verify that the appearance is vector quality.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-0f3851faeeb21e12be4ca0ad1eb6f7d64fde01a8%2F3ff78f58be92584a00ccf74239b16030e9ee8e7e-604x597.png?alt=media)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/annotation/custom-appearances.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
