Um ficheiro PDF é, em sua essência, uma coleção de objetos que apontam uns para os outros. Retire a compactação, a contabilidade de referência cruzada e os deslocamentos de bytes, e o que resta é um gráfico: um pequeno conjunto de valores tipados, conectados por referências, enraizados em um único objeto que o leitor sabe como encontrar. Tudo o que um PDF pode expressar, desde um parágrafo de texto a uma fonte embutida ou uma assinatura digital, é construído a partir de oito tipos primitivos de objetos e da regra que permite que um objeto faça referência a outro. Aprenda isso e o resto do formato será lido como composição em vez de mistério
Esta é a camada lógica do PDF, definida na cláusula 7.3 da ISO 32000-1, e ela fica um nível acima do layout físico do ficheiro (o cabeçalho, corpo, tabela de referência cruzada e trailer, que é seu próprio assunto na visão geral técnica da estrutura de ficheiros PDF). O modelo lógico é o que esses bytes significam uma vez analisados. Um visualizador lê o ficheiro de trás para frente para encontrar o trailer, segue-o até a raiz e, a partir daí, o documento se desdobra como objetos referenciando objetos. Esta é a parte sobre a qual você raciocina ao depurar uma página malformada, escrever um analisador ou confiar a uma biblioteca a montagem de um documento
Oito tipos de objetos e nada mais
O PDF define exatamente oito tipos básicos de objetos. Todo valor em um documento é um deles, o que mantém o formato tratável apesar de seu alcance
Boleanos são as palavras-chave true e false. Eles ativam e desativam sinalizadores, como se uma anotação deve ser impressa
Números vêm em dois sabores que a especificação trata como um tipo: inteiros como 42 e reais como 3.14 ou -0.002. O PDF não tem notação de expoente, então você nunca verá 1e6 em um ficheiro em conformidade. Coordenadas, tamanhos de fonte e ângulos de rotação são todos números
Strings mantêm sequências de bytes, escritas entre parênteses, (Hello), ou em colchetes angulares como hexadecimal, <48656C6C6F>. Ambas as notações codificam conteúdo idêntico; hexadecimal é a alternativa de escape para bytes difíceis dentro de parênteses. Strings carregam texto, mas são bytes primeiro, o que importa no momento em que você lida com qualquer coisa além de ASCII
Nomes (Names) são tokens atômicos introduzidos por uma barra: /Type, /Pages, /MediaBox. Um nome não é uma string; é um identificador, usado como uma chave de dicionário ou um valor enumerado, e dois nomes são iguais apenas se eles corresponderem byte por byte. A barra é a sintaxe, não faz parte do nome. Isso confunde os recém-chegados que tratam /Times-Roman e a string (Times-Roman) como intercambiáveis; o formato não o faz
Matrizes (Arrays) são listas ordenadas e heterogêneas entre colchetes: [0 0 612 792] é um retângulo de página, e uma matriz pode misturar tipos livremente, incluindo referências a outros objetos. Dicionários são o burro de carga. Escrito entre << e >>, um dicionário mapeia chaves de nomes a valores de qualquer tipo, e quase todas as estruturas significativas em PDF, página, catálogo, fonte, anotação, é um dicionário com uma chave /Type declarando o que é
Fluxos (Streams) são dicionários com uma cauda de bytes brutos entre as palavras-chave stream e endstream. O dicionário descreve os bytes (seu comprimento e quaisquer filtros como FlateDecode que os comprimem), e os bytes carregam a carga útil volumosa: instruções de conteúdo de página, programas de fontes incorporadas, imagens. Um fluxo é onde o PDF coloca qualquer coisa muito grande ou muito binária para ser inserida na linha
O oitavo tipo é o objeto nulo, a palavra-chave null. Ele é um valor real, distinto de uma chave estar ausente. Uma entrada de dicionário definida como null é tratada como se não estivesse presente, e uma referência a um objeto inexistente também resulta em null em vez de um erro. Esse comportamento tolerante é deliberado: permite que um ficheiro danificado degrade em vez de se recusar a abrir. Não há um nono tipo; tudo que o PDF expressa vem de como esses oito se combinam
Valores diretos, objetos indiretos e referências
Qualquer um desses oito tipos pode aparecer de duas maneiras. Um objeto direto é gravado no local, como o 612 dentro de um array MediaBox. Um objeto indireto recebe uma identidade para que outros objetos possam apontar para ele: dois números inteiros, um número de objeto e um número de geração, agrupando a definição em obj e endobj:
Este é o objeto 12, geração 0, um dicionário de fonte. Em qualquer outro lugar no ficheiro, outro objeto se refere a ele com uma referência indireta: os mesmos dois números seguidos pela palavra-chave R, 12 0 R. A referência é um ponteiro. Quando o dicionário de recursos de uma página diz /Font << /F1 12 0 R >>, ele nomeia o objeto 12 como a fonte por trás do nome do recurso /F1, sem copiar a definição da fonte na página
O número da geração existe para exclusões e reutilização. Quando um objeto é liberado e seu slot é reutilizado, a geração é incrementada para que um 12 0 R obsoleto não consiga resolver para o novo inquilino do slot 12. Arquivos recém-gravados são quase todos de geração 0, mas um ficheiro muito editado pode carregar números maiores, e um analisador que ignora a geração acabará lendo o objeto errado
A indireção é o que torna o PDF eficiente e editável. Uma fonte, imagem ou espaço de cores pode ser definido uma vez e referenciado a partir de cem páginas. Uma pequena alteração pode ser anexada como uma nova revisão que substitui um único objeto em vez de reescrever o ficheiro. A tabela de referência cruzada é o índice que transforma o número de um objeto em um deslocamento de byte, de modo que o leitor salta diretamente para 12 0 obj sem verificar, mas essa é uma otimização física. Logicamente, tudo que você precisa saber é que 12 0 R significa "o objeto identificado como 12 0"
O catálogo: onde todo documento começa
Resolver referências tem que começar em algum lugar, e esse lugar é a entrada /Root do trailer, que aponta para o catálogo de documentos: a raiz do gráfico de objetos, um dicionário com /Type /Catalog. O leitor a atinge primeiro porque o trailer é encontrado primeiro e, a partir daí, todas as outras partes do documento podem ser acessadas seguindo as referências
O catálogo traz apenas duas entradas estritamente obrigatórias: seu /Type e /Pages, uma referência indireta para a raiz da árvore de páginas. O resto é opcional e descreve o comportamento em todo o documento e não o conteúdo: /Outlines aponta para a árvore de favoritos, /Names possui árvores de nomes baseadas em chaves por string, /Metadata faz referência a um fluxo de metadados XMP, e /PageMode e /PageLayout sugerem como um visualizador deve abrir o documento. Nenhum deles é necessário para renderizar uma página; eles configuram a experiência em torno das páginas. O marcador, os metadados e as estruturas de anotação anexados ao catálogo são abordados no artigo sobre metadados PDF, marcadores e anotações
O diagrama abaixo mostra onde o corpo do objeto fica no ficheiro circundante. O catálogo e a árvore de páginas vivem dentro desse corpo como objetos indiretos comuns; o cabeçalho, a tabela de referência cruzada e o trailer ao redor deles são os andaimes físicos que permitem que um leitor os localize

A árvore de páginas: uma hierarquia balanceada de páginas
De /Pages o documento se ramifica para a árvore de páginas, onde a escolha do PDF de um gráfico em vez de uma lista plana compensa. As páginas não são armazenadas como uma sequência simples; elas ficam penduradas em uma árvore cujos nós interiores são nós de árvore de páginas (/Type /Pages) e cujas folhas são objetos de página (/Type /Page). Um nó interno lista seus filhos em uma matriz /Kids e registra, em /Count, quantas páginas filhas vivem abaixo dele. Cada nó, exceto a raiz, carrega uma referência /Parent de volta para cima, de modo que a árvore caminha em qualquer direção
Aqui o objeto 2 é a raiz, com três páginas abaixo dele: a página folha 3, mais duas acessíveis através do nó interior 4. A /Count da raiz de 3 deve ser igual ao total de folhas abaixo dela, e uma contagem que discorda da estrutura real é uma maneira comum de um ficheiro editado à mão dar errado. O ponto da árvore é a localidade de acesso. Um leitor que abre a página 900 de um documento de mil páginas não anda 900 objetos; ele desce um punhado de nós, porque uma árvore bem formada permanece rasa e equilibrada. Construir uma árvore assim manualmente é complicado o suficiente para valer a pena ver de ponta a ponta, o que o passo a passo sobre a construção de um documento PDF do zero faz
A árvore ganha seu segundo sustento por meio de herança. Um punhado de atributos de página, /Resources, /MediaBox, /CropBox e /Rotate, podem ser definidos em um nó interior e deixados de fora das páginas individuais, que então herdam o valor do ancestral mais próximo. Defina /MediaBox uma vez na raiz e cada folha recebe o mesmo tamanho de página sem repeti-lo; uma página que precisa diferir declara o seu próprio. Este é o único lugar no modelo de objeto onde o significado de um valor depende da posição de um objeto na árvore, não apenas em seu próprio conteúdo
O que uma página folha realmente contém
Um objeto de página é o ponto de junção entre o modelo estrutural e o conteúdo visível. Sua entrada /Contents faz referência a um ou mais fluxos de conteúdo, os operadores de desenho que pintam texto e gráficos na página. Seu dicionário /Resources nomeia as fontes, imagens e espaços de cores dos quais esses operadores dependem, cada entrada uma referência indireta a um objeto compartilhado nas páginas. A /MediaBox dá o retângulo da página em pontos (1/72 polegada), e entradas como /Rotate e /CropBox ajustam como ela é apresentada
Essa divisão de trabalho é o modelo inteiro em miniatura. O dicionário de páginas é a estrutura: entradas digitadas e referências que dizem o que a página é e com o que ela é desenhada. O fluxo de conteúdo são as instruções: um blob separado e compressível que diz como desenhar. A fonte por trás de /F1 é um recurso compartilhado, definido uma vez e apontado onde quer que seja usado. O dicionário, o fluxo e a referência cooperam para renderizar uma página, e os mesmos padrões escalam para o documento inteiro. Os operadores de fluxo de conteúdo dentro desse blob são cobertos separadamente para texto e fontes e para gráficos e elementos visuais
Por que vale a pena conhecer este modelo
A maioria dos desenvolvedores encontra o modelo de objetos apenas quando algo quebra: uma página fica em branco porque sua referência /Contents oscila, o texto sai como caixas porque um recurso de fonte nunca foi incorporado, uma ferramenta relata uma /Count que não corresponde às páginas que pode encontrar. Cada uma dessas é uma declaração sobre o gráfico, e ler o gráfico diretamente é melhor do que adivinhar. Os oito tipos e a regra de referência são um vocabulário pequeno o suficiente para manter em sua cabeça, e uma vez que você vê um PDF como objetos apontando para objetos, ficheiros malformados deixam de ser opacos
Dito isso, escrever o modelo à mão raramente é a decisão certa, além de aprender. Manter os deslocamentos de referência cruzada, números de geração, contagens de árvore de páginas e comprimentos de fluxo consistentes nas edições é o tipo de contabilidade que uma biblioteca existe para manipular. Na produção, uma biblioteca de desenvolvimento de PDF madura gerencia o gráfico de objetos enquanto deixa você pensar em páginas e conteúdo. Conhecer o modelo ainda compensa: você entende o que a biblioteca constrói por baixo e por quê