Komponenta PDFium Component pro Delphi validuje tiskové dokumenty PDF/X prostřednictvím metody TPdf.ValidatePdfX, která implementuje kontrolu podle normy ISO 15930 ve dvou vrstvách: osm kontrol obsahu na úrovni bajtů (zakázaná komprese LZW, JavaScript, formulářová pole, odkazy OPI, chybějící TrimBox, nenastavený klíč Trapped a další) plus a průchod objektovým modelem PDFium, který využívá funkci FPDFFont_GetIsEmbedded k ověření vložení písem u každého textového objektu na každé stránce. Výsledkem je záznam TPdfXValidationResult, který specifikuje zjištěnou úroveň shody a vypisuje každé porušení jako typovaný výčet (enum), takže vaše aplikace v Delphi může zákazníkovi přesně říct, proč bude soubor v tiskárně odmítnut, ještě než kdokoli začáce osvitovat tiskové desky
Pokud jste někdy poslali zakázku do komerční tiskárny a dostali ji zpět s jedořádkovým odmítnutím — „no TrimBox“, „fonts not embedded“, „Trapped not set“ — znáte cenu pozdního zjištění. PDF/X je předtiskovým protějškem k PDF/A: zatímco archivační PDF/A zaručuje, že se dokument vykreslí identicky i za desítky let, PDF/X zaručuje, že se dokument zítra ráno na RIPu někoho jiného identicky separuje, osvítí a ořízne. Tyto dva standardy sdílejí mechanismy (XMP identifikaci, OutputIntents, vložené ICC profily), ale odpovídají na různé otázky, což je důvod, proč komponenta dodává samostatné validátory pro každý z nich — strana PDF/A je popsána v validaci PDF/A před tiskem pomocí komponenty PDFium Component
Co vlastně norma ISO 15930 vyžaduje od tiskového PDF?
Norma ISO 15930 existuje proto, aby umožnila takzvanou výměnu naslepo (blind exchange): designér předá soubor tiskárně, se kterou nikdy nemluvil, a tiskárna dokáže vytvořit správný výstup bez telefonování, bez e-mailů o chybějících písmech a bez odkazovaných obrázků, které zůstaly na notebooku designéra. Každé pravidlo ve standardu tomuto cíli slouží. Písma musí být vložena, protože nelze předpokládat, že je přijímací RIP vlastní. Externí odkazy jsou zakázány, protože soubor musí být kompletní sám o sobě. Interaktivní funkce jsou zakázány, protože inkoust nemá žádnou obsluhu kliknutí (onclick handler)
Komponenta PDFium Component rozpoznává tři rodiny shody a hlásí je prostřednictvím výčtu TPdfXConformance ve výsledku validace: pxc1a pro PDF/X-1a:2001 (ISO 15930-1, přísný CMYK-plus-spot základ na PDF 1.3/1.4), pxc3 pro PDF/X-3:2002 (ISO 15930-3, který připouští RGB, Lab a barvy řízené profilem ICC) a pxc4 pro PDF/X-4:2010 (ISO 15930-7, který nakonec povoluje živou průhlednost a vrstvy na základu PDF 1.6). Soubor, který nenese vůbec žádnou identifikaci PDF/X, se vrátí jako pxcNone, což je samo o sobě užitečná odpověď: dokument nikdy netvrdil, že je připraven k tisku, a vše ostatní, co validátor nahlásí, vysvětluje, co by bylo potřeba udělat pro dosažení tohoto cíle
Zákazy dávají smysl, jakmile začnete uvažovat jako dodavatel RIPů. Filtr /LZWDecode je zakázán v každé variantě PDF/X, aby vyhovující spotřebitel nikdy nezávisel na filtru s historií kompatibility a licencování; Flate dělá stejnou práci bez této zátěže. JavaScript, pole AcroForm a slovníky dalších akcí /AA jsou zakázány, protože tiskový soubor musí být pevným popisem značek na papíře — cokoli, co může změnit vzhled v čase otevření, narušuje záruku, že to, co bylo schváleno, je také vytištěno. Zástupné symboly OPI (Open Prepress Interface) jsou zakázány, protože jsou z návrhu odkazy na obrázky ve vysokém rozlišení uložené někde jinde, a „někde jinde“ je přesně to, co výměna naslepo zakazuje
Proč tiskárny odmítají soubory PDF bez TrimBoxu?
TrimBox představuje hotovou stránku — obdélník, který zůstane po oříznutí řezačkou. MediaBox, který má každá stránka PDF, je pouze arch: obsahuje spadávku, ořezové značky, registrační terčíky a barevné klíny. Vyřazovací software umisťuje stránky na tiskový arch podle jejich TrimBoxů; bez něj musí operátor hádat, kde vaše vizitka skutečně končí, a špatný odhad odřízne spadávku nebo zanechá na jednom okraji bílý proužek. Proto norma ISO 15930 vyžaduje TrimBox (nebo ArtBox) na každé stránce, a proto ValidatePdfX vyvolá chybu pvxiMissingTrimBox, pokud na žádné stránce dokumentu není nalezen klíč /TrimBox
Klíč /Trapped odpovídá na jinou výrobní otázku. Trapping (přetisk) je předtisková technika mírného překrytí sousedních barev tak, aby drobné odchylky tiskového stroje nezpůsobily bílé mezery mezi nimi. Tiskárna potřebuje vědět, zda tato práce již byla provedena: trapping již ošetřeného souboru zdvojnásobuje překryvy a vynechání trappingu u neošetřeného souboru riskuje viditelné mezery. PDF/X proto vyžaduje, aby slovník Info explicitně uváděl /Trapped /True nebo /Trapped /False — chybějící klíč nebo hodnota /Unknown nutí člověka soubor zkontrolovat, což je přesně ten rozhovor, kterému měla výměna naslepo zabránit. Komponenta to označuje jako pvxiTrappedNotSet
Spuštění dvouvrstvé validace pomocí TPdf.ValidatePdfX
Metoda TPdf.ValidatePdfX nepřijímá žádné argumenty a vrací záznam TPdfXValidationResult se třemi členy: Conformance (zjištěná verze PDF/X), Issues (pascalovská množina hodnot TPdfXValidationIssue) a pomocný prvek IsCompliant. Interně serializuje načtený dokument do paměťového streamu, spustí nad ním inspektor na úrovni bajtů a poté projde objektový model PDFium kvůli kontrole vložení jednotlivých písem. Minimální předtisková brána vypadá takto:
uses PDFium, FPdfPdfx;
procedure CheckPrintReadiness(const FileName: string);
var
Pdf: TPdf;
Res: TPdfXValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
Res := Pdf.ValidatePdfX;
Writeln('Detected conformance: ',
Ord(Res.Conformance)); // pxc1a, pxc3, pxc4, pxcNone...
if Res.IsCompliant then
Writeln('PDF/X checks passed')
else
begin
if pvxiMissingTrimBox in Res.Issues then
Writeln('REJECT: no /TrimBox on the pages');
if pvxiTrappedNotSet in Res.Issues then
Writeln('REJECT: /Trapped missing or /Unknown');
if pvxiPdfiumFontNotEmbedded in Res.Issues then
Writeln('REJECT: a page uses a non-embedded font');
if pvxiLzwForbidden in Res.Issues then
Writeln('REJECT: LZWDecode filter present');
end;
finally
Pdf.Free;
end;
end;
Protože Issues je běžná pascalovská množina, můžete ji rozdělit podle potřeb vašeho pracovního postupu — strukturální problémy považovat za striktní odmítnutí, chybu pvxiMissingTitle (kterou norma doporučuje, ale nevyžaduje) považovat za varování a zbytek pouze logovat. Stejný typ záznamu slouží také jako zdroj pro generátor sestav komponenty, takže pokud byste raději vygenerovali dokument čitelný pro lidi než se větvili podle enums, vzor popsaný v vytváření CLI nástroje pro dávkový předtiskový report pomocí komponenty PDFium Component platí pro PDF/X beze změny
Co bajtová vrstva zachytí — a co jí unikne
Bajtová vrstva provádí skenování tokenů strukturálních bajtů dokumentu s vynechanými těly streamů, takže obrázek JPEG, který náhodou obsahuje bajtový vzor /JavaScript, nemůže vyvolat falešný poplach. Nad rámec kontroly značek (XMP pdfxid:GTS_PDFXVersion, OutputIntent s vloženým profilem ICC, trailer /ID, zákaz šifrování) přidává průchod obsahem osm kontrol, z nichž každá má svou vlastní hodnotu výčtu:
pvxiLzwForbidden— filter/LZWDecodese vyskytuje kdekoli v souboru (zakázáno ve všech variantách PDF/X)pvxiJavaScriptForbidden— je přítomen akční nebo jmenný strom/JavaScriptpvxiFormFieldsForbidden— existuje slovník/AcroFormnebo položka/XFApvxiAdditionalActions— je přítomen slovník dalších akcí/AApvxiEmbeddedFilesForbidden— jsou přítomny/EmbeddedFilesnebo anotace/FileAttachmentpvxiOpiForbidden— položka/OPInebo/Alternatesodkazuje na nahraditelný obsah obrázkupvxiMissingTrimBox— na žádné stránce nebyl nalezen/TrimBoxpvxiTrappedNotSet—/Trappedchybí nebo je nastaven na/Unknown
Skenování bajtů je rychlé a nepotřebuje vykreslovací jádro, ale má přirozené slepé místo u písem: na této úrovni může inspektor použít pouze hrubou heuristiku — označí dokument, pouze pokud v něm nenajde vůbec žádný program vloženého písma. Soubor s devíti vloženými písmy a jedním podstrčeným systémovým písmem vypadá pro bajtový sken v pořádku. Tato jediná mezera je důvodem, proč existuje druhá vrstva
Vkládání písem po jednom prostřednictvím objektového modelu PDFium
Vrstva objektového modelu v komponentě PDFium Component odpovídá na otázku písma přesně. Po průchodu na úrovni bajtů metoda TPdf.ValidatePdfX prochází každou stránku, získá seznam objektů přes FPDFPage_CountObjects a pro každý textový objekt zjistí handle písma pomocí FPDFTextObj_GetFont a dotáže se na FPDFFont_GetIsEmbedded. Jedno nevložené písmo kdekoli v dokumentu přidá do množiny problémů hodnotu pvxiPdfiumFontNotEmbedded. Procházení se zkracuje (short-circuits) na dvou úrovních — zastaví skenování objektů na stránce a zastaví načítání dalších stránek v okamžiku, kdy je problém potvrzen — takže u nevyhovujícího 300stránkového katalogu verdikt často dorazí hned po první stránce
Stojí za to znát dvě poznámky o limitech. Za prvé, tato vrstva vyžaduje načtenou knihovnu PDFium a sestavení, která exportují funkci FPDFFont_GetIsEmbedded; pokud export chybí, kontrola se přeskočí, místo aby selhala, takže starší DLL nikdy neprodukuje falešná odmítnutí. Za druhé, kontrola odpovídá pouze na otázku „vloženo či nikoli“ a nic víc — nerozlišuje plné vložení od podmnožin (subsetting) ani nezkoumá pokrytí glyfů. Pokud soubor selže a vy potřebujete zjistit, které písmo na které stránce chybí, techniky procházení popsané v analýze vlastností písem PDF pomocí PDFium v Delphi navazují přesně tam, kde logická hodnota validátoru končí
Validace streamů bez načítání dokumentu — nebo DLL
Inspektor na úrovni bajtů je také zpřístupněn jako samostatná funkce, ValidatePdfXCompliance(Source: TStream) v jednotce FPdfPdfx, a jedná se o čistý Object Pascal bez jakékoli závislosti na DLL PDFium. Díky tomu je nasaditelný v místech, kde je vykreslovací jádro nežádoucí: odlehčená brána pro nahrávání na webovém serveru, úloha CI, která prověřuje generovanou grafiku, nebo služba v Lazarusu na platformě, kde nechcete dodávat nativní binární soubory. Můžete mu předložit jakýkoli stream podporující vyhledávání pozice (seekable):
uses Classes, FPdfPdfx;
function QuickPdfXGate(const FileName: string): Boolean;
var
Fs: TFileStream;
Res: TPdfXValidationResult;
begin
Fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfXCompliance(Fs);
Result := Res.IsCompliant and (Res.Conformance <> pxcNone);
finally
Fs.Free;
end;
end;
Kompromis je zřejmý: samostatná cesta spouští kontroly značek a všech osm kontrol obsahu, ale nikoli vrstvu PDFium pro jednotlivá písma, takže její verdikt ohledně písem se vrací k hrubé heuristice. Rozumná architektura používá funkci ValidatePdfXCompliance jako levnou první bránu a plnou metodu TPdf.ValidatePdfX si vyhrazuje pro soubory, které jí projdou
Kde tento validátor končí a začíná plný předtiskový test (preflight)
U nástrojů pro předtiskovou kontrolu (preflight) záleží na upřímnosti, takže zde jsou limity. Metoda ValidatePdfX ověřuje identifikační značky, strukturální zákazy, klíče geometrie stránek, deklaraci Trapped a vložení písem až na úroveň jednotlivých textových objektů. Neměří celkové pokrytí inkoustem, neověřuje, zda je každý barevný prostor legální pro deklarovanou variantu (např. pravidlo pouze pro CMYK u X-1a), nekontroluje rozlišení obrázků vůči rastru ani nevyhodnocuje chování přetisku a sloučení průhledností — k tomu je zapotřebí barevně kalibrované předtiskové jádro a samotná dokumentace jednotky doporučuje pro finální certifikaci její spárování s takovým nástrojem. To, co vám tato dvouvrstvá kontrola dává, je 80 % odmítnutí, která jsou strukturální a zjistitelná včas, odhalená během milisekund ve vašem vlastním kódu v Delphi namísto zítřejšího e-mailu z tiskárny
Obě validační vrstvy, API pro vkládání značek PDF/X pro vytváření vyhovujícího výstupu a validátory PDF/A, PDF/UA, PDF/E a PDF/VT sdílející stejnou architekturu, jsou dodávány v komponentě PDFium Component pro Delphi a C++Builder — jedna komponenta, od vykreslování až po předtiskovou kontrolu