Sending data to the printer

For desktop apps, printing is very similar to drawing to the screen. But instead of drawing into a Graphics object of a Canvas control, you draw to a Graphics object created specifically for printing. There are methods available to allow you to create new pages and get printer settings.

For web apps, printing is far more limited. Usually what works best is to create HTML, display it in an HTML Viewer control and then print that.

Desktop printing

Printer settings

Before printing anything, you usually want to give the user the ability to select the printer they want to use. This is done using the PrinterSetup class, which displays the Page Setup dialog for the operating system. In addition, this class returns the settings so that you can use them again later without prompting the user.

' PrinterSettings is a String property on the Window
Var ps As New PrinterSetup
ps.Settings = mPrinterSettings

If ps.PageSetupDialog Then
  PrinterSettings = ps.Settings
End If

Since PrinterSettings is a String, you can save it outside of your app (such as in a database or a file) for use the next time the user prints.

The PrinterSetup class has properties for printer settings such as landscape, page size and resolution. The PrinterSetup page has more details on these properties.

PrinterSetup is not supported in Linux apps.

Printing text and graphics

To print text and graphics you draw to the Graphics object for the printer. To get this Graphics objects, you call either the ShowPrinterDialog or PrinterSetup.OpenPrinter global methods. The only difference between these two methods is that one displays the Print dialog and the other does not. Since the printing system on macOS has the automatic capability to print to PDF, you can use this method to easily generate PDF files. On Windows you can install a "Print to PDF" printer driver to enable sending printed output to PDF files. If you need to generate PDF files, you can also use the PDFDocument class.

Since you are drawing to a Graphics object, all the commands covered in the Getting Started with Graphics topic can be used.

In addition, you also use the printing-specific method, NextPage to create multiple pages. This code prints “Hello” on page 1 and “World” on page 2:

Var ps As New PrinterSetup
If ps.ShowPageSetupDialog Then
  Var page As Graphics
  page = ps.ShowPrinterDialog
  If page <> Nil Then
    ' Draw text on page 1 with 1 inch left/top margin
    page.DrawText("Hello", ps.HorizontalResolution, ps.VerticalResolution)
    page.NextPage
    ' Draw text on page 2 with 1 inch left/top margin
    page.DrawText("World", ps.HorizontalResolution, ps.VerticalResolution)
  End If
End If

You can also use any printer settings that were previously stored from using the PrinterSetup class.

Var ps As New PrinterSetup
ps.Settings = PrinterSettings ' Property containing previously saved settings

If ps.PageSetupDialog Then
  PrinterSettings = ps.Settings ' Get new settings

  Var page As Graphics
  ' Use Printer Settings
  page = ps.ShowPrinterDialog
  If page <> Nil Then
    ' Draw text on page 1 with 1 inch left/top margin
    page.DrawText("Hello", ps.HorizontalResolution, ps.VerticalResolution)
    page.NextPage
    ' Draw text on page 2 with 1 inch left/top margin
    page.DrawText("World", ps.HorizontalResolution, ps.VerticalResolution)
  End If
End If

Each time you call NextPage, the current page is sent to the printer and the graphics object (in this case page) is cleared so that you can immediately begin drawing the new content. The final page gets sent to the printer when the page goes out of scope.

To print without displaying the Printer dialog, you call the OpenPrinter method:

Var ps As New PrinterSetup
Var page As Graphics
page = PrinterSetup.OpenPrinter(ps)
If page <> Nil Then
  ' Draw text on page 1 with 1 inch left/top margin
  page.DrawText("Hello", ps.HorizontalResolution, ps.VerticalResolution)
  page.NextPage
  ' Draw text on page 2 with 1 inch left/top margin
  page.DrawText("World", ps.HorizontalResolution, ps.VerticalResolution)
  page.NextPage
End If

Printing multiple pages of text

To print multiple pages, you need to parse each line and print it if it will fit on the page. If it will not fit, then you call NextPage so that your text will start printing at the top of the next page.

A common thing to do is to split the text into paragraphs and then track the height of the page and the height of the next paragraph to print to make sure that it will fit onto the page. If it will not fit, then the current page is sent to the printer and a new page is created to start printing the next paragraph.

This code shows you how you can print text from a TextArea onto multiple pages:

Var ps As New PrinterSetup
If ps.ShowPageSetupDialog Then
  Var page As Graphics
  page = ps.ShowPrinterDialog
  If page <> Nil Then
    ' Draw text on page 1 with 1 inch left/top margin
    Var leftMargin As Double = ps.HorizontalResolution
    Var topMargin As Double = ps.VerticalResolution
    page.FontSize = FontPopup.Text.Val

    ' To print multiple pages, you need to parse each line and print it
    ' if it will fit on the page. If it will not fit, then
    ' you call NextPage so that your text will start printing at the top
    ' of the next page.

    ' First, split the text into multiple paragraphs
    Var paragraphs() As String = TextArea1.Text.ReplaceLineEndings(EndOfLine).ToArray(EndOfLine)

    Var pageTextHeight As Double = topMargin
    Var newpageTextHeight As Double
    Var lineWidth As Double

    For i As Integer = 0 To paragraphs.LastIndex
      newpageTextHeight = pageTextHeight + page.TextHeight(paragraphs(i), ps.Width - leftMargin * 2)

      If newPageTextHeight > ps.Height - topMargin * 2 Then
        ' String does not fit on page, so move to next page
        page.NextPage
        pageTextHeight = topMargin
        newpageTextHeight = pageTextHeight + page.TextHeight(paragraphs(i), ps.Width - leftMargin * 2)
      End If
      ' If string fits on page, then draw it
      page.DrawText(paragraphs(i), leftMargin, pageTextHeight, ps.Width - leftMargin * 2)
      ' Keep a cumulative count of the pageheight as strings are added to it
      pageTextHeight = newpageTextHeight ' Adjust page text height
      pageTextHeight = pageTextHeight + topMargin / 8 ' 1/8 inch between paragraphs
    Next
  End If
End If

The above code gives you a lot of control, but it is more work to track everything yourself. In the next section you'll see how you can take advantage of StyledTextPrinter to make this easier.

Printing styled text

Because TextAreas are capable of displaying styled text and multiple font sizes, you will usually want to retain the styled text when you print. The StyledTextPrinter class is used for this purpose, using the DrawBlock method.

The StyledTextPrinter class only works with MacOS apps.

To print styled text, you first create a StyledTextPrinter object and then call the StyledTextPrinter method of the TextArea (specifying the graphics to use and the width of the text) to get an instance of a StyledTextPrinter that can be used for printing.

With this instance, you can call the DrawBlock method to draw the styled text to the page (specifying the starting coordinates and the height).

This code prints styled text in a TextArea:

Var stp As StyledTextPrinter
Var g As Graphics
Var p As New PrinterSetup

If p.ShowPageSetupDialog Then
  g = ps.ShowPrinterDialog

  If g <> Nil Then
    ' Set width as 7.5 inches
    stp = PrintTextArea.StyledTextPrinter(g, 7.5 * p.HorizontalResolution)
    Do Until stp.EndOfText
      ' Fill the page with text
      ' at 10 inches height
      stp.DrawBlock(0, 0, 10 * p.VerticalResolution)
      If Not stp.EndOfText Then
        ' There is more text, so add a page
        g.NextPage
      End If
    Loop
  End If
End If

In order to support styled printing, the Text Area must have both its Multiline and Styled properties ON (True).

If the text to print is larger than what will fit in the specified block, then you can iterate through the text until it is all printed. You do this by checking the EOF property of the StyledTextPrinter class after each call to DrawBlock.

This code prints the contents of a Text Area into two columns with a quarter inch spacing between the columns:

Var g As Graphics
Var p As New PrinterSetup

If p.ShowPageSetupDialog Then
  g = ps.ShowPrinterDialog

  ' 1 inch margin
  Var leftRightMargin As Double = 1 * p.HorizontalResolution
  Var topBottomMargin As Double = 1 * p.VerticalResolution

  ' Page width after accounting for margins
  Var pageWidth As Double = p.Width - (leftRightMargin * 2)

  ' Column gap is 1/4 of the margin (so, 1/4 inch)
  Var columnGap As Double = leftRightMargin / 4

  ' Calculate the column width
  ' Subtract column gap from page with and
  ' split result 2
  Var columnWidth As Double = (pageWidth - columnGap) / 2

  ' Page size after accounting for margins
  Var pageHeight As Double = p.Height - (topBottomMargin * 2)

  Var stp As StyledTextPrinter
  stp = PrintTextArea.StyledTextPrinter(g, pageWidth)
  stp.Width = columnWidth

  Var columnToPrint As Integer = 1
  Do Until stp.EndOfText
    stp.DrawBlock(leftRightMargin + (columnWidth + columnGap) * (columnToPrint - 1), _
    topBottomMargin, pageHeight)
    If columnToPrint = 2 Then ' printing last column
      If Not stp.EndOfText Then ' more text to print
        g.NextPage
        columnToPrint = 1
      End If
    Else ' more columns to print on this page
      columnToPrint = columnToPrint + 1
    End If
  Loop
End If

HTML printing

Another technique you can use is to create what you want to print using HTML, display it in an HTMLViewer and then call its print method. This can sometimes be useful for simple reports. As a simple example, this code loads an HTMLViewer with some HTML:

Var html As String = "<body><em>Hello</em>, World!</body>"
HTMLViewer1.LoadPage(html, FolderItem.TemporaryFile)

This code then displays the print dialog for the user to print the HTML:

HTMLViewer1.Print(True)

Web printing

Web printing is considerably simpler than desktop printing because it is much more restricted. Your web application runs in a web browser, so you are limited by what a web browser can print.

Web browsers generally do a good job of printing HTML, so in order to generate something to print, you want to first render it as HTML (either to a string or a file) and use an HTML Viewer control to display the HTML. Once you have what you want displayed in an HTML Viewer, you can have a button that calls the Print method of the HTML Viewer to ask the browser to print its contents:

HTMLViewer1.Print