1 /*
2  * The font parser for the BDF files
3  *
4  * Copyright (c) 2001 by the TTF2PT1 project
5  * Copyright (c) 2001 by Sergey Babkin
6  *
7  * see COPYRIGHT for the full copyright notice
8  */
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include "pt1.h"
15 #include "global.h"
16 
17 /* prototypes of call entries */
18 static void openfont(char *fname, char *arg);
19 static void closefont( void);
20 static int getnglyphs ( void);
21 static int glnames( GLYPH *glyph_list);
22 static void readglyphs( GLYPH *glyph_list);
23 static int glenc( GLYPH *glyph_list, int *encoding, int *unimap);
24 static void fnmetrics( struct font_metrics *fm);
25 static void glpath( int glyphno, GLYPH *glyph_list);
26 static void kerning( GLYPH *glyph_list);
27 
28 /* globals */
29 
30 /* front-end descriptor */
31 struct frontsw bdf_sw = {
32 	/*name*/       "bdf",
33 	/*descr*/      "BDF bitmapped fonts",
34 	/*suffix*/     { "bdf" },
35 	/*open*/       openfont,
36 	/*close*/      closefont,
37 	/*nglyphs*/    getnglyphs,
38 	/*glnames*/    glnames,
39 	/*glmetrics*/  readglyphs,
40 	/*glenc*/      glenc,
41 	/*fnmetrics*/  fnmetrics,
42 	/*glpath*/     glpath,
43 	/*kerning*/    kerning,
44 };
45 
46 /* statics */
47 
48 #define MAXLINE	10240 /* maximal line length in the input file */
49 
50 static int lineno; /* line number */
51 
52 #define GETLEN(s)	s, (sizeof(s)-1)
53 #define LENCMP(str, txt)	strncmp(str, txt, sizeof(txt)-1)
54 
55 static FILE *bdf_file;
56 static int nglyphs;
57 static struct font_metrics fmet;
58 
59 /* many BDF fonts are of small pixel size, so we better try
60  * to scale them by an integer to keep the dimensions in
61  * whole pixels. However if the size is too big and a non-
62  * integer scaling is needed, we use the standard ttf2pt1's
63  * scaling abilities.
64  */
65 static int pixel_size;
66 static int scale;
67 static int scale_external;
68 
69 static char *slant;
70 static char xlfdname[201];
71 static char *spacing;
72 static char *charset_reg;
73 static char *charset_enc;
74 static char *fnwidth;
75 static int is_unicode = 0;
76 
77 /* tempoary storage for returning data to ttf2pt1 later on request */
78 static int maxenc = 0;
79 static int *fontenc;
80 static GENTRY **glpaths;
81 
82 static int got_glyphs = 0;
83 static GLYPH *glyphs;
84 static int curgl;
85 
86 static int readfile(FILE *f, int (*strfunc)(int len, char *str));
87 
88 /*
89  * Read the file and parse each string with strfunc(),
90  * until strfunc() returns !=0 or the end of file happens.
91  * Returns -1 on EOF or strfunc() returning <0, else 0
92  */
93 
94 static int
readfile(FILE * f,int (* strfunc)(int len,char * str))95 readfile(
96 	FILE *f,
97 	int (*strfunc)(int len, char *str)
98 )
99 {
100 	static char str[MAXLINE]; /* input line, maybe should be dynamic ? */
101 	char *s;
102 	int len, c, res;
103 
104 	len=0;
105 	while(( c=getc(f) )!=EOF) {
106 		if(c=='\n') {
107 			str[len]=0;
108 
109 			res = strfunc(len, str);
110 			lineno++;
111 			if(res<0)
112 				return -1;
113 			else if(res!=0)
114 				return 0;
115 
116 			len=0;
117 		} else if(len<MAXLINE-1) {
118 			if(c!='\r')
119 				str[len++]=c;
120 		} else {
121 			fprintf(stderr, "**** bdf: line %d is too long (>%d)\n", lineno, MAXLINE-1);
122 			exit(1);
123 		}
124 	}
125 	return -1; /* EOF */
126 }
127 
128 /*
129  * Parse the header of the font file.
130  * Stop after the line CHARS is encountered. Ignore the unknown lines.
131  */
132 
133 struct line {
134 	char *name; /* property name with trailing space */
135 	int namelen; /* length of the name string */
136 	enum {
137 		ALLOW_REPEAT = 0x01, /* this property may be repeated in multiple lines */
138 		IS_SEEN = 0x02, /* this property has been seen already */
139 		MUST_SEE = 0x04, /* this property must be seen */
140 		IS_LAST = 0x08 /* this is the last property to be read */
141 	} flags;
142 	char *fmt; /* format string for the arguments, NULL means a string arg */
143 	int nvals; /* number of values to be read by sscanf */
144 	void *vp[4]; /* pointers to values to be read */
145 };
146 
147 static struct line header[] = {
148 	{ GETLEN("FONT "), 0, " %200s", 1, {&xlfdname} },
149 	{ GETLEN("SIZE "), MUST_SEE, " %d", 1, {&pixel_size} },
150 	{ GETLEN("FONTBOUNDINGBOX "), MUST_SEE, " %hd %hd %hd %hd", 4,
151 		{&fmet.bbox[2], &fmet.bbox[3], &fmet.bbox[0], &fmet.bbox[1]} },
152 	{ GETLEN("FAMILY_NAME "), MUST_SEE, NULL, 1, {&fmet.name_family} },
153 	{ GETLEN("WEIGHT_NAME "), MUST_SEE, NULL, 1, {&fmet.name_style} },
154 	{ GETLEN("COPYRIGHT "), 0, NULL, 1, {&fmet.name_copyright} },
155 	{ GETLEN("SLANT "), MUST_SEE, NULL, 1, {&slant} },
156 	{ GETLEN("SPACING "), 0, NULL, 1, {&spacing} },
157 	{ GETLEN("SETWIDTH_NAME "), 0, NULL, 1, {&fnwidth} },
158 	{ GETLEN("CHARSET_REGISTRY "), 0, NULL, 1, {&charset_reg} },
159 	{ GETLEN("CHARSET_ENCODING "), 0, NULL, 1, {&charset_enc} },
160 	{ GETLEN("FONT_ASCENT "), 0, " %hd", 1, {&fmet.ascender} },
161 	{ GETLEN("FONT_DESCENT "), 0, " %hd", 1, {&fmet.descender} },
162 
163 	/* these 2 must go in this order for post-processing */
164 	{ GETLEN("UNDERLINE_THICKNESS "), 0, " %hd", 1, {&fmet.underline_thickness} },
165 	{ GETLEN("UNDERLINE_POSITION "), 0, " %hd", 1, {&fmet.underline_position} },
166 
167 	{ GETLEN("CHARS "), MUST_SEE|IS_LAST, " %d", 1, {&nglyphs} },
168 	{ NULL, 0, 0 } /* end mark: name==NULL */
169 };
170 
171 static int
handle_header(int len,char * str)172 handle_header(
173 	int len,
174 	char *str
175 )
176 {
177 	struct line *cl;
178 	char *s, *p;
179 	char bf[2000];
180 	int c;
181 
182 #if 0
183 	fprintf(stderr, "line: %s\n", str);
184 #endif
185 	for(cl = header; cl->name != 0; cl++) {
186 		if(strncmp(str, cl->name, cl->namelen))
187 			continue;
188 #if 0
189 		fprintf(stderr, "match: %s\n", cl->name);
190 #endif
191 		if(cl->flags & IS_SEEN) {
192 			if(cl->flags & ALLOW_REPEAT)
193 				continue;
194 
195 			fprintf(stderr, "**** input line %d redefines the property %s\n", lineno, cl->name);
196 			exit(1);
197 		}
198 		cl->flags |= IS_SEEN;
199 		if(cl->fmt == 0) {
200 			if(len - cl->namelen + 1 > sizeof bf)
201 				len = sizeof bf; /* cut it down */
202 
203 			s = bf; /* a temporary buffer to extract the value */
204 
205 			/* skip until a quote */
206 			for(p = str+cl->namelen; len!=0 && (c = *p)!=0; p++, len--) {
207 				if(c == '"') {
208 					p++;
209 					break;
210 				}
211 			}
212 			for(; len!=0 && (c = *p)!=0; p++, len--) {
213 				if(c == '"') {
214 					c = *++p;
215 					if(c == '"')
216 						*s++ = c;
217 					else
218 						break;
219 				} else
220 					*s++ = c;
221 			}
222 			*s = 0; /* end of line */
223 
224 			*((char **)(cl->vp[0])) = dupcnstring(bf, s-bf);
225 		} else {
226 			c = sscanf(str+cl->namelen, cl->fmt, cl->vp[0], cl->vp[1], cl->vp[2], cl->vp[3]);
227 			if(c != cl->nvals) {
228 				fprintf(stderr, "**** property %s at input line %d must have %d arguments\n",
229 					cl->name, lineno, cl->nvals);
230 				exit(1);
231 			}
232 		}
233 		if(cl->flags & IS_LAST)
234 			return 1;
235 		else
236 			return 0;
237 	}
238 	return 0;
239 }
240 
241 /*
242  * Parse the description of the glyphs
243  */
244 
245 static int
handle_glyphs(int len,char * str)246 handle_glyphs(
247 	int len,
248 	char *str
249 )
250 {
251 	static int inbmap=0;
252 	static char *bmap;
253 	static int xsz, ysz, xoff, yoff;
254 	static int curln;
255 	int i, c;
256 	char *p, *plim, *psz;
257 
258 	if(!LENCMP(str, "ENDFONT")) {
259 		if(curgl < nglyphs) {
260 			fprintf(stderr, "**** unexpected end of font file after %d glyphs\n", curgl);
261 			exit(1);
262 		} else
263 			return 1;
264 	}
265 	if(curgl >= nglyphs) {
266 		fprintf(stderr, "**** file contains more glyphs than advertised (%d)\n", nglyphs);
267 		exit(1);
268 	}
269 	if(!LENCMP(str, "STARTCHAR")) {
270 		/* sizeof will count \0 instead of ' ' */
271 		for(i=sizeof("STARTCHAR"); str[i] == ' '; i++)
272 			{}
273 
274 		glyphs[curgl].name = strdup(str + i);
275 		if(glyphs[curgl].name == 0) {
276 			fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
277 			exit(255);
278 		}
279 	} else if(!LENCMP(str, "ENCODING")) {
280 		if(sscanf(str, "ENCODING %d", &fontenc[curgl])!=1) {
281 			fprintf(stderr,"**** weird ENCODING statement at line %d\n", lineno);
282 			exit(1);
283 		}
284 		if(fontenc[curgl] == -1)  /* compatibility format */
285 			sscanf(str, "ENCODING -1 %d", &fontenc[curgl]);
286 		if(fontenc[curgl] > maxenc)
287 			maxenc = fontenc[curgl];
288 	} else if(!LENCMP(str, "DWIDTH")) {
289 		if(sscanf(str, "DWIDTH %d %d", &xsz, &ysz)!=2) {
290 			fprintf(stderr,"**** weird DWIDTH statement at line %d\n", lineno);
291 			exit(1);
292 		}
293 		glyphs[curgl].width = xsz*scale;
294 	} else if(!LENCMP(str, "BBX")) {
295 		if(sscanf(str, "BBX %d %d %d %d", &xsz, &ysz, &xoff, &yoff)!=4) {
296 			fprintf(stderr,"**** weird BBX statement at line %d\n", lineno);
297 			exit(1);
298 		}
299 		bmap=malloc(xsz*ysz);
300 		if(bmap==0) {
301 			fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
302 			exit(255);
303 		}
304 		glyphs[curgl].lsb = -xoff*scale;
305 		glyphs[curgl].xMin = -xoff*scale;
306 		glyphs[curgl].xMax = (xsz-xoff)*scale;
307 		glyphs[curgl].yMin = -yoff*scale;
308 		glyphs[curgl].yMax = (ysz-xoff)*scale;
309 	} else if(!LENCMP(str, "BITMAP")) {
310 		inbmap=1;
311 		curln=ysz-1; /* the lowest line has index 0 */
312 	} else if(!LENCMP(str, "ENDCHAR")) {
313 		inbmap=0;
314 		if(bmap) {
315 			glyphs[curgl].lastentry = 0;
316 			glyphs[curgl].path = 0;
317 			glyphs[curgl].entries = 0;
318 			bmp_outline(&glyphs[curgl], scale, bmap, xsz, ysz, xoff, yoff);
319 			free(bmap);
320 			/* remember in a static table or it will be erased */
321 			glpaths[curgl] = glyphs[curgl].entries;
322 			glyphs[curgl].entries = 0;
323 
324 			if(glpaths[curgl])
325 				glyphs[curgl].ttf_pathlen = 1;
326 			else
327 				glyphs[curgl].ttf_pathlen = 0;
328 		}
329 		curgl++;
330 	} else if(inbmap) {
331 		if(curln<0) {
332 			fprintf(stderr,"**** bitmap is longer than %d lines at line %d\n", ysz, lineno);
333 			exit(1);
334 		}
335 
336 		i=0;
337 		p=&bmap[curln*xsz]; psz=p+xsz;
338 		while(i<len) {
339 			c=str[i++];
340 			if(!isxdigit(c)) {
341 				fprintf(stderr,"**** non-hex digit in bitmap at line %d\n", lineno);
342 				exit(1);
343 			}
344 			if(c<='9')
345 				c-='0';
346 			else
347 				c= tolower(c)-'a'+10;
348 
349 			for(plim=p+4; p<psz && p<plim; c<<=1)
350 				*p++ = (( c & 0x08 )!=0);
351 		}
352 		if(p<psz) {
353 			fprintf(stderr,"**** bitmap line is too short at line %d\n", lineno);
354 			exit(1);
355 		}
356 		curln--;
357 	}
358 	return 0;
359 }
360 
361 /*
362  * Read all the possible information about the glyphs
363  */
364 
365 static void
readglyphs(GLYPH * glyph_list)366 readglyphs(
367 	GLYPH *glyph_list
368 )
369 {
370 	int i;
371 	GLYPH *g;
372 
373 	if(got_glyphs)
374 		return;
375 
376 	/* pass them to handle_glyphs() through statics */
377 	glyphs = glyph_list;
378 	curgl = 2; /* skip the empty glyph and .notdef */
379 
380 	/* initialize the empty glyph and .notdef */
381 
382 	for(i=0; i<2; i++) {
383 		g = &glyphs[i];
384 		g->lsb = 0;
385 		g->width = fmet.bbox[2];
386 		g->xMin = 0;
387 		g->yMin = 0;
388 	}
389 	g = &glyphs[0];
390 	g->name = ".notdef";
391 	g->xMax = fmet.bbox[2]*4/5;
392 	g->yMax = fmet.bbox[3]*4/5;
393 	g->entries = g->path = g->lastentry = 0;
394 	/* make it look as a black square */
395 	fg_rmoveto(g, 0.0, 0.0);
396 	fg_rlineto(g, 0.0, (double)g->yMax);
397 	fg_rlineto(g, (double)g->xMax, (double)g->yMax);
398 	fg_rlineto(g, (double)g->xMax, 0.0);
399 	fg_rlineto(g, 0.0, 0.0);
400 	g_closepath(g);
401 	glpaths[0] = g->entries;
402 	g->entries = 0;
403 	g->ttf_pathlen = 4;
404 
405 	g = &glyphs[1];
406 	g->name = ".null";
407 	g->xMax = g->yMax = 0;
408 	g->ttf_pathlen = 0;
409 
410 	if(readfile(bdf_file, handle_glyphs) < 0) {
411 		fprintf(stderr, "**** file does not contain the ENDFONT line\n");
412 		exit(1);
413 	}
414 	got_glyphs = 1;
415 }
416 
417 /*
418  * Open font and prepare to return information to the main driver.
419  * May print error and warning messages.
420  * Exit on error.
421  */
422 
423 static void
openfont(char * fname,char * arg)424 openfont(
425 	char *fname,
426 	char *arg /* unused now */
427 )
428 {
429 	struct line *cl;
430 	int i, l;
431 
432 	if ((bdf_file = fopen(fname, "r")) == NULL) {
433 		fprintf(stderr, "**** Cannot open file '%s'\n", fname);
434 		exit(1);
435 	} else {
436 		WARNING_2 fprintf(stderr, "Processing file %s\n", fname);
437 	}
438 
439 	lineno = 1;
440 
441 	for(cl = header; cl->name != 0; cl++)
442 		cl->flags &= ~IS_SEEN;
443 	if(readfile(bdf_file, handle_header) < 0) {
444 		fprintf(stderr, "**** file does not contain the CHARS definition\n");
445 		exit(1);
446 	}
447 	for(cl = header; cl->name != 0; cl++) {
448 		if( (cl->flags & MUST_SEE) && !(cl->flags & IS_SEEN) ) {
449 			fprintf(stderr, "**** mandatory property %sis not found in the input line\n",
450 				cl->name); /* cl->name has a space at the end */
451 			exit(1);
452 		}
453 
454 		/* set a few defaults */
455 		if( !(cl->flags & IS_SEEN) ) {
456 			if(cl->vp[0] == &fmet.underline_thickness) {
457 				fmet.underline_thickness = 1;
458 			} else if(cl->vp[0] == &fmet.underline_position) {
459 				fmet.underline_position = fmet.bbox[1] + fmet.underline_thickness
460 					- (pixel_size - fmet.bbox[3]);
461 			} else if(cl->vp[0] == &fmet.ascender) {
462 				fmet.ascender = fmet.bbox[2] + fmet.bbox[0];
463 			} else if(cl->vp[0] == &fmet.descender) {
464 				fmet.descender = fmet.bbox[0];
465 			}
466 		}
467 	}
468 
469 	nglyphs += 2; /* add empty glyph and .notdef */
470 
471 	/* postprocessing to compensate for the differences in the metric formats */
472 	fmet.bbox[2] += fmet.bbox[0];
473 	fmet.bbox[3] += fmet.bbox[1];
474 
475 	scale = 1000/pixel_size; /* XXX ? */
476 	if(scale*pixel_size < 950) {
477 		scale = 1;
478 		scale_external = 1;
479 		fmet.units_per_em = pixel_size;
480 	} else {
481 		scale_external = 0;
482 		fmet.units_per_em = scale*pixel_size;
483 
484 		fmet.underline_position *= scale;
485 		fmet.underline_thickness *= scale;
486 		fmet.ascender *= scale;
487 		fmet.descender *= scale;
488 		for(i=0; i<4; i++)
489 			fmet.bbox[i] *= scale;
490 	}
491 
492 	fmet.italic_angle = 0.0;
493 	if(spacing == 0 /* possibly an old font */
494 	|| toupper(spacing[0]) != 'P') /* or anything non-proportional */
495 		fmet.is_fixed_pitch = 1;
496 	else
497 		fmet.is_fixed_pitch = 0;
498 
499 	if(fmet.name_copyright==NULL)
500 		fmet.name_copyright = "";
501 
502 	/* create the full name */
503 	l = strlen(fmet.name_family)
504 		+ (fmet.name_style? strlen(fmet.name_style) : 0)
505 		+ (fnwidth? strlen(fnwidth) : 0)
506 		+ strlen("Oblique") + 1;
507 
508 	if(( fmet.name_full = malloc(l) )==NULL) {
509 		fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
510 		exit(255);
511 	}
512 	strcpy(fmet.name_full, fmet.name_family);
513 	if(fnwidth && strcmp(fnwidth, "Normal")) {
514 		strcat(fmet.name_full, fnwidth);
515 	}
516 	if(fmet.name_style && strcmp(fmet.name_style, "Medium")) {
517 		strcat(fmet.name_full, fmet.name_style);
518 	}
519 	switch(toupper(slant[0])) {
520 	case 'O':
521 		strcat(fmet.name_full, "Oblique");
522 		break;
523 	case 'I':
524 		strcat(fmet.name_full, "Italic");
525 		break;
526 	}
527 
528 	fmet.name_ps = fmet.name_full;
529 	fmet.name_version = "1.0";
530 
531 	if(charset_reg && charset_enc
532 	&& !strcmp(charset_reg, "iso10646") && !strcmp(charset_enc, "1"))
533 		is_unicode = 1;
534 
535 	if(( fontenc = calloc(nglyphs, sizeof *fontenc) )==NULL) {
536 		fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
537 		exit(255);
538 	}
539 	for(i=0; i<nglyphs; i++)
540 		fontenc[i] = -1;
541 	if(( glpaths = calloc(nglyphs, sizeof *glpaths) )==NULL) {
542 		fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
543 		exit(255);
544 	}
545 }
546 
547 /*
548  * Close font.
549  * Exit on error.
550  */
551 
552 static void
closefont(void)553 closefont(
554 	void
555 )
556 {
557 	if(fclose(bdf_file) < 0) {
558 		WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n");
559 	}
560 }
561 
562 /*
563  * Get the number of glyphs in font.
564  */
565 
566 static int
getnglyphs(void)567 getnglyphs (
568 	void
569 )
570 {
571 	return nglyphs;
572 }
573 
574 /*
575  * Get the names of the glyphs.
576  * Returns 0 if the names were assigned, non-zero if the font
577  * provides no glyph names.
578  */
579 
580 static int
glnames(GLYPH * glyph_list)581 glnames(
582 	GLYPH *glyph_list
583 )
584 {
585 	readglyphs(glyph_list);
586 	return 0;
587 }
588 
589 /*
590  * Get the original encoding of the font.
591  * Returns 1 for if the original encoding is Unicode, 2 if the
592  * original encoding is other 16-bit, 0 if 8-bit.
593  */
594 
595 static int
glenc(GLYPH * glyph_list,int * encoding,int * unimap)596 glenc(
597 	GLYPH *glyph_list,
598 	int *encoding,
599 	int *unimap
600 )
601 {
602 	int i, douni, e;
603 
604 	if(is_unicode || forcemap)
605 		douni = 1;
606 	else
607 		douni = 0;
608 
609 	for(i=0; i<nglyphs; i++) {
610 		e = fontenc[i];
611 		if(douni)
612 			e = unicode_rev_lookup(e);
613 		if(e>=0 && e<ENCTABSZ && encoding[e] == -1)
614 			encoding[e] = i;
615 	}
616 
617 	if(is_unicode)
618 		return 1;
619 	else if(maxenc > 255)
620 		return 2;
621 	else
622 		return 0;
623 }
624 
625 /*
626  * Get the font metrics
627  */
628 static void
fnmetrics(struct font_metrics * fm)629 fnmetrics(
630 	struct font_metrics *fm
631 )
632 {
633 	*fm = fmet;
634 }
635 
636 /*
637  * Get the path of contrours for a glyph.
638  */
639 
640 static void
glpath(int glyphno,GLYPH * glyf_list)641 glpath(
642 	int glyphno,
643 	GLYPH *glyf_list
644 )
645 {
646 	readglyphs(glyf_list);
647 	glyf_list[glyphno].entries = glpaths[glyphno];
648 	glpaths[glyphno] = 0;
649 }
650 
651 /*
652  * Get the kerning data.
653  */
654 
655 static void
kerning(GLYPH * glyph_list)656 kerning(
657 	GLYPH *glyph_list
658 )
659 {
660 	return; /* no kerning in BDF */
661 }
662