Paketinis parengties (preflight) įrankis yra konsolės programa be lango, nukreipta į aplanką su PDF failais, kuri patikrina kiekvieną dokumentą pagal nurodytus atitikties standartus ir palieka mašininiu būdu nuskaitomą įrodymą apie tai, ką rado. Niekas nesėdi ir nestebi jo darbo. Įrankis veikia antrą nakties, paleistas „cron“ arba „Windows Task Scheduler“, ir kitas žmogus, kuriam rūpės jo išvestis, bus planuotojas (scheduler), skaitantis išėjimo kodą (exit code), arba auditorius (auditor), atidarantis ataskaitą po kelių savaičių. Tai keičia sąvokos „teisingas“ (correct) prasmę. Parengties variklis komponente „PDFium Component“ (suteikiančiame prieigą prie PDF bibliotekos „Delphi“, „C++Builder“ ir „Lazarus“ sistemoms) paverčia pačius validacijos iškvietimus beveik trivialiais. Darbas, lemiantis, ar įrankis pasiteisins, sukasi aplink šiuos iškvietimus: kokį profilį patikrinote, ką išėjimo kodas pranešė planuotojui ir ar ataskaita, galėjusi aptikti klaidą, vis dar tebeegzistuoja, kai kas nors pabando ją rasti
Kontraktas: ką iš tikrųjų mato planuotojas
CI paleidėjas (runner) arba „Windows Task Scheduler“ iš jūsų įrankio mato lygiai du dalykus: išėjimo kodą ir bet kokius failus, kuriuos jis paliko po savęs. Žurnalo eilutės, konsolės spalvos, progreso išvestis – visa tai skirta žmogui, kuris stebi procesą gyvai, o antrą valandą nakties to nedaro niekas. Todėl pirmiausia nustatykite išėjimo kodų žodyną, prieš liesdami API, ir palaikykite jį nuobodžiu (boring):
0: kiekvienas failas atitiko kiekvieną prašomą profilį1: bent viename faile rasta validacijos neatitikimų (findings)2: pats įrankis patyrė klaidą bent su vienu failu (sugadintas failas, užraktas, strigtis)
Skirtumą tarp 1 ir 2 kodų komandos dažnai praleidžia, bet vėliau dėl to gailisi. Sugadintas PDF failas, kurio neįmanoma atidaryti, nėra validacijos klaida. Priskirkite jį prie 1 kodo, ir kalnas pažeistų skenavimų pasirodys jūsų prietaisų skydeliuose kaip staigus atitikties kolapsas. Tai privers kažką ieškoti standartų regresijos, kurios niekada nebuvo, kai tikroji priežastis tėra sugedęs skeneris srauto pradžioje
Į kontraktą priklauso dar du elementai. Pirmasis – laiko limitas kiekvienam failui. Patologinis PDF, turintis tūkstančius puslapių ir giliai įdėtas objektų struktūras, gali sulaikyti vieną validacijos perėjimą net kelias minutes, o naktiniam langui kantrybės neužtenka. Nutraukite to failo apdorojimą pasibaigus terminui, priskirkite jį kaip įrankio gedimą ir tęskite paketinį darbą. Antrasis – karantino katalogas: perkelkite kiekvieną failą, kurio laikas baigėsi arba kurio negalima atidaryti, į šalį, vietoj to, kad paliktumėte jį ten pat. Per kelis mėnesius tame kataloge tyliai kaupiasi prasčiausi dokumentai, kokius atsiunčia jūsų tikri klientai, ir tas rinkinys (corpus) yra vertingesnis leidimo (release) testavimui nei bet koks sintetinis pavyzdys, kurį galėtumėte parašyti ranka
Standartų pasirinkimas ir kodėl atitikties lygis yra svarbus
TPdfPreflightStandard enumeracija apima šeimas (families), su kuriomis susiduriama praktikoje: ppsPdfA (ISO 19005 archyvinei atitikčiai), ppsPdfUa (ISO 14289 prieinamumui), ppsPdfX (spaudos mainams), plius ppsPdfE, ppsPdfR bei ppsPdfVT (inžineriniams, rastriniams bei kintamų duomenų darbams). Šeimos viduje variklis nuskaito atitikties lygį, kurį dokumentas teigia turįs, ir praneša tai atitinkamam standartui per rezultato ConformanceName. Nurodyti vien šeimą dažniausiai nepakanka, nes lygis yra tas, kur slypi tikrasis skirtumas. PDF/A-2b žada vizualų atkuriamumą ir nieko daugiau. PDF/A-3a papildomai reikalauja loginės struktūros žymėjimo (tagging) bei leidžia įterpti šaltinio failus, o tai kur kas aukštesnė kartelė skenuotai medžiagai, kuri visiškai neturi jokių žymų medžio. Jei čia suklysite į vieną ar kitą pusę – partija jums meluos. Jei pagal jūsų duomenų saugojimo politiką iš tiesų reikia PDF/A-2b, bet jūs atmetate failus dėl trūkstamų struktūrinių žymų, ataskaita pasipildys išvadomis, kurių niekas niekada netaisys. Jei priimsite bet kokį PDF/A ženklą netikrindami jo lygio – pasirašysite dokumentus, kurie atitinka silpnesnius standartus, nei žadėjote. Valdžios pirkėjų reikalavimai prieinamumui (accessibility) vis dažniau papildomai reikalauja ir PDF/UA standarto, kas neprideda papildomų išlaidų darbo vykdymui, nes BuildPdfPreflightReport (iš FPdfPreflightReport modulio) priima standartų rinkinį (set):
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Vienas iškvietimas įvertina abu standartus ir grąžina vieną konsoliduotą ataskaitos įrašą
Kodėl tuščias rastų klaidų (findings) sąrašas nereiškia sėkmės
Ataskaita nurodo kiekvieno standarto radinius (findings), todėl tuščias klaidų sąrašas reiškia tik tai, kad „fakeluose, kurie buvo įvertinti jokių klaidų nerasta“. Tai gerokai siauresnis teiginys nei „failas atitinka jūsų pageidaujamą standartą“, ir būtent šiame atotrūkyje paketinė parengtis (batch preflight) tyliai tampa nenaudinga. Konfigūracijos klaida, išmetanti ppsPdfA iš patikrinimo rinkinio, sugeneruoja visiškai tokį patį tuščią klaidų sąrašą kaip ir išties švarus failas. Todėl į tylą žiūrėkite įtariai. Pereikite per Report.Results ir patvirtinkite du dalykus kiekvienam standartui, kurį ketinote tikrinti: kad jo rezultato įrašas apskritai egzistuoja, ir kad jo IsCompliant vėliavėlė (flag), paremta savybe Status = pfsPass, yra teisinga (true). Naktinis darbas, kuriam „klaidų nėra“ prilygsta „parengta archyvui“, nė karto nepatvirtinus, kurie standartai buvo vertinami, yra klasikinis būdas, kuriuo tūkstančiai standartų neatitinkančių failų mėnesių mėnesiais praeina patikrą. Kol išorės auditorius atidarys vieną su veraPDF įrankiu, ir visas archyvas taps ginčytinu
Antri spąstai slypi tame, kas apskritai yra „radinys“ (finding). Kiekvienas TPdfPreflightIssue turi parametrą Code (Kodas), Category (Kategorija), Description (Aprašymas) bei Recommendation (Rekomendacija), o jame nurodoma pažeista taisyklė, ne puslapis ar objektas. Tai yra dizaino sprendimas, turintis pasekmių grįžtamajam ryšiui (feedback loop). Ataskaita praneša failus ruošiančiai komandai kokios klasės defektas egzistuoja (neįterptas šriftas arba trūkstantis XMP identifikatorius), o rasti konkretų pažeidimą darantį objektą yra tolesnės grandies ištaisymo įrankio darbas, o ne validatoriaus. Kurkite ataskaitų vartotojus pagal stabilias Code reikšmes, niekada pagal žmogui skaitomą aprašymo tekstą, nes jis gali būti performuluojamas (reworded) tarp versijų (releases) be jokio perspėjimo
Ataskaitų failai sistemoms ir budinčiam darbuotojui
Ataskaitos įrašas išsaugo tuos pačius radinius (findings) penkiais formatais: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile ir SaveMarkdownToFile. Kiekvienas jų turi atitinkamą, į ToJson panašią funkciją, kai jums eilutės (string) reikia atmintyje, o ne diske. Neatsiduokite pagundai pasirinkti tik vieną. Įrašykite JSON dėl duomenų srauto (pipeline), kad CI (continuous integration) procesas galėtų jį pridėti prie darbo įrašo bei nuskaityti išdavimo kodus (issue codes) ar statusus atskiriems standartams netraukdamas (scraping) teksto. Įrašykite HTML žmogui, kuris gauna iškvietimą esant klaidai, nes ji atsidaro bet kurioje naršyklėje nenaudojant papildomų įrankių. Du failai kartu kainuoja vieną papildomą kodo eilutę vienam dokumentui, tačiau tai išvaduoja jūsų budintį inžinierių (on-call engineer) nuo blogiausios užduoties apdorojant paketus: inžinerinio atkūrimo (reverse-engineering) iš nesuformatuoto JSON masyvo blokų antrą valandą nakties, kad suprastų, koks konkrečiai failas sukėlė avariją. Viena disciplina čia svarbesnė už formato pasirinkimą: kiekvieną ataskaitos pavadinimą formuluokite pagal įvesties (input) failo pavadinimą, jokiu būdu ne pagal laiko žymą (timestamp). Priešingu atveju, du paraleliai vykdomi procesai sumaišys (interleave) ataskaitas taip, jog nebebus įmanoma jų susieti su joms priklausančiais įvesties failais
Sunkumo (severity) slenksčiai (thresholds) priklauso konfigūracijai, ne programiniam kodui. Anotacija be jokio alternatyvaus aprašymo (alternate description) yra kritinė klaida PDF/UA pateikimo portale ir tik paprasta pastaba, kurią galima ignoruoti, vidiniame archyve. Vis tik radinys (finding) abiejose situacijose visiškai toks pats. Pateikite klaidos ribą (fail-on level) kiekvienam profiliui atskirai, kad politiką būtų galima keisti neatliekant (recompile) kompiliavimo pakartotinai. Įspauskite tuo metu galiojantį lygį (level) tiesiai į pačią darbo suvestinę. Kitą ketvirtį niekas neatsimins, pagal kokią ribą buvo vykdomas praėjusių metų spalio mėnesio paketinis darbas (batch). Suvestinė – tai vienintelė vieta, išsauganti tą atsiminimą
Failų izoliavimas, kad vienas pažeistas PDF nesužlugdytų partijos
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); // šviežia instancija kiekvienam failui: jokio būsenų persidengimo (state bleed)
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // krovimo klaidos yra tylios, ne-generuoja exception'ų
raise EPdfError.Create('Nepavyko atidaryti ' + 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); // išvesties-kodo-2 teritorija (exit-code-2 territory), ne validacijos išvada
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
Šiame cikle sąmoningai priimti trys sprendimai. Šviežias TPdf kiekvienam failui garantuoja, kad vienas dokumentas sugadinantis variklio būseną (state) negali užkrėsti (poison) failų ateinančių po jo. Aiškus Active tikrinimas užsitarnauja savo vietą, nes Active := True tiesiog „nuryja“ apkrovos klaidas vietoj jų atskleidimo. Jeigu nenaudosite apsaugos (guard), dalinis arba pažeistas failas „praplauks“ tiesiai į validacijos patikrinimą (validation call) ir paskui kažkur procese sukels nesėkmę kartu pranešdamas klaidinančią žinutę. Vidinis blokas try..except tyčia gyvena pačiame „per-file scope“, todėl pasirodžius klaidai, tik klaidos iškvietimas padidėja viena padala. O pats procesas veikia toliau – jums reikalingos „švarios“ ataskaitos likusiems geriems 4999 dokumentams netgi tada, kai 5000-asis bus sugadintas. Galiausiai abu raportų formatai į disko atmintį išsaugomi prieš galutinai suskaičiuojant suvestinę (verdict). Tai garantuoja, kad net įvykus klaidai tolimesnėje suvestinės skaičiavimo dalyje (logic miscounts), pagrindiniai įrodymai išlieka ir nedingsta
Po viso šito, išėjimo kodų paskirstymas (exit-code mapping) programos projekto faile virsta vos keliomis eilutėmis kodo:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// nuoseklus vykdymas pereina (falling through) ir užbaigiamas 0 formatu: visi failai atitinka reikalavimus (every file conformed)
end.
Ko "Preflight" padaryti negali
Apdorojimo variklis sugeba fiksuoti defektus; tačiau nesugeba jų pataisyti. Pastaba dėl ne-integruoto (unembedded) šrifto, ar nuo konkretaus prietaiso priklausančio (device-dependent) spalvų modelio reikalauja sprendimo iš asmens, atsakingo už failų gamybą (producer). Nes pats sistemos validatorius neturi jokios techninės galimybės viską pakoreguoti vietoje. Ataskaitos (reports) turi pasiekti departamentą, kuriame jas iš tikrųjų kas nors peržiūri – antraip tas pats patikrinimo radinys (finding) kartosis kiekvieną naktį, tol kol atsiradęs entuziastas galiausiai paklaus „kodėl reikalavimų suderinamumas apskritai nedidėja“? Verta pasitikrinti sugeneruotų sprendimų pavyzdį naudojant nepriklausomą priemonę (validator) pvz., programą veraPDF skirtą formatui PDF/A arba „Acrobat's preflight“ įskiepį skirtą formatui PDF/X. Taip sužinosite problemą dar prieš atvykstant nepriklausomam išoriniam auditoriui. Tuo atveju jei abudu varikliai (engines) pateiks skirtingą įvertinimą to paties klientinio dokumento atžvilgiu, neignoruokite jo! Toks failas anaiptol nėra laiko švaistymas, nes tai lyg tikra regresinė (regression) klaida, kurios jūsų vidinis patikrinimas dar nebuvo atradęs anksčiau. Išsaugokite, sugalvokite pavadinimą ir prasukite pro patikrinimą su kiekvienu nauju sistemos generavimu
Kitas susiejimas (pairing) taip pat naudingas. Identiškas patikros variklis perima visą interaktyvią veiklą, kai dokumentas analizuojamas specialioje programoje, skirtoje atsiliepimų teikimui (review UI). Dėl šios priežasties atviro tipo terminalo įrankis (CLI) ir speciali su vartotojo sąsaja analitikui pateikta PDF failų priėmimo ir analizės aplinka naudos bendrą techninį žodyną (vocabulary) ir vėliau neims vienas kitam prieštarauti. Kadangi komanda [ppsPdfA, ppsPdfUa] patikrina netgi tokius dokumento atributus kaip pritaikomumą (accessibility) to paties veikimo proceso metu, partijos modulis išlaiko ryšį atliekant užduotis programos vidinėje (viewer-side) aplinkoje, prilygstančias tokiems etapams kaip prieinamo PDF skaitytuvo kūrimas Delphi platformoje. Profiliai, ataskaitų formatai ir visi kitas parengiamasis (preflight API) funkcionalumas pilnai dokumentuotas PDFium Component produkto pagrindiniame informaciniame puslapyje