Artigo Técnico

Saída em 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 arquiváveis PDF/A-4 e a saída acessível PDF/UA-2 com elementos de estrutura em namespaces. Selecioná-los é uma questão de duas propriedades, mas os padrões por trás dessas propriedades mudaram mais do que o número de versão sugere: PDF/A-4 derrubou as letras de conformidade que todo mundo aprendeu com PDF/A-2, e PDF/UA-2 introduziu namespaces de estrutura que um documento da parte 1 nunca teve

Este artigo cobre o que de fato muda no arquivo gerado, e quais erros o HotPDF transforma em uma exceção em EndDoc em vez de em um documento que falha na validação no site do cliente

Como a identificação do PDF/A-4 difere das partes 2 e 3

PDF/A-4 se identifica 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 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 incorporados à 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 arquivo embutido de qualquer formato. Todos os três forçam um cabeçalho PDF 2.0, exigem as verificações usuais de output intent e metadados do PDF/A, e proíbem criptografia — um arquivo arquivável criptografado é uma contradição que o padrão não acolhe

Matriz dos valores de seletor PDF/A-4 no HotPDF mostrando o 4 base sem letra de conformidade ao lado dos perfis 4E engineering e 4F embedded-file sobre restrições compartilhadas do PDF 2.0
a base '4' grava a parte 4 rev 2020 sem letra alguma; apenas 4E e 4F mantêm uma
var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archive.pdf';
    Pdf.PDFACompliance := '4F';   // PDF/A-4f: associated files de qualquer formato
    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 arquivo, constrói seu FileSpec com um /AFRelationship, e o registra tanto no array /AF do Catálogo quanto na árvore de nomes EmbeddedFiles. Ambos os registros são exigidos; um arquivo listado em apenas um deles é o motivo mais comum de uma fatura híbrida passar por uma verificação rápida a olho nu e falhar em um validador de verdade. A string de relacionamento aceita Source, Data, Alternative, Supplement ou Unspecified, e o perfil ativo deve ser PDF/A-3, PDF/A-4e ou PDF/A-4f — o perfil base da parte 4 não admite arquivos associados. O nome antigo AddPDFA3AssociatedFile ainda funciona para código existente

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

PDF/UA-2 força PDF 2.0 e emite pdfuaid:part=2 com pdfuaid:rev=2024, e introduz namespaces 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 carregar papéis personalizados desde que cada um pertença a um namespace declarado, que é o que torna a marcação específica de domínio legível para tecnologia assistiva em vez de adivinhação

Dois métodos implementam isso. RegisterStructureNamespace cria ou reutiliza um dicionário indireto /Type /Namespace e o lista em StructTreeRoot /Namespaces, devolvendo o dicionário para que você possa reutilizá-lo. 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 único dicionário em vez de acumular duplicatas

Diagrama contrastando o vocabulário de papéis plano do PDF/UA-1 com os namespaces do PDF/UA-2 vinculados por meio de RegisterStructureNamespace e AddStructureElementNS em Delphi
um URI sempre reutiliza um único dicionário de namespaces — sem pilhas duplicadas
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 tagged sem idioma natural declarado deixa um leitor de tela adivinhando a pronúncia, e o PDF/UA trata a omissão como defeito em vez de preferência

Quais erros de estrutura o EndDoc pega?

Quatro, e cada um corresponde a um documento que de outro modo chegaria quebrado a um validador. A raiz da estrutura deve conter exatamente um elemento Document no nível superior. Cada dicionário de namespace deve ser indireto, tipado como Namespace, e carregar um URI único não vazio. Toda referência /NS de elemento de estrutura deve resolver para um dicionário de fato listado no array /Namespaces da raiz. E um papel sem namespace deve ser um papel padrão de PDF 2.0 ou resolver por meio do RoleMap

Esses disparam em EndDoc porque esse é o último momento em que a árvore inteira existe em memória e o primeiro em que está completa. Pegá-los antes significaria rejeitar estados intermediários válidos; pegá-los depois significaria não pegá-los de jeito nenhum. A consequência prática para o seu código é que um bug de estrutura aparece no fim da geração com uma mensagem nomeando o problema, em vez de aparecer semanas depois como um relato veraPDF que alguém encaminha de um cliente

Fluxo dos quatro portões de árvore de estrutura que o HotPDF verifica no EndDoc: raiz Document única, dicionários de namespace limpos, referências NS resolvíveis, papéis padrão ou mapeados
capturar os portões mais cedo rejeitaria estados intermediários válidos

Os papéis do PDF 2.0 que valem conhecer

O enum de papéis tipados ganha DocumentFragment, Aside, Title, FENote, Sub, Em, Strong e Artifact. Três desses mudam como você marca documentos de negócios comuns. Aside finalmente dá a barras laterais e pull quotes uma casa que não seja um Sect mal usado. FENote marca notas de rodapé e notas finais como o que são, de modo que um leitor pode oferecê-las em vez de intercalá-las com o texto do corpo. Em e Strong substituem a adivinhação semântica que vinha de marcar ênfase como formatação em nível de span

A sobrecarga por string adicionalmente aceita a forma aberta Hn, incluindo H7 e além. PDF 1.7 parava em H6, o que forçava documentos técnicos profundos a achatarem seu esboço ou a reutilizar níveis. Se você gera documentos de normas, códigos jurídicos ou catálogos de peças, só isso pode ser o motivo para mover a saída para PDF 2.0

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

PDF 2.0 é uma mudança de cabeçalho com uma cauda longa. Ferramentas antigas de ingestão arquivável, alguns RIPs de impressão e um número surpreendente de visualizadores line-of-business aceitam apenas até PDF 1.7, e falham no cabeçalho em vez de em qualquer coisa que você tenha feito de errado. Antes de trocar, confirme os sistemas consumidores, e lembre-se de que selecionar um perfil PDF/A-4 seleciona PDF 2.0 tenha você pedido ou não

Uma sequência segura é manter PDF/A-3 para documentos que vão para fora a leitores desconhecidos, usar PDF/A-4f para arquivos internos onde você controla a ingestão, e adotar PDF/UA-2 apenas quando a política de acessibilidade o nomeia. Se você está trabalhando primeiro o lado arquivável, os guias de validação de 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 build em vez de uma etapa manual

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