O veraPDF sinaliza a ISO 19005-1 6.2.3.3 num arquivo que nunca desenha RGB, chama um re f solto de não calibrado, ou reprova a página 2 enquanto uma página 1 idêntica passa. O PDF/A julga essas regras pelo que cada página pinta, então o validador de PDF/A do PDFium Component para Delphi rastreia cada página através de forms, patterns, glifos e anotações antes de decidir sobre cor e transparência
O que 6.2.3.3 e 6.2.4.3 realmente exigem
A ISO 19005-1 6.2.3.3 comprime três regras independentes num único parágrafo. Um arquivo em conformidade pode usar DeviceRGB ou DeviceCMYK, mas não deve usar os dois; se qualquer espaço de cores não calibrado for usado, o arquivo deve conter um OutputIntent de PDF/A-1; e o DeviceRGB só é permitido com uma intent cujo perfil é RGB, o DeviceCMYK só com uma CMYK. O DeviceGray fica fora dessa lista porque a cláusula segue explicando a conversão de cinza sob qualquer uma das intents. A intent em si é opcional: a 6.2.2 diz que um arquivo pode especificar uma, e só conta uma entrada OutputIntents com /S /GTS_PDFA1 e um stream de perfil ICC válido em /DestOutputProfile. Então um verificador que exige uma intent de todo arquivo rejeita documentos em conformidade que nunca pintam cor de dispositivo. A Parte 1 não menciona DefaultRGB nem DefaultCMYK, mas uma cor remapeada através deles é renderizada num espaço baseado em CIE (ISO 32000-1 §8.6.5.6), o que satisfaz a exigência de independência de dispositivo da 6.2.3.1; a suíte de testes do veraPDF para a 6.2.3.3 a trata como calibrada, e o PDFium Component lê a cláusula do mesmo jeito. A proibição de mistura fica à parte: um arquivo PDF/A-1 que pinta RGB e CMYK falha mesmo quando entradas Default calibraram ambos. A ISO 19005-2 6.2.4.3 derruba essa proibição e calibra cada espaço nos termos próprios dele, como a coluna da direita abaixo mostra, então o PDFium Component roda uma varredura para cada parte e aplica a checagem de mistura só a declarações PDF/A-1
| Espaço de cores de dispositivo pintado | PDF/A-1 (ISO 19005-1 6.2.3.3) | PDF/A-2 e posteriores (ISO 19005-2 6.2.4.3) |
|---|---|---|
| DeviceRGB | DefaultRGB em escopo ou uma intent RGB; não ao lado de DeviceCMYK | DefaultRGB em escopo, ou um OutputIntent RGB |
| DeviceCMYK | DefaultCMYK em escopo ou uma intent CMYK; não ao lado de DeviceRGB | DefaultCMYK em escopo, ou um OutputIntent CMYK |
| DeviceGray | DefaultGray em escopo, ou qualquer OutputIntent PDF/A-1 | DefaultGray em escopo, ou qualquer OutputIntent PDF/A |
O que conta como usar um espaço de cores?
Usar um espaço de cores significa pintar com ele num caminho que um leitor vai renderizar. Declará-lo não basta. A suíte do veraPDF faz o ponto com um arquivo aprovado na 6.2.3.3: um Form XObject nomeado define CMYK com K e k, fica no dicionário de recursos da página, nunca é referenciado do conteúdo da página, e o OutputIntent do arquivo é RGB. Uma busca por nomes de cores de dispositivo no arquivo inteiro reprova esse arquivo, e perde um que pinta só DeviceRGB sob uma intent CMYK, onde nenhum par de nomes conflita. O PDFium Component percorre o grafo de desenho em vez disso, construído sobre o scanner de preflight PDF/A byte a byte dele. De cada página, a varredura conta a cor pintada por:
- os content streams da página, e todo Form XObject que eles invocam com
Do, forms aninhados incluídos - um pattern selecionado com
scnouSCN, lido com asResourcesdo próprio pattern - os
CharProcsde uma fonte Type 3 selecionada comTf - image XObjects e imagens inline, pelo espaço de cores que o dicionário próprio deles nomeia
- appearance streams de anotações, pintados por cima da página; quando o
/AP /Nde um widget é um dicionário de estados, todo estado conta, porque alternar o campo pinta aquele que o/ASnão selecionou
A metade mais sutil da alcançabilidade é a cor que o conteúdo jamais nomeia. q 0 0 10 10 re f Q não contém operador de cor nenhum. Ainda pinta. O espaço de cores inicial do estado gráfico é DeviceGray (ISO 32000-1 Table 52), então o fill é preto DeviceGray, não calibrado sob toda parte, a menos que exista um OutputIntent de PDF/A ou um DefaultGray. A varredura portanto rastreia se algo já escolheu uma cor: rg, k, g e as formas de stroke deles selecionam um espaço de dispositivo, cs e CS selecionam o que nomeiam, e um operador de fill, stroke ou texto alcançado antes de qualquer escolha registra DeviceGray. /CS0 cs 1 0 0 /P0 scn já escolheu um espaço, então aquele scn não é cinza
Imagens inline trazem o espaço de cores delas e as amostras cruas delas para dentro do próprio content stream, e um scanner precisa ler as duas com cuidado. O /CS pode ser uma abreviação (G, RGB, CMYK, I) ou um nome de recurso como um que resolve para [/Indexed /DeviceRGB 255 13 0 R], cuja base conta; e os bytes entre ID e EI são binário arbitrário, então o EI só termina a imagem entre whitespace, senão um rg dentro das amostras comprimidas vira um operador fantasma
% página 1: DefaultRGB em escopo, e um XObject que nada invoca
4 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R
/Resources << /ColorSpace << /DefaultRGB 9 0 R >>
/XObject << /Fm0 6 0 R >> >> >>
endobj
% página 2: o mesmo content stream, sem subdicionário ColorSpace
7 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R /Resources << >> >>
endobj
5 0 obj << /Length 46 >> stream
1 0 0 rg 72 72 200 100 re f
q 0 0 10 10 re f Q
endstream
endobj
6 0 obj << /Type /XObject /Subtype /Form /BBox [0 0 50 50] /Length 24 >> stream
0 0 0 1 k 0 0 50 50 re f
endstream
endobj
9 0 obj [/ICCBased 10 0 R]
endobj
Cada sintoma da abertura se depreende deste fragmento. O /Fm0 nunca encontra um Do, então o k dele é irrelevante. O re f solto nas duas páginas é DeviceGray sem nada para calibrá-lo. O rg da página 1 está coberto pelo DefaultRGB nos recursos com que a página 1 pinta; a página 2 roda o content stream idêntico contra recursos que não têm o mapeamento, então os mesmos bytes ficam não calibrados lá. Esperado para uma declaração PDF/A-1b: pvaiMissingOutputIntent com pvaiMissingIccProfile, movidos pelo RGB da página 2 e pelo cinza das duas páginas. O form não contribui com nada; invocado, o CMYK dele somaria a proibição de mistura do PDF/A-1 por cima
Espaços de cores Default pertencem ao dicionário de recursos que pinta
Os mapeamentos Default são procurados no subdicionário ColorSpace do dicionário de recursos atual (ISO 32000-1 §8.6.5.6), o que os torna exatamente tão locais quanto os recursos em si; um documento não tem default global. Um image XObject não tem Resources próprio, então uma imagem cujo dicionário diz /ColorSpace /DeviceCMYK é calibrada pelo DefaultCMYK de qualquer página ou form que a desenhe, e a mesma imagem desenhada de dois lugares pode estar calibrada num e não no outro. Form XObjects, tiling patterns e procedimentos de glifo Type 3 carregam as Resources próprias deles, então o DefaultCMYK de uma página não alcança o interior deles, enquanto um mapeamento no dicionário próprio deles alcança. O PDFium Component dá escopo aos mapeamentos do mesmo jeito. O veredito volta como um record TPdfAValidationResult: a Conformance detectada mais um conjunto Issues. Isso basta para triar uma pasta
uses
System.SysUtils, System.Classes, System.TypInfo, FPdfPdfa;
procedure ReportColorFindings(const FileName: string; Log: TStrings);
const
ColorRules: TPdfAValidationIssues = [pvaiMissingOutputIntent,
pvaiMissingIccProfile, pvaiMixedDeviceColorSpaces, pvaiBlendSpaceUndefined];
var
Source: TFileStream;
Res: TPdfAValidationResult;
Found: TPdfAValidationIssues;
Issue: TPdfAValidationIssue;
begin
Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfACompliance(Source); // lê a partir da posição 0
finally
Source.Free;
end;
Log.Add(ExtractFileName(FileName) + ': ' +
GetEnumName(TypeInfo(TPdfAConformance), Ord(Res.Conformance)));
Found := Res.Issues * ColorRules;
for Issue in Found do
case Issue of
pvaiMissingOutputIntent:
Log.Add(' reachable uncalibrated device color, no PDF/A OutputIntent');
pvaiMissingIccProfile:
Log.Add(' no OutputIntent with a DestOutputProfile ICC stream');
pvaiMixedDeviceColorSpaces:
Log.Add(' RGB/CMYK use disagrees with the intent, or PDF/A-1 paints both');
pvaiBlendSpaceUndefined:
Log.Add(' transparent page without a blending color space');
end;
if Found = [] then
Log.Add(' device color and blending rules satisfied');
end;
O pvaiMissingOutputIntent e o pvaiMissingIccProfile aparecem juntos quando a varredura alcança cor de dispositivo não calibrada e não existe OutputIntent de PDF/A. O pvaiMissingIccProfile sozinho significa que uma intent existe mas não carrega perfil, o que a 6.2.2 não aceita como intent de PDF/A. O pvaiMixedDeviceColorSpaces cobre tanto a proibição de mistura do PDF/A-1 quanto uma divergência de componentes: DeviceRGB não calibrado sob uma intent de quatro componentes, ou DeviceCMYK sob uma de três. O conjunto nomeia a regra em vez da página, então num documento longo ou mesclado, compare quais páginas declaram entradas Default e quais não
Quando uma página transparente precisa de um espaço de mesclagem próprio?
Uma página transparente só precisa de um espaço de cores de mesclagem próprio quando o documento não tem OutputIntent de PDF/A. A ISO 19005-2 6.2.10 (6.2.9 no PDF/A-4) faz da intent o espaço de cores de mesclagem default no sentido da ISO 32000-1 §11.3.4; sem uma intent, toda página que contém transparência deve carregar um Group cujo dicionário de atributos (ISO 32000-1 Table 147) inclua CS. Esse CS também é cobrado pela 6.2.4, então um grupo de página de /DeviceCMYK sob uma intent sRGB é reportado como se fosse ausente, e o PDFium Component levanta pvaiBlendSpaceUndefined para ambos; CalRGB ou um espaço de grupo ICCBased não tem essa dependência. O PDF/A-1 não chega a essa questão, porque a ISO 19005-1 6.4 elimina a transparência por completo
A parte difícil é a expressão contém transparência. O Anexo A da ISO 19005-2 é normativo e testa da página para fora; uma contagem no arquivo inteiro de chaves /SMask ou valores /ca responde outra pergunta. Buscas por bytes também colidem com sintaxe de transparência em outro lugar: /S /Trans, a ação de transição proibida, é prefixo de todo grupo /S /Transparency, e /Screen é tanto um subtipo de anotação proibido quanto um modo de mesclagem padrão, então um nome não significa nada até ser pareado com a chave dele. Seguindo o Anexo A, o PDFium Component trata uma página como transparente quando qualquer um destes é alcançado a partir dela:
- um ExtGState que o conteúdo seleciona com
gse que tem um dicionário de soft mask, umcaouCAabaixo de 1, ou um modo de mesclagem diferente deNormal; um declarado mas jamais selecionado não conta - um Form XObject invocado com
Docujo/Grouptem/S /Transparency, ou cujo próprio conteúdo atende uma destas condições; um tiling pattern é julgado como um form - um image XObject com um stream
SMaskou um valorSMaskInDataacima de 0 - uma descrição de glifo de uma fonte Type 3 com que a página mostra texto, julgada como um form
- um appearance stream de anotação, todo estado de widget incluído, e um modo de mesclagem no próprio dicionário da anotação
Os grupos também retroalimentam a regra de cor de dispositivo, e aqui o corpus nos corrigiu. A suíte do veraPDF para a 6.2.4.3 guarda arquivos PDF/A-4 sem OutputIntent algum que pintam RGB de dispositivo e devem passar, porque o /Group de cada página nomeia um CS calibrado. Nós assumimos que só grupos isolados podiam calibrar, já que um grupo não isolado compõe sobre o fundo dele, e isso estava errado para grupos de página: a Table 147 nota que uma página imposta diretamente sobre o meio de saída é efetivamente isolada independente do I, então o CS dela é respeitado. Num Form XObject ou numa aparência de anotação a flag decide mesmo; a suíte tem um arquivo de aprovação e um de reprovação que diferem só no /I true. O DeviceGray não recebe calibração de grupo nenhum, porque a 6.2.4.3 pede DefaultGray ou uma intent, e um grupo cujo CS é um espaço de dispositivo precisa ser calibrado ele mesmo. O PDFium Component portanto deixa um grupo de página calibrar seja lá o que o /I dele disser, deixa um grupo de form ou de aparência calibrar só quando isolado, mantém o cinza fora da calibração por grupo, e ainda checa um espaço de dispositivo que um grupo nomeie
Um OutputIntent em vez de um grupo em cada página
Se o seu próprio código escreve o acervo, comece com um OutputIntent de PDF/A no nível do documento. Uma entrada de catálogo calibra o DeviceGray em todo lugar, cobre o espaço de dispositivo correspondente em cada página, e é o espaço de mesclagem default de toda página transparente. Grupos de página e entradas Default têm escopo de um dicionário de recursos cada, e a página que uma mesclagem posterior adiciona sem eles é a que reprova. O PDF 2.0 adiciona OutputIntents ao objeto de página (ISO 32000-2 §7.7.3.3 e §14.11.5); o PDFium Component aceita essa intent de nível de página para o PDF/A-4 quando o catálogo não tem nenhuma, enquanto as partes 1 a 3, construídas sobre PDF 1.4 a 1.7, olham só para o catálogo. Para conteúdo RGB, o SaveAsPdfAToStream adiciona uma intent de catálogo construída sobre o perfil ICC sRGB embutido do writer de PDF/A, e validar a saída mostra se o conteúdo era mesmo só RGB
uses
System.SysUtils, System.Classes, PDFium, FPdfPdfa;
function ArchiveWithSrgbIntent(const InFile, OutFile: string;
out Remaining: TPdfAValidationIssues): Boolean;
var
Pdf: TPdf;
Opts: TPdfASaveOptions;
Output: TMemoryStream;
Check: TPdfAValidationResult;
begin
Result := False;
Remaining := [];
Pdf := TPdf.Create(nil);
Output := TMemoryStream.Create;
try
Pdf.FileName := InFile;
Pdf.Active := True;
if not Pdf.Active then
Exit; // o arquivo não pôde ser aberto
Opts := TPdfASaveOptions.Default;
Opts.Conformance := pac2b; // o PDF/A-1 proibiria a transparência de cara
// IccProfileData permanece vazio: o perfil sRGB IEC61966-2.1 embutido é usado
if not Pdf.SaveAsPdfAToStream(Output, Opts) then
Exit;
Check := ValidatePdfACompliance(Output);
Remaining := Check.Issues;
// Esperado para conteúdo só RGB: pvaiMissingOutputIntent e
// pvaiBlendSpaceUndefined somem. pvaiMixedDeviceColorSpaces agora significa
// um caminho alcançável pinta DeviceCMYK que nenhum DefaultCMYK calibra
if Check.IsCompliant then
begin
Output.SaveToFile(OutFile);
Result := True;
end;
finally
Output.Free;
Pdf.Free;
end;
end;
Um pvaiMixedDeviceColorSpaces que sobrevive à intent sRGB é a varredura reportando DeviceCMYK num caminho alcançável, e esse documento precisa de um mapeamento DefaultCMYK nas páginas que o pintam, ou de um perfil de saída CMYK, antes que o PDFium Component o aceite como PDF/A-2b