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

CI PyPI Python 3.11+ License: MIT Downloads

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 verify audita 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.

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)

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

RecursoO que fazComando
Extração zero-configRoteia para o melhor backend automaticamentepdfmux convert file.pdf
Chunking para RAGChunks cientes de seção com estimativas de tokenspdfmux convert file.pdf --chunk --max-tokens 500
Modos de custoeconomy / balanced / premium com limites de orçamentopdfmux convert file.pdf --mode economy --budget 0.50
Extração com schema5 predefinições integradas (invoice, receipt, contract, resume, paper)pdfmux convert file.pdf --schema invoice
PerfisSalvar e reutilizar configuração; integrados para invoices/receipts/papers/contracts/bulk-ragpdfmux convert file.pdf --profile invoices
LLM BYOKGemini, Gemma 3, Claude, GPT-4o, Ollama, Mistral, qualquer API compatível com OpenAIpdfmux convert file.pdf --llm-provider claude
Estimativa de custoPreveja gastos antes de executarpdfmux estimate file.pdf --llm-provider gemini
Saída em streamingEventos NDJSON página por página para documentos longospdfmux stream file.pdf
Cache inteligenteCache de resultados com chave hash, TTL de 30 dias, LRU de 1 GBpdfmux convert file.pdf (automático), --no-cache para ignorar
Modo de observaçãoConverte automaticamente qualquer PDF adicionado a uma pastapdfmux watch ./inbox/
DiffCompara duas extraçõespdfmux diff a.pdf b.pdf
BenchmarkAvalia todos os extratores instalados contra a verdade de basepdfmux benchmark
DoctorMostra backends instalados, lacunas de cobertura, recomendaçõespdfmux doctor
Servidor MCPAgentes de IA leem PDFs via stdio ou HTTPpdfmux serve
Processamento em loteConverte diretórios inteirospdfmux convert ./docs/
API de streaming em nível de páginaIteração de página com memória limitada para arquivos grandesfor page in ext.extract("500pg.pdf")
Retry com backoffTodo 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:

ProvedorModelosLocal?Custo
Gemini2.5 Flash, 2.5 ProNão~$0,01/página
Gemma 327B IT, 12B IT (ótimo para árabe)Não (via chave Gemini)~$0,0002/página
ClaudeSonnet, OpusNão~$0,015/página
GPT-4oGPT-4o, GPT-4o-miniNão~$0,01/página
Mistralmistral-ocr-latestNão$0,002/página
OllamaQualquer modelo localSimGratuito
CustomQualquer API compatível com OpenAIConfigurávelVaria

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 convert detecta 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 como True sempre 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çãoMecanismoGeralOrdem de leituraTabelas (TEDS)LicençaGPU
1opendataloader-hybrid0.9090.9350.928Apache-2.0Não
2pdfmux0.9030.9200.911MITNão
3Docling0.8770.9000.887MITOpcional
4marker0.8610.8900.808gratuitoGPU
5mineru0.8310.8570.873gratuitoGPU

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

ModoComportamentoCusto típico
economyApenas backends baseados em regras. Sem chamadas de LLM.$0/página
balancedLLM apenas para páginas que falham na extração baseada em regras.~$0.002/página em média
premiumLLM 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.

FerramentaBom emLimitação
PyMuPDFTexto digital rápidoNão lida com digitalizações ou layouts de imagem
DoclingTabelas (97.9% de precisão)Lento em documentos sem tabelas
MarkerExtração neural para artigos acadêmicosPrecisa de GPU para velocidade; exagero para PDFs digitais
Mistral OCRTabelas (96.6% TEDS), $0.002/páginaAPI somente em nuvem
UnstructuredPlataforma empresarialConfiguração complexa, níveis pagos
LlamaParseNativo em nuvemRequer chaves de API, não é local
ReductoAlta precisão$0.015/página, código fechado
pdfmuxOrquestra todos os acimaRoteia 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

  1. Faça um fork do repositório
  2. Crie um branch (git checkout -b feature/your-feature)
  3. Escreva testes para novas funcionalidades
  4. Garanta que pytest e ruff check passem
  5. 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.