PDFium komponenta za Delphi validira PDF/X dokumente spremne za štampu preko metode TPdf.ValidatePdfX, koja implementira provere standarda ISO 15930 u dva sloja: osam provera sadržaja na nivou bajtova (zabranjena LZW kompresija, JavaScript, polja obrasca, OPI reference, TrimBox koji nedostaje, nepostavljeni Trapped ključ i još mnogo toga) i prolaz kroz PDFium model objekata koji koristi FPDFFont_GetIsEmbedded da proveri ugradnju fontova na svakom tekstualnom objektu svake stranice. Rezultat je zapis TPdfXValidationResult koji imenuje detektovani nivo usaglašenosti i navodi svaki prekršaj u vidu enuma, tako da vaša Delphi aplikacija može tačno da saopšti korisniku zašto će datoteka biti odbijena u štampariji pre nego što se uopšte krene sa štampom
Ako ste ikada poslali posao komercijalnom štampaču i dobili ga nazad sa odbijanjem u jednoj rečenici — "nema TrimBox-a", "fontovi nisu ugrađeni", "Trapped nije postavljen" — znate koliko košta kada to saznate kasno. PDF/X je pandan standarda PDF/A za pripremu za štampu (prepress): dok arhivski PDF/A garantuje da će se dokument renderovati identično i decenijama od danas, PDF/X garantuje da će se dokument separisati, osvetliti i opsecati identično na tuđem RIP-u (Raster Image Processor) sutra ujutru. Ova dva standarda dele mehanizme (XMP identifikaciju, OutputIntents, ugrađene ICC profile) ali odgovaraju na različita pitanja, zbog čega komponenta isporučuje odvojene validatore za svaki — PDF/A strana je pokrivena u članku o PDF/A preflight validaciji pomoću PDFium komponente
Šta standard ISO 15930 zapravo zahteva od PDF-a spremnog za štampu?
Standard ISO 15930 postoji da bi omogućio razmenu na slepo (blind exchange): dizajner predaje datoteku štampariji sa kojom nikada nije razgovarao, i štamparija može da proizvede ispravan otisak bez telefonskog poziva, bez e-pošte o nedostatku fonta i bez povezane slike koja je ostala na dizajnerovom laptopu. Svako pravilo u standardu služi tom cilju. Fontovi moraju biti ugrađeni jer se ne može pretpostaviti da ih prijemni RIP poseduje. Eksterne reference su zabranjene jer datoteka mora biti kompletna sama po sebi. Interaktivne funkcije su zabranjene jer mastilo nema onclick rukovalac
PDFium komponenta prepoznaje tri porodice usaglašenosti i prijavljuje ih kroz enum TPdfXConformance u rezultatu validacije: pxc1a za PDF/X-1a:2001 (ISO 15930-1, striktni CMYK-plus-spot minimum na PDF 1.3/1.4), pxc3 za PDF/X-3:2002 (ISO 15930-3, koji dopušta RGB, Lab i ICC-upravljane boje) i pxc4 za PDF/X-4:2010 (ISO 15930-7, koji konačno dozvoljava aktivnu providnost i slojeve na bazi PDF 1.6). Datoteka koja uopšte ne nosi PDF/X identifikaciju vraća se kao pxcNone, što je samo po sebi koristan odgovor: dokument nikada nije tvrdio da je spreman za štampu, a sve ostalo što validator prijavljuje objašnjava šta bi bilo potrebno da se to postigne
Zabrane imaju smisla kada razmišljate kao dobavljač RIP-a. Filter /LZWDecode je zabranjen u svim PDF/X varijantama kako usaglašeni potrošač nikada ne bi zavisio od filtera sa istorijom problema kompatibilnosti i licenciranja; Flate radi isti posao bez tog prtljaga. JavaScript, AcroForm polja i rečnici dodatnih akcija /AA su zabranjeni jer datoteka za štampu mora biti fiksni opis otisaka na papiru — bilo šta što može da promeni izgled u vreme otvaranja narušava garanciju da je odobreni probni otisak ono što se zaista štampa. OPI (Open Prepress Interface) čuvari mesta su zabranjeni jer su oni po dizajnu reference na slike visoke rezolucije sačuvane negde drugde, a "negde drugde" je upravo ono što razmena na slepo zabranjuje
Zašto štamparije odbijaju PDF-ove bez TrimBox-a?
TrimBox je gotova stranica — pravougaonik koji ostaje nakon što giljotina izvrši sečenje. MediaBox, koji ima svaka PDF stranica, je samo papirni tabak: on uključuje prepust za obrez (bleed), oznake sečenja, mete za registraciju i trake boja. Softver za nametanje (imposition) pozicionira stranice na štamparskom tabaku prema njihovim TrimBox-ovima; bez njega, operater mora da nagađa gde se vaša vizitkarta zapravo završava, a pogrešna procena odseca prepust ili ostavlja belu ivicu. Zato standard ISO 15930 zahteva TrimBox (or ArtBox) na svakoj stranici, i zato ValidatePdfX podiže grešku pvxiMissingTrimBox kada se ključ /TrimBox ne pronađe na bilo kojoj stranici dokumenta
Ključ /Trapped odgovara na jedno drugo štamparsko pitanje. Trapping (uklapanje) je tehnika pripreme za štampu kojom se blago preklapaju susedne boje kako mala štamparska neregistracija ne bi otvorila bele praznine između njih. Štampač mora da zna da li je taj posao već urađen: uklapanje već uklopljene datoteke duplira preklapanja, a preskakanje uklapanja na neuklopljenoj datoteci rizikuje vidljive praznine. PDF/X stoga zahteva od Info rečnika da eksplicitno navede /Trapped /True ili /Trapped /False — ključ koji nedostaje ili ima vrednost /Unknown primorava čoveka da pregleda datoteku, što je upravo razgovor koji je razmena na slepo trebala da eliminiše. Komponenta ovo označava kao pvxiTrappedNotSet
Pokretanje dvoslojne validacije pomoću TPdf.ValidatePdfX
Metod TPdf.ValidatePdfX ne prima argumente i vraća zapis TPdfXValidationResult sa tri člana: Conformance (detektovani PDF/X stil), Issues (Pascal skup vrednosti TPdfXValidationIssue) i pomoćnik IsCompliant. Interno, on serijalizuje učitani dokument u memorijski tok (stream), pokreće inspektor na nivou bajtova nad njim, a zatim prolazi kroz PDFium model objekata radi provere ugradnje po fontu. Minimalna provera pripreme za štampu 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;
Pošto je Issues običan Pascal skup, možete ga podeliti kako god zahteva vaš proces rada — tretirajte strukturne probleme kao neopoziva odbijanja, tretirajte pvxiMissingTitle (opcija TREBA u standardu, a ne MORA) kao upozorenje, a ostalo zabeležite u log. Isti tip zapisa takođe hrani generator izveštaja komponente, pa ako radije želite da emitujete dokument čitljiv ljudima nego da se granate po enumima, šablon iz članka o izgradnji batch preflight izveštaja CLI pomoću PDFium komponente primenjuje se na PDF/X nepromenjeno
Šta sloj na nivou bajtova hvata — a šta propušta
Sloj na nivou bajtova je skeniranje tokena preko strukturnih bajtova dokumenta sa praznim telima tokova, tako da JPEG koji slučajno sadrži uzorak bajtova /JavaScript ne može da izazove lažno pozitivan rezultat. Pored provera markera (XMP pdfxid:GTS_PDFXVersion, OutputIntent sa ugrađenim ICC profilom, najava /ID na kraju, zabrana šifrovanja), prolaz kroz sadržaj dodaje osam provera, od kojih svaka ima svoju vrednost enuma:
pvxiLzwForbidden— filter/LZWDecodese pojavljuje bilo gde u datoteci (zabranjen u svim PDF/X varijantama)pvxiJavaScriptForbidden— prisutna je akcija/JavaScriptili stablo imenapvxiFormFieldsForbidden— postoji rečnik/AcroFormili unos/XFApvxiAdditionalActions— prisutan je rečnik dodatnih akcija/AApvxiEmbeddedFilesForbidden— prisutan je/EmbeddedFilesili anotacija/FileAttachmentpvxiOpiForbidden— unos/OPIili/Alternatesupućuje na zamenljivi sadržaj slikepvxiMissingTrimBox—/TrimBoxnije pronađen ni na jednoj stranicipvxiTrappedNotSet—/Trappednedostaje ili je postavljen na/Unknown
Skeniranje bajtova je brzo i ne zahteva motor za renderovanje, ali ima urođenu slepu tačku sa fontovima: na tom nivou inspektor može primeniti samo grubu heuristiku — on označava dokument kada uopšte ne pronađe ugrađen program fonta. Datoteka sa devet ugrađenih fontova i jednim sistemskim fontom koji se provukao izgleda u redu pri skeniranju bajtova. Taj jedinstveni jaz je razlog zašto postoji drugi sloj
Ugradnja po fontu kroz PDFium model objekata
Sloj modela objekata PDFium komponente precizno odgovara na pitanje o fontu. Nakon prolaza na nivou bajtova, TPdf.ValidatePdfX prolazi kroz svaku stranicu, traži od FPDFPage_CountObjects listu objekata, i za svaki tekstualni objekat razrešava ručku fonta preko FPDFTextObj_GetFont i upućuje upit za FPDFFont_GetIsEmbedded. Jedan neugrađeni font bilo gde u dokumentu dodaje pvxiPdfiumFontNotEmbedded u skup problema. Obilazak se prekida na dva nivoa (short-circuits) — prestaje skeniranje objekata na stranici i prestaje učitavanje daljih stranica onog trenutka kada se problem potvrdi — tako da na katalogu od 300 stranica koji krši pravila presuda često stiže već nakon prve stranice
Dve granične napomene koje vredi znati. Prvo, ovaj sloj zahteva učitanu PDFium biblioteku i zahteva verzije koje izvoze funkciju FPDFFont_GetIsEmbedded; kada izvoz nedostaje, provera se preskače umesto da ne uspe, tako da stariji DLL nikada ne proizvodi fantomska odbijanja. Drugo, provera daje odgovor "ugrađen ili ne" i ništa više — ne pravi razliku između potpune ugradnje i delimične (subsetting), niti provereva pokrivenost glifova. Kada datoteka ne prođe i morate da znate koji font na kojoj stranici je problem, tehnike nabrajanja iz članka o analizi svojstava PDF fontova pomoću PDFium-a u Delphi-ju nastavljaju tačno tamo gde se validatorov boolean završava
Validacija tokova bez učitavanja dokumenta — ili DLL-a
Inspektor na nivou bajtova je takođe izložen kao samostalna funkcija, ValidatePdfXCompliance(Source: TStream) u jedinici FPdfPdfx, i to je čisti Object Pascal bez zavisnosti od PDFium DLL-a. To ga čini pogodnim za primenu na mestima gde motor za renderovanje nije poželjan: lagani upload prolaz na veb serveru, CI posao koji proverava generisani dizajn, ili Lazarus servis na platformi gde ne želite da isporučujete izvorne binarne datoteke. Možete mu proslediti bilo koji tok (stream) sa mogućnošću pozicioniranja:
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 putanja pokreće provere markera i svih osam provera sadržaja, ali ne i PDFium sloj po fontu, pa se njena presuda o fontovima vraća na grubu heuristiku. Razumna arhitektura koristi ValidatePdfXCompliance kao jeftin prvi prolaz i rezerviše puni TPdf.ValidatePdfX za datoteke koje prođu taj prvi korak
Gde se ovaj validator završava i gde počinje potpuni preflight
Iskrenost je važna kod alata za proveru (preflight), pa evo te granice. Metod ValidatePdfX proverava identifikacione markere, strukturne zabrane, ključeve geometrije stranice, Trapped deklaraciju i ugradnju fontova do pojedinačnih tekstualnih objekata. On ne meri ukupnu pokrivenost bojom (total ink coverage), ne proverava da li je svaki prostor boja dozvoljen za deklarisanu varijantu (na primer, X-1a pravilo samo za CMYK), ne proverava rezoluciju slike u odnosu na linijski raster, niti procenjuje ponašanje preklapanja (overprint) i ravnanja providnosti (transparency flattening) — za to je potreban kolor-menadžovani preflight motor, a i sama dokumentacija ove jedinice savetuje da se ona upari sa jednim takvim motorom za konačnu sertifikaciju. Ono što vam ova dvoslojna provera pruža jeste 80% odbijanja koja su strukturne prirode i koja se mogu otkriti rano, uhvaćena u milisekundama unutar vašeg sopstvenog Delphi koda, umesto u sutrašnjoj e-poruci od štamparije
Oba sloja validacije, API-ji za ubacivanje PDF/X markera za proizvodnju usaglašenog izlaza, kao i PDF/A, PDF/UA, PDF/E i PDF/VT validatori koji dele istu arhitekturu, isporučuju se u sklopu komponente PDFium Component za Delphi i C++Builder — jedna komponenta, od renderovanja do kontrole pripreme za štampu