PDFium komponenta za Delphi provjerava valjanost PDF/X dokumenata spremnih za ispis putem metode TPdf.ValidatePdfX, koja implementira provjeru ISO 15930 u dva sloja: osam provjera sadržaja na razini bajta (zabranjena LZW kompresija, JavaScript, polja obrasca, OPI reference, TrimBox koji nedostaje, nepostavljeni ključ Trapped i više) plus prolaz kroz PDFium model objekata koji koristi funkciju FPDFFont_GetIsEmbedded kako bi provjerio ugradnju fontova na svakom tekstualnom objektu svake stranice. Rezultat je zapis TPdfXValidationResult koji imenuje otkrivenu razinu sukladnosti (conformance level) i navodi svako kršenje kao tipizirani enum, tako da vaša Delphi aplikacija može točno reći klijentu zašto će datoteka biti odbijena u tiskari prije nego što itko išta pokrene
Ako ste ikada poslali posao komercijalnoj tiskari i primili ga natrag s odbijenicom u jednom retku — "nema TrimBoxa", "fontovi nisu ugrađeni", "Trapped nije postavljen" — znate koliko košta kasno saznanje o tome. PDF/X je pripremni pandan PDF/A-u: dok arhivski PDF/A jamči da će se dokument prikazivati identično desetljećima od sada, PDF/X jamči da će se dokument razdvajati, osvjetljavati i obrezivati identično na tuđem RIP-u sutra ujutro. Ova dva standarda dijele mehanizme (XMP identifikacija, OutputIntents, ugrađeni ICC profili), ali odgovaraju na različita pitanja, zbog čega komponenta dolazi s odvojenim validatorima za svaki — PDF/A strana je pokrivena u provjeri valjanosti PDF/A prije leta (preflight) s PDFium komponentom
Što ISO 15930 zapravo zahtijeva od PDF-a spremnog za ispis?
Standard ISO 15930 postoji kako bi omogućio razmjenu naslijepo (blind exchange): dizajner predaje datoteku tiskaru s kojim nikada nije razgovarao, a tiskar može proizvesti ispravan ispis bez telefonskog poziva, bez e-pošte o fontu koji nedostaje i bez povezane slike koja je ostala na dizajnerovom prijenosnom računalu. Svako pravilo u standardu služi tom cilju. Fontovi moraju biti ugrađeni jer se ne može pretpostaviti da ih primateljski RIP posjeduje. Vanjske reference su zabranjene jer datoteka mora biti potpuna sama po sebi. Interaktivne značajke su zabranjene jer tinta nema onclick rukovatelj
PDFium komponenta prepoznaje tri obitelji sukladnosti i prijavljuje ih putem enuma TPdfXConformance u rezultatu provjere valjanosti: pxc1a za PDF/X-1a:2001 (ISO 15930-1, stroga CMYK-plus-spot polazna točka na PDF-u 1.3/1.4), pxc3 za PDF/X-3:2002 (ISO 15930-3, koji dopušta RGB, Lab i bojama upravljanim ICC-om) te pxc4 za PDF/X-4:2010 (ISO 15930-7, koji konačno dopušta živu prozirnost i slojeve na PDF 1.6 bazi). Datoteka koja uopće ne nosi PDF/X identifikaciju vraća se kao pxcNone, što je i samo po sebi koristan odgovor: dokument nikada nije tvrdio da je spreman za ispis, a sve ostalo što validator prijavljuje objašnjava što bi bilo potrebno da to postane
Zabrane imaju smisla kada razmišljate kao dobavljač RIP-a. /LZWDecode je zabranjen u svakoj varijanti PDF/X-a kako se sukladni potrošač nikada ne bi oslanjao na filtar s poviješću kompatibilnosti i licenciranja; Flate radi isti posao bez tog tereta. JavaScript, polja AcroForma i rječnici dodatnih akcija /AA zabranjeni su jer datoteka za ispis mora biti fiksni opis oznaka na papiru — sve što može promijeniti izgled u trenutku otvaranja krši jamstvo da se ispisuje ono što je odobreno. OPI (Open Prepress Interface) rezervirana mjesta (placeholders) zabranjena su jer su oni, po dizajnu, reference na slike visoke razlučivosti pohranjene negdje drugdje, a to "negdje drugdje" je upravo ono što razmjena naslijepo zabranjuje
Zašto tiskare odbijaju PDF-ove bez TrimBox-a?
TrimBox je gotova stranica — pravokutnik koji ostaje nakon što giljotina reže papir. MediaBox, koji ima svaka PDF stranica, samo je list papira: uključuje napust (bleed), oznake obrezivanja (crop marks), registracijske ciljeve i trake u boji. Softver za nametanje postavlja stranice na tiskarski list prema njihovim TrimBox-ovima; bez njega, operater mora pogoditi gdje točno završava vaša vizitka, a pogrešna procjena odrezat će vaš napust ili ostaviti bijelu crtu na jednom rubu. Zato ISO 15930 zahtijeva TrimBox (or ArtBox) na svakoj stranici, i zato ValidatePdfX javlja pvxiMissingTrimBox kada na bilo kojoj stranici dokumenta ne pronađe ključ /TrimBox
Ključ /Trapped odgovara na drugačije pitanje u proizvodnji. Trapping (skupno preklapanje boja) je tehnika pripreme za tisak kojom se lagano preklapaju susjedne boje kako sitne netočnosti u registraciji stroja ne bi stvorile bijele razmake između njih. Tiskar mora znati je li taj posao već obavljen: ponovni trapping na već obrađenoj datoteci udvostručuje preklapanja, a preskakanje trappinga na neobrađenoj datoteci riskira vidljive praznine. PDF/X stoga zahtijeva da rječnik Info eksplicitno navodi /Trapped /True ili /Trapped /False — ključ koji nedostaje ili vrijednost /Unknown prisiljava čovjeka da pregleda datoteku, što je upravo razgovor koji je razmjena naslijepo trebala eliminirati. Komponenta to označava kao pvxiTrappedNotSet
Pokretanje provjere valjanosti u dva sloja pomoću TPdf.ValidatePdfX
Metoda TPdf.ValidatePdfX ne prima argumente i vraća zapis TPdfXValidationResult s tri člana: Conformance (otkrivena varijanta PDF/X-a), Issues (Pascal skup vrijednosti TPdfXValidationIssue) i pomoćnik IsCompliant. Interno serijalizira učitani dokument u tok memorije, pokreće inspektor na razini bajta nad njim, a zatim prolazi kroz model objekata PDFium-a radi provjere ugradnje po fontu. Minimalna provjera preflighta izgleda ovako:
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;
Budući da je Issues običan Pascal skup, možete ga podijeliti kako god zahtijeva vaš tijek rada — tretirajte strukturne probleme kao stroga odbijanja, tretirajte pvxiMissingTitle (opcija SHOULD u standardu, a ne MUST) kao upozorenje, a ostalo zabilježite u zapisnik. Isti tip zapisa također hrani generator izvješća komponente, pa ako radije želite emitirati dokument čitljiv ljudima nego se granati po enumima, uzorak iz članka o izgradnji CLI alata za serijsko preflight izvješće s PDFium komponentom primjenjuje se na PDF/X nepromijenjen
Što hvata sloj na razini bajta — a što propušta
Sloj na razini bajta je skeniranje tokena preko strukturnih bajtova dokumenta s isključenim tijelima toka (stream bodies), tako da JPEG koji slučajno sadrži bajt uzorak /JavaScript ne može pokrenuti lažno pozitivan rezultat. Povrh provjera markera (XMP pdfxid:GTS_PDFXVersion, OutputIntent s ugrađenim ICC profilom, najava trailer /ID, zabrana šifriranja), prolaz kroz sadržaj dodaje osam provjera, od kojih svaka ima svoju enum vrijednost:
pvxiLzwForbidden— filtar/LZWDecodese pojavljuje bilo gdje u datoteci (zabranjeno u svim PDF/X varijantama)pvxiJavaScriptForbidden— akcija/JavaScriptili stablo imena je prisutnopvxiFormFieldsForbidden— rječnik/AcroFormili unos/XFApostojipvxiAdditionalActions— rječnik dodatnih akcija/AAje prisutanpvxiEmbeddedFilesForbidden—/EmbeddedFilesili bilješka/FileAttachmentje prisutnapvxiOpiForbidden— unos/OPIili/Alternatesreferencira zamjenjivi sadržaj slikepvxiMissingTrimBox— TrimBox nije pronađen ni na jednoj stranicipvxiTrappedNotSet— Trapped nedostaje ili je postavljen na/Unknown
Skeniranje na razini bajta je brzo i ne treba pokretač renderiranja, ali ima urođenu slijepu točku s fontovima: na toj razini inspektor može primijeniti samo grubu heuristiku — on označava dokument kada uopće ne pronađe ugrađeni program fonta. Datoteka s devet ugrađenih fontova i jednim sustavnim fontom provučenim unutra izgleda dobro pri skeniranju bajtova. Ta jedna praznina je razlog zašto postoji drugi sloj
Ugradnja po fontu kroz model objekata PDFium-a
Sloj modela objekata PDFium komponente precizno odgovara na pitanje o fontu. Nakon prolaza na razini bajta, TPdf.ValidatePdfX prolazi kroz svaku stranicu, pita FPDFPage_CountObjects za popis objekata, i za svaki tekstualni objekt razrješava ručku fonta putem FPDFTextObj_GetFont i upituje FPDFFont_GetIsEmbedded. Jedan neugrađeni font bilo gdje u dokumentu dodaje pvxiPdfiumFontNotEmbedded u skup problema. Obilazak se prekida na dvije razine — prestaje skenirati objekte na stranici i prestaje učitavati daljnje stranice onog trenutka kada se problem potvrdi — pa na katalogu od 300 stranica s kršenjem presuda često stiže već nakon prve stranice
Vrijedi znati dvije granične bilješke. Prvo, ovaj sloj zahtijeva učitanu knjižnicu PDFium i zahtijeva verzije koje izvoze funkciju FPDFFont_GetIsEmbedded; kada izvoz nedostaje, provjera se preskače, a ne označava se neuspješnom, tako da stariji DLL nikada ne proizvodi fantomska odbijanja. Drugo, provjera odgovara na pitanje "ugrađeno ili ne" i ništa više — ne razlikuje potpunu ugradnju od podskupa (subsetting), niti provjerava pokrivenost glifovima. Kada datoteka ne prođe provjeru, a vi morate znati koji font na kojoj stranici stvara problem, tehnike nabrajanja u analiziranju svojstava PDF fontova s PDFium-om u Delphiju nastavljaju točno tamo gdje validatorov boolean staje
Provjera toka bez učitavanja dokumenta — ili DLL-a
Inspektor na razini bajta također je izložen kao samostalna funkcija, ValidatePdfXCompliance(Source: TStream) u jedinici FPdfPdfx, i napisan je u čistom Object Pascalu bez ovisnosti o PDFium DLL-u. To ga čini primjenjivim na mjestima gdje mehanizam za renderiranje nije poželjan: lagana provjera učitavanja na web poslužitelju, CI posao koji provjerava generirane ilustracije ili Lazarus usluga na platformi na kojoj ne želite isporučivati nativne binarne datoteke. Usmjerite mu bilo koji stream koji podržava pretraživanje (seekable stream):
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 jasan: samostalna staza pokreće provjere markera i svih osam provjera sadržaja, ali ne i PDFium sloj po fontu, pa se njezina presuda o fontu vraća na grubu heuristiku. Razumna arhitektura koristi ValidatePdfXCompliance kao brzu i jeftinu prvu provjeru, a puni TPdf.ValidatePdfX rezervira za datoteke koje prođu tu prvu fazu
Gdje ovaj validator završava, a potpuni preflight počinje
Iskrenost je važna u alatima za preflight, pa evo gdje je granica. ValidatePdfX provjerava identifikacijske markere, strukturne zabrane, ključeve geometrije stranice, Trapped deklaraciju i ugradnju fontova sve do pojedinačnih tekstualnih objekata. Ne mjeri ukupnu pokrivenost tintom, ne provjerava je li svaki prostor boja dopušten za deklariranu varijantu (primjerice, pravilo samo za CMYK kod varijante X-1a), ne provjerava razlučivost slike u odnosu na rastersku gustoću (line screen) niti procjenjuje ponašanje preklapanja (overprint) i izravnavanja prozirnosti — za to je potreban preflight mehanizam s upravljanjem bojama, a i sama dokumentacija jedinice predlaže da je uparite s takvim alatom za konačnu certifikaciju. Ono što vam ova dvoslojna provjera pruža je 80% strukturnih odbijanja koja se mogu rano otkriti, ulovljenih u milisekundama unutar vašeg vlastitog Delphi koda umjesto u sutrašnjoj e-poruci tiskara
Oba sloja provjere valjanosti, API-ji za ubrizgavanje PDF/X markera za proizvodnju sukladnog izlaza te validatori za PDF/A, PDF/UA, PDF/E i PDF/VT koji dijele istu arhitekturu, dolaze u sklopu komponente PDFium Component za Delphi i C++Builder — jedna komponenta, od renderiranja do predtiskarske kontrole