Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2006年12月03日] [更新 2018年08月08日] Delphi サンプルプログラム集
720_PDF 文書のフォームへの表示と印刷と関連リンク
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro + Adobe Reader DC
Adobe Reader DC のリリースは 2015年5月
720_Acrobat.zip [4,185 KB] 2018年08月08日版 (EXE 同梱、PDFMaker同梱)



  • 2011年07月22日
  • 構成を変更して、[03],[04],[06] を追加
  • 動作確認環境の変更
  • 2011年07月24日
  • TAcroPDF と直接関係ないのと、[722_PDF 文書の印刷] の記事を追加しているので、ShellExcute 関数による印刷のサンプルを削除
  • 2014年02月18日
  • [722] [724] に掲載していた PDF 関係の参考リンクを、本ページにまとめた
  • 2018年08月18日
  • EXE 終了時に発生する例外とメモリリーク対策のコードを添付の AcroPDFLib_TLB.pas に追加





PDF 文書のフォーム表示と印刷


Adobe Acrobat (アドビ アクロバット) の文書をフォームに表示して制御、また、コードで印刷するサンプルプログラムです。[タイプライブラリの取り込み] で、AcroPDF (AcroPDFLib_TLB.pas) をインストールしておく必要があります。

この Acrobat のコントロールはバージョンによって内部の API に変更があるようです (DLL 内の関数処理の変更)。
Adobe のサイトで確認したところ、これらの ActiveX コントロールは、ブラウザウィンドウ内に PDF ファイルを表示するためのものであり、これらのインターフェイスは、バージョンが変わるたびに各ブラウザの能力を引き出すために変更され、API も変更される。とあります。したがって、システムにインストールされている Adobe Reader のバージョンと Windows のバージョンによっては正常に動作しない場合があります。動作しても動作仕様が異なる場合もあります。また、以下のサンプルプログラムの動作は、Adobe Reader の設定によっても異なってきます。

システムに Adobe Acrobat がインストールされていない場合は AcroRd.dll を、インストールしている場合は、Acrobat.dll を使用して制御しているようです。したがって、Adobe Acrobat と Adobe Reader のバージョンが違っていると正常動作しません。

参考リンクの [AcroPDFLib.AxAcroPDF 一覧] に、本ページのサンプルで使用しているメソッド類の一覧と説明があります。






01_タイプライブラリの取り込み


[コンポーネント]、[コンポーネントのインポート] [ActiveX の取り込み] で取り込みます。
この手順で作成した AcroPDFLib_TLB をそのまま使用すると、EXE 終了時に例外が発生したりメモリリークが発生することがあります。添付の AcroPDFLib_TLB.pas には、それらの現象に対する対策コードを追加しています。

添付の AcroPDFLib_TLB.pas を使用する場合は、コンポーネントとしてインストールするか、以下の手順で作成したユニットファイルと置き換えてください。



下図の手順で作成した AcroPDFLib_TLB をそのまま使用して、EXE 終了時に例外が発生したりメモリリークが発生する時は、アプリの終了時に以下のような処理を実行してください。
Adobe Reader DC より前のバージョンでは発生しないという報告があります。


リスト1
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if Assigned(AcroPDF1) then begin
    //参照カウンタがインクリメントされていないようなので
    //解放破棄操作される前に _AddRef しておく
    AcroPDF1.ControlInterface._AddRef;
  end;
end;


  • 図1
  • [Adobe Acrobat 7.0 Browser Control Type Library 1.0] を選択
  • [タイプライブラリの取り込み] で [コンポーネントラッパーを作成] をチェック

  • 図2
  • 適当なパッケージにインストール
  • ここでは [既存のパッケージにインストール] としている




02_PDF 文書のフォームへの表示


TAcroPDF コントロールを使用して Form 上に PDF 文書を表示します。
ここでは、表示形式を変更するだけのサンプルとなっています。他の関数類は、AcroPDFLib_TLB.pas 内を参照してください。


TAcroPDF (IAcroAXDocShim) のページ移動関係メソッド類
ページ数、現在のページを取得するメソッドやプロパティはない
TAcroPDF (IAcroAXDocShim) の機能は限られているので、PDF 文書作成時の設定が重要

    gotoFirstPage
    gotoLastPage
    gotoNextPage
    gotoPreviousPage
    setCurrentPage(n: Integer)


図3
設計時画面
  • 左が TAcroPDF コントロール
  • 右の一番上がファイル選択ダイアログ表示用
  • その下のボタン類は表示形式の切換用
図4
実行開始時の画面
図5
[PDF 表示] ボタンで読込んだところ
  • 表示文書は添付の TestAcro.pdf
図6
表示を fitv に変更

リスト2
TAcroPDF コントロールで PDF 文書を表示
unit AcrobatShowUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, OleCtrls, ExtCtrls, AcroPDFLib_TLB;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    OpenDialog1: TOpenDialog;
    PrintDialog1: TPrintDialog;
    AcroPDF1: TAcroPDF;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  ファイル選択ダイアログでPDFファイルを選択して表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenDialog1.DefaultExt := 'PDF';
  OpenDialog1.Filter     := 'Acrobat PDFファイル|*.PDF';
  OpenDialog1.Options    := [ofReadOnly];

  if OpenDialog1.Execute then begin
    Update;
    AcroPDF1.LoadFile(OpenDialog1.FileName);
    AcroPDF1.setShowToolbar(False);
  end;
end;
//=============================================================================
//  表示方式の変更
//  Button2からButton8までに共通使用のイベント
//  その他のメソッド(関数)類は、AcroPDFLib_TLB.pas内を参照
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if not AcroPDF1.Visible then exit;

  if Sender = Button2 then AcroPDF1.setView('fit');
  if Sender = Button3 then AcroPDF1.setView('fitwindow');
  if Sender = Button4 then AcroPDF1.setView('fith');
  if Sender = Button5 then AcroPDF1.setView('fitb');
  if Sender = Button6 then AcroPDF1.setView('fitbh');
  if Sender = Button7 then AcroPDF1.setView('fitbv');
  if Sender = Button8 then AcroPDF1.setView('fitv');
end;

end.


フォーム表示開始時に PDF 文書を表示したい場合は、例えば次のように OnCreate イベントで PDF 文書を設定します。フォームの表示処理中である OnShow イベントで PDF 文書のファイルを指定しても表示できません。


リスト3
procedure TForm1.FormCreate(Sender: TObject);
var
  LPdfFilePath : string;
begin
  LPdfFilePath := ExpandFileName('..\TestAcro.pdf');
  AcroPDF1.LoadFile(LPdfFilePath);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  AcroPDF1.setView('fitv');
end;




03_PDF 文書のフォームへの表示 - フォーカス制御


コントロールのフォーカスは、一般的には、意図的に操作しない限り変化しません。しかし、OLE オブジェクトを操作するとフォーカスが移動してしまう場合があります。今回の場合は PDF 文書を開いて表示する際に、PDF 文書にフォーカスが移動してしまいます。
TAcroPDF には、文書の表示完了等のイベントやメッセージは実装されていません。そこで、ここではタイマーコンポーネントを使用してフォーカス制御しています。

このサンプルでは、ボタンクリックで、そのボタンにフォーカス移動しないように、TSpeedButton を使用しています。
このサンプルコードは以下の記事を元に作成しています。



図7
設計時画面
  • 左が TAcroPDF コントロール
  • タイマーコンポーネントを配置
図8
PDF 文書を開いて表示
  • 表示後の入力フォーカスは Edit1

リスト4
TAcroPDF コントロールで PDF 文書を表示後、フォーカスを元に戻す
unit AcrobatShowUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, OleCtrls, ExtCtrls, AcroPDFLib_TLB, ComCtrls, ActiveX, Buttons;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    OpenDialog1: TOpenDialog;
    PrintDialog1: TPrintDialog;
    AcroPDF1: TAcroPDF;
    Edit1: TEdit;
    Edit2: TEdit;
    Timer1: TTimer;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    SpeedButton4: TSpeedButton;
    SpeedButton5: TSpeedButton;
    SpeedButton6: TSpeedButton;
    SpeedButton7: TSpeedButton;
    SpeedButton8: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure AcroPDF1Enter(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
  private
    { Private 宣言 }
    AFocusControl : TWinControl;
  public
    { Public 宣言 }

  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーカスをセットするコントロールを指定
//  タイマーコンポ設定
//  このコードでは、フォームを表示した時のフォーカスコントロールはEdit1となる
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  AFocusControl  := Edit1;
  ActiveControl  := AFocusControl;

  Timer1.Enabled  := False;
  Timer1.Interval := 10;
end;

//=============================================================================
//  ファイル選択ダイアログでPDFファイルを選択して表示
//  クリックで、このフォーカス移動しないようにTSpeedButtonを使用
//  TSpeedButtonを使用しているので、ここでAFocusContolにそのコントロールを設定
//  しておけば、PDF表示後にそのコントロールにフォーカス移動可能
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  OpenDialog1.DefaultExt := 'PDF';
  OpenDialog1.Filter     := 'Acrobat PDFファイル|*.PDF';
  OpenDialog1.Options    := [ofReadOnly];

  if OpenDialog1.Execute then begin
    Update;
    AcroPDF1.LoadFile(OpenDialog1.FileName);
    AcroPDF1.setShowToolbar(False);
    Timer1.Enabled := True;
  end;
end;

//=============================================================================
//  表示方式の変更
//  SpeedButton2からSpeedButton8までに共通使用のイベント
//  クリックで、このフォーカス移動しないようにTSpeedButtonを使用
//
//  その他のメソッド(関数)類は、AcroPDFLib_TLB.pas内を参照
//=============================================================================
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
  if not AcroPDF1.Visible then exit;

  if Sender = SpeedButton2 then AcroPDF1.setView('fit');
  if Sender = SpeedButton3 then AcroPDF1.setView('fitwindow');
  if Sender = SpeedButton4 then AcroPDF1.setView('fith');
  if Sender = SpeedButton5 then AcroPDF1.setView('fitb');
  if Sender = SpeedButton6 then AcroPDF1.setView('fitbh');
  if Sender = SpeedButton7 then AcroPDF1.setView('fitbv');
  if Sender = SpeedButton8 then AcroPDF1.setView('fitv');
end;

//=============================================================================
//  フォーカス移動のためのタイマーイベント
//  PDF文書は、読み込み表示の際、PDF文書にフォーカス移動してしまうのでその対策
//=============================================================================
procedure TForm1.Timer1Timer(Sender: TObject);
var
  hWindow :HWnd;
  cName   : array[0..MAX_PATH - 1] of char;
begin
  if Form1.Active then begin
    hWindow := Windows.GetFocus;
    GetClassName(hWindow, cName, sizeof(cName));
    if cName = 'AVL_AVView' then begin
      if not AFocusControl.Focused then Windows.SetFocus(AFocusControl.Handle);
    end;
  end;
end;

//=============================================================================
//  PDFをクリックしたらPDFをキーやマウスで操作可能にするため、タイマーをOFF
//  これがない場合(タイマーイベントが有効な場合)は、PDF文書のページ移動は、ス
//  クロールバーのマウス操作。あるいは、操作用のコードでのみ可能となる
//=============================================================================
procedure TForm1.AcroPDF1Enter(Sender: TObject);
begin
  Timer1.Enabled := False;
end;

end.




04_PDF 文書のフォームへの表示 - TWebBrowser で表示


TWebBrowser に PDF 文書を表示するサンプルコードです。
IAcroAXDocShim インターフェイスのオブジェクトを取得して、操作しています。使用可能なメソッド (関数) 類は TAcroPDF と全く同じです。TAcroPDF は、内部で IAcroAXDocShim を使用しています。
このサンプルはフォーカス制御をしていません。必要であれば前のサンプルコードを参考にして実装してください。
TWebBrowser に PDF 文書を表示していますが、OnDocumentComplete や OnDownloadComplete イベントでは PDF 文書表示完了のタイミングは取得できません。


図9
設計時画面
  • 左が TWebBrowser コンポーネント
図10
PDF 文書を開いて表示

リスト5
TWebBrowser に PDF 文書を表示
unit AcrobatShowUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, OleCtrls, ExtCtrls, AcroPDFLib_TLB, SHDocVw;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    OpenDialog1: TOpenDialog;
    PrintDialog1: TPrintDialog;
    WebBrowser1: TWebBrowser;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ComObj;

{$R *.DFM}

//=============================================================================
//  ファイル選択ダイアログでPDFファイルを選択して表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenDialog1.DefaultExt := 'PDF';
  OpenDialog1.Filter     := 'Acrobat PDFファイル|*.PDF';
  OpenDialog1.Options    := [ofReadOnly];

  if OpenDialog1.Execute then begin
    Update;
    //about:blankは表示のクリアとしても利用できる
    WebBrowser1.Navigate('about:blank');
    WebBrowser1.Navigate(OpenDialog1.FileName);
  end;
end;
//=============================================================================
//  表示方式の変更
//  Button2からButton8までに共通使用のイベント
//  その他のメソッド(関数)類は、AcroPDFLib_TLB.pas内を参照
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  AcroPDF : IAcroAXDocShim;
begin
  if WebBrowser1.Document = nil then exit;

  AcroPDF := (WebBrowser1.Document as IAcroAXDocShim);

  if Sender = Button2 then AcroPDF.setView('fit');
  if Sender = Button3 then AcroPDF.setView('fitwindow');
  if Sender = Button4 then AcroPDF.setView('fith');
  if Sender = Button5 then AcroPDF.setView('fitb');
  if Sender = Button6 then AcroPDF.setView('fitbh');
  if Sender = Button7 then AcroPDF.setView('fitbv');
  if Sender = Button8 then AcroPDF.setView('fitv');
end;

end.


ツールバーを非表示にしたければ、例えば以下のように OnDocumentComplete イベントを利用します。


procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  AcroPDF : IAcroAXDocShim;
begin
  if WebBrowser1.Document = nil then exit;
  if UpperCase(ExtractFileExt(URL)) <> '.PDF' then exit;

  AcroPDF := (WebBrowser1.Document as IAcroAXDocShim);
  AcroPDF.setShowToolbar(False);
end;




05_PDF 文書の印刷 - ページ範囲を指定して印刷


ページ範囲を指定して印刷するサンプルです。TAcroPDF コントロールまたは IAcroAXDocShim インターフェイスのオブジェクトでは、PDF文書を非表示にしてしまうと印刷できません。そこで、ここでは表示サイズを、1 × 1 というサイズにして、見掛け上、非表示の状態にしています。
TAcroPDF または、IAcroAXDocShim インターフェイスで印刷する場合、プリンタ名の指定や用紙等、プリンタ関係の設定はできません。また、印刷結果は、その PDF 文書の設定によっても異なります。もし期待通りの印刷ができない場合は、その PDF 文書の設定を見直す必要があります。

このサンプルプログラムでは、印刷終了前にフォームを閉じてしまうと、印刷も途中で終了してしまいます。印刷の終了を検出したい場合は、[722_PDF 文書の印刷] を参考にしてください。



TAcroPDF (IAcroAXDocShim) の印刷関係メソッド類
    Print
    printWithDialog
    printPages(from: Integer; to_: Integer)
    printPagesFit(from: Integer; to_: Integer; shrinkToFit: WordBool)
    printAll
    printAllFit(shrinkToFit: WordBool)


図11
設計時画面
  • 白い部分は TWebBrowser
図12
実行して PDF を読み込んだところ
  • [印刷] ボタンが使用可能になる
  • TWebBrowser のサイズが 1 X 1 なので、実際には PDF は見えない

[印刷] ボタンをクリックすると、下図のようなダイアログが現れます。このダイアログのテキストに [文書全体が印刷されます] と書いてありますが、実際には指定した頁だけが印刷されます。


図13
[印刷] ボタンクリック
  • 警告ダイアログが表示される
  • 文書全体となっているが、実際には指定頁だけ印刷する
図14
PDF 文書のプロパティ
  • ページの拡大/縮小が [なし] でない場合、印刷出力用紙が期待通りにならないことがある

リスト6
ページ範囲を指定して PDF 文書を印刷
unit AcrobatPrintUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, OleCtrls, ExtCtrls, AcroPDFLib_TLB, SHDocVw;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    Button2: TButton;
    WebBrowser1: TWebBrowser;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    procedure WebBrowser1DocumentComplete(ASender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  初期設定
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  //PDF文書を非表示にすると印刷できないので、事実上非表示に近い大きさにする
  WebBrowser1.Height := 1;
  WebBrowser1.Width  := 1;

  Button2.Enabled := False;
  //印刷ページ範囲を設定
  LabeledEdit1.Text := '5';
  LabeledEdit2.Text := '7';
end;

//=============================================================================
//  ファイル選択ダイアログでPDFファイルを選択して表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenDialog1.DefaultExt := 'PDF';
  OpenDialog1.Filter     := 'Acrobat PDFファイル|*.PDF';
  OpenDialog1.Options    := [ofReadOnly];

  if OpenDialog1.Execute then begin
    Update;
    WebBrowser1.Navigate(OpenDialog1.FileName);
  end;
end;

//=============================================================================
//  [印刷]ボタン
//  LastPage < FirstPageの時は逆順印刷になる
//  印刷開始ページと終了ページがPDF文書のページ数を超える場合は全ページ印刷
//  AcroPDFLib_TLBには、ページ数取得の関数はないので注意
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  AcroPDF   : IAcroAXDocShim;
  FirstPage : Integer;
  LastPage  : Integer;
begin
  if WebBrowser1.Document = nil then exit;

  AcroPDF   := (WebBrowser1.Document as IAcroAXDocShim);
  FirstPage := StrToInt(LabeledEdit1.Text);
  LastPage  := StrToInt(LabeledEdit2.Text);
  AcroPDF.printPages(FirstPage, LastPage);
end;

//=============================================================================
//  文章をロードしたら印刷ボタンを使用可能にする
//  このイベント発生時、文書が完全に表示完了となっているわけではない
//=============================================================================
procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
  if WebBrowser1.Document = nil then exit;
  if UpperCase(ExtractFileExt(URL)) <> '.PDF' then exit;

  Button2.Enabled := True;
end;

end.




06_PDF 文書の作成や操作関係の参考リンク  -  Adobe サイト関係


アドビ社の PDF 関係へのリンクです。PDF文書の作成や操作は、基本的には、Adobe Reader あるいは Adobe Acrobat が開発環境にも実行環境にも必要です。これは、ワードやエクセルがインストールされていない環境では、基本的には、それらの操作ができないのと同じです。
Adobe 社で提供している Adobe Reader Integration Key を取得すれば可能となるようですが、筆者は確認していません。これは [プラグインの開発について] のページで入手できます。また、Adobe PDF Library (有料) というのも用意されているようです。

Acrobat SDK の文書には、各オブジェクトの引数の Time, Rect, Point, HiliteList が省略されています。また、AcroPDFLib.AxAcroPDF, AFormAut オブジェクトの説明がありません。
Acrobat XI の SDK は現在のところ公開されていないようです。




07_PDF 文書の作成や操作関係の参考リンク  -  Adobe サイト以外


PDF の操作関係のコンポーネント類またはライブラリ等の紹介です。
PowerPDF 日本語版は、Takeshi kanno 氏によって開発されたものを MAM 氏が Delphi 2009 で使用可能にした日本語対応バージョンです。使用可能なフォントの制限についてはリファレンスの記事で確認してください。
SourceForge.net にも同名のコンポーネントがありますが、日本語フォントは使用できません。

Debenu Quick PDF Library には フリーの Lite もありますが、かなりの制限があります。例えば使用可能なフォントは欧文フォントの 10 種類程度で、日本語フォントは使用できません。Lite エディションには、フォント取得用の関数が実装されていません。
PDFtk は PDF 編集用のコマンドラインツールです。

COM 版または ActiveX 版は、開発環境にも実行環境にも COM または AcitveX の登録が必要ですが、開発環境では、インストールの際に登録してくれるライブラリもあります。
ネット上で検索すれば他にもある思いますが、有料のものは機能が豊富な分、比較的高価です、評価版等で機能や仕様を確認することをお勧めします。

これらのほとんどは PDF 文書の作成、レンダリング用です。PDF 文書作成や PDF 文書の編集等に使用します。PDF 文書の表示や印刷には、Adobe Acrobat または Adobe Readerが必要です。


Delphi 用のオープンソースの Synopse PDF engine 関係のリンクです。2015年10月30日現在のバージョンは 1.18 で、Delphi 10 Seattle 対応です。GitHub からもタウンロードできます。
au2010 さんのブログのコメントに書き込んだ方は、いつ、どこからダウンロードしたかの記述がありませんが、2015年10月30日現在、deflate.obj は、GitHub からダウンロードした zip ファイルには含まれています。

Delphi 10 Seattle で ORM を利用しなければ、コンポーネントとしてのインストールは必要ありません。uses に SynPdf を追加して使用します。
そのままで使用すると、Open Type フォントと固定幅のフォント (等倍フォント) が正常に描画されない場合があります。修正方法を harry さんが提供しています。


以下は papy's さんのサイトの PDF 関係のリンクですが、Acrobat 6 以降には未対応です。




08_PDF 文書の作成や操作関係の参考リンク - 仮想プリンタドライバ


上の記事で紹介しているものと少し主旨が違いますが、以下のツールは仮想プリンタドライバとして動作します。Adobe Acrobat をインストールすると、プリンタドライバの Adobe PDF がインストールされます。これと同じような使い方ができます。
プリンタとして使用しますから、TCanvas に描画した内容を PDF 文書として出力できます。




09_Adobe Acrobat または Adobe Reader がない環境での印刷


例えば、上で紹介しているコンポーネント等を使用する方法があります。その場合、ActiveX, COM オブジェクト等で提供されているものは、DLL 等の登録が必要です。
Delphi 用のコンポーネント Quick PDF Library では、以下の関数を使用すると PDF 文書の印刷が可能です。

Adobe 社では、PDF 文書の仕様を、数 100 ページに及ぶ文書で公開しています。その仕様にしたがって印刷のプログラムを作成することも考えられます。ただし、かなりの根性が必要でしょう。



function TDebenuPDFLibrary1015.LoadFromFile(FileName, Password: WideString): Integer; 

function TDebenuPDFLibrary1015.PrintDocument(PrinterName: WideString; StartPage, 
  EndPage, Options: Integer): Integer; 





10_FastReport 等のレポート作成ツールによる PDF 文書の作成


レポート作成ツール FastReport の TfrxPDFExport コンポーネントを使用すると、作成したレポートを PDF 文書として保存できます。Delphi XE2 の場合、 TfrxPDFexport は、[FastReport 4 exports] タブにあります。
その他、[FastReport 4 exports] タブには各種画像形式での保存用コンポーネント類もあります。
Delphi XE2 以降には、FastReport の Embarcadero Edition がバンドルされています。FastReport の製品版は Delphi 2009 以降で利用可能です。

QuickReport V5 Pro 版にも PDF 文書の出力用の TQRPDFDocumentFilter コンポーネントや、エクセル、ワード文書への出力用のコンポーネントがありますが、日本語の処理はできないようです。
また、Rave Reports の PDF 出力用のコンポーネントも日本語には対応していないようです。




11_PDF 文書作成における文字のフォント設定


PDF 文書作成の際、文字列に日本語を使用する場合、日本語が使用可能なフォントを使用するようにします。また、TFont.CharSet にも注意が必要です。
多くの場合、日本語では TFont.CharSet に SHIFTJIS_CHARSET を使用します。

Windows には「代替フォント」の機能があります。Delphi では、Charset に DEFAULT_CHARSET を指定すると、指定したフォントで表示できない場合、別のフォントで表示します。実際には、他の Charset でも代替フォントが使われるようですが、筆者には、その規則性についての知識はありません。
PDF やプリンタ出力の場合は、代替フォントを使用しない場合があります。その結果、指定したフォントで処理できない文字は正しく出力されないことになります。しかし、それが本来のフォントの仕様に基づく結果です。
「プレビュー画面では表示できるのですが ... 」という状態は、プレビュー用のコントロールでは代替フォントを使用していることになります。
PDF 作成、表示用アプリには、代替フォントの使用可否の設定が可能なものもあります。

ベンダから提供されている特殊な文字、フォントで提供されているバーコード類があります。
Delphi の TFont.Charset のオンライヘルプの記事にあるように、ベンダから提供されている情報を確認して設定するようにします。

PDF 作成の際、フォントを埋め込めば、そのフォントがインストールされていない環境でも正常に表示されますが、PDF 文書ファイルのサイズは大きくなります。また、フォントを埋め込んだ文書を配布することについては、フォントの著作権も関係してきます。



図15
MS Pゴシック
  • 日本語が表示できる
  • リストから選択できる
図16
Arial
  • 日本語は表示できない
  • リストにも現れない。つまり選択できない
  • これが本来の [フォント] の仕様
図17
Adobe PDF の代替フォント設定
  • ほんどが代替なしであるが、一部のフォントで代替設定がある
  • システムにインストールされているフォントであれば、代替フォントを使用する必要はないが、別のフォントで出力するように設定することもできる




12_Haru Free PDF library ( libHaru - PDF 文書作成用ライブラリ)


ダウンロードファイルに Haru Free PDF library を添付しています。
Haru Free PDF library で PDF 文書を作成する際、実行環境に必要なファイルは DLL だけですが、
ライブラリのバージョンアップに伴う DLL の作成に大変な手間がかかります。そこで、サンプルの添付だけとしています。DLL の作成方法は、添付の hpdf_Readme.txt で確認してください。
添付の DLL は、Visual Studio 2013 Express を使用して libHaru 2.3.0RC2 で作成しています。

関数類の使用方法については、以下のリンクを参考にしてください。これらの記事には、Version 2.0.8 以後に追加された関数類の説明はありません。