Adapt src/lib-snprintf (src/libs/snprintf)
[s-roff.git] / src / preproc / eqn / text.cpp
blob8563199a39c0178ad453286c4bd9f3a4681fc37c
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2003, 2007
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include <ctype.h>
23 #include "eqn.h"
24 #include "pbox.h"
25 #include "ptable.h"
27 struct map {
28 const char *from;
29 const char *to;
32 struct map entity_table[] = {
33 // Classic troff special characters
34 {"%", "&shy;"}, // ISOnum
35 {"'", "&acute;"}, // ISOdia
36 {"!=", "&ne;"}, // ISOtech
37 {"**", "&lowast;"}, // ISOtech
38 {"*a", "&alpha;"}, // ISOgrk3
39 {"*A", "A"},
40 {"*b", "&beta;"}, // ISOgrk3
41 {"*B", "B"},
42 {"*d", "&delta;"}, // ISOgrk3
43 {"*D", "&Delta;"}, // ISOgrk3
44 {"*e", "&epsilon;"}, // ISOgrk3
45 {"*E", "E"},
46 {"*f", "&phi;"}, // ISOgrk3
47 {"*F", "&Phi;"}, // ISOgrk3
48 {"*g", "&gamma;"}, // ISOgrk3
49 {"*G", "&Gamma;"}, // ISOgrk3
50 {"*h", "&theta;"}, // ISOgrk3
51 {"*H", "&Theta;"}, // ISOgrk3
52 {"*i", "&iota;"}, // ISOgrk3
53 {"*I", "I"},
54 {"*k", "&kappa;"}, // ISOgrk3
55 {"*K", "K;"},
56 {"*l", "&lamda;"}, // ISOgrk3
57 {"*L", "&Lambda;"}, // ISOgrk3
58 {"*m", "&mu;"}, // ISOgrk3
59 {"*M", "M"},
60 {"*n", "&nu;"}, // ISOgrk3
61 {"*N", "N"},
62 {"*o", "o"},
63 {"*O", "O"},
64 {"*p", "&pi;"}, // ISOgrk3
65 {"*P", "&Pi;"}, // ISOgrk3
66 {"*q", "&psi;"}, // ISOgrk3
67 {"*Q", "&PSI;"}, // ISOgrk3
68 {"*r", "&rho;"}, // ISOgrk3
69 {"*R", "R"},
70 {"*s", "&sigma;"}, // ISOgrk3
71 {"*S", "&Sigma;"}, // ISOgrk3
72 {"*t", "&tau;"}, // ISOgrk3
73 {"*T", "&Tau;"}, // ISOgrk3
74 {"*u", "&upsilon;"}, // ISOgrk3
75 {"*U", "&Upsilon;"}, // ISOgrk3
76 {"*w", "&omega;"}, // ISOgrk3
77 {"*W", "&Omega;"}, // ISOgrk3
78 {"*x", "&chi;"}, // ISOgrk3
79 {"*X", "&Chi;"}, // ISOgrk3
80 {"*y", "&eta;"}, // ISOgrk3
81 {"*Y", "&Eta;"}, // ISOgrk3
82 {"*z", "&zeta;"}, // ISOgrk3
83 {"*Z", "&Zeta;"}, // ISOgrk3
84 {"+-", "&plusmn;"}, // ISOnum
85 {"->", "&rarr;"}, // ISOnum
86 {"12", "&frac12;"}, // ISOnum
87 {"14", "&frac14;"}, // ISOnum
88 {"34", "&frac34;"}, // ISOnum
89 {"<-", "&larr;"}, // ISOnum
90 {"==", "&equiv;"}, // ISOtech
91 {"Fi", "&ffilig;"}, // ISOpub
92 {"Fl", "&ffllig;"}, // ISOpub
93 {"aa", "&acute;"}, // ISOdia
94 {"ap", "&sim;"}, // ISOtech
95 {"bl", "&phonexb;"}, // ISOpub
96 {"br", "&boxv;"}, // ISObox
97 {"bs", "&phone;"}, // ISOpub (for the Bell logo)
98 {"bu", "&bull;"}, // ISOpub
99 {"bv", "&verbar;"}, // ISOnum
100 {"ca", "&cap;"}, // ISOtech
101 {"ci", "&cir;"}, // ISOpub
102 {"co", "&copy;"}, // ISOnum
103 {"ct", "&cent;"}, // ISOnum
104 {"cu", "&cup;"}, // ISOtech
105 {"da", "&darr;"}, // ISOnum
106 {"de", "&deg;"}, // ISOnum
107 {"dg", "&dagger;"}, // ISOpub
108 {"dd", "&Dagger;"}, // ISOpub
109 {"di", "&divide;"}, // ISOnum
110 {"em", "&mdash;"}, // ISOpub
111 {"eq", "&equals;"}, // ISOnum
112 {"es", "&empty;"}, // ISOamso
113 {"ff", "&fflig;"}, // ISOpub
114 {"fi", "&filig;"}, // ISOpub
115 {"fl", "&fllig;"}, // ISOpub
116 {"fm", "&prime;"}, // ISOtech
117 {"ge", "&ge;"}, // ISOtech
118 {"gr", "&nabla;"}, // ISOtech
119 {"hy", "&hyphen;"}, // ISOnum
120 {"ib", "&sube;"}, // ISOtech
121 {"if", "&infin;"}, // ISOtech
122 {"ip", "&supe;"}, // ISOtech
123 {"is", "&int;"}, // ISOtech
124 {"le", "&le;"}, // ISOtech
125 // Some pile characters go here
126 {"mi", "&minus;"}, // ISOtech
127 {"mo", "&isin;"}, // ISOtech
128 {"mu", "&times;"}, // ISOnum
129 {"no", "&not;"}, // ISOnum
130 {"or", "&verbar;"}, // ISOnum
131 {"pl", "&plus;"}, // ISOnum
132 {"pt", "&prop;"}, // ISOtech
133 {"rg", "&trade;"}, // ISOnum
134 // More pile characters go here
135 {"rn", "&macr;"}, // ISOdia
136 {"ru", "&lowbar;"}, // ISOnum
137 {"sb", "&sub;"}, // ISOtech
138 {"sc", "&sect;"}, // ISOnum
139 {"sl", "/"},
140 {"sp", "&sup;"}, // ISOtech
141 {"sq", "&squf;"}, // ISOpub
142 {"sr", "&radic;"}, // ISOtech
143 {"ts", "&sigmav;"}, // ISOgrk3
144 {"ua", "&uarr;"}, // ISOnum
145 {"ul", "_"},
146 {"~=", "&cong;"}, // ISOtech
147 // Extended specials supported by groff; see groff_char(7).
148 // These are listed in the order they occur on that man page.
149 {"-D", "&ETH;"}, // ISOlat: Icelandic uppercase eth
150 {"Sd", "&eth;"}, // ISOlat1: Icelandic lowercase eth
151 {"TP", "&THORN;"}, // ISOlat1: Icelandic uppercase thorn
152 {"Tp", "&thorn;"}, // ISOlat1: Icelandic lowercase thorn
153 {"ss", "&szlig;"}, // ISOlat1
154 // Ligatures
155 // ff, fi, fl, ffi, ffl from old troff go here
156 {"AE", "&AElig;"}, // ISOlat1
157 {"ae", "&aelig;"}, // ISOlat1
158 {"OE", "&OElig;"}, // ISOlat2
159 {"oe", "&oelig;"}, // ISOlat2
160 {"IJ", "&ijlig;"}, // ISOlat2: Dutch IJ ligature
161 {"ij", "&IJlig;"}, // ISOlat2: Dutch ij ligature
162 {".i", "&inodot;"}, // ISOlat2,ISOamso
163 {".j", "&jnodot;"}, // ISOamso (undocumented but in 1.19)
164 // Accented characters
165 {"'A", "&Aacute;"}, // ISOlat1
166 {"'C", "&Cacute;"}, // ISOlat2
167 {"'E", "&Eacute;"}, // ISOlat1
168 {"'I", "&Iacute;"}, // ISOlat1
169 {"'O", "&Oacute;"}, // ISOlat1
170 {"'U", "&Uacute;"}, // ISOlat1
171 {"'Y", "&Yacute;"}, // ISOlat1
172 {"'a", "&aacute;"}, // ISOlat1
173 {"'c", "&cacute;"}, // ISOlat2
174 {"'e", "&eacute;"}, // ISOlat1
175 {"'i", "&iacute;"}, // ISOlat1
176 {"'o", "&oacute;"}, // ISOlat1
177 {"'u", "&uacute;"}, // ISOlat1
178 {"'y", "&yacute;"}, // ISOlat1
179 {":A", "&Auml;"}, // ISOlat1
180 {":E", "&Euml;"}, // ISOlat1
181 {":I", "&Iuml;"}, // ISOlat1
182 {":O", "&Ouml;"}, // ISOlat1
183 {":U", "&Uuml;"}, // ISOlat1
184 {":Y", "&Yuml;"}, // ISOlat2
185 {":a", "&auml;"}, // ISOlat1
186 {":e", "&euml;"}, // ISOlat1
187 {":i", "&iuml;"}, // ISOlat1
188 {":o", "&ouml;"}, // ISOlat1
189 {":u", "&uuml;"}, // ISOlat1
190 {":y", "&yuml;"}, // ISOlat1
191 {"^A", "&Acirc;"}, // ISOlat1
192 {"^E", "&Ecirc;"}, // ISOlat1
193 {"^I", "&Icirc;"}, // ISOlat1
194 {"^O", "&Ocirc;"}, // ISOlat1
195 {"^U", "&Ucirc;"}, // ISOlat1
196 {"^a", "&acirc;"}, // ISOlat1
197 {"^e", "&ecirc;"}, // ISOlat1
198 {"^i", "&icirc;"}, // ISOlat1
199 {"^o", "&ocirc;"}, // ISOlat1
200 {"^u", "&ucirc;"}, // ISOlat1
201 {"`A", "&Agrave;"}, // ISOlat1
202 {"`E", "&Egrave;"}, // ISOlat1
203 {"`I", "&Igrave;"}, // ISOlat1
204 {"`O", "&Ograve;"}, // ISOlat1
205 {"`U", "&Ugrave;"}, // ISOlat1
206 {"`a", "&agrave;"}, // ISOlat1
207 {"`e", "&egrave;"}, // ISOlat1
208 {"`i", "&igrave;"}, // ISOlat1
209 {"`o", "&ograve;"}, // ISOlat1
210 {"`u", "&ugrave;"}, // ISOlat1
211 {"~A", "&Atilde;"}, // ISOlat1
212 {"~N", "&Ntilde;"}, // ISOlat1
213 {"~O", "&Otilde;"}, // ISOlat1
214 {"~a", "&atilde;"}, // ISOlat1
215 {"~n", "&ntilde;"}, // ISOlat1
216 {"~o", "&otilde;"}, // ISOlat1
217 {"vS", "&Scaron;"}, // ISOlat2
218 {"vs", "&scaron;"}, // ISOlat2
219 {"vZ", "&Zcaron;"}, // ISOlat2
220 {"vz", "&zcaron;"}, // ISOlat2
221 {",C", "&Ccedil;"}, // ISOlat1
222 {",c", "&ccedil;"}, // ISOlat1
223 {"/L", "&Lstrok;"}, // ISOlat2: Polish L with a slash
224 {"/l", "&lstrok;"}, // ISOlat2: Polish l with a slash
225 {"/O", "&Oslash;"}, // ISOlat1
226 {"/o", "&oslash;"}, // ISOlat1
227 {"oA", "&Aring;"}, // ISOlat1
228 {"oa", "&aring;"}, // ISOlat1
229 // Accents
230 {"a\"","&dblac;"}, // ISOdia: double acute accent (Hungarian umlaut)
231 {"a-", "&macr;"}, // ISOdia: macron or bar accent
232 {"a.", "&dot;"}, // ISOdia: dot above
233 {"a^", "&circ;"}, // ISOdia: circumflex accent
234 {"aa", "&acute;"}, // ISOdia: acute accent
235 {"ga", "&grave;"}, // ISOdia: grave accent
236 {"ab", "&breve;"}, // ISOdia: breve accent
237 {"ac", "&cedil;"}, // ISOdia: cedilla accent
238 {"ad", "&uml;"}, // ISOdia: umlaut or dieresis
239 {"ah", "&caron;"}, // ISOdia: caron (aka hacek accent)
240 {"ao", "&ring;"}, // ISOdia: ring or circle accent
241 {"a~", "&tilde;"}, // ISOdia: tilde accent
242 {"ho", "&ogon;"}, // ISOdia: hook or ogonek accent
243 {"ha", "^"}, // ASCII circumflex, hat, caret
244 {"ti", "~"}, // ASCII tilde, large tilde
245 // Quotes
246 {"Bq", "&lsquor;"}, // ISOpub: low double comma quote
247 {"bq", "&ldquor;"}, // ISOpub: low single comma quote
248 {"lq", "&ldquo;"}, // ISOnum
249 {"rq", "&rdquo;"}, // ISOpub
250 {"oq", "&lsquo;"}, // ISOnum: single open quote
251 {"cq", "&rsquo;"}, // ISOnum: single closing quote (ASCII 39)
252 {"aq", "&zerosp;'"}, // apostrophe quote
253 {"dq", "\""}, // double quote (ASCII 34)
254 {"Fo", "&laquo;"}, // ISOnum
255 {"Fc", "&raquo;"}, // ISOnum
256 //{"fo", "&fo;"},
257 //{"fc", "&fc;"},
258 // Punctuation
259 {"r!", "&iexcl;"}, // ISOnum
260 {"r?", "&iquest;"}, // ISOnum
261 // Old troff \(em goes here
262 {"en", "&ndash;"}, // ISOpub: en dash
263 // Old troff \(hy goes here
264 // Brackets
265 {"lB", "&lsqb;"}, // ISOnum: left (square) bracket
266 {"rB", "&rsqb;"}, // ISOnum: right (square) bracket
267 {"lC", "&lcub;"}, // ISOnum: left (curly) brace
268 {"rC", "&rcub;"}, // ISOnum: right (curly) brace
269 {"la", "&lang;"}, // ISOtech: left angle bracket
270 {"ra", "&rang;"}, // ISOtech: right angle bracket
271 // Old troff \(bv goes here
272 // Bracket-pile characters could go here.
273 // Arrows
274 // Old troff \(<- and \(-> go here
275 {"<>", "&harr;"}, // ISOamsa
276 {"da", "&darr;"}, // ISOnum
277 {"ua", "&uarr;"}, // ISOnum
278 {"lA", "&lArr;"}, // ISOtech
279 {"rA", "&rArr;"}, // ISOtech
280 {"hA", "&iff;"}, // ISOtech: horizontal double-headed arrow
281 {"dA", "&dArr;"}, // ISOamsa
282 {"uA", "&uArr;"}, // ISOamsa
283 {"vA", "&vArr;"}, // ISOamsa: vertical double-headed double arrow
284 //{"an", "&an;"},
285 // Lines
286 {"-h", "&planck;"}, // ISOamso: h-bar (Planck's constant)
287 // Old troff \(or goes here
288 {"ba", "&verbar;"}, // ISOnum
289 // Old troff \(br, \{u, \(ul, \(bv go here
290 {"bb", "&brvbar;"}, // ISOnum
291 {"sl", "/"},
292 {"rs", "&bsol;"}, // ISOnum
293 // Text markers
294 // Old troff \(ci, \(bu, \(dd, \(dg go here
295 {"lz", "&loz;"}, // ISOpub
296 // Old troff sq goes here
297 {"ps", "&para;"}, // ISOnum: paragraph or pilcrow sign
298 {"sc", "&sect;"}, // ISOnum (in old troff)
299 // Old troff \(lh, \{h go here
300 {"at", "&commat;"}, // ISOnum
301 {"sh", "&num;"}, // ISOnum
302 //{"CR", "&CR;"},
303 {"OK", "&check;"}, // ISOpub
304 // Legalize
305 // Old troff \(co, \{g go here
306 {"tm", "&trade;"}, // ISOnum
307 // Currency symbols
308 {"Do", "&dollar;"}, // ISOnum
309 {"ct", "&cent;"}, // ISOnum
310 {"eu", "&euro;"},
311 {"Eu", "&euro;"},
312 {"Ye", "&yen;"}, // ISOnum
313 {"Po", "&pound;"}, // ISOnum
314 {"Cs", "&curren;"}, // ISOnum: currency sign
315 {"Fn", "&fnof"}, // ISOtech
316 // Units
317 // Old troff de goes here
318 {"%0", "&permil;"}, // ISOtech: per thousand, per mille sign
319 // Old troff \(fm goes here
320 {"sd", "&Prime;"}, // ISOtech
321 {"mc", "&micro;"}, // ISOnum
322 {"Of", "&ordf;"}, // ISOnum
323 {"Om", "&ordm;"}, // ISOnum
324 // Logical symbols
325 {"AN", "&and;"}, // ISOtech
326 {"OR", "&or;"}, // ISOtech
327 // Old troff \(no goes here
328 {"te", "&exist;"}, // ISOtech: there exists, existential quantifier
329 {"fa", "&forall;"}, // ISOtech: for all, universal quantifier
330 {"st", "&bepsi"}, // ISOamsr: such that
331 {"3d", "&there4;"}, // ISOtech
332 {"tf", "&there4;"}, // ISOtech
333 // Mathematical symbols
334 // Old troff "12", "14", "34" goes here
335 {"S1", "&sup1;"}, // ISOnum
336 {"S2", "&sup2;"}, // ISOnum
337 {"S3", "&sup3;"}, // ISOnum
338 // Old troff \(pl", \-, \(+- go here
339 {"t+-", "&plusmn;"}, // ISOnum
340 {"-+", "&mnplus;"}, // ISOtech
341 {"pc", "&middot;"}, // ISOnum
342 {"md", "&middot;"}, // ISOnum
343 // Old troff \(mu goes here
344 {"tmu", "&times;"}, // ISOnum
345 {"c*", "&otimes;"}, // ISOamsb: multiply sign in a circle
346 {"c+", "&oplus;"}, // ISOamsb: plus sign in a circle
347 // Old troff \(di goes here
348 {"tdi", "&divide;"}, // ISOnum
349 {"f/", "&horbar;"}, // ISOnum: horizintal bar for fractions
350 // Old troff \(** goes here
351 {"<=", "&le;"}, // ISOtech
352 {">=", "&ge;"}, // ISOtech
353 {"<<", "&Lt;"}, // ISOamsr
354 {">>", "&Gt;"}, // ISOamsr
355 {"!=", "&ne;"}, // ISOtech
356 // Old troff \(eq and \(== go here
357 {"=~", "&cong;"}, // ISOamsr
358 // Old troff \(ap goes here
359 {"~~", "&ap;"}, // ISOtech
360 // This appears to be an error in the groff table.
361 // It clashes with the Bell Labs use of ~= for a congruence sign
362 // {"~=", "&ap;"}, // ISOamsr
363 // Old troff \(pt, \(es, \(mo go here
364 {"nm", "&notin;"}, // ISOtech
365 {"nb", "&nsub;"}, // ISOamsr
366 {"nc", "&nsup;"}, // ISOamsn
367 {"ne", "&nequiv;"}, // ISOamsn
368 // Old troff \(sb, \(sp, \(ib, \(ip, \(ca, \(cu go here
369 {"/_", "&ang;"}, // ISOamso
370 {"pp", "&perp;"}, // ISOtech
371 // Old troff \(is goes here
372 {"sum", "&sum;"}, // ISOamsb
373 {"product", "&prod;"}, // ISOamsb
374 {"gr", "&nabla;"}, // ISOtech
375 // Old troff \(sr. \{n, \(if go here
376 {"Ah", "&aleph;"}, // ISOtech
377 {"Im", "&image;"}, // ISOamso: Fraktur I, imaginary
378 {"Re", "&real;"}, // ISOamso: Fraktur R, real
379 {"wp", "&weierp;"}, // ISOamso
380 {"pd", "&part;"}, // ISOtech: partial differentiation sign
381 // Their table duplicates the Greek letters here.
382 // We list only the variant forms here, mapping them into
383 // the ISO Greek 4 variants (which may or may not be correct :-()
384 {"+f", "&b.phiv;"}, // ISOgrk4: variant phi
385 {"+h", "&b.thetas;"}, // ISOgrk4: variant theta
386 {"+p", "&b.omega;"}, // ISOgrk4: variant pi, looking like omega
387 // Card symbols
388 {"CL", "&clubs;"}, // ISOpub: club suit
389 {"SP", "&spades;"}, // ISOpub: spade suit
390 {"HE", "&hearts;"}, // ISOpub: heart suit
391 {"DI", "&diams;"}, // ISOpub: diamond suit
394 const char *special_to_entity(const char *sp)
396 struct map *mp;
397 for (mp = entity_table;
398 mp < entity_table + sizeof(entity_table)/sizeof(entity_table[0]);
399 mp++) {
400 if (strcmp(mp->from, sp) == 0)
401 return mp->to;
403 return NULL;
406 class char_box : public simple_box {
407 unsigned char c;
408 char next_is_italic;
409 char prev_is_italic;
410 public:
411 char_box(unsigned char);
412 void debug_print();
413 void output();
414 int is_char();
415 int left_is_italic();
416 int right_is_italic();
417 void hint(unsigned);
418 void handle_char_type(int, int);
421 class special_char_box : public simple_box {
422 char *s;
423 public:
424 special_char_box(const char *);
425 ~special_char_box();
426 void output();
427 void debug_print();
428 int is_char();
429 void handle_char_type(int, int);
432 enum spacing_type {
433 s_ordinary,
434 s_operator,
435 s_binary,
436 s_relation,
437 s_opening,
438 s_closing,
439 s_punctuation,
440 s_inner,
441 s_suppress
444 const char *spacing_type_table[] = {
445 "ordinary",
446 "operator",
447 "binary",
448 "relation",
449 "opening",
450 "closing",
451 "punctuation",
452 "inner",
453 "suppress",
457 const int DIGIT_TYPE = 0;
458 const int LETTER_TYPE = 1;
460 const char *font_type_table[] = {
461 "digit",
462 "letter",
466 struct char_info {
467 int spacing_type;
468 int font_type;
469 char_info();
472 char_info::char_info()
473 : spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE)
477 static char_info char_table[256];
479 declare_ptable(char_info)
480 implement_ptable(char_info)
482 PTABLE(char_info) special_char_table;
484 static int get_special_char_spacing_type(const char *ch)
486 char_info *p = special_char_table.lookup(ch);
487 return p ? p->spacing_type : ORDINARY_TYPE;
490 static int get_special_char_font_type(const char *ch)
492 char_info *p = special_char_table.lookup(ch);
493 return p ? p->font_type : DIGIT_TYPE;
496 static void set_special_char_type(const char *ch, int st, int ft)
498 char_info *p = special_char_table.lookup(ch);
499 if (!p) {
500 p = new char_info[1];
501 special_char_table.define(ch, p);
503 if (st >= 0)
504 p->spacing_type = st;
505 if (ft >= 0)
506 p->font_type = ft;
509 void init_char_table()
511 set_special_char_type("pl", s_binary, -1);
512 set_special_char_type("mi", s_binary, -1);
513 set_special_char_type("eq", s_relation, -1);
514 set_special_char_type("<=", s_relation, -1);
515 set_special_char_type(">=", s_relation, -1);
516 char_table['}'].spacing_type = s_closing;
517 char_table[')'].spacing_type = s_closing;
518 char_table[']'].spacing_type = s_closing;
519 char_table['{'].spacing_type = s_opening;
520 char_table['('].spacing_type = s_opening;
521 char_table['['].spacing_type = s_opening;
522 char_table[','].spacing_type = s_punctuation;
523 char_table[';'].spacing_type = s_punctuation;
524 char_table[':'].spacing_type = s_punctuation;
525 char_table['.'].spacing_type = s_punctuation;
526 char_table['>'].spacing_type = s_relation;
527 char_table['<'].spacing_type = s_relation;
528 char_table['*'].spacing_type = s_binary;
529 for (int i = 0; i < 256; i++)
530 if (csalpha(i))
531 char_table[i].font_type = LETTER_TYPE;
534 static int lookup_spacing_type(const char *type)
536 for (int i = 0; spacing_type_table[i] != 0; i++)
537 if (strcmp(spacing_type_table[i], type) == 0)
538 return i;
539 return -1;
542 static int lookup_font_type(const char *type)
544 for (int i = 0; font_type_table[i] != 0; i++)
545 if (strcmp(font_type_table[i], type) == 0)
546 return i;
547 return -1;
550 void box::set_spacing_type(char *type)
552 int t = lookup_spacing_type(type);
553 if (t < 0)
554 error("unrecognised type `%1'", type);
555 else
556 spacing_type = t;
557 a_delete type;
560 char_box::char_box(unsigned char cc)
561 : c(cc), next_is_italic(0), prev_is_italic(0)
563 spacing_type = char_table[c].spacing_type;
566 void char_box::hint(unsigned flags)
568 if (flags & HINT_PREV_IS_ITALIC)
569 prev_is_italic = 1;
570 if (flags & HINT_NEXT_IS_ITALIC)
571 next_is_italic = 1;
574 void char_box::output()
576 if (output_format == troff) {
577 int font_type = char_table[c].font_type;
578 if (font_type != LETTER_TYPE)
579 printf("\\f[%s]", current_roman_font);
580 if (!prev_is_italic)
581 fputs("\\,", stdout);
582 if (c == '\\')
583 fputs("\\e", stdout);
584 else
585 putchar(c);
586 if (!next_is_italic)
587 fputs("\\/", stdout);
588 else
589 fputs("\\&", stdout); // suppress ligaturing and kerning
590 if (font_type != LETTER_TYPE)
591 fputs("\\fP", stdout);
593 else if (output_format == mathml) {
594 if (isdigit(c))
595 printf("<mn>");
596 else if (char_table[c].spacing_type)
597 printf("<mo>");
598 else
599 printf("<mi>");
600 if (c == '<')
601 printf("&lt;");
602 else if (c == '>')
603 printf("&gt;");
604 else if (c == '&')
605 printf("&amp;");
606 else
607 putchar(c);
608 if (isdigit(c))
609 printf("</mn>");
610 else if (char_table[c].spacing_type)
611 printf("</mo>");
612 else
613 printf("</mi>");
617 int char_box::left_is_italic()
619 int font_type = char_table[c].font_type;
620 return font_type == LETTER_TYPE;
623 int char_box::right_is_italic()
625 int font_type = char_table[c].font_type;
626 return font_type == LETTER_TYPE;
629 int char_box::is_char()
631 return 1;
634 void char_box::debug_print()
636 if (c == '\\') {
637 putc('\\', stderr);
638 putc('\\', stderr);
640 else
641 putc(c, stderr);
644 special_char_box::special_char_box(const char *t)
646 s = strsave(t);
647 spacing_type = get_special_char_spacing_type(s);
650 special_char_box::~special_char_box()
652 a_delete s;
655 void special_char_box::output()
657 if (output_format == troff) {
658 int font_type = get_special_char_font_type(s);
659 if (font_type != LETTER_TYPE)
660 printf("\\f[%s]", current_roman_font);
661 printf("\\,\\[%s]\\/", s);
662 if (font_type != LETTER_TYPE)
663 printf("\\fP");
665 else if (output_format == mathml) {
666 const char *entity = special_to_entity(s);
667 if (entity != NULL)
668 printf("<mo>%s</mo>", entity);
669 else
670 printf("<merror>unknown eqn/troff special char %s</merror>", s);
674 int special_char_box::is_char()
676 return 1;
679 void special_char_box::debug_print()
681 fprintf(stderr, "\\[%s]", s);
685 void char_box::handle_char_type(int st, int ft)
687 if (st >= 0)
688 char_table[c].spacing_type = st;
689 if (ft >= 0)
690 char_table[c].font_type = ft;
693 void special_char_box::handle_char_type(int st, int ft)
695 set_special_char_type(s, st, ft);
698 void set_char_type(const char *type, char *ch)
700 assert(ch != 0);
701 int st = lookup_spacing_type(type);
702 int ft = lookup_font_type(type);
703 if (st < 0 && ft < 0) {
704 error("bad character type `%1'", type);
705 a_delete ch;
706 return;
708 box *b = split_text(ch);
709 b->handle_char_type(st, ft);
710 delete b;
713 /* We give primes special treatment so that in ``x' sub 2'', the ``2''
714 will be tucked under the prime */
716 class prime_box : public pointer_box {
717 box *pb;
718 public:
719 prime_box(box *);
720 ~prime_box();
721 int compute_metrics(int style);
722 void output();
723 void compute_subscript_kern();
724 void debug_print();
725 void handle_char_type(int, int);
728 box *make_prime_box(box *pp)
730 return new prime_box(pp);
733 prime_box::prime_box(box *pp) : pointer_box(pp)
735 pb = new special_char_box("fm");
738 prime_box::~prime_box()
740 delete pb;
743 int prime_box::compute_metrics(int style)
745 int res = p->compute_metrics(style);
746 pb->compute_metrics(style);
747 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]"
748 "+\\n[" WIDTH_FORMAT "]\n",
749 uid, p->uid, pb->uid);
750 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
751 ">?\\n[" HEIGHT_FORMAT "]\n",
752 uid, p->uid, pb->uid);
753 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
754 ">?\\n[" DEPTH_FORMAT "]\n",
755 uid, p->uid, pb->uid);
756 return res;
759 void prime_box::compute_subscript_kern()
761 p->compute_subscript_kern();
762 printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]"
763 "+\\n[" SUB_KERN_FORMAT "]>?0\n",
764 uid, pb->uid, p->uid);
767 void prime_box::output()
769 p->output();
770 pb->output();
773 void prime_box::handle_char_type(int st, int ft)
775 p->handle_char_type(st, ft);
776 pb->handle_char_type(st, ft);
779 void prime_box::debug_print()
781 p->debug_print();
782 putc('\'', stderr);
785 box *split_text(char *text)
787 list_box *lb = 0;
788 box *fb = 0;
789 char *s = text;
790 while (*s != '\0') {
791 char c = *s++;
792 box *b = 0;
793 switch (c) {
794 case '+':
795 b = new special_char_box("pl");
796 break;
797 case '-':
798 b = new special_char_box("mi");
799 break;
800 case '=':
801 b = new special_char_box("eq");
802 break;
803 case '\'':
804 b = new special_char_box("fm");
805 break;
806 case '<':
807 if (*s == '=') {
808 b = new special_char_box("<=");
809 s++;
810 break;
812 goto normal_char;
813 case '>':
814 if (*s == '=') {
815 b = new special_char_box(">=");
816 s++;
817 break;
819 goto normal_char;
820 case '\\':
821 if (*s == '\0') {
822 lex_error("bad escape");
823 break;
825 c = *s++;
826 switch (c) {
827 case '(':
829 char buf[3];
830 if (*s != '\0') {
831 buf[0] = *s++;
832 if (*s != '\0') {
833 buf[1] = *s++;
834 buf[2] = '\0';
835 b = new special_char_box(buf);
837 else {
838 lex_error("bad escape");
841 else {
842 lex_error("bad escape");
845 break;
846 case '[':
848 char *ch = s;
849 while (*s != ']' && *s != '\0')
850 s++;
851 if (*s == '\0')
852 lex_error("bad escape");
853 else {
854 *s++ = '\0';
855 b = new special_char_box(ch);
858 break;
859 case 'f':
860 case 'g':
861 case 'k':
862 case 'n':
863 case '*':
865 char *escape_start = s - 2;
866 switch (*s) {
867 case '(':
868 if (*++s != '\0')
869 ++s;
870 break;
871 case '[':
872 for (++s; *s != '\0' && *s != ']'; s++)
874 break;
876 if (*s == '\0')
877 lex_error("bad escape");
878 else {
879 ++s;
880 char *buf = new char[s - escape_start + 1];
881 memcpy(buf, escape_start, s - escape_start);
882 buf[s - escape_start] = '\0';
883 b = new quoted_text_box(buf);
886 break;
887 case '-':
888 case '_':
890 char buf[2];
891 buf[0] = c;
892 buf[1] = '\0';
893 b = new special_char_box(buf);
895 break;
896 case '`':
897 b = new special_char_box("ga");
898 break;
899 case '\'':
900 b = new special_char_box("aa");
901 break;
902 case 'e':
903 case '\\':
904 b = new char_box('\\');
905 break;
906 case '^':
907 case '|':
908 case '0':
910 char buf[3];
911 buf[0] = '\\';
912 buf[1] = c;
913 buf[2] = '\0';
914 b = new quoted_text_box(strsave(buf));
915 break;
917 default:
918 lex_error("unquoted escape");
919 b = new quoted_text_box(strsave(s - 2));
920 s = strchr(s, '\0');
921 break;
923 break;
924 default:
925 normal_char:
926 b = new char_box(c);
927 break;
929 while (*s == '\'') {
930 if (b == 0)
931 b = new quoted_text_box(0);
932 b = new prime_box(b);
933 s++;
935 if (b != 0) {
936 if (lb != 0)
937 lb->append(b);
938 else if (fb != 0) {
939 lb = new list_box(fb);
940 lb->append(b);
942 else
943 fb = b;
946 a_delete text;
947 if (lb != 0)
948 return lb;
949 else if (fb != 0)
950 return fb;
951 else
952 return new quoted_text_box(0);