1 /**
2  ** fdv_fna.c -- driver for ascii font file format
3  **
4  ** Copyright (C) 2003 Dimitar Zhekov
5  ** [e-mail: jimmy@is-vn.bg]
6  **
7  ** This file is part of the GRX graphics library.
8  **
9  ** The GRX graphics library is free software; you can redistribute it
10  ** and/or modify it under some conditions; see the "copying.grx" file
11  ** for details.
12  **
13  ** This library is distributed in the hope that it will be useful,
14  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  **
17  **/
18 
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "libgrx.h"
25 #include "grfontdv.h"
26 #include "arith.h"
27 
28 #ifndef SEEK_SET
29 #define SEEK_SET        0
30 #endif
31 
32 static FILE   *fontfp = NULL;
33 
34 static struct {
35     char buffer[131];
36     long offset;
37     int  index;
38     int  minchar;
39     int  maxchar;
40     int  width;
41     int  height;
42     int  isfixed;
43 } fhdr;
44 
readline(void)45 static int readline(void)
46 {
47 	int res;
48 	char *s;
49 	GRX_ENTER();
50 	res = FALSE;
51 	do {
52 	    if(fgets(fhdr.buffer, sizeof fhdr.buffer, fontfp) == NULL) {
53 		DBGPRINTF(DBG_FONT, ("read line failed at index %d\n", fhdr.index));
54 		goto done;
55 	    }
56 	    s = fhdr.buffer + strlen(fhdr.buffer);
57 	    while(--s >= fhdr.buffer && (*s == '\n' || *s == '\r'));
58 	    *++s = '\0';
59 	    if(strlen(fhdr.buffer) > 127) {
60 		DBGPRINTF(DBG_FONT, ("line too long \"%s\"", fhdr.buffer));
61 		goto done;
62 	    }
63 	    while(--s >= fhdr.buffer && isspace(*s));
64 	    *++s = '\0';
65 	} while(s == fhdr.buffer || *fhdr.buffer == ';');
66 	res = TRUE;
67 done:	GRX_RETURN(res);
68 }
69 
readindex(int chr,int y)70 static int readindex(int chr, int y)
71 {
72 	int res;
73 	int index;
74 	GRX_ENTER();
75 	res = FALSE;
76 	index = (chr - fhdr.minchar) * fhdr.height + y;
77 	if(fhdr.index > index) {
78 	    DBGPRINTF(DBG_FONT, ("current index %d > requested %d\n", fhdr.index, index));
79 	    if(fseek(fontfp, fhdr.offset, SEEK_SET) < 0) goto done;
80 	    fhdr.index = -1;
81 	}
82 	while(fhdr.index < index) {
83 	    if(!readline()) goto done;
84 	    fhdr.index++;
85 	}
86 	res = TRUE;
87 done:	GRX_RETURN(res);
88 }
89 
cleanup(void)90 static void cleanup(void)
91 {
92 	GRX_ENTER();
93 	if(fontfp != NULL) fclose(fontfp);
94 	fontfp = NULL;
95 	fhdr.index = -1;
96 	GRX_LEAVE();
97 }
98 
openfile(char * fname)99 static int openfile(char *fname)
100 {
101 	int res;
102 	GRX_ENTER();
103 	res = FALSE;
104 	cleanup();
105 	fontfp = fopen(fname, "rb");
106 	if(fontfp == NULL) {
107 	    DBGPRINTF(DBG_FONT, ("fopen(\"%s\") failed\n", fname));
108 	    goto done;
109 	}
110 	res = TRUE;
111 done:	if(!res) cleanup();
112 	GRX_RETURN(res);
113 }
114 
header(GrFontHeader * hdr)115 static int header(GrFontHeader *hdr)
116 {
117 	int res;
118 	char *s;
119 	int index;
120 	int i, n;
121 	static char *names[] = {
122 	    "name",
123 	    "family",
124 	    "isfixed",
125 	    "width",
126 	    "height",
127 	    "minchar",
128 	    "maxchar",
129 	    "baseline",
130 	    "undwidth",
131 	    "avgwidth",
132 	    "minwidth",
133 	    "maxwidth",
134 	    "note",
135 	    NULL
136 	};
137 	int attrib;
138 	GRX_ENTER();
139 	res = FALSE;
140 	if(fontfp == NULL) goto done;
141 	attrib = 0;
142 	while(readline() && isalpha(*fhdr.buffer)) {
143 	    fhdr.offset = ftell(fontfp);
144 	    if(fhdr.offset == -1) {
145 		DBGPRINTF(DBG_FONT, ("ftell failed after \"%s\"\n", fhdr.buffer));
146 		goto done;
147 	    }
148 	    if(!strcmp(fhdr.buffer, "note")) continue;
149 	    s = fhdr.buffer;
150 	    while(isalpha(*++s));
151 	    if(!isspace(*s)) {
152 		DBGPRINTF(DBG_FONT, ("invalid header line \"%s\"\n", fhdr.buffer));
153 		goto done;
154 	    }
155 	    *s = '\0';
156 	    while(isspace(*++s));
157 	    for(index = 0; names[index] != NULL; index++)
158 		if(!strcmp(fhdr.buffer, names[index])) break;
159 	    if(names[index] == NULL) {
160 		DBGPRINTF(DBG_FONT, ("unknown attribute \"%s\"\n", fhdr.buffer));
161 		goto done;
162 	    }
163 	    if(index == 9) index = 3;
164 	    if(attrib & (1 << index)) {
165 		DBGPRINTF(DBG_FONT, ("duplicate attribute \"%s\"\n", fhdr.buffer));
166 		goto done;
167 	    }
168 	    if(index >= 2 && index <= 11) {
169 		if(sscanf(s, "%d%n", &i, &n) != 1 || n != strlen(s)) {
170 		    DBGPRINTF(DBG_FONT, ("invalid number \"%s\"\n", s));
171 		    goto done;
172 		}
173 		if(i < 0) {
174 		    DBGPRINTF(DBG_FONT, ("negative number %d\n", i));
175 		    goto done;
176 		}
177 	    }
178 	    switch(index) {
179 		case 0 : strcpy(hdr->name, s); break;
180 		case 1 : strcpy(hdr->family, s); break;
181 		case 2 :
182 		    fhdr.isfixed = i;
183 		    hdr->proportional = !fhdr.isfixed;
184 		    break;
185 		case 3 : hdr->width = fhdr.width = i; break;
186 		case 4 : hdr->height = fhdr.height = i; break;
187 		case 5 : hdr->minchar = fhdr.minchar = i; break;
188 		case 6 : fhdr.maxchar = i; break;
189 		case 7 : hdr->baseline = i; break;
190 		case 8 : hdr->ulheight = i; break;
191 		case 10 :
192 		    if(i == 0) {
193 			DBGPRINTF(DBG_FONT, ("invalid width %d\n", i));
194 		        goto done;
195 		    }
196 		    break;
197 		case 11 :
198 		    if(i > 127) {
199 			DBGPRINTF(DBG_FONT, ("invalid width %d\n", i));
200 		        goto done;
201 		    }
202 		    break;
203 		case 12 : continue;
204 		default :
205 		    DBGPRINTF(DBG_FONT, ("unsupported attribute \"%s\"\n", fhdr.buffer));
206 		    goto done;
207 	    }
208 	    attrib |= 1 << index;
209 	}
210 	if((attrib & 0xFF) != 0xFF) {
211 	    DBGPRINTF(DBG_FONT, ("insufficient attributes 0x%x\n", attrib));
212 	    goto done;
213 	}
214 	hdr->numchars = fhdr.maxchar - fhdr.minchar + 1;
215 	if(hdr->numchars <= 0) {
216 	    DBGPRINTF(DBG_FONT, ("minchar %d > maxchar %d\n", fhdr.minchar, fhdr.maxchar));
217 	    goto done;
218 	}
219 	fhdr.index++;
220 	hdr->scalable = FALSE;
221 	hdr->preloaded = FALSE;
222 	hdr->modified = GR_FONTCVT_NONE;
223 	if((attrib & 0x0100) == 0) hdr->ulheight = imax(1, hdr->height / 15);
224 	hdr->ulpos = hdr->height - hdr->ulheight;
225 	res = TRUE;
226 done:	GRX_RETURN(res);
227 }
228 
charwdt(int chr)229 static int charwdt(int chr)
230 {
231 	int res;
232 	GRX_ENTER();
233 	DBGPRINTF(DBG_FONT, ("charwdt(%d)\n", chr));
234 	res = -1;
235 	if(fontfp != NULL && chr >= fhdr.minchar && chr <= fhdr.maxchar) {
236 	    if(fhdr.isfixed) res = fhdr.width;
237 	    else if(readindex(chr, 0)) res = strlen(fhdr.buffer);
238 	}
239 	GRX_RETURN(res);
240 }
241 
bitmap(int chr,int w,int h,char * buffer)242 static int bitmap(int chr, int w, int h, char *buffer)
243 {
244 	int res;
245 	int y, x;
246 	int bytes;
247 	GRX_ENTER();
248 	DBGPRINTF(DBG_FONT, ("bitmap(%d, %d, %d)\n", chr, w, h));
249 	res = FALSE;
250 	if(w != charwdt(chr) || h != fhdr.height) goto done;
251 	bytes = (w - 1) / 8 + 1;
252 	memset(buffer, '\0', bytes * h);
253 	for(y = 0; y < h; y++) {
254 	    if(!readindex(chr, y)) goto done;
255 	    if(strlen(fhdr.buffer) != w) {
256 		DBGPRINTF(DBG_FONT, ("strlen(\"%s\") != %d\n", fhdr.buffer, w));
257 		goto done;
258 	    }
259 	    for(x = 0; x < w; x++) {
260 		if(fhdr.buffer[x] == '#') buffer[x >> 3] |= 1 << (7 - (x & 7));
261 		else if(fhdr.buffer[x] != '.') {
262 		    DBGPRINTF(DBG_FONT, ("invalid character data \'%c\'\n", fhdr.buffer[x]));
263 		    goto done;
264 		}
265 	    }
266 	    buffer += bytes;
267 	}
268 	res = TRUE;
269 done:	GRX_RETURN(res);
270 }
271 
272 GrFontDriver _GrFontDriverFNA = {
273     "FNA",                              /* driver name (doc only) */
274     ".fna",                             /* font file extension */
275     FALSE,                              /* scalable */
276     openfile,                           /* file open and check routine */
277     header,                             /* font header reader routine */
278     charwdt,                            /* character width reader routine */
279     bitmap,                             /* character bitmap reader routine */
280     cleanup                             /* cleanup routine */
281 };
282