Artigo Técnico

Saída PDF 2.0, PDF/A-4 e PDF/UA-2 com HotPDF em Delphi

O HotPDF escreve documentos PDF 2.0 nativos a partir de Delphi e C++Builder, incluindo os três perfis de arquivo PDF/A-4 e a saída acessível PDF/UA-2 com elementos de estrutura nomeados em espaços de nomes. Selecioná-los é uma questão de duas propriedades, mas as normas por trás dessas propriedades mudaram mais do que o número de versão sugere: o PDF/A-4 abandonou as letras de conformidade que todos aprenderam com o PDF/A-2, e o PDF/UA-2 introduziu espaços de nomes de estrutura que um documento da parte 1 nunca teve

Este artigo aborda o que muda efetivamente no ficheiro gerado, e que erros o HotPDF transforma numa exceção em EndDoc em vez de num documento que falha a validação no lado do cliente

Como é que a identificação PDF/A-4 difere da parte 2 e 3

O PDF/A-4 identifica-se por número de parte e ano de revisão, sem letra de conformidade para a parte base. Defina PDFACompliance como '4' e o HotPDF emite pdfaid:part=4 com pdfaid:rev=2020 e sem nenhuma entrada pdfaid:conformance. A letra não se perdeu — a parte 4 não tem níveis A/B/U, porque os requisitos que costumavam separá-los foram integrados na parte base

Duas extensões mantêm uma letra. '4E' seleciona PDF/A-4e para documentos de engenharia e emite conformidade E, que permite os caminhos de anotação 3D e RichMedia que os outros perfis proíbem. '4F' seleciona PDF/A-4f e emite conformidade F, que permite um ficheiro embutido de qualquer formato. Todos os três forçam um cabeçalho PDF 2.0, exigem as verificações habituais de intenção de saída e metadados do PDF/A, e proíbem encriptação — um ficheiro de arquivo encriptado é uma contradição que a norma não contempla

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archive.pdf';
    Pdf.PDFACompliance := '4F';   // PDF/A-4f: associated files of any format
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-0731');
    Pdf.AddPDFAssociatedFile('invoice.xml', 'text/xml',
      'Structured invoice data', 'Data', LoadInvoiceBytes);
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

AddPDFAssociatedFile embute o ficheiro, constrói o seu FileSpec com um /AFRelationship, e regista-o tanto no array /AF do Catálogo como na árvore de nomes EmbeddedFiles. Ambos os registos são exigidos; um ficheiro listado apenas num deles é a razão individual mais comum pela qual uma fatura híbrida passa numa verificação visual rápida e falha num validador real. A cadeia de relação aceita Source, Data, Alternative, Supplement ou Unspecified, e o perfil ativo tem de ser PDF/A-3, PDF/A-4e ou PDF/A-4f — o perfil base da parte 4 não admite ficheiros associados. O nome antigo AddPDFA3AssociatedFile ainda funciona para código existente

O que o PDF/UA-2 exige que o PDF/UA-1 não exigia

O PDF/UA-2 força PDF 2.0 e emite pdfuaid:part=2 com pdfuaid:rev=2024, e introduz espaços de nomes na árvore de estrutura. Um documento da parte 1 tinha um vocabulário plano de papéis padrão. Um documento da parte 2 pode transportar papéis personalizados desde que cada um pertença a um espaço de nomes declarado, que é o que torna a marcação específica de domínio legível para as tecnologias de apoio em vez de adivinhação

Dois métodos implementam isto. RegisterStructureNamespace cria ou reutiliza um dicionário indireto /Type /Namespace e lista-o em StructTreeRoot /Namespaces, devolvendo o dicionário para que o possa reutilizar. AddStructureElementNS cria um elemento de estrutura cuja entrada /NS aponta para esse dicionário, que é o que licencia um nome de papel fora do conjunto padrão. Chamadas repetidas com o mesmo URI reutilizam um dicionário em vez de acumular duplicados

var
  Root: THPDFDictionaryObject;
begin
  Pdf.PDFUACompliance := True;
  Pdf.PDFUAPart := 2;             // part 2 forces PDF 2.0
  Pdf.Lang := 'en-US';
  Pdf.BeginDoc;
  Root := Pdf.AddStructureElement('Document', nil);
  Pdf.AddStructureElementNS('WidgetGroup',
    'https://example.com/ns/widgets', Root);
  Pdf.EndDoc;
end;

Lang não é decoração aqui. Um documento marcado sem idioma natural declarado deixa um leitor de ecrã a adivinhar a pronúncia, e o PDF/UA trata a omissão como um defeito em vez de uma preferência

Que erros de estrutura é que o EndDoc apanha?

Quatro, e cada um corresponde a um documento que de outro modo chegaria a um validador partido. A raiz de estrutura tem de conter exatamente um elemento Document de topo. Cada dicionário de espaço de nomes tem de ser indireto, com tipo Namespace, e transportar um URI único não vazio. Cada referência /NS de elemento de estrutura tem de resolver para um dicionário efetivamente listado no array /Namespaces da raiz. E um papel sem espaço de nomes tem de ser um papel padrão de PDF 2.0 ou resolver através do RoleMap

Estes disparam em EndDoc porque esse é o último momento em que a árvore inteira existe em memória e o primeiro momento em que está completa. Apanhá-los mais cedo significaria rejeitar estados intermédios válidos; apanhá-los mais tarde significaria não os apanhar de todo. A consequência prática para o seu código é que um bug de estrutura aparece no fim da geração com uma mensagem a nomear o problema, em vez de aparecer semanas depois como um relatório veraPDF que alguém reencaminha de um cliente

Os papéis de PDF 2.0 que vale a pena conhecer

O enumerado de papéis com tipo ganha DocumentFragment, Aside, Title, FENote, Sub, Em, Strong e Artifact. Três deles mudam como marca documentos de negócio comuns. Aside finalmente dá a barras laterais e citações destacadas um sítio que não seja um Sect mal usado. FENote marca notas de rodapé e notas finais como o que são, para que um leitor as possa oferecer em vez de as intercalar com o texto do corpo. Em e Strong substituem a adivinhação semântica que vinha de marcar ênfase como formatação ao nível do span

A sobrecarga de cadeia adicionalmente aceita a forma aberta Hn, incluindo H7 e além. O PDF 1.7 parava em H6, o que forçava documentos técnicos profundos a aplanar o seu esboço ou a reutilizar níveis. Se gerar documentos de normas, códigos legais ou catálogos de peças, só por isto pode ser a razão para mudar a saída para PDF 2.0

O que verificar antes de mudar a saída de produção

PDF 2.0 é uma mudança de cabeçalho com uma cauda longa. Ferramentas antigas de ingestão de arquivo, alguns RIPs de impressão e um número surpreendente de visualizadores de linha de negócio aceitam apenas até PDF 1.7, e falham no cabeçalho em vez de em qualquer coisa que tenha feito errado. Antes de mudar, confirme os sistemas consumidores, e lembre-se de que selecionar um perfil PDF/A-4 seleciona PDF 2.0 o queria ou não

Uma sequência segura é manter PDF/A-3 para documentos que vão para fora para leitores desconhecidos, usar PDF/A-4f para arquivos internos onde controla a ingestão, e adotar PDF/UA-2 apenas onde a política de acessibilidade o nomeie. Se estiver a trabalhar primeiro o lado de arquivo, os guias de validação PDF/A, PDF/X e PDF/UA e de faturas híbridas ZUGFeRD e Factur-X em PDF/A-3 cobrem as escolhas de perfil que importam antes do número de versão, e as notas sobre relatórios de preflight automatizados mostram como tornar o veredito parte da sua compilação em vez de um passo manual

O HotPDF distribui toda a superfície de autoria PDF 2.0 como código VCL nativo para Delphi e C++Builder, pelo que a saída PDF/A-4 e PDF/UA-2 não precisa de nenhum motor externo nem redistribuível — a página do componente HotPDF lista os perfis suportados e as versões de RAD Studio