Technischer Artikel

HotPDF PDF/X-1a-Farbprüfung: RGB-Inhalte in Delphi finden

Wenn PDFXCompliance auf 'X-1a' steht, prüft HotPDF die Inhaltsfarben ganz am Ende des Jobs: EndDoc ruft THotPDF.EnsurePDFX1aContentColorSpaces auf, das DeviceRGB-, CalRGB- oder ICCBased-RGB-Inhalte an jeder Stelle auf jeder Seite abweist und eine Exception wirft, welche die betroffene Ressource benennt. Die Datei wird nie als PDF/X-1a-Dokument geschrieben, das die Druckerei zurückschickt

Dieser letzte Teil ist der Punkt. Eine PDF/X-1a-Datei mit einem einzelnen herumstreunenden RGB-Logo sieht auf dem Bildschirm tadellos aus, besteht eine beiläufige Sichtprüfung und fällt dann im Preflight der Druckerei zwei Tage vor dem Maschinenslot durch. Das im eigenen Delphi-Build zu erwischen, mit einer Meldung, die dir sagt, welche Seite und welches Bild, ist deutlich billiger, als es von einem genervten Druckvorstufen-Operator zu erfahren

Warum weist PDF/X-1a RGB-Inhalte ab?

PDF/X-1a weist RGB ab, weil das Format verspricht, dass jede Farbe bereits final für eine bestimmte CMYK-Druckbedingung ist. ISO 15930-1:2001 §6.2.3 begrenzt die Inhaltsfarben auf DeviceCMYK, DeviceGray und Spot-Farben, gemeint sind Separation- und DeviceN-Räume, deren Alternates CMYK oder Gray sind. Der in der Datei eingebettete GTS_PDFX-OutputIntent nennt jene Druckbedingung, etwa FOGRA39 coated offset, und der empfangende Workflow darf die Zahlen ohne jede Farbkonvertierung direkt an die Platten schicken

RGB bricht diesen Vertrag. Ein RGB-Wert beschreibt eine Farbe relativ zu einer Anzeige oder einem Arbeitsfarbraum, kein Tintenrezept, also müsste irgendwer Downstream entscheiden, wie er zu separieren ist, und X-1a existiert gerade, um dem Empfänger diese Entscheidung abzunehmen. ISO 32000-1 §8.6 gruppiert PDF-Farbräume in Geräte-, CIE-basierte und besondere Familien, und die RGB-Mitglieder tauchen in mehr als einer Familie auf: DeviceRGB ist ein Geräterraum, CalRGB und ICCBased mit drei Komponenten sind CIE-basiert, und ein Indexed- oder Separation-Raum kann eine RGB-Basis oder -Alternative darunter verstecken. Ein Prüfer, der nur nach dem rg-Operator sucht, verpasst die meisten davon. Die spätere Ausgabe PDF/X-1a:2003 (ISO 15930-4) behält dieselbe Farbregel; beachte, dass HotPDFs 'X-1a'-Wert die PDF/X-1a:2001-Identifikation ins XMP-Paket schreibt

Wenn deine Inhalte wirklich RGB sind und so bleiben sollen, ist X-1a das falsche Ziel. HotPDF begrenzt diese Prüfung auf 'X-1a', also bleiben 'X-3' und 'X-4', die ICC-basierte und kalibrierte Farbe erlauben, unberührt

Was EnsurePDFX1aContentColorSpaces tatsächlich prüft

EnsurePDFX1aContentColorSpaces kombiniert ein Draw-Zeitpunkt-Flag mit einem rekursiven Gang durch die Ressourcen jeder Seite und läuft zuletzt im PDF/X-Tor. EndDoc setzt zuerst die allgemeinen PDF/X-Anforderungen durch: Title muss gesetzt sein, Trapped muss 'True', 'False' oder 'Unknown' sein, ein GTS_PDFX-OutputIntent muss existieren (EnsurePDFXOutputIntent), und jede Seite braucht eine TrimBox oder ArtBox (EnsurePDFXPageBoxes). Erst nach deren Bestehen läuft die Farbprüfung

Sequenz des PDF/X-Tors innerhalb von HotPDF EndDoc für Delphi: zuerst Metadaten, OutputIntent und Seitenboxen, dann die RGB-Inhaltsfarbprüfung
die Farbprüfung läuft zuletzt im X-1a-Tor, und ein Draw-Zeitpunkt-Flag heißt, RGB, das vor der Compliance-Zuweisung gezeichnet wurde, wird trotzdem erwischt

Der erste Pfad ist ein dokumentweites Flag. HotPDFs RGB-Farbsetter, das Einbetten von Bitmap-Bildern und die Anmerkungs-Farbeinträge notieren die DeviceRGB-Verwendung während des Zeichnens, was Inline-rg- und RG-Operatoren abdeckt, die nie in einem Resource-Dictionary auftauchen. Der zweite Pfad geht jedes /Resources einer Seite durch und inspiziert:

  • Benannte Farbräume in /ColorSpace, inklusive der Inline-Image-Abkürzung RGB als Synonym für DeviceRGB
  • Das /ColorSpace jedes Image-XObjects
  • Form-XObjects und tiling-Patterns, durch Rekursion in ihre eigenen verschachtelten /Resources
  • Shading-Patterns und Einträge im /Shading-Dictionary der Seite, über das /ColorSpace des Shading
  • Indexed-Basisräume, Separation- und DeviceN-Alternates sowie Pattern-Basisräume, verfolgt bis zu einer Verschachtelungstiefe von 8

Drei Dinge werden abgewiesen: DeviceRGB, CalRGB und ICCBased-Räume, deren Profil-Stream N = 3 deklariert. Zwischen Form-XObjects und Patterns geteilte Resource-Dictionarys werden einmal besucht, sodass ein sich gegenseitig referenzierender Form-Satz den Gang nicht in eine Schleife schickt. Die Methode ist öffentlich auf THotPDF, im Normalgebrauch rufst du sie aber nie selbst auf, denn EndDoc macht das für dich

Wo RGB sich in den Ressourcen einer PDF-Seite versteckt: verschachtelte Forms, Patterns, Shadings und Alternates führen den HotPDF-Gang in Delphi zu DeviceRGB, CalRGB oder ICCBased RGB
der Gang folgt verschachtelten Ressourcen bis Tiefe 8 und weist DeviceRGB, CalRGB und ICCBased-Räume mit N = 3 ab, wo immer sie auftauchen

Eine X-1a-Seite in Delphi zeichnen, die die Prüfung besteht

Ein konformes HotPDF-X-1a-Dokument braucht einen CMYK-OutputIntent, eine TrimBox auf jeder Seite und Farbe, gesetzt nur über die CMYK-, Gray- und Spot-APIs. Das Skelett unten nutzt das FOGRA39-Profil; AddPDFXOutputIntent nimmt die Output-Condition-Kennung, einen Info-String, den ICC-Profil-Stream, seine Komponentenzahl und den alternativen Farbraum

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);   // nötig auf jeder Seite

    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;                                    // Farbprüfung läuft hier
  finally
    Pdf.Free;
  end;
end;

Jeder TColor-basierte Aufruf ist in diesem Kontext eine Falle. SetRGBFillColor, SetRGBStrokeColor und SetRGBColor nehmen alle eine GDI-Farbe entgegen und geben DeviceRGB aus, also verunreinigt eine vor Jahren für bildschirmorientierte Reports geschriebene Helferroutine einen Druckjob, sobald man sie wiederverwendet. Nimm SetCMYKFillColor, SetCMYKStrokeColor oder SetCMYKColor mit Komponenten im Bereich 0 bis 1, oder die Gray-Entsprechungen

Spot-Farben und CMYK-Verläufe

Spot-Farben sind in X-1a erlaubt, solange ihr Alternate CMYK oder Gray ist. RegisterSeparation baut den Separation-Raum und liefert seinen Ressourcennamen, und RegisterAxialGradient wählt den Shading-Farbraum nach der Komponentenzahl, die du übergibst, wobei vier Komponenten DeviceCMYK bedeuten

var
  SpotCS, Ramp: AnsiString;
begin
  // Spot-Tinte mit einer angenäherten CMYK-Mischung als Alternate
  SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
    [0, 1, 0.8, 0.05]);
  Pdf.CurrentPage.SetFillColorSpace(SpotCS);
  Pdf.CurrentPage.SetFillColor([1.0]);           // 100% Anteil
  Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
  Pdf.CurrentPage.Fill;

  // 4 Komponenten pro Stopp: das Shading ist DeviceCMYK, nicht 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;

Übergib denselben Gradient-Aufruf drei Komponenten, und du bekommst ein DeviceRGB-Shading, das der Ressourcengang unter dem Namen des Shading-Patterns ablehnt. Dasselbe gilt für eine Separation, registriert mit 'DeviceRGB' als Alternate: Der Spot-Name sieht harmlos aus, aber der Gang folgt dem Alternate und findet darunter RGB. Wie solche Spot-Tinten komponiert werden, wenn ein Viewer oder RIP auf den Alternate zurückfällt, behandelt der Artikel zu Separation- und DeviceN-Spot-Farben in Delphi rendern

Rasterbilder brauchen dieselbe Disziplin. HotPDF pinnt Flate-komprimierte Bitmaps auf DeviceRGB, während ein mit icJpeg eingebettetes JPEG seine eigene Komponentenzahl behält, also ist ein Vierkomponenten-CMYK-JPEG oder ein Graustufen-JPEG in Ordnung und ein gewöhnliches RGB-JPEG nicht. Konvertiere Artwork in deiner Imaging-Pipeline zum Druckprofil, bevor es das PDF erreicht

Warum rettet /DefaultRGB keine PDF/X-1a-Datei?

/DefaultRGB hilft bei X-1a nicht, denn die Regel handelt davon, was die Maschine empfängt, nicht davon, ob das RGB charakterisiert ist. Für PDF/A lässt HotPDF RegisterDefaultColorSpace('DefaultRGB', ...) die DeviceRGB-Verwendung absorbieren: Sobald ein Default-Farbraum DeviceRGB auf ein ICC-Profil mappt, ist der Inhalt geräteunabhängig, und die PDF/A-Konsistenzprüfung akzeptiert ihn. Entwickler, die diesen Workflow hinter sich haben, versuchen hier gern dieselbe Reparatur

EnsurePDFX1aContentColorSpaces ignoriert dieses Mapping bewusst. Ein charakterisierter RGB-Wert bleibt RGB, und der X-1a-OutputIntent ist eine CMYK-Druckbedingung, also müsste ihn trotzdem jemand konvertieren. Wenn du dir einen Unterschied zwischen den beiden Archiv- und Druckprofilen merken willst, dann diesen: PDF/A fragt, ob eine Farbe eindeutig ist, PDF/X-1a fragt, ob sie bereits Tinte ist

Die Zeichenreihenfolge, und das Seiten-Setup, das nicht zählt

Weil die Prüfung bei EndDoc über bereits geschriebene Inhalte läuft, spielt die Reihenfolge, in der du das Setup machst, keine Rolle. RGB, gezeichnet bevor PDFXCompliance zugewiesen war oder bevor der OutputIntent registriert war, wird trotzdem erwischt. Das zählt für Code, der Seiten in gemeinsamen Routinen baut und sich spät für das Ausgabeprofil entscheidet, manchmal aus einer Benutzereinstellung, gewählt, nachdem das halbe Dokument existiert

Pdf.BeginDoc;
// ... OutputIntent und TrimBox wie zuvor ...
Pdf.CurrentPage.SetRGBFillColor(clRed);   // Legacy-Helfer, gibt DeviceRGB aus
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;

Pdf.PDFXCompliance := 'X-1a';             // nach dem Zeichnen entschieden
Pdf.EndDoc;                               // wirft trotzdem: DeviceRGB vermerkt

Ein Draw-Zeitpunkt-Flag hat eine Falle, die sich zu kennen lohnt, wenn du selbst eine ähnliche Prüfung baust. Jede neue Seite wird mit einem schwarzen Grafikzustand initialisiert, über SetRGBColor(0), was ein Paar 0 0 0 rg und 0 0 0 RG ausgibt, und dürfte jenes Seiten-Setup-Boilerplate das DeviceRGB-Flag setzen, sähe jedes Dokument RGB-verunreinigt aus, für diese Prüfung wie für die PDF/A-Farb-Konsistenzprüfung. HotPDF sichert das Flag um jene Initialisierung herum und stellt es wieder her, also zählt nur Farbe, die du tatsächlich zeichnest

Wie findest du den RGB-Inhalt, wenn EndDoc wirft?

Lies die Ortsangabe am Anfang der Exception-Meldung, denn sie sagt dir, welcher der beiden Pfade das Problem gefunden hat. Die Meldung hat die Form PDFXCompliance 'X-1a': <where> uses <what>, gefolgt von der ISO-15930-1-Referenz und dem Rat, zu CMYK oder Gray zu konvertieren oder auf X-3 oder X-4 zu wechseln. Der Validator wirft eine schlichte Exception beim ersten Verstoß, matche also auf den Text statt auf eine Exception-Klasse

Anatomie der HotPDF-EndDoc-RGB-Exception in Delphi: das Draw-Zeitpunkt-Flag meldet dokumentweite Verwendung, während der Ressourcengang die exakte Seite und den Schlüssel benennt
lies die Ortsangabe am Anfang der Meldung, ein dokumentweiter Report schickt dich in den Zeichen-Code, ein Seiten- und Ressourcenname zeigt auf einen Schlüssel
  • document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing) heißt, das Draw-Zeitpunkt-Flag hat ausgeschlagen. Es gibt keine Seitennummer, denn das Flag ist dokumentweit und wird vor dem Gang geprüft. Durchsuche deinen Zeichen-Code nach SetRGB*-Aufrufen, Flate-komprimierten Bitmaps, RGB-JPEGs und TColor-Anmerkungs-Farben
  • page 3 image XObject /Im2 uses DeviceRGB content oder page 1 shading Pattern /P1 uses DeviceRGB content kommt aus dem Ressourcengang und zeigt auf einen exakten Ressourcenschlüssel
  • uses an ICCBased RGB colour space (N=3) nach einer ColorSpace /...-Ortsangabe ist das typische Ergebnis, ein sRGB-Profil mit RegisterICCProfile zu registrieren und es über SetFillColorSpace zu wählen, ein Pfad, den das Draw-Zeitpunkt-Flag nicht sieht
  • Verschachtelte Ortsangaben ketten sich, etwa page 2 Form XObject /Fm1 ColorSpace /CS1, du kannst dem Pfad also von der Seite hinunter zu der Ressource folgen, die den RGB-Raum trägt
try
  Pdf.EndDoc;
except
  on E: Exception do
  begin
    if Pos('X-1a', E.Message) > 0 then
      LogPrepressFailure(Pdf.FileName, E.Message);  // dein eigener Logger
    raise;
  end;
end;

Weil nur der erste Verstoß gemeldet wird, ist die Reparatur eines Jobs mit mehreren RGB-Quellen iterativ: beheben, neu bauen, nächste Meldung lesen. Bei Batch-Jobs zahlt es sich aus, die volle Meldung je Datei zu loggen und eine kurze Liste bekannter Täter zu führen, etwa eine Logo-Bitmap oder einen Chart-Helfer, der noch TColor nimmt

Wo das RGB-Tor in dein Preflight gehört

EnsurePDFX1aContentColorSpaces ist ein gezieltes RGB-Tor, das innerhalb deines Builds läuft, und es ergänzt einen vollständigen PDF/X-1a-Preflight, statt ihn zu ersetzen. Es deckt die RGB-Familien ab, über den Ressourcengang von jedem /Resources der Seite und über das Draw-Zeitpunkt-Flag, das HotPDFs Farbsetter für Inline-rg- und RG-Operatoren führen; verschachtelte Farbräume werden bis Tiefe 8 verfolgt als Schutz gegen missgestaltete Zyklen, und der erste Verstoß wird je EndDoc gemeldet. Andere Farb-Familien wie Lab oder CalGray sind Sache des vollen Preflights, also jage die fertige Datei als Teil deines Builds durch einen externen Validator für das vollständige Konformitätsurteil. Das größere Bild der Konformitätsausgabe zeigt der Leitfaden zu PDF/A-, PDF/X- und PDF/UA-Ausgabe mit HotPDF, und einen Validator in eine unbeaufsichtigte Pipeline zu hängen, behandelt PDF-Preflight-Prüfungen in Delphi automatisieren

HotPDF ist eine native VCL-PDF-Komponente für Delphi und C++Builder mit PDF/X-, PDF/A- und PDF/UA-Ausgabe, CMYK- und Spot-Farb-APIs und ICC-OutputIntent-Unterstützung. Details, Lizenzierung und der Trial-Download liegen auf der Produktseite der HotPDF Delphi PDF component