Skip to content

Annotation Creation

PDF Oxide supports creating all standard PDF annotation types. Annotations can be added through FluentPageBuilder (DocumentBuilder API) or PageBuilder (PdfWriter API).

Binding coverage (v0.3.38). The 15 DocumentBuilder / FluentPageBuilder annotation methods — link_url / link_page / link_named, highlight, underline, strikeout, squiggly, sticky note, stamp (14 standard types + custom), free text, and watermark (custom / DRAFT / CONFIDENTIAL) — ship in Rust, Python, Node/TypeScript, C#, Go, and WASM. The lower-level PdfWriter surface with per-method shapes (ink, polygon, polyline, caret, popup, file-attachment, redact) is Rust-only; use DocumentBuilder from other bindings, or edit existing PDFs via Annotation Editing.

Quick Example

Rust (DocumentBuilder)

use pdf_oxide::writer::{DocumentBuilder, PageSize, StampType};

let mut builder = DocumentBuilder::new();
builder.page(PageSize::Letter)
    .at(72.0, 720.0)
    .text("Click here for details")
    .link_url("https://example.com")
    .text("Important finding")
    .highlight((1.0, 1.0, 0.0))  // Yellow highlight
    .sticky_note("Review this section carefully")
    .stamp(StampType::Approved)
    .done();

builder.save("annotated.pdf")?;

Rust (PdfWriter)

use pdf_oxide::writer::PdfWriter;
use pdf_oxide::geometry::Rect;

let mut writer = PdfWriter::new();
{
    let mut page = writer.add_letter_page();
    page.add_text("Document text", 72.0, 720.0, "Helvetica", 12.0);
    page.link(Rect::new(72.0, 720.0, 100.0, 12.0), "https://example.com");
    page.highlight_rect(Rect::new(72.0, 700.0, 200.0, 12.0));
    page.sticky_note(Rect::new(300.0, 720.0, 24.0, 24.0), "A note");
    page.finish();
}
writer.save("annotated.pdf")?;

Python

from pdf_oxide import DocumentBuilder, StampType

pdf = (DocumentBuilder()
    .letter_page()
        .at(72, 720).text("Click here for details")
        .link_url("https://example.com")
        .text("Important finding")
        .highlight((1.0, 1.0, 0.0))
        .sticky_note("Review this section carefully")
        .stamp(StampType.APPROVED)
    .done()
    .save("annotated.pdf"))

Node / TypeScript

import { DocumentBuilder, StampType } from "pdf-oxide";

await new DocumentBuilder()
  .letterPage()
    .at(72, 720).text("Click here for details")
    .linkUrl("https://example.com")
    .text("Important finding")
    .highlight([1.0, 1.0, 0.0])
    .stickyNote("Review this section carefully")
    .stamp(StampType.Approved)
  .done()
  .save("annotated.pdf");

C#

using PdfOxide;

DocumentBuilder.Create()
    .LetterPage()
        .At(72, 720).Text("Click here for details")
        .LinkUrl("https://example.com")
        .Text("Important finding")
        .Highlight(1.0, 1.0, 0.0)
        .StickyNote("Review this section carefully")
        .Stamp(StampType.Approved)
    .Done()
    .Save("annotated.pdf");

Go

builder := pdfoxide.NewDocumentBuilder()
builder.LetterPage().
    At(72, 720).Text("Click here for details").
    LinkUrl("https://example.com").
    Text("Important finding").
    Highlight(1.0, 1.0, 0.0).
    StickyNote("Review this section carefully").
    Stamp(pdfoxide.StampApproved).
    Done()
_ = builder.Save("annotated.pdf")

JavaScript (WASM) – existing-PDF annotation shortcuts

The legacy addLink / addHighlight / addNote helpers continue to work for editing existing PDFs:

import init, { WasmPdfDocument } from "pdf-oxide-wasm/web";
await init();

const doc = new WasmPdfDocument(bytes);
doc.addLink(0, 100, 200, 300, 50, "https://oxide.fyi");
doc.addHighlight(0, 100, 700, 400, 20);
doc.addNote(0, 50, 750, "Review this section");
const saved = doc.save();
doc.free();

For creation-time annotations from WASM, use DocumentBuilder (same API as the Node/TS example above).

Annotation Types

Text Annotations (Sticky Notes)

Pop-up notes with various icons.

use pdf_oxide::writer::PdfWriter;
use pdf_oxide::annotation_types::TextAnnotationIcon;
use pdf_oxide::geometry::Rect;

let mut writer = PdfWriter::new();
{
    let mut page = writer.add_letter_page();

    // Default note icon
    page.sticky_note(Rect::new(72.0, 720.0, 24.0, 24.0), "Review this section");

    // Comment icon
    page.comment(Rect::new(72.0, 690.0, 24.0, 24.0), "Needs clarification");

    // Custom icon
    page.text_note_with_icon(
        Rect::new(72.0, 660.0, 24.0, 24.0),
        "Important",
        TextAnnotationIcon::Key,
    );

    page.finish();
}

Available icons: Note, Comment, Key, Help, NewParagraph, Paragraph, Insert

FluentPageBuilder equivalent:

builder.page(PageSize::Letter)
    .at(72.0, 720.0)
    .sticky_note("Review this")
    .sticky_note_with_icon("Important", TextAnnotationIcon::Key)
    .sticky_note_at(300.0, 720.0, "Positioned note")
    .done();

URL links and internal page navigation.

// URL link
page.link(Rect::new(72.0, 720.0, 150.0, 12.0), "https://example.com");

// Internal page link (0-indexed page number)
page.internal_link(Rect::new(72.0, 700.0, 100.0, 12.0), 2);

FluentPageBuilder:

builder.page(PageSize::Letter)
    .at(72.0, 720.0)
    .text("Visit website")
    .link_url("https://example.com")
    .text("Go to appendix")
    .link_page(5)
    .text("Jump to glossary")
    .link_named("glossary")
    .done();

FreeText Annotations

Text displayed directly on the page surface.

use pdf_oxide::geometry::Rect;

// Basic text box
page.textbox(Rect::new(72.0, 650.0, 200.0, 50.0), "Annotation text");

// Styled text box
page.textbox_styled(
    Rect::new(72.0, 580.0, 200.0, 50.0),
    "Styled text",
    "Courier",
    14.0,
);

// Centered text
page.textbox_centered(Rect::new(72.0, 520.0, 200.0, 30.0), "Centered");

// Callout with leader line
page.callout(
    Rect::new(200.0, 450.0, 150.0, 50.0),
    "Callout text",
    vec![150.0, 430.0, 200.0, 475.0],
);

// Typewriter (borderless text)
page.typewriter(Rect::new(72.0, 400.0, 300.0, 20.0), "Typewriter text");

FluentPageBuilder:

builder.page(PageSize::Letter)
    .freetext(Rect::new(100.0, 600.0, 200.0, 50.0), "Comment text")
    .freetext_styled(Rect::new(100.0, 530.0, 200.0, 50.0), "Styled", "Courier", 14.0)
    .done();

Highlight, Underline, Strikeout, Squiggly

Text markup annotations for reviewing.

use pdf_oxide::geometry::Rect;

page.highlight_rect(Rect::new(72.0, 720.0, 200.0, 12.0));
page.underline_rect(Rect::new(72.0, 700.0, 200.0, 12.0));
page.strikeout_rect(Rect::new(72.0, 680.0, 200.0, 12.0));
page.squiggly_rect(Rect::new(72.0, 660.0, 200.0, 12.0));

With explicit QuadPoints for precise positioning:

page.highlight(
    Rect::new(72.0, 720.0, 200.0, 12.0),
    vec![[72.0, 732.0, 272.0, 732.0, 72.0, 720.0, 272.0, 720.0]],
);

FluentPageBuilder (color is RGB 0.0-1.0):

builder.page(PageSize::Letter)
    .at(72.0, 720.0)
    .text("Highlighted text")
    .highlight((1.0, 1.0, 0.0))    // Yellow
    .text("Underlined text")
    .underline((0.0, 0.0, 1.0))    // Blue
    .text("Deleted text")
    .strikeout((1.0, 0.0, 0.0))    // Red
    .text("Questionable text")
    .squiggly((1.0, 0.5, 0.0))     // Orange
    .done();

Line Annotations

Lines and arrows between two points.

// Simple line
page.line((100.0, 500.0), (300.0, 500.0));

// Arrow
page.arrow((100.0, 470.0), (300.0, 470.0));

// Double-headed arrow
page.double_arrow((100.0, 440.0), (300.0, 440.0));

Shape Annotations (Square, Circle)

Rectangles and ellipses.

use pdf_oxide::geometry::Rect;

// Rectangle outline
page.rectangle(Rect::new(72.0, 400.0, 150.0, 80.0));

// Filled rectangle
page.rectangle_filled(
    Rect::new(250.0, 400.0, 150.0, 80.0),
    (0.0, 0.0, 1.0),  // Blue stroke
    (0.8, 0.8, 1.0),  // Light blue fill
);

// Circle outline
page.circle(Rect::new(72.0, 300.0, 80.0, 80.0));

// Filled circle
page.circle_filled(
    Rect::new(180.0, 300.0, 80.0, 80.0),
    (1.0, 0.0, 0.0),  // Red stroke
    (1.0, 0.9, 0.9),  // Light red fill
);

Polygon and Polyline Annotations

Closed polygons and open polylines.

// Closed triangle
page.polygon(vec![(200.0, 250.0), (250.0, 300.0), (150.0, 300.0)]);

// Filled polygon
page.polygon_filled(
    vec![(300.0, 250.0), (350.0, 300.0), (250.0, 300.0)],
    (0.0, 0.5, 0.0),  // Green stroke
    (0.8, 1.0, 0.8),  // Light green fill
);

// Open polyline (zigzag)
page.polyline(vec![
    (72.0, 200.0), (150.0, 230.0), (220.0, 200.0), (300.0, 230.0),
]);

Ink Annotations (Freehand Drawing)

Freehand strokes and drawings.

// Single stroke
page.ink(vec![(100.0, 150.0), (120.0, 170.0), (140.0, 150.0), (160.0, 170.0)]);

// Multiple strokes
page.freehand(vec![
    vec![(100.0, 100.0), (200.0, 100.0)],  // Horizontal line
    vec![(150.0, 50.0), (150.0, 150.0)],    // Vertical line
]);

// Styled ink
page.ink_styled(
    vec![(200.0, 150.0), (250.0, 180.0), (300.0, 150.0)],
    (1.0, 0.0, 0.0),  // Red
    3.0,               // 3pt line width
);

Stamp Annotations

Standard rubber-stamp annotations.

use pdf_oxide::writer::StampType;
use pdf_oxide::geometry::Rect;

page.stamp(Rect::new(400.0, 700.0, 150.0, 50.0), StampType::Approved);
page.stamp_approved(Rect::new(400.0, 640.0, 150.0, 50.0));
page.stamp_draft(Rect::new(400.0, 580.0, 120.0, 40.0));
page.stamp_confidential(Rect::new(400.0, 520.0, 150.0, 50.0));
page.stamp_final(Rect::new(400.0, 460.0, 100.0, 40.0));
page.stamp_not_approved(Rect::new(400.0, 400.0, 150.0, 50.0));
page.stamp_for_comment(Rect::new(400.0, 340.0, 150.0, 50.0));
page.stamp_custom(Rect::new(400.0, 280.0, 150.0, 50.0), "ReviewPending");

StampType variants: Approved, Experimental, NotApproved, AsIs, Expired, NotForPublicRelease, Confidential, Final, Sold, Departmental, ForComment, TopSecret, Draft, ForPublicRelease, Custom(String)

Watermark Annotations

Page-level watermarks that appear behind content.

// FluentPageBuilder API
builder.page(PageSize::Letter)
    .watermark("DRAFT")
    .done();

builder.page(PageSize::Letter)
    .watermark_confidential()
    .done();

builder.page(PageSize::Letter)
    .watermark_draft()
    .done();

Redact Annotations

Mark regions for redaction.

use pdf_oxide::geometry::Rect;

page.redact(Rect::new(72.0, 600.0, 200.0, 20.0));
page.redact_with_text(Rect::new(72.0, 570.0, 200.0, 20.0), "REDACTED");

Additional Annotation Types

use pdf_oxide::geometry::Rect;

// Popup window
page.popup(Rect::new(200.0, 500.0, 200.0, 100.0), true);

// Caret (text insertion marker)
page.caret(Rect::new(72.0, 450.0, 20.0, 20.0));
page.caret_paragraph(Rect::new(72.0, 420.0, 20.0, 20.0));
page.caret_with_comment(Rect::new(72.0, 390.0, 20.0, 20.0), "Insert paragraph here");

// File attachment
page.file_attachment(Rect::new(72.0, 350.0, 24.0, 24.0), "report.xlsx");
page.file_attachment_paperclip(Rect::new(72.0, 320.0, 24.0, 24.0), "notes.txt");

Generic Annotation Method

Add any annotation type using the generic add_annotation() method:

use pdf_oxide::writer::{LinkAnnotation, Annotation};
use pdf_oxide::geometry::Rect;

let link = LinkAnnotation::uri(
    Rect::new(72.0, 720.0, 100.0, 12.0),
    "https://example.com",
);

page.add_annotation(link);

Advanced Example

Full Annotation Showcase

use pdf_oxide::writer::{PdfWriter, StampType};
use pdf_oxide::geometry::Rect;

let mut writer = PdfWriter::new();
{
    let mut page = writer.add_letter_page();

    // Links
    page.add_text("Visit Rust", 72.0, 750.0, "Helvetica", 12.0);
    page.link(Rect::new(72.0, 750.0, 70.0, 12.0), "https://rust-lang.org");

    // Text markup
    page.highlight_rect(Rect::new(72.0, 720.0, 150.0, 12.0));
    page.underline_rect(Rect::new(72.0, 700.0, 150.0, 12.0));
    page.strikeout_rect(Rect::new(72.0, 680.0, 150.0, 12.0));
    page.squiggly_rect(Rect::new(72.0, 660.0, 150.0, 12.0));

    // Notes
    page.sticky_note(Rect::new(300.0, 720.0, 24.0, 24.0), "Important");
    page.comment(Rect::new(340.0, 720.0, 24.0, 24.0), "Review needed");

    // Shapes
    page.line((72.0, 620.0), (250.0, 620.0));
    page.arrow((72.0, 600.0), (250.0, 600.0));
    page.rectangle(Rect::new(72.0, 540.0, 100.0, 50.0));
    page.circle(Rect::new(200.0, 540.0, 50.0, 50.0));

    // Ink
    page.ink(vec![(72.0, 500.0), (120.0, 520.0), (170.0, 500.0)]);

    // Stamp
    page.stamp_approved(Rect::new(400.0, 720.0, 150.0, 50.0));

    // Redact
    page.redact(Rect::new(72.0, 450.0, 200.0, 15.0));

    page.finish();
}
writer.save("annotation_showcase.pdf")?;