Als PDFXCompliance op 'X-1a' staat, valideert HotPDF de inhoudskleur op het allereind van de klus: EndDoc roept THotPDF.EnsurePDFX1aContentColorSpaces aan, dat alle DeviceRGB-, CalRGB- of ICCBased-RGB-inhoud op elke pagina weigert en een exception gooit die de overtredende resource bij naam noemt. Het bestand wordt nooit weggeschreven als een PDF/X-1a-document dat een drukkerij terugstuurt
Dat laatste is het punt. Een PDF/X-1a-bestand met één verdwaald RGB-logo ziet er op het scherm prima uit, komt door een vluchtige controle en faalt dan bij de preflight van de drukkerij, twee dagen voor de pers-slot. Het in je Delphi-build vangen, met een melding die je vertelt welke pagina en welk beeld, is een stuk goedkoper dan het horen van een chagrijnse prepress-medewerker
Waarom weigert PDF/X-1a RGB-inhoud?
PDF/X-1a weigert RGB omdat het formaat belooft dat elke kleur al definitief is voor één specifieke CMYK-drukconditie. ISO 15930-1:2001 §6.2.3 beperkt inhoudskleur tot DeviceCMYK, DeviceGray en steunkleurig, dus Separation- en DeviceN-ruimten waarvan de alternatieven CMYK of Gray zijn. De GTS_PDFX-OutputIntent die in het bestand zit, noemt die drukconditie, bijvoorbeeld FOGRA39 coated offset, en de ontvangende workflow mag de getallen rechtstreeks naar de platen sturen zonder enige kleurconversie
RGB breekt dat contract. Een RGB-waarde beschrijft een kleur relatief aan een beeldscherm of een werkkleurruimte, geen inktrecept, dus iemand stroomafwaarts zou moeten beslissen hoe hij gesepareerd wordt, en X-1a bestaat juist om die beslissing bij de ontvanger weg te nemen. ISO 32000-1 §8.6 verdeelt de PDF-kleurruimten in device-, CIE-based- en speciale families, en de RGB-leden duiken in meer dan één familie op: DeviceRGB is een device-ruimte, CalRGB en ICCBased met drie componenten zijn CIE-based, en een Indexed- of Separation-ruimte kan onderaan een RGB-base of alternate verbergen. Een controleur die alleen naar de rg-operator kijkt, mist het merendeel. De latere PDF/X-1a:2003-editie (ISO 15930-4) houdt dezelfde kleurregel aan; let op dat de 'X-1a'-waarde van HotPDF de PDF/X-1a:2001-identificatie in het XMP-pakket schrijft
Is je inhoud echt RGB en hoort die dat ook te blijven, dan is X-1a de verkeerde target. HotPDF beperkt deze controle tot 'X-1a', dus 'X-3' en 'X-4', die ICC-based en gekalibreerde kleur toestaan, blijven ongemoeid
Wat EnsurePDFX1aContentColorSpaces werkelijk controleert
EnsurePDFX1aContentColorSpaces combineert een vlag uit de tekenfase met een recursieve tocht langs de resources van elke pagina, en hij draait als laatste in de PDF/X-poort. EndDoc dwingt eerst de algemene PDF/X-eisen af: Title moet zijn gezet, Trapped moet 'True', 'False' of 'Unknown' zijn, er moet een GTS_PDFX-OutputIntent bestaan (EnsurePDFXOutputIntent) en elke pagina heeft een TrimBox of ArtBox nodig (EnsurePDFXPageBoxes). Pas als die slagen, draait de kleurcontrole
Het eerste pad is een vlag voor het hele document. De RGB-kleursetters van HotPDF, het insluiten van bitmapafbeeldingen en de kleurentries van annotaties noteren DeviceRGB-gebruik terwijl je tekent, wat inline rg- en RG-operators dekt die nooit in een resource-dictionary opduiken. Het tweede pad loopt langs de /Resources van elke pagina en bekijkt:
- Benoemde kleurruimten in
/ColorSpace, inclusief de inline-afbeeldingsafkortingRGBals synoniem voor DeviceRGB - De
/ColorSpacevan elke Image XObject - Form XObjects en tiling Patterns, door recursief in hun eigen geneste
/Resourceste duiken - Shading Patterns en entries in de
/Shading-dictionary van de pagina, via de/ColorSpacevan de shading - Indexed-baseruimten, Separation- en DeviceN-alternatieven en Pattern-baseruimten, gevolgd tot een nestingsdiepte van 8
Drie dingen worden geweigerd: DeviceRGB, CalRGB en ICCBased-ruimten waarvan de profile-stream N = 3 aangeeft. Resource-dictionaries die Form XObjects en patterns delen, worden één keer bezocht, dus een set forms die naar elkaar verwijst, stuurt de tocht niet in een lus. De methode is publiek op THotPDF, maar in normaal gebruik roep je hem nooit zelf aan, want EndDoc doet dat voor je
Een X-1a-pagina tekenen in Delphi die de controle haalt
Een conform HotPDF X-1a-document heeft een CMYK-OutputIntent, een TrimBox op elke pagina en kleur die uitsluitend via de CMYK-, Gray- en spot-API's wordt gezet nodig. Het skelet hieronder gebruikt het FOGRA39-profiel; AddPDFXOutputIntent krijgt de output-conditie-identifier, een infostring, de ICC-profielstream, zijn aantal componenten en de alternate-ruimte mee
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); // verplicht op elke 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); // DeviceGray-tekst
Pdf.CurrentPage.SetFont('Arial', [], 14);
Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
Pdf.EndDoc; // hier draait de kleurcontrole
finally
Pdf.Free;
end;
end;
Elke aanroep op basis van TColor is in deze context een valkuil. SetRGBFillColor, SetRGBStrokeColor en SetRGBColor nemen allemaal een GDI-kleur en emitteer DeviceRGB, dus een hulproutine van jaren geleden voor schermgerichte rapporten besmet een printklus zodra hij wordt hergebruikt. Gebruik SetCMYKFillColor, SetCMYKStrokeColor of SetCMYKColor met componenten in het bereik 0 tot 1, of de Gray-equivalenten
Steunkleuren en CMYK-gradients
Steunkleurig is in X-1a toegestaan zolang de alternate CMYK of Gray is. RegisterSeparation bouwt de Separation-ruimte en geeft de resourcenaam terug, en RegisterAxialGradient kiest de shading-kleurruimte op basis van het aantal componenten dat je meegeeft, waarbij vier componenten DeviceCMYK betekent
var
SpotCS, Ramp: AnsiString;
begin
// Steunkleur met een benaderende CMYK-opbouw als alternate
SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
[0, 1, 0.8, 0.05]);
Pdf.CurrentPage.SetFillColorSpace(SpotCS);
Pdf.CurrentPage.SetFillColor([1.0]); // 100% tint
Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
Pdf.CurrentPage.Fill;
// 4 componenten per stop: de shading is DeviceCMYK, niet 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;
Geef je drie componenten mee aan dezelfde gradient-aanroep, dan krijg je een DeviceRGB-shading, die de resource-tocht weigert onder de naam van het shading-pattern. Hetzelfde geldt voor een Separation die met 'DeviceRGB' als alternate is geregistreerd: de spotnaam ziet er ongevaarlijk uit, maar de tocht volgt de alternate en vindt eronder RGB. Hoe zulke steunkleuren worden gecompositeerd wanneer een viewer of RIP terugvalt op de alternate, wordt behandeld in het artikel over het renderen van Separation- en DeviceN-steunkleuren in Delphi
Rasterafbeeldingen vragen om dezelfde discipline. HotPDF klemmt met Flate gecomprimeerde bitmaps vast aan DeviceRGB, terwijl een JPEG die met icJpeg wordt ingesloten zijn eigen aantal componenten houdt, dus een CMYK JPEG met vier componenten of een grijswaarden-JPEG is prima en een gewone RGB JPEG niet. Converteer artwork in je imaging-pipeline naar het drukprofiel voordat het de PDF bereikt
Waarom redt /DefaultRGB een PDF/X-1a-bestand niet?
/DefaultRGB helpt X-1a niet, want de regel gaat over wat de pers ontvangt, niet over of de RGB gekarakteriseerd is. Voor PDF/A laat HotPDF RegisterDefaultColorSpace('DefaultRGB', ...) het DeviceRGB-gebruik opnemen: zodra een Default-kleurruimte DeviceRGB naar een ICC-profiel mapt, is de inhoud device-onafhankelijk en accepteert de PDF/A-consistentiecontrole haar. Ontwikkelaars die die workflow al eens hebben doorlopen, proberen hier vaak dezelfde fix
EnsurePDFX1aContentColorSpaces negeert die mapping bewust. Een gekarakteriseerde RGB-waarde is nog steeds RGB, en de X-1a-OutputIntent is een CMYK-drukconditie, dus iemand zou haar alsnog moeten omzetten. Onthoud je één verschil tussen de twee archief- en drukprofielen, laat het dit zijn: PDF/A vraagt of een kleur eenduidig is, PDF/X-1a vraagt of ze al inkt is
Tekenvolgorde, en de pagina-opzet die niet telt
Omdat de controle bij EndDoc draait over al geschreven inhoud, maakt de volgorde waarin je dingen opzet niet uit. RGB dat werd getekend voordat PDFXCompliance was toegewezen, of voordat de OutputIntent was geregistreerd, wordt alsnog gepakt. Dat telt voor code die pagina's bouwt in gedeelde routines en het output-profiel pas laat kiest, soms op basis van een gebruikersinstelling die pas na de helft van het document wordt gekozen
Pdf.BeginDoc;
// ... OutputIntent en TrimBox als eerder ...
Pdf.CurrentPage.SetRGBFillColor(clRed); // legacy-helper, emitteert DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;
Pdf.PDFXCompliance := 'X-1a'; // besloten na het tekenen
Pdf.EndDoc; // raise't nog steeds: DeviceRGB genoteerd
Een vlag uit de tekenfase heeft één valkuil die je moet kennen als je zelf een vergelijkbare controle bouwt. Elke nieuwe pagina wordt geïnitialiseerd met een zwarte graphics state via SetRGBColor(0), wat een 0 0 0 rg- en 0 0 0 RG-paar emitteert, en als die pagina-opzet-boilerplate de DeviceRGB-vlag mocht zetten, zou elk document er RGB-besmet uitzien, zowel voor deze controle als voor de PDF/A-kleurconsistentiecontrole. HotPDF bewaart de vlag rond die initialisatie en herstelt haar, dus alleen kleur die je echt tekent telt
Hoe vind je de RGB-inhoud zodra EndDoc een exception gooit?
Lees de locatie aan het begin van de exceptionmelding, want die vertelt je welk van de twee paden het probleem vond. De melding heeft de vorm PDFXCompliance 'X-1a': <where> uses <what>, gevolgd door de ISO 15930-1-verwijzing en het advies om naar CMYK of Gray te converteren of over te stappen op X-3 of X-4. De validator gooit een kale Exception bij de eerste overtreding, dus match op de tekst in plaats van op een exceptionklasse
document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing)betekent dat de vlag uit de tekenfase afgaan. Er staat geen paginanummer, want de vlag is documentbreed en wordt vóór de tocht gecontroleerd. Doorzoek je tekenkode opSetRGB*-aanroepen, met Flate gecomprimeerde bitmaps, RGB JPEGs enTColor-annotatiekleurenpage 3 image XObject /Im2 uses DeviceRGB contentofpage 1 shading Pattern /P1 uses DeviceRGB contentkomt uit de resource-tocht en wijst naar een exacte resource-keyuses an ICCBased RGB colour space (N=3)na een locatieColorSpace /...is het typische resultaat van het registreren van een sRGB-profiel metRegisterICCProfileen het selecteren ervan viaSetFillColorSpace, een pad dat de vlag uit de tekenfase niet ziet- Geneste locaties schakelen aan elkaar, bijvoorbeeld
page 2 Form XObject /Fm1 ColorSpace /CS1, zodat je het pad van de pagina kunt volgen tot bij de resource die de RGB-ruimte draagt
try
Pdf.EndDoc;
except
on E: Exception do
begin
if Pos('X-1a', E.Message) > 0 then
LogPrepressFailure(Pdf.FileName, E.Message); // je eigen logger
raise;
end;
end;
Omdat alleen de eerste overtreding wordt gemeld, is het fixen van een klus met meerdere RGB-bronnen iteratief: fixen, opnieuw bouwen, de volgende melding lezen. Voor batchklussen loont het om de volledige melding per bestand te loggen en een korte lijst bekende overtreders bij te houden, zoals een logo-bitmap of een grafiekhelper die nog steeds TColor neemt
Waar de RGB-poort in je preflight past
EnsurePDFX1aContentColorSpaces is een gerichte RGB-poort die binnen je build draait, en hij gaat samen met een volledige PDF/X-1a-preflight in plaats van een te vervangen. Hij dekt de RGB-families, via de resource-tocht vanuit de /Resources van elke pagina en via de vlag uit de tekenfase die de kleursetters van HotPDF bijhouden voor inline rg- en RG-operators; geneste kleurruimten worden tot diepte 8 gevolgd als vangnet tegen misvormde cycli, en de eerste overtreding wordt per EndDoc gemeld. Andere kleurfamilies zoals Lab of CalGray zijn een zaak voor de volledige preflight, dus draai het afgeronde bestand als onderdeel van je build door een externe validator voor het volledige conformiteitsoordeel. Het bredere beeld van conformance-output staat in de gids voor PDF/A-, PDF/X- en PDF/UA-output met HotPDF, en het inbouwen van een validator in een onbemande pipeline wordt behandeld in PDF-preflight-controles automatiseren in Delphi
HotPDF is een native VCL PDF-component voor Delphi en C++Builder met PDF/X-, PDF/A- en PDF/UA-output, CMYK- en steunkleur-API's en ICC-OutputIntent-ondersteuning. Details, licenties en de proefdownload staan op de productpagina van de HotPDF Delphi PDF-component