Article technique

Documents d'ingénierie PDF/E-1 en Delphi avec PDFlibPas

PDF/E-1 est le profil d'archivage des documents d'ingénierie, et PDFlibPas l'implémente comme un mode auteur qu'on active avec SetPDFEMode plus une prévérification bornée qui lit les flux de contenu opérateur par opérateur. Le profil n'est pas PDF/A avec une autre étiquette : il a son propre espace de noms d'identification, sa propre exigence de métadonnées de cycle de vie, et une règle qui rend la validation de contenu plus stricte que dans tout profil d'archivage que vous avez rencontré

Les livrables d'ingénierie sont la raison d'être du profil. Un jeu de plans qui devra être lisible et prouvablement inchangé dans vingt ans, avec un historique de révisions qui survit, et avec des couleurs qui signifient la même chose sur la traceur du bâtiment d'à côté. Ces exigences produisent une spécification dont les demandes se situent surtout hors du contenu de page, dans les métadonnées et la gestion des couleurs, exactement là où un rédacteur PDF générique se trompe

Sa propre identification, pas une variation de PDF/A

La première chose à bien faire est de comprendre que l'identification PDF/E-1 ne peut pas être produite en adaptant le schéma PDF/A ou PDF/X. Elle utilise un espace de noms XMP distinct, http://www.aim.org/pdfe/ns/id/, et la valeur de version doit apparaître à deux endroits : comme entrée d'informations de document et comme propriété XMP qualifiée par l'espace de noms. N'émettre que la propriété XMP, ou que l'entrée d'informations, produit un fichier qui porte l'intention et échoue à la validation

Le output intent a une forme tout aussi spécifique. PDF/E-1 exige un profil ICC embarqué avec l'identifiant de sous-type ISO_PDFE1, et le profil doit avoir un nombre de composantes correspondant à la famille de couleurs périphériques que le document utilise réellement. C'est cette dernière clause qui fait dévier discrètement les implémentations, car elle signifie qu'on ne peut pas choisir l'intent une fois pour toutes puis l'ignorer

Pourquoi la couleur périphérique exige-t-elle un balayage de tout le document ?

Parce que les espaces colorimétriques se cachent dans des dictionnaires de ressources qu'un balayage au niveau page n'atteint jamais. PDF/E-1 traite DeviceRGB et DeviceCMYK comme des familles mutuellement exclusives pour un document, donc valider le profil suppose de connaître chaque espace de couleur périphérique utilisé par quoi que ce soit dans le fichier. Un form XObject a ses propres ressources. Un pattern aussi, et une image de même. Un tiling pattern à l'intérieur d'un form XObject à l'intérieur d'une page est trois niveaux plus profond, et un validateur qui ne contrôle que les ressources de premier niveau de la page laissera passer un document qui utilise les deux familles

Le balayage enregistre donc les espaces colorimétriques en parcourant pages, formulaires, images et patterns comme une seule traversée, et décide seulement ensuite si le document est cohérent et si l'output intent correspond. Le même raisonnement commande l'architecture générale de la prévérification : une traversée partielle produit de faux passages positifs, et un faux positif sur un contrôle de conformité est pire que pas de contrôle, car il est enregistré comme preuve

var
  Lib: TPDFlib;
  Diag: WideString;
begin
  Lib := TPDFlib.Create(nil);
  try
    Lib.LoadFromFile('assembly-drawings.pdf');

    if Lib.SetPDFEMode(1) = 0 then
      raise Exception.Create('PDF/E author mode was refused');

    // Le mode auteur garde les métadonnées de cycle de vie en phase à chaque sauvegarde.
    // Demandez avant de sauvegarder si le document passerait sa propre barrière
    if not Lib.PDFEReadyForSave then
    begin
      Diag := Lib.GetPDFEDiagnostics;
      Writeln('PDF/E blockers: ', Diag);
      Exit;
    end;

    Lib.SaveToFile('assembly-drawings-pdfe.pdf');
  finally
    Lib.Free;
  end;
end;

Les métadonnées de cycle de vie sont une obligation à chaque sauvegarde

PDF/E-1 demande plus qu'un identifiant de document. L'ensemble minimal comprend l'identifiant de document de gestion des médias, un identifiant de version, une classe de rendition, l'heure de création, l'heure de modification, l'heure des métadonnées et un titre. C'est un vocabulaire de suivi de révisions, et il existe parce qu'un livrable d'ingénierie est censé être réédité plutôt qu'écrit une fois

La conséquence pour une implémentation est que ces champs ne peuvent pas être fixés à la création du document. Si l'heure de modification est écrite quand vous activez le mode et que le document est ensuite modifié, l'instantané XMP et l'état réel du document ont divergé, et un validateur qui les compare signale une incohérence que personne n'a voulue. Le mode auteur synchronise donc les champs immédiatement avant chaque sauvegarde, si bien que les métadonnées décrivent les octets sur le point d'être écrits plutôt que ceux qui existaient à l'activation du mode

C'est un principe général pour les métadonnées de conformité, qui mérite d'être énoncé séparément de PDF/E : les métadonnées dérivées appartiennent au chemin de sauvegarde, pas au chemin d'édition. Tout champ calculé depuis l'état du document doit être recalculé au moment où l'état est figé, sinon c'est un cache sans invalidation

Schéma PDFlibPas PDF/E-1 du balayage des couleurs périphériques sur tout le document, qui parcourt les dictionnaires de ressources des pages, form XObject, tiling patterns et images en collectant les familles DeviceRGB et DeviceCMYK avant de juger la cohérence, à côté des champs de métadonnées de cycle de vie que le mode auteur resynchronise immédiatement avant chaque sauvegarde pour que l'instantané XMP corresponde aux octets sur le point d'être écrits
La cohérence des couleurs ne peut être jugée qu'après une traversée atteignant chaque dictionnaire de ressources, et les métadonnées de cycle de vie dérivées sont recalculées au moment où l'état du document est figé, pas à l'activation du mode

La règle qui rend la validation de contenu stricte

PDF/E-1 ne permet pas aux opérateurs de section de compatibilité d'absorber du contenu inconnu. En PDF ordinaire, BX et EX encadrent une région dans laquelle un consommateur doit ignorer les opérateurs qu'il ne reconnaît pas, c'est la trappe de secours qui permet à un producteur d'émettre des constructions plus récentes sans casser les lecteurs plus anciens. Sous PDF/E-1 cette échappatoire est fermée, donc tout opérateur que la prévérification ne reconnaît pas est signalé sans condition, qu'il se trouve ou non dans une section de compatibilité

L'effet sur un validateur est considérable. Il ne peut pas sauter les régions qu'il ne comprend pas, ce qui signifie que l'analyseur d'opérandes doit réellement analyser chaque opérateur de chaque flux de contenu. C'est là que les bornes interviennent. La traversée est plafonnée à 128 niveaux d'imbrication, un million d'objets et 64 MiB de contenu, et ces limites ne sont pas du réglage de performance. Un fichier hostile ou simplement cassé peut présenter un graphe d'objets avec des cycles ou une profondeur d'imbrication qui transforme un validateur récursif en débordement de pile, et ce sont les limites qui empêchent une passe de validation de devenir un vecteur de déni de service. La même posture défensive est décrite dans l'analyse sûre des PDF non fiables

// Validation autonome d'un fichier que vous n'avez pas produit, sans
// le charger dans une instance de document
var
  Issues: TStringList;
  Stream: TFileStream;
  I: Integer;
begin
  Issues := TStringList.Create;
  Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
  try
    if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
      for I := 0 to Issues.Count - 1 do
        Writeln('PDF/E: ', Issues[I]);
  finally
    Stream.Free;
    Issues.Free;
  end;
end;

Ce que la barrière de sauvegarde répare et ce qu'elle refuse

La barrière découpe son travail en deux étapes, et ce découpage est une idée de conception utilisable en soi. D'abord elle normalise ce qui est réparable sans risque : les drapeaux d'impression d'annotations, les drapeaux sans-zoom et sans-rotation des annotations de texte, et le drapeau de génération d'apparence du dictionnaire de formulaire. Ce sont des réglages qui n'ont qu'une seule valeur correcte sous le profil et aucun contenu informationnel, donc les corriger silencieusement est juste, et refuser à leur sujet serait de la pédanterie

Ensuite elle contrôle les contraintes qui ne peuvent pas être réparées sans changer ce que le document signifie : version, identification, chiffrement, output intent, cohérence des couleurs périphériques et présence de contenu de formulaire dynamique. Un document qui échoue sur l'une d'elles est refusé, car inventer un output intent ou choisir une famille de couleurs à la place de l'auteur produirait un fichier qui passe la validation et dénature le contenu

Schéma de la barrière de sauvegarde PDFlibPas PDF/E-1 pour Delphi montrant la prévérification bornée qui analyse chaque opérateur de flux de contenu sous des plafonds de 128 niveaux d'imbrication, un million d'objets et 64 MiB, répare silencieusement les drapeaux d'impression, de zoom et de rotation des annotations, refuse version, identification, chiffrement, output intent, couleur périphérique ou contenu de formulaire dynamique incorrects, et signale les bloqueurs via GetPDFEDiagnostics
La barrière ne répare silencieusement que ce qui ne porte aucune information, refuse toute contrainte qu'une réparation dénaturerait, et transforme le refus en liste de bloqueurs via GetPDFEDiagnostics avant qu'aucun octet n'atteigne le disque

Lire les diagnostics via GetPDFEDiagnostics avant de sauvegarder transforme ce refus en liste exploitable plutôt qu'en opération échouée. Dans un pipeline par lots, appelez-le sur chaque document, journalisez les bloqueurs par fichier et routez les échecs vers une file qu'un humain consulte. C'est bien plus utile qu'une sauvegarde qui lève une exception, car les bloqueurs se regroupent en général : quarante documents qui échouent pour le même output intent manquant, c'est une seule correction, pas quarante

Choisir entre les profils d'archivage

PDF/E-1 est la bonne cible quand le livrable est de la documentation d'ingénierie avec un cycle de révisions, et spécifiquement quand la cohérence des couleurs périphériques compte parce que la sortie part sur des traceurs et des imprimantes grand format. PDF/A est la bonne cible quand l'objectif est la lisibilité à long terme de documents en général, et c'est le profil avec le support de validation le plus large. Les deux ne sont pas interchangeables, et un document peut satisfaire l'un et échouer sur l'autre

Schéma de décision PDFlibPas comparant les profils d'archivage PDF/E-1 et PDF/A pour Delphi : PDF/E-1 pour les livrables d'ingénierie avec cycles de révisions, couleurs de traceur et validation contractuelle sous son propre espace de noms XMP avec output intent ISO_PDFE1, PDF/A pour la lisibilité générale à long terme avec le support de validation le plus large
Partez de qui valide le fichier au bout de la chaîne : les profils exigent des garanties d'identification, de métadonnées et de couleurs différentes, et un document peut satisfaire l'un en échouant sur l'autre

Si vous devez choisir, partez de qui valide le fichier au bout de la chaîne. L'outillage de validation PDF/A est partout, et la prévérification correspondante dans PDFlibPas est décrite dans la prévérification PDF/A et PDF/UA. La validation PDF/E est plus spécialisée et relève en général d'une exigence contractuelle plutôt que d'un défaut. Quand un archivage existant doit être mis au niveau d'un profil pour lequel il n'a jamais été écrit, le chemin de réparation des métadonnées décrit dans la conversion vers PDF/A avec réparation des métadonnées est le schéma à suivre, et la même forme s'applique ici : identifier, réparer ce qui est sûr, refuser le reste avec une liste

Le mode auteur, la prévérification de contenu bornée et le contrôle de conformité autonome sont tous livrés avec la bibliothèque PDF Delphi PDFlibPas, si bien qu'un document peut être produit sous le profil puis vérifié indépendamment via un chemin de code séparé, ce qui est la seule disposition digne de confiance pour une affirmation de conformité