lopdf
A pure Rust library for low-level PDF document creation, parsing, editing, and merging
Repository Health
Technical Analysis
lopdf is a Rust library that gives developers direct, low-level access to the internal object graph of a PDF document — dictionaries, streams, cross-reference tables, and the trailer — rather than a high-level “add text here” API. It can parse existing PDFs, mutate their object trees in place, merge multiple documents into one, and serialize the result back to a valid PDF file, including compressed object streams for smaller output.
Because it works at the object level, lopdf is often reached for by tools that need precise control over PDF internals: form fillers, PDF mergers/splitters, redaction tools, and document-processing pipelines that need to inspect or rewrite specific PDF objects rather than treat the file as an opaque blob.
What You Get
- A
Documenttype modeling the full PDF object graph (dictionaries, streams, arrays, references) - Parsing of existing PDFs including xref tables, object streams, and linearized files
- PDF merging utilities with bookmark/outline preservation across merged documents
- Content stream encoding/decoding for reading and writing page operators (text, graphics)
- Encryption and decryption support (RC4/AES) including password-protected PDFs
- Optional feature flags for font embedding, async I/O (tokio), and serde support
Common Use Cases
- Programmatically generating PDF reports or invoices from application data
- Merging multiple PDF files into a single document while preserving bookmarks
- Extracting text and metadata from existing PDF files
- Building redaction or form-filling tools that need direct object-level edits
- Decrypting and inspecting password-protected PDFs for archival processing
Under The Hood
Architecture: lopdf centers on a Document struct holding a BTreeMap<ObjectId, Object> plus a trailer dictionary, directly mirroring the PDF file format’s own object-graph structure (src/document.rs, src/object.rs). Reading a file (src/reader.rs, ~1500 lines) parses the header, cross-reference table/streams, and trailer, then lazily resolves indirect references into this map; writing (src/writer.rs) walks the same map to serialize objects, optionally grouping them into compressed PDF 1.5+ object streams (src/object_stream.rs) via save_modern(). Content streams (text/graphics operators) are handled separately in src/content.rs, keeping page-drawing concerns decoupled from the object model.
Tech Stack: Pure Rust (edition 2024, MSRV 1.88) with #![forbid(unsafe_code)]. Core dependencies are narrowly scoped to their task: nom for binary/text parsing, flate2 for stream compression, aes/cbc/ecb/md-5/sha2 for encryption, indexmap for order-preserving dictionaries, and thiserror for error types. Heavier capabilities (image, tokio, serde, rayon, chrono/jiff, font embedding) are all gated behind optional Cargo features, keeping the default build lean.
Code Quality: The crate ships an extensive tests/ suite (12+ integration test files covering object streams, linearized files, fonts, metadata, encryption edge cases) plus numerous runnable examples/. Error handling is centralized through a thiserror-based Error/ParseError/DecompressError enum rather than panics, and clippy::all is denied at the crate root, indicating an enforced lint baseline.
API Design: The public surface (src/lib.rs) re-exports a compact set of types — Document, Object, Dictionary, Stream, Reader — with a dictionary! macro that makes constructing nested PDF dictionaries readable without heavy boilerplate. The trade-off for this low-level, spec-mirroring design is a real learning curve: callers need PDF-format literacy (object IDs, xref, content operators) that a page-drawing abstraction would hide, though the README’s FAQ and worked examples mitigate this.
Used by 2 apps in this directory
cocoindex
Data Engineering · AI Development
An incremental data indexing engine that keeps AI agent context perpetually fresh by reprocessing only what changed.
PostgresML
Databases · AI Development
Run ML training and LLM inference natively inside PostgreSQL with GPU acceleration — no data movement required.