Schriften unter Windows
Willemers Informatik-Ecke

Schrift unter Windows

Es gibt drei Datentypen, die mit Schrift zu tun haben, denen man in diesem Text begegnen wird.
LOGFONT
Beschreibt die Schrift mit ihren Eigenschaften, wie Schrifttyp, -größe und -farbe. Die Größe ist allerdings leider die in Pixeln und muß entsprechend je nach Ausgabemedium umgerechnet werden.
CHOOSEFONT
Ist die Struktur für den Schriftauswahldialog. Sie enthält auch einen Zeiger auf einen LOGFONT, der die Schrift beschreibt, die der Anwender auswählt. Diese Struktur enthält die Größe in Punkt, allerdings mit 10 multipliziert.
HFONT
Dies ist ein Handle auf eine Schrift, das von Windows benötigt wird, um eine Schrift mit einem Ausgabemedium zu verheiraten. Mit Hilfe der Funktion GetFontIndirect kann sie aus einem LOGFONT erzeugt werden.

ChooseFont: Der Schriftendialog

Wir beginnen mit der Auswahl der Schrift durch den Benutzer. Seit Windows 3.1 gibt es den Font-Dialog, ein Common Dialog, der den Anwender eine Schrift auswählen läßt. Er wird mit der Funktion ChooseFont aufgerufen und will als Parameter einen Zeiger auf eine Variable vom Typ CHOOSEFONT.

Erster Aufruf

Die Struktur CHOOSEFONT enthält einen Zeiger auf eine Variable vom Typ LOGFONT im Feld lpLogFont. Um die Dialogbox zu steuern, wird die CHOOSEFONT-Variable bestückt und die Ergebnisse befinden sich ebenfalls dort, respektive in der LOGFONT-Struktur.

Für den Anfang sollte man alle Strukturen auf binär 0 setzen.

Wie einige der Windows-Strukturen braucht auch CHOOSEFONT Informationen über die eigene Größe im Feld lStructSize. Die Besetzung der Flags ist wichtig. Es gibt unter anderem an, welche Schriftarten auswählbar sind. Steht dort weder CF_SCREEN noch CF_PRINTER noch CF_BOTH, behauptet Windows, es seien keine Schriften installiert. So präpariert, startet schon einmal der Font-Dialog.

CHOOSEFONT myChooseFont = {0};
LOGFONT    myLogFont    = {0};

   myChooseFont.lStructSize = sizeof( CHOOSEFONT );
   myChooseFont.lpLogFont = &myLogFont;
   myChooseFont.Flags = CF_EFFECTS | CF_BOTH;
   if( ChooseFont( &myChooseFont ) != TRUE ) {

Auslesen des Font-Dialogs

Nach Aufruf von ChooseFont finden sich die Aktionen des Anwenders teils in myLogFont teils in myChooseFont. Die Schriftgröße in Punkt findet sich in iPointSize der CHOOSEFONT-Struktur. Allerdings hat Windows sie noch mit 10 multipliziert. Für die Berechnung müssen wir also später durch 10 teilen.

Vorbesetzen der Werte im Fontdialog

Der Anwender wird erwarten, daß er die zuletzt ausgewählte Schrift in seinem Font-Dialog wieder vorfindet. Dazu müssen wir die Werte vorbelegen. Naheliegenderweise geschieht dies, indem man die Werte der LOGFONT füllt. Leider funktioniert das nur, wenn Flags auch CF_INITTOLOGFONTSTRUCT enthält.

Verfeinerungen

Das Feld hwndOwner sollte mit dem HWND des startenden Fensters versehen werden, sonst steht die Dialogbox so beziehungslos auf dem Desktop herum.

Wird Flag mit CF_BOTH besetzt, liefert der Font-Dialog die Schriften, die sowohl auf dem Bildschirm als auch auf dem Drucker verfügbar sind. Allerdings muß man den Device-Kontext des Druckers dazu im hDC eingetragen haben.

Skalierung und Erzeugung der Schrift

Nun kann die Schriftart erzeugt werden. Allerdings wird bei einem simplen Aufruf von CreateFont (oder CreateFontIndirect, die beiden unterscheiden sich nur in der Art der Parameterübergabe) für den Drucker eine viel zu kleine Schrift verwendet. Um dies anzupassen, muß das Feld lfHeight in der LOGFONT-Struktur angepaßt werden. Ist das Feld lfWidth 0, berechnet Windows die Breite aus den Charakteristika der Schrift selbst. Dies wird normalerweise auch so von ChooseFont übernommen.

Wie erhält man die korrekte Höhe? Die gewählte Punktgröße wurde in der CHOOSEFONT-Struktur beim Aufruf von ChooseFont abgelegt. Ein Punkt ist ein 72stel Zoll. Wieviele Pixel pro Zoll gedruckt werden, kann man den Druckerkontext mit Hilfe der Funktion GetDeviceCaps fragen.

Also multipliziert man senkrechte Auflösung mit Punktgröße und teilt dies durch 72. Die Funktion MulDiv erledigt dies für uns. Das Ergebnis kommt nach lfHeight im LOGFONT und nun können wir CreateFont darauf loslassen. Windows sucht sich eine passende Schrift aus und liefert ein Handle vom Typ HFONT zurück. Genau diesen erwartet später SelectObject von uns.

LONG yLogPixelsPerInch;
HFONT MyFont;

   yLogPixelsPerInch = GetDeviceCaps(pd.hDC, LOGPIXELSY);
   lfTemp.lfHeight = -MulDiv(cfTemp.iPointSize/10, yLogPixelsPerInch, 72);

   MyFont = CreateFontIndirect( &lfTemp );

Darstellen der Schrift

Gezeichnet wird unter Windows immer in einen DC, einen Device-Context. Man spricht diesen über ein Handle an, eine Variable vom Typ HDC. Hinter diesem HDC wird die Hardware der Grafikausgabe verborgen.

Der einfachste Fall des DC taucht in jedem "Hello World"-Programm auf: es ist der DC des Fensters, der von der Funktion BeginPaint geliefert wird, wenn die Fensterfunktion WM_PAINT empfangen hat.

Die Skalierung benoetigt hier eine Variable namens PunktGroesse, die an anderer Stelle ermittelt oder vorgegeben wird.

Mit der Funktion CreateFontIndirect wird aus der LOGFONT-Struktur ein Font gebildet. Das Handle HFONT wird als Rückgabewert geliefert und wird mit SelectObject an den DeviceContext (DC) gebunden.

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		RECT rt;
		GetClientRect(hWnd, &rt);
		// Skalierung
		LogFont.lfHeight = -MulDiv(PunktGroesse, 
			GetDeviceCaps(pd.hDC, LOGPIXELSY), 72);
		// Font-Initialisierung
		HFONT Font, oldFont;
		Font = CreateFontIndirect(&LogFont);
		oldFont = (HFONT)SelectObject(hdc, Font);
		DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);

		SelectObject(hdc, oldFont); // Font aufraeumen
		EndPaint(hWnd, &ps);
		break;

Für den Drucker erhält man den DC beispielsweise nach dem Öffnen der Druckdialogbox.

Ein Beispiel

Im folgenden Listing ist eine kurze Testfunktion aufgeführt, die nichts macht, als zunächst eine Schrift auszuwählen, anschließend den Drucker und zwei Zeilen auf den Drucker zu geben. Einziger benötigter Parameter von außen ist ein HWND, das aber nur zur Positionierund der Dialogboxen gebraucht wird. Man kann also die Routine fast in jedes Windows-Programm einhängen, um es zu testen.

TryPrinting(HWND hWnd)
{
PRINTDLG pd;
DOCINFO di;
CHOOSEFONT cfTemp;
LOGFONT lfTemp;
HFONT MyFont;
char String[80];

   memset(&lfTemp, '\0', sizeof(LOGFONT));
   memset(&cfTemp, '\0', sizeof(CHOOSEFONT));
   cfTemp.lStructSize = sizeof( CHOOSEFONT );
   cfTemp.hwndOwner = hWnd;
   cfTemp.hDC = 0;
   cfTemp.lpLogFont = &lfTemp;
   cfTemp.Flags = CF_EFFECTS | CF_BOTH;
   if( ChooseFont( &cfTemp ) != TRUE ) {
      return 0;
   }

   memset(&pd, 0, sizeof(PRINTDLG));
   pd.lStructSize = sizeof(PRINTDLG);
   pd.hwndOwner = hWnd;
   pd.hDevMode  = NULL;
   pd.hDevNames = NULL;
   pd.Flags     = PD_RETURNDC;
   pd.nCopies   = 1;

   // die DOCINFO dient der Beschreibung des Jobs fuer den Druckmanager
   memset(&di, 0, sizeof(DOCINFO));
   di.cbSize	= sizeof(DOCINFO);
   di.lpszDocName	= "drucktest";
   di.lpszOutput	= NULL;	// Ziel wird in Dialog ausgewählt
   if (PrintDlg(&pd)) {
      // Skalierung
      LONG yLogPixelsPerInch;

      yLogPixelsPerInch = GetDeviceCaps(pd.hDC, LOGPIXELSY);
      lfTemp.lfHeight = -MulDiv(cfTemp.iPointSize/10, yLogPixelsPerInch, 72);

      MyFont = CreateFontIndirect( &lfTemp );

      SelectObject(pd.hDC, MyFont);

      // drucken
      StartDoc(pd.hDC, &di); // Dokument wird eröffnet

      StartPage(pd.hDC);
      // Inhalt ausgeben
      sprintf(String, "Dies ist ein Drucktest, lfHeight: %ld", lfTemp.lfHeight);

      SelectObject(pd.hDC, MyFont);

      TextOut(pd.hDC, 70, 70, String, strlen(String));
      TextOut(pd.hDC, 10, 17, String, strlen(String));
      EndPage(pd.hDC);

      EndDoc(pd.hDC);  // Dokument wird geschlossen
      DeleteDC(pd.hDC);
   }
   if (pd.hDevMode != NULL) GlobalFree(pd.hDevMode);
   if (pd.hDevNames != NULL) GlobalFree(pd.hDevNames);
}

Literatur:

  1. Thomas Hornschuh: Druckmittel nutzen - Druckerprogrammierung unter Windows. c't 8/95, S. 222ff.
  2. Marcellus Buchheit: Windows Programmierbuch. Sybex 1992.
  3. Charles Petzold: Programmierung unter Windows 3.1, Microsoft Press, 1992.
  4. Peter Norton, Paul Yao: Windows 3, Programmiertechniken für Profis, Markt & Technik, 1991.
  5. Thomas Hornschuh: Tuschkasten-Bürokratie - Grafikprogrammierung unter Windows. c't 7/94, S. 206ff.