Et batch-preflightværktøj er et konsolprogram uden vindue, rettet mod en mappe med PDF-filer, som validerer hver enkelt op mod de konformitetsstandarder, du angiver, og efterlader maskinlæsbart bevis på, hvad det fandt. Ingen sidder og holder øje med det. Det kører klokken to om natten under cron eller Windows Task Scheduler, eller som en port i en CI-pipeline, og den næste, der bekymrer sig om dets output, er enten en planlægger, der læser en exit-kode, eller en revisor, der åbner en rapport flere uger senere. Det ændrer, hvad "korrekt" betyder. Preflight-motoren i PDFium Component, et kildekodebaseret PDF-bibliotek til Delphi, C++Builder og Lazarus, gør selve valideringskaldene næsten trivielle. Det arbejde, der afgør, om værktøjet tjener sin plads, ligger omkring de kald: hvilken profil du kontrollerede, hvad exit-koden fortalte planlæggeren, og om den rapport, der ville have fanget en fejl, stadig findes, når nogen går på jagt efter den
Kontrakten: hvad en planlægger faktisk kan se
En CI-runner eller Windows Task Scheduler ser præcis to ting fra dit værktøj: exit-koden og de filer, det efterlod. Loglinjer, konsolfarver, fremdriftsoutput: alt det er til et menneske, der ser med live, og klokken to om natten er der ingen. Så fastlæg exit-kodernes ordforråd, før du rører API'et, og hold det kedeligt:
0: hver fil overholdt hver anmodet profil1: mindst én fil gav valideringsfund2: selve værktøjet fejlede på mindst én fil (beskadiget input, lås, nedbrud)
Skellet mellem kode 1 og 2 er det, teams springer over og senere fortryder. En beskadiget PDF, der ikke vil åbne, er ikke en valideringsfejl. Fold den ind i kode 1, og en lastbil fuld af beskadigede scanninger dukker op på dine dashboards som et pludseligt konformitetssammenbrud, og sender nogen ud for at jage en standardregression, der aldrig fandt sted, mens den virkelige historie er en defekt scanner længere oppe i kæden
To yderligere punkter hører til i kontrakten. Det første er en timeout pr. fil. En patologisk PDF, tusindvis af sider med dybt indlejrede objektstrukturer, kan holde et enkelt valideringsgennemløb fanget i flere minutter, og et natligt vindue har ingen tålmodighed til det. Afbryd den fils job ved deadline, tæl det som en værktøjsfejl, og hold batchen i bevægelse. Det andet er en karantænemappe: flyt hvert input, der får timeout eller ikke kan åbnes, til side i stedet for at lade det blive liggende. Over et par måneder akkumulerer den mappe stille og roligt de værste dokumenter, dine rigtige kunder sender, og det korpus er mere værd for udgivelsestest end nogen syntetisk prøve, du kunne skrive i hånden
At vælge standarder, og hvorfor konformitetsniveauet betyder noget
Optællingen TPdfPreflightStandard dækker de familier, der dukker op i praksis: ppsPdfA for ISO 19005-arkivkonformitet, ppsPdfUa for ISO 14289-tilgængelighed, ppsPdfX for trykudveksling, plus ppsPdfE, ppsPdfR og ppsPdfVT for ingeniør-, raster- og variabeldataarbejde. Inden for en familie læser motoren det konformitetsniveau, dokumentet hævder at have, og rapporterer det pr. standard i resultatets ConformanceName. At navngive familien er sjældent nok, for det er på niveauet, den virkelige forskel ligger. PDF/A-2b lover visuel reproducerbarhed og intet derudover. PDF/A-3a tilføjer et krav om logisk strukturmærkning og tillader indlejrede kildefiler, hvilket er en langt sværere barre at klare for scannet materiale, der slet ikke har et mærketræ. Tag fejl af dette i en af retningerne, og batchen lyver for dig. Hvis din opbevaringspolitik i virkeligheden vil have PDF/A-2b, men du dumper filer for manglende strukturmærker, fyldes rapporten med fund, som ingen nogensinde vil rette. Acceptér enhver PDF/A-mærkning uden at kontrollere niveauet, og du godkender dokumenter, der opfylder en svagere barre, end du lovede. Tilgængelighedskrav fra offentlige indkøbere stabler i stigende grad PDF/UA oven på alt dette, hvilket ikke koster noget ekstra i kørslen, fordi BuildPdfPreflightReport (fra FPdfPreflightReport-unit'en) tager et sæt af standarder:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Ét kald evaluerer begge standarder og leverer en enkelt samlet rapportpost tilbage
Hvorfor en tom liste over fund ikke er en bestået kontrol
Rapporten optæller fund pr. standard, og en tom problemliste betyder kun "ingen problemer fundet i de standarder, der faktisk kørte." Det er en snævrere påstand end "filen overholder den standard, du bekymrer dig om," og kløften mellem de to er der, hvor batch-preflight stille og roligt rådner. En konfigurationsslåfejl, der fjerner ppsPdfA fra sættet, producerer nøjagtig den samme tomme problemliste som en ægte ren fil. Så behandl tavshed som mistænkelig. Gennemløb Report.Results, og bekræft to ting for hver standard, du havde til hensigt at kontrollere: at der overhovedet findes en resultatpost for den, og at dens IsCompliant-flag, understøttet af Status = pfsPass, er sandt. Et natligt job, der sætter lighedstegn mellem "ingen fund" og "klar til arkivering" uden nogensinde at bekræfte, hvilke standarder der blev evalueret, er den klassiske måde, hvorpå en mappe med ikke-konforme filer sejler igennem i månedsvis, indtil en ekstern revisor åbner én med veraPDF, og hele arkivet drages i tvivl
En anden fælde gemmer sig i, hvad et fund overhovedet er. Hvert TPdfPreflightIssue bærer en Code, en Category, en Description og en Recommendation, og det navngiver den regel, der blev overtrådt, ikke en side eller et objekt. Det er et designvalg med konsekvenser for feedback-løkken. Rapporten fortæller det producerende team hvilken klasse af defekt der findes, en ikke-indlejret skrifttype eller en manglende XMP-identifikator, og at finde det specifikke krænkende objekt er udbedringsværktøjets opgave længere nede i kæden, ikke validatorens. Byg dine rapportforbrugere op mod de stabile Code-værdier, aldrig mod den menneskelæsbare beskrivelsestekst, som kan blive omformuleret mellem udgivelser uden varsel
Rapportfiler til maskiner og til personen på vagt
Rapportposten skriver de samme fund i fem formater: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile og SaveMarkdownToFile, hver med en tilsvarende ToJson-lignende funktion, når du vil have strengen i hukommelsen frem for på disk. Modstå trangen til at vælge én. Skriv JSON til pipelinen, så CI kan vedhæfte den til jobposten og parse problemkoder og statusser pr. standard uden at skrabe tekst. Skriv HTML til mennesket, der bliver tilkaldt, fordi den åbner i enhver browser helt uden værktøjer. De to sammen koster én ekstra linje pr. fil og sparer din vagthavende ingeniør for den absolut værste opgave i batch-behandling, nemlig at omvendt-konstruere en rå JSON-blok klokken to om natten for at finde ud af, hvilken fil der gik i stykker. Én disciplin betyder mere end formatvalget: udled hvert rapportnavn fra inputfilens navn, aldrig fra et tidsstempel, ellers vil to parallelle kørsler flette rapporter sammen, som du ikke længere kan matche tilbage til deres input
Tærskler for alvorsgrad hører til i konfiguration frem for i kode. En annotation uden alternativ beskrivelse er en hård fejl for en PDF/UA-indsendelsesportal og en ubetydelig note for et internt arkiv, men det er det identiske fund i begge tilfælde. Eksponér et fail-on-niveau pr. profil, så politikken kan ændre sig uden en genkompilering, og stempl det niveau, der var gældende, ind i selve jobresuméet. Næste kvartal vil ingen huske, hvilken tærskel sidste oktobers batch kørte under, og resuméet er det eneste sted, hvor den hukommelse overlever
At isolere filer, så én dårlig PDF ikke kan sænke batchen
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;
Tre bevidste valg lever i den løkke. En frisk TPdf pr. fil garanterer, at ét dokument, der korrumperer motorens tilstand, ikke kan forgifte de filer, der følger efter det. Den eksplicitte Active-kontrol fortjener sin plads, fordi Active := True sluger indlæsningsfejl i stedet for at rejse dem; fjern beskyttelsen, og en afkortet fil driver videre ind i valideringskaldet, før den fejler et sted længere nede med en vildledende besked. Den indre try..except lever inde i scopet pr. fil med vilje, så en enkelt undtagelse hæver fejltælleren, og løkken fortsætter. Du vil have rene rapporter for de 4.999 gode filer, selv når fil 5.000 er flænset. Og begge rapportformater skrives til disk, før dommen gøres op, hvilket betyder, at beviset overlever, selv hvis en fejl senere i resumélogikken tæller forkert
Afbildningen til exit-koder kollapser så til nogle få linjer i projektfilen:
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.
Hvad preflight ikke vil gøre for dig
Motoren registrerer; den reparerer ikke. Et fund om en ikke-indlejret skrifttype eller et enhedsafhængigt farverum er en arbejdsordre til den, der producerer filerne, og validatoren har ingen måde at lappe det på stedet. Så planlæg feedback-løkken bevidst. Rapporter skal lande der, hvor det producerende team faktisk læser dem, ellers dukker de samme fund op hver nat, indtil nogen til sidst spørger, hvorfor konformitetsraten aldrig forbedres. Det betaler sig også at krydstjekke en stikprøve af domme mod en uafhængig validator, veraPDF for PDF/A eller Acrobats preflight for PDF/X, før en ekstern revisor krydstjekker dem for dig. Når to motorer er uenige om en rigtig kundefil, er det dokument ikke en plage; det er præcis det regressionstilfælde, din udgivelsestest manglede. Behold det, navngiv det, og kør det ved hver build
Endnu en sammenkobling er værd at kende. Den samme valideringsmotor driver de interaktive kontroller i et review-UI, så denne hovedløse CLI og en analytikervendt PDF-modtagelses-reviewbænk kan dele et enkelt valideringsordforråd i stedet for at glide fra hinanden over tid. Og fordi [ppsPdfA, ppsPdfUa] evaluerer tilgængelighed i det samme gennemløb, flugter PDF/UA-siden af batchen rent med visningsside-arbejde som at bygge en tilgængelig PDF-læser i Delphi. Profiler, rapportformater og det fulde preflight-API er dokumenteret på produktsiden for PDFium Component