Article technique

Preflight PDF/A et PDF/UA en Delphi avec PDF Library for Delphi

PDF/A et PDF/UA répondent à deux questions qui n'ont rien à voir l'une avec l'autre, et les traiter comme une seule case « accessibilité et archivage » est exactement ainsi que des fichiers cassés arrivent dans une archive avec une étiquette de conformité. PDF/A demande si un fichier se rendra encore fidèlement dans vingt ans. PDF/UA demande si une technologie d'assistance peut le lire aujourd'hui. Un document peut réussir l'un haut la main et échouer à l'autre ; le seul verdict honnête vient donc de l'exécution des deux, et de leur exécution avant que le fichier ne soit écrit, pas après qu'un système en aval a fait confiance à l'identifiant de conformité inscrit dans ses métadonnées. Cet identifiant est une auto-déclaration : rien dans le format n'exige qu'il soit vrai, et une application qui écrit « PDF/A-1b » dans le XMP sans valider contre la norme produit un fichier qui paraît conforme à tout consommateur qui ne lit que l'étiquette. losLab PDF Library (PDF Library for Delphi) referme cet écart pour Delphi et C++Builder en intégrant les deux validateurs à la bibliothèque, si bien que le contrôle tourne in-process, sans service externe à déployer

Deux normes qui font échouer les fichiers pour des raisons opposées

ISO 19005 (PDF/A) est un contrat de reproduction : un fichier conforme doit se rendre à l'identique dans des décennies, sur un logiciel qui n'a jamais vu le système d'origine. Ses règles attaquent donc les dépendances externes — chaque police incorporée, la couleur ancrée à un OutputIntent ICC intégré ou exprimée dans des espaces indépendants du périphérique, pas de chiffrement en PDF/A-1, pas de JavaScript, des métadonnées XMP cohérentes avec le dictionnaire d'informations du document. ISO 14289 (PDF/UA) est au contraire un contrat de sémantique : les technologies d'assistance doivent pouvoir parcourir le document et en retirer du sens, ce qui vit dans une couche entièrement différente — arbre de structure complet, texte alternatif sur les figures, titre de document configuré pour l'affichage, niveaux de titres qui ne sautent jamais, relations d'en-têtes de tableau qui tiennent une fois la page hors écran

Les deux sont orthogonales, et les fichiers dangereux se trouvent au milieu. Un document parfait pour l'archivage peut être illisible pour un lecteur d'écran ; un document parfaitement balisé peut référencer une police de bureau qui n'existera plus dans dix ans. Les pipelines qui nécessitent les deux — la publication du secteur public est le cas canonique — doivent exécuter les deux contrôles et acheminer les constats vers des responsables différents, car les polices non incorporées sont un défaut du code de génération tandis que le texte alternatif manquant appartient à l'équipe qui maintient les modèles

Le choix de partie compte autant que le verdict. PDF/A-1, figé sur PDF 1.4, rejette la transparence et JPEG2000 — des fonctionnalités que les sorties de reporting modernes utilisent librement. PDF/A-2 (ISO 19005-2, bâti sur ISO 32000-1) accepte les deux et constitue le choix par défaut raisonnable pour de nouvelles archives. PDF/A-3 autorise en plus des fichiers incorporés de tout type, ce dont dépendent les formats de facturation électronique réglementés. Une équipe qui standardise encore sur PDF/A-1b en 2026 hérite généralement d'une exigence écrite il y a quinze ans, et la correction la moins chère consiste souvent à renégocier la partie cible plutôt qu'à retirer la transparence de chaque graphique

Diagramme PDF Library for Delphi comparant le contrat de reproduction PDF/A avec le contrat de sémantique PDF/UA, plus une matrice réussite-échec de documents satisfaisant l'un et échouant à l'autre
PDF/A garantit un rendu fidèle pour des décennies tandis que PDF/UA garantit une lecture assistée dès aujourd'hui, et aucun des deux verdicts n'implique l'autre

Constats structurés au moment de l'ingestion

Le point d'entrée de l'API flat est CheckFileCompliance, avec le sélecteur de test 1 pour PDF/A et 2 pour PDF/UA. Il renvoie un handle de liste de chaînes dont les éléments sont des constats individuels, ce qui en fait la bonne forme pour une barrière automatisée :

Diagramme PDF Library for Delphi de l'ingestion preflight à l'échelle avec une instance de bibliothèque en streaming par worker, des ouvertures conditionnées à la fin des uploads, et des listes de constats libérées
Les portes ne s'ouvrent qu'après l'achèvement des transferts, chaque worker diffuse l'entrée en lecture seule via une instance privée de la bibliothèque, et chaque handle retourné doit être libéré
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 veut dire "aucun constat" OU "fichier illisible" -- à lever avant de valider
    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;

Deux détails d'implémentation décident si ce mécanisme fonctionne sans surveillance. D'abord, CheckFileCompliance renvoie 0 aussi bien lorsque le fichier est entièrement conforme que lorsque le fichier n'a pas pu être ouvert — en interne, une liste de résultats vide produit 0 dans les deux cas — si bien qu'une barrière qui lit 0 comme un succès laissera entrer des uploads corrompus dans l'archive. Désambiguïsez avec LastErrorCode, comme ci-dessus. Ensuite, le contrôleur travaille sur le lecteur en flux de la bibliothèque plutôt que sur le modèle de document complet : il ouvre le fichier directement avec partage en lecture et n'a jamais besoin de LoadFromFile, ce qui explique pourquoi il gère des fichiers de plusieurs gigaoctets sans construire d'arbres d'objets — mais il échouera pendant qu'un autre processus détient encore le fichier ouvert en écriture, exactement l'état d'un upload en cours. Filtrez après la fin du transfert, pas pendant

Le débit découle du même design. Comme chaque contrôle ouvre son entrée en lecture seule et partage le fichier pour lecture, un audit de corpus se parallélise naturellement entre workers threads ou processus, avec une instance TPDFlib par worker. Les handles de listes de chaînes sont la ressource qui demande de la discipline : tout handle non nul issu de CheckFileCompliance reste alloué jusqu'à ReleaseStringList, et une barrière longue durée qui les fuit se dégrade lentement plutôt que de casser bruyamment

Des rapports pour les humains, des diffs pour les barrières de build

Les listes de constats sont la bonne forme pour une barrière et la mauvaise pour un e-mail à l'équipe modèles. CreatePreflightReport rend la même analyse sous forme de rapport lisible, CreatePreflightReportEx ajoute un sélecteur de format de rapport, et SavePreflightReport écrit le rapport sur disque afin qu'il puisse voyager dans le paquet documentaire — une exigence contractuelle dans beaucoup d'accords de livraison archivistique

Le membre sous-estimé de la famille est ComparePreflightReports. Les résultats de conformité sont une surface de régression comme les autres : une modification de modèle, une nouvelle police d'entreprise ou une mise à niveau de bibliothèque peuvent introduire chacune des constats absents à la version précédente. Conservez sous contrôle de version des rapports de référence pour un corpus de documents représentatifs, régénérez-les après chaque changement, et laissez ComparePreflightReports calculer le delta. Un diff vide devient un artefact de release ; un constat inattendu fait échouer le build au lieu de faire échouer l'audit

Générer une sortie qui réussit dès le premier passage

Le preflight gagne sa place sur les fichiers entrants ; pour les documents produits par votre propre code, réparer les constats après génération prend le problème à l'envers. PDF Library for Delphi embarque un mode côté génération pour chaque norme, et les deux se composent :

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 : obligatoire pour PDF/UA
    // ... dessiner ici le contenu taggé ...
    Diag := Pdf.GetPDFUADiagnostics;
    if Diag <> '' then
      Writeln('fix before shipping: ', Diag);
    Pdf.SaveToFile('statement.pdf');
    // le preflight qui compte tourne sur le fichier enregistré :
    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
  finally
    Pdf.Free;
  end;
end;

Le piège se cache à l'enregistrement. Plusieurs réparations de conformité — forcer le flag d'impression sur les annotations, écrire la valeur par défaut AFRelationship pour les fichiers incorporés PDF/A-3, normaliser l'ordre de tabulation et les descriptions de champs de formulaire pour PDF/UA — sont appliquées pendant la sérialisation du document, pas au moment où le mode est activé. Le document en mémoire n'est pas identique, octet pour octet, à celui qui atteint le disque ; le seul verdict de preflight qui compte est donc celui calculé depuis le fichier enregistré. Validez statement.pdf ; ne déduisez jamais la conformité depuis l'objet document encore en mémoire

Diagramme PDF Library for Delphi des correctifs de conformité appliqués à l'enregistrement pendant la sérialisation, raison pour laquelle le preflight porte sur le fichier PDF sauvegardé plutôt que sur le modèle en mémoire
La sérialisation force les indicateurs d'impression des annotations, définit AFRelationship par défaut et normalise l'ordre de tabulation, si bien que l'inspection mémoire juge des octets que personne n'expédie jamais

Les scénarios de facture qui incorporent du XML lisible machine à côté du document visuel — le modèle ZUGFeRD et Factur-X, bâti sur PDF/A-3 — doivent définir explicitement la relation de pièce jointe via SetPDFA3DefaultAFRelationship, car ISO 19005-3 exige que chaque fichier incorporé déclare son rôle par rapport au document

Arbitres indépendants : veraPDF et Acrobat

Ne laissez jamais un producteur noter sa propre copie. Les contrôleurs PDF Library for Delphi donnent des verdicts rapides, structurés et in-process ; la barrière de release pour les lots d'archive doit tout de même inclure un validateur indépendant. veraPDF est l'implémentation de référence maintenue par la communauté pour la validation PDF/A et l'outil que la plupart des archives nomment dans leurs critères d'acceptation ; les profils de preflight d'Acrobat fournissent une troisième opinion utile lorsque deux outils divergent. Enregistrez le nom et la version du validateur à côté de chaque rapport stocké — prétendre « passe veraPDF » ne signifie pas grand-chose sans savoir quel veraPDF

Lorsque les validateurs ne sont pas d'accord, ce qui arrivera parfois aux marges des normes, réduisez le fichier à un échantillon minimal et tranchez face au texte de la norme plutôt que face à l'un ou l'autre outil. L'exercice prend une heure et révèle généralement soit un bug d'outil à signaler, soit une clause mal lue à documenter dans les notes de conformité de l'équipe

Les entrées chiffrées méritent une règle spéciale. Les deux contrôleurs acceptent un argument mot de passe, mais pour PDF/A-1 le dictionnaire de chiffrement est déjà une violation de conformité — ISO 19005-1 l'interdit purement et simplement — si bien qu'une soumission chiffrée peut être rejetée avant toute analyse plus profonde. Auditer ce qu'un dictionnaire de chiffrement autorise réellement est un exercice séparé, traité dans l'audit du chiffrement PDF et des permissions

Les constats PDF/UA remontent presque toujours à la manière dont l'arbre de structure a été authoré ; les techniques de balisage sont couvertes dans la construction d'arbres de structure Tagged PDF en Delphi. Les archives qui exigent aussi des signatures numériques doivent associer cette barrière au flux décrit dans la signature et validation PAdES. La référence complète de l'API preflight se trouve sur la page produit losLab PDF Library pour Delphi