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