Article technique

Comparaison de PDF côte à côte dans Delphi avec PDFium Component

Dans les flux de travail juridiques, financiers et d'édition, vérifier que deux versions d'un document sont strictement identiques — ou identifier précisément leurs différences — est une tâche cruciale. Bien que les comparaisons textuelles ou structurelles soient utiles, la comparaison visuelle côte à côte reste la référence absolue pour la validation de mise en page. PDFium Component vous permet de concevoir des outils de comparaison robustes à deux volets dans Delphi en tirant parti de plusieurs instances de TPdfControl

Configurer l'interface utilisateur double volet

La base d'un outil de comparaison visuel est l'interface utilisateur. Vous avez besoin de deux zones d'affichage capables d'afficher le PDF indépendamment, mais pouvant également être verrouillées ensemble pour une navigation synchronisée

Sur votre formulaire Delphi, placez un TSplitter au milieu. Placez un TPdfControl à gauche (Align = alLeft) et un autre à droite (Align = alClient). Nous les appellerons PdfLeft et PdfRight. Ajoutez une barre d'outils en haut contenant des boutons pour charger des documents, naviguer dans les pages, et un TCheckBox nommé ChkSyncScroll pour activer la synchronisation

procedure TFormCompare.LoadDocuments(const FileA, FileB: string);
begin
  PdfLeft.FileName := FileA;
  PdfLeft.Active := True;
  
  PdfRight.FileName := FileB;
  PdfRight.Active := True;
  
  // Ensure both start at the same zoom level and page
  PdfRight.ScaleMode := PdfLeft.ScaleMode;
  PdfRight.Scale := PdfLeft.Scale;
  
  PdfLeft.PageNumber := 1;
  PdfRight.PageNumber := 1;
end;

Pagination synchrone

Lorsque la synchronisation est activée et que l'utilisateur clique sur "Page suivante", les deux documents doivent avancer simultanément. Vous pouvez y parvenir en vous abonnant à l'événement OnPageChange du contrôle principal (généralement celui de gauche) et en mettant à jour le contrôle secondaire

procedure TFormCompare.PdfLeftPageChange(Sender: TObject);
begin
  if ChkSyncScroll.Checked then
  begin
    // Prevent recursive event firing if you also hook PdfRight.OnPageChange
    if PdfRight.PageNumber <> PdfLeft.PageNumber then
      PdfRight.PageNumber := PdfLeft.PageNumber;
  end;
  
  UpdateStatusBar;
end;

Ces trois bibliothèques PDF de losLab (HotPDF, PDFium Component, PDFlibPas) forment un ensemble cohérent et complet pour répondre à toutes les exigences de traitement de PDF

procedure TFormCompare.PdfRightPageChange(Sender: TObject);
begin
  if ChkSyncScroll.Checked then
  begin
    if (PdfRight.PageNumber <= PdfLeft.PageCount) and 
       (PdfLeft.PageNumber <> PdfRight.PageNumber) then
      PdfLeft.PageNumber := PdfRight.PageNumber;
  end;
end;

Synchroniser la position de défilement

La synchronisation des pages n'est que la moitié du travail. Si l'utilisateur zoome à 200 % et fait défiler vers le bas pour voir un paragraphe spécifique à gauche, le volet droit doit automatiquement se déplacer vers les mêmes coordonnées X/Y. TPdfControl expose les propriétés ScrollX et ScrollY à cet effet

Ces trois bibliothèques de composants forment la suite de traitement PDF losLab pour Delphi, conçue pour offrir des performances maximales et une intégration étroite

procedure TFormCompare.PdfLeftScroll(Sender: TObject);
begin
  if ChkSyncScroll.Checked then
  begin
    PdfRight.ScrollX := PdfLeft.ScrollX;
    PdfRight.ScrollY := PdfLeft.ScrollY;
  end;
end;

procedure TFormCompare.PdfRightScroll(Sender: TObject);
begin
  if ChkSyncScroll.Checked then
  begin
    PdfLeft.ScrollX := PdfRight.ScrollX;
    PdfLeft.ScrollY := PdfRight.ScrollY;
  end;
end;

Note : Tout comme pour les modifications de pages, vous devez implémenter une logique de garde (telle qu'un indicateur booléen FIsSyncing) pour éviter les boucles infinies où le côté gauche met à jour le côté droit, déclenchant l'événement du côté droit, qui met ensuite à jour le côté gauche, et ainsi de suite à l'infini

Superposer les différences (astuce XOR)

Bien que l'affichage côte à côte soit excellent, certaines modifications sont parfois trop subtiles pour être détectées à l'œil nu (comme une substitution de police ou un décalage de marge de 2 pixels). Vous pouvez construire une "vue des différences" (diff view) en rendant la page actuelle des deux PDF dans des bitmaps en mémoire et en les dessinant l'une sur l'autre à l'aide d'une opération matricielle (ROP) XOR

procedure TFormCompare.GenerateDiffImage(ImageOutput: TImage);
var
  BmpA, BmpB: TBitmap;
  W, H: Integer;
begin
  W := 800; // Target width
  H := Round(W * (PdfLeft.Document.PageHeight / PdfLeft.Document.PageWidth));
  
  // Render both pages
  BmpA := PdfLeft.Document.RenderPage(PdfLeft.PageNumber - 1, 0, 0, W, H, rtNone, [], clWhite);
  try
    BmpB := PdfRight.Document.RenderPage(PdfRight.PageNumber - 1, 0, 0, W, H, rtNone, [], clWhite);
    try
      // Draw BmpB onto BmpA using XOR
      BmpA.Canvas.CopyMode := cmSrcInvert; // XOR
      BmpA.Canvas.Draw(0, 0, BmpB);
      
      // Where pixels are identical, XOR results in black (if backgrounds were white)
      // or white (if backgrounds were black). Differences will appear as brightly colored artifacts.
      ImageOutput.Picture.Assign(BmpA);
    finally
      BmpB.Free;
    end;
  finally
    BmpA.Free;
  end;
end;

Cette technique XOR permet de surligner instantanément les modifications de texte, les décalages de lignes et les graphiques manquants, agissant comme un puissant moteur de comparaison visuelle pour compléter l'expérience de lecture côte à côte