How-to guides/Annotations

Read and parse annotations from a PDF in Rust

Iterate over every annotation on every page. Read annotation type, author, contents, bounding box, colour, and creation date.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("reviewed.pdf")?;
    for page in 0..doc.page_count() {
        for a in doc.annotations(page)? {
            println!("{}: {:?}", a.subtype, a.contents);
        }
    }
    Ok(())
}

Step by step

1

Add PDFluent to your project

Add the pdfluent crate to Cargo.toml.

rust
[dependencies]
pdfluent = "1.0.0-beta.8"
2

Open the PDF

Open the file as a read-only document. You do not need a mutable reference just to read annotations.

rust
use pdfluent::PdfDocument;

let doc = PdfDocument::open("reviewed_contract.pdf")?;
3

Iterate over pages and annotations

Call annotations() on each page to get a slice of Annotation objects. Each object exposes its type, position, and metadata.

rust
for page_idx in 0..doc.page_count() {
    let annotations = doc.annotations(page_idx)?;
    println!("Page {} has {} annotations", page_idx + 1, annotations.len());

    for ann in &annotations {
        println!("  Type: {}", ann.subtype);
        println!("  Rect: {:?}", ann.rect);
        println!("  Contents: {:?}", ann.contents);
    }
}
4

Filter by annotation type

Use annotation_type() to select only the types you care about. AnnotationType is an enum with variants for each standard PDF annotation type.

rust
for page_idx in 0..doc.page_count() {
    for ann in doc.annotations(page_idx)? {
        if ann.subtype == "Highlight" {
            println!("Highlight at {:?}: {}", ann.rect, ann.contents.unwrap_or_default());
        }
    }
}
5

Export annotations to JSON

Collect all annotations into a serialisable struct. This is useful for syncing review comments to an external system.

rust
// AnnotationInfo exposes: subtype, rect (Option<[f64;4]>), contents (Option<String>)
let mut records = Vec::new();
for page_idx in 0..doc.page_count() {
    for ann in doc.annotations(page_idx)? {
        records.push(format!(
            "{{\"page\":{},\"type\":\"{}\",\"contents\":{:?}}}",
            page_idx + 1, ann.subtype, ann.contents.unwrap_or_default()
        ));
    }
}
println!("[{}]", records.join(","));

Notes and tips

  • annotations() returns an empty slice on pages with no annotations. No error is raised.
  • FreeText annotations may have content rendered on the page. The text is also available via contents().
  • Ink annotations have a list of point paths instead of a single rect. Use ann.ink_paths() to read them.
  • Reply annotations (threaded comments) expose reply_to() which returns the ID of the parent annotation.

Why PDFluent for this

Pure Rust

No JVM, no runtime, no DLL dependencies. Ships as a single native binary or WASM module.

Memory safe

Rust's ownership model prevents buffer overflows and use-after-free. No segfaults in PDF parsing.

Runs anywhere

Same code runs server-side, in Docker, on AWS Lambda, on Cloudflare Workers, or in the browser via WASM.

Frequently asked questions