技术文章

在 Delphi 中使用 PDFium VCL 打印 PDF 文档

· PDF 编程

准确渲染和正确缩放是打印 PDF 文档的常见需求。 打印 PDF 演示程序展示了如何使用 PDFium VCL 组件实现专业的 PDF 打印功能,包括预览、页面选择和质量选项。

概述

这个全面的演示程序提供了一个完整的打印解决方案,包括打印预览、页面范围选择、质量设置、缩放选项以及最近文件历史记录。它展示了在 Delphi 应用程序中进行 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 系统目录。

基础 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
procedure PrintPdfSimple(const FileName: string);
var
  Pdf: TPdf;
  I: Integer;
  Bitmap: TBitmap;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;
    
    Printer.Title := Pdf.Title;
    Printer.BeginDoc;
    try
      for I := 1 to Pdf.PageCount do
      begin
        if I > 1 then
          Printer.NewPage;
          
        Pdf.PageNumber := I;
        
        // Render page to printer
        Bitmap := Pdf.RenderPage(
          0, 0,
          Printer.PageWidth,
          Printer.PageHeight,
          ro0,
          [rePrinting]
        );
        try
          PrintBitmap(Printer, Bitmap);
        finally
          Bitmap.Free;
        end;
      end;
    finally
      Printer.EndDoc;
    end;
    
  finally
    Pdf.Active := False;
    Pdf.Free;
  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 PrintBitmap(Printer: TPrinter; Bitmap: TBitmap);
var
  InfoHeaderSize, ImageSize: DWORD;
  InfoHeader: PBitmapInfo;
  Image: Pointer;
begin
  GetDIBSizes(Bitmap.Handle, InfoHeaderSize, ImageSize);
  
  InfoHeader := AllocMem(InfoHeaderSize);
  try
    Image := AllocMem(ImageSize);
    try
      GetDIB(Bitmap.Handle, 0, InfoHeader^, Image^);
      
      StretchDIBits(
        Printer.Canvas.Handle,
        0, 0, Bitmap.Width, Bitmap.Height,
        0, 0, Bitmap.Width, Bitmap.Height,
        Image, InfoHeader^,
        DIB_RGB_COLORS, SRCCOPY
      );
    finally
      FreeMem(Image);
    end;
  finally
    FreeMem(InfoHeader);
  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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
procedure TFormMain.PrintPDFPages;
var
  FromPage, ToPage, Page, Copy, CopyCount: Integer;
  FirstPage: Boolean;
  Bitmap: TBitmap;
  ScaleX, ScaleY, Scale: Double;
  DestWidth, DestHeight: Integer;
  Dpi: Integer;
begin
  // Get print quality DPI
  case cmbQuality.ItemIndex of
    0: Dpi := 150;  // Draft
    1: Dpi := 300;  // Normal
    2: Dpi := 600;  // High
  else
    Dpi := 300;
  end;
  
  // Determine page range
  if radAllPages.Checked then
  begin
    FromPage := 1;
    ToPage := Pdf.PageCount;
  end
  else
  begin
    FromPage := StrToIntDef(edtFromPage.Text, 1);
    ToPage := StrToIntDef(edtToPage.Text, Pdf.PageCount);
  end;
  
  // Handle copies and collation
  if chkCollate.Checked then
    CopyCount := 1  // Will repeat entire job
  else
    CopyCount := spnCopies.Value;
    
  FProcessing := True;
  FCancelled := False;
  
  Printer.Title := ExtractFileName(Pdf.FileName);
  Printer.BeginDoc;
  try
    FirstPage := True;
    progressPrint.Max := (ToPage - FromPage + 1) * spnCopies.Value;
    progressPrint.Position := 0;
    
    for Page := FromPage to ToPage do
    begin
      for Copy := 1 to CopyCount do
      begin
        if FCancelled then
          Break;
          
        if FirstPage then
          FirstPage := False
        else
          Printer.NewPage;
          
        Pdf.PageNumber := Page;
        
        // Calculate scaling based on settings
        case cmbScaling.ItemIndex of
          0: // No scaling
          begin
            DestWidth := Round(Pdf.PageWidth * Dpi / 72);
            DestHeight := Round(Pdf.PageHeight * Dpi / 72);
          end;
          
          1: // Fit to page
          begin
            ScaleX := Printer.PageWidth / (Pdf.PageWidth * Dpi / 72);
            ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72);
            Scale := Min(ScaleX, ScaleY);
            DestWidth := Round(Pdf.PageWidth * Dpi / 72 * Scale);
            DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale);
          end;
          
          2: // Shrink to fit (only if needed)
          begin
            ScaleX := Printer.PageWidth / (Pdf.PageWidth * Dpi / 72);
            ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72);
            Scale := Min(ScaleX, ScaleY);
            if Scale > 1 then Scale := 1;
            DestWidth := Round(Pdf.PageWidth * Dpi / 72 * Scale);
            DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale);
          end;
        end;
        
        // Render and print
        Bitmap := Pdf.RenderPage(0, 0, DestWidth, DestHeight,
          ro0, [rePrinting, reAnnotations]);
        try
          PrintBitmap(Printer, Bitmap);
        finally
          Bitmap.Free;
        end;
        
        progressPrint.Position := progressPrint.Position + 1;
        Application.ProcessMessages;
      end;
      
      if FCancelled then
        Break;
    end;
    
  finally
    Printer.EndDoc;
    FProcessing := False;
  end;
end;

使用打印对话框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
procedure TFormMain.btnPrintClick(Sender: TObject);
begin
  if not FileExists(edtPdfFile.Text) then
  begin
    ShowError('Please select a valid PDF file first.');
    Exit;
  end;
  
  // Setup print dialog
  PrintDialog.MinPage := 1;
  PrintDialog.MaxPage := Pdf.PageCount;
  PrintDialog.FromPage := 1;
  PrintDialog.ToPage := Pdf.PageCount;
  PrintDialog.Options := [poPageNums, poSelection];
  
  if PrintDialog.Execute then
  begin
    PrintPDFPages;
  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
procedure TFormMain.UpdatePreview;
var
  PreviewWidth, PreviewHeight: Integer;
  Scale: Double;
begin
  if not Pdf.Active then
    Exit;
    
  Pdf.PageNumber := FCurrentPreviewPage;
  
  // Calculate preview size to fit panel
  Scale := Min(
    pnlPreview.Width / Pdf.PageWidth,
    pnlPreview.Height / Pdf.PageHeight
  ) * (FCurrentZoom / 100);
  
  PreviewWidth := Round(Pdf.PageWidth * Scale);
  PreviewHeight := Round(Pdf.PageHeight * Scale);
  
  // Render preview
  FPreviewBitmap.Free;
  FPreviewBitmap := Pdf.RenderPage(
    0, 0, PreviewWidth, PreviewHeight,
    ro0, [reAnnotations], clWhite
  );
  
  // Display in image control
  imgPreview.Picture.Assign(FPreviewBitmap);
  
  // Update page info
  lblPageInfo.Caption := Format('Page %d of %d',
    [FCurrentPreviewPage, Pdf.PageCount]);
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
procedure TFormMain.btnFirstPageClick(Sender: TObject);
begin
  if Pdf.Active then
  begin
    FCurrentPreviewPage := 1;
    edtPreviewPage.Text := '1';
    UpdatePreview;
  end;
end;
 
procedure TFormMain.btnPrevPageClick(Sender: TObject);
begin
  if Pdf.Active and (FCurrentPreviewPage > 1) then
  begin
    Dec(FCurrentPreviewPage);
    edtPreviewPage.Text := IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;
 
procedure TFormMain.btnNextPageClick(Sender: TObject);
begin
  if Pdf.Active and (FCurrentPreviewPage < Pdf.PageCount) then
  begin
    Inc(FCurrentPreviewPage);
    edtPreviewPage.Text := IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;
 
procedure TFormMain.btnLastPageClick(Sender: TObject);
begin
  if Pdf.Active then
  begin
    FCurrentPreviewPage := Pdf.PageCount;
    edtPreviewPage.Text := IntToStr(FCurrentPreviewPage);
    UpdatePreview;
  end;
end;

缩放控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
procedure TFormMain.cmbZoomChange(Sender: TObject);
begin
  case cmbZoom.ItemIndex of
    0: FCurrentZoom := 50;
    1: FCurrentZoom := 75;
    2: FCurrentZoom := 100;
    3: FCurrentZoom := 125;
    4: FCurrentZoom := 150;
    5: FCurrentZoom := 200;
  else
    FCurrentZoom := 100;
  end;
  
  UpdatePreview;
end;

处理页面旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function GetPrintRotation(Rotation: TRotation): TRotation;
begin
  // Adjust rotation for printing (printer coordinate system)
  case Rotation of
    ro90:  Result := ro270;
    ro270: Result := ro90;
  else
    Result := Rotation;
  end;
end;
 
// Use in printing
Bitmap := Pdf.RenderPage(0, 0, Width, Height,
  GetPrintRotation(Pdf.PageRotation), [rePrinting]);

保存/加载设置

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
procedure TFormMain.SaveSettings;
var
  Ini: TIniFile;
begin
  Ini := TIniFile.Create(FSettingsFile);
  try
    Ini.WriteInteger('Print', 'Quality', cmbQuality.ItemIndex);
    Ini.WriteInteger('Print', 'Scaling', cmbScaling.ItemIndex);
    Ini.WriteInteger('Print', 'Copies', spnCopies.Value);
    Ini.WriteBool('Print', 'Collate', chkCollate.Checked);
    Ini.WriteInteger('Preview', 'Zoom', FCurrentZoom);
  finally
    Ini.Free;
  end;
end;
 
procedure TFormMain.LoadSettings;
var
  Ini: TIniFile;
begin
  if FileExists(FSettingsFile) then
  begin
    Ini := TIniFile.Create(FSettingsFile);
    try
      cmbQuality.ItemIndex := Ini.ReadInteger('Print', 'Quality', 1);
      cmbScaling.ItemIndex := Ini.ReadInteger('Print', 'Scaling', 1);
      spnCopies.Value := Ini.ReadInteger('Print', 'Copies', 1);
      chkCollate.Checked := Ini.ReadBool('Print', 'Collate', False);
      FCurrentZoom := Ini.ReadInteger('Preview', 'Zoom', 100);
    finally
      Ini.Free;
    end;
  end;
end;

用例

  • 文档打印 – 高质量打印 PDF 文档
  • 批量打印 – 顺序打印多个文档
  • 报告分发 - 打印报告以供分发。
  • 样印。 - 在最终生产之前,打印预览稿。

结论。

Print PDF 演示程序展示了如何使用 PDFium VCL 实现专业的 PDF 打印功能。从简单的单页打印到高级的多份打印任务,包括预览和质量设置,该组件提供了您所需的一切。

好的。 RenderPage 使用以下方法: rePrinting 此选项可确保打印机输出最佳效果,而预览功能允许用户在打印前验证其设置。

添加专业 PDF 打印功能。 将其集成到您的应用程序中。 PDFium Delphi 组件。.