pdfmux
Roteador inteligente de PDF para Markdown que seleciona o melhor extrator por página, audita a qualidade da saída e reextrai falhas automaticamente. Pontuação de confiança, suporte a LLM com chave própria (BYOK), fragmentação para RAG.
Documentação
pdfmux
Extração de PDF com autocorreção que sinaliza as páginas que não consegue ler em vez de descartá-las — e agora certifica a saída de qualquer extrator contra descartes silenciosos. Alternativa open-source ao LlamaParse para pipelines de RAG, servidor MCP para Claude Desktop, loaders LangChain + LlamaIndex.
pdfmux extrai PDFs e verifica o próprio trabalho — e agora certifica o de qualquer extrator, informando quais páginas foram descartadas silenciosamente. Gratuito, MIT. Método com patente pendente.
pip install pdfmux.
Duas funções, uma ferramenta:
- Extração com autocorreção. O único extrator de PDF que audita a própria saída. Detecta páginas em branco, colunas embaralhadas, tabelas quebradas — reextrai com um backend mais robusto e sinaliza o que ainda não consegue ler em vez de descartar silenciosamente. Assim, seu LLM recebe dados limpos, não lixo silencioso. Roteia cada página para o melhor de 7 backends de extração integrados + fallback de LLM BYOK (Gemini / Claude / GPT-4o / Ollama). Um CLI. Uma API. Zero configuração.
- Certify Anything — novo na v1.8.1.
pdfmux verifyaudita a saída de qualquer mecanismo de extração contra o PDF de origem — Reducto, Mistral OCR, LlamaParse, Docling, seu parser interno — e informa quais páginas foram descartadas silenciosamente. Gratuito, MIT, sem conflitos de patente.
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)
Instalação
pip install pdfmux
Isso resolve PDFs digitais. Para qualquer lote do mundo real, instale o pdfmux[ocr] também — quase todo diretório de PDFs tem pelo menos um scan, e sem OCR essas páginas retornam texto vazio:
pip install "pdfmux[ocr]" # ⭐ recommended — RapidOCR for scanned pages (~200MB, CPU)
Outros 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
Requer Python 3.11+.
Início 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 processamento em lote, use batch_extract() — não um loop de subprocess.run(['pdfmux', ...]). Mesmo pipeline, sem spawn de processo por arquivo, lida com nomes de arquivo não 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
Não envolva o pdfmux com seu próprio fallback pypdf/pdfplumber. O pdfmux já roteia por página através de PyMuPDF → RapidOCR → LLM de visão. O PyMuPDF tolera PDFs malformados que o pypdf rejeita ("Stream has ended unexpectedly"), então um fallback pypdf downstream transforma PDFs recuperáveis em falhas. Confie no roteador; verifique a pontuação de confiança no resultado.
Certify Anything
pdfmux verify audita a saída de qualquer mecanismo de extração contra o PDF de origem e informa quais páginas foram descartadas silenciosamente — não apenas a extração do próprio pdfmux. Aponte para a saída do Reducto, Mistral OCR, LlamaParse, Docling ou seu parser interno e ele rederiva o texto de origem com a própria passada de auditoria do pdfmux, alinha a extração a ele e pontua cada página.
A falha que ele detecta: uma página onde a origem tem texto real, mas o mecanismo retornou nada — enquanto reportava sucesso. Esse "descarte silencioso" é exatamente a falha que envenena um índice RAG sem um único erro nos logs.
# 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 execução imprime um veredito PASS / REVIEW / FAIL, confiança geral e cobertura, e — quando encontra — as 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 você recebe um veredito (pass / review / fail), confiança, cobertura, alinhamento, risco de alucinação e integridade de tabela/cabeçalho. O modo lote resume isso em uma única linha "N páginas descartadas silenciosamente em M documentos" — o relatório que você executa em 100 dos seus próprios PDFs para encontrar as falhas silenciosas já presentes no seu pipeline.
Funciona com a saída de qualquer mecanismo
--extracted aceita JSON, Markdown ou texto simples (--extracted-format auto | json | markdown | text). Quando a extração expõe estrutura real por página, o pdfmux compara página por página; quando é um blob único, ele recorre a verificações de presença de conteúdo para nunca fabricar um "descarte silencioso" a partir de uma incompatibilidade de paginação.
API 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 manifesto carrega uma assinatura de conteúdo SHA-256 à prova de adulteração sobre seu corpo canônico e uma lista de limitações honesta embutida: o certificador é lexical, não linguístico — ele detecta conteúdo ausente e corrompido, não paráfrase fiel ou tradução.
MCP
verify_extraction é exposto como uma ferramenta MCP (a 7ª — veja Servidor MCP), para que um agente possa certificar a saída de um mecanismo na mesma sessão em que extrai.
Gratuito, MIT, sem conflitos de patente
O Certify Anything reutiliza apenas a camada de auditoria MIT incluída no pdfmux. Ele não inclui, e não requer, o método de rastreamento de decisão com patente pendente — isso permanece no pdfmux Cloud/Pro. pip install pdfmux oferece o comando completo de verify sem custo.
Referência completa: docs/CERTIFY-ANYTHING.md.
Quando você precisa provar para outra pessoa
Uma instalação local pode auditar uma extração, mas não pode atestar uma — qualquer coisa que ela assinar, qualquer um poderia forjar. O pdfmux Cloud retorna um manifesto assinado com Ed25519 sobre a extração: seu auditor verifica offline, contra uma chave pública publicada, sem conta e sem confiar no pdfmux.
pdfmux verify-manifest manifest.json # free, MIT, offline — no account
A verificação é gratuita e aberta para sempre; apenas a geração é paga ($49/mês). Essa assimetria é deliberada — você nunca deve precisar da nossa permissão para verificar nosso trabalho.
Ferramenta gratuita, sem cadastro: app.pdfmux.com/audit — envie um PDF e veja quais páginas seu extrator atual descartou silenciosamente. Precisão medida (e seus pontos cegos) publicada no pdfmux-bench.
Arquitetura
┌─────────────────────────────┐
│ 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)│
└────────────────────────────────────────┘
Principais decisões de design
- Roteador, não extrator. O pdfmux não compete com PyMuPDF ou Docling. Ele escolhe o melhor por página.
- Multi-passagem agêntica. Extrair, auditar confiança, reextrair falhas com um backend mais forte. Páginas ruins são tentadas novamente automaticamente.
- Detecção em nível de segmento. Cada página é classificada por tipo de conteúdo (texto, tabelas, imagens, fórmulas, cabeçalhos) antes do roteamento.
- Confiança com 4 sinais. Pontuação de qualidade dinâmica a partir de densidade de caracteres, taxa de ruído de OCR, integridade de tabela e estrutura de cabeçalho. Sem limites fixos.
- Cache de documentos. Cada PDF é aberto uma vez, não uma vez por extrator. Compartilhado em todo o pipeline.
- Flywheel de dados. Telemetria local rastreia quais extratores vencem por tipo de documento. O roteamento melhora com o uso.
Recursos
| Recurso | O que faz | Comando |
|---|---|---|
| Extração zero-config | Roteia para o melhor backend automaticamente | pdfmux convert file.pdf |
| Chunking para RAG | Chunks cientes de seção com estimativas de tokens | pdfmux convert file.pdf --chunk --max-tokens 500 |
| Modos de custo | economy / balanced / premium com limites de orçamento | pdfmux convert file.pdf --mode economy --budget 0.50 |
| Extração com schema | 5 predefinições integradas (invoice, receipt, contract, resume, paper) | pdfmux convert file.pdf --schema invoice |
| Perfis | Salvar e reutilizar configuração; integrados para invoices/receipts/papers/contracts/bulk-rag | pdfmux convert file.pdf --profile invoices |
| LLM BYOK | Gemini, Gemma 3, Claude, GPT-4o, Ollama, Mistral, qualquer API compatível com OpenAI | pdfmux convert file.pdf --llm-provider claude |
| Estimativa de custo | Preveja gastos antes de executar | pdfmux estimate file.pdf --llm-provider gemini |
| Saída em streaming | Eventos NDJSON página por página para documentos longos | pdfmux stream file.pdf |
| Cache inteligente | Cache de resultados com chave hash, TTL de 30 dias, LRU de 1 GB | pdfmux convert file.pdf (automático), --no-cache para ignorar |
| Modo de observação | Converte automaticamente qualquer PDF adicionado a uma pasta | pdfmux watch ./inbox/ |
| Diff | Compara duas extrações | pdfmux diff a.pdf b.pdf |
| Benchmark | Avalia todos os extratores instalados contra a verdade de base | pdfmux benchmark |
| Doctor | Mostra backends instalados, lacunas de cobertura, recomendações | pdfmux doctor |
| Servidor MCP | Agentes de IA leem PDFs via stdio ou HTTP | pdfmux serve |
| Processamento em lote | Converte diretórios inteiros | pdfmux convert ./docs/ |
| API de streaming em nível de página | Iteração de página com memória limitada para arquivos grandes | for page in ext.extract("500pg.pdf") |
| Retry com backoff | Todo provedor de LLM tenta novamente automaticamente com backoff exponencial + Retry-After | (integrado) |
Referência 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 o servidor MCP para integração com 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
Preveja gastos (e quais backends serão executados) antes do processamento.
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 conforme as páginas são concluídas — útil para PDFs muito longos e UIs ao 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
Converte automaticamente qualquer PDF que chegar a um diretório. Permanece ativo até Ctrl+C.
pdfmux watch ./inbox/ -o ./output/ --profile bulk-rag
pdfmux diff
Comparação de extração lado a lado (qualidade, conteúdo, custo).
pdfmux diff a.pdf b.pdf --quality standard
pdfmux profiles
Configurações salvas em ~/.config/pdfmux/profiles.yaml. Integrados para os formatos
comuns; salve os seus para padrões de projeto.
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 Python
Extração 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
Extração estruturada
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": [...]}]
Chunking para 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']})")
Extração guiada por schema
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 (memória 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 e erros
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
)
Integrações com 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 no mcpservers.org. Configuração em uma linha:
{
"mcpServers": {
"pdfmux": {
"command": "npx",
"args": ["-y", "pdfmux-mcp"]
}
}
}
Ou via Claude Code:
claude mcp add pdfmux -- npx -y pdfmux-mcp
Ferramentas expostas: convert_pdf, analyze_pdf, extract_structured,
extract_streaming, get_pdf_metadata, batch_convert.
Configuração de LLM BYOK
O pdfmux suporta qualquer LLM com 5 linhas de YAML. Traga suas próprias chaves — nada sai da sua máquina a menos que você configure para isso.
# ~/.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
Provedores suportados:
| Provedor | Modelos | Local? | Custo |
|---|---|---|---|
| Gemini | 2.5 Flash, 2.5 Pro | Não | ~$0,01/página |
| Gemma 3 | 27B IT, 12B IT (ótimo para árabe) | Não (via chave Gemini) | ~$0,0002/página |
| Claude | Sonnet, Opus | Não | ~$0,015/página |
| GPT-4o | GPT-4o, GPT-4o-mini | Não | ~$0,01/página |
| Mistral | mistral-ocr-latest | Não | $0,002/página |
| Ollama | Qualquer modelo local | Sim | Gratuito |
| Custom | Qualquer API compatível com OpenAI | Configurável | Varia |
O extract_page() de todo provedor é envolvido em @with_retry(max_attempts=3, backoff_base=2.0), which honors Retry-After em 429s e ignora
tentativas em falhas de autenticação para que uma chave inválida falhe rapidamente.
Suporte a Árabe e RTL
O pdfmux oferece suporte de primeira classe para árabe, persa, urdu e hebraico. Pronto para uso, a detecção de RTL é executada em todo PDF e as páginas extraídas pelo PyMuPDF passam pelo Algoritmo Bidirecional Unicode para que glifos armazenados em ordem da esquerda para a direita sejam renderizados na ordem de leitura correta.
# 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=...
O que acontece automaticamente:
pdfmux convertdetecta conteúdo em árabe e roteia páginas com >5% de caracteres árabes pela cadeia de extração ciente de árabe.- As saídas do PyMuPDF, RapidOCR e Docling são pós-processadas com o
algoritmo Bidi — cabeçalhos markdown (
#) e linhas de tabela pipe preservam estrutura, apenas o texto interno é reordenado. DocumentResult.has_arabicé definido comoTruesempre que qualquer página contém escrita árabe.
O que requer adesão:
- Extração com LLM de visão. Defina
--llm-provider gemma(ou qualquer provedor de visão) para rotear páginas em árabe pelo Gemma em vez do PyMuPDF. - Normalização agressiva (remoção de Tatweel, unificação de Alef/Yeh,
remoção de Tashkeel) — chame
pdfmux.arabic.normalize_arabic(text)em strings extraídas se precisar de saída canonizada para busca ou incorporação.
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("أَحْمَدْ") # → "احمد"
Prova: um lote real de cliente
Medimos o pdfmux em 433 documentos reais de clientes — fichas técnicas e de segurança, mistura de digital e escaneado, alguns com codificação corrompida. Executado da forma ingênua primeiro (um CLI inicial do pdfmux em um subprocesso, fallback pypdf, sem OCR), o pipeline descartou silenciosamente 16 documentos — 11 deles sem nenhuma linha de log. Essa era a nossa própria ferramenta falhando exatamente no que promete.
Reconstruído com a auditoria por página + cascata de OCR com orçamento: 433 de 433 processados, zero falhas silenciosas. Toda página irrecuperável é sinalizada, não descartada.
(Um pequeno conjunto interno de calibração de confiança também é distribuído sob eval/ — é uma proteção de regressão no portão de confiança, não um benchmark competitivo; veja eval/README.md.)
Benchmark
Em opendataloader-bench — 200 PDFs do mundo real (documentos financeiros, artigos acadêmicos, contratos legais, relatórios governamentais) — pdfmux pontua 0.903 no geral — #2 dos 8 mecanismos medidos, atrás de opendataloader-hybrid (0.909). Reexecução em 2026-07-16 (reprodução abaixo).
| Posição | Mecanismo | Geral | Ordem de leitura | Tabelas (TEDS) | Licença | GPU |
|---|---|---|---|---|---|---|
| 1 | opendataloader-hybrid | 0.909 | 0.935 | 0.928 | Apache-2.0 | Não |
| 2 | pdfmux | 0.903 | 0.920 | 0.911 | MIT | Não |
| 3 | Docling | 0.877 | 0.900 | 0.887 | MIT | Opcional |
| 4 | marker | 0.861 | 0.890 | 0.808 | gratuito | GPU |
| 5 | mineru | 0.831 | 0.857 | 0.873 | gratuito | GPU |
Pontuações completas por documento: o confronto direto de 200 PDFs · metodologia: melhor biblioteca de extração de PDF, avaliada.
Cache Inteligente de Resultados
Reexecutar a mesma extração é instantâneo. pdfmux calcula o hash de cada PDF de entrada (SHA-256) e chaveia os resultados em (file_hash, quality, format, schema). Os arquivos de cache ficam em ~/.cache/pdfmux/results/, expiram após 30 dias e são removidos 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
O cache também acelera as alternâncias de --profile, --schema e --format — cada combinação é chaveada independentemente, então você pode alternar entre Markdown e JSON para o mesmo documento gratuitamente após a primeira extração.
Pontuação de Confiança
Cada resultado inclui uma pontuação de confiança com 4 sinais:
- 95-100% -- texto digital limpo, totalmente extraível
- 80-95% -- boa extração, ruído menor de OCR em algumas páginas
- 50-80% -- extração parcial, algumas páginas irrecuperáveis
- <50% -- conteúdo significativo ausente, avisos incluídos
Quando a confiança cai abaixo de 80%, o pdfmux informa exatamente o que deu errado e como corrigir:
Page 4: 32% confidence. 0 chars extracted from image-heavy page.
-> Install pdfmux[ocr] for RapidOCR support on 6 image-heavy pages.
Modos de Custo
| Modo | Comportamento | Custo típico |
|---|---|---|
| economy | Apenas backends baseados em regras. Sem chamadas de LLM. | $0/página |
| balanced | LLM apenas para páginas que falham na extração baseada em regras. | ~$0.002/página em média |
| premium | LLM em todas as páginas para máxima qualidade. | ~$0.01/página |
Defina um limite rígido de orçamento: --budget 0.50 interrompe chamadas de LLM quando o gasto atinge $0.50 por documento.
Por que pdfmux?
pdfmux não é mais um extrator de PDF. É a camada de orquestração que escolhe o extrator certo por página, verifica o resultado e tenta novamente as falhas.
| Ferramenta | Bom em | Limitação |
|---|---|---|
| PyMuPDF | Texto digital rápido | Não lida com digitalizações ou layouts de imagem |
| Docling | Tabelas (97.9% de precisão) | Lento em documentos sem tabelas |
| Marker | Extração neural para artigos acadêmicos | Precisa de GPU para velocidade; exagero para PDFs digitais |
| Mistral OCR | Tabelas (96.6% TEDS), $0.002/página | API somente em nuvem |
| Unstructured | Plataforma empresarial | Configuração complexa, níveis pagos |
| LlamaParse | Nativo em nuvem | Requer chaves de API, não é local |
| Reducto | Alta precisão | $0.015/página, código fechado |
| pdfmux | Orquestra todos os acima | Roteia por página, audita, reextrai |
Alternativa open source ao Reducto: o que custa $0.015/página em outros lugares é gratuito com os backends baseados em regras do pdfmux, ou ~$0.002/página em média com fallback de LLM BYOK.
Desenvolvimento
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/
Contribuindo
- Faça um fork do repositório
- Crie um branch (
git checkout -b feature/your-feature) - Escreva testes para novas funcionalidades
- Garanta que
pytesteruff checkpassem - Abra um PR
Licença
A biblioteca pdfmux e o servidor MCP neste repositório são licenciados sob MIT — gratuitos para qualquer uso, e cada versão lançada permanece MIT.
O método decision-trace com orçamento de confiança (o rastreamento de decisão persistido por página com candidatos rejeitados retidos e a proteção de reparo monotônico) é patente pendente (US Provisional App No. 64/106,302) e é reservado para pdfmux Cloud/Pro sob uma licença comercial separada — não faz parte da concessão MIT. Consulte LICENSING.md e NOTICE.