技術文章

在 Delphi 中使用 PDFium VCL 從 PDF 文件中提取影像

· PDF 程式設計

PDF文件通常包含有價值的影像,例如照片、圖表、圖形和示意圖。 提取影像 demo演示瞭如何使用PDFium VCL從PDF文件中提取所有嵌入的影像,並根據其特性將其儲存為最佳格式。

概述

此demo從PDF頁面中提取嵌入的影像(點陣圖),並將其儲存為單獨的影像檔案。它包括影像預覽、格式檢測以及帶進度跟蹤的批次提取。

主要特性

  • 提取所有影像 - 從PDF中提取每個嵌入的影像
  • 頁面範圍選擇 - 僅從特定頁面提取
  • 智慧格式檢測 – 自動選擇 JPEG、PNG 或 BMP,基於影像特徵。
  • 影像預覽 – 在儲存之前預覽提取的影像。
  • 詳細資訊 – 檢視尺寸、格式和檔案大小。
  • 批次處理 – 提取多個影像,並跟蹤進度。

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 系統目錄。

基礎影像提取

通過以下方式訪問嵌入的影像: Bitmap 和 BitmapCount 屬性:

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 ExtractImagesSimple;
var
  Pdf: TPdf;
  I, J: Integer;
  Bitmap: TBitmap;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'document.pdf';
    Pdf.Active := True;
    
    // Loop through all pages
    for I := 1 to Pdf.PageCount do
    begin
      Pdf.PageNumber := I;
      
      // Loop through all images on this page
      for J := 0 to Pdf.BitmapCount - 1 do
      begin
        Bitmap := Pdf.Bitmap[J];
        try
          // Save as BMP
          Bitmap.SaveToFile(Format('Page%d_Image%d.bmp', [I, J + 1]));
        finally
          Bitmap.Free;
        end;
      end;
    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
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
procedure TFormMain.ButtonExtractClick(Sender: TObject);
var
  I, J, StartPage, EndPage: Integer;
  Bitmap: TBitmap;
  FullFileName, DetectedFormat, ImageExtension: string;
  ImageInfo: TImageInfo;
begin
  FCancelled := False;
  FProcessedImages := 0;
  FTotalImages := 0;
  
  ClearExtractedImages;
  EnableControls(False);
  
  try
    Pdf.FileName := EditPdfFile.Text;
    Pdf.PageNumber := 0;
    Pdf.Active := True;
    
    ParsePageRange(EditPageRange.Text, StartPage, EndPage);
    if EndPage = -1 then
      EndPage := Pdf.PageCount;
      
    // Calculate total images for progress
    for I := StartPage to EndPage do
    begin
      Pdf.PageNumber := I;
      FTotalImages := FTotalImages + Pdf.BitmapCount;
    end;
    
    ProgressBar.Max := FTotalImages;
    
    // Extract images
    for I := StartPage to EndPage do
    begin
      if FCancelled then
        Break;
        
      Pdf.PageNumber := I;
      
      for J := 0 to Pdf.BitmapCount - 1 do
      begin
        if FCancelled then
          Break;
          
        Bitmap := Pdf.Bitmap[J];
        if Assigned(Bitmap) then
        begin
          try
            // Detect optimal format
            DetectedFormat := DetectImageFormat(Bitmap);
            ImageExtension := GetExtensionForFormat(DetectedFormat);
            
            FullFileName := Format('%s\Page%d_Image%d%s',
              [FCurrentOutputDir, I, J + 1, ImageExtension]);
              
            SaveBitmapInOptimalFormat(Bitmap, FullFileName);
            
            // Store image info for preview
            ImageInfo.FileName := FullFileName;
            ImageInfo.PageNumber := I;
            ImageInfo.ImageIndex := J + 1;
            ImageInfo.Width := Bitmap.Width;
            ImageInfo.Height := Bitmap.Height;
            ImageInfo.Format := DetectedFormat;
            ImageInfo.Bitmap := TBitmap.Create;
            ImageInfo.Bitmap.Assign(Bitmap);
            
            AddImageInfo(ImageInfo);
            
            Inc(FProcessedImages);
            ProgressBar.Position := FProcessedImages;
            
          finally
            Bitmap.Free;
          end;
        end;
      end;
    end;
    
    UpdateImageList;
    
  finally
    Pdf.Active := False;
    EnableControls(True);
  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
function TFormMain.DetectImageFormat(ABitmap: TBitmap): string;
begin
  // Check if image has transparency (alpha channel)
  if ABitmap.PixelFormat = pf32bit then
  begin
    // PNG for transparency support
    Result := 'PNG';
  end
  // Check if it's likely a photographic image
  else if (ABitmap.Width * ABitmap.Height > 100000) and
          (ABitmap.PixelFormat in [pf24bit, pf32bit]) then
  begin
    // Large, complex image - use JPEG for smaller file size
    Result := 'JPEG';
  end
  else
  begin
    // Small or simple image - preserve quality with BMP
    Result := 'BMP';
  end;
end;
 
function TFormMain.GetExtensionForFormat(const AFormat: string): string;
begin
  case UpperCase(AFormat)[1] of
    'J': Result := '.jpg';
    'P': Result := '.png';
    'B': Result := '.bmp';
  else
    Result := '.bmp';
  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
procedure TFormMain.SaveBitmapInOptimalFormat(ABitmap: TBitmap;
  const AFileName: string);
var
  JpegImg: TJPEGImage;
  FileExt: string;
begin
  FileExt := UpperCase(ExtractFileExt(AFileName));
  
  if FileExt = '.JPG' then
  begin
    // Save as JPEG with good quality
    JpegImg := TJPEGImage.Create;
    try
      JpegImg.Assign(ABitmap);
      JpegImg.CompressionQuality := 85; // Good quality/size balance
      JpegImg.SaveToFile(AFileName);
    finally
      JpegImg.Free;
    end;
  end
  else if FileExt = '.PNG' then
  begin
    // PNG would require additional library
    // Fall back to BMP for compatibility
    ABitmap.SaveToFile(ChangeFileExt(AFileName, '.bmp'));
  end
  else
  begin
    // BMP - lossless quality
    ABitmap.SaveToFile(AFileName);
  end;
end;

使用 TPdfImage 處理原始影像資料。

對於高階用例,訪問原始影像資料:

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 ProcessRawImageData;
var
  Pdf: TPdf;
  I: Integer;
  PdfImage: TPdfImage;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'document.pdf';
    Pdf.Active := True;
    Pdf.PageNumber := 1;
    
    for I := 0 to Pdf.ImageCount - 1 do
    begin
      PdfImage := Pdf.Image[I];
      
      // Access raw image properties
      ShowMessage(Format('Image %d: %d x %d, %d bytes',
        [I, PdfImage.Width, PdfImage.Height, Length(PdfImage.Data)]));
        
      // PdfImage.Data contains raw pixel data
    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
procedure TFormMain.UpdatePreview(Index: Integer);
var
  Info: TImageInfo;
begin
  if (Index >= 0) and (Index < Length(FExtractedImages)) then
  begin
    Info := FExtractedImages[Index];
    
    // Update preview
    if Assigned(Info.Bitmap) then
      ImagePreview.Picture.Assign(Info.Bitmap);
      
    // Update info display
    MemoInfo.Lines.Clear;
    MemoInfo.Lines.Add('File: ' + ExtractFileName(Info.FileName));
    MemoInfo.Lines.Add('Page: ' + IntToStr(Info.PageNumber));
    MemoInfo.Lines.Add('Dimensions: ' + IntToStr(Info.Width) +
                       ' x ' + IntToStr(Info.Height));
    MemoInfo.Lines.Add('Format: ' + Info.Format);
    if Info.Size > 0 then
      MemoInfo.Lines.Add('Size: ' + FormatFloat('#,##0', Info.Size) + ' bytes');
  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
procedure TFormMain.ParsePageRange(const ARange: string;
  var AStartPage, AEndPage: Integer);
var
  RangeStr: string;
  DashPos: Integer;
begin
  RangeStr := Trim(ARange);
  AStartPage := 1;
  AEndPage := -1; // -1 means extract to end
  
  if (RangeStr = '') or (UpperCase(RangeStr) = 'ALL') then
    Exit;
    
  DashPos := Pos('-', RangeStr);
  if DashPos > 0 then
  begin
    // Range format: start-end
    AStartPage := StrToIntDef(Trim(Copy(RangeStr, 1, DashPos - 1)), 1);
    AEndPage := StrToIntDef(Trim(Copy(RangeStr, DashPos + 1, Length(RangeStr))), -1);
  end
  else
  begin
    // Single page
    AStartPage := StrToIntDef(RangeStr, 1);
    AEndPage := AStartPage;
  end;
end;

開啟提取的影像資料夾。

1
2
3
4
5
6
7
procedure TFormMain.ButtonOpenFolderClick(Sender: TObject);
begin
  if DirectoryExists(FCurrentOutputDir) then
    ShellExecute(Handle, 'open', PChar(FCurrentOutputDir), nil, nil, SW_SHOWNORMAL)
  else
    ShowMessage('Output directory does not exist.');
end;

用例

  • 提取數字資產。 – 從營銷材料中提取照片和圖形。
  • 文件轉換。 準備用於網頁或其他格式的影像。
  • 歸檔處理。 從掃描文件檔案中提取影像。
  • 內容分析。 用於機器學習或分析,提取影像。

結論。

"提取影像" 演示程式展示了使用 PDFium VCL 從 PDF 文件中輕鬆提取嵌入影像的方法。該元件處理複雜的 PDF 解析,而您專注於如何在您的應用程式中使用提取的影像。

結合智慧格式檢測,您可以構建專業的影像提取工具,以最佳化輸出,適用於任何用例。

探索。 PDFium 元件 訪問 loslab.com 並解鎖您 PDF 文件中的內容。