- - * - WhiteUnicorn - * - -




* #WhiteUnicorn/ StartPage/ Documentation/DelphiFAQ >


Frequently Asked Questions

Changing the printer setup between printed pages?

Question:

How can I change the printer setup between printed pages?

Answer:

This is normally accomplished by retrieving a copy of the device

mode structure that is associated with the current printer, making

the necessary changes to the device independent portion of the

structure, and then calling the Windows API function ResetDc() in

between pages.



Since the ResetDc() function is disabled by Windows during the

printing of a page, and the TPrinter unit does not have an event that

is fired between pages, a work around is required.



While it is possible to insert and modify code directly in the

implementation section of TPrinter, it is not possible to make changes

to the interface portion of TPrinter and still maintain stability in

the VCL and other third party components. With the advent of packages,

rebuilding the main VCL package is also not an ideal solution.

Finally, TPrinter is one of the few VCL classes that does not lend

itself to subclassing, making it difficult to add an event that would

fire between pages.



Before the advent of packages, the work around consisted of creating

an additional unit that sat between TPrinter and the unit(s)

containing your printing code. A uses statement was added to the

TPrinter unit in the implementation section that pointed to the new

unit (avoiding any change to TPrinter's interface section). Finally,

code was added to the TPrinter unit that would call a function pointer

in the new unit between each printed page. You could then hook into

TPrinter by adding the new unit to the uses clause of the unit(s) you

are printing from, and reassigning the function pointer to point to a

function in your unit. While this may sound complex, in practice, it

was a clean solution to an interesting problem, that worked well with

both VCL components and custom printing code produced directly in

your application.



With the advent of packages, a new solution is needed, since the

TPrinter unit is compiled into the main VCL system package, where

rebuilding is not recommended. Here we will provide such a solution

in the form of a single unit that encapsulates both the work around

and the printing code. The code is designed so it will compile under

all versions of Delphi and Borland's C++ Builder. Since there will be

no hooks added to the TPrinter unit, this solution will only work with

custom printing code provided from your application, and not work 

with VCL and other "non-aware" third part components that offer a 

print method, or code utilizing the AssignPrn() function.

                                                       

The solution presented allows changes to the page setup by making

direct calls to the Windows API to simulate TPrinter's NewPage method,

and calling the Windows API function ResetDc() between pages. Note

that not all printers support the ResetDc() escape. Under 16 bit

versions of Windows, testing for escape support by calling

Escape(QueryEscapeSupport...) returns the correct value for the

ResetDc undocumented escape code #128, but Windows 95 instead 

steals the call, and returns true even if the driver does not support it.

The only way to test the if the ResetDc() function is supported is to

make a call directly to the function itself. To avoid printing blank

pages in the event of an error, the example calls the ResetDC function

and passes Printer.Handle as the DC before the printing actually

starts to test for support of the function. This is ok, as the call

is transformed by Windows to an escape to the print driver, and the

dc is not actually passed but is instead used by Windows to identify

the driver. If the print driver does not support the ResetDc() escape,

we will accomplish changing the printer's settings by printing each

page as separate print job. Since we are bypassing TPrinter's 

NewPage method, and since the print job may be accomplished by 

printing separate pages, the PageNumber property of TPrinter will 

not remain accurate through out the print job and should be tracked 

by the application. Finally, if abort method of TPrinter must be called,

it should be called in a section of code after only after a StartPage

command.



Note: See TDEVMODE in the Delphi 1.02 help file or DEVMODE in 

the 32 bit versions of Delphi and C++ Builder for other settings you can

change, such as the bin, paper sizes and so forth.



Example:



unit Unit1;



interface



{$IFDEF WIN32}

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls, Printers;

{$ELSE}

  uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,

  Controls, Forms, Dialogs, StdCtrls, Printers, Print;

{$ENDIF}





type

  TForm1 = class(TForm)

    PrintDialog1: TPrintDialog;

    Button1: TButton;

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

    function PageSetup : bool;

    function NewPage : bool;

  public

    { Public declarations }

    PageNumber : integer;

    CanReset : bool;

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}



{$IFNDEF WIN32}

 const MAX_PATH = 144;

{$ENDIF}



function TForm1.PageSetup : bool;

var

  pDevice : pChar;

  pDriver : pChar;

  pPort   : pChar;

  hDMode : THandle;

  PDMode : PDEVMODE;

  PrnHandle : THandle;

begin

  result := false;

  GetMem(pDevice, cchDeviceName);

  GetMem(pDriver, MAX_PATH);

  GetMem(pPort, MAX_PATH);

  Printer.GetPrinter(pDevice, pDriver, pPort, hDMode);

  if hDMode <> 0 then begin

    pDMode := GlobalLock(hDMode);

    if pDMode <> nil then begin



     {Change your printing settings here}

      if not odd(PageNumber) then begin

        pDMode^.dmFields := pDMode^.dmFields or dm_PaperSize;

        pDMode^.dmPaperSize := DMPAPER_LETTER;

      end else begin

        pDMode^.dmFields := pDMode^.dmFields or dm_PaperSize;

        pDMode^.dmPaperSize := DMPAPER_LEGAL;

      end;



      if Printer.Printing then

        PrnHandle := Printer.Canvas.Handle

       else

        PrnHandle := Printer.Handle;

     {$IFDEF WIN32}

      if ResetDc(PrnHandle, pDMode^) <> 0 then

     {$ELSE}

      if ResetDc(PrnHandle, pDMode) <> 0 then

     {$ENDIF}

        CanReset := true else

        CanReset := false;

      Result := true;

      GlobalUnlock(hDMode);

    end;

  end;

  FreeMem(pDevice, cchDeviceName);

  FreeMem(pDriver, MAX_PATH);

  FreeMem(pPort, MAX_PATH);

end;



function TForm1.NewPage : bool;

begin

  Result := true;

  if CanReset then

    EndPage(Printer.Canvas.Handle) else

    Printer.EndDoc;

  Inc(PageNumber);

  if PageSetUp then begin

    if CanReset then begin

      StartPage(Printer.Canvas.Handle);

      Printer.Canvas.Refresh;

    end else

      Printer.BeginDoc;

  end else begin

    if CanReset then begin

      StartPage(Printer.Canvas.Handle);

      Printer.Abort;

    end;

    Result := false;

  end;

end;



procedure TForm1.Button1Click(Sender: TObject);

begin

  if PrintDialog1.Execute then begin



   {Setup printing}

    PageNumber := 1;

    if not PageSetUp then begin

      ShowMessage('Unable to customize printer settings');

      exit;

    end;

    Printer.BeginDoc;



   {Print page one}

    Printer.Canvas.TextOut(100,100, 'Test Page 1');

    if not NewPage then begin

      ShowMessage('Unable to customize printer settings');

      exit;

    end;



   {Print page two}

    Printer.Canvas.TextOut(100,100, 'Test Page 2');

    if not NewPage then begin

      ShowMessage('Unable to customize printer settings');

      exit;

     end;



   {Print page three}

    Printer.Canvas.TextOut(100,100, 'Test Page 3');

    Printer.EndDoc;

  end;

end;



end.



* #WhiteUnicorn/ StartPage/ Documentation/DelphiFAQ >



- - * - Anastasija aka WhiteUnicorn - * - - LJLiveJournal
PFPhotoFile