Migaro. 技術Tips

                       

ミガロ. 製品の技術情報
IBMiの活用に役立つ情報を掲載!


【Delphi】PDFiumを使用してPDFから画像や文字を抽出

1995年2月に最初のバージョンが登場し、
当時のアプリケーション開発に変革をもたらした「Delphi」が、2025年2月に30周年を迎えます。

当時はまだInternet ExplorerもYahoo!もGoogleも存在せず、Windows 95も発売前。
そこから30年の長きにわたってDelphiが愛されてきた要因の一つには、
機能拡張やサードパーティ製品が数多く登場し、その発展を支えてきたことが挙げられるでしょう。

今回はそんな拡張機能の中から、
PDFから画像や文字を抽出できるツール「PDFium」を紹介します。

 

PDFiumの概要

『PDFium』はChromeで利用されているPDFレンダリングエンジンです。
pdfium – Git at Google

読み方は諸説ありますが、海外の動画だと
「ピーディーエフィアム」や「ピーディーエファイウム」と発音されていました。
Apacheライセンスなので商用利用も可能です。

このライブラリを使用して、
DelphiのFireMonkeyアプリケーションでPDFの読み込みや出力が可能になります。

 

PDFium Component Suiteのダウンロード

今回使用するPDFium Component Suite for FireMonkeyは、
スロバキアのWinSoft社が開発した、PDFiumをDelphiで使用するためのコンポーネント スイートです。

Windows向けはFireMonkeyが利用可能な各バージョンに対応していますが、
iOS・Android向けのライブラリはDelphi/400 10.4 Sydney以上での対応となっています。

【エンバカデロ社の紹介記事】
PDFium – Delphi/C++Builder FireMonkeyアプリケーション向けPDFエンジン
(インストール動画:WinSoft PDFium for FireMonkey – Install Guide

概要は上記の紹介記事にもありますが、
ここからは導入手順を紹介していきます。

PDFium for FireMonkey

WinSoft社のHPにあるこちらのページにアクセスし、
『Download PDFium for FireMonkey 6.9 trial version』をクリックしてダウンロードします。
デモプログラムをダウンロードしたい場合は、それぞれのリンクをクリックします。

上記のtrial versionのリンクをクリックすると
『fpdfium.exe』がダウンロードされます。
こちらをダブルクリックで起動してインストールを行います。

 

なお、「trial version」とある通り評価版のため、
EXE起動後最初の処理時には以下のような評価版であることを示す
ダイアログが表示されますが、処理結果には影響はありません。
(購入時の価格等詳細は公式サイトをご確認ください。)

 

PDFium Component Suiteを使って画像抽出

DelphiのFireMonkeyアプリケーションを新規作成したら、
画面に『TFPdf』および各コンポーネントを配置します。

 
以下のようにイベントを定義し、ロジックを記述します。

TFPdfコンポーネントを使って、PDFをページ単位で画像に出力できます。
出力直前のロジック(①②部分)で出力画像の幅と高さを変更できます。
また、保存時のファイル名によって画像の形式(JPG/PNG)を変更できます。(③部分)

uses FMX.BehaviorManager; // uses節追加

{*****************************************************************************
  画面生成時処理(初期設定)
*****************************************************************************}
procedure TFormMain.FormCreate(Sender: TObject);
var DeviceBehavior: IDeviceBehavior;
begin
  PixelsPerInch := 96;
  if TBehaviorServices.Current.SupportsBehaviorService(IDeviceBehavior, DeviceBehavior, Self) then
    PixelsPerInch := DeviceBehavior.GetDisplayMetrics(Self).PixelsPerInch;
end;
{*****************************************************************************
  Convert JPG ボタン押下時処理(PDFと同階層にページ毎に出力)
*****************************************************************************}
procedure TFormMain.ButtonConvertClick(Sender: TObject);
var
  I: Integer;
  FileName: string;
  Bitmap: TBitmap;
begin
  ProgressBar.Value := 0;
  EditPdfFile.Enabled := False;
  ButtonText.Enabled := False;
  ButtonConvert.Enabled := False;
  FormMain.Cursor := crHourGlass;
  try
    FPdf.FileName := EditPdfFile.Text;
    FPdf.PageNumber := 0;
    FPdf.Active := True;
    ProgressBar.Max := FPdf.PageCount;

    FileName := ExtractFileName(FPdf.FileName);
    FileName := Copy(FileName, 1, Length(FileName) - Length(ExtractFileExt(FPdf.FileName)));
    FileName := ExtractFilePath(FPdf.FileName) + FileName;

    for I := 1 to FPdf.PageCount do
    begin
      FPdf.PageNumber := I;

      Bitmap := FPdf.RenderPage(0, 0,
        Round(PointsToPixels(FPdf.PageWidth, PixelsPerInch)),   // ①出力画像の横幅
        Round(PointsToPixels(FPdf.PageHeight, PixelsPerInch))); // ②出力画像の高さ
      try
        Bitmap.SaveToFile(FileName + '_Page' + IntToStr(I) + '.jpg'); // ③拡張子はpngも対応
      finally
        Bitmap.Free;
      end;

      ProgressBar.Value := I;
      Application.ProcessMessages;
    end;
  finally
    FormMain.Cursor := crDefault;
    FPdf.Active := False;
    EditPdfFile.Enabled := True;
    ButtonText.Enabled := True;
    ButtonConvert.Enabled := True;
  end;
end;

例えば弊社テクニカルレポートのPDFをこのロジックで
画像にして出力した場合、このようなイメージになります。

 

PDFium Component Suiteを使ってテキスト抽出

次はTFPdfコンポーネントを使って、PDF内の文字を抽出します。

抽出可能なのはPDF内に文字データとして埋め込まれている文字列のみです。
PDF内の画像に書かれている文字を認識させる機能はありません。

{*****************************************************************************
  Convert TEXT ボタン押下時処理(画面下部のTMemoに出力)
*****************************************************************************}
procedure TFormMain.ButtonTextClick(Sender: TObject);
var
  I: Integer;
  Text: RawByteString;
  FileName: string;
begin
  FPdf.FileName := EditPdfFile.Text;
  FPdf.PageNumber := 0;
  FPdf.Active := True;
  ProgressBar.Max := FPdf.PageCount;

  FileName := ExtractFileName(FPdf.FileName);
  FileName := Copy(FileName, 1, Length(FileName) - Length(ExtractFileExt(FPdf.FileName)));
  FileName := ExtractFilePath(FPdf.FileName) + FileName;

  try
    for I := 1 to FPdf.PageCount do
    begin
      FPdf.PageNumber := I;
      Text := UTF8Encode(FPdf.Text);
      if Text <> '' then
      begin
        Memo1.Lines.Add(Text);
      end;
      ProgressBar.Value := I;
      Application.ProcessMessages;
     end;
  finally
    FPdf.Active := False;
  end;
end;

例えば弊社テクニカルレポートのPDFから文字抽出した場合、
このようなイメージでMemoに出力されます。