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