Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to print text in C#

0.00/5 (No votes)
20 Apr 2005 1  
How printing works in Multipad, a Notepad clone.

Introduction

Multipad is my Notepad clone. It also has the typical Page Setup, Print Preview and Print menu items. Read on if you want to know how printing works in Multipad. It is best to download the source code and take a look at it while reading the article.

The PrintDocument

PrintDocument is a .NET class which glues together the page settings, the printer settings, the data to print and the print logic. For Multipad, I derived a class from PrintDocument called MultipadPrintDocument. It has properties to set the text (which can contain Environment.NewLines) and the font.

MultipadPrintDocument implements the virtual methods OnBeginPrint() and OnPrintPage(). OnBeginPrint() sets the pointer to the current character (_offset) to 0 and the current page number to 1.

OnPrintPage() has one parameter of type PrintPageEventArgs. When the system calls OnPrintPage(), we have to typeset the page using the Graphics object in the PrintPageEventArgs parameter. We typeset the page by printing lines of text until the page is full or until we reach the end of the text. The PrintPageEventArgs parameter has a property MarginBounds which contains the page size. Unfortunately, the dimensions are expressed in hundredths of an inch. In .NET, there is no GraphicsUnit which represents a hundredth of an inch. Because we're going to measure strings in GraphicsUnit.Document (1/300th of an inch), we convert the page dimensions by multiplying them with 3. Now we're ready to start printing the lines of the text.

First, we create a StringBuilder which will hold the text of a single line. We also create a GenericTypographic StringFormat object. It's very important to use GenericTypographic because otherwise, MeasureString() and DrawString() wouldn't behave as expected. We also set the tab stops: if a line of text contains tabs, the text will be formatted correctly (the tabs will not be replaced with spaces, they will act as real tabs). Finally, we set the PageUnit property of the Graphics object to GraphicsUnit.Document.

Before we start filling the page with lines, we check to see if there is enough space to print at least four lines: one line of text and three lines for the page number (two empty lines above the page number). If there isn't enough room, we simply leave the page blank. This situation should rarely occur.

Now we're ready to break up the text in lines. We read the text string one character at a time. We add the character to the line buffer (the StringBuilder object), except if the character is a NewLine or Eos (end of string). Note that NewLine is "\r\n" in Windows or "\n" in Unix, so we have to keep in mind that we don't simply skip one character but Environment.NewLine.Length characters (yes, we strive to perfection). If the character is a space or a tab, we save this position because when the line is full, we don't want to break the line in the middle of a word, no, we will print the line up to the last tab or space. For example, if we have the line "The quick brown ... lazy dog." and the line overflows at the 'd' of "dog", we will begin the next line with "dog" instead of "og". If the last character is a space or tab, we continue to add these characters. This way, the next line will never start with white space. If the line overflows or we encounter a NewLine or Eos, we print the text in the line buffer. Then we increment the y position of the next line and empty the line buffer. If there's still room for a new line of text and we still have text to print, we continue the loop; if not, we exit the loop, print the page number at the bottom of the page, increment the page number and set the property HasMorePages of the PrintPageEventArgs parameter to true if there's still text to print, otherwise we set it to false.

Using MultipadPrintDocument in your own code

If you want to use MultipadPrintDocument in your own code, it's best to rename the class and its constructor to something more appropriate. Then add a variable of type XXXPrintDocument to your form and implement the menu items as follows:

void OnFilePrint(Object sender, EventArgs e)
{
    PrintDialog pd = new PrintDialog();
    _printdocument.Text = _multipadbox.Text;
    _printdocument.Font = _multipadbox.Font;
    pd.Document = _printdocument;
    pd.ShowDialog();
}

void OnFilePrintPreview(Object sender, EventArgs e)
{
    PrintPreviewDialog ppd = new PrintPreviewDialog();
    _printdocument.Text = _multipadbox.Text;
    _printdocument.Font = _multipadbox.Font;
    ppd.Document = _printdocument;
    ppd.ShowDialog();
}

void OnFilePageSetup(Object sender, EventArgs e)
{
    PageSetupDialog psd = new PageSetupDialog();
    psd.Document = _printdocument;
    psd.ShowDialog();
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here