Un instrument de preflight în lot este un program de consolă fără fereastră, îndreptat spre un folder de PDF-uri, care validează fiecare fișier față de standardele de conformitate pe care le numiți și lasă în urmă dovezi lizibile de mașină despre ce a găsit. Nimeni nu stă să îl privească. Rulează la două noaptea sub cron sau Windows Task Scheduler, sau ca poartă într-un pipeline CI, iar următoarea persoană căreia îi pasă de rezultatul lui este fie un scheduler care citește un cod de ieșire, fie un auditor care deschide un raport săptămâni mai târziu. Asta schimbă ce înseamnă „corect”. Motorul de preflight al PDFium Component, o bibliotecă PDF cu cod sursă pentru Delphi, C++Builder și Lazarus, face ca apelurile de validare în sine să fie aproape triviale. Munca ce decide dacă instrumentul își justifică existența se află în jurul acelor apeluri: ce profil ați verificat, ce i-a spus codul de ieșire scheduler-ului și dacă raportul care ar fi prins o greșeală mai există încă atunci când cineva îl caută
Contractul: ce poate vedea efectiv un scheduler
Un runner CI sau Windows Task Scheduler vede exact două lucruri de la instrumentul dvs.: codul de ieșire și orice fișiere pe care le-a lăsat în urmă. Liniile de jurnal, culorile din consolă, ieșirea de progres: toate acestea sunt pentru un om care privește în timp real, iar la două noaptea nu privește nimeni. Așa că fixați vocabularul codurilor de ieșire înainte să atingeți API-ul, și păstrați-l plictisitor:
0: fiecare fișier a fost conform cu fiecare profil solicitat1: cel puțin un fișier a produs constatări de validare2: instrumentul însuși a eșuat la cel puțin un fișier (intrare coruptă, blocaj, blocare/crash)
Distincția dintre codurile 1 și 2 este cea pe care echipele o omit și pe care o regretă mai târziu. Un PDF corupt care nu se deschide nu este un eșec de validare. Includeți-l sub codul 1, iar un camion întreg de scanări deteriorate apare în panourile dvs. de bord ca un colaps brusc de conformitate, trimițând pe cineva să vâneze o regresie de standarde care nu s-a produs niciodată, când adevărata poveste este un scaner defect în amonte
Încă două elemente aparțin contractului. Primul este un timeout per fișier. Un PDF patologic, cu mii de pagini și structuri de obiecte imbricate adânc, poate ține o singură trecere de validare ocupată minute întregi, iar o fereastră nocturnă nu are răbdare pentru asta. Opriți sarcina acelui fișier la termenul-limită, contabilizați-l drept eșec al instrumentului și lăsați lotul să continue. Al doilea este un director de carantină: mutați deoparte fiecare intrare care a expirat sau nu s-a putut deschide, în loc să o lăsați pe loc. În câteva luni, acel director acumulează în tăcere cele mai proaste documente pe care le trimit clienții dvs. reali, iar acel corpus valorează mai mult pentru testarea versiunilor decât orice eșantion sintetic pe care l-ați putea scrie manual
Alegerea standardelor și de ce contează nivelul de conformitate
Enumerarea TPdfPreflightStandard acoperă familiile care apar în practică: ppsPdfA pentru conformitatea de arhivare ISO 19005, ppsPdfUa pentru accesibilitatea ISO 14289, ppsPdfX pentru schimbul destinat tipăririi, plus ppsPdfE, ppsPdfR și ppsPdfVT pentru lucrul de inginerie, raster și date variabile. În interiorul unei familii, motorul citește nivelul de conformitate pe care îl revendică documentul și îl raportează per standard în ConformanceName al rezultatului. A numi familia este rareori suficient, deoarece nivelul este locul unde trăiește diferența reală. PDF/A-2b promite reproductibilitate vizuală și nimic mai mult. PDF/A-3a adaugă o cerință de marcare a structurii logice și permite fișiere sursă încorporate, ceea ce reprezintă o bară mult mai greu de trecut pentru materialul scanat, care nu are deloc un arbore de etichete. Greșiți asta în oricare direcție, iar lotul vă minte. Dacă politica dvs. de retenție dorește de fapt PDF/A-2b, dar respingeți fișiere pentru lipsa etichetelor de structură, raportul se umple cu constatări pe care nimeni nu le va corecta vreodată. Acceptați orice etichetă PDF/A fără să verificați nivelul și semnați pentru documente care îndeplinesc o bară mai slabă decât ați promis. Mandatele de accesibilitate ale cumpărătorilor guvernamentali suprapun tot mai des PDF/UA peste toate acestea, ceea ce nu adaugă niciun cost rulării, deoarece BuildPdfPreflightReport (din unitatea FPdfPreflightReport) ia un set de standarde:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Un singur apel evaluează ambele standarde și returnează o singură înregistrare de raport consolidată
De ce o listă de constatări goală nu înseamnă trecerea testului
Raportul enumeră constatările per standard, iar o listă de probleme goală înseamnă doar „nicio problemă găsită în standardele care chiar au rulat”. Aceasta este o afirmație mai restrânsă decât „fișierul este conform cu standardul care vă interesează”, iar diferența dintre cele două este locul unde preflight-ul în lot putrezește în tăcere. O greșeală de tastare în configurare, care elimină ppsPdfA din set, produce exact aceeași listă de probleme goală ca un fișier cu adevărat curat. Așa că tratați tăcerea ca fiind suspectă. Parcurgeți Report.Results și verificați două lucruri pentru fiecare standard pe care intenționați să îl verificați: că există deloc o intrare de rezultat pentru el, și că indicatorul său IsCompliant, susținut de Status = pfsPass, este adevărat. O sarcină nocturnă care echivalează „nicio constatare” cu „arhivă gata” fără să confirme niciodată ce standarde au fost evaluate este modul clasic în care un folder cu fișiere neconforme trece nedetectat luni de zile, până când un auditor extern deschide unul cu veraPDF și întreaga arhivă este pusă sub semnul întrebării
O a doua capcană se ascunde în ce anume este o constatare. Fiecare TPdfPreflightIssue poartă un Code, o Category, o Description și o Recommendation, și numește regula care a fost încălcată, nu o pagină sau un obiect. Aceasta este o decizie de proiectare cu consecințe pentru bucla de feedback. Raportul îi spune echipei producătoare ce clasă de defect există, un font neîncorporat sau un identificator XMP lipsă, iar găsirea obiectului specific vinovat este treaba instrumentului de remediere din aval, nu a validatorului. Construiți-vă consumatorii de raport pe baza valorilor stabile Code, niciodată pe baza textului descriptiv lizibil pentru oameni, care poate fi reformulat între versiuni fără avertisment
Fișiere de raport pentru mașini și pentru persoana de gardă
Înregistrarea de raport scrie aceleași constatări în cinci formate: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile și SaveMarkdownToFile, fiecare cu o funcție de stil ToJson corespunzătoare, atunci când doriți șirul în memorie, nu pe disc. Rezistați tentației de a alege doar unul. Scrieți JSON pentru pipeline, ca CI să îl poată atașa la înregistrarea sarcinii și să analizeze codurile de probleme și stările per standard fără să extragă text. Scrieți HTML pentru omul care este alertat, deoarece se deschide în orice browser fără nicio unealtă suplimentară. Cele două împreună costă o linie în plus per fișier și îi scutesc pe inginerul de gardă de cea mai proastă sarcină din procesarea în lot, aceea de a face inginerie inversă pe un blob JSON brut la două noaptea pentru a afla ce fișier s-a stricat. O disciplină contează mai mult decât alegerea formatului: derivați fiecare nume de raport din numele fișierului de intrare, niciodată dintr-un timestamp, altfel două rulări paralele vor intercala rapoarte pe care nu le mai puteți asocia înapoi cu intrările lor
Pragurile de severitate aparțin configurării, nu codului. O adnotare fără descriere alternativă este un eșec dur pentru un portal de depunere PDF/UA și o notă ignorabilă pentru o arhivă internă, deși este constatarea identică în ambele cazuri. Expuneți un nivel de fail-on per profil, astfel încât politica să poată fi schimbată fără o recompilare, și înregistrați nivelul care era în vigoare direct în rezumatul sarcinii. Trimestrul viitor, nimeni nu își va mai aminti sub ce prag a rulat lotul din octombrie trecut, iar rezumatul este singurul loc unde acea amintire supraviețuiește
Izolarea fișierelor astfel încât un singur PDF stricat să nu scufunde lotul
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); // instanță nouă pentru fiecare fișier: fără scurgere de stare
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // eșecurile de încărcare sunt silențioase, nu sunt ridicate
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); // teritoriu de cod de ieșire 2, nu un verdict de validare
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
Trei decizii deliberate trăiesc în acea buclă. O instanță nouă de TPdf pentru fiecare fișier garantează că un document care corupe starea motorului nu poate contamina fișierele care îi urmează. Verificarea explicită a Active își câștigă locul deoarece Active := True înghite erorile de încărcare în loc să le ridice; renunțați la această gardă, iar un fișier trunchiat va deriva mai departe către apelul de validare înainte să eșueze undeva în aval cu un mesaj înșelător. try..except-ul interior trăiește intenționat în interiorul scopului per fișier, așa că o singură excepție incrementează contorul de eșecuri, iar bucla continuă. Vreți rapoarte curate pentru cele 4.999 de fișiere bune, chiar și atunci când fișierul 5.000 este distrus. Și ambele formate de raport sunt scrise pe disc înainte ca verdictul să fie totalizat, ceea ce înseamnă că dovezile supraviețuiesc chiar dacă o eroare ulterioară din logica de rezumat numără greșit
Maparea codurilor de ieșire se reduce apoi la câteva linii în fișierul proiectului:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// trecerea mai departe iese cu 0: fiecare fișier a fost conform
end.
Ce nu va face preflight-ul pentru dvs
Motorul detectează; nu repară. O constatare despre un font neîncorporat sau un spațiu de culoare dependent de dispozitiv este un ordin de lucru pentru cel care produce fișierele, iar validatorul nu are cum să îl corecteze pe loc. Așa că planificați bucla de feedback în mod deliberat. Rapoartele trebuie să ajungă acolo unde echipa producătoare chiar le citește, altfel aceleași constatări reapar în fiecare noapte până când cineva întreabă în sfârșit de ce rata de conformitate nu se îmbunătățește niciodată. De asemenea, merită să verificați încrucișat un eșantion de verdicte față de un validator independent, veraPDF pentru PDF/A sau preflight-ul din Acrobat pentru PDF/X, înainte ca un auditor extern să le verifice încrucișat pentru dvs. Când două motoare nu sunt de acord asupra unui fișier real al unui client, acel document nu este o bătaie de cap; este exact cazul de regresie pe care testarea versiunii dvs. îl rata. Păstrați-l, denumiți-l și rulați-l la fiecare build
Mai merită cunoscută o asociere. Același motor de validare conduce verificările interactive dintr-o interfață de revizuire, așa că acest CLI headless și un banc de lucru pentru revizuirea la recepție a PDF-urilor destinat analiștilor pot partaja un singur vocabular de validare, în loc să se îndepărteze unul de celălalt în timp. Și pentru că [ppsPdfA, ppsPdfUa] evaluează accesibilitatea în aceeași trecere, partea de PDF/UA a lotului se aliniază curat cu munca de partea vizualizatorului, precum construirea unui cititor PDF accesibil în Delphi. Profilurile, formatele de raport și API-ul complet de preflight sunt documentate pe pagina de produs a PDFium Component