Skip to main content

Dumb PDF2MD

简体中文

dumb-pdf2md is a lightweight, typed Python library that converts text-based PDF documents into structured, LLM-friendly Markdown. It reconstructs document structure from the PDF text layer, geometry, and typography without layout models, OCR models, GPU resources, or external services.

Installation

With pip:

python -m pip install dumb-pdf2md

With uv:

uv add dumb-pdf2md

Requires Python 3.10 or later.

Benchmarks

READoc selectable-text holdout (12 real-world PDFs, 161 pages):

Metric dumb-pdf2md MarkItDown 0.1.6 Docling 2.115.0 PyMuPDF4LLM 1.28.0 pdftext 0.7.1 pdfplumber 0.11.10
Text token F1 0.852 0.756 0.899 0.863 0.893 0.691
Reading-order bigram F1 0.734 0.627 0.831 0.793 0.818 0.562
Table-content F1 (6-PDF subset) 0.452 0.326 0.890 0.685 N/A N/A
Conversion time 188.7 ms/page 53.5 ms/page 917.1 ms/page 143.7 ms/page 24.2 ms/page 53.6 ms/page
Peak RSS 248.5 MiB 115.1 MiB 4.44 GiB 667.7 MiB 122.6 MiB 202.6 MiB
Uses OCR model No No Yes No No No
Uses layout model No No Yes Yes No No
Installed size with dependencies 112.5 MiB 168.8 MiB 5.25 GiB 233.2 MiB 70.0 MiB 50.2 MiB
License Apache-2.0 MIT MIT AGPL-3.0 + PolyForm NC / commercial Apache-2.0 MIT

pdftext and pdfplumber are the two base extraction libraries used by dumb-pdf2md, not end-to-end Markdown converters. Their columns score native text output without dumb-pdf2md's structure analysis or Markdown rendering, so table-content F1 is not applicable to those baseline modes. pdfplumber does provide table-finding APIs; dumb-pdf2md combines their candidates with pdftext geometry, custom aligned-table detection, cell normalization, deduplication, and Markdown rendering.

Model rows describe the exact benchmark configuration. Docling ran on CPU with EasyOCR and its layout/table models enabled, without forcing full-page OCR on pages that already contain text. PyMuPDF4LLM uses its installed pymupdf-layout dependency. MarkItDown's optional OCR plugin and cloud integrations were not enabled. The PyMuPDF4LLM license row includes the license of its layout dependency.

Quality metrics are higher-is-better; time and memory are lower-is-better. Performance uses three isolated process runs per tool, excludes import time, and reports median conversion time and maximum peak RSS.

Installed size is the site-packages footprint in a clean Python 3.13 environment after installing the package and all transitive dependencies. It excludes Python itself, package caches, and models downloaded at runtime.

Performance host: Intel Xeon Platinum 8488C, 16 vCPUs (8 cores/16 threads), 64 GB RAM, Ubuntu 24.04.4 LTS (x86_64), and CPython 3.13.3. Results are directly comparable only on the same environment.

0.1.2 release benchmark

On a fixed 17-PDF service corpus, five isolated measured runs on the same host showed the following change from 0.1.1 to 0.1.2:

Metric 0.1.1 0.1.2 Change
Median aggregate conversion time 24.668951 s 21.363090 s 13.40% faster
Maximum peak RSS 190,406,656 bytes 190,771,200 bytes +0.19%

Sixteen of 17 document output hashes were unchanged. The only difference was an approved correctness fix in evidence.pdf: numbered references on page 8 are no longer misclassified as a 63-row table, while the real table on page 5 is preserved. A 16-worker, 24-conversion stress run was deterministic and reported no native extraction failures. See the reproduction details.

READoc was not used for parser tuning. See benchmark reproduction, the base-engine results, the MarkItDown results, the Docling results, the performance results, and the evaluation methodology.

Quality-resource map

quadrantChart
    title Overall extraction quality versus runtime memory
    x-axis Higher peak RSS --> Lower peak RSS
    y-axis Lower composite F1 --> Higher composite F1
    quadrant-1 Best quality-resource balance
    quadrant-2 High quality, high resource use
    quadrant-3 High resource use, weaker structure
    quadrant-4 Lightweight, weaker structure
    "MarkItDown": [0.96, 0.570]
    "pdfplumber": [0.82, 0.418]
    "★ Dumb PDF2MD": [0.77, 0.679]
    "PyMuPDF4LLM": [0.51, 0.781]
    "Docling": [0.02, 0.873]

Licenses:

  • ★ Dumb PDF2MD: Apache-2.0
  • MarkItDown: MIT
  • pdfplumber: MIT
  • PyMuPDF4LLM: AGPL-3.0 + PolyForm Noncommercial / commercial
  • Docling: MIT

The horizontal coordinates reverse and normalize log10(peak RSS), so lower memory use moves a tool to the right while Docling's 4.44 GiB result does not compress the other tools at the origin. They show relative resource tiers, not raw memory values; the exact measurements are in the table above. Composite F1 is the equal-weight mean of text-token, reading-order-bigram, and table-content F1; the table component uses the six-PDF subset with ground-truth tables. For this visualization only, pdfplumber receives zero for the table component because its benchmark mode does not produce Markdown tables; the source table keeps this result as N/A because no table-quality measurement was made.

Positioning

dumb-pdf2md fills the gap between raw text extraction and model-based document understanding:

  • It adds headings, lists, multi-column reading order, table reconstruction, deduplication, and Markdown rendering on top of lightweight PDF engines.
  • It has higher text, reading-order, and table F1 than MarkItDown on this holdout, while remaining fully local and model-free.
  • It uses substantially less memory and disk space than Docling, and less memory than PyMuPDF4LLM.
  • Its Apache-2.0 runtime has no model-weight license, GPU requirement, model download, or external service dependency.

The tradeoff is deliberate: Docling is more accurate on complex structures and supports scanned PDFs, while raw extractors are faster when structure is not needed. dumb-pdf2md targets selectable-text PDFs where useful Markdown structure matters but OCR/model deployment cost does not make sense.

Features

  • Accepts file paths, bytes-like objects, and readable binary streams.
  • Runs locally on CPU without downloading layout or OCR models.
  • Preserves headings, paragraphs, lists, footers, and reading order.
  • Converts ruled and structurally aligned tables to Markdown tables.
  • Handles common multi-column layouts and CJK text.
  • Produces clean Markdown suitable for LLM context, indexing, and RAG pipelines.
  • Returns page counts and non-fatal diagnostics alongside Markdown.
  • Provides immutable result models and a documented exception hierarchy.
  • Supports concurrent callers without taking ownership of caller-provided streams.

dumb-pdf2md is designed for PDFs that contain extractable text. Scanned or image-only PDFs require OCR before conversion. The library does not currently extract images, preserve inline text styling, or expose page-level bounding boxes and metadata.

Usage

Use parse when you need conversion metadata:

from dumb_pdf2md import parse

result = parse("report.pdf")

print(result.markdown)
print(f"Pages: {result.page_count}")

for diagnostic in result.diagnostics:
    print(diagnostic.code, diagnostic.page, diagnostic.message)

Use to_markdown when you only need the generated Markdown:

from dumb_pdf2md import to_markdown

markdown = to_markdown("report.pdf")

Both functions also accept PDF data and readable binary streams:

from io import BytesIO

from dumb_pdf2md import parse

with open("report.pdf", "rb") as file:
    pdf_data = file.read()

from_bytes = parse(pdf_data)

stream = BytesIO(pdf_data)
from_stream = parse(stream)
assert not stream.closed

Caller-owned streams remain open after conversion.

Parser Options

Document heuristics are enabled by default and can be controlled with ParserOptions:

from dumb_pdf2md import ParserOptions, parse

result = parse(
    "report.pdf",
    options=ParserOptions(
        promote_bold_headings=False,
        detect_columns=False,
    ),
)
  • promote_bold_headings promotes qualifying short bold lines to headings.
  • detect_columns detects common column layouts and restores reading order.

Error Handling

All public conversion errors inherit from PdfParserError:

from dumb_pdf2md import PdfInputError, PdfParserError, parse

try:
    result = parse("report.pdf")
except PdfInputError as error:
    print(f"Invalid PDF input: {error}")
except PdfParserError as error:
    print(f"PDF conversion failed: {error}")

PdfInputError reports invalid, empty, missing, or unreadable input. PdfConversionError reports extraction failures or an empty conversion result. Wrapped failures retain the original exception in __cause__.

Development

Run the complete release checkpoint:

scripts/release-check.sh

Or run individual checks:

uv run ruff check .
uv run mypy
uv run pytest
uv build

The test suite covers the public API, parsing algorithms, resource cleanup, concurrent use, distribution artifacts, and a reproducible golden PDF corpus.

Documentation

License

Licensed under the Apache License 2.0.

Metadata

Release files for dumb-pdf2md 0.1.2

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

Source distribution (sdist)

Source distribution for dumb-pdf2md 0.1.2
File Size Uploaded
dumb_pdf2md-0.1.2.tar.gz 297.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dumb-pdf2md 0.1.2
File Interpreter ABI Platform
dumb_pdf2md-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 326.0 kB

Release files / dumb_pdf2md-0.1.2.tar.gz

Download URL dumb_pdf2md-0.1.2.tar.gz
Size 297.9 kB
Tags Source
SHA-256 checksum
How to use checksums
646a03eecfb36d0e7ceeb123c18e718d0b48a9092dd76b021cea1f604059ef94
BLAKE2b-256 checksum
How to use checksums
4a277e10d1142beb938f72850d0dc8f16a7a485bb53942937a8408a68453c23e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.2

Release files / dumb_pdf2md-0.1.2-py3-none-any.whl

Download URL dumb_pdf2md-0.1.2-py3-none-any.whl
Size 28.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6d1f48fc6ddc8086e7be31a4a690c3bbf97ee89ca6927dfca0e530ec2c04ae0b
BLAKE2b-256 checksum
How to use checksums
e8b32155cd990ca9cf3968fe2a931dbc5f8b55550efefd628631f26e170cec61
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.7.2

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

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