Recent

Author Topic: Printer Info  (Read 439 times)

J-G

  • Hero Member
  • *****
  • Posts: 1114
Printer Info
« on: June 09, 2026, 06:58:55 pm »
I'm very familiar with how to set up & execute a routine to print data created during a program run but in the past I've not needed to access the full details of the printer in use. I have used PrinterSetup and Printer Dialogues and can select a particular printer perfectly easily. This knowledge led me to think that there must be a simple way to gleen the information about the currently selected Printer - ie. Page Width, Height, DPI or even the 'Name'  but having gone arround in circles for the past few hours I'm stumped  :(

I CAN determine the page size within a Proc to actually 'Print' but that is 'after the event' as it were.

How can I dertemine the full printer details during a PrinterSetup ?  The 'Event' sets up a single call to Execute and adding code beyond this doesn't allow me to determine PaperSize.Height   -  ie.  The list of attributes that come up after Printer. does not include such - nor XDPI etc. which do appear at other times.

The use of .Name simply returnes 'Printer'; .Title returns 'PrinterDlg'

I have a number of printers available and of course they are all listed for selection in the Printer Setup display.

Is there a way to extract the name shown in that dialogue so that I can make it the caption to a label ?
 
I've already tried looking at OnClose and  OnShow Events in the PrinterSetup  but they don't even get access - ie. Break-Points do not get triggered  -  when the dialogue is shown or closed

??????
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

LeP

  • Sr. Member
  • ****
  • Posts: 451
Re: Printer Info
« Reply #1 on: June 09, 2026, 07:57:09 pm »
You must use the operating system API, in Windows you can use DeviceCapabilities:https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-devicecapabilitiesw

The DevMode structure result from API contain all details.

In Delphi there is a direct function call GetPrinter that return this record.
« Last Edit: June 09, 2026, 07:59:32 pm by LeP »
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

cdbc

  • Hero Member
  • *****
  • Posts: 2875
    • http://www.cdbc.dk
Re: Printer Info
« Reply #2 on: June 09, 2026, 09:37:47 pm »
Hi
Hmmm, yes - you definitely need to call the winapi to get a hold of the printerCAPS, there's a call that returns a device-caps record for the provided printer-index and AFAIR the default printer is index 0...
Try having a sniff around in the fpc printer units and see what API calls they use... Maybe you're lucky, they're grouped together in the windows unit...
If you search google it'll turn up something like this:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   Windows, Printers, Win32Printers; // Vital for Windows printing subsystems
  10.  
  11. type
  12.   TForm1 = class(TForm)
  13.     BtnGetCaps: TButton;
  14.     MemoLog: TMemo;
  15.     procedure BtnGetCapsClick(Sender: TObject);
  16.   private
  17.   public
  18.   end;
  19.  
  20. var
  21.   Form1: TForm1;
  22.  
  23. implementation
  24.  
  25. {$R *.lfm}
  26.  
  27. procedure TForm1.BtnGetCapsClick(Sender: TObject);
  28. var
  29.   PrnHandle: HDC;
  30.   PageWidth, PageHeight: Integer;
  31.   PrintWidth, PrintHeight: Integer;
  32.   OffsetX, OffsetY: Integer;
  33.   DpiX, DpiY: Integer;
  34. begin
  35.   MemoLog.Clear;
  36.  
  37.   // Verify that at least one printer is installed on Windows 7
  38.   if Printer.Printers.Count = 0 then
  39.   begin
  40.     MemoLog.Lines.Add('No printers found on this system.');
  41.     Exit;
  42.   end;
  43.  
  44.   try
  45.     MemoLog.Lines.Add('Target Printer: ' + Printer.Printers[Printer.PrinterIndex]);
  46.  
  47.     {
  48.       We invoke Refresh to ensure a valid device context handle (Handle)
  49.       is created internally by the Lazarus Win32 printing subsystem.
  50.     }
  51.     Printer.Refresh;
  52.     PrnHandle := Printer.Handle;
  53.  
  54.     if PrnHandle = 0 then
  55.     begin
  56.       MemoLog.Lines.Add('Failed to retrieve Printer Device Context (DC) Handle.');
  57.       Exit;
  58.     end;
  59.  
  60.     // 1. Fetch Dots Per Inch (DPI)
  61.     DpiX := GetDeviceCaps(PrnHandle, LOGPIXELSX);
  62.     DpiY := GetDeviceCaps(PrnHandle, LOGPIXELSY);
  63.  
  64.     // 2. Fetch overall physical sheet dimensions (including unprintable margins)
  65.     PageWidth := GetDeviceCaps(PrnHandle, PHYSICALWIDTH);
  66.     PageHeight := GetDeviceCaps(PrnHandle, PHYSICALHEIGHT);
  67.  
  68.     // 3. Fetch exact printable area sizes
  69.     PrintWidth := GetDeviceCaps(PrnHandle, HORZRES);
  70.     PrintHeight := GetDeviceCaps(PrnHandle, VERTRES);
  71.  
  72.     // 4. Fetch the unprintable hardware margins (Offsets)
  73.     OffsetX := GetDeviceCaps(PrnHandle, PHYSICALOFFSETX);
  74.     OffsetY := GetDeviceCaps(PrnHandle, PHYSICALOFFSETY);
  75.  
  76.     // Format output to UI
  77.     MemoLog.Lines.Add(Format('Resolution: %d x %d DPI', [DpiX, DpiY]));
  78.     MemoLog.Lines.Add(Format('Physical Paper Size: %d x %d pixels', [PageWidth, PageHeight]));
  79.     MemoLog.Lines.Add(Format('Printable Area Size: %d x %d pixels', [PrintWidth, PrintHeight]));
  80.     MemoLog.Lines.Add(Format('Hardware Margins (Left/Top): %d x %d pixels', [OffsetX, OffsetY]));
  81.    
  82.     // Convert to millimeters for human-readable outputs
  83.     MemoLog.Lines.Add(Format('Physical Size (mm): %.1f x %.1f mm',
  84.       [(PageWidth / DpiX) * 25.4, (PageHeight / DpiY) * 25.4]));
  85.  
  86.   except
  87.     on E: Exception do
  88.       MemoLog.Lines.Add('Error retrieving capabilities: ' + E.Message);
  89.   end;
  90. end;
  91.  
  92. end.
  93.  
<untested code>
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

J-G

  • Hero Member
  • *****
  • Posts: 1114
Re: Printer Info
« Reply #3 on: June 09, 2026, 10:24:53 pm »
Well - Thanks to @Lep & @benny for their response - regrettably I have no knowledge of C (any flavour!) so could make little to no sense of the information about 'Device Capabilities' from the Microsoft site.

I have found a way to determine the page size etc. both at program initialisation and whenever PrinterSetup is called. This at least gives me the means to display the page size on a TLabel.

The only thing missing is the printer Name.

I'd already created code to determine what seems to be returned by the Unit @Benny provided - but in much less space  :o   viz:
Code: Pascal  [Select][+][-]
  1. procedure FindPageSize;
  2. begin
  3.   prPgHeight := Printer.PaperSize.Height;      // A4 = 7017   A3 = 9925
  4.   prPgWidth  := Printer.PaperSize.Width;       // A4 = 4958   A3 = 7017
  5.   XDPM       := Printer.XDPI / 25.4;            
  6.   YDPM       := Printer.YDPI / 25.4;              
  7.                                                                  
  8.   if (prPgWidth / XDPM) < 150 then
  9.     TriangleSolution.PageSize.Caption:='A5'
  10.   else
  11.     if (prPgWidth / XDPM) < 215 then
  12.       TriangleSolution.PageSize.Caption:='A4'
  13.     else
  14.       TriangleSolution.PageSize.Caption:='A3';
  15. end;
  16.  

This proc is called at the end of FormCreate which returns the default state but I can also do so upon returning from the PrinterSetup and it does reset the PageSize.Caption.

I was not aware of the Win32Printers Unit which may well be the key to getting the printer 'Name' but that is a cosmetic requirement which I may well forego for the time being.

It may well be that I need to address the paper orientation - I've currently assumed Portrait  -  but that will be for another day  ;D
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

LeP

  • Sr. Member
  • ****
  • Posts: 451
Re: Printer Info
« Reply #4 on: June 09, 2026, 10:31:26 pm »
Code: Pascal  [Select][+][-]
  1.     // 1. Fetch Dots Per Inch (DPI)
  2.     DpiX := GetDeviceCaps(PrnHandle, LOGPIXELSX);
  3.     DpiY := GetDeviceCaps(PrnHandle, LOGPIXELSY);
  4.  
Regards Benny

Uhmm, according the docs, the DPI is in:

Code: Pascal  [Select][+][-]
  1.  dmFields->dmPrintQuality

while
 
Quote
dmLogPixels

The number of pixels per logical inch. Printer drivers do not use this member.

So, I think that OP should do some tests.
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

LeP

  • Sr. Member
  • ****
  • Posts: 451
Re: Printer Info
« Reply #5 on: June 09, 2026, 10:44:09 pm »
If you want, try to view LazReport, file "lr_prntr.pas" in:

....\lazarus\components\lazreport\source

Inside there are all informations that you need.
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

rvk

  • Hero Member
  • *****
  • Posts: 7063
Re: Printer Info
« Reply #6 on: June 09, 2026, 10:45:11 pm »
Uhmm, according the docs, the DPI is in:

Code: Pascal  [Select][+][-]
  1.  dmFields->dmPrintQuality

while
 
Quote
dmLogPixels

The number of pixels per logical inch. Printer drivers do not use this member.
No. dmFields->dmPrintQuality contains one of these values.
DMRES_HIGH, DMRES_MEDIUM, DMRES_LOW, DMRES_DRAFT
It does not contain the actual DPI.
Besides that, there is always a DPI for the X-axis and one other for the Y-axis.

Josh

  • Hero Member
  • *****
  • Posts: 1460
Re: Printer Info
« Reply #7 on: June 10, 2026, 12:09:43 am »
hi
using printers and printer4lazarus you can get access to these relatively easily, not sure if it helps u though.
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

J-G

  • Hero Member
  • *****
  • Posts: 1114
Re: Printer Info
« Reply #8 on: June 10, 2026, 01:04:36 am »
If you want, try to view LazReport, file "lr_prntr.pas" in:

....\lazarus\components\lazreport\source

Inside there are all informations that you need.

That is certainly an interesting Unit -  as far as handling the multitude of Paper Sizes/Names is concerned,  but there is only one reference to 'PrinterName' (not Printer.Name) and that is in a Debug Line;  so I fail to see how that would help in finding the selected printer 'Name'. Also, of the 118 paper size Names enumerated, only 4 are of any real interest in my real world  -  though in a past life I have indeed referenced quite a few that are not in the list (Elephant, Emperor, Imperial, Royal, Crown etc.)

I have done many 'tests' and I am well familiar with the fact that some printer drivers do allow the user to select various print qualities, not just by High/Medium etc. but specifically by DPI  - it does seem somewhat incongruous, living in a world which predominently uses the Metric Sytem of paper sizes (A series) that printer manufacturers still stick to DPI (inch) rather than DPM (mm/cm) - My 'Default' Printer is CutePDF and that allows me to select from 8 different DPI (72 - 4000).  My tests confirm that the figures reported from my FindPageSize proc are in fact accurate.

I've just done further tests using a Brother Printer which allows a selection of 300, 600 or 1200 DPI and can confirm that the DPM figures are exactly as expected.



FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

LeP

  • Sr. Member
  • ****
  • Posts: 451
Re: Printer Info
« Reply #9 on: June 10, 2026, 01:09:49 am »
SRY ... I don't see previous post. Mat be it's not more useful

No. dmFields->dmPrintQuality contains one of these values.
DMRES_HIGH, DMRES_MEDIUM, DMRES_LOW, DMRES_DRAFT
It does not contain the actual DPI.
Besides that, there is always a DPI for the X-axis and one other for the Y-axis.

In my code "dmPrintQuality" report the DPI in X axis, and "dmYResolution" report Y resolution, but they are not subfields of dmFields: I was wrong about dmFields ...

Delphi expose the the DeviceMode (in Windows unit) and fill it when you choose a Printer. The same structure is used in GDI (i.e for desktop).

In FPC that is exposed in struct.inc (under wininc).
This is basic code to get some info in Delphi ... but I'm sure that is similar in FPC
Code: Pascal  [Select][+][-]
  1. var
  2.   ADevice, ADriver, APort: string;
  3.   ADeviceMode: THandle;
  4.   PDevPrintMode: PDeviceMode;
  5. begin
  6. //First printer, Printer is global in Delphi
  7.   Printer.PrinterIndex := 0;
  8.   Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
  9.   PDevPrintMode := GlobalLock(ADeviceMode);
  10.   //Show if orientation is portrait
  11.   ShowMessage(BoolToStr(PDevMode^.dmOrientation = DMORIENT_PORTRAIT, true));
  12.   //Paper size (i.e. A4)
  13.   ShowMessage(PDevMode^.dmFormName);
  14.   //X DPI
  15.   ShowMessage(PDevMode^.dmPrintQuality.ToString);
  16.   //Y DPI
  17.   ShowMessage(PDevMode^.dmYResolution.ToString);
  18. end;
  19.  
« Last Edit: June 10, 2026, 01:12:11 am by LeP »
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

J-G

  • Hero Member
  • *****
  • Posts: 1114
Re: Printer Info
« Reply #10 on: June 10, 2026, 02:37:49 am »
Thanks for your input @LeP  -  for while I thought that you might have given me the 'Key' to reading the Printer details but regrettably under Lazarus, Printer does not have a member GetPrinter so I cannot see whether that routine would return the 'Name'.

You have given me the means by which I can 'cheat' though. The Printer.PriterIndex does return a number which I can use to look up against a hand created list of names.  I can manually use a Break-point to stop on the line knowing which printer I've [selected] - not ideal, since it wouldn't work on another system with different printers - or even on my system should I change or delete a device - but it will do for now.

« Last Edit: June 10, 2026, 02:47:03 am by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

rvk

  • Hero Member
  • *****
  • Posts: 7063
Re: Printer Info
« Reply #11 on: June 10, 2026, 02:50:05 am »
In my code "dmPrintQuality" report the DPI in X axis, and "dmYResolution" report Y resolution, but they are not subfields of dmFields: I was wrong about dmFields ...
Ah, I see. For negative values they are as I stated.
For positive values they can be the X DPI.

Do note this is heavily driver dependent and not guaranteed. The GetDeviceCaps might be a better choice. At least you would need to build in a check for negative values if you use dmPrintQuality.

https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodew

Quote
DUMMYUNIONNAME.DUMMYSTRUCTNAME.dmPrintQuality

For printers, specifies the printer resolution. The following negative constant values are defined in wingdi.h:

DMRES_HIGH
DMRES_MEDIUM
DMRES_LOW
DMRES_DRAFT
If a positive value is specified, it represents the number of dots per inch (DPI) for the x resolution, and the y resolution is specified by dmYResolution.

As far as I now, ALL the printers I've ever tested gave me the negative value for dmPrintQuality and not the X DPI. I've always used the GetDeviceCaps for resolution.
« Last Edit: June 10, 2026, 02:57:28 am by rvk »

LeP

  • Sr. Member
  • ****
  • Posts: 451
Re: Printer Info
« Reply #12 on: June 10, 2026, 09:18:08 am »
Thanks for your input @LeP  -  for while I thought that you might have given me the 'Key' to reading the Printer details but regrettably under Lazarus, Printer does not have a member GetPrinter so I cannot see whether that routine would return the 'Name'.

You have given me the means by which I can 'cheat' though. The Printer.PriterIndex does return a number which I can use to look up against a hand created list of names.  I can manually use a Break-point to stop on the line knowing which printer I've [selected] - not ideal, since it wouldn't work on another system with different printers - or even on my system should I change or delete a device - but it will do for now.

You can call "Printer.Printers" that return a TStrings:

Code: Pascal  [Select][+][-]
  1. var ListofPrinters: TStrings;
  2. begin
  3.   ListofPrinters := Printer.Printers;
  4.   //If you use a combobox you can ssign directly the results
  5.   ComboBox1.Items.Assign(Printer.Printers);
  6. end;
  7.  
  8. //You can assign directly a Printer from OnSelect event (of course non sort options should be selected in ComboBox):
  9. procedure ....ComboBox1.Select(Sender: TObject);
  10.   begin
  11.      If ComboBox1.ItemIndex >= 0 then
  12.        Printer.PrinterIndex := ComboBox1.ItemIndex;
  13.   end;
  14.  
  15. //BTW, look at "Printer" fields .... they offer already some settings you need:
  16.   Printer.XDPI;
  17.   Printer.YDPI;
  18. //These are different form Delphi object "Printer"
  19.  

I don't see the @Josh attach (previous post), but may be inside there you'll find something usefull.
« Last Edit: June 10, 2026, 09:26:32 am by LeP »
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

LeP

  • Sr. Member
  • ****
  • Posts: 451
Re: Printer Info
« Reply #13 on: June 10, 2026, 09:25:16 am »
Ah, I see. For negative values they are as I stated.
For positive values they can be the X DPI.
Do note this is heavily driver dependent and not guaranteed. The GetDeviceCaps might be a better choice. At least you would need to build in a check for negative values if you use dmPrintQuality.

You are right, of course you should check every conditions.
In Delphi, that is done directly from the class TPrinter so one have all things in the right place. But I really don't use that since years (decades too).
I always use FasteReport for printing, and never had any issue. Typically I don't set any option, except "portrait" sets.
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

J-G

  • Hero Member
  • *****
  • Posts: 1114
Re: Printer Info
« Reply #14 on: June 10, 2026, 11:26:52 am »
You can call "Printer.Printers" that return a TStrings:

Code: Pascal  [Select][+][-]
  1. var ListofPrinters: TStrings;
  2. begin
  3.   ComboBox1.Items.Assign(Printer.Printers);
  4. end;
  5.  
  6. //BTW, look at "Printer" fields .... they offer already some settings you need:
  7.   Printer.XDPI;
  8.   Printer.YDPI;
  9. //These are different form Delphi object "Printer"
  10.  

I don't see the @Josh attach (previous post), but may be inside there you'll find something usefull.

!! SORTED !!

Less than 5 minutes work to add a ComboBox and re-compile and I do have a complete list of all Printers with their 'given' names which can be selected by reference to the PrinterIndex.  Fantastic!

My simple mind looked for [Name], [Title] or some such member of Printers - it never occurred to me to look for 'Printers%)

Thanks @LeP -  I had already found those attributes - (XDPI,YDPI) and am using them to determine the Page size set by the PrinterSetUp Dialogue.

I also haven't looked at the Printers4Lazarus suggested by @Josh and since your suggestion has proved effective and elegant I probably won't bother - but I am grateful for his input - as I am to all who contributed.


« Last Edit: June 10, 2026, 11:35:18 am by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

 

TinyPortal © 2005-2018