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"

For supported, well-formed PDFs, the output is identical to the default engine="eager". Both engines are regression-tested against shared fixtures. Recovery of malformed input can differ between engines. Damaged cross-reference data is rebuilt in place by a bounded forward scan, so a broken file stays lazy; 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.6

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

Total release size: 28.8 MB

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

Download URL distillpdf-0.1.6-cp38-abi3-win_amd64.whl
Size 10.3 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
5ba4b257406663e75b4dc5b19b7ff9645bc35346c12f439a6ed0ec7990fb289c
BLAKE2b-256 checksum
How to use checksums
22276d47eb4e453efd8a751955319c7f7af8524071f6140474bf367c5ba320ab
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 Sep 9, 2026.

Transparency log

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

Download URL distillpdf-0.1.6-cp38-abi3-manylinux_2_39_x86_64.whl
Size 9.6 MB
Tags CPython 3.8 Linux glibc 2.39+ x86-64 abi3
SHA-256 checksum
How to use checksums
e9e8f66f40a986675d5ff7fe7274b861ff8421b30b458e0b4ec28060d0c93709
BLAKE2b-256 checksum
How to use checksums
88f70fbb162d1e324f0dca6127b859d6866db5effe8acd10f5180e29a1bd4d70
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 Sep 9, 2026.

Transparency log

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

Download URL distillpdf-0.1.6-cp38-abi3-macosx_11_0_arm64.whl
Size 9.0 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
63f7bdb1ba17678e027fe954a3c409eb006fe0aafe4fbce32bea160a56d6adfd
BLAKE2b-256 checksum
How to use checksums
629f72ac4e7d09a3ae73e8f71dc915f2de03f1130a3714993d03dd7d0bb607f5
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 Sep 9, 2026.

Transparency log

Security for distillpdf 0.1.6

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.6-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 Sep 9, 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.6 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