14.7 Text mit X
Unter X haben Sie die Möglichkeit, aus einer unzähligen Menge von Schriften zu wählen. Standardmäßig wird immer der ASCII-Zeichensatz unterstützt (Zeichen Nr. 20 bis 126) – aber häufig finden dabei auch die ausländischen Schriftzeichen zwischen 128 und 255 Platz. Für jede Schriftgröße wird ein eigener Font benötigt, was dazu führt, dass eine enorme Menge an Fonts vorhanden ist (auf meinem Rechner sind dies ca. 3200). Jeder Font, der gerade verwendet wird, besitzt eine eigene Kennungs-ID, die im Grafikkontext gespeichert wird. Somit benötigen Sie auch für die Ausgabe von Schriften den Grafikkontext GC.
14.7.1 Fonts benutzen
Standardmäßig verwendet der Grafikkontext eine voreingestellte Schriftart. Sofern Sie eine andere Schriftart verwenden wollen, müssen Sie diese erst laden. Folgende Schritte sind dazu nötig:
1. |
Font mit der Funktion XLoadFont() laden: |
|
|
Display *display;
Font font;
...
font = XLoadFont (display, fontname);
|
Als Rückgabewert erhalten Sie eine Kennungs-ID font vom Typ Font, die Sie für weitere Funktionsaufrufe benötigen werden. fontname ist ein String mit einer bestimmten Schriftart. Hierbei ist auch z. B. "*normal--10*" erlaubt – dann sucht sich der Server einfach irgendeine Schriftart normaler Dicke mit 10 Pixel Höhe aus. |
|
|
2. |
Font (Schriftart) im Grafikkontext GC eintragen (Font setzen): |
|
|
Display *display;
GC mygc;
Font font;
...
XSetFont (display, mygc, font);
3. |
Text mit der Funktion XDrawString() oder XDrawImageString() ausgeben: |
|
|
Display *display;
Window win;
GC mygc;
int x, y, len;
char *text;
...
XDrawString (display, win, mygc, x, y, text, len);
XDrawImageString (display, win, mygc, x, y, text, len);
|
text ist eine beliebige Stringvariable oder -konstante, die ASCII-Text (ohne Steuerzeichen o. Ä.) enthält. len (Integer) muss die Länge von text enthalten, selbst wenn text sauber mit 0 terminiert ist. x, y sind die Koordinaten des Textes relativ zur linken oberen Fensterecke. x, y beziehen sich auf die Grundlinie der Schrift – z. B. auf die Ecke links unten vom Buchstaben E. Beide Funktionen benutzen als Textfarbe die im GC eingetragene Vordergrundfarbe. |
|
|
|
Der Unterschied der beiden Funktionen liegt darin, dass XDrawString() im Gegensatz zu XDrawImageString() nicht die Hintergrundfarbe verwendet, auf die der Grafikkontext GC eingestellt ist, sondern den Hintergrund als durchsichtig betrachtet. |
|
|
4. |
Font (Speicher) wieder freigeben mit: XUnloadFont (display, font). |
|
|
14.7.2 Übersicht zu den Fonts
Wenn Sie sich einen Überblick über alle Schriftarten (Fonts) auf Ihrem System machen wollen, verwenden Sie am besten das X-Tool xlsfonts (mit einer Pipe über less). Gewöhnlich ist der Name einer Schriftart wie folgt aufgebaut:
...
-adobe-courier-medium-r-normal--8–80–75–75-m-50-iso8859–4
-adobe-courier-medium-r-normal--8–80–75–75-m-50-iso8859–9
-adobe-helvetica-bold-o-normal--10–100–75–75-p-60-iso10646–1
-adobe-helvetica-bold-o-normal—10–100–75–75-p-60-iso8859–1
...
Es gibt auch noch einige ältere Schriftarten, die nicht mit dem Minuszeichen am Anfang beginnen. Folgende Bedeutung haben die einzelnen Felder getrennt durch '-' (leeres Feld ist somit '--'):
|
Hersteller der Schrift: adobe, sony, schumacher, b&h, bitstream, (misc) |
|
Schrifttyp-Familie: times, courier, helvetica, new century schoolbook, fixed, symbol, lucida, lucidabright, lucidatypewriter, open look glyph |
|
Dicke des Schrifttyps: bold, demibold, light, medium, clean |
|
Neigung der Schrift: r für roman, i für italic, o für oblique |
|
keine Serifen: sans |
|
Zeichenhöhe der Schrift in Pixel: 10, 12, 13, 16, 19 ... |
|
Der sog. Schriftgrad in Zehntelpunkten (= 0.023 mm), d. h. die vom Bildschirm unabhängige Zeichengröße: 100, 120, 190 ... |
|
horizontale/vertikale Auflösung (in dpi): 75–75, 100–100 ... |
|
Zeichenabstand der Schrift: p für proportional, m für monospaced |
Natürlich lässt sich diese Liste der Schriftarten auch mit einer X-Funktion, nämlich mit XListFonts(), ermitteln:
Display *display;
char *mask;
char **list;
int count;
...
list=XListFonts(mydisplay, mask, 1000, &count);
...
XFreeFontNames(list);
mask ist ein String, womit Sie bestimmte Schriften herausfiltern können, z. B.:
|
"*" – Alle Schriftarten auswählen |
|
"*adobe*" – Nur Schriftarten von Adobe auswählen |
|
"*italic*" – Nur Italic-Schriftarten auswählen |
|
"*times*" – Nur Schriften vom Typ Times auswählen |
In count befindet sich die Anzahl der gefundenen Schriftarten, die Sie mit dem dritten Parameter begrenzen können. In diesem Fall werden max. 1000 Schriftarten ermittelt – egal wie viele noch existieren. Im Stringarray list befinden sich alle Namen, worauf Sie jetzt mit dem Index – z. B. um eine Schriftart zu laden – zurückgreifen können:
font=XLoadFont(display, list[5]);
Um den Speicher der Liste wieder freizugeben, sollten Sie die Funktion XFreeFontNames() verwenden.
14.7.3 Höhe und Breite ermitteln
Um einen Text auch ordentlich auf dem Bildschirm platzieren zu können, benötigt man Informationen über die Breite und Höhe der Schrift. Die Höhe ist unabhängig vom eigentlichen Text und nur durch den Font festgelegt. Zahlreiche Größeninformationen (in Pixel) über einen Font erhalten Sie über die Struktur XFontStruct:
Display *display;
XFontStruct *fontinfo;
...
fontinfo=XQueryFont(display,font);
printf("ascent: %d descent: %d\n",
(*fontinfo).ascent, (*fontinfo).descent);
Die Struktur XFontStruct enthält u. a. die beiden Variablen ascent, welche die Höhe über der Grundlinie (mit Platz nach oben) anzeigt, und descent, die angibt, wie viel Platz nach unten hin gebraucht wird (mit Platz nach unten). Somit erhalten Sie die gesamte Höhe durch die Summe der beiden Zahlen. Mehrzeiliger Text kann einfach mit diesem Abstand ausgegeben werden.
Die Breite hängt (bei Proportionalschriften) natürlich vom konkreten Text ab. Auch hierfür gibt es eine Funktion, die ebenfalls die Struktur XFontStruct benötigt:
XFontStruct *fontinfo;
char *text;
int len,width;
...
width=XTextWidth(fontinfo, text,len);
14.7.4 Listing mit Textausgabe
Das folgende Listing stellt eine Erweiterung des Listings vom Beispiel zuvor dar, nur dass die Funktionen, mit denen die Grafikprimitiven erstellt wurden, nun auch als Beschreibung mit ausgegeben werden. Außerdem wird mit einem rechten Mausklick ein Zufallsfont aus der Fontliste geladen, und alle (falls möglich) 255 Zeichen werden ausgegeben. Bei erneutem Rechtsklick werden wieder zufällige Fonts geladen und angezeigt. Die Anzahl der vorhandenen Fonts wird auf der Konsole ausgegeben.
/* xfont.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "my_delay.h"
#define WINDOW_WIDTH 600
#define WINDOW_HEIGHT 450
/* Funktionsprototypen */
static int create_window (void);
static void process_event (XEvent report);
static void eventloop (void);
static void close_window (void);
extern int delay (int i);
extern int time_diff (void);
static void draw_graphics (void);
static void print_text (GC, int, int, char *, char *);
static void print_font_demo (void);
/* Globale Variablen */
static unsigned int width, height;
static Display *display;
static int screen;
static int depth;
static Window win;
/* Schriftarten */
static char *my_fonts[] = {
"-adobe-times-bold-r-normal--18*",
"-adobe-times-bold-r-normal--12*",
"-b&h-lucida-medium-r-normal-*-*-180-*-*-*-*-*-*",
"-*-times-*-*-*-*-*-240-*-*-*-*-*-*"
};
enum { ADOBE_BOLD_18, ADOBE_BOLD_12, LUCIA_NORMAL, TIMES };
/* Funktion erzeugt ein Fenster */
static int create_window (void) {
char *window_name = "Fontdemo";
char *icon_name = "window";
static XSizeHints size_hints;
Window rootwin;
width = WINDOW_WIDTH;
height = WINDOW_HEIGHT;
/* X-Sitzung öffnen */
display = XOpenDisplay (NULL);
/* Fehlerüberprüfung */
if (display == NULL) {
printf ("Verbindung zum X-Server fehlgeschlagen?!?\n");
exit(EXIT_FAILURE);
}
/* Standardbildschirm eines Displays */
screen = XDefaultScreen (display);
/* Standardtiefe des Screens - Anzahl der Ebenen */
depth = XDefaultDepth (display, screen);
rootwin = RootWindow (display, screen);
win = XCreateSimpleWindow ( display, rootwin, 10, 10,
width, height, 5,
BlackPixel (display, screen),
WhitePixel (display, screen) );
size_hints.flags = PSize | PMinSize | PMaxSize;
size_hints.min_width = width;
size_hints.max_width = width;
size_hints.min_height = height;
size_hints.max_height = height;
XSetStandardProperties ( display, win, window_name, icon_name,
None, 0, 0, &size_hints );
XSelectInput (display, win,
ButtonPressMask | KeyPressMask | ExposureMask );
XMapWindow (display, win);
return 1;
}
static void close_window (void) {
XCloseDisplay (display);
}
static void eventloop (void) {
XEvent xev;
int num_events;
XFlush (display);
num_events = XPending (display);
while ((num_events != 0)) {
num_events--;
XNextEvent (display, &xev);
process_event (xev);
}
}
static void process_event (XEvent report) {
switch (report.type) {
case Expose: /* Expose-Event => Bild zeichnen */
if (report.xexpose.count == 0) {
draw_graphics ();
}
break;
case KeyPress:
printf ("Tastaturaktion\n");
break;
case ButtonPressMask:
/* Rechte Maustaste gedrückt? */
if (report.xbutton.button == 3)
print_font_demo ();
else
printf ("Mausaktion\n");
break;
default:
break;
}
}
static void draw_graphics (void) {
GC mygc; /* neu: Grafikkontext */
unsigned long black, white; /* Nummern der Farben */
int i;
XPoint punkte[50]; /* Punkteschar */
XPoint poly[4]; /* 4 Punkte fuer Polygon */
/* Ein paar Felder belegen */
/* Eine Schräge mithilfe von Punkten zeichnen */
for (i = 0; i < 50; i++) {
punkte[i].x = 470 + i;
punkte[i].y = 200 + i;
}
/* geschlossenes Dreieck */
poly[0].x = 200;
poly[0].y = 200;
poly[1].x = 300;
poly[1].y = 300;
poly[2].x = 400;
poly[2].y = 200;
poly[3].x = 200;
poly[3].y = 200;
/* Fenster oben drauf anzeigen */
XMapRaised (display, win);
/* Farbnummern besorgen */
black = BlackPixel (display, screen);
white = WhitePixel (display, screen);
/* Graphical Context erzeugen, Farben eintragen */
mygc = XCreateGC (display, win, 0, 0);
XSetForeground (display, mygc, black);
XSetBackground (display, mygc, white);
/* sicherheitshalber alles löschen */
XClearWindow (display, win);
/* Ein Rechteck zeichnen */
XDrawRectangle (display, win, mygc, 10, 50, 50, 50);
print_text (mygc, 10, 320, "1. XDrawRectangle()",
my_fonts[ADOBE_BOLD_12]);
/* Einen rechteckigen Bereich mit Farbe füllen */
XFillRectangle (display, win, mygc, 110, 50, 50, 50);
print_text (mygc, 110, 240, "2. XFillRectangle()",
my_fonts[ADOBE_BOLD_12]);
/* Einen Kreis zeichnen */
XDrawArc (display, win, mygc, 210, 50, 50, 50, 0, 360 * 64);
print_text (mygc, 210, 320, "3. XDrawArc()",
my_fonts[ADOBE_BOLD_12]);
/* Einen Kreisbereich füllen */
XFillArc (display, win, mygc, 310, 50, 50, 50, 0, 360 * 64);
print_text (mygc, 310, 240, "4. XFillArc()",
my_fonts[ADOBE_BOLD_12]);
/* Eine Ellipse zeichnen */
XDrawArc (display, win, mygc, 410, 50, 60, 40, 0, 360 * 64);
print_text (mygc, 410, 320, "5. XDrawArc()",
my_fonts[ADOBE_BOLD_12]);
/* Einen Ellipsenbereich füllen */
XFillArc (display, win, mygc, 510, 50, 60, 40, 0, 360 * 64);
print_text (mygc, 510, 240, "6. XFillArc()",
my_fonts[ADOBE_BOLD_12]);
/* Einen Halbkreis zeichnen */
XDrawArc (display, win, mygc, 10, 200, 60, 40, 90*64, 180*64);
print_text (mygc, 10, 480, "7. XDrawArc()",
my_fonts[ADOBE_BOLD_12]);
/* Einen Halbkreis füllen */
XFillArc (display,win, mygc, 110, 200, 60, 40, 90*64, 180*64);
print_text (mygc, 110, 380, "8. XFillArc()",
my_fonts[ADOBE_BOLD_12]);
/* ausgefuelltes Polygon bzw. Dreieck */
XFillPolygon(display, win, mygc, poly, 4, 0, CoordModeOrigin);
print_text (mygc, 250, 520, "9. XFillPolygon()",
my_fonts[ADOBE_BOLD_12]);
/* Eine schräge Linie mit Punkten */
XDrawPoints (display, win, mygc, punkte, 50, CoordModeOrigin);
print_text (mygc, 470, 380, "10. XDrawPoints()",
my_fonts[ADOBE_BOLD_12]);
print_text (mygc, 150, 590,
"Mit rechter Maustaste zum Font-Demo",
my_fonts[ADOBE_BOLD_18] );
XFlush (display);
XFreeGC (display, mygc);
}
static void
print_text (GC mygc, int x, int y, char *string, char *myfonts){
Font myfont; /* Schriftarten */
char *fname = myfonts;
/* Schriftart laden */
myfont = XLoadFont (display, fname);
/* im Grafikkontext eintragen */
XSetFont (display, mygc, myfont);
XDrawString ( display, win, mygc, x, y - 200,
string, strlen (string) );
/* Schriftart wieder freigeben */
XUnloadFont (display, myfont);
}
static void print_font_demo (void) {
int fcount; /* Zahl der Fonts von XListFont */
char **fontlist; /* Zeiger auf Feld mit Fontnamen */
Font headerfont, myfont; /* Textfonts */
int fontnr; /* Fontnummer */
char *fname; /* Text mit Fontnamen */
char clist[256]; /* 256 Zeichen */
XFontStruct *fontinfo, *headerinfo; /* Fontinformationen */
int headerlength; /* Textlaenge Header */
int headerwidth; /* Textbreite Header */
int headerheight; /* Hoehe des Header-Fonts */
int fontheight; /* Hoehe des Zufallsfonts */
int width; /* Breite des Textes mit Zufallsfont */
int i;
GC mygc;
mygc = XCreateGC (display, win, 0, 0);
/* Fontliste holen */
fontlist = XListFonts (display, "*", 5000, &fcount);
printf ("%d Schriften gefunden!\n", fcount);
/* Fenster loeschen */
XClearWindow (display, win);
/* Schriftart fuer die Ueberschrift */
headerfont =
XLoadFont (display, "-adobe-times-bold-r-normal--18*");
headerinfo = XQueryFont (display, headerfont);
headerheight = (*headerinfo).ascent + (*headerinfo).descent;
for (i = 0; i < 256; i++)
clist[i] = i; /* 256 ASCII-Zeichen definieren */
/* zufaelligen Font mit modulo! */
fontnr = (rand ()) % fcount;
/* das ist sein Name */
fname = fontlist[fontnr];
/* Header schreiben: Name des Fonts zentriert oben */
/* mit Schrifttyp = headerfont */
headerlength = strlen (fname); /* Laenge des Headers */
XSetFont (display, mygc, headerfont);/* Header-Font setzen */
headerwidth = XTextWidth (headerinfo, fname, headerlength);
XDrawString (
display, win, mygc, /* Header darstellen */
(600 - headerwidth)/2, headerheight*3/2, /* x,y */
fname, headerlength );
/* clist mit 256 Zeichen schreiben in neuer Schrift */
myfont = XLoadFont (display, fname); /* Font laden */
fontinfo = XQueryFont (display, myfont); /* Infos besorgen */
XSetFont (display, mygc, myfont); /* in GC eintragen */
fontheight = (*fontinfo).ascent + (*fontinfo).descent;
for (i = 0; i < 4; i++) {
width = XTextWidth (fontinfo, &clist[64 * i], 64);
XDrawString (
display, win, mygc,
(600 - width)/2, (3*headerheight+i*fontheight),
&clist[64*i], 64 );
}
print_text (mygc, 150, 590,
"Mit rechter Maustaste neue Schrift laden",
my_fonts[ADOBE_BOLD_18]);
XUnloadFont (display, myfont);
XFreeGC (display, mygc);
}
int main (int argc, char **argv) {
int quit = 0;
int pause = 0;
int td;
create_window ();
gettimeofday (&st, NULL);
while (!quit) {
gettimeofday (&rt, NULL);
td = time_diff ();
pause = delay (FRAME_LEN - td + pause);
st = rt;
eventloop ();
}
close_window ();
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o xfont xfont.c -L/usr/X11R6/lib -lX11
$ ./xfont
Im Beispiel sehen Sie übrigens auch, warum es unbedingt nötig ist, in der Praxis die Breite der Fonts zu ermitteln. Bei der zufälligen Ausgabe von Fonts werden viele größere Schriften nicht komplett am Bildschirm angezeigt. Man sollte die Breite der einzelnen Zeichen addieren und darauf achten, dass dabei die Breite des Fensters nicht überschritten wird. Sobald die Summe der einzelnen Zeichen mehr als die Breite des Fensters ist, sollte man in der nächsten Zeile mit der Ausgabe fortfahren.
|