Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 26 - Drucken

## 26.2 Drucken mit dem JDK 1.1

- 26.2 Drucken mit dem JDK 1.1
  - 26.2.1 Grundlagen
  - 26.2.2 Seitenweise Ausgabe
  - 26.2.3 Plazierung des Codes zur Druckausgabe

### 26.2.1 Grundlagen

Das Druck-API der Version 1.1 ist recht übersichtlich und leicht zu verstehen. Es gibt zwar einige Restriktionen und Besonderheiten, die beim Erstellen von Druckausgaben zu Problemen führen können, aber für einfache Ausdrucke von Text und Grafik ist die Schnittstelle dennoch geeignet.

Grundlage der Druckausgabe ist die Methode `getPrintJob` der Klasse `Toolkit`:

    public PrintJob getPrintJob(
       Frame frame,
       String jobtitle,
       Properties props
    )
| java.awt.Toolkit

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()
| java.awt.PrintJob

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 (so, wie er beispielsweise auch 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.

### 26.2.2 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.

|  |  |  | Bei der Druckausgabe ist es wichtig zu wissen, wie groß die Abmessungen des Ausgabegeräts sind. Die hierzu angebotenen Methoden `getPageDimension` und `getPageResolution` sind im JDK 1.1 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. |  |
Warnung

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 später 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 machen. Es bleibt demnach zu hoffen, daß die nachfolgenden Versionen des JDK die Bestimmung der Abmessungen auf eine flexiblere Weise ermöglichen.

|  |  |  | 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 Pixelgrafiken beispielsweise nicht 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. |  |
Warnung

### 26.2.3 Plazierung des Codes zur Druckausgabe

Es gibt grundsätzlich zwei Möglichkeiten, die Druckausgabe im Programm zu plazieren. Einmal kann die `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)
| java.awt.Component

Auf diese Weise kann bereits ohne zusätzliche Erweiterungen eine einfache Druckausgabe realisiert werden, die der Bildschirmausgabe relativ ä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` zwischen Bildschirm- und Druckerausgabe unterschieden werden, 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 zuvor besprochen bestimmen. Die Methode müßte dann nicht so programmiert werden, daß sie für Bildschirm- und Druckausgabe vernünftige Resultate liefert, sondern könnte ihre Ausgaben ausschließlich für die Druckausgabe optimieren. Der Nachteil bei dieser Löung ist natürlich, daß Programmcode zum Erstellen der Ausgabe möglicherweise doppelt vorhanden ist und doppelt gepflegt werden muß.

Das nachfolgende Listing kombiniert beide Varianten und zeigt den Ausdruck einer 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 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:

    001 /* Listing2601.java */
    002
    003 import java.awt.*;
    004 import java.awt.event.*;
    005
    006 public class Listing2601
    007 extends Frame
    008 {
    009   public static void main(String[] args)
    010   {
    011     Listing2601 wnd = new Listing2601();
    012   }
    013
    014   public Listing2601()
    015   {
    016     super("Drucken");
    017     addWindowListener(new WindowClosingAdapter(true));
    018     setBackground(Color.lightGray);
    019     setSize(400,400);
    020     setVisible(true);
    021     //Ausdruck in 2 Sekunden starten
    022     try {
    023       Thread.sleep(2000);
    024     } catch (InterruptedException e) {
    025       //nichts
    026     }
    027     printTestPage();
    028   }
    029
    030   public void paint(Graphics g)
    031   {
    032     paintGrayBoxes(g, 40, 50);
    033   }
    034
    035   public void printTestPage()
    036   {
    037     PrintJob pjob = getToolkit().getPrintJob(
    038       this,
    039       "Testseite",
    040       null
    041     );
    042     if (pjob != null) {
    043       //Metriken
    044       int pres = pjob.getPageResolution();
    045       int sres = getToolkit().getScreenResolution();
    046       Dimension d2 = new Dimension(
    047         (int)(((21.0 - 2.0) / 2.54) * sres),
    048         (int)(((29.7 - 2.0) / 2.54) * sres)
    049       );
    050       //Ausdruck beginnt
    051       Graphics pg = pjob.getGraphics();
    052       if (pg != null) {
    053         //Rahmen
    054         pg.drawRect(0, 0, d2.width, d2.height);
    055         //Text
    056         pg.setFont(new Font("TimesRoman",Font.PLAIN,24));
    057         pg.drawString("Testseite",40,70);
    058         pg.drawString(
    059           "Druckerauflösung : " + pres + " dpi",
    060           40,
    061           100
    062         );
    063         pg.drawString(
    064           "Bildschirmauflösung : " + sres + " dpi",
    065           40,
    066           130
    067         );
    068         pg.drawString(
    069           "Seitengröße : " + d2.width + " * " + d2.height,
    070           40,
    071           160
    072         );
    073         //Graustufenkästchen
    074         paintGrayBoxes(pg, 40, 200);
    075         //Seite ausgeben
    076         pg.dispose();
    077       }
    078       pjob.end();
    079     }
    080   }
    081
    082   private void paintGrayBoxes(Graphics g, int x, int y)
    083   {
    084     for (int i = 0; i < 16; ++i) {
    085       for (int j = 0; j < 16; ++j) {
    086         int level = 16 * i + j;
    087         g.setColor(Color.black);
    088         g.drawRect(x + 20 * j, y + 20 * i, 20, 20);
    089         g.setColor(new Color(level, level, level));
    090         g.fillRect(x + 1 + 20 * j, y + 1 + 20 * i, 19, 19);
    091       }
    092     }
    093   }
    094 }
| Listing2601.java

Listing 26.1: Ausdruck einer Testseite

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

Abbildung 26.1: Das Programm zur Druckausgabe

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de