Programmatically Create PDF Annotations on iOS

You can programmatically create link, highlight, free text, ink, or note annotations just like any other types of supported annotations, but it’s easy to get this wrong.

To create annotations programmatically, roughly follow these steps:

  1. Create the annotation by initializing it and optionally setting its contents.

  2. Define where you want to put the annotation in the document. You can do this by setting the boundingBox property of the annotation object.

  3. Customize the appearance of the annotation (optional).

  4. Add the annotation to the document with add(annotations:options:).

For a list of supported annotations, please see the Introduction to Annotations guide.

Customize the Appearance

There are a few different ways to customize the appearance of annotations. You can set the properties on Annotation or any of its subclasses to style the annotation to your liking.

Use AnnotationStyleManager to get the last used styles that were changed in the UI for a specific annotation type and apply them to a new annotation:

let annotation = // Create an `Annotation` (subclass) instance.
let style = SDK.shared.styleManager.lastUsedStyle(forKey: Annotation.ToolVariantID(tool: annotation.typeString))
style?.apply(to: annotation)
PSPDFAnnotation *annotation = // Create a `PSPDFAnnotation` (subclass) instance.
PSPDFAnnotationStyle *style = [PSPDFKitGlobal.sharedInstance.styleManager lastUsedStyleForKey:PSPDFAnnotationStateVariantIDMake(annotation.typeString, nil)];
[style applyStyleToAnnotation:annotation];

Examples for Creating Some Annotation Types

The PSPDFKit Catalog contains many examples for programmatically adding annotations. Take a look at AddAnnotationsProgrammaticallyExamples for more details.

LinkAnnotation API

// Create `Document`.
let document = Document(url: documentURL)

// Create a new link annotation and set its URL.
let linkAnnotation = LinkAnnotation(url: URL(string: "https://pspdfkit.com")!)

// Define where you want to place the annotation in the document.
let boundingBox = CGRect(x: 200, y: 400, width: 50, height: 300)
linkAnnotation.boundingBox = boundingBox

// Customize the link annotation's appearance.
LinkAnnotationView.appearance().borderColor = .blue

// Add the newly created annotation to the document.
document.add(annotations: [linkAnnotation])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a new link annotation and set its URL.
PSPDFLinkAnnotation *linkAnnotation = [[PSPDFLinkAnnotation alloc] initWithURL:[NSURL URLWithString:@“https://pspdfkit.com”]];

// Define where you want to place the annotation in the document.
CGRect boundingBox = { .origin.x = 200.f, .origin.y = 400.f, .size.height = 50.f, .size.width = 300.f };
linkAnnotation.boundingBox = boundingBox;

// Customize the link annotation's appearance.
[[PSPDFLinkAnnotationView appearance] setBorderColor:[UIColor blueColor]];

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

Highlight Annotations

HighlightAnnotation API

// Create `Document`.
let document = Document(url: documentURL)

// Create a new highlight annotation.
let highlightAnnotation = HighlightAnnotation()

// Define where you want to place the annotation in the document.
let boundingBox = CGRect(x: 200, y: 400, width: 50, height: 300)
highlightAnnotation.boundingBox = boundingBox

// For highlight annotations, you also need to set the rect array accordingly.
highlightAnnotation.rects = [boundingBox]

// Add the newly created annotation to the document.
document.add(annotations: [highlightAnnotation])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a new highlight annotation.
PSPDFHighlightAnnotation *highlightAnnotation = [[PSPDFHighlightAnnotation alloc] init];

// Define where you want to place the annotation in the document.
CGRect boundingBox = { .origin.x = 200.f, .origin.y = 400.f, .size.height = 50.f, .size.width = 300.f };
highlightAnnotation.boundingBox = boundingBox;

// For highlight annotations, you also need to set the rect array accordingly.
highlightAnnotation.rects = @[[NSValue valueWithCGRect:boundingBox]];

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

Free Text Annotations

FreeTextAnnotation API

// Create `Document`.
let document = Document(url: documentURL)

// Create a new free text annotation by defining its contents.
let freeTextAnnotation = FreeTextAnnotation(contents: "PSPDFKit")

// Define where you want to place the annotation in the document.
let boundingBox = CGRect(x: 200, y: 400, width: 50, height: 300)
freeTextAnnotation.boundingBox = boundingBox

// Add the newly created annotation to the document.
document.add(annotations: [freeTextAnnotation])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a new free text annotation by defining its contents.
PSPDFFreeTextAnnotation *freeTextAnnotation = [[PSPDFFreeTextAnnotation alloc] initWithContents:@"PSPDFKit"];

// Define where you want to place the annotation in the document.
CGRect boundingBox = { .origin.x = 200.f, .origin.y = 400.f, .size.height = 50.f, .size.width = 300.f };
freeTextAnnotation.boundingBox = boundingBox;

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

Ink Annotations

InkAnnotation API

// Create `Document`.
let document = Document(url: documentURL)

// Create a line by defining the start and end points.
let startPoint = CGPoint(x: 200, y: 400)
let endPoint = CGPoint(x: 400, y: 600)

let pdfLines = [
	DrawingPoint(location: startPoint, intensity: 0.5),
	DrawingPoint(location: endPoint, intensity: 1),
]

// Create a new ink annotation by setting lines.
let inkAnnotation = InkAnnotation(lines: [pdfLines])

// There is no need to set `boundingBox` for ink annotations if the lines are defined.

// Customize the line color.
// Note that the `fillColor` property changes the background color of the entire bounding box.
inkAnnotation.borderColor = .blue

// Customize the line width.
inkAnnotation.lineWidth = 5

// Add the newly created annotation to the document.
document.add(annotations: [inkAnnotation])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a line by defining the start and end points.
CGPoint startPoint = { .x = 200.f, .y = 400.f };
CGPoint endPoint = { .x = 400.f, .y = 600.f };

NSArray *lines = @[@[[NSValue valueWithCGPoint:startPoint], [NSValue valueWithCGPoint:endPoint]]];

// Create a new ink annotation by setting lines.
PSPDFInkAnnotation *inkAnnotation = [[PSPDFInkAnnotation alloc] initWithLines:lines];

// There is no need to set `boundingBox` for ink annotations if the lines are defined.

// Customize the line color.
// Note that the `fillColor` property changes the background color of the entire bounding box.
inkAnnotation.borderColor = [UIColor blueColor];

// Customize the line width.
inkAnnotation.lineWidth = 5.f;

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

Note Annotations

NoteAnnotation API

// Create `Document`.
let document = Document(url: documentURL)

// Create a new note annotation and set its contents.
let noteAnnotation = NoteAnnotation(contents: "PSPDFKit")

// The note annotation is rendered as a fixed size, much like how Adobe Acrobat renders it.
// PSPDFKit will always render note annotations at a fixed size of 32x32pt. We recommend that you set the `boundingBox` to the same value.
let boundingBox = CGRect(x: 200, y: 400, width: 32, height: 32)
noteAnnotation.boundingBox = boundingBox

// Add the newly created annotation to the document.
document.add(annotations: [noteAnnotation])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a new note annotation and set its contents.
PSPDFNoteAnnotation *noteAnnotation = [[PSPDFNoteAnnotation alloc] initWithContents:@"PSPDFKit"];

// The note annotation is rendered as a fixed size, much like how Adobe Acrobat renders it.
// PSPDFKit will always render note annotations at a fixed size of 32x32pt. We recommend that you set the `boundingBox` to the same value.
CGRect boundingBox = { .origin.x = 200.f, .origin.y = 400.f, .size.height = 32.f, .size.width = 32.f };
noteAnnotation.boundingBox = boundingBox;

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

Sound Annotations

SoundAnnotation API

To create a sound annotation with an existing audio file, you need to use a Core Audio Format (.caf) file. You can convert an MP3 file to CAF using afconvert in the Terminal:

/usr/bin/afconvert -f caff -d LEI16 sound.mp3 sound.caf

Learn more in Apple’s Technical Q&A: QA1534 — Creating Core Audio Format (.caf) Files.

For more information about converting the file in your own app, please refer to Apple’s Audio Toolbox Convert File sample project.

// Create `Document`.
let document = Document(url: documentURL)

// Create a new sound annotation with the sound URL.
// The sound file needs to be a `.caf` file.

do {
	let soundAnnotation = try SoundAnnotation(url: soundURL)

	// The sound annotation is rendered as a fixed size, much like how Adobe Acrobat renders it.
	// PSPDFKit will always render sound annotations at a fixed size of 74x44pt. We recommend that you set the `boundingBox` to the same value.
	let boundingBox = CGRect(x: 200, y: 400, width: 74, height: 44)
	soundAnnotation.boundingBox = boundingBox

	// Add the newly created annotation to the document.
	document.add(annotations: [soundAnnotation])
} catch {
	// handle error
}
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create a new sound annotation with the sound URL.
// The sound file needs to be a `.caf` file.
PSPDFSoundAnnotation *soundAnnotation = [[PSPDFSoundAnnotation alloc] initWithURL:soundURL error:&error];
if (!soundAnnotation) {
	// Handle error.
	return;
}

// The sound annotation is rendered as a fixed size, much like how Adobe Acrobat renders it.
// PSPDFKit will always render sound annotations at a fixed size of 74x44pt. We recommend that you set the `boundingBox` to the same value.
CGRect boundingBox = { .origin.x = 200.f, .origin.y = 400.f, .size.height = 32.f, .size.width = 32.f };
noteAnnotation.boundingBox = boundingBox;

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

Stamp Annotations

StampAnnotation API

// Create `Document`.
let document = Document(url: documentURL)

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

// Set the image.
imageStamp.image = UIImage(named: "exampleimage.jpg")

// Set the bounding box.
let boundingBox = CGRect(x: 300, y: 150, width: 200, height: 200)
imageStamp.boundingBox = boundingBox

// Add the newly created annotation to the document.
document.add(annotations: [imageStamp])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

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

// Set the image.
imageStamp.image = [UIImage imageNamed:@"exampleimage.jpg"];

// Set the bounding box.
CGRect boundingBox = { .origin.x = 300.f, .origin.y = 150.f, .size.height = 200.f, .size.width = 200.f };
imageStamp.boundingBox = boundingBox;

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

File Annotations with Embedded Files

FileAnnotation API and EmbeddedFile API

// Create `Document`.
let document = Document(url: documentURL)

// Create the URL of the embedded file that uses an Excel document file.
let samplesURL = Bundle.main.resourceURL!.appendingPathComponent("Samples")
let embeddedFileURL = samplesURL.appendingPathComponent("Monthly Report.docx")

// Create a new file annotation and set its properties.
let fileAnnotation = FileAnnotation()
fileAnnotation.pageIndex = 0
fileAnnotation.iconName = .graph
fileAnnotation.color = .blue
fileAnnotation.boundingBox = CGRect(x: 500, y: 250, width: 32, height: 32)

// Create an embedded file and add it to the file annotation.
let embeddedFile = EmbeddedFile(fileURL: embeddedFileURL, fileDescription: "Monthly Report")
fileAnnotation.embeddedFile = embeddedFile

// Add the newly created annotation to the document.
document.add(annotations: [fileAnnotation])
// Create `PSPDFDocument`.
PSPDFDocument *document = [[PSPDFDocument alloc] initWithURL:documentURL];

// Create the URL of the embedded file that uses an Excel document file.
NSURL *samplesURL = [NSBundle.mainBundle.resourceURL URLByAppendingPathComponent:@"Samples"];
NSURL *embeddedFileURL = [samplesURL URLByAppendingPathComponent:@"Monthly Report.docx"];

// Create a new file annotation and set its properties.
PSPDFFileAnnotation *fileAnnotation = [[PSPDFFileAnnotation alloc] init];
fileAnnotation.pageIndex = 0;
fileAnnotation.iconName = PSPDFFileIconNameGraph;
fileAnnotation.color = UIColor.blueColor;
fileAnnotation.boundingBox = CGRectMake(500.f, 250.f, 32.f, 32.f);

// Create an embedded file and add it to the file annotation.
PSPDFEmbeddedFile *embeddedFile = [[PSPDFEmbeddedFile alloc] initWithFileURL:embeddedFileURL fileDescription:@"Monthly Report"];
fileAnnotation.embeddedFile = embeddedFile;

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