Skip to content

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 0x41 pode 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 arrays TJ deslocam 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