技術文章

在Delphi中使用PDFium VCL對PDF文件進行並排比較

· PDF 程式設計

並排比較多個PDF文件對於修訂審查、質量控制和文件驗證至關重要。 分屏檢視 該演示程式展示瞭如何使用PDFium VCL同時顯示兩個或三個PDF文件。

概述

該高階演示程式以同步檢視顯示多個PDF文件,允許使用者直觀地比較文件。它支援雙向和三向比較模式,併為每個檢視提供獨立的導航和縮放控制元件。

主要特性

  • 雙面板模式 – 並排比較兩個文件。
  • 三面板模式 – 同時比較三個文件
  • 獨立導航 – 獨立瀏覽每個文件
  • 同步縮放 – 對所有檢視或單獨的檢視應用縮放
  • 文本選擇 – 從任何面板選擇和複製文本
  • 儲存為影像 – 將當前檢視匯出為影像
  • 啟用面板高亮 – 啟用面板的視覺指示

PDFium DLL 的要求

在執行任何 PDFium VCL 應用程式之前,請確保已安裝 PDFium DLL 檔案:

  • pdfium32.dll / pdfium64.dll – 標準版本(約 5-6 MB)
  • pdfium32v8.dll / pdfium64v8.dll – 包含 V8 JavaScript 引擎的版本(約 23-27 MB)

安裝: 執行 PDFiumVCL\DLLs\CopyDlls.bat 以管理員身份,自動將 DLL 檔案複製到 Windows 系統目錄。

設定多個檢視

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type
  TFormMain = class(TForm)
    // Three PDF components
    Pdf1: TPdf;
    Pdf2: TPdf;
    Pdf3: TPdf;
    
    // Three view components
    PdfView1: TPdfView;
    PdfView2: TPdfView;
    PdfView3: TPdfView;
    
    // Container panels
    ScrollBox1: TScrollBox;
    ScrollBox2: TScrollBox;
    ScrollBox3: TScrollBox;
    
    // Splitters for resizing
    Splitter1: TSplitter;
    Splitter2: TSplitter;
    
  private
    FActivePdfView: TPdfView;  // Currently active view
    FAllViewsMode: Boolean;    // Apply actions to all views
    ThreeViewMode: Boolean;    // Three-panel mode enabled
  end;

在不同的檢視模式之間切換

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
procedure TFormMain.SpeedButtonThreeViewClick(Sender: TObject);
begin
  ThreeViewMode := not ThreeViewMode;
  SpeedButtonThreeView.Down := ThreeViewMode;
  UpdateLayout;
end;
 
procedure TFormMain.UpdateLayout;
var
  TotalWidth: Integer;
begin
  if ThreeViewMode then
  begin
    // Three view mode: equal thirds
    ScrollBox1.Align := alNone;
    ScrollBox2.Align := alNone;
    ScrollBox3.Align := alNone;
    
    Splitter1.Visible := False;
    Splitter2.Visible := False;
    
    TotalWidth := ClientWidth;
    
    ScrollBox1.Left := 0;
    ScrollBox1.Width := TotalWidth div 3;
    ScrollBox1.Height := ClientHeight - PanelButtons.Height;
    
    ScrollBox2.Left := ScrollBox1.Width;
    ScrollBox2.Width := TotalWidth div 3;
    ScrollBox2.Height := ClientHeight - PanelButtons.Height;
    
    ScrollBox3.Left := ScrollBox2.Left + ScrollBox2.Width;
    ScrollBox3.Width := TotalWidth - ScrollBox3.Left;
    ScrollBox3.Height := ClientHeight - PanelButtons.Height;
    ScrollBox3.Visible := True;
    
    SpeedButtonOpenPdf3.Visible := True;
  end
  else
  begin
    // Two view mode: equal halves
    ScrollBox3.Visible := False;
    Splitter2.Visible := False;
    
    TotalWidth := ClientWidth;
    
    ScrollBox1.Left := 0;
    ScrollBox1.Width := TotalWidth div 2;
    ScrollBox1.Height := ClientHeight - PanelButtons.Height;
    
    ScrollBox2.Left := ScrollBox1.Width;
    ScrollBox2.Width := TotalWidth - ScrollBox2.Left;
    ScrollBox2.Height := ClientHeight - PanelButtons.Height;
    
    SpeedButtonOpenPdf3.Visible := False;
  end;
end;

在每個面板中開啟 PDF 檔案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
  PdfViewComponent: TPdfView; const Title: string);
var
  Password: string;
begin
  if OpenDialog.Execute then
  begin
    PdfComponent.Active := False;
    PdfComponent.FileName := OpenDialog.FileName;
    PdfComponent.Password := '';
    
    try
      PdfViewComponent.Active := True;
    except
      on Error: EPdfError do
        if Error.Message = 'Password required or incorrect password' then
        begin
          if InputQuery('Enter Password', 'Password: ', Password) then
          begin
            PdfComponent.Password := Password;
            PdfViewComponent.Active := True;
          end
          else
            raise;
        end
        else
          raise;
    end;
    
    SetActivePdfView(PdfViewComponent);
    UpdateControlsState;
  end;
end;
 
procedure TFormMain.SpeedButtonOpenPdf1Click(Sender: TObject);
begin
  OpenPdfFile(Pdf1, PdfView1, 'PDF 1');
end;
 
procedure TFormMain.SpeedButtonOpenPdf2Click(Sender: TObject);
begin
  OpenPdfFile(Pdf2, PdfView2, 'PDF 2');
end;
 
procedure TFormMain.SpeedButtonOpenPdf3Click(Sender: TObject);
begin
  OpenPdfFile(Pdf3, PdfView3, 'PDF 3');
end;

管理當前活動面板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
procedure TFormMain.SetActivePdfView(PdfView: TPdfView);
begin
  FActivePdfView := PdfView;
  
  // Visual feedback for active panel
  if PdfView = PdfView1 then
  begin
    ScrollBox1.Color := clHighlight;
    ScrollBox2.Color := clWindow;
    ScrollBox3.Color := clWindow;
  end
  else if PdfView = PdfView2 then
  begin
    ScrollBox1.Color := clWindow;
    ScrollBox2.Color := clHighlight;
    ScrollBox3.Color := clWindow;
  end
  else if PdfView = PdfView3 then
  begin
    ScrollBox1.Color := clWindow;
    ScrollBox2.Color := clWindow;
    ScrollBox3.Color := clHighlight;
  end;
  
  UpdateControlsState;
end;
 
procedure TFormMain.PdfView1Click(Sender: TObject);
begin
  SetActivePdfView(PdfView1);
end;
 
procedure TFormMain.PdfView2Click(Sender: TObject);
begin
  SetActivePdfView(PdfView2);
end;
 
procedure TFormMain.PdfView3Click(Sender: TObject);
begin
  SetActivePdfView(PdfView3);
end;

將操作應用於當前活動檢視或所有檢視

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
procedure TFormMain.ApplyToAllViews(const Operation: string);
begin
  if FAllViewsMode then
  begin
    // Apply to all active views
    if PdfView1.Active then
      ApplyOperation(PdfView1, Operation);
    if PdfView2.Active then
      ApplyOperation(PdfView2, Operation);
    if PdfView3.Active and ThreeViewMode then
      ApplyOperation(PdfView3, Operation);
  end
  else
  begin
    // Apply only to active view
    if Assigned(FActivePdfView) then
      ApplyOperation(FActivePdfView, Operation);
  end;
end;
 
procedure TFormMain.SpeedButtonZoomInClick(Sender: TObject);
begin
  ApplyToAllViews('ZoomIn');
end;
 
procedure TFormMain.SpeedButtonZoomOutClick(Sender: TObject);
begin
  ApplyToAllViews('ZoomOut');
end;

同步頁面導航(可選)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
procedure TFormMain.SynchronizePages(SourceView: TPdfView);
var
  PageNum: Integer;
begin
  if not chkSynchronize.Checked then
    Exit;
    
  PageNum := SourceView.PageNumber;
  
  // Apply to other views
  if (SourceView <> PdfView1) and PdfView1.Active then
    if PageNum <= PdfView1.PageCount then
      PdfView1.PageNumber := PageNum;
      
  if (SourceView <> PdfView2) and PdfView2.Active then
    if PageNum <= PdfView2.PageCount then
      PdfView2.PageNumber := PageNum;
      
  if (SourceView <> PdfView3) and PdfView3.Active and ThreeViewMode then
    if PageNum <= PdfView3.PageCount then
      PdfView3.PageNumber := PageNum;
end;

獨立的縮放控制元件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
procedure TFormMain.DoZoom1;
begin
  if PdfView1.Active and (PdfView1.PageNumber > 0) then
  begin
    case ComboBoxZoom.ItemIndex of
      0: PdfView1.Zoom := 0.5;
      1: PdfView1.Zoom := 0.75;
      2: PdfView1.Zoom := 1.0;
      3: PdfView1.Zoom := 1.5;
      4: PdfView1.Zoom := 2.0;
      5: PdfView1.Zoom := PdfView1.PageWidthZoom[PdfView1.PageNumber];
      6: PdfView1.Zoom := PdfView1.PageZoom[PdfView1.PageNumber];
    end;
  end;
end;
 
procedure TFormMain.DoZoom2;
begin
  if PdfView2.Active and (PdfView2.PageNumber > 0) then
  begin
    // Same zoom logic for view 2
  end;
end;
 
procedure TFormMain.DoZoom3;
begin
  if PdfView3.Active and (PdfView3.PageNumber > 0) then
  begin
    // Same zoom logic for view 3
  end;
end;

將當前檢視儲存為影像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
procedure TFormMain.MenuItemSaveAsImage1Click(Sender: TObject);
begin
  SaveViewAsImage(PdfView1, Pdf1);
end;
 
procedure TFormMain.SaveViewAsImage(PdfView: TPdfView; Pdf: TPdf);
var
  Bitmap: TBitmap;
  JpegImage: TJpegImage;
begin
  if not PdfView.Active then
  begin
    ShowMessage('No document loaded in this view.');
    Exit;
  end;
  
  SavePictureDialog.FileName := 'page_' + IntToStr(PdfView.PageNumber) + '.jpg';
  
  if SavePictureDialog.Execute then
  begin
    Pdf.PageNumber := PdfView.PageNumber;
    
    Bitmap := Pdf.RenderPage(
      0, 0,
      Round(Pdf.PageWidth * 2),  // 2x resolution
      Round(Pdf.PageHeight * 2),
      PdfView.Rotation, [], clWhite
    );
    
    try
      JpegImage := TJpegImage.Create;
      try
        JpegImage.Assign(Bitmap);
        JpegImage.CompressionQuality := 90;
        JpegImage.SaveToFile(SavePictureDialog.FileName);
        ShowMessage('Image saved successfully.');
      finally
        JpegImage.Free;
      end;
    finally
      Bitmap.Free;
    end;
  end;
end;

在每個面板中進行文本選擇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
procedure TFormMain.PdfView1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  SetActivePdfView(PdfView1);
  
  if Button = mbLeft then
  begin
    Selecting := True;
    SelectionStart := PdfView1.CharacterIndexAtPos(X, Y, 5.0, 5.0);
    SelectionEnd := SelectionStart;
  end;
end;
 
procedure TFormMain.PdfView1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
  if Selecting then
  begin
    SelectionEnd := PdfView1.CharacterIndexAtPos(X, Y, 5.0, 5.0);
    PdfView1.Invalidate;
  end;
end;
 
procedure TFormMain.PdfView1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Selecting := False;
end;

用例

  • 文件修訂審查 – 比較原始文件和修訂後的文件
  • 質量控制 驗證生產環境與模板的一致性。
  • 法律稽核。 比較合同版本。
  • 翻譯驗證。 檢視原始文件和已翻譯文件。
  • 多語言文件。 檢視相同內容的不同語言版本。

結論。

Split View 演示展示了 PDFium VCL 在構建高階文件比較工具方面的靈活性。它支援多個獨立的檢視,同步導航以及每個面板的控制元件,您可以建立專業的比較應用程式。

能夠在每個面板中開啟不同的 PDF 檔案並進行視覺比較,對於修訂審查和質量控制工作流程非常有價值。

構建強大的文件比較工具 使用 Delphi PDFium VCL 元件.