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

How to Retrieve Printer Information in Delphi: Using DeviceCapabilities, LOGPIXELSX, and Related Techniques

Japanese

How to Retrieve Printer Information in Delphi: Using DeviceCapabilities, LOGPIXELSX, and Related Techniques

When controlling printers in Delphi, you often need to retrieve detailed information such as paper size, resolution, and printable area. To do this, you use Windows API functions like DeviceCapabilities and GetDeviceCaps.
This page explains how to obtain values such as LOGPIXELSX, LOGPIXELSY, PHYSICALWIDTH, and PHYSICALOFFSETX, with practical code examples.
It also covers useful techniques for improving printing accuracy, including pixel conversion for A3 paper size and how to specify DMPAPER constants.

For information on printing with TPrinter in Delphi, see: Printing in Delphi.

To work with printers, add the following units to uses:
uses Vcl.Printers, WinApi.WinSpool;

Retrieving Printer Lists and Selecting a Printer

Use the following properties and methods to retrieve the list of printers and select the printer to use.

Value to RetrieveHow to Retrieve
Number of installed printers Printer.Printers.Count
List of printer names Printer.Printers[ index ]
Index ranges from 0 to Printer.Printers.Count - 1
Selecting the active printer Printer.PrinterIndex := index;
Index ranges from 0 to Printer.Printers.Count - 1

Paper Size List and Paper Size Settings

Use the following methods to retrieve the list of available paper sizes for the current printer and to set the desired paper size.

Value to RetrieveHow to Retrieve
Paper Size List With the following source code:
The dynamic array PaperNo[0 ~ PaperCount‑1] will contain the paper size numbers,
and the dynamic array PaperName[0 ~ PaperCount‑1] will contain the paper size names (such as A4, A3).
The displayed names may differ depending on the currently selected printer—some drivers return English names, others Japanese.
This behavior likely depends on each printer driver.
type
  TPaperName = array[0..63] of Char;            // Static array (stack memory)
var
  Device, Driver, Port: array[0..255] of Char;  // Static arrays (stack memory)
  DeviceModeHandle: THandle;
  PMode: PDevMode;
  PaperCount, i: Integer;
  PaperNo: Array of WORD;                     // Dynamic array (heap memory)
  PaperName: Array of TPaperName;             // Dynamic array (heap memory)
begin
  // Get the DeviceMode handle of the current printer
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then Exit;

  // Get a pointer to the DevMode structure from the DeviceMode handle
  PMode := GlobalLock(DeviceModeHandle);
  try
    // When the 4th argument is nil, the number of paper sizes is returned
    PaperCount := DeviceCapabilities(Device, Port, DC_PAPERS, nil, PMode);
    if PaperCount > 0 then
    begin
      SetLength(PaperNo, PaperCount);
      SetLength(PaperName, PaperCount);

      // Retrieve paper size numbers
      DeviceCapabilities(Device, Port, DC_PAPERS, PChar(@PaperNo[0]), PMode);

      // Retrieve paper size names
      DeviceCapabilities(Device, Port, DC_PAPERNAMES, PChar(@PaperName[0]), PMode);
    end;
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
end;
Retrieving the Paper Size Number
var
  Device, Driver, Port: array[0..255] of Char;
  DeviceModeHandle: THandle;
  PMode: PDevMode;
begin
  // Get the DeviceMode handle of the current printer
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then Exit;

  // Get a pointer to the DevMode structure from the DeviceMode handle
  PMode := GlobalLock(DeviceModeHandle);
  try
    ShowMessage('Paper size number: ' + IntToStr(PMode.dmPaperSize));
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
end;
Setting the Paper Size With the following source code:
you can set the paper size by assigning, for example:
pMode.dmPaperSize := DMPAPER_B4;
Paper size numbers are defined as constants.
For example, DMPAPER_B4 = 13 (paper size number 13), DMPAPER_A4 = 9 (paper size number 9).
var
  Device, Driver, Port: array[0..255] of Char;  // Static arrays (stack memory)
  DeviceModeHandle: THandle;
  PMode: PDevMode;
begin
  // Get the DeviceMode handle of the current printer
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then Exit;

  // Get a pointer to the DevMode structure from the DeviceMode handle
  PMode := GlobalLock(DeviceModeHandle);
  try
    // Enable the paper size change flag
    PMode.dmFields := PMode.dmFields or DM_PAPERSIZE;

    // Change the paper size
    PMode.dmPaperSize := DMPAPER_A3;
  finally
    GlobalUnlock(DeviceModeHandle);
  end;

  // Apply the paper size setting to the printer
  Printer.SetPrinter(Device, Driver, Port, DeviceModeHandle);
end;

Retrieving Current Paper Information from the Active Printer

You can obtain the current paper settings of the active printer using the following values.

Value to RetrieveHow to Retrieve
Physical paper width (px) GetDeviceCaps(Printer.Handle, PHYSICALWIDTH)
Physical paper height (px) GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT)
Non-printable left margin X (px) GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX)
Non-printable top margin Y (px) GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY)
Resolution X (dpi) GetDeviceCaps(Printer.Handle, LOGPIXELSX)
Resolution Y (dpi) GetDeviceCaps(Printer.Handle, LOGPIXELSY)
Printable width (px) GetDeviceCaps(Printer.Handle, HORZRES)
or
Printer.PageWidth
Printable height (px) GetDeviceCaps(Printer.Handle, VERTRES)
or
Printer.PageHeight
Paper orientation Printer.Orientation
One of the following values:
  • TPrinterOrientation.poPortrait (Portrait)
  • TPrinterOrientation.poLandscape (Landscape)
You can also set the orientation using:
Printer.Orientation := TPrinterOrientation.poLandscape;

The relationship between the values above and the actual paper layout is illustrated below.

Physical Paper Height (px)

Physical Paper Width (px)

↓ Non‑printable upper‑left area (X px, Y px)
Origin
Printable Width (px)

Printable Height (px)

All units are in pixels (px).

For example, when converting the following values to millimeters (mm):

Since 1 inch = 25.4 mm and dpi means “dots (pixels) per inch,”
you can convert pixels to millimeters using:
pixels ÷ resolution × 25.4

To convert a position measured from the upper‑left corner of the paper—e.g., (x = 20 mm, y = 40 mm)—into pixels while accounting for the non‑printable area:
(20 mm ÷ 25.4 × 600 dpi − 120 px, 40 mm ÷ 25.4 × 600 dpi − 120 px) = (352 px, 825 px)

Creating an Application to Retrieve and Configure Printer and Paper Information

Creating the Project and Designing the UI

Launch Delphi and select “File” → “New” → “Windows VCL Application – Delphi (W)” to create a new project.
Drag and drop the following components onto the form:
TListBox × 2, TRadioGroup × 1, TMemo × 1.

Writing the Source Code and Setting Properties

Press F12 to switch to Code mode, then copy and paste the source code shown below.
Press F12 again to return to Design mode, then open the Object Inspector and switch to the Events tab.
Assign the following event handlers:
Form1.OnCreateFormCreate
ListBox1.OnClickListBox1Click
ListBox2.OnClickListBox2Click
RadioGroup1.OnClickRadioGroup1Click

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 宣言 }
    //Retrieve current paper information from the active printer
    procedure GetPaperInfo();
    //Retrieve the list of available paper sizes for the active printer
    procedure GetPapersList();
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Vcl.Printers, WinApi.WinSpool;

// Form initialization
procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
begin
  //Retrieve the list of installed printers
  for i := 0 to Printer.Printers.Count-1 do
    ListBox1.AddItem(Printer.Printers[i],nil);
  ListBox1.ItemIndex:=Printer.PrinterIndex;
  //Paper orientation
  RadioGroup1.Caption:='Paper orientation';
  RadioGroup1.Items.Add('Portrait');
  RadioGroup1.Items.Add('Landscape');
  RadioGroup1.ItemIndex:=0;
  //Retrieve paper size list for the current printer
  GetPapersList();
  //Retrieve current paper information
  GetPaperInfo();
end;

//Retrieve current paper information from the active printer
procedure TForm1.GetPaperInfo;
var Device, Driver, Port: array[0..255] of char;
    DeviceModeHandle: THandle;
    PMode : PDevMode;
begin
  Memo1.Clear;
  Memo1.Lines.Add('Physical Width:'   + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALWIDTH))+'px');
  Memo1.Lines.Add('Physical Height:' + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT))+'px');
  Memo1.Lines.Add('Non-printable X:'  + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX))+'px');
  Memo1.Lines.Add('Non-printable Y:'  + IntToStr(GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY))+'px');
  Memo1.Lines.Add('Resolution X:'        + IntToStr(GetDeviceCaps(Printer.Handle, LOGPIXELSX))+'dpi');
  Memo1.Lines.Add('Resolution Y:'        + IntToStr(GetDeviceCaps(Printer.Handle, LOGPIXELSY))+'dpi');
  Memo1.Lines.Add('Printable Width:'     + IntToStr(GetDeviceCaps(Printer.Handle, HORZRES))+'px');
  Memo1.Lines.Add('Printable Height:'   + IntToStr(GetDeviceCaps(Printer.Handle, VERTRES))+'px');
  Memo1.Lines.Add('Printable Width:'     + IntToStr(Printer.PageWidth) +'px');
  Memo1.Lines.Add('Printable Height:'   + IntToStr(Printer.PageHeight)+'px');
  if Printer.Orientation=TPrinterOrientation.poPortrait then
    Memo1.Lines.Add('Orientation: Portrait')
  else
    Memo1.Lines.Add('Orientation: Landscape');
  //Retrieve DeviceMode handle
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //Retrieve pointer to DevMode structure
  PMode := GlobalLock(DeviceModeHandle);
  try
    Memo1.Lines.Add('Paper Size Number:'+IntToStr(PMode.dmPaperSize));
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
end;

//Retrieve the list of available paper sizes for the active printer
procedure TForm1.GetPapersList;
type
  TPaperName = array[0..63] of Char;            //static array (stack memory)
var Device, Driver, Port: array[0..255] of char;//static array (stack memory)
    DeviceModeHandle: THandle;
    PMode : PDevMode;
    PaperCount, i:Integer;
    PaperNo:Array of WORD;                      //dynamic array (heap memory)
    PaperName:Array of TPaperName;              //dynamic array (heap memory)
begin
  ListBox2.Clear;
  //Retrieve DeviceMode handle
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //Retrieve pointer to DevMode structure
  PMode := GlobalLock(DeviceModeHandle);
  try
    //If 4th argument is nil, returns number of paper sizes
    PaperCount := DeviceCapabilities(Device, Port, DC_PAPERS, nil, PMode);
    if PaperCount>0 then
    begin
      SetLength(PaperNo,PaperCount);
      SetLength(PaperName,PaperCount);
      //Retrieve paper size numbers
      DeviceCapabilities(Device, Port, DC_PAPERS, PChar(@PaperNo[0]), PMode);
      //Retrieve paper size names
      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;

//When ListBox1 (printers) is clicked
procedure TForm1.ListBox1Click(Sender: TObject);
begin
  Printer.PrinterIndex:=ListBox1.ItemIndex;
  //Refresh paper size list
  GetPapersList;
  //Refresh current paper info
  GetPaperInfo();
end;

//When ListBox2 (paper sizes) is clicked
procedure TForm1.ListBox2Click(Sender: TObject);
var Device, Driver, Port: array[0..255] of char;
    DeviceModeHandle: THandle;
    PMode : PDevMode;
begin
  //Retrieve DeviceMode handle
  Printer.GetPrinter(Device, Driver, Port, DeviceModeHandle);
  if DeviceModeHandle = 0 then exit;
  //DeviceModeハンドルからDevMode構造体のポインタを取得
  PMode := GlobalLock(DeviceModeHandle);
  try
    //Enable paper size change flag
    pMode.dmFields := pMode.dmFields or DM_PAPERSIZE;
    //Change paper size
    pMode.dmPaperSize :=
      StrToInt( ListBox2.Items.Names[ListBox2.ItemIndex] );
  finally
    GlobalUnlock(DeviceModeHandle);
  end;
  //Apply paper size to printer
  Printer.SetPrinter(Device,Driver,Port,DeviceModeHandle);
  //Refresh current paper info
  GetPaperInfo();
end;

//When RadioGroup1 (orientation) is clicked
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if RadioGroup1.ItemIndex=0 then
    Printer.Orientation:=TPrinterOrientation.poPortrait
  else
    Printer.Orientation:=TPrinterOrientation.poLandscape;
  //Refresh current paper info
  GetPaperInfo();
end;

end.

Running the Application

Select "Run" -> "Run" from the menu to compile and execute the application.
The list of installed printers will appear in ListBox1, with the current printer automatically selected.
ListBox2 will display the list of available paper sizes for the selected printer, and the currently active paper size will be highlighted.
Memo1 will show detailed information about the current printer’s active paper settings.