Skip to content

Repository files navigation

easypdf-rust

An idiomatic Rust PDF toolkit -- create, read, manipulate, convert, encrypt, and sign.

Inspired by Alibaba EasyExcel's builder-pattern API design.

Crates.io docs.rs MSRV License unsafe forbidden tests

English · 简体中文


Version: 0.1.1 · MSRV: Rust 1.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
Loading

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
[dependencies]
easypdf = "0.1.1"

Create a PDF:

use easypdf::prelude::*;

EasyPdf::create("output.pdf")
    .page(PageSize::A4)
    .add_text("Hello, world!")
        .font(PdfFont::helvetica(12.0))
        .position(72.0, 700.0)
    .do_write()?;
# Ok::<(), easypdf::PdfError>(())

Read a PDF:

use easypdf::prelude::*;

let text = EasyPdf::read("input.pdf")
    .pages(0..10)
    .extract_text()?;
# Ok::<(), easypdf::PdfError>(())

Merge PDFs:

use easypdf::prelude::*;

EasyPdf::merge(&["a.pdf", "b.pdf", "c.pdf"], "merged.pdf")?;
# Ok::<(), easypdf::PdfError>(())

Fill a form:

use easypdf::prelude::*;

#[derive(PdfModel)]
struct MyData {
    #[pdf(field = "name")]
    name: String,
}

EasyPdf::fill_form("template.pdf", &MyData { name: "Alice".into() })
    .save("filled.pdf")?;
# Ok::<(), easypdf::PdfError>(())

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 easypdf::prelude::*;

let writer = EasyPdf::writer("My Report")
    .backend(WriteBackend::auto(10 * 1024 * 1024))  // 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::<(), easypdf::PdfError>(())

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 easypdf::prelude::*;

EasyPdf::export_markdown("input.pdf", "output.md")
    .pages(0..20)
    .profile(MarkdownProfile::Llm)
    .tables(TablePolicy::Detect)
    .ocr(OcrPolicy::Auto)
    .do_export()?;
# Ok::<(), easypdf::PdfError>(())
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 easypdf::prelude::*;

let enc = PdfEncryption::new("user_pass", "owner_pass")
    .with_algorithm(PdfEncryptionAlgorithm::Aes256)
    .with_permissions(PdfPermissions::PRINT | PdfPermissions::COPY);

let encrypted = encrypt_pdf(&pdf_bytes, &enc)?;
# Ok::<(), easypdf::PdfError>(())

PKCS#7 digital signatures with RSA-PKCS#1v1.5 + SHA-256 (via ring):

use easypdf::prelude::*;

let signer = PdfSigner::new(cert_pem, key_pem)
    .with_reason("Document approval")
    .with_location("Beijing");

let signed = sign_pdf(&pdf_bytes, &signer)?;
let info = verify_pdf_signature(&signed)?;
# Ok::<(), easypdf::PdfError>(())

Resident Daemon and MCP Server

Resident daemon keeps PDF sessions in memory across requests:

use easypdf::EasyPdf;

// Start daemon (blocks):
EasyPdf::serve(None)?;

// Attach from another process:
if let Some(client) = EasyPdf::attach() {
    // use client to interact with the daemon
}

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::EasyPdf;

let server = EasyPdf::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
easypdf = "0.1.1"

# Minimal build (no markdown)
easypdf = { version = "0.1.1", default-features = false }

# Enable everything
easypdf = { version = "0.1.1", features = ["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:

cargo check -p easypdf --no-default-features
cargo check -p easypdf --all-features
cargo test --workspace --quiet
cargo doc --workspace --no-deps

New public API must include docs, examples, tests, and SemVer impact notes.

License

Licensed under Apache-2.0.

Related Projects


About

An idiomatic Rust library for quick PDF operations — builder API for creating, reading, manipulating, and filling PDFs. Pure Rust, zero unsafe.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages