Using the printer

From Lazarus wiki
Revision as of 00:04, 30 October 2010 by Abeomega (talk | contribs) (Article discussing the basic steps in using the printer object. Information collected from forums and sampels.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Introduction

Printing is easy in FreePascal. But, only after you follow some required steps. First steps first. After those first steps, you can branch out to more advanced printing. This article discusses the required steps, as collected from various forum pages and examples. After the basic steps, we are going to print some text. Finally a hint of some more advanced printing is given.

The Basic Steps

The following you must do to be able to use printers.

  1. Add the Printer4Lazarus package to your project requirements.
  2. Add the Printers unit to the uses section of your unit.
  3. Use the existing Printer object.

Adding the Printer4Lazarus package to your project requirements

The Printer4Lazarus package defines a basic printer and provides platform independent printing. The following can thus be used on various platforms.

In the Lazarus IDE, do the following:

  1. In the Project menu, click Project Inspector. A window is shown with a tree view, one of the branches is named Required Packages. By default, the Required Packages branch shows the LCL package.
  2. Click the Add button, the button with the plus sign at the top of the window.
  3. Open the New Requirements page.
  4. From the Package Name list box, select Printer4Lazarus.
  5. Click OK.
  6. Printer4Lazarus is now shown in the Required Packages branch.

Adding the Printers unit to the uses section of your unit

This is simple and straight forward and could look like this:

unit MainUnt;

{$mode objfpc}{$H+}

interface

uses
 Classes, SysUtils, Forms, Printers;

Using the existing Printer object

Assuming you want to click a button to print a text. On you form you put a button called PrintBtn and in the OnClick event you can now use the following:

procedure TForm1.PrintBtnClick(Sender: TObject);
const
  LEFTMARGIN = 100;
  HEADLINE = 'I Printed My Very First Text On ';
var
  YPos, LineHeight, VerticalMargin : Integer;
  SuccessString : String;
  MyPrinter : TPrinter;
begin
  MyPrinter := Printer;
  MyPrinter.BeginDoc;
    MyPrinter.Canvas.Font.Name := 'Courier New';
    MyPrinter.Canvas.Font.Size:=10;
    MyPrinter.Canvas.Font.Color:= clBlack;
    LineHeight := Round(1.2 * Abs(MPrinter.Canvas.TextHeight('I')));
    VerticalMargin := 4 * LineHeight;
    //There we go
    YPos := VerticalMargin;
    SuccessString := HEADLINE + DateTimeToStr(Now);
    MPrinter.Canvas.TextOut(LEFTMARGIN, YPos, SuccessString);
  MyPrinter.EndDoc;
 end;

Did I write basic and simple. The above example is somewhat complex. Next to the basic text output in the bold line, it also provides an example of formatting your text.

From begin to end; the following happens.

  • You can use the Printer object directly, without your own variable such as MyPrinter. So, the MyPrinter object is not really needed, it is just the way how I want to write my code.
  • With MyPrinter.BeginDoc you start printing, however nothing is sent to the printer until you finish with MyPrinter.EndDoc;.
  • The printer uses a Canvas to draw the output on. It is this drawing that ends up on the page. Canvas.Font is the font object for that moment. That is, the TextOut call we use later will output text using the settings of the font object of that moment.
  • Everything you draw on the canvas must be positioned using coordinates. So, we calculate a LineHeight to position text vertically. You could do the same for the horizontal position, which I left here to be LEFTMARGIN.
  • The text is drawn with the TextOut call.
  • This magnificent result is sent to the printer by MyPrinter.EndDoc.

In some forums it is suggested that the use of PrintDialog (the printer selection dialog) is required for good functioning, but I did not find this to be true (any more).

Next steps

After the above basic steps, you can do the next steps. I leave it to the reader to try:

  • Make drawing on the paper.
  • Format text nicely.
  • Select another printer and compare results.

In the Lazarus distribution there is an example project that uses Raw printing to the printer. You can find this example with the following location(on Windows): C:\lazarus\components\printers\samples\rawmode\rawmodetest.lpi. Another sample shows how to select another printer: C:\lazarus\components\printers\samples\dialogs\selectprinter.lpi.

Advanced steps

The printer object primarily allows you to draw on a canvas and send that canvas image to the printer. If we continue on the path shown above, then you would use the printer's canvas methods to draw text, ellipses, diamonds and what not. However, this can hardly be interesting to any serious programmer. You are working on a more perfect CAD program or yet another image file sorter and want to print the wonderful result of your program to your printer. Trying to translate the perfect picture into canvas method calls is not the way, you already have the picture.

Each and every control you put on a form, also draws a picture on a TCanvas object just like the printer. We can use that to bring the big picture to the printer. Imagine you are going to make a preview program. You create a form and on this form you put a TPanel, this panel will provide the nice grayish background of the preview. On this panel you put another TPanel object called page. This page will be white and represents the paper. You can nicely size the page. On this page we put a TShape object, for example a nice red, rounded rectangle. Now try the following in the PrintBtnClick event method:

 MyPrinter.BeginDoc;
   page.PaintTo(myPrinter.Canvas, 0, 0);
 MyPrinter.EndDoc;

What happens:

  • BeginDoc starts the printing (but nothing is sent yet).
  • page.PaintTo sends the output of our TPanel object that represents the page to the canvas of the printer. Note the following:
    1. You can use the PaintTo method of any control in the control hierarchy. You could also send the output of the whole window to the printer if you want to.
    2. You can send the output of any control with a PaintTo method to the printer, be creative. To send the output of your image sorter to the printer you may send the output of the TImage to the printer.
    3. TCanvas also has a method to copy rectangles from another canvas. However, you can only do that if an object really draws to a canvas. I think most controls rely on containers to provide a real canvas, so you cannot copy rectangles from just any control. At least, it didn't work for me.
    4. Make sure the control you want to paint to the printer is visible. If the control is not visible, nothing will be painted not even to the printer.
  • EndDoc sends the drawing to the printer.

Stumbling

That is how far I am at this moment. Then I started stumbling. Scaling the output from a control for the printer. The printer uses a lot more pixels per inch on paper than the monitor uses pixels per inch on the screen. As a result, the output that is redirected from the screen to the printer ends up rather smallish on the paper. Scaling and controlling the layout is rather important for good looking output. It would be nice if you can have an exact sized copy of what you see on the screen.

The basic printer doesn't allow much interaction with the printer and hence I wasn't able to figure out how to scale the output. But, that is how a Lazarus life is. Find out and conquer.