Ein paar kleine C-Routinen ermöglichen die Ausgabe beliebiger GEM-Fonts auf dem Atari ST, auch ohne GDOS vorher gestartet zu haben.
GEM bietet die praktische Möglichkeit, die Ausgabe von Texten auf dem Bildschirm durch verschiedene Zeichensätze zu gestalten. Diese können in beliebiger Größe und auch als Proportionalschrift vorliegen. Atari selbst bietet eine kleine Auswahl als Public Domain-Auslage in ihrer hauseigenen Mailbox an. Dem Zeichenprogramm Arabesque ist ein Utility beigelegt, das Signum-Fonts ins GDOS-Format wandelt, womit allein schon eine riesige Auswahl an Zeichensätzen zur Verfügung steht. Trotzdem sind die GDOS-Fonts eine leider bisher wenig genutzte Eigenschaft des „Graphic Environment Managers“.
Das mag wohl daran liegen, daß die Benutzung der VDI-Funktionen zum Laden und zur Ausgabe der Zeichen ein geladenes GDOS voraussetzt. Nun ist es aber nicht immer praktisch, seinen eigenen Programmen die Betriebssystemerweiterung GDOS beizulegen. LineA gibt uns da einen kleinen Lichtblick...
TextBlt ist eigentlich nichts weiter als eine, um die Bearbeitung von Stileinstellungen (kursiv, hell usw.), erweiterte Bit-Blt-Funktion. Sie bietet die Möglichkeit, ein Zeichen aus einem geladenen GDOS-Font auf dem Bildschirm darzustellen. Weiterhin stehen in C unter gestartetem LineA Routinen wie set_wrt_mode() und sctjxt_blt() zur Einstellung der Stilattribute zur Verfügung. In anderen Sprachen, in denen diese Funktionen nicht implementiert sind, müßte man direkt auf den LineA-Parameterblock, dessen Adresse man nach einem linea_init()-Aufruf bekommt, zugreifen. Die Struktur des Parameterblocks, und übrigens auch die des GDOS-Fontheaders, findet man in jeder zweitbesseren Betriebssystembeschreibung.
Eigentlich fehlen uns zur kompletten Nutzung der GDOS-Zeichensätze nur noch eine Laderoutine und ein Programmteil zur Ausgabe ganzer Textzeilen. Beides befindet sich in meinem Listing. Zusätzlich dazu sogar noch eine Funktion zum Ermitteln der pixelweisen Breiten eines Strings unter Benutzung eines bestimmten Zeichensatzes.
Bei dieser Funktion handelt es sich, wie der Name schon zu erkennen gibt, um die Laderoutine. Sie lädt beliebige GEM-Fonts - also auch die, die vom MS DOS-GEM kommen und im Intel-Format vorliegen. Diese werden (wenn nötig) in intel_to_68000() gewandelt. Wer kein brennendes Interesse daran hat, was da genau passiert, kann den nächsten Abschnittruhigen Gewissens überlesen.
Liegen Zeichensätze im sogenannten Intel-Format vor, müssen diese nach dem Laden erst in das Motorola- bzw. 68000er-Format gewandelt werden. Der Unterschied: Im Intel-Format liegen die Words in der Reihenfolge Lowbyte-Highbyte und die Longs in der Form LO2-HI2-LO1-HI1 (also ebenfalls gespiegelt) vor. Die Funktion intel_to_68000() übernimmt diese Wandlung des Fontheaders und der character offset table. Eigentlich müßte die HOT (horizontal offset table) auch noch umgerechnet werden. Jedoch ist mir bisher noch kein GDOS-Zeichensatz unter die Hände gekommen, in dem die HOT genutzt wird. Aus diesem Grund wollte ich nicht über ihren genauen Aufbau mutmaßen, um „blind“ eine Routine zur HOT-Umrechnung zu schreiben. Prinzipiell dürfte der Aufbau aber ähnlich (oder genauso) wie der der COT aussehen. Aber Achtung! Wer den fehlenden Programmteil selber schreiben möchte, sollte darauf achten, daß die Zeiger auf COT und HOT nicht gleich sind. Das würde nämlich bei der HOT-Umrechnung eine erneute Spiegelung der COT zur Folge haben, wodurch dann alle Arbeit rückgängig gemacht wäre.
FNT_unload() tut nichts weiter, als mittels free() den für einen geladenen Zeichensatz reservierten Speicherplatz wieder freizugeben. Der Aufruf von sollte nach dem Programmende auf keinen Fall vergessen werden, da sonst umsonst Speicher belegt bleibt, der keinen weiteren Nutzen hat!
... gibt einen Text an einer bestimmten Bildschirmposition, unter Beachtung bestimmter Stileinstellungen, die auch übergeben werden, aus. Der Text kann wie vom GEM gewohnt kursiv, hell, outlined, fett dargestellt und/oder unterstrichen werden. Dazu sind im Parameters?// die entsprechenden Bits laut der LineA-Funktion set_txt_blt() zu setzen. Weiterhin kann der Text in 90°-Schritten gedreht werden. Auch den Ausgabemodus kann man mit wrmode zwischen Replace, X-Oder, Oder und Invers auswählen. Es gibt natürlich noch andere Darstellungsvarianten, die ich aber nicht für implementierenswert hielt. FNT_text() soll auch keine endgültige Vorgabe sein und ist für bestimmte Anwendungen noch zu erweitern.
Dies ist ein kleines Zusatzbonbon, das im Demoprogramm eigentlich nicht benutzt wird, aber noch einmal die Rechnung mit den Proportionalabständen demonstriert. Sie gibt, wie oben schon besprochen, bei Angabe eines Strings und des benutzten Zeichensatzes, die daraus resultierende Gesamtlänge des Textes in Pixeln zurück.
Als Erklärung der restlichen Routinen reichen, so denke ich, die Programmkommentare, die üppig ausgelegt sind. Wer genau hinguckt, sieht, daß sich die Tipparbeit auf nur ca. 130 reine C-Zeilen reduziert. Bleibt mir also nur noch übrig. Ihnen viel Spaß mit dem Demoprogramm zu wünschen, welches beliebige FNT-, F09-, F24- und F30-Zeichensätze lädt und einen kleinen Demotext dazu ausgibt. Verlassen wird das Progrämmchen mit dem Abbruch-Button der Fileselectbox.
typedef struct fonthdr
{
int id, /* Fontnummer */
size; /* Fontgroße in Punkten */
char facename[32]; /* Name */
int ade_lo, /* kleinster ASCII-Wert */
ade_hi, /* größter ASCII-Wert */
top_dist, /* Abstand Top <-> Baseline */
asc_dist, /* Abstand Ascent <-> Baseline */
hlf_dist, /* Abstand Half <-> Baseline */
des_dist, /* Abstand Descent <-> Baseline */
bot_dist, /* Abstand Bottom <-> Baseline */
wchr_wdt, /* maximale Zeichenbreite */
wcel_wdt, /* maximale Zeichenzellenbreite */
lft_ofst, /* Offset links */
rgt_ofst, /* Offset rechts */
thckning, /* Verbreiterungsfaktor für Bold */
undrline, /* Dicke der Unterstreichung */
lghtng_m, /* Maske für Light */
skewng_m, /* Maske für Kursiv */
struct
{
unsigned : 12; /* Frei */
unsigned mono spaced : 1;
/* Proportional/Monospaced */
unsigned f68000 : 1;
/* 8086-/68000 Format */
unsigned hot : 1;
/* HOT verwenden */
unsigned system : 1;
/* Default system font */
} flags;
char *hz_ofst; /* Horiz.-Offset-Tabelle */
int *ch_ofst; /* Font-Offset-Tabelle */
void *fnt_dta, /* Zeiehensatz-Image */
int frm_wdt, /* Breite des Font-Image */
frm_hgt, /* Höhe des Fonts */
struct fonthdr *next; /* Nächster Font */
} FONT_HDR;
/=====================================/ /* Die FCNT-GDOS-PROTHESE / / / / Von: PATRICK HOFFMANN / / In: TURBO-C 2.xx / / / / (c) 1991 MAXON Computer / /=====================================*/
/==== Sub-Sub-Routines ===============/
#include <linea.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <tos.h> #include <ext.h>
/==== Prototypen (Sub-Routines) ======/
FONT_HDR *FNT_load( char *file ); void FNT_unload( FONT_HDR *font ); void FNT_text( int x, int y, char *text, FONT_HDR *font, int Winkel, int stil, int wrmode ); int FNT_len( char *text, FONT_HDR *font);
/==== Prototypen (Sub-Sub-Routines) ==/
char *ext( char *file, char *ext ); char *strbchr( char *text. char find ); FONT_HDR *intel_to_68000( FONT_HDR *hdr ); void wswitch( int *w ); void lswitch( long *1 );
/=== Demoprogramm ===================/
void main() /* GDOS-PROTHESE-Demo */ { FONT_HDR font1, / Zeiger auf Fontheader */ font2, / Zeiger auf Fontheader */ font3, / Zeiger auf Fontheader */ font4; / Zeiger auf Fontheader */
int exitb; /* Exit-Flag für die Hauptschleife */
char file[13], /* Dateiname */
path[255], /* Pfadname */
fontfile[255], /* Pfad+Datemame */
/* Demotext: */
*text = "The quick brown fox jumps over the lazy dog!";
strcpy( file, "ROMAN.FNT" );
/* Defaultauswahl */
strcpy( path, "\\*.F??" );
/* Defaultpfadname */
do { fsel_input( path, file, &exitb );
if( exitb == 1 ) /* Abbruch-Button^ */
{
Cconws( "\x1B\x45" );
strcpy ( fontfile, path );
/* Pfad u. Dateiname verkoppeln */
memcpy( strbchr( fontfile, '\\' ) +1, file, strlen(file) +1 );
font1 = FNT_load( ext(fontfile,"FNT") ); /* Font laden */
font2 = FNT_load( ext(fontfile,"F09") ); /* Font laden */
font3 = FNT_load( ext(fontfile,"F30") ); /* Font laden */
font4 = FNT_load( ext(fontfile,"F24") ); /* Font lader, */
linea_init(); /* LineA starten */
/* Texte in verschiedenen Fonts ausgeben */
FNT_text( 30, 30, text, font1, 0, 0, REPLACE );
FNT_text( 30, 100, text, font2, 0, 0, REPLACE );
FNT_text( 30, 200, text, font3, 0, 0, REPLACE );
FNT_text( 30, 300, text, font4, 0, 0, REPLACE );
FNT_text( 5, 390, "by Patrick Hoffmann"
"(c) 1991 MAXON Computer", fontl, 900, 0, REPLACE );
FNT_unload( font1 );
/* Fontspeicher wieder freigeben */
FNT_unload( font2 );
FNT_unload( font3 );
FNT_unload( font4 );
getch() /* Auf Taste warten */
}
}while( exitb == 1 );
/* Bis Abbruch-Button gewählt wurde */
}
/=== Sub-Routines ========================/
/++ Textausgabe +++++++++++++++++++++++++++++++++++++++++/ /* -> x,y : Textposition / / text : auszugebender Text / / font : Zeiger auf Fontheader / / winkel : Ausgabewinkel (0, 900, 1800 o. 2700) / / stil : Textstil laut set_text_blt(...,style,..)/ / wrmode : Verknupfungsmodi (REPLACE... / /++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void FNT_text( int x, int y, char *text, FONT_HDR *font, int winkel, int stil, int wrmode ) { int i, step;
if( font != (void*)0L ) /* Font adresse ok? */
{
hide_mouse(); /* Maus weg! */
set_wrt_mode( wrmode );
/* Verknüpfungsmodi */
set_text_blt( font, 0, stil, winkel, 1,0 );
/* Text-Stil */
for( i=0, i<strlen (text); i++)
{
text_blt( x, y, *(text+i) );
/* proportionale Schrittweite holen */
step = *(font->ch_ofst+(*(text+i)-font->ade_lo) +1 ) -
*(font->ch_ofst+(*(text+i)-font->ade_lo));
switch( winkel )
{
case 0: x += step; break;
/* Von rechts nach links */
case 900: y -= step; break;
/* Von unten nach oben */
case 1800: x -= step; break;
/* Von links nach rechts */
case 2700: y += step,
/* Von oben nach unten */
}
}
show_mouse( 1 ); /* Maus hin! */
}
}
/++ Textlängenbestimmung +++++++++++++++/ /* -> text : Text / / font : Zeiger auf Fontheader / / <- errechnete Länge in Pixeln / /+++++++++++++++++++++++++++++++++++++++*/
int FNT_len ( char *text, FONT_HDR *font ) { int i, len; len = 0;
for( i=0, i<strlen (text) ; i++)
len += *(font->ch_ofst+(*(text+i)-font->ade_lo) +1 ) -
*(font->ch_ofst+(*(text+i)-font->ade_lo));
return( len );
}
/++ GDOS-Font laden ++++++++++++++++++++++++++/ /* -> file: Dateiname nach TOS-Norm / / <- Zeiger auf geladene Fontstruktur / / laut FONT_HDR (siehe / / LineA-Doku), Bei Ladefehler wird / / NULL zurückgegeben! / /+++++++++++++++++++++++++++++++++++++++++++++*/
FONT_HDR *FNT_load( char *file ) { FONT_HDR buffer; / Hier wird der Font hingeladen / int handle; / File-Handle */
if( (handle = Fopen( file, 0 )) >= 0 )
/* Datei öffnen. Handle */
/* holen u. Fehler abfragen */
{
/* Speicher reservieren (nach Dateilänge) */
buffer = malloc( filelength( handle ) );
/* Datei in den reservierten Buffer lesen */
Fread( handle, filelength( handle ) buffer );
/* Die originalen GDOS-Fonts haben die dubiose Eigenschaft Byte- */
/* weise vollkommen "verdreht" zu sein! Das liegt daran, daß */
/* Digital Research geschickter Weise darauf gekommen ist, für */
/* ihre Zeichensatze das Intelformat zu verwenden Das wiederum */
/* heißt Jedes WORD oder LONG komplett spiegeln! (siehe */
/* intel_to_68000(), wswitchf) und lswitch() ). */
/* Da aber zum Bleistift die von ARABESQUE konvertierten Fonts */
/* im 68000er Format vorliegen, müssen */
/* beide Formate beachtet werden. */
/* Wenn kein Bit im Lowbyte der Flagstruktur gesetzt ist, dann */
/* ist entweder kein Bit in flags gesetzt oder flags liegt auch */
/* im Intelformat vor. In beiden Fällen kann das Umrechnen ins Motorola- */
/* Format nichts kaputtmachen. */
/* Die Ausrufezeichen am Anfang beachten!!! */
if( !(*(int*)&(buffer->flags) & 0x00FF) )
wswitch( (int*)&(buffer->flags) );
/* Also umrechnen' */
if( !buffer->flags.f68000 )
/* Intelformat-Flag7 */
intel_to_68000( buffer );
/* Umrechnen... ! */
/* Zeig' mir, daß Du als Zeiger auf einen anderen Zeiger auf */
/* auf den Zeiger eines Zeigers, der auf einen Integer zeigt, zeigst! */
/* (Na los, 10x hintereinander!) */
/* Nein, nein hier addiere ich lediglich den Dateioffset für */
/* die HOT, COT und Fontdaten zur Adresse der geladenen */
/* Struktur im Speicher (Ein Relativ-Verschiebung sozusagen!) */
buffer->hz_ofst = buffer->hz_ofst + (long)buffer;
buffer->ch_ofst = (int*)((char*)buffer->ch_ofst + (long)buffer);
buffer->fnt_dta = (int*)((char*)buffer->fnt_dta + (long)buffer);
Fclose( handle ) ;
/* Schließe was Du offen hast! */
return( buffer ); /* Zeiger zurückgeben */
}else /* Bei Ladefehler. */
return( (void*)0L );
/* Nullzeiger zurückgeben */
}
/++ Fontspeicher wieder freigeben ++++++/ /* -> font : Zeiger auf Fontheader / /+++++++++++++++++++++++++++++++++++++++*/
void FNT_unload( FONT_HDR font ) / Font "ent"-laden / { free( font ); / Speicherbereich freigeben */ }
/==== Sub-Sub-Routines ==================/
/++ Extender eines Pfadnamens austauschen ++/ /* -> file : Pfad+Dateiname / / ext : Neuer Extender / / <- Zeiger : auf geänderten Namen / / Bemerkung: Arbeitet nur, wenn schon / / ein Extender vorhanden ist! / /+++++++++++++++++++++++++++++++++++++++++++*/
char ext ( char ‘file. char ext ) { memcpy( strbchr(file,'.')+1, ext, 4 ); return( file ); } /+ Zeichen in einem String rückwärts suchen +/ /* -> text : Zu durchsuchender Text / / find : Zu suchendes Zeichen / / <- Zeiger auf gefundenes Zeichen / /++++++++++++++++++++++++++++++++++++++++++++*/
char *strbchr( char *text, char find ) { int i; for( i=(int)strlen(text)-1; i>=0; i—- ) { if( (text+i) == find ) { return ( text+i ); } } return( (void)0L ); }
/++ Umrechnen eines Fontheaders +++++++++++++/ /* vom Intel- ins 68000er-Format / / -> hdr : Umzurechnender Header / / <- hdr wird auch wieder zurückgegeben / /++++++++++++++++++++++++++++++++++++++++++++*/
FONT_HDR *intel_to_68000 ( FONT_HDR *hdr ) { int i;
if( hdr->id > 255 ) /* Wenn ID > $FF -> INTEL-Format */
{
/* Wandeln vom Intel ins Motorola-"Normalformat"... */
wswitch( &(hdr -> id) );
wswitch( &(hdr -> size) );
/* Von .ade_lo bis .skewng_m (15 Words) */
for( i=0; i<15, i++)
wswitch( &(hdr->ade_lo)+i );
lswitch( (long*)&(hdr -> hz_ofst) );
lswitch( (long*)&(hdr -> ch_ofst) );
lswitch( (long*)&(hdr -> fnt_dta) );
wswitch( &(hdr -> frm_wdt) );
wswitch( &(hdr -> frm_hgt) );
/* Die COT (character-offset-table) auch noch switchen! */
for( i=0; i< (hdr->ade_hi - hdr->ade_lo) +2; i++ )
wswitch( ((int*)((long)hdr->ch_ofst +(long)hdr) + i) );
/* Von der HOT (horizontal-offset-table) hab' ich erst einmal */
/* die Hände gelassen, denn ich hab' noch keinen Font */
/* analysieren können, der die HOT benutzt. Eigentlich müßte */
/* man die auch noch spiegeln */
/* Aber VORSICHT! Wird die HOT nicht benutzt, so kann der Off- */
/* set für HOT und COT gleich sein! Ergebnis: Die COT wird dann */
/* wieder zuruckgespiegelt */
}
return( hdr );
}
/++ Word ins 68000er Format ++++++++++/ /* -> w : umzurechnendes Word / /+++++++++++++++++++++++++++++++++++++*/
void wswitch( int *w ) { char i;
i = *(char*)w; /* Dreieckstausch */
*(char*)w = *((char*)w+1);
*((char*)w+1) = i;
}
/++ Long in 68000er-Format +++++++++++/ /* -> l : umzurechnendes Long / /+++++++++++++++++++++++++++++++++++++*/
void lswitch( long *l ) { int i;
wswitch( (int*)l ); /* HI-/LO-Bytes der beiden Wörter */
wswitch( ((int*)l+1) );
i = * (int*)l; /* ... und die beiden Wörter selbst */
*(int*)l = *((int*)l+1); /* tauschen */
*((int*)l+1) = i;
}