PDF/A og PDF/UA besvarer to spørgsmål, der intet har med hinanden at gøre, og at behandle dem som ét afkrydsningsfelt for tilgængelighed-og-arkivering er måden, hvorpå ødelagte filer når et arkiv iført et compliance-mærkat. PDF/A spørger, om en fil stadig vil gengives trofast om tyve år. PDF/UA spørger, om hjælpeteknologi kan læse den i dag. Et dokument kan bestå den ene med bravur og dumpe den anden, så den eneste ærlige dom kommer af at køre begge, og af at køre dem, før filen skrives ud, ikke efter at et system længere nede har fæstet lid til den conformance-identifikator, der er bagt ind i dens metadata. Den identifikator er en selverklæring. Intet i formatet kræver, at den er sand, og et program, der skriver "PDF/A-1b" ind i XMP'en uden at validere mod standarden, producerer en fil, der ser konform ud for enhver aftager, der kun læser mærkatet. losLab PDF Library (PDF Library for Delphi) lukker det hul for Delphi og C++Builder ved at bygge begge validatorer ind i biblioteket, så tjekket kører i processen uden nogen ekstern tjeneste, der skal rejses
To standarder, der dumper filer af modsatte grunde
ISO 19005 (PDF/A) er en gengivelseskontrakt. En konform fil skal gengives identisk om årtier på software, der aldrig så det system, der frembragte den, så reglerne går efter eksterne afhængigheder: hver skrifttype indlejret, farve forankret i en indlejret ICC OutputIntent eller udtrykt i et enhedsuafhængigt rum, ingen kryptering i PDF/A-1, ingen JavaScript, XMP-metadata der stemmer med dokumentets informationsordbog. ISO 14289 (PDF/UA) er derimod en semantikkontrakt. Hjælpeteknologi skal kunne gennemløbe dokumentet og komme ud med mening, og den bor i et helt andet lag: et komplet strukturtræ, alternativ tekst på figurer, en dokumenttitel sat til visning, overskriftsniveauer der ikke springer, tabelhoved-relationer der overlever, når siden er ude af skærmen
Fordi de to standarder patruljerer forskellige lag, er de filer, der bider, dem der sidder imellem. Et arkivperfekt dokument kan være stumt over for en skærmlæser. Et smukt tagget et kan referere til en desktop-skrifttype, der ikke vil findes om ti år. Udgivelse i den offentlige sektor er det sædvanlige sted, hvor begge krav lander på én gang, og en pipeline dér kan ikke klappe dem sammen til én port. Fundene går til forskellige mennesker. Ikke-indlejrede skrifttyper er en defekt i den kode, der genererer PDF'en, mens manglende alternativ tekst hører til hos den, der ejer indholdsskabelonerne, og en rapport, der blander de to, bliver bare videresendt to gange
Hvilken del af PDF/A du sigter efter, betyder lige så meget som, om du rammer den. PDF/A-1 er frosset på PDF 1.4 og afviser gennemsigtighed og JPEG2000, som moderne rapportoutput begge griber efter uden at tænke over det. PDF/A-2 (ISO 19005-2, bygget på ISO 32000-1) accepterer begge og er det fornuftige standardvalg til et nyt arkiv. PDF/A-3 går videre og tillader indlejrede filer af enhver type, hvilket er det, regulerede e-faktureringsformater hviler på. Et team, der stadig standardiserer på PDF/A-1b i 2026, bærer som regel rundt på et krav, nogen skrev for femten år siden, og at genforhandle den valgte del er ofte billigere end at fjerne gennemsigtighed fra hver eneste graf, systemet udsender
Strukturerede fund ved indtag
Indgangspunktet i det flade API er CheckFileCompliance, med testvælgeren 1 for PDF/A og 2 for PDF/UA. Den rækker et handle til en strengliste tilbage, hvis poster er enkeltstående fund, ét per linje, hvilket er præcis den form, en automatiseret port vil gennemløbe:
function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;
var
ListId, I: Integer;
begin
ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0); // 1 = PDF/A
if ListId = 0 then
begin
// 0 betyder "ingen fund" ELLER "filen kan ikke læses" -- afklar det før godkendelse
Result := Pdf.LastErrorCode = 0;
Exit;
end;
for I := 0 to Pdf.GetStringListCount(ListId) - 1 do
LogFinding(FileName, Pdf.GetStringListItem(ListId, I));
Pdf.ReleaseStringList(ListId);
Result := False;
end;
To detaljer afgør, om dette kan køre uden opsyn. Den første er en returværdi, der betyder to modsatte ting. CheckFileCompliance returnerer 0, når filen er fuldt konform, og også når filen slet ikke kunne åbnes, fordi en tom resultatliste internt falder sammen til 0 i begge tilfælde. En port, der læser 0 som en beståelse, vil vinke ødelagte uploads direkte ind i arkivet, så afklar det med LastErrorCode, før du stoler på nullet, sådan som porten ovenfor gør. Den anden handler om, hvor filen er i sin livscyklus. Checkeren kører på bibliotekets streaming-læser i stedet for den fulde dokumentmodel, åbner filen direkte med læsedeling og kalder aldrig LoadFromFile, og det er derfor, den kan tygge sig gennem input på flere gigabyte uden at bygge et objekttræ. Netop den streaming-åbning fejler, mens en anden proces stadig holder filen til skrivning, og en upload i gang er præcis den tilstand. Sæt porten efter overførslen er færdig
Streaming-designet betaler sig igen under belastning. Hvert tjek åbner sit input skrivebeskyttet og deler det til læsning, så en korpusrevision skalerer ud over arbejdstråde eller processer med én TPDFlib-instans per arbejder og ingen kappestrid mellem dem. Den ressource, der kræver disciplin, er selve handlet. Hvert resultat forskelligt fra nul fra CheckFileCompliance forbliver allokeret, indtil du kalder ReleaseStringList, og en langtidskørende port, der glemmer at frigive dem, crasher ikke, den bløder bare langsomt hukommelse, indtil nogen går på jagt efter hvorfor
Rapporter til mennesker, diffs til build-porte
En liste med fund er den rigtige form til en port og den forkerte form til en e-mail til skabelonteamet. CreatePreflightReport gengiver den samme analyse som læsbar prosa, CreatePreflightReportEx tilføjer en vælger til rapportformat, og SavePreflightReport skriver den til disk, så rapporten kan rejse med inde i den leverede dokumentpakke. Rigeligt mange arkivkontrakter gør den rapport til en leverance i sin egen ret, ikke bare et internt artefakt
Det medlem af familien, der stille fortjener sin plads, er ComparePreflightReports. Compliance er en regressionsflade som enhver anden form for adfærd. En justering af en skabelon, en nyligt licenseret virksomhedsskrifttype eller en biblioteksopgradering kan hver især indføre et fund, der ikke var der ved sidste udgivelse, og ingen af dem melder sig selv. Hold gyldne rapporter for et sæt repræsentative dokumenter under versionsstyring, generér dem igen efter hver ændring, og kør ComparePreflightReports for at beregne forskellen. En tom diff er et udgivelsesartefakt værd at gemme. Et overraskende fund fælder builden, hvilket er et langt billigere sted at opdage det end i revisionen
At generere output, der består i første forsøg
Preflight tjener sit brød på filer, der ankommer udefra. For dokumenter, din egen kode producerer, er det den langsomme vej rundt at finde overtrædelser efter genereringen og lappe dem ind igen. PDF Library for Delphi bærer en tilstand på genereringssiden for hver standard, og du kan slå begge til for det samme dokument:
var
Pdf: TPDFlib;
Diag: WideString;
begin
Pdf := TPDFlib.Create;
try
Pdf.NewDocument;
Pdf.SetPDFAMode(1);
Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');
Pdf.SetPDFUAMode('en-US');
Pdf.SetInformation(1, 'Quarterly Statement'); // /Title: påkrævet for PDF/UA
// ... tegn tagget indhold her ...
Diag := Pdf.GetPDFUADiagnostics;
if Diag <> '' then
Writeln('fix before shipping: ', Diag);
Pdf.SaveToFile('statement.pdf');
// den preflight, der tæller, kører på den gemte fil:
Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
finally
Pdf.Free;
end;
end;
Fælden gemmer sig ved gemmetidspunktet. Flere af conformance-reparationerne sker, mens dokumentet serialiseres, frem for når du slår tilstanden til: at gennemtvinge print-flaget på annotationer, at skrive standard-AFRelationship for indlejrede PDF/A-3-filer, at normalisere tabulatorrækkefølge og beskrivelser af formularfelter for PDF/UA. Dokumentet, der ligger i hukommelsen, er ikke byte-identisk med det, der lander på disken, så den eneste preflight-dom, der betyder noget, er den, der beregnes ud fra den gemte fil. Validér statement.pdf selv. Slut dig ikke til compliance ud fra objektet, der stadig ligger i hukommelsen, for de bytes, du så ville bedømme, er ikke de bytes, du leverede
Fakturascenarier, der bærer maskinlæsbar XML ved siden af det visuelle dokument, følger ZUGFeRD- og Factur-X-mønstret, som bygger på PDF/A-3. De bør sætte vedhæftningsrelationen eksplicit med SetPDFA3DefaultAFRelationship, eftersom ISO 19005-3 kræver, at hver indlejret fil erklærer sin rolle i forhold til dokumentet. Lad den stå usat, og den indlejrede XML er blot en blob uden angivet formål, hvilket validatoren bemærker
Uafhængige dommere: veraPDF og Acrobat
En producent bør ikke være den eneste dommer over sit eget output. Checkerne i PDF Library for Delphi giver dig hurtige, strukturerede domme i processen, hvilket er det, du vil have på den varme sti, men udgivelsesporten for et arkivbatch bør stadig sende outputtet gennem en validator, ingen på dit team har skrevet. veraPDF er den fællesskabsvedligeholdte referenceimplementering for PDF/A og det værktøj, de fleste arkiver nævner i deres acceptkriterier, så det er det, man skal stemme overens med. Acrobats preflight-profiler er en nyttig opmand, når veraPDF og tjekket i processen er uenige. Notér validatorens navn og dens version ved siden af hver gemt rapport. En påstand om, at en fil bestod veraPDF, siger meget lidt uden det build-nummer, der lod den bestå, eftersom værktøjet strammer sine regler mellem udgivelser
Validatorer er faktisk uenige i standardernes yderkanter, og når de er det, er svaret ikke at vælge det værktøj, du bedst kan lide. Skrump filen til et minimalt eksempel, der stadig udløser uenigheden, og læs den op mod standardteksten. En times arbejde med det bringer som regel én af to ting frem: en ægte værktøjsfejl værd at melde opstrøms, eller en klausul, dit team har læst forkert og bør skrive ned i compliance-noterne, så den næste ikke skal tage sagen op igen
Krypteret input får en genvej. Begge checkere tager et adgangskodeargument, men en PDF/A-1-fil med en krypteringsordbog er allerede ikke-konform, fordi ISO 19005-1 forbyder kryptering fuldstændigt, så en krypteret indsendelse kan afvises, før nogen dybere analyse overhovedet kører. At regne ud, hvad en krypteringsordbog faktisk giver lov til, er en opgave for sig, dækket i revision af PDF-kryptering og tilladelser
PDF/UA-fund kan næsten altid spores tilbage til, hvordan strukturtræet blev forfattet i første omgang, og tagging-teknikkerne bag det bor i opbygning af strukturtræer i tagget PDF med Delphi. Arkiver, der også kræver digitale signaturer, bør parre denne port med arbejdsgangen i PAdES-signering og -validering. Den fulde API-reference for preflight findes på produktsiden for losLab PDF Library til Delphi