Skip to main content

distillPDF

Turn any PDF into clean, LLM-ready HTML or Markdown — structure-aware, pure-Rust, MIT-licensed.

PyPI Python versions Docs License: MIT CI Built with Rust

distillpdf does structure-aware PDF extraction: it reconstructs a document's structure — reading order, headings, paragraphs, lists, tables, and figures — then emits compact, semantic HTML or Markdown (or plain text) ready to feed to an LLM or a RAG pipeline. No styling noise, no layout junk. It's pure Rust on lopdf, shipped to Python via PyO3 + maturin as a small, self-contained wheel — a lightweight, permissively licensed alternative to AGPL/heavyweight extractors (PyMuPDF, pdfminer, Unstructured), with no system dependencies and no Python runtime deps.

OCR for scanned PDFs: detect image-only pages and turn them into clean HTML or a compact searchable PDF, with a bundled offline engine or an optional accurate VLM — OCR guide » · example notebook »

Install

pip install distillpdf

Prebuilt wheels — no compiler or system libraries required. Installing also puts a distillpdf command on your PATH.

Quickstart

import distillpdf

doc = distillpdf.open("paper.pdf")        # or distillpdf.from_bytes(data)

doc.to_html()                             # writes paper.html (one self-contained file)
doc.to_markdown()                         # writes paper.md + an img/ folder of figures

html     = doc.to_html(return_string=True)   # ...or get the rendered string back instead
text     = doc.extract_text()                # plain text, in reading order
abstract = doc.section("abstract")           # one section's HTML (or None if no match)

By default these methods write a file and return 1; pass return_string=True for the string. PDF to HTML/Markdown — full guide »

distillpdf paper.pdf                  # HTML to stdout
distillpdf paper.pdf -o paper.html    # ...or to a file (.md infers Markdown)
distillpdf paper.pdf --markdown       # Markdown to stdout
distillpdf *.pdf -o out/              # batch: out/<name>.html per input
distillpdf scan.pdf --ocr             # OCR a scan → scan.searchable.pdf (bundled, no extra)

Command-line guide »

What's in the box

  • Rendering modes & image handling — section-first or page-first HTML/Markdown, an optional table of contents, and embed / external (an img/ folder) / drop image modes. Rendering »
  • Raw extraction — extract_tables(), rich analyze_tables(), extract_images(), extract_links(), extract_fonts() give you the structured pieces, not rendered output. Extraction »
  • The .dpdf document model — distill() once to a durable file, then re-render and query it forever with no source PDF and no re-analysis. .dpdf »
  • Honest search — coverage-reporting lexical find() (never a silent miss) and opt-in semantic search() over BAAI/bge-m3 vectors stored inside the .dpdf. Search »
  • OCR, two tiers — a bundled fast Tesseract engine (~0.8 s/page, offline, no download) and an optional accurate granite-docling VLM (structure + tables), folded into the same HTML / Markdown / searchable-PDF outputs. OCR » · setup »
  • An agent shell over .dpdf — any shell with distillpdf installed can drive info / toc / read / find / search against a distilled model, no SDK or server. CLI »

Lazy engine (experimental)

Opt in with engine="lazy" to read the PDF container through an on-demand index instead of parsing it whole, keeping peak memory far below the file size on large documents:

doc = distillpdf.open("10000-pages.pdf", engine="lazy")
doc.engine          # "lazy"

The output is identical to the default engine="eager" — it is a memory strategy, not a fidelity choice, and it is regression-tested against the eager route as the oracle. Damaged cross-reference data is rebuilt in place by a bounded forward scan, so a broken file stays lazy; only the rare shapes the index refuses — an oversized object, an unusually deep page tree, an unusual encryption — fall back to the eager engine, so engine="lazy" never turns a readable PDF into an error. The fallback is never silent: doc.engine then reports "lazy (eager fallback)". Experimental in that the default engine may change in a future release — the keyword and its two values are stable. Lazy engine »

Comparison

distillPDF PyMuPDF pdfminer.six Unstructured
License MIT AGPL / commercial MIT Apache (heavy deps)
Structure-aware HTML ✅ partial ❌ ✅
System deps none none none many
Implementation Rust C Python Python

Documentation

Full documentation — guides, the Python API reference, and the .dpdf format spec — lives at distillpdf.readthedocs.io. New here? Start with Getting started, then the Python API reference and the .dpdf format reference.

Early release — testers wanted

This is a young project and the API is small and may still change. If you have PDFs that come out wrong, please open an issue with the file (or a description) — real-world documents are exactly what this needs to get better. The .dpdf document model is also experimental (schema_version 0) and not yet a stable contract.

Contributing

Feedback is the fastest way to improve this. Try it on your PDFs and tell me where the output is wrong, star the repo if it's useful, and PRs are welcome. See Contributing and development to build from source or run the tests.

License

MIT — see LICENSE. Use it anywhere, including commercial and closed-source projects.

Metadata

Release files for distillpdf 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for distillpdf 0.1.4
File Interpreter ABI Platform
distillpdf-0.1.4-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
distillpdf-0.1.4-cp38-abi3-manylinux_2_39_x86_64.whl CPython 3.8 abi3 Linux glibc 2.39+ x86-64 Details
distillpdf-0.1.4-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details

Total release size: 28.5 MB

Release files / distillpdf-0.1.4-cp38-abi3-win_amd64.whl

Download URL distillpdf-0.1.4-cp38-abi3-win_amd64.whl
Size 10.1 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
886512d127b63710937c8d46b35d96494392ec9b19305d78b1c89bdb3f31ae6e
BLAKE2b-256 checksum
How to use checksums
87693da6ca31396eb920ad3f0664090284532a30c3da736baeffb8b017d1e0ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / distillpdf-0.1.4-cp38-abi3-manylinux_2_39_x86_64.whl

Download URL distillpdf-0.1.4-cp38-abi3-manylinux_2_39_x86_64.whl
Size 9.5 MB
Tags CPython 3.8 Linux glibc 2.39+ x86-64 abi3
SHA-256 checksum
How to use checksums
059e5f586b86395700628e3e7c3530d769a6504f0b80a26fc91541fd8e1942a5
BLAKE2b-256 checksum
How to use checksums
509a3e9ac0793b7dfab6d0fa3cca705b1d99433bd17865e6f820feb33b7a16cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / distillpdf-0.1.4-cp38-abi3-macosx_11_0_arm64.whl

Download URL distillpdf-0.1.4-cp38-abi3-macosx_11_0_arm64.whl
Size 8.9 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6395f9435ddee64d530d4be9ff73ebd0dbc14e24cb5ef2e5f1d98ab6ad74f3d3
BLAKE2b-256 checksum
How to use checksums
df29bb57ca2b1d3d1a38eed57552dcfec51359155d6ffd07b723bc741cbed6fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Security for distillpdf 0.1.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 3 files in this release have been uploaded with provenance metadata.

Select from 3 files

Attestation for distillpdf-0.1.4-cp38-abi3-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.4 This release

3 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page