xref: /plan9-contrib/sys/src/cmd/eqn/text.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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 void text(int t, char *p1)	/* convert text string p1 of type t */
41 {
42 	int c;
43 	char *p;
44 	tbl *tp;
45 
46 	yyval = salloc();
47 	ebase[yyval] = 0;
48 	eht[yyval] = EM(1.0, ps);	/* ht in ems of orig size */
49 	lfont[yyval] = rfont[yyval] = ROM;
50 	lclass[yyval] = rclass[yyval] = OTHER;
51 	if (t == QTEXT) {
52 		for (p = p1; *p; p++)	/* scan for embedded \f's */
53 			if (*p == '\\' && *(p+1) == 'f')
54 				break;
55 		if (*p)		/* if found \f, leave it alone and hope */
56 			p = p1;
57 		else {
58 			sprintf(cs, "\\f%s%s\\fP", ftp->name, p1);
59 			p = cs;
60 		}
61 	} else if (t == SPACE)
62 		p = "\\ ";
63 	else if (t == THIN)
64 		p = "\\|";
65 	else if (t == TAB)
66 		p = "\\t";
67 	else if ((tp = lookup(restbl, p1)) != NULL) {
68 		p = tp->cval;
69 	} else {
70 		lf = rf = 0;
71 		lastft = 0;
72 		nclass = NONE;	/* get started with no class == no pad */
73 		csp = cs;
74 		for (psp = p1; (c = *psp++) != '\0'; ) {
75 			nextft = ft;
76 			pclass = nclass;
77 			rf = trans(c, p1);
78 			if (lf == 0) {
79 				lf = rf;	/* left stuff is first found */
80 				lclass[yyval] = nclass;
81 			}
82 			if (csp-cs > CSSIZE)
83 				ERROR "converted token %.25s... too long", p1 FATAL ;
84 		}
85 		sadd("\\fP");
86 		*csp = '\0';
87 		p = cs;
88 		lfont[yyval] = lf;
89 		rfont[yyval] = rf;
90 		rclass[yyval] = nclass;
91 	}
92 	dprintf(".\t%dtext: S%d <- %s; b=%g,h=%g,lf=%c,rf=%c,ps=%d\n",
93 		t, yyval, p, ebase[yyval], eht[yyval], lfont[yyval], rfont[yyval], ps);
94 	printf(".ds %d \"%s\n", yyval, p);
95 }
96 
97 trans(int c, char *p1)
98 {
99 	int f;
100 
101 	if (isalpha(c) && ft == ITAL && c != 'f' && c != 'j') {	/* italic letter */
102 		shim(pclass, nclass = ILET);
103 		cadd(c);
104 		return ITAL;
105 	}
106 	if (isalpha(c) && ft != ITAL) {		/* other letter */
107 		shim(pclass, nclass = OLET);
108 		cadd(c);
109 		return ROM;
110 	}
111 	if (isdigit(c)) {
112 		shim(pclass, nclass = DIG);
113 		roman(c);
114 		return ROM;	/* this is the right side font of this object */
115 	}
116 	f = ROM;
117 	nclass = OTHER;
118 	switch (c) {
119 	case ':': case ';': case '!': case '%': case '?':
120 		shim(pclass, nclass);
121 		roman(c);
122 		return f;
123 	case '(': case '[':
124 		shim(pclass, nclass = LPAR);
125 		roman(c);
126 		return f;
127 	case ')': case ']':
128 		shim(pclass, nclass = RPAR);
129 		roman(c);
130 		return f;
131 	case ',':
132 		shim(pclass, nclass = OTHER);
133 		roman(c);
134 		return f;
135 	case '.':
136 		if (rf == ROM)
137 			roman(c);
138 		else
139 			cadd(c);
140 		return f;
141 	case '|':		/* postscript needs help with default width! */
142 		shim(pclass, nclass = VBAR);
143 		sadd("\\v'.17m'\\z|\\v'-.17m'\\|");	/* and height */
144 		return f;
145 	case '=':
146 		shim(pclass, nclass = PLUS);
147 		sadd("\\(eq");
148 		return f;
149 	case '+':
150 		shim(pclass, nclass = PLUS);
151 		sadd("\\(pl");
152 		return f;
153 	case '>':
154 	case '<':		/* >, >=, >>, <, <-, <=, << */
155 		shim(pclass, nclass = PLUS);
156 		if (*psp == '=') {
157 			sadd(c == '<' ? "\\(<=" : "\\(>=");
158 			psp++;
159 		} else if (c == '<' && *psp == '-') {	/* <- only */
160 			sadd("\\(<-");
161 			psp++;
162 		} else if (*psp == c) {		/* << or >> */
163 			cadd(c);
164 			cadd(c);
165 			psp++;
166 		} else {
167 			cadd(c);
168 		}
169 		return f;
170 	case '-':
171 		shim(pclass, nclass = PLUS);	/* probably too big for ->'s */
172 		if (*psp == '>') {
173 			sadd("\\(->");
174 			psp++;
175 		} else {
176 			sadd("\\(mi");
177 		}
178 		return f;
179 	case '/':
180 		shim(pclass, nclass = SLASH);
181 		cadd('/');
182 		return f;
183 	case '~':
184 	case ' ':
185 		sadd("\\|\\|");
186 		return f;
187 	case '^':
188 		sadd("\\|");
189 		return f;
190 	case '\\':	/* troff - pass only \(xx without comment */
191 		shim(pclass, nclass);
192 		cadd('\\');
193 		cadd(c = *psp++);
194 		if (c == '(' && *psp && *(psp+1)) {
195 			cadd(*psp++);
196 			cadd(*psp++);
197 		} else
198 			fprintf(stderr, "eqn warning: unquoted troff command \\%c, line %d, file %s\n",
199 				c, curfile->lineno, curfile->fname);
200 		return f;
201 	case '\'':
202 		shim(pclass, nclass);
203 		sadd("\\(fm");
204 		return f;
205 
206 	case 'f':
207 		if (ft == ITAL) {
208 			shim(pclass, nclass = ILETF);
209 			cadd('f');
210 			f = ITAL;
211 		} else
212 			cadd('f');
213 		return f;
214 	case 'j':
215 		if (ft == ITAL) {
216 			shim(pclass, nclass = ILETJ);
217 			cadd('j');
218 			f = ITAL;
219 		} else
220 			cadd('j');
221 		return f;
222 	default:
223 		shim(pclass, nclass);
224 		cadd(c);
225 		return ft==ITAL ? ITAL : ROM;
226 	}
227 }
228 
229 char *pad(int n)	/* return the padding as a string */
230 {
231 	static char buf[20];
232 
233 	buf[0] = 0;
234 	if (n < 0) {
235 		sprintf(buf, "\\h'-%du*\\w'\\^'u'", -n);
236 		return buf;
237 	}
238 	for ( ; n > 1; n -= 2)
239 		strcat(buf, "\\|");
240 	if (n > 0)
241 		strcat(buf, "\\^");
242 	return buf;
243 }
244 
245 void shim(int lc, int rc)	/* add padding space suitable to left and right classes */
246 {
247 	sadd(pad(class[lc][rc]));
248 }
249 
250 void roman(int c)	/* add char c in "roman" font */
251 {
252 	nextft = ROM;
253 	cadd(c);
254 }
255 
256 void sadd(char *s)		/* add string s to cs */
257 {
258 	while (*s)
259 		cadd(*s++);
260 }
261 
262 void cadd(int c)		/* add char c to end of cs */
263 {
264 	char *p;
265 
266 	if (lastft != nextft) {
267 		if (lastft != 0) {
268 			*csp++ = '\\';
269 			*csp++ = 'f';
270 			*csp++ = 'P';
271 		}
272 		*csp++ = '\\';
273 		*csp++ = 'f';
274 		if (ftp == ftstack) {	/* bottom level */
275 			if (ftp->ft == ITAL)	/* usual case */
276 				*csp++ = nextft;
277 			else		/* gfont set, use it */
278 				for (p = ftp->name; *csp = *p++; )
279 					csp++;
280 		} else {	/* inside some kind of font ... */
281 			for (p = ftp->name; *csp = *p++; )
282 				csp++;
283 		}
284 		lastft = nextft;
285 	}
286 	*csp++ = c;
287 }
288