15 pontos por GN⁺ 2025-08-04 | 5 comentários | Compartilhar no WhatsApp
  • O parsing de PDF deveria funcionar com base em uma ordem e estrutura bem definidas, mas os arquivos reais frequentemente não seguem essa especificação
  • Vários erros e inconsistências ocorrem na busca de ponteiros de cross-reference (xref) e offsets
  • Na prática, muitos problemas surgem por causa de dados desnecessários antes do cabeçalho do PDF ou por posições incorretas de ponteiros e offsets
  • Também há muitos casos em que a própria tabela xref do PDF é ambígua ou mal formatada
  • Por isso, os principais visualizadores implementam lógica adicional para suportar até arquivos PDF fora do padrão

Abordagem ideal para parsing de PDF

  • Em teoria, o parsing de PDF segue etapas fixas
    • Encontrar o comentário de cabeçalho de versão no início do arquivo
    • Encontrar o ponteiro de cross-reference (xref)
    • Coletar todos os offsets dos objetos
    • Encontrar o dicionário trailer para acessar a estrutura completa do catálogo

Introdução aos objetos PDF

  • Objetos PDF são unidades que encapsulam e armazenam vários elementos do PDF, como números, strings e dicionários
  • Cada objeto existe entre os marcadores obj e endobj
  • Os objetos se conectam entre si por meio de referências indiretas (indirect reference, por exemplo: 16 0 R)
  • A forma de dividir os objetos no arquivo é livre, mas alguns tipos de objeto obrigatoriamente precisam ser referências indiretas

Encontrando offsets de cross-reference

  • Estruturalmente, o PDF tem uma tabela de cross-reference (xref), que funciona como um índice das posições dos objetos
  • No fim do arquivo, a expressão startxref indica uma posição específica em bytes como ponteiro
  • Esse ponteiro indica a posição da xref, mas há diferenças entre a especificação e os arquivos reais. Por exemplo, o marcador %EOF deveria estar na última linha, mas em PDFs reais ele pode estar em qualquer lugar dentro dos últimos 1.024 bytes
  • Nos arquivos reais, aparecem várias variações, como erros de formato no ponteiro (startref etc.) e ausência de quebra de linha

Encontrando offsets de objetos

  • A tabela xref segue a ordem xref, número inicial do objeto e quantidade de objetos, e o offset/número de geração/estado de cada objeto (n ou f) é registrado em uma linha
  • Pode haver várias tabelas xref, ou elas podem estar ligadas entre si por meio da entrada /Prev

Localizando o dicionário trailer

  • Acima do marcador startxref, existe o dicionário trailer, que contém os metadados essenciais para encontrar o objeto raiz
  • A partir do objeto raiz, é possível iniciar a interpretação de toda a estrutura

Ambiente real: problemas inesperados

  • Há muitos arquivos que não seguem a especificação do PDF, então parsers comuns têm dificuldade para processá-los

  • Casos em que a busca pelo ponteiro de cross-reference costuma falhar

    • O ponteiro não está no fim do arquivo nem nos últimos 1.024 bytes
    • Erros de digitação (startref etc.)
    • Formatos excepcionais
  • Em uma análise de 3.977 amostras reais de PDF, cerca de 0,5% tinham erro na declaração da xref

O conteúdo do PDF começa em um offset diferente de zero

  • Se houver dados inúteis (junk) antes do cabeçalho, todos os offsets em bytes são deslocados, e a posição de startxref fica incorreta
  • É preciso recalcular os offsets com base na posição do cabeçalho e verificar as duas posições
  • Isso representa cerca de 50% de todos os erros

O ponteiro xref aponta para o meio da tabela xref

  • O offset especificado pode acabar levando para o meio do conteúdo da tabela xref
  • Isso foi encontrado em cerca de 5 dos 3.977 exemplos

O ponteiro está perto da xref

  • Muitas vezes, o ponteiro não está exato, mas só deslocado pela diferença de espaços em branco ou quebras de linha logo antes ou depois da xref

O ponteiro está certo, mas os offsets da xref estão errados

  • Os próprios offsets registrados na tabela xref podem estar errados
  • Alguns objetos podem estar corretos, enquanto outros têm erro de offset

O primeiro ponteiro está normal, mas o offset anterior (/Prev) está estranho

  • Há muitos casos em que, ao modificar um PDF, o ponteiro /Prev recebe um valor incorreto (por exemplo, 0)

O formato da tabela xref é anormal

  • Aparecem casos variados, como xref colado com números sem quebra de linha, mais entradas do que objetos declarados, ou dados lixo no meio da tabela
  • Muitos desses casos foram relatados como issues em projetos como o PdfPig

Conclusão

  • Segundo a especificação, o parsing de PDF deveria seguir uma sequência padronizada, mas muitos arquivos reais não são assim, o que causa diversos problemas de parsing
  • Visualizadores de PDF usados na prática já incluem por padrão recursos para ampliar o suporte a PDFs fora da especificação
  • Este resumo tratou apenas de uma pequena parte do parsing coberta pela especificação do PDF (22 páginas de um total de 1300)

5 comentários

 
mhj5730 2025-08-06

Este resumo trata apenas da análise de uma pequena parte da especificação do PDF (22 páginas de um total de 1300) <-... 1300 páginas é algo realmente assustador...

 
kaydash 2025-08-05

Uau..

 
spp00 2025-08-05

Para ser bem sincero, PDF é um formato amigável para leitura humana, feito para preservar ao máximo a formatação criada por pessoas, e péssimo para lidar com máquinas.

 
reagea0 2025-08-05

Concordo. Na verdade, nem sei se é bom de ler... é pesado demais e inconveniente.

 
GN⁺ 2025-08-04
Comentários do Hacker News
  • A resposta é clara

    1. O PDF suporta anexar metadados em qualquer formato que você quiser
    2. Todo software que gera PDF deveria anexar as mesmas informações de um jeito fácil para máquinas lerem
    3. Aí quem quiser fazer parsing de PDF só precisaria olhar para os metadados
      Na prática, meu nome é Geoff, e metade dos parsers de currículo reconhece meu nome separadamente como "Geo" e "ff"
      Isso acontece por causa da forma como o texto entra no PDF, e é um problema que continua ocorrendo em vários aplicativos de origem
    • Fazer parsing de PDF e fazer parsing do conteúdo de um PDF são coisas completamente diferentes
      Fazer parsing do arquivo PDF já é uma dor de cabeça, mas como o próprio PDF é baseado em "desenhar alguma coisa em uma posição especificada", e não em texto bem definido dentro de caixas delimitadoras, para extrair palavras você acaba tendo que adivinhar quais caracteres pertencem juntos
      Se quiser ajudar parsers de currículo, talvez valha a pena olhar para a accessibility tree
      Nem todo renderizador de PDF exporta PDF acessível, mas um PDF acessível pode ao menos ajudar a ler corretamente coisas como nomes
      O problema com "ff" provavelmente é um caso em que o analisador de currículos não consegue lidar com caracteres não ASCII, como a ligadura ff
      Dá para configurar o renderizador de PDF para não gerar ligaduras, mas isso pode deixar o texto feio

    • Parece que estão depositando expectativa demais na palavra "deveria"
      Quando o uso de PDF é, na prática, bem hostil, as pessoas não parecem pensar tanto assim
      O próprio motivo de enviar um currículo em PDF é impedir que intermediários mexam nele, e também existe "edição" por vários motivos, como ocultar algo desenhando uma caixa em cima da imagem, ou gerar uma tabela em PDF em vez de CSV para dificultar a análise

    • Na verdade esse método funciona bem em alguns casos, e alguns apps usam essa abordagem
      Só que continua existindo o problema de as duas representações (corpo/metadados) não necessariamente coincidirem

    • Fica a dúvida sobre como lidar com digitalizações de manuscritos ou outros documentos escaneados se o scanner e um computador doméstico comum não tiverem suporte perfeito a OCR

    • Provavelmente o problema é que ff está sendo renderizado como uma ligadura

  • Sou fundador da Tensorlake
    Criamos uma API de parsing de documentos para desenvolvedores
    Esse é o motivo de abordagens de Computer Vision funcionarem tão bem em parsing de PDF no mundo real
    Depender só dos metadados do arquivo não escala para a variedade de PDFs de origem
    Então a abordagem é converter o PDF em imagem, aplicar primeiro um modelo de reconhecimento de layout, depois rodar modelos especializados para reconhecimento de texto e tabelas, entre outros, e então juntar as partes, obtendo resultados utilizáveis até em áreas onde a precisão é essencial

    • À primeira vista esse jeito parece ridículo, mas na verdade parece ser a solução mais realista
      PDF é, em essência, um formato criado para expressar um layout legível por humanos, não algo projetado para ser lido por computadores; o foco é exibição bonita
      Por isso, imitar a forma como humanos leem parece uma abordagem lógica
      Ainda assim, é uma pena que em mais de 30 anos o PDF não tenha conseguido adicionar legibilidade para máquinas
      Fico me perguntando que incentivo faltou para tornar isso possível
      Se alguém tiver alguma percepção sobre isso, eu gostaria de ouvir

    • Tem algo meio engraçado nisso
      Tirar sarro de quem imprime um PDF, escaneia e manda por e-mail parece natural, mas em parsing de PDF estamos basicamente fazendo a mesma coisa
      É frustrante que essa abordagem seja necessária
      Ninguém faz parsing de HTML desse jeito

    • Sou cofundador da Nutrient.io e trabalho com PDF há mais de 10 anos
      Assim como navegadores web, visualizadores de PDF precisam aceitar uma variedade enorme de PDFs
      Como o PDF é muito antigo, quem gera os arquivos muitas vezes faz alterações arbitrárias desde que fique bom no visualizador que usa
      Por isso nossa empresa criou um SDK de processamento de documentos com IA (REST API, você envia um PDF e recebe dados estruturados em JSON)
      Além dos métodos visuais, nossa experiência com pré-processamento e pós-processamento estrutural permite entregar resultados melhores tanto em desempenho quanto em custo em comparação com uma abordagem puramente baseada em visão
      Se você não quiser se preocupar em lidar diretamente com PDFs e preferir focar no seu trabalho principal, isso pode ajudar
      https://www.nutrient.io/sdk/ai-document-processing

    • Já que há alguém aqui especialista na estrutura interna de PDFs, tenho uma pergunta
      Gostaria de entender por que o mupdf-gl é muito mais rápido que todos os outros programas (no Linux desktop padrão)
      A velocidade de busca em PDFs grandes é visivelmente superior, e sempre me perguntei por que outros visualizadores não conseguem ser tão rápidos
      Se alguém tiver alguma percepção a respeito, gostaria de ouvir

    • No fim das contas, vocês terceirizaram o trabalho de parsing para o software usado para renderizar o PDF como imagem

  • Há muito tempo acho que precisamos sair da comunicação documental centrada em layout
    Ou seja, o próprio layout profissionalmente diagramado me parece, na verdade, algo mais próximo de um costume antigo, com pouca relação com a compreensão real do conteúdo
    Por exemplo, documentos enviados a órgãos reguladores costumam ser enormes, e seguir as regras de layout faz você passar muito tempo trabalhando no Microsoft Word
    Para garantir esse layout, enviam tudo em DOCX ou PDF, mas esses formatos são muito inadequados para programas extraírem ou processarem o conteúdo automaticamente
    Um LLM até consegue ler esses arquivos, mas o custo computacional é muito maior do que em arquivos simples e amigáveis para máquinas, como texto, markdown, XML ou JSON

    Como alternativa, penso na possibilidade de padronizar formatos simples realmente "machine-first" e "content-first", baseados em JSON, XML ou HTML
    Eles teriam apenas estrutura mínima e informações de incorporação de imagens, e quando humanos precisassem ler, um aplicativo visualizador reorganizaria tudo de forma agradável
    O processamento por máquinas seria muito mais fácil
    Mesmo já existindo formatos parecidos, como HTML/navegadores e EPUB, acho que chegou a hora de substituir o jeito antigo
    Espero que a revolução dos LLMs nos empurre nessa direção, e que no futuro o parsing caro de PDF fique apenas como parte de pipelines legados

    • Concordo com o problema do PDF, mas será que DOCX é realmente tão ruim assim?
      Nunca cheguei a criar um parser de DOCX, mas como DOCX é baseado em XML e nem tudo vira coordenadas absolutas a menos que você especifique explicitamente o layout, se JPEG vale 0, PDF vale 15 e markdown vale 100, minha impressão é que DOCX deve estar mais ou menos em 80 em facilidade
  • Achei um ótimo resumo, e houve um ponto adicional que me chamou atenção
    Cadeia de incremental save: o primeiro offset de startxref até pode estar certo, mas quando o Acrobat vai anexando repetidamente links /Prev a cada nova modificação, muitas vezes eles apontam alguns bytes antes do próximo xref
    A maioria dos visualizadores (PDF.js, MuPDF, até Adobe Reader) simplesmente sai procurando por tokens obj no arquivo inteiro e reconstrói uma nova tabela, enquanto parsers mais fiéis à especificação explodem
    Se você quiser lidar no mundo real com documentos que foram modificados repetidamente por vários aplicativos, esse caminho de recuperação é indispensável

    • É uma observação correta, esse era um caso de falha que eu via com frequência no conjunto de amostras
      Muitas vezes a referência anterior, ou algum item da cadeia, aponta para um offset fora do arquivo, offset 0 ou outro valor inválido
      O que me levou a escrever esse texto foi uma reformulação da lógica inicial de parsing no meu projeto PdfPig
      No começo eu tinha portado o código do Java PDFBox, mas queria deixá-lo mais rápido e simples
      A nova lógica escaneia o arquivo inteiro se perder qualquer xref table/stream, e no caminho de recuperação confia apenas naquele offset
      Mas ela certamente ficou mais lenta do que antes, e estou em dúvida se a mudança realmente vale a pena
      Estou explorando vários casos esquisitos com um conjunto de testes de 10 mil arquivos
      https://github.com/UglyToad/PdfPig/pull/1102
  • Parece que seria fácil se houvesse suposições que funcionassem e um parser adequado de objetos PDF, mas acho que a realidade está longe disso
    Essa situação é um inferno de PDF
    PDF não é uma especificação, é um acordo social, um nível de "vibe"
    Quanto mais você se debate, mais afunda, e agora parece que todos nós vivemos num atoleiro afastado da visão de Deus
    Essa fala me fez rir

    • Piada dizendo que esse texto parece ter sido escrito por James Mickens
  • Sobre a pergunta "Você quer fazer parsing de PDF?", posso afirmar categoricamente que não
    O motivo está muito bem explicado no texto original

    • Eu queria que meu banco fornecesse esses materiais em um formato mais fácil de ler, mas até lá não tem jeito

    • Já cometi esse erro antes e não pretendo repetir

  • Como alguém que já escreveu um parser de PDF, acho PDF um formato realmente estranho
    Acho que esse lado esquisito vem da origem do formato como uma mistura de binário e texto
    Também suponho que o problema dos offsets de xref levemente imprecisos venha de bugs no tratamento da conversão de quebras de linha LF/CR
    Uma coisa que o texto não mencionou é que PDFs mais novos (v1.5+) muitas vezes vêm com "xref stream" em vez de uma xref table em texto simples
    No v1.6 ou superior, os próprios objetos também podem ficar dentro de object streams

    • Também me surpreendeu que ele não tenha ido além de xref tables simples para falar de streams e compressão
      Parece que está tudo bem até você descobrir que o objeto que quer está dentro de um stream, que esse stream usa uma variante da compressão PNG, ou que o offset está dentro de um xref stream comprimido com flate
      Além disso, há várias versões do documento misturadas, então também é complicado determinar onde começa e termina a versão mais recente
      É fácil conseguir a documentação do PDF 1.7, mas até apenas 2 anos atrás a especificação do PDF 2.0 ainda estava atrás de paywall
  • PDF é um formato que não foi pensado para streaming
    Por causa do trailer dictionary no final, fica difícil fazer parsing antes de carregar o arquivo inteiro
    Ainda assim, também existem PDFs "streamable", então se as informações necessárias estiverem no começo, dá para renderizar a primeira página imediatamente, mesmo que o resto não
    Faz algum tempo que estou mais distante da área de PDF, então vale considerar isso

    • Mesmo com um rodapé no fim, se o site suportar Range Request e usar corretamente o cabeçalho Content-Length, PDF pode ser transmitido em streaming
      Um leitor com streaming pode fazer uma requisição HEAD, pedir as últimas centenas de bytes do arquivo para obter os ponteiros e a tabela, e depois continuar baixando o resto
      Isso não serve para PDFs gerados em tempo real, mas para um servidor web razoavelmente antigo, 1 ou 2 RTTs extras já bastam
      Infelizmente é raro ver casos em que alguém se preocupa com parsers baseados em Range por arquivo, mas tecnicamente eu não diria que é impossível

    • Isso mesmo, existe um formato chamado Linearized PDF, projetado para mostrar rapidamente a primeira página sem precisar baixar o arquivo inteiro
      No resumo, eu tinha deixado isso de fora porque exigiria muita explicação adicional

  • Um dos primeiros projetos que tentei quando estava aprendendo Python foi um parser de PDF
    Eu queria automatizar a extração de mapas para uma campanha de DnD, mas fracassei no fim das contas haha

  • Já escrevi um leitor de TIFF
    TIFF também tem fama de ser fácil de escrever e difícil de ler
    PDF me parece entrar exatamente na mesma categoria