* * *

Author Topic: [SOLVED] Setting printer properties in PrintDialog  (Read 1739 times)

wp

  • Hero Member
  • *****
  • Posts: 4602
Re: Setting printer properties in PrintDialog
« Reply #15 on: June 01, 2018, 10:44:34 am »
Am I a fool if I think TPrinter must have some other properties like "ColorMode" or "DuplexMode"?
To answer your question literally: Yes, but no offense.  ;)

I think the philosophy is that TPrinter supports only the minimum features which all (or most?) printers provide, such as page orientation, or paper size. Many models, however, have more features, like duplex mode. But the print dialogs are taken from the OS, they are no Lazarus forms, Lazarus has no possibility to change them (well, some hacker methods certainly exist). In principle, access to the additional features in the print dialogs can be made via OS-specific calls, but Lazarus does not provide this in TPrinter because it must be platform-independent and nobody, so far, has taken the burden to re-write TPrinter using a widgetset technology like in the LCL.

If you want the print dialogs to show only those options supported by your program you must write your own dialogs. Apply the parameters supported by TPrinter to TPrinter directly. Accomplish other features in your application - I am thinking of color/grayscale mode here: if the user wants a grayscale print-out of an image by checking the grayscale box in the print dialog, then convert the bitmap to grayscale before sending it to the printer- you now can read the grayscale option from the dialog because it is your own form, not that of the OS.

If you want features which can be accomplished by hardware only (such as duplex print) you return to the main problem again: How do you know that the user's printer can operate in duplex? Since Lazarus does not support platform-independent access to all properties or the printer driver you will have a hard life in squeezing everything out of the printer in the most general way.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

RayoGlauco

  • Jr. Member
  • **
  • Posts: 57
Re: Setting printer properties in PrintDialog
« Reply #16 on: June 01, 2018, 11:04:34 am »
Thanks, wp.
Now I am more clear about what to do.
I will design my own PrintDialog, including only the options that I can really handle.
« Last Edit: June 01, 2018, 11:07:54 am by RayoGlauco »
To err is human, but to really mess things up, you need a computer.

jamie

  • Hero Member
  • *****
  • Posts: 760
Re: Setting printer properties in PrintDialog
« Reply #17 on: June 01, 2018, 08:03:08 pm »
@wp: that is Delphi code however, The GetPrinter function can be adopted from the API call of GetPrinter


 I do have a couple of apps in laz that uses the printer and I did do something with some API code to get things
working...


 I'll take a look to see what it was I did...


jamie

  • Hero Member
  • *****
  • Posts: 760
Re: Setting printer properties in PrintDialog
« Reply #18 on: June 01, 2018, 09:27:50 pm »
Ok,  I have had a look at an app we use at work that I converted from Delphi over laz..

I use the "DocumentProperties" along with info from the Printers Unit.

I do not know why "Printers" does not support the SetPrinter and GetPrinter as did/does Delphi. On top of all
that, the DocumentProperties API is not defined in the windows unit but the records are? Which is most likely the
reason I had to an Define of it in my app..

 For now you can use the DocumentProperties function, follow the guide lines laid out via MS.

 There are many examples out there to do this with Lazarus and many posted examples of using this API with Delphi which
should compile.

wp

  • Hero Member
  • *****
  • Posts: 4602
Re: Setting printer properties in PrintDialog
« Reply #19 on: June 01, 2018, 10:12:49 pm »
You want to say that the DocumentProperties function (which is declared in unit WinUtilPrn) is the way to access details on the print dialogs? As usual, the MSN  page on DocumentProperties (https://msdn.microsoft.com/en-us/library/windows/desktop/dd183576(v=vs.85).aspx) is hard reading...
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

jamie

  • Hero Member
  • *****
  • Posts: 760
Re: Setting printer properties in PrintDialog
« Reply #20 on: June 02, 2018, 12:41:09 am »
The DocumentProperties can but does not need to, present a dialog to the user.

Its in the parameters.

 fMode parameter indicates what to do..

  DM_IN_PROMPT will present a setup dialog to the user.
 
  DM_IN_BUFFER will read the current printers DEVMODE record which contains all of the standard settings, plus duplex mode

  DM_OUT_BUFFER will output this Record back to the print driver

  If you set this parameter to 0 then what happens is the function will return the size of memory needed to
contain a DEVMODE record.

  This is needed first to make sure you have enough space because printer venders will add to the end.

 So first call is 0 for this fMode.

 So after the initial call to determine needed space,  you then call it again with DM_IN_BUFFER and pass it the pointer
of the buffer to the function.
  This will fill the DEVMODE record

Personally, I think the TPrinter Class should provide a Property that you can read and write this DEVMODE and if for some
reason other targets can't provide this, have it return a blank or fill in as much of it as possible.

EDIT:

 Forgot to mention , the pDevModeInput will point to the incoming DEVMODE and the pDevModeOutput will be another
DEVMODE that is modified and gets sent to the printer when you do the DM_OUT....

 so you simply move a copy of the incoming over to the out-going and make your changes to the out going..

« Last Edit: June 02, 2018, 12:47:51 am by jamie »

jamie

  • Hero Member
  • *****
  • Posts: 760
Re: Setting printer properties in PrintDialog
« Reply #21 on: June 02, 2018, 02:55:49 am »
Something I came up with as an example...
I don't have a printer at this location where I can test the duplex mode.

Code: Pascal  [Select]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. Var
  3.   pDev:pDevmodeW; // Made a boo boo bere, needed to use the (W) version..
  4.   DevSize :integer;
  5.   PrnH:Thandle;
  6.   NS :WideString;
  7. begin
  8.    NS := Printer.PrinterName+#0#0;
  9.  If  OpenPrinterW(@NS[1],@PrnH,nil) Then
  10.    Begin
  11.     DevSize := DocumentPropertiesW(Handle,PrnH,@NS[1],nil,nil,0);
  12.     GetMem(pDEV,DevSize);
  13.     DocumentPropertiesW(Handle, PrnH,@NS[1],LPDEVMODEW(pDev), nil, DM_OUT_BUFFER);
  14.     If pDev^.dmFields And DM_DUPLEX <> 0 Then   //if it supports it, try a Duplex change.
  15.       Begin
  16.        pDev^.dmFields := DM_DUPLEX;
  17.        pDev^.dmDuplex := DMDUP_HORIZONTAL;
  18.      DocumentPropertiesW(Handle,PrnH,@NS[1],LPDEVMODEW(pDEV),LPDEVMODEw(pDEV),DM_IN_BUFFER or DM_OUT_BUFFER);
  19.      //If getLastError <> 0 Then it failed!
  20.       end;
  21.     ClosePrinter(PrnH);
  22.     FreeMem(pDev);
  23.    End;
  24. end;                          
  25.  

This gives example of how to Read/Write the DEVMODE of the printer.

P.S.
 Thanks to WP for informing me of the WinUtilPRN unit, it would have saved me some work a few months ago :)

 And of course you must include that along with the Printer unit..

EDIT2:
  I used the wrong DEVMODE, it should be DEVMODEW , sorry...


« Last Edit: June 02, 2018, 03:53:30 am by jamie »

RayoGlauco

  • Jr. Member
  • **
  • Posts: 57
Re: Setting printer properties in PrintDialog
« Reply #22 on: June 05, 2018, 01:14:43 pm »
Hello,

I wrote some code to get and set some properties of the printer. I get successfully the current values, but when I call DocumentPropertiesW to change a value, I get no error, but no change is done.

Code: Pascal  [Select]
  1. // Set new value
  2. if DocumentPropertiesW(0, hPrinter, PWideChar(PrinterNameW), pDM, pDM, DM_IN_BUFFER or DM_OUT_BUFFER) < 0 then
  3.     error_message;
  4. if getLastError <>0 then RaiseLastOSError;
  5. // Check current value
  6. if DocumentPropertiesW(0, hPrinter, PWideChar(PrinterNameW), pDM, nil, DM_OUT_BUFFER) < 0 then
  7.     error_message;

This code runs without error, but no changes are done. I checked it, and pDM has the correct value that I want to set, before and after calling DocumentPropertiesW. But when I call again DocumentPropertiesW to chek if all is ok, I get the original value, not the new one. If I check the printer at the windows control panel, the value is not changed either.

Any idea of what and why is going wrong?
To err is human, but to really mess things up, you need a computer.

jamie

  • Hero Member
  • *****
  • Posts: 760
Re: Setting printer properties in PrintDialog
« Reply #23 on: June 05, 2018, 11:16:24 pm »
try the DM_VERTICAL instead..

Also, before calling DocumentProperties after you do a SET, check the DEVMODE record to see it overwrote original
record.
 
  Its possible you may need to split into two records, first read the record, make a copy of it and change the copy, return
the copy for the DM_IN_BUFFER..

 Also there is a possibility your print driver is resetting back to default when close it which can then present a problem here
because the PRINTER class has a handle of its own and it is not the same, usually.
  These go to the print spooler.

I will check later for this.. but please try two different DEVMODE records to make sure things are not getting overlapped.



jamie

  • Hero Member
  • *****
  • Posts: 760
Re: Setting printer properties in PrintDialog
« Reply #24 on: June 06, 2018, 01:25:03 am »
Apparently the DocumentProperties will set the dialog properties and also verifies if the property of the printer
you are attempting to set is a valid one... But it does not actually send it to the driver..

This code here sends it to the driver of the printer....
Code: Pascal  [Select]
  1. // need to import SetPrinter, it is not in the WinUtilPrn file ?
  2. Function SetPrinter(hPrinter:Handle;Level:DWORD;pPrinter:PByte;Command:DWord):LongBool; StdCall; External
  3.  libWinSpool name 'SetPrinterA';                                        
  4.  
  5. procedure TForm1.Button3Click(Sender: TObject);
  6. Var
  7.    pPrnInFo:pPRINTER_INFO_2;
  8.    pDev:PDevMode;
  9.    H:Handle;
  10.    N:WideString;
  11.    S:Integer;
  12. begin
  13.   N := Printer.PrinterName;
  14.   OpenPrinterW(@N[1],@H, nil);//Open printer;
  15.   GetPrinter(H,2,Nil,0,@S);   //Get Size needed for Info_2;
  16.   GetMem(pPrnInfo,S);
  17.   GetPrinter(H,2,PByte(pPRNInFo),S,@s);
  18.   pDev := pPRNInfo^.pDevMode;
  19.   if pDev^.dmFields and Dm_Orientation <> 0 Then
  20.    Begin
  21.     pDev^.dmOrientation := 2;
  22.    end;
  23.   SetPrinter(H,2,PByte(pPrnInFo),0);  //Need to import the function ? Hmmmmmm
  24.   freeMem(pPrnInfo);
  25.   ClosePrinter(H);
  26. end;                                    
  27.  

 In this example I used the PDF printer to see if I could change the orientation, it succeeded...

 you can change the Duplex mode using the same approach.

 You may want to add some extra error checking on each function return, I did not to shorten it up but it works on my end
for the basic printer. This PC has no printer on  >:(

RayoGlauco

  • Jr. Member
  • **
  • Posts: 57
Re: Setting printer properties in PrintDialog
« Reply #25 on: June 06, 2018, 11:03:30 am »
Thank you, jamie,

Finally it seems that all is working. I paste my code, in case someone finds it useful:

Code: Pascal  [Select]
  1. unit GetAndSetPrinterSettings;   // Only for Windows
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Printers;
  9.  
  10. function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;
  11. function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint;
  12.  
  13. implementation
  14.  
  15. uses
  16.   Classes, SysUtils, Windows, WinUtilPrn;
  17.  
  18. Function SetPrinter(hPrinter:Handle;Level:DWORD;pPrinter:PByte;Command:DWord):LongBool; StdCall; External
  19.  libWinSpool name 'SetPrinterA';
  20.  
  21. function GetDevModeField(pDM: PDEVMODE; Field: longint): longint;
  22. // Only for fields whose value type and range is integer >=0
  23. // Result >= 0 ==> succeeded
  24. // Result < 0  ==> failed
  25. begin
  26.   if (pDM^.dmFields and Field)=0 then
  27.     Result := -1   // Field not supported
  28.   else
  29.   case Field of
  30.     DM_ORIENTATION: Result := pDM^.dmOrientation;
  31.     DM_PAPERSIZE: Result := pDM^.dmPaperSize;
  32.     DM_PAPERLENGTH: Result := pDM^.dmPaperLength;
  33.     DM_PAPERWIDTH: Result := pDM^.dmPaperWidth;
  34.     DM_SCALE: Result := pDM^.dmScale;
  35.     //  DM_POSITION: Result:=pDM^.dmPosition;
  36.     DM_COPIES: Result := pDM^.dmCopies;
  37.     DM_DEFAULTSOURCE: Result := pDM^.dmDefaultSource;
  38.     DM_PRINTQUALITY: Result := pDM^.dmPrintQuality;
  39.     DM_COLOR: Result := pDM^.dmColor;
  40.     DM_DUPLEX: Result := pDM^.dmDuplex;
  41.     DM_YRESOLUTION: Result := pDM^.dmYResolution;
  42.     DM_TTOPTION: Result := pDM^.dmTTOption;
  43.     DM_COLLATE: Result := pDM^.dmCollate;
  44.     //  DM_FORMNAME: Result:=pDM^.dmFormName;
  45.     DM_LOGPIXELS: Result := pDM^.dmLogPixels;
  46.     DM_ICMMETHOD: Result := pDM^.dmICMMethod;
  47.     DM_ICMINTENT: Result := pDM^.dmICMIntent;
  48.     DM_MEDIATYPE: Result := pDM^.dmMediaType;
  49.     DM_DITHERTYPE: Result := pDM^.dmDitherType;
  50.     else
  51.       Result := -1;   // Field not supported
  52.   end;
  53. end;
  54.  
  55. function SetDevModeField(pDM: PDEVMODE; Field: longint; Value: longint):longint;
  56. // Only for fields whose value type and range is integer >=0
  57.   // Result >= 0 ==> change succeeded; result = previous value (maybe we want to restore it later)
  58.   // Result < 0  ==> change failed
  59. begin
  60.   if (pDM^.dmFields and Field)=0 then
  61.     Result := -1   // Field not supported
  62.   else
  63.   begin
  64.   Result := GetDevModeField(pDM,Field);
  65.   case Field of
  66.     DM_ORIENTATION: pDM^.dmOrientation := Value;
  67.     DM_PAPERSIZE: pDM^.dmPaperSize := Value;
  68.     DM_PAPERLENGTH: pDM^.dmPaperLength := Value;
  69.     DM_PAPERWIDTH: pDM^.dmPaperWidth := Value;
  70.     DM_SCALE: pDM^.dmScale := Value;
  71.     //  DM_POSITION: pDM^.dmPosition := Value;
  72.     DM_COPIES: pDM^.dmCopies := Value;
  73.     DM_DEFAULTSOURCE: pDM^.dmDefaultSource := Value;
  74.     DM_PRINTQUALITY: pDM^.dmPrintQuality := Value;
  75.     DM_COLOR: pDM^.dmColor := Value;
  76.     DM_DUPLEX: pDM^.dmDuplex := Value;
  77.     DM_YRESOLUTION: pDM^.dmYResolution := Value;
  78.     DM_TTOPTION: pDM^.dmTTOption := Value;
  79.     DM_COLLATE: pDM^.dmCollate := Value;
  80.     //  DM_FORMNAME: pDM^.dmFormName := Value;
  81.     DM_LOGPIXELS: pDM^.dmLogPixels := Value;
  82.     DM_ICMMETHOD: pDM^.dmICMMethod := Value;
  83.     DM_ICMINTENT: pDM^.dmICMIntent := Value;
  84.     DM_MEDIATYPE: pDM^.dmMediaType := Value;
  85.     DM_DITHERTYPE: pDM^.dmDitherType := Value;
  86.     else
  87.       Result := -1;   // Field not supported
  88.   end;
  89.   end;
  90. end;
  91.  
  92.  
  93. function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;
  94.   // Only for fields whose value type and range is integer >=0
  95.   // Result > 0 ==> change succeeded; result = previous value (maybe we want to restore it later)
  96.   // Result < 0 ==> change failed
  97.  
  98. label
  99.   exit_with_error;
  100. const
  101.   LEVEL_2 = 2;
  102. var
  103.   hPrinter: tHandle;
  104.   pPrnInFo: pPRINTER_INFO_2;
  105.   pDev:PDevMode;
  106.   datasize: integer;
  107.   PrinterNameW: WideString;
  108. begin
  109.   pPrnInfo:=nil;
  110.   PrinterNameW := prn.PrinterName;
  111.  
  112.   if not OpenPrinterW(PWideChar(PrinterNameW),@hPrinter, nil) then  //Open printer;
  113.     goto exit_with_error;
  114.  
  115.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  116.   GetPrinter(hPrinter, LEVEL_2, Nil, 0, @datasize);
  117.  
  118.   GetMem(pPrnInfo, datasize);
  119.   if pPrnInfo = nil then
  120.     goto exit_with_error;
  121.  
  122.   // get config data; GetPrinter returns false but it works ¿?
  123.   GetPrinter(hPrinter, LEVEL_2 ,PByte(pPRNInFo), datasize ,@datasize);
  124.  
  125.   pDev := pPRNInfo^.pDevMode;
  126.  
  127.   result:= SetDevModeField(pDev, FieldToChange, ValueToSet);
  128.   if result>=0 then
  129.     SetPrinter(hPrinter, LEVEL_2, PByte(pPrnInFo), 0);  // set new config data
  130.  
  131.   // clean and exit
  132.   FreeMem(pPrnInfo);         // Free allocated memory
  133.   ClosePrinter(hPrinter);    // Close printer
  134.   exit;
  135.  
  136.   // ----------------- ERROR ----------------------
  137.   exit_with_error:
  138.     Result := -1;
  139.   if pPrnInfo<>nil then      // Free memory if allocated
  140.     freeMem(pPrnInfo);
  141.   if (hPrinter <> 0) then    // Close printer if opened
  142.     ClosePrinter(hPrinter);
  143. end;
  144.  
  145. function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint;
  146. // Only for fields whose value type and range is integer >=0
  147. // Result >= 0 ==> succeeded
  148. // Result < 0  ==> failed
  149. label
  150.   exit_with_error;
  151. const
  152.   LEVEL_2 = 2;
  153. var
  154.   hPrinter: tHandle;
  155.   pPrnInFo: pPRINTER_INFO_2;
  156.   pDev:PDevMode;
  157.   datasize: integer;
  158.   PrinterNameW: WideString;
  159. begin
  160.   pPrnInfo:=nil;
  161.   PrinterNameW := prn.PrinterName;
  162.  
  163.   if not OpenPrinterW(PWideChar(PrinterNameW),@hPrinter, nil) then  // Open printer;
  164.     goto exit_with_error;
  165.  
  166.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  167.   GetPrinter(hPrinter, LEVEL_2, Nil, 0, @datasize);
  168.  
  169.   GetMem(pPrnInfo, datasize);
  170.   if pPrnInfo = nil then
  171.     goto exit_with_error;
  172.  
  173.   // get config data; GetPrinter returns false but it works ¿?
  174.   GetPrinter(hPrinter, LEVEL_2 ,PByte(pPRNInFo), datasize ,@datasize);
  175.  
  176.   pDev := pPRNInfo^.pDevMode;
  177.  
  178.   result:= GetDevModeField(pDev, FieldToGet);
  179.  
  180.   // clean and exit
  181.   FreeMem(pPrnInfo);         // Free allocated memory
  182.   ClosePrinter(hPrinter);    // Close printer
  183.   exit;
  184.  
  185.   // ----------------- ERROR ----------------------
  186.   exit_with_error:
  187.     Result := -1;
  188.   if pPrnInfo<>nil then      // Free memory if allocated
  189.     freeMem(pPrnInfo);
  190.   if (hPrinter <> 0) then    // Close printer if opened
  191.     ClosePrinter(hPrinter);
  192. end;
  193.  
  194. end.          

Examples of use:

Code: Pascal  [Select]
  1.   // uses Printers, Windows, GetAndSetPrinterSettings;
  2.   ActualValue:= GetPrinterOption(Printer, DM_DUPLEX);   // get current value
  3.   // ...
  4.   PreviousValue:= SetPrinterOption(Printer, DM_COLOR, DMCOLOR_MONOCHROME);  // Save value and then change it
  5.   // print something here (output will be monochrome)
  6.   SetPrinterOption(Printer, DM_COLOR, PreviousValue);  // Restore previous value

Edit: some aesthetic changes in the code.
« Last Edit: June 07, 2018, 01:44:37 pm by RayoGlauco »
To err is human, but to really mess things up, you need a computer.

RayoGlauco

  • Jr. Member
  • **
  • Posts: 57
Re: [SOLVED] Setting printer properties in PrintDialog
« Reply #26 on: June 09, 2018, 12:58:01 pm »
I wrote some more code to access printer settings in Windows.

I paste the code, in case someone finds it useful.

Code: Pascal  [Select]
  1. unit GetAndSetPrinterSettings;   // Only for Windows
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Printers, Windows, WinUtilPrn, Classes;  // WinUtilPrn is at:  \lazarus\components\printers\win32\
  9.  
  10. function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;
  11. function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint;
  12.  
  13. function GetPrinterSettings(prn: tPrinter; out pDM: PDevMode): boolean;
  14. function SetPrinterSettings(prn: tPrinter; pDM: PDevMode): boolean;
  15. function PrinterSettingsDialog(prn: tPrinter; out pDM: PDevMode; ApplyNow: boolean): boolean;
  16. function PrinterSettingsDialog(prn: tPrinter): boolean;
  17.  
  18. function GetDevModeField(pDM: PDEVMODE; Field: longint): longint;
  19. function SetDevModeField(pDM: PDEVMODE; Field: longint; Value: longint): longint;
  20.  
  21. implementation
  22.  
  23. uses
  24.   SysUtils;
  25.  
  26. function SetPrinter(hPrinter: Handle; Level: DWORD; pPrinter: PByte; Command: DWord): longbool; stdcall; external
  27.   libWinSpool Name 'SetPrinterA';
  28.  
  29. function DocumentProperties(_para1: HWND; _para2: HANDLE; _para3: LPSTR; _para4: PDEVMODE;
  30.   _para5: PDEVMODE; _para6: DWORD): LONG; stdcall; external LibWinSpool Name 'DocumentPropertiesA';
  31.  
  32. function GetDevModeField(pDM: PDEVMODE; Field: longint): longint;
  33.   // Only for fields whose value type and range is integer >=0
  34.   // Result >= 0 ==> succeeded
  35.   // Result < 0  ==> failed
  36. begin
  37.   if (pDM^.dmFields and Field) = 0 then
  38.     Result := -1   // Field not supported
  39.   else
  40.     case Field of
  41.       DM_ORIENTATION: Result := pDM^.dmOrientation;
  42.       DM_PAPERSIZE: Result := pDM^.dmPaperSize;
  43.       DM_PAPERLENGTH: Result := pDM^.dmPaperLength;
  44.       DM_PAPERWIDTH: Result := pDM^.dmPaperWidth;
  45.       DM_SCALE: Result := pDM^.dmScale;
  46.       //  DM_POSITION: Result:=pDM^.dmPosition;
  47.       DM_COPIES: Result := pDM^.dmCopies;
  48.       DM_DEFAULTSOURCE: Result := pDM^.dmDefaultSource;
  49.       DM_PRINTQUALITY: Result := pDM^.dmPrintQuality;
  50.       DM_COLOR: Result := pDM^.dmColor;
  51.       DM_DUPLEX: Result := pDM^.dmDuplex;
  52.       DM_YRESOLUTION: Result := pDM^.dmYResolution;
  53.       DM_TTOPTION: Result := pDM^.dmTTOption;
  54.       DM_COLLATE: Result := pDM^.dmCollate;
  55.       //  DM_FORMNAME: Result:=pDM^.dmFormName;
  56.       DM_LOGPIXELS: Result := pDM^.dmLogPixels;
  57.       DM_ICMMETHOD: Result := pDM^.dmICMMethod;
  58.       DM_ICMINTENT: Result := pDM^.dmICMIntent;
  59.       DM_MEDIATYPE: Result := pDM^.dmMediaType;
  60.       DM_DITHERTYPE: Result := pDM^.dmDitherType;
  61.       else
  62.         Result := -1;   // Field not supported
  63.     end;
  64. end;
  65.  
  66. function SetDevModeField(pDM: PDEVMODE; Field: longint; Value: longint): longint;
  67.   // Only for fields whose value type and range is integer >=0
  68.   // Result >= 0 ==> change succeeded; result = previous value (maybe we want to restore it later)
  69.   // Result < 0  ==> change failed
  70. begin
  71.   if (pDM^.dmFields and Field) = 0 then
  72.     Result := -1   // Field not supported
  73.   else
  74.   begin
  75.     Result := GetDevModeField(pDM, Field);
  76.     case Field of
  77.       DM_ORIENTATION: pDM^.dmOrientation := Value;
  78.       DM_PAPERSIZE: pDM^.dmPaperSize := Value;
  79.       DM_PAPERLENGTH: pDM^.dmPaperLength := Value;
  80.       DM_PAPERWIDTH: pDM^.dmPaperWidth := Value;
  81.       DM_SCALE: pDM^.dmScale := Value;
  82.       //  DM_POSITION: pDM^.dmPosition := Value;
  83.       DM_COPIES: pDM^.dmCopies := Value;
  84.       DM_DEFAULTSOURCE: pDM^.dmDefaultSource := Value;
  85.       DM_PRINTQUALITY: pDM^.dmPrintQuality := Value;
  86.       DM_COLOR: pDM^.dmColor := Value;
  87.       DM_DUPLEX: pDM^.dmDuplex := Value;
  88.       DM_YRESOLUTION: pDM^.dmYResolution := Value;
  89.       DM_TTOPTION: pDM^.dmTTOption := Value;
  90.       DM_COLLATE: pDM^.dmCollate := Value;
  91.       //  DM_FORMNAME: pDM^.dmFormName := Value;
  92.       DM_LOGPIXELS: pDM^.dmLogPixels := Value;
  93.       DM_ICMMETHOD: pDM^.dmICMMethod := Value;
  94.       DM_ICMINTENT: pDM^.dmICMIntent := Value;
  95.       DM_MEDIATYPE: pDM^.dmMediaType := Value;
  96.       DM_DITHERTYPE: pDM^.dmDitherType := Value;
  97.       else
  98.         Result := -1;   // Field not supported
  99.     end;
  100.   end;
  101. end;
  102.  
  103.  
  104. function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;
  105.   // Only for fields whose value type and range is integer >=0
  106.   // Result > 0 ==> change succeeded; result = previous value (maybe we want to restore it later)
  107.   // Result < 0 ==> change failed
  108.  
  109. label
  110.   exit_with_error;
  111. const
  112.   LEVEL_2 = 2;
  113. var
  114.   hPrinter: tHandle;
  115.   pPrnInFo: pPRINTER_INFO_2;
  116.   pDev: PDevMode;
  117.   datasize: integer;
  118.   PrinterNameW: WideString;
  119. begin
  120.   pPrnInfo := nil;
  121.   PrinterNameW := prn.PrinterName;
  122.  
  123.   if not OpenPrinterW(PWideChar(PrinterNameW), @hPrinter, nil) then  //Open printer;
  124.     goto exit_with_error;
  125.  
  126.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  127.   GetPrinter(hPrinter, LEVEL_2, nil, 0, @datasize);
  128.  
  129.   GetMem(pPrnInfo, datasize);
  130.   if pPrnInfo = nil then
  131.     goto exit_with_error;
  132.  
  133.   // get config data; GetPrinter returns false but it works ¿?
  134.   GetPrinter(hPrinter, LEVEL_2, PByte(pPRNInFo), datasize, @datasize);
  135.  
  136.   pDev := pPRNInfo^.pDevMode;
  137.  
  138.   Result := SetDevModeField(pDev, FieldToChange, ValueToSet);
  139.   if Result >= 0 then
  140.     SetPrinter(hPrinter, LEVEL_2, PByte(pPrnInFo), 0);  // set new config data
  141.  
  142.   // clean and exit
  143.   FreeMem(pPrnInfo);         // Free allocated memory
  144.   ClosePrinter(hPrinter);    // Close printer
  145.   exit;
  146.  
  147.   // ----------------- ERROR ----------------------
  148.   exit_with_error:
  149.     Result := -1;
  150.   if pPrnInfo <> nil then      // Free memory if allocated
  151.     freeMem(pPrnInfo);
  152.   if (hPrinter <> 0) then    // Close printer if opened
  153.     ClosePrinter(hPrinter);
  154. end;
  155.  
  156. function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint;
  157.   // Only for fields whose value type and range is integer >=0
  158.   // Result >= 0 ==> succeeded
  159.   // Result < 0  ==> failed
  160. label
  161.   exit_with_error;
  162. const
  163.   LEVEL_2 = 2;
  164. var
  165.   hPrinter: tHandle;
  166.   pPrnInFo: pPRINTER_INFO_2;
  167.   pDev: PDevMode;
  168.   datasize: integer;
  169.   PrinterNameW: WideString;
  170. begin
  171.   pPrnInfo := nil;
  172.   PrinterNameW := prn.PrinterName;
  173.  
  174.   if not OpenPrinterW(PWideChar(PrinterNameW), @hPrinter, nil) then  // Open printer;
  175.     goto exit_with_error;
  176.  
  177.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  178.   GetPrinter(hPrinter, LEVEL_2, nil, 0, @datasize);
  179.  
  180.   GetMem(pPrnInfo, datasize);
  181.   if pPrnInfo = nil then
  182.     goto exit_with_error;
  183.  
  184.   // get config data; GetPrinter returns false but it works ¿?
  185.   GetPrinter(hPrinter, LEVEL_2, PByte(pPRNInFo), datasize, @datasize);
  186.  
  187.   pDev := pPRNInfo^.pDevMode;
  188.  
  189.   Result := GetDevModeField(pDev, FieldToGet);
  190.  
  191.   // clean and exit
  192.   FreeMem(pPrnInfo);         // Free allocated memory
  193.   ClosePrinter(hPrinter);    // Close printer
  194.   exit;
  195.  
  196.   // ----------------- ERROR ----------------------
  197.   exit_with_error:
  198.     Result := -1;
  199.   if pPrnInfo <> nil then      // Free memory if allocated
  200.     freeMem(pPrnInfo);
  201.   if (hPrinter <> 0) then    // Close printer if opened
  202.     ClosePrinter(hPrinter);
  203. end;
  204.  
  205. function PrintStateToText(ps: tPrinterState): string;
  206. begin
  207.   case ps of
  208.     psNoDefine: Result := '';
  209.     psReady: Result := 'Ready';
  210.     psPrinting: Result := 'Printing';
  211.     psStopped: Result := 'Stopped';
  212.     else
  213.       Result := '';
  214.   end;
  215. end;
  216.  
  217. function GetPrinterSettings(prn: tPrinter; out pDM: PDevMode): boolean;
  218.   // This function allocates memory for pDM and saves printer settings in it
  219.   // Result: True = ok; False = error
  220.   // -
  221. label
  222.   exit_with_error;
  223. const
  224.   LEVEL_2 = 2;
  225. var
  226.   hPrinter: tHandle;
  227.   pPrnInFo: pPRINTER_INFO_2;
  228.   datasize, dmsize: integer;
  229.   PrinterNameW: WideString;
  230. begin
  231.   pDM := nil;
  232.   pPrnInfo := nil;
  233.   PrinterNameW := prn.PrinterName;
  234.  
  235.   if not OpenPrinterW(PWideChar(PrinterNameW), @hPrinter, nil) then  // Open printer;
  236.     goto exit_with_error;
  237.  
  238.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  239.   GetPrinter(hPrinter, LEVEL_2, nil, 0, @datasize);
  240.  
  241.   GetMem(pPrnInfo, datasize);
  242.   if pPrnInfo = nil then
  243.     goto exit_with_error;
  244.  
  245.   // get config data; GetPrinter returns false but it works ¿?
  246.   GetPrinter(hPrinter, LEVEL_2, PByte(pPRNInFo), datasize, @datasize);
  247.  
  248.   dmsize := DocumentProperties(0, hPrinter, PChar(prn.PrinterName), nil, nil, 0);
  249.   GetMem(pDM, dmsize);  // Allocate memory if necessary
  250.   if pDM = nil then
  251.     goto exit_with_error;
  252.   CopyMemory(pDM, pPRNInfo^.pDevMode, dmsize);
  253.  
  254.   Result := True;
  255.  
  256.   // clean and exit
  257.   FreeMem(pPrnInfo);         // Free allocated memory
  258.   ClosePrinter(hPrinter);    // Close printer
  259.   exit;
  260.  
  261.   // ----------------- ERROR ----------------------
  262.   exit_with_error:
  263.     Result := False;
  264.   if pPrnInfo <> nil then      // Free memory if allocated
  265.     freeMem(pPrnInfo);
  266.   if pDM <> nil then      // Free memory if allocated
  267.     freeMem(pDM);
  268.   if (hPrinter <> 0) then    // Close printer if opened
  269.     ClosePrinter(hPrinter);
  270. end;
  271.  
  272. function SetPrinterSettings(prn: tPrinter; pDM: PDevMode): boolean;
  273.   // This function Applies values in pDM to the printer
  274.   // Result: True = ok; False = error
  275.   // -
  276. label
  277.   exit_with_error;
  278. const
  279.   LEVEL_2 = 2;
  280. var
  281.   hPrinter: tHandle;
  282.   pPrnInFo: pPRINTER_INFO_2;
  283.   datasize, dmsize: integer;
  284.   PrinterNameW: WideString;
  285. begin
  286.   pPrnInfo := nil;
  287.   PrinterNameW := prn.PrinterName;
  288.  
  289.   if not OpenPrinterW(PWideChar(PrinterNameW), @hPrinter, nil) then  // Open printer;
  290.     goto exit_with_error;
  291.  
  292.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  293.   GetPrinter(hPrinter, LEVEL_2, nil, 0, @datasize);
  294.  
  295.   GetMem(pPrnInfo, datasize);
  296.   if pPrnInfo = nil then
  297.     goto exit_with_error;
  298.  
  299.   // get config data; GetPrinter returns false but it works ¿?
  300.   GetPrinter(hPrinter, LEVEL_2, PByte(pPRNInFo), datasize, @datasize);
  301.  
  302.   dmsize := DocumentProperties(0, hPrinter, PChar(prn.PrinterName), nil, nil, 0);
  303.   CopyMemory(pPRNInfo^.pDevMode, pDM, dmsize);
  304.   SetPrinter(hPrinter, LEVEL_2, PByte(pPrnInFo), 0);  // set new config data
  305.   Result := True;
  306.  
  307.   // clean and exit
  308.   FreeMem(pPrnInfo);         // Free allocated memory
  309.   ClosePrinter(hPrinter);    // Close printer
  310.   exit;
  311.  
  312.   // ----------------- ERROR ----------------------
  313.   exit_with_error:
  314.     Result := False;
  315.   if pPrnInfo <> nil then      // Free memory if allocated
  316.     freeMem(pPrnInfo);
  317.   if (hPrinter <> 0) then    // Close printer if opened
  318.     ClosePrinter(hPrinter);
  319. end;
  320.  
  321. function PrinterSettingsDialog(prn: tPrinter; out pDM: PDevMode; ApplyNow: boolean): boolean;
  322.   // This function opens a Dialog asking for printer settings; then ...
  323.   // ... allocates memory for pDM and saves printer settings in it
  324.   // Result: True = ok; False = error
  325.   // ApplyNow = true --> applies the new settings to the printer
  326.   // ApplyNow = false --> only saves printer settings in pDM
  327. label
  328.   exit_with_error;
  329. const
  330.   LEVEL_2 = 2;
  331. var
  332.   hPrinter: tHandle;
  333.   pPrnInFo: pPRINTER_INFO_2;
  334.   datasize, dmsize: integer;
  335.   PrinterNameW: WideString;
  336. begin
  337.   pDM := nil;
  338.   pPrnInfo := nil;
  339.   PrinterNameW := prn.PrinterName;
  340.  
  341.   if not OpenPrinterW(PWideChar(PrinterNameW), @hPrinter, nil) then  // Open printer;
  342.     goto exit_with_error;
  343.  
  344.   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?
  345.   GetPrinter(hPrinter, LEVEL_2, nil, 0, @datasize);
  346.  
  347.   GetMem(pPrnInfo, datasize);
  348.   if pPrnInfo = nil then
  349.     goto exit_with_error;
  350.  
  351.   // get config data; GetPrinter returns false but it works ¿?
  352.   GetPrinter(hPrinter, LEVEL_2, PByte(pPRNInFo), datasize, @datasize);
  353.  
  354.   if DocumentProperties(0, hPrinter, PChar(prn.PrinterName), pPRNInfo^.pDevMode, pPRNInfo^.pDevMode,
  355.     DM_IN_BUFFER or DM_IN_PROMPT or DM_OUT_BUFFER) < 0 then
  356.     goto exit_with_error;
  357.  
  358.   dmsize := DocumentProperties(0, hPrinter, PChar(prn.PrinterName), nil, nil, 0);
  359.   GetMem(pDM, dmsize);  // Allocate memory if necessary
  360.   if pDM = nil then
  361.     goto exit_with_error;
  362.   CopyMemory(pDM, pPRNInfo^.pDevMode, dmsize);  // return new config data
  363.  
  364.   if ApplyNow then
  365.     SetPrinter(hPrinter, LEVEL_2, PByte(pPrnInFo), 0);       // set new config data
  366.  
  367.   Result := True;
  368.  
  369.   // clean and exit
  370.   FreeMem(pPrnInfo);         // Free allocated memory
  371.   ClosePrinter(hPrinter);    // Close printer
  372.   exit;
  373.  
  374.   // ----------------- ERROR ----------------------
  375.   exit_with_error:
  376.     Result := False;
  377.   if pPrnInfo <> nil then      // Free memory if allocated
  378.     freeMem(pPrnInfo);
  379.   if pDM <> nil then      // Free memory if allocated
  380.     freeMem(pDM);
  381.   if (hPrinter <> 0) then    // Close printer if opened
  382.     ClosePrinter(hPrinter);
  383. end;
  384.  
  385. function PrinterSettingsDialog(prn: tPrinter): boolean;
  386.   // This function opens a Dialog asking for printer settings and ...
  387.   // ... applies the new settings to the printer
  388.   // Result: True = ok; False = error
  389. var
  390.   pNewDM: PDevMode;
  391. begin
  392.   Result := PrinterSettingsDialog(prn, pNewDM, True);     // ask for and apply new settings
  393.   FreeMem(pNewDM);           // free memory  
  394. end;
  395.  
  396. end.

Example of use:

Code: Pascal  [Select]
  1. uses
  2.   Printers, Windows, GetAndSetPrinterSettings;
  3. // You must add Printer4Lazarus as a required package
  4. // Not cheching for errors to keep simple the code
  5.  
  6. procedure TForm1.Button1Click(Sender: TObject);
  7. // Example 1: change printer duplex and color/monochrome mode
  8. var
  9.   pDM: PDevMode;
  10. begin
  11.   // get actual settings
  12.   GetPrinterSettings(Printer, pDM);
  13.   // alternate duplex
  14.   if GetDevModeField(pDM, DM_DUPLEX) = DMDUP_SIMPLEX then
  15.     SetDevModeField(pDM, DM_DUPLEX, DMDUP_VERTICAL)
  16.   else
  17.     SetDevModeField(pDM, DM_DUPLEX, DMDUP_SIMPLEX);
  18.   // alternate color/gray
  19.   if GetDevModeField(pDM, DM_COLOR) = DMCOLOR_COLOR then
  20.     SetDevModeField(pDM, DM_COLOR, DMCOLOR_MONOCHROME)
  21.   else
  22.     SetDevModeField(pDM, DM_COLOR, DMCOLOR_COLOR);
  23.   // apply changes to the printer
  24.   SetPrinterSettings(Printer, pDM);
  25.   // free memory
  26.   FreeMem(pDM);
  27. end;
  28.  
  29. procedure TForm1.Button2Click(Sender: TObject);
  30. // Example 2: show a dialog to configure printer
  31. begin
  32.   // ask for and apply new settings
  33.   PrinterSettingsDialog(Printer);
  34. end;
  35.  
  36. procedure TForm1.Button3Click(Sender: TObject);
  37. // Example 3: show a dialog to configure a printing
  38. var
  39.   pOriginalDM: PDevMode;
  40. begin
  41.   // save original settings
  42.   GetPrinterSettings(Printer, pOriginalDM);
  43.   // ask for and apply new settings
  44.   PrinterSettingsDialog(Printer);
  45.   // ... some printing here
  46.   Printer.BeginDoc;
  47.   Printer.Canvas.Font.Size := 14;
  48.   Printer.Canvas.Font.Color := $00ff0044;
  49.   Printer.Canvas.TextOut(200, 200, 'TESTING... ');
  50.   Printer.EndDoc;
  51.   // restore original settings
  52.   SetPrinterSettings(Printer, pOriginalDM);
  53.   // free memory
  54.   FreeMem(pOriginalDM);
  55. end;

I think that printing is a common task in programming, and should be easier in Lazarus. It took me many hours to find the information (only for windows). I still do not understand why Lazarus offers a print dialog with a printer settings button, if when pressing it the user can change many things, but then the programmer can not know what changes made the user.

Edit: some changes in the code & example; I attached the GetAndSetPrinterSettings unit.
« Last Edit: June 12, 2018, 02:38:15 pm by RayoGlauco »
To err is human, but to really mess things up, you need a computer.

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus