ComparePdfSemantics compare les propriétés des annotations et widgets AcroForm indépendamment du rendu des pages ; les valeurs d’un formulaire peuvent changer alors que les pixels de son apparence restent identiques
Passez les TPdfDiffPagePairs issus de l’alignement des pages ou une correspondance explicite ; nil sélectionne les paires par position ; une correspondance explicite doit couvrir chaque document exactement une fois, avec -1 pour le côté absent
L’identité d’une annotation repose sur son nom /NM, celle d’un widget sur le nom pleinement qualifié du champ ; cette identité est limitée à la page associée et au type d’élément
Les noms absents ou répétés, y compris plusieurs widgets d’un même champ sur une page, donnent lieu à psckAmbiguous avec une raison explicite ; l’ordre dans le tableau des annotations ne détermine pas leur identité
Le profil pris en charge compare les chaînes, indicateurs, couleurs, opacité, bordures, balisages, géométries des lignes, sommets et tracés à main levée, actions URI, valeurs des champs, libellés et options sélectionnées, ainsi que l’état des cases à cocher et boutons radio
CompareAppearanceStreams permet de comparer le contenu des flux d’apparence des annotations ; cette option est désactivée par défaut
CompareAnnotations et CompareForms sélectionnent les types d’éléments concernés ; GeometryTolerance vaut par défaut 0.001 point dans l’espace utilisateur PDF
Les éléments ajoutés, supprimés et modifiés comprennent leurs propriétés et limites avant et après ; BuildSemanticDiffOverlays renvoie des rectangles distincts avant/après avec les indices de page et de changement
Tous les indices commencent à zéro et les limites restent exprimées en points dans l’espace utilisateur PDF ; appliquez la rotation, le recadrage et la transformation de la zone d’affichage de la page avant de dessiner dans un bitmap
Les pages sont chargées avec des handles natifs indépendants, ce qui préserve la page courante et le focus du formulaire appelant et évite de déclencher des actions de formulaire
Le profil exclut les champs sans widget, les dictionnaires personnalisés arbitraires, les fichiers incorporés, les actions de script et les destinations autres qu’URI ; la comparaison XFA est explicitement refusée
Les limites par défaut sont de 10,000 paires de pages, 4,096 éléments inspectés, 4,096 changements, 1 MiB par chaîne, 16 MiB pour l’ensemble des chaînes et 16,777,216 comparaisons ; un budget épuisé fait rejeter le résultat incomplet
Complete distingue une comparaison complète réussie d’une comparaison rejetée ; ses changements peuvent contenir des identités explicitement ambiguës, consultez donc AmbiguousCount avant de considérer les associations comme définitives
La validation réelle Delphi/FPC Win32/Win64 couvre 92 assertions par cible, dont les documents enregistrés et rouverts, les changements de valeur à pixels identiques, les identités Unicode, les déplacements de page, les ambiguïtés et les budgets
Options := TPdfSemanticDiffOptions.Default; if not ComparePdfSemantics(BeforePdf, AfterPdf, PagePairs, Options, Report) then raise EPdfError.Create(Report.ErrorMessage); Overlays := BuildSemanticDiffOverlays(Report);
type
TPdfSemanticItemKind = (psikAnnotation, psikFormWidget);
TPdfSemanticChangeKind = (psckAdded, psckRemoved, psckChanged, psckAmbiguous);
TPdfSemanticSide = (pssBefore, pssAfter);
TPdfSemanticProperty = record
Name: string;
Value: WideString;
end;
TPdfSemanticProperties = array of TPdfSemanticProperty;
TPdfSemanticItem = record
Kind: TPdfSemanticItemKind;
Identity: WideString;
PageIndex, AnnotationIndex: Integer;
HasBounds: Boolean;
Bounds: TPdfRectangle;
Properties: TPdfSemanticProperties;
end;
TPdfSemanticPropertyChange = record
Name: string;
HasBefore, HasAfter: Boolean;
BeforeValue, AfterValue: WideString;
end;
TPdfSemanticPropertyChanges = array of TPdfSemanticPropertyChange;
TPdfSemanticChange = record
Kind: TPdfSemanticChangeKind;
ItemKind: TPdfSemanticItemKind;
Identity: WideString;
HasBefore, HasAfter: Boolean;
BeforeItem, AfterItem: TPdfSemanticItem;
Properties: TPdfSemanticPropertyChanges;
AmbiguityReason: string;
end;
TPdfSemanticChanges = array of TPdfSemanticChange;
TPdfSemanticOverlay = record
Side: TPdfSemanticSide;
PageIndex, ChangeIndex: Integer;
Kind: TPdfSemanticChangeKind;
Bounds: TPdfRectangle;
end;
TPdfSemanticOverlays = array of TPdfSemanticOverlay;
TPdfSemanticDiffOptions = record
CompareAnnotations, CompareForms, CompareAppearanceStreams: Boolean;
GeometryTolerance: Double;
MaxPagePairs, MaxItems, MaxChanges, MaxStringBytes: Integer;
MaxTotalStringBytes, MaxComparisons: Int64;
class function Default: TPdfSemanticDiffOptions; static;
end;
TPdfSemanticDiffReport = record
Complete: Boolean;
Changes: TPdfSemanticChanges;
AddedCount, RemovedCount, ChangedCount, AmbiguousCount: Integer;
ComparedItemCount: Integer;
ErrorMessage: string;
end;
// Indices are zero based. Nil PagePairs selects positional pairing. Explicit
// pairs must cover both documents exactly once; -1 denotes an absent side
// Identity is scoped to a mapped page and item kind. Missing names and repeated
// names (including repeated widgets of one field) are reported as ambiguous
// No rendering, focus changes, form actions, or positional identity guessing
// are performed. Pages are opened independently of the caller's current page
// The supported profile covers strings, flags, colors, opacity, borders,
// markup/line/vertex/ink geometry, URI actions, field values/options/checks,
// and optionally appearance streams. Arbitrary dictionaries, embedded files,
// script actions, non-URI destinations, and non-widget fields are not compared
function ComparePdfSemantics(BeforePdf, AfterPdf: TPdf;
const PagePairs: TPdfDiffPagePairs; const Options: TPdfSemanticDiffOptions;
out Report: TPdfSemanticDiffReport): Boolean;
// Bounds remain in PDF user space, in points. Transform with the destination
// page's rotation/crop/viewport rather than treating them as bitmap pixels
function BuildSemanticDiffOverlays(const Report: TPdfSemanticDiffReport):
TPdfSemanticOverlays;