Articolo tecnico

Convertire un PDF in PDF/A e ripararne i metadati in Delphi

ConvertToPDFA trasforma un documento ordinario in uno archivistico in una sola chiamata: rimuove ciò che la parte scelta vieta, aggiunge ciò che la parte richiede, dichiara la parte che il documento rivendica e poi verifica il risultato. La rivendicazione viene riportata come soddisfatta solo quando il controllo passa, e GetPDFAConversionReport elenca cosa è stato fatto e cosa resta ancora di ostacolo

Quell'ultima proprietà è la decisione di progettazione su cui vale la pena soffermarsi. Un convertitore che appone la rivendicazione senza verificare è peggiore di nessun convertitore, perché un file che dichiara di essere archivistico e non lo è passa indisturbato attraverso gli stessi sistemi che altrimenti lo intercetterebbero. Il fallimento emerge anni dopo, in un audit, su un documento che nessuno può rigenerare

Perché un PDF apparentemente valido non supera un controllo PDF/A?

Nella maggior parte dei casi perché i due punti in cui un PDF dice chi lo ha scritto non sono d'accordo. Un validatore legge sia il dizionario delle informazioni del documento sia il pacchetto XMP e rifiuta un file in cui differiscono — e la maggior parte dei file che falliscono su questo punto semplicemente non ha mai avuto la metà XMP scritta per niente

RepairDocumentMetadata li porta in accordo e restituisce quante voci ha riparato. Dove solo una metà porta un valore, l'altra viene riempita da quello, così niente di quanto già registrato viene scartato. Nessuno deve decidere quale copia sia autorevole, perché in pratica una copia è vuota

C'è una seconda riparazione nella stessa chiamata che intercetta un caso più sottile. Un documento impostato in una modalità PDF/A vede ripristinata la propria identificazione degli standard se era andata persa, il che accade ogni volta che un chiamante fornisce un pacchetto XMP proprio. Senza quell'identificazione un validatore legge il file come un PDF ordinario e riporta ogni regola della parte rivendicata come non soddisfatta — un fallimento dall'aspetto spettacolare con una sola piccola causa

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;

Scegliere la parte prima di convertire

SetPDFAMode e ConvertToPDFA condividono la stessa numerazione delle modalità, e tre dei valori sono recenti. La modalità 9 è PDF/A-4, la parte costruita su PDF 2.0. La modalità 10 è PDF/A-4e, che ammette inoltre 3D e rich media, e la modalità 11 è PDF/A-4f, che ammette un file incorporato di qualsiasi formato

La parte 4 si identifica diversamente dalle parti precedenti: per numero di parte e anno di pubblicazione, senza lettera di conformance per il PDF/A-4 base e con la lettera E o F per le due estensioni. Il controllo riconosce la parte 4, giudica i suoi file rispetto a PDF 2.0 anziché a 1.7, e segnala un file di parte 4 che non dichiara il proprio anno di revisione

Ogni file incorporato in un documento di parte 4 dichiara come si relaziona al documento, come richiedono sia la parte 3 sia la parte 4. Questa è la regola che intercettava gli allegati ordinari: la relazione veniva scritta solo per gli allegati dopo il primo e mai per l'ultimo, così un documento con un solo allegato — il caso comune — non ne portava alcuna e falliva la validazione esattamente su quel punto

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;

A cosa serve il rapporto di conversione

A decidere cosa fare dopo. Una conversione che riesce non ha bisogno di rapporto; una conversione che non riesce è il motivo per cui il rapporto esiste. Alcuni ostacoli sono rimovibili da un convertitore e altri no — cifratura, contenuto proibito che porta significato, un programma di font che semplicemente non è presente da nessuna parte sulla macchina. Il rapporto distingue cosa è stato fatto da cosa resta, il che trasforma «conversione fallita» in un elemento di lavoro

Tratta il verdetto come il gate in una pipeline batch. Converti, leggi il verdetto e instrada il file: archivia quelli che hanno superato, accoda il resto per un umano con il rapporto allegato. Quello che non devi fare è salvare l'output di una conversione fallita nell'archivio perché sembra migliore dell'input — ora porta una rivendicazione che il controllo si è rifiutato di confermare

Leggere il marchio che un file porta già

Prima di convertire qualsiasi cosa, sappi cosa il documento dice di sé. Un controllo PDF/A che non sa leggere il marchio degli standard esistente giudica ogni file rispetto alla parte 1 qualunque cosa dichiari, il che significa che un documento PDF/A-2 o PDF/A-3 perfettamente valido viene riportato come privo di marchio e di versione troppo alta — l'opposto della verità

Il marchio viene letto sia che il producer lo abbia scritto come elemento XMP sia come attributo. Entrambe le forme sono XMP ordinario, e accettarne solo una lascerebbe i file di altri producer apparentemente non marcati. Se ti sei mai chiesto perché un documento che si convalida altrove fallisca nella tua pipeline, questo è un buon posto da cui iniziare

Sanificare prima di archiviare, e il bug che vale la pena conoscere

La conversione archivistica e la sanificazione spesso girano insieme, perché il contenuto che una policy di sicurezza vuole rimuovere si sovrappone pesantemente al contenuto che PDF/A vieta. SanitizeDocument rimuove JavaScript, e rimuovere l'ultimo script rimuove anche l'albero di nomi vuoto che si lascia dietro — un albero che altrimenti continuerebbe a dire a un lettore che il documento portava script

Quella seconda metà è stata imparata a proprie spese: un off-by-one nella lista dei pacchetti faceva sì che la sanificazione riferisse di aver rimosso script senza rimuoverne alcuno, così un documento che era stato sanizzato eseguiva ancora i suoi script all'apertura. È un buon argomento per il principio generale su cui poggia tutto questo articolo — verifica il risultato invece di fidarti dell'operazione, nella tua pipeline tanto quanto nella libreria

Per il lavoro archivistico circostante, vedi le guide su preflight PDF/A e PDF/UA, redazione vera e rimozione del contenuto, e sugli schemi di estensione XMP per PDF/A-3 per Factur-X, che copre il lato metadati quando il documento archiviato porta anche dati strutturati di fatturazione

PDFlibPas è una libreria PDF nativa in Pascal per Delphi, C++Builder e Lazarus, quindi conversione, riparazione e validazione avvengono tutte dentro il tuo processo senza alcuno strumento esterno nella catena — vedi la pagina prodotto PDFlibPas per le parti PDF/A supportate e le piattaforme