> 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/annotation-types/customannotation.md).

# Work with Custom Annotations in WebViewer

Create custom annotations with WebViewer to enhance your PDFs. Stamp annotations can be programmatically added and viewed across different viewers. Customize rendering and functionality to suit your n

{% hint style="warning" %}
Custom annotations are not officially part of the PDF specification, but can be programmatically created through WebViewer and viewed in other viewers as [stamp](/web/annotation/annotation-types/stampannotation.md) annotations.
{% endhint %}

Custom annotations in WebViewer allow you to create annotations that will suit your needs and use case. They can have custom rendering and functionality. They can also be preserved in the document to view in other viewers or reloaded in your app without additional conversion. However, they will lose all custom functionality in other viewers since they are custom and only exist in your app.

Check out our [custom annotation guide](/web/annotation/customize/custom-annotations.md) that will guide you through making a custom triangle annotation!

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-a4f17bc9cf375588f62b5202d441e38b08ced41a%2F8e5abc7339b2913438ec2bdf72f3447454fd73fb-225x145.png?alt=media)

## Instantiation

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

```js
WebViewer(
  // ...
).then(function(instance) {
  const { Annotations } = instance.Core;

  class TriangleAnnotation extends Annotations.CustomAnnotation {
    constructor() {
      super('triangle'); // Provide the custom XFDF element name. You can provide an object initializer as a second parameter.
      this.Subject = 'Triangle';
    }
  }

  // This is necessary to set the elementName before instantiation
  TriangleAnnotation.prototype.elementName = 'triangle';
});
```

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

[CustomAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.CustomAnnotation.html)

{% hint style="warning" %}
This annotation type is only supported in WebViewer 7.3+.
{% endhint %}

## XFDF

Element name: `stamp` or varies

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

```sh
<stamp page="0" rect="249.460,610.030,340.110,688.120" color="#000000" flags="print" name="12a17eba-e0a2-0baf-9fe5-5fa1e314e485" title="Guest" subject="Triangle" date="D:20220729130723-07'00'" creationdate="D:20220729130718-07'00'" vertices="294.785,688.12;340.11,610.03;249.46,610.03">
  <trn-custom-data bytes="{"trn-annot-type":"triangle","trn-annot-data":"{}"}"/>
  <imagedata>...</imagedata>
</stamp>
```

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

{% hint style="info" %}
If you would like to understand why this the type is **stamp**, then refer to the the [Other Notes](#other-notes) section.
{% endhint %}

## Required properties

### PageNumber

Gets or sets the page number of a document that the annotation appears on.

## Notable properties

For the full list of properties, please visit the annotation's [API docs](https://sdk.apryse.com/api/web/Core.Annotations.CustomAnnotation.html).

### \[static] SerializationTypes

A constant of custom annotation serialization method types to use with [`SerializationType`](https://sdk.apryse.com/api/web/Core.Annotations.CustomAnnotation.html#.SerializationType). Possible values are:

* STAMP
* CUSTOM

### \[static] QualityScale

Gets or sets the quality of the annotation when saved as a stamp. The higher the value, the higher the quality and memory usage. The default is 2.

### \[static] SerializationType

Gets or sets the method this annotation class will be saved with. Possible values can be found on the [`SerializationTypes`](https://sdk.apryse.com/api/web/Core.Annotations.CustomAnnotation.html#.SerializationTypes) constant. The default is `STAMP`.

### \[static] OutputImagePadding

Gets or sets the additional padding around the custom annotation stamp appearance.

### SerializedData

Gets an object that can be used to store primitive, custom data. Data set on this object will be saved to the annotations custom data automatically which gets preserved in the document. This property cannot be initialized through the initializer.

### Author

The author of the annotation.

### Color

Gets or sets the annotation's stroke color.

### Hidden

Gets or sets whether the annotation is hidden.

### Invisible

Gets or sets whether the annotation is invisible, only if it is an unknown annotation type. Generally for hiding annotations you should use "Hidden".

### Listable

Gets or sets whether the annotation should be listed in annotation lists. If set to false, the annotation will also become unselectable.

### Locked

Gets or sets whether the annotation is locked or not. If it's locked it can't be edited or deleted, but the note can be edited.

### LockedContents

Gets or sets whether the annotation contents are locked or not. If the contents are locked then note can't be edited but the annotation can be edited or deleted.

### NoDelete

Gets or sets if this annotation can be deleted.

### NoRotate

Gets or sets if this annotation can be rotated.

### NoView

Gets or sets whether the annotation is visible on the screen. Differs from Hidden in that it can still be printed if the print flag is set.

### NoZoom

Gets or sets if this annotation scales with the page.

### Opacity

Gets or sets the opacity of the annotation.

### Printable

Gets or sets whether the annotation should be displayed when printing the page.

### ReadOnly

Gets or sets whether the annotation is readonly or not. If it's readonly both the annotation itself and its note can't be edited or deleted.

### ToggleNoView

Gets or sets whether the ToggleNoView flag is set on the annotation.

## Useful methods

### \[static] createFromClass

If you need to create a custom annotation class based on an existing annotation class, the [`createFromClass`](https://sdk.apryse.com/api/web/Core.Annotations.CustomAnnotation.html#.createFromClass) static, utility function makes it easy. Just provide the new custom element name as well as the base annotation class.

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

```js
WebViewer(
  // ...
).then(function(instance) {
  const { Annotations, annotationManager } = instance.Core;

  // Create my custom rectangle class based on a rectangle annotation
  const MyRectangle = Annotations.CustomAnnotation.createFromClass('myrect', Annotations.RectangleAnnotation);

  // Register custom annotation class with the annotation manager
  annotationManager.registerAnnotationType('myrect', MyRectangle);

  // Customize drawing
  const origDraw = MyRectangle.prototype.draw;
  MyRectangle.prototype.draw = function(ctx) {
      origDraw.apply(this, arguments);

      ctx.save();
      ctx.fillStyle = '#000000';
      ctx.fillText('My Rect', this.X, this.Y + 12); // This refers to the annotation object.
      ctx.restore();
  }

  documentViewer.addEventListener('annotationsLoaded', () => {
    // Create the annotation
    const annot = new MyRectangle({
        PageNumber: 1,
        X: 50,
        Y: 50,
        Width: 100,
        Height: 25,
    });
    instance.Core.annotationManager.addAnnotation(annot);
    instance.Core.annotationManager.redrawAnnotation(annot);
  });
});
```

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

[CustomAnnotation.createFromClass](https://sdk.apryse.com/api/web/Core.Annotations.CustomAnnotation.html#.createFromClass) [AnnotationManager.registerAnnotationType](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#registerAnnotationType)

## Other notes

### Saving Custom Annotations

Custom annotations exist outside of the PDF specification. As such, specification compliant viewers will not understand these annotations and ignore them.

WebViewer is a compliant viewer that allows you to support your custom annotations if you wish. WebViewer even allows the annotations to exist in other compliant viewers. This is done by saving it as a stamp, rather than its custom type, which will allow the annotation to appear in other viewers.

When the document is reloaded in WebViewer (where the custom annotation is [registered](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#registerAnnotationType) and supported), WebViewer can deserialize the annotation back into its original custom type. This is done through storing the unique element type of the custom annotation in the custom data.

If you would prefer to store the XFDF only, as opposed to a stamp, and would like the custom type to appear in the XFDF, then you can change the static property `SerializationType` on the custom annotation class to `SerializationTypes.CUSTOM`.

### Rendering out of bounds

Custom annotations offer a wide possibility of options to work with. It should come without saying that custom annotations can be drawn in various ways as well and possibly beyond it's own bounds. When this happens, the annotation may be clipped after saving as stamp annotations, as the image cannot exist beyond the rectangular bounds of the stamp.

You can use the `OutputImagePadding` to pad the annotations of the custom class to ensure the annotation renders in its entirety when saved. However, this adds padding and shrinks the annotation a bit based on the amount of padding added.


---

# 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/annotation-types/customannotation.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.
