Extrair texto de PDF em Python
Extrair texto de PDF é uma das tarefas mais comuns em qualquer pipeline de processamento de documentos — da construção de índices de busca e da alimentação de sistemas RAG até a mineração de dados e os fluxos de compliance. Este guia reúne tudo o que você precisa para extrair texto de PDFs em Python, JavaScript e Rust com o PDF Oxide, passando por extração de texto puro, posicionamento no nível do caractere, spans com estilo, OCR para documentos escaneados, suporte a arquivos criptografados e ajuste fino de performance para pipelines em lote.
Extraia texto de qualquer PDF em três linhas:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("document.pdf")
text = doc.extract_text(0) # pagina 0
print(text)
WASM
import { WasmPdfDocument } from "pdf-oxide-wasm";
const bytes = new Uint8Array(buffer);
const doc = new WasmPdfDocument(bytes);
const text = doc.extractText(0); // pagina 0
console.log(text);
doc.free();
Rust
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("document.pdf")?;
let text = doc.extract_text(0)?;
println!("{}", text);
Go
package main
import (
"fmt"
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
doc, err := pdfoxide.Open("document.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
text, err := doc.ExtractText(0) // pagina 0
if err != nil { log.Fatal(err) }
fmt.Println(text)
}
C#
using PdfOxide;
using var doc = PdfDocument.Open("document.pdf");
var text = doc.ExtractText(0); // pagina 0
Console.WriteLine(text);
O PDF Oxide extrai texto com média de 0,8 ms por página — 5× mais rápido que o PyMuPDF e 15× mais rápido que o pypdf — com taxa de sucesso de 100 % em 3.830 PDFs de teste.
Por que extrair texto de PDF é difícil
O PDF é um formato visual, não textual. Diferente do HTML ou do Markdown, um arquivo PDF não guarda “parágrafos” nem “frases”: ele guarda caracteres individuais posicionados em coordenadas específicas de cada página. Para transformar isso em texto legível, é preciso lidar com várias etapas:
- Decodificação das fontes — as fontes de um PDF mapeiam códigos de caractere para glifos usando tabelas de codificação (WinAnsi, MacRoman, CMaps Unicode, Type 1, TrueType, CIDFont). O código
0x41pode significar “A” em uma fonte e “α” em outra. - Parsing do fluxo de texto — operadores como
Tj,TJ,'e"posicionam caracteres na página. Ajustes de kerning nos arraysTJdeslocam caracteres em frações de ponto e os espaços ausentes precisam ser inferidos a partir do vão entre as posições. - Reconstrução de layout — os caracteres numa página não têm ordem de leitura explícita. Layouts de duas colunas, cabeçalhos, rodapés, tabelas e barras laterais precisam de uma análise espacial para gerar um fluxo linear de texto.
- Casos de borda de codificação — texto CJK (chinês, japonês, coreano) usa codificações CIDFont/CMap com milhares de glifos. Árabe e hebraico exigem reordenação da direita para a esquerda. Ligaduras (fi, fl, ffi) precisam ser decompostas.
- Subconjuntos embutidos — muitos PDFs embutem apenas os glifos que realmente usam, com vetores de codificação próprios. Uma fonte pode mapear o glifo 1→“T”, 2→“h”, 3→“e” sem seguir nenhuma codificação padrão.
Por isso bibliotecas de PDF diferentes geram saídas de texto diferentes para o mesmo arquivo — e algumas falham por completo em documentos complexos. O PDF Oxide trata todos esses casos com um parser em Rust validado em 3.830 PDFs reais com 100 % de sucesso.
Instalação
Python (PyPI):
pip install pdf_oxide
Wheels pré-compilados para Linux (x86_64, aarch64), macOS (Intel e Apple Silicon) e Windows (x86_64). Python 3.8 ou superior. Sem dependências de sistema: o núcleo em Rust é compilado dentro do wheel, então você não precisa instalar Poppler, MuPDF nem nenhuma biblioteca em C.
JavaScript (npm):
npm install pdf-oxide-wasm
Funciona em Node.js 18 ou superior e em navegadores modernos. O binário WASM já vem embutido no pacote.
Rust (Cargo):
cargo add pdf_oxide
Requer Rust 1.70 ou superior. Fora a toolchain padrão do Rust, não há outras dependências de sistema.
Extrair todas as páginas
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
full_text = []
for i in range(doc.page_count()):
text = doc.extract_text(i)
full_text.append(text)
print("\n".join(full_text))
WASM
const doc = new WasmPdfDocument(bytes);
const fullText = doc.extractAllText();
console.log(fullText);
doc.free();
Rust
let mut doc = PdfDocument::open("report.pdf")?;
let mut full_text = Vec::new();
for i in 0..doc.page_count()? {
full_text.push(doc.extract_text(i)?);
}
println!("{}", full_text.join("\n"));
Go
doc, err := pdfoxide.Open("report.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
full, err := doc.ExtractAllText()
if err != nil { log.Fatal(err) }
fmt.Println(full)
C#
using var doc = PdfDocument.Open("report.pdf");
var parts = new List<string>();
for (int i = 0; i < doc.PageCount; i++)
parts.Add(doc.ExtractText(i));
Console.WriteLine(string.Join("\n", parts));
Extrair texto com as posições dos caracteres
Obtenha coordenadas exatas, nomes de fonte e tamanhos para cada caractere:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
chars = doc.extract_chars(0)
for ch in chars[:20]:
print(f"'{ch.char}' at ({ch.x:.1f}, {ch.y:.1f}) "
f"font={ch.font_name} size={ch.font_size:.1f}")
WASM
const doc = new WasmPdfDocument(bytes);
const chars = doc.extractChars(0);
for (const ch of chars.slice(0, 20)) {
console.log(`'${ch.char}' at (${ch.x.toFixed(1)}, ${ch.y.toFixed(1)}) font=${ch.fontName} size=${ch.fontSize.toFixed(1)}`);
}
doc.free();
Rust
let mut doc = PdfDocument::open("paper.pdf")?;
let chars = doc.extract_chars(0)?;
for ch in chars.iter().take(20) {
println!("'{}' at ({:.1}, {:.1}) font={} size={:.1}",
ch.char, ch.x, ch.y, ch.font_name, ch.font_size);
}
Go
doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()
chars, _ := doc.ExtractChars(0)
for _, ch := range chars[:20] {
fmt.Printf("%q at (%.1f, %.1f) font=%s size=%.1f\n",
ch.Char, ch.X, ch.Y, ch.FontName, ch.FontSize)
}
C#
using var doc = PdfDocument.Open("paper.pdf");
var chars = doc.ExtractChars(0);
foreach (var ch in chars.Take(20))
Console.WriteLine($"'{ch.Char}' at ({ch.X:F1}, {ch.Y:F1}) font={ch.FontName} size={ch.FontSize:F1}");
Cada caractere inclui:
| Campo | Tipo | Descrição |
|---|---|---|
char |
str |
O caractere Unicode |
x, y |
float |
Posição em pontos |
font_size |
float |
Tamanho da fonte em pontos |
font_name |
str |
Nome PostScript da fonte |
bbox |
tuple |
Caixa delimitadora (x0, y0, x1, y1) |
A extração no nível do caractere é útil para reconstruir tabelas, detectar títulos pelo tamanho da fonte ou desenhar caixas delimitadoras ao redor de regiões de texto. Você pode, por exemplo, agrupar caracteres em linhas pela coordenada y e identificar os limites das colunas pelos vãos nas posições x.
Extrair spans de texto estilizados
Agrupa caracteres consecutivos com a mesma fonte e o mesmo tamanho:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
spans = doc.extract_spans(0)
for span in spans:
print(f"'{span.text}' font={span.font_name} size={span.font_size:.1f}")
WASM
const doc = new WasmPdfDocument(bytes);
const spans = doc.extractSpans(0);
for (const span of spans) {
console.log(`'${span.text}' font=${span.fontName} size=${span.fontSize.toFixed(1)}`);
}
doc.free();
Rust
let mut doc = PdfDocument::open("paper.pdf")?;
let spans = doc.extract_spans(0)?;
for span in &spans {
println!("'{}' font={} size={:.1}", span.text, span.font_name, span.font_size);
}
Útil para identificar títulos, trechos em negrito ou montar uma saída estruturada.
Processamento em lote
Processe centenas ou milhares de PDFs de uma vez:
from pdf_oxide import PdfDocument, PdfError
from pathlib import Path
pdf_dir = Path("documents/")
for pdf_path in pdf_dir.glob("*.pdf"):
try:
doc = PdfDocument(str(pdf_path))
for i in range(doc.page_count()):
text = doc.extract_text(i)
# Processar texto...
except PdfError as e:
print(f"Ignorado {pdf_path.name}: {e}")
A 0,8 ms por página, processar 3.830 PDFs leva cerca de 3,1 segundos. Para pipelines de produção, consulte o guia de processamento em lote, com padrões de paralelização usando multiprocessing e I/O assíncrono.
Tratamento de PDFs escaneados (OCR)
Quando o PDF traz imagens escaneadas em vez de texto, extract_text() devolve uma string vazia ou quase vazia. Nesses casos, use o OCR embutido no PDF Oxide:
from pdf_oxide import PdfDocument
doc = PdfDocument("scanned.pdf")
text = doc.extract_text(0)
if not text.strip():
# Pagina provavelmente escaneada — usar OCR
text = doc.extract_text_ocr(0)
print(text)
O PDF Oxide usa o PaddleOCR via ONNX Runtime — você não precisa instalar o Tesseract. Veja o guia de OCR para escolher e configurar o modelo.
Tratamento de PDFs criptografados
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("protected.pdf", password="secret")
text = doc.extract_text(0)
print(text)
WASM
const doc = new WasmPdfDocument(bytes);
doc.authenticate("secret");
const text = doc.extractText(0);
console.log(text);
doc.free();
Rust
let mut doc = PdfDocument::open_with_password("protected.pdf", "secret")?;
let text = doc.extract_text(0)?;
println!("{}", text);
Go
doc, _ := pdfoxide.Open("protected.pdf")
defer doc.Close()
if _, err := doc.Authenticate("secret"); err != nil { log.Fatal(err) }
text, _ := doc.ExtractText(0)
fmt.Println(text)
C#
using var doc = PdfDocument.OpenWithPassword("protected.pdf", "secret");
Console.WriteLine(doc.ExtractText(0));
Há suporte a PDFs criptografados com AES-256, AES-128 e RC4. Diferente do pdfplumber (que não consegue abrir arquivos criptografados) e do pdfminer (que falha em AES-256), o PDF Oxide lida de forma transparente com todos os métodos padrão de criptografia de PDF.
Saída em Markdown
Quando precisar de uma saída estruturada com títulos e formatação:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
md = doc.to_markdown(0, detect_headings=True)
print(md)
WASM
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdown(0);
console.log(md);
doc.free();
Rust
let mut doc = PdfDocument::open("paper.pdf")?;
let md = doc.to_markdown(0, true)?;
println!("{}", md);
Go
doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()
md, _ := doc.ToMarkdown(0)
fmt.Println(md)
C#
using var doc = PdfDocument.Open("paper.pdf");
Console.WriteLine(doc.ToMarkdown(0));
Veja o guia de PDF para Markdown com padrões de integração para RAG e LLMs.
Busca dentro de PDFs
Encontre texto em todas as páginas com dados de posição:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("manual.pdf")
results = doc.search("configuration")
for r in results:
print(f"Page {r.page}: '{r.text}' at ({r.x:.0f}, {r.y:.0f})")
WASM
const doc = new WasmPdfDocument(bytes);
const results = doc.search("configuration", false);
for (const r of results) {
console.log(`Page ${r.page}: '${r.text}' at (${r.x.toFixed(0)}, ${r.y.toFixed(0)})`);
}
doc.free();
Rust
let mut pdf = Pdf::open("manual.pdf")?;
let results = pdf.search("configuration")?;
for r in &results {
println!("Page {}: '{}' at ({:.0}, {:.0})", r.page, r.text, r.bbox.x, r.bbox.y);
}
Go
doc, _ := pdfoxide.Open("manual.pdf")
defer doc.Close()
results, _ := doc.SearchAll("configuration", false)
for _, r := range results {
fmt.Printf("Page %d: %q at (%.0f, %.0f)\n", r.PageIndex, r.Text, r.X, r.Y)
}
C#
using var doc = PdfDocument.Open("manual.pdf");
foreach (var r in doc.SearchAll("configuration", caseSensitive: false))
Console.WriteLine($"Page {r.PageIndex}: '{r.Text}' at ({r.X:F0}, {r.Y:F0})");
Comparação com outras bibliotecas de PDF em Python
Existem várias bibliotecas em Python para extrair texto de PDFs. Veja como elas se comparam:
- pypdf — Python puro, sem dependências em C. Fácil de instalar, mas lento (12 ms por página) e falha em 1,6 % dos PDFs por conta do suporte limitado a fontes e codificações. Não oferece dados de posição de caractere. Serve para PDFs simples em que velocidade não importa.
- pdfplumber — construído sobre o pdfminer, oferece extração detalhada de caracteres e tabelas. Muito lento (23 ms por página) e não abre PDFs criptografados. É uma boa escolha para tabelas quando você precisa de dados por célula e não se importa com o desempenho.
- PyMuPDF (fitz) — bindings de Python para a biblioteca em C MuPDF. Rápido (4,6 ms por página) e confiável (99,3 % de sucesso). Exige a instalação de uma biblioteca C e tem licença AGPL. É uma opção sólida se a licença couber no seu projeto.
- pypdfium2 — bindings de Python para o motor PDFium do Google. Rápido (4,1 ms por página), mas com p99 elevado (42 ms) em documentos complexos. A superfície de API é menor que a do PyMuPDF.
- pdfminer.six — Python puro com análise detalhada de layout. Muito lento e sem manutenção. Falha em PDFs criptografados com AES-256. Foi, em grande parte, substituído pelo pdfplumber.
- PDF Oxide — núcleo em Rust com bindings de Python via PyO3. A opção mais rápida (0,8 ms por página), 100 % de sucesso, cobre todos os métodos de criptografia e traz OCR embutido. Licença MIT e sem dependências de sistema.
O PDF Oxide foi criado justamente para preencher as lacunas das bibliotecas existentes: os limites de desempenho dos parsers em Python puro, as restrições de licença do MuPDF e os problemas de confiabilidade que fazem outras bibliotecas falharem em PDFs reais com fontes incomuns, tabelas de referência cruzada quebradas ou codificações não padronizadas.
Performance: quão rápido é o PDF Oxide?
Medimos em 3.830 PDFs de três conjuntos de teste públicos e independentes:
| Biblioteca | Média | p99 | Taxa de sucesso |
|---|---|---|---|
| PDF Oxide | 0,8 ms | 9 ms | 100 % |
| PyMuPDF | 4,6 ms | 28 ms | 99,3 % |
| pypdfium2 | 4,1 ms | 42 ms | 99,2 % |
| pypdf | 12,1 ms | 97 ms | 98,4 % |
| pdfplumber | 23,2 ms | 189 ms | 98,8 % |
Para um pipeline que processa 10.000 PDFs:
- PDF Oxide: 8 segundos
- PyMuPDF: 46 segundos
- pypdf: 2 minutos
- pdfplumber: 3,9 minutos
Veja os benchmarks completos com metodologia e como reproduzir os resultados.
Problemas comuns e soluções
Saída de texto vazia
Se extract_text() devolver uma string vazia, é provável que a página traga imagens escaneadas em vez de texto. Use extract_text_ocr() no lugar. O passo a passo de configuração está em OCR em PDFs escaneados.
Caracteres estranhos ou incorretos
Costuma ser uma fonte com vetor de codificação fora do padrão ou sem CMap ToUnicode. O PDF Oxide trata a maior parte dos casos, mas alguns PDFs propositalmente ofuscados (conteúdo com DRM) podem gerar saída incorreta.
Espaços faltando ou palavras grudadas
Os operadores de texto do PDF posicionam caracteres um a um. A inferência de espaços depende do vão entre posições de caractere em relação à largura do espaço da fonte. Se as palavras aparecerem coladas, tente extract_chars() e aplique sua própria lógica de espaçamento com base nas coordenadas.
Saída diferente da de outras bibliotecas
Cada biblioteca usa suas próprias heurísticas para inferir espaços, quebras de linha e ordem de leitura. O PDF Oxide atinge 99,5 % de paridade textual com o PyMuPDF em 3.830 PDFs. Os 0,5 % de diferença ficam por conta da normalização de espaços e do tratamento de ligaduras.
Casos reais de uso
Indexação para busca — Extraia o texto de cada página de cada PDF de um repositório documental e mande esse texto para o Elasticsearch, o Typesense ou um banco de dados vetorial para busca textual completa. A velocidade do PDF Oxide torna viável reindexar milhares de documentos sob demanda.
Pipelines RAG (geração aumentada por recuperação) — Extraia e fragmente o texto dos PDFs para gerar embeddings com OpenAI, Cohere ou modelos open source. Use extract_spans() para preservar a estrutura dos títulos e manter os chunks alinhados com as seções do documento. Para saída otimizada para LLMs, veja o guia de PDF para Markdown.
Compliance e auditoria — Varra contratos, notas fiscais e documentos regulatórios em busca de cláusulas ou palavras-chave específicas. Use doc.search() para localizar termos em todas as páginas com posições precisas, ou extraia o texto completo para detecção de cláusulas por NLP.
Extração de dados — Extraia dados estruturados de faturas, recibos, extratos bancários e formulários. Combine extract_chars() com regras do seu domínio para localizar campos como “Valor total” ou “Data da fatura” e capturar os valores adjacentes.
Pesquisa acadêmica — Processe milhares de artigos para revisões de literatura, extração de citações ou meta-análises. O PDF Oxide cobre todo o leque de produtores de PDF (LaTeX, Word, InDesign, Quark) e das codificações de fonte encontradas em publicações acadêmicas.
Páginas relacionadas
- API de extração de texto — referência completa da API
- PDF para Markdown — conversão estruturada
- Processamento em lote — padrões de paralelização
- OCR em PDFs escaneados — configuração e uso do OCR
- Benchmarks de performance — metodologia e resultados