PDF koji stiže na proizvodnu granicu (production boundary) — u red za štampanje, arhivu, portal za klijentsko otpremanje fajlova — trebalo bi proći kroz reviziju pre nego što ga bilo šta renderuje. Datoteka možda nosi sa sobom neku Launch (pokretanje) akciju koja je programirana da pokrene eksterni program, zatim slike koje su previše grube da bi preživele štampanje, rečnik za šifrovanje koji zapravo zabranjuje baš onaj posao štampanja za koji je i priložena, ili pak PDF/A oznaku kojoj ta datoteka zapravo uopšte ni ne odgovara. Preispitivanje dokumenta naspram pravila kao što su ova pre nego što on uđe u radni proces (workflow) naziva se preflight, a PDFium C API Delphi-ju daje sve što mu je potrebno kako bi implementirao same te provere direktno, bez ikakvog renderovanja makar i jedne jedine stranice
Ovaj članak gradi same te provere: to su četiri klase revizije (audit classes), gde je svaka zapravo po jedna mala rutina koja samo nadovezuje pronalaske i nalaze (findings) u deljenu listu rezultata (shared result list). Interaktivni elementi, metrika resursa, stanje bezbednosti, kao i markeri standarda u potpunosti dobijaju radni i aktivni kôd, podrazumevajući tu čak i samu aritmetiku. Ukoliko je ono što je vama pak potrebno zapravo celokupna pozadinska mašinerija koja prati provere — to jest serijske i iterativne (batch) petlje nad svim fasciklama, JSON i HTML izveštajni fajlovi, i izolacija ponaosob za svaki fajl — PDFium Component isporučuje gotov engine za preflight (preflight engine), pa stoga naš članak o iterativnom preflight-u na komandnoj liniji (CLI) pokriva svu tu logistiku i infrastrukturu. Ovo dvoje zapravo s namerom dele isti vokabular po pitanju izlaznog koda, tako da sam onaj auditor o kom je ovde pisano u potpunosti staje pravo pod taj iterativni i serijski (batch) drajver
Zapis pronalazaka (nalaza) kao i sam ugovor o izlaznom kodu (exit-code contract)
Svaka provera piše sasvim direktno u jedan jedini ravni (flat) tip zapisa, jer ona druga, alternativna varijanta gde bi svaka provera recimo štampala neku svoju ličnu prozu, nikako ne bi mogla naknadno da se broji, a zatim ni da se filtrira pa ni da se nad njom prate granice i pragovi (thresholded). Za sve to su dovoljna svega samo četiri polja
uses
System.SysUtils, System.Math, System.IOUtils,
System.Generics.Collections, pdfium_lib;
type
TFindingSeverity = (fsInfo, fsWarning, fsError);
TPreflightFinding = record
Severity: TFindingSeverity;
Code: string; // stable machine key, e.g. 'ACT-LAUNCH'
Page: Integer; // 1-based; 0 means document level
Message: string; // for humans; free to reword between releases
end;
TFindings = TList<TPreflightFinding>;
procedure Add(Findings: TFindings; Severity: TFindingSeverity;
const Code: string; Page: Integer; const Msg: string);
var
F: TPreflightFinding;
begin
F.Severity := Severity;
F.Code := Code;
F.Page := Page;
F.Message := Msg;
Findings.Add(F);
end;
Svi oni alati nižeg nivoa (downstream tooling) se ključem vezuju uz Code, a apsolutno nikada na sam Message tekst, koji se može menjati. Izlazni kôd tog procesa u potpunosti prati onaj isti trodelni (three-value) ugovor kao što je to opisano i u onom iterativnom (batch) članku: 0 označava da fajl nije proizveo nalaze, 1 znači da nalazi postoje, a 2 ukazuje na to da se revizija apsolutno nije mogla izvršiti jer fajl uopšte i nije mogao biti isparsiran, ili zato što taj fajl prosto zahteva nekakvu lozinku. Držanje tog koda 2 sasvim odvojenim i te kako je važno. Fascikla puna korumpiranih skeniranih slika zapravo u stvarnosti znači polomljeni skener uzvodno (upstream), a ne i sasvim neki iznenadni kolaps na polju usaglašenosti (compliance), te bi pak mešanje i spajanje ta dva potpuno različita razloga podrazumevano naprosto dovelo do toga da se juri jedan sasvim pogrešan problem
Interaktivni elementi: skripte, mete za pokretanje (launch), eksterni linkovi
PDFium klasifikuje apsolutno svaku akciju na koju naiđe po nekom tipu celog broja, i one konstante iz fpdf_doc.h zaista vredi zapisati vrlo precizno, zbog toga što pogrešno prekopirane vrednosti naprosto učine da taj skener sasvim i potpuno tiho i neopaženo postane slep. Pravo to pobrojavanje jeste ovakvo PDFACTION_UNSUPPORTED = 0, PDFACTION_GOTO = 1, PDFACTION_REMOTEGOTO = 2, PDFACTION_URI = 3, PDFACTION_LAUNCH = 4, i PDFACTION_EMBEDDEDGOTO = 5. Primetite tu šta ovde od ovoga fali: nema JavaScript člana. Skripte na nivou dokumenta apsolutno nisu te linkovne akcije i nikada se ne pokazuju kroz dati FPDFAction_GetType; one bivaju pobrojane uz pomoć jedne potpuno odvojene porodice poziva. Auditor koji pak testira tipove akcije naspram neke zamišljene (imagined) JavaScript konstante uspeva uspešno da uradi kompilaciju, i on se pokreće u potpunosti, ali pronalazi apsolutno ništa, i to tako zauvek
const
PDFACTION_GOTO = 1; // in-document jump: harmless
PDFACTION_REMOTEGOTO = 2; // jump into another local file
PDFACTION_URI = 3; // opens an external URL
PDFACTION_LAUNCH = 4; // starts an external program
PDFACTION_EMBEDDEDGOTO = 5; // jump into an embedded file
function ActionTarget(Doc: FPDF_DOCUMENT; Action: FPDF_ACTION;
AType: ULONG): string;
var
Buf: array[0..2047] of AnsiChar;
begin
FillChar(Buf, SizeOf(Buf), 0);
if AType = PDFACTION_URI then
FPDFAction_GetURIPath(Doc, Action, @Buf, SizeOf(Buf))
else
FPDFAction_GetFilePath(Action, @Buf, SizeOf(Buf));
Result := string(UTF8String(PAnsiChar(@Buf)));
end;
procedure AuditPageActions(Doc: FPDF_DOCUMENT; Page: FPDF_PAGE;
PageNo: Integer; Findings: TFindings);
var
StartPos: Integer;
Link: FPDF_LINK;
Action: FPDF_ACTION;
AType: ULONG;
begin
StartPos := 0;
while FPDFLink_Enumerate(Page, @StartPos, @Link) <> 0 do
begin
Action := FPDFLink_GetAction(Link);
if Action = nil then
Continue; // destination-only link, nothing to flag
AType := FPDFAction_GetType(Action);
case AType of
PDFACTION_LAUNCH:
Add(Findings, fsError, 'ACT-LAUNCH', PageNo,
'Launch action targets "' + ActionTarget(Doc, Action, AType) + '"');
PDFACTION_URI:
Add(Findings, fsWarning, 'ACT-URI', PageNo,
'link opens ' + ActionTarget(Doc, Action, AType));
PDFACTION_REMOTEGOTO, PDFACTION_EMBEDDEDGOTO:
Add(Findings, fsWarning, 'ACT-XFILE', PageNo,
'cross-file destination "' + ActionTarget(Doc, Action, AType) + '"');
end; // PDFACTION_GOTO stays silent by design
end;
end;
procedure AuditDocumentBehaviors(Doc: FPDF_DOCUMENT; Findings: TFindings);
var
N: Integer;
begin
N := FPDFDoc_GetJavaScriptActionCount(Doc);
if N > 0 then
Add(Findings, fsError, 'JS-DOC', 0,
Format('%d document-level JavaScript action(s) run on open', [N]));
N := FPDFDoc_GetAttachmentCount(Doc);
if N > 0 then
Add(Findings, fsWarning, 'ATT-EMB', 0,
Format('%d embedded file attachment(s)', [N]));
end;
Samo to razdvajanje na osnovu ozbiljnosti (severity) je zapravo kodirana polisa (politika). Launch akcija je recimo naravno tu bez sumnje (error) greška zato što je pokretanje nekog proizvoljnog programa apsolutno najopasnija moguća stvar koju bi i sam neki klik unutar PDF-a mogao da uradi, a i nijednoj fakturi to nije uopšte ni potrebno (no invoice needs it). Eksterni URI-ji jesu zapravo (warnings) upozorenja: veoma su česti kod legitimnih dokumenata, ali revizor bi svakako trebalo da može da vidi samu metu i bez ikakvog klikanja na nju, i to iz razloga pošto se taj vidljivi tekst unutar linka i sama stvarna njena (actual destination) destinacija apsolutno ne moraju poklapati. GoTo skokovi unutar samog dokumenta su struktura (structure), a ne ponašanje (behavior), pa onda oni ostaju u potpunosti izvan celog izveštaja (report) — jer onaj preflight koji diže paniku na svaki mogući unos u sadržaju samo obučava ljude da ga ignorišu. Za potrebe čitanja tela samih tih skripti koje stoje u pozadini pobrojavanja samog JavaScript-a, kao i za MDP nivoe samog potpisa za detekciju samog XFA, naš članak na temu revizije bezbednosnog rizika hoda po apsolutno istoj toj površini i kroz taj sam omotač (wrapper) tog objekta komponente
Metrika resursa: efektivni DPI slike
Slika unutar PDF-a uopšte nema svoj lični DPI. Ona ima piksele, a stranica na koju se postavlja onda smešta baš sve te njene piksele unutar jednog datog pravougaonika izmerenog u nekim tačkama (points), pri čemu se na 72 takve tačke onda čini svega jedan inč. Rezolucija postoji samo kao jedan redovni odnos između tog dvoje, i upravo zbog toga ta apsolutno ista fotografija u formatu 600 puta 400 može biti i neverovatno oštra (razor sharp) kao neka umanjena sličica (thumbnail), ali ujedno da sa tim u istom momentu postane i onaj potpuno zamućeni nered (blurry mess) ukoliko se pak tu postavi kao ona hero slika rasuta preko cele stranice (full-page hero). Reviziji su zbog toga potrebna oba broja za svaku sliku: dimenzije izvornog piksela izvedene iz njenih metapodataka same te slike, kao i onaj pozicionirani (placed rectangle) pravougaonik iz samih tih redovnih granica objekta (object bounds)
procedure AuditPageImages(Page: FPDF_PAGE; PageNo: Integer;
Findings: TFindings);
var
I, ObjCount: Integer;
Obj: FPDF_PAGEOBJECT;
Meta: FPDF_IMAGEOBJ_METADATA;
L, B, R, T: Single;
WidthPt, HeightPt, DpiX, DpiY, EffDpi: Double;
begin
ObjCount := FPDFPage_CountObjects(Page);
for I := 0 to ObjCount - 1 do
begin
Obj := FPDFPage_GetObject(Page, I);
if FPDFPageObj_GetType(Obj) <> FPDF_PAGEOBJ_IMAGE then
Continue;
if FPDFImageObj_GetImageMetadata(Obj, Page, @Meta) = 0 then
Continue;
if FPDFPageObj_GetBounds(Obj, @L, @B, @R, @T) = 0 then
Continue;
WidthPt := R - L; // placed size on the page, in points
HeightPt := T - B;
if (WidthPt <= 0) or (HeightPt <= 0) or
(Meta.Width = 0) or (Meta.Height = 0) then
Continue;
// 72 points = 1 inch, so placed inches = points / 72, and
// effective DPI = source pixels / placed inches.
DpiX := Meta.Width / (WidthPt / 72.0);
DpiY := Meta.Height / (HeightPt / 72.0);
EffDpi := Min(DpiX, DpiY); // the worse axis decides print quality
if EffDpi < 150.0 then
Add(Findings, fsWarning, 'IMG-LOWRES', PageNo,
Format('image %dx%d px placed at %.1fx%.1f pt = %.0f DPI effective',
[Meta.Width, Meta.Height, WidthPt, HeightPt, EffDpi]))
else if EffDpi > 600.0 then
Add(Findings, fsInfo, 'IMG-BLOAT', PageNo,
Format('image is %.0f DPI at placed size; resampling would ' +
'shrink the file with no visible loss', [EffDpi]));
end;
end;
Ti pragovi su tu samo čista polisa, a ne i nekakva fizika: tih 150 DPI jeste onaj sasvim donji redovni usputni i pod (floor) ispod koga kancelarijsko štampanje biva vidljivo na oko u potpunosti pikselizovano, 300 jeste jedan uobičajeni komercijalni cilj, a sve preko 600 pak tu apsolutno nikako ne kupuje niti donosi (buys) bilo kakav vidljivi kvalitet dok apsolutno pri svemu tom uvećava samu veličinu datog fajla, što je i razlog zašto se onda to redovno i prijavljuje samo kao jedan informacioni balon i redovno naduvavanje (informational bloat) a ne kao jedan dati defekt. Samo jedno pošteno upozorenje (One honest caveat): FPDFPageObj_GetBounds vraća okvir (box) samo poravnat po onoj datoj svojoj osi (axis-aligned), tako da za sliku koja bude postavljena pod nekom redovnom rotacijom (with rotation), izračunata ta brojka zapravo potcenjuje (underestimates) tu njenu pravu gustinu. I FPDF_IMAGEOBJ_METADATA struktura takođe nosi horizontal_dpi i vertical_dpi polja koja PDFium izvodi iz te pune transformacione matrice, tako da i poređenje ova dva rezultata predstavlja jeftin način da prosto zapazite rotirane postavke (placements). Ista tu points-to-pixels (iz tačaka u piksele) aritmetika navodi renderovanje u suprotnom pravcu, što je sve objašnjeno u ovom našem članku o izvozu u dati JPEG
Stanje bezbednosti: šifrovanje i bitovi dozvola (permission bits)
Samo PDF šifrovanje definiše dve lozinke i svaka sa sasvim drugačijim poslovima. Korisnička (user) lozinka kontroliše (gates) dešifrovanje: bez nje sam fajl se neće apsolutno uopšte ni otvoriti, i onda FPDF_LoadDocument tu na komandi vraća onaj nil dok FPDF_GetLastError prijavljuje FPDF_ERR_PASSWORD. Vlasnička (owner) pak onda lozinka sa svoje strane kontroliše dozvole (permissions): jedan fajl koji biva u procedurama zaštićen samo vlasničkom lozinkom se otvara i to apsolutno bez ikakvih akreditiva ali on nosi one redovne restrikcijske bitove (restriction bits) odnosno ograničenja koje obavezno jedan taj usaglašeni (a conforming reader) čitač obavezno mora da ispoštuje. Sam taj pokušaj učitavanja jeste usput onda zbog toga na redu i ona prava prva bezbednosna proba (security probe), te i ta data razlika odlučuje o onom datom izlaznom (exit) kodu — jer fajl koji traži korisničku lozinku je redovno nerevidiv to jest nemoguć za reviziju (kôd 2), dok onaj sa vlasničkom lozinkom uspeva da glatko prođe reviziju (audits normally) sasvim normalno i onda puko akumulira (accumulates findings) same te tamo redovne nalaze
const
FPDF_ERR_PASSWORD = 4;
function AuditSecurity(const FileName: string;
Findings: TFindings): FPDF_DOCUMENT;
var
Perms: ULONG;
Revision: Integer;
begin
Result := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
if Result = nil then
begin
if FPDF_GetLastError() = FPDF_ERR_PASSWORD then
Add(Findings, fsError, 'SEC-USERPW', 0,
'user (open) password required; audit cannot proceed')
else
Add(Findings, fsError, 'DOC-BROKEN', 0, 'file failed to parse');
Exit;
end;
Revision := FPDF_GetSecurityHandlerRevision(Result);
if Revision >= 0 then // -1 means the file is not encrypted
begin
// Opened with an empty password yet encrypted: owner-password-only.
// Anyone may read it, but the permission bits restrict what a
// conforming reader lets them do. Unencrypted files report all
// bits set, which is why the revision gate comes first.
Perms := FPDF_GetDocPermissions(Result);
Add(Findings, fsInfo, 'SEC-ENC', 0,
Format('encrypted, security handler revision %d', [Revision]));
if (Perms and 4) = 0 then // bit 3: print
Add(Findings, fsWarning, 'SEC-NOPRINT', 0,
'printing is not permitted');
if (Perms and 16) = 0 then // bit 5: copy / extract content
Add(Findings, fsInfo, 'SEC-NOCOPY', 0,
'content extraction is not permitted');
if (Perms and 2048) = 0 then // bit 12: high-resolution print
Add(Findings, fsWarning, 'SEC-LOWPRINT', 0,
'only low-resolution printing is permitted');
end;
end;
Ove maske dolaze iz Tabele 22 unutar ISO 32000-1, koja inače i broji bitove krenuvši baš od 1: bit 3 unutar te /P vrednosti na komandi jeste maska 4, bit 5 jeste onda redovna maska 16, a onaj bit 12 jeste redom 2048. Da li je dati nalaz bitan zapravo i predstavlja jednu sasvim odluku o rutiranju (routing decision). Jedan biro (bureau) za štampanje bi trebalo da odbaci (bounce) taj SEC-NOPRINT fajl odmah pri prijemu (at intake), tamo gde bi taj podnosilac (submitter) dobio jednu jasnu poruku (message) o tome, umesto da se to (rather than) desi tek tamo na onom RIP-u (Raster Image Processor) i to tri sata pre isteka datog tog roka (deadline). Arhiva bi trebala da i sam onaj SEC-ENC tretira kao jednog blokera (blocker), jer se šifrovanje i dugoročno očuvanje (preservation) apsolutno ne mešaju (do not mix) — a to jeste upravo ta poenta i stvar koju ta provera standarda upravo sprema da napravi na komandama i potpuno formalno
Markeri standarda: iščitavanje PDF/A tvrdnje (claim)
Jedan fajl deklariše svoju PDF/A usaglašenost (conformance) u svom paketu sa XMP metapodacima, i to kroz to dato svojstvo (property) po imenu pdfaid:part (1 do 4) i tu usputnu datu osobinu pdfaid:conformance (onim slovom za nivo (level letter), kao što je recimo b za redovnu vizuelnu vernost (visual fidelity) ili a za puno strukturalno označavanje). PDFium-ov redovni C API nažalost uopšte ne nudi apsolutno nikakav mehanizam za pristup (accessor) za XMP; FPDF_GetMetaText čita samo onaj Info rečnik (Info dictionary), što uopšte i nije ono mesto gde ta identifikacija živi. Izlaz u slučaju nužde (The escape hatch) jeste i samo pravilo unutar samog tog standarda: to da ISO 19005 apsolutno zahteva da onaj XMP tok sa metapodacima (metadata stream) obavezno mora da biva sačuvan nekompresovan, i to upravo zato kako bi ga svi alati mogli naći bez celokupnog punog PDF parsera. Skeniranje sirovog bajta jeste stoga legitiman detektor tvrdnje (claim detector) — a fajl čija se tvrdnja skriva unutar kompresovanog toka (compressed stream) jeste već prekršio onaj standard koji on i tvrdi (da zadovoljava)
function PdfAClaim(const FileName: string): string;
var
Bytes: TBytes;
S: RawByteString;
P, Limit: Integer;
begin
Result := ''; // empty = no PDF/A claim present
Bytes := TFile.ReadAllBytes(FileName);
if Length(Bytes) = 0 then
Exit;
SetString(S, PAnsiChar(@Bytes[0]), Length(Bytes));
P := Pos('pdfaid:part', S); // XMP identification schema
if P = 0 then
Exit;
// Handles both <pdfaid:part>2</pdfaid:part> and pdfaid:part="2":
// take the first digit after the property name.
Limit := Min(P + 32, Length(S));
Inc(P, Length('pdfaid:part'));
while (P <= Limit) and not (S[P] in ['1'..'4']) do
Inc(P);
if P <= Limit then
Result := 'PDF/A-' + Char(S[P]);
end;
Nalaz je namerno samo informativan, jer je tvrdnja deklaracija, a ne svojstvo datoteke. XMP unos je jedan red XML-a koji može zapisati bilo koji proizvođač, uključujući neispravan; stvarna usklađenost znači da datoteka zadovoljava stotine pravila o ugrađenim fontovima, bojama nezavisnim od uređaja i zabranjenim funkcijama. Otkrivanje tvrdnje govori koje datoteke treba poslati na pravu validaciju i ništa više. Ugrađeni preflight engine komponente obavlja tu validaciju za PDF/A, PDF/UA i PDF/X profile, a članak o grupnom CLI preflight-u pokazuje kako da ga povežete u cevovod sa izveštajima koje revizor može kasnije da otvori
Jedno pokretanje naspram problematičnog fajla
Ovaj drajver u potpunosti niže (strings together) ove provere na sledeći način: prvo bezbednost, jer ona upravo i odlučuje o tome da li ta revizija uopšte može i da se pokrene (runs at all), a zatim idu ponašanja (behaviors) na redovnom nivou samog dokumenta kao i sama ta tvrdnja (claim) o standardu (standards), i tek onda ide ta neka redovna petlja (loop) po svim stranicama za te akcije i slike
function AuditFile(const FileName: string; Findings: TFindings): Integer;
var
Doc: FPDF_DOCUMENT;
Page: FPDF_PAGE;
I: Integer;
Claim: string;
begin
Doc := AuditSecurity(FileName, Findings);
if Doc = nil then
Exit(2); // audit failure, not a verdict
try
AuditDocumentBehaviors(Doc, Findings);
Claim := PdfAClaim(FileName);
if Claim <> '' then
Add(Findings, fsInfo, 'STD-PDFA', 0,
Claim + ' conformance claimed (declaration only, not validated)');
for I := 0 to FPDF_GetPageCount(Doc) - 1 do
begin
Page := FPDF_LoadPage(Doc, I);
if Page = nil then
begin
Add(Findings, fsError, 'PAGE-BROKEN', I + 1, 'page failed to parse');
Continue;
end;
try
AuditPageActions(Doc, Page, I + 1, Findings);
AuditPageImages(Page, I + 1, Findings);
finally
FPDF_ClosePage(Page);
end;
end;
finally
FPDF_CloseDocument(Doc);
end;
if Findings.Count > 0 then
Result := 1
else
Result := 0;
end;
Naspram neke brošure (brochure) koja se vratila iz neke spoljne agencije (outside agency), ovaj izlaz izgleda ovako:
> preflight_audit brochure_final.pdf
brochure_final.pdf: 5 finding(s)
[ERROR] ACT-LAUNCH page 3 Launch action targets "..\tools\setup.exe"
[ERROR] JS-DOC doc 2 document-level JavaScript action(s) run on open
[WARNING] IMG-LOWRES page 7 image 412x287 px placed at 396.0x275.8 pt = 75 DPI effective
[WARNING] SEC-NOPRINT doc printing is not permitted
[INFO] STD-PDFA doc PDF/A-2 conformance claimed (declaration only, not validated)
exit code 1
Svaka ova linija ponaosob ukazuje na potrebu za akcijom, ali njihova kombinacija jeste ta stvarna i prava presuda (verdict). Ovaj fajl tvrdi da poseduje PDF/A-2 dok ujedno sa sobom u potpunosti nosi rečnik za šifrovanje i onaj živi (live) i aktivan JavaScript, a PDF/A oboje i eksplicitno i u potpunosti zabranjuje (forbids both outright) — pa stoga jeste ova tvrdnja (claim) apsolutno dokazano lažna i pre nego što se bilo koji onaj duboki validator uopšte i pokrene. To jeste apsolutno ta vrsta kontradikcije koju izbacuje na površinu (surfaces) jedna ravna lista nalaza, a koju skriva (hides) neki logički pristup tipa prolazi/pada (boolean pass/fail)
Šta vam ova revizija ne može reći
Iskrenost o opsegu je ono što preflight alat drži pouzdanim. Sve iznad navedeno iščitava ono što fajl i sam lično deklariše o sebi: PDFium parsira strukturu, a ova revizija to inventariše. Ona ni na koji način ne vrši PDF/A validaciju — nema provera pokrivenosti glifovima naspram ugrađenih fontova, nema analize prostora boja naspram namera izlaza (output intents), i nema ni jednog pravila na nivou klauzula koja odvajaju (separate) tvrdnju (claim) od stvarne usaglašenosti; za to vam je potreban namenski validator kao što je onaj ugrađeni preflight engine u komponenti ili veraPDF. Bitovi dozvole jesu deklaracije koje usaglašeni čitači poštuju, a nikako nekakvi kriptografski zidovi, pa stoga SEC-NOPRINT opisuje nameru a ne sprovođenje (enforcement) te neke zabrane. Skeniranje akcija pokriva anotacije linkova i skripte na nivou samog dokumenta; dok za skripte koje su zakopane unutar rečnika za događaje na poljima za unos (form-field), biće vam potrebni i API-ji za forme i to povrh svega toga (on top). A provera potpisa, ako njom proširite tu reviziju prijavljuje deklarisanu nameru, a nikako nekakvu verifikovanu kriptografiju — ta validacija lanca sertifikata jeste redovno jedan potpuno odvojeni posao. Preflight revizija jeste prijemni intervju onako (intake interview), a ne suđenje (trial): njen posao jeste da učini da ta odluka o rutiranju bude informisana, brza, i na kraju ponovljiva (repeatable)
Napomena: Dokument, stranica, anotacija, kao i API-ji za objekte slike koji su korišćeni kroz ovu reviziju (audit), zajedno sa nekim Delphi omotačem visokog nivoa i jednim punim preflight engine-om za validaciju standarda, isporučuju se uz PDFium Component