O PDFium Component para Delphi valida documentos PDF/X prontos para impressão por meio do método TPdf.ValidatePdfX, que implementa a verificação da norma ISO 15930 em duas camadas: oito verificações de conteúdo no nível de bytes (como compressão LZW proibida, JavaScript, campos de formulário, referências OPI, TrimBox ausente, chave Trapped não definida e outros) mais uma varredura do modelo de objetos do PDFium que usa FPDFFont_GetIsEmbedded para verificar a incorporação de fontes em cada objeto de texto de cada página. O resultado é um registro TPdfXValidationResult que indica o nível de conformidade detectado e lista cada violação como uma enumeração tipada, de modo que seu aplicativo Delphi possa informar a um cliente exatamente por que um arquivo será rejeitado na gráfica antes mesmo que alguém prepare uma matriz
Se você já enviou um trabalho para uma gráfica comercial e o recebeu de volta com uma rejeição de uma única linha — "sem TrimBox", "fontes não incorporadas", "Trapped não definido" —, você sabe o custo de descobrir isso tarde demais. O PDF/X é a contraparte de pré-impressão do PDF/A: enquanto o PDF/A de arquivamento garante que um documento seja renderizado de forma idêntica daqui a décadas, o PDF/X garante que um documento seja separado, gravado e cortado de forma idêntica no processador de imagens (RIP) de outra pessoa amanhã de manhã. Os dois padrões compartilham recursos (identificação XMP, OutputIntents, perfis ICC incorporados), mas respondem a perguntas diferentes, razão pela qual o componente fornece validadores separados para cada um — o lado do PDF/A é abordado em validação de pré-voo PDF/A com o PDFium Component
O que a ISO 15930 realmente exige de um PDF pronto para impressão?
A ISO 15930 existe para tornar possível a troca às cegas (blind exchange): um designer entrega um arquivo a uma gráfica com a qual nunca conversou, e a gráfica pode produzir o resultado correto sem telefonemas, e-mails sobre fontes ausentes ou imagens vinculadas que ficaram esquecidas no laptop do designer. Cada regra no padrão serve a esse objetivo. As fontes devem ser incorporadas porque não se pode presumir que o RIP receptor as possua. Referências externas são proibidas porque o arquivo deve ser completo em si mesmo. Recursos interativos são proibidos porque a tinta não tem um manipulador de clique (onclick)
O PDFium Component reconhece três famílias de conformidade e os relata através da enumeração TPdfXConformance no resultado de validação: pxc1a para PDF/X-1a:2001 (ISO 15930-1, a linha de base estrita de CMYK mais cores especiais em PDF 1.3/1.4), pxc3 para PDF/X-3:2002 (ISO 15930-3, que admite cores RGB, Lab e cores gerenciadas por ICC), e pxc4 para PDF/X-4:2010 (ISO 15930-7, que finalmente permite transparência ativa e camadas em uma base de PDF 1.6). Um arquivo que não carrega nenhuma identificação PDF/X retorna como pxcNone, o que por si sob é uma resposta útil: o documento nunca alegou estar pronto para impressão, e tudo o mais que o validador relata explica o que seria necessário para chegar lá
As proibições fazem sentido quando você pensa como um fornecedor de RIP. O /LZWDecode é proibido em todas as variantes de PDF/X para que um consumidor em conformidade nunca dependa de um filtro com histórico de compatibilidade e licenciamento; o Flate faz o mesmo trabalho sem essa bagagem. Dicionários de JavaScript, campos AcroForm e ações adicionais /AA são proibidos porque um arquivo de impressão deve ser uma descrição fixa de marcações no papel — qualquer coisa que possa alterar a aparência no momento da abertura quebra a garantia de que o que foi provado é o que será impresso. Marcadores de posição OPI (Open Prepress Interface) são proibidos porque são, por design, referências a imagens de alta resolução armazenadas em outro lugar, e "outro lugar" é exatamente o que a troca às cegas proíbe
Por que as gráficas rejeitam PDFs sem um TrimBox?
O TrimBox é a página finalizada — o retângulo que resta após o corte da guilhotina. O MediaBox, que toda página PDF possui, é meramente a folha física: inclui sangria (bleed), marcas de corte, alvos de registro e barras de cores. Softwares de imposição posicionam as páginas em uma folha de impressão através de seus TrimBoxes; sem ele, o operador precisa adivinhar onde seu cartão de visitas realmente termina, e uma suposição incorreta corta sua sangria ou deixa uma fresta branca em uma das bordas. É por isso que a ISO 15930 exige um TrimBox (or an ArtBox) em cada página, e por que o ValidatePdfX gera o erro pvxiMissingTrimBox quando nenhuma chave /TrimBox é encontrada em qualquer página do documento
A chave /Trapped responde a uma questão de produção diferente. O trapping (sobreposição de cores) é a técnica de pré-impressão de sobrepor levemente cores adjacentes para que um pequeno desalinhamento na prensa não abra espaços brancos entre elas. A gráfica precisa saber se esse trabalho já foi feito: aplicar trapping a um arquivo que já o possui duplica as sobreposições, e ignorá-lo em um arquivo sem trapping arrisca aberturas visíveis. O PDF/X, portanto, exige que o dicionário Info declare /Trapped /True ou /Trapped /False explicitamente — uma chave ausente ou configurada como /Unknown força uma inspeção manual do arquivo, que é precisamente o tipo de conversa que a troca às cegas pretendia eliminar. O componente sinaliza isso como pvxiTrappedNotSet
Executando a validação em duas camadas com o TPdf.ValidatePdfX
O método TPdf.ValidatePdfX não recebe argumentos e retorna um registro TPdfXValidationResult com três membros: Conformance (a variante PDF/X detectada), Issues (um conjunto Pascal de valores TPdfXValidationIssue) e um utilitário IsCompliant. Internamente, ele serializa o documento carregado para um fluxo de memória, executa o inspetor em nível de bytes sobre ele e, em seguida, percorre o modelo de objetos do PDFium para a verificação de incorporação por fonte. Um portal mínimo de validação de pré-voo (preflight) se parece com isto:
uses PDFium, FPdfPdfx;
procedure CheckPrintReadiness(const FileName: string);
var
Pdf: TPdf;
Res: TPdfXValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
Res := Pdf.ValidatePdfX;
Writeln('Detected conformance: ',
Ord(Res.Conformance)); // pxc1a, pxc3, pxc4, pxcNone...
if Res.IsCompliant then
Writeln('PDF/X checks passed')
else
begin
if pvxiMissingTrimBox in Res.Issues then
Writeln('REJECT: no /TrimBox on the pages');
if pvxiTrappedNotSet in Res.Issues then
Writeln('REJECT: /Trapped missing or /Unknown');
if pvxiPdfiumFontNotEmbedded in Res.Issues then
Writeln('REJECT: a page uses a non-embedded font');
if pvxiLzwForbidden in Res.Issues then
Writeln('REJECT: LZWDecode filter present');
end;
finally
Pdf.Free;
end;
end;
Como o Issues é um conjunto Pascal comum, você pode dividi-lo conforme a necessidade do seu fluxo de trabalho: trate problemas estruturais como rejeições definitivas, trate o pvxiMissingTitle (uma recomendação no padrão, não uma obrigação rígida) como um aviso e registre o restante em logs. O mesmo tipo de registro também alimenta o gerador de relatórios do componente, portanto, se você preferir emitir um documento legível por humanos em vez de ramificar em enumerações, o padrão descrito em construção de um relatório de pré-voo em lote por CLI com o PDFium Component se aplica ao PDF/X sem alterações
O que a camada em nível de bytes detecta — e o que ela perde
A camada em nível de bytes é uma varredura de tokens sobre os bytes estruturais do documento com os corpos dos fluxos limpos, de modo que um JPEG que por acaso contenha o padrão de bytes /JavaScript não possa acionar um falso positivo. Além das verificações de marcadores (como XMP pdfxid:GTS_PDFXVersion, OutputIntent com um perfil ICC incorporado, trailer /ID e a proibição de criptografia), a passagem de conteúdo adiciona oito verificações, cada uma com seu próprio valor de enumeração:
pvxiLzwForbidden— um filtro/LZWDecodeaparece em qualquer parte do arquivo (proibido em todas as variantes de PDF/X)pvxiJavaScriptForbidden— uma ação ou árvore de nomes de/JavaScriptestá presentepvxiFormFieldsForbidden— um dicionário/AcroFormou entrada/XFAexistepvxiAdditionalActions— um dicionário de ações adicionais/AAestá presentepvxiEmbeddedFilesForbidden—/EmbeddedFilesou uma anotação/FileAttachmentestá presentepvxiOpiForbidden— uma entrada/OPIou/Alternatesfaz referência a conteúdo de imagem substituívelpvxiMissingTrimBox— nenhum/TrimBoxfoi encontrado em qualquer páginapvxiTrappedNotSet— o/Trappedestá ausente ou definido como/Unknown
A varredura de bytes é rápida e não precisa de um mecanismo de renderização, mas possui um ponto cego inerente em relação às fontes: nesse nível, o inspetor pode aplicar apenas uma heurística bruta — ele sinaliza um documento quando não encontra nenhum programa de fonte incorporado. Um arquivo com nove fontes incorporadas e uma fonte de sistema adicionada no meio parecerá correto para uma varredura de bytes. Essa lacuna isolada é o motivo de a segunda camada existir
Incorporação por fonte por meio do modelo de objetos do PDFium
A camada do modelo de objetos do PDFium Component responde à questão das fontes com precisão. Após a passagem pelo nível de bytes, o TPdf.ValidatePdfX itera em cada página, solicita ao FPDFPage_CountObjects a lista de objetos e, para cada objeto de texto, resolve o identificador de fonte por meio de FPDFTextObj_GetFont e consulta o FPDFFont_GetIsEmbedded. Uma única fonte não incorporada em qualquer parte do documento adiciona pvxiPdfiumFontNotEmbedded ao conjunto de problemas. O percurso realiza um curto-circuito em dois níveis — ele para de analisar objetos em uma página e para de carregar páginas seguintes no instante em que o problema é confirmado —, de modo que, em um catálogo com 300 páginas violando as regras, o veredicto costuma chegar logo na página um
Duas observações sobre limites que vale a pena conhecer. Primeiro, esta camada precisa da biblioteca PDFium carregada e exige compilações que exportem a função FPDFFont_GetIsEmbedded; quando a exportação está ausente, a verificação é ignorada em vez de falhar, de modo que uma DLL mais antiga nunca produza rejeições fantasmas. Segundo, a verificação responde "incorporada ou não" e nada mais — ela não faz distinção entre incorporação completa ou subsetting (subconjunto), nem inspeciona a cobertura de glifos. Quando um arquivo falha e você precisa saber qual fonte em qual página, as técnicas de enumeração em análise de propriedades de fontes PDF com o PDFium no Delphi continuam exatamente onde o booleano do validador para
Validando fluxos sem carregar um documento — ou a DLL
O inspetor em nível de bytes também é exposto como uma função independente, ValidatePdfXCompliance(Source: TStream) na unidade FPdfPdfx, e é Pascal puro de objetos sem dependência da DLL do PDFium. Isso o torna implantável em locais onde um mecanismo de renderização não é bem-vindo: um portal leve de uploads em um servidor web, um trabalho de CI que analisa ilustrações geradas ou um serviço Lazarus em uma plataforma na qual você prefere não distribuir binários nativos. Alimente-o com qualquer fluxo que suporte busca (seekable stream):
uses Classes, FPdfPdfx;
function QuickPdfXGate(const FileName: string): Boolean;
var
Fs: TFileStream;
Res: TPdfXValidationResult;
begin
Fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfXCompliance(Fs);
Result := Res.IsCompliant and (Res.Conformance <> pxcNone);
finally
Fs.Free;
end;
end;
A relação de troca é explícita: o caminho independente executa as verificações de marcadores e todas as oito verificações de conteúdo, mas não a camada PDFium por fonte, portanto seu veredicto de fonte cai na heurística bruta. Uma arquitetura sensata usa o ValidatePdfXCompliance como o portal inicial de baixo custo e reserva o TPdf.ValidatePdfX completo para arquivos que passarem por ele
Onde este validador termina e um preflight completo começa
A honestidade importa em ferramentas de preflight (pré-voo), portanto aqui está o limite. O ValidatePdfX verifica marcadores de identificação, proibições estruturais, chaves de geometria de página, a declaração Trapped e a incorporação de fontes até objetos de texto individuais. Ele não mede a cobertura total de tinta, não valida se cada espaço de cores é legal para a variante alegada (como a regra de apenas CMYK do X-1a, por exemplo), não verifica a resolução da imagem contra a retícula de impressão, nem avalia o comportamento de sobreimpressão (overprint) e achatamento de transparência — estes necessitam de um mecanismo de preflight com gerenciamento de cores, e a própria documentação da unidade aconselha emparelhá-lo com um para certificação final. O que a verificação em duas camadas oferece a você são os 80% de rejeições que são estruturais e detectáveis antecipadamente, detectados em milissegundos dentro do seu próprio código Delphi, em vez de constar no e-mail de amanhã da gráfica
Ambas as camadas de validação, as APIs de injeção de marcadores PDF/X para produção de saídas em conformidade, e os validadores de PDF/A, PDF/UA, PDF/E e PDF/VT que compartilham da mesma arquitetura são fornecidos no PDFium Component para Delphi e C++Builder — um único componente, da renderização ao gatekeeping de pré-impressão