Read, edit, encrypt, sanitize and extract text from PDFs: pure Rust, built for untrusted input
crates.io | Docs | API guide | Embedding guide
An Open Tech Foundation project
A PDF is opened lazily: objects are parsed on first use, damaged files are repaired (and every repair reported), and edits go into a copy-on-write overlay, so a save can rewrite the file cleanly or append just the changes.
use ;
let doc = open?;
println!;
println!;
let mut doc = doc;
doc.delete_pages?;
write?;
[!NOTE] Pre-1.0. The API can change between minor versions; see the changelog.
What it does
| Area | |
|---|---|
| ๐ Read | PDF 1.0โ2.0; classic and stream xrefs, object streams, incremental updates; page tree with inheritance; Info and XMP metadata, outlines, attachments |
| ๐ Decrypt | Standard security handler R2โR6 (RC4, AES-128, AES-256), user and owner passwords; empty passwords open automatically |
| ๐ฉน Repair | Rebuilds broken xrefs, recovers trailers, catalogs and page trees, fixes wrong stream lengths; every repair is a reported diagnostic |
| โ๏ธ Edit | Merge, split, extract, reorder, delete, rotate and crop pages; outlines, named destinations, metadata (Info and XMP kept in sync), attachments |
| ๐พ Save | Full save (garbage-collected, compressed, object streams) or incremental save (original bytes kept as a prefix); deterministic output |
| ๐ Encrypt | AES-256 (R6) with permissions; legacy RC4/AES-128 are read, never written |
| ๐งน Sanitize | Remove JavaScript, launch/submit actions, attachments, XFA, multimedia and, optionally, links and metadata |
| ๐ค Extract text | Lines and words with positions; Type1, TrueType, Type3 and CID fonts, ToUnicode, CMaps, Adobe CJK collections; form field text |
Not yet: creating PDFs from scratch, rendering, forms filling, signatures, PDF/A. See the plan.
Why
| Need | How it is met |
|---|---|
| ๐ฆ Pure Rust | No C libraries; built from scratch on a small, pinned dependency set (RustCrypto, flate2, Fontations) |
| ๐ก๏ธ Safe on hostile input | unsafe is forbidden; malformed files are errors or repairs, never panics; every resource has a limit and a CPU budget hook; seven fuzz targets |
| ๐งช Checked against references | 3,842 real-world test files; files we write are verified by qpdf and poppler; text extraction scored against poppler and known source text |
| ๐ Embeddable | No I/O, clock, randomness or global state of its own; input through a Source trait, output through a Sink, entropy injected; types are Send + Sync |
Quality, measured
| Check | Result |
|---|---|
| Open with every page enumerated | 3,832 of 3,842 corpus files; page counts match poppler |
| Open โ save โ open, page content identical | Every file that opens |
| Files written, checked by qpdf and poppler | 695 files, 0 problems |
| Decryption, known plaintext (qpdf-encrypted, R2โR6) | 11 of 11 |
| Text: character error rate vs poppler (Latin, CJK) | 0.35%, 0.0% |
| Text: character error rate vs source (Hindi, Bengali, Tamil, Telugu, Chinese, Japanese, Korean) | 0.0% |
Install
[]
= "0.1"
Rust 1.88 or later. The otf-pdf-cli crate in the repository is a
development tool (otf-pdf info|pages|text|save|merge|sanitize โฆ), not
published.
License
Apache-2.0. See NOTICE for the font data from pdf.js and Adobe.