Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 14 Grafikprogrammierung mit dem AWT
gp 14.1 Das Abstract-Window-Toolkit
gp 14.1.1 Java Foundation Classes
gp 14.2 Fenster unter grafischen Oberflächen
gp 14.2.1 Fenster öffnen
gp 14.2.2 Größe und Position des Fensters verändern
gp 14.2.3 Fenster- und Dialog-Dekoration
gp 14.3 Das Toolkit
gp 14.3.1 Einen Hinweis beepen
gp 14.4 Grundlegendes zum Zeichnen
gp 14.4.1 Die paint()-Methode
gp 14.4.2 Auffordern zum Neuzeichnen mit repaint()
gp 14.4.3 Fensterinhalte ändern und die ereignisorientierte Programmierung
gp 14.5 Punkte, Linien und Rechtecke aller Art
gp 14.5.1 Linien
gp 14.5.2 Rechtecke
gp 14.6 Alles was rund ist
gp 14.7 Polygone und Polylines
gp 14.7.1 Die Polygon-Klasse
gp 14.7.2 N-Ecke zeichnen
gp 14.7.3 Vollschlanke Linien zeichnen
gp 14.8 Zeichenketten schreiben
gp 14.8.1 Einen neuen Zeichensatz bestimmen
gp 14.8.2 Ableiten eines neuen Fonts aus einem gegebenen Font
gp 14.8.3 Zeichensätze des Systems ermitteln
gp 14.8.4 Die Klasse FontMetrics
gp 14.8.5 True Type Fonts
gp 14.9 Clipping-Operationen
gp 14.10 Farben
gp 14.10.1 Zufällige Farbblöcke zeichnen
gp 14.10.2 Farbanteile zurückgeben
gp 14.10.3 Vordefinierte Farben
gp 14.10.4 Farben aus Hexadezimalzahlen erzeugen
gp 14.10.5 Einen helleren oder dunkleren Farbton wählen
gp 14.10.6 Farbmodelle HSB und RGB
gp 14.10.7 Die Farben des Systems
gp 14.11 Bilder anzeigen und Grafiken verwalten
gp 14.11.1 Eine Grafik zeichnen
gp 14.11.2 Grafiken zentrieren
gp 14.11.3 Laden von Bildern mit dem MediaTracker beobachten
gp 14.11.4 Kein Flackern durch Double-Buffering
gp 14.11.5 Bilder skalieren
gp 14.12 Programm-Icon setzen
gp 14.12.1 VolatileImage
gp 14.13 Grafiken speichern
gp 14.13.1 Bilder im GIF-Format speichern
gp 14.13.2 Gif speichern mit dem ACME-Paket
gp 14.13.3 JPEG-Dateien mit dem Sun-Paket schreiben
gp 14.13.4 Java Image Management Interface (JIMI)
gp 14.14 Von Produzenten, Konsumenten und Beobachtern
gp 14.14.1 Producer und Consumer für Bilder
gp 14.14.2 Beispiel für die Übermittlung von Daten
gp 14.14.3 Bilder selbst erstellen
gp 14.14.4 Die Bildinformationen wieder auslesen
gp 14.15 Filter
gp 14.15.1 Grundlegende Eigenschaft von Filtern
gp 14.15.2 Konkrete Filterklassen
gp 14.15.3 Mit CropImageFilter Teile ausschneiden
gp 14.15.4 Transparenz
gp 14.16 Alles wird bunt mit Farbmodellen
gp 14.16.1 Die abstrakte Klasse ColorModel
gp 14.16.2 Farbwerte im Pixel mit der Klasse DirectColorModel
gp 14.16.3 Die Klasse IndexColorModel
gp 14.17 Drucken
gp 14.17.1 Drucken mit dem einfachen Ansatz
gp 14.17.2 Ein PrintJob
gp 14.17.3 Drucken der Inhalte
gp 14.17.4 Komponenten drucken
gp 14.17.5 Den Drucker am Parallelport ansprechen
gp 14.18 Java 2D-API
gp 14.18.1 Grafische Objekte zeichnen
gp 14.18.2 Geometrische Objekte durch Shape gekennzeichnet
gp 14.18.3 Eigenschaften geometrischer Objekte
gp 14.18.4 Transformationen mit einem AffineTransform-Objekt
gp 14.19 Graphic Layers Framework
gp 14.20 Grafikverarbeitung ohne grafische Oberfläche
gp 14.20.1 Xvfb-Server
gp 14.20.2 Pure Java AWT Toolkit (PJA)


Galileo Computing

14.8 Zeichenketten schreibendowntop

Die Methode, mit der Zeichen in verschiedenen Zeichensätzen (engl. fonts) auf die Zeichenfläche gebracht werden, heißt drawString(). Diese Funktion besitzt drei Parameter: die zu schreibende Zeichenkette, x-Koordinate und y-Koordinate. drawString() zeichnet im aktuell eingestellten Zeichensatz, und die Grundlinie (engl. baseline) befindet sich auf der übergebenden y-Position.


abstract class java.awt.Graphics

gp abstract void drawString( String s, int x, int y )
Schreibt einen String in der aktuellen Farbe und dem aktuellen Zeichensatz. Die x- und y-Werte bestimmen die Startpunkte der Grundlinie.
gp abstract void drawString( AttributedCharacterIterator iterator, int x, int y )
Schreibt einen String, der durch den Attribut-Iterator gegeben ist.
gp abstract void drawChars( char data[], int offset, int length, int x, int y )
Schreibt die Zeichenkette und bezieht die Daten aus einem Char-Feld.
gp abstract void drawBytes( byte data[], int offset, int length, int x, int y )
Schreibt die Zeichenkette und bezieht die Daten aus einem Bytefeld.

Galileo Computing

14.8.1 Einen neuen Zeichensatz bestimmendowntop

Die Funktion drawString() verwendet immer den aktuellen Zeichensatz. Um diesen zu ändern, benutzen wir die Funktion setFont(). Der Übergabeparameter ist ein Font-Objekt, welches wir erst erzeugen müssen. Der Konstruktor von Font ist durch verschiedene Parameter definiert:


class java.awt.Font
implements Serializable

gp Font( String name, int style, int size )
Erzeugt ein Font-Objekt.

Name Die Namen des Zeichensatzes können von System zu System unterschiedlich sein. Unter WinNT, MacOs, Linux, Solaris und IRIX sind jedenfalls die Zeichensätze Monospaced (früher Courier), Dialog, SansSerif (früher Helvetica) und Serif (früher TimesRoman) erlaubt, unter MacOS kommt noch der Zeichensatz Geneva hinzu. Vor Java 1.1 gab es noch den Zeichensatz Symbol (beziehungsweise ZapfDingbats), der aber durch die Unicode-Zeichen abgedeckt wird.
Stil Das Font-Objekt definiert drei Konstanten, um die Schriftart fett und kursiv darzustellen. Die symbolischen Werte sind: Font.ITALIC, Font.BOLD und für einen nicht ausgezeichneten Schriftsatz Font.PLAIN. Die Attribute können mit dem binären Oder oder dem arithmetischem Plus verbunden werden, ein fetter und kursiver Zeichensatz ist so durch Font.BOLD|Font.ITALIC (beziehungsweise durch Font.BOLD+Font.ITALIC) zu erreichen.
Größe Eine Angabe in Punkten, wie groß die Schrift sein soll. Ein Punkt entspricht etwa 1/72 Zoll (etwa 0,376 mm).

Tabelle 14.1 Parameter des Font-Konstruktors


Beispiel Ein Font-Objekt erzeugen
Font f = new Font( "Serif", Font.PLAIN, 14 );
Häufig wird dieses Zeichensatz-Objekt sofort in setFont() genutzt, so wie
setFont( new Font( "Serif", Font.BOLD, 20 ) );

Abbildung
Hier klicken, um das Bild zu Vergrößern

Ist im Programm lediglich der aktuell verwendete Zeichensatz nötig, können wir getFont() von der Graphics-Klasse verwenden.


abstract class java.awt.Graphics

gp abstract Font getFont()
Liefert den aktuellen Zeichensatz.
gp abstract Font setFont( Font f )
Setzt den Zeichensatz für das Graphics-Objekt.

Galileo Computing

14.8.2 Ableiten eines neuen Fonts aus einem gegebenen Fontdowntop

Steht ein Font zur Verfügung. Soll ausgehend von diesem ein neues Font-Objekt mit einer kleinen Änderung etwa in der Größe oder im Attribut (fett, kursiv) hergestellt werden, so lassen sich die deriveFont()-Funktionen einsetzen.


Beispiel Ausgehend von einem existierenden Font-Objekt f soll ein neuer Font abgeleitet werden, der jedoch 20 Pixel groß ist.
Font font = f.deriveFont( 20f );
Der Parameter ist ein float und kein double. Der Grund wird sein, dass das Literal 20 auch für eine Ganzzahl steht und es auch ein deriveFont(int) gibt - das liefert einen neuen Font in normal, fett oder kursiv. Bei einem float muss ein »f« an das Literal geschrieben werden, so dass einer Verwechselung vorgebeugt wird.


class java.awt.Font
implements Serializable

gp Font deriveFont( AffineTransformation trans )
Erzeugt ein neues Font-Objekt, welches über ein Transformationsobjekt modifiziert ist. Liefert ein neues Zeichensatzobjekt in der Größe von einem Punkt und keinem besonderen Stil.
gp Font deriveFont( float size )
Wie der Originalzeichensatz, nur mit einer neuen Größe.
gp Font deriveFont( int style )
Wie der Originalzeichensatz, nur mit einem neuen Stil.
gp Font deriveFont( int style, float size )
Wie der Originalzeichensatz, nur mit einer neuen Größe und einem neuen Stil.

Galileo Computing

14.8.3 Zeichensätze des Systems ermittelndowntop

Die Umsetzung der Namen auf den verschiedenen Rechnerplattformen übernimmt Java, so heißt »Helvetica« unter Windows »Arial« (aber mit denselben Laufweiten). Der Grund ist, dass der Name »Helvetica« von Adobe geschützt ist. Die Hersteller sind also bei ihrer Namenswahl nicht frei. Doch auch unter X11 heißt Helvetica nicht Helvetica. Da die verschiedenen Zeichensatzhersteller den Namen »Helvetica« aber kaufen können, ist der Originalzeichensatz unter X11 Adobe-Helvetica. Die Firma Adobe war so gnädig und hat die Zeichensätze als Type-1-Schriftarten beigelegt. Type-1-Schriftarten sind unter X11 relativ neu, denn erst als von IBM der Server-Zusatz programmiert wurde, konnten Type-1-Schriften benutzt werden. Vorher wurden die Anwender immer mit kleinen Klötzen abgefertigt, wenn die Schriftgröße einmal zu hoch gewählt wurde. Leider ist dies bei einigen Zeichensätzen immer noch der Fall. Selbst Star-Office unter X11 hat damit zu kämpfen, und wir auch, falls wir einen Zeichensatz verlangen, der nur als Bitmap in den Standardgrößen gerastert ist.

Um herauszufinden, welche Zeichensätze auf einem System installiert sind, kann die getFontList()-Methode der Klasse Toolkit bemüht werden. Die Methode ist aber mittlerweile veraltet. Einen neuen Weg geht die Funktion getAvailableFontFamilyNames(). Sie ist auf einem GraphicsEnvironment definiert, und dies ist die Ausprägung einer grafischen Oberfläche oder eines Druckers. Die statische Fabrik-Funktion getLocalGraphicsEnvironment() liefert ein solches GraphicsEnvironment-Objekt.


abstract class java.awt.GraphicsEnvironment

gp abstract Font[] getAllFonts()
Liefert ein Feld mit allen verfügbaren Font-Objekten im aktuellen GraphicsEnvironment in einer Größe von einem Punkt.
gp abstract String[] getAvailableFontFamilyNames()
Liefert ein Feld mit allen verfügbaren Zeichensatzfamilien im aktuellen GraphicsEnvironment.
gp abstract String[] getAvailableFontFamilyNames( Locale l )
Liefert ein Feld mit allen verfügbaren Zeichensatzfamilien im aktuellen GraphicsEnvironment, die zu einer Sprache l gehören.

Nachfolgendes Codesegment zeigt die Implementierung einer Schleife, die alle Zeichensatznamen ausgibt. Wir müssen kein Fenster geöffnet haben, um die Zeichensätze abzurufen.

Listing 14.9 ListFont.java

import java.awt.*;
class ListFont
{
  public static void main( String args[] )
  {
    String fonts[] =
        GraphicsEnvironment.getLocalGraphicsEnvironment().
          getAvailableFontFamilyNames();

    for ( int i = 0; i < fonts.length; i++ )
      System.out.println(fonts[i] );
  }
}

Nach dem Aufruf des Programms ist die Ausgabe bei mir:

Arial
Arial Black
Arial Narrow
...
Wingdings
Wingdings 2
Wingdings 3
Zurich BT

Galileo Computing

14.8.4 Die Klasse FontMetricsdowntop

Jedes Font-Objekt beinhaltet lediglich Information über Schriftsatzfamilie, Schriftsatznamen, Größe und Stil. Sie bietet keinen Zugriff auf die Abmessungen des Zeichensatzes. Um diese Daten aufzuspüren, erzeugen wir ein FontMetrics-Objekt. Es verwaltet metrische Informationen, die mit einer Schriftart verbunden sind. Dazu gehören Ober- und Unterlänge, Schrifthöhe und Zeilenabstand.

Um das FontMetric-Objekt des aktuellen Grafikkontexts zu nutzen, findet sich eine Methode getFont(). Diese Methode gehört zur Graphics-Klasse und ist nicht mit der getFont()-Methode aus FontMetrics zu verwechseln. Sie verhält sich zwar gleich, ist aber einer anderen Klasse zugeordnet.

In der paint()-Methode kann also mittels

FontMetrics fm = getFontMetrics();

auf die Metriken des aktuellen Zeichensatzes zugegriffen werden.


abstract class java.awt.Graphics

gp FontMetrics getFontMetrics()
Liefert die Font-Metriken zum aktuellen Zeichensatz.
gp abstract FontMetrics getFontMetrics( Font f )
Liefert die Font-Metriken für den Zeichensatz f.

Die Klasse FontMetrics bietet die folgenden Methoden an, wobei sich alle Angaben auf das jeweilige Zeichensatzobjekt beziehen. Beziehen sich die Rückgabeparameter auf die Zeichengröße, so ist die Angabe immer in Pixel.


abstract class java.awt.FontMetrics
implements Serializable

gp int bytesWidth( byte data[], int off, int len )
int charsWidth( char data[], int off, int len )
Gibt die Breite aller Zeichen des Felds zurück. Beginnt bei off und liest len Zeichen.
gp int charWidth( int ch ), int charWidth( char ch )
Liefert die Breite zu einem Zeichen.
gp int getAscent()
Gibt den Abstand von der Grundlinie zur oberen Grenze (Oberlänge) zurück.
gp int getDescent()
Gibt den Abstand von der Grundlinie zur unteren Grenze (Unterlänge) zurück.
gp Font getFont()
Liefert den aktuellen Zeichensatz.
gp int getHeight()
Gibt die Schrifthöhe einer Textzeile in Pixel zurück. Sie berechnet sich aus Zeilendurchschuss + Oberlänge + Unterlänge.
gp int getLeading()
Gibt den Zwischenraum zweier Zeilen zurück.
gp int getMaxAdvance()
Liefert die Breite des breitesten Zeichens.

Abbildung
Hier klicken, um das Bild zu Vergrößern

gp int getMaxAscent()
Liefert das Maximum aller Oberlängen in Pixel. Einige Zeichen können sich oberhalb der Oberlänge bewegen.
gp int getMaxDescent()
Liefert das Maximum aller Unterlängen in Pixel.
gp int[] getWidths()
Liefert in einem neuen Ganzzahlfeld die Breiten der Zeichen zurück.
gp int stringWidth( String str )
Gibt die Breite der Zeichenkette zurück, wenn diese gezeichnet würde.

Einen String unterstreichen

Wir wollen nun stringWidth() benutzen, um unterstrichenen Text darzustellen. Dafür gibt es keine Standardfunktion. Also schreiben wir uns einfach eine Methode, die die Koordinaten sowie den String übergeben bekommt. Die Methode drawUnderlinedString() schreibt mit drawString() die Zeichenkette. drawLine() erhält die Breite der Linie durch die Breite der Zeichenkette. Die Linie ist zwei Punkte unter der Baseline. Natürlich achtet so eine kleine Funktion nicht auf das Aussparen von Buchstaben, die unter der Baseline liegen. Die Buchstaben »y« oder »q« sind dann unten gnadenlos durchgestrichen.

void drawUnderlinedString( Graphics g, int x, int y, String s )
{
  g.drawString( s,10,10 );
  g.drawLine( x, y + 2,
              x + getFontMetrics(getFont()).stringWidth(s) , y + 2 );
}

Galileo Computing

14.8.5 True Type Fontstoptop

Grafische Oberflächen stellen selbstverständlich wie Drucker Zeichensätze dar. Doch der Weg von der Datei bis zur Darstellung ist lang und führt unweigerlich über die Firma Adobe, die erstmalig die standardisierte Zeichendefinition PostScript öffentlich machte. Genauer gesagt, definiert PostScript noch etwas mehr, doch das soll uns hier nicht interessieren. Die erste kommerzielle Zeichensatzrevolution begann dann 1985, als der Drucker LaserWriter von Apple das Adobe-Format PostScript rastern konnte. Die Definition eines Zeichensatzes lag bis dahin nur in Bitmaps vor, doch die PostScript-Zeichensätze wie auch die TrueType-Zeichensätze, um die es später gehen soll, lagen als Punktbeschreibung vor. Die Rasterung übersetzte diese Punkte in eine Bitmap, die dann entweder auf dem Bildschirm oder dem Drucker ausgegeben wurde. Durch die Punktbeschreibung waren also nicht mehr größenabhängige Beschreibungen vorhanden, sondern die Zeichen (auch Glyphs genannt) wurden durch Linien und Kurven in kubischen Bézier-Kurven beschrieben.

Die Visualisierung der Zeichensätze machte Microsoft und Apple Sorgen, da Adobe mehrere Defintionen der PostScript-Zeichensätze pflegte, darunter Type 1 (PS-1) und Type 3 (PS-3). Type 1 nutzt so genannte Hints (Hinweise), um auch bei unterschiedlichen Größen und grafischen Oberflächen optimale Darstellungen zuzulassen. Diese Definition war jedoch geheim. Zeichensätze des Type 3 sehen zwar auf dem Papier gut aus, nicht aber auf dem Bildschirm mit niedriger Auflösung - hier fehlen die Informationen aus den Hints. Microsoft und Apple wollten nun ihre Zeichensatzausgabe nicht Adobe überlassen (die natürlich einen Type-1-Rasterer im Programm hatten), sondern sie definierten ihre eigene Font-Technologie, die nicht mehr auf Bézier-Kurven, sondern auf quadratischen B-Splines basierte.1 Apple machte dabei den Anfang mit Royal, welches später in TrueType (TT) umgetauft wurde. Dies war sechs Jahre nach den PostScript Fonts. Der einzige Hersteller, der dennoch bei PostScript-Type 1-Zeichensätzen geblieben ist, ist IBM mit dem Betriebssystem OS/2. Daneben nutzte auch NeXtStep diese Zeichensatzdefinitionen, doch das System hallte nicht lange nach.

Nachdem Apple den Anfang mit TT gemacht hatte und es 1991 in MacOS integrierte, übernahm auch Microsoft, das sich bis dahin an einem wenig lauffähigen PostScript-Clone TrueImage versuchte, die Technologie für Windows 3.1. Adobe erkannte früh die Konsequenz dieser Allianz und öffnete die Spezifikation für PostScript-Type-1-Zeichensätze im März 1990. In der Mitte des Jahres lieferte Adobe zusätzlich den Adobe Type Manager (ATM) aus, der Type-1-(aber keine Type-3-)PostScript-Zeichensätze für den Bildschirm und für nicht PostScript-fähige Drucker darstellte. Heutzutage existieren beide Definitionen immer noch parallel, und für Drucker ist die Frage, welche nun besser ist, nicht zu beantworten. Moderne Drucker haben auch ein eigenes TrueType-Raster im ROM eingebaut. In Zukunft wird die Unterscheidung wohl auch unwichtiger werden, da Microsoft die »offene« OpenType-Spezifikation (auch »TrueType Open Version 2« genannt) nach vorne bringt. Der Zeichensatz PS-1 oder TrueType wird dabei in einer OpenType-Datei gekapselt und dem Rasterer übergeben und berechnet. Dabei übernimmt die PS-1-Rasterung Adobe, die eine Zusammenarbeit mit Microsoft unterstützt, und die TT-Rasterung Microsoft. In Zukunft möchten Microsoft und Adobe Zeichensätze im OpenType unterstützen und deren Verbreitung fördern.

TTF in Java nutzen

Die vordefinierten Standardzeichensätze (Dialog, DialogInput, Monospaced, Serif, SansSerif, Symbol) sind leider etwas wenig. Doch die Font-Klasse bietet die statische Methode createFont() an, die aus einem Eingabestrom eines TrueType-Zeichensatzes das entsprechende Font-Objekt erstellt.

Font f = Font.createFont( Font.TRUETYPE_FONT, new FileInputStream("f.ttf") );

Der erste Parameter ist die fest vorgeschriebene Konstante Font.TRUETYPE_FONT, andere Parameter sind nicht definiert und führen zu einer IllegalArgumentException("font format not recognized"). Der zweite Parameter ist ein Eingabestrom aus der Binärdatei mit den Zeichensatzinformationen. Die Daten werden ausgelesen und zu einem Font-Objekt verarbeitet. Da die Daten intern über einen gepufferten Datenstrom in eine temporäre Datei geschrieben werden, ist eine eigene Pufferung über einen BufferedInputStream nur zusätzlicher Overhead.

Waren die Beschreibungsinformationen in der Datei ungültig, so erzeugt die Font-Klasse eine FontFormatException("Unable to create font - bad font data"). Dateifehler fallen hier nicht darunter und werden extra über eine IOException angezeigt. Der Datenstrom wird anschließend nicht wieder geschlossen.

An dieser Stelle verwundert es vielleicht, dass von der Arbeitsweise her die Methode createFont() der des Konstruktors ähnlich sein müsste, aber der Parameterliste die Attribute fehlen. Das liegt daran, dass die Methode automatisch einen Zeichensatz der Größe 1 im Stil Font.PLAIN erzeugt. Um einen größeren Zeichensatz zu erzeugen, müssen wir ein zweites Font-Objekt anlegen, was am einfachsten mit der Methode deriveFont() geschieht.


class java.awt.Font
implements Serializable

gp static Font createFont( int fontFormat, InputStream fontStream )
throws FontFormatException, IOException
Liefert ein neues Zeichensatzobjekt in der Größe von einem Punkt und keinem besonderen Stil.





1 Obwohl quadratische B-Splines eine Untermenge von kubischen Bézier-Kurven ist, macht die Konvertierung der Zeichensätze den Anbietern viele Sorgen. Zudem lassen sich die Hints nur sehr schwer umsetzen.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de