pdfmux

io.github.NameetPPublished by GitHub account NameetPv1.8.7Updated Oct 25, 2026

PDF-to-Markdown extraction that audits its own output and flags any extractor's silent drops.

Overview

AI-generated overview

Local MCP server that converts PDFs to Markdown, JSON or RAG chunks, auditing its own output and certifying other extractors for silently dropped pages.

What you can do
Convert PDFs to Markdown, JSON, CSV or RAG-ready chunks, with per-page routing across local backends and an optional LLM fallback, plus batch directory conversion and streaming output for long documents. Analyze and audit extraction quality, returning per-page confidence, coverage and flagged pages instead of silently dropping them. Extract structured data using built-in schema presets such as invoice, receipt, contract, resume and paper. Read PDF metadata and certify another engine's output against the source PDF to report silently dropped pages.
When to use it
Useful when an assistant needs to read or index PDFs for RAG pipelines, especially mixed batches with scans, tables or complex layouts, and when you want confidence scores and explicit flags for pages that failed rather than silent gaps. Also suited to checking whether an existing extraction engine dropped pages.
Requirements
Runs as a local process via pip install pdfmux (Python 3.11+); the OCR extra is recommended for scanned pages. Optional GEMINI_API_KEY enables the paid Gemini Flash fallback; without it everything runs locally. LLM fallbacks send pages to third-party providers only if configured.
Before you install
The optional GEMINI_API_KEY enables paid per-page LLM calls; cost modes and a budget cap exist, and premium mode calls the LLM on every page. Configuring a cloud LLM provider sends PDF page content to that provider. Local telemetry tracks which extractors win per document type. The paid cloud manifest signing service is separate and not required.

Installation

In SourceWeft

  1. Open pdfmux in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

pdfmux

[CI] [PyPI] [Python 3.11+] [License: MIT] [Downloads]

Self-healing PDF extraction that flags the pages it can't read instead of dropping them — and now certifies any extractor's output for silent drops. Open-source LlamaParse alternative for RAG pipelines, MCP server for Claude Desktop, LangChain + LlamaIndex loaders.

pdfmux extracts PDFs and checks its own work — and now certifies any extractor's, telling you which pages it silently dropped. Free, MIT. Patent-pending method. pip install pdfmux.

Two jobs, one tool:

  • Self-healing extraction. The only PDF extractor that audits its own output. Catches blank pages, scrambled columns, broken tables — re-extracts them with a stronger backend, and flags what it still can't read instead of silently dropping it. So your LLM gets clean data, not silent garbage. Routes each page to the best of 7 built-in extraction backends + BYOK LLM fallback (Gemini / Claude / GPT-4o / Ollama). One CLI. One API. Zero config.
  • Certify Anything — new in v1.8.1. pdfmux verify audits any extraction engine's output against the source PDF — Reducto, Mistral OCR, LlamaParse, Docling, your in-house parser — and tells you which pages it silently dropped. Free, MIT, patent-clean.

[pdfmux terminal demo]

PDF ──> pdfmux router ──> best extractor per page ──> audit ──> re-extract failures ──> Markdown / JSON / chunks            |            ├─ PyMuPDF         (digital text, 0.01s/page)            ├─ OpenDataLoader  (complex layouts, 0.05s/page)            ├─ RapidOCR        (scanned pages, CPU-only)            ├─ Docling         (tables, 97.9% TEDS)            ├─ Surya           (heavy OCR fallback)            ├─ Marker          (academic papers, neural)            ├─ Mistral OCR     ($0.002/page, 96.6% tables)            └─ YOUR LLM        (Gemini / Gemma 3 / Claude / GPT-4o / Ollama / Mistral — BYOK via YAML)

Install

bash
pip install pdfmux

That handles digital PDFs. For any real-world batch, install pdfmux[ocr] too — almost every directory of PDFs has at least one scan, and without OCR those pages return empty text:

bash
pip install "pdfmux[ocr]"             # ⭐ recommended — RapidOCR for scanned pages (~200MB, CPU)

Other backends, by document type:

bash
pip install "pdfmux[tables]"          # Docling — table-heavy docs (~500MB)pip install "pdfmux[opendataloader]"  # OpenDataLoader — complex layouts (Java 11+)pip install "pdfmux[marker]"          # Marker — neural extraction for academic paperspip install "pdfmux[llm]"             # Gemini fallback (default LLM)pip install "pdfmux[llm-claude]"      # Claude (Sonnet / Opus)pip install "pdfmux[llm-openai]"      # GPT-4o familypip install "pdfmux[llm-ollama]"      # Ollama (any local model)pip install "pdfmux[llm-mistral]"     # Mistral OCR API ($0.002/page)pip install "pdfmux[llm-all]"         # all LLM providers (incl. Gemma via Gemini key)pip install "pdfmux[watch]"           # `pdfmux watch <dir>` auto-convert on changepip install "pdfmux[all]"             # everything

Requires Python 3.11+.

Quick Start

CLI

bash
# zero config — just workspdfmux convert invoice.pdf# invoice.pdf -> invoice.md (2 pages, 95% confidence, via pymupdf4llm)
# RAG-ready chunks with token limitspdfmux convert report.pdf --chunk --max-tokens 500
# cost-aware extraction with budget cappdfmux convert report.pdf --mode economy --budget 0.50
# schema-guided structured extraction (5 built-in presets)pdfmux convert invoice.pdf --schema invoice
# BYOK any LLM for hardest pagespdfmux convert scan.pdf --llm-provider claude
# use a built-in or saved profile (invoices, receipts, papers, contracts, bulk-rag)pdfmux convert invoice.pdf --profile invoices
# predict cost before running anythingpdfmux estimate big-report.pdf --llm-provider gemini
# stream pages as NDJSON as they finish (great for long documents)pdfmux stream report.pdf --quality high
# auto-convert any new PDFs that land in a folderpdfmux watch ./inbox/ -o ./output/
# diff two extractions side-by-sidepdfmux diff old.pdf new.pdf
# batch a directory — writes manifest.json with per-doc confidencepdfmux convert ./docs/ -o ./output/
# CI mode: fail the run if any document is below 0.20 confidencepdfmux convert ./docs/ -o ./output/ --strict --min-confidence 0.20
# pre-flight a directory: which extras do you actually need for THIS batch?pdfmux doctor --check ./docs/
# results are cached by file hash — re-runs are instant; bypass with --no-cachepdfmux convert report.pdf --no-cachepdfmux convert report.pdf --clear-cache

Python

For batch processing, use batch_extract() — not a subprocess.run(['pdfmux', ...]) loop. Same pipeline, no per-file process spawn, handles non-ASCII filenames:

python
import pdfmuxfrom pathlib import Path
# Batch extract — yields (path, result) tuples as each PDF completes.pdfs = list(Path("./inbox").glob("*.pdf"))for path, result in pdfmux.batch_extract(pdfs, quality="standard"):    if isinstance(result, Exception):        print(f"FAILED {path.name}: {result}")        continue    if result.confidence < 0.50:        print(f"REVIEW {path.name} ({result.confidence:.2f})")    else:        print(f"OK     {path.name} ({result.confidence:.2f})")
# Single-file helpers.text   = pdfmux.extract_text("report.pdf")             # markdown stringdata   = pdfmux.extract_json("report.pdf")             # locked schema dictchunks = pdfmux.chunk("report.pdf", max_tokens=500)    # RAG-ready chunks

Don't wrap pdfmux with your own pypdf/pdfplumber fallback. pdfmux already routes per page through PyMuPDF → RapidOCR → vision LLM. PyMuPDF tolerates malformed PDFs that pypdf rejects ("Stream has ended unexpectedly"), so a downstream pypdf fallback turns recoverable PDFs into failures. Trust the router; check the confidence score on the result.

Certify Anything

pdfmux verify audits any extraction engine's output against the source PDF and tells you which pages it silently dropped — not just pdfmux's own extraction. Point it at the output of Reducto, Mistral OCR, LlamaParse, Docling, or your in-house parser and it re-derives the source text with pdfmux's own audit pass, aligns the extraction to it, and scores every page.

The failure it catches: a page where the source has real text but the engine returned nothing — while reporting success. That "silent drop" is the exact failure that poisons a RAG index without a single error in the logs.

bash
# Certify pdfmux's own extraction of a documentpdfmux verify --source report.pdf --engine pdfmux
# Certify ANOTHER engine's output (JSON / Markdown / text)pdfmux verify --source report.pdf --extracted reducto.json --engine-name reducto
# Batch a whole directory — the "M pages silently dropped across N docs" reportpdfmux verify --source ./pdfs/ --extracted ./engine-outputs/ -o certification.json
# CI gate: exit non-zero unless the overall verdict is PASSpdfmux verify --source report.pdf --extracted out.json --strict

Every run prints a PASS / REVIEW / FAIL verdict, overall confidence and coverage, and — when it finds them — the silently dropped pages by number:

pdfmux verify — report.pdf · engine: reducto  FAIL   confidence 71% · coverage 68%  reducto: FAIL; 3 page(s) SILENTLY DROPPED (pages 7, 12, 31); overall  confidence 71%, coverage 68% across 40 page(s).
❌ 3 page(s) SILENTLY DROPPED: 7, 12, 31

Per page you get a verdict (pass / review / fail), confidence, coverage, alignment, hallucination-risk, and table/heading integrity. Batch mode rolls that up into a single "N pages silently dropped across M documents" line — the report you run on 100 of your own PDFs to find the silent failures already in your pipeline.

It works on any engine's output

--extracted accepts JSON, Markdown, or plain text (--extracted-format auto | json | markdown | text). When the extraction exposes real per-page structure, pdfmux compares page-by-page; when it's a single blob, it falls back to content-presence checks so it never fabricates a "silent drop" from a pagination mismatch.

Python API

python
from pdfmux import verify_extraction, verify_batch
# Single document → a CertificationManifestmanifest = verify_extraction("report.pdf", "reducto.json", engine="reducto")print(manifest.verdict)        # "PASS" | "REVIEW" | "FAIL"print(manifest.silent_drops)   # e.g. (7, 12, 31)  — 1-indexed page numbersprint(manifest.coverage)       # 0.0–1.0
# Many documents → a BatchCertification ("M pages dropped across N docs")batch = verify_batch([("a.pdf", "a.json"), ("b.pdf", "b.json")], engine="llamaparse")print(batch.total_silent_drops, "pages dropped across", batch.doc_count, "docs")

Each manifest carries a tamper-evident SHA-256 content signature over its canonical body and an embedded, honest limitations list: the certifier is lexical, not linguistic — it detects missing and garbled content, not faithful paraphrase or translation.

MCP

verify_extraction is exposed as an MCP tool (the 7th — see MCP Server), so an agent can certify an engine's output in the same session it extracts.

Free, MIT, patent-clean

Certify Anything reuses only pdfmux's shipped MIT audit layer. It does not include, and does not require, the patent-pending decision-trace method — that stays in pdfmux Cloud/Pro. pip install pdfmux gives you the full verify command at no cost.

Full reference: docs/CERTIFY-ANYTHING.md.

When you need to prove it to someone else

A local install can audit an extraction, but it cannot attest to one — anything it signs, anyone could forge. pdfmux Cloud returns an Ed25519-signed manifest over the extraction: your auditor verifies it offline, against a published public key, without an account and without trusting pdfmux.

bash
pdfmux verify-manifest manifest.json      # free, MIT, offline — no account

Verification is free and open forever; only generation is paid ($49/mo). That asymmetry is deliberate — you should never need our permission to check our work.

Free tool, no signup: app.pdfmux.com/audit — upload a PDF and see which pages your current extractor silently dropped. Measured accuracy (and its blind spots) published in pdfmux-bench.

Architecture

                           ┌─────────────────────────────┐                           │     Segment Detector         │                           │  text / tables / images /    │                           │  formulas / headers per page │                           └─────────────┬───────────────┘                                         │                    ┌────────────────────────────────────────┐                    │            Router Engine                │                    │                                        │                    │   economy ── balanced ── premium        │                    │   (minimize $)  (default)  (max quality)│                    │   budget caps: --budget 0.50            │                    └────────────────────┬───────────────────┘                                         │          ┌──────────┬──────────┬────────┴────────┬──────────┐          │          │          │                  │          │     PyMuPDF   OpenData    RapidOCR           Docling     LLM     digital   Loader      scanned            tables    (BYOK)     0.01s/pg  complex     CPU-only           97.9%    any provider               layouts                        TEDS          │          │          │                  │          │          └──────────┴──────────┴────────┬────────┴──────────┘                                         │                    ┌────────────────────────────────────────┐                    │           Quality Auditor               │                    │                                        │                    │   4-signal dynamic confidence scoring   │                    │   per-page: good / bad / empty          │                    │   if bad -> re-extract with next backend│                    └────────────────────┬───────────────────┘                                         │                    ┌────────────────────────────────────────┐                    │           Output Pipeline               │                    │                                        │                    │   heading injection (font-size analysis)│                    │   table extraction + normalization      │                    │   text cleanup + merge                  │                    │   confidence score (honest, not inflated)│                    └────────────────────────────────────────┘

Key design decisions

  • Router, not extractor. pdfmux does not compete with PyMuPDF or Docling. It picks the best one per page.
  • Agentic multi-pass. Extract, audit confidence, re-extract failures with a stronger backend. Bad pages get retried automatically.
  • Segment-level detection. Each page is classified by content type (text, tables, images, formulas, headers) before routing.
  • 4-signal confidence. Dynamic quality scoring from character density, OCR noise ratio, table integrity, and heading structure. Not hardcoded thresholds.
  • Document cache. Each PDF is opened once, not once per extractor. Shared across the full pipeline.
  • Data flywheel. Local telemetry tracks which extractors win per document type. Routing improves with usage.

Features

FeatureWhat it doesCommand
Zero-config extractionRoutes to best backend automaticallypdfmux convert file.pdf
RAG chunkingSection-aware chunks with token estimatespdfmux convert file.pdf --chunk --max-tokens 500
Cost modeseconomy / balanced / premium with budget capspdfmux convert file.pdf --mode economy --budget 0.50
Schema extraction5 built-in presets (invoice, receipt, contract, resume, paper)pdfmux convert file.pdf --schema invoice
ProfilesSave and re-use config; built-ins for invoices/receipts/papers/contracts/bulk-ragpdfmux convert file.pdf --profile invoices
BYOK LLMGemini, Gemma 3, Claude, GPT-4o, Ollama, Mistral, any OpenAI-compatible APIpdfmux convert file.pdf --llm-provider claude
Cost estimatePredict spend before runningpdfmux estimate file.pdf --llm-provider gemini
Streaming outputNDJSON events page-by-page for long docspdfmux stream file.pdf
Smart cacheHash-keyed result cache, 30-day TTL, 1 GB LRUpdfmux convert file.pdf (auto), --no-cache to bypass
Watch modeAuto-convert any PDF added to a folderpdfmux watch ./inbox/
DiffCompare two extractionspdfmux diff a.pdf b.pdf
BenchmarkEval all installed extractors against ground truthpdfmux benchmark
DoctorShow installed backends, coverage gaps, recommendationspdfmux doctor
MCP serverAI agents read PDFs via stdio or HTTPpdfmux serve
Batch processingConvert entire directoriespdfmux convert ./docs/
Page-level streaming APIBounded-memory page iteration for large filesfor page in ext.extract("500pg.pdf")
Retry with backoffEvery LLM provider auto-retries with exponential backoff + Retry-After(built-in)

CLI Reference

pdfmux convert

bash
pdfmux convert <file-or-dir> [options]
Options:  -o, --output PATH          Output file or directory  -f, --format FORMAT        markdown | json | csv | llm (default: markdown)  -q, --quality QUALITY      fast | standard | high (default: standard)  -s, --schema SCHEMA        JSON schema file or preset (invoice, receipt, contract, resume, paper)  --chunk                    Output RAG-ready chunks  --max-tokens N             Max tokens per chunk (default: 500)  --mode MODE                economy | balanced | premium (default: balanced)  --budget AMOUNT            Max spend per document in USD  --llm-provider PROVIDER    LLM backend: gemini | claude | openai | ollama  --confidence               Include confidence score in output  --stdout                   Print to stdout instead of file

pdfmux serve

Start the MCP server for AI agent integration.

bash
pdfmux serve              # stdio mode (Claude Desktop, Cursor)pdfmux serve --http 8080  # HTTP mode

pdfmux doctor

bash
pdfmux doctor# ┌──────────────────┬─────────────┬─────────┬──────────────────────────────────┐# │ Extractor        │ Status      │ Version │ Install                          │# ├──────────────────┼─────────────┼─────────┼──────────────────────────────────┤# │ PyMuPDF          │ installed   │ 1.25.3  │                                  │# │ OpenDataLoader   │ installed   │ 0.3.1   │                                  │# │ RapidOCR         │ installed   │ 3.0.6   │                                  │# │ Docling          │ missing     │ --      │ pip install pdfmux[tables]       │# │ Surya            │ missing     │ --      │ pip install pdfmux[ocr-heavy]    │# │ LLM (Gemini)     │ configured  │ --      │ GEMINI_API_KEY set               │# └──────────────────┴─────────────┴─────────┴──────────────────────────────────┘

pdfmux benchmark

bash
pdfmux benchmark report.pdf# ┌──────────────────┬────────┬────────────┬─────────────┬──────────────────────┐# │ Extractor        │   Time │ Confidence │      Output │ Status               │# ├──────────────────┼────────┼────────────┼─────────────┼──────────────────────┤# │ PyMuPDF          │  0.02s │        95% │ 3,241 chars │ all pages good       │# │ Multi-pass       │  0.03s │        95% │ 3,241 chars │ all pages good       │# │ RapidOCR         │  4.20s │        88% │ 2,891 chars │ ok                   │# │ OpenDataLoader   │  0.12s │        97% │ 3,310 chars │ best                 │# └──────────────────┴────────┴────────────┴─────────────┴──────────────────────┘

pdfmux estimate

Predict spend (and which backends will run) before processing.

bash
pdfmux estimate report.pdf --quality high --llm-provider gemini# Pages       : 47# Extractors  : pymupdf4llm + gemini-2.5-flash on 9 pages# Estimated   : $0.0234# Cache hit?  : no  (first run for this file)

pdfmux stream

Emit NDJSON events as pages complete — useful for very long PDFs and live UIs.

bash
pdfmux stream long.pdf --quality high# {"event":"classified","page_count":312,"plan":"pymupdf+gemini-fallback"}# {"event":"page","page_num":0,"confidence":0.97,"chars":1842}# {"event":"page","page_num":1,"confidence":0.92,"chars":1611,"ocr":true}# ...# {"event":"complete","confidence":0.94,"cost_usd":0.0712}

pdfmux watch

Auto-convert any PDFs that land in a directory. Survives until Ctrl+C.

bash
pdfmux watch ./inbox/ -o ./output/ --profile bulk-rag

pdfmux diff

Side-by-side extraction comparison (quality, content, cost).

bash
pdfmux diff a.pdf b.pdf --quality standard

pdfmux profiles

Saved configs at ~/.config/pdfmux/profiles.yaml. Built-ins ship for the common shapes; save your own for project defaults.

bash
pdfmux profiles list# invoices    quality=standard, schema=invoice, format=json# receipts    quality=fast,     schema=receipt, format=json# papers      quality=high,     chunk=true, max_tokens=500# contracts   quality=high,     schema=contract# bulk-rag    quality=standard, format=llm, chunk=true
pdfmux profiles show invoicespdfmux profiles save my-default --quality high --format llm --chunkpdfmux profiles delete my-default
# use a profile when convertingpdfmux convert file.pdf --profile invoices

Python API

Text extraction

python
import pdfmux
text = pdfmux.extract_text("report.pdf")                    # -> str (markdown)text = pdfmux.extract_text("report.pdf", quality="fast")    # PyMuPDF only, instanttext = pdfmux.extract_text("report.pdf", quality="high")    # LLM-assisted

Structured extraction

python
data = pdfmux.extract_json("report.pdf")# data["page_count"]   -> 12# data["confidence"]   -> 0.91# data["ocr_pages"]    -> [2, 5, 8]# data["pages"][0]["key_values"]  -> [{"key": "Date", "value": "2026-02-28"}]# data["pages"][0]["tables"]      -> [{"headers": [...], "rows": [...]}]

RAG chunking

python
chunks = pdfmux.chunk("report.pdf", max_tokens=500)for c in chunks:    print(f"{c['title']}: {c['tokens']} tokens (pages {c['page_start']}-{c['page_end']})")

Schema-guided extraction

python
data = pdfmux.extract_json("invoice.pdf", schema="invoice")# Uses built-in invoice preset: extracts date, vendor, line items, totals# Also accepts a path to a custom JSON Schema file

Streaming (bounded memory)

python
from pdfmux.extractors import get_extractor
ext = get_extractor("fast")for page in ext.extract("large-500-pages.pdf"):  # Iterator[PageResult]    process(page.text)  # constant memory, even on 500-page PDFs

Types and errors

python
from pdfmux import (    # Enums    Quality,              # FAST, STANDARD, HIGH    OutputFormat,         # MARKDOWN, JSON, CSV, LLM    PageQuality,          # GOOD, BAD, EMPTY
    # Data objects (frozen dataclasses)    PageResult,           # page: text, page_num, confidence, quality, extractor    DocumentResult,       # document: pages, source, confidence, extractor_used    Chunk,                # chunk: title, text, page_start, page_end, tokens
    # Errors    PdfmuxError,          # base -- catch this for all pdfmux errors    FileError,            # file not found, unreadable, not a PDF    ExtractionError,      # extraction failed    ExtractorNotAvailable,# requested backend not installed    FormatError,          # invalid output format    AuditError,           # audit could not complete)

Framework Integrations

LangChain

bash
pip install langchain-pdfmux
python
from langchain_pdfmux import PDFMuxLoader
loader = PDFMuxLoader("report.pdf", quality="standard")docs = loader.load()  # -> list[Document] with confidence metadata

LlamaIndex

bash
pip install llama-index-readers-pdfmux
python
from llama_index.readers.pdfmux import PDFMuxReader
reader = PDFMuxReader(quality="standard")docs = reader.load_data("report.pdf")  # -> list[Document]

MCP Server (AI Agents)

Listed on mcpservers.org. One-line setup:

json
{  "mcpServers": {    "pdfmux": {      "command": "npx",      "args": ["-y", "pdfmux-mcp"]    }  }}

Or via Claude Code:

bash
claude mcp add pdfmux -- npx -y pdfmux-mcp

Tools exposed: convert_pdf, analyze_pdf, extract_structured, extract_streaming, get_pdf_metadata, batch_convert.

BYOK LLM Configuration

pdfmux supports any LLM via 5 lines of YAML. Bring your own keys -- nothing leaves your machine unless you configure it to.

yaml
# ~/.pdfmux/llm.yamlprovider: claude          # gemini | claude | openai | ollama | any OpenAI-compatiblemodel: claude-sonnet-4-20250514api_key: ${ANTHROPIC_API_KEY}base_url: https://api.anthropic.com  # optional, for custom endpointsmax_cost_per_page: 0.02   # budget cap

Supported providers:

ProviderModelsLocal?Cost
Gemini2.5 Flash, 2.5 ProNo~$0.01/page
Gemma 327B IT, 12B IT (great for Arabic)No (via Gemini key)~$0.0002/page
ClaudeSonnet, OpusNo~$0.015/page
GPT-4oGPT-4o, GPT-4o-miniNo~$0.01/page
Mistralmistral-ocr-latestNo$0.002/page
OllamaAny local modelYesFree
CustomAny OpenAI-compatible APIConfigurableVaries

Every provider's extract_page() is wrapped in @with_retry(max_attempts=3, backoff_base=2.0), which honors Retry-After headers on 429s and skips retries on auth failures so a bad key fails fast.

Arabic & RTL Support

pdfmux ships first-class support for Arabic, Persian, Urdu, and Hebrew. Out of the box, RTL detection runs on every PDF and PyMuPDF-extracted pages are passed through the Unicode Bidirectional Algorithm so glyphs that were stored in left-to-right order render in correct reading order.

bash
# Default install — already includes python-bidi for RTL reorderingpip install pdfmux
# Recommended for Arabic-heavy docs — adds Gemma vision OCR# (Gemma speaks the OpenAI protocol, so it needs the openai SDK)pip install "pdfmux[llm-openai]"
# One credential covers Gemma + Gemini (same Google endpoint)export GEMINI_API_KEY=...

What happens automatically:

  • pdfmux convert detects Arabic content and routes pages with >5% Arabic characters through the Arabic-aware extractor chain.
  • PyMuPDF, RapidOCR, and Docling outputs are post-processed with the Bidi algorithm — markdown headings (#) and pipe-table rows preserve structure, only inner text is reordered.
  • DocumentResult.has_arabic is set to True whenever any page contains Arabic script.

What requires opt-in:

  • Vision LLM extraction. Set --llm-provider gemma (or any vision provider) to route Arabic pages through Gemma instead of PyMuPDF.
  • Aggressive normalization (Tatweel removal, Alef/Yeh unification, Tashkeel stripping) — call pdfmux.arabic.normalize_arabic(text) on extracted strings if you need canonicalized output for search or embedding.
python
from pdfmux.arabic import (    is_arabic_text,    is_rtl_dominant,    fix_bidi_order,    normalize_arabic,)
text = "مرحبا بالعالم"assert is_arabic_text(text)assert is_rtl_dominant(text)
# Fix glyph order from PyMuPDF / OCR enginesvisual = fix_bidi_order(text)
# Canonicalize for indexing — strip Tatweel, unify Alef variants, drop diacriticsindexable = normalize_arabic("أَحْمَدْ")  # → "احمد"

Proof: a real customer batch

We measured pdfmux on 433 real customer documents — technical and safety data sheets, mixed digital and scanned, some encoding-corrupted. Run the naive way first (an early pdfmux CLI in a subprocess, pypdf fallback, no OCR), the pipeline silently dropped 16 documents — 11 of them with no log line at all. That was our own tool failing at the exact thing it promises.

Rebuilt with the per-page audit + budgeted OCR cascade: 433 of 433 processed, zero silent failures. Every unrecoverable page is flagged, not dropped.

(A small internal confidence-calibration set also ships under eval/ — it's a regression guard on the confidence gate, not a competitive benchmark; see eval/README.md.)

Benchmark

On opendataloader-bench — 200 real-world PDFs (financial filings, academic papers, legal contracts, government reports) — pdfmux scores 0.903 overall — #2 of the 8 engines measured, behind opendataloader-hybrid (0.909). Re-run 2026-07-16 (reproduction below).

RankEngineOverallReading orderTables (TEDS)LicenseGPU
1opendataloader-hybrid0.9090.9350.928Apache-2.0No
2pdfmux0.9030.9200.911MITNo
3Docling0.8770.9000.887MITOptional
4marker0.8610.8900.808freeGPU
5mineru0.8310.8570.873freeGPU

Full per-document scores: the 200-PDF head-to-head · methodology: best PDF extraction library, benchmarked.

Smart Result Cache

Re-running the same extraction is instant. pdfmux hashes every input PDF (SHA-256) and keys results on (file_hash, quality, format, schema). Cache files live under ~/.cache/pdfmux/results/, expire after 30 days, and are LRU-evicted at 1 GB.

bash
pdfmux convert big-report.pdf            # first run: 14.2spdfmux convert big-report.pdf            # cache hit: 0.05spdfmux convert big-report.pdf --no-cache # bypass cache (still writes back)pdfmux convert big-report.pdf --clear-cache  # purge and re-run

The cache also speeds up --profile, --schema, and --format switches — each combination is keyed independently, so you can flip between Markdown and JSON for the same document for free after the first extraction.

Confidence Scoring

Every result includes a 4-signal confidence score:

  • 95-100% -- clean digital text, fully extractable
  • 80-95% -- good extraction, minor OCR noise on some pages
  • 50-80% -- partial extraction, some pages unrecoverable
  • <50% -- significant content missing, warnings included

When confidence drops below 80%, pdfmux tells you exactly what went wrong and how to fix it:

Page 4: 32% confidence. 0 chars extracted from image-heavy page.  -> Install pdfmux[ocr] for RapidOCR support on 6 image-heavy pages.

Cost Modes

ModeBehaviorTypical cost
economyRule-based backends only. No LLM calls.$0/page
balancedLLM only for pages that fail rule-based extraction.~$0.002/page avg
premiumLLM on every page for maximum quality.~$0.01/page

Set a hard budget cap: --budget 0.50 stops LLM calls when spend reaches $0.50 per document.

Why pdfmux?

pdfmux is not another PDF extractor. It is the orchestration layer that picks the right extractor per page, verifies the result, and retries failures.

ToolGood atLimitation
PyMuPDFFast digital textCannot handle scans or image layouts
DoclingTables (97.9% accuracy)Slow on non-table documents
MarkerNeural extraction for academic papersNeeds GPU for speed; overkill for digital PDFs
Mistral OCRTables (96.6% TEDS), $0.002/pageCloud-only API
UnstructuredEnterprise platformComplex setup, paid tiers
LlamaParseCloud-nativeRequires API keys, not local
ReductoHigh accuracy$0.015/page, closed source
pdfmuxOrchestrates all of the aboveRoutes per page, audits, re-extracts

Open source Reducto alternative: what costs $0.015/page elsewhere is free with pdfmux's rule-based backends, or ~$0.002/page average with BYOK LLM fallback.

Development

bash
git clone https://github.com/NameetP/pdfmux.gitcd pdfmuxpython3.12 -m venv .venv && source .venv/bin/activatepip install -e ".[dev]"
pytest              # 659 testsruff check src/ tests/ruff format src/ tests/

Contributing

  1. Fork the repo
  2. Create a branch (git checkout -b feature/your-feature)
  3. Write tests for new functionality
  4. Ensure pytest and ruff check pass
  5. Open a PR

License

The pdfmux library and MCP server in this repository are MIT licensed — free for any use, and every released version stays MIT.

The confidence-budgeted decision-trace method (the persisted per-page decision trace with retained rejected candidates, and the monotonic repair guard) is patent-pending (US Provisional App No. 64/106,302) and is reserved for pdfmux Cloud/Pro under a separate commercial license — it is not part of the MIT grant. See LICENSING.md and NOTICE.

Source: README.md at commit 3e6e88d

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

1
  1. v1.8.7LatestSep 16, 2026

Score

50% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts