Eine Transaktionsdruckerei schickt Ihren 80.000-seitigen Kontoauszugslauf mit einer einzeiligen Ablehnung zurück: „Kein PDF/VT, RIP kann nicht cachen.“ Die Datei lässt sich in jedem Viewer auf Ihrem Schreibtisch einwandfrei öffnen, die Farben stimmen, die Daten wurden korrekt zusammengeführt. Nichts davon ist das, was die Digitaldruckmaschine verlangt hat. Der Hochgeschwindigkeits-Variablendatendruck steht und fällt damit, ob die Druckmaschine erkennen kann, dass der Kunden-Logo-Block auf Seite 1 Byte für Byte dasselbe Objekt ist wie der auf Seite 40.000, dieses einmal rendert und dann wiederverwendet. PDF/VT is der Standard, der dieses Versprechen maschinell überprüfbar macht, und „sieht korrekt aus“ ist genau die Falle, da die Struktur, die der RIP liest, auf dem Bildschirm unsichtbar ist
PDFiumPas legt diese Struktur über eine kleine Schnittstelle auf TPdf offen: SaveAsPdfVT schreibt sie, ValidatePdfVT prüft sie. In diesem Artikel geht es darum, was diese beiden Methoden tatsächlich auf der Festplatte ablegen und untersuchen, an welchen Stellen ISO 16612-2 strenger ist, als es auf den ersten Blick aussieht, und welche Teile ehrliche strukturelle Verankerungen anstelle eines vollständigen Preflights sind, gegen den Sie einen Kunden abrechnen können
Was PDF/VT standardisiert und warum PDF/X an erster Stelle steht
PDF/VT (ISO 16612-2:2010) ist kein neues Dateiformat. Es handelt sich um eine Ebene von Optimierungs-Metadaten, die auf eine PDF/X-Datei aufgesetzt wird, und diese Reihenfolge ist tragend. Der Standard definiert drei Konformitätsstufen, aber nur zwei davon bezeichnen eine PDF-Datei: PDF/VT-1, ein einzelnes in sich geschlossenes Dokument, und PDF/VT-2, ein Dateisatz-Modell, bei dem Seiten auf gemeinsam genutzte externe Ressourcen verweisen. Das dritte Token, dem Sie begegnen können, PDF/VT-2s, ist überhaupt kein Wert auf Dateiebene; es lebt in einem in Anhang A beschriebenen MIME-Stream-Header. Wenn Sie Code finden, der GTS_PDFVTVersion = "PDF/VT-2s" in die XMP einer Datei schreibt, ist dieser Code fehlerhaft
Die nicht verhandelbare Regel für eine einzelne Datei ist die PDF/X-Basis. ISO 16612-2 §6.2.1 verlangt, dass jede PDF/VT-1-Datei auch eine gültige PDF/X-4-Datei sein muss. Der Dateisatz von PDF/VT-2 muss gemäß §6.2.2 stattdessen auf PDF/X-4p, PDF/X-5g oder PDF/X-5pg basieren. Aus diesem Grund kann ein PDF/VT-Writer nicht einfach ein paar Identifikationsschlüssel anhängen: Er muss den gesamten PDF/X-4-Markersatz mit sich führen, was einen OutputIntent, ein eingebettetes ICC-Zielprofil, die passenden XMP- und Dokument-Info-Einträge, eine Trailer-/ID und keine Verschlüsselung bedeutet. Wenn Sie eines dieser Kriterien auslassen, haben Sie eine Datei, die PDF/VT beansprucht und in dem Moment fehlschlägt, in dem ein konformer Verbraucher die Basis überprüft. PDFiumPas behandelt die PDF/X-4-Ebene als Teil der PDF/VT-Speicherung, sodass Sie nicht zuerst ein separates SaveAsPdfX aufrufen müssen; der Injektor schreibt beide Ebenen in einem einzigen Durchgang
Schreiben einer Datei mit SaveAsPdfVT
Der minimale Aufruf benötigt nichts weiter als ein aktives Dokument, da TPdfVTSaveOptions.Default ein integriertes sRGB-ICC-Profil und die Konformität pvc1 bereitstellt. Das Speichern führt intern drei Schritte aus: Es entfernt jegliche Sicherheitsbeschränkungen (das Injizieren von Klartext-Markern in einen verschlüsselten Objekt-Stream würde diesen beschädigen), es gleicht das vorhandene Info-Dictionary des Dokuments und die Trailer-/ID mit dem Markersatz ab, sodass die XMP- und Info-Werte übereinstimmen, und hängt dann die PDF/X-4- und PDF/VT-Objekte durch ein inkrementelles Update an
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
if Pdf.LoadFromFile('statements-merged.pdf') then
begin
// Default options: built-in sRGB OutputIntent, PDF/VT-1, synthesised DPart
if Pdf.SaveAsPdfVT('statements-pdfvt.pdf') then
Writeln('PDF/VT-1 written')
else
Writeln('Save failed (document not active?)');
end;
finally
Pdf.Free;
end;
end;
Für echte Produktionsausgaben möchten Sie fast immer den OutputIntent durch die Charakterisierung Ihrer Druckmaschine überschreiben, anstatt den generischen sRGB-Fallback zu verwenden. Übergeben Sie die ICC-Bytes und die Bedingungs-Identifikatoren über TPdfVTSaveOptions:
var
Pdf: TPdf;
Opt: TPdfVTSaveOptions;
Icc: TBytes;
begin
Pdf := TPdf.Create(nil);
try
Pdf.LoadFromFile('directmail-merged.pdf');
Icc := LoadIccProfile('GRACoL2013_CRPC6.icc'); // your own loader
Opt := TPdfVTSaveOptions.Default;
Opt.Conformance := pvc1; // pvc2 is normalised to pvc1 on write
Opt.IccProfileData := Icc;
Opt.OutputConditionIdentifier := 'CGATS21_CRPC6';
Opt.OutputCondition := 'Commercial print, coated, CRPC6';
Opt.RegistryName := 'http://www.color.org';
Opt.Title := 'Spring 2026 Direct Mail Run';
Opt.Trapped := ptvFalse; // PDF/X Info /Trapped state
Pdf.SaveAsPdfVT('directmail-pdfvt.pdf', Opt);
finally
Pdf.Free;
end;
end;
Ein Detail in diesem Schnipsel ist eher ein bewusster Schutzmechanismus als eine Einschränkung, über die man streiten kann. Das Setzen von Opt.Conformance := pvc2 erzeugt keine PDF/VT-2-Datei. Der Writer normalisiert jede Nicht-pvc1-Anforderung zurück auf pvc1, da PDF/VT-2 ein Dateisatz-Format ist und ein Einzeildatei-Writer, der ein Ausgabedokument physisch anhängt, die vom Standard §6.2.2 geforderten externen Ressourcen nicht zusammenstellen kann. Der Wert pvc2 existiert für den Lesepfad, damit ValidatePdfVT ein vorhandenes Dateisatz-Dokument erkennen und melden kann; er ist kein Schreibziel
Der DPart-Baum: Die Struktur, die der RIP tatsächlich liest
Das Herzstück von PDF/VT ist die Document Part (DPart)-Hierarchie. Sie ermöglicht es einer Druckmaschine, einen langen Lauf in Datensätze zu unterteilen, Datensätze nach Empfängern oder Postbündeln zu gruppieren und Document Part Metadata anzuhängen, damit nachgelagerte Geräte jedes Teil sortieren und abrechnen können. ISO 16612-2 §6.5 legt die Verkabelung fest: Der Katalog trägt einen /DPartRoot, der DPart-Wurzelknoten trägt /DPartRootNode und eine /NodeNameList, die jede Hierarchieebene benennt, Blatt-DParts (Leaf DParts) decken Bereiche des Seitenbaums ab, und jede Seite, die zu einem Teil gehört, verweist über einen seitenbezogenen /DPart-Eintrag zurück auf ihr Blatt
Wenn Ihr Quelldokument bereits eine nutzbare Hierarchie enthält, bleibt diese durch SaveAsPdfVT erhalten. Wenn nicht, erzeugt der Writer eine minimale Struktur: einen einzelnen DPart auf Dokumentebene, der den aktuellen Seitenbaum in der richtigen Reihenfolge überspannt, mit einer seitenweisen /DPart-Rückreferenz, die an jedes Live-Seitenobjekt angehängt wird, und einer einstufigen /NodeNameList [/Document]. Seien Sie ehrlich zu sich selbst, was diesen minimalen Baum betrifft. Er ist eine strukturelle Verankerung, die die Formanforderungen von §6.5 erfüllt; er ist keine geschäftliche Metadatenstruktur. Er kann keine Empfänger, Poststückgrenzen oder Produktchargen erfinden, da diese Informationen niemals in der Quelle vorhanden waren. Wenn Sie empfängerspezifische Daten haben, wird von Ihnen erwartet, dass Sie selbst einen tieferen DPart-Baum aufbauen und die /NodeNameList entsprechend den von Ihnen erstellten Ebenen erweitern
Validierung, die über das bloße Vorhandensein von Schlüsseln hinausgeht
ValidatePdfVT gibt einen TPdfVTValidationResult-Record mit drei Dingen zurück: der erkannten Conformance, einer menge von Issues und einer IsCompliant-Hilfsfunktion, die nur dann wahr ist, wenn die Konformität einer echten Stufe entspricht und die Menge der Probleme leer ist. Die Problemaufzählung ist bewusst spezifisch gehalten, sodass ein fehlerhaftes Ergebnis Ihnen sagt, welche Klausel Sie verfehlt haben, anstatt nur „ungültig“ zu melden:
var
Pdf: TPdf;
Res: TPdfVTValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.LoadFromFile('statements-pdfvt.pdf');
Res := Pdf.ValidatePdfVT;
if Res.IsCompliant then
Writeln('PDF/VT compliant: ', VTLevelName(Res.Conformance))
else
begin
if pvviMissingDPartRoot in Res.Issues then
Writeln('DPart hierarchy missing or unusable');
if pvviMissingPdfXIdentifier in Res.Issues then
Writeln('PDF/X-4 base identifier absent');
if pvviMissingOutputIntent in Res.Issues then
Writeln('OutputIntent / ICC profile missing');
if pvviEncryptionPresent in Res.Issues then
Writeln('Encrypted - PDF/X forbids this');
end;
finally
Pdf.Free;
end;
end;
Die beiden Prüfungen, die man im Detail verstehen sollte, sind die Konformitätspaarung und der DPart-Durchlauf, da beide früher zu nachsichtig waren und verschärft wurden, um dem Standard zu entsprechen. Auf der Paarungsseite führt der Validator einen exakten Abgleich durch, nicht nach dem Motto „jedes PDF/X reicht aus“: Eine PDF/VT-1-Datei wird nur auf einer PDF/X-4-Basis akzeptiert und eine PDF/VT-2-Datei nur auf PDF/X-4p, PDF/X-5g oder PDF/X-5pg. Ein PDF/VT-1-Marker, der auf einer PDF/X-1a-Basis sitzt, wird gemeldet und nicht einfach durchgewunken
Im DPart-Durchlauf liegt der größte Teil der Strenge. Es reicht nicht aus, dass der Katalog einen /DPartRoot-Schlüssel enthält, da ein gefälschtes leeres Ojekt oder eines ohne Seitenverknüpfungen immer noch nicht verarbeitet werden kann. HasValidDPartHierarchy und das rekursive ValidateDPartNode verfolgen die gesamte Struktur: Sie folgen den Elternverknüpfungen, lehnen doppelte Kinder und Zyklen ab, stellen sicher, dass /Start und /DParts sich gegenseitig ausschließen, und verlangen, dass die Blatt-Seitenbereiche den Seitenbaum in einer Tiefensuche-Reihenfolge abdecken, wobei der /DPart jeder Seite auf das Blatt zeigt, das sie enthält. Alle diese internen Fehler laufen im einzelnen Problem-Bit pvviMissingDPartRoot zusammen, anstatt das öffentliche Enum zu erweitern. Betrachten Sie dieses eine Flag also als „die DPart-Hierarchie ist unbrauchbar“ und nicht wörtlich als „der Wurzel-Schlüssel fehlt“
Drei syntaktische Fallen, die der Validator nun durchsetzt
Aufeinanderfolgende Durchgänge gegen §6.5 Tabelle 4 brachten Formen zutage, die frühere Versionen akzeptierten, der Standard jedoch nicht. Dies sind die typischen Fehler, die bei einem manuell erstellten DPart-Baum auftreten, weshalb sie hier explizit genannt werden sollen:
/DPartsist ein Array von Arrays, kein flaches Array. Jedes Element des äußeren Arrays muss selbst ein Array mit indirekten Referenzen sein. Ein flaches/DParts [9 0 R]wird abgelehnt; die konforme Form ist/DParts [[9 0 R] [10 0 R]]. Dies verhindert, dass sich eine nicht-hierarchische Struktur als gültige Ebene ausgibt/Endmarkiert nur einen echten mehrseitigen Bereich. Ein Blatt-DPart darf/Endnur tragen, wenn es auch/Startbesitzt, und/Endmuss in der Reihenfolge des Seitenbaums nach/Startliegen. Ein degeneriertes/Start 3 0 R /End 3 0 Rführt nun dazu, dass die Hierarchie unbrauchbar wird, anstatt als einseitiger Teil interpretiert zu werden- Namen in
/NodeNameListmüssen die PDF-Namen-Dekodierung als XML-NMTOKENs überstehen. Ein Name wie/Bad#20Nameexpandiert zu einem Namen, der ein Leerzeichen enthält, was kein gültiges Token ist. Die Implementierung führt eine einfache ASCII-Prüfung durch (Buchstaben, Ziffern,.,-,_,:sowie Nicht-ASCII-Bytes), die Leerzeichen- und Trennzeichenfehler abfängt, ohne legitime lokalisierte oder herstellerspezifische Namen abzulehnen
XMP-Marker: Zwei Wege, dieselbe Eigenschaft zu schreiben
Die PDF/VT-Identifizierung lebt im XMP-Bereich unter dem Namensraum pdfvtid, speziell GTS_PDFVTVersion und GTS_PDFVTModDate, neben den Standardwerten xmp:CreateDate und xmp:ModifyDate. Eine Feinheit, die bei naiven Readern zu falschen „Fehlt“-Meldungen führt, besteht darin, dass diese auf zwei Arten serialisiert werden können: als Elementtext (<pdfvtid:GTS_PDFVTVersion>PDF/VT-1</pdfvtid:GTS_PDFVTVersion>) oder als RDF-Attribut im Description-Element. PDFiumPas liest beide Formen, sodass eine Datei, die von einem anderen Tool im Attribut-Stil geschrieben wurde, nicht benachteiligt wird. Es erzwingt auch die Konsistenzregel von §6.3, dass GTS_PDFVTModDate gleich xmp:ModifyDate sein muss; eine Abweichung löst pvviModDateMismatch aus
Eine weitere Regel aus derselben Klausel: Ein unbekannter GTS_PDFVTVersion-Wert wird als pvcUnknown beibehalten, anstatt zu pvcNone zurückgefaltet zu werden. Dieser Unterschied ist betrieblich wichtig. pvcNone bedeutet „überhaupt kein PDF/VT-Marker, ein gewöhnliches PDF“, während pvcUnknown bedeutet „etwas hat eine Version eingeprägt, die dieser Validator nicht erkennt“ (darunter der Fall PDF/VT-2s). Das Vermischen der beiden würde eine fehlerhafte Datei im selben Topf wie ein normales Dokument verstecken
Wo die Garantie endet
Es lohnt sich, genau zu bestimmen, wo die Grenzen dessen liegen, was diese Methoden versprechen, da mit der Einhaltung des Variablendatendrucks echtes Geld verbunden ist. Die DPart- und Paarungsprüfungen sind strukturelle Validierungen auf Byte-Ebene. Sie bestätigen, dass das Optimierungsskelett, die PDF/X-4-Basismarker, der OutputIntent und die XMP vorhanden und in sich konsistent sind. Sie sind kein Preflight auf Inhaltsebene für PDF/X-4: Sie überprüfen nicht, ob jede Farbe innerhalb der deklarierten Ausgabebedingung liegt, ob alle Schriften eingebettet sind oder ob sich kein verbotener Transparenzmischungs-Grenzfall eingeschlichen hat. Für einen Auftrag, den Sie an eine Vertragsdruckerei übergeben, kombinieren Sie die strukturelle Validierung von PDFiumPas mit einer dedizierten PDF/X-Preflight-Engine und einem Probedruck, genau so, wie Sie jede andere Konformitätsbehauptung überprüfen würden. Die strukturelle Ebene fängt die Fehler ab, die das RIP-Caching lautlos stören; sie ist eine Hälfte einer vollständigen Prüfung, nicht das Ganze
Wenn Sie diese Prüfungen in ein breiteres Release-Gate einbauen: Dieselbe Methode des Scannens auf Byte-Ebene bildet die Grundlage für die anderen Standardarbeiten der Bibliothek, einschließlich der Validierung von Objekt- und Cross-Reference-Streams, bevor eine Datei überhaupt einen Preflight erreicht, und der Disziplin der gemeinsam genutzten Objekte bei der Wiederverwendung von Seitenstempeln mit Form-XObjects, die ein Dokument überhaupt erst RIP-freundlich macht. Die hier beschriebenen PDF/VT- und PDF/X-Speicher- und Validierungs-APIs sind Teil der PDFium-VCL-Komponente für Delphi und C++Builder, deren Produktseite die vollständige Konformitätsreferenz enthält