Resposta rápida: instale pypdf, abra documentos com PdfReader, monte o resultado com PdfWriter e grave-o em um novo arquivo. A biblioteca une, divide e gira páginas, lê metadados, tenta extrair texto e aplica criptografia. Ela não faz OCR nem funciona como editor visual do conteúdo.
python -m pip install pypdf
PDF não é um formato de texto comum: é uma descrição de como elementos devem aparecer em páginas. Essa diferença explica tanto o poder quanto os limites da automação. Antes de começar, vale organizar o projeto em um ambiente virtual Python e usar pathlib para caminhos. A referência de cada classe está na documentação oficial do pypdf.
Unir vários PDFs na ordem correta
O fluxo mais simples recebe uma lista explícita. Isso evita depender da ordem imprevisível do sistema de arquivos. O programa também recusa sobrescrever uma entrada por engano e cria o diretório de saída.
from pathlib import Path
from pypdf import PdfReader, PdfWriter
def unir_pdfs(entradas: list[Path], saida: Path) -> None:
if not entradas:
raise ValueError("Informe ao menos um PDF")
saida = saida.resolve()
writer = PdfWriter()
for caminho in entradas:
caminho = caminho.resolve()
if caminho == saida:
raise ValueError("A saída não pode ser uma entrada")
if caminho.suffix.lower() != ".pdf" or not caminho.is_file():
raise ValueError(f"PDF inválido: {caminho}")
reader = PdfReader(caminho)
if reader.is_encrypted:
raise ValueError(f"PDF protegido: {caminho.name}")
for pagina in reader.pages:
writer.add_page(pagina)
saida.parent.mkdir(parents=True, exist_ok=True)
with saida.open("wb") as arquivo:
writer.write(arquivo)
unir_pdfs(
[Path("contrato.pdf"), Path("anexo.pdf")],
Path("saida/documento-completo.pdf"),
)
Para lotes, gere a lista com uma regra clara, como sorted(pasta.glob("*.pdf")), e registre os nomes processados. Não use entrada fornecida por terceiros diretamente como caminho de saída. Essas precauções combinam bem com um fluxo maior de automação de tarefas em Python.
Dividir páginas e girar conteúdo
Dividir significa criar um novo PdfWriter para cada página ou intervalo. Os índices em Python começam em zero, embora nomes destinados a pessoas normalmente comecem em um. A rotação aceita múltiplos de 90 graus.
from pathlib import Path
from pypdf import PdfReader, PdfWriter
def dividir_pdf(origem: Path, pasta: Path) -> list[Path]:
reader = PdfReader(origem)
pasta.mkdir(parents=True, exist_ok=True)
criados = []
for numero, pagina in enumerate(reader.pages, start=1):
writer = PdfWriter()
writer.add_page(pagina)
destino = pasta / f"pagina-{numero:03d}.pdf"
with destino.open("wb") as arquivo:
writer.write(arquivo)
criados.append(destino)
return criados
reader = PdfReader("entrada.pdf")
writer = PdfWriter()
for indice, pagina in enumerate(reader.pages):
if indice == 0:
pagina.rotate(90)
writer.add_page(pagina)
with open("rotacionado.pdf", "wb") as arquivo:
writer.write(arquivo)
Rotação altera a orientação da página no resultado, mas não corrige automaticamente recortes, caixas ou coordenadas de formulários. Sempre abra uma amostra e verifique páginas em retrato e paisagem.
Ler e atualizar metadados
Metadados podem conter título, autor e assunto, mas não são confiáveis como fonte de verdade: podem estar ausentes ou incorretos. Use-os para catalogação, nunca para autorizar acesso.
from pypdf import PdfReader, PdfWriter
reader = PdfReader("relatorio.pdf")
print(reader.metadata.title if reader.metadata else None)
writer = PdfWriter()
writer.append_pages_from_reader(reader)
writer.add_metadata({
"/Title": "Relatório mensal",
"/Author": "Equipe de Dados",
"/Subject": "Consolidação interna",
})
with open("relatorio-catalogado.pdf", "wb") as arquivo:
writer.write(arquivo)
Regravar um documento pode não preservar todos os recursos avançados. Assinaturas digitais, anexos, JavaScript, formulários e marcadores exigem testes específicos. Preserve o original e compare o resultado antes de substituir qualquer artefato.
Extração de texto e seus limites
extract_text() recupera caracteres que já existem na estrutura do PDF. Em digitalizações, a página é uma imagem e o retorno pode ser vazio; pypdf não executa OCR. Colunas, tabelas, ligaturas, ordem de leitura, fórmulas e posicionamento absoluto também podem produzir texto fora da ordem humana. A própria documentação detalha por que extrair texto de PDF é difícil.
from pypdf import PdfReader
reader = PdfReader("relatorio.pdf")
partes = []
for numero, pagina in enumerate(reader.pages, start=1):
texto = pagina.extract_text() or ""
if not texto.strip():
print(f"Página {numero}: sem camada de texto; talvez precise de OCR")
partes.append(texto)
conteudo = "\n\n".join(partes)
print(conteudo[:500])
Não trate o resultado como cópia perfeita. Faça validação por amostragem, mantenha número da página e confira campos essenciais. Se o objetivo for analisar dados estruturados, normalize depois da extração; o guia de expressões regulares em Python ajuda em padrões simples, sem transformar regex em um parser universal.
Criptografar e abrir um PDF protegido
A senha deve vir de variável de ambiente ou gerenciador de segredos, não do código. O exemplo cria uma cópia protegida e evita imprimir a credencial.
import os
from pypdf import PdfReader, PdfWriter
senha = os.environ.get("PDF_PASSWORD")
if not senha:
raise RuntimeError("Defina PDF_PASSWORD")
reader = PdfReader("privado.pdf")
writer = PdfWriter()
writer.append_pages_from_reader(reader)
writer.encrypt(senha, algorithm="AES-256")
with open("privado-protegido.pdf", "wb") as arquivo:
writer.write(arquivo)
protegido = PdfReader("privado-protegido.pdf")
if protegido.is_encrypted and protegido.decrypt(senha) == 0:
raise ValueError("Senha incorreta")
print(len(protegido.pages))
Alguns ambientes podem exigir dependência criptográfica adicional para determinados algoritmos. Consulte a versão instalada e a documentação, pois suporte e padrões aceitos mudam. Criptografia não impede que alguém autorizado redistribua uma cópia já aberta e não substitui controle de acesso.
Projeto prático: processador de relatórios
Monte uma pasta entrada, una os relatórios por nome, acrescente metadados, gire páginas conhecidas e grave primeiro em um arquivo temporário. Depois valide se o resultado abre e tem o total esperado de páginas; só então renomeie para o destino final. Registre data, entradas e resultado com o módulo apresentado no guia de logging em Python.
Separe funções de leitura, transformação e gravação. Assim, testes podem usar PDFs pequenos sem tocar documentos reais. Nunca execute o lote diretamente sobre a única cópia. Para arquivos enviados por usuários, imponha limites de tamanho e páginas, rejeite extensões inesperadas e use uma área isolada.
Preserve geometria, links e formulários de propósito
Uma página PDF possui diferentes caixas, entre elas media, crop, bleed, trim e art. A caixa de recorte delimita a área normalmente exibida, enquanto a caixa de mídia descreve a página física. Ao reunir documentos gerados por ferramentas diferentes, é possível obter páginas cortadas ou com tamanhos inconsistentes. Inspecione essas propriedades antes de normalizá-las, pois alterar dimensões sem transformar o conteúdo pode esconder ou deslocar informações. O guia oficial de recortes e transformações do pypdf detalha as operações disponíveis.
Anotações e formulários interativos exigem cuidado semelhante. Um link visível pode estar armazenado como anotação, e um campo de formulário pode depender de estruturas do documento além da página em que aparece. Copiar páginas isoladas nem sempre equivale a copiar o documento completo. Se links, marcadores, anexos ou campos AcroForm forem requisitos, crie um arquivo de teste com cada recurso e confira o resultado em mais de um leitor de PDF. Achatar um formulário ou preservar uma assinatura digital são requisitos separados, não consequências automáticas da gravação com PdfWriter.
Planeje testes com invariantes do documento
Um teste confiável não compara integralmente os bytes de dois PDFs. Datas em metadados, numeração de objetos, compactação e detalhes de serialização podem variar mesmo quando os documentos são visualmente equivalentes. Verifique invariantes relevantes: total de páginas, dimensões, rotação, metadados selecionados, estado de criptografia e presença de texto esperado em páginas conhecidas. Quando o layout importar, renderize uma amostra representativa para comparação visual.
Inclua arquivos de teste corrompidos, vazios, criptografados, grandes, em retrato e em paisagem. Cada teste deve usar um diretório temporário próprio, evitando colisões em execuções paralelas. Em lotes de produção, calcule antes o total esperado de saídas e faça a conciliação no fim. Assim, falhas parciais ficam visíveis e uma nova tentativa não duplica páginas silenciosamente.
Erros comuns e checklist
PdfReadError: arquivo corrompido, incompleto ou incompatível; registre o nome e preserve-o para inspeção.- Senha incorreta: verifique
is_encryptede o retorno dedecryptantes de acessar páginas. - Texto vazio: confirme se existe camada textual; caso contrário, use uma etapa de OCR adequada.
- Saída truncada: escreva com
wb, feche o arquivo e valide reabrindo-o. - Memória alta: limite tamanho e páginas; PDFs complexos podem consumir muito mais memória que o arquivo em disco.
Antes de colocar em produção, confirme: entradas ordenadas; original preservado; saída fora da pasta de entrada; senhas fora do repositório; limites definidos; exceções registradas sem dados sensíveis; páginas contadas; amostras conferidas; dependências fixadas e atualizadas. O guia de tratamento de erros ajuda a capturar falhas específicas sem esconder problemas de programação.
Conclusão
O pypdf resolve automações estruturais de PDF com uma API direta. Comece por cópias pequenas, valide cada transformação e trate extração de texto como uma aproximação dependente da origem. Com caminhos seguros, logs, limites e testes, o mesmo conjunto de operações pode sustentar um processador confiável sem prometer recursos que o formato ou a biblioteca não oferecem.