rust-pdf-box Documentation
A comprehensive, pure Rust implementation of Apache PDFBox featuring modern, context-oriented, and ergonomic PDF processing.
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 usingPDFTextStripper. -
🔄 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.
Project Links & Community
- GitHub Repository: techwastiinfotech/rust-pdf-box
- Crates.io Package: crates.io/crates/rust-pdf-box
- Docs.rs Reference: docs.rs/rust-pdf-box
- License: MIT or Apache-2.0
- Maintained by: Techwasti Infotech
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 arm64andIntel 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#XXescapes, and binary stream payloads.Parser: Capable of reading backwards to locatestartxref, 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), andW n(clipping). - Color Systems: RGB, CMYK, Grayscale, and Hex conversion.
- Transparency: Automatic
/ExtGStateregistration 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
SignedDatastructures 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/)
PDFTextStripperparsesBT ... ET,Tj,TJ,',",Td, andTmoperators, 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 Variant | Description | Example |
|---|---|---|
PageRange::All | Applies to every page in the document | doc.stamp().pages(PageRange::All) |
PageRange::Single(n) | Applies to 1-based page n | doc.stamp().pages(PageRange::Single(1)) |
PageRange::Range(a..=b) | Applies to range a through b (inclusive) | doc.stamp().pages(PageRange::Range(2..=5)) |
PageRange::Odd | Applies to odd pages (1, 3, 5, …) | doc.stamp().pages(PageRange::Odd) |
PageRange::Even | Applies to even pages (2, 4, 6, …) | doc.stamp().pages(PageRange::Even) |
PageRange::Pages(vec) | Applies to specific page numbers | doc.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
- Placeholder Allocation: The document is prepared with
/ByteRange [ 0 offset1 offset2 len2 ]and/Contents <000...000>. - Digest Calculation: SHA-256 is computed over Part 1 and Part 2.
- PKCS#7 / CMS Generation:
ContentType:id-data(1.2.840.113549.1.7.1)SignedAttributes:contentType:id-datamessageDigest: SHA-256 hash of Part 1 + Part 2signingTime: timestamp
SignerInfo: RSA-SHA256 signature calculated over DER encodedSignedAttributes.
- 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 Type | PDF Type Code | Description |
|---|---|---|
FormFieldType::Text | /Tx | Single-line or multi-line text input fields |
FormFieldType::Button | /Btn | Pushbuttons, checkboxes (/Yes or /Off), and radio buttons |
FormFieldType::Choice | /Ch | Dropdown combo boxes and multi-selection list boxes |
FormFieldType::Signature | /Sig | Cryptographic signature placeholders |
FormFieldType::Unknown | Custom | Vendor-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
Clickable Hyperlink Annotations (/Link)
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) andET(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
| Dimension | Apache PDFBox (Java) | rust-pdf-box (Rust) |
|---|---|---|
| Runtime & Dependencies | Requires JVM (JDK 8+), off-heap memory buffers, BouncyCastle for CMS | Pure Rust, zero C-libraries, single lightweight static binary |
| Concurrency & Safety | Manual synchronization, mutable shared state | Safe multithreading (Send + Sync), compiler-enforced borrow checker |
| Resource Management | AutoCloseable / close(), garbage collection pauses | RAII (deterministic destruction), minimal allocation, zero memory leaks |
| API Paradigm | Imperative object graphs with mutable setters | Context-oriented fluent builders with strict type guarantees |
| Cryptographic Signatures | Heavy 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.PDDocument | rust_pdf_box::facade::PdfDocument or rust_pdf_box::pd::PDDocument | High-level document facade or granular PD model |
org.apache.pdfbox.pdmodel.PDPage | rust_pdf_box::pd::PDPage | Page object holding media box and resources |
org.apache.pdfbox.pdmodel.common.PDRectangle | rust_pdf_box::pd::PDRectangle | Standard paper sizes (LETTER, A4, LEGAL, custom) |
org.apache.pdfbox.pdmodel.PDPageContentStream | rust_pdf_box::content::PDPageContentStream | Vector drawing, color models, text matrix |
org.apache.pdfbox.pdmodel.font.PDType1Font | rust_pdf_box::font::PDType1Font | Built-in Standard 14 PDF fonts with glyph width tables |
org.apache.pdfbox.pdmodel.font.PDTrueTypeFont | rust_pdf_box::font::PDTrueTypeFont | Embedded TrueType fonts (.ttf parsing) |
org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject | rust_pdf_box::image::PDImageXObject | Native JPEG passthrough (/DCTDecode) & PNG with /SMask |
org.apache.pdfbox.text.PDFTextStripper | rust_pdf_box::text::PDFTextStripper | Content stream text extraction & layout reconstruction |
org.apache.pdfbox.multipdf.PDFMergerUtility | rust_pdf_box::manipulation::PDFMerger | Merging PDFs with conflict-free object ID remapping |
org.apache.pdfbox.multipdf.Splitter | rust_pdf_box::manipulation::PDFSplitter | Splitting documents into page ranges or single pages |
org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm | rust_pdf_box::interactive::PDAcroForm | Form inspection, setting values, flattening |
org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature | rust_pdf_box::sign::Signer | Cryptographic 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
- 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).
- Safe Concurrency: Immutable PDF documents can be read simultaneously across threads without locks.
- Ergonomic Facade: Common workflows take 3 lines of code instead of 30 lines of boilerplate.
- 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
Option A: From Crates.io (Recommended)
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:
| Platform | Target Architecture | Archive |
|---|---|---|
| Linux | x86_64-unknown-linux-gnu | pdfbox-linux-x86_64.tar.gz |
| Windows | x86_64-pc-windows-msvc | pdfbox-windows-x86_64.zip |
| macOS (Apple Silicon) | aarch64-apple-darwin | pdfbox-macos-aarch64.tar.gz |
| macOS (Intel) | x86_64-apple-darwin | pdfbox-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.
Direct Links to API Docs
Module Overview
The rust-pdf-box crate is divided into the following key modules:
| Module | Description |
|---|---|
prelude | The most common types, builders, and enums re-exported for easy glob import (use rust_pdf_box::prelude::*;). |
facade | PdfDocument - high-level, context-oriented document management, stamping, and signing facade. |
cos | Low-level PDF object system: COSObject, COSDocument, COSStream, COSArray, COSDictionary, parser, and lexer. |
pd | High-level Page Description layer: PDDocument, PDPage, PDRectangle, PDResources, and PDDocumentCatalog. |
content | Content stream engine: PDPageContentStream, path painting, clipping, color models, and graphics states. |
stamp | Fluent watermarking, rubber stamps, headers/footers, and page range selectors (PageRange, StampPosition). |
sign | Cryptographic signature engine: Signer, SignatureVerifier, visual appearances, and detached PKCS#7 signing. |
crypto | Pure-Rust cryptographic building blocks: ASN.1 DER encoder, X.509 v3 certificate generator, and RSA-SHA256 signer. |
interactive | Interactive AcroForms (PDAcroForm, PDFormField), form flattening, and page annotations (PDAnnotation). |
font | Standard 14 Type 1 PDF fonts (PDType1Font) and embedded TrueType fonts (PDTrueTypeFont). |
image | Image embedding (PDImageXObject), JPEG direct pass-through, and PNG with transparency /SMask. |
manipulation | Document assembly tools: PDFMerger and PDFSplitter. |
text | Text extraction engine: PDFTextStripper. |