Guido Krüger - JAVA 1.1 lernen - Kapitel 15

Previous Page Previous Page TOC TOC Index Next Page Previous Page



Drucken in Java

Grundlagen

Seit der Version 1.1 kann in Java gedruckt werden! Das ist deshalb bemerkenswert, weil es in den Vorgängerversionen keine Möglichkeit gab, dies zu tun. Im Laufe der Zeit entwickelten sich zwar einige inkompatible Erweiterungen, keine von ihnen konnte sich jedoch auf breiter Front durchsetzen.

Um so erfreulicher ist es, daß das Druck-API der Version 1.1 relativ übersichtlich gehalten wurde und leicht zu verstehen ist. Es gibt zwar einige Restriktionen und Besonderheiten zu beachten, die beim Erstellen von Druckausgaben zu Problemen führen können, aber für einfache Ausdrucke von Text und Grafik ist die Schnittstelle recht gut geeignet.

Grundlage der Druckausgabe ist die Methode getPrintJob der Klasse Toolkit:

public PrintJob getPrintJob(
   Frame frame,
   String jobtitle,
   Properties props
);

Sie liefert ein Objekt des Typs PrintJob, das zur Initialisierung eines Druckjobs verwendet werden kann. Ein Aufruf von getPrintJob führt gleichzeitig dazu, daß ein plattformspezifischer Druckdialog aufgerufen wird, der vom Anwender bestätigt werden muß. Bricht der Anwender den Druckdialog ab, liefert getPrintJob den Rückgabewert null, andernfalls wird der Drucker initialisiert und die Ausgabe kann beginnen.

Die Klasse PrintJob stellt einige Methoden zur Verfügung, die für den Ausdruck benötigt werden:

public Graphics getGraphics();

public Dimension getPageDimension();

public int getPageResolution();

public abstract void end();

Die wichtigste von ihnen ist getGraphics. Sie liefert den Devicekontext zur Ausgabe auf den Drucker. Der Rückgabewert ist ein Objekt vom Typ PrintGraphics, das aus Graphics abgeleitet ist und wie ein normaler Devicekontext verwendet werden kann, der beispielsweise an paint übergeben wird. Mit Hilfe des von getGraphics zurückgegebenen Devicekontexts können alle Grafik- und Textroutinen, die auch in Graphics zur Verfügung stehen, verwendet werden. Bezüglich der Verwendung von Farben gilt scheinbar, daß diese bei den linienbezogenen Ausgaberoutinen nicht unterstützt werden. Hier wird alles schwarz gezeichnet, was nicht den Farbwert (255, 255, 255) hat. Im Gegensatz dazu stellen die Füllfunktionen Farben (auf einem Schwarzweiß-Drucker) als Grauwerte dar. Dabei kann - je nach Druckertyp - auch Weiß eine Vollfarbe sein und dahinter liegende Objekte verdecken.

Seitenweise Ausgabe

Ein wichtiger Unterschied zu einem bildschirmbezogenen Devicekontext besteht darin, daß jeder Aufruf von getGraphics eine neue Druckseite beginnt. Die fertige Druckseite wird durch Aufruf von dispose an den Drucker geschickt. Für den Aufbau der Seite, das Ausgeben von Kopf- oder Fußzeilen, die Seitennumerierung und ähnliche Dinge ist die Anwendung selbst verantwortlich. Die Methode end ist aufzurufen, wenn der Druckjob beendet ist und alle Seiten ausgegeben wurden. Dadurch werden alle belegten Ressourcen freigegeben und die Druckerschnittstelle geschlossen.

Hinweis

Bei der Druckausgabe ist es wichtig zu wissen, wie groß die Abmessungen des Ausgabegeräts sind. Die hierzu angebotenen Methoden getPageDimension und getPageResolution sind in der aktuellen Version leider vollkommen unbrauchbar. getPageResolution liefert die tatsächliche Auflösung des Druckers in Pixel per Zoll (also z.B. 600 für einen Laserjet IV), während getPageDimension die Anzahl der Pixel liefert, die sich errechnet, wenn man ein Blatt Papier im US-Letter-Format (8,5 mal 11 Zoll) mit 72 dpi Auflösung darstellen würde. Leider erfolgt die Druckausgabe nicht mit 72 dpi, sondern in der aktuellen Bildschirmauflösung, wie sie von der Methode getScreenResolution der Klasse Toolkit geliefert wird. Da diese typischerweise bei 120 dpi liegt, füllen die von getPageResolution gelieferten Abmessungen nur etwa 60 % einer Seite.

Derzeit gibt es keine portable Lösung für dieses Problem. Ein Workaround besteht darin, die Papiergröße als fest anzunehmen (beispielsweise DIN A4 mit 21,0*29,7 cm), davon den nicht bedruckbaren Rand abzuziehen, das Ergebnis durch 2,54 (Anzahl cm je Zoll) zu teilen und mit der Auflösung von 120 dpi malzunehmen. Wir werden weiter unten ein Beispiel sehen, in dem diese Technik angewandt wird. Portabel ist sie allerdings nicht, denn das Programm muß Annahmen über die Papier- und Randgröße beisteuern. Es bleibt demnach zu hoffen, daß die nachfolgenden Versionen des JDK die Bestimmung der Abmessungen auf eine flexiblere Weise ermöglichen.

Warnung

Durch die fixe Einstellung der Ausgabeauflösung ergibt sich ein weiteres Problem. So kann ein Drucker mit 600 dpi aus Java heraus nämlich nur mit der aktuellen Bildschirmauflösung (z.B. 120 dpi) angesteuert werden. Das bedeutet zwar nicht automatisch, daß Schriften oder schräge Linien mit Treppenmustern dargestellt werden, denn sie werden meist als Vektorgrafiken an den Drucker übergeben. Allerdings können beispielsweise keine Pixelgrafiken in der aktuellen Druckerauflösung ausgegeben werden, denn die Positioniergenauigkeit eines einzelnen Pixels liegt bei 120 dpi. Eine Lösung für dieses Problem ist derzeit nicht bekannt.

Plazierung des Codes zur Druckausgabe

Es gibt grundsätzlich zwei Möglichkeiten, die Druckausgabe im Programm zu plazieren. Einmal kann die normale paint-Methode dazu verwendet werden, sowohl Bildschirm- als auch Druckausgaben zu realisieren. Bei einem Aufruf der Methode print oder printAll der Klasse Component wird nämlich ein PrintJob erstellt, daraus der Grafikkontext beschafft und an paint übergeben:

public void print(Graphics g);

public void printAll(Graphics g);

Auf diese Weise kann bereits ohne zusätzliche Erweiterungen eine einfache Druckausgabe realisiert werden, die der Bildschirmausgabe realtiv ähnlich sieht. Im Gegensatz zu print gibt printAll dabei nicht nur die aktuelle Komponente, sondern die komplette Container-Hierarchie eines komplexen Dialogs aus. Soll innerhalb von paint unterschieden werden, ob auf den Bildschirm oder den Drucker ausgegeben wird, so kann mit dem Ausdruck g instanceof PrintGraphics das übergebene Graphics-Objekt g auf Zugehörigkeit zur Klasse PrintGraphics getestet werden.

Die zweite Möglichkeit, die Druckausgabe zu plazieren, besteht darin, eine eigene Methode zu schreiben, die nur für die Ausgabe auf den Drucker verantwortlich ist. Diese könnte zunächst den PrintJob und das PrintGraphics-Objekt beschaffen und anschließend die Abmessungen der Ausgabefläche wie oben besprochen bestimmen. Die Methode wäre dann nicht darauf angewiesen, daß sie sowohl für die Bildschirm- als auch für die Druckausgabe vernünftige Resultate liefern muß, sondern könnte ihre Ausgaben für die Druckausgabe optimieren. Der Nachteil bei dieser Löung ist allerdings, daß möglicherweise Code zum Erstellen der Ausgabe doppelt vorhanden ist und doppelt gepflegt werden muß.

Beispiel

Das nachfolgende Listing kombiniert beide Varianten und demonstriert dies am Ausdruck eine Testseite. Das Programm erstellt ein Hauptfenster und ruft zwei Sekunden später die Methode printTestPage zur Druckausgabe auf. Darin wird zunächst ein PrintJob erzeugt und dann gemäß dem oben beschriebenen Verfahren die Ausgabegröße ermittelt. Anschließend wird der Grafikkontext beschafft und ein Rahmen, einige Textzeilen mit Angaben zu den Metriken und eine Graustufenmatrix werden ausgegeben. Nach Ende der Druckausgabe wird die Seite mit dispose ausgegeben und der Druckjob mit end geschlossen. Der Code für die Darstellung der Graustufenmatrix wurde in der Methode paintGrayBoxes implementiert und wird von der Bildschirm- und Druckausgabe gemeinsam verwendet:

import java.awt.*;
import java.awt.event.*;

public class Example1507
extends Frame
{
   public static void main(String[] args)
   {
      Example1507 wnd = new Example1507();
   }

   public Example1507()
   {
      super("Example1507");
      addWindowListener(
         new WindowAdapter() {
            public void windowClosing(WindowEvent event)
            {
               System.exit(0);
            }
         }
      );
      setBackground(Color.lightGray);
      setSize(400,400);
      setVisible(true);
      //Ausdruck in 2 Sekunden starten
      try {
         Thread.sleep(2000);
      } catch (InterruptedException e) {
         //nichts
      }
      printTestPage();
   }

   public void paint(Graphics g)
   {
      paintGrayBoxes(g, 40, 50);
   }

   public void printTestPage()
   {
      PrintJob pjob = getToolkit().getPrintJob(
         this,
         "Testseite",
         null
      );
      if (pjob != null) {
         //Metriken
         int pres = pjob.getPageResolution();
         int sres = getToolkit().getScreenResolution();
         Dimension d2 = new Dimension(
            (int)(((21.0 - 2.0) / 2.54) * sres),
            (int)(((29.7 - 2.0) / 2.54) * sres)
         );
         //Ausdruck beginnt
         Graphics pg = pjob.getGraphics();
         if (pg != null) {
            //Rahmen
            pg.drawRect(0, 0, d2.width, d2.height);
            //Text
            pg.setFont(new Font("TimesRoman",Font.PLAIN,24));
            pg.drawString("Testseite",40,70);
            pg.drawString(
               "Druckerauflösung : " + pres + " dpi",
               40,
               100
            );
            pg.drawString(
               "Bildschirmauflösung : " + sres + " dpi",
               40,
               130
            );
            pg.drawString(
               "Seitengröße : " + d2.width + " * " + d2.height,
               40,
               160
            );
            //Graustufenkästchen
            paintGrayBoxes(pg, 40, 200);
            //Seite ausgeben
            pg.dispose();
         }
         pjob.end();
      }
   }

   private void paintGrayBoxes(Graphics g, int x, int y)
   {
      for (int i = 0; i < 16; ++i) {
         for (int j = 0; j < 16; ++j) {
            int level = 16 * i + j;
            g.setColor(Color.black);
            g.drawRect(x + 20 * j, y + 20 * i, 20, 20);
            g.setColor(new Color(level, level, level));
            g.fillRect(x + 1 + 20 * j, y + 1 + 20 * i, 19, 19);
         }
      }
   }
}

Die Bildschirmausgabe des Programmes kann Abbildung 15.8 entnommen werden. Die Druckausgabe sieht ähnlich aus, enthält aber zusätzlich noch einen Rahmen und die Textausgabe mit den Informationen zu den Druckmetriken.

Undisplayed Graphic

Abbildung 15.8: Das Programm zur Druckausgabe


Previous Page Previous Page Page Top TOC Index Next Page Previous Page

(C) 1997 Guido Krueger, "Java 1.1 lernen", Addison-Wesley, Bonn, 1997