Articolo tecnico

Controllo colori PDF/X-1a di HotPDF: intercettare RGB

Quando PDFXCompliance è impostato a 'X-1a', HotPDF valida il colore dei contenuti proprio alla fine del lavoro: EndDoc chiama THotPDF.EnsurePDFX1aContentColorSpaces, che rifiuta qualunque contenuto DeviceRGB, CalRGB o ICCBased RGB in qualunque punto di qualunque pagina e solleva un'eccezione che nomina la risorsa colpevole. Il file non viene mai scritto come documento PDF/X-1a che una tipografia rimbalzerà

Quell'ultima parte è il punto. Un file PDF/X-1a con un logo RGB sfuggito sembra perfettamente a posto sullo schermo, supera una revisione distratta, e poi fallisce il preflight in tipografia due giorni prima dello slot di stampa. Beccarlo nella vostra build Delphi, con un messaggio che dice quale pagina e quale immagine, costa molto meno che scoprirlo da un operatore prepress infastito

Perché PDF/X-1a rifiuta i contenuti RGB?

PDF/X-1a rifiuta RGB perché il formato promette che ogni colore è già definitivo per una precisa condizione di stampa CMYK. ISO 15930-1:2001 §6.2.3 limita il colore dei contenuti a DeviceCMYK, DeviceGray e coloranti spot, cioè spazi Separation e DeviceN i cui alternativi sono CMYK o Gray. L'OutputIntent GTS_PDFX incorporato nel file nomina quella condizione di stampa, per esempio FOGRA39 coated offset, e il flusso ricevente ha il diritto di mandare i numeri dritti alle lastre senza alcuna conversione di colore

RGB rompe quel contratto. Un valore RGB descrive un colore relativo a un display o a uno spazio di lavoro, non una ricetta di inchiostro, quindi qualcuno a valle dovrebbe decidere come separarlo, e X-1a esiste proprio per togliere quella decisione al ricevente. ISO 32000-1 §8.6 raggruppa gli spazi colore PDF nelle famiglie device, CIE-based e speciali, e i membri RGB compaiono in più di una famiglia: DeviceRGB è uno spazio device, CalRGB e ICCBased a tre componenti sono CIE-based, e uno spazio Indexed o Separation può nascondere sotto una base o un alternativo RGB. Un checker che cerca solo l'operatore rg se ne perde la maggior parte. L'edizione successiva PDF/X-1a:2003 (ISO 15930-4) mantiene la stessa regola sul colore; notate che il valore 'X-1a' di HotPDF scrive l'identificazione PDF/X-1a:2001 nel pacchetto XMP

Se il vostro contenuto è davvero RGB e deve restarlo, X-1a è il bersaglio sbagliato. HotPDF delimita questo controllo al solo 'X-1a', quindi 'X-3' e 'X-4', che consentono colore ICC-based e calibrato, non sono toccati

Cosa verifica davvero EnsurePDFX1aContentColorSpaces

EnsurePDFX1aContentColorSpaces combina una flag al momento del disegno con una passeggiata ricorsiva nelle risorse di ogni pagina, e corre per ultimo nel gate PDF/X. EndDoc prima impone i requisiti generali PDF/X: Title deve essere impostato, Trapped deve essere 'True', 'False' o 'Unknown', deve esistere un OutputIntent GTS_PDFX (EnsurePDFXOutputIntent), e ogni pagina vuole una TrimBox o ArtBox (EnsurePDFXPageBoxes). Solo dopo che questi passano corre il controllo sul colore

Sequenza del gate PDF/X dentro EndDoc di HotPDF per Delphi: prima metadati, OutputIntent e page box, poi il controllo del colore dei contenuti RGB
il controllo del colore corre per ultimo nel gate X-1a, e una flag al momento del disegno significa che l'RGB disegnato prima che la compliance fosse assegnata viene comunque beccato

Il primo percorso è una flag estesa a tutto il documento. I setter di colore RGB di HotPDF, l'incorporamento di immagini bitmap e le voci di colore delle annotation registrano l'uso di DeviceRGB mentre disegnate, il che copre gli operatori inline rg e RG che non compaiono mai in un dizionario risorse. Il secondo percorso percorre il /Resources di ogni pagina e ispeziona:

  • gli spazi colore nominati in /ColorSpace, inclusa l'abbreviazione inline-image RGB come sinonimo di DeviceRGB
  • il /ColorSpace di ogni Image XObject
  • i Form XObject e i tiling Pattern, ricorrendo nei loro /Resources annidati
  • i Shading Pattern e le voci nel dizionario /Shading della pagina, via il /ColorSpace dello shading
  • le basi degli spazi Indexed, gli alternativi Separation e DeviceN, e le basi dei Pattern, seguiti fino a una profondità di annidamento di 8

Tre cose vengono rifiutate: DeviceRGB, CalRGB, e gli spazi ICCBased il cui profile stream dichiara N = 3. I dizionari risorse condivisi tra Form XObject e pattern vengono visitati una volta sola, così un insieme di form che si riferiscono a vicenda non manda la passeggiata in loop. Il metodo è pubblico su THotPDF, ma nell'uso normale non lo chiamate mai voi, perché lo fa EndDoc per voi

Dove si nasconde RGB nelle risorse di pagina PDF: form annidati, pattern, shading e alternativi portano la passeggiata HotPDF in Delphi a DeviceRGB, CalRGB o ICCBased RGB
la passeggiata segue le risorse annidate fino a profondità 8 e rifiuta DeviceRGB, CalRGB e gli spazi ICCBased con N = 3 ovunque affiorino

Disegnare in Delphi una pagina X-1a che passa il controllo

Un documento X-1a HotPDF conforme vuole un OutputIntent CMYK, una TrimBox su ogni pagina, e colore impostato solo tramite le API CMYK, Gray e spot. Lo scheletro qui sotto usa il profilo FOGRA39; AddPDFXOutputIntent prende l'identificatore della condizione di output, una stringa info, lo stream del profilo ICC, il numero di componenti, e lo spazio alternativo

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);   // richiesto su ogni pagina

    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);         // testo DeviceGray
    Pdf.CurrentPage.SetFont('Arial', [], 14);
    Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
    Pdf.EndDoc;                                    // il controllo del colore corre qui
  finally
    Pdf.Free;
  end;
end;

Ogni chiamata basata su TColor è una trappola in questo contesto. SetRGBFillColor, SetRGBStrokeColor e SetRGBColor prendono tutte un colore GDI ed emettono DeviceRGB, così una routine di servizio scritta anni fa per report orientati allo schermo inquina un lavoro di stampa nel momento in cui viene riusata. Usate SetCMYKFillColor, SetCMYKStrokeColor o SetCMYKColor con componenti nell'intervallo 0 a 1, o gli equivalenti Gray

Colori spot e gradienti CMYK

I coloranti spot sono ammessi in X-1a finché il loro alternativo è CMYK o Gray. RegisterSeparation costruisce lo spazio Separation e ne restituisce il nome risorsa, e RegisterAxialGradient sceglie lo spazio colore dello shading dal numero di componenti che passate, con quattro componenti che significano DeviceCMYK

var
  SpotCS, Ramp: AnsiString;
begin
  // Inchiostro spot con una composizione CMYK approssimata come alternativo
  SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
    [0, 1, 0.8, 0.05]);
  Pdf.CurrentPage.SetFillColorSpace(SpotCS);
  Pdf.CurrentPage.SetFillColor([1.0]);           // tinta al 100%
  Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
  Pdf.CurrentPage.Fill;

  // 4 componenti per stop: lo shading è DeviceCMYK, non 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;

Passate tre componenti alla stessa chiamata di gradiente e ottenete uno shading DeviceRGB, che la passeggiata delle risorse rifiuta sotto il nome del shading pattern. Lo stesso vale per una Separation registrata con 'DeviceRGB' come alternativo: il nome spot sembra innocuo, ma la passeggiata segue l'alternativo e trova RGB sotto. Come questi inchiostri spot vengono composti quando un viewer o un RIP ripiega sull'alternativo è trattato nell'articolo su rendering di colori spot Separation e DeviceN in Delphi

Le immagini raster chiedono la stessa disciplina. HotPDF vincola le bitmap compresse Flate a DeviceRGB, mentre un JPEG incorporato con icJpeg conserva il suo numero di componenti, quindi un JPEG CMYK a quattro componenti o un JPEG in scala di grigi va bene e un JPEG RGB ordinario no. Convertite le artwork al profilo di stampa nella vostra pipeline di imaging prima che raggiungano il PDF

Perché /DefaultRGB non salva un file PDF/X-1a?

/DefaultRGB non aiuta X-1a perché la regola riguarda ciò che la stampa riceve, non se l'RGB è caratterizzato. Per PDF/A, HotPDF lascia che RegisterDefaultColorSpace('DefaultRGB', ...) assorba l'uso di DeviceRGB: una volta che uno spazio colore Default mappa DeviceRGB su un profilo ICC, il contenuto è device-independent e il controllo di coerenza PDF/A lo accetta. Gli sviluppatori passati per quel flusso tendono a provare la stessa cura qui

EnsurePDFX1aContentColorSpaces ignora deliberatamente quella mappatura. Un valore RGB caratterizzato è pur sempre RGB, e l'OutputIntent X-1a è una condizione di stampa CMYK, quindi qualcuno dovrebbe comunque convertirlo. Se ricordate una sola differenza tra i due profili, d'archivio e di stampa, fate che sia questa: PDF/A chiede se un colore è non ambiguo, PDF/X-1a chiede se è già inchiostro

Ordine di disegno, e il setup di pagina che non conta

Poiché il controllo corre a EndDoc su contenuto già scritto, l'ordine in cui impostate le cose non conta. L'RGB disegnato prima che PDFXCompliance fosse assegnato, o prima che l'OutputIntent fosse registrato, viene comunque beccato. Questo conta per il codice che costruisce pagine in routine condivise e decide il profilo di output tardi, a volte da un'impostazione utente scelta dopo che mezzo documento esiste

Pdf.BeginDoc;
// ... OutputIntent e TrimBox come prima ...
Pdf.CurrentPage.SetRGBFillColor(clRed);   // helper legacy, emette DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;

Pdf.PDFXCompliance := 'X-1a';             // deciso dopo il disegno
Pdf.EndDoc;                               // solleva comunque: DeviceRGB registrato

Una flag al momento del disegno ha una trappola che vale conoscere se costruite un controllo simile da soli. Ogni nuova pagina viene inizializzata con uno stato grafico nero tramite SetRGBColor(0), che emette una coppia 0 0 0 rg e 0 0 0 RG, e se quel boilerplate di setup pagina potesse impostare la flag DeviceRGB, ogni documento sembrerebbe inquinato da RGB, sia per questo controllo sia per il controllo di coerenza colore PDF/A. HotPDF salva e ripristina la flag attorno a quell'inizializzazione, così conta solo il colore che disegnate davvero

Come trovate il contenuto RGB quando EndDoc solleva?

Leggete la posizione all'inizio del messaggio di eccezione, perché dice quale dei due percorsi ha trovato il problema. Il messaggio ha la forma PDFXCompliance 'X-1a': <where> uses <what>, seguita dal riferimento ISO 15930-1 e dal consiglio di convertire in CMYK o Gray o passare a X-3 o X-4. Il validatore solleva una semplice Exception alla prima violazione, quindi fate match sul testo piuttosto che su una classe di eccezione

Anatomia dell'eccezione RGB di EndDoc HotPDF in Delphi: la flag al momento del disegno segnala l'uso a livello documento mentre la passeggiata delle risorse nomina pagina e chiave esatte
leggete la posizione all'inizio del messaggio — un report a livello documento manda al codice di disegno, un nome pagina e risorsa punta a una sola chiave
  • document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing) significa che la flag del disegno è scattata. Non c'è numero di pagina, perché la flag è estesa a tutto il documento e viene controllata prima della passeggiata. Cercate nel vostro codice di disegno chiamate SetRGB*, bitmap compresse Flate, JPEG RGB e colori annotation TColor
  • page 3 image XObject /Im2 uses DeviceRGB content o page 1 shading Pattern /P1 uses DeviceRGB content arriva dalla passeggiata delle risorse e punta a una chiave risorsa esatta
  • uses an ICCBased RGB colour space (N=3) dopo una posizione ColorSpace /... è il risultato tipico della registrazione di un profilo sRGB con RegisterICCProfile e della sua selezione tramite SetFillColorSpace, un percorso che la flag del disegno non vede
  • Le posizioni annidate si concatenano, per esempio page 2 Form XObject /Fm1 ColorSpace /CS1, così potete seguire il percorso dalla pagina fino alla risorsa che porta lo spazio RGB
try
  Pdf.EndDoc;
except
  on E: Exception do
  begin
    if Pos('X-1a', E.Message) > 0 then
      LogPrepressFailure(Pdf.FileName, E.Message);  // il vostro logger
    raise;
  end;
end;

Dato che viene riportata solo la prima violazione, sistemare un lavoro con diverse fonti RGB è iterativo: correggete, ricompilate, leggete il messaggio successivo. Per i lavori batch conviene registrare il messaggio completo per file e tenere una breve lista dei colpevoli noti, come una bitmap di logo o un helper di grafici che prende ancora TColor

Dove sta il gate RGB nel vostro preflight

EnsurePDFX1aContentColorSpaces è un gate RGB mirato che gira dentro la vostra build, e si abbina a un preflight PDF/X-1a completo invece di sostituirlo. Copre le famiglie RGB, tramite la passeggiata delle risorse dal /Resources di ogni pagina e tramite la flag del disegno che i setter di colore di HotPDF mantengono per gli operatori inline rg e RG; gli spazi colore annidati vengono seguiti fino a profondità 8 come guardia contro cicli malformati, e la prima violazione viene riportata per EndDoc. Le altre famiglie di colore come Lab o CalGray sono affare del preflight completo, quindi passate il file finito attraverso un validatore esterno come parte della build per avere il verdetto di conformità completo. Il quadro più ampio dell'output di conformità è nella guida all'output PDF/A, PDF/X e PDF/UA con HotPDF, e cablare un validatore in una pipeline non presidiata è trattato in automazione dei controlli preflight PDF in Delphi

HotPDF è un componente PDF VCL nativo per Delphi e C++Builder con output PDF/X, PDF/A e PDF/UA, API di colore CMYK e spot, e supporto OutputIntent ICC. Dettagli, licenze e download di prova sono sulla pagina del prodotto HotPDF Delphi PDF component