Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rust-pdf-box Documentation

A comprehensive, pure Rust implementation of Apache PDFBox featuring modern, context-oriented, and ergonomic PDF processing.


🚀 Get Started Immediately

ResourceDescriptionLink
Quick Start5-minute tutorial to create, stamp, and sign PDFsRead Guide →
CLI UtilityStandalone pdfbox command-line utility referenceCLI Reference →
System ArchitectureMulti-layer COS, PD, content stream, and crypto diagramsView Architecture →
API DocumentationFull Rustdoc crate reference with all types and traitsBrowse Rustdoc →

What is rust-pdf-box?

rust-pdf-box is an open-source, pure Rust library bringing the full feature set of Apache PDFBox into modern Rust with zero external C/C++ or OpenSSL dependencies.

Designed and maintained by Techwasti Infotech, rust-pdf-box eliminates the boilerplate typical of low-level PDF manipulation. It provides a simple, context-oriented facade that makes common, high-demand operations—such as digital signing with ISO 32000 compliance, watermarking, rubber stamping, interactive AcroForm processing, and document merging/splitting—straightforward, fast, and reliable.


Core Feature Guides

  • 📑 Stamping & Watermarking
    Apply semi-transparent diagonal watermarks, official rubber stamps, headers, and footers across page ranges with dynamic {page} and {total} substitutions.

  • 🔒 Digital Signatures (ISO 32000 PKCS#7 / CMS)
    Pure-Rust detached PKCS#7 signing, built-in X.509 v3 self-signed certificate generation, visual signature appearances, and cryptographic tamper verification.

  • 📝 Interactive Forms (AcroForms) & Annotations
    Inspect, update, and flatten interactive form fields (/Tx, /Btn, /Ch, /Sig), and attach clickable hyperlink annotations (/Link).

  • 🗂️ Document Assembly
    Merge multiple PDF documents with automatic conflict-free object ID remapping, split into single pages, or rotate pages.

  • 🔍 Text Extraction
    Extract UTF-8 text from PDF content streams with accurate layout reconstruction using PDFTextStripper.

  • 🔄 Apache PDFBox Migration Guide
    Class-by-class comparison, memory safety advantages, and side-by-side Java vs. Rust code samples.

  • 🏢 Enterprise Integration & Artifacts
    Guidance for Python, Node.js, Docker, pre-compiled platform binaries, and CI/CD pipelines.

  • 📚 Full Rustdoc API Reference
    Complete auto-generated Rust documentation covering every module, function, and struct.


Quick Start Guide

Get up and running with rust-pdf-box in your Rust project in just a few minutes.


1. Installation

Add rust-pdf-box to your Cargo.toml:

[dependencies]
rust-pdf-box = "0.1.0"

Or using Cargo CLI:

cargo add rust-pdf-box

2. Your First Document

Create a blank PDF document, add a page, write text, and save:

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Initialize a new PDF document
    let mut doc = PdfDocument::new();

    // 2. Add an A4 page
    let page_id = doc.add_blank_page(PDRectangle::a4())?;

    // 3. Write text onto the page
    let mut cs = doc.page_content_stream(page_id, AppendMode::Overwrite)?;
    cs.begin_text()?
        .set_font(PDType1Font::HelveticaBold, 22.0)?
        .set_non_stroking_color(Color::rgb(0.05, 0.45, 0.40))? // Techwasti Teal
        .set_text_matrix(Matrix::translation(50.0, 750.0))?
        .show_text("Hello from rust-pdf-box!")?
        .end_text()?;
    cs.finish()?;

    // 4. Save to file
    doc.save("hello_world.pdf")?;
    println!("Successfully generated hello_world.pdf");

    Ok(())
}

3. Watermarking in 3 Lines

Apply a semi-transparent diagonal watermark across all pages:

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("invoice.pdf")?;

    doc.stamp()
        .pages(PageRange::All)
        .watermark("CONFIDENTIAL", 54.0, Color::rgb(0.85, 0.1, 0.1), 45.0, 0.25)
        .apply(&mut doc.inner)?;

    doc.save("invoice_watermarked.pdf")?;
    Ok(())
}

4. Digital Signature with In-Memory Key

Digitally sign a PDF document adhering to ISO 32000 CMS/PKCS#7 with a visual appearance stamp:

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("contract.pdf")?;

    // Generate an in-memory X.509 v3 self-signed RSA-2048 signing key
    let key = SigningKey::generate_self_signed(
        "CN=Techwasti Signer, O=Techwasti Infotech, C=IN"
    )?;

    // Configure visual signature appearance
    let appearance = SignatureAppearance::new(1, PDRectangle::new(50.0, 50.0, 250.0, 100.0))
        .with_signer_name("Techwasti Infotech")
        .with_reason("Contract Approval")
        .with_location("Bangalore, India");

    // Apply detached PKCS#7 signature
    doc.signer()
        .with_key(key)
        .with_appearance(appearance)
        .sign_to_file("contract.pdf", "contract_signed.pdf")?;

    println!("Signed document successfully created!");
    Ok(())
}

5. Merging Documents

Merge multiple PDF files into one combined output:

use rust_pdf_box::manipulation::PDFMerger;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut merger = PDFMerger::new();
    merger.add_source("cover.pdf")?;
    merger.add_source("report.pdf")?;
    merger.add_source("appendix.pdf")?;

    merger.merge("complete_report.pdf")?;
    println!("Merged report generated!");
    Ok(())
}

6. Text Extraction

Extract plain text from an existing document:

use rust_pdf_box::prelude::*;
use rust_pdf_box::text::PDFTextStripper;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = PdfDocument::open("sample.pdf")?;
    let stripper = PDFTextStripper::new();
    let text = stripper.get_text(&doc.inner)?;

    println!("Extracted Text:\n{}", text);
    Ok(())
}

Command Line Utility (CLI)

rust-pdf-box includes a high-performance standalone command-line executable named pdfbox. It allows developers, sysadmins, and automation pipelines to manipulate, sign, stamp, and inspect PDF files without writing code.


Installation

Cargo Install

If you have Rust and Cargo installed:

cargo install --path . --bin pdfbox

Or install directly from git:

cargo install --git https://github.com/techwastiinfotech/rust-pdf-box.git --bin pdfbox

Pre-Compiled Standalone Binaries

Pre-compiled single-binary executables with zero external dependencies are published on every GitHub Release for:

  • Linux (x86_64)
  • Windows (x86_64)
  • macOS (Apple Silicon arm64 and Intel x86_64)

Subcommand Reference

1. Document Information (info)

Inspect document metadata, PDF version, catalog references, and page count:

pdfbox info sample.pdf

Example output:

=== PDF Document Info ===
File: sample.pdf
Version: 1.7
Page Count: 12
Title: Annual Financial Report 2026
Author: Techwasti Infotech
Producer: rust-pdf-box v0.1.0
Encrypted: false

2. Text Extraction (text)

Extract all readable text streams from a PDF file:

# Print to standard output
pdfbox text invoice.pdf

# Save directly to a text file
pdfbox text invoice.pdf extracted.txt

3. Watermarking (watermark)

Stamp a diagonal, semi-transparent watermark across all pages:

pdfbox watermark input.pdf watermarked.pdf "CONFIDENTIAL" [opacity] [angle_deg]

Example:

pdfbox watermark contract.pdf contract_marked.pdf "DRAFT VERSION" 0.2 45

4. Rubber Stamping (stamp)

Apply an official boxed rubber stamp in the upper right corner:

pdfbox stamp input.pdf stamped.pdf "<TITLE>" ["<SUBTITLE>"]

Example:

pdfbox stamp proposal.pdf approved.pdf "APPROVED" "Security Compliance Team"

5. Digital Signatures (sign)

Digitally sign a PDF document with an ISO 32000 CMS/PKCS#7 detached signature and an official visual appearance stamp:

pdfbox sign input.pdf signed.pdf "<SIGNER_NAME>" ["<REASON>"] ["<LOCATION>"]

Example:

pdfbox sign contract.pdf contract_signed.pdf "Techwasti Authority" "Enterprise Contract Agreement" "Bangalore, India"

6. Signature Verification (verify)

Verify detached digital signatures and detect if the document was altered or tampered with after signing:

pdfbox verify contract_signed.pdf

Example output:

=== Signature Verification ===
Found 1 signature(s):
  [1] Signer: CN=Techwasti Authority
      Filter: /Adobe.PPKLite
      SubFilter: /adbe.pkcs7.detached
      Status: VALID (Document integrity verified)

7. Merging PDFs (merge)

Merge multiple PDF documents into a single document:

pdfbox merge output.pdf file1.pdf file2.pdf file3.pdf

8. Splitting PDFs (split)

Split a multi-page PDF into single-page PDF files:

pdfbox split multi_page.pdf ./output_directory/

9. Flattening Forms (flatten)

Burn all interactive AcroForm fields into permanent page graphics so form inputs can no longer be edited:

pdfbox flatten input_form.pdf flattened.pdf

Architecture of rust-pdf-box

rust-pdf-box is an idiomatic, pure Rust implementation of Apache PDFBox engineered for safety, performance, context-oriented ergonomics, and ISO 32000 conformance.


1. High-Level System Architecture

The codebase is structured into clear, decoupled layers:

graph TD
    UserApp["Developer Application / CLI"] --> Facade["PdfDocument (Context-Oriented Facade)"]
    Facade --> Stamper["Stamper & Watermarking Engine"]
    Facade --> Signer["Signer & PKCS#7 Engine"]
    Facade --> Stripper["PDFTextStripper"]
    Facade --> Merger["PDFMerger & Splitter"]
    
    Stamper --> PD["High-Level PD Model (PDDocument, PDPage, PDResources)"]
    Signer --> PD
    Stripper --> PD
    Merger --> PD
    
    PD --> ContentStream["Content Stream Engine (PDPageContentStream)"]
    PD --> FontEngine["Font Engine (Standard 14 Type 1 & TrueType)"]
    PD --> ImageEngine["Image XObject (JPEG & PNG SoftMask)"]
    PD --> Forms["Interactive AcroForms & Annotations"]
    
    PD --> COS["Low-Level COS Model (COSDocument, COSObject, COSStream)"]
    Signer --> Crypto["Crypto Subsystem (ASN.1 DER, X.509, PKCS#7 CMS)"]
    Crypto --> COS
    
    COS --> Lexer["PDF Tokenizer / Lexer"]
    COS --> Parser["Incremental PDF Parser"]
    COS --> Writer["PDF Serializer & Incremental Writer"]

2. Layer Descriptions

2.1 The COS Model (src/cos/)

The COS (Certificate / Content Object Stream) layer is the foundation of PDF:

  • COSObject: The fundamental enum representing PDF primitives:
    • Null, Boolean, Integer, Real, String, Name, Array, Dictionary, Stream, Reference.
  • COSDocument: In-memory representation of the PDF object table, dirty-object tracking set, and trailer dictionary.
  • Lexer: Fast, zero-allocation scanner handling PDF comments, literal strings with escape codes and octal numbers, hex strings, names with #XX escapes, and binary stream payloads.
  • Parser: Capable of reading backwards to locate startxref, traversing multi-revision classic XRef tables and modern compressed XRef streams (/Type /XRef), and expanding object streams (/Type /ObjStm).
  • Writer: Serializes COS structures with optional Deflate compression, builds sorted indirect object tables, formats 10-digit XRef entries, and supports incremental updates (save_incremental) to append revisions without touching previous byte ranges.

2.2 The PD Model (src/pd/)

The PD (Page Description) layer models the visual document hierarchy:

  • PDDocument: Root container providing high-level page lifecycle methods (add_page, insert_page, remove_page, page_count).
  • PDPage: Represents individual pages with MediaBox, CropBox, Rotation (0°, 90°, 180°, 270°), resource dictionaries, and content stream lists.
  • PDRectangle: Standard coordinate geometry with standard sizes (A4, A3, LETTER, LEGAL, etc.).
  • PDResources: Manages fonts, XObjects, and Extended Graphics States (/ExtGState).

2.3 The Content Stream Engine (src/content/)

  • PDPageContentStream: Emits PDF graphics operators with three append modes:
    • AppendMode::Overwrite: Replaces page content entirely.
    • AppendMode::Append: Appends graphics after existing content (foreground stamps).
    • AppendMode::Prepend: Prepends graphics before existing content (background watermarks).
  • Graphics State Operators: q (save), Q (restore), w (line width), J (cap), j (join), cm (transform matrix), re (rect), m / l / c (path construction), s / f / B (painting), and W n (clipping).
  • Color Systems: RGB, CMYK, Grayscale, and Hex conversion.
  • Transparency: Automatic /ExtGState registration for fill and stroke alpha opacity.

2.4 Cryptography & Digital Signatures (src/crypto/ & src/sign/)

  • ASN.1 DER Engine: Pure-Rust encoder for Sequences, Sets, OIDs, OctetStrings, UTCTime, GeneralizedTime, and Context-Specific tagged elements.
  • X.509 Generator: Enables 1-line generation of RSA key pairs and self-signed X.509 v3 certificates in pure Rust with zero OpenSSL dependencies.
  • Detached PKCS#7 / CMS: Builds standard Adobe-compatible SignedData structures containing signed attributes (contentType, messageDigest, signingTime) signed by RSA-SHA256.
  • ByteRange Integrity: Reserves pre-allocated /Contents <...> byte gaps, computes SHA-256 over exact document byte ranges, and verifies signatures with tamper detection.

2.5 Stamping & Watermarking (src/stamp/)

  • Context-oriented builder supporting text watermarks, rubber stamps, running headers/footers with dynamic {page} and {total} substitutions, and image stamps.

2.6 Interactive AcroForms & Flattening (src/interactive/)

  • Discovers form fields (Tx, Btn, Ch, Sig), modifies values, and flattens forms (flatten) by burning widget appearance streams into page content streams and removing interactive widget dictionaries.

2.7 Text Extraction (src/text/)

  • PDFTextStripper parses BT ... ET, Tj, TJ, ', ", Td, and Tm operators, reconstructing readable text and line breaks.

Context-Oriented Stamping & Watermarking Guide

rust-pdf-box provides a fluent, context-oriented stamping engine designed to make watermarking, rubber stamping, headers/footers, and bates numbering effortless in Rust.


1. Quick Example

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("invoice.pdf")?;

    // Fluent watermark on all pages
    doc.stamp()
        .pages(PageRange::All)
        .watermark("CONFIDENTIAL", 54.0, Color::rgb(0.85, 0.1, 0.1), 45.0, 0.25)
        .apply(&mut doc.inner)?;

    doc.save("invoice_watermarked.pdf")?;
    Ok(())
}

2. Targeting Page Ranges

You can selectively target pages using PageRange:

PageRange VariantDescriptionExample
PageRange::AllApplies to every page in the documentdoc.stamp().pages(PageRange::All)
PageRange::Single(n)Applies to 1-based page ndoc.stamp().pages(PageRange::Single(1))
PageRange::Range(a..=b)Applies to range a through b (inclusive)doc.stamp().pages(PageRange::Range(2..=5))
PageRange::OddApplies to odd pages (1, 3, 5, …)doc.stamp().pages(PageRange::Odd)
PageRange::EvenApplies to even pages (2, 4, 6, …)doc.stamp().pages(PageRange::Even)
PageRange::Pages(vec)Applies to specific page numbersdoc.stamp().pages(PageRange::Pages(vec![1, 4, 7]))

3. Stamp Placement and Positions

Use StampPosition to anchor your stamp:

  • StampPosition::Center: Centered horizontally and vertically on the page.
  • StampPosition::TopLeft { margin_x, margin_y }: Upper-left corner with offsets.
  • StampPosition::TopCenter { margin_y }: Top-centered (ideal for headers).
  • StampPosition::TopRight { margin_x, margin_y }: Upper-right corner (ideal for stamps/seals).
  • StampPosition::BottomLeft { margin_x, margin_y }: Lower-left corner.
  • StampPosition::BottomCenter { margin_y }: Bottom-centered (ideal for footers & page numbers).
  • StampPosition::BottomRight { margin_x, margin_y }: Lower-right corner.
  • StampPosition::Custom { x, y }: Exact user space coordinates (1/72 inch).

4. Layering: UnderContent vs OverContent

  • StampLayer::UnderContent: Prepend graphics before page contents. Perfect for semi-transparent diagonal watermarks (“CONFIDENTIAL”, “DRAFT”) so the stamp sits behind page text and never obscures critical data.
  • StampLayer::OverContent: Append graphics after page contents. Ideal for rubber stamps (“APPROVED”, “PAID”), visual signatures, headers, and footers.

5. Rubber Stamps

Create authentic boxed rubber stamps with rounded borders and optional subtitles:

#![allow(unused)]
fn main() {
doc.stamp()
    .pages(PageRange::Single(1))
    .position(StampPosition::TopRight { margin_x: 36.0, margin_y: 36.0 })
    .rubber_stamp(
        "APPROVED",
        Some("Techwasti Infotech".to_string()),
        Color::rgb(0.0, 0.6, 0.2), // Green border & text
        -15.0,                      // Slight rotation angle
    )
    .apply(&mut doc.inner)?;
}

6. Running Headers & Footers with Dynamic Placeholders

Headers and footers support automatic {page} and {total} substitutions:

#![allow(unused)]
fn main() {
// Running Header
doc.stamp()
    .pages(PageRange::All)
    .header(
        "Statement of Work - Page {page} of {total}",
        StampPosition::TopCenter { margin_y: 20.0 },
        10.0,
        Color::GRAY,
    )
    .apply(&mut doc.inner)?;

// Running Footer
doc.stamp()
    .pages(PageRange::All)
    .footer(
        "CONFIDENTIAL - Techwasti Infotech (C) 2026",
        StampPosition::BottomCenter { margin_y: 20.0 },
        9.0,
        Color::GRAY,
    )
    .apply(&mut doc.inner)?;
}

7. Image Stamping

You can stamp PNG (with transparency) or JPEG logos onto any page:

#![allow(unused)]
fn main() {
let img_bytes = std::fs::read("logo.png")?;
let img = PDImageXObject::create_from_image_bytes(&mut doc.inner.cos_doc, &img_bytes)?;

doc.stamp()
    .pages(PageRange::Single(1))
    .position(StampPosition::TopRight { margin_x: 40.0, margin_y: 40.0 })
    .image(img, 120.0, 50.0, 0.85) // width, height, opacity
    .apply(&mut doc.inner)?;
}

Digital Signatures & Security Guide

rust-pdf-box implements full ISO 32000-1 / ISO 32000-2 and Adobe Acrobat compliant detached PKCS#7 / CMS digital signatures in pure Rust.


1. How PDF Digital Signing Works

A PDF signature does NOT hash the entire file as a single blob. Instead, it leaves an exact byte placeholder for the signature dictionary (/Contents <00...00>), and hashes all other bytes in the document using /ByteRange:

[ Document Bytes Part 1 ]  <-- Range 1: 0 to offset1
/Contents < 3082... >      <-- Excluded signature bytes gap (offset1 to offset2)
[ Document Bytes Part 2 ]  <-- Range 2: offset2 to offset2 + len2
  1. Placeholder Allocation: The document is prepared with /ByteRange [ 0 offset1 offset2 len2 ] and /Contents <000...000>.
  2. Digest Calculation: SHA-256 is computed over Part 1 and Part 2.
  3. PKCS#7 / CMS Generation:
    • ContentType: id-data (1.2.840.113549.1.7.1)
    • SignedAttributes:
      • contentType: id-data
      • messageDigest: SHA-256 hash of Part 1 + Part 2
      • signingTime: timestamp
    • SignerInfo: RSA-SHA256 signature calculated over DER encoded SignedAttributes.
  4. Hex Insertion: The DER bytes of the PKCS#7 structure are converted to uppercase hexadecimal and written directly into the placeholder inside < and >.

2. In-Memory Self-Signed Certificate Generation

To eliminate the need for complicated OpenSSL command-line invocations, rust-pdf-box provides a built-in X.509 v3 self-signed certificate generator:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let key = SigningKey::generate_self_signed("CN=Techwasti Signer, O=Techwasti Infotech, C=IN")?;
}

This generates:

  • An RSA 2048-bit (or 3072/4096-bit) private key.
  • An X.509 v3 certificate valid for 10 years with Subject and Issuer RDNs, SubjectPublicKeyInfo, and RSA-SHA256 signature.

3. Signing a PDF with a Visual Appearance Stamp

A visual signature creates both the cryptographic /Type /Sig dictionary and a visual /Type /Annot /Subtype /Widget on the selected page:

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("contract.pdf")?;

    let key = SigningKey::generate_self_signed("CN=Techwasti Official")?;

    let signer = Signer::new(key)
        .with_name("Techwasti Infotech Authorized Signatory")
        .with_reason("Service Contract Approval")
        .with_location("Bangalore, India")
        .with_contact_info("legal@techwasti.com")
        .with_visual_stamp(
            VisualSignature::at_page(1)
                .rect(50.0, 50.0, 290.0, 130.0)
                .title("Digitally Signed by Techwasti")
                .detail("Signer: Techwasti Authorized\nDate: 2026-10-25\nStatus: Verified")
                .border_color(Color::rgb(0.0, 0.45, 0.85))
                .background_color(Color::rgb(0.96, 0.98, 1.0)),
        );

    let signed_pdf_bytes = doc.sign(signer)?;
    std::fs::write("contract_signed.pdf", &signed_pdf_bytes)?;

    println!("Signed PDF generated successfully!");
    Ok(())
}

4. Digital Signature Verification & Tamper Detection

SignatureVerifier extracts the /ByteRange, recalculates SHA-256 over the designated file slices, and verifies that the embedded PKCS#7 signed digest matches:

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pdf_bytes = std::fs::read("contract_signed.pdf")?;

    let report = SignatureVerifier::verify(&pdf_bytes)?;

    println!("Signature Verification:");
    println!("  -> Valid: {}", report.is_valid);
    println!("  -> Signer Name: {:?}", report.signer_name);
    println!("  -> Reason: {:?}", report.reason);
    println!("  -> Location: {:?}", report.location);
    println!("  -> Signing Time: {:?}", report.signing_time);
    println!("  -> SHA-256 Digest: {}", report.computed_digest_hex);

    if !report.is_valid {
        eprintln!("WARNING: Document has been tampered with!");
    }

    Ok(())
}

Interactive Forms (AcroForms) and Annotations

rust-pdf-box provides a robust engine for creating, inspecting, filling, and flattening interactive PDF forms (AcroForms) and annotations (Links, Highlights, and Notes).


1. Overview & Architecture

Interactive PDF forms are rooted in the document catalog dictionary (/Root) under the /AcroForm entry. Each AcroForm contains:

  • /Fields: An array of field dictionaries (PDFormField).
  • /NeedAppearances: A boolean indicating whether viewers should regenerate appearance streams.
  • /DA: Default appearance string containing font and color commands (e.g., /Helv 10 Tf 0 g).
Document Catalog (/Root)
 └── /AcroForm
      ├── /Fields [ Ref(10 0 R), Ref(11 0 R) ]
      ├── /DA "(/Helv 10 Tf 0 g)"
      └── /NeedAppearances true

Annotations reside directly on individual pages under /Annots and link visual geometry (/Rect) with actions (such as URI navigation) or form field widgets (/Subtype /Widget).


2. Reading and Inspecting Form Fields

You can query all form fields present in a document:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let mut doc = PdfDocument::open("sample_form.pdf")?;

if let Some(form) = doc.inner.catalog_mut()?.acro_form()? {
    let fields = form.fields(&doc.inner)?;
    for field in &fields {
        println!("Field Name: {}", field.name);
        println!("  Type: {:?}", field.field_type);
        println!("  Value: {:?}", field.value);
        println!("  Read-only: {}", field.is_read_only());
    }
}
}

Supported Field Types

Field TypePDF Type CodeDescription
FormFieldType::Text/TxSingle-line or multi-line text input fields
FormFieldType::Button/BtnPushbuttons, checkboxes (/Yes or /Off), and radio buttons
FormFieldType::Choice/ChDropdown combo boxes and multi-selection list boxes
FormFieldType::Signature/SigCryptographic signature placeholders
FormFieldType::UnknownCustomVendor-specific or specialized field types

3. Modifying Field Values

To set or update form field values programmatically:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let mut doc = PdfDocument::open("sample_form.pdf")?;

if let Some(mut form) = doc.inner.catalog_mut()?.acro_form()? {
    // Fill text fields
    form.set_field_value(&mut doc.inner, "FullName", "Jane Doe")?;
    form.set_field_value(&mut doc.inner, "Email", "jane.doe@techwasti.com")?;
    form.set_field_value(&mut doc.inner, "Comments", "Expedited delivery requested.")?;

    // Check a checkbox (typically "Yes" or "Off")
    form.set_field_value(&mut doc.inner, "SubscribeNewsletter", "Yes")?;
}

doc.save("filled_form.pdf")?;
}

4. Form Flattening

Form flattening converts interactive widgets into static, non-editable vector graphics directly rendered onto the page content streams, and removes the /AcroForm catalog entry.

Why Flatten?

  • Security & Immutability: Prevents recipients from tampering with filled data in legal contracts and invoices.
  • Universal Rendering: Ensures consistent appearance across third-party mobile viewers and print drivers that do not evaluate AcroForm appearances.
  • Archival Compliance: Essential for PDF/A long-term archiving standards.

How to Flatten:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let mut doc = PdfDocument::open("filled_form.pdf")?;

if let Some(mut form) = doc.inner.catalog_mut()?.acro_form()? {
    // Burns all field values into page content streams and clears widget annotations
    form.flatten(&mut doc.inner)?;
}

doc.save("contract_permanent.pdf")?;
}

5. Adding Annotations to Pages

You can attach clickable URI links to arbitrary page rectangles:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;
use rust_pdf_box::interactive::PDAnnotation;

let mut doc = PdfDocument::create();
let page_idx = doc.add_page(PDRectangle::A4);

// Create a clickable link rectangle: (x, y, width, height)
let link = PDAnnotation::link(
    PDRectangle::new(50.0, 700.0, 200.0, 20.0),
    "https://techwasti.com"
);

// Register annotation on the page
doc.inner.page_mut(page_idx)?.add_annotation(&mut doc.inner, link)?;
doc.save("document_with_links.pdf")?;
}

Document Assembly (Merge & Split)

rust-pdf-box provides dedicated utilities for merging multiple PDF documents, splitting documents into ranges or individual pages, and rotating/reordering pages without data loss.


1. Merging Multiple Documents (PDFMerger)

When combining multiple PDF documents, naive object concatenation causes severe identifier collisions. PDFMerger automatically resolves object number conflicts by building a collision-free remapping table while preserving fonts, XObjects, bookmarks, and metadata:

use rust_pdf_box::manipulation::PDFMerger;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut merger = PDFMerger::new();

    // Add source documents in order
    merger.add_source("cover.pdf")?;
    merger.add_source("executive_summary.pdf")?;
    merger.add_source("financial_statements.pdf")?;
    merger.add_source("appendix.pdf")?;

    // Produce merged document
    merger.merge("complete_annual_report.pdf")?;

    println!("Successfully merged 4 documents into complete_annual_report.pdf");
    Ok(())
}

2. Splitting Documents (PDFSplitter)

PDFSplitter divides a large PDF into manageable parts, either page-by-page or by custom range:

use rust_pdf_box::manipulation::PDFSplitter;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let splitter = PDFSplitter::new();

    // Split into individual single-page documents
    let split_docs = splitter.split_to_single_pages("large_handbook.pdf")?;

    for (index, mut single_page_doc) in split_docs.into_iter().enumerate() {
        let output_path = format!("page_{:03}.pdf", index + 1);
        single_page_doc.save(&output_path)?;
    }

    Ok(())
}

3. Page Reordering & Rotation

Individual pages can be rotated (in multiples of 90 degrees) and rearranged:

use rust_pdf_box::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("scanned_document.pdf")?;

    // Rotate page 1 by 90 degrees clockwise
    if let Ok(page) = doc.inner.page_mut(1) {
        page.set_rotation(90);
    }

    // Delete an obsolete page
    doc.inner.remove_page(3)?;

    doc.save("scanned_document_corrected.pdf")?;
    Ok(())
}

Text Extraction (PDFTextStripper)

PDFTextStripper extracts plain text from PDF content streams while respecting text positioning, word spacing, and line breaks.


1. Overview

PDF content streams represent text as instructions such as:

  • BT (Begin Text) and ET (End Text)
  • Tf (Set Font and Size)
  • Td / TD / Tm (Position and Text Matrix)
  • Tj / TJ / ' / " (Show Text String and Kerning Arrays)

PDFTextStripper parses these operators, decodes string literals and hex sequences according to font encodings, and groups characters into words, sentences, and paragraphs.


2. Basic Text Extraction

Extract the entire text content of a PDF file:

use rust_pdf_box::prelude::*;
use rust_pdf_box::text::PDFTextStripper;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = PdfDocument::open("document.pdf")?;

    let stripper = PDFTextStripper::new();
    let extracted_text = stripper.get_text(&doc.inner)?;

    println!("--- Extracted Content ---");
    println!("{}", extracted_text);

    Ok(())
}

3. Page-by-Page Extraction

To extract text from specific pages:

use rust_pdf_box::prelude::*;
use rust_pdf_box::text::PDFTextStripper;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = PdfDocument::open("document.pdf")?;
    let stripper = PDFTextStripper::new();

    let page_count = doc.page_count();
    for page_num in 1..=page_count {
        let page_text = stripper.get_page_text(&doc.inner, page_num)?;
        println!("=== Page {} ===", page_num);
        println!("{}", page_text);
    }

    Ok(())
}

4. CLI Extraction

You can also use the standalone pdfbox command-line tool:

# Print to stdout
pdfbox text document.pdf

# Save to output file
pdfbox text document.pdf extracted_output.txt

Apache PDFBox to rust-pdf-box Migration Guide

This guide helps Java developers familiar with Apache PDFBox transition seamlessly to rust-pdf-box. It highlights conceptual equivalents, architectural differences, memory management benefits, and side-by-side code comparisons.


1. Architectural Philosophy

DimensionApache PDFBox (Java)rust-pdf-box (Rust)
Runtime & DependenciesRequires JVM (JDK 8+), off-heap memory buffers, BouncyCastle for CMSPure Rust, zero C-libraries, single lightweight static binary
Concurrency & SafetyManual synchronization, mutable shared stateSafe multithreading (Send + Sync), compiler-enforced borrow checker
Resource ManagementAutoCloseable / close(), garbage collection pausesRAII (deterministic destruction), minimal allocation, zero memory leaks
API ParadigmImperative object graphs with mutable settersContext-oriented fluent builders with strict type guarantees
Cryptographic SignaturesHeavy external provider configuration (BouncyCastle, PKCS#11)Pure Rust self-signed key generation, ISO 32000 detached PKCS#7 signing

2. Class & Struct Mapping Cheat Sheet

Apache PDFBox (Java)rust-pdf-box (Rust)Notes
org.apache.pdfbox.pdmodel.PDDocumentrust_pdf_box::facade::PdfDocument or rust_pdf_box::pd::PDDocumentHigh-level document facade or granular PD model
org.apache.pdfbox.pdmodel.PDPagerust_pdf_box::pd::PDPagePage object holding media box and resources
org.apache.pdfbox.pdmodel.common.PDRectanglerust_pdf_box::pd::PDRectangleStandard paper sizes (LETTER, A4, LEGAL, custom)
org.apache.pdfbox.pdmodel.PDPageContentStreamrust_pdf_box::content::PDPageContentStreamVector drawing, color models, text matrix
org.apache.pdfbox.pdmodel.font.PDType1Fontrust_pdf_box::font::PDType1FontBuilt-in Standard 14 PDF fonts with glyph width tables
org.apache.pdfbox.pdmodel.font.PDTrueTypeFontrust_pdf_box::font::PDTrueTypeFontEmbedded TrueType fonts (.ttf parsing)
org.apache.pdfbox.pdmodel.graphics.image.PDImageXObjectrust_pdf_box::image::PDImageXObjectNative JPEG passthrough (/DCTDecode) & PNG with /SMask
org.apache.pdfbox.text.PDFTextStripperrust_pdf_box::text::PDFTextStripperContent stream text extraction & layout reconstruction
org.apache.pdfbox.multipdf.PDFMergerUtilityrust_pdf_box::manipulation::PDFMergerMerging PDFs with conflict-free object ID remapping
org.apache.pdfbox.multipdf.Splitterrust_pdf_box::manipulation::PDFSplitterSplitting documents into page ranges or single pages
org.apache.pdfbox.pdmodel.interactive.form.PDAcroFormrust_pdf_box::interactive::PDAcroFormForm inspection, setting values, flattening
org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignaturerust_pdf_box::sign::SignerCryptographic signature creation & appearance generation
org.apache.pdfbox.cos.* (COSDictionary, COSArray, etc.)rust_pdf_box::cos::*Direct low-level access to the PDF object tree

3. Side-by-Side Code Comparisons

3.1 Creating a Blank Document with Text

Apache PDFBox (Java):

try (PDDocument doc = new PDDocument()) {
    PDPage page = new PDPage(PDRectangle.A4);
    doc.addPage(page);

    try (PDPageContentStream stream = new PDPageContentStream(doc, page)) {
        stream.beginText();
        stream.setFont(PDType1Font.HELVETICA_BOLD, 16);
        stream.newLineAtOffset(50, 750);
        stream.showText("Hello from Apache PDFBox!");
        stream.endText();
    }

    doc.save("output.pdf");
}

rust-pdf-box (Rust):

use rust_pdf_box::prelude::*;

fn main() -> PdfResult<()> {
    let mut doc = PdfDocument::create();
    let page_idx = doc.add_page(PDRectangle::A4);

    let mut stream = PDPageContentStream::new(&mut doc.inner, page_idx, AppendMode::Overwrite)?;
    stream.begin_text()?;
    let font_ref = stream.set_font_type1(&mut doc.inner, page_idx, PDType1Font::HelveticaBold, 16.0)?;
    stream.move_text_position(50.0, 750.0)?;
    stream.show_text("Hello from rust-pdf-box!")?;
    stream.end_text()?;

    doc.save("output.pdf")?;
    Ok(())
}

3.2 Stamping & Watermarking

In Apache PDFBox, adding a background watermark across multiple pages typically requires building a secondary overlay PDF with Overlay or manually opening a content stream in PREPEND mode for every page.

rust-pdf-box Fluent Context API:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let mut doc = PdfDocument::open("document.pdf")?;

// 1. Semi-transparent diagonal watermark across all pages
doc.stamp()
    .pages(PageRange::All)
    .watermark("INTERNAL ONLY", 48.0, Color::rgb(0.9, 0.1, 0.1), 45.0, 0.25)
    .apply(&mut doc.inner)?;

// 2. Official rubber stamp on the first page
doc.stamp()
    .pages(PageRange::Single(1))
    .position(StampPosition::TopRight { margin_x: 36.0, margin_y: 36.0 })
    .rubber_stamp("APPROVED", Some("Audit Team".into()), Color::rgb(0.0, 0.5, 0.2), -10.0)
    .apply(&mut doc.inner)?;

// 3. Running page numbers in the footer
doc.stamp()
    .pages(PageRange::All)
    .footer("Page {page} of {total}", StampPosition::BottomCenter { margin_y: 25.0 }, 10.0, Color::GRAY)
    .apply(&mut doc.inner)?;

doc.save("stamped.pdf")?;
}

3.3 Digital Signatures (PKCS#7 / CMS)

In Apache PDFBox, signing requires implementing SignatureInterface, providing a Java Keystore (KeyStore.getInstance("PKCS12")), wiring BouncyCastle CMS generators, calculating byte ranges manually, and managing temporary files.

rust-pdf-box Pure Rust Digital Signing:

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let mut doc = PdfDocument::open("contract.pdf")?;

// Generate self-signed RSA key in memory (or load standard PEM / PKCS#8 key)
let key = SigningKey::generate_self_signed("CN=Techwasti Signer, O=Techwasti Infotech")?;

let signer = Signer::new(key)
    .with_name("Techwasti Infotech")
    .with_reason("Contract Authorization")
    .with_location("Mumbai, India")
    .with_visual_stamp(
        VisualSignature::at_page(1)
            .rect(50.0, 50.0, 260.0, 80.0)
            .title("Digitally Signed Document")
            .detail("Authorized by Techwasti Security\nIntegrity: ISO 32000 Detached PKCS#7")
    );

// Produces signed PDF bytes with cryptographically valid /ByteRange and /Contents
let signed_bytes = doc.sign(signer)?;
std::fs::write("contract_signed.pdf", &signed_bytes)?;

// Cryptographic verification
let report = SignatureVerifier::verify(&signed_bytes)?;
assert!(report.is_valid);
}

3.4 AcroForms & Field Flattening

Apache PDFBox (Java):

PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
    PDField field = acroForm.getField("EmployeeName");
    if (field instanceof PDTextField) {
        field.setValue("Alex Johnson");
    }
    acroForm.flatten();
}

rust-pdf-box (Rust):

#![allow(unused)]
fn main() {
use rust_pdf_box::prelude::*;

let mut doc = PdfDocument::open("application_form.pdf")?;

if let Some(mut form) = doc.inner.catalog_mut()?.acro_form()? {
    // Inspect or update fields
    form.set_field_value(&mut doc.inner, "EmployeeName", "Alex Johnson")?;
    form.set_field_value(&mut doc.inner, "Approved", "Yes")?;

    // Burn interactive fields into static visible page graphics
    form.flatten(&mut doc.inner)?;
}

doc.save("application_flattened.pdf")?;
}

4. Key Advantages of rust-pdf-box

  1. Performance: No garbage collection pauses, lower memory consumption, and near-instant cold starts (ideal for AWS Lambda, Google Cloud Run, Docker containers, and edge microservices).
  2. Safe Concurrency: Immutable PDF documents can be read simultaneously across threads without locks.
  3. Ergonomic Facade: Common workflows take 3 lines of code instead of 30 lines of boilerplate.
  4. Self-Contained: No external native binaries (libpng, libjpeg, libssl, poppler) required.

Enterprise Integration & Artifacts

rust-pdf-box can be integrated into your projects in multiple ways: as a native Rust library, as a pre-compiled standalone CLI binary, as a step in your CI/CD workflows, or from languages like Python, Node.js, and Go.


1. Using as a Rust Dependency

Add rust-pdf-box directly to your Cargo.toml:

[dependencies]
rust-pdf-box = "0.1.0"

Or run:

cargo add rust-pdf-box

Option B: Directly from GitHub

To track the latest updates on the main branch:

[dependencies]
rust-pdf-box = { git = "https://github.com/techwastiinfotech/rust-pdf-box.git", branch = "main" }

Or pin to a specific release tag:

[dependencies]
rust-pdf-box = { git = "https://github.com/techwastiinfotech/rust-pdf-box.git", tag = "v0.1.0" }

2. Using Pre-Compiled Binary Artifacts

For projects written in Python, Node.js, Go, PHP, or Shell Scripts, you can use the standalone pdfbox binary without needing Rust installed.

Pre-Compiled Platform Downloads

Every GitHub release publishes pre-compiled binaries with SHA-256 checksums:

PlatformTarget ArchitectureArchive
Linuxx86_64-unknown-linux-gnupdfbox-linux-x86_64.tar.gz
Windowsx86_64-pc-windows-msvcpdfbox-windows-x86_64.zip
macOS (Apple Silicon)aarch64-apple-darwinpdfbox-macos-aarch64.tar.gz
macOS (Intel)x86_64-apple-darwinpdfbox-macos-x86_64.tar.gz

Quick Install (Linux / macOS)

curl -sSL https://github.com/techwastiinfotech/rust-pdf-box/releases/latest/download/pdfbox-linux-x86_64.tar.gz | tar -xz
sudo mv pdfbox /usr/local/bin/
pdfbox --help

3. Using in Other GitHub Actions Workflows

You can integrate rust-pdf-box into any GitHub Actions workflow to watermark, sign, merge, or inspect PDFs during your build pipeline:

name: PDF Processing Pipeline

on: [push]

jobs:
  stamp-and-sign:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Download rust-pdf-box CLI
        run: |
          curl -sSL https://github.com/techwastiinfotech/rust-pdf-box/releases/latest/download/pdfbox-linux-x86_64.tar.gz | tar -xz
          chmod +x pdfbox

      - name: Watermark Document
        run: |
          ./pdfbox watermark document.pdf watermarked.pdf "CONFIDENTIAL" 0.3 45

      - name: Digitally Sign Document
        run: |
          ./pdfbox sign watermarked.pdf signed.pdf "Techwasti Automated CI" "Automated Verification" "Global"

      - name: Verify Cryptographic Signature
        run: |
          ./pdfbox verify signed.pdf

      - name: Upload Final PDF
        uses: actions/upload-artifact@v4
        with:
          name: processed-document
          path: signed.pdf

4. Using from Python or Node.js

Because the pdfbox binary is self-contained with zero runtime shared-library dependencies, you can invoke it from any language:

Python Example

import subprocess

# Stamp a watermark
subprocess.run([
    "pdfbox", "watermark",
    "invoice.pdf", "invoice_stamped.pdf",
    "PAID", "0.2", "45"
], check=True)

# Digitally sign
subprocess.run([
    "pdfbox", "sign",
    "invoice_stamped.pdf", "invoice_final.pdf",
    "Billing Department", "Invoice Clearance", "India"
], check=True)

# Verify signature
result = subprocess.run(["pdfbox", "verify", "invoice_final.pdf"], capture_output=True, text=True)
print(result.stdout)

Node.js Example

const { execFileSync } = require('child_process');

// Extract text
const text = execFileSync('pdfbox', ['text', 'report.pdf'], { encoding: 'utf-8' });
console.log('Extracted Text:', text);

// Merge documents
execFileSync('pdfbox', ['merge', 'combined.pdf', 'part1.pdf', 'part2.pdf']);

5. Docker Integration

Add pdfbox to your Docker containers with a single curl step:

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y curl ca-certificates && rm -rf /var/lib/apt/lists/*

RUN curl -sSL https://github.com/techwastiinfotech/rust-pdf-box/releases/latest/download/pdfbox-linux-x86_64.tar.gz | tar -xz -C /usr/local/bin

ENTRYPOINT ["pdfbox"]

API Documentation (Rustdoc)

The complete, item-level Rust documentation for rust-pdf-box is generated using Rust’s official cargo doc tool and bundled alongside this guide.



Module Overview

The rust-pdf-box crate is divided into the following key modules:

ModuleDescription
preludeThe most common types, builders, and enums re-exported for easy glob import (use rust_pdf_box::prelude::*;).
facadePdfDocument - high-level, context-oriented document management, stamping, and signing facade.
cosLow-level PDF object system: COSObject, COSDocument, COSStream, COSArray, COSDictionary, parser, and lexer.
pdHigh-level Page Description layer: PDDocument, PDPage, PDRectangle, PDResources, and PDDocumentCatalog.
contentContent stream engine: PDPageContentStream, path painting, clipping, color models, and graphics states.
stampFluent watermarking, rubber stamps, headers/footers, and page range selectors (PageRange, StampPosition).
signCryptographic signature engine: Signer, SignatureVerifier, visual appearances, and detached PKCS#7 signing.
cryptoPure-Rust cryptographic building blocks: ASN.1 DER encoder, X.509 v3 certificate generator, and RSA-SHA256 signer.
interactiveInteractive AcroForms (PDAcroForm, PDFormField), form flattening, and page annotations (PDAnnotation).
fontStandard 14 Type 1 PDF fonts (PDType1Font) and embedded TrueType fonts (PDTrueTypeFont).
imageImage embedding (PDImageXObject), JPEG direct pass-through, and PNG with transparency /SMask.
manipulationDocument assembly tools: PDFMerger and PDFSplitter.
textText extraction engine: PDFTextStripper.