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.3

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.3
File Interpreter ABI Platform
distillpdf-0.1.3-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
distillpdf-0.1.3-cp38-abi3-manylinux_2_39_x86_64.whl CPython 3.8 abi3 Linux glibc 2.39+ x86-64 Details
distillpdf-0.1.3-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details

Total release size: 28.4 MB

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

Download URL distillpdf-0.1.3-cp38-abi3-win_amd64.whl
Size 10.1 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
e34e473e87e1e5f319007af23ab11d19afc1fae8ca6a63426cbe7e1aa1aae922
BLAKE2b-256 checksum
How to use checksums
677fd4630d61a340532e0d83959975d9e72775e44f7eaf7856f0f6040fd00744
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 24, 2026.

Transparency log

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

Download URL distillpdf-0.1.3-cp38-abi3-manylinux_2_39_x86_64.whl
Size 9.4 MB
Tags CPython 3.8 Linux glibc 2.39+ x86-64 abi3
SHA-256 checksum
How to use checksums
84741fed7fe6494a38dda6306d617287d12349e91acf9641311bf9de4fb551c4
BLAKE2b-256 checksum
How to use checksums
46ad368fd9c8297aefcdd93c70c87d4bd2bc2ae5ce16a468a732ba5085de7314
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 24, 2026.

Transparency log

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

Download URL distillpdf-0.1.3-cp38-abi3-macosx_11_0_arm64.whl
Size 8.8 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
ecfbd7ba83f6c8739273d0f06d408610229fc2e8002826b3223531af9cf383d1
BLAKE2b-256 checksum
How to use checksums
0bfe06c69163570c2e5a3e15a1cb34571c28b519953edec4f1ef252fedbf4da5
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 24, 2026.

Transparency log

Security for distillpdf 0.1.3

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.3-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 24, 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.3 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