Artigo Técnico

Limites de Implementação de PDF/A e Verificações de Codificação de Fonte

O PDFium Component valida os limites de implementação da ISO 19005-1 Anexo C — tokens de nome de 127 bytes, 8191 elementos de array, 4095 entradas de dicionário e 28 níveis de aninhamento de contêiner — e relata uma fonte TrueType simbólica que carrega uma entrada /Encoding. Ambas as verificações rodam no caminho de varredura de bytes, então uma aplicação Delphi ou Lazarus obtém o veredito sem carregar a DLL do PDFium de forma alguma

Essas são as falhas que mais intrigam, porque o documento parece bem. Renderiza, imprime, todas as fontes estão embutidas, o output intent está presente. Então um validador o rejeita por um dicionário que tem 4096 entradas, e nada no documento visível explica por quê

O que os limites do Anexo C de fato protegem?

Interoperabilidade com implementações anteriores ao seu gerador. O Anexo C carrega os limites de implementação da PDF Reference para toda parte PDF/A, e os números não são arbitrários — eles descrevem o que um leitor em conformidade era historicamente obrigado a tratar. Um arquivo que os excede pode abrir perfeitamente num visualizador moderno e falhar no leitor arquivável sobre o qual um sistema de registros se padronizou há quinze anos, que é precisamente o cenário que o PDF/A existe para evitar

Os quatro limites são inclusivos. Um token de nome de exatamente 127 bytes valida; 128 não. Um array com exatamente 8191 elementos valida; 8192 não. O PDFium Component fixa ambos os lados de toda fronteira em sua suíte de testes por esse motivo, porque um off-by-one numa verificação de limite produz o pior tipo de validador: um que rejeita arquivos compatíveis e mesmo assim é acreditado

uses FPdfPdfa;

var
  Src: TFileStream;
  Res: TPdfAValidationResult;
begin
  Src := TFileStream.Create('archive.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfACompliance(Src);
    if pvaiArrayOverLimit in Res.Issues then
      Memo1.Lines.Add('An array carries more than 8191 elements');
    if pvaiDictOverLimit in Res.Issues then
      Memo1.Lines.Add('A dictionary carries more than 4095 entries');
    if pvaiNestingOverLimit in Res.Issues then
      Memo1.Lines.Add('Containers nest deeper than 28 levels');
    if pvaiNameOverLimit in Res.Issues then
      Memo1.Lines.Add('A name token is longer than 127 bytes');
  finally
    Src.Free;
  end;
end;

Quais geradores de fato batem nesses limites?

Os que constroem estrutura programaticamente, que é a maior parte da saída line-of-business. Um formulário com vários milhares de campos produz um array /Annots ou um array /Fields do AcroForm que cresce além de 8191. Uma página cujo dicionário de recursos acumula uma entrada por imagem gerada ou instância de fonte cruza 4095. Árvores de estrutura geradas profundamente — um documento tagged construído por recursão sobre um modelo de dados aninhado — passam de 28 níveis sem ninguém notar, porque ninguém olha a profundidade de aninhamento

Nomes longos vêm de um hábito diferente: codificar dados em tokens de nome. Um nome de corante construído a partir de um identificador de cliente, um grupo de conteúdo opcional nomeado a partir de um caminho de arquivo completo, um campo de formulário cujo nome totalmente qualificado concatena seis níveis de hierarquia. Nomes são baratos de gerar e fáceis de tornar longos, e 127 bytes somem mais rápido do que você esperaria uma vez que um rótulo codificado em UTF-8 está envolvido

O conserto é estrutural em todos os casos. Divida o array, divida o dicionário, achate o aninhamento, encurte o nome — a recomendação de preflight para cada problema nomeia o limite concreto em vez de dizer que o arquivo é inválido. Injeção de marcador não consegue ajudar aqui: essas não são reivindicações de metadados, são a forma do grafo de objetos

Por que uma fonte TrueType simbólica não pode carregar /Encoding

Porque a ISO 19005-1 §6.3.7 admite apenas o cmap embutido da fonte para fontes TrueType simbólicas, e uma entrada /Encoding a contradiria. Uma fonte simbólica mapeia códigos para glifos em seus próprios termos — isso é o que simbólica significa. Adicione uma tabela de codificação e agora há duas respostas para a pergunta "qual glifo o byte 0x41 seleciona", sem regra no arquivo dizendo qual vence. Leitores diferentes o resolvem de modo diferente, e um documento que renderiza como texto num visualizador renderiza como dingbats noutro

O PDFium Component lê o flag simbólico do /FontDescriptor, não importando se o descritor é escrito inline no dicionário de fonte ou referenciado indiretamente. Uma fonte TrueType não simbólica mantém sua /WinAnsiEncoding ou /MacRomanEncoding obrigatória sem ser sinalizada, porque para fontes não simbólicas a codificação é exatamente o que o padrão pede. A verificação dispara na contradição, não na presença de uma codificação

if pvaiSymbolicTrueTypeEncoding in Res.Issues then
  Memo1.Lines.Add(
    'A symbolic TrueType font carries /Encoding; PDF/A admits only its ' +
    'built-in cmap (ISO 19005-1 6.3.7)');

A fonte prática desse defeito é o subsetting de fontes feito por um produtor que trata toda fonte TrueType do mesmo jeito. Symbol, Wingdings, fontes de código de barras e fontes de ícone são os portadores habituais — exatamente as fontes que um documento de negócios usa para caixas de seleção, logotipos e códigos de barras, e exatamente aquelas que ninguém reexamina quando um documento falha na validação por causa de "fontes"

Como os problemas chegam num relatório de preflight

Os quatro limites de contêiner são classificados como estrutura; o problema de codificação TrueType simbólica é classificado como conteúdo. Essa divisão importa quando um relatório vai para duas pessoas diferentes: descobertas de estrutura costumam pertencer a quem escreveu o gerador, e descobertas de conteúdo costumam pertencer a quem forneceu os ativos

Cada problema carrega uma recomendação que nomeia o remédio em termos concretos — encurtar tokens de nome para 127 bytes ou menos, dividir arrays para que nenhum carregue mais de 8191 elementos, remover /Encoding de fontes TrueType simbólicas. Um relatório que diz "não conforme com PDF/A" inicia uma investigação. Um relatório que diz qual limite foi excedido e por quê encerra uma

Validando sem a DLL, e por que isso importa aqui

Todas as verificações acima rodam contra os bytes do arquivo, então funcionam num serviço que não tem nenhum binário do PDFium implantado, numa etapa de build, ou numa máquina onde carregar uma DLL nativa é um problema de política. Essa é uma linha de projeto deliberada no PDFium Component: as verificações que podem ser respondidas a partir da estrutura são respondidas a partir da estrutura, e a DLL é reservada para as que de fato precisam de um motor de render

Para o fluxo ao redor — rodar validação sobre uma pasta, produzir relatórios e decidir o que fazer com as descobertas — veja os guias de validação de preflight de PDF/A em Delphi e do CLI de relatório de preflight em lote. Para a escolha de perfil arquivável que fica acima de todas essas verificações, as notas sobre conformidade arquivável de PDF/A cobrem qual parte e nível mirar antes de você começar a corrigir descobertas

O PDFium Component envolve o motor PDFium para Delphi, C++Builder e Lazarus com uma API VCL de alto nível e um conjunto de validadores de conformidade que rodam com ou sem a DLL — veja a página do produto PDFium Component para os padrões e plataformas suportados