Skip to main content

pdfmuse

English · 中文

crates.io PyPI npm CI live demo license

▶ Live playground — drag a PDF, watch it parse in your browser (nothing is uploaded)

pdfmuse playground: original PDF ↔ pdfmuse reconstruction

Deterministic PDF/DOCX parser for RAG / LLMs — one Rust core, with Python, Node & WASM bindings that produce byte-identical output.

pdfmuse is a precision pre-layer for AI/RAG: it extracts everything a file actually contains — text with exact coordinates, fonts, vector rules, tables, links — fast, robustly, and identically across every binding. It stops cleanly at the ML boundary: OCR and visual layout inference are left to a pluggable backend, so the core stays deterministic with zero ML dependencies. It is not another probabilistic vision model.

Why pdfmuse

Complete Keeps the finest-grained chars + coordinates; never silently drops content.
Fast Zero-copy streaming Rust core with a custom O(1) object parser + content tokenizer and per-page parallelism.
Robust A broken page/object never sinks the doc — returns structured errors, never panics (fuzz-tested).
Deterministic Same input → same output. No probabilistic models, no time/RNG in the core path.
Consistent Python / Node / WASM call one Rust core; output is byte-identical (CI-enforced).
CJK first-class CID/Type0 fonts + CMap/ToUnicode in the main path; compatibility codepoints NFKC-normalized for clean search.

Performance

Two things matter for a RAG pre-layer: speed, and whether it keeps the content. Both are measured on a public, reproducible corpus — 61 arXiv papers across 8 fields (large, dense PDFs — a deliberately hard case), so you can rerun the exact benchmark:

python benches/fetch_corpus.py --out /tmp/corpus      # the same PDFs, from a fixed manifest
pip install "pdfmuse==0.1.10" "pymupdf==1.28.0" "pdfplumber==0.11.10"
python benches/compare.py --dir /tmp/corpus

Text extraction (to_text, median of 7 runs after warm-up; PyMuPDF 1.28 / MuPDF 1.29, pdfplumber 0.11, macOS arm64, 65 papers):

vs speedup (geomean) win rate worst case
PyMuPDF ~7.7× faster 65 / 65 (100%) still 2.5× faster
pdfplumber ~150× faster 65 / 65 (100%) 69×

pdfmuse is faster on every file in this corpus — including a 22 MB paper (9× faster) and a plot-heavy one that draws 18k marker glyphs. Content is preserved: median 100% of PyMuPDF's non-whitespace characters (n=65).

to_text() / to_markdown() return a string straight from the Rust core (no full-IR deserialization). The full parse() — chars + bboxes + tables, far more than text — costs only ~2.3× the to_text time, still under PyMuPDF on most files. The native Node binding is ~as fast as the Rust core; WASM ~1.7×.

Honest limit — reading order: extraction is complete (100% of chars) and deterministic, but flattening a 2-D page to 1-D text is where the hard cases live. Single-column, tables, and clean two-column read correctly; dense two-column academic PDFs with very tight gutters can still interleave the columns (a known geometric edge — see docs/ / issue tracker). Eyeball any file with examples/visual_check.py.

Install

# Rust
cargo add pdfmuse-core
# Python (abi3 wheels)
pip install pdfmuse
# Node
npm install @pdfmuse/node   # native binding
# WASM (browser + Node, no native binary)
npm install @pdfmuse/core   # ESM `import` in the browser, `require` on Node >= 14

Usage

CLI (debug/inspection):

pdfmuse parse report.pdf --format md      # structured Markdown (headings, tables)
pdfmuse parse report.pdf --format json    # full IR (chars, bboxes, blocks, warnings)

Rust:

let data = std::fs::read("report.pdf")?;
let doc = pdfmuse_core::parse(&data, None)?;                 // auto-detect PDF/DOCX
for page in &doc.pages {
    for ch in &page.chars { /* ch.text, ch.bbox {x0,y0,x1,y1}, ch.size */ }
}
let md = pdfmuse_core::to_markdown(&doc);
let chunks = pdfmuse_core::chunk(&doc);                      // RAG chunks + {page, bbox, heading_path}

Python:

import pdfmuse
data = open("report.pdf", "rb").read()
text = pdfmuse.to_text(data)         # plain text — fast path (~1.3ms, no full-IR json.loads)
md = pdfmuse.to_markdown(data)       # structured Markdown — headings (PDF & DOCX) + tables
doc = pdfmuse.parse(data)            # full IR: doc.pages[i].chars/blocks with bboxes
clean = pdfmuse.to_text(data, drop_boilerplate=True)  # strip running headers/footers

Node:

const { toText, toMarkdown, parse } = require("@pdfmuse/node");
const data = fs.readFileSync("report.pdf");
const text = toText(data);           // plain text — fast path
const clean = toText(data, undefined, true);  // strip running headers/footers
const doc = parse(data);             // full IR (typed Document)

WASM (browser — digital PDFs; scanned pages return a NeedsOcr warning to hand off server-side):

import init, { to_text, parse } from "@pdfmuse/core";
await init();
const text = to_text(new Uint8Array(bytes));         // plain text
const doc = JSON.parse(parse(new Uint8Array(bytes))); // full IR

Integrations

  • LangChainlangchain-pdfmuse: a PdfmuseLoader with single / page / elements modes. In elements mode each chunk carries section-aware metadata (heading_path, bbox, category) — reproducible chunks for RAG.

    from langchain_pdfmuse import PdfmuseLoader
    docs = PdfmuseLoader("report.pdf", mode="elements").load()
    
  • LlamaIndexllama-index-readers-pdfmuse: a PdfmuseReader with the same modes and section-aware metadata.

    from llama_index.readers.pdfmuse import PdfmuseReader
    docs = PdfmuseReader(mode="elements").load_data("report.pdf")
    
  • Haystackpdfmuse-haystack: a PdfmuseConverter component (text / markdown) for Haystack 2.x pipelines.

    from pdfmuse_haystack import PdfmuseConverter
    docs = PdfmuseConverter(mode="markdown").run(sources=["report.pdf"])["documents"]
    

Scope boundary

In the core (deterministic): text + coordinates/font/size/color · vector rules & rects · line/paragraph/column clustering · heading detection (font-size + numbering) · running header/footer detection + opt-in removal · ruled & whitespace-aligned table reconstruction · full DOCX structure · JSON / Markdown / RAG-chunk output.

Out of the core (pluggable VisionBackend): scanned-page OCR · borderless-table structure recognition · heading/body/caption classification. Text-less (scanned/image) pages are flagged NeedsOcr and left for a backend — see docs/adr/0001-pdf-engine-strategy.md.

Guarding this boundary is what keeps pdfmuse fast, stable, and distinct from vision models.

Layout

crates/
  pdfmuse-core/     pure-Rust core: PDF/DOCX → unified IR (parser, tokenizer, layout, output)
  pdfmuse-python/   PyO3 (abi3) binding
  pdfmuse-node/     napi-rs binding
  pdfmuse-wasm/     wasm-bindgen binding
  pdfmuse-cli/      debug CLI (`pdfmuse`)
tests/{corpus,snapshots}   golden corpus + insta snapshots
tests/parity/              cross-binding byte-identical gate (Python == Node == WASM)
examples/visual_check.py   render original ↔ coordinate reconstruction for QA
fuzz/                      cargo-fuzz targets (never-panic)

Testing gates

  • Snapshot tests (insta + tests/corpus)
  • Cross-binding parity CI — Python/Node/WASM output byte-identical (a red gate blocks merge)
  • Robustness — mutated/garbage input never panics (tests/robustness.rs + fuzz/)
  • CJK correctness suite

Status

Core is feature-complete (milestones M0–M4 + real-world hardening M4.5): PDF + DOCX → unified IR → JSON / Markdown / RAG chunks, three byte-identical bindings, encryption, CJK. Currently in M5 · polish & release. Roadmap and tasks live in Linear (project pdfmuse).

License

Dual-licensed under MIT or Apache-2.0, at your option.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pdfmuse-0.1.11-cp38-abi3-win_amd64.whl (786.7 kB view details)

Uploaded CPython 3.8+Windows x86-64

pdfmuse-0.1.11-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (937.7 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

pdfmuse-0.1.11-cp38-abi3-macosx_11_0_arm64.whl (835.0 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file pdfmuse-0.1.11-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: pdfmuse-0.1.11-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 786.7 kB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pdfmuse-0.1.11-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 31a30e9255674ebf36bc9c062e886799c9738ca9b3157d7d6c843d4f6c8912f9
MD5 6c6aae6df359db9ad41ee77cfcfbedc1
BLAKE2b-256 9ddc2e9034085a977b649bba1e3ef9b954f9628f67db7894a3effc4b17bd8982

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfmuse-0.1.11-cp38-abi3-win_amd64.whl:

Publisher: release.yml on casperkwok/pdfmuse

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdfmuse-0.1.11-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pdfmuse-0.1.11-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ab98c6ede31a730868e24fcf336a446099ebda3f2142e9bdbb82cc055df257dd
MD5 f8b19aaa74e141aab59597c8dcc61ca9
BLAKE2b-256 b6274a90539119182a9b44218c1517a5ee3c8494011b640b41e876bb263ba165

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfmuse-0.1.11-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on casperkwok/pdfmuse

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdfmuse-0.1.11-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pdfmuse-0.1.11-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0971564f2421b1c7e27ff688c328c93c537b75acb6343f5a2c5a07065ad657e1
MD5 cb107eb35e176c557a4f5e458ae5e09a
BLAKE2b-256 c1e3fdad6266af22370ab54215002820c578c7d6b5344dd3a924df05d64f3c11

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdfmuse-0.1.11-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on casperkwok/pdfmuse

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.12

3 files

This release

0.1.11 This release

3 files

0.1.10

3 files

0.1.9

3 files

0.1.8

3 files

0.1.7

3 files

0.1.6

3 files

0.1.5

3 files

0.1.4

3 files

0.1.3

3 files

0.1.2

3 files

0.1.1

3 files

0.1.0

3 files

0.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page