Tehnični članak

PDF/X Validation in Delphi with the PDFium Component

Komponenta PDFium za Delphi validira dokumente PDF/X za tisk prek metode TPdf.ValidatePdfX, ki izvaja preverjanje ISO 15930 v dveh ravneh: osem vsebinskih preverjanj na ravni bajtov (prepovedano LZW stiskanje, JavaScript, polja obrazcev, sklici OPI, manjkajoč TrimBox, nenastavljena zastavica Trapped in drugo) ter prehod objekta v PDFium modelu, ki uporablja FPDFFont_GetIsEmbedded za potrditev vgrajevanja pisave pri vsakem besedilnem objektu na vsaki strani. Rezultat je zapis TPdfXValidationResult, ki poimenuje zaznano raven skladnosti in navede vsako kršitev kot določen enum, tako da lahko vaša aplikacija v Delphiju stranki natančno pove, zakaj bo datoteka zavrnjena v tiskarni, preden kdor koli začne izdelovati plošče

Če ste kdaj poslali naročilo komercialnemu tiskarju in ga prejeli nazaj z enovrstično zavrnitvijo — "ni TrimBox-a", "pisave niso vgrajene", "Trapped ni nastavljen" —, poznate stroške poznega iskanja napak. PDF/X je predtiskarski dvojnik standardu PDF/A: medtem ko arhivski PDF/A zagotavlja, da se dokument čez desetletja izriše enako, PDF/X zagotavlja, da se dokument jutri zjutraj na tujem RIP-u enako loči, osvetli in obreže. Oba standarda delita mehanizme (identifikacija XMP, OutputIntents, vgrajeni profili ICC), vendar odgovarjata na različna vprašanja, zato komponenta pošilja ločene validatorje za vsakega — stran PDF/A je obravnavana v predtiskarski validaciji PDF/A s komponento PDFium

Kaj standard ISO 15930 dejansko zahteva od PDF-ja za tisk?

Standard ISO 15930 obstaja zato, da omogoči slepo izmenjavo: oblikovalec izroči datoteko tiskarju, s katerim nikoli ni govoril, tiskar pa lahko izdela pravilen izhod brez telefonskega klica, brez e-pošte o manjkajoči pisavi in brez povezane slike, ki je ostala na prenosniku oblikovalca. Vsako pravilo v standardu služi temu cilju. Pisave morajo biti vgrajene, ker ni mogoče predpostaviti, da jih ima prejemni RIP. Zunanji sklici so prepovedani, ker mora biti datoteka popolna sama po sebi. Interaktivne funkcije so prepovedane, ker črnilo nima upravljalnika dogodkov onclick

Komponenta PDFium prepozna tri družine skladnosti in o njih poroča prek enuma TPdfXConformance v rezultatu validacije: pxc1a za PDF/X-1a:2001 (ISO 15930-1, stroga osnova CMYK in spot barv na PDF 1.3/1.4), pxc3 za PDF/X-3:2002 (ISO 15930-3, ki dopušča barve RGB, Lab in barve, upravljane z ICC), ter pxc4 za PDF/X-4:2010 (ISO 15930-7, ki končno omogoča živo prosojnost in plasti na osnovi PDF 1.6). Datoteka, ki sploh ne vsebuje identifikacije PDF/X, se vrne kot pxcNone, kar je sam po sebi uporaben odgovor: dokument nikoli ni trdil, da je pripravljen za tisk, vse ostalo, kar validator sporoči, pa pojasnjuje, kaj bi bilo potrebno, da bi tja prišli

Prepovedi so smiselne, ko razmišljate kot ponudnik RIP-ov. Filter /LZWDecode je prepovedan v vseh različicah PDF/X, tako da skladen bralnik nikoli ni odvisen od filtra z zapleteno zgodovino združljivosti in licenciranja; Flate opravlja enako delo brez te prtljage. Koda JavaScript, polja AcroForm in slovarji dodatnih dejanj /AA so prepovedani, ker mora biti tiskarska datoteka fiksen opis oznak na papirju — vse, kar lahko spremeni videz ob odpiranju, krši jamstvo, da je tisto, kar je bilo preizkušeno, tudi tisto, kar se natisne. Oznake mesta OPI (Open Prepress Interface) so prepovedane, ker so po zasnovi sklici na slike visoke ločljivosti, shranjene nekje drugje, "nekje drugje" pa je natanko tisto, kar slepa izmenjava prepoveduje

Zakaj tiskarne zavračajo PDF-je brez TrimBox-a?

TrimBox je končna stran — pravokotnik, ki ostane po rezu giljotine. MediaBox, ki ga ima vsaka stran PDF, je le list: vključuje bleed (dodatek za porezavo), oznake za obrezovanje, registracijske tarče in barvne lestvice. Programska oprema za montiranje strani postavlja strani na tiskalno polo po njihovih okvirjih TrimBox; brez njega mora operater ugibati, kje se vaša vizitka dejansko konča, napačno ugibanje pa odreže bleed ali pusti bel rob na eni strani. Zato standard ISO 15930 zahteva TrimBox (ali ArtBox) na vsaki strani in zato ValidatePdfX sporoči pvxiMissingTrimBox, ko na nobeni strani dokumenta ne najde ključa /TrimBox

Ključ /Trapped odgovarja na drugo vprašanje proizvodnje. Lovljenje barv (trapping) je predtiskarska tehnika rahlega prekrivanja sosednjih barv, tako da majhne registracijske napake tiska ne odprejo belih rež med njimi. Tiskar mora vedeti, ali je bilo to delo že opravljeno: lovljenje barv na že obdelani datoteki podvoji prekrivanja, izpuščanje na neobdelani datoteki pa tvega vidne vrzeli. PDF/X zato zahteva, da slovar Info eksplicitno navaja /Trapped /True ali /Trapped /False — manjkajoči ključ ali vrednost /Unknown sili človeka v pregled datoteke, kar pa je natanko tisti pogovor, ki naj bi ga slepa izmenjava odpravila. Komponenta to označi kot pvxiTrappedNotSet

Izvajanje dvonivojske validacije s TPdf.ValidatePdfX

Metoda TPdf.ValidatePdfX ne sprejme argumentov in vrne zapis TPdfXValidationResult s tremi člani: Conformance (zaznan okus PDF/X), Issues (Pascalova množica vrednosti TPdfXValidationIssue) in pomočnikom IsCompliant. Interno serializira naloženi dokument v tok pomnilnika, zažene pregledovalnik ravni bajtov nad njim in nato prehodi objektni model PDFium za preverjanje vgrajevanja pisav. Minimalna predtiskarska vrata so videti takole:

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;

Ker je Issues običajna Pascalova množica, jo lahko razdelite glede na potrebe vašega delovnega toka — strukturne težave obravnavajte kot stroge zavrnitve, pvxiMissingTitle (ki je v standardu določen s SHOULD in ne z MUST) obravnavajte kot opozorilo, ostalo pa zabeležite. Isti tip zapisa napaja tudi generator poročil komponente, tako da če želite raje izdati človeku berljiv dokument kot pa se vejati na enumih, vzorec iz izgradnje predtiskarskega poročila CLI s komponento PDFium velja za PDF/X nespremenjen

Kaj ujame raven bajtov — in kaj spregleda

Raven bajtov je iskanje žetonov po strukturnih bajtih dokumenta z izpraznjenimi telesi tokov, tako da slika JPEG, ki slučajno vsebuje vzorec bajtov /JavaScript, ne more sprožiti lažno pozitivnega rezultata. Poleg preverjanja označevalcev (XMP pdfxid:GTS_PDFXVersion, OutputIntent z vgrajenim profilom ICC, napovednik /ID, prepoved šifriranja) prehod vsebine doda osem preverjanj, vsako s svojo vrednostjo enuma:

  • pvxiLzwForbidden — filter /LZWDecode se pojavi kjer koli v datoteki (prepovedano v vseh različicah PDF/X)
  • pvxiJavaScriptForbidden — prisotno je dejanje /JavaScript ali imensko drevo
  • pvxiFormFieldsForbidden — obstaja slovar /AcroForm ali vnos /XFA
  • pvxiAdditionalActions — prisoten je slovar dodatnih dejanj /AA
  • pvxiEmbeddedFilesForbidden — prisoten je /EmbeddedFiles ali označitev /FileAttachment
  • pvxiOpiForbidden — vnos /OPI ali /Alternates se sklicuje na zamenljivo slikovno vsebino
  • pvxiMissingTrimBox — na nobeni strani ni najden /TrimBox
  • pvxiTrappedNotSet/Trapped je odsoten ali nastavljen na /Unknown

Iskanje po bajtih je hitro in ne potrebuje izrisovalnika, vendar ima inherentno slepo pego pri pisavah: na tej ravni lahko inšpektor uporabi le grobo hevristiko — dokument označi le, ko sploh ne najde vgrajenega programa pisave. Datoteka z devetimi vgrajenimi pisavami in eno vstavljeno sistemsko pisavo je pri iskanju po bajtih videti v redu. Ta ena vrzel je razlog, zakaj obstaja druga raven

Vgrajevanje pisav prek objektnega modela PDFium

Raven objektnega modela komponente PDFium natančno odgovarja na vprašanje o pisavah. Po prehodu na ravni bajtov metoda TPdf.ValidatePdfX prehodi vsako stran, povpraša FPDFPage_CountObjects po seznamu objektov in za vsak besedilni objekt razreši ročico pisave prek FPDFTextObj_GetFont ter poizve FPDFFont_GetIsEmbedded. Ena nevgrajena pisava kjer koli v dokumentu doda pvxiPdfiumFontNotEmbedded v množico težav. Prehod se skrajša na dveh ravneh — preneha skenirati objekte na strani in preneha nalagati nadaljnje strani takoj, ko je težava potrjena —, tako da na kršiteljskem katalogu s 300 stranmi sodba pogosto pride že po prvi strani

Dve opombi glede meja, ki ju je vredno poznati. Prvič, ta raven potrebuje naloženo knjižnico PDFium in zahteva gradnje, ki izvažajo FPDFFont_GetIsEmbedded; ko izvoz ni prisoten, se preverjanje preskoči in ne spodleti, tako da starejši DLL nikoli ne ustvari lažnih zavrnitev. Drugič, preverjanje odgovarja le na "vgrajeno ali ne" in nič več — ne razlikuje polnega vgrajevanja od delnega (subsetting) niti ne preverja pokritosti znakov (glyphs). Ko datoteka ne uspe in morate vedeti, katera pisava na kateri strani je povzročila napako, tehnike enumeracije iz analize lastnosti pisav PDF s PDFium v Delphiju nadaljujejo točno tam, kjer se logična vrednost validatorja konča

Validacija tokov brez nalaganja dokumenta — ali DLL-ja

Inšpektor na ravni bajtov je izpostavljen tudi kot samostojna funkcija, ValidatePdfXCompliance(Source: TStream) v enoti FPdfPdfx, in je čisti Object Pascal brez odvisnosti od PDFium DLL. Zaradi tega ga je mogoče uporabiti na mestih, kjer izrisovalnik ni dobrodošel: lahka vrata za nalaganje na spletnem strežniku, opravilo CI, ki preverja ustvarjena umetniška dela, ali storitev Lazarus na platformi, kjer ne bi radi pošiljali domačih binarnih datotek. Dovajajte kateri koli tok, ki podpira iskanje (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 jasen: samostojna pot zažene preglede označevalcev in vseh osem vsebinskih preverjanj, ne pa tudi ravni pisav v PDFium, zato se njena sodba o pisavah vrne na grobo hevristiko. Razumna arhitektura uporablja ValidatePdfXCompliance kot poceni prva vrata, polno metodo TPdf.ValidatePdfX pa rezervira za datoteke, ki gredo skozi prvo raven

Kje se ta validator konča in kje se začne polna preverba

Pri predtiskarskih orodjih je pomembna iskrenost, zato je tukaj meja. ValidatePdfX preverja identifikacijske označevalce, strukturne prepovedi, ključe geometrije strani, deklaracijo Trapped in vgrajevanje pisav do posameznih besedilnih objektov. Ne meri skupne pokritosti s črnilom (total ink coverage), ne preverja, ali je vsak barvni prostor zakonit za trden model (npr. pravilo CMYK za X-1a), ne preverja ločljivosti slik glede na tiskarski raster ali ocenjuje vedenja prekrivanja (overprint) in sploščevanja prosojnosti (transparency flattening) — te potrebujejo barvno upravljan predtiskarski mehanizem, dokumentacija same enote pa pravi, da jo za končno certificiranje združite z njim. Dvonivojsko preverjanje vam prinaša 80 % zavrnitev, ki so strukturne in zaznavne zgodaj ter jih v milisekundah ujamete znotraj lastne Delphijeve kode namesto v jutrišnji e-pošti tiskarja

Obe ravni validacije, API-ji za vbrizgavanje označevalcev PDF/X za ustvarjanje skladnih izhodov ter validatorji PDF/A, PDF/UA, PDF/E in PDF/VT, ki delijo enako arhitekturo, so del komponente PDFium Component za Delphi in C++Builder — ena komponenta, od izrisa do predtiskarskih vrat