Orodje za paketno predhodno preverjanje (preflight) je konzolni program brez okna, usmerjen v mapo z datotekami PDF, ki vsako datoteko preveri glede na določene standarde skladnosti in ustvari strojno berljivo dokazilo o svojih ugotovitvah. Nihče ga ne spremlja v živo. Zagon se izvede ob dveh zjutraj s storitvijo cron ali Windows Task Scheduler ali pa kot kontrolna točka v cevovodu CI. Naslednji, ki ga zanima rezultat, je bodisi razporejevalnik opravil, ki prebere izhodno kodo, ali pa revizor, ki čez nekaj tednov odpre poročilo. To spremeni pomen besede "pravilno". Mehanizem za preflight v komponenti PDFium Component (knjižnici za PDF z izvorno kodo za Delphi, C++Builder in Lazarus) poskrbi, da so sami klici za preverjanje skoraj trivialni. Delo, ki določa vrednost tega orodja, pa se vrti okoli teh klicev: kateri profil ste preverili, kaj je izhodna koda sporočila razporejevalniku in ali poročilo, ki bi ujelo napako, še vedno obstaja, ko ga nekdo začne iskati
Pogodba: kaj razporejevalnik dejansko vidi
Zagonik CI ali Windows Task Scheduler iz vašega orodja vidi natanko dve stvari: izhodno kodo in datoteke, ki jih je orodje ustvarilo. Vrstice dnevnika, barve v konzoli in izpisi napredka: vse to je namenjeno človeku, ki spremlja izvajanje v živo, a ob dveh zjutraj tega nihče ne počne. Zato določite pomen izhodnih kod, še preden se dotaknete API-ja, in jih ohranite preproste:
0: vsaka datoteka je bila skladna z vsemi zahtevanimi profili1: vsaj ena datoteka je imela opozorila o skladnosti2: orodje samo je odpovedalo pri vsaj eni datoteki (poškodovan vhod, zaklepanje datoteke, sesutje)
Razlikovanje med kodama 1 in 2 je tisto, kar ekipe pogosto izpustijo in pozneje obžalujejo. Poškodovan PDF, ki se ne odpre, ni napaka pri validaciji skladnosti. Če ga združete pod kodo 1, se bo na vaših nadzornih ploščah kup poškodovanih optično prebranih dokumentov prikazal kot nenaden padec skladnosti. To bo nekoga pognalo v iskanje sistemske napake, ki se sploh ni zgodila, medtem ko je dejanski razlog okvarjen skener na začetku procesa
V to pogodbo spadata še dve postavki. Prva je časovna omejitev (timeout) za posamezno datoteko. Patološki PDF s tisoči strani in globoko gnezdenimi strukturami objektov lahko za nekaj minut blokira celoten postopek preverjanja, nočno časovno okno pa nima potrpljenja za to. Ob prekoračitvi roka prekinite obdelavo te datoteke, jo zabeležite kot napako orodja in nadaljujte s paketom. Druga postavka je karantenski imenik: vse datoteke s potekom časa ali tiste, ki jih ni mogoče odpreti, premaknite na stran, namesto da jih pustite na mestu. V nekaj mesecih se bo v tem imeniku tiho nabrala zbirka najslabših dokumentov, ki jih pošiljajo vaše stranke, ta zbirka pa je za testiranje izdaj vredna več kot kateri koli umetni testni primer, ki bi ga lahko napisali sami
Izbira standardov in zakaj je raven skladnosti pomembna
Naštevanje TPdfPreflightStandard pokriva družine standardov, ki se pojavljajo v praksi: ppsPdfA za arhivsko skladnost ISO 19005, ppsPdfUa za dostopnost ISO 14289, ppsPdfX za grafično pripravo in tisk ter ppsPdfE, ppsPdfR in ppsPdfVT za inženiring, raster in variabilne podatke. Znotraj družine mehanizem prebere raven skladnosti, ki jo dokument navaja, in jo za vsak standard vrne v rezultatu ConformanceName. Sama družina standardov redko zadošča, saj se bistvo skriva v ravni skladnosti. PDF/A-2b zagotavlja vizualno ponovljivost in nič več. PDF/A-3a zahteva logično označevanje strukture in dovoljuje vgrajene izvorne datoteke, kar je precej težja ovira za optično prebrano gradivo brez drevesa oznak. Če raven razlagate napačno, bo paketno preverjanje dajalo zavajajoče rezultate. Če vaša politika hrambe zahteva PDF/A-2b, datotek ne zavračajte zaradi pravil, ki pripadajo višji ravni. Če sprejmete katero koli oznako PDF/A brez preverjanja ravni, boste potrdili dokumente, ki dosegajo nižji standard od obljubljenega. Zahteve vladnih kupcev vse pogosteje vključujejo tudi PDF/UA, funkcija BuildPdfPreflightReport iz enote FPdfPreflightReport pa sprejme množico standardov:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
En sam klic ovrednoti oba standarda in vrne enoten združen zapis poročila
Zakaj prazen seznam ugotovitev ne pomeni uspeha
Poročilo navaja ugotovitve za posamezen standard, prazen seznam težav pa pomeni le "v standardih, ki so se dejansko izvedli, nismo našli težav". To je veliko ožja trditev kot "datoteka ustreza standardu, ki vas zanima", in v tej razliki se skriva tihi propad paketnega preverjanja. Tipkarska napaka v konfiguraciji, ki iz nabora izpusti ppsPdfA, ustvari enak prazen seznam težav kot resnično skladna datoteka. Zato obravnavajte tišino kot sumljivo. Pojdite skozi Report.Results in za vsak standard, ki ste ga želeli preveriti, potrdite dvoje: da vnos rezultata zanj sploh obstaja in da je njegova zastavica IsCompliant, podprta s stanjem Status = pfsPass, resnična. Nočno opravilo, ki enači "brez ugotovitev" z "pripravljeno za arhiv", ne da bi sploh potrdilo, kateri standardi so bili preverjeni, je klasičen način, da mapa neskladnih datotek nemoteno prehaja skozi proces mesece dolgo, dokler zunanji revizor ne odpre ene izmed njih z orodjem veraPDF in postavi pod vprašaj celoten arhiv
Druga past se skriva v tem, kaj sploh je posamezna ugotovitev. Vsaka TPdfPreflightIssue vsebuje podatke Code, Category, Description in Recommendation ter poimenuje pravilo, ki je bilo kršeno, ne pa same strani ali objekta. To je načrtovalska odločitev s posledicami za povratno zanko. Poročilo ekipi, ki ustvarja datoteke, pove, kateri razred napake je prisoten (npr. nevgrajena pisava ali manjkajoči identifikator XMP), iskanje konkretnega kršitelja pa je naloga orodij za odpravljanje napak v nadaljnjem procesu in ne samega validatorja. Svoje porabnike poročil zgradite na podlagi stabilnih vrednosti Code in nikoli na podlagi človeku berljivega opisa v Description, saj se slednji med različicami knjižnice lahko spremeni brez opozorila
Datoteke s poročili za stroje in za dežurnega inženirja
Zapis poročila zapiše iste ugotovitve v petih oblikah: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile in SaveMarkdownToFile, pri čemer ima vsaka ustrezno funkcijo tipa ToJson, ko želite niz v pomnilniku namesto na disku. Uprite se skušnjavi, da bi izbrali le eno. Zapišite JSON za cevovod, da ga lahko sistem CI priloži zapisu opravila ter razčleni kode težav in stanja standardov brez brskanja po besedilu. Zapišite HTML za človeka, ki prejme poziv ob napaki, saj se odpre v vsakem brskalniku brez kakršnih koli dodatnih orodij. Uporaba obeh skupaj stane le eno dodatno vrstico kode na datoteko in prihrani vašemu dežurnemu inženirju najhujše opravilo pri paketni obdelavi: ročno razčlenjevanje surovega JSON-a ob dveh zjutraj, da bi ugotovili, katera datoteka se je pokvarila. Ena stvar pa je pomembnejša od same izbire formata: ime vsakega poročila izpeljite iz imena vhodne datoteke in nikoli iz časovnega žiga, sicer bosta dva vzporedna zagona pomešala poročila, ki jih ne boste več mogli povezati z njihovimi vhodi
Pragi resnosti sodijo v konfiguracijo in ne v kodo. Anotacija brez alternativnega opisa je kritična napaka za portal za oddajo dokumentov PDF/UA, hkrati pa zanemarljiva opomba za interni arhiv, čeprav gre v obeh primerih za isto ugotovitev. Omogočite nastavitev praga zavrnitve za vsak profil posebej, da se politika lahko spremeni brez ponovnega prevajanja kode, prag, ki je bil v veljavi, pa zapišite v sam povzetek opravila. Naslednje četrtletje se nihče več ne bo spomnil, pod katerim pragom se je izvajal lanski oktobrski paket, povzetek pa je edino mesto, kjer se ta informacija ohrani
Izolacija datotek, da ena slaba datoteka PDF ne potopi celotnega paketa
procedure RunPreflightBatch(const InputDir, ReportDir: string;
out FilesWithFindings, ToolFailures: Integer);
var
SR: TSearchRec;
Pdf: TPdf;
Report: TPdfPreflightReport;
begin
FilesWithFindings := 0;
ToolFailures := 0;
if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
try
repeat
Pdf := TPdf.Create(nil); // fresh instance per file: no state bleed
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // load failures are silent, not raised
raise EPdfError.Create('Cannot open ' + SR.Name);
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
if Report.TotalIssueCount > 0 then
Inc(FilesWithFindings);
except
on E: Exception do
begin
Inc(ToolFailures); // exit-code-2 territory, not a validation verdict
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
V tej zanki so tri premišljene odločitve. Sveža instanca TPdf za vsako datoteko zagotavlja, da dokument, ki bi morda pokvaril stanje mehanizma, ne more vplivati na datoteke, ki sledijo. Eksplicitno preverjanje lastnosti Active je nujno, ker nastavitev Active := True zadrži napake pri nalaganju, namesto da bi sprožila izjeme. Če to zaščito izpustite, bo skrajšana datoteka prešla v validacijski klic in odpovedala pozneje z zavajajočim sporočilom. Notranji blok try..except je znotraj obsega posamezne datoteke, zato ena sama izjema le poveča števec napak, zanka pa se nadaljuje. Tako dobite poročila za 4.999 dobrih datotek, tudi če je 5.000. datoteka povsem uničena. Obe obliki poročila se zapišeta na disk pred končnim štetjem, zato dokazi preživijo tudi napako pri poznejšem seštevanju
Preslikava izhodne kode se nahaja v nekaj vrsticah v datoteki projekta:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// falling through exits with 0: every file conformed
end.
Česa preflight ne bo naredil namesto vas
Mehanizem zaznava, ne popravlja. Opozorilo o nevgrajeni pisavi ali barvnem prostoru, odvisnem od naprave, je delovni nalog za tistega, ki datoteke ustvarja, in validator nima možnosti, da bi to popravil na mestu. Zato premišljeno načrtujte povratno zanko. Poročila morajo pristati tam, kjer jih ekipa, ki dokumente pripravlja, dejansko bere, sicer se bodo iste ugotovitve ponavljale vsako noč, dokler nekdo končno ne vpraša, zakaj se stopnja skladnosti nikoli ne izboljša. Prav tako se splača primerjati vzorec rezultatov z neodvisnim validatorjem (npr. veraPDF za PDF/A ali Acrobatov preflight za PDF/X), preden to namesto vas stori zunanji revizor. Ko se dva mehanizma ne strinjata glede dejanske datoteke stranke, ta dokument ni nadloga, temveč ravno tisti regresijski primer, ki je manjkal pri vašem testiranju izdaj. Shranite ga, poimenujte in zaženite ob vsaki gradnji programske opreme
Vredno je poznati še eno kombinacijo. Isti validacijski mehanizem poganja interaktivna preverjanja v pregledovalnem uporabniškem vmesniku, zato lahko ta konzolni CLI in analitikom namenjeno delovno okolje za pregled prejetih PDF-jev uporabljata enak validacijski besednjak, namesto da bi se sčasoma oddaljila. Ker [ppsPdfA, ppsPdfUa] ocenjuje dostopnost v istem prehodu, se stran PDF/UA v paketnem procesu lepo ujema z delom na strani pregledovalnika, kot je izdelava dostopnega bralnika PDF v Delphiju. Profili, formati poročil in celoten preflight API so dokumentirani na strani izdelka za PDFium Component