Technical Article

Verificação Prévia PDF/A e PDF/UA em Delphi com PDFlibPas

As normas PDF/A e PDF/UA respondem a duas questões totalmente independentes, e tratá-las como um único requisito de acessibilidade e arquivo é a forma mais fácil de ficheiros corrompidos chegarem a um arquivo exibindo um rótulo de conformidade. O PDF/A questiona se um ficheiro continuará a ser renderizado de forma fidedigna daqui a vinte anos. O PDF/UA avalia se as tecnologias de apoio conseguem lê-lo hoje. Um documento pode passar com distinção num teste e falhar redondamente no outro, pelo que o único veredicto fidedigno provém da execução de ambos, e de os executar antes de o ficheiro ser gravado, e não após um sistema recetor aceitar o identificador de conformidade gravado nos seus metadados. Esse identificador é uma autodeclaração. Nada no formato exige que seja verdadeiro, e uma aplicação que escreva "PDF/A-1b" no XMP sem validar o ficheiro face à norma produz um documento que parece em conformidade apenas para os leitores que se limitam a ler a etiqueta. A losLab PDF Library (PDFlibPas) resolve este problema para Delphi e C++Builder integrando ambos os validadores na biblioteca, permitindo que a verificação ocorra localmente sem a necessidade de configurar serviços externos.

Duas normas que reprovam ficheiros por motivos opostos

A norma ISO 19005 (PDF/A) representa um contrato de reprodução fidedigna. Um ficheiro em conformidade deve ser renderizado de forma idêntica daqui a décadas num software que nunca interagiu com o sistema que o gerou, pelo que as regras visam eliminar dependências externas: todos os tipos de letra têm de ser incorporados, as cores devem estar vinculadas a um perfil OutputIntent ICC incorporado ou expressas num espaço de cor independente do dispositivo, não pode existir encriptação no PDF/A-1, não é permitido JavaScript e os metadados XMP devem coincidir com o dicionário de informações do documento. Por outro lado, a norma ISO 14289 (PDF/UA) constitui um contrato semântico. As tecnologias de apoio devem ser capazes de percorrer o documento e extrair o seu significado, que reside numa camada inteiramente diferente: uma árvore de estrutura completa, texto alternativo em figuras, título do documento definido para exibição, níveis de cabeçalho contínuos e relações de cabeçalho de tabela que sobrevivam quando a página deixa de estar no ecrã.

Dado que as duas normas fiscalizam camadas distintas, os ficheiros mais problemáticos são os que se situam no intervalo entre elas. Um documento perfeito para arquivo pode ser totalmente silencioso para um leitor de ecrã. Um documento com uma etiquetagem excelente pode referenciar um tipo de letra do sistema de trabalho que deixará de existir em dez anos. As publicações do setor público são o local habitual onde ambos os requisitos convergem, e um fluxo de processamento nesse ambiente não os pode agrupar numa única validação. Os resultados destinam-se a destinatários diferentes: tipos de letra não incorporados representam um erro no código que gera o PDF, enquanto a ausência de texto alternativo é uma falha de quem desenhou os modelos de conteúdo, pelo que um relatório que misture ambos acaba apenas por ser reencaminhado duas vezes.

A parte da norma PDF/A selecionada como objetivo é tão importante como a sua própria conformidade. O PDF/A-1 baseia-se no PDF 1.4 e rejeita transparências e compressão JPEG2000, recursos que as ferramentas de relatórios modernos utilizam frequentemente de forma automática. O PDF/A-2 (ISO 19005-2, baseado em ISO 32000-1) aceita ambos e representa o padrão recomendado para novos arquivos. O PDF/A-3 vai mais além e autoriza a incorporação de ficheiros de qualquer formato, o que serve de base aos formatos normalizados de faturação eletrónica. Uma equipa que ainda adote a norma PDF/A-1b em 2026 está habitualmente a cumprir uma diretiva redigida há quinze anos, e redefinir a versão alvo da norma é quase sempre menos dispendioso do que remover as transparências de todos os gráficos gerados pelo sistema.

Resultados estruturados no momento da admissão

O ponto de entrada da API plana é o método CheckFileCompliance, utilizando o seletor de teste 1 para PDF/A e 2 para PDF/UA. Este devolve um handle de lista de strings cujos itens representam cada falha detetada, uma por linha, que constitui a estrutura ideal para ser percorrida por um validador automatizado:

function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;
var
  ListId, I: Integer;
begin
  ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0);  // 1 = PDF/A
  if ListId = 0 then
  begin
    // 0 means "no findings" OR "file unreadable" -- disambiguate before passing
    Result := Pdf.LastErrorCode = 0;
    Exit;
  end;
  for I := 0 to Pdf.GetStringListCount(ListId) - 1 do
    LogFinding(FileName, Pdf.GetStringListItem(ListId, I));
  Pdf.ReleaseStringList(ListId);
  Result := False;
end;

Existem dois detalhes que determinam se este processo pode decorrer de forma autónoma. O primeiro é um valor de retorno com dois significados opostos. O método CheckFileCompliance devolve 0 tanto quando o ficheiro está em total conformidade como quando não pôde ser aberto, visto que internamente uma lista de resultados vazia é reduzida a 0 em ambos os casos. Um validador que encare o 0 como aprovação aceitará carregamentos corrompidos diretamente no arquivo; por isso, realize a distinção utilizando LastErrorCode antes de confiar no valor zero, tal como exemplificado no código acima. O segundo detalhe refere-se à fase em que o ficheiro se encontra no seu ciclo de vida. O validador atua sobre o leitor de fluxo da biblioteca em vez de carregar o modelo de documento completo, abrindo o ficheiro diretamente com partilha de leitura sem chamar LoadFromFile, razão pela qual consegue analisar volumes de dados de gigabytes sem construir uma árvore de objetos. Essa mesma abertura de fluxo falha se outro processo mantiver o ficheiro aberto para escrita, sendo o carregamento em curso o exemplo típico deste estado. Realize a validação apenas após a conclusão da transferência.

A arquitetura baseada em fluxos (streaming) revela-se vantajosa em cenários de elevada carga. Cada verificação abre a respetiva entrada apenas para leitura e partilha-a, permitindo que a auditoria de um conjunto documental seja distribuída por threads ou processos de trabalho com uma instância de TPDFlib por executor, sem qualquer contenção entre eles. O recurso que requer atenção é o próprio handle. Cada resultado diferente de zero obtido de CheckFileCompliance mantém-se alocado em memória até que se chame ReleaseStringList, e um validador de execução contínua que ignore esta libertação não falhará de imediato, mas sofrerá uma perda gradual de memória (memory leak) visível ao longo do tempo.

Relatórios legíveis e diferenças para validação de compilação

Uma lista de falhas é o formato adequado para um validador automático, mas inadequado para enviar por correio eletrónico à equipa de modelos de conteúdo. O método CreatePreflightReport apresenta a mesma análise em formato de texto corrido e legível, CreatePreflightReportEx adiciona um seletor do formato do relatório e SavePreflightReport escreve-o no disco para que o relatório acompanhe o pacote do documento final. Diversos contratos de arquivo exigem este relatório como um produto final obrigatório e não apenas como um registo técnico interno.

O membro desta família que se destaca pela sua utilidade silenciosa é o método ComparePreflightReports. A conformidade é uma área sujeita a regressões como qualquer outro comportamento do software. Um ajuste num modelo, a introdução de um novo tipo de letra corporativo ou uma atualização da biblioteca podem introduzir falhas que não existiam na versão anterior, sem qualquer aviso. Mantenha os relatórios de referência (golden reports) de um conjunto de documentos modelo sob controlo de versões, regenere-os após cada alteração e execute ComparePreflightReports para calcular o desvio. Um resultado sem alterações é um registo de lançamento que vale a pena conservar. Uma inconformidade inesperada faz falhar a compilação, o que representa um custo de correção muito menor do que se for detetada apenas na auditoria final.

Gerar documentos em conformidade na primeira execução

A verificação prévia é útil para ficheiros recebidos de fontes externas. Contudo, para os documentos gerados pelo seu próprio código, detetar inconformidades após a geração e tentar corrigi-las é o processo mais ineficiente. O PDFlibPas disponibiliza um modo de geração específico para cada norma, sendo possível ativar ambos para o mesmo documento:

var
  Pdf: TPDFlib;
  Diag: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.NewDocument;
    Pdf.SetPDFAMode(1);
    Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');
    Pdf.SetPDFUAMode('en-US');
    Pdf.SetInformation(1, 'Quarterly Statement');  // /Title: required for PDF/UA
    // ... draw tagged content here ...
    Diag := Pdf.GetPDFUADiagnostics;
    if Diag <> '' then
      Writeln('fix before shipping: ', Diag);
    Pdf.SaveToFile('statement.pdf');
    // the preflight that counts runs on the saved file:
    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
  finally
    Pdf.Free;
  end;
end;

A armadilha esconde-se no momento da gravação. Diversas correções de conformidade ocorrem enquanto o documento está a ser serializado e não no momento de ativação do modo: a imposição da sinalização de impressão nas anotações, a escrita do valor AFRelationship predefinido para ficheiros incorporados no PDF/A-3 e a normalização da ordem de tabulação e descrições de campos no PDF/UA. O documento em memória não é idêntico ao nível dos bytes ao que é gravado no disco, pelo que o único veredicto de conformidade válido é o obtido a partir do ficheiro guardado. Valide o ficheiro statement.pdf real. Não assuma a conformidade com base no objeto presente na memória, uma vez que os bytes que estaria a testar não são os bytes que serão distribuídos.

Os cenários de faturação que contêm dados XML estruturados juntamente com o documento visual adotam o padrão ZUGFeRD e Factur-X, assente no PDF/A-3. Nestes casos, deve configurar a relação do anexo explicitamente com SetPDFA3DefaultAFRelationship, dado que a norma ISO 19005-3 exige que cada ficheiro incorporado declare a sua função face ao documento principal. Deixar este valor indefinido fará com que o XML seja visto apenas como um bloco de dados sem finalidade declarada, o que será assinalado pelo validador.

Validadores independentes: veraPDF e Acrobat

O gerador de documentos não deve ser o único auditor do seu próprio resultado. Os validadores do PDFlibPas fornecem veredictos rápidos e estruturados diretamente no processo, o que é ideal para o fluxo de execução rápida, mas o controlo de lançamento para lotes de arquivo deve submeter o documento a uma ferramenta que não tenha sido desenvolvida pela sua equipa. O veraPDF é a implementação de referência mantida pela comunidade para o PDF/A e a ferramenta mais indicada nos critérios de aceitação de arquivos, sendo a referência a atingir. Os perfis de verificação prévia do Acrobat constituem um elemento útil de desempate quando o veraPDF e a validação integrada divergem. Registe o nome do validador e a respetiva versão junto a cada relatório arquivado. A afirmação de que um ficheiro obteve aprovação no veraPDF tem pouco valor sem o número da versão que o processou, dado que as regras da ferramenta se tornam mais estritas entre atualizações.

Os validadores divergem pontualmente na interpretação das normas, e nesses casos a resposta não consiste em escolher a ferramenta da sua preferência. Reduza o ficheiro a uma amostra mínima que continue a manifestar a divergência e analise-a face ao texto da norma. Geralmente, esta verificação revela uma de duas situações: uma falha real na ferramenta que deve ser comunicada aos programadores ou uma secção da norma que a sua equipa interpretou de forma incorreta e que deve registar nas notas de conformidade para evitar futuras discussões.

Os dados de entrada encriptados contam com um atalho. Ambos os validadores aceitam um argumento de palavra-passe, mas um ficheiro PDF/A-1 que possua um dicionário de encriptação é por si só considerado não conforme, dado que a norma ISO 19005-1 proíbe a encriptação na sua totalidade, pelo que uma submissão protegida pode ser rejeitada antes de se iniciarem análises detalhadas. A identificação das ações permitidas por um dicionário de encriptação é abordada no guia sobre encriptação de PDF e auditoria de permissões.

As falhas em PDF/UA remontam quase sempre à forma como a árvore de estrutura foi originalmente construída, e as técnicas de etiquetagem adequadas são detalhadas no artigo sobre a construção de árvores de estrutura de PDFs Etiquetados em Delphi. Os arquivos que exijam assinaturas digitais devem associar esta etapa ao fluxo apresentado no guia sobre assinatura e validação de PAdES. A referência completa da API de verificação prévia está disponível na página do produto losLab PDF Library para Delphi.