Groups | Blog | Home
all groups > dotnet drawing api > june 2005 >

dotnet drawing api : PrintDocument lines per page calculation


AMercer
6/8/2005 2:33:09 PM
At the end of this note is a VB .NET sample of printing a text file. MS
provided it in .NET help. It shows how to print lines of text all in the
same font. The PrintPage event handler calculates the number of lines of
text that can be printed as follows:
linesPerPage = e.MarginBounds.Height/printFont.GetHeight(e.Graphics)

Well, this cant be right, can it? With an old HP LaserJet 6P, with no
margins, MarginBounds.Height is 1100, or 11 inches. You lose 1/6 inch top
and bottom as nonprinting area, for a total of 1/3 inch.
e.Graphics.ClipBounds.Height is 1066.666, or 11 inches less 1/3 an inch, so
it would seem that the lines per page calculation would need to take this
into account. The code provided by MS in the sample yields linesperpage that
is 2 or 3 lines too big.

I have tried many ways to compute lines per page, and nothing seems to work
right in all cases. My objective is to get my VB program to know exactly how
many lines it can print to a page under Courier New font sizes 8, 10, and 12
points for portrait and landscape. I can't figure it out - sometimes there
is either a lost line or extra white space at the bottom of a page. By this
I mean if I code a calculation and run the 6 cases (portrait/landscape cross
8/10/12 points), the six answers I get are some mix of one too low, correct,
and one too high.

Subsequently, I have tried using
If Not e.Graphics.IsVisible(rf.X, rf.Y, 1, rf.Height) Then Exit While
In other words, I print lines until the IsVisible function indicates not
visible, and end the while loop this way rather than by counting lines. This
shows some promise, but I have more testing to do.

So, how are you supposed to do this simple thing? My printing requirement
is for a very dense one page work sheet in a monospace font, and this means I
have to accurately know how many lines I can actually print.

Example Basic code follows:

Dim fileToPrint As System.IO.StreamReader
Dim printFont As System.Drawing.Font
Private Sub PrintButton_Click(sender As Object, e As EventArgs) Handles _
PrintButton.Click
Dim PrintPath As String = System.Environment. _
GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
fileToPrint = New System.IO.StreamReader(PrintPath & "\myFile.txt")
printFont = New System.Drawing.Font("Arial", 10)
PrintDocument1.Print()
fileToPrint.Close()
End Sub

Private Sub PrintDocument1_PrintPage(sender As Object, e As _
System.Drawing.Printing.PrintPageEventArgs) Handles _
PrintDocument1.PrintPage
Dim linesPerPage As Single = 0
Dim yPos As Single = 0
Dim count As Integer = 0
Dim leftMargin As Single = e.MarginBounds.Left
Dim topMargin As Single = e.MarginBounds.Top
Dim line As String = Nothing
linesPerPage = e.MarginBounds.Height/printFont.GetHeight(e.Graphics)
While count < linesPerPage
line = fileToPrint.ReadLine()
If line Is Nothing Then
Exit While
End If
yPos = topMargin + count * printFont.GetHeight(e.Graphics)
e.Graphics.DrawString(line, printFont, Brushes.Black, leftMargin, _
yPos, New StringFormat())
count += 1
End While
If Not (line Is Nothing) Then
e.HasMorePages = True
End If
End Sub
Ron Allen
6/9/2005 8:52:39 AM
What version of the Framework are you using? When I do a standard print
and check e.MarginBounds on my system with FW 1.1 and a Lexmark printer I
get 900 returned which is a 1" margin around the paper height.
You can set the margins if desired or if you really need the printable
area you can PInvoke GetDeviceCaps to get the actual printable size of the
current page. Search for GetHardMargins in
microsoft.public.dotnet.languages.vb for a translation of my C# routine to
do this to VB (done by H. Wagner). Using the height returned by this you
should be able to do an exact fit.

Ron Allen

[quoted text, click to view]

AMercer
6/9/2005 10:02:03 AM
Thanks for the reply.

[quoted text, click to view]
FW 1.1, SP1

I've been testing with e.Graphics.IsVisible as I indicated in the original
note. Recall that there is a 1/6 inch no-print area at the top and bottom of
the HP 6P. If I Print with a 1" margin top and bottom, I actually get 1" +
1/6" margins.

So... It looks like using e.Graphics.IsVisible will work fine to decide when
enough lines have been printed. If e.Graphics.IsVisible is true, then ink
showd up on paper, and if false, no ink. (More testing to be done.) Before
beginning printing, I can use this trick to determine the line count if I
need it. Finally, since margins seem to be additive to the 1/6" hard
margins, I can make an adjustment by reducing the requested margin by 1/6".

This looks like it will work, but it seems a bit peculiar. The peculiarity
may arise from HP 6P, a roughly 10 year old printer product. I will worry
the problem of general purpose code getting bent by changes in support of one
particular device.

[quoted text, click to view]
Ron Allen
6/10/2005 8:41:31 AM
The only way to reliably determine the exact 'hard' margins of any
printer including all the new ones is to use PInvoke on GetDeviceCaps on the
printer's hDc. I use this all the time to get the margins around various
printers so I can properly align output text and, if the printers total
display area is too small, properly scale the output. Once you have the
hard margins you will know exactly where to print and you can
TranslateTransform the Graphics so that the 0,0 location is at the top right
of the physical page instead of the printable area.
Also in your case with FW1.1 you could set OriginAtMargins to true so
that your printout would start at the margins (assuming they are set within
the printable area).

If you use FW 2.0 there are some PrintDocument properties added that get
this for you.

Ron Allen
[quoted text, click to view]
---------------------snip-----------

AMercer
6/13/2005 10:30:02 AM
Thanks for the help. I've learned a few things over the last few days, and
this issue is close to solved. I think I'll finish it under FW 2.0.

[quoted text, click to view]
AddThis Social Bookmark Button