Article technique

Convertir un PDF en PDF/A et Réparer ses Métadonnées en Delphi

ConvertToPDFA transforme un document ordinaire en document d'archivage en un seul appel : il supprime ce que la partie choisie interdit, ajoute ce que la partie exige, déclare la partie que le document réclame, puis vérifie le résultat. La réclamation n'est rapportée comme satisfaite que lorsque la vérification réussit, et GetPDFAConversionReport liste ce qui a été fait et ce qui se tient encore en travers du chemin

Cette dernière propriété est la décision de conception qui mérite que l'on s'y attarde. Un convertisseur qui tamponne la réclamation sans vérifier est pire qu'aucun convertisseur du tout, car un fichier qui se dit d'archivage sans l'être passe droit à travers les systèmes mêmes qui l'auraient autrement intercepté. L échec affleure des années plus tard, lors d'un audit, sur un document que personne ne peut régénérer

Pourquoi un PDF d'apparence valide échoue-t-il à un contrôle PDF/A ?

Le plus souvent parce que les deux endroits où un PDF indique qui l'a écrit ne sont pas d'accord. Un validateur lit à la fois le dictionnaire d'informations du document et le paquet XMP et rejette un fichier où ils diffèrent — et la plupart des fichiers qui échouent sur ce point n'ont tout simplement jamais eu la moitié XMP écrite du tout

RepairDocumentMetadata les met en accord et renvoie le nombre d'entrées qu'il a réparées. Quand une seule moitié porte une valeur, l'autre est remplie à partir d'elle, donc rien de déjà enregistré n'est jeté. Personne n'a à décider quelle copie fait autorité, car en pratique une copie est vide

Il existe une seconde réparation dans le même appel qui attrape un cas plus subtil. Un document réglé sur un mode PDF/A voit son identification de norme restaurée si elle a été perdue, ce qui se produit chaque fois qu'un appelant fournit son propre paquet XMP. Sans cette identification, un validateur lit le fichier comme un PDF ordinaire et signale chaque règle de la partie réclamée comme non satisfaite — un échec spectaculaire avec une petite cause unique

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;

Choisir la partie avant de convertir

SetPDFAMode et ConvertToPDFA partagent la même numérotation de mode, et trois des valeurs sont récentes. Le mode 9 est PDF/A-4, la partie construite sur PDF 2.0. Le mode 10 est PDF/A-4e, qui autorise en outre la 3D et les médias riches, et le mode 11 est PDF/A-4f, qui autorise un fichier incorporé de n'importe quel format

La partie 4 s'identifie différemment des parties précédentes : par numéro de partie et l'année de publication de sa partie, sans lettre de conformité pour le PDF/A-4 simple et la lettre E ou F pour les deux extensions. La vérification reconnaît la partie 4, juge ses fichiers par rapport à PDF 2.0 plutôt que 1.7, et signale un fichier de partie 4 qui n'indique pas son année de révision

Chaque fichier incorporé dans un document de partie 4 indique comment il se rapporte au document, comme les parties 3 et 4 l'exigent toutes deux. C'est la règle qui attrapait autrefois les pièces jointes ordinaires : la relation était écrite uniquement pour les pièces jointes après la première et jamais pour la dernière, donc un document avec une seule pièce jointe — le cas courant — n'en portait aucune du tout et échouait à la validation exactement sur ce point

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;

À quoi sert le rapport de conversion

À décider de la suite. Une conversion qui réussit n'a besoin d'aucun rapport ; une conversion qui ne réussit pas est toute la raison pour laquelle le rapport existe. Certains obstacles sont retirables par un convertisseur et d'autres non — chiffrement, contenu interdit qui porte signification, un programme de police qui n'est tout simplement présent nulle part sur la machine. Le rapport distingue ce qui a été fait de ce qui reste, ce qui transforme « conversion échouée » en élément de travail

Traitez le verdict comme la porte dans un pipeline par lots. Convertissez, lisez le verdict et acheminez le fichier : archivez ceux qui ont réussi, mettez les autres en file pour un humain avec le rapport en pièce jointe. Ce que vous ne devez pas faire, c'est enregistrer la sortie d'une conversion échouée dans l'archive parce qu'elle est meilleure que l'entrée — elle porte désormais une réclamation que la vérification a refusé de confirmer

Lire la marque qu'un fichier porte déjà

Avant de convertir quoi que ce soit, sachez ce que le document dit de lui-même. Un contrôle PDF/A qui ne peut pas lire la marque de norme existante juge chaque fichier par rapport à la partie 1 quelle que soit sa déclaration, ce qui signifie qu'un document PDF/A-2 ou PDF/A-3 parfaitement valide est signalé comme ne portant aucune marque et comme étant d'une version trop élevée — l'inverse de la vérité

La marque est lue que le producteur l'ait écrite comme élément XMP ou comme attribut. Les deux formes sont du XMP ordinaire, et n'accepter qu'une seule laisse les fichiers d'autres producteurs paraître non marqués. Si vous vous êtes déjà demandé pourquoi un document qui valide ailleurs échoue dans votre propre pipeline, c'est un bon endroit pour regarder en premier

Assainir avant archivage, et le bogue à connaître

La conversion d'archivage et l'assainissement vont souvent de pair, car le contenu qu'une politique de sécurité veut supprimer chevauche largement le contenu que PDF/A interdit. SanitizeDocument supprime JavaScript, et supprimer le dernier script supprime aussi l'arbre de noms vide qu'il laisse derrière lui — un arbre qui continuerait autrement d'indiquer à un lecteur que le document portait des scripts

Cette seconde moitié a été apprise à la dure : un décalage d'une unité dans la liste des paquets faisait que l'assainissement signalait la suppression de scripts tout en n'en supprimant aucun, donc un document qui avait été assaini exécutait toujours ses scripts à l'ouverture. C'est un bon argument pour le principe général sur lequel repose tout cet article — vérifiez le résultat plutôt que de faire confiance à l'opération, dans votre propre pipeline autant que dans la bibliothèque

Pour le travail d'archivage environnant, voir les visites guidées de préflight PDF/A et PDF/UA, réparation véritable et suppression de contenu, et schémas d'extension XMP PDF/A-3 pour Factur-X, qui couvre le côté métadonnées quand le document archivé porte aussi des données de facture structurées

PDFlibPas est une bibliothèque PDF native en Pascal pour Delphi, C++Builder et Lazarus, donc la conversion, la réparation et la validation se déroulent toutes à l'intérieur de votre propre processus sans aucun outil externe dans la chaîne — voir la page produit PDFlibPas pour les parties PDF/A et plateformes prises en charge