xref: /plan9/sys/src/cmd/proof/font.c (revision bacfa46c74e1c310aff15aef9cb6bc4e6302513a)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include <bio.h>
6 #include "proof.h"
7 
8 char	fname[NFONT][20];	/* font names */
9 char lastload[NFONT][20];	/* last file name prefix loaded for this font */
10 Font	*fonttab[NFONT][NSIZE];	/* pointers to fonts */
11 int	fmap[NFONT];		/* what map to use with this font */
12 
13 static void	bufchar(Point, Subfont *, uchar *);
14 static void	loadfont(int, int);
15 static void	fontlookup(int, char *);
16 static void	buildxheight(Biobuf*);
17 static void	buildmap(Biobuf*);
18 static void	buildtroff(char *);
19 static void	addmap(int, char *, int);
20 static char	*map(Rune*, int);
21 static void	scanstr(char *, char *, char **);
22 
23 int	specfont;	/* somehow, number of special font */
24 
25 #define	NMAP	5
26 #define	QUICK	2048	/* char values less than this are quick to look up */
27 #define	eq(s,t)	strcmp((char *) s, (char *) t) == 0
28 
29 int	curmap	= -1;	/* what map are we working on */
30 
31 typedef struct Link Link;
32 struct Link	/* link names together */
33 {
34 	uchar	*name;
35 	int	val;
36 	Link	*next;
37 };
38 
39 typedef struct Map Map;
40 struct Map	/* holds a mapping from uchar name to index */
41 {
42 	double	xheight;
43 	Rune	quick[QUICK];	/* low values get special treatment */
44 	Link	*slow;	/* other stuff goes into a link list */
45 };
46 
47 Map	charmap[5];
48 
49 typedef struct Fontmap Fontmap;
50 struct Fontmap	/* mapping from troff name to filename */
51 {
52 	char	*troffname;
53 	char	*prefix;
54 	int	map;		/* which charmap to use for this font */
55 	char	*fallback;	/* font to look in if can't find char here */
56 };
57 
58 Fontmap	fontmap[100];
59 int	pos2fontmap[NFONT];	/* indexed by troff font position, gives Fontmap */
60 int	nfontmap	= 0;	/* how many are there */
61 
62 
63 void
dochar(Rune r[])64 dochar(Rune r[])
65 {
66 	char *s, *fb;
67 	Font *f;
68 	Point p;
69 	int fontno, fm, i;
70 	char buf[32];
71 
72 	fontno = curfont;
73 	if((s = map(r, curfont)) == 0){		/* not on current font */
74 		if ((s = map(r, specfont)) != 0)	/* on special font */
75 			fontno = specfont;
76 		else{
77 			/* look for fallback */
78 			fm = pos2fontmap[curfont];
79 			fb = fontmap[fm].fallback;
80 			if(fb){
81 				/* see if fallback is mounted */
82 				for(i = 0; i < NFONT; i++){
83 					if(eq(fb, fontmap[pos2fontmap[i]].troffname)){
84 						s = map(r, i);
85 						if(s){
86 							fontno = i;
87 							goto found;
88 						}
89 					}
90 				}
91 			}
92 			/* no such char; use name itself on defont */
93 			/* this is not a general solution */
94 			p.x = hpos/DIV + xyoffset.x + offset.x;
95 			p.y = vpos/DIV + xyoffset.y + offset.y;
96 			p.y -= font->ascent;
97 			snprint(buf, sizeof buf, "%S", r);
98 			string(screen, p, display->black, ZP, font, buf);
99 			return;
100 		}
101 	}
102     found:
103 	p.x = hpos/DIV + xyoffset.x + offset.x;
104 	p.y = vpos/DIV + xyoffset.y + offset.y;
105 	while ((f = fonttab[fontno][cursize]) == 0)
106 		loadfont(fontno, cursize);
107 	p.y -= f->ascent;
108 	dprint(2, "putting %S at %d,%d font %d, size %d\n", r, p.x, p.y, fontno, cursize);
109 	string(screen, p, display->black, ZP, f, s);
110 }
111 
112 /* imported from libdraw/arith.c to permit an extern log2 function */
113 static int log2[] = {
114 	-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4,
115 	-1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5
116 };
117 
118 static void
loadfont(int n,int s)119 loadfont(int n, int s)
120 {
121 	char file[256];
122 	int i, fd, t, deep;
123 	static char *try[3] = {"", "times/R.", "pelm/"};
124 	Subfont *f;
125 	Font *ff;
126 
127 	try[0] = fname[n];
128 	for (t = 0; t < 3; t++){
129 		i = s * mag * charmap[fmap[n]].xheight/0.72;	/* a pixel is 0.72 points */
130 		if (i < MINSIZE)
131 			i = MINSIZE;
132 		dprint(2, "size %d, i %d, mag %g\n", s, i, mag);
133 		for(; i >= MINSIZE; i--){
134 			/* if .font file exists, take that */
135 			snprint(file, sizeof file, "%s/%s%d.font",
136 				libfont, try[t], i);
137 			ff = openfont(display, file);
138 			if(ff != 0){
139 				fonttab[n][s] = ff;
140 				dprint(2, "using %s for font %d %d\n", file, n, s);
141 				return;
142 			}
143 			/* else look for a subfont file */
144 			for (deep = log2[screen->depth]; deep >= 0; deep--){
145 				snprint(file, sizeof file, "%s/%s%d.%d",
146 					libfont, try[t], i, deep);
147 				dprint(2, "trying %s for %d\n", file, i);
148 				if ((fd = open(file, 0)) >= 0){
149 					f = readsubfont(display, file, fd, 0);
150 					if (f == 0) {
151 						fprint(2, "can't rdsubfontfile %s: %r\n", file);
152 						exits("rdsubfont");
153 					}
154 					close(fd);
155 					ff = mkfont(f, 0);
156 					if(ff == 0){
157 						fprint(2, "can't mkfont %s: %r\n", file);
158 						exits("rdsubfont");
159 					}
160 					fonttab[n][s] = ff;
161 					dprint(2, "using %s for font %d %d\n", file, n, s);
162 					return;
163 				}
164 			}
165 		}
166 	}
167 	fprint(2, "can't find font %s.%d or substitute, quitting\n", fname[n], s);
168 	exits("no font");
169 }
170 
171 void
loadfontname(int n,char * s)172 loadfontname(int n, char *s)
173 {
174 	int i;
175 	Font *f, *g = 0;
176 
177 	if (strcmp(s, fname[n]) == 0)
178 		return;
179 	if(fname[n] && fname[n][0]){
180 		if(lastload[n] && strcmp(lastload[n], fname[n]) == 0)
181 			return;
182 		strcpy(lastload[n], fname[n]);
183 	}
184 	fontlookup(n, s);
185 	for (i = 0; i < NSIZE; i++)
186 		if (f = fonttab[n][i]){
187 			if (f != g) {
188 				freefont(f);
189 				g = f;
190 			}
191 			fonttab[n][i] = 0;
192 		}
193 }
194 
195 void
allfree(void)196 allfree(void)
197 {
198 	int i;
199 
200 	for (i=0; i<NFONT; i++)
201 		loadfontname(i, "??");
202 }
203 
204 
205 void
readmapfile(char * file)206 readmapfile(char *file)
207 {
208 	Biobuf *fp;
209 	char *p, cmd[100];
210 
211 	if ((fp=Bopen(file, OREAD)) == 0){
212 		fprint(2, "proof: can't open map file %s\n", file);
213 		exits("urk");
214 	}
215 	while((p=Brdline(fp, '\n')) != 0) {
216 		p[Blinelen(fp)-1] = 0;
217 		scanstr(p, cmd, 0);
218 		if(p[0]=='\0' || eq(cmd, "#"))	/* skip comments, empty */
219 			continue;
220 		else if(eq(cmd, "xheight"))
221 			buildxheight(fp);
222 		else if(eq(cmd, "map"))
223 			buildmap(fp);
224 		else if(eq(cmd, "special"))
225 			buildtroff(p);
226 		else if(eq(cmd, "troff"))
227 			buildtroff(p);
228 		else
229 			fprint(2, "weird map line %s\n", p);
230 	}
231 	Bterm(fp);
232 }
233 
234 static void
buildxheight(Biobuf * fp)235 buildxheight(Biobuf *fp)	/* map goes from char name to value to print via *string() */
236 {
237 	char *line;
238 
239 	line = Brdline(fp, '\n');
240 	if(line == 0){
241 		fprint(2, "proof: bad map file\n");
242 		exits("map");
243 	}
244 	charmap[curmap].xheight = atof(line);
245 }
246 
247 static void
buildmap(Biobuf * fp)248 buildmap(Biobuf *fp)	/* map goes from char name to value to print via *string() */
249 {
250 	uchar *p, *line, ch[100];
251 	int val;
252 	Rune r;
253 
254 	curmap++;
255 	if(curmap >= NMAP){
256 		fprint(2, "proof: out of char maps; recompile\n");
257 		exits("charmap");
258 	}
259 	while ((line = Brdline(fp, '\n'))!= 0){
260 		if (line[0] == '\n')
261 			return;
262 		line[Blinelen(fp)-1] = 0;
263 		scanstr((char *) line, (char *) ch, (char **) &p);
264 		if (ch[0] == '\0') {
265 			fprint(2, "bad map file line '%s'\n", (char*)line);
266 			continue;
267 		}
268 		val = strtol((char *) p, 0, 10);
269 dprint(2, "buildmap %s (%x %x) %s %d\n", (char*)ch, ch[0], ch[1], (char*)p, val);
270 		chartorune(&r, (char*)ch);
271 		if(utflen((char*)ch)==1 && r<QUICK)
272 			charmap[curmap].quick[r] = val;
273 		else
274 			addmap(curmap, strdup((char *) ch), val);	/* put somewhere else */
275 	}
276 }
277 
278 static void
addmap(int n,char * s,int val)279 addmap(int n, char *s, int val)	/* stick a new link on */
280 {
281 	Link *p = (Link *) malloc(sizeof(Link));
282 	Link *prev = charmap[n].slow;
283 
284 	if(p == 0)
285 		exits("out of memory in addmap");
286 	p->name = (uchar *) s;
287 	p->val = val;
288 	p->next = prev;
289 	charmap[n].slow = p;
290 }
291 
292 static void
buildtroff(char * buf)293 buildtroff(char *buf)	/* map troff names into bitmap filenames */
294 {				/* e.g., R -> times/R., I -> times/I., etc. */
295 	char *p, cmd[100], name[200], prefix[400], fallback[100];
296 
297 	scanstr(buf, cmd, &p);
298 	scanstr(p, name, &p);
299 	scanstr(p, prefix, &p);
300 	while(*p!=0 && isspace(*p))
301 		p++;
302 	if(*p != 0){
303 		scanstr(p, fallback, &p);
304 		fontmap[nfontmap].fallback = strdup(fallback);
305 	}else
306 		fontmap[nfontmap].fallback = 0;
307 	fontmap[nfontmap].troffname = strdup(name);
308 	fontmap[nfontmap].prefix = strdup(prefix);
309 	fontmap[nfontmap].map = curmap;
310 	dprint(2, "troff name %s is bitmap %s map %d in slot %d fallback %s\n",
311 		name, prefix, curmap, nfontmap, fontmap[nfontmap].fallback?
312 		fontmap[nfontmap].fallback: "<null>");
313 	nfontmap++;
314 }
315 
316 static void
fontlookup(int n,char * s)317 fontlookup(int n, char *s)	/* map troff name of s into position n */
318 {
319 	int i;
320 
321 	for(i = 0; i < nfontmap; i++)
322 		if (eq(s, fontmap[i].troffname)) {
323 			strcpy(fname[n], fontmap[i].prefix);
324 			fmap[n] = fontmap[i].map;
325 			pos2fontmap[n] = i;
326 			if (eq(s, "S"))
327 				specfont = n;
328 			dprint(2, "font %d %s is %s\n", n, s, fname[n]);
329 			return;
330 		}
331 	/* god help us if this font isn't there */
332 }
333 
334 
335 static char *
map(Rune rp[],int font)336 map(Rune rp[], int font)	/* figure out mapping for char in this font */
337 {
338 	static char s[100];
339 	unsigned m;
340 	char c[32];
341 	Link *p;
342 	Rune r;
343 
344 	if((unsigned)font >= NFONT) {
345 		dprint(2, "map: font %ud >= NFONT (%d)\n", font, NFONT);
346 		return 0;
347 	}
348 	m = fmap[font];
349 	if(m >= nelem(charmap)) {
350 		dprint(2, "map: fmap[font] %ud >= nelem(charmap) (%d)\n",
351 			m, nelem(charmap));
352 		return 0;
353 	}
354 	if(rp[1] == 0 && rp[0] < QUICK)		/* fast lookup */
355 		r = charmap[m].quick[rp[0]];
356 	else {			/* high-valued or compound character name */
357 		snprint(c, sizeof c, "%S", rp);
358 		r = 0;
359 		for (p = charmap[m].slow; p; p = p->next)
360 			if(eq(c, p->name)){
361 				r = p->val;
362 				break;
363 			}
364 	}
365 	if(r == 0){	/* not there */
366 		dprint(2, "didn't find %S font# %d\n", rp, font);
367 		return 0;
368 	}
369 	dprint(2, "map %S to %s font# %d\n", rp, s, font);
370 	s[runetochar(s, &r)] = 0;
371 	return s;
372 }
373 
374 static void
scanstr(char * s,char * ans,char ** ep)375 scanstr(char *s, char *ans, char **ep)
376 {
377 	for (; isspace((uchar) *s); s++)
378 		;
379 	for (; *s!=0 && !isspace((uchar) *s); )
380 		*ans++ = *s++;
381 	*ans = 0;
382 	if (ep)
383 		*ep = s;
384 }
385