14.8 Zeichenketten schreiben
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
|
|
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. |
|
abstract void drawString( AttributedCharacterIterator iterator, int x, int y )
Schreibt einen String, der durch den Attribut-Iterator gegeben ist. |
|
abstract void drawChars( char data[], int offset, int length, int x, int y )
Schreibt die Zeichenkette und bezieht die Daten aus einem Char-Feld. |
|
abstract void drawBytes( byte data[], int offset, int length, int x, int y )
Schreibt die Zeichenkette und bezieht die Daten aus einem Bytefeld. |
14.8.1 Einen neuen Zeichensatz bestimmen
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
|
|
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 ) );
|
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
|
|
abstract Font getFont()
Liefert den aktuellen Zeichensatz. |
|
abstract Font setFont( Font f )
Setzt den Zeichensatz für das Graphics-Objekt. |
14.8.2 Ableiten eines neuen Fonts aus einem gegebenen Font
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
|
|
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. |
|
Font deriveFont( float size )
Wie der Originalzeichensatz, nur mit einer neuen Größe. |
|
Font deriveFont( int style )
Wie der Originalzeichensatz, nur mit einem neuen Stil. |
|
Font deriveFont( int style, float size )
Wie der Originalzeichensatz, nur mit einer neuen Größe und einem neuen Stil. |
14.8.3 Zeichensätze des Systems ermitteln
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
|
|
abstract Font[] getAllFonts()
Liefert ein Feld mit allen verfügbaren Font-Objekten im aktuellen GraphicsEnvironment in einer Größe von einem Punkt. |
|
abstract String[] getAvailableFontFamilyNames()
Liefert ein Feld mit allen verfügbaren Zeichensatzfamilien im aktuellen GraphicsEnvironment. |
|
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
14.8.4 Die Klasse FontMetrics
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
|
|
FontMetrics getFontMetrics()
Liefert die Font-Metriken zum aktuellen Zeichensatz. |
|
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
|
|
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. |
|
int charWidth( int ch ), int charWidth( char ch )
Liefert die Breite zu einem Zeichen. |
|
int getAscent()
Gibt den Abstand von der Grundlinie zur oberen Grenze (Oberlänge) zurück. |
|
int getDescent()
Gibt den Abstand von der Grundlinie zur unteren Grenze (Unterlänge) zurück. |
|
Font getFont()
Liefert den aktuellen Zeichensatz. |
|
int getHeight()
Gibt die Schrifthöhe einer Textzeile in Pixel zurück. Sie berechnet sich aus Zeilendurchschuss + Oberlänge + Unterlänge. |
|
int getLeading()
Gibt den Zwischenraum zweier Zeilen zurück. |
|
int getMaxAdvance()
Liefert die Breite des breitesten Zeichens. |
Hier klicken, um das Bild zu Vergrößern
|
int getMaxAscent()
Liefert das Maximum aller Oberlängen in Pixel. Einige Zeichen können sich oberhalb der Oberlänge bewegen. |
|
int getMaxDescent()
Liefert das Maximum aller Unterlängen in Pixel. |
|
int[] getWidths()
Liefert in einem neuen Ganzzahlfeld die Breiten der Zeichen zurück. |
|
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 );
}
14.8.5 True Type Fonts
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
|
|
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.
|