Quando PDFXCompliance é configurado como 'X-1a', o HotPDF valida a cor do conteúdo bem no fim do trabalho: o EndDoc chama THotPDF.EnsurePDFX1aContentColorSpaces, que rejeita qualquer conteúdo DeviceRGB, CalRGB ou ICCBased RGB em qualquer lugar de qualquer página e levanta uma exceção que nomeia o recurso infrator. O arquivo nunca é gravado como um documento PDF/X-1a que a gráfica vai devolver
Essa última parte é o ponto. Um arquivo PDF/X-1a com um logo RGB perdido parece perfeitamente bem na tela, passa numa revisão casual e então falha no preflight da gráfica dois dias antes da janela de impressão. Pegar isso no seu build Delphi, com uma mensagem que diz qual página e qual imagem, é muito mais barato que descobrir por um operador de pré-impressão irritado
Por que o PDF/X-1a rejeita conteúdo RGB?
O PDF/X-1a rejeita RGB porque o formato promete que toda cor já está final para uma condição de impressão CMYK específica. A ISO 15930-1:2001 §6.2.3 limita a cor do conteúdo a DeviceCMYK, DeviceGray e colorantes spot, ou seja, espaços Separation e DeviceN cujos alternates são CMYK ou Gray. O OutputIntent GTS_PDFX embutido no arquivo nomeia essa condição de impressão, por exemplo FOGRA39 coated offset, e o fluxo receptor está no direito de mandar os números direto para as chapas sem nenhuma conversão de cor
RGB quebra esse contrato. Um valor RGB descreve uma cor relativa a um display ou a um working space, não uma receita de tinta, então alguém a jusante teria de decidir como separá-la, e o X-1a existe justamente para tirar essa decisão do receptor. A ISO 32000-1 §8.6 agrupa os color spaces do PDF em famílias device, CIE-based e especiais, e os membros RGB aparecem em mais de uma família: DeviceRGB é um espaço device, CalRGB e ICCBased com três componentes são CIE-based, e um espaço Indexed ou Separation pode esconder uma base ou alternate RGB por baixo. Um checker que só procura o operador rg perde a maioria deles. A edição posterior PDF/X-1a:2003 (ISO 15930-4) mantém a mesma regra de cor; note que o valor 'X-1a' do HotPDF grava a identificação PDF/X-1a:2001 no pacote XMP
Se o seu conteúdo realmente é RGB e deve continuar assim, o X-1a é o alvo errado. O HotPDF limita essa checagem ao 'X-1a' apenas, então 'X-3' e 'X-4', que permitem cor baseada em ICC e calibrada, não são afetados
O que o EnsurePDFX1aContentColorSpaces realmente confere
O EnsurePDFX1aContentColorSpaces combina uma flag de hora do desenho com uma varredura recursiva dos recursos de cada página, e roda por último no gate PDF/X. O EndDoc primeiro impõe os requisitos gerais do PDF/X: Title precisa estar definido, Trapped precisa ser 'True', 'False' ou 'Unknown', um OutputIntent GTS_PDFX precisa existir (EnsurePDFXOutputIntent) e toda página precisa de um TrimBox ou ArtBox (EnsurePDFXPageBoxes). Só depois disso a checagem de cores roda
O primeiro caminho é uma flag de documento inteiro. Os setters de cor RGB do HotPDF, o embedding de imagens bitmap e as entradas de cor de anotações registram uso de DeviceRGB enquanto você desenha, o que cobre operadores rg e RG inline que nunca aparecem num dicionário de recursos. O segundo caminho percorre o /Resources de cada página e inspeciona:
- Color spaces nomeados no
/ColorSpace, incluindo a abreviação de imagem inlineRGBcomo sinônimo de DeviceRGB - O
/ColorSpacede todo Image XObject - Form XObjects e tiling Patterns, recursando nos próprios
/Resourcesaninhados - Shading Patterns e entradas no dicionário
/Shadingda página, pelo/ColorSpacedo shading - Espaços base Indexed, alternates Separation e DeviceN, e espaços base de Pattern, seguidos até uma profundidade de aninhamento de 8
Três coisas são rejeitadas: DeviceRGB, CalRGB e espaços ICCBased cujo profile stream declara N = 3. Dicionários de recursos compartilhados entre Form XObjects e patterns são visitados uma vez, então um conjunto de forms com referências mútuas não manda a varredura num loop. O método é público no THotPDF, mas no uso normal você nunca o chama, porque o EndDoc faz isso por você
Desenhando uma página X-1a no Delphi que passa na checagem
Um documento X-1a do HotPDF em conformidade precisa de um OutputIntent CMYK, de um TrimBox em cada página e de cor definida só pelas APIs CMYK, Gray e spot. O esqueleto abaixo usa o profile FOGRA39; o AddPDFXOutputIntent recebe o identificador da condição de saída, uma string de informações, o stream do profile ICC, a contagem de componentes e o espaço alternate
var
Pdf: THotPDF;
Profile: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'brochure-x1a.pdf';
Pdf.Title := 'Spring brochure, press edition';
Pdf.Trapped := 'False';
Pdf.PDFXCompliance := 'X-1a';
Pdf.BeginDoc;
Profile := TFileStream.Create('CoatedFOGRA39.icc', fmOpenRead or fmShareDenyWrite);
try
Pdf.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)',
'Coated FOGRA39 offset', Profile, 4, 'DeviceCMYK');
finally
Profile.Free;
end;
Pdf.CurrentPage.SetTrimBox(0, 0, 612, 792); // obrigatório em toda página
Pdf.CurrentPage.SetCMYKFillColor(0, 0.5, 0.8, 0.1);
Pdf.CurrentPage.Rectangle(72, 600, 468, 120);
Pdf.CurrentPage.Fill;
Pdf.CurrentPage.SetGrayFillColor(0.2); // texto DeviceGray
Pdf.CurrentPage.SetFont('Arial', [], 14);
Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
Pdf.EndDoc; // a checagem de cores roda aqui
finally
Pdf.Free;
end;
end;
Toda chamada baseada em TColor é uma armadilha nesse contexto. SetRGBFillColor, SetRGBStrokeColor e SetRGBColor recebem uma cor GDI e emitem DeviceRGB, então uma rotina auxiliar escrita anos atrás para relatórios voltados à tela contaminará um trabalho de impressão no momento em que for reutilizada. Use SetCMYKFillColor, SetCMYKStrokeColor ou SetCMYKColor com componentes na faixa de 0 a 1, ou os equivalentes Gray
Cores spot e gradientes CMYK
Colorantes spot são permitidos no X-1a desde que o alternate deles seja CMYK ou Gray. O RegisterSeparation constrói o espaço Separation e devolve o nome do recurso dele, e o RegisterAxialGradient escolhe o color space do shading pela quantidade de componentes que você passar, com quatro componentes significando DeviceCMYK
var
SpotCS, Ramp: AnsiString;
begin
// Tinta spot com uma build CMYK aproximada como alternate
SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
[0, 1, 0.8, 0.05]);
Pdf.CurrentPage.SetFillColorSpace(SpotCS);
Pdf.CurrentPage.SetFillColor([1.0]); // tint de 100%
Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
Pdf.CurrentPage.Fill;
// 4 componentes por stop: o shading é DeviceCMYK, não DeviceRGB
Ramp := Pdf.RegisterAxialGradient(72, 300, 540, 300,
[0, 0, 0, 0], [1, 0.6, 0, 0.2]);
Pdf.CurrentPage.SetFillPattern(Ramp);
Pdf.CurrentPage.Rectangle(72, 260, 468, 80);
Pdf.CurrentPage.Fill;
end;
Passe três componentes para a mesma chamada de gradiente e você recebe um shading DeviceRGB, que a varredura de recursos rejeita sob o nome do shading pattern. O mesmo vale para um Separation registrado com 'DeviceRGB' como alternate: o nome spot parece inofensivo, mas a varredura segue o alternate e encontra RGB por baixo. Como tais tintas spot são compostas quando um viewer ou RIP recua para o alternate está coberto no artigo sobre renderizar cores spot Separation e DeviceN no Delphi
Imagens raster precisam da mesma disciplina. O HotPDF fixa bitmaps comprimidos com Flate em DeviceRGB, enquanto um JPEG embutido com icJpeg mantém a própria contagem de componentes, então um JPEG CMYK de quatro componentes ou um JPEG em tons de cinza está bom e um JPEG RGB comum não está. Converta a arte para o profile de impressão no seu pipeline de imagem antes de ela chegar ao PDF
Por que o /DefaultRGB não salva um arquivo PDF/X-1a?
O /DefaultRGB não ajuda no X-1a porque a regra é sobre o que a impressora recebe, não sobre se o RGB é caracterizado. Para PDF/A, o HotPDF deixa o RegisterDefaultColorSpace('DefaultRGB', ...) absorver uso de DeviceRGB: uma vez que um Default color space mapeia DeviceRGB para um profile ICC, o conteúdo é independente de dispositivo e a checagem de consistência do PDF/A o aceita. Desenvolvedores que já passaram por esse fluxo tendem a tentar a mesma correção aqui
O EnsurePDFX1aContentColorSpaces ignora deliberadamente esse mapeamento. Um valor RGB caracterizado continua sendo RGB, e o OutputIntent do X-1a é uma condição de impressão CMYK, então alguém ainda teria de convertê-lo. Se você lembra de uma diferença entre os dois perfis de arquivamento e impressão, que seja esta: o PDF/A pergunta se uma cor é inequívoca, o PDF/X-1a pergunta se ela já é tinta
Ordem de desenho, e o page setup que não conta
Como a checagem roda no EndDoc sobre conteúdo já escrito, a ordem em que você configura as coisas não importa. RGB desenhado antes de o PDFXCompliance ser atribuído, ou antes de o OutputIntent ser registrado, ainda é pegado. Isso importa para código que constrói páginas em rotinas compartilhadas e decide o output profile tarde, às vezes a partir de uma configuração de usuário escolhida depois de metade do documento existir
Pdf.BeginDoc;
// ... OutputIntent e TrimBox como antes ...
Pdf.CurrentPage.SetRGBFillColor(clRed); // helper legado, emite DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;
Pdf.PDFXCompliance := 'X-1a'; // decidido depois de desenhar
Pdf.EndDoc; // ainda levanta: DeviceRGB registrado
Uma flag de hora do desenho tem uma armadilha que vale conhecer se você construir uma checagem similar por conta própria. Toda página nova é inicializada com um graphics state preto via SetRGBColor(0), que emite um par 0 0 0 rg e 0 0 0 RG, e se aquele boilerplate de page setup pudesse setar a flag DeviceRGB, todo documento pareceria contaminado por RGB, tanto para essa checagem quanto para a checagem de consistência de cores do PDF/A. O HotPDF salva e restaura a flag em torno dessa inicialização, então só a cor que você realmente desenha conta
Como encontrar o conteúdo RGB quando o EndDoc levanta?
Leia a localização no início da mensagem de exceção, porque ela diz qual dos dois caminhos encontrou o problema. A mensagem tem a forma PDFXCompliance 'X-1a': <where> uses <what>, seguida da referência ISO 15930-1 e do conselho de converter para CMYK ou Gray ou trocar para X-3 ou X-4. O validador levanta um Exception simples na primeira violação, então faça match no texto em vez de numa classe de exceção
document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing)significa que a flag de hora do desenho disparou. Não há número de página, porque a flag é de documento inteiro e é conferida antes da varredura. Procure no seu código de desenho chamadasSetRGB*, bitmaps comprimidos com Flate, JPEGs RGB e cores de anotaçãoTColorpage 3 image XObject /Im2 uses DeviceRGB contentoupage 1 shading Pattern /P1 uses DeviceRGB contentvem da varredura de recursos e aponta para uma chave de recurso exatauses an ICCBased RGB colour space (N=3)depois de uma localizaçãoColorSpace /...é o resultado típico de registrar um profile sRGB comRegisterICCProfilee selecioná-lo viaSetFillColorSpace, um caminho que a flag de hora do desenho não vê- Localizações aninhadas se encadeiam, por exemplo
page 2 Form XObject /Fm1 ColorSpace /CS1, então você pode seguir o caminho da página até o recurso que carrega o espaço RGB
try
Pdf.EndDoc;
except
on E: Exception do
begin
if Pos('X-1a', E.Message) > 0 then
LogPrepressFailure(Pdf.FileName, E.Message); // seu próprio logger
raise;
end;
end;
Como só a primeira violação é reportada, consertar um trabalho com várias fontes de RGB é iterativo: conserte, recompile, leia a próxima mensagem. Para trabalhos em lote vale registrar a mensagem completa por arquivo e manter uma lista curta de infratores conhecidos, como um bitmap de logo ou um helper de gráficos que ainda recebe TColor
Onde o gate RGB se encaixa no seu preflight
O EnsurePDFX1aContentColorSpaces é um gate RGB focado que roda dentro do seu build, e ele combina com um preflight PDF/X-1a completo em vez de substituir um. Ele cobre as famílias RGB, pela varredura de recursos a partir do /Resources de cada página e pela flag de hora do desenho que os setters de cor do HotPDF mantêm para operadores inline rg e RG; color spaces aninhados são seguidos até a profundidade 8 como guarda contra ciclos malformados, e a primeira violação é reportada por EndDoc. Outras famílias de cor como Lab ou CalGray são assunto do preflight completo, então rode o arquivo final por um validador externo como parte do seu build para ter o veredito completo de conformidade. O quadro mais amplo da saída de conformidade está em o guia de saída PDF/A, PDF/X e PDF/UA com o HotPDF, e ligar um validador a um pipeline não assistido está coberto em automatizar checagens de preflight PDF no Delphi
O HotPDF é um componente PDF VCL nativo para Delphi e C++Builder com saída PDF/X, PDF/A e PDF/UA, APIs de cor CMYK e spot e suporte a OutputIntent ICC. Detalhes, licenciamento e o download de avaliação estão na página do produto HotPDF Delphi PDF component