xref: /plan9/sys/src/cmd/proof/font.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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
64 dochar(Rune r[])
65 {
66 	char *s, *fb;
67 	Font *f;
68 	Point p;
69 	int fontno, fm, i;
70 	char buf[10];
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 			sprint(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 
113 static void
114 loadfont(int n, int s)
115 {
116 	char file[100];
117 	int i, fd, t, deep;
118 	static char *try[3] = {"", "times/R.", "pelm/"};
119 	Subfont *f;
120 	Font *ff;
121 
122 	try[0] = fname[n];
123 	for (t = 0; t < 3; t++){
124 		i = s * mag * charmap[fmap[n]].xheight/0.72;	/* a pixel is 0.72 points */
125 		if (i < MINSIZE)
126 			i = MINSIZE;
127 		dprint(2, "size %d, i %d, mag %g\n", s, i, mag);
128 		for(; i >= MINSIZE; i--){
129 			/* if .font file exists, take that */
130 			sprint(file, "%s/%s%d.font", libfont, try[t], i);
131 			ff = openfont(display, file);
132 			if(ff != 0){
133 				fonttab[n][s] = ff;
134 				dprint(2, "using %s for font %d %d\n", file, n, s);
135 				return;
136 			}
137 			/* else look for a subfont file */
138 			for (deep = log2[screen->depth]; deep >= 0; deep--){
139 				sprint(file, "%s/%s%d.%d", libfont, try[t], i, deep);
140 				dprint(2, "trying %s for %d\n", file, i);
141 				if ((fd = open(file, 0)) >= 0){
142 					f = readsubfont(display, file, fd, 0);
143 					if (f == 0) {
144 						fprint(2, "can't rdsubfontfile %s: %r\n", file);
145 						exits("rdsubfont");
146 					}
147 					close(fd);
148 					ff = mkfont(f, 0);
149 					if(ff == 0){
150 						fprint(2, "can't mkfont %s: %r\n", file);
151 						exits("rdsubfont");
152 					}
153 					fonttab[n][s] = ff;
154 					dprint(2, "using %s for font %d %d\n", file, n, s);
155 					return;
156 				}
157 			}
158 		}
159 	}
160 	fprint(2, "can't find font %s.%d or substitute, quitting\n", fname[n], s);
161 	exits("no font");
162 }
163 
164 void
165 loadfontname(int n, char *s)
166 {
167 	int i;
168 	Font *f, *g = 0;
169 
170 	if (strcmp(s, fname[n]) == 0)
171 		return;
172 	if(fname[n] && fname[n][0]){
173 		if(lastload[n] && strcmp(lastload[n], fname[n]) == 0)
174 			return;
175 		strcpy(lastload[n], fname[n]);
176 	}
177 	fontlookup(n, s);
178 	for (i = 0; i < NSIZE; i++)
179 		if (f = fonttab[n][i]){
180 			if (f != g) {
181 				freefont(f);
182 				g = f;
183 			}
184 			fonttab[n][i] = 0;
185 		}
186 }
187 
188 void
189 allfree(void)
190 {
191 	int i;
192 
193 	for (i=0; i<NFONT; i++)
194 		loadfontname(i, "??");
195 }
196 
197 
198 void
199 readmapfile(char *file)
200 {
201 	Biobuf *fp;
202 	char *p, cmd[100];
203 
204 	if ((fp=Bopen(file, OREAD)) == 0){
205 		fprint(2, "proof: can't open map file %s\n", file);
206 		exits("urk");
207 	}
208 	while((p=Brdline(fp, '\n')) != 0) {
209 		p[Blinelen(fp)-1] = 0;
210 		scanstr(p, cmd, 0);
211 		if(p[0]=='\0' || eq(cmd, "#"))	/* skip comments, empty */
212 			continue;
213 		else if(eq(cmd, "xheight"))
214 			buildxheight(fp);
215 		else if(eq(cmd, "map"))
216 			buildmap(fp);
217 		else if(eq(cmd, "special"))
218 			buildtroff(p);
219 		else if(eq(cmd, "troff"))
220 			buildtroff(p);
221 		else
222 			fprint(2, "weird map line %s\n", p);
223 	}
224 	Bterm(fp);
225 }
226 
227 static void
228 buildxheight(Biobuf *fp)	/* map goes from char name to value to print via *string() */
229 {
230 	char *line;
231 
232 	line = Brdline(fp, '\n');
233 	if(line == 0){
234 		fprint(2, "proof: bad map file\n");
235 		exits("map");
236 	}
237 	charmap[curmap].xheight = atof(line);
238 }
239 
240 static void
241 buildmap(Biobuf *fp)	/* map goes from char name to value to print via *string() */
242 {
243 	uchar *p, *line, ch[100];
244 	int val;
245 	Rune r;
246 
247 	curmap++;
248 	if(curmap >= NMAP){
249 		fprint(2, "proof: out of char maps; recompile\n");
250 		exits("charmap");
251 	}
252 	while ((line = Brdline(fp, '\n'))!= 0){
253 		if (line[0] == '\n')
254 			return;
255 		line[Blinelen(fp)-1] = 0;
256 		scanstr((char *) line, (char *) ch, (char **) &p);
257 		if (ch[0] == '\0') {
258 			fprint(2, "bad map file line '%s'\n", (char*)line);
259 			continue;
260 		}
261 		val = strtol((char *) p, 0, 10);
262 dprint(2, "buildmap %s (%x %x) %s %d\n", (char*)ch, ch[0], ch[1], (char*)p, val);
263 		chartorune(&r, (char*)ch);
264 		if(utflen((char*)ch)==1 && r<QUICK)
265 			charmap[curmap].quick[r] = val;
266 		else
267 			addmap(curmap, strdup((char *) ch), val);	/* put somewhere else */
268 	}
269 }
270 
271 static void
272 addmap(int n, char *s, int val)	/* stick a new link on */
273 {
274 	Link *p = (Link *) malloc(sizeof(Link));
275 	Link *prev = charmap[n].slow;
276 
277 	if(p == 0)
278 		exits("out of memory in addmap");
279 	p->name = (uchar *) s;
280 	p->val = val;
281 	p->next = prev;
282 	charmap[n].slow = p;
283 }
284 
285 static void
286 buildtroff(char *buf)	/* map troff names into bitmap filenames */
287 {				/* e.g., R -> times/R., I -> times/I., etc. */
288 	char *p, cmd[100], name[200], prefix[400], fallback[100];
289 
290 	scanstr(buf, cmd, &p);
291 	scanstr(p, name, &p);
292 	scanstr(p, prefix, &p);
293 	while(*p!=0 && isspace(*p))
294 		p++;
295 	if(*p != 0){
296 		scanstr(p, fallback, &p);
297 		fontmap[nfontmap].fallback = strdup(fallback);
298 	}else
299 		fontmap[nfontmap].fallback = 0;
300 	fontmap[nfontmap].troffname = strdup(name);
301 	fontmap[nfontmap].prefix = strdup(prefix);
302 	fontmap[nfontmap].map = curmap;
303 	dprint(2, "troff name %s is bitmap %s map %d in slot %d fallback %s\n", name, prefix, curmap, nfontmap, fontmap[nfontmap].fallback? fontmap[nfontmap].fallback : "<null>");
304 	nfontmap++;
305 }
306 
307 static void
308 fontlookup(int n, char *s)	/* map troff name of s into position n */
309 {
310 	int i;
311 
312 	for(i = 0; i < nfontmap; i++)
313 		if (eq(s, fontmap[i].troffname)) {
314 			strcpy(fname[n], fontmap[i].prefix);
315 			fmap[n] = fontmap[i].map;
316 			pos2fontmap[n] = i;
317 			if (eq(s, "S"))
318 				specfont = n;
319 			dprint(2, "font %d %s is %s\n", n, s, fname[n]);
320 			return;
321 		}
322 	/* god help us if this font isn't there */
323 }
324 
325 
326 static char *
327 map(Rune rp[], int font)	/* figure out mapping for char in this font */
328 {
329 	static char s[100];
330 	char c[10];
331 	Link *p;
332 	Rune r;
333 
334 	if(rp[1]==0 &&  rp[0]<QUICK)	/* fast lookup */
335 		r = charmap[fmap[font]].quick[rp[0]];
336 	else {	/* high-valued or compound character name */
337 		sprint(c, "%S", rp);
338 		r = 0;
339 		for (p = charmap[fmap[font]].slow; p; p = p->next)
340 			if(eq(c, p->name)){
341 				r = p->val;
342 				break;
343 			}
344 	}
345 	if(r == 0){	/* not there */
346 		dprint(2, "didn't find %S font# %d\n", rp, font);
347 		return 0;
348 	}
349 	dprint(2, "map %S to %s font# %d\n", rp, s, font);
350 	s[runetochar(s, &r)] = 0;
351 	return s;
352 }
353 
354 static void
355 scanstr(char *s, char *ans, char **ep)
356 {
357 	for (; isspace((uchar) *s); s++)
358 		;
359 	for (; *s!=0 && !isspace((uchar) *s); )
360 		*ans++ = *s++;
361 	*ans = 0;
362 	if (ep)
363 		*ep = s;
364 }
365