Delphi - Printing via the TPrinter Canvas

The "normal" way to print is to use a QuickReport.

This page is about how to print without using a report generator.

Writing directly to the printer's canvas provides more control than using someone else's components.

Unfortunately, Delphi 5 does not properly handle some functions. As a result, you must access the Windows API yourself.

The "Printers" Unit | Simple text printing | Drawing on the Canvas | Images | Selecting a Printer
Printer Properties | Relevant definitions from windows.pas | References


The "Printers" Unit

In Delphi, you print via the TPrinter object. The TPrinter help says
A print job is started whenever any rendering is done through a Text variable or the printerís canvas.

Canvas example


Simple text printing

AssignPrn allows you to treat the current printer as a text file. This example is verbatim from Delphi's AssignPrn help.

This is from Printers.pas


Drawing on the Canvas

Using Delphi commands, all content generation is produced via the canvas.

As far as I can tell, all the canvas commands work ... except those that copy a TGraphic or a TCanvas. Basically, canvas methods based on the Windows StretchDraw method fail. (Of course, this is not documented anywhere in Delphi 5 since Windows XP was not written back then.)

The following Canvas commands fail when printing under Windows XP.


Images

This is very tricky ... and completely undocumented in Delphi 5.

Actually, with Delphi 5 and Windows 98, the provided documentation is pretty good. The problem is that with Windows 2000 and Windows XP, the technique used with Windows 98 no longer works. (Some references imply that this is a problem with all versions of Windows. However, my experience is that an *.exe file that worked perfectly with Windows 98 failed to print the images with Windows XP. Same printer, but different drivers - obviously.)

With Windows 98, you just copy the image canvas to the printer canvas. This example is from the Delphi 5 Printer function help.

However, starting with Windows 2000, that example no longer works. Now you need to use a device independent bitmap to accomplish this. This code fragment (based on code in Forms.pas) uses several Windows API calls.


bmp.HandleType

One of the references suggests simply forcing the bitmap to be device independent before executing StretchDraw. It does NOT work.


Selecting a Printer

I have a custom application that runs on only one system and prints to a special barcode printer. As a result, I want the application to automatically default to that printer.

Be sure to set the paper size ... even if you set it to a nonsense value. Using the Brother PT-2500PC barcode printer on a Windows 2000 system, just setting the printer produced access errors when I tried to open either the Print or Print Setup dialog boxes. Setting the paper size to an non-existent value ('11') fixed the problem.

In the final application, the printer name will be read from an ini file - never hard code strings.


Printer Properties

Several TPrinter properties are read only These are read/write The Windows API provides several additional properties that Delphi does not make available, it also provides a way to write the readonly properties.

Normally, you won't need this ... but just in case.

dmFields specifies which parameters are actually used.
Click on DEVMODE and press F1 to get the related Windows API help.

In the above example, instead of

You could use Do not use the following construct (I did), it assigns a copy of the structure to dm - when you set parameters, they never get back to the actual printer.

For alternate syntax, see Changing the papersize of a print job - by Borland Developer Support Staff. This reference also sets Printer.PrinterIndex to its current value before executing GetPrinter and after executing SetPrinter. I have no idea why (or if) that is necessary. (In one of the news groups, I was told that it fixed an obscure problem.)

Also see Macro to Obtain a List of Paper Names Supported by the Active Printer.

Selecting Paper Source (Paper Tray) shows how to modify parameters.


Selecting Paper Source - Errors

Of course, nothing is that easy.

I've been working with an HP 4 Plus - a fairly common, but old, printer that reports 7 paper sources (bins) ... it actually only has 2 physical bins and 2 auto-select "bins" - the other 3 are options but not present.

The currently selected paper source is provided by

Strings representing the available sources are retrieved via The numbers associated with these is obtained via This are the numbers (in order) that my system retrieves Presumably, one of these should match Pdm^.dmDefaultSource above. I'm sure that you already see a (the?) problem - 2 sources are associated with the number "1".

The default bin is 15 - Automatically Select
The problem is bin 257 - Auto Select (257 = 256 + 1 - It is actually the first "1")

The constants 1 through 15 are defined in windows.pas - device specific bins start at 256.


Selecting Paper Size - Errors

Standards, please. returns a 32 character string returns a longer (64 character) string.

Several paper names are over 32 characters long.

Search windows.pas for "DMPAPER_".


Relevant definitions from windows.pas

These definitions are from windows.pas - _devicemodeA defines the Windows API DEVMODE record. In order to simplify design and debug, when given a choice, I prefer to use strings connected to the Delphi help system. When you click on DEVMODE and press F1, the related Windows API help is displayed. If you use TDeviceMode, nothing happens.


References


Author: Robert Clemenzi - clemenzi@cpcug.org
URL: http:// cpcug.org / user / clemenzi / technical / Languages / Delphi / Printing.html