Appearance Streams

An annotation or form element in a PDF can specify an appearance stream, which allows the annotation to be rendered differently than its default annotation rendering. Each appearance stream is a self-contained PDF object that will be rendered inside the annotation rectangle. (See also: What Are Appearance Streams?)

Most appearance streams are just a representation of the same underlying annotation. This is to help third-party PDF viewers that are unable to render annotations directly. The appearance streams also ensure that the annotations in the document are displayed exactly as they were created in the source PDF editor, without subtle differences that could occur if the third-party PDF viewer had to recreate the annotation from raw annotation data.

However, sometimes appearance streams contain surprises. One example is that of square annotations that host images; there are a lot of edge cases out there, so one cannot just assume or disable appearance stream rendering.

Changing an annotation will remove existing appearance streams and rerender them using the PSPDFKit drawing engine. Some exceptions apply, such as stamp annotations containing an image or signature form elements.

PSPDFKit allows you to set an appearance stream to any annotation by setting AppearanceStreamGenerator for your Annotations via the setAppearanceStreamGenerator() method.

Appearance Stream Generator

We provide basic implementations of the AppearanceStreamGenerator interface:

ℹ️ Note: Setting an appearance stream generator does not automatically remove the existing appearance stream. The appearance stream only gets generated when the annotation has been modified or when calling generateAppearanceStream() on Annotation explicitly.

Annotation with an Appearance Stream Generator

Appearance streams can contain simple images or complex vector drawing data. PSPDFKit supports both. Unlike bitmap stamp (UIImage-based) annotations, vector annotations allow transparency and high-resolution zooming. For more on this topic, please refer to our Use Vector Stamps Instead of Blurry Shapes and What Are Appearance Streams? blog posts.

Here’s how to programmatically add a vector stamp annotation:

Copy
1
2
3
4
5
6
7
8
// Create a stamp annotation. You'll need to specify the annotation's page, bounding box in PDF coordinates, and custom subject.
val stampAnnotation = StampAnnotation(pageIndex, RectF(300f, 500f, 500f, 300f), "Stamp with custom AP stream")

// Create an appearance stream generator from a PDF containing a vector logo stored in assets.
stampAnnotation.appearanceStreamGenerator = AssetAppearanceStreamGenerator("PSPDFKit Logo.pdf")

// Add the newly created annotation to the document.
document.getAnnotationProvider().addAnnotationToPage(stampAnnotation)
Copy
1
2
3
4
5
6
7
8
// Create a stamp annotation. You'll need to specify the annotation's page, bounding box in PDF coordinates, and custom subject.
StampAnnotation stampAnnotation = new StampAnnotation(pageIndex, new RectF(300f, 500f, 500f, 300f), "Stamp with custom AP stream");

// Create an appearance stream generator from a PDF containing a vector logo stored in assets.
stampAnnotation.setAppearanceStreamGenerator(new AssetAppearanceStreamGenerator("PSPDFKit Logo.pdf"));

// Add the newly created annotation to the document.
document.getAnnotationProvider().addAnnotationToPage(stampAnnotation);

For a complete example, take a look at AnnotationCreationActivity inside the Catalog app, which shows how to create vector stamp annotations programmatically using an appearance stream generator.

You can also refer to our Default Stamp Annotations guide, which illustrates how to customize the default stamp annotations available in the stamp picker dialog.

ℹ️ Note: Make sure the bounding box of the vector stamps you create matches the aspect ratio of the source document exactly. Otherwise, you might encounter some distortion when rotating vector stamps that have mismatched bounding boxes.

Global Appearance Stream Generators

Custom appearance streams generated via AppearanceStreamGenerator are saved into a document when saving. However, appearance streams need to be regenerated whenever annotations are modified after the document is reloaded.

It can be inconvenient to set appearance stream generators for all annotations that need to have custom appearances. A cleaner approach is to add global appearance stream generators via addAppearanceStreamGenerator() on AnnotationProvider. Global appearance stream generators added via this method have a chance to override appearance stream generation for all annotations in the document.

Your custom appearance stream generator will be asked if it wishes to generate an annotation’s appearance stream via shouldUseGeneratorForAnnotation(). When true, you are expected to return a DataProvider instance to a PDF that should be used as the annotation’s appearance stream when getDataProviderForAnnotation() is called.