Skip to main content
Version: Version 1.7

Manage annotations

Manage annotations in PDF files with the Toolbox add-on. PDF annotations let you add comments, suggestions, and markup-like shapes and highlights to PDFs. Annotations are not part of the page content as they are applied on the top of a page. In contrast to ordinary page content, many annotation types behave interactively in PDF viewers.

info

This functionality is part of the Toolbox add-on, a separate SDK that you can use with the same license key as the Pdftools SDK. To use and integrate this add-on, review Getting started with the Toolbox add-on and Toolbox add-on code samples.

Quick start

Download the full sample now in C# and Java.

Interested in C, Python, Go, or other language samples? Let us know on the contact page, and we'll add it to our samples backlog.

The Toolbox add-on supports the following types of annotations:

TypeAnnotation name
Text MarkupHighlight
Popup
StickyNote
StrikeThrough
Underline
FreeText
ShapeEllipse
Ink
Line
Polygon
PolyLine
Rectangle
Squiggly
NavigationInternalLink
WebLink
StampTextStamp
CustomStamp
FileFileAttachment

Steps to add annotations to a document:

  1. Reading the input document
  2. Copying document-wide data
  3. Copying and annotating the input pages
  4. Adding copied pages to the output document
  5. Full example
Before you begin

Reading the input document

Read the PDF file you want to annotate by loading the input document from the file system into a read-only input Document object. At the same time, create a writeable output Document object that contains the annotated output PDF file.

// Open input document
using (Stream inStream = new FileStream(inPath, FileMode.Open, FileAccess.Read))
using (Document inDoc = Document.Open(inStream, null))
{
// Create output document
using Stream outStream = new FileStream(outPath, FileMode.Create, FileAccess.ReadWrite);
using Document outDoc = Document.Create(outStream, inDoc.Conformance, null);
try (// Open input document
FileStream inStream = new FileStream(inPath, FileStream.Mode.READ_ONLY);
Document inDoc = Document.open(inStream, null);
// Create file stream
FileStream outStream = new FileStream(outPath, FileStream.Mode.READ_WRITE_NEW)) {
try (// Create output document
Document outDoc = Document.create(outStream, inDoc.getConformance(), null)) {

Copying document-wide data

In the Toolbox add-on, you always start with an empty output document (unlike the Pdftools SDK). To retain document-wide information you must explicitly copy it from the input document to the output. Document-wide information includes:

  • Metadata
  • Viewer settings
  • Embedded files

You can control which document-wide information is kept and which is removed using the CopyDocumentData function. In this example, all document-wide information is copied from the input document to the output document.

// Copy document-wide data
CopyDocumentData(inDoc, outDoc);
// Copy document-wide data
copyDocumentData(inDoc, outDoc);
CopyDocumentData function
private static void CopyDocumentData(Document inDoc, Document outDoc)
{
// Copy document-wide data

// Output intent
if (inDoc.OutputIntent != null)
outDoc.OutputIntent = IccBasedColorSpace.Copy(outDoc, inDoc.OutputIntent);

// Metadata
outDoc.Metadata = Metadata.Copy(outDoc, inDoc.Metadata);

// Viewer settings
outDoc.ViewerSettings = ViewerSettings.Copy(outDoc, inDoc.ViewerSettings);

// Associated files (for PDF/A-3 and PDF 2.0 only)
FileReferenceList outAssociatedFiles = outDoc.AssociatedFiles;
foreach (FileReference inFileRef in inDoc.AssociatedFiles)
outAssociatedFiles.Add(FileReference.Copy(outDoc, inFileRef));

// Plain embedded files
FileReferenceList outEmbeddedFiles = outDoc.PlainEmbeddedFiles;
foreach (FileReference inFileRef in inDoc.PlainEmbeddedFiles)
outEmbeddedFiles.Add(FileReference.Copy(outDoc, inFileRef));
}
private static void copyDocumentData(Document inDoc, Document outDoc) throws ToolboxException, IOException {
// Copy document-wide data

// Output intent
if (inDoc.getOutputIntent() != null)
outDoc.setOutputIntent(IccBasedColorSpace.copy(outDoc, inDoc.getOutputIntent()));

// Metadata
outDoc.setMetadata(Metadata.copy(outDoc, inDoc.getMetadata()));

// Viewer settings
outDoc.setViewerSettings(ViewerSettings.copy(outDoc, inDoc.getViewerSettings()));

// Associated files (for PDF/A-3 and PDF 2.0 only)
FileReferenceList outAssociatedFiles = outDoc.getAssociatedFiles();
for (FileReference inFileRef : inDoc.getAssociatedFiles())
outAssociatedFiles.add(FileReference.copy(outDoc, inFileRef));

// Plain embedded files
FileReferenceList outEmbeddedFiles = outDoc.getPlainEmbeddedFiles();
for (FileReference inFileRef : inDoc.getPlainEmbeddedFiles())
outEmbeddedFiles.add(FileReference.copy(outDoc, inFileRef));
}

Copying and annotating the input pages

When copying page content between documents, the PageCopyOptions class lets you define which types of page information to copy. For example, you can choose to copy or remove information such as annotations, form fields, and structure elements to the output page. In this example, all page information except digital signatures is copied to the output document.

The CopyAndAddAnnotations function copies the page content and selected page information from the first page of the input document. It then adds a StickyNote, an Ellipse, a Highlight, a FreeText, and a WebLink annotation to the page.

// Define page copy options
PageCopyOptions copyOptions = new PageCopyOptions();

// Copy first page and add annotations
Page outPage = CopyAndAddAnnotations(outDoc, inDoc.Pages[0], copyOptions);
// Define page copy options
PageCopyOptions copyOptions = new PageCopyOptions();

// Copy first page and add annotations
Page outPage = copyAndAddAnnotations(outDoc, inDoc.getPages().get(0), copyOptions);
CopyAndAddAnnotations function
private static Page CopyAndAddAnnotations(Document outDoc, Page inPage, PageCopyOptions copyOptions)
{
// Copy page to output document
Page outPage = Page.Copy(outDoc, inPage, copyOptions);

// Make a RGB color space
ColorSpace rgb = ColorSpace.CreateProcessColorSpace(outDoc, ProcessColorSpaceType.Rgb);

// Get the page size for positioning annotations
Size pageSize = outPage.Size;

// Get the output page's list of annotations for adding annotations
AnnotationList annotations = outPage.Annotations;

// Create a sticky note and add to output page's annotations
Paint green = Paint.Create(outDoc, rgb, new double[] { 0, 1, 0 }, null);
Point stickyNoteTopLeft = new Point() { X = 10, Y = pageSize.Height - 10 };
StickyNote stickyNote = StickyNote.Create(outDoc, stickyNoteTopLeft, "Hello world!", green);
annotations.Add(stickyNote);

// Create an ellipse and add to output page's annotations
Paint blue = Paint.Create(outDoc, rgb, new double[] { 0, 0, 1 }, null);
Paint yellow = Paint.Create(outDoc, rgb, new double[] { 1, 1, 0 }, null);
Rectangle ellipseBox = new Rectangle() { Left = 10, Bottom = pageSize.Height - 60, Right = 70, Top = pageSize.Height - 20 };
EllipseAnnotation ellipse = EllipseAnnotation.Create(outDoc, ellipseBox, new Stroke(blue, 1.5), yellow);
annotations.Add(ellipse);

// Create a free text and add to output page's annotations
Paint yellowTransp = Paint.Create(outDoc, rgb, new double[] { 1, 1, 0 }, new Transparency(0.5));
Rectangle freeTextBox = new Rectangle() { Left = 10, Bottom = pageSize.Height - 170, Right = 120, Top = pageSize.Height - 70 };
FreeText freeText = FreeText.Create(outDoc, freeTextBox, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", yellowTransp);
annotations.Add(freeText);

// A highlight and a web-link to be fitted on existing page content elements
Highlight highlight = null;
WebLink webLink = null;
// Extract content elements from the input page
ContentExtractor extractor = new ContentExtractor(inPage.Content);
foreach (ContentElement element in extractor)
{
// Take the first text element
if (highlight == null && element is TextElement textElement)
{
// Get the quadrilaterals of this text element
QuadrilateralList quadrilaterals = new QuadrilateralList();
foreach (TextFragment fragment in textElement.Text)
quadrilaterals.Add(fragment.Transform.TransformRectangle(fragment.BoundingBox));

// Create a highlight and add to output page's annotations
highlight = Highlight.CreateFromQuadrilaterals(outDoc, quadrilaterals, yellow);
annotations.Add(highlight);
}

// Take the first image element
if (webLink == null && element is ImageElement)
{
// Get the quadrilateral of this image
QuadrilateralList quadrilaterals = new QuadrilateralList();
quadrilaterals.Add(element.Transform.TransformRectangle(element.BoundingBox));

// Create a web-link and add to the output page's links
webLink = WebLink.CreateFromQuadrilaterals(outDoc, quadrilaterals, "https://www.pdf-tools.com");
Paint red = Paint.Create(outDoc, rgb, new double[] { 1, 0, 0 }, null);
webLink.BorderStyle = new Stroke(red, 1.5);
outPage.Links.Add(webLink);
}

// Exit loop if highlight and webLink have been created
if (highlight != null && webLink != null)
break;
}

// return the finished page
return outPage;
}
private static Page copyAndAddAnnotations(Document outDoc, Page inPage, PageCopyOptions copyOptions) throws ConformanceException, CorruptException, IOException, UnsupportedFeatureException {
// Copy page to output document
Page outPage = Page.copy(outDoc, inPage, copyOptions);

// Make a RGB color space
ColorSpace rgb = ColorSpace.createProcessColorSpace(outDoc, ProcessColorSpaceType.RGB);

// Get the page size for positioning annotations
Size pageSize = outPage.getSize();

// Get the output page's list of annotations for adding annotations
AnnotationList annotations = outPage.getAnnotations();

// Create a sticky note and add to output page's annotations
Paint green = Paint.create(outDoc, rgb, new double[] { 0, 1, 0 }, null);
Point stickyNoteTopLeft = new Point(10, pageSize.height - 10 );
StickyNote stickyNote = StickyNote.create(outDoc, stickyNoteTopLeft, "Hello world!", green);
annotations.add(stickyNote);

// Create an ellipse and add to output page's annotations
Paint blue = Paint.create(outDoc, rgb, new double[] { 0, 0, 1 }, null);
Paint yellow = Paint.create(outDoc, rgb, new double[] { 1, 1, 0 }, null);
Rectangle ellipseBox = new Rectangle(10, pageSize.height - 60, 70, pageSize.height - 20);
EllipseAnnotation ellipse = EllipseAnnotation.create(outDoc, ellipseBox, new Stroke(blue, 1.5), yellow);
annotations.add(ellipse);

// Create a free text and add to output page's annotations
Paint yellowTransp = Paint.create(outDoc, rgb, new double[] { 1, 1, 0 }, new Transparency(0.5));
Rectangle freeTextBox = new Rectangle(10, pageSize.height - 170, 120, pageSize.height - 70);
FreeText freeText = FreeText.create(outDoc, freeTextBox, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", yellowTransp);
annotations.add(freeText);

// A highlight and a web-link to be fitted on existing page content elements
Highlight highlight = null;
WebLink webLink = null;
// Extract content elements from the input page
ContentExtractor extractor = new ContentExtractor(inPage.getContent());
for (ContentElement element : extractor) {
// Take the first text element
if (highlight == null && element instanceof TextElement) {
TextElement textElement = (TextElement)element;
// Get the quadrilaterals of this text element
QuadrilateralList quadrilaterals = new QuadrilateralList();
for (TextFragment fragment : textElement.getText())
quadrilaterals.add(fragment.getTransform().transformRectangle(fragment.getBoundingBox()));

// Create a highlight and add to output page's annotations
highlight = Highlight.createFromQuadrilaterals(outDoc, quadrilaterals, yellow);
annotations.add(highlight);
}

// Take the first image element
if (webLink == null && element instanceof ImageElement) {
// Get the quadrilateral of this image
QuadrilateralList quadrilaterals = new QuadrilateralList();
quadrilaterals.add(element.getTransform().transformRectangle(element.getBoundingBox()));

// Create a web-link and add to the output page's links
webLink = WebLink.createFromQuadrilaterals(outDoc, quadrilaterals, "https://www.pdf-tools.com");
Paint red = Paint.create(outDoc, rgb, new double[] { 1, 0, 0 }, null);
webLink.setBorderStyle(new Stroke(red, 1.5));
outPage.getLinks().add(webLink);
}

// Exit loop if highlight and webLink have been created
if (highlight != null && webLink != null)
break;
}

// return the finished page
return outPage;
}

Adding copied pages to the output document

After copying and adding annotations to the page, the annotated page is added to the output document. After this step, it is no longer possible to change the page.

Finally, the remaining pages are copied and added to the output document as a PageList.

// Add the page to the output document's page list
outDoc.Pages.Add(outPage);

// Copy the remaining pages and add to the output document's page list
PageList inPages = inDoc.Pages.GetRange(1, inDoc.Pages.Count - 1);
PageList outPages = PageList.Copy(outDoc, inPages, copyOptions);
outDoc.Pages.AddRange(outPages);
// Add the page to the output document's page list
outDoc.getPages().add(outPage);

// Copy the remaining pages and add to the output document's page list
PageList inPages = inDoc.getPages().subList(1, inDoc.getPages().size());
PageList outPages = PageList.copy(outDoc, inPages, copyOptions);
outDoc.getPages().addAll(outPages);

Full example

Download the full example in C# and Java.