Teknisk artikel

HotPDF PDF/X-1a-färgkontroll: fånga RGB-innehåll i Delphi

När PDFXCompliance sätts till 'X-1a' validerar HotPDF innehållsfärgen alldeles i slutet av jobbet: EndDoc anropar THotPDF.EnsurePDFX1aContentColorSpaces, som avvisar alla DeviceRGB-, CalRGB- eller ICCBased-RGB-innehåll var som helst på vilken sida som helst och kastar ett exception som namnger den trassliga resursen. Filen skrivs aldrig ut som ett PDF/X-1a-dokument som ett tryckeri studsar

Den sista delen är poängen. En PDF/X-1a-fil med en enstaka friarande RGB-logo ser helt fin ut på skärmen, klarar en tillfällig granskning, och faller sedan i preflighten hos tryckeriet två dagar innan pressen rullar. Att fånga den i din Delphi-build, med ett meddelande som talar om vilken sida och vilken bild, är betydligt billigare än att få höra det av en irriterad prepress-operatör

Varför avvisar PDF/X-1a RGB-innehåll?

PDF/X-1a avvisar RGB för formatet lovar att varje färg redan är slutgiltig för ett specifikt CMYK-tryckvillkor. ISO 15930-1:2001 §6.2.3 begränsar innehållsfärg till DeviceCMYK, DeviceGray och spotfärgsämnen, alltså Separation- och DeviceN-rymder vars alternativ är CMYK eller Gray. Den i filen inbäddade GTS_PDFX OutputIntenten namnger det tryckvillkoret, till exempel FOGRA39 coated offset, och det mottagande arbetsflödet har all rätt att skicka talen rakt till plåtarna utan någon färgkonvertering

RGB bryter kontraktet. Ett RGB-värde beskriver en färg i förhållande till en skärm eller en arbetsyta, inte ett bläckrecept, så någon nedströms skulle behöva avgöra hur den separeras, och X-1a finns just för att ta det beslutet från mottagaren. ISO 32000-1 §8.6 grupperar PDF-färgrymder i device-, CIE-baserade och special-familjer, och RGB-medlemmarna dyker upp i mer än en familj: DeviceRGB är en device-rymd, CalRGB och ICCBased med tre komponenter är CIE-baserade, och en Indexed- eller Separation-rymd kan gömma en RGB-bas eller alternativ under sig. En kontrollant som bara letar efter rg-operatorn missar de flesta. Den senare upplagan PDF/X-1a:2003 (ISO 15930-4) behåller samma färgregel; notera att HotPDF:s 'X-1a'-värde skriver PDF/X-1a:2001-identifieringen i XMP-paketet

Om ditt innehåll faktiskt är RGB och ska förbli så är X-1a fel mål. HotPDF avgränsar den här kontrollen till 'X-1a' bara, så 'X-3' och 'X-4', som tillåter ICC-baserad och kalibrerad färg, är opåverkade

Vad kontrollerar EnsurePDFX1aContentColorSpaces egentligen?

EnsurePDFX1aContentColorSpaces kombinerar en rit-tidsflagga med en rekursiv genomgång av varje sidas resurser, och den körs sist i PDF/X-porten. EndDoc driver först de allmänna PDF/X-kraven: Title måste vara satt, Trapped måste vara 'True', 'False' eller 'Unknown', en GTS_PDFX OutputIntent måste finnas (EnsurePDFXOutputIntent), och varje sida behöver en TrimBox eller ArtBox (EnsurePDFXPageBoxes). Först efter att de klarats körs färgkontrollen

Sekvensen i PDF/X-porten inuti HotPDF EndDoc för Delphi: metadata, OutputIntent och sideboxar först, sedan RGB-innehållsfärgkontrollen
färgkontrollen körs sist i X-1a-porten, och en rit-tidsflagga betyder att RGB ritad innan överensstämmelsen tilldelats ändå fångas

Den första vägen är en dokumentvid flagga. HotPDF:s RGB-färgsättare, inbäddning av bitmapbilder och annoteringens färgposter registrerar DeviceRGB-användning medan du ritar, vilket täcker inline rg- och RG-operatorer som aldrig syns i en resursdictionary. Den andra vägen går igenom varje sidas /Resources och inspekterar:

  • Namngivna färgrymder i /ColorSpace, inklusive inline-bildförkortningen RGB som synonym för DeviceRGB
  • /ColorSpace för varje Image XObject
  • Form XObjects och tiling Patterns, genom att rekursera in i deras egna nästade /Resources
  • Shading Patterns och poster i sidans /Shading-dictionary, via shadings /ColorSpace
  • Indexed-basrymder, Separation- och DeviceN-alternativ, och Pattern-basrymder, följda ner till ett nästningsdjup av 8

Tre saker avvisas: DeviceRGB, CalRGB, och ICCBased-rymder vars profilström deklarerar N = 3. Resursdictionaryer delade mellan Form XObjects och patterns besöks en gång, så en ömsesidigt refererande uppsättning former skickar inte genomgången in i en loop. Metoden är publik på THotPDF, men i normal användning anropar du den aldrig själv, för EndDoc gör det åt dig

Var RGB gömmer sig i PDF-sidresurser: nästade former, patterns, shadings och alternativ leder HotPDF:s genomgång i Delphi till DeviceRGB, CalRGB eller ICCBased RGB
genomgången följer nästade resurser ner till djup 8 och avvisar DeviceRGB, CalRGB och ICCBased-rymder med N = 3 vart de än dyker upp

Rita en X-1a-sida i Delphi som klarar kontrollen

Ett överensstämmande HotPDF X-1a-dokument behöver en CMYK-OutputIntent, en TrimBox på varje sida, och färg satt bara genom CMYK-, Gray- och spot-API:erna. Skelettet nedan använder FOGRA39-profilen; AddPDFXOutputIntent tar utskriftsvillkorets identifierare, en infosträng, ICC-profilströmmen, dess komponentantal, och alternativrymden

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);   // krävs på varje sida

    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;                                    // färgkontrollen körs här
  finally
    Pdf.Free;
  end;
end;

Varje TColor-baserat anrop är en fälla i det här sammanhanget. SetRGBFillColor, SetRGBStrokeColor och SetRGBColor tar alla en GDI-färg och avger DeviceRGB, så en hjälprutin skriven för år sedan för skärmorienterade rapporter smutsar ner ett tryckjobb i samma stund den återanvänds. Använd SetCMYKFillColor, SetCMYKStrokeColor eller SetCMYKColor med komponenter i intervallet 0 till 1, eller Gray-motsvarigheterna

Spotfärger och CMYK-gradienter

Spotfärgsämnen tillåts i X-1a så länge deras alternativ är CMYK eller Gray. RegisterSeparation bygger Separation-rymden och returnerar dess resursnamn, och RegisterAxialGradient plockar shadingens färgrymd utifrån antalet komponenter du skickar, med fyra komponenter som betyder DeviceCMYK

var
  SpotCS, Ramp: AnsiString;
begin
  // Spotbläck med en ungefärlig CMYK-uppbyggnad som alternate
  SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
    [0, 1, 0.8, 0.05]);
  Pdf.CurrentPage.SetFillColorSpace(SpotCS);
  Pdf.CurrentPage.SetFillColor([1.0]);           // 100 % ton
  Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
  Pdf.CurrentPage.Fill;

  // 4 komponenter per stopp: shadingen är DeviceCMYK, inte 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;

Skicka tre komponenter till samma gradientanrop och du får en DeviceRGB-shading, som resursgenomgången avvisar under shading-patterns namn. Samma gäller en Separation registrerad med 'DeviceRGB' som alternativ: spotnamnet ser harmlöst ut, men genomgången följer alternativet och hittar RGB under. Hur sådana spotbläck komponeras när en viewer eller RIP faller tillbaka till alternativet tas upp i artikeln om att rendera Separation- och DeviceN-spotfärger i Delphi

Rasterbilder behöver samma disciplin. HotPDF spikar Flate-komprimerade bitmaps till DeviceRGB, medan en JPEG inbäddad med icJpeg behåller sitt eget komponentantal, så en fyra-komponents CMYK JPEG eller en gråskale-JPEG är fin och en vanlig RGB JPEG är det inte. Konvertera grafiken till pressprofilen i din bildpipeline innan den når PDF:en

Varför räddar /DefaultRGB inte en PDF/X-1a-fil?

/DefaultRGB hjälper inte X-1a för regeln handlar om vad pressen tar emot, inte om RGB:en är karakteriserad. För PDF/A låter HotPDF RegisterDefaultColorSpace('DefaultRGB', ...) absorbera DeviceRGB-användning: när en Default-färgrymd en gång mappar DeviceRGB till en ICC-profil är innehållet device-oberoende och PDF/A-konsekvenskontrollen accepterar det. Utvecklare som varit igenom det arbetsflödet tenderar att prova samma fix här

EnsurePDFX1aContentColorSpaces ignorerar den mappningen medvetet. Ett karakteriserat RGB-värde är fortfarande RGB, och X-1a-OutputIntenten är ett CMYK-tryckvillkor, så någon skulle ändå behöva konvertera det. Om du ska minnas en skillnad mellan de två arkiverings- och tryckprofilerna, låt det bli den här: PDF/A frågar om en färg är entydig, PDF/X-1a frågar om den redan är bläck

Ritordning, och den siduppsättning som inte räknas

Eftersom kontrollen körs vid EndDoc över redan skrivet innehåll spelar ordningen du sätter upp saker i ingen roll. RGB ritad innan PDFXCompliance tilldelats, eller innan OutputIntenten registrerades, fångas ändå. Det spelar roll för kod som bygger sidor i delade rutiner och bestämmer utdataprofilen sent, ibland från en användarinställning vald efter att halva dokumentet finns

Pdf.BeginDoc;
// ... OutputIntent och TrimBox som tidigare ...
Pdf.CurrentPage.SetRGBFillColor(clRed);   // äldre hjälprutin, avger DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;

Pdf.PDFXCompliance := 'X-1a';             // beslutat efter ritandet
Pdf.EndDoc;                               // kastar ändå: DeviceRGB registrerad

En rit-tidsflagga har en fälla värd att känna till om du bygger en liknande kontroll själv. Varje ny sida initieras med ett svart graphics state via SetRGBColor(0), som avger ett 0 0 0 rg- och 0 0 0 RG-par, och om den siduppsättningskittet fick sätta DeviceRGB-flaggan skulle varje dokument se RGB-nedsmutsat ut, både för den här kontrollen och för PDF/A-färgkonsekvenskontrollen. HotPDF sparar och återställer flaggan runt den initieringen, så bara färg du faktiskt ritar räknas

Hur hittar du RGB-innehållet när EndDoc kastar?

Läs platsen i början av exceptionmeddelandet, för den talar om vilken av de två vägarna som hittade problemet. Meddelandet har formen PDFXCompliance 'X-1a': <where> uses <what>, följt av ISO 15930-1-referensen och rådet att konvertera till CMYK eller Gray eller byta till X-3 eller X-4. Validatorn kastar ett vanligt Exception vid första överträdelsen, så matcha på texten snarare än på en exceptionklass

Anatomin i HotPDF EndDoc RGB-exception i Delphi: rit-tidsflaggan rapporterar dokumentvid användning medan resursgenomgången namnger exakt sida och nyckel
läs platsen i början av meddelandet — en dokumentvid rapport skickar dig till ritkoden, ett sid- och resursnamn pekar på en enda nyckel
  • document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing) betyder att rit-tidsflaggan slog till. Det finns inget sidnummer, för flaggan är dokumentvid och kontrolleras före genomgången. Sök din ritkod efter SetRGB*-anrop, Flate-komprimerade bitmaps, RGB-JPEG:er och TColor-annoteringfärger
  • page 3 image XObject /Im2 uses DeviceRGB content eller page 1 shading Pattern /P1 uses DeviceRGB content kommer från resursgenomgången och pekar på en exakt resursnyckel
  • uses an ICCBased RGB colour space (N=3) efter en ColorSpace /...-plats är det typiska resultatet av att registrera en sRGB-profil med RegisterICCProfile och välja den via SetFillColorSpace, en väg rit-tidsflaggan inte ser
  • Nästade platser kedjas ihop, till exempel page 2 Form XObject /Fm1 ColorSpace /CS1, så du kan följa vägen från sidan ner till resursen som bär RGB-rymden
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;

Eftersom bara den första överträdelsen rapporteras är fixande av ett jobb med flera RGB-källor iterativt: fixa, bygg om, läs nästa meddelande. För batchjobb lönar det sig att logga hela meddelandet per fil och hålla en kort lista över kända synder, som en logobitmap eller en diagramhjälp som fortfarande tar TColor

Var RGB-porten passar in i din preflight

EnsurePDFX1aContentColorSpaces är en riktad RGB-port som körs inuti din build, och den paras med en fullständig PDF/X-1a-preflight i stället för att ersätta en. Den täcker RGB-familjerna, genom resursgenomgången från varje sidas /Resources och genom rit-tidsflaggan som HotPDF:s färgsättare underhåller för inline rg- och RG-operatorer; nästade färgrymder följs ner till djup 8 som skydd mot felformade cykler, och den första överträdelsen rapporteras per EndDoc. Andra färgfamiljer som Lab eller CalGray är en fråga för hela preflighten, så kör den färdiga filen genom en extern validator som del av din build för att få hela överensstämmelsedomslutet. Den bredare bilden av överensstämmelseutdata finns i guiden till PDF/A-, PDF/X- och PDF/UA-utdata med HotPDF, och att koppla in en validator i en obemannad pipeline tas upp i att automatisera PDF-preflightkontroller i Delphi

HotPDF är en nativ VCL PDF-komponent för Delphi och C++Builder med PDF/X-, PDF/A- och PDF/UA-utdata, CMYK- och spotfärg-API:er, och stöd för ICC OutputIntent. Detaljer, licensiering och testnedladdningen finns på produktsidan för HotPDF Delphi PDF-komponenten