easypdf-rust
An idiomatic Rust PDF toolkit -- create, read, manipulate, convert, encrypt, and sign.
Inspired by Alibaba EasyExcel's builder-pattern API design.
Version:
0.1.1· MSRV: Rust1.88· Edition:2024· License: Apache-2.0
Architecture
9-crate workspace with clean separation of concerns:
flowchart TB
facade["<b>easypdf</b>\nEasyPdf facade + builders"]
core["<b>easypdf-core</b>\ntypes + errors + crypto + model + io + layout"]
derive["<b>easypdf-derive</b>\n#[derive(PdfModel)]"]
reader["<b>easypdf-reader</b>\nread + manipulate + streaming"]
writer["<b>easypdf-writer</b>\nwrite + template + backends"]
markdown["<b>easypdf-markdown</b>\npipeline + table + OCR + render"]
ocr["<b>easypdf-ocr</b>\ncloud OCR engines"]
runtime["<b>easypdf-runtime</b>\nMCP server + resident daemon"]
test["<b>easypdf-test</b>\nintegration tests"]
facade --> reader & writer & markdown & ocr
runtime --> reader & writer & markdown
markdown --> reader & core
reader --> core
writer --> core
ocr --> markdown & core
derive --> core
test --> facade
style facade fill:#e1f5fe
style core fill:#fff3e0
style runtime fill:#f3e5f5
Key Capabilities
| Capability | Status | Details |
|---|---|---|
| PDF creation | Stable | Builder pattern, text/images/shapes, custom fonts, metadata |
| PDF reading | Stable | 3 strategies (Full/Lazy/Streaming), session reuse (~129x faster) |
| Page manipulation | Stable | Merge, split, rotate, reorder, watermark, extract |
| Form filling | Stable | AcroForm field mapping via #[derive(PdfModel)] |
| PDF to Markdown | Preview | Pipeline with profiles, table detection, OCR fallback |
| Cloud OCR | Preview | GLM, HunyuanOCR, Baidu -- synchronous HTTP |
| Encryption | Stable | AES-128/256, permission control, ISO 32000 compliant |
| Digital signing | Stable | PKCS#7/CMS, RSA-PKCS#1v1.5 + SHA-256, X.509 |
| MCP server | Preview | 7 tools for LLM agent integration |
| Resident daemon | Preview | In-memory sessions via TCP / Unix socket |
Quick Start
# Cargo.toml
[]
= "0.1.1"
Create a PDF:
use *;
create
.page
.add_text
.font
.position
.do_write?;
# Ok::
Read a PDF:
use *;
let text = read
.pages
.extract_text?;
# Ok::
Merge PDFs:
use *;
merge?;
# Ok::
Fill a form:
use *;
fill_form
.save?;
# Ok::
9-Crate Overview
| Crate | Role | Key Types |
|---|---|---|
| easypdf | Facade + builder API | EasyPdf, PdfCreateBuilder, PdfReadBuilder, PdfManipulateBuilder |
| easypdf-core | Core types, traits, crypto, model, IO, layout | PdfError, PdfBlock, PdfDocumentModel, PdfEncryption, PdfSigner |
| easypdf-derive | #[derive(PdfModel)] proc-macro |
PdfModel derive, field attributes |
| easypdf-reader | PDF parsing, text extraction, page operations | PdfReader, PdfManipulator, ReadStrategy |
| easypdf-writer | PDF creation, template filling, backend selection | PdfWriter, PdfTemplateFiller, WriteBackend |
| easypdf-markdown | PDF to Markdown conversion pipeline | ProcessorPipeline, MarkdownRenderer, MarkdownProfile |
| easypdf-ocr | Cloud OCR engine collection | GlmConfig, HunyuanConfig, BaiduConfig |
| easypdf-runtime | MCP server + resident daemon | McpServer, ResidentServer, ResidentClient |
| easypdf-test | Integration tests + golden samples | Test harness |
PDF Creation (Builder Pattern)
The writer supports text, images, shapes, custom fonts, and metadata:
use *;
let writer = writer
.backend // 10 MB threshold
.build?;
// WriteBackend::InMemory -- default, fast for small docs
// WriteBackend::Spill -- page-level temp files, constant memory
// WriteBackend::Auto -- auto-select by threshold
# Ok::
PDF Reading (3 Strategies)
PdfReader automatically selects the optimal strategy based on file size:
| File Size | Strategy | Behavior |
|---|---|---|
| 0 -- 5 MB | Full |
Load entire document into memory |
| 5 -- 100 MB | Lazy |
Parse headers, load pages on demand |
| > 100 MB | Streaming |
Byte-stream scan, no Document construction |
Session reuse parses the document once and reuses the in-memory representation -- ~129x faster than re-opening for repeated access.
Markdown Conversion
PDF to Markdown with profiles, table detection, and OCR fallback:
use *;
export_markdown
.pages
.profile
.tables
.ocr
.do_export?;
# Ok::
| Profile | Use Case |
|---|---|
MarkdownProfile::Gfm |
GitHub/GitLab rendering with GFM tables |
MarkdownProfile::Llm |
Token-efficient markup for LLM context |
MarkdownProfile::Plain |
Human-readable plain text |
Pipeline flow: PDF -> PdfReader -> PdfDocumentModel -> ProcessorPipeline -> MarkdownRenderer -> String
Encryption and Signing
AES-128/256 encryption with permission control:
use *;
let enc = new
.with_algorithm
.with_permissions;
let encrypted = encrypt_pdf?;
# Ok::
PKCS#7 digital signatures with RSA-PKCS#1v1.5 + SHA-256 (via ring):
use *;
let signer = new
.with_reason
.with_location;
let signed = sign_pdf?;
let info = verify_pdf_signature?;
# Ok::
Resident Daemon and MCP Server
Resident daemon keeps PDF sessions in memory across requests:
use EasyPdf;
// Start daemon (blocks):
serve?;
// Attach from another process:
if let Some = attach
MCP server exposes 7 tools for LLM agent integration:
| Tool | Description |
|---|---|
pdf_read_text |
Extract text from PDF |
pdf_to_markdown |
Convert PDF to Markdown |
pdf_create_text |
Create text PDF |
pdf_merge |
Merge multiple PDFs |
pdf_split |
Split PDF into pages |
pdf_metadata |
Extract document metadata |
pdf_page_count |
Get page count |
use EasyPdf;
let server = mcp_server;
server.run?;
Performance
Benchmarked against pdftotext (Poppler) on Apple M4 Pro:
| Metric | easypdf | pdftotext | Result |
|---|---|---|---|
| 100-page extraction | 2.4 ms | 17 ms | ~7x faster |
| Peak memory (small files) | ~7 MB | ~10 MB | 29% less |
| Peak memory (100 pages) | 8.7 MB | 10.5 MB | 17% less |
| Text accuracy (avg) | 89% | baseline | 92--98% on structured PDFs |
| Session reuse | ~1,047 ns | ~135,011 ns | ~129x faster |
Test Coverage
| Metric | Value |
|---|---|
| Tests passed | 136 |
| Code coverage | 91.61% |
| Total Rust code | ~52,626 lines |
| Crates | 9 |
Cargo Features
| Feature | Enables | Default |
|---|---|---|
markdown |
PDF to Markdown pipeline | Yes |
markdown-table |
Table detection in markdown | No |
markdown-ocr |
OCR fallback for scanned pages | No |
ocr |
Cloud OCR (GLM/Hunyuan/Baidu) | No |
render |
PDF page rendering to PNG | No |
html |
HTML to PDF (requires Chromium) | No |
runtime |
Resident daemon + MCP server | No |
mcp |
MCP server only | No |
resident |
Resident daemon only | No |
full |
Everything enabled | No |
# Default: markdown enabled
= "0.1.1"
# Minimal build (no markdown)
= { = "0.1.1", = false }
# Enable everything
= { = "0.1.1", = ["full"] }
Toolchain
| Item | Value |
|---|---|
| MSRV | Rust 1.88 |
| Edition | 2024 |
| Resolver | 3 |
| unsafe | forbid (workspace-wide) |
| Platform | macOS / Linux / Windows |
Documentation
| Document | Description |
|---|---|
| Architecture (EN) | Architecture design document |
| Architecture (中文) | 架构设计文档 |
| Usage Guide | Complete API guide with 12 chapters |
| Benchmark Report | Performance baseline vs pdftotext |
| Compatibility | Feature matrix + coverage report |
| 版本规划 | Version plan and roadmap |
| Changelog | Version history |
| Contributing | Development setup and conventions |
Roadmap
| Metric | Current |
|---|---|
| Tests passing | 1522 |
| Test coverage | 91.61% |
| Cargo audit CVEs | 0 |
| Clippy warnings | 0 |
| Rustdoc warnings | 0 |
| Fuzz targets | 6 |
| crates.io published | v0.1.1 (8 crates) |
| Workspace crates | 9 (consolidated from 22) |
| Version | Focus | Status |
|---|---|---|
| v0.1 | Foundation: core types, read/write/manipulate/template, derive macro, 22-crate-to-9-crate consolidation, EasyPdf builder, #[derive(PdfModel)], PDF read/write/merge/split/rotate/reorder, AcroForm template fill, PDF-to-Markdown, atomic output, resource limits, #![forbid(unsafe_code)], 136 tests |
Done |
| v0.2 | Architecture Consolidation: 22-to-9 crate consolidation, Streaming ReadStrategy, CMap/ToUnicode (CJK), WriteBackend selection (InMemory/Spill/Auto), ConverterRegistry, 4 cloud OCR engines (GLM/Hunyuan/Baidu/DeepSeek), Resident daemon (Unix socket + Windows TCP), MCP server (7 tools), PdfBlock IR expanded to 14 variants, ProcessorPipeline, ISO 32000 encryption (AES-128/256), PKCS#7 signature, tracing observability, security fixes (rsa-to-ring, SSRF IPv6, API key redact), cargo-fuzz (6 targets), 91.61% coverage, v0.1.0 published to crates.io | Done |
| v0.3 | Rich Content: add_table Builder API, table border style enhancements (zebra striping, custom borders), image insertion (JPEG/PNG) with size/position control, vector shapes (lines, rectangles, circles), custom TTF/OTF font registration and embedding, multi-page writer with automatic page breaks |
In Progress |
| v0.4 | Security: AES-256 encryption/decryption, password protection (user + owner), permission flags (print/copy/modify/annotate), PDF-to-Markdown OCR real integration | Mostly Done |
| v0.5 | Compliance: PDF/A-1b, PDF/A-2b, PDF/A-3b validation, XMP metadata, document info dictionary standardization | Planned |
| v0.6 | Converters: HTML-to-PDF (Chromium-based, feature-gated), Markdown-to-PDF optimization, SVG-to-PDF, PDF-to-image rasterize | Partial |
| v1.0 | Stable: public API on crates.io, semver guarantees (0.2.x to 0.3.x to 1.0), CI matrix (Linux + macOS), Windows MSRV testing, property-based testing, complete migration guide | Planned |
详见 docs/superpowers/version-plan.md。
Contributing
Before submitting, run all quality gates:
New public API must include docs, examples, tests, and SemVer impact notes.
License
Licensed under Apache-2.0.
Related Projects
- easyexcel-rs -- Rust port of Alibaba EasyExcel
- easyexcel -- Original Java library
- lopdf -- Pure Rust PDF manipulation
- printpdf -- Pure Rust PDF generation
Back to top · docs.rs · crates.io · Issues