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 define custom appearance streams on any annotation type by setting the Annotation’s appearanceStreamGenerator property.

Checking If an Annotation Includes an Appearance Stream

The hasAppearanceStream property indicates if the annotation has an appearance stream attached.

To remove an appearance stream, set the dirty property to true.

Appearance Stream Generator

We provide basic implementations of the AppearanceStreamGenerating interface:

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 bitmap stamp annotation:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create `Document`.
let document = Document(url: documentURL)

// Create a new stamp annotation with a `UIImage`.
let imageStamp = StampAnnotation()

// Set the appearance stream.
imageStamp.image = UIImage(named: "PSPDFKit Logo.jpg")

// Set the bounding box.
imageStamp.boundingBox = CGRect(x: 180, y: 150, width: 444, height: 500)

// Add the newly created annotation to the document.
document.add(annotations: [imageStamp])
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a new stamp annotation with a `UIImage`.
PSPDFStampAnnotation *imageStampAnnotation = [[PSPDFStampAnnotation alloc] init];

// Set the appearance stream.
imageStampAnnotation.image = [UIImage imageNamed: @"PSPDFKit Logo.jpg"];

// Set the bounding box.
imageStampAnnotation.boundingBox = { .origin.x = 180.f, .origin.y = 150.f, .size.height = 444.f, .size.width = 500.f };

// Add the newly created annotation to the document.
[document addAnnotations:@[imageStampAnnotation] options:nil];

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

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Create `Document`.
let document = Document(url: documentURL)

// Create the URL of the appearance stream that uses a PDF file.
let samplesURL = Bundle.main.resourceURL?.appendingPathComponent("Samples")
let logoURL = samplesURL!.appendingPathComponent("PSPDFKit Logo.pdf")

// Create a new stamp annotation using the appearance stream generator.
let vectorStampAnnotation = StampAnnotation()

// Set the appearance stream.
vectorStampAnnotation.appearanceStreamGenerator = FileAppearanceStreamGenerator(fileURL: logoURL)

// Set the bounding box.
vectorStampAnnotation.boundingBox = CGRect(x: 180, y: 150, width: 444, height: 500)

// Add the newly created annotation to the document.
document.add(annotations: [vectorStampAnnotation])
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create the URL of the appearance stream that uses a PDF file.
NSURL *samplesURL = [NSBundle.mainBundle.resourceURL URLByAppendingPathComponent:@"Samples"];
NSURL *logoURL = [samplesURL URLByAppendingPathComponent:@"PSPDFKit Logo.pdf"];

// Create a new stamp annotation using the appearance stream generator.
PSPDFStampAnnotation *vectorStampAnnotation = [[PSPDFStampAnnotation alloc] init];

// Set the appearance stream.
vectorStampAnnotation.appearanceStreamGenerator = [[PSPDFFileAppearanceStreamGenerator alloc] initWithFileURL:logoURL];
vectorStampAnnotation.boundingBox = CGRectMake(180.f, 150.f, 444.f, 500.f);

// Set the bounding box.
vectorStampAnnotation.boundingBox = { .origin.x = 180.f, .origin.y = 150.f, .size.height = 444.f, .size.width = 500.f };;

// Add the newly created annotation to the document.
[document addAnnotations:@[vectorStampAnnotation] options:nil];

Take a look at AddVectorStampAnnotationProgrammaticallyExample inside the Catalog app, as it shows how to create vector stamp annotations programmatically.

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