Remova as descrições das páginas e restará uma fina camada de estrutura que ninguém imprime, mas da qual todos os leitores, indexadores e sistemas de arquivo dependem. Um objeto de página nada sabe sobre o capítulo a que pertence, o autor que o escreveu ou a nota de rodapé que liga a outro local. Esse conhecimento reside um nível acima, em três estruturas anexadas ao catálogo do documento: os fluxos de metadados, a árvore de contornos (outlines) e as matrizes de anotações de cada página. Partilham uma característica que as torna propensas a erros: nenhuma delas apresenta marcas visíveis na página, pelo que um ficheiro pode ser composto perfeitamente e, ainda assim, não ter os seus marcadores, contradizer o seu próprio campo de autor ou apontar uma ligação para um objeto de página que já não existe.
Esta é a camada que uma biblioteca PDF expõe como propriedades do documento, APIs de marcadores e chamadas de ligação ou anotação, e a camada que um indexador de pesquisa lê para decidir sobre o que trata o seu documento. O modelo de objetos subjacente é abordado no guia detalhado da estrutura de documentos PDF. Aqui o foco é estritamente no que está pendente do catálogo.
As três estruturas anexam-se ao catálogo. Um catálogo completo que as interliga assemelha-se a isto:
1 0 obj
<< /Type /Catalog
/Pages 2 0 R
/Outlines 3 0 R
/Names << /EmbeddedFiles 4 0 R >>
/Metadata 5 0 R
>>
endobj
Quatro entradas, quatro subsistemas independentes. /Pages é o documento visível; /Outlines é a árvore de marcadores; /Metadata aponta para o fluxo XMP; /Names alcança o dicionário de nomes global do documento, que entre outras coisas contém ficheiros anexos incorporados. Cada uma é opcional, e um leitor que não encontre nenhuma delas ainda assim apresenta as páginas. Essa opcionalidade é precisamente a razão pela qual a camada de navegação é a primeira a degradar-se quando um ficheiro é editado por ferramentas que apenas compreendem páginas.
Dois repositórios de metadados que discordam
O PDF armazena metadados do documento em dois locais em simultâneo, e o problema surge quando estes dizem coisas diferentes. O mecanismo original é o dicionário de informações do documento, referenciado por /Info no trailer: um conjunto simples de pares chave-valor para /Title, /Author, /Subject, /Keywords, /Creator, /Producer e as duas datas. É simples e todos os leitores o conseguem ler. O PDF 2.0 descontinua a maior parte deste dicionário em favor do segundo mecanismo, o fluxo de metadados XMP.
O XMP é un documento XML autónomo, escrito em RDF, armazenado como um fluxo que o catálogo alcança através de /Metadata e marcado como /Type /Metadata /Subtype /XML. Ao contrário do dicionário Info oculto na estrutura de objetos do PDF, um pacote XMP foi concebido para ser extraído e analisado de forma independente por ferramentas que não conhecem nada de PDF. Eis um pacote representativo:
5 0 obj
<< /Type /Metadata /Subtype /XML /Length 1235 >>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
Quarterly Report
A. Author
2026-06-16T10:46:27+08:00
Reporting Service 4.2
losLab PDF Library
<?xpacket end="w"?>
endstream
endobj
Três detalhes neste bloco decidem se os metadados sobrevivem ao contacto com ferramentas reais. As instruções de processamento xpacket não são decorativas: delimitam o pacote para que um extrator o encontre num fluxo de bytes maior, e um editor que omita o fecho <?xpacket end="w"?> produzirá um ficheiro que abre sem problemas, mas falha em validadores estritos. Os tipos de dados das propriedades também importam. dc:title é uma alternativa de idioma envolvida em rdf:Alt, enquanto dc:creator é uma lista ordenada e requer rdf:Seq; emitir qualquer um deles como um nó de texto simples é o erro de XMP mais comum, tolerado pela maioria dos leitores até aparecer um que o rejeite. Os prefixos de namespace são convencionais, mas os URIs a que se associam são normativos: o analisador baseia-se no URI, não no prefixo.
A regra de ouro com dois repositórios é que estes têm de concordar. Se o /Info indica que o autor é uma pessoa e o dc:creator nomeia outra, distribuiu um documento que responde à mesma pergunta de duas formas, dependendo a resposta vencedora do campo lido pela ferramenta de consumo. Uma biblioteca geralmente escreve ambos por si, mas no momento em que edita um manualmente ou funde ficheiros de diferentes geradores, os dois divergem. Trate o dicionário Info como compatibilidade herdada e o XMP como a fonte da verdade, e regenere ambos a partir de um único conjunto de valores em vez de os corrigir de forma independente. Para o PDF/A, isto torna-se um requisito de conformidade: a norma ISO 19005 exige o XMP e proíbe qualquer propriedade Info que contradiga o seu homólogo XMP.
A árvore de contornos por trás do painel de marcadores
O que um leitor apresenta como painel de marcadores é, no ficheiro, uma árvore duplamente ligada de dicionários chamada contorno do documento (document outline). O catálogo aponta para um dicionário de contorno raiz através de /Outlines; a raiz aponta para os seus primeiros e últimos elementos do nível de topo; e cada elemento está encadeado com os seus vizinhos e com o seu pai. Não existe nenhuma matriz de marcadores. Toda a estrutura é reconstruída seguindo referências, sendo esta a razão pela qual uma única ligação quebrada pode fazer desaparecer um ramo inteiro do painel sem qualquer erro.
8 0 obj % the outline root
<< /Type /Outlines /Count 4 /First 9 0 R /Last 9 0 R >>
endobj
9 0 obj % top-level: a chapter
<< /Title (Chapter 1: Results)
/Parent 8 0 R /Count 2
/First 12 0 R /Last 15 0 R >>
endobj
12 0 obj % first child
<< /Title (Introduction)
/Parent 9 0 R /Next 15 0 R
/Dest [3 0 R /XYZ 72 720 0] >>
endobj
15 0 obj % second child, last sibling
<< /Title (Methodology)
/Parent 9 0 R /Prev 12 0 R
/Dest [3 0 R /Fit] >>
endobj
Leia as ligações e os invariantes tornam-se óbvios. Cada elemento aponta de volta para o seu /Parent. Os elementos irmãos formam uma cadeia através de /Prev e /Next, omitindo o primeiro elemento o /Prev e o último omitindo o /Next. Um pai identifica o seu primeiro e último filho através de /First e /Last, e os filhos intermédios só são alcançáveis percorrendo a cadeia de irmãos. Se errar num deles, a falha será silenciosa: um /Next desatualizado trunca um capítulo, um pai cujo /Last não termina a cadeia deixa elementos órfãos, e o leitor renderiza apenas o que consegue alcançar.
O campo /Count armazena um estado que surpreende as pessoas. Na raiz e em qualquer elemento expandido, contém o número de descendentes atualmente visíveis; num elemento recolhido, é um número negativo cujo valor absoluto representa quantos descendentes apareceriam ao ser expandido. Portanto, o /Count não é um facto estrutural fixo sobre a árvore, é o estado aberto ou fechado guardado do painel, e um gerador que o defina como um total positivo reabrirá todos os ramos que o autor pretendia deixar fechados.
Cada elemento merece o seu lugar ao apontar para algum sítio. O /Title é o que o painel exibe; o /Dest é onde o clique aterra. Um destino pode estar em linha no elemento, como mostrado acima, ou ser um nome que se resolve através do dicionário de nomes do documento, que é a melhor escolha quando muitos marcadores e ligações visam os mesmos pontos, pois corrige um destino movido num único local. Uma biblioteca geralmente oculta esta árvore atrás de um manipulador outline-root e de métodos que adicionam entradas secundárias; no HotPDF, o documento expõe um OutlineRoot do tipo THPDFDocOutlineObject e encadeia as ligações /Prev, /Next, /Parent e /Count por si à medida que anexa elementos. Vale a pena aproveitar esta funcionalidade, pois manter estes invariantes manualmente durante edições é onde os contornos se quebram.
Destinos: a gramática de onde o clique vai
Tanto os marcadores como as anotações de ligação apontam para destinos, e un destino é mais do que um número de página. Trata-se de uma matriz que nomeia um objeto de página e especifica, através de um verbo na segunda posição, como o leitor o deve enquadrar. O mais comum e também o mais abusado é o /XYZ, sob a forma [page /XYZ left top zoom]. Os seus três operandos são independentes, e qualquer um deles pode ser null para significar "manter o estado em que o leitor o tinha". Assim, [page /XYZ null null null] salta para a página sem alterar a posição de deslocamento ou o zoom, o que normalmente se deseja numa ligação para "ir para a página". Os números estão no espaço do utilizador predefinido, medidos a partir do canto inferior esquerdo com o y a aumentar para cima, o mesmo sistema de coordenadas que o conteúdo da página utiliza. Os autores habituados ao desenho de ecrã medem intuitivamente a partir do topo e enviam o leitor para o lado errado da página.
A família /Fit abdica do posicionamento preciso em prol da resiliência. [page /Fit] ajusta a página inteira à janela, [page /FitH top] ajusta a largura da página a partir de um limite superior especificado e [page /FitR l b r t] faz zoom de um retângulo para preencher a visualização. Como estes métodos calculam a escala a partir da geometria da página e não de coordenadas fixas, um destino /Fit continua a funcionar corretamente após o redimensionamento da página, enquanto um destino /XYZ com um zoom fixo pode deixar o leitor a olhar para a margem. Para um índice, o /FitH com a coordenada de topo da secção envelhece melhor do que um /XYZ com um zoom estimado.
Anotações: tudo o que é interativo e não pertence ao conteúdo da página
Uma anotação é um objeto que se sobrepõe à página sem fazer parte do seu fluxo de conteúdo. Ligações, notas adesivas, realces, controlos de formulários, ícones de ficheiros anexos, carimbos: todos são anotações, listados na matriz /Annots da página onde se situam. Remover uma anotação desta matriz retira-a da página, mesmo que o conteúdo subjacente permaneça intacto. Esse é o objetivo: as anotações constituem uma camada de edição separada das marcas visíveis sobre as quais se sobrepõem.
Cada anotação partilha uma pequena espinha dorsal. /Subtype define o tipo, /Rect define a sua caixa de delimitação em coordenadas de página e /Contents armazena o texto que serve também de descrição acessível. A anotação de ligação é o caso de estudo ideal, pois apresenta-se de duas formas: um destino direto ou uma ação.
12 0 obj % link to a destination
<< /Type /Annot /Subtype /Link
/Rect [100 200 300 250]
/Border [0 0 0]
/Dest [5 0 R /XYZ null null null] >>
endobj
13 0 obj % link that runs an action
<< /Type /Annot /Subtype /Link
/Rect [50 50 200 100]
/Border [0 0 0]
/A << /Type /Action /S /URI /URI (https://www.example.com) >> >>
endobj
O /Rect é uma área ativa; clicar dentro dela envia o leitor para o destino, reutilizando a mesma gramática que o contorno utiliza. A instrução /Border [0 0 0] desempenha um papel importante, suprimindo o retângulo predefinido inestético que os leitores desenham à volta das ligações. A segunda forma substitui o /Dest direto por uma ação /A, cujo subtipo /S seleciona o comportamento: /GoTo dentro deste ficheiro, /GoToR para outro ficheiro, /URI para um endereço web ou /Launch para executar um programa externo. Este último deve ser visto com desconfiança. Um /Launch que inicia um executável constitui o comportamento que torna os ficheiros PDF um vetor de malware, pelo que os leitores em conformidade bloqueiam-no ou emitem avisos sonoros, falhando a ligação para a maioria dos utilizadores. Opte por /URI e /GoTo e evite a utilização de /Launch.
As anotações de marcação, tais como realces e notas adesivas, e as anotações geométricas, tais como /Square, acrescentam um detalhe extra: o seu aspeto no ecrã não está implícito no seu tipo. O leitor renderiza a sua própria versão, a menos que defina a aparência com um fluxo de aparência, a entrada /AP entry, que referencia um formulário XObject contendo os operadores de desenho. Se a omitir, o mesmo realce pode parecer diferente em dois leitores ou após a edição do ficheiro. Para tudo o que exija um aspeto visual exato no documento, forneça a entrada /AP. Os ficheiros anexos, aliás, reutilizam este mesmo mecanismo: um fluxo de ficheiro incorporado e um dicionário de especificação de ficheiro, expostos como uma anotação /FileAttachment ou através da árvore de nomes /EmbeddedFiles sob o catálogo em /Names.
Onde esta camada falha e como a detetar
A falha recorrente em toda esta estrutura é a referência quebrada (dangling reference). Os marcadores deixam de aparecer quando o catálogo não possui a entrada /Outlines ou quando a cadeia de irmãos se quebra a meio da árvore; os metadados são ignorados quando o fluxo XMP não tem a marcação /Type /Metadata /Subtype /XML ou quando o delimitador xpacket está incorreto. Em todos os casos, o conteúdo da página está correto, pelo que uma abertura superficial parece perfeita e o defeito só surge no painel que ninguém verificou.
Dois hábitos simples evitam a maioria destes problemas. Abra o ficheiro finalizado num leitor real e clique no painel de marcadores e numa amostra de ligações, o que testa o gráfico de referências tal como o leitor o fará. Em seguida, leia os metadados com outra ferramenta e confirme se o dicionário Info e o XMP coincidem, uma vez que esta discrepância não é revelada por cliques. Gerar esta camada através de uma biblioteca que faça a gestão das ligações evita a maioria destas armadilhas. O HotPDF Component para Delphi e C++Builder expõe as estruturas de contornos, anotações e metadados através de APIs ao nível do documento, bastando descrever a hierarquia de marcadores e as ligações para que a biblioteca faça o encadeamento de referências. Relativamente ao modelo de objetos ao qual estas estruturas se anexam, a visão geral técnica da estrutura de ficheiros PDF descreve o catálogo e a tabela de referências cruzadas das quais dependem.