Some test text!
Web / Guides / Custom annotations
Custom annotations are non-standard annotations typically defined by the user and beyond the PDF specification. As such, they are only fully supported in the viewer they are implemented in. WebViewer provides the ability to create custom annotations that can be tailored to your needs. The custom annotations can even be viewed in other viewers and loaded back into WebViewer as it's custom type.
Creating a class of a custom annotation is the most straightforward way to start creating a custom annotation. By extending from the CustomAnnotation
class, you automatically gain the benefits of preserving the annotation in other compliant viewers.
WebViewer(
// ...
).then(function(instance) {
const { Annotations } = instance.Core;
class TriangleAnnotation extends Annotations.CustomAnnotation {
constructor() {
super('triangle'); // provide the custom XFDF element name
this.Subject = 'Triangle';
}
}
// this is necessary to set the elementName before instantiation
TriangleAnnotation.prototype.elementName = 'triangle';
});
Just to emphasize, setting the elementName
on the class prototype is ncessary for your custom annotation to be properly recognized.
TriangleAnnotation.prototype.elementName = 'triangle';
Next, let's define the draw
function on the class so that the annotation knows how to render itself. The draw
function takes a canvas context and is called whenever the annotation should be drawn.
class TriangleAnnotation extends Annotations.CustomAnnotation {
// ...
draw(ctx, pageMatrix) {
// the setStyles function is a function on markup annotations that sets up
// certain properties for us on the canvas for the annotation's stroke thickness.
this.setStyles(ctx, pageMatrix);
// first we need to translate to the annotation's x/y coordinates so that it's
// drawn in the correct location
ctx.translate(this.X, this.Y);
ctx.beginPath();
ctx.moveTo(this.Width / 2, 0);
ctx.lineTo(this.Width, this.Height);
ctx.lineTo(0, this.Height);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
Lastly, we want to register our annotation type so that the AnnotationManager
recognizes our custom type when reading and outputting XFDF.
const { annotationManager } = instance.Core;
// ...
// register the annotation type so that it can be saved to XFDF files
annotationManager.registerAnnotationType(TriangleAnnotation.prototype.elementName, TriangleAnnotation);
Sometimes, you may want to create a custom annotation class or extend from an existing class on the fly. WebViewer provides the createFromClass
API to generate custom annotation classes from an existing anntoation class. This differs from extending existing annotations since the output is a custom annotation class that receives the same benefits from extending CustomAnnotation
.
const MyRectangle = CustomAnnotation.createFromClass('myrect', Annotations.RectangleAnnotation);
You can then use our setCustomDrawHandler
or setCustomSerializeHandler
APIs to change how the annotation class behaves.
// Change how the custom annotation renders
Annotations.setCustomDrawHandler(MyRectangle, function(ctx, pageMatrix, rotation, options) {
options.originalDraw(ctx, pageMatrix, rotation);
ctx.save();
ctx.fillStyle = '#000000';
ctx.fillText('Hello', this.X, this.Y + 12);
ctx.restore();
});
This gets you completely different, custom annotation class that will behave similar to the original while being a custom annotation.
const rect = new MyRectangle({
X: 50,
Y: 50,
Width: 150,
Height: 50,
});
You might have noticed if you open this custom annotation in another viewer, the triangle edges are cut off and it may look lower res. This is because the custom annotation is saved as a stamp annotation and the edges of the triangle are rendered past the bounds of the annotation.
There are two static properties you can tweak to adjust this: OutputImagePadding
and QualityScale
.
TriangleAnnotation.OutputImagePadding = 25; // adds 25 pixels all around
TriangleAnnotation.QualityScale = 2; // doubles the resolution at the cost of memory
Please note that adding too much padding may scale down the perceived image. These options will not affect your WebViewer as the custom render logic is available there.
Using the annotation's custom data is useful for storing custom data. With CustomAnnotation
, there is a SerializedData
property that will automatically save the data attached to it. It is better to use this for primitive values rather than for complex objects.
class TriangleAnnotation extends Annotations.CustomAnnotation {
// custom property
get CustomID() {
// attempt to get a customId value from the map
return this.SerializedData.customId;
}
set CustomID(id) {
// set a customId value from the map
this.SerializedData.customId = id;
}
}
There may be some cases where you would prefer the XFDF to reflect the actual type of the custom annotation and not a stamp. For example, if you are only saving the XFDF of the annotations as opposed to the document. In this case, you can switch the static SerializationType
property on the CustomAnnotation
class from STAMP
to CUSTOM
. Please note that this will affect annotations of the same type and the custom XFDF will be discarded when merging with the document. If you are downloading the document, be sure to switch it back to stamp temporarily.
TriangleAnnotation.SerializationType = Annotations.CustomAnnotation.SerializationTypes.CUSTOM; // use custom XFDF
Instead of a stamp in the XFDF:
<stamp page="0" rect="131.96,227.76999999999998,294.27,407.23" color="#000000" flags="print" name="bb8ac8fa-ff92-08ff-c2e5-90dbaeb9edde" title="Guest" subject="Triangle" date="D:20210319141059-07'00'" creationdate="D:20210319140524-07'00'">
<trn-custom-data bytes="..."/>
<imagedata>data:image/png;base64,...</imagedata>
</stamp>
Your output XFDF should then look like this:
<triangle page="0" rect="131.96,227.76999999999998,294.27,407.23" color="#000000" flags="print" name="bb8ac8fa-ff92-08ff-c2e5-90dbaeb9edde" title="Guest" subject="Triangle" date="D:20210319141059-07'00'" creationdate="D:20210319140524-07'00'">
<trn-custom-data bytes="..."/>
</triangle>
Read the full tutorial on how to create a custom triangle annotation or check out how to alter annotation rendering .
Trial setup questions? Ask experts on Discord
Need other help? Contact Support
Pricing or product questions? Contact Sales