PDFlibPas valide les fichiers PDF/UA-2 (ISO 14289-2) en Delphi via TPDFlib.CheckFilePDFUACompliance avec une partie cible de 2, ce qui déclenche un profil PDF/UA-2 distinct : les rôles de structure sont résolus à travers les espaces de noms PDF 2.0 et /RoleMapNS, une entrée tronquée ou illisible est signalée comme un échec, et le rapport de couverture renvoyé ne revendique jamais une certification complète
Ce dernier point compte plus qu'il n'y paraît. La validation d'accessibilité est un domaine où une coche verte finit copiée dans un tableur d'achat et n'est plus jamais questionnée. Un validateur qui saute en silence la moitié de la norme tout en imprimant "pass" est pire que pas de validateur du tout, parce qu'il transforme une inconnue en fausse certitude. PDFlibPas prend le parti inverse : vérifier ce qui peut l'être mécaniquement, dire à voix haute ce qui ne l'a pas été, et traiter chaque "impossible de terminer" comme un constat plutôt que comme un succès
Pourquoi ne peut-on pas valider le PDF/UA-2 avec les règles du PDF/UA-1 ?
Le PDF/UA-2 n'est pas un PDF/UA-1 dont on aurait incrémenté le numéro de partie. Le PDF/UA-1 repose sur PDF 1.7 et une table de rôles unique et plate ; le PDF/UA-2 repose sur PDF 2.0 (ISO 32000-2), où chaque élément de structure peut appartenir à un espace de noms et où chaque espace de noms apporte sa propre table de rôles. Un vérificateur UA-1 pointé vers un fichier UA-2 passera à côté de vrais problèmes et en inventera de faux
Les faux sont la partie embarrassante. Les validateurs PDF/UA-1 imposent typiquement des règles sur les éléments Note porteurs d'un ID et sur l'usage autorisé du titre non numéroté H. Aucune de ces règles ne se transpose. Dans l'espace de noms de structure PDF 2.0, les notes de bas de page s'expriment avec FENote, et les titres vont de H1 à Hn sans plafond à six niveaux. Le PDF/UA-2 va plus loin et interdit purement et simplement le rôle H non numéroté. Appliquez la logique des titres UA-1 à cet arbre et vous vous plaignez de ce que la norme autorise, ou vous laissez passer la seule chose qu'elle interdit
Le profil PDF/UA-2 de PDFlibPas est donc un chemin de code séparé, pas un drapeau posé sur l'ancien. Il refuse aussi de réutiliser les numéros d'article du UA-1. Les diagnostics partagés avec le vérificateur UA-1 sont repositionnés sur ISO 14289-2 dans les rapports UA-2, et quand un numéro d'article n'a pas été vérifié contre le texte de la norme, le rapport laisse le champ vide plutôt que de deviner. Un article vide satisfait moins qu'un numéro assuré, mais c'est la seule sortie défendable quand personne n'a confirmé la correspondance
Comment PDFlibPas résout les rôles via RoleMapNS
PDFlibPas résout chaque rôle de structure vers un rôle standard dans un espace de noms connu, en suivant les chaînes /RoleMapNS à travers les espaces de noms, et signale toute chaîne qui se termine sur un rôle personnalisé, qui boucle, ou qui enfreint les règles de correspondance. C'est le cœur de la validation de structure PDF/UA-2, car les technologies d'assistance ne comprennent que les rôles standards ; un /InvoiceLine personnalisé ne veut rien dire tant qu'il ne retombe pas sur quelque chose comme P ou TR
Le résolveur part du nom /S de l'élément et de son espace de noms /NS. Sans espace de noms, l'élément se trouve implicitement dans l'espace de noms standard PDF 1.7 et c'est le /RoleMap historique de la racine de l'arbre de structure qui s'applique. Avec un espace de noms, c'est le /RoleMapNS propre au dictionnaire de l'espace de noms qui s'applique, et une correspondance peut pointer vers un rôle d'un autre espace de noms via un tableau à deux éléments. Les contrôles qui en découlent sont concrets :
- Un espace de noms référencé depuis un élément de structure doit être un dictionnaire indirect avec un URI
/NSvalide et doit figurer dans le tableau/Namespacesde la racine de l'arbre de structure (diagnostic 10054) - Un rôle personnalisé doit finir par se résoudre vers un rôle standard dans l'espace de noms PDF 1.7, l'espace de noms PDF 2.0 (
http://iso.org/pdf2/ssn) ou l'espace de noms MathML ; les chaînes circulaires et les impasses sont signalées comme 10055 - Un rôle standard ne peut pas être remappé vers un rôle personnalisé, et un rôle ne peut pas être remappé au sein de son propre espace de noms explicite
- La racine de structure doit contenir exactement un élément
Documentdans l'espace de noms PDF 2.0 (10057), et le/Pde chaque élément doit pointer vers son parent réel (10058) - Un
Hnon numéroté, ou uneNotehistorique PDF 1.7 non mappée vers un rôle PDF 2.0, est signalée comme 10056
Un détail à copier dans vos propres outils : PDFlibPas valide chaque correspondance déclarée, pas seulement celles accessibles depuis l'arbre. Une entrée cassée dans une table de rôles qu'aucun élément n'utilise actuellement reste un défaut latent, car la prochaine mise à jour de contenu commencera à l'utiliser. MathML est traité honnêtement lui aussi. L'espace de noms MathML est reconnu comme cible de résolution valide, mais le vocabulaire MathML lui-même n'est pas validé, et le rapport de couverture le dit
Lancer une vérification PDF/UA-2 depuis Delphi
TPDFlib.CheckFilePDFUACompliance est le point d'entrée en un seul appel : passez le fichier, le mot de passe, la partie cible et les options, et vous récupérez un handle de liste de chaînes contenant les problèmes codés. Un handle à zéro avec LastErrorCode égal à zéro signifie que les contrôles implémentés n'ont rien trouvé ; un LastErrorCode non nul signifie que l'appel lui-même a échoué
var
Lib: TPDFlib;
Issues, I: Integer;
begin
Lib := TPDFlib.Create;
try
// TargetPart : 0 = détection depuis XMP pdfuaid:part, 1 = PDF/UA-1, 2 = PDF/UA-2
// Options : 1 = arrêt au premier échec, 0 = collecte de tous les problèmes
Issues := Lib.CheckFilePDFUACompliance('report.pdf', '', 2, 0);
if Issues = 0 then
begin
if Lib.LastErrorCode <> 0 then
Writeln('Validation did not run, error ', Lib.LastErrorCode)
else
Writeln('No issues from the implemented PDF/UA-2 checks');
end
else
try
for I := 1 to Lib.GetStringListCount(Issues) do // les éléments sont indexés à partir de 1
Writeln(Lib.GetStringListItem(Issues, I));
finally
Lib.ReleaseStringList(Issues);
end;
finally
Lib.Free;
end;
end;
Choisissez la partie cible en connaissance de cause. Une cible de 0 lit pdfuaid:part dans les métadonnées XMP et en déduit le profil, ce qui convient à un validateur de boîte de réception générique. Pour votre propre pipeline de production, passez 2 explicitement. Si votre générateur a oublié d'écrire l'identification, l'auto-détection retombe sur les règles UA-1 et vous validez la mauvaise norme ; un 2 explicite produit à la place le diagnostic 10050 pour la partie manquante, plus 10051 si pdfuaid:rev n'est pas 2024 et 10052 si la version PDF effective n'est pas 2.0. Côté génération, TPDFlib.SetPDFUA2Mode écrit cette identification, monte la version à 2.0 et sélectionne l'espace de noms de structure PDF 2.0 par défaut ; le modèle d'espaces de noms lui-même est expliqué dans le pas à pas sur les espaces de noms PDF 2.0, PDF/A-4 et PDF/UA-2
Que se passe-t-il quand le validateur épuise son budget ?
PDFlibPas fonctionne en fail-closed : quand le vérificateur PDF/UA-2 bute sur une limite de parcours, un cycle, une limite du parseur ou un document qu'il ne peut pas ouvrir, il émet un diagnostic, et un résultat sans diagnostic n'est jamais promu en succès. Le diagnostic 10059 est réservé précisément à ce cas de "lecture qui n'a pas pu aller au bout"
Les budgets sont explicites et publiés dans le JSON de couverture : profondeur de structure 256, 100 000 objets de structure, et 256 sauts le long d'une chaîne de table de rôles. Ces nombres existent parce que les arbres de structure proviennent d'entrées non fiables. Un fichier hostile ou simplement cassé peut imbriquer des éléments sur des milliers de niveaux, faire pointer un enfant vers un ancêtre, ou construire une table de rôles qui s'enchaîne à travers des centaines d'espaces de noms. Un validateur sans budget plante ou se bloque, et un validateur avec budget mais sans diagnostic fait quelque chose de plus subtil et de pire : il s'arrête tôt, ne trouve rien d'anormal dans la partie qu'il a lue, et rapporte un succès
Le même principe s'applique un niveau au-dessus. Si le lecteur sous-jacent renvoie un score inférieur à 100 sans aucune ligne de problème, ce qui peut arriver quand le parsing s'interrompt avant que la moindre règle ne tourne, PDFlibPas insère une ligne 10059 pour que la liste vide ne puisse pas passer pour un résultat propre. Une exception pendant la vérification devient elle aussi une ligne 10059. C'est peu de code, et cela ferme la manière la plus courante dont les pipelines de validation mentent à leurs propriétaires
Partager un parsing avec le PDF/A et construire un rapport
Pour le travail par lots, l'API de pipeline de validation parse un fichier une seule fois et fait tourner plusieurs vérifications contre le parsing partagé. RunPDFUAValidationPipelineTask est le membre PDF/UA de cette famille et prend les mêmes valeurs de partie cible que la fonction à appel unique, ce qui évite à un preflight PDF/A plus PDF/UA-2 de payer deux lectures complètes d'un gros document
var
Pipe, PDFAIssues, UAIssues: Integer;
Json: WideString;
begin
Pipe := Lib.BeginValidationPipeline('archive.pdf', '');
if Pipe = 0 then
Exit;
try
PDFAIssues := Lib.RunValidationPipelineTask(Pipe, 1, 0); // 1 = PDF/A
UAIssues := Lib.RunPDFUAValidationPipelineTask(Pipe, 2, 0); // PDF/UA-2
// ... inspectez et libérez les deux handles comme dans l'exemple précédent ...
Writeln('Parses: ', Lib.GetValidationPipelineParseCount(Pipe));
// ComplianceTests : 1 = PDF/A, 2 = PDF/UA, 0 = les deux ; ReportFormat 1 = JSON
Json := Lib.CreateValidationPipelineReport(Pipe, 0, 0, 1);
finally
Lib.ReleaseValidationPipeline(Pipe);
end;
end;
Le rapport structuré étiquette la section accessibilité PDF/UA-2 et ISO 14289-2 quand le fichier s'identifie comme partie 2, et chaque section PDF/UA porte "fullCertification": false avec l'objet de couverture embarqué. Le rapport preflight texte ajoute une ligne précisant que seuls les contrôles implémentés ont tourné et qu'une certification d'accessibilité complète demande une validation supplémentaire et une revue humaine. Les formats de rapport plus larges, les ID de règles et les schémas de gating CI sont décrits dans le guide de preflight PDF/A et PDF/UA
Que garantit réellement le rapport de couverture ?
TPDFlib.GetPDFUAReadValidationCoverageJSON garantit une chose : la liste exacte de ce que le vérificateur PDF/UA-2 a examiné et de ce qu'il n'a pas examiné. Pour la partie 2, il renvoie fullCertification figé à false, un tableau checked, un tableau unverified, et les limites de parcours. Il ne garantit pas qu'un fichier est accessible, et il est conçu pour qu'aucun chemin de code ne puisse lui faire dire le contraire
var
Coverage: WideString;
begin
Coverage := Lib.GetPDFUAReadValidationCoverageJSON(2);
// Rangez-le à côté du résultat de validation, pas à sa place
TFile.WriteAllText('report.coverage.json', Coverage, TEncoding.UTF8);
if Pos('"fullCertification":false', Coverage) > 0 then
Writeln('Machine checks only; schedule a human accessibility review');
end;
La liste checked couvre l'identification (partie, révision, préfixe), la version PDF effective, les métadonnées de titre, /MarkInfo, DisplayDocTitle, les références d'espaces de noms, la résolution des tables de rôles, la racine du document, les parents de structure, les cycles d'arbre, et les règles H et Note. La liste unverified est tout aussi précise : marquage du contenu et ordre de lecture, confinement de structure, polices et mappage Unicode, annotations et formulaires, langue naturelle, descriptions alternatives, vocabulaire MathML, et revue d'accessibilité humaine. Plusieurs de ces points ne sont même pas décidables mécaniquement. Aucun parseur ne peut vous dire si un texte alternatif décrit réellement le graphique, ou si l'ordre de lecture a du sens pour quelqu'un qui l'écoute
Traitez le JSON de couverture comme une partie du résultat. Archivez-le à chaque exécution de validation, et quand un auditeur demande ce que voulait dire "passé" à une date donnée, vous pouvez remettre le périmètre exact au lieu de le reconstituer de mémoire avec un numéro de version
Contre-vérification avec veraPDF
Le cœur PDF/UA-2 a été contre-vérifié avec le validateur en ligne de commande veraPDF 1.30.3 et son profil ua2, et les résultats concordent là où les couvertures se recouvrent. Un échantillon de cœur valide généré indépendamment est passé dans les deux outils ; un échantillon déclarant pdfuaid:rev 2023 et un échantillon utilisant le rôle H non numéroté ont échoué dans les deux
Un quatrième échantillon mérite qu'on en parle. Il contenait du contenu de dessin non balisé du tout, et veraPDF l'a signalé quand les contrôles de cœur PDFlibPas non, parce que le balisage du contenu est hors de la couverture actuelle. Ce trou est exactement la raison pour laquelle content-tagging-and-reading-order tête la liste unverified. Faire tourner un second validateur est une assurance bon marché, et un désaccord entre deux outils est souvent le moyen le plus rapide de trouver la frontière de ce que chacun vérifie. Pour le côté génération du même problème, construire des arbres qui passent dès le départ, voir le guide de l'arbre de structure PDF balisé
FAQ
PDFlibPas certifie-t-il un fichier comme conforme PDF/UA-2 ?
Non. PDFlibPas exécute les contrôles machine implémentés pour le PDF/UA-2 et renvoie un rapport de couverture avec fullCertification toujours à false, listant chaque domaine qui demande encore d'autres outils ou une revue humaine
Quelle valeur TargetPart passer à CheckFilePDFUACompliance ?
Passez 2 quand vous savez que le fichier doit être PDF/UA-2, pour que l'identification manquante soit signalée. Passez 0 pour auto-détecter la partie depuis XMP pdfuaid:part, et 1 pour forcer les règles PDF/UA-1
Que signifie le diagnostic 10059 dans un rapport PDF/UA-2 ?
Le diagnostic 10059 signifie que le validateur n'a pas pu terminer : le fichier n'a pas pu être parsé ni ouvert, une limite de parcours a été dépassée, un cycle a été trouvé, ou une exception s'est produite. Il compte toujours comme un échec, jamais comme un succès
La validation PDF/UA-2, le pipeline de validation partagé et les API de génération PDF 2.0 sont livrés dans la PDF Library losLab pour Delphi, aux côtés du preflight PDF/A et des rapports de validation structurés