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