xref: /plan9/sys/src/cmd/eqn/text.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include "e.h"
2 #include "y.tab.h"
3 #include <ctype.h>
4 
5 #define	CSSIZE	1000
6 char	cs[CSSIZE+20];	/* text string converted into this */
7 char	*csp;		/* next spot in cs[] */
8 char	*psp;		/* next character in input token */
9 
10 int	lf, rf;		/* temporary spots for left and right fonts */
11 int	lastft;		/* last \f added */
12 int	nextft;		/* next \f to be added */
13 
14 int	pclass;		/* class of previous character */
15 int	nclass;		/* class of next character */
16 
17 int class[LAST][LAST] ={	/* guesswork, tuned to times roman postscript */
18 
19 	/*OT OL IL DG LP RP SL PL IF IJ VB */
20 /*OT*/	{ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 0 },		/* OTHER */
21 /*OL*/	{ 1, 0, 1, 1, 1, 1, 1, 2, 2, 2, 0 },		/* OLET */
22 /*IL*/	{ 1, 1, 0, 1, 1, 1, 1, 3, 2, 1, 0 },		/* ILET */
23 /*DG*/	{ 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 0 },		/* DIG */
24 /*LP*/	{ 1, 1, 1, 1, 1, 2, 1, 2, 3, 3, 0 },		/* LPAR */
25 /*RP*/	{ 2, 2, 2, 1, 1, 1, 1, 2, 3, 3, 0 },		/* RPAR */
26 /*SL*/	{ 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0 },		/* SLASH */
27 /*PL*/	{ 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 0 },		/* PLUS */
28 /*IF*/	{ 3, 3, 1, 2, 2, 3, 2, 3, 0, 1, 1 },		/* ILETF */
29 /*IJ*/	{ 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0 },		/* ILETJ */
30 /*VB*/	{ 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 1 },		/* VBAR */
31 
32 };
33 
34 extern void shim(int, int);
35 extern void roman(int);
36 extern void sadd(char *);
37 extern void cadd(int);
38 extern int trans(int, char *);
39 
40 int textc(void)	/* read next UTF rune from psp */
41 {
42 	wchar_t r;
43 	int w;
44 
45 	w = mbtowc(&r, psp, 3);
46 	if(w == 0){
47 		psp++;
48 		return 0;
49 	}
50 	if(w < 0){
51 		psp += 1;
52 		return 0x80;	/* Plan 9-ism */
53 	}
54 	psp += w;
55 	return r;
56 }
57 
58 void text(int t, char *p1)	/* convert text string p1 of type t */
59 {
60 	int c;
61 	char *p;
62 	tbl *tp;
63 
64 	yyval = salloc();
65 	ebase[yyval] = 0;
66 	eht[yyval] = EM(1.0, ps);	/* ht in ems of orig size */
67 	lfont[yyval] = rfont[yyval] = ROM;
68 	lclass[yyval] = rclass[yyval] = OTHER;
69 	if (t == QTEXT) {
70 		for (p = p1; *p; p++)	/* scan for embedded \f's */
71 			if (*p == '\\' && *(p+1) == 'f')
72 				break;
73 		if (*p)		/* if found \f, leave it alone and hope */
74 			p = p1;
75 		else {
76 			sprintf(cs, "\\f%s%s\\fP", ftp->name, p1);
77 			p = cs;
78 		}
79 	} else if (t == SPACE)
80 		p = "\\ ";
81 	else if (t == THIN)
82 		p = "\\|";
83 	else if (t == TAB)
84 		p = "\\t";
85 	else if ((tp = lookup(restbl, p1)) != NULL) {
86 		p = tp->cval;
87 	} else {
88 		lf = rf = 0;
89 		lastft = 0;
90 		nclass = NONE;	/* get started with no class == no pad */
91 		csp = cs;
92 		for (psp = p1; (c = textc()) != '\0'; ) {
93 			nextft = ft;
94 			pclass = nclass;
95 			rf = trans(c, p1);
96 			if (lf == 0) {
97 				lf = rf;	/* left stuff is first found */
98 				lclass[yyval] = nclass;
99 			}
100 			if (csp-cs > CSSIZE)
101 				ERROR "converted token %.25s... too long", p1 FATAL ;
102 		}
103 		sadd("\\fP");
104 		*csp = '\0';
105 		p = cs;
106 		lfont[yyval] = lf;
107 		rfont[yyval] = rf;
108 		rclass[yyval] = nclass;
109 	}
110 	dprintf(".\t%dtext: S%d <- %s; b=%g,h=%g,lf=%c,rf=%c,ps=%d\n",
111 		t, yyval, p, ebase[yyval], eht[yyval], lfont[yyval], rfont[yyval], ps);
112 	printf(".ds %d \"%s\n", yyval, p);
113 }
114 
115 int isalpharune(int c)
116 {
117 	return ('a'<=c && c<='z') || ('A'<=c && c<='Z');
118 }
119 
120 int isdigitrune(int c)
121 {
122 	return ('0'<=c && c<='9');
123 }
124 
125 trans(int c, char *p1)
126 {
127 	int f;
128 
129 	if (isalpharune(c) && ft == ITAL && c != 'f' && c != 'j') {	/* italic letter */
130 		shim(pclass, nclass = ILET);
131 		cadd(c);
132 		return ITAL;
133 	}
134 	if (isalpharune(c) && ft != ITAL) {		/* other letter */
135 		shim(pclass, nclass = OLET);
136 		cadd(c);
137 		return ROM;
138 	}
139 	if (isdigitrune(c)) {
140 		shim(pclass, nclass = DIG);
141 		roman(c);
142 		return ROM;	/* this is the right side font of this object */
143 	}
144 	f = ROM;
145 	nclass = OTHER;
146 	switch (c) {
147 	case ':': case ';': case '!': case '%': case '?':
148 		shim(pclass, nclass);
149 		roman(c);
150 		return f;
151 	case '(': case '[':
152 		shim(pclass, nclass = LPAR);
153 		roman(c);
154 		return f;
155 	case ')': case ']':
156 		shim(pclass, nclass = RPAR);
157 		roman(c);
158 		return f;
159 	case ',':
160 		shim(pclass, nclass = OTHER);
161 		roman(c);
162 		return f;
163 	case '.':
164 		if (rf == ROM)
165 			roman(c);
166 		else
167 			cadd(c);
168 		return f;
169 	case '|':		/* postscript needs help with default width! */
170 		shim(pclass, nclass = VBAR);
171 		sadd("\\v'.17m'\\z|\\v'-.17m'\\|");	/* and height */
172 		return f;
173 	case '=':
174 		shim(pclass, nclass = PLUS);
175 		sadd("\\(eq");
176 		return f;
177 	case '+':
178 		shim(pclass, nclass = PLUS);
179 		sadd("\\(pl");
180 		return f;
181 	case '>':
182 	case '<':		/* >, >=, >>, <, <-, <=, << */
183 		shim(pclass, nclass = PLUS);
184 		if (*psp == '=') {
185 			sadd(c == '<' ? "\\(<=" : "\\(>=");
186 			psp++;
187 		} else if (c == '<' && *psp == '-') {	/* <- only */
188 			sadd("\\(<-");
189 			psp++;
190 		} else if (*psp == c) {		/* << or >> */
191 			cadd(c);
192 			cadd(c);
193 			psp++;
194 		} else {
195 			cadd(c);
196 		}
197 		return f;
198 	case '-':
199 		shim(pclass, nclass = PLUS);	/* probably too big for ->'s */
200 		if (*psp == '>') {
201 			sadd("\\(->");
202 			psp++;
203 		} else {
204 			sadd("\\(mi");
205 		}
206 		return f;
207 	case '/':
208 		shim(pclass, nclass = SLASH);
209 		cadd('/');
210 		return f;
211 	case '~':
212 	case ' ':
213 		sadd("\\|\\|");
214 		return f;
215 	case '^':
216 		sadd("\\|");
217 		return f;
218 	case '\\':	/* troff - pass only \(xx without comment */
219 		shim(pclass, nclass);
220 		cadd('\\');
221 		cadd(c = *psp++);
222 		if (c == '(' && *psp && *(psp+1)) {
223 			cadd(*psp++);
224 			cadd(*psp++);
225 		} else
226 			fprintf(stderr, "eqn warning: unquoted troff command \\%c, file %s:%d\n",
227 				c, curfile->fname, curfile->lineno);
228 		return f;
229 	case '\'':
230 		shim(pclass, nclass);
231 		sadd("\\(fm");
232 		return f;
233 
234 	case 'f':
235 		if (ft == ITAL) {
236 			shim(pclass, nclass = ILETF);
237 			cadd('f');
238 			f = ITAL;
239 		} else
240 			cadd('f');
241 		return f;
242 	case 'j':
243 		if (ft == ITAL) {
244 			shim(pclass, nclass = ILETJ);
245 			cadd('j');
246 			f = ITAL;
247 		} else
248 			cadd('j');
249 		return f;
250 	default:
251 		shim(pclass, nclass);
252 		cadd(c);
253 		return ft==ITAL ? ITAL : ROM;
254 	}
255 }
256 
257 char *pad(int n)	/* return the padding as a string */
258 {
259 	static char buf[20];
260 
261 	buf[0] = 0;
262 	if (n < 0) {
263 		sprintf(buf, "\\h'-%du*\\w'\\^'u'", -n);
264 		return buf;
265 	}
266 	for ( ; n > 1; n -= 2)
267 		strcat(buf, "\\|");
268 	if (n > 0)
269 		strcat(buf, "\\^");
270 	return buf;
271 }
272 
273 void shim(int lc, int rc)	/* add padding space suitable to left and right classes */
274 {
275 	sadd(pad(class[lc][rc]));
276 }
277 
278 void roman(int c)	/* add char c in "roman" font */
279 {
280 	nextft = ROM;
281 	cadd(c);
282 }
283 
284 void sadd(char *s)		/* add string s to cs */
285 {
286 	while (*s)
287 		cadd(*s++);
288 }
289 
290 void cadd(int c)		/* add character c to end of cs */
291 {
292 	char *p;
293 	int w;
294 
295 	if (lastft != nextft) {
296 		if (lastft != 0) {
297 			*csp++ = '\\';
298 			*csp++ = 'f';
299 			*csp++ = 'P';
300 		}
301 		*csp++ = '\\';
302 		*csp++ = 'f';
303 		if (ftp == ftstack) {	/* bottom level */
304 			if (ftp->ft == ITAL)	/* usual case */
305 				*csp++ = nextft;
306 			else		/* gfont set, use it */
307 				for (p = ftp->name; *csp = *p++; )
308 					csp++;
309 		} else {	/* inside some kind of font ... */
310 			for (p = ftp->name; *csp = *p++; )
311 				csp++;
312 		}
313 		lastft = nextft;
314 	}
315 	w = wctomb(csp, c);
316 	if(w > 0)	/* ignore bad characters */
317 		csp += w;
318 }
319