Bhinish Dhiman
Por que juntar um PDF criptografado pode deixar páginas em branco: nós medimos
Traduzido do original em inglês. Ler o original
Juntar um PDF protegido por senha com o pdf-lib original e a opção ignoreEncryption produziu um arquivo de quatro páginas em que as três páginas protegidas ficaram em branco e só 49 de 373 caracteres sobreviveram, porque o conteúdo das páginas foi copiado ainda criptografado. A mesma junção com o @cantoo/pdf-lib e a senha deu quatro páginas, 373 caracteres e nada em branco.
O sintoma é conhecido: você junta um extrato bancário a uma carta de apresentação, nenhum erro aparece, e as páginas do extrato saem brancas. Reproduzimos isso em código e contamos o que se perdeu.
O que a criptografia de PDF realmente criptografa
Criptografia de PDF aplica uma cifra (AES-256, segundo a ISO 32000-2) às strings e aos streams do arquivo, e só a eles. A estrutura de objetos fica às claras: um leitor consegue ver, sem senha, que há três páginas, qual é o tamanho delas e qual stream de conteúdo cada uma usa. O que ele não consegue fazer sem a senha é transformar esses streams de conteúdo de texto cifrado em instruções de desenho.
Essa divisão é todo o mecanismo da falha das páginas em branco. Uma biblioteca consegue percorrer a estrutura, copiar as páginas e nunca perceber que os bytes que copiou continuam criptografados.
A medição
Montamos um extrato de três páginas com 324 caracteres de texto conhecido e o criptografamos com AES-256 e uma senha de usuário, usando a mesma chamada encrypt() da nossa ferramenta Protect PDF. Depois o juntamos, após uma carta de apresentação de uma página e 49 caracteres, por dois caminhos: o pdf-lib 1.17.1 original (upstream) carregado com ignoreEncryption: true, e o @cantoo/pdf-lib 2.11.1 carregado com a senha, que é o que o src/lib/pdf/ops.ts faz. Cada resultado foi aberto no pdf.js 6.3.289, teve o texto extraído e todas as páginas renderizadas em meia escala e varridas em busca de qualquer pixel não branco. Executado no Node 24 em 1º de outubro de 2026.
| Caminho | Páginas na saída | Caracteres extraídos | Páginas em branco | Dicionário Encrypt na saída |
|---|---|---|---|---|
pdf-lib original, ignoreEncryption: true |
4 | 49 | 3 | Não |
| @cantoo/pdf-lib, com a senha | 4 | 373 | 0 | Não |
Os dois caminhos informam sucesso. Os dois produzem um arquivo que abre sem senha. Um deles jogou o extrato fora.
Por que as páginas ficam em branco em vez de embaralhadas
O pdf-lib original recusa um arquivo criptografado por padrão, com uma mensagem que aponta a saída de emergência: “Input document to PDFDocument.load is encrypted. You can use PDFDocument.load(..., { ignoreEncryption: true }).” (o documento de entrada está criptografado; você pode usar a opção ignoreEncryption). As ferramentas que querem parecer compatíveis com arquivos criptografados usam essa saída.
Com a opção ativada, a biblioteca analisa a estrutura que consegue ler e copia três objetos de página, cada um apontando para um stream de conteúdo com texto cifrado em AES. O novo documento não tem dicionário de criptografia, então o leitor trata esses streams como dados comuns comprimidos com Flate, descomprime, obtém ruído e para de desenhar. O pdf.js registra Trying to parse invalid object e Invalid object ref: 14 0 R e segue em frente. Os 49 caracteres que sobreviveram são os da carta de apresentação, que nunca foi criptografada.
A variante no próprio arquivo é pior
Também carregamos o arquivo criptografado com ignoreEncryption, alteramos um campo de metadados e o salvamos sem copiar nada. Esse resultado nem abria: o pdf.js informou Incorrect Password (senha incorreta) quando recebeu a senha correta. O salvamento tinha reescrito a estrutura do arquivo em volta de um texto cifrado cujas chaves já não batiam, o que gera um documento que ninguém consegue recuperar, e não um documento com uma página em branco.
O fork faz a descriptografia
O @cantoo/pdf-lib é um fork do pdf-lib que adiciona uma opção password ao load() e um método encrypt(). Com a senha, ele descriptografa cada stream durante a análise, então o copyPages move texto às claras, e o resultado da junção guarda os 373 caracteres com todas as páginas renderizando. Sem a senha, ele lança um erro exatamente como o original: não tenta adivinhar, e não existe caminho que remova uma senha que você não conhece.
O teste para fazer em qualquer ferramenta de juntar PDF
Junte um arquivo protegido e extraia o texto depois, com a nossa ferramenta PDF to text ou qualquer outra. Se os caracteres do documento protegido não estiverem lá, a ferramenta copiou texto cifrado. Uma página em branco não é uma falha de renderização que outro visualizador vá resolver; o conteúdo não sobreviveu à junção.