Transakční tiskárna vám vrátí běh výpisů o 80 000 stranách s jednovětým odmítnutím: „není to PDF/VT, RIP si to neumí uložit do mezipaměti.“ Soubor se v každém prohlížeči na vašem stole otevře bez problémů, barvy jsou správné, data se slila správně. Nic z toho ale není to, co digitální tiskový stroj požadoval. Rychlý tisk proměnných dat stojí a padá s tím, zda stroj dokáže poznat, že blok s logem zákazníka na stránce 1 je bajt po bajtu stejný objekt jako ten na stránce 40 000, vykreslit ho jednou a pak ho znovu použít. PDF/VT je standard, který tuhle záruku dělá strojově ověřitelnou, a právě „vypadá správně“ je ta past, protože struktura, kterou RIP čte, je na obrazovce neviditelná
PDFiumPas zpřístupňuje tuto strukturu přes malé rozhraní na TPdf: SaveAsPdfVT zapisuje, ValidatePdfVT kontroluje. Tento článek rozebírá, co tyto dvě metody skutečně ukládají na disk a kontrolují, kde je ISO 16612-2 přísnější, než se na první pohled zdá, a které části jsou poctivé strukturální kotvy, nikoli plnohodnotný preflight, který by šlo klientovi účtovat
Co standardizuje PDF/VT a proč nejdřív přichází PDF/X
PDF/VT (ISO 16612-2:2010) není nový formát souboru. Je to vrstva optimalizačních metadat přilepená na soubor PDF/X a na tom pořadí záleží. Norma definuje tři úrovně shody, ale jen dvě z nich pojmenovávají soubor PDF: PDF/VT-1, samostatný dokument v jednom celku, a PDF/VT-2, model souborové sady, v němž stránky odkazují na sdílené externí zdroje. Třetí token, který můžete vidět, PDF/VT-2s, vůbec není hodnota na úrovni souboru; žije v hlavičce MIME streamu popsané v příloze A. Pokud najdete kód, který razítkuje GTS_PDFVTVersion = "PDF/VT-2s" do XMP dokumentu, je ten kód chybný
Nezpochybnitelné pravidlo pro jediný soubor je základ PDF/X. ISO 16612-2 §6.2.1 vyžaduje, aby každý soubor PDF/VT-1 byl zároveň platným souborem PDF/X-4. Souborová sada PDF/VT-2 musí podle §6.2.2 naopak stát na PDF/X-4p, PDF/X-5g nebo PDF/X-5pg. Proto zapisovač PDF/VT nemůže jen připojit pár identifikačních klíčů: musí s sebou nést celou sadu značek PDF/X-4, což znamená OutputIntent, vložený ICC profil cílového barevného prostoru, odpovídající položky XMP a Info dokumentu, trailer /ID, a žádné šifrování. Vynechte cokoli z toho a máte soubor, který tvrdí, že je PDF/VT, ale selže ve chvíli, kdy ho kompatibilní čtečka zkontroluje vůči základu. PDFiumPas považuje vrstvu PDF/X-4 za součást uložení PDF/VT, takže nejdřív nevoláte samostatný SaveAsPdfX nejdřív; injektor zapíše obě vrstvy v jediném průchodu
Ukládání souboru pomocí SaveAsPdfVT
Minimální volání nepotřebuje nic jiného než aktivní dokument, protože TPdfVTSaveOptions.Default dodává vestavěný profil sRGB ICC a shodu pvc1. Uložení uvnitř probíhá ve třech krocích: odstraní veškeré zabezpečení (vkládání prostých značek do šifrovaného objektového proudu by ho poškodilo), propojí existující slovník Info dokumentu a trailer /ID do sady značek tak, aby se hodnoty XMP a Info shodovaly, a pak přes přírůstkovou aktualizaci připojí objekty PDF/X-4 a PDF/VT
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;
Pro skutečný produkční výstup obvykle chcete OutputIntent přepsat charakteristikou svého tiskového stroje, ne generickým náhradním profilem sRGB. ICC bajty a identifikátory podmínek zadejte přes 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;
Jeden detail v tom ukázkovém kódu je záměrná pojistka, ne omezení, o něž by se dalo přít. Nastavení Opt.Conformance := pvc2 nevytvoří soubor PDF/VT-2. Zapisovač jakýkoli požadavek na jinou hodnotu než pvc1 normalizuje zpět na pvc1, protože PDF/VT-2 je formát souborové sady a zapisovač pro jeden soubor, který připojuje jeden výstupní dokument, fyzicky nedokáže sestavit sadu externích zdrojů, kterou vyžaduje §6.2.2. Hodnota pvc2 existuje pro čtecí cestu, aby ValidatePdfVT dokázal rozpoznat a nahlásit existující dokument souborové sady; není to cíl zápisu
Strom DPart: struktura, kterou RIP skutečně čte
Jádrem PDF/VT je hierarchie Document Part (DPart). Díky ní může tiskový stroj rozdělit dlouhý běh na záznamy, seskupovat záznamy podle příjemců nebo poštovních balíčků a připojovat metadata Document Part, aby následné zařízení mohlo každou položku směrovat a účtovat. ISO 16612-2 §6.5 popisuje zapojení: katalog nese /DPartRoot, kořenový uzel DPart nese /DPartRootNode a /NodeNameList pojmenovávající každou úroveň hierarchie, listové DParty pokrývají rozsahy stromu stránek a každá stránka, která patří k části, ukazuje zpět na svůj list přes položku na úrovni stránky /DPart entry
Když váš zdrojový dokument už obsahuje použitelnou hierarchii, SaveAsPdfVT ji zachová. Když ne, zapisovač ji sestaví jako minimální verzi: jeden DPart na úrovni dokumentu, který postupně pokrývá aktuální strom stránek, s /DPart zpětným odkazem připojeným ke každému živému objektu stránky a jednoúrovňovým /NodeNameList [/Document]. Buďte k sobě upřímní, co ten minimální strom vlastně je. Je to strukturální opora, která splňuje požadavky na tvar v §6.5; není to obchodní metadata. Nemůže si vymyslet příjemce, hranice poštovních zásilek ani dávky produktů, protože tyto informace ve zdroji nikdy nebyly. Pokud máte data po jednotlivých příjemcích, očekává se, že si sami sestavíte hlubší strom DPart a rozšíříte /NodeNameList tak, aby odpovídal úrovním, které vytvoříte
Validace, která jde dál než pouhá přítomnost klíče
ValidatePdfVT vrací TPdfVTValidationResult záznam se třemi věcmi: zjištěnou Conformance, sadou Issues, a IsCompliant pomocnou hodnotou, která je true jen tehdy, když je shoda skutečnou úrovní a množina problémů je prázdná. Výčet problémů je záměrně konkrétní, takže neúspěšný výsledek vám řekne, které ustanovení jste minuli, a ne jen „neplatné“:
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;
Dvě kontroly, které stojí za to pochopit do hloubky, jsou párování shody a průchod stromem DPart, protože obě bývaly příliš benevolentní a byly zpřísněny, aby odpovídaly specifikaci. Na straně párování dělá validátor přesné porovnání, ne že by platilo „jakýkoli PDF/X projde“: soubor PDF/VT-1 je přijat jen na PDF/X-4 bázi, a soubor PDF/VT-2 jen na PDF/X-4p, PDF/X-5g nebo PDF/X-5pg. Značka PDF/VT-1 umístěná na bázi PDF/X-1a se nahlásí, neprojde jen mávnutím ruky
Nejvíc přísnosti je právě v průchodu stromem DPart. Nestačí, aby katalog měl /DPartRoot klíč, protože ani zfalšovaný prázdný objekt nebo objekt bez odkazů na stránky se stejně nedá zpracovat. HasValidDPartHierarchy a rekurzivní ValidateDPartNode projdou celou strukturou: sledují odkazy na rodiče, odmítají duplicitní potomky i cykly, vynucují, že /Start a /DParts se vzájemně vylučují, a vyžadují, aby rozsahy listových stránek pokrývaly strom stránek v pořadí průchodu do hloubky, přičemž každý /DPart ukazuje na list, který ji obsahuje. Všechny tyto vnitřní chyby se sloučí do jediného pvviMissingDPartRoot bitu problému místo toho, aby rozšiřovaly veřejné výčtové hodnoty, takže ten jediný příznak berte jako „hierarchie DPart je nepoužitelná“, ne doslova jako „chybí kořenový klíč“
Tři syntaktické pasti, které validátor nyní vynucuje
Opakované průchody tabulkou 4 v §6.5 odhalily tvary, které dřívější verze přijímaly, ale norma ne. To jsou přesně ty chyby, které ručně sestavený strom DPart dělá, takže stojí za to je výslovně zmínit:
/DPartsje pole polí, ne ploché pole. Každý prvek vnějšího pole musí být sám polem nepřímých odkazů. Ploché/DParts [9 0 R]je odmítnuto; správný tvar je/DParts [[9 0 R] [10 0 R]]. Tím se zabrání tomu, aby nehierarchická struktura předstírala platnou úroveň/Endoznačuje jen skutečný vícestránkový rozsah. Listový DPart smí nést/Endjen tehdy, když zároveň má/Start, a/Endmusí být později než/Startv pořadí stromu stránek. Degenerované/Start 3 0 R /End 3 0 Rnyní činí hierarchii nepoužitelnou místo toho, aby se četlo jako jednostránková část/NodeNameListnázvy musí po rozbalení PDF jména obstát jako XML NMTOKENy. Název jako/Bad#20Namese rozbalí do názvu obsahujícího mezeru, což není platný token. Implementace provádí lehkou kontrolu ASCII (písmena, číslice,.,-,_,:, plus bajty mimo ASCII) zachytí chyby v mezerách a oddělovačích, aniž by odmítl legitimní lokalizované nebo dodavatelsky specifické názvy
Značky XMP: dva způsoby zápisu téže vlastnosti
Identifikace PDF/VT je v XMP pod pdfvtid jmenným prostorem, konkrétně GTS_PDFVTVersion a GTS_PDFVTModDate, vedle standardních xmp:CreateDate a xmp:ModifyDate. Jemný detail, který u naivních čteček vyvolává falešné hlášení o chybějící hodnotě, je ten, že kterákoli z nich může být zapsána dvěma způsoby: jako text prvku (<pdfvtid:GTS_PDFVTVersion>PDF/VT-1</pdfvtid:GTS_PDFVTVersion>) nebo jako atribut RDF na prvku description. PDFiumPas čte oba tvary, takže soubor, který jiný nástroj uložil v atributové podobě, není penalizován. Zároveň vynucuje pravidlo konzistence z §6.3, podle něhož GTS_PDFVTModDate musí být rovno xmp:ModifyDate; nesoulad vyvolá pvviModDateMismatch
Ještě jedno pravidlo ze stejného odstavce: neznámá hodnota GTS_PDFVTVersion se zachová jako pvcUnknown místo aby byla složena zpět na pvcNone. Ten rozdíl má provozní význam. pvcNone znamená „žádná značka PDF/VT, prostý PDF“, zatímco pvcUnknown znamená „něco označilo verzi, kterou tento validátor nerozpozná“ (mezi nimi i případ PDF/VT-2s). Směšování obou by skrylo vadný soubor ve stejné skupině jako obyčejný dokument
Kde záruka končí
Je dobré být přesný v hranici toho, co tyto metody slibují, protože shoda pro tisk s proměnnými daty je otázka skutečných peněz. Kontroly DPart a párování jsou strukturální validace na úrovni bajtů. Potvrzují, že optimalizační kostra, základní značky PDF/X-4, OutputIntent a XMP jsou přítomné a vnitřně konzistentní. Nejsou to předtiskové ověření PDF/X-4 na úrovni obsahu: nekontrolují, že každá barva spadá do deklarovaných výstupních podmínek, že všechna písma jsou vložená nebo že neproklouzl žádný zakázaný okrajový případ míchání průhlednosti. U zakázky, kterou posíláte na smluvní tisk, spojte strukturální validaci PDFiumPas s vyhrazeným předtiskovým enginem PDF/X a zkušebním tiskem, stejně jako byste prověřili jakékoli jiné tvrzení o shodě. Strukturální vrstva zachytí chyby, které tiše rozbijí cache RIPu; je to jedna polovina kompletní kontroly, ne celek
Pokud tyto kontroly zapojujete do širší release gate, stejný přístup skenování na úrovni bajtů stojí i za dalšími standardizačními pracemi knihovny, včetně ověřování objektových a cross-reference streamů ještě dřív, než se soubor dostane k preflightu, a disciplíny sdílených objektů za znovupoužitelnými stránkovými razítky s Form XObjects, díky níž je dokument od začátku přívětivý pro RIP. Zde popsaná rozhraní pro ukládání a validaci PDF/VT a PDF/X jsou součástí komponenty PDFium VCL pro Delphi a C++Builder, jejíž produktová stránka obsahuje úplnou referenci souladu