Teknisk artikel

Konvertering af PDF til PDF/A og reparation af dens metadata

ConvertToPDFA omdanner et almindeligt dokument til et arkivdokument i ét kald: det fjerner det, den valgte del forbyder, tilføjer det, delen kræver, angiver den del, dokumentet giver sig ud for at være, og tjekker derefter resultatet. Kravet rapporteres kun som opfyldt, når tjekket bestås, og GetPDFAConversionReport opregner, hvad der blev gjort, og hvad der stadig står i vejen

Den sidste egenskab er den designbeslutning, der er værd at dvæle ved. En konverter, der stempler kravet uden at tjekke, er værre end slet ingen konverter, fordi en fil, der siger, at den er arkivmæssig uden at være det, passerer lige gennem de systemer, der ellers ville have fanget den. Fejlen dukker op flere år senere, i en revision, på et dokument, ingen kan regenerere

Hvorfor fejler en gyldigt udseende PDF et PDF/A-tjek?

Oftest fordi de to steder, en PDF siger, hvem der skrev den, er uenige. En validator læser både dokumentets information-dictionary og XMP-pakken og afviser en fil, hvor de afviger — og de fleste filer, der fejler på dette punkt, havde aldrig fået skrevet XMP-halvdelen overhovedet

RepairDocumentMetadata bringer dem i overensstemmelse og returnerer, hvor mange poster den reparerede. Hvor kun den ene halvdel bærer en værdi, udfyldes den anden fra den, så intet, der allerede er registreret, bliver kasseret. Ingen skal beslutte, hvilken kopi der er autoritativ, for i praksis er den ene kopi tom

Der er en anden reparation i samme kald, der fanger et mere subtilt tilfælde. Et dokument sat i en PDF/A-tilstand får genoprettet sin standard-identifikation, hvis den var gået tabt, hvilket sker, når en kalder leverer en XMP-pakke selv. Uden den identifikation læser en validator filen som en almindelig PDF og rapporterer hver regel i den pågældende del som uopfyldt — en spektakulært udseende fejl med én lille årsag

var
  Lib: TPDFlib;
  Repaired: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('incoming.pdf', '');
    Repaired := Lib.RepairDocumentMetadata;
    Log(Format('%d metadata entries brought into agreement', [Repaired]));
    Lib.SaveToFile('incoming-fixed.pdf');
  finally
    Lib.Free;
  end;
end;

Valg af del før du konverterer

SetPDFAMode og ConvertToPDFA deler samme tal for tilstande, og tre af værdierne er nylige. Tilstand 9 er PDF/A-4, den del der bygger på PDF 2.0. Tilstand 10 er PDF/A-4e, der yderligere tillader 3D og rich media, og tilstand 11 er PDF/A-4f, der tillader en indlejret fil i ethvert format

Del 4 identificerer sig selv anderledes end delene før den: ved del-nummer og det år, dens del blev udgivet, uden noget conformance-bogstav for almindelig PDF/A-4 og med bogstavet E eller F for de to udvidelser. Tjekket genkender del 4, bedømmer dens filer mod PDF 2.0 frem for 1.7, og rapporterer en del 4-fil, der ikke angiver sit revisionsår

Hver indlejret fil i et del 4-dokument angiver, hvordan den relaterer sig til dokumentet, som både del 3 og 4 kræver. Det er den regel, der plejede at fange almindelige vedhæftninger: relationen blev kun skrevet for vedhæftninger efter den første og aldrig for den sidste, så et dokument med en enkelt vedhæftning — det almindelige tilfælde — bar slet ingen og fejlede validering på netop det punkt

var
  Verdict: Integer;
begin
  Lib.LoadFromFile('report.pdf', '');
  Verdict := Lib.ConvertToPDFA(9);        // 9 = PDF/A-4, 10 = 4e, 11 = 4f
  Memo1.Lines.Text := Lib.GetPDFAConversionReport;
  if Verdict = 1 then
    Lib.SaveToFile('report-pdfa4.pdf')
  else
    Log('conversion incomplete - see the report for what stands in the way');
end;

Hvad konverteringsrapporten er til

At beslutte, hvad man gør herefter. En konvertering, der lykkes, behøver ingen rapport; en konvertering, der ikke gør, er hele grunden til, at rapporten findes. Nogle forhindringer kan fjernes af en konverter, og andre kan ikke — kryptering, forbudt indhold, der bærer mening, et skrifttype-program, der simpelthen ikke findes noget sted på maskinen. Rapporten skelner mellem, hvad der blev gjort, og hvad der resterer, hvilket forvandler "konvertering fejlede" til et arbejdspunkt

Behandl dommen som porten i en batch-pipeline. Konverter, læs dommen, og rutér filen: arkivér dem, der bestod, sæt resten i kø til et menneske med rapporten vedhæftet. Hvad du ikke skal gøre, er at gemme outputtet af en mislykket konvertering i arkivet, fordi det ser bedre ud end inputtet — det bærer nu et krav, som tjekket nægtede at bekræfte

Læsning af det mærke, en fil allerede bærer

Før du konverterer noget som helst, så ved, hvad dokumentet siger om sig selv. Et PDF/A-tjek, der ikke kan læse det eksisterende standard-mærke, bedømmer hver fil mod del 1 uanset hvad den erklærer, hvilket betyder, at et perfekt gyldigt PDF/A-2- eller PDF/A-3-dokument rapporteres som om det bærer intet mærke og som værende af for høj en version — det modsatte af sandheden

Mærket læses, uanset om producenten skrev det som et XMP-element eller som en attribut. Begge former er almindelig XMP, og at acceptere kun den ene efterlader filer fra andre producenter tilsyneladende umærkede. Har du nogensinde undret dig over, hvorfor et dokument, der validerer andre steder, fejler i din egen pipeline, er det et godt sted at kigge først

Sanering før arkivering, og den fejl det kan betale sig at kende

Arkivkonvertering og sanering løber ofte sammen, fordi det indhold, en sikkerhedspolitik ønsker fjernet, overlapper kraftigt med det indhold, PDF/A forbyder. SanitizeDocument fjerner JavaScript, og at fjerne det sidste script fjerner også det tomme navnetræ, det efterlader — et træ, der ellers stadig ville fortælle en læser, at dokumentet bar scripts

Den anden halvdel blev lært på den hårde måde: en off-by-one i pakke-listen betød, at sanering rapporterede at fjerne scripts uden at fjerne nogen, så et dokument, der var blevet saneret, stadig kørte sine scripts, når det blev åbnet. Det er et godt argument for det generelle princip, hele denne artikel hviler på — verificér resultatet frem for at stole på operationen, i din egen pipeline lige så meget som i biblioteket

For det omgivende arkivarbejde, se gennemgangene af PDF/A- og PDF/UA-preflight, sand redaktion og indholds-fjernelse, og PDF/A-3 XMP-udvidelsesskemaer til Factur-X, som dækker metadata-siden, når det arkiverede dokument også bærer strukturerede fakturadata

PDFlibPas er et Pascal-baseret PDF-bibliotek til Delphi, C++Builder og Lazarus, så konvertering, reparation og validering sker alt sammen inde i din egen proces uden noget eksternt værktøj i kæden — se PDFlibPas-produktsiden for de understøttede PDF/A-dele og platforme