Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5131 clevermous 1
/*
2
SDL_bdf - renders BDF fonts
3
Copyright (C) 2002-2003 Andre de Leiradella
4
 
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
 
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
Lesser General Public License for more details.
14
 
15
You should have received a copy of the GNU Lesser General Public
16
License along with this library; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
19
For information about SDL_bdf contact leiradella@bigfoot.com
20
 
21
Version 1.0: first public release.
22
Version 1.1: removed SDL dependecies, now SDL_bdf can be used with any graphics
23
	     library.
24
Version 1.2: fixed BDF_SizeH and BDF_SizeEntitiesH to return the correct sizes.
25
*/
26
#include 
27
#include 
28
#include 
29
#include 
30
#include 
31
#include 
32
#include 
33
 
34
/*
35
BDF fonts are encoded in ascii. Each line of the file begins with a keyword,
36
has zero or more arguments which can be integers, numbers (floats), strings and
37
quoted string, and ends with an eol. Some keywords are optional or
38
user-defined, unquoted strings can begin with any character which means that it
39
can start with a minus sign making it hard to distinguish them from negative
40
integers or numbers (and it do happen in some places). Adobe's spec isn't clear
41
sometimes so we have to be cautious (eg it doesn't say how eols are encoded so
42
we have to accept MSDOS (cr lf), Unix (lf) and Mac (cr) styles. I implemented a
43
*very* relaxed parser which won't stop on some errors. The parser reads the
44
font line by line and for each one verifies which is the keyword (using a hash
45
table generated by gperf) and takes the appropriate action through a switch
46
statement.
47
*/
48
 
49
/* BDF keywords. */
50
#define BBX		1
51
#define BITMAP		2
52
#define CHARS		3
53
#define COMMENT 	4
54
#define CONTENTVERSION	5
55
#define DWIDTH		6
56
#define DWIDTH1 	7
57
#define ENCODING	8
58
#define ENDCHAR 	9
59
#define ENDFONT 	10
60
#define ENDPROPERTIES	11
61
#define FONT		12
62
#define FONTBOUNDINGBOX 13
63
#define METRICSSET	14
64
#define SIZE		15
65
#define STARTCHAR	16
66
#define STARTFONT	17
67
#define STARTPROPERTIES 18
68
#define SWIDTH		19
69
#define SWIDTH1 	20
70
#define VVECTOR 	21
71
 
72
/* Include GPERF hash function generated from bdf.in. */
73
#include "bdf.gperf"
74
 
75
/* Reads a line from rwops up to 65536 characters. */
76
static int readline(BDF_ReadByte getbyte, void *info, char *data) {
77
	int k, i = 65536;
78
 
79
	for (;;) {
80
		k = getbyte(info);
81
		if (k == -1)
82
			return 0;
83
		switch (k) {
84
			default:
85
				*data++=k;
86
				if (--i==0) {
87
			case '\n':
88
					*data='\0';
89
					return 1;
90
				}
91
			case '\r':
92
				;
93
		}
94
	}
95
}
96
 
97
/* Parse an integer updating the pointer. */
98
static int readinteger(char **data, int *integer) {
99
	char *aux;
100
	int  i, signal;
101
 
102
	aux = *data;
103
	signal = 1;
104
	/* Adobe's spec doesn't say numbers can be preceeded by '+' but we never know. */
105
	switch (*aux) {
106
		case '-':
107
			signal = -1;
108
		case '+':
109
			aux++;
110
			break;
111
	}
112
	/* Skip spaces between signal and first digit. */
113
	while (isspace(*aux)) aux++;
114
	if (!isdigit(*aux)) return 0;
115
	/* Now we start reading digits. */
116
	i = 0;
117
	while (isdigit(*aux)) i = i * 10 + *aux++ - '0';
118
	/* We're done, update pointer and return value. */
119
	*data = aux;
120
	if (integer != NULL) *integer = signal * i;
121
	return 1;
122
}
123
 
124
/* Parse a double updating the pointer. */
125
static int readnumber(char **data, double *number) {
126
	register char *aux;
127
	double	      n, d;
128
	int	      signal;
129
 
130
	aux = *data;
131
	signal = 1;
132
	/* Adobe's spec doesn't say numbers can be preceeded by '+' but we never know. */
133
	switch (*aux) {
134
		case '-':
135
			signal = -1;
136
		case '+':
137
			aux++;
138
			break;
139
	}
140
	/* Skip spaces between signal and first digit. */
141
	while (isspace(*aux)) aux++;
142
	if (!isdigit(*aux)) return 0;
143
	/* Now we start reading digits */
144
	n = 0;
145
	while (isdigit(*aux)) n = n * 10 + *aux++ - '0';
146
	d = 10;
147
	/* If next character is a dot then we have a decimal part. */
148
	if (*aux == '.') {
149
		aux++;
150
		/* Decimal point must be succeeded by one or more digits. */
151
		if (!isdigit(*aux)) return 0;
152
		while (isdigit(*aux)) n += (*aux++ - '0') / d, d /= 10;
153
	}
154
	/* We're done, update pointer and return value. */
155
	*data = aux;
156
	if (number != NULL) *number = signal*n;
157
	return 1;
158
}
159
 
160
/* Parse a string updating the pointer. */
161
static int readstring(char **data, char **string) {
162
	int len;
163
 
164
	len = strlen(*data);
165
	if (string != NULL) {
166
		/* Malloc the required space. */
167
		*string=(char *)malloc(len + 1);
168
		if (*string == NULL)
169
			return 0;
170
		/* Copy everything. */
171
		strcpy(*string, *data);
172
	}
173
	/* We're done, update pointer. */
174
	*data += len;
175
	return 1;
176
}
177
 
178
/* Scan the line (just after the keyword) for elements listed in format. */
179
static int scan(char *data, char *format, ...) {
180
	va_list args;
181
	int	*i;
182
	double	*n;
183
	char	**s;
184
 
185
	/* Keyword already skipped, skip spaces. */
186
	while (*data != '\0' && isspace(*data)) data++;
187
	/* Scan the data for the pattern in format. */
188
	va_start(args, format);
189
	while (*format != '\0') {
190
		switch (*format++) {
191
			case 'i': /* integer. */
192
				i = va_arg(args, int *);
193
				if (!readinteger(&data, i)) {
194
					va_end(args);
195
					return 0;
196
				}
197
				break;
198
			case 'n': /* number. */
199
				n = va_arg(args, double *);
200
				if (!readnumber(&data, n)) {
201
					va_end(args);
202
					return 0;
203
				}
204
				break;
205
			case 's': /* string. */
206
				s = va_arg(args, char **);
207
				if (!readstring(&data, s)) {
208
					va_end(args);
209
					return 0;
210
				}
211
				break;
212
		}
213
		/* Skip spaces between elements. */
214
		while (*data != '\0' && isspace(*data)) data++;
215
	}
216
	va_end(args);
217
	return *data == '\0';
218
}
219
 
220
/* Compare function to sort characters by their names. */
221
static int compare(const void *c1, const void *c2) {
222
	return strcmp(((BDF_Char *)c1)->name, ((BDF_Char *)c2)->name);
223
}
224
 
225
#define XVAL(x) (isdigit((x)) ? (x) - '0' : toupper((x)) - 'A' + 10)
226
 
227
BDF_Font *BDF_OpenFont(BDF_ReadByte getbyte, void *info, int *error) {
228
	BDF_Font       *font;
229
	BDF_Char       *chr;
230
	char	       *data;
231
	register char  *aux;
232
	struct bdfword *word;
233
	unsigned char  *bits;
234
	double	       n;
235
	int	       numChars;
236
	int	       dwx0, dwy0;
237
	int	       dwx1, dwy1;
238
	int	       bbw, bbh, bbxoff0x, bbyoff0y, i;
239
 
240
	/* Malloc the font. */
241
	font = (BDF_Font *)malloc(sizeof(BDF_Font));
242
	if (font == NULL) {
243
		if (error != NULL) *error = BDF_MEMORYERROR;
244
		goto error;
245
	}
246
	/* Null array of characters. */
247
	font->chars = NULL;
248
	/* Looks ugly but I'm lazy... */
249
	data = (char *)malloc(65537 * sizeof(char));
250
	if (data == NULL) {
251
		if (error != NULL) *error = BDF_MEMORYERROR;
252
		goto error;
253
	}
254
	/* Zero the structure. */
255
	font->metricsSet = font->numChars = 0;
256
	dwx0 = dwy0 = 0;
257
	dwx1 = dwy1 = 0;
258
	bbw = bbh = 0;
259
	bbxoff0x = bbyoff0y = 0;
260
	/* chr holds the current character or NULL if we're not inside a character definition. */
261
	chr = NULL;
262
	for (;;) {
263
		/* Read one line at a time. */
264
		if (!readline(getbyte, info, data)) {
265
			if (*error != NULL) *error = BDF_READERROR;
266
			goto error;
267
		}
268
		/* Find end of keyword. */
269
		aux = data;
270
		while (*aux != '\0' && !isspace(*aux)) aux++;
271
		/* Find which keyword it is using gperf's hash function. */
272
		word = (struct bdfword *)in_word_set(data, aux - data);
273
		switch (word == NULL ? 0 : word->code) {
274
			case STARTFONT:
275
				/* Issue an error on versions higher than 2.2. */
276
				if (!scan(aux, "n", &n)) {
277
					if (error != NULL) *error = BDF_PARSEERROR;
278
					goto error;
279
				}
280
				if (n > 2.2) {
281
					if (error != NULL) *error = BDF_WRONGVERSION;
282
					goto error;
283
				}
284
				break;
285
			case FONTBOUNDINGBOX:
286
				/* The FONTBOUNDINGBOX values seems to be defaults for BBX values. */
287
				if (!scan(aux, "iiii", &bbw, &bbh, &bbxoff0x, &bbyoff0y)) {
288
					if (error != NULL) *error = BDF_PARSEERROR;
289
					goto error;
290
				}
291
				break;
292
			case METRICSSET:
293
				/* We only handle horizontal writing by now. */
294
				if (!scan(aux, "i", &font->metricsSet)) {
295
					if (error != NULL) *error = BDF_PARSEERROR;
296
					goto error;
297
				}
298
				if (font->metricsSet != 0) {
299
					if (error != NULL) *error = BDF_CANNOTHANDLEVERTICAL;
300
					goto error;
301
				}
302
				break;
303
			case DWIDTH:
304
				/* This is the character's width in pixels. */
305
				if (chr != NULL)
306
					if (!scan(aux, "ii", &chr->dwx0, &chr->dwy0)) {
307
						if (error != NULL) *error = BDF_PARSEERROR;
308
						goto error;
309
					}
310
				else
311
					if (!scan(aux, "ii", &dwx0, &dwy0)) {
312
						if (error != NULL) *error = BDF_PARSEERROR;
313
						goto error;
314
					}
315
				break;
316
			case DWIDTH1:
317
				/* This is the character's width in pixels for vertical writing. */
318
				if (chr != NULL)
319
					if (!scan(aux, "ii", &chr->dwx1, &chr->dwy1)) {
320
						if (error != NULL) *error = BDF_PARSEERROR;
321
						goto error;
322
					}
323
				else
324
					if (!scan(aux, "ii", &dwx1, &dwy1)) {
325
						if (error != NULL) *error = BDF_PARSEERROR;
326
						goto error;
327
					}
328
				break;
329
			case CHARS:
330
				/* We read the number of chars in this font and malloc the required memory. */
331
				if (!scan(aux, "i", &font->numChars)) {
332
					if (error != NULL) *error = BDF_PARSEERROR;
333
					goto error;
334
				}
335
				font->chars = (BDF_Char *)malloc(font->numChars * sizeof(BDF_Char));
336
				if (font->chars == NULL) {
337
					if (error != NULL) *error = BDF_MEMORYERROR;
338
					goto error;
339
				}
340
				/* Zero all characters' info. */
341
				for (i = font->numChars, chr = font->chars; i != 0; i--, chr++) {
342
					chr->name = NULL;
343
					chr->code = -1;
344
					chr->dwx0 = chr->dwy0 = 0;
345
					chr->dwx1 = chr->dwy1 = 0;
346
					chr->bbw = chr->bbh = 0;
347
					chr->bbxoff0x = chr->bbyoff0y = 0;
348
					chr->bits = NULL;
349
				}
350
				/* chr points to the current character. */
351
				chr = font->chars;
352
				break;
353
			case STARTCHAR:
354
				/* If chr is NULL there are more characters in the font then expected. */
355
				if (chr == NULL) {
356
					if (error != NULL) *error = BDF_TOOMANYCHARACTERS;
357
					goto error;
358
				}
359
				if (!scan(aux, "s", &chr->name)) {
360
					if (error != NULL) *error = BDF_PARSEERROR;
361
					goto error;
362
				}
363
				/* Copy default values. */
364
				chr->code = -1;
365
				chr->dwx0 = dwx0;
366
				chr->dwy0 = dwy0;
367
				chr->dwx1 = dwx1;
368
				chr->dwy1 = dwy1;
369
				chr->bbw = bbw;
370
				chr->bbh = bbh;
371
				chr->bbxoff0x = bbxoff0x;
372
				chr->bbyoff0y = bbyoff0y;
373
				break;
374
			case ENCODING:
375
				/* Read character's code, it can be -1. */
376
				if (chr != NULL)
377
					if (!scan(aux, "i", &chr->code)) {
378
						if (error != NULL) *error = BDF_PARSEERROR;
379
						goto error;
380
					}
381
				break;
382
			case BBX:
383
				/* The bounding box around the character's black pixels. */
384
				if (chr != NULL)
385
					if (!scan(aux, "iiii", &chr->bbw, &chr->bbh, &chr->bbxoff0x, &chr->bbyoff0y)) {
386
						if (error != NULL) *error = BDF_PARSEERROR;
387
						goto error;
388
					}
389
				break;
390
			case BITMAP:
391
				/* BITMAP signals the start of the hex data. */
392
				if (chr != NULL) {
393
					/* wbytes is the width of the char in bytes. */
394
					chr->wbytes = (chr->bbw + 7) / 8;
395
					/* Malloc the memory for the pixels. */
396
					chr->bits = (unsigned char *)malloc(chr->wbytes * chr->bbh);
397
					if (chr->bits == NULL) {
398
						if (error != NULL) *error = BDF_MEMORYERROR;
399
						goto error;
400
					}
401
					/* Read all pixels from file. */
402
					for (i = chr->bbh, bits = chr->bits; i != 0; i--) {
403
						if (!readline(getbyte, info, data)) {
404
							if (error != NULL) *error = BDF_READERROR;
405
							goto error;
406
						}
407
						aux = data;
408
						while (aux[0] != '\0' && aux[1] != '\0') {
409
							*bits++ = XVAL(aux[0]) * 16 + XVAL(aux[1]);
410
							aux += 2;
411
						}
412
					}
413
				}
414
				break;
415
			case ENDCHAR:
416
				/* Skip to the next character, makes a bound check. */
417
				chr++;
418
				if ((chr-font->chars) >= font->numChars)
419
					chr = NULL;
420
				break;
421
			case ENDFONT:
422
				/* Ends the font, if chr is not NULL then we are short on characters. */
423
				if (chr != NULL) {
424
					if (error != NULL) *error = BDF_TOOFEWCHARACTERS;
425
					goto error;
426
				}
427
				/* Sort font by character names, should be an hash table. */
428
				qsort(font->chars, font->numChars, sizeof(BDF_Char), compare);
429
				/* Fast pointers to characters encoded between [0..255]. */
430
				for (i = 0; i < 256; i++)
431
					font->code[i] = NULL;
432
				for (i = font->numChars, chr = font->chars; i != 0; i--, chr++)
433
					if (chr->code >= 0 && chr->code <= 255)
434
						font->code[chr->code] = chr;
435
				if (error != NULL) *error = BDF_OK;
436
				free(data);
437
				return font;
438
		}
439
	}
440
	error:
441
	/* Free everything. */
442
	free(data);
443
	BDF_CloseFont(font);
444
	return NULL;
445
}
446
 
447
void BDF_CloseFont(BDF_Font *font) {
448
	int	 i;
449
	BDF_Char *chr;
450
 
451
	/* Free everything. */
452
	if (font != NULL) {
453
		if (font->chars != NULL) {
454
			for (i = font->numChars, chr = font->chars; i != 0; i--, chr++) {
455
				free(chr->name);
456
				free(chr->bits);
457
			}
458
			free(font->chars);
459
		}
460
		free(font);
461
	}
462
}
463
 
464
/* Finds a char in the font, if entities is not zero then handle entities. */
465
static BDF_Char *findchar(BDF_Font *font, char **text, int entities) {
466
	char	 *aux;
467
	BDF_Char key, *chr;
468
 
469
	/* Handle entities. */
470
	if (entities != 0 && **text == '&') {
471
		if ((*text)[1] != '&') {
472
			key.name = *text + 1;
473
			aux = strchr(*text, ';');
474
			if (aux == NULL) {
475
				*text = *text + strlen(*text);
476
				return NULL;
477
			}
478
			*aux = '\0';
479
			*text = aux + 1;
480
			chr = (BDF_Char *)bsearch(&key, font->chars, font->numChars, sizeof(BDF_Char), compare);
481
			*aux = ';';
482
			return chr;
483
		} else
484
			(*text)++;
485
	}
486
	/* Return the character in the range [0..255]. */
487
	return font->code[*(unsigned char *)(*text)++];
488
}
489
 
490
/* Determines the size of the horizontal text. */
491
static void sizeh(BDF_Font *font, char *text, int entities, int *x0, int *y0, int *width, int *height) {
492
	BDF_Char *chr;
493
	int	 first, y, h, minh, maxh;
494
 
495
	first = 1;
496
	minh = *y0 = INT_MAX;
497
	maxh = INT_MIN;
498
	y = 0;
499
	while (*text != '\0') {
500
		chr = findchar(font, &text, entities);
501
		if (first != 0) {
502
			first = 0;
503
			*x0 = *width = -chr->bbxoff0x;
504
		}
505
		if (chr != NULL) {
506
			h = y - (chr->bbyoff0y + chr->bbh);
507
			if (h < minh)
508
				minh = h;
509
			h += chr->bbh - 1;
510
			if (h > maxh)
511
				maxh = h;
512
			*width += chr->dwx0;
513
			if (chr->bbyoff0y < *y0)
514
				*y0 = chr->bbyoff0y;
515
			y += chr->dwy0;
516
		}
517
	}
518
	*height = maxh - minh + 1;
519
	*y0 += *height;
520
}
521
 
522
void BDF_SizeH(BDF_Font *font, char *text, int *x0, int *y0, int *width, int *height) {
523
	int _x0, _y0, _width, _height;
524
 
525
	sizeh(font, text, 0, &_x0, &_y0, &_width, &_height);
526
	if (x0 != NULL) *x0 = _x0;
527
	if (y0 != NULL) *y0 = _y0;
528
	if (width != NULL) *width = _width;
529
	if (height != NULL) *height = _height;
530
}
531
 
532
void BDF_SizeEntitiesH(BDF_Font *font, char *text, int *x0, int *y0, int *width, int *height) {
533
	int _x0, _y0, _width, _height;
534
 
535
	sizeh(font, text, 1, &_x0, &_y0, &_width, &_height);
536
	if (x0 != NULL) *x0 = _x0;
537
	if (y0 != NULL) *y0 = _y0;
538
	if (width != NULL) *width = _width;
539
	if (height != NULL) *height = _height;
540
}
541
 
542
/* Draws a char on the surface. */
543
static void drawchar(void *surface, BDF_PutPixel putpixel, BDF_Char *chr, int x, int y, unsigned int color) {
544
	int	      xx;
545
	unsigned char *bits, *endfont, *endline;
546
 
547
	/* Calculate the position of the first pixel. */
548
	x += chr->bbxoff0x;
549
	y -= (chr->bbyoff0y + chr->bbh);
550
	bits = chr->bits;
551
	/* Put them! */
552
	for (endfont = bits + chr->wbytes * chr->bbh; bits < endfont; y++)
553
		for (endline = bits + chr->wbytes, xx = x; bits < endline; xx += 8, bits++) {
554
			if ((*bits) & 0x80) putpixel(surface, xx, y, color);
555
			if ((*bits) & 0x40) putpixel(surface, xx + 1, y, color);
556
			if ((*bits) & 0x20) putpixel(surface, xx + 2, y, color);
557
			if ((*bits) & 0x10) putpixel(surface, xx + 3, y, color);
558
			if ((*bits) & 0x08) putpixel(surface, xx + 4, y, color);
559
			if ((*bits) & 0x04) putpixel(surface, xx + 5, y, color);
560
			if ((*bits) & 0x02) putpixel(surface, xx + 6, y, color);
561
			if ((*bits) & 0x01) putpixel(surface, xx + 7, y, color);
562
		}
563
}
564
 
565
/* Draws an entire line of text. */
566
static int drawh(void *surface, BDF_PutPixel putpixel, BDF_Font *font, char *text, int entities, int x, int y, unsigned int color) {
567
	BDF_Char *chr;
568
 
569
	/* For each character... */
570
	while (*text != '\0') {
571
		chr = findchar(font, &text, entities);
572
		if (chr != NULL) {
573
			/* ... draw it. */
574
			drawchar(surface, putpixel, chr, x, y, color);
575
			x += chr->dwx0;
576
			y += chr->dwy0;
577
		}
578
	}
579
	return x;
580
}
581
 
582
int BDF_DrawH(void *surface, BDF_PutPixel putpixel, BDF_Font *font, char *text, int x, int y, unsigned int color) {
583
	return drawh(surface, putpixel, font, text, 0, x, y, color);
584
}
585
 
586
int BDF_DrawEntitiesH(void *surface, BDF_PutPixel putpixel, BDF_Font *font, char *text, int x, int y,unsigned int color) {
587
	return drawh(surface, putpixel, font, text, 1, x, y, color);
588
}