How to Work with PDF Annotations in C++

How to Work with PDF Annotations in C++

Aspose.PDF FOSS for C++ represents every PDF annotation as a subclass of the common Annotation base class, and exposes the annotations on a page through the AnnotationCollection returned by Page::Annotations(). This guide adds a plain text-note annotation and a styled annotation to a page with TextAnnotation, saves the document, then reopens it to read the annotations back and remove one. The library is integrated into a CMake project as a subdirectory dependency — there is no package-registry install step.

Step-by-Step Guide

Step 1: Install the Package

Add the library as a CMake subdirectory and link the static library target:

add_subdirectory(aspose.pdf-foss-for-cpp)
target_link_libraries(your_app PRIVATE aspose_pdf_foss)

Verify the toolchain and link step resolve correctly with a minimal program:

#include <aspose/pdf/document.hpp>
#include <iostream>

int main() {
    Aspose::Pdf::Document doc;
    std::cout << "Linked OK — pages: " << doc.Pages().Count() << "\n";
}

A successful build that prints pages: 0 confirms the library is linked.


Step 2: Import Required Classes

#include <aspose/pdf/document.hpp>
#include <aspose/pdf/page_collection.hpp>
#include <aspose/pdf/rectangle.hpp>
#include <aspose/pdf/color.hpp>
#include <aspose/pdf/annotations/annotation.hpp>
#include <aspose/pdf/annotations/annotation_collection.hpp>
#include <aspose/pdf/annotations/annotation_type.hpp>
#include <aspose/pdf/annotations/annotation_flags.hpp>
#include <aspose/pdf/annotations/border.hpp>
#include <aspose/pdf/annotations/border_style.hpp>
#include <aspose/pdf/annotations/text_annotation.hpp>

using namespace Aspose::Pdf;
using namespace Aspose::Pdf::Annotations;

Step 3: Create a Document and Add a Page

Create a new document and add a page to it. PageCollection::Add() returns the newly added Page:

Document doc;
Page page = doc.Pages().Add();

Step 4: Add a Text Annotation to the Page

TextAnnotation renders as a sticky-note style comment. Construct it from the owning Document, set its position with Rect() and its message with Contents(), then add it to the page’s AnnotationCollection:

TextAnnotation note{doc};
note.Rect(Rectangle(100.0, 700.0, 200.0, 720.0, false));
note.Contents("Reviewed and approved.");

page.Annotations().Add(note);

Step 5: Style a Second Annotation with Color, a Border, and Flags

Annotation also exposes Color(), Border(), and Flags(). Flags() takes an AnnotationFlags bit field — combine values with bitwise OR to set more than one flag at a time:

TextAnnotation flagged{doc};
flagged.Rect(Rectangle(100.0, 600.0, 300.0, 650.0, false));
flagged.Contents("Needs follow-up");
flagged.Color(Color::FromRgb(220, 20, 20));
flagged.Flags(AnnotationFlags::Print | AnnotationFlags::ReadOnly);
flagged.Border().Width(2);
flagged.Border().Style(BorderStyle::Dashed);

page.Annotations().Add(flagged);

Step 6: Save the Document

Nothing is written to disk until Save() runs:

doc.Save("annotated.pdf");

Step 7: Reopen the PDF and Read Its Annotations

Reopen the saved file and read its first page’s AnnotationCollection back. AnnotationCollection is 0-based, and Count() reports how many annotations the page holds:

Document reopened("annotated.pdf");
auto& annots = reopened.Pages()[1].Annotations();

std::cout << "Annotation count: " << annots.Count() << "\n";

for (int i = 0; i < annots.Count(); ++i) {
    const auto& a = annots[i];
    if (a.AnnotationType() == AnnotationType::Text) {
        std::cout << "Text annotation: " << a.Contents() << "\n";
    }
}

Step 8: Remove an Annotation and Re-save

AnnotationCollection::Delete(index) removes an annotation by its 0-based position. Delete the first annotation and save the result to a new file:

annots.Delete(0);
reopened.Save("annotated-cleaned.pdf");

Common Issues and Fixes

Nothing changes in the saved PDF after calling Annotations().Add()

Two things commonly go wrong here. First, PageCollection is 1-based (doc.Pages()[1] is the first page) while AnnotationCollection is 0-based (annots[0] is the first annotation) — mixing up the two indexing schemes leads to operating on the wrong object. Second, confirm Document::Save() actually runs after the annotation is added; nothing is written to disk until then.

AnnotationCollection indexing or Delete(index) throws std::out_of_range

Valid indices run from 0 to Count() - 1. Passing a negative index or an index equal to or greater than Count() throws std::out_of_range by design. Call Count() first to bound any loop or lookup.

Remove() returns false the second time I call it on the same annotation

This is not an error. Remove() reports whether the annotation was found and removed as a bool; calling it again on an annotation that is already gone is a no-op that returns false. Check the return value instead of assuming Remove() always succeeds.

Setting a new AnnotationFlags value clears flags I had already set

Flags(value) replaces the whole flag set — it does not merge with the previous value. To add or clear a single flag, read the current flags first and combine them with bitwise operators, for example a.Flags(a.Flags() | AnnotationFlags::Print) to add a flag, or a.Flags(a.Flags() & ~AnnotationFlags::Print) to clear one flag while leaving the rest untouched.

Frequently Asked Questions

What is the relationship between Annotation, TextAnnotation, and the other annotation subtypes?

Annotation is the common base class. TextAnnotation and the other concrete annotation types each specialize it for one PDF annotation subtype, while sharing the base class’s Rect(), Contents(), Color(), Flags(), and Border() accessors.

Are annotations scoped to the whole document or to one page?

Each page has its own AnnotationCollection, returned by that page’s Annotations() method. Annotations are always scoped to the page they were added to, not to the document as a whole.

How do I count how many annotations are on a page?

Call Count() on the AnnotationCollection returned by Page::Annotations().

Can an annotation be both printable and read-only at the same time?

Yes. AnnotationFlags is a bit field, so combine values with bitwise OR, for example AnnotationFlags::Print | AnnotationFlags::ReadOnly.

Does deleting an annotation from AnnotationCollection remove it from the PDF file on disk?

Only after Document::Save() runs. Delete(), Remove(), and Clear() only modify the in-memory collection — the PDF on disk is unchanged until the document is saved.

See Also