トップへ(mam-mam.net/)

プリンタ一覧、用紙サイズ一覧、用紙情報の取得 ~Delphiソースコード集

プリンタ一覧、用紙サイズ一覧、用紙情報の取得 ~Delphiソースコード集

TPrinterクラスのインスタンスであるPrinterオブジェクトを使用するとプリンタに印刷することが出来ます。
プリンタに出力する前に出力先の情報(解像度、印刷可能領域の幅/高さ、印刷不可領域など)を取得する方法等も含めて解説します。
プリンタを扱うには Uses に Vcl.Printers, WinApi.WinSpool を追加してください。
uses Vcl.Printers, WinApi.WinSpool;

プリンタの一覧取得や使用するプリンタ設定

プリンタの一覧取得や使用するプリンタを設定するには以下を使います。

取得したい値取得方法
プリンタの数の取得 Printer.Printers.Count
プリンタ名一覧 Printer.Printers[ インデックス ]
インデックスは 0 ~ Printer.Printers.Count-1
使用プリンタ設定 Printer.PrinterIndex := インデックス;
インデックスは 0 ~ Printer.Printers.Count-1

用紙サイズ一覧と用紙サイズ設定

現在のプリンタの用紙サイズ一覧の取得や用紙サイズ設定は以下を使います。

取得したい値取得方法
用紙サイズ一覧 以下ソースコードで
動的配列 PaperNo[0 ~ PaperCount-1] に用紙サイズ番号、
動的配列 PaperName[0 ~ PaperCount-1)] に用紙サイズの文字列(A4、A3など)
が取得できます。
現在設定されている出力先プリンタによって英語だったり日本語だったり表記が違います。
各プリンタドライバに依存しているのかもしれません。
type
  TPaperName = array[0..63] of Char;            //静的配列はスタックメモリ
var Device, Driver, Port: array[0..255] of char;//静的配列はスタックメモリ
    DeviceModeHandle: THandle;
    PMode : PDevMode;
    PaperCount, i:Integer;
    PaperNo:Array of WORD;                      //動的配列はヒープメモリ
    PaperName:Array of TPaperName;              //動的配列はヒープメモリ
begin
  //現在のプリンタのDeviceModeハンドルを取得
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
    //第4引数が nil の場合は用紙の数が返る
    PaperCount := DeviceCapabilities(Device, Port, DC_PAPERS, nil, PMode);
    if PaperCount>0 then
    begin
      SetLength(PaperNo,PaperCount);
      SetLength(PaperName,PaperCount);
      //用紙サイズ番号を取得
      DeviceCapabilities(Device, Port, DC_PAPERS, PChar(@PaperNo[0]), PMode);
      //用紙サイズ名を取得
      DeviceCapabilities(Device, Port, DC_PAPERNAMES, PChar(@PaperName[0]), PMode);
    end;
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
end;
用紙サイズ番号取得
var Device, Driver, Port: array[0..255] of char;
    DeviceModeHandle: THandle;
    PMode : PDevMode;
begin
  //現在のプリンタのDeviceModeハンドルを取得
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
   Showmessage('用紙サイズ番号:'+IntToStr(PMode.dmPaperSize));
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
end;
用紙サイズ設定 以下ソースコードで
pMode.dmPaperSize := DMPAPER_B4;
とすることで用紙を設定できます。
(参考)用紙サイズ番号は定数定義されています。
例えばDMPAPER_B4=13(用紙サイズ番号13)、DMPAPER_A4=9(用紙サイズ番号9)
var Device, Driver, Port: array[0..255] of char;//静的配列はスタックメモリ
    DeviceModeHandle: THandle;
    PMode : PDevMode;
begin
  //現在のプリンタのDeviceModeハンドルを取得
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
    //用紙サイズ変更フラグを設定
    PMode.dmFields := PMode.dmFields or DM_PAPERSIZE;
    //用紙サイズを変更
    pMode.dmPaperSize := DMPAPER_A3;
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
  //プリンタに用紙サイズを設定
  Printer.SetPrinter(Device,Driver,Port,DeviceModeHandle);
end;

現在のプリンタの現在の用紙情報の取得

現在のプリンタの現在の用紙情報は以下のように取得することが出来ます。

取得したい値取得方法
用紙の物理幅(px) GetDeviceCaps(Printer.Handle, PHYSICALWIDTH)
用紙の物理高さ(px) GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT)
左上印刷不可X(px) GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX)
左上印刷不可Y(px) GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY)
解像度X(dpi) GetDeviceCaps(Printer.Handle, LOGPIXELSX)
解像度Y(dpi) GetDeviceCaps(Printer.Handle, LOGPIXELSY)
印刷可能幅(px) GetDeviceCaps(Printer.Handle, HORZRES)
又は
Printer.PageWidth
印刷可能高さ(px) GetDeviceCaps(Printer.Handle, VERTRES)
又は
Printer.PageHeight
用紙の方向 Printer.Orientation
以下のどちらかの値が入っている
  • TPrinterOrientation.poPortrait(縦)
  • TPrinterOrientation.poLandscape(横)
Printer.Orientation := TPrinterOrientation.poLandscape;
として用紙の方向を設定することも可能。

上記の値と用紙との関係は以下です。

用紙の物理高さ(px)

用紙の物理幅(px)

↓左上印刷不可(X px, Y px)
原点
印刷可能幅(px)

印刷可能高さ(px)

単位は全て px です。

例えば以下をミリメートル(mm)に変換する場合

1インチ = 25.4ミリメートル(mm)、単位dpi = ドット(ピクセル)/インチ なので
ピクセル÷解像度×25.4 でミリメートルに変換できます。

用紙左上から(x=20mm, y=40mm)の位置を印刷不可範囲を考慮してピクセルに換算するには
(20mm÷25.4×600dpi-120px, 40mm÷25.4×600dpi-120px) = (352px, 825px)
となります。

プリンタ・用紙サイズ一覧取得と設定、用紙情報取得アプリケーションを作成

プロジェクトの作成と画面設計

Delphiを起動し、メニューから「ファイル」⇒「新規作成」⇒ 「Windows VCLアプリケーション -Delphi(W)」をクリックしてプロジェクトを作成します。
「TListBox」×2個、「TRadioGroup」×1個、「TMemo」×1個をフォームにドラッグ&ドロップします。

ソースコードの記述とプロパティ設定

F12を押して「コード」モードに切り替えて、以下ソースコードをコピー&ペースとします。
F12を押して「デザイン」モードに切り替えて、左下ペイン「オブジェクト インスペクタ」を「イベント」タブに切り替えて
Form1.OnCreate イベントプロパティ に FormCreate を設定します。
ListBox1.OnClick イベントプロパティ に ListBox1Click を設定します。
ListBox2.OnClick イベントプロパティ に ListBox2Click を設定します。
RadioGroup1Click イベントプロパティ に RadioGroup1Click を設定します。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    ListBox2: TListBox;
    RadioGroup1: TRadioGroup;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure ListBox2Click(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
  private
    { Private 宣言 }
    //現在のプリンタの現在の用紙情報を取得
    procedure GetPaperInfo();
    //現在のプリンタの用紙サイズ一覧を取得
    procedure GetPapersList();
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Vcl.Printers, WinApi.WinSpool;


procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
begin
  //プリンターの一覧を取得
  for i := 0 to Printer.Printers.Count-1 do
    ListBox1.AddItem(Printer.Printers[i],nil);
  ListBox1.ItemIndex:=Printer.PrinterIndex;
  //用紙方向
  RadioGroup1.Caption:='用紙方向';
  RadioGroup1.Items.Add('縦(Portrait)');
  RadioGroup1.Items.Add('横(Landscape)');
  RadioGroup1.ItemIndex:=0;
  //現在のプリンタの用紙サイズ一覧を取得
  GetPapersList();
  //現在のプリンタの現在の用紙情報を取得
  GetPaperInfo();
end;

//現在のプリンタの現在の用紙情報を取得
procedure TForm1.GetPaperInfo;
var Device, Driver, Port: array[0..255] of char;
    DeviceModeHandle: THandle;
    PMode : PDevMode;
begin
  Memo1.Clear;
  Memo1.Lines.Add('用紙の物理幅:'   + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALWIDTH))+'px');
  Memo1.Lines.Add('用紙の物理高さ:' + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT))+'px');
  Memo1.Lines.Add('左上印刷不可X:'  + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX))+'px');
  Memo1.Lines.Add('左上印刷不可Y:'  + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY))+'px');
  Memo1.Lines.Add('解像度X:'        + IntToStr(GetDeviceCaps(Printer.Handle, LOGPIXELSX))+'dpi');
  Memo1.Lines.Add('解像度Y:'        + IntToStr(GetDeviceCaps(Printer.Handle, LOGPIXELSY))+'dpi');
  Memo1.Lines.Add('印刷可能幅:'     + IntToStr(GetDeviceCaps(Printer.Handle, HORZRES))+'px');
  Memo1.Lines.Add('印刷可能高さ:'   + IntToStr(GetDeviceCaps(Printer.Handle, VERTRES))+'px');
  Memo1.Lines.Add('印刷可能幅:'     + IntToStr(Printer.PageWidth) +'px');
  Memo1.Lines.Add('印刷可能高さ:'   + IntToStr(Printer.PageHeight)+'px');
  if Printer.Orientation=TPrinterOrientation.poPortrait then
    Memo1.Lines.Add('用紙方向:縦')
  else
    Memo1.Lines.Add('用紙方向:横');
  //現在のプリンタのDeviceModeハンドルを取得
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
    Memo1.Lines.Add('用紙サイズ番号:'+IntToStr(PMode.dmPaperSize));
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
end;

//現在のプリンタの用紙サイズ一覧を取得
procedure TForm1.GetPapersList;
type
  TPaperName = array[0..63] of Char;            //静的配列はスタックメモリ
var Device, Driver, Port: array[0..255] of char;//静的配列はスタックメモリ
    DeviceModeHandle: THandle;
    PMode : PDevMode;
    PaperCount, i:Integer;
    PaperNo:Array of WORD;                      //動的配列はヒープメモリ
    PaperName:Array of TPaperName;              //動的配列はヒープメモリ
begin
  ListBox2.Clear;
  //現在のプリンタのDeviceModeハンドルを取得
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
    //第4引数が nil の場合は用紙サイズの数が返る
    PaperCount := DeviceCapabilities(Device, Port, DC_PAPERS, nil, PMode);
    if PaperCount>0 then
    begin
      SetLength(PaperNo,PaperCount);
      SetLength(PaperName,PaperCount);
      //用紙サイズ番号を取得
      DeviceCapabilities(Device, Port, DC_PAPERS, PChar(@PaperNo[0]), PMode);
      //用紙サイズ名を取得
      DeviceCapabilities(Device, Port, DC_PAPERNAMES, PChar(@PaperName[0]), PMode);
      for i := 0 to PaperCount-1 do
        ListBox2.AddItem(IntToStr(PaperNo[i])+'='+ PaperName[i], nil);
    end;
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
  if ListBox2.Items.Count>0 then
  begin
    ListBox2.ItemIndex:=
      ListBox2.Items.IndexOfName( IntToStr(PMode.dmPaperSize) );
  end;
end;

//ListBox1(プリンタ)クリック時
procedure TForm1.ListBox1Click(Sender: TObject);
begin
  Printer.PrinterIndex:=ListBox1.ItemIndex;
  //現在のプリンタの用紙サイズ一覧を取得
  GetPapersList;
  //現在のプリンタの現在の用紙情報を取得
  GetPaperInfo();
end;

//ListBox2(用紙サイズ)クリック時
procedure TForm1.ListBox2Click(Sender: TObject);
var Device, Driver, Port: array[0..255] of char;
    DeviceModeHandle: THandle;
    PMode : PDevMode;
begin
  //現在のプリンタのDeviceModeハンドルを取得
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
    //用紙サイズ変更フラグを設定
    pMode.dmFields := pMode.dmFields or DM_PAPERSIZE;
    //用紙サイズを変更
    pMode.dmPaperSize :=
      StrToInt( ListBox2.Items.Names[ListBox2.ItemIndex] );
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
  //プリンタに用紙を設定
  Printer.SetPrinter(Device,Driver,Port,DeviceModeHandle);
  //現在のプリンタの現在の用紙情報を取得
  GetPaperInfo();
end;

//RadioGroup1(用紙方向)クリック時
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if RadioGroup1.ItemIndex=0 then
    Printer.Orientation:=TPrinterOrientation.poPortrait
  else
    Printer.Orientation:=TPrinterOrientation.poLandscape;
  //現在のプリンタの現在の用紙情報を取得
  GetPaperInfo();
end;

end.

実行する

メニューから「実行」⇒「実行」をクリックするとコンパイルと実行が行われます。
ListBox1にプリンタの一覧が表示され、現在のプリンタが選択されます。
ListBox1に現在のプリンタの用紙サイズの一覧が表示され、現在の用紙サイズが選択されます。
Memo1に現在のプリンタの現在の用紙の情報が表示されます。