Delphiでプリンタ情報を取得する方法|DeviceCapabilities・LOGPIXELSXなどの活用例
Delphiでプリンタ制御を行う際、用紙サイズや解像度、印刷可能範囲などの詳細な情報を取得するには、Windows APIのDeviceCapabilitiesやGetDeviceCaps関数を活用する必要があります。
本ページでは、LOGPIXELSX・LOGPIXELSY・PHYSICALWIDTH・PHYSICALOFFSETXなどの取得方法を、実際のコード例とともに詳しく解説。
A3サイズのピクセル換算や、DMPAPERサイズの指定方法など、印刷精度を高めるための実践的なノウハウを紹介します。
DelphiでTPrinterを使って印刷する解説は「Delphiで印刷」を参照してください。
プリンタを扱うには 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 以下のどちらかの値が入っている
として用紙の方向を設定することも可能。 |
上記の値と用紙との関係は以下です。
用紙の物理幅(px)
印刷可能高さ(px)
単位は全て px です。
例えば以下をミリメートル(mm)に変換する場合
- 用紙の物理幅 = 4960px
- 用紙の物理高さ = 7014px
- 左上印刷不可X = 120px
- 左上印刷不可Y = 120px
- 印刷可能幅 = 4720px
- 印刷可能高さ = 6774px
- 右下印刷不可X = 4960-120-4720 = 120px
- 右下印刷不可Y = 7014-120-6774 = 120px
- 解像度X = 600dpi
- 解像度Y = 600dpi
1インチ = 25.4ミリメートル(mm)、単位dpi = ドット(ピクセル)/インチ なので
ピクセル÷解像度×25.4 でミリメートルに変換できます。
- 用紙の物理幅 = 4960px ÷ 600dpi × 25.4 = 209.97mm
- 用紙の物理高さ = 7014px ÷ 600dpi × 25.4 = 296.93mm
- 左上印刷不可X = 120px ÷ 600dpi × 25.4 = 5.08mm
- 左上印刷不可Y = 120px ÷ 600dpi × 25.4 = 5.08mm
- 印刷可能幅 = 4720px ÷ 600dpi × 25.4 = 199.81mm
- 印刷可能高さ = 6774px ÷ 600dpi × 25.4 = 286.77mm
- 右下印刷不可X = 120px ÷ 600dpi × 25.4 = 5.08mm
- 右下印刷不可Y = 120px ÷ 600dpi × 25.4 = 5.08mm
用紙左上から(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に現在のプリンタの現在の用紙の情報が表示されます。
