home page blog programming articles

Print preview in VB using metafiles

Sample Code

The sample code for this article is here

Whats the problem?

Writing a print preview in VB is difficult. Especially if you want to use the printer object directly. Microsoft suggest what you do (in knowledge base article Q193379) is to use a scale and multiply things up, passing into your routine either a printer object or picture object. So you end up with code that looks like this:

Private Sub DoDraw(O As Object)
   Dim i As Long
   For i = 0 To 1440 Step 144
     O.Line (i * Zoom, 0)-(i * Zoom, 1440 * Zoom), &H808080
     O.Line (0, i * Zoom)-(1440 * Zoom, i * Zoom), &H808080
   Next
   O.CurrentX = 0
   O.CurrentY = 0
   O.Font.Name = "Arial"
   O.Font.Size = 12 * Zoom
   O.Print "Top Left"
End Sub
Private Sub mnPrintMe_Click()
   Zoom = 1
   DoDraw Printer
   Printer.EndDoc
End Sub
Private Sub mnZoom200_Click()
   Zoom = 2
   imgPreview.Cls
   DoDraw imgPreview
End Sub

This all looks fine until you try and zoom it. This fixable with by using Windows meta files, some discussion of which follows (actually the same kinda thing happens with the Zoom provided by MFC code it's not quite as noticable)

What comes out on the printer

Using the sample code from the top of the page if you select Tests/Show type 1 in VB then select Print me from the form that pops up you get the following output (scanned at 600 dpi):

If you use the GDI enhanced meta file method by selecting Tests/Add Test type 1 page then file print the output looks like this:

To get output like this you need to make sure the printer is downloading truetype fonts as softfonts NOT using the printer fonts

What does zooming look like native

The big problem with zooming by using the MS recommended method is that it never looks anything like the printer output. This is due to font rounding on screen. (These screen captures were done with standard font smoothing on, and then resized to the same size) The table below shows what happens to "ft" out of top left as you zoom.

100 %200 %400 %800 %

As you can see the "ft" drift relative to lines and the only bit that looks right is 400%. If you want to draw bounding boxes around text this is a real pain

Now heres the same thing drawn using metafiles

100 %200 %400 %800 %

As can clearly be seen the whole thing looks much better even 100% is a pretty good appoximation

Heres the unzoomed samples

VB NativeVia metafile
100%
200%
400%
800%

Solving the problem and some notes

Basically the problem is solved by using a metafile created using the printers DC then drawing on the metafile using normal GDI commands.The metafile is replayed back to screen correctly scaled or 1 to 1 when going to the printer. This means you need to allocate things like fonts and pens. The sample code illustrates this but isn't complete. The scroll routines need rewriting (The original used some custom controls) but it should be enough to get you started.

The code contained here is ripped off out of a our commercial product and doesn't cover everything but it works well enough to get you started. However you will need to add a pile of code to turn it from its present state to something thats shippable

One I'll get around to converting this to .NET but until I do this isn't a bad place to start from