Printer Projects
Printer Demo #1  Lab Report

Printing in Delphi: Draw, StretchDraw, StretchDIBits, CopyRect
Scanned Image of 8.5"-by-11" Printout from Epson Stylus Color 850
    
Scanned Image of 8.5"-by-11" Printout from HP LaserJet 5
NOTE:  For reasons I have seen but do not understand, this
program may given an immediate protection fault in the GDI.EXE
on some machines.

Purpose
The purpose of this project was to explore printing in Delphi, especially the various ways to print an image. The various Windows printer "Device Capabilities" are also to be studied.

Materials and Equipment

Software Requirements
Windows 95/98/2000
Delphi 3/4/5 (to recompile)
PrinterDemo1.EXE

Hardware Requirements
VGA display
Windows printer

Procedure

  1. Double click on the PrinterDemo1.EXE icon to start the program.
  2. On the "Absolute Inches" tab, select the Print button. After several seconds, a sheet like those seen at the top of this page will print.
  3. If desired, select the checkbox for the Transparency Test.  For some reason, this test can result in GPFs in the GDI.EXE under Windows 98.  (I haven't figured out why yet, so I just made this test an option.)  [See Joe Hecht's notes about why Transparency on printers is not guaranteed -- see Item 11 on the Delphi Printing Info and Links page.]
  4. Repeat on a variety of Windows printers and compare results.

Data

Printers

    Pixels LogPixelsX PhysicalOffset  
Printer Type Width Height LogPixelsY X/Y Comments/Features
Canon BJC-620 Color ink jet 3750 2879 360 167/90 Good color. Not much difference between StretchDraw and StretchDIBits. "CopyRect Enigma" present in "normal" orientation. Because of the large PhysicalOffsetX value, the image runs off the right margin (unlike most ink jets that "hug" the left margin).
Epson Stylus Color 850 Color ink jet 7440 5952 720

[What about the marketing claim of 1440 x 720 DPI?  See notes below.]

84/84

Excellent color, especially with photo-quality paper. "CopyRect Enigma" is present in "flipped" orientation but is missing in the "normal" orientation. Transparent "Blue O" is yellow for Draw and StretchDraw (not sure why).
HP DeskJet 682C Color ink jet 3112 2400 300 12/75 StretchDIBits is much better than StretchDraw. No "CopyRect Enigma," except for slight color shift in "Flipped" orientation.
HP DeskJet 870C Color ink jet 3150 2400 300 12/75 StretchDIBits is much better than StretchDraw. No "CopyRect Enigma," except for slight color shift in "Flipped" orientation.
HP DesignJet 750C Color ink jet "plotter" 6481 4698 600 59/200 Excellent color. StretchDIBits is much better than StretchDraw. GetDeviceCaps values such as LOGPIXELSX are for a "logical" inch, which may be 2 physical inches.
HP LaserJet III Black/ White laser 3150 2425 300 75/75 StretchDIBits is much better than StretchDraw. No "CopyRect Enigma" at all! "Draw" of spectrum does not appear.
HP LaserJet 5 Black/ White laser 6352 4896 600 124/102 StretchDIBits is much better than StretchDraw. StretchDraw is very bad using Raster Graphics mode. "CopyRect Enigma" sometimes present in "normal" orientation.
"Draw" of spectrum does not appear. "Banding" in spectrum images. Sometimes Draw doesn't result in any image.  GDI GPFs under Windows 98 are sometimes observed during the transparency test.
IBM 4039 Black/ White laser 3158 2408 300 75/79 No difference between StretchDraw and StretchDIBits. No "CopyRect Enigma" at all!
Lexmark 4039 Plus Black/ White laser 3163 2413 300 71/75 No difference between StretchDraw and StretchDIBits. "CopyRect Enigma" in both "normal" and "flipped" orientation.
Lexmark Z31 Color ink jet 6260 4800 600 600 "Normal" Copyrect doesn't work, but "Flipped" Copyrect does.  No difference between StrechDraw and StretchDIBits.
Okidata 192 Black/ White dot matrix 1584 2376 144 x 288 0/0 Draw image is twice as wide as normal due to ratio of LOGPIXELSY to LOGPIXELSX. StretchDraw show a "weak" image. StretchDIBits does not work at all.

Discussion
Here is pseudocode for this program:

  1. Draw rectangle to show margins and printable area.

  2. Display labels and Date

  3. Show Delphi Printer and Page Properties

  4. Show Printer Device Capabilities

  5. Draw "Blue O" eight different ways: "regular" and "transparent" images printed using Draw, StretchDraw, StretchDIBits and CopyRect

  6. Show Spectrum Images: Draw, CopyRect, StretchDraw, StretchDIBits

  7. Show KC Fox: Draw, StretchDraw, StretchDIBits

  8. Show Hue-Saturation Color Circle: StretchDraw, StretchDIBits (from in-memory, computed bitmap)

  9. Show Sine Curve (the "CopyRect Enigma")

  10. Show List of Fonts.  (Print font name in its own font -- can be a viewing problem for symbol fonts)

Draw and StretchDraw use the StretchBlt API call.

The "CopyRect Enigma" is so named because of a problem in initially getting CopyRect to work at all with a HP LaserJet 5. When I accidentally coded the "flipped" image, I noticed the graphics were wrong and "corrected" the image. But with the "correct" orientation, the image would not print at all! (Note: the sine curve initially goes "down" when the direction of the "Y" axis is reversed.)   [Is it possible this is caused by a pfDevice PixelFormat?  I need to check this.  1 Dec 98, efg]

In a few observed cases with a LaserJet 5, the use of "Draw" in Delphi 1did not result in any image. The same program in Delphi 2 or later resulted in a small image being drawn.

Physical Page Versus Logical Page

The dimensions of the physical page (GetDeviceCaps values PHYSICALWIDTH and PHYSICALHEIGHT in dots) are larger the the logical page (GetDeviceCaps values HORZRES and VERTRES in dots, or HORZSIZE and VERTSIZE in millimeters).  There doesn't seem to be much of a "pattern" in what manufacturers use for the left and top margins (PhysicalOffsetX and PhysicalOffsetY).  Often the Logical Page is not even centered horizontally and vertically within the Physical page.  Often with InkJet printers there is much as a 0.5-inch gap at the bottom of a page in portrait orientation, or the right of the page in landscape orientation.

Important GetDeviceCaps Values

GetDeviceCaps Parameter Description
HORZSIZE horizontal size [mm]
VERTSIZE vertical size [mm]
HORZRES horizontal resolution [dots]
VERTRES vertical resolution [dots]
LOGPIXELSX horizontal dot density [dots/inch]
LOGPIXELSY vertical dot density [dots/inch]
PHYSICALWIDTH page width [dots]
PHYSICALHEIGHT page height [dots]
PHYSICALOFFSETX horizontal offset from physical page edge to logical page edge [dots]
PHYSICALOFFSETY vertical offset from physical page edge to logical page edge [dots]

Here's a brief description of some of the relationships among the GetDeviceCaps values:

Horizontal Dimension
(not drawn to scale)

PHYSICALWIDTH

PHYSICALOFFSETX HORZRES gap

Horizontal gap (or margin at the right) = PHYSICALWIDTH - PHYSICALOFFSETX - HORZRES

Example:  Epson Color Stylus 850 in Landscape Orientation, 8.5-by-11" paper:

GetDeviceCaps Value Dots Inches
Dots/LOGPIXELSX
Millimeters
Inches * 25.4
PHYSICALWIDTH 7920 11.00 279.4
PHYSICALOFFSETX 84 0.12 3.0
HORZRES 7440 10.33 262.5
gap 7920 - 84 - 7440 = 396 0.55 14.0

Ideally, the gap would be the same as the PHYSICALOFFSETX value to center the logical page within the physical page.  A horizontal "gap" of 84 dots would have centered the logical page within the physical page horizontally.   So the page is positioned  396 - 84 = 312 dots from being centered.  (On the Epson this is 312/720 = 0.43 inch.)  Since printing outside of the logical page is impossible, there is no way to print anything in this "gap" area.  As mentioned earlier, many inkjet printers have a significant "gap" in the longer dimension.

Vertical Dimension
(not drawn to scale)

PHYSICALHEIGHT PHYSICALOFFSETY
VERTRES
gap

Vertical gap (or margin at the bottom) = PHYSICALHEIGHT - PHYSICALOFFSETY - VERTRES

Example:  Epson Color Stylus 850 in Landscape Orientation, 8.5-by-11" paper:

GetDeviceCaps Value Dots Inches
Dots/LOGPIXELSY
Millimeters
Inches * 25.4
PHYSICALHEIGHT 6120 8.50 215.9
PHYSICALOFFSETY 84 0.12 3.0
VERTRES 5952 8.27 210.0
gap 6120 - 84 - 5952 = 84 0.12 3.0

With the vertical gap the same as the PHYSICALOFFSETY, the logical page is centered vertically within the physical page.

Other GetDeviceCaps issues

Epson claims a 1440-by-720 DPI on printers such as the Epson 850 Color Stylus.  Neither the LOGPIXELSX nor LOGPIXELSY value is ever 1440! Is this marketing hype?

We tend to think that DotsPerInch  = PixelsPerInch.  Sometimes they do not. The Epson can print slightly
overlapping dots of different colors to create a "pixel".  There may be 1440 dots in an inch, but there are only 720 pixels. I know that's kinda hard to put into concept. (and be happy that the driver reports a "square pixel" as you have seen the problems that both the VCL (and the GDI) can have when dealing with a surface that does not have a 1:1 aspect ratio.  [Thanks to Joe Hecht for this information.]

Transparency

The Epson Stylus Color 850 was the first printer found to have a problem with transparency. For some unexplained reason, Draw or StretchDraw with transparency seems to create an inverted image with this Epson printer. The "Blue O" test was added to this demo program as a result.  This transparency test will sometimes cause GDI.EXE GPFs in Windows 98.  [See Joe Hecht's notes about why Transparency on printers is not guaranteed -- see Item 11 on the Delphi Printing Info and Links page.]

DIB vs DDB and the Sine Curve

Ulrich Strautz reports (October 2000) with his Olivetti JP170 Bubble-Jet printer that the sine curve prints correctly ONLY when a PixelFormat statement is added:

OffScreenBitmap := TBitMap.Create;
OFFScreenBitmap.PixelFormat:=pf24bit;    
// here is the missing line

The bitmap becomes a DIB (device independent bitmap) when the PixelFormat statement is added, and apparently at least with the Olivetti printer, the sine curve prints correctly with a DIB but not a DDB.  (Bitmaps were DDBs in Delphi 1 and 2, and can be either a DIB or a DDB in Delphi 3 or later.)

Miscellaneous

Why use StretchDIBitsSee Blitting Between DCs for Different Devices Is Unsupported (MS Q195830).

Conclusions
Getting the "same" printout on a variety of Windows printers is almost impossible! (This is a Windows issue, not a Delphi problem.)

Almost always use StretchDIBits to print images.

When isn't StretchDIBits the best way to go? If you need to use transparency (Transparent := TRUE), then you must use Draw or StretchDraw.   BUT, transparency is not guaranteed.   [See Joe Hecht's notes about why Transparency on printers is not guaranteed -- see Item 11 on the Delphi Printing Info and Links page.]

Future
A "Print Preview" would be a nice addition to this demonstration program to show how to draw on a canvas, whether the screen or the printer, in a device independent way.


Keywords
Delphi printing, Draw, StretchDraw, StretchDIBits, CopyRect, GetDeviceCaps, Pixels Per Inch, Aspect Ratio, Fonts, Scanline, HSVtoRGB, TextOut, Rect, sorting using TStringList, Transparency

Files
Delphi 3/4/5 Source and EXE (218 KB): PrinterDemo1.ZIP


Delphi Conversion Notes
No changes are needed to compile in D3-D5.
Size of Delphi 3 EXE:  345 KB
Size of Delphi 4 EXE:  441 KB
Size of Delphi 5 EXE:  460 KB

Because of the use of Scanline, this program no longer works in D1 or D2, but some of the conditional compilation statements for D1/D2 are still present in the code.


Thomas Bornhaupt's Printer Demo 1  project that creates a preview version of the page that is to be printed.  (Delete RzButton, RzPanel, TMultiP, ToolWin from Uses to compile.)

This is a very interesting modification to Printer Demo 1.  Because of the huge bitmap that is created to allow a print preview, this program may only reliably work in Windows NT/2000 and may have problems in Win 95/98.


Updated 18 Feb 2002


since 1 Nov 1998