Using the printer/de

From Free Pascal wiki
Revision as of 16:54, 21 September 2013 by Billyraybones (talk | contribs) (translation continued)

Deutsch (de) English (en) español (es) 日本語 (ja) 中文(中国大陆)‎ (zh_CN)

Einführung

Drucken ist einfach in Lazarus/FreePascal. Dennoch sollten Sie einige erforderliche Schritte beachten. Wir fangen mit den ersten einfachen Schritten an. Anschließend können Sie dem Weg zu gehobenen Drucktechniken folgen. Dieser Artikel beschreibt die dazu erforderlichen Schritte, die von verschiedenen Forumsseiten und Beispielen zusammengetragen wurden. Nach den grundlegenden Schritten werden wir etwas Text ausdrucken. Zuletzt gibt es noch einen Tipp für das etwas fortgeschrittenere Ausdrucken.

Die grundlegenden Schritte

Um Drucker zu benutzen, müssen Sie Folgendes beachten:

  1. Fügen Sie das Printer4Lazarus - Package zu Ihren Projektanforderungen hinzu.
  2. Fügen Sie die Printers - Unit zum uses-Abschnitt Ihrer Unit hinzu.
  3. Verwenden Sie das existierende Printer - Objekt.

Fügen Sie das Printer4Lazarus - Package zu Ihren Projektanforderungen hinzu

Das Printer4Lazarus - Package legt einen Drucker als Grundlage fest und bietet somit plattformunabhängiges Ausdrucken. Deshalb kann folgendes Verfahren auf verschiedenen Plattformen eingesetzt werden.

Führen Sie in der Lazarus IDE folgende Schritte durch:

  1. Klicken Sie im Menü Projekt auf Projektinspektor .... Es wird ein Fenster mit einer Baumstruktur angezeigt. Einer der Zweige lautet Benötigte Packages. Als Standard wird von Benötigte Packages das LCL - Package angezeigt.
  2. Klicken Sie die Schaltfläche Hinzufügen, den Knopf mit dem Pluszeichen am oberen Fensterrand.
  3. Öffnen Sie die Seite Neue Anforderung.
  4. Aus dem Listenfeld Package-Name wählen Sie Printer4Lazarus aus.
  5. Klicken Sie auf Neue Abhängigkeit erzeugen.
  6. Printer4Lazarus wird jetzt im Zweig Benötigte Packages angezeigt.

Fügen Sie die Printers - Unit zum uses-Abschnitt Ihrer Unit hinzu

Dieser Schritt ist einfach und das Ergebnis sollte so ähnlich aussehen:

unit MainUnit;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, Forms, Printers;

Verwenden Sie das existierende Printer - Objekt

Nehmen wir an, Sie wollen zum Ausdrucken eines Textes eine Schaltfläche verwenden. Legen Sie auf Ihr Form eine Schaltfläche namens PrintBtn und bearbeiten Sie das OnClick-Ereignis zu Folgendem:

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

Habe ich grundlegend und einfach geschrieben? Das obige Beispiel ist schon irgendwie komplex. Neben der reinen Textausgabe stellt es auch ein Beispiel dar, wie Sie den Text formatieren können.

Von begin bis zum end; geschieht Folgendes:

  • With Printer.BeginDoc you start printing - however, nothing is sent to the printer until you finish with Printer.EndDoc;.
  • The printer uses a Canvas to draw the output on. It is this drawing that ends up on the printed page. Canvas.Font is the font object used for the output canvas. 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 Printer.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).

Die nächsten Schritte

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

  • Make drawings 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 $(lazarusdir)\components\printers\samples\rawmode\rawmodetest.lpi. Another sample shows how to select another printer: $(lazarusdir)\components\printers\samples\dialogs\selectprinter.lpi.

Advanced steps: printing controls

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 picture from the screen 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 the 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, so you can 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.

Und weiter geht's: Änderung der Größe

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 important for good looking output. It would be nice if you can have an exact sized copy of what you see on the screen.

For the moment we are not striving for the ideal, just for the idea. As said before, controls do not have their own canvas but rely on the canvas of a container or owner. However, there are components that have their own canvas. I chose TBitMap and then it works as follows.

procedure TForm1.PrintBtnClick(Sender: TObject);
var
  MyPrinter : TPrinter;
  myBitMap : TBitMap;
begin
  myBitMap := TBitMap.Create;
  myBitMap.Width := page.Width;
  myBitMap.Height := page.Height;
  page.BorderStyle:=bsNone;
  page.PaintTo(myBitMap.Canvas, 0, 0);
  page.BorderStyle:=bsSingle;
  //
  MyPrinter := Printer;
  MyPrinter.BeginDoc;
    //page.PaintTo(myPrinter.Canvas, 0, 0);
    //myPrinter.Canvas.Draw(0,0, myBitMap);
    myPrinter.Canvas.CopyRect(Classes.Rect(0, 0, myPrinter.PaperSize.Width, myPrinter.PaperSize.Height),
       myBitMap.Canvas, Classes.Rect(0, 0, myBitMap.Width, myBitMap.Height));
  MyPrinter.EndDoc;
  myBitMap.Free;
end;

For this to work, do not use the Windows unit. The Windows unit has other definitions for Rect. What you see in the example is the following:

  • A bitmap is created and made the same size as the page control.
  • To avoid the border to print, the BorderStyle of the page is switched off before painting it to the bitmap and set back to its old value afterwards.
  • Then printing is started and the BitMap canvas content is copied to the printer canvas.

But notice one thing, in the process page is magnified. The Printer.papersize is considerably bigger than the the size of the bitmap, but the copy process fills the destination rectangle nicely. So, when we want to do this, we must make sure that the page dimensions have the same ratio as the paper dimension. You can figure out how to do it.

A problem with this way of working is of course that the pixels of the screen will show on the printed paper. As said, it is not ideal but it shows a principle. Controls do not have their own canvas; to print controls we first paint them on an object that does have its own canvas: the TBitMap. Now you know how it works, you can figure out a way to create fine artwork or documents. This will need objects that draw themselves properly in a TPanel with low resolution, but also on a TBitMap with high resolution. But, that is food for another article.