Uma ferramenta de preflight em lote é um programa de console sem janela, apontada para uma pasta de PDFs, que valida cada um contra os padrões de conformidade que você nomeia e deixa para trás prova legível por máquina do que encontrou. Ninguém fica sentado observando. Ela roda às duas da manhã sob cron ou Windows Task Scheduler, ou como um portão em um pipeline de CI, e a próxima pessoa a se importar com sua saída é um agendador lendo um código de saída ou um auditor abrindo um relatório semanas depois. Isso muda o que "correto" significa. O motor de preflight do PDFium Component, uma biblioteca PDF de código-fonte para Delphi, C++Builder e Lazarus, torna as próprias chamadas de validação quase triviais. O trabalho que decide se a ferramenta se paga fica em torno dessas chamadas: qual perfil você verificou, o que o código de saída disse ao agendador, e se o relatório que teria pegado um erro ainda existe quando alguém vai procurá-lo
O contrato: o que um agendador pode realmente ver
Um runner de CI ou Windows Task Scheduler vê exatamente duas coisas da sua ferramenta: o código de saída e quaisquer arquivos que ela deixou para trás. Linhas de log, cores de console, saída de progresso: tudo isso é para um humano observando ao vivo, e às duas da manhã ninguém está. Então fixe o vocabulário de código de saída antes de tocar no API, e mantenha-o maçante:
0: todo arquivo conformou a todo perfil solicitado1: pelo menos um arquivo produziu achados de validação2: a própria ferramenta falhou em pelo menos um arquivo (entrada corrompida, bloqueio, travamento)
A distinção entre códigos 1 e 2 é a que equipes pulam e depois se arrependem. Um PDF corrompido que não abre não é uma falha de validação. Dobre-o no código 1 e um caminhão de scans danificados aparece em seus dashboards como um colapso de conformidade repentino, mandando alguém perseguir uma regressão de padrões que nunca aconteceu, quando a história real é um scanner quebrado a montante
Dois itens a mais pertencem ao contrato. O primeiro é um timeout por arquivo. Um PDF patológico, milhares de páginas com estruturas de objeto profundamente aninhadas, pode segurar um único passe de validação por minutos, e uma janela noturna não tem paciência para isso. Mate o trabalho daquele arquivo no prazo, conte-o como uma falha da ferramenta, e mantenha o lote movendo. O segundo é um diretório de quarentena: mova cada entrada com timeout ou não-abrível para o lado em vez de deixá-la no lugar. Ao longo de alguns meses esse diretório silenciosamente acumula os piores documentos que seus clientes reais enviam, e esse corpus vale mais para teste de release do que qualquer amostra sintética que você poderia escrever à mão
Escolhendo padrões, e por que o nível de conformidade importa
A enumeração TPdfPreflightStandard cobre as famílias que surgem na prática: ppsPdfA para conformidade de arquivo ISO 19005, ppsPdfUa para acessibilidade ISO 14289, ppsPdfX para troca de impressão, mais ppsPdfE, ppsPdfR e ppsPdfVT para trabalho de engenharia, raster e dados variáveis. Dentro de uma família o motor lê o nível de conformidade que o documento reivindica e o relata por padrão no ConformanceName do resultado. Nomear a família raramente é suficiente, porque o nível é onde a diferença real vive. PDF/A-2b promete reprodutibilidade visual e nada mais. PDF/A-3a adiciona uma exigência de marcação de estrutura lógica e permite arquivos fonte incorporados, que é uma barra muito mais difícil de limpar para material escaneado que não tem árvore de tags nenhuma. Erre isso em qualquer direção e o lote mente para você. Se sua política de retenção realmente quer PDF/A-2b mas você reprova arquivos por tags de estrutura faltantes, o relatório se enche de achados que ninguém nunca corrigirá. Aceite qualquer rótulo PDF/A sem verificar o nível e você aprova documentos que atendem a uma barra mais fraca do que prometeu. Mandatos de acessibilidade de compradores governamentais cada vez mais empilham PDF/UA em cima de tudo isso, o que não adiciona custo à execução porque BuildPdfPreflightReport (da unit FPdfPreflightReport) recebe um conjunto de padrões:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Uma chamada avalia ambos os padrões e entrega de volta um único registro de relatório consolidado
Por que uma lista de achados vazia não é aprovação
O relatório enumera achados por padrão, e uma lista de questões vazia significa apenas "nenhum problema encontrado nos padrões que realmente rodaram." Essa é uma alegação mais estreita que "o arquivo conforms ao padrão que você se importa," e a lacuna entre os dois é onde preflight em lote silenciosamente apodrece. Um erro de digitação de configuração que derruba ppsPdfA do conjunto produz exatamente a mesma lista de questões vazia que um arquivo genuinamente limpo. Então trate silêncio como suspeito. Percorra Report.Results e afirme duas coisas para cada padrão que você pretendia verificar: que uma entrada de resultado para ele existe de todo, e que sua flag IsCompliant, respaldada por Status = pfsPass, é verdadeira. Um trabalho noturno que equivale "nenhum achado" com "pronto para arquivar" sem nunca confirmar quais padrões foram avaliados é a forma clássica de uma pasta de arquivos não conformes navegar por meses, até que um auditor externo abre um com veraPDF e todo o arquivo entra em questão
Uma segunda armadilha se esconde no que um achado sequer é. Cada TPdfPreflightIssue carrega um Code, uma Category, uma Description e uma Recommendation, e nomeia a regra que foi violada, não uma página ou um objeto. Essa é uma escolha de projeto com consequências para o laço de feedback. O relatório diz à equipe produtora que classe de defeito existe, uma fonte não incorporada ou um identificador XMP faltante, e encontrar o objeto ofensor específico é trabalho da ferramenta de remediação a jusante, não do validador. Construa seus consumidores de relatório contra os valores estáveis de Code, nunca contra o texto legível por humano da descrição, que pode ser reescrito entre releases sem aviso
Arquivos de relatório para máquinas e para a pessoa de plantão
O registro de relatório escreve os mesmos achados em cinco formatos: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile e SaveMarkdownToFile, cada um com uma função ToJson correspondente quando você quer a string na memória em vez de no disco. Resista à vontade de escolher um. Escreva JSON para o pipeline, assim o CI pode anexá-lo ao registro do trabalho e analisar códigos de questão e status por padrão sem raspar texto. Escreva HTML para o humano que é chamado, porque abre em qualquer navegador sem ferramenta nenhuma. Os dois juntos custam uma linha extra por arquivo e poupam seu engenheiro de plantão da pior tarefa única em processamento em lote, que é engenharia reversa de um blob JSON cru às duas da manhã para aprender qual arquivo quebrou. Uma disciplina importa mais que a escolha de formato: derive cada nome de relatório do nome do arquivo de entrada, nunca de um timestamp, ou duas execuções paralelas intercalarão relatórios que você não consegue mais corresponder de volta às suas entradas
Limiares de severidade pertencem à configuração em vez de ao código. Uma anotação sem descrição alternativa é uma falha dura para um portal de submissão PDF/UA e uma nota ignorável para um arquivo interno, mas é o achado idêntico em ambos. Exponha um nível de reprovação por perfil para que a política possa mudar sem uma recompilação, e estampe o nível que estava em vigor no próprio resumo do trabalho. Próximo trimestre ninguém se lembrará sob qual limiar o lote de outubro passado rodou, e o resumo é o único lugar onde essa memória sobrevive
Isolando arquivos para que um PDF ruim não possa afundar o lote
procedure RunPreflightBatch(const InputDir, ReportDir: string;
out FilesWithFindings, ToolFailures: Integer);
var
SR: TSearchRec;
Pdf: TPdf;
Report: TPdfPreflightReport;
begin
FilesWithFindings := 0;
ToolFailures := 0;
if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
try
repeat
Pdf := TPdf.Create(nil); // instância nova por arquivo: sem vazamento de estado
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // falhas de carga são silenciosas, não lançadas
raise EPdfError.Create('Cannot open ' + SR.Name);
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
if Report.TotalIssueCount > 0 then
Inc(FilesWithFindings);
except
on E: Exception do
begin
Inc(ToolFailures); // território de código de saída 2, não um veredito de validação
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
Três escolhas deliberadas vivem naquele loop. Um TPdf fresco por arquivo garante que um documento que corrompe o estado do motor não possa envenenar os arquivos que o seguem. A checagem explícita de Active ganha seu lugar porque Active := True engole erros de carga em vez de lançá-los; solte a guarda e um arquivo truncado deriva para dentro da chamada de validação antes de falhar em algum lugar a jusante com uma mensagem enganosa. O try..except interno vive dentro do escopo por arquivo de propósito, para que uma única exceção incremente o contador de falhas e o loop continue. Você quer relatórios limpos para os 4.999 arquivos bons mesmo quando o arquivo 5.000 está em pedaços. E ambos os formatos de relatório são escritos no disco antes de o veredito ser contabilizado, o que significa que a evidência sobrevive mesmo se um bug depois na lógica de resumo contar errado
O mapeamento de código de saída então colapsa para algumas linhas no arquivo de projeto:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// seguir adiante sai com 0: todo arquivo estava em conformidade
end.
O que o preflight não fará por você
O motor detecta; ele não repara. Um achado sobre uma fonte não incorporada ou um espaço de cor dependente de dispositivo é uma ordem de serviço para quem produz os arquivos, e o validador não tem como corrigi-lo no lugar. Então planeje o laço de feedback deliberadamente. Relatórios têm que pousar onde a equipe produtora realmente os lê, ou os mesmos achados reaparecem toda noite até que alguém finalmente pergunte por que a taxa de conformidade nunca melhora. Também vale a pena verificar cruzadamente uma amostra de vereditos contra um validador independente, veraPDF para PDF/A ou o preflight do Acrobat para PDF/X, antes que um auditor externo os verifique cruzadamente por você. Quando dois motores discordam em um arquivo real de cliente, esse documento não é um incômodo; é exatamente o caso de regressão que faltava no seu teste de release. Guarde-o, nomeie-o, e execute-o em todo build
Um emparelhamento a mais vale a pena conhecer. O mesmo motor de validação dirige as checagens interativas em uma UI de revisão, então este CLI headless e um workbench de revisão de intake de PDF voltado para analista podem compartilhar um único vocabulário de validação em vez de derivar separados ao longo do tempo. E porque [ppsPdfA, ppsPdfUa] avalia acessibilidade no mesmo passe, o lado PDF/UA do lote se alinha limpo com trabalho do lado do visualizador como construir um leitor de PDF acessível em Delphi. Perfis, formatos de relatório e a API de preflight completa estão documentados na página do produto do PDFium Component