ComparePdfSemantics はページ描画とは独立して注釈と AcroForm ウィジェットの属性を比較します。外観のピクセルが同じでも、フォーム値は変わることがあります
ページ対応付けの TPdfDiffPagePairs または明示的な対応表を渡します。nil は位置順の組み合わせを選び、明示的な対応表は両文書をそれぞれ一度だけ網羅する必要があります。片側がない場合は -1 を使います
注釈の識別には /NM 名、フォームウィジェットには完全修飾フィールド名を使います。識別の範囲は対応ページと項目の種類です
名前の欠落や重複は、同じページにある同一フィールドの複数ウィジェットも含め、理由付きの psckAmbiguous として報告します。注釈配列の順序で識別することはありません
対応プロファイルは文字列、flags、色、不透明度、境界線、markup、line、vertex、ink の形状、URI アクション、フィールド値、選択肢ラベルと選択済み項目、checkbox/radio 状態を比較します
CompareAppearanceStreams で注釈の外観ストリーム内容も比較できます。既定では無効です
CompareAnnotations と CompareForms で各種類を選びます。GeometryTolerance の既定値は PDF ユーザー空間の 0.001 ポイントです
追加、削除、変更の項目には前後の属性と境界を含みます。BuildSemanticDiffOverlays はページ番号と変更番号を持つ、前後別々の矩形を返します
すべての番号は 0 始まりで、境界は PDF ユーザー空間のポイント単位です。ビットマップに描く前に、該当ページの回転、crop、viewport 変換を適用してください
ページは独立したネイティブハンドルで読み込み、呼び出し元の現在ページとフォームフォーカスを維持し、フォームアクションを避けます
ウィジェットでないフィールド、任意の独自辞書、埋め込みファイル、script アクション、URI 以外の宛先は対象外です。XFA 比較は明示的に拒否します
既定の上限は 10,000 ページ組、走査項目 4,096 件、変更 4,096 件、文字列ごとに 1 MiB、文字列合計 16 MiB、比較 16,777,216 回です。上限に達すると不完全な出力を拒否します
Complete は成功した完全比較と拒否した比較を区別します。完全比較でも明示的に曖昧な識別が含まれ得るので、確定した項目対応として扱う前に AmbiguousCount を確認してください
実際の Delphi/FPC Win32/Win64 受け入れ検証は対象ごとに 92 assertions を含み、保存/再読み込み、同一ピクセルでの値変更、Unicode の識別、ページ移動、曖昧性、上限を確認しています
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;