Manage annotations in PDF
This guide outlines a functionality that requires extensive development effort. Use the readily available features of the PDF Viewer SDK instead of implementing a viewer from scratch. Only use this guide if you want to implement your PDF viewer and customize it as much as possible. To test and implement the primary functionality of this SDK, review Getting started with the PDF Viewer SDK.
Add, modify, and delete annotations in a PDF document with the PDF Web SDK.
Steps to work with annotations in a PDF document:
Learn how to get started and make static assets available.
Initialize the SDK
Before instantiating objects and working with PDF Web SDK API, it needs to be initialized.
import { pdfToolsWebSdk, Pdf, UI } from '@pdftools/pdf-web-sdk';
async function initialize() {
await pdfToolsWebSdk.initialize({
path: './pdftools-web-sdk/',
});
}
initialize();
Create controller
A Pdf.Controller
needs to be created to open the desired document. This controller is responsible for reading document information.
const controller = new Pdf.Controller();
Open PDF
Finally the document can be loaded to be later used to access the document information.
const pdfDocument = await controller.openDocument({
uri: '/pdf/WebViewer_Demo.pdf',
});
Use AnnotationManager
Overview
The AnnotationManager
is a central interaction hub designed to handle annotation-related operations within a document.
It serves as a bridge between user interactions and the underlying document structure, facilitating dynamic changes to annotations based on user actions.
It can be access through the annotations
property in the Pdf.Document
.
Get annotations
Annotation
objects can be retrieved via AnnotationManager.getAll()
or AnnotationManager.getAllOnPage(page: number)
.
The following code snippet shows how to iterate through the annotations on a specific page:
const annotations = await pdfDocument.annotations.getAllOnPage(1);
console.log(annotationsAfter);
annotations.forEach((annotation) => {
// Do something with the annotation
});
Add annotations
Annotation
objects can be added individually via AnnotationManager.add(a: Annotation)
or using the overload AnnotationManager.add(arr: Annotation[])
.
The following code snippet shows how to add annotations to a document:
// Add a single annotation
await pdfDocument.annotations.add(
new Pdf.Annotations.FreeTextAnnotation({
page: pdfDocument.pages.getByNumber(1),
boundingBox: new Pdf.Geometry.Rectangle(100, 100, 100, 100),
content: 'First free text annotation',
author: 'John Doe',
subject: 'First subject',
})
);
// Add multiple annotations
await pdfDocument.annotations.add([
new Pdf.Annotations.FreeTextAnnotation({
page: pdfDocument.pages.getByNumber(2),
boundingBox: new Pdf.Geometry.Rectangle(200, 200, 200, 200),
content: 'Second free text annotation',
author: 'John Doe',
subject: 'Second subject',
}),
new Pdf.Annotations.FreeTextAnnotation({
page: pdfDocument.pages.getByNumber(3),
boundingBox: new Pdf.Geometry.Rectangle(300, 300, 300, 300),
content: 'Third free text annotation',
author: 'John Doe',
subject: 'Third subject',
}),
]);
When new Annotation
objects are created, they are not automatically added to the document structure, but instead they are only existing as losely connected objects.
To effectively add the annotations to the document and to be able to save the document with the new annotations AnnotationManager.add
or respectively AnnotationManager.update
must be called.
This decoupling of annotation creation and adding is done for flexibilty and performance reasons. By deferring annotation updates until explicitly requested, unnecessary rendering operations and updates of the document structure can be avoided.
Modify annotations
Updating Annotation
objects involves modifying their properties and then calling AnnotationManager.update(a: Annotation)
or respectively AnnotationManager.update(arr: Annotation[])
method.
The following code snippet shows how to edit a single annotation in a document.
// Get annotations
const annotationsBefore = await pdfDocument.annotations.getAll();
console.log(annotationsBefore);
// Edit an annotation
annotationsBefore[0].content = annotationsBefore[0].content + ' (Edited)';
annotationsBefore[0].boundingBox.topLeft.x += 100;
// Apply changes
await pdfDocument.annotations.update(annotationsBefore[0]);
// Get annotations again
const annotationsAfterEdit = await pdfDocument.annotations.getAll();
console.log(annotationsAfterEdit);
});
When Annotation
objects are modified, changes are not automatically applied to the document, but instead changes are only cached in the Annotation
object.
To make the changes effective and to be able to save the document with the modifications AnnotationManager.update
must be called.
This decoupling of annotation modification and updating is done for flexibilty and performance reasons. By deferring annotation updates until explicitly requested, unnecessary rendering operations and updates of the document structure can be avoided.
Delete Annotations
Annotation
objects can be deleted individually via AnnotationManager.delete(a: Annotation)
or using the overload AnnotationManager.delete(arr: Annotation[])
.
The following code snippet shows how to delete an annotations from a document.
// Get annotations
const annotationsBefore = await pdfDocument.annotations.getAll();
console.log(annotationsBefore);
// Delete a single annotation
await pdfDocument.annotations.delete(annotations[0]);
// Delete multiple annotation
await pdfDocument.annotations.delete([annotations[1], annotations[2]]);
const annotationsAfterDelete = await pdfDocument.annotations.getAll();
console.log(annotationsAfterDelete);
Full example
This full sample demonstrates how to continously add, update, and delete annotations in document.
import { pdfToolsWebSdk, Pdf } from '@pdftools/pdf-web-sdk';
pdfToolsWebSdk.initialize().then(async () => {
const controller = new Pdf.Controller();
const pdfDocument = await controller.openDocument({
uri: '/pdf/WebViewer_Demo.pdf',
});
let annotationCounter = 0;
setTimeout(() => {
// move all annotations a bit to the right
const annotationsBefore = await pdfDocument.annotations.getAll();
annotationsBefore.forEach((a) => {
a.boundingBox.topLeft.x += 10;
});
await pdfDocument.annotations.update(annotationsBefore);
const annotationsUpdated = await pdfDocument.annotations.getAll();
// if last annotations reaches end of the page -> delete it
const lastAnnotation = annotationsUpdated[annotationsUpdated.length-1];
if(lastAnnotation.boundingBox.topLeft.x > 500)
{
pdfDocument.delete(lastAnnotation);
}
// if first annotation has moved enough to the right -> insert a new one on the left
const firstAnnotation = annotationsUpdated[0];
if(firstAnnotation.boundingBox.topLeft.x > 100)
{
annotationCounter++;
pdfDocument.add(new Pdf.Annotations.FreeTextAnnotation({
page: doc.pages.getByNumber(1),
boundingBox: new Pdf.Geometry.Rectangle(10, 100, 50, 50),
content: `Annotation #${annotationCounter}`,
author: 'John Doe',
subject: `Subject #${annotationCounter}`,
}));
}
}, 100);
});