Printing Text


The printing application described in the previous section is extremely simple. It prints a very straightforward shape on a single page. You know the positions of the diamond before starting, so the program needs to perform little arranging and formatting. The only formatting it does is to make its diamond fit the page’s margins.

This section describes a more useful example that prints a long series of paragraphs using different font sizes. The program must figure out how to break the text into pages. It also assumes that you will print the pages double-sided and then later bind the results into a booklet. To allow extra room for the binding, the program adds a gutter to the margin of edge on each page on the side where the binding will be. The program assumes that you will place the first page on the outside of the booklet, so it adds the gutter to the left margin on odd-numbered pages and to the right margin on even-numbered pages. Finally, the program displays a page number in the upper corner opposite the gutter.

Figure 24-4 shows the results displayed in a print preview dialog box, so you can understand the goals. If you look closely, you can see that the left margins on the first and third pages and the right margin on the second page are enlarged to allow room for the gutter. You can also see that the page numbers are in the upper corner on the side that doesn’t have the gutter. Imagine the second page printed on the back of the first, so their gutters lie on the same edge of the paper.

image from book
Figure 24-4: This preview shows text broken across pages with a gutter and displaying page numbers along the outside edges.

The following code shows the program that generated this printout. The program begins by declaring a new PrintDocument object with the WithEvents keyword. The program uses ParagraphInfo structures to hold information about the paragraphs it will print. Each ParagraphInfo entry contains a font size and some text. The m_Paragraphs collection holds the ParagraphInfo objects that describe the program’s text. The m_ParagraphsToPrint collection holds a copy of the text while the program is printing. The code uses this copy to keep track of the text that has yet to be printed. Variable m_PagesPrinted keeps track of the number of pages printed during the printing process. The code uses this value to display page numbers and to determine whether it is printing an odd or even page.

This little piece of code should have been omitted. When the program loads, the Form1_Load event handler attaches the program’s print and print preview dialogs to the PrintDocument object. It then creates the m_Paragraphs collection and adds ParagraphInfo objects to it. This is just a bunch of data, so little of it is shown here.

When the user clicks the Print Preview or Print Dialog buttons, the corresponding event handlers simply display the appropriate dialog boxes. When the user clicks the Print Now button, the program calls the PrintDocument object’s Print method.

More interesting differences between this program and the one described in the previous section come in the PrintDocument object’s event handlers. The BeginPrint event handler sets m_PagesPrinted to zero. It then fills the m_ParagraphsToPrint collection with copies of the ParagraphInfo structures holding the program’s text data. The program will use the objects in this collection to keep track of the paragraphs printed.

The QueryPageSettings event handler sets the margins for the page that the PrintDocument object is about the print. First, it sets the gutter size to 100 printer units (or one inch). Next, the program adjusts the margins based on the page number. If the page to be printed is the first page in the document, the program adds the gutter size to the left margin. If the page is some other odd-numbered page, the program moves the whole page to the right by increasing the left margin and decreasing the right margin. Finally, if the next page will be odd numbered, the program shifts the page to the left by decreasing the left margin and increasing the right margin.

The PrintPage event handler starts by incrementing the number of pages printed. It then includes commented code to draw a rectangle around the page’s margins. When you are debugging a printing routine, drawing this rectangle can help you see where your drawing is in relation to the page’s margins.

Next, the routine creates a font for the page number. Depending on whether this page is odd or even numbered, it calculates an X coordinate halfway between the nongutter margin and the edge of the printable page. It sets a StringFormat object’s Alignment property to make numbers in the left margin left-justified and to make numbers in the right margin right-justified. It then draws the page number at the calculated X position, halfway between the top margin and the paper’s top printable boundary.

The program then prepares to draw the text for this page. It sets the StringFormat object’s properties so that the text is left-justified and lines wrap at word boundaries instead of in the middle of words. It sets the FormatFlags property to LineLimit. If only part of a line of text would fit vertically on the page, this makes Visual Basic not draw the line rather than drawing just the top halves of its letters.

After this preparation, the program sets variable ymin to the minimum Y coordinate where the routine can draw text. Initially, this is the top margin. It then enters a Do loop to process as much text as will fit on the page.

Inside the loop, the program takes the first ParagraphInfo structure from the m_ParagraphsToPrint collection and makes a font that has the right size for that paragraph. It creates a RectangleF representing the remaining area on the page. This includes the area between the left and right margins horizontally, and between ymin and the bottom margin vertically.

The program then uses the e.Graphics object’s MeasureString method to see how much space the next piece of text will need. It passes MeasureString the layout rectangle’s size and the StringFormat object so that Visual Basic can decide how it will need to wrap the paragraph’s text when it prints it. The code also passes in the variables characters_fitted and lines_filled. These parameters are passed by reference, so MeasureString can fill in the number of characters and lines it could draw within the allowed size.

The routine then checks characters_fitted to see if any characters will fit in the available area. If any characters can fit, the program draws the paragraph. Commented code draws a rectangle around the text to help with debugging. The program increases ymin by the paragraph’s printed height plus half of the font’s height to provide a break between paragraphs.

Next, the program determines whether the entire paragraph fits in the allowed area. If some of the paragraph did not fit, the program stores the remaining text in the ParagraphInfo structure and puts the structure back at the beginning of the m_ParagraphsToPrint collection so that it can be printed on the next page. It then exits the Do loop because the current page is full.

When the page is full or the m_ParagraphsToPrint collection is empty, the PrintPage event handler is finished. It sets e.HasMorePages to True if m_ParagraphsToPrint is not empty.

Finally, when the PrintDocument has finished printing the whole document, the EndPrint event handler executes. This routine cleans up by setting the m_ParagraphsToPrint variable to Nothing, freeing up the collection object’s memory. In this program, freeing the collection is a small matter. In a program that allocated more elaborate data structures, cleaning up in this event handler would be more important.

  ' The PrintDocument. Private WithEvents m_PrintDocument As New PrintDocument ' Information about the paragraphs to print. Private Structure ParagraphInfo     Public FontSize As Integer     Public Text As String     Public Sub New(ByVal font_size As Integer, ByVal txt As String)         FontSize = font_size         Text = txt     End Sub End Structure ' The paragraphs. Private m_Paragraphs As Collection Private m_ParagraphsToPrint As Collection Private m_PagesPrinted As Integer ' Load the paragraph info. Private Sub Form1_Load(ByVal sender As Object, _  ByVal e As System.EventArgs) Handles MyBase.Load     ' Attach the PrintDocument to the      ' PrintDialog and PrintPreviewDialog.     dlgPrint.Document = m_PrintDocument     dlgPrintPreview.Document = m_PrintDocument     ' Make the text to print.     m_Paragraphs = New Collection     m_Paragraphs.Add(New ParagraphInfo(45, "23"))     m_Paragraphs.Add(New ParagraphInfo(27, "Printing"))     ... Code omitted... End Sub ' Display the preview dialog. Private Sub btnPrintPreview_Click(ByVal sender As System.Object, _  ByVal e As System.EventArgs) Handles btnPrintPreview.Click     dlgPrintPreview.WindowState = FormWindowState.Maximized     dlgPrintPreview.ShowDialog() End Sub ' Display the print dialog. Private Sub btnPrintDialog_Click(ByVal sender As System.Object, _  ByVal e As System.EventArgs) Handles btnPrintDialog.Click     dlgPrint.ShowDialog() End Sub ' Print immediately. Private Sub btnPrintNow_Click(ByVal sender As System.Object, _  ByVal e As System.EventArgs) Handles btnPrintNow.Click     m_PrintDocument.Print() End Sub ' Get ready to print pages. Private Sub m_PrintDocument_BeginPrint(ByVal sender As Object, _  ByVal e As System.Drawing.Printing.PrintEventArgs) _  Handles m_PrintDocument.BeginPrint     ' We have not yet printed any pages.     m_PagesPrinted = 0     ' Make a copy of the text to print.     m_ParagraphsToPrint = New Collection     For Each para_info As ParagraphInfo In m_Paragraphs         m_ParagraphsToPrint.Add(_             New ParagraphInfo(para_info.FontSize, para_info.Text))     Next para_info End Sub ' Set the margins for the following page. Private Sub m_PrintDocument_QueryPageSettings(ByVal sender As Object, _  ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) _  Handles m_PrintDocument.QueryPageSettings     ' Use a 1 inch gutter (printer units are 100 per inch).     Const gutter As Integer = 100     ' See if the next page will be the first, odd, or even.     If m_PagesPrinted = 0 Then         ' The next page is the first.         ' Increase the left margin.         e.PageSettings.Margins.Left += gutter     ElseIf (m_PagesPrinted Mod 2) = 0 Then         ' The next page will be odd.         ' Shift the margins right.         e.PageSettings.Margins.Left += gutter         e.PageSettings.Margins.Right -= gutter     Else         ' The next page will be even.         ' Shift the margins left.         e.PageSettings.Margins.Left -= gutter         e.PageSettings.Margins.Right += gutter     End If End Sub ' Print the next page.Private Sub m_PrintDocument_PrintPage(ByVal sender As Object, _  ByVal e As System.Drawing.Printing.PrintPageEventArgs) _  Handles m_PrintDocument.PrintPage     ' Increment the page number.     m_PagesPrinted += 1     ' Draw the margins (for debugging).     'e.Graphics.DrawRectangle(Pens.Red, e.MarginBounds)     ' Print the page number right justified      ' in the upper corner opposite the gutter     ' and outside of the margin.     Dim x As Integer     Using string_format As New StringFormat         ' See if this is an odd or even page.         If (m_PagesPrinted Mod 2) = 0 Then             ' This is an even page.              ' The gutter is on the right and             ' the page number is on the left.             x = (e.MarginBounds.Left + e.PageBounds.Left) \ 2             string_format.Alignment = StringAlignment.Near         Else             ' This is an odd page.             ' The gutter is on the left and             ' the page number is on the right.             x = (e.MarginBounds.Right + e.PageBounds.Right) \ 2             string_format.Alignment = StringAlignment.Far         End If         ' Print the page number.         Using the_font As New Font("Times New Roman", 20, _           FontStyle.Regular, GraphicsUnit.Point)             e.Graphics.DrawString(m_PagesPrinted.ToString, _                 the_font, Brushes.Black, x, _                 (e.MarginBounds.Top + e.PageBounds.Top) \ 2, _                 string_format)         End Using ' the_font         ' Draw the rest of the text left justified,         ' wrap at words, and don't draw partial         string_format.Alignment = StringAlignment.Near         string_format.FormatFlags = StringFormatFlags.LineLimit         string_format.Trimming = StringTrimming.Word         ' Draw some text.         Dim paragraph_info As ParagraphInfo         Dim ymin As Integer = e.MarginBounds.Top         Dim layout_rect As RectangleF         Dim text_size As SizeF         Dim characters_fitted As Integer         Dim lines_filled As Integer         Do While m_ParagraphsToPrint.Count > 0             ' Print the next paragraph.             paragraph_info = DirectCast(m_ParagraphsToPrint(1), ParagraphInfo)             m_ParagraphsToPrint.Remove(1)             ' Get the area available for this paragraph.             layout_rect = New RectangleF( _                 e.MarginBounds.Left, ymin, _                 e.MarginBounds.Width, _                 e.MarginBounds.Bottom - ymin)             ' See how big the text will be and              ' how many characters will fit.             ' Get the font.             Using the_font As New Font("Times New Roman", _               paragraph_info.FontSize, FontStyle.Regular, GraphicsUnit.Point)                 text_size = e.Graphics.MeasureString( _                     paragraph_info.Text, the_font, _                     New SizeF(layout_rect.Width, layout_rect.Height), _                     string_format, characters_fitted, lines_filled)                 ' See if any characters will fit.                 If characters_fitted > 0 Then                     ' Draw the text.                     e.Graphics.DrawString(paragraph_info.Text, _                         the_font, Brushes.Black, _                         layout_rect, string_format)                     ' Debugging: Draw a rectangle around the text.                     'e.Graphics.DrawRectangle(Pens.Green, _                     '    layout_rect.Left, _                     '    layout_rect.Top, _                     '    text_size.Width, _                     '    text_size.Height)                     ' Increase the location where we can start.                     ' Add a little interparagraph spacing.                     ymin += CInt(text_size.Height + _                         e.Graphics.MeasureString("M", the_font).Height / 2)                 End If             End Using ' the_font             ' See if some of the paragraph didn't             If characters_fitted < Len(paragraph_info.Text) Then                 ' Some of the paragraph didn't                 ' Prepare to print the rest on the next page.                 paragraph_info.Text = paragraph_info.Text. _                     Substring(characters_fitted)                 m_ParagraphsToPrint.Add(paragraph_info, Before:=1)                 ' That's all that will fit on                 Exit Do             End If         Loop     End Using ' string_format     ' If we have more paragraphs, we have more pages.     e.HasMorePages = (m_ParagraphsToPrint.Count > 0) End Sub ' Clean up. Private Sub m_PrintDocument_EndPrint(ByVal sender As Object, _  ByVal e As System.Drawing.Printing.PrintEventArgs) _  Handles m_PrintDocument.EndPrint     m_ParagraphsToPrint = Nothing End Sub 




Visual Basic 2005 with  .NET 3.0 Programmer's Reference
Visual Basic 2005 with .NET 3.0 Programmer's Reference
ISBN: 470137053
EAN: N/A
Year: 2007
Pages: 417

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net