A anatomia de um arquivo PDF
Visão geral
Seção intitulada “Visão geral”Abra qualquer PDF em um editor de texto simples e a primeira coisa que você vê é
reconfortante: um cabeçalho %PDF-1.x ou %PDF-2.0. A última coisa que você vê
é %%EOF. Tudo entre essas duas linhas é uma pequena e arrumada máquina para
encontrar objetos por número. Esta página é uma dissecação. Abrimos o arquivo,
nomeamos cada órgão e mostramos como eles se conectam.
É a companheira estrutural de dois vizinhos. O que um PDF realmente é trata o arquivo como um grafo de objetos; atualizações incrementais cobrem como ele cresce ao longo do tempo. Esta página fica perto dos bytes — as regiões físicas que um parser percorre, na ordem em que elas se assentam no disco.
Por que isso importa
Seção intitulada “Por que isso importa”Você quase nunca precisa disto para usar um PDF. Você precisa no dia em que um deles dá errado. Um arquivo abre em um visualizador e não em outro; um validador reporta uma “tabela de referências cruzadas danificada”; um documento assinado de repente falha ao verificar. Nenhum desses casos é mistério quando você consegue ler a anatomia. Eles são um número que já não corresponde a uma posição, uma região no lugar errado, ou uma cauda que aponta para o nada.
Conhecer o layout transforma “o PDF está corrompido” em um diagnóstico sobre o qual você pode agir. É a diferença entre dar de ombros diante de uma caixa-preta e apontar para o byte exato que mente.
A versão resumida
Seção intitulada “A versão resumida”Um PDF em conformidade tem quatro partes físicas, nesta ordem de arquivo (Spec: ISO 32000-2, §7.5.1ISO 32000-2 §7.5.1):
- Um cabeçalho — uma linha,
%PDF-2.0, nomeando a versão. - Um corpo — o grosso do arquivo: uma sequência de objetos indiretos numerados.
- Uma seção de referências cruzadas — um índice do número de objeto até o offset de byte onde esse objeto vive. PDFs clássicos usam uma tabela de texto; o PDF 2.0 usa um xref stream comprimido.
- Um trailer — um pequeno dicionário nomeando o ponto de entrada, seguido de
startxref, um offset e%%EOF.
A reviravolta: um leitor não começa pelo topo. Ele começa pelo fim, lê
startxref para achar o índice, e usa esse índice para alcançar qualquer objeto
diretamente. O arquivo é escrito de frente para trás, mas lido de trás para a
frente.
Como o NextPDF aborda isso
Seção intitulada “Como o NextPDF aborda isso”Vamos percorrer as quatro regiões em ordem, com os bytes à nossa frente.
O cabeçalho é uma linha. O NextPDF escreve %PDF-2.0 e, por convenção, uma
segunda linha de comentário com bytes de bit alto, para que ferramentas de
transferência ingênuas tratem o arquivo como binário, não como texto. Essa
segunda linha é o motivo de um PDF aberto como texto simples mostrar um pequeno
embaralhado logo depois da versão.
O corpo é onde o documento vive. Cada objeto indireto é um número, uma
geração, a palavra-chave obj, um valor, e endobj
(Spec: ISO 32000-2, §7.3.10ISO 32000-2 §7.3.10). O valor é uma de poucas
formas — mas duas carregam quase todo o peso:
- Um dicionário,
<< /Key value … >>, é um mapa de nomes para valores. A árvore de páginas, o catálogo, os descritores de fonte: todos dicionários. - Um stream é um dicionário seguido de
stream, um bloco de bytes arbitrários, eendstream. O conteúdo da página, fontes embutidas e imagens são streams, quase sempre comprimidos. (Seus filtros são uma história à parte, contada em streams e filtros.)
Os objetos apontam uns para os outros por referência indireta — 2 0 R
significa “objeto 2, geração 0”. É essa a fiação que transforma uma lista plana de
objetos em um grafo.
A seção de referências cruzadas é a parte que a maioria das pessoas nunca
imagina corretamente. Na forma clássica ela é texto simples: a palavra-chave
xref, depois subseções de linhas de largura fixa de 20 bytes
(Spec: ISO 32000-2, §7.5.4ISO 32000-2 §7.5.4). Cada linha é um offset de byte
de dez dígitos, uma geração de cinco dígitos, e um único flag — n para em uso,
f para livre — preenchida até exatamente vinte bytes, de modo que um leitor pode
saltar para qualquer entrada apenas por aritmética. O PDF 2.0 substitui isso por
um xref stream: o mesmo índice, mas binário e comprimido dentro de um objeto
stream marcado como /Type /XRef (Spec: ISO 32000-2, §7.5.8ISO 32000-2 §7.5.8).
Menor, e capaz de descrever objetos empacotados dentro de object streams.
O trailer é o sumário do arquivo (Spec: ISO 32000-2, §7.5.5ISO 32000-2 §7.5.5).
Ele nomeia /Root — o catálogo do documento, o único objeto do qual tudo o mais
pende — e /Size, a contagem de objetos. Depois vem o aperto de mão que torna
possível a leitura de trás para a frente: startxref, um offset de byte em sua
própria linha, e %%EOF. Um leitor salta para o fim, lê esse offset, pula direto
para a seção de referências cruzadas, e parte dali.
- CabeçalhoUma linha, %PDF-2.0, nomeando a versão. Um comentário marcador de binário geralmente vem em seguida.
- CorpoObjetos indiretos numerados — dicionários e streams — referenciados por N G R.
- Seção de referências cruzadasUma tabela de texto de entradas de 20 bytes, ou um stream comprimido /Type /XRef no PDF 2.0.
- TrailerNomeia /Root e /Size, depois startxref + offset + %%EOF.
- Ordem de leituraUm leitor começa em %%EOF, segue startxref até o índice, e então alcança cada objeto diretamente.
Há uma quinta região que um arquivo de vida longa cria: uma atualização
incremental. Uma mudança não é escrita no lugar. Os objetos alterados, uma nova
seção de referências cruzadas e um novo trailer são anexados depois do primeiro
%%EOF, e esse novo trailer carrega /Prev — o offset da seção de referências
cruzadas anterior (Spec: ISO 32000-2, §7.5.6ISO 32000-2 §7.5.6). As seções
formam uma cadeia para trás; para qualquer número de objeto, a entrada mais nova
vence. Como os bytes originais nunca se movem, a verificação criptográfica sobre a
faixa de bytes que uma assinatura de fato cobre continua válida após uma
atualização. Uma atualização incremental posterior ainda pode alterar o que um
validador reporta sobre o documento como um todo — se as mudanças
pós-assinatura são permitidas, e o que se entende que a assinatura certifica —
mas ela não pode alterar os bytes assinados em si. Essa propriedade é o assunto
inteiro de atualizações incrementais.
Exemplo prático
Seção intitulada “Exemplo prático”Aqui está a anatomia inteira em um arquivo mínimo. Os números sob xref são
offsets de byte, e eles têm de ser exatos — aponte um caractere além de onde um
objeto começa e um leitor estrito desiste.
%PDF-2.01 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>endobjxref0 40000000000 65535 f0000000009 00000 n0000000058 00000 n0000000115 00000 ntrailer<< /Size 4 /Root 1 0 R >>startxref186%%EOFLeia-o do jeito que um parser faz. Última linha: %%EOF. Acima dela:
startxref 186, então salte para o byte 186, onde xref começa. A tabela diz que
o objeto 1 vive no byte 9. O /Root 1 0 R do trailer aponta para lá — o catálogo
— e a partir do catálogo você percorre /Pages até a árvore de páginas e encontra
a única página. O objeto 0 é sempre a cabeça da lista de livres com geração
65535, um fóssil do primeiro design do formato que todo leitor ainda espera ver.
Equívoco comum
Seção intitulada “Equívoco comum”A armadilha é ler um PDF como uma história — de cima para baixo, em ordem. Ele não
é uma história; é um índice com um ponteiro para trás. Os números de objeto não
precisam ser sequenciais no arquivo, os objetos podem aparecer em qualquer ordem
física, e um leitor nunca confia na posição deles. O único mapa autoritativo é a
seção de referências cruzadas, e a única forma de encontrar esse mapa é o offset
startxref no fim de tudo.
A consequência surpreende as pessoas. Um PDF com um corpo impecável e um dígito
errado em startxref é ilegível — o leitor não consegue achar o índice. Um PDF
com seus objetos em ordem embaralhada, mas uma seção de referências cruzadas
correta, está perfeitamente bem. A posição física não carrega significado algum. A
posição registrada carrega todo ele.
Limites e fronteiras
Seção intitulada “Limites e fronteiras”Esta página descreve a estrutura física, não o conteúdo da página. Como as marcas
chegam a uma página — operadores de stream de conteúdo, exibição de texto, estado
gráfico — é um assunto à parte. Ela também descreve um arquivo bem-formado. PDFs
do mundo real frequentemente estão um pouco quebrados e sobrevivem apenas porque
visualizadores tolerantes reconstroem a tabela de referências cruzadas varrendo
por palavras-chave obj. Esse resgate é um comportamento do visualizador, não
algo que o formato garante.
| Edition | Availability |
|---|---|
| Core | O NextPDF é um escritor. Ele registra cada offset a partir do buffer de saída no momento em que cada objeto é emitido, de modo que os arquivos que produz têm uma seção de referências cruzadas que corresponde ao corpo por construção. |
| Pro | Fazer parsing, reconstruir ou reparar uma tabela de referências cruzadas danificada em um arquivo que o NextPDF não escreveu está fora do escopo em todas as edições. |
| Enterprise | Para inspeção da estrutura de um arquivo existente, use um parser ou validador dedicado; o NextPDF garante correção para o que escreve, não para o que lê. |
Mini-FAQ
Seção intitulada “Mini-FAQ”Por que um PDF tem uma linha marcadora de binário depois do cabeçalho? Algumas ferramentas de transferência mais antigas estragavam um arquivo que julgavam ser texto simples. O comentário de bit alto faz o arquivo parecer inequivocamente binário, de modo que ele sobrevive à viagem sem alterações.
O xref stream é só uma tabela menor? Quase, com um poder extra. Além de ser comprimido, um xref stream pode descrever objetos armazenados dentro de object streams — entradas que a clássica tabela de texto de 20 bytes não tem como expressar.
Posso ter tanto uma tabela quanto um stream em um arquivo? Uma única revisão usa uma ou outra. Mas um arquivo híbrido pode parear uma tabela clássica para leitores antigos com um xref stream para os novos, de modo que cada tipo de leitor encontra um índice que entende.
Documentos relacionados
Seção intitulada “Documentos relacionados”- O que um PDF realmente é — as mesmas quatro partes vistas como um grafo de objetos em vez de um layout físico.
- Atualizações incrementais e por que importam — como a quinta região anexada faz um arquivo crescer e protege as assinaturas.
- Streams e filtros — o que há dentro dos objetos stream do corpo e como eles são comprimidos.
- PDF 2.0: o que mudou — por que o xref stream é a estrutura padrão que o NextPDF escreve.
Glossário
Seção intitulada “Glossário”- Cabeçalho — a primeira linha,
%PDF-2.0, nomeando a versão; geralmente seguida de um comentário marcador de binário. - Objeto indireto — um objeto numerado no corpo, escrito
N G obj … endobj, ondeNé o número do objeto eGa geração. - Dicionário — um mapa
<< /Key value … >>de nomes para valores; a forma de objeto mais comum. - Stream — um dicionário mais um bloco de bytes entre
streameendstream, usado para conteúdo, fontes e imagens. - Tabela de referências cruzadas (xref) — o índice do número de objeto até o
offset de byte; classicamente uma tabela de texto de 20 bytes por entrada, um
stream
/Type /XRefno PDF 2.0. - Trailer — o dicionário que nomeia
/Roote/Size, localizado via o offsetstartxrefno fim do arquivo. - Atualização incremental — objetos alterados, uma nova seção de referências
cruzadas e um novo trailer anexados depois de
%%EOF, com/Prevencadeando de volta à seção anterior.