Teknisk artikel

HotPDF PDF/X-1a farvetjek: fang RGB-indhold i Delphi

Når PDFXCompliance er sat til 'X-1a', validerer HotPDF indholdets farver helt til sidst i jobbet: EndDoc kalder THotPDF.EnsurePDFX1aContentColorSpaces, som afviser alt DeviceRGB-, CalRGB- eller ICCBased RGB-indhold hvor som helst på enhver side og udløser en exception, der navngiver den overtrædende ressource. Filen bliver aldrig skrevet som et PDF/X-1a-dokument, som et trykkeri ville afvise

Den sidste del er poenget. En PDF/X-1a-fil med ét uvedkommende RGB-logo ser helt fint ud på skærmen, består et hurtigt gennemsyn og fejler så preflight hos trykkeriet to dage før presetidspunktet. At fange det i dit Delphi-build, med en besked, der fortæller dig hvilken side og hvilket billede, er langt billigere end at få det at vide fra en irriteret prepress-operatør

Hvorfor afviser PDF/X-1a RGB-indhold?

PDF/X-1a afviser RGB, fordi formatet lover, at hver farve allerede er endelig for én bestemt CMYK-tryktilstand. ISO 15930-1:2001 §6.2.3 begrænser indholdsfarver til DeviceCMYK, DeviceGray og spotfarvestoffer, altså Separation- og DeviceN-rum, hvis alternativer er CMYK eller Gray. Den indlejrede GTS_PDFX OutputIntent navngiver den tryktilstand, for eksempel FOGRA39 coated offset, og det modtagende workflow har ret til at sende tallene direkte til trykpladerne uden nogen farvekonvertering

RGB bryder den kontrakt. En RGB-værdi beskriver en farve relativt til en skærm eller et arbejdsrum, ikke en trykfarveopskrift, så nogen downstream måtte beslutte, hvordan den skal separeres, og X-1a findes netop for at tage den beslutning fra modtageren. ISO 32000-1 §8.6 grupperer PDF-farverum i device-, CIE-based- og special-familier, og RGB-medlemmerne optræder i mere end én familie: DeviceRGB er et device-rum, CalRGB og ICCBased med tre komponenter er CIE-based, og et Indexed- eller Separation-rum kan skjule en RGB-base eller et RGB-alternativ nedenunder. Et tjek, der kun leder efter rg-operatoren, misser de fleste af dem. Den senere PDF/X-1a:2003-udgave (ISO 15930-4) beholder samme farveregel; bemærk at HotPDFs 'X-1a'-værdi skriver PDF/X-1a:2001-identifikationen ind i XMP-pakken

Hvis dit indhold reelt er RGB og skal forblive det, er X-1a det forkerte mål. HotPDF afgrænser dette tjek til 'X-1a' alene, så 'X-3' og 'X-4', som tillader ICC-based og kalibreret farve, er upåvirkede

Hvad EnsurePDFX1aContentColorSpaces faktisk tjekker

EnsurePDFX1aContentColorSpaces kombinerer et tegnetidsflag med et rekursivt gennemløb af hver sides ressourcer og kører sidst i PDF/X-porten. EndDoc håndhæver først de generelle PDF/X-krav: Title skal være sat, Trapped skal være 'True', 'False' eller 'Unknown', en GTS_PDFX OutputIntent skal findes (EnsurePDFXOutputIntent), og hver side skal have en TrimBox eller ArtBox (EnsurePDFXPageBoxes). Først efter de er bestået, kører farvetjekket

Sekvens af PDF/X-porten inde i HotPDF EndDoc til Delphi: først metadata, OutputIntent og sidebokse, derefter farvetjekket af RGB-indhold
farvetjekket kører sidst i X-1a-porten, og et tegnetidsflag betyder, at RGB tegnet, før overensstemmelsen blev tildelt, stadig fanges

Den første vej er et dokumentbredt flag. HotPDFs RGB-farvesættere, bitmapbillede-indlejring og annotationsfarveposter noterer DeviceRGB-forbrug, mens du tegner, hvilket dækker inline rg- og RG-operatorer, der aldrig optræder i en ressource-dictionary. Den anden vej går hver sides /Resources igennem og inspicerer:

  • Navngivne farverum i /ColorSpace, inklusiv inline-billedforkortelsen RGB som synonym for DeviceRGB
  • /ColorSpace for hvert Image XObject
  • Form XObjects og tiling Patterns, ved at rekursere ned i deres egne nestede /Resources
  • Shading Patterns og poster i sidens /Shading-dictionary, via shadingens /ColorSpace
  • Indexed baserum, Separation- og DeviceN-alternativer og Pattern-baserum, fulgt ned til en nestingdybde på 8

Tre ting afvises: DeviceRGB, CalRGB og ICCBased-rum, hvis profilstream deklarerer N = 3. Ressource-dictionaries delt mellem Form XObjects og patterns besøges én gang, så et gensidigt refererende sæt former ikke sender gennemløbet ind i en løkke. Metoden er offentlig på THotPDF, men i normal brug kalder du den aldrig selv, for EndDoc gør det for dig

Hvor RGB gemmer sig i PDF-sideressourcer: nestede forms, patterns, shadings og alternativer fører HotPDFs gennemløb i Delphi til DeviceRGB, CalRGB eller ICCBased RGB
gennemløbet følger nestede ressourcer til dybde 8 og afviser DeviceRGB, CalRGB og ICCBased-rum med N = 3, hvor end de dukker op

At tegne en X-1a-side i Delphi, der består tjekket

Et overensstemmende HotPDF X-1a-dokument skal bruge en CMYK OutputIntent, en TrimBox på hver side og farver sat udelukkende gennem CMYK-, Gray- og spot-API'erne. Skelettet nedenfor bruger FOGRA39-profilen; AddPDFXOutputIntent tager outputtilstand-identifikatoren, en info-streng, ICC-profilstreamen, dens komponentantal og alternativrummet

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);   // påkrævet på hver side

    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;                                    // farvetjekket kører her
  finally
    Pdf.Free;
  end;
end;

Ethvert TColor-baseret kald er en fælde i den her kontekst. SetRGBFillColor, SetRGBStrokeColor og SetRGBColor tager alle en GDI-farve og udsender DeviceRGB, så en hjælperutine skrevet for år siden til skærmorienterede rapporter tilsmudser et printjob, i samme sekund den genbruges. Brug SetCMYKFillColor, SetCMYKStrokeColor eller SetCMYKColor med komponenter i området 0 til 1, eller Gray-ækvivalenterne

Spotfarver og CMYK-gradients

Spotfarvestoffer er tilladt i X-1a, så længe deres alternativ er CMYK eller Gray. RegisterSeparation bygger Separation-rummet og returnerer dets ressourcenavn, og RegisterAxialGradient vælger shading-farverummet ud fra det antal komponenter, du giver, med fire komponenter betydende DeviceCMYK

var
  SpotCS, Ramp: AnsiString;
begin
  // Spotfarve med en tilnærmet CMYK-opbygning som alternativ
  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 komponenter pr. stop: shadingen er DeviceCMYK, ikke 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;

Giver du tre komponenter til det samme gradientkald, får du en DeviceRGB-shading, som ressourcegennemløbet afviser under shading-patternets navn. Det samme gælder en Separation registreret med 'DeviceRGB' som alternativ: spotnavnet ser uskyldigt ud, men gennemløbet følger alternativet og finder RGB nedenunder. Hvordan sådanne spotfarver kompositeres, når en viewer eller RIP falder tilbage til alternativet, er dækket i artiklen om rendering af Separation- og DeviceN-spotfarver i Delphi

Rasterbilleder behøver samme disciplin. HotPDF låser Flate-komprimerede bitmaps til DeviceRGB, mens en JPEG indlejret med icJpeg beholder sit eget komponentantal, så en firekomponent CMYK JPEG eller en grayscale JPEG er fint, og en almindelig RGB JPEG er ikke. Konvertér artworket til trykprofilen i din imaging-pipeline, før det når PDF'en

Hvorfor redder /DefaultRGB ikke en PDF/X-1a-fil?

/DefaultRGB hjælper ikke X-1a, for reglen handler om, hvad presset modtager, ikke om RGB'en er karakteriseret. Til PDF/A lader HotPDF RegisterDefaultColorSpace('DefaultRGB', ...) absorbere DeviceRGB-forbrug: når et Default-farverum mapper DeviceRGB til en ICC-profil, er indholdet device-uafhængigt, og PDF/A-konsistenstjekket accepterer det. Udviklere, der har været igennem det workflow, har tendens til at prøve samme løsning her

EnsurePDFX1aContentColorSpaces ignorerer bevidst den mapping. En karakteriseret RGB-værdi er stadig RGB, og X-1a OutputIntent er en CMYK-tryktilstand, så nogen måtte alligevel konvertere den. Skal du huske én forskel mellem de to arkiv- og trykprofiler, så lad det være denne: PDF/A spørger, om en farve er entydig, PDF/X-1a spørger, om den allerede er trykfarve

Tegnerækkefølgen og den sideopsætning, der ikke tæller

Fordi tjekket kører ved EndDoc over allerede skrevet indhold, betyder rækkefølgen, du satte tingene op i, ingenting. RGB tegnet, før PDFXCompliance blev tildelt, eller før OutputIntent blev registreret, fanges stadig. Det betyder noget for kode, der bygger sider i delte rutiner og beslutter outputprofilen sent, nogle gange ud fra en brugerindstilling valgt, efter halvdelen af dokumentet findes

Pdf.BeginDoc;
// ... OutputIntent og TrimBox som før ...
Pdf.CurrentPage.SetRGBFillColor(clRed);   // legacy-hjælper, udsender DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;

Pdf.PDFXCompliance := 'X-1a';             // besluttet efter tegning
Pdf.EndDoc;                               // udløser stadig fejl: DeviceRGB registreret

Et tegnetidsflag har én fælde, du bør kende, hvis du bygger et lignende tjek selv. Hver ny side initialiseres med en sort graphics state gennem SetRGBColor(0), som udsender et 0 0 0 rg- og 0 0 0 RG-par, og hvis den sideopsætningsboilerplate måtte sætte DeviceRGB-flaget, ville hvert dokument se RGB-tilsmudset ud, både for dette tjek og for PDF/A-farvekonsistenstjekket. HotPDF gemmer og gendanner flaget omkring den initialisering, så kun farver, du faktisk tegner, tæller

Hvordan finder du RGB-indholdet, når EndDoc udløser en fejl?

Læs placeringen i starten af exception-beskeden, for den fortæller dig, hvilken af de to veje, der fandt problemet. Beskeden har formen PDFXCompliance 'X-1a': <where> uses <what>, efterfulgt af ISO 15930-1-referencen og rådet om at konvertere til CMYK eller Gray eller skifte til X-3 eller X-4. Validatoren udløser en almindelig Exception ved den første overtrædelse, så match på teksten i stedet for på en exception-klasse

Anatomi af HotPDF EndDoc RGB-exceptionen i Delphi: tegnetidsflaget rapporterer dokumentbredt forbrug, mens ressourcegennemløbet navngiver den præcise side og nøgle
læs placeringen i starten af beskeden — en dokumentbred rapport sender dig til tegnekoden, et side- og ressourcenavn peger på én nøgle
  • document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing) betyder, at tegnetidsflaget slog til. Der er intet sidetal, for flaget er dokumentbredt og tjekkes før gennemløbet. Søg din tegnekode efter SetRGB*-kald, Flate-komprimerede bitmaps, RGB-JPEG'er og TColor-annotationsfarver
  • page 3 image XObject /Im2 uses DeviceRGB content eller page 1 shading Pattern /P1 uses DeviceRGB content kommer fra ressourcegennemløbet og peger på en præcis ressourcenøgle
  • uses an ICCBased RGB colour space (N=3) efter en ColorSpace /...-placering er det typiske resultat af at registrere en sRGB-profil med RegisterICCProfile og vælge den gennem SetFillColorSpace, en vej, tegnetidsflaget ikke ser
  • Nestede placeringer kæder sig sammen, for eksempel page 2 Form XObject /Fm1 ColorSpace /CS1, så du kan følge vejen fra siden ned til den ressource, der bærer RGB-rummet
try
  Pdf.EndDoc;
except
  on E: Exception do
  begin
    if Pos('X-1a', E.Message) > 0 then
      LogPrepressFailure(Pdf.FileName, E.Message);  // din egen logger
    raise;
  end;
end;

Fordi kun den første overtrædelse rapporteres, er det at rette et job med flere RGB-kilder iterativt: ret, genbyg, læs næste besked. Til batchjobs betaler det sig at logge hele beskeden pr. fil og holde en kort liste over kendte syndere, som et logo-bitmap eller en chart-hjælper, der stadig tager TColor

Hvor RGB-porten passer ind i din preflight

EnsurePDFX1aContentColorSpaces er en målrettet RGB-port, der kører inde i dit build, og den parres med en fuld PDF/X-1a-preflight frem for at erstatte én. Den dækker RGB-familierne gennem ressourcegennemløbet fra hver sides /Resources og gennem tegnetidsflaget, som HotPDFs farvesættere vedligeholder for inline rg- og RG-operatorer; nestede farverum følges til dybde 8 som sikring mod misdannede cykler, og den første overtrædelse rapporteres pr. EndDoc. Andre farvefamilier som Lab eller CalGray er en sag for den fulde preflight, så kør den færdige fil gennem en ekstern validator som del af dit build for at få den komplette konformitetsvurdering. Det bredere billede af konformitetsoutput er i guiden til PDF/A-, PDF/X- og PDF/UA-output med HotPDF, og tilkobling af en validator i en uovervåget pipeline er dækket i automatisering af PDF-preflight-tjek i Delphi

HotPDF er en native VCL PDF-komponent til Delphi og C++Builder med PDF/X-, PDF/A- og PDF/UA-output, CMYK- og spotfarve-API'er og ICC OutputIntent-understøttelse. Detaljer, licensering og trial-download findes på HotPDF Delphi PDF component-productsiden