Article technique

Automatiser les contrôles preflight PDF dans Delphi avec HotPDF

Le lot mensuel de relevés semblait correct sur tous les écrans développeur, il a donc été livré. L'imprimeur a renvoyé tout le lot deux jours plus tard : images RGB dans un job CMYK et aucune déclaration /Trapped. Le coût n'était pas la réimpression ; c'était deux jours sur une échéance réglementaire. « Preflight » est le terme de prépresse pour capturer exactement cette classe de problèmes avant que les fichiers quittent le bâtiment, et la question d'ingénierie intéressante pour une équipe Delphi est l'endroit où placer ce contrôle quand les PDF sont produits par votre propre code avec une bibliothèque comme HotPDF plutôt que par les outils desktop d'un designer.

La prévention bat l'inspection quand vous possédez le générateur

Le preflight classique suppose un fichier étranger de qualité inconnue et l'inspecte après coup. Quand votre propre application génère le document, cette architecture est inversée : chaque propriété que l'inspecteur vérifierait, intégration des polices, usage des espaces couleur, output intents, métadonnées, a été décidée par votre code quelques millisecondes plus tôt. L'échec preflight le moins coûteux est celui rendu impossible à la génération.

Il vaut la peine d'être précis sur ce que HotPDF offre ou non ici. Le composant livre une fenêtre de rapport preflight dans son application de démonstration GUI, mais il n'existe pas d'API preflight programmatique appelable depuis un service ou un script de build. C'est moins un manque qu'il n'y paraît d'abord, car pour les documents générés le modèle robuste comporte de toute façon deux moitiés indépendantes : contraindre le générateur pour qu'il ne puisse pas émettre de structures non conformes, puis vérifier la sortie avec un validateur que vous ne maintenez pas vous-même. Une bibliothèque qui valide sa propre sortie corrige sa propre copie ; un outil externe fournit une preuve acceptable pour un client ou un auditeur.

Côté génération : faire de la conformité une configuration, pas un point de revue

Les propriétés de standards de HotPDF constituent la couche de prévention. Lorsque PDFACompliance ou PDFXCompliance est défini avant BeginDoc, le composant applique les règles correspondantes pendant la génération, intègre les polices, suit l'usage DeviceRGB et DeviceCMYK par rapport à l'output intent déclaré, et rejette les fonctions interdites par le profil. Après enregistrement, ces mêmes propriétés consignent ce qui a été appliqué, exactement ce dont votre log de pipeline a besoin :

// After EndDoc: record the enforced profiles with the run metadata
if Pdf.PDFACompliance <> '' then
  Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
  Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);

Écrivez ces flags, le hash des données d'entrée et la version HotPDF dans la même ligne de log. Quand un validateur contredira plus tard le générateur, cette ligne indiquera quelle révision de modèle et quelle version de bibliothèque ont produit le fichier contesté, et une discipline de log en une ligne remplacera un après-midi de conjectures forensiques. La configuration complète derrière ces flags, output intents, profils ICC et balisage est couverte dans notre guide de sortie PDF/A, PDF/X et PDF/UA avec HotPDF.

Trier les fichiers entrants avant les contrôles coûteux

Beaucoup de pipelines ne sont pas purement génératifs : des clients téléversent des PDF, des scanners les déposent, des partenaires les envoient par e-mail. Lancer une validation structurelle complète sur chaque fichier entrant gaspille du temps de file sur des entrées qui ne sont même pas ouvrables. La Direct File API de HotPDF lit la structure du fichier sans charger tout l'arbre d'objets, ce qui en fait une première porte économique :

function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
  Handle, Pages: Integer;
begin
  Result := False;
  Handle := Pdf.DAOpenFileReadOnly(FileName, '');
  if Handle <= 0 then
    Exit;  // structurally unreadable: quarantine, do not validate
  try
    Pages := Pdf.DAGetPageCount(Handle);
    Result := Pages > 0;
  finally
    Pdf.DACloseFile(Handle);
  end;
end;

Deux comportements de cette API façonnent la logique environnante. DAOpenFileReadOnly garde une mémoire plate seulement pour les entrées non chiffrées ; passez un mot de passe et elle retombe en interne sur un parse complet ; routez donc les fichiers connus comme chiffrés par DecryptFile pour produire d'abord une copie claire de travail. Et DAGetPageCount n'est valide que sur un handle issu d'une ouverture réussie ; gardez donc le test de handle strict au lieu de supposer une valeur positive. D'autres modèles pour cette API sont dans l'article Direct File API pour grands workflows PDF.

Côté vérification : veraPDF comme étape de build

Pour les revendications PDF/A et PDF/UA, veraPDF est le validateur à câbler dans le pipeline : il fonctionne sans interface, traite les lots, émet du XML ou JSON exploitable par machine et rapporte chaque échec par numéro de clause ISO, si bien qu'un constat comme une règle en échec contre ISO 19005-1 clause 6.2.2 se mappe directement vers un réglage de générateur connu. L'invoquer depuis Delphi relève du contrôle de processus ordinaire :

function RunVeraPdf(const PdfFile, ReportFile: string): Cardinal;
var
  Cmd: string;
  SI: TStartupInfo;
  PI: TProcessInformation;
begin
  Cmd := Format('cmd /c verapdf.bat --format xml "%s" > "%s"',
    [PdfFile, ReportFile]);
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  if not CreateProcess(nil, PChar(Cmd), nil, nil, False,
      CREATE_NO_WINDOW, nil, nil, SI, PI) then
    RaiseLastOSError;
  try
    WaitForSingleObject(PI.hProcess, 120000);  // bound the wait per file
    GetExitCodeProcess(PI.hProcess, Result);
  finally
    CloseHandle(PI.hThread);
    CloseHandle(PI.hProcess);
  end;
end;

Le timeout n'est pas décoratif. Un fichier malformé peut envoyer n'importe quel parseur dans un territoire pathologique, et une attente non bornée dans un worker de file met toute la file à terre. Bornez l'attente, traitez un timeout comme un échec avec son propre code, et mettez l'entrée en quarantaine pour inspection humaine. Parsez le XML pour les identifiants de règle plutôt que de racler des messages lisibles : les IDs de règle sont stables entre versions de validateur, les formulations ne le sont pas, et les codes stables sont ce que le support peut rechercher dans les anciens tickets.

Le comportement en lot mérite autant de soin que la justesse fichier par fichier. Lancez le validateur comme un processus par fichier plutôt qu'un processus par lot, afin qu'une entrée pathologique coûte le timeout de ce fichier au lieu de celui du lot ; plafonnez les processus validateurs concurrents au nombre de cœurs, car la génération de rapports XML est liée CPU ; imposez un plafond de taille à l'ingestion, car un monstre scanné de 2 Go dominera la file même si le parseur se comporte bien. Rien de cela n'est de la logique preflight, mais c'est la différence entre une porte qui survit au volume de fin de mois et une porte désactivée la première fois qu'elle bloque le pipeline à 2 heures du matin.

PDF/X est le trou de cette histoire : veraPDF ne le couvre pas, et le contrôle pratique reste Preflight d'Acrobat avec le profil ISO 15930 correspondant. Acrobat est interactif ; utilisez-le donc pour l'échantillonnage, premier article d'un nouveau modèle plus un petit tirage aléatoire par lot, pendant que la porte automatisée couvre ce qui peut l'être. Un contrôle manuel échantillonné qui a réellement lieu vaut mieux qu'une automatisation complète théorique que personne n'a terminée.

Ce que le rapport doit contenir pour mériter d'être conservé

Une porte preflight produit de la valeur deux fois : d'abord lorsqu'elle bloque un mauvais fichier, puis des mois plus tard lorsque quelqu'un demande pourquoi un fichier a été accepté. Le second usage dicte le format de rapport. Conservez, pour chaque fichier contrôlé : le hash d'entrée, les flags et la version de conformité du générateur depuis la ligne de log ci-dessus, le nom et la version du validateur, le profil contrôlé, le résultat succès ou échec, et la liste des IDs de règles échouées avec les numéros de page lorsque le validateur les fournit. Stockez le rapport à côté de l'artefact qu'il décrit, pas dans un système séparé qui sera retiré avant l'archive.

Les écarts acceptés ont eux aussi besoin de papier. Quand un client insiste pour expédier un fichier que la porte n'aime pas, consignez qui l'a approuvé, pourquoi, et jusqu'à quand, puis attachez cette dispense au rapport plutôt que d'affaiblir la règle globalement. Une dispense avec propriétaire et date d'expiration est une exception gérée ; un contrôle commenté est un incident futur.

Les échecs méritent une étape de plus : copiez le fichier fautif dans un dossier de régression nommé. Chaque incident preflight que nous avons aidé à déboguer a fini par se réduire à une entrée reproductible, et les équipes qui gardaient ces entrées corrigeaient les régressions en heures au lieu de les redécouvrir en production.

FAQ

HotPDF peut-il valider programmatiquement un PDF tiers arbitraire ?

Non. Le rapport preflight du produit est une fonction de démonstration GUI, pas une API appelable. Le modèle d'automatisation pris en charge consiste à imposer côté génération via les propriétés de conformité, puis à utiliser un validateur externe comme veraPDF pour le verdict formel.

veraPDF suffit-il pour les travaux d'impression ?

Il couvre PDF/A et PDF/UA. Pour les masters PDF/X, exécutez Acrobat Preflight avec le profil spécifié par votre imprimeur, et confirmez que l'output intent correspond à la caractérisation de presse attendue.

Qu'est-ce qui doit faire échouer le build : erreurs seulement, ou aussi avertissements ?

Bloquez sur les échecs de règles du profil dont vous revendiquez la conformité, et journalisez les avertissements avec suivi de tendance. Promouvoir chaque avertissement en bloqueur apprend aux gens à contourner la porte, ce qui est pire que de n'en avoir aucune.

Référence produit

Les propriétés de conformité et la Direct File API utilisées dans ce pipeline appartiennent au HotPDF Component pour Delphi et C++Builder ; sa documentation décrit en détail chaque appel montré ici.