O PDFlibPas valida arquivos PDF/UA-2 (ISO 14289-2) em Delphi por meio do TPDFlib.CheckFilePDFUACompliance com parte alvo 2, que executa um perfil PDF/UA-2 separado: ele resolve papéis de estrutura através de namespaces do PDF 2.0 e do /RoleMapNS, reporta entrada truncada ou ilegível como falha e devolve um relatório de cobertura que nunca alega certificação completa
Essa última parte importa mais do que parece. Validação de acessibilidade é um campo em que um visto verde é copiado para uma planilha de compras e nunca mais questionado. Um validador que silenciosamente pula metade da norma e ainda imprime "pass" é pior do que nenhum validador, porque transforma um desconhecido em um falso conhecido. O PDFlibPas toma a posição oposta: verificar o que pode ser verificado mecanicamente, dizer em voz alta o que não foi verificado e tratar cada "não conseguiu terminar" como uma constatação, não como um sucesso
Por que não dá para validar PDF/UA-2 com regras de PDF/UA-1?
PDF/UA-2 não é PDF/UA-1 com o número da parte incrementado. O PDF/UA-1 se apoia no PDF 1.7 e em um único role map plano; o PDF/UA-2 se apoia no PDF 2.0 (ISO 32000-2), em que todo elemento de estrutura pode pertencer a um namespace e cada namespace carrega o seu próprio role map. Um verificador UA-1 apontado para um arquivo UA-2 vai ao mesmo tempo deixar de ver problemas reais e inventar problemas falsos
Os falsos são a parte constrangedora. Validadores PDF/UA-1 normalmente impõem regras sobre elementos Note carregarem um ID e sobre como o título não numerado H pode ser usado. Nenhuma das duas regras se transfere. No namespace de estrutura do PDF 2.0, notas de rodapé são expressas com FENote, e títulos vão de H1 a Hn sem teto de seis níveis. O PDF/UA-2 vai além e proíbe de vez o papel H não numerado. Aplique a lógica de títulos do UA-1 a essa árvore e você ou reclama do que a norma permite ou deixa passar a única coisa que ela proíbe
Por isso o perfil PDF/UA-2 do PDFlibPas é um caminho de código separado, não uma flag no caminho antigo. Ele também se recusa a reutilizar números de cláusula do UA-1. Diagnósticos compartilhados com o verificador UA-1 são revinculados à ISO 14289-2 nos relatórios UA-2, e onde um número de cláusula não foi verificado contra o texto da norma, o relatório deixa o campo de cláusula vazio em vez de dar um chute. Uma cláusula vazia é menos satisfatória do que um número de cláusula confiante, mas é a única saída defensável quando ninguém confirmou o mapeamento
Como o PDFlibPas resolve papéis através do RoleMapNS
O PDFlibPas resolve todo papel de estrutura para um papel padrão em um namespace conhecido, seguindo cadeias de /RoleMapNS entre namespaces, e reporta qualquer cadeia que termine em um papel customizado, entre em loop ou quebre as regras de mapeamento. Esse é o núcleo da validação de estrutura do PDF/UA-2, porque tecnologia assistiva só entende papéis padrão; um /InvoiceLine customizado não significa nada até aterrissar em algo como P ou TR
O resolvedor parte do nome /S do elemento e do seu namespace /NS. Sem namespace, o elemento está implicitamente no namespace padrão do PDF 1.7 e vale o /RoleMap legado na raiz da árvore de estrutura. Com namespace, vale o /RoleMapNS do próprio dicionário de namespace, e um mapeamento pode apontar para um papel em outro namespace por meio de um array de dois elementos. As verificações que daí decorrem são concretas:
- Um namespace referenciado a partir de um elemento de estrutura deve ser um dicionário indireto com URI
/NSválido e deve estar listado no array/Namespacesda raiz da árvore de estrutura (diagnóstico 10054) - Um papel customizado deve eventualmente resolver para um papel padrão no namespace do PDF 1.7, no namespace do PDF 2.0 (
http://iso.org/pdf2/ssn) ou no namespace MathML; cadeias circulares e becos sem saída são reportados como 10055 - Um papel padrão não pode ser remapeado para um customizado, e um papel não pode ser remapeado dentro do seu próprio namespace explícito
- A raiz da estrutura deve conter exatamente um elemento
Documentno namespace do PDF 2.0 (10057), e o/Pde cada elemento deve apontar para o seu pai real (10058) - Um
Hnão numerado, ou umNotelegado do PDF 1.7 que não esteja mapeado para um papel do PDF 2.0, é reportado como 10056
Um detalhe que vale copiar para a sua própria ferramenta: o PDFlibPas valida todo mapeamento declarado, não apenas os alcançáveis a partir da árvore. Uma entrada quebrada em um role map que nenhum elemento usa hoje continua sendo um defeito latente, porque a próxima atualização de conteúdo vai começar a usá-la. O MathML também é tratado com honestidade. O namespace MathML é reconhecido como alvo de resolução válido, mas o vocabulário MathML em si não é validado, e o relatório de cobertura diz isso
Executando uma verificação PDF/UA-2 a partir do Delphi
TPDFlib.CheckFilePDFUACompliance é o ponto de entrada de chamada única: passe o arquivo, a senha, a parte alvo e as opções, e você recebe de volta um handle de string list com os problemas codificados. Handle zero com LastErrorCode igual a zero significa que as verificações implementadas não acharam nada; um LastErrorCode diferente de zero significa que a própria chamada falhou
var
Lib: TPDFlib;
Issues, I: Integer;
begin
Lib := TPDFlib.Create;
try
// TargetPart: 0 = detectar pelo XMP pdfuaid:part, 1 = PDF/UA-1, 2 = PDF/UA-2
// Options: 1 = parar na primeira falha, 0 = coletar todo problema
Issues := Lib.CheckFilePDFUACompliance('report.pdf', '', 2, 0);
if Issues = 0 then
begin
if Lib.LastErrorCode <> 0 then
Writeln('Validation did not run, error ', Lib.LastErrorCode)
else
Writeln('No issues from the implemented PDF/UA-2 checks');
end
else
try
for I := 1 to Lib.GetStringListCount(Issues) do // itens são 1-based
Writeln(Lib.GetStringListItem(Issues, I));
finally
Lib.ReleaseStringList(Issues);
end;
finally
Lib.Free;
end;
end;
Escolha a parte alvo de forma deliberada. Alvo 0 lê pdfuaid:part dos metadados XMP e escolhe o perfil a partir daí, o que é adequado para um validador genérico de caixa de entrada. Para o seu próprio pipeline de saída, passe 2 explicitamente. Se o seu gerador esqueceu de escrever a identificação, a detecção automática cai nas regras do UA-1 e você valida a norma errada; um 2 explícito produz o diagnóstico 10050 para a parte ausente, mais 10051 se pdfuaid:rev não for 2024 e 10052 se a versão efetiva do PDF não for 2.0. No lado da geração, o TPDFlib.SetPDFUA2Mode escreve essa identificação, eleva a versão para 2.0 e seleciona o namespace de estrutura do PDF 2.0 como padrão; o modelo de namespaces em si é explicado no passo a passo de namespaces do PDF 2.0, PDF/A-4 e PDF/UA-2
O que acontece quando o validador esgota o orçamento?
O PDFlibPas falha fechado: quando o verificador PDF/UA-2 atinge um limite de travessia, um ciclo, um limite de parser ou um documento que não consegue abrir, ele emite um diagnóstico, e um resultado sem diagnósticos nunca é promovido a aprovação. O diagnóstico 10059 é reservado exatamente para esse caso de "o leitor não conseguiu terminar"
Os orçamentos são explícitos e publicados no JSON de cobertura: profundidade de estrutura 256, 100.000 objetos de estrutura e 256 saltos ao longo de qualquer cadeia de role map. Esses números existem porque árvores de estrutura vêm de entrada não confiável. Um arquivo hostil ou simplesmente quebrado pode aninhar elementos milhares de níveis fundo, apontar um filho de volta para um ancestral ou construir um role map que encadeia centenas de namespaces. Um validador sem orçamentos ou trava ou cai, e um validador com orçamentos mas sem diagnósticos faz algo mais sutil e pior: para cedo, não acha nada de errado na parte que leu e reporta sucesso
O mesmo princípio vale um nível acima. Se o leitor subjacente devolve uma pontuação abaixo de 100 mas nenhuma linha de problema, o que pode acontecer quando o parsing aborta antes de qualquer regra rodar, o PDFlibPas insere uma linha 10059 para que a lista vazia não seja confundida com um resultado limpo. Uma exceção durante a verificação também vira uma linha 10059. É pouca coisa de código, e fecha o jeito mais comum de pipelines de validação mentirem para os seus donos
Compartilhando um parse com PDF/A e construindo um relatório
Para trabalho em lote, a API de pipeline de validação faz o parse de um arquivo uma vez e roda várias verificações contra o parse compartilhado. O RunPDFUAValidationPipelineTask é o membro PDF/UA dessa família e aceita os mesmos valores de parte alvo da função de chamada única, o que evita que um preflight PDF/A mais PDF/UA-2 pague por duas leituras completas de um documento grande
var
Pipe, PDFAIssues, UAIssues: Integer;
Json: WideString;
begin
Pipe := Lib.BeginValidationPipeline('archive.pdf', '');
if Pipe = 0 then
Exit;
try
PDFAIssues := Lib.RunValidationPipelineTask(Pipe, 1, 0); // 1 = PDF/A
UAIssues := Lib.RunPDFUAValidationPipelineTask(Pipe, 2, 0); // PDF/UA-2
// ... inspecione e libere ambos os handles como no exemplo anterior ...
Writeln('Parses: ', Lib.GetValidationPipelineParseCount(Pipe));
// ComplianceTests: 1 = PDF/A, 2 = PDF/UA, 0 = ambos; ReportFormat 1 = JSON
Json := Lib.CreateValidationPipelineReport(Pipe, 0, 0, 1);
finally
Lib.ReleaseValidationPipeline(Pipe);
end;
end;
O relatório estruturado rotula a seção de acessibilidade como PDF/UA-2 e ISO 14289-2 quando o arquivo se identifica como parte 2, e toda seção PDF/UA carrega "fullCertification": false junto com o objeto de cobertura embutido. O relatório de preflight em texto adiciona uma linha afirmando que apenas as verificações implementadas rodaram e que a certificação completa de acessibilidade exige validação adicional e revisão humana. Os formatos de relatório mais amplos, os IDs de regras e os padrões de gating de CI são descritos no guia de preflight PDF/A e PDF/UA
O que o relatório de cobertura realmente garante?
O TPDFlib.GetPDFUAReadValidationCoverageJSON garante uma coisa: uma lista exata do que o verificador PDF/UA-2 examinou e do que não examinou. Para a parte 2 ele retorna fullCertification fixado em false, um array checked, um array unverified e os limites de travessia. Ele não garante que um arquivo seja acessível, e é desenhado para que nenhum caminho de código consiga fazê-lo dizer o contrário
var
Coverage: WideString;
begin
Coverage := Lib.GetPDFUAReadValidationCoverageJSON(2);
// Guarde-o junto do resultado de validação, não no lugar dele
TFile.WriteAllText('report.coverage.json', Coverage, TEncoding.UTF8);
if Pos('"fullCertification":false', Coverage) > 0 then
Writeln('Machine checks only; schedule a human accessibility review');
end;
A lista checked cobre identificação (parte, revisão, prefixo), versão efetiva do PDF, metadados de título, /MarkInfo, DisplayDocTitle, referências de namespace, resolução de role map, a raiz do documento, pais de estrutura, ciclos na árvore e as regras de H e Note. A lista unverified é igualmente específica: marcação de conteúdo e ordem de leitura, contenção de estrutura, fontes e mapeamento Unicode, anotações e formulários, idioma natural, descrições alternativas, o vocabulário MathML e revisão humana de acessibilidade. Vários desses itens nem são decidíveis mecanicamente. Nenhum parser consegue dizer se um alt text realmente descreve o gráfico, ou se a ordem de leitura faz sentido para quem a escuta
Trate o JSON de cobertura como parte do resultado. Arquive-o com cada execução de validação, e quando um auditor perguntar o que "aprovado" significava em uma determinada data, você pode entregar o escopo exato em vez de reconstruí-lo de memória e por um número de versão
Conferência cruzada com o veraPDF
O núcleo PDF/UA-2 foi conferido de forma cruzada contra o validador de linha de comando veraPDF 1.30.3 usando o perfil ua2 dele, e os resultados batem onde a cobertura se sobrepõe. Uma amostra core válida gerada de forma independente passou nas duas ferramentas; uma amostra declarando pdfuaid:rev 2023 e uma amostra usando o papel H não numerado falharam nas duas
A quarta amostra é a que vale a conversa. Ela continha conteúdo de desenho que não estava marcado de forma alguma, e o veraPDF a sinalizou enquanto as verificações core do PDFlibPas não, porque marcação de conteúdo está fora da cobertura atual. Essa lacuna é exatamente o motivo de content-tagging-and-reading-order encabeçar a lista unverified. Rodar um segundo validador é um seguro barato, e uma discordância entre duas ferramentas costuma ser o caminho mais rápido para achar a borda do que cada uma verifica. Para o lado de geração do mesmo problema, construir árvores que passam de primeira, veja o guia da árvore de estrutura de PDF marcado
FAQ
O PDFlibPas certifica um arquivo como em conformidade com PDF/UA-2?
Não. O PDFlibPas roda as verificações de máquina implementadas para PDF/UA-2 e devolve um relatório de cobertura com fullCertification sempre false, listando toda área que ainda precisa de outras ferramentas ou de revisão humana
Que valor de TargetPart passar para CheckFilePDFUACompliance?
Passe 2 quando você souber que o arquivo deve ser PDF/UA-2, para que a identificação ausente seja reportada. Passe 0 para detectar automaticamente a parte pelo XMP pdfuaid:part, e 1 para forçar as regras do PDF/UA-1
O que significa o diagnóstico 10059 em um relatório PDF/UA-2?
O diagnóstico 10059 significa que o validador não conseguiu concluir: o arquivo não pôde ser parseado ou aberto, um limite de travessia foi excedido, um ciclo foi encontrado ou ocorreu uma exceção. Ele conta sempre como falha, nunca como aprovação
A validação PDF/UA-2, o pipeline de validação compartilhado e as APIs de geração PDF 2.0 vêm no losLab PDF Library para Delphi, junto com preflight PDF/A e relatórios de validação estruturados