PDF gerado pelo meu sistema não abre: o que verificar
Seu sistema gera um PDF que não abre? Confira os primeiros bytes do arquivo, os erros de base64 e de texto no código e o cabeçalho certo para salvar o download.
Quando um PDF gerado pelo seu sistema não abre, quase sempre os bytes mudaram no caminho entre o servidor e o disco: foram lidos como texto, decodificados duas vezes ou o download parou no meio. Um PDF válido começa com %PDF- logo no primeiro byte. Confira isso antes de mexer no gerador, porque o teste leva um minuto e separa problemas diferentes.
Primeiro teste: os primeiros bytes do arquivo
Abra o Terminal na pasta do arquivo e rode head -c 8 relatorio.pdf. Um arquivo íntegro mostra %PDF-1. seguido de um número de versão. No fim, o PDF termina com %%EOF, normalmente seguido de uma quebra de linha. Se o começo e o fim estiverem certos e o arquivo ainda não abre, o problema está na estrutura interna, e não no download.
| O que aparece no começo do arquivo | O que provavelmente aconteceu | Onde corrigir |
|---|---|---|
| %PDF-1.7, mas o leitor reclama | Índice interno com defeito | Reparo ou outro leitor |
| Texto só de letras e números, começando por JVBERi0 | Base64 gravado sem decodificar | Código que grava o arquivo |
| <html ou <!DOCTYPE | Página de erro ou de login salva como .pdf | A requisição e a autenticação |
| Mensagem de erro em JSON | O endpoint falhou e o cliente gravou a resposta | O status HTTP da resposta |
| Três bytes EF BB BF antes do %PDF | Marca de ordem de bytes UTF-8 colada no início | Função que escreve o arquivo |
| Começa certo, mas bem menor que o esperado | Download interrompido | Repetir a geração e o download |
Base64 e texto: os dois erros mais comuns
Sistemas que devolvem o PDF dentro de uma resposta em JSON costumam mandá-lo em base64, um texto de letras, números, + e /. O arquivo só volta a ser PDF depois de decodificado para bytes. Como o cabeçalho %PDF- em base64 começa sempre com JVBERi0, esse é o sinal mais rápido: se o texto recebido começa assim e o arquivo salvo não abre, o erro está no passo de decodificação.
O segundo erro é tratar bytes como texto. Em Python, com a biblioteca requests, response.text decodifica a resposta como caracteres e troca qualquer byte acima de 127; response.content entrega os bytes crus. Em JavaScript, res.text() tem o mesmo efeito. Em Node, escrever uma string com fs.writeFile sem indicar codificação binária corrompe o arquivo da mesma forma: crie um Buffer com Buffer.from(base64, "base64") e grave esse Buffer.
Decodificar duas vezes também quebra o arquivo, e acontece mais do que parece: a biblioteca HTTP já decodifica uma camada e o código decodifica de novo. Conte quantas vezes o conteúdo passa por uma função de decodificação antes de gravar.
Cabeçalho Content-Type e download
Um servidor que responde Content-Type: text/html para um PDF faz o navegador salvar uma página no lugar do arquivo, ou mudar a extensão sem avisar. O valor correto para PDF é application/pdf (o tipo MIME do PDF e o download que abre como texto). Esse cabeçalho não estraga os bytes, mas esconde o problema: o arquivo salvo parece PDF e não abre. Para conferir, abra a aba Rede (Network) do DevTools no Chrome ou no Edge, repita o download e veja o cabeçalho da resposta.
Se o tamanho do arquivo em KB é muito menor do que o gerador deveria produzir, a conexão caiu ou o servidor fechou antes do fim. Gerar e baixar de novo resolve. Se o problema se repete só em redes lentas, compare o tamanho recebido com o campo de tamanho da resposta, quando o servidor envia esse campo.
Quando o arquivo está certo e só o leitor reclama
Se o arquivo começa com %PDF- e termina com %%EOF, ele está no lugar. Abra em outro leitor antes de culpar o gerador (se só aparecem páginas pretas, o caso é o de PDF que abre com tela preta). Leitores diferentes reagem de forma diferente a um índice interno com defeito: um abre, outro pede reparo. Nesse caso, a ferramenta Reparar PDF reconstrói o índice e copia as páginas como estão; se a estrutura estiver danificada demais, ela redesenha as páginas legíveis num PDF novo.
Como corrigir na origem
- Grave o conteúdo como bytes. Em Python, use o modo
wbou response.content; em Node, um Buffer; no Android, o mesmo cuidado vale ao gravar o PDF pelo MediaStore, como em gerar PDF no Android com Kotlin. - Decodifique o base64 uma vez, no ponto em que a resposta chega, e não em duas funções diferentes.
- Responda com
Content-Type: application/pdfe, se o arquivo for para download,Content-Disposition: attachmentcom o nome terminando em .pdf, o que no Laravel odownloaddo DomPDF já faz, como mostra gerar PDF no Laravel com DomPDF. - Teste o arquivo que está no disco, com
head -c 8, e não o que a tela mostrou. - Gere um PDF mínimo, de uma página com um texto, e confira se ele abre. Se o mínimo abre e o seu não, o defeito está no conteúdo gerado, não no caminho de entrega.
O que o reparo não resolve
Reparar recupera a estrutura de um arquivo que existiu por inteiro, não o conteúdo que nunca chegou. Se o arquivo começa com <html, a resposta de erro foi salva com outro nome e não há PDF para recuperar. Nesse caso, o AjustaPDF devolve a mensagem dizendo que não há conteúdo legível, e a tentativa não conta na cota diária. A correção está na requisição, não no arquivo.
O mesmo vale para um PDF que pede senha. Senha não é defeito: o arquivo está íntegro e criptografado, e quem resolve é o desbloqueio, e não o reparo. Se o seu sistema gera PDFs com senha de abertura, o leitor precisa dessa senha para abrir, e isso não indica que o arquivo saiu quebrado.
Quando vale enviar o arquivo para o reparo
O reparo é útil para o arquivo que já existe e está quebrado, enquanto você corrige o gerador ou quando não controla o sistema que o produziu. Ele não substitui a correção de origem, e um gerador que devolve arquivo quebrado vai continuar devolvendo. Use a ferramenta para salvar o arquivo de hoje e o código para salvar o de amanhã. Se o problema não for esse, o diagnóstico de PDF corrompido que não abre cobre as outras causas. E se o PDF abre bem e só o sistema que o recebe recusa o conteúdo, veja PDF com conteúdo não reconhecido em sistema.
Perguntas frequentes
Meu sistema gera PDF em base64 e o arquivo baixado não abre, o que checar?
Confira se o texto recebido começa com JVBERi0, que é como o base64 de “%PDF-” aparece. Se começa, o sistema devolveu o PDF codificado e o seu código precisa decodificar antes de gravar o arquivo. Se não começa, o problema está antes: a resposta não é o PDF.
Como saber se o arquivo baixado é um PDF de verdade e não uma página de erro salva com .pdf?
Um PDF íntegro começa com %PDF-1 seguido de um número de versão, como 1.4 ou 1.7. Se os primeiros bytes forem <html ou <!DOCTYPE, o servidor mandou uma página de erro ou de login. No Terminal, o comando head -c 8 arquivo.pdf mostra esses bytes sem abrir nenhum programa.
O PDF gerado começa com %PDF e mesmo assim o leitor diz que está corrompido: tem conserto?
Quase sempre sim. Um arquivo com cabeçalho certo costuma ter o índice interno gravado com posições erradas, comum em geradores de PDF feitos à mão. O reparo reconstrói esse índice e copia as páginas; se não bastar, redesenha o que for legível. Se o arquivo estiver cortado antes da primeira página, ou se o que chegou nem é PDF, não há o que recuperar.
Por que o PDF que minha API devolve aparece com caracteres estranhos quando eu salvo?
Porque os bytes do PDF foram tratados como texto em UTF-8, e qualquer byte acima de 127 foi trocado. Em Python com requests, use response.content em vez de response.text. Em JavaScript, grave o resultado de res.arrayBuffer() como Buffer, e não como string.