Technischer Artikel

PDF/X-1a, X-3 und X-4 mit PDFlibPas in Delphi erzeugen

PDFlibPas erzeugt PDF/X-Druckdateien aus Delphi mit einem einzigen Aufruf, TPDFlib.SetPDFXMode, der PDF/X-1a:2003, PDF/X-3:2003 oder PDF/X-4 wählt. Der Aufruf schreibt die XMP-pdfxid-Identifikation, fügt einen GTS_PDFX-Output-Intent hinzu, deklariert /Trapped /False, fixiert die PDF-Version und verweigert von da an jeden Zeichenaufruf, den der gewählte Part verbietet. CheckFileCompliance mit Test 3 auditiert die gespeicherte Datei

Das ist die Kurzantwort. Die lange zählt, weil Druckereien Dateien nicht wegen hässlicher Optik zurückweisen, sondern im Preflight, meist an einem Freitag, meist wegen etwas, das der erzeugende Code hätte abfangen können: ein RGB-Logo in einem X-1a-Job, ein Font, der referenziert statt eingebettet wurde, eine Seite ohne Zuschnittgröße. PDF/X existiert, um diese Fehler strukturell statt visuell zu machen, und die Bibliotheksseite der Aufgabe ist, dass Sie sie gar nicht erst erzeugen

Welchen PDF/X-Part sollte eine Delphi-Anwendung anvisieren?

Nehmen Sie PDF/X-4, solange der empfangende Workflow nichts anderes verlangt, und PDF/X-1a, wenn er es tut. Die drei Parts unterscheiden sich auf genau drei Achsen: Farbe, Transparenz und Basis-PDF-Version; und jede Achse mappt auf ein Gate in PDFlibPas:

  • PDF/X-1a:2003 (ISO 15930-4): PDF-1.4-Syntax, nur CMYK, Grau- und Sonderfarben, keine ICC-basierte Farbe, keine Transparenz. Die Datei ist bereits in Druckfarben separiert, sodass die Druckerei gar keine Farbumwandlung mehr macht
  • PDF/X-3:2003 (ISO 15930-6): weiterhin PDF 1.4 und weiterhin keine Transparenz, aber farbverwaltete Farbräume sind erlaubt, und Device-RGB wird akzeptiert, wenn der Output-Intent selbst eine RGB-Bedingung beschreibt
  • PDF/X-4 (ISO 15930-7): PDF-1.6-Syntax, mit echter Transparenz und Optionalem Content (Ebenen). Das ist der Part, für den moderne RIPs gebaut sind, denn das Flatten von Transparenz auf dem Weg hinaus war die Quelle viel des alten X-1a-Schmerzes

X-1a ist in der Werbemittel-Auslieferung und in älteren Druck-Workflows, die eine Datei wollen, die sie ohne Nachdenken belichten können, nach wie vor überall. X-4 ist die bessere Ingenieurswahl, wo immer die Druckerei ihn akzeptiert: Die Transparenz überlebt unangetastet, und die Farbentscheidungen fallen am RIP mit dem echten Druckprofil statt in Ihrem Code. Die Mode-Nummern sind 1, 2 und 3, und GetInformation(203) meldet den aktiven Mode zurück. PDFlibPas definiert auch höhere Mode-Nummern für die PDF/X-6-Familie auf PDF 2.0, die anderen Versions- und Output-Intent-Regeln folgen und hier außer Scope bleiben

Was SetPDFXMode in die Datei schreibt

TPDFlib.SetPDFXMode übernimmt die Identifikation und den Boilerplate, die ein PDF/X-Reader zuerst prüft, sodass der Anwendungscode nur das echte Druckprofil und den Content liefern muss. Konkret tut ein erfolgreicher Aufruf vier Dinge:

  • Schreibt den Editions-String als pdfxid:GTS_PDFXVersion ins XMP (PDF/X-1a:2003, PDF/X-3:2003 oder PDF/X-4). Die 2003er-Editionen identifizieren den Part vollständig über diesen String, deshalb wird keine separate Conformance-Property geschrieben
  • Fügt einen Output-Intent hinzu, wenn das Dokument keinen hat, und setzt seinen Subtypen auf /S /GTS_PDFX, den Subtypen, nach dem PDF/X-Reader suchen (ISO 32000-1 §14.11.5 definiert das Output-Intent-Dictionary)
  • Setzt /Trapped /False im Document-Information-Dictionary, denn der Writer trappt nie, und PDF/X verlangt, dass der Trapping-Zustand deklariert wird statt unbekannt zu bleiben
  • Fixiert die Versionsbasis: 1.4 für X-1a und X-3, 1.6 für X-4

Zwei weitere Reparaturen passieren beim Speichern. Jede Seite ohne TrimBox bekommt eine, die ihrer MediaBox entspricht, denn PDF/X verlangt eine deklarierte Fertiggröße auf jeder Seite (ISO 32000-1 §14.11.2 behandelt die Seitenbegrenzungen), und Annotationen bekommen das Print-Flag, das der Standard erwartet. Der TrimBox-Fallback ist ein Sicherheitsnetz, keine Layout-Entscheidung. Ein Full-Bleed-Job braucht ein echtes Trim Box innerhalb einer größeren Media Box, und der Guide zu TrimBox-, BleedBox- und CropBox-Defaults in PDFlibPas erklärt, wie man diese Boxen setzt und wie Vererbung Sie überraschen kann

uses
  PDFlibrary;

procedure BuildX1aAd(const OutFile, PressProfile: string);
var
  Pdf: TPDFlib;
begin
  Pdf := TPDFlib.Create;
  try
    if Pdf.SetPDFXMode(1) <> 1 then          // 1 = PDF/X-1a:2003
      raise Exception.CreateFmt('PDF/X mode refused (error %d)',
        [Pdf.LastErrorCode]);

    // Default-Profil ersetzen durch die echte Druckbedingung
    if Pdf.LoadOutputIntentProfile(PressProfile, 'DeviceCMYK') <> 1 then
      raise Exception.Create('Could not load the CMYK output profile');

    // Embed = 1 bettet das komplette Font-Programm ein; PDF/X akzeptiert keine 0
    if Pdf.AddTrueTypeFont('Arial {1252}', 1) = 0 then
      raise Exception.Create('Font could not be loaded for embedding');

    Pdf.SetTextSize(24);
    Pdf.SetFillColorCMYK(0, 1, 1, 0);           // Prozessrot, kein RGB
    Pdf.DrawText(72, 720, 'Spring catalog, page 1');

    Pdf.SaveToFile(OutFile);
  finally
    Pdf.Free;
  end;
end;

Die Reihenfolge in diesem Sketch ist absichtlich. LoadOutputIntentProfile ersetzt das Profil eines bereits existierenden Output-Intents, muss also laufen, nachdem SetPDFXMode einen angelegt hat. Der Aufruf kann auch keinen Intent aus dem Nichts zaubern: Auf einem Dokument ohne Output-Intent liefert er schlicht 0 zurück

Warum muss der Default-Output-Intent ersetzt werden?

Der Output-Intent, den SetPDFXMode für die Modes 1 bis 3 hinzufügt, deklariert DeviceCMYK, aber die ICC-Bytes dahinter sind das mitgelieferte sRGB-Profil der Bibliothek, dessen Komponentenanzahl auf 4 umgeschrieben wurde. Dieser Default existiert aus Kompatibilitätsgründen und reicht, um die Datei strukturell zu PDF/X zu machen, doch das eingebettete Profil beschreibt keine Druckmaschine. Ein Preflight-Tool, das in den Profil-Header schaut, sieht ein RGB-Profil mit CMYK-Label, und eine Druckerei, die sich fürs Proofing auf den Intent verlässt, bekommt Unsinn

Behandeln Sie LoadOutputIntentProfile(FileName, 'DeviceCMYK') deshalb als Pflicht für echte Druckarbeit und übergeben das ICC-Profil, das Ihr Druckdienstleister für Papier und Maschine nennt. TPDFlib.LoadOutputIntentProfile tauscht den DestOutputProfile-Stream aus und aktualisiert /N, den Rest des Intent-Dictionary lässt es unverändert. Insbesondere tragen Output-Condition-Identifier und beschreibende Strings des Default-Intents weiterhin sRGB-typische Werte; wenn Ihre Druckerei also auf den Identifier matcht statt auf das eingebettete Profil, klären Sie vorher, was sie prüft. Das ist eine echte Lücke der 2003er-Defaults, kein Fehler Ihres Codes

Stolperfall eins: die Version, die Sie nicht gewählt haben

Ein neues PDFlibPas-Dokument startet beim Writer-Default von PDF 1.5, was über der 1.4-Basis liegt, die PDF/X-1a und PDF/X-3 verlangen. TPDFlib.SetPDFXMode löst das im Normalfall lautlos auf: Ist die Save-Version nicht gelockt, senkt er die Dokumentversion auf die Part-Basis und macht weiter. Die erste Implementierung hat stattdessen verweigert, und jeder SetPDFXMode(1) auf einem frischen Dokument gab 0 zurück – ein gutes Beispiel für eine Regel, die auf dem Papier korrekt und in der Praxis unbrauchbar ist

Die Verweigerung existiert weiterhin für den einen Fall, in dem sie richtig ist. Hat etwas zuvor LockSaveVersion mit einer Version über der Basis gerufen, gibt SetPDFXMode 0 mit LastErrorCode 601 zurück, denn der Aufrufer hat die Version absichtlich festgenagelt, und die Bibliothek überstimmt das nicht. In geteiltem Document-Setup-Code, wo ein Helper die Version aus unabhängigen Gründen lockt, stirbt ein X-1a-Job genau hier:

Pdf := TPDFlib.Create;
try
  Pdf.LockSaveVersion('1.7');     // gesetzt von einem generischen "House-Defaults"-Helper
  if Pdf.SetPDFXMode(1) = 0 then
    // LastErrorCode = 601: 1.7 ist gelockt, PDF/X-1a braucht 1.4.
    // Für Druckjobs den Lock weglassen. PDF/X-4 ist hier kein Ausweg:
    // seine 1.6-Basis liegt ebenfalls unter dem 1.7-Lock und failt genauso.
    LogError('PDF/X refused, error %d', [Pdf.LastErrorCode]);
finally
  Pdf.Free;
end;

Dasselbe Gate erklärt eine verwandte Grenze. Der PDF/X-Mode ist ein Schalter zur Dokumenterstellung: SetPDFXMode funktioniert auf einem neuen Dokument und gibt bei einem aus einer Datei geladenen 0 zurück, denn die Bibliothek kann für Content, den sie nicht selbst geschrieben hat, nicht garantieren. Ein bestehendes PDF nach PDF/X zu konvertieren ist ein anderer Job, der Inspektion und Reparatur braucht, kein Flag

Stolperfall zwei: Gates, die lautlos verweigern

Sobald ein PDF/X-Mode aktiv ist, verweigert PDFlibPas jeden verbotenen Content-Aufruf genau in dem Moment, in dem Sie ihn machen, statt die Datei später am Preflight scheitern zu lassen. Die Verweigerungen umfassen Device-RGB-Fills und -Strokes (ganz X-1a; X-3 und X-4, außer der Output-Intent ist RGB), ICC-basierte Farben in X-1a, Transparenz und Blend-Modes in X-1a und X-3, Optionalen Content in X-1a und X-3, JavaScript-Actions, eingebettete Datei-Anhänge, den LZW-Bildfilter, Standard-14-Fonts, die un eingebettet bleiben würden, und Public-Key-Verschlüsselung. Passwort-Verschlüsselung über Encrypt wird zur Erstellungszeit nicht gegated, aber der Validator markiert jede verschlüsselte Datei, halten Sie Druckjobs also unverschlüsselt

Was die Verweigerungen nicht teilen, ist ein Fehlercode – und das ist die Falle. Die Farbraum-Gates in SetFillColor, SetLineColor, die ICC-basierten Farbaufrufe und EncryptPublicKey setzen LastErrorCode auf 601, den Conformance-Code der Bibliothek, den auch die PDF/A-Gates nutzen. Die Transparenz-, JavaScript-, Anhang-, LZW- und AddStandardFont-Gates geben schlicht 0 zurück und lassen LastErrorCode unangetastet, was heißt, dass er noch einen Wert von einem früheren Aufruf halten kann. Code, der nach einem Batch von Zeichenaufrufen nur LastErrorCode prüft, verpasst die Hälfte der Verweigerungen – prüfen Sie jeden Rückgabewert:

procedure Require(Ok: Integer; const What: string);
begin
  if Ok = 0 then
    raise EPrintConformance.CreateFmt('%s refused under PDF/X', [What]);
end;

Require(Pdf.SetFillColorCMYK(0, 0, 0, 1), 'CMYK fill');
Require(Pdf.SetTransparency(40), 'transparency');   // 0 in X-1a / X-3
Require(Pdf.AddLinkToJavaScript(72, 72, 120, 20,
  'app.alert(1)', 0), 'JavaScript link');           // 0 in jedem Part

Fonts verdienen eine eigene Warnung. AddStandardFont gibt unter PDF/X 0 zurück, weil die Bibliothek die Standard-14-Outlines nicht mitliefert und sie daher nicht einbetten kann. AddTrueTypeFont ist anders: Im PDF/A-Mode stuft er Embed = 0 für Sie zu einem vollständigen Embed auf, im PDF/X-Mode tut er das nicht, also landet ein referenzierter Systemfont in der Datei, und nur der Validator erwischt es. Übergeben Sie selbst 1 (vollständig) oder 2 (Subset) oder rufen einmal beim Setup SetEmbedAllFonts(1), damit eine verirrte 0 nicht durchrutscht

Wie prüfen Sie eine PDF/X-Datei, bevor sie zur Druckerei geht?

Fahren Sie CheckFileCompliance mit ComplianceTest = 3 über die gespeicherte Datei. TPDFlib.CheckFileCompliance gibt 0 zurück, wenn er nichts gefunden hat, sonst ein String-List-Handle, dessen Items Zeilen mit dem Präfix PDFX- sind. Das PDF/X-Audit liest die Datei durch denselben Streaming-Reader wie das PDF/A- und PDF/UA-Preflight in PDFlibPas und prüft die Regeln, die ein Druck-Preflight zuerst prüft:

  • Die Datei ist nicht verschlüsselt, das XMP trägt eine bekannte GTS_PDFXVersion, und die PDF-Version überschreitet die Part-Basis nicht
  • Genau ein Output-Intent existiert, sein Subtyp ist GTS_PDFX, und er trägt ein Ziel-Profil und einen Output-Condition-Identifier
  • Das Information-Dictionary deklariert /Trapped
  • Jede Seite hat eine TrimBox, die innerhalb der MediaBox liegt, und eine BleedBox, wenn vorhanden, umschließt die TrimBox
  • Jeder auf einer Seite verwendete Font hat ein eingebettetes Programm, wobei Type0-Fonts über ihren Descendant-Font geprüft werden
  • Keine verbotenen Annotationstypen (RichMedia, Screen, 3D, Movie, Sound, FileAttachment), keine JavaScript-Open-Action oder document-level-JavaScript, keine eingebetteten Dateien und kein LZWDecode-Filter irgendwo im Objektgraphen
function PreflightPDFX(Pdf: TPDFlib; const FileName: string;
  Report: TStrings): Boolean;
var
  ListId, I: Integer;
begin
  ListId := Pdf.CheckFileCompliance(FileName, '', 3, 0);  // 3 = PDF/X
  Result := ListId = 0;
  if Result then
    Exit;
  try
    for I := 1 to Pdf.GetStringListCount(ListId) do       // Items sind 1-basiert
      Report.Add(Pdf.GetStringListItem(ListId, I));
  finally
    Pdf.ReleaseStringList(ListId);
  end;
end;

Seien Sie sich im Klaren, was dieses Bestehen bedeutet. Der PDFlibPas-Validator auditiert die oben gelisteten Strukturregeln, und das sind die, die Generatorfehler erwischen. Er ist kein farbakkurates Preflight: Er prüft keine Farbauftragssumme, keine Bildauflösung und nicht, ob das eingebettete Profil zur Bedingung passt, die Ihre Druckerei fährt. Nutzen Sie ihn als Gate in Ihrer Build- oder Export-Pipeline und überlassen die maschinenspezifischen Prüfungen dem Preflight-Profil der Druckerei – das fahren sie sowieso

FAQ

Unterstützt PDFlibPas PDF/X-4-Transparenz?

Ja. In SetPDFXMode(3) erlaubt PDFlibPas Transparenz, Blend-Modes und Optional-Content-Groups und schreibt eine PDF-1.6-Datei. Dieselben Aufrufe geben unter PDF/X-1a und PDF/X-3 0 zurück

Kann PDFlibPas ein bestehendes PDF nach PDF/X konvertieren?

Nein. SetPDFXMode greift nur auf Dokumente, die in der aktuellen TPDFlib-Session erzeugt wurden, und gibt bei einer geladenen Datei 0 zurück. Ein bestehendes PDF können Sie weiterhin mit CheckFileCompliance und Test 3 auditieren

Welchen Fehlercode liefert ein verweigerter PDF/X-Aufruf?

Farbraum- und Public-Key-Verschlüsselungs-Verweigerungen setzen LastErrorCode auf 601. Andere Gates wie Transparenz, JavaScript und Standard-14-Fonts geben nur 0 zurück, testen Sie also immer das Funktionsergebnis

PDF/X-Ausgabe, der PDF/X-Validator und der Rest des Druck- und Archivierungs-Werkzeugs sind Teil von PDFlibPas, der nativen Object-Pascal-PDF-Bibliothek von losLab für Delphi. Editionen, Plattformen und die vollständige API-Referenz finden Sie auf der PDFlibPas Delphi PDF Library Produktseite