Technický článek

Kontrola RGB obsahu pro PDF/X-1a v HotPDF

Když je PDFXCompliance nastavené na 'X-1a', kontroluje HotPDF barvy obsahu úplně na konci jobu: EndDoc zavolá THotPDF.EnsurePDFX1aContentColorSpaces, které odmítne jakýkoli obsah DeviceRGB, CalRGB nebo ICCBased RGB kdekoliv na jakékoli stránce a vyhodí výjimku pojmenující problematický zdroj. Soubor se nikdy nezapíše jako PDF/X-1a dokument, který by tiskárna odmítla

Ta poslední část je to podstatné. PDF/X-1a soubor s jedním zatoulaným RGB logem vypadá na obrazovce naprosto v pořádku, projde letmou revizí a pak selže v preflightu u tiskárny dva dny před tiskovým slotem. Chytit to ve vašem buildu v Delphi, se zprávou, která řekne, která stránka a který obrázek, je o dost levnější než se to dovědět od naštvaného prepress operátora

Proč PDF/X-1a odmítá RGB obsah?

PDF/X-1a odmítá RGB, protože formát slibuje, že každá barva už je finální pro jednu konkrétní CMYK tiskovou podmínku. ISO 15930-1:2001 §6.2.3 omezuje barvy obsahu na DeviceCMYK, DeviceGray a spot colorants, to je Separation a DeviceN prostory, jejichž alternates jsou CMYK nebo Gray. GTS_PDFX OutputIntent vložený do souboru tu tiskovou podmínku pojmenovává, třeba FOGRA39 coated offset, a přijímající workflow má právo poslat čísla rovnou na desky bez jakékoli konverze barev

RGB tu smlouvu láme. Hodnota RGB popisuje barvu relativně k displeji nebo pracovnímu prostoru, ne recept na inkoust, takže někdo po proudu by se musel rozhodnout, jak ji oddělit, a X-1a existuje přesně proto, aby tohle rozhodnutí přijímající straně vzala. ISO 32000-1 §8.6 člení PDF barevné prostory na rodiny device, CIE-based a special a členové RGB se objevují ve více rodinách: DeviceRGB je device prostor, CalRGB a ICCBased se třemi komponentami jsou CIE-based a Indexed nebo Separation prostor může pod sebou schovat RGB base nebo alternate. Kontrola, která hledá jen operátor rg, jich většinu minula. Pozdější vydání PDF/X-1a:2003 (ISO 15930-4) drží totéž pravidlo barev; HotPDF hodnota 'X-1a' zapisuje do XMP paketu identifikaci PDF/X-1a:2001

Pokud váš obsah doopravdy je RGB a má tak zůstat, X-1a je špatný cíl. HotPDF tahle kontrola platí jen pro 'X-1a', takže 'X-3' a 'X-4', které dovolují ICC-based a kalibrované barvy, se nedotkne

Co EnsurePDFX1aContentColorSpaces doopravdy kontroluje

EnsurePDFX1aContentColorSpaces kombinuje příznak zaznamenaný při kreslení s rekurzivním průchodem zdroji každé stránky a v bráně PDF/X běží poslední. EndDoc nejdřív vynucuje obecné požadavky PDF/X: Title musí být nastavený, Trapped musí být 'True', 'False' nebo 'Unknown', musí existovat GTS_PDFX OutputIntent (EnsurePDFXOutputIntent) a každá stránka potřebuje TrimBox nebo ArtBox (EnsurePDFXPageBoxes). Teprve když tyhle kontroly projdou, běží kontrola barev

Posloupnost brány PDF/X uvnitř HotPDF EndDoc pro Delphi: nejdřív metadata, OutputIntent a page boxy, pak kontrola RGB obsahu
kontrola barev běží v bráně X-1a poslední a příznak z doby kreslení znamená, že se chytí i RGB nakreslené před přiřazením compliance

První cesta je příznak na úrovni dokumentu. RGB color settery HotPDF, vkládání bitmapových obrázků a barevné položky anotací si zaznamenávají použití DeviceRGB už při kreslení, čímž pokrývají inline operátory rg a RG, které se ve zdrojovém slovníku vůbec neobjeví. Druhá cesta projde /Resources každé stránky a prohlédne:

  • Pojmenované barevné prostory v /ColorSpace, včetně zkráceného zápisu inline obrázků RGB jako synonyma pro DeviceRGB
  • /ColorSpace každého Image XObject
  • Form XObjects a tiling Patterns, rekurzí do jejich vlastních vnořených /Resources
  • Shading Patterns a položky ve slovníku /Shading stránky, přes /ColorSpace daného shadingu
  • Indexed base prostory, Separation a DeviceN alternates a Pattern base prostory, sledované do hloubky zanoření 8

Odmítají se tři věci: DeviceRGB, CalRGB a ICCBased prostory, jejichž profilový stream deklaruje N = 3. Zdrojové slovníky sdílené mezi Form XObjects a patterns navštíví průchod jen jednou, takže si vzájemně odkazující sada formulářů nezacyklí průchod. Metoda je veřejná na THotPDF, ale při běžném použití ji sami nikdy nevoláte, protože to za vás udělá EndDoc

Kde se RGB skrývá ve zdrojích PDF stránek: vnořené formuláře, patterns, shadings a alternates dovedou průchod HotPDF v Delphi k DeviceRGB, CalRGB nebo ICCBased RGB
průchod sleduje vnořené zdroje do hloubky 8 a odmítá DeviceRGB, CalRGB a ICCBased prostory s N = 3, kdekoli se objeví

Nakreslení X-1a stránky v Delphi, která kontrolou projde

Souladný HotPDF X-1a dokument potřebuje CMYK OutputIntent, TrimBox na každé stránce a barvy nastavované jen přes CMYK, Gray a spot API. Kostra níže používá profil FOGRA39; AddPDFXOutputIntent bere identifikátor výstupní podmínky, informační řetězec, stream ICC profilu, počet jeho komponent a alternate prostor

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);   // povinné na každé stránce

    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);         // DeviceGray text
    Pdf.CurrentPage.SetFont('Arial', [], 14);
    Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
    Pdf.EndDoc;                                    // kontrola barev běží tady
  finally
    Pdf.Free;
  end;
end;

Každé volání založené na TColor je v tomhle kontextu past. SetRGBFillColor, SetRGBStrokeColor a SetRGBColor všechny berou GDI barvu a emitují DeviceRGB, takže pomocná rutina napsaná před lety pro reporty orientované na obrazovku zamoří tiskový job v okamžiku, kdy se znovu použije. Používejte SetCMYKFillColor, SetCMYKStrokeColor nebo SetCMYKColor s komponentami v rozsahu 0 až 1, případně Gray ekvivalenty

Spot barvy a CMYK gradienty

Spot colorants jsou v X-1a povolené, dokud je jejich alternate CMYK nebo Gray. RegisterSeparation postaví Separation prostor a vrátí jeho zdrojový název a RegisterAxialGradient vybere barevný prostor shadingu podle počtu komponent, které předáte, přičemž čtyři komponenty znamenají DeviceCMYK

var
  SpotCS, Ramp: AnsiString;
begin
  // Spot inkoust s přibližným CMYK buildem jako alternatou
  SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
    [0, 1, 0.8, 0.05]);
  Pdf.CurrentPage.SetFillColorSpace(SpotCS);
  Pdf.CurrentPage.SetFillColor([1.0]);           // tint 100 %
  Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
  Pdf.CurrentPage.Fill;

  // 4 komponenty na stop: shading je DeviceCMYK, ne 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;

Týmuž gradientnímu volání dáte tři komponenty a dostanete DeviceRGB shading, který průchod zdroji odmítne pod názvem shading patternu. Totéž platí pro Separation registrovaný s 'DeviceRGB' jako alternatou: spot název vypadá neškodně, ale průchod sleduje alternatu a pod ní najde RGB. Jak se takové spot inkousty skládají, když se prohlížeč nebo RIP vrátí k alternatě, popisuje článek o renderování Separation a DeviceN spot barev v Delphi

Rastrové obrázky potřebují tutéž disciplínu. HotPDF připne Flate komprimované bitmapy k DeviceRGB, zatímco JPEG vložený s icJpeg si drží vlastní počet komponent, takže čtyřkomponentový CMYK JPEG nebo grayscale JPEG je v pořádku a obyčejný RGB JPEG ne. Převádějte podklady na tiskový profil ve vaší imaging pipeline, ještě než se dostanou do PDF

Proč /DefaultRGB nezachrání soubor PDF/X-1a?

/DefaultRGB X-1a nepomůže, protože pravidlo se týká toho, co tisk dostane, ne toho, jestli je RGB charakterizované. Pro PDF/A HotPDF nechá RegisterDefaultColorSpace('DefaultRGB', ...) pohltit použití DeviceRGB: jakmile Default barevný prostor namapuje DeviceRGB na ICC profil, obsah je nezávislý na zařízení a kontrola konzistence PDF/A ho přijme. Vývojáři, kteří tím workflow prošli, tu mívají chuť zkusit stejnou opravu

EnsurePDFX1aContentColorSpaces tohle mapování záměrně ignoruje. Charakterizovaná hodnota RGB je pořád RGB a X-1a OutputIntent je CMYK tisková podmínka, takže někdo by ji stejně musel konvertovat. Pokud si z obou archivačních a tiskových profilů máte zapamatovat jeden rozdíl, ať je to tenhle: PDF/A se ptá, jestli je barva jednoznačná, PDF/X-1a se ptá, jestli už je inkoust

Pořadí kreslení a page setup, který se nepočítá

Protože kontrola běží v EndDoc nad už zapsaným obsahem, nezáleží na pořadí, v jakém věci nastavujete. RGB nakreslené před přiřazením PDFXCompliance nebo před registrací OutputIntent se stejně chytí. To má význam pro kód, který staví stránky ve sdílených rutinách a o výstupním profilu rozhoduje pozdě, někdy podle uživatelského nastavení zvoleného poté, co už polovina dokumentu existuje

Pdf.BeginDoc;
// ... OutputIntent a TrimBox jako dřív ...
Pdf.CurrentPage.SetRGBFillColor(clRed);   // legacy helper, emituje DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;

Pdf.PDFXCompliance := 'X-1a';             // rozhodnuto po kreslení
Pdf.EndDoc;                               // stejně vyhodí: DeviceRGB zaznamenáno

Příznak z doby kreslení má jednu past, kterou stojí za to znát, když si podobnou kontrolu stavíte sami. Každá nová stránka se inicializuje černým graphics state přes SetRGBColor(0), což emituje pár 0 0 0 rg a 0 0 0 RG a kdyby tenhle page-setup boilerplate směl příznak DeviceRGB nastavit, vypadal by každý dokument jako zamořený RGB, a to jak pro tahle kontrolu, tak pro kontrolu konzistence barev PDF/A. HotPDF kolem té inicializace příznak uloží a obnoví, takže se počítá jen barva, kterou skutečně nakreslíte

Jak najít RGB obsah, když EndDoc selže?

Čtěte umístění na začátku zprávy výjimky, protože řekne, která z obou cest problém našla. Zpráva má tvar PDFXCompliance 'X-1a': <where> uses <what>, následuje odkaz na ISO 15930-1 a rada převést do CMYK nebo Gray, případně přepnout na X-3 nebo X-4. Validátor vyhazuje obyčejný Exception při prvním porušení, takže matchujte na text, ne na třídu výjimky

Anatomie RGB výjimky HotPDF EndDoc v Delphi: příznak z doby kreslení hlásí použití v celém dokumentu, průchod zdroji pojmenuje přesnou stránku a klíč
čtěte umístění na začátku zprávy — hlášení pro celý dokument pošle do kreslicího kódu, stránka a název zdroje ukazuje na jeden klíč
  • document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing) znamená, že se spustil příznak z doby kreslení. Číslo stránky není, protože příznak je na úrovni dokumentu a kontroluje se před průchodem. Prohledejte kreslicí kód po voláních SetRGB*, Flate komprimovaných bitmapách, RGB JPEGech a anotačních barvách TColor
  • page 3 image XObject /Im2 uses DeviceRGB content nebo page 1 shading Pattern /P1 uses DeviceRGB content přichází z průchodu zdroji a ukazuje na přesný zdrojový klíč
  • uses an ICCBased RGB colour space (N=3) po umístění ColorSpace /... je typický výsledek registrace sRGB profilu přes RegisterICCProfile a výběru přes SetFillColorSpace, cesty, kterou příznak z doby kreslení nevidí
  • Vnořená umístění se řetězí, třeba page 2 Form XObject /Fm1 ColorSpace /CS1, takže cestu ze stránky dolů ke zdroji nesoucímu RGB prostor můžete sledovat
try
  Pdf.EndDoc;
except
  on E: Exception do
  begin
    if Pos('X-1a', E.Message) > 0 then
      LogPrepressFailure(Pdf.FileName, E.Message);  // váš vlastní logger
    raise;
  end;
end;

Protože se hlásí jen první porušení, oprava jobu s několika RGB zdroji je iterativní: opravte, rebuildujte, přečtěte další zprávu. U dávkových jobů se vyplatí logovat celou zprávu pro každý soubor a držet si krátký seznam známých pachatelů, třeba logo bitmapu nebo chart helper, který pořád bere TColor

Kam RGB brána patří ve vašem preflightu

EnsurePDFX1aContentColorSpaces je cílená RGB brána běžící uvnitř vašeho buildu a spáruje se s plným PDF/X-1a preflightem, nikoli ho nahrazuje. Pokrývá rodiny RGB, přes průchod zdroji z /Resources každé stránky i přes příznak z doby kreslení, který si udržují color settery HotPDF pro inline operátory rg a RG; vnořené barevné prostory se sledují do hloubky 8 jako pojistka proti deformovaným cyklům a hlásí se první porušení na EndDoc. Ostatní barevné rodiny jako Lab nebo CalGray jsou věc plného preflightu, takže hotový soubor pusťte jako součást buildu externím validátorem, abyste dostali kompletní verdikt souladu. Širší souvislost výstupu conformance popisuje průvodce výstupem PDF/A, PDF/X a PDF/UA s HotPDF a napojení validátoru do nehlídané pipeline pokrývá automatizace PDF preflight kontrol v Delphi

HotPDF je nativní VCL PDF component pro Delphi a C++Builder s výstupem PDF/X, PDF/A a PDF/UA, s CMYK a spot barevnými API a s podporou ICC OutputIntent. Detaily, licence a trial ke stažení najdete na produktové stránce HotPDF Delphi PDF component