pdfmux

Enrutador inteligente de PDF a Markdown que selecciona el mejor extractor por página, audita la calidad de salida y reextrae fallos automáticamente. Puntuación de confianza, soporte de LLM con clave propia, fragmentación para RAG.

Documentación

pdfmux

CI PyPI Python 3.11+ License: MIT Downloads

Extracción de PDF auto-reparable que marca las páginas que no puede leer en lugar de descartarlas — y ahora certifica la salida de cualquier extractor para detección de caídas silenciosas. Alternativa de código abierto a LlamaParse para pipelines de RAG, servidor MCP para Claude Desktop, cargadores de LangChain + LlamaIndex.

pdfmux extrae PDFs y verifica su propio trabajo — y ahora certifica el de cualquier extractor, indicándote qué páginas descartó silenciosamente. Gratis, MIT. Método con patente pendiente. pip install pdfmux.

Dos trabajos, una herramienta:

  • Extracción auto-reparable. El único extractor de PDF que audita su propia salida. Detecta páginas en blanco, columnas desordenadas, tablas rotas — las re-extrae con un backend más potente, y marca lo que aún no puede leer en lugar de descartarlo silenciosamente. Así tu LLM recibe datos limpios, no basura silenciosa. Enruta cada página al mejor de 7 backends de extracción integrados + respaldo LLM BYOK (Gemini / Claude / GPT-4o / Ollama). Una CLI. Una API. Cero configuración.
  • Certify Anything — nuevo en v1.8.1. pdfmux verify audita la salida de cualquier motor de extracción contra el PDF fuente — Reducto, Mistral OCR, LlamaParse, Docling, tu parser interno — y te dice qué páginas descartó silenciosamente. Gratis, MIT, sin patentes.

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)

Instalación

pip install pdfmux

Eso maneja PDFs digitales. Para cualquier lote del mundo real, instala también pdfmux[ocr] — casi todo directorio de PDFs tiene al menos un escaneo, y sin OCR esas páginas devuelven texto vacío:

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

Otros backends, por tipo de documento:

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 papers
pip install "pdfmux[llm]"             # Gemini fallback (default LLM)
pip install "pdfmux[llm-claude]"      # Claude (Sonnet / Opus)
pip install "pdfmux[llm-openai]"      # GPT-4o family
pip 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 change
pip install "pdfmux[all]"             # everything

Requiere Python 3.11+.

Inicio Rápido

CLI

# zero config — just works
pdfmux convert invoice.pdf
# invoice.pdf -> invoice.md (2 pages, 95% confidence, via pymupdf4llm)

# RAG-ready chunks with token limits
pdfmux convert report.pdf --chunk --max-tokens 500

# cost-aware extraction with budget cap
pdfmux 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 pages
pdfmux 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 anything
pdfmux 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 folder
pdfmux watch ./inbox/ -o ./output/

# diff two extractions side-by-side
pdfmux diff old.pdf new.pdf

# batch a directory — writes manifest.json with per-doc confidence
pdfmux convert ./docs/ -o ./output/

# CI mode: fail the run if any document is below 0.20 confidence
pdfmux 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-cache
pdfmux convert report.pdf --no-cache
pdfmux convert report.pdf --clear-cache

Python

Para procesamiento por lotes, usa batch_extract() — no un bucle subprocess.run(['pdfmux', ...]). Mismo pipeline, sin generación de procesos por archivo, maneja nombres de archivo no ASCII:

import pdfmux
from 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 string
data   = pdfmux.extract_json("report.pdf")             # locked schema dict
chunks = pdfmux.chunk("report.pdf", max_tokens=500)    # RAG-ready chunks

No envuelvas pdfmux con tu propio respaldo pypdf/pdfplumber. pdfmux ya enruta por página a través de PyMuPDF → RapidOCR → LLM de visión. PyMuPDF tolera PDFs malformados que pypdf rechaza ("Stream has ended unexpectedly"), por lo que un respaldo pypdf posterior convierte PDFs recuperables en fallos. Confía en el enrutador; revisa la puntuación de confianza en el resultado.

Certify Anything

pdfmux verify audita la salida de cualquier motor de extracción contra el PDF fuente y te dice qué páginas descartó silenciosamente — no solo la extracción propia de pdfmux. Apúntalo a la salida de Reducto, Mistral OCR, LlamaParse, Docling, o tu parser interno y re-deriva el texto fuente con el pase de auditoría propio de pdfmux, alinea la extracción con él, y puntúa cada página.

El fallo que detecta: una página donde la fuente tiene texto real pero el motor no devolvió nada — mientras reporta éxito. Esa "caída silenciosa" es exactamente el fallo que envenena un índice RAG sin un solo error en los registros.

# Certify pdfmux's own extraction of a document
pdfmux 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" report
pdfmux verify --source ./pdfs/ --extracted ./engine-outputs/ -o certification.json

# CI gate: exit non-zero unless the overall verdict is PASS
pdfmux verify --source report.pdf --extracted out.json --strict

Cada ejecución imprime un veredicto PASS / REVIEW / FAIL, confianza y cobertura generales, y — cuando los encuentra — las páginas descartadas silenciosamente por número:

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

Por página obtienes un veredicto (pass / review / fail), confianza, cobertura, alineación, riesgo de alucinación, e integridad de tablas/encabezados. El modo por lotes resume eso en una sola línea "N páginas descartadas silenciosamente en M documentos" — el informe que ejecutas en 100 de tus propios PDFs para encontrar los fallos silenciosos ya presentes en tu pipeline.

Funciona con la salida de cualquier motor

--extracted acepta JSON, Markdown o texto plano (--extracted-format auto | json | markdown | text). Cuando la extracción expone estructura real por página, pdfmux compara página por página; cuando es un solo bloque, recurre a comprobaciones de presencia de contenido para nunca fabricar una "caída silenciosa" por un desajuste de paginación.

API de Python

from pdfmux import verify_extraction, verify_batch

# Single document → a CertificationManifest
manifest = 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 numbers
print(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")

Cada manifiesto lleva una firma de contenido SHA-256 a prueba de manipulaciones sobre su cuerpo canónico y una lista de limitaciones honesta integrada: el certificador es léxico, no lingüístico — detecta contenido faltante y distorsionado, no paráfrasis fieles ni traducciones.

MCP

verify_extraction se expone como herramienta MCP (la 7ª — ver Servidor MCP), para que un agente pueda certificar la salida de un motor en la misma sesión en la que extrae.

Gratis, MIT, sin patentes

Certify Anything reutiliza solo la capa de auditoría MIT incluida en pdfmux. No incluye, ni requiere, el método de traza de decisión con patente pendiente — eso permanece en pdfmux Cloud/Pro. pip install pdfmux te da el comando completo verify sin costo.

Referencia completa: docs/CERTIFY-ANYTHING.md.

Cuando necesitas probarlo a otra persona

Una instalación local puede auditar una extracción, pero no puede atestiguar una — cualquier cosa que firme, cualquiera podría falsificar. pdfmux Cloud devuelve un manifiesto firmado con Ed25519 sobre la extracción: tu auditor lo verifica sin conexión, contra una clave pública publicada, sin cuenta y sin confiar en pdfmux.

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

La verificación es gratuita y abierta para siempre; solo la generación es de pago ($49/mes). Esa asimetría es deliberada — nunca deberías necesitar nuestro permiso para comprobar nuestro trabajo.

Herramienta gratuita, sin registro: app.pdfmux.com/audit — sube un PDF y ve qué páginas descartó silenciosamente tu extractor actual. Precisión medida (y sus puntos ciegos) publicada en pdfmux-bench.

Arquitectura

                           ┌─────────────────────────────┐
                           │     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)│
                    └────────────────────────────────────────┘

Decisiones clave de diseño

  • Enrutador, no extractor. pdfmux no compite con PyMuPDF o Docling. Elige el mejor por página.
  • Multi-pase agéntico. Extrae, audita confianza, re-extrae fallos con un backend más potente. Las páginas malas se reintentan automáticamente.
  • Detección a nivel de segmento. Cada página se clasifica por tipo de contenido (texto, tablas, imágenes, fórmulas, encabezados) antes de enrutar.
  • Confianza de 4 señales. Puntuación de calidad dinámica a partir de densidad de caracteres, ratio de ruido OCR, integridad de tablas y estructura de encabezados. No umbrales fijos.
  • Caché de documentos. Cada PDF se abre una vez, no una vez por extractor. Compartido en todo el pipeline.
  • Volante de datos. Telemetría local rastrea qué extractores ganan por tipo de documento. El enrutamiento mejora con el uso.

Características

CaracterísticaQué haceComando
Extracción sin configuraciónEnruta automáticamente al mejor backendpdfmux convert file.pdf
Fragmentación RAGFragmentos conscientes de sección con estimaciones de tokenspdfmux convert file.pdf --chunk --max-tokens 500
Modos de costoeconomy / balanced / premium con límites de presupuestopdfmux convert file.pdf --mode economy --budget 0.50
Extracción de esquema5 presets integrados (factura, recibo, contrato, currículum, artículo)pdfmux convert file.pdf --schema invoice
PerfilesGuarda y reutiliza configuración; integrados para facturas/recibos/artículos/contratos/bulk-ragpdfmux convert file.pdf --profile invoices
LLM BYOKGemini, Gemma 3, Claude, GPT-4o, Ollama, Mistral, cualquier API compatible con OpenAIpdfmux convert file.pdf --llm-provider claude
Estimación de costoPredice el gasto antes de ejecutarpdfmux estimate file.pdf --llm-provider gemini
Salida en streamingEventos NDJSON página por página para documentos largospdfmux stream file.pdf
Caché inteligenteCaché de resultados con clave hash, TTL de 30 días, LRU de 1 GBpdfmux convert file.pdf (auto), --no-cache para omitir
Modo vigilanciaConvierte automáticamente cualquier PDF añadido a una carpetapdfmux watch ./inbox/
DiffCompara dos extraccionespdfmux diff a.pdf b.pdf
BenchmarkEvalúa todos los extractores instalados contra la verdad de referenciapdfmux benchmark
DoctorMuestra backends instalados, brechas de cobertura, recomendacionespdfmux doctor
Servidor MCPAgentes de IA leen PDFs vía stdio o HTTPpdfmux serve
Procesamiento por lotesConvierte directorios completospdfmux convert ./docs/
API de streaming a nivel de páginaIteración de páginas con memoria limitada para archivos grandesfor page in ext.extract("500pg.pdf")
Reintento con retrocesoCada proveedor LLM reintenta automáticamente con retroceso exponencial + Retry-After(integrado)

Referencia de CLI

pdfmux convert

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

Inicia el servidor MCP para integración con agentes de IA.

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

pdfmux doctor

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

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

Predice el gasto (y qué backends se ejecutarán) antes de procesar.

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

Emite eventos NDJSON a medida que se completan las páginas — útil para PDFs muy largos y UIs en vivo.

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

Convierte automáticamente cualquier PDF que llegue a un directorio. Permanece hasta Ctrl+C.

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

pdfmux diff

Comparación de extracción lado a lado (calidad, contenido, costo).

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

pdfmux profiles

Configuraciones guardadas en ~/.config/pdfmux/profiles.yaml. Los integrados se envían para las formas comunes; guarda las tuyas para valores predeterminados del proyecto.

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 invoices
pdfmux profiles save my-default --quality high --format llm --chunk
pdfmux profiles delete my-default

# use a profile when converting
pdfmux convert file.pdf --profile invoices

API de Python

Extracción de texto

import pdfmux

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

Extracción estructurada

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": [...]}]

Fragmentación RAG

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']})")

Extracción guiada por esquema

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 (memoria limitada)

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

Tipos y errores

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
)

Integraciones con frameworks

LangChain

pip install langchain-pdfmux
from langchain_pdfmux import PDFMuxLoader

loader = PDFMuxLoader("report.pdf", quality="standard")
docs = loader.load()  # -> list[Document] with confidence metadata

LlamaIndex

pip install llama-index-readers-pdfmux
from llama_index.readers.pdfmux import PDFMuxReader

reader = PDFMuxReader(quality="standard")
docs = reader.load_data("report.pdf")  # -> list[Document]

Servidor MCP (Agentes de IA)

Listado en mcpservers.org. Configuración en una línea:

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

O vía Claude Code:

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

Herramientas expuestas: convert_pdf, analyze_pdf, extract_structured, extract_streaming, get_pdf_metadata, batch_convert.

Configuración de LLM BYOK

pdfmux soporta cualquier LLM mediante 5 líneas de YAML. Trae tus propias claves -- nada sale de tu máquina a menos que lo configures.

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

Proveedores soportados:

ProveedorModelos¿Local?Costo
Gemini2.5 Flash, 2.5 ProNo~$0.01/página
Gemma 327B IT, 12B IT (excelente para árabe)No (vía clave de Gemini)~$0.0002/página
ClaudeSonnet, OpusNo~$0.015/página
GPT-4oGPT-4o, GPT-4o-miniNo~$0.01/página
Mistralmistral-ocr-latestNo$0.002/página
OllamaCualquier modelo localGratis
PersonalizadoCualquier API compatible con OpenAIConfigurableVaría

El extract_page() de cada proveedor está envuelto en @with_retry(max_attempts=3, backoff_base=2.0), which honors Retry-After en respuestas 429 y omite reintentos en fallos de autenticación para que una clave mala falle rápido.

Soporte para árabe y RTL

pdfmux incluye soporte de primera clase para árabe, persa, urdu y hebreo. De serie, la detección RTL se ejecuta en cada PDF y las páginas extraídas con PyMuPDF pasan por el Algoritmo Bidireccional Unicode para que los glifos almacenados en orden de izquierda a derecha se rendericen en el orden de lectura correcto.

# Default install — already includes python-bidi for RTL reordering
pip 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=...

Qué ocurre automáticamente:

  • pdfmux convert detecta contenido árabe y enruta páginas con >5% caracteres árabes a través de la cadena de extractores conscientes del árabe.
  • Las salidas de PyMuPDF, RapidOCR y Docling se post-procesan con el algoritmo Bidi — los encabezados de markdown (#) y las filas de tablas con tuberías preservan la estructura, solo se reordena el texto interno.
  • DocumentResult.has_arabic se establece a True cuando cualquier página contiene escritura árabe.

Qué requiere opt-in:

  • Extracción con LLM de visión. Configura --llm-provider gemma (o cualquier proveedor de visión) para enrutar páginas árabes a través de Gemma en lugar de PyMuPDF.
  • Normalización agresiva (eliminación de Tatweel, unificación de Alef/Yeh, eliminación de Tashkeel) — llama a pdfmux.arabic.normalize_arabic(text) sobre las cadenas extraídas si necesitas salida canonizada para búsqueda o embeddings.
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 engines
visual = fix_bidi_order(text)

# Canonicalize for indexing — strip Tatweel, unify Alef variants, drop diacritics
indexable = normalize_arabic("أَحْمَدْ")  # → "احمد"

Prueba: un lote real de clientes

Medimos pdfmux en 433 documentos reales de clientes — hojas de datos técnicos y de seguridad, mezcla de digitales y escaneados, algunos con codificación corrupta. Ejecutado primero de forma ingenua (una CLI temprana de pdfmux en un subproceso, respaldo pypdf, sin OCR), el pipeline descartó silenciosamente 16 documentos — 11 de ellos sin ninguna línea de registro. Ese era nuestro propio fallando exactamente en lo que promete.

Reconstruido con la auditoría por página + cascada OCR con presupuesto: 433 de 433 procesados, cero fallos silenciosos. Cada página irrecuperable se marca, no se descarta. (Un pequeño conjunto interno de calibración de confianza también se incluye bajo eval/ — es una salvaguarda de regresión para la puerta de confianza, no un benchmark competitivo; ver eval/README.md.)

Benchmark

En opendataloader-bench — 200 PDFs del mundo real (declaraciones financieras, artículos académicos, contratos legales, informes gubernamentales) — pdfmux obtiene 0.903 en general — #2 de los 8 motores medidos, detrás de opendataloader-hybrid (0.909). Re-ejecución 2026-07-16 (reproducción a continuación).

RangoMotorGeneralOrden de lecturaTablas (TEDS)LicenciaGPU
1opendataloader-hybrid0.9090.9350.928Apache-2.0No
2pdfmux0.9030.9200.911MITNo
3Docling0.8770.9000.887MITOpcional
4marker0.8610.8900.808gratisGPU
5mineru0.8310.8570.873gratisGPU

Puntuaciones completas por documento: el cara a cara de 200 PDF · metodología: mejor biblioteca de extracción de PDF, evaluada.

Caché de Resultados Inteligente

Re-ejecutar la misma extracción es instantáneo. pdfmux calcula el hash de cada PDF de entrada (SHA-256) y clavea los resultados en (file_hash, quality, format, schema). Los archivos de caché viven bajo ~/.cache/pdfmux/results/, expiran después de 30 días y se eliminan por LRU a 1 GB.

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

La caché también acelera los cambios de --profile, --schema y --format — cada combinación se clavea de forma independiente, por lo que puedes alternar entre Markdown y JSON para el mismo documento sin costo después de la primera extracción.

Puntuación de Confianza

Cada resultado incluye una puntuación de confianza de 4 señales:

  • 95-100% -- texto digital limpio, totalmente extraíble
  • 80-95% -- buena extracción, ruido menor de OCR en algunas páginas
  • 50-80% -- extracción parcial, algunas páginas irrecuperables
  • <50% -- falta contenido significativo, se incluyen advertencias

Cuando la confianza cae por debajo del 80%, pdfmux te dice exactamente qué salió mal y cómo solucionarlo:

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

Modos de Costo

ModoComportamientoCosto típico
economySolo backends basados en reglas. Sin llamadas a LLM.$0/página
balancedLLM solo para páginas que fallan en la extracción basada en reglas.~$0.002/página promedio
premiumLLM en cada página para máxima calidad.~$0.01/página

Establece un límite de presupuesto duro: --budget 0.50 detiene las llamadas a LLM cuando el gasto alcanza $0.50 por documento.

¿Por qué pdfmux?

pdfmux no es otro extractor de PDF. Es la capa de orquestación que elige el extractor adecuado por página, verifica el resultado y reintenta los fallos.

HerramientaBueno enLimitación
PyMuPDFTexto digital rápidoNo puede manejar escaneos o diseños de imagen
DoclingTablas (97.9% de precisión)Lento en documentos sin tablas
MarkerExtracción neuronal para artículos académicosNecesita GPU para velocidad; excesivo para PDFs digitales
Mistral OCRTablas (96.6% TEDS), $0.002/páginaAPI solo en la nube
UnstructuredPlataforma empresarialConfiguración compleja, niveles de pago
LlamaParseNativo de la nubeRequiere claves API, no local
ReductoAlta precisión$0.015/página, código cerrado
pdfmuxOrquesta todo lo anteriorEnruta por página, audita, re-extrae

Alternativa de código abierto a Reducto: lo que cuesta $0.015/página en otros lugares es gratis con los backends basados en reglas de pdfmux, o ~$0.002/página promedio con respaldo de LLM BYOK.

Desarrollo

git clone https://github.com/NameetP/pdfmux.git
cd pdfmux
python3.12 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

pytest              # 659 tests
ruff check src/ tests/
ruff format src/ tests/

Contribuciones

  1. Haz un fork del repositorio
  2. Crea una rama (git checkout -b feature/your-feature)
  3. Escribe pruebas para la nueva funcionalidad
  4. Asegúrate de que pytest y ruff check pasen
  5. Abre un PR

Licencia

La biblioteca pdfmux y el servidor MCP en este repositorio tienen licencia MIT — gratis para cualquier uso, y cada versión publicada permanece MIT.

El método de rastro de decisión con presupuesto de confianza (el rastro de decisión persistido por página con candidatos rechazados retenidos, y la salvaguarda de reparación monótona) está pendiente de patente (Solicitud Provisional de EE. UU. No. 64/106,302) y está reservado para pdfmux Cloud/Pro bajo una licencia comercial separada — no es parte de la concesión MIT. Ver LICENSING.md y NOTICE.