Et satsvist preflight-verktøy er et konsollprogram uten vindu, rettet mot en mappe med PDF-filer, som validerer hver enkelt mot de samsvarsstandardene du navngir, og etterlater maskinlesbart bevis på hva det fant. Ingen sitter og ser på det. Det kjører klokken to om natten under cron eller Windows Task Scheduler, eller som en port i en CI-rørledning, og den neste personen som bryr seg om utdataene, er enten en planlegger som leser en avslutningskode, eller en revisor som åpner en rapport flere uker senere. Det endrer hva "riktig" betyr. Preflight-motoren til PDFium Component, et PDF-bibliotek med kildekode for Delphi, C++Builder og Lazarus, gjør selve valideringsanropene nesten trivielle. Arbeidet som avgjør om verktøyet gjør nytte for seg, ligger rundt disse anropene: hvilken profil du sjekket, hva avslutningskoden fortalte planleggeren, og om rapporten som ville fanget opp en feil fremdeles eksisterer når noen begynner å lete etter den
Kontrakten: Hva en planlegger faktisk kan se
En CI-runner eller Windows Task Scheduler ser nøyaktig to ting fra verktøyet ditt: avslutningskoden og hvilke filer det la igjen. Logglinjer, konsollfarger, fremdriftsutdata: alt dette er for et menneske som ser på direkte, og klokken to om natten er det ingen som gjør det. Så fastsett avslutningskodens ordforråd før du rører API-et, og hold det kjedelig:
0: hver fil var i samsvar med hver forespurte profil1: minst én fil produserte valideringsfunn2: verktøyet i seg selv mislyktes på minst én fil (korrupt inndata, lås, krasj)
Forskjellen mellom kode 1 og 2 er den team hopper over, for deretter å angre senere. En korrupt PDF som ikke vil åpnes, er ikke en valideringsfeil. Hvis du slår det sammen med kode 1, vil et lastebillass med skadede skanninger dukke opp på dashbordene dine som en plutselig kollaps i samsvar, og sende noen for å jage et standardtilbakeslag som aldri har skjedd, når den virkelige historien er en ødelagt skanner lenger oppstrøms
To andre elementer hører hjemme i kontrakten. Det første er et tidsavbrudd per fil. En patologisk PDF, med tusenvis av sider med dypt nestede objektstrukturer, kan holde på med ett valideringspass i minutter, og et nattlig vindu har ikke tålmodighet til det. Avslutt den filens jobb ved fristen, tell det som en verktøyfeil, og hold den satsvise prosessen i gang. Det andre er en karantenekatalog: flytt alle inndata med tidsavbrudd eller som ikke kan åpnes, til siden i stedet for å la dem ligge. I løpet av noen få måneder akkumulerer den katalogen i stillhet de verste dokumentene de virkelige kundene dine sender inn, og det korpuset er verdt mer for utgivelsestesting enn noen syntetisk prøve du noen gang kunne skrevet for hånd
Å velge standarder, og hvorfor samsvarsnivået er viktig
TPdfPreflightStandard-enumereringen dekker de familiene som dukker opp i praksis: ppsPdfA for ISO 19005 arkivsamsvar, ppsPdfUa for ISO 14289 tilgjengelighet, ppsPdfX for utveksling av utskrifter, pluss ppsPdfE, ppsPdfR og ppsPdfVT for ingeniørarbeid, raster- og variabedata-arbeid. Innenfor en familie leser motoren samsvarsnivået dokumentet hevder, og rapporterer det per standard i resultatets ConformanceName. Å navngi familien er sjelden nok, for det er på nivået den virkelige forskjellen ligger. PDF/A-2b lover visuell reproduserbarhet og ingenting mer. PDF/A-3a legger til et krav om logisk strukturtagging og tillater innebygde kildefiler, som er en langt høyere list for å bli godkjent for skannet materiale som ikke har noe tag-tre i det hele tatt. Gjør du feil i noen retning, og den satsvise jobben lyver for deg. Hvis lagringspolicyen din faktisk ønsker PDF/A-2b, men du avviser filer for manglende struktur-tags, vil rapporten fylles med funn ingen noen gang vil fikse. Aksepter du enhver PDF/A-etikett uten å sjekke nivået, signerer du på dokumenter som møter et svakere krav enn du lovet. Tilgjengelighetskrav fra offentlige innkjøpere stabler i økende grad PDF/UA på toppen av alt dette, noe som ikke gir noen kostnad for kjøringen fordi BuildPdfPreflightReport (fra FPdfPreflightReport-enheten) tar et sett med standarder:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Ett anrop evaluerer begge standardene og leverer tilbake en enkelt, konsolidert rapport
Hvorfor en tom funnliste ikke betyr godkjent
Rapporten teller opp funn per standard, og en tom problemliste betyr bare "ingen problemer funnet i standardene som faktisk kjørte." Dette er en snevrere påstand enn "filen samsvarer med den standarden du bryr deg om," og det er i gapet mellom disse to at den satsvise preflighten gradvis råtner. En konfigurasjonsskrifeil som fjerner ppsPdfA fra utvalget produserer akkurat den samme tomme problemlisten som en genuint ren fil. Vær derfor kritisk til stillhet. Gå gjennom Report.Results og fastslå to ting for hver standard du hadde til hensikt å sjekke: at en resultatoppføring for den eksisterer i det hele tatt, og at dens IsCompliant-flagg, støttet av Status = pfsPass, er satt til sant. En nattlig jobb som likestiller "ingen funn" med "klar for arkiv" uten noen gang å bekrefte hvilke standarder som ble evaluert, er den klassiske måten en mappe med ikke-samsvarende filer glir umerket igjennom i månedsvis, til en ekstern revisor åpner en med veraPDF og hele arkivet trekkes i tvil
En annen felle skjuler seg i hva et funn faktisk er. Hver TPdfPreflightIssue inneholder en Code, en Category, en Description, og en Recommendation, og den navngir regelen som ble brutt, ikke en side eller et objekt. Det er et designvalg med konsekvenser for tilbakemeldingssløyfen. Rapporten forteller det produserende teamet hvilken klasse av feil som eksisterer, en ikke-innleiret font eller en manglende XMP-identifikator, og å finne det spesifikke fornærmende objektet er retteverktøyets jobb lenger nedstrøms, ikke validatorens. Bygg rapportkonsumentene dine mot de stabile Code-verdiene, aldri mot den menneskelesbare beskrivelsesteksten, som kan bli omformulert mellom utgivelser uten forvarsel
Rapportfiler for maskiner og for personen på vakt
Rapporten skriver de samme funnene i fem formater: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile og SaveMarkdownToFile, hver med en matchende funksjon i ToJson-stil når du vil ha strengen i minnet i stedet for på disk. Motstå fristelsen til å velge én. Skriv JSON for pipelinen, slik at CI kan knytte den til jobbregistreringen og tolke problemkoder og statuser per standard uten å skrape tekst. Skriv HTML for mennesket som blir varslet (paged), fordi det åpnes i en hvilken som helst nettleser uten noe verktøy i det hele tatt. De to sammen koster én ekstra linje per fil og sparer vakt-ingeniøren din for den desidert verste oppgaven innen batchbehandling, som er å revers-engineere en rå JSON-blob klokken to om natten for å finne ut hvilken fil som feilet. Én disiplin er viktigere enn valg av format: utled alltid hvert rapportnavn fra inndatafilnavnet, aldri fra et tidsstempel, ellers vil to parallelle kjøringer flette rapporter sammen som du ikke lenger klarer å koble tilbake til inndataene sine
Terskelverdier for alvorlighetsgrad hører hjemme i konfigurasjonen snarere enn i koden. En kommentar (annotation) uten en alternativ beskrivelse er en hard feil for en PDF/UA-innsendingsportal og et ignorerbart notat for et internt arkiv, men likevel er det det identiske funnet i begge tilfeller. Eksponer et nivå for feil-på (fail-on) per profil, slik at retningslinjene kan endres uten en ny kompilering (recompile), og stemple inn nivået som gjaldt inn i selve jobbsammendraget. Neste kvartal vil ingen huske hvilken terskel som var aktiv for partiet fra oktober i fjor, og sammendraget er det eneste stedet der det minnet overlever
Å isolere filer så én dårlig PDF ikke kan senke partiet
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 bevisste valg ligger i denne løkken. En ny TPdf per fil garanterer at ett dokument som korrumperer motortilstanden, ikke kan forgifte filene som følger etter. Den eksplisitte Active-sjekken fortjener sin plass fordi Active := True svelger innlastingsfeil i stedet for å kaste unntak; hvis du fjerner vakten (guard), glir en avkortet fil videre inn i valideringsanropet før den svikter et sted lenger nede med en misvisende melding. Den indre try..except lever bevisst innenfor per-fil-omfanget, slik at ett enkelt unntak (exception) øker feiltelleren og løkken fortsetter. Du vil ha rene rapporter for de 4 999 gode filene, selv om fil 5 000 er knust. I tillegg skrives begge rapportformatene til disk før dommen telles opp, noe som betyr at bevisene overlever selv om en feil (bug) senere i oppsummeringslogikken teller feil
Avslutningskodetilordningen (exit-code mapping) kan deretter reduseres til noen få linjer i prosjektfilen:
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.
Hva preflight ikke vil gjøre for deg
Motoren oppdager; den reparerer ikke. Et funn om en ikke-innleiret font eller et enhetsavhengig fargerom (device-dependent color space) er en arbeidsordre til hvem som enn produserer filene, og validatoren har ingen mulighet til å lappe det (patch) på stedet. Planlegg derfor tilbakemeldingssløyfen (feedback loop) bevisst. Rapporter må lande der det produserende teamet faktisk leser dem, ellers vil de samme funnene dukke opp hver natt helt til noen til slutt spør hvorfor samsvarsgraden (conformance rate) aldri forbedres. Det lønner seg også å kryss-sjekke et utvalg av dommer mot en uavhengig validator, som veraPDF for PDF/A eller Acrobat sin preflight for PDF/X, før en ekstern revisor gjør den sjekken for deg. Når to motorer er uenige om en ekte kundefil, er ikke det dokumentet til bry (a nuisance); det er akkurat det regresjonstilfellet (regression case) utgivelsestestingen (release testing) din manglet. Behold det, navngi det, og kjør det på hver bygging (build)
Ytterligere ett partnerskap er verdt å kjenne til. Den samme valideringsmotoren driver de interaktive sjekkene i et gjennomgangsgrensesnitt (review UI), så denne hodeløse CLI-en (headless CLI) og en arbeidsbenk for PDF-inntaksgjennomgang som møter analytikeren, kan dele et felles valideringsvokabular i stedet for å drive fra hverandre over tid. Og ettersom [ppsPdfA, ppsPdfUa] evaluerer tilgjengelighet i samme passering, vil PDF/UA-siden av den satsvise jobben stemme overens med arbeid på visningssiden (viewer-side), som å bygge en tilgjengelig PDF-leser i Delphi. Profiler, rapportformater og hele preflight-API-et er dokumentert på produktsiden for PDFium Component