diff options
Diffstat (limited to 'src/preproc/eqn/text.cpp')
-rw-r--r-- | src/preproc/eqn/text.cpp | 957 |
1 files changed, 957 insertions, 0 deletions
diff --git a/src/preproc/eqn/text.cpp b/src/preproc/eqn/text.cpp new file mode 100644 index 0000000..272f3fe --- /dev/null +++ b/src/preproc/eqn/text.cpp @@ -0,0 +1,957 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or +(at your option) any later version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ctype.h> +#include <stdlib.h> +#include "eqn.h" +#include "pbox.h" +#include "ptable.h" + +struct map { + const char *from; + const char *to; +}; + +struct map entity_table[] = { + // Classic troff special characters + {"%", "­"}, // ISOnum + {"'", "´"}, // ISOdia + {"!=", "≠"}, // ISOtech + {"**", "∗"}, // ISOtech + {"*a", "α"}, // ISOgrk3 + {"*A", "A"}, + {"*b", "β"}, // ISOgrk3 + {"*B", "B"}, + {"*d", "δ"}, // ISOgrk3 + {"*D", "Δ"}, // ISOgrk3 + {"*e", "ε"}, // ISOgrk3 + {"*E", "E"}, + {"*f", "φ"}, // ISOgrk3 + {"*F", "Φ"}, // ISOgrk3 + {"*g", "γ"}, // ISOgrk3 + {"*G", "Γ"}, // ISOgrk3 + {"*h", "θ"}, // ISOgrk3 + {"*H", "Θ"}, // ISOgrk3 + {"*i", "ι"}, // ISOgrk3 + {"*I", "I"}, + {"*k", "κ"}, // ISOgrk3 + {"*K", "K;"}, + {"*l", "λ"}, // ISOgrk3 + {"*L", "Λ"}, // ISOgrk3 + {"*m", "μ"}, // ISOgrk3 + {"*M", "M"}, + {"*n", "ν"}, // ISOgrk3 + {"*N", "N"}, + {"*o", "o"}, + {"*O", "O"}, + {"*p", "π"}, // ISOgrk3 + {"*P", "Π"}, // ISOgrk3 + {"*q", "ψ"}, // ISOgrk3 + {"*Q", "&PSI;"}, // ISOgrk3 + {"*r", "ρ"}, // ISOgrk3 + {"*R", "R"}, + {"*s", "σ"}, // ISOgrk3 + {"*S", "Σ"}, // ISOgrk3 + {"*t", "τ"}, // ISOgrk3 + {"*T", "Τ"}, // ISOgrk3 + {"*u", "υ"}, // ISOgrk3 + {"*U", "Υ"}, // ISOgrk3 + {"*w", "ω"}, // ISOgrk3 + {"*W", "Ω"}, // ISOgrk3 + {"*x", "χ"}, // ISOgrk3 + {"*X", "Χ"}, // ISOgrk3 + {"*y", "η"}, // ISOgrk3 + {"*Y", "Η"}, // ISOgrk3 + {"*z", "ζ"}, // ISOgrk3 + {"*Z", "Ζ"}, // ISOgrk3 + {"+-", "±"}, // ISOnum + {"->", "→"}, // ISOnum + {"12", "½"}, // ISOnum + {"14", "¼"}, // ISOnum + {"34", "¾"}, // ISOnum + {"<-", "←"}, // ISOnum + {"==", "≡"}, // ISOtech + {"Fi", "ffi"}, // ISOpub + {"Fl", "ffl"}, // ISOpub + {"aa", "´"}, // ISOdia + {"ap", "∼"}, // ISOtech + {"bl", "&phonexb;"}, // ISOpub + {"br", "│"}, // ISObox + {"bs", "☎"}, // ISOpub (for the Bell logo) + {"bu", "•"}, // ISOpub + {"bv", "|"}, // ISOnum + {"ca", "∩"}, // ISOtech + {"ci", "○"}, // ISOpub + {"co", "©"}, // ISOnum + {"ct", "¢"}, // ISOnum + {"cu", "∪"}, // ISOtech + {"da", "↓"}, // ISOnum + {"de", "°"}, // ISOnum + {"dg", "†"}, // ISOpub + {"dd", "‡"}, // ISOpub + {"di", "÷"}, // ISOnum + {"em", "—"}, // ISOpub + {"eq", "="}, // ISOnum + {"es", "∅"}, // ISOamso + {"ff", "ff"}, // ISOpub + {"fi", "fi"}, // ISOpub + {"fl", "fl"}, // ISOpub + {"fm", "′"}, // ISOtech + {"ge", "≥"}, // ISOtech + {"gr", "∇"}, // ISOtech + {"hy", "‐"}, // ISOnum + {"ib", "⊆"}, // ISOtech + {"if", "∞"}, // ISOtech + {"ip", "⊇"}, // ISOtech + {"is", "∫"}, // ISOtech + {"le", "≤"}, // ISOtech + // Some pile characters go here + {"mi", "−"}, // ISOtech + {"mo", "∈"}, // ISOtech + {"mu", "×"}, // ISOnum + {"no", "¬"}, // ISOnum + {"or", "|"}, // ISOnum + {"pl", "+"}, // ISOnum + {"pt", "∝"}, // ISOtech + {"rg", "™"}, // ISOnum + // More pile characters go here + {"rn", "¯"}, // ISOdia + {"ru", "_"}, // ISOnum + {"sb", "⊂"}, // ISOtech + {"sc", "§"}, // ISOnum + {"sl", "/"}, + {"sp", "⊃"}, // ISOtech + {"sq", "▪"}, // ISOpub + {"sr", "√"}, // ISOtech + {"ts", "ς"}, // ISOgrk3 + {"ua", "↑"}, // ISOnum + {"ul", "_"}, + {"~=", "≅"}, // ISOtech + // Extended specials supported by groff; see groff_char(7). + // These are listed in the order they occur on that man page. + {"-D", "Ð"}, // ISOlat: Icelandic uppercase eth + {"Sd", "ð"}, // ISOlat1: Icelandic lowercase eth + {"TP", "Þ"}, // ISOlat1: Icelandic uppercase thorn + {"Tp", "þ"}, // ISOlat1: Icelandic lowercase thorn + {"ss", "ß"}, // ISOlat1 + // Ligatures + // ff, fi, fl, ffi, ffl from old troff go here + {"AE", "Æ"}, // ISOlat1 + {"ae", "æ"}, // ISOlat1 + {"OE", "Œ"}, // ISOlat2 + {"oe", "œ"}, // ISOlat2 + {"IJ", "ij"}, // ISOlat2: Dutch IJ ligature + {"ij", "IJ"}, // ISOlat2: Dutch ij ligature + {".i", "ı"}, // ISOlat2,ISOamso + {".j", "&jnodot;"}, // ISOamso (undocumented but in 1.19) + // Accented characters + {"'A", "Á"}, // ISOlat1 + {"'C", "Ć"}, // ISOlat2 + {"'E", "É"}, // ISOlat1 + {"'I", "Í"}, // ISOlat1 + {"'O", "Ó"}, // ISOlat1 + {"'U", "Ú"}, // ISOlat1 + {"'Y", "Ý"}, // ISOlat1 + {"'a", "á"}, // ISOlat1 + {"'c", "ć"}, // ISOlat2 + {"'e", "é"}, // ISOlat1 + {"'i", "í"}, // ISOlat1 + {"'o", "ó"}, // ISOlat1 + {"'u", "ú"}, // ISOlat1 + {"'y", "ý"}, // ISOlat1 + {":A", "Ä"}, // ISOlat1 + {":E", "Ë"}, // ISOlat1 + {":I", "Ï"}, // ISOlat1 + {":O", "Ö"}, // ISOlat1 + {":U", "Ü"}, // ISOlat1 + {":Y", "Ÿ"}, // ISOlat2 + {":a", "ä"}, // ISOlat1 + {":e", "ë"}, // ISOlat1 + {":i", "ï"}, // ISOlat1 + {":o", "ö"}, // ISOlat1 + {":u", "ü"}, // ISOlat1 + {":y", "ÿ"}, // ISOlat1 + {"^A", "Â"}, // ISOlat1 + {"^E", "Ê"}, // ISOlat1 + {"^I", "Î"}, // ISOlat1 + {"^O", "Ô"}, // ISOlat1 + {"^U", "Û"}, // ISOlat1 + {"^a", "â"}, // ISOlat1 + {"^e", "ê"}, // ISOlat1 + {"^i", "î"}, // ISOlat1 + {"^o", "ô"}, // ISOlat1 + {"^u", "û"}, // ISOlat1 + {"`A", "À"}, // ISOlat1 + {"`E", "È"}, // ISOlat1 + {"`I", "Ì"}, // ISOlat1 + {"`O", "Ò"}, // ISOlat1 + {"`U", "Ù"}, // ISOlat1 + {"`a", "à"}, // ISOlat1 + {"`e", "è"}, // ISOlat1 + {"`i", "ì"}, // ISOlat1 + {"`o", "ò"}, // ISOlat1 + {"`u", "ù"}, // ISOlat1 + {"~A", "Ã"}, // ISOlat1 + {"~N", "Ñ"}, // ISOlat1 + {"~O", "Õ"}, // ISOlat1 + {"~a", "ã"}, // ISOlat1 + {"~n", "ñ"}, // ISOlat1 + {"~o", "õ"}, // ISOlat1 + {"vS", "Š"}, // ISOlat2 + {"vs", "š"}, // ISOlat2 + {"vZ", "Ž"}, // ISOlat2 + {"vz", "ž"}, // ISOlat2 + {",C", "Ç"}, // ISOlat1 + {",c", "ç"}, // ISOlat1 + {"/L", "Ł"}, // ISOlat2: Polish L with a slash + {"/l", "ł"}, // ISOlat2: Polish l with a slash + {"/O", "Ø"}, // ISOlat1 + {"/o", "ø"}, // ISOlat1 + {"oA", "Å"}, // ISOlat1 + {"oa", "å"}, // ISOlat1 + // Accents + {"a\"","˝"}, // ISOdia: double acute accent (Hungarian umlaut) + {"a-", "¯"}, // ISOdia: macron or bar accent + {"a.", "˙"}, // ISOdia: dot above + {"a^", "ˆ"}, // ISOdia: circumflex accent + {"aa", "´"}, // ISOdia: acute accent + {"ga", "`"}, // ISOdia: grave accent + {"ab", "˘"}, // ISOdia: breve accent + {"ac", "¸"}, // ISOdia: cedilla accent + {"ad", "¨"}, // ISOdia: umlaut or dieresis + {"ah", "ˇ"}, // ISOdia: caron (aka hacek accent) + {"ao", "˚"}, // ISOdia: ring or circle accent + {"a~", "˜"}, // ISOdia: tilde accent + {"ho", "˛"}, // ISOdia: hook or ogonek accent + {"ha", "^"}, // ASCII circumflex, hat, caret + {"ti", "~"}, // ASCII tilde, large tilde + // Quotes + {"Bq", "‚"}, // ISOpub: low double comma quote + {"bq", "„"}, // ISOpub: low single comma quote + {"lq", "“"}, // ISOnum + {"rq", "”"}, // ISOpub + {"oq", "‘"}, // ISOnum: single open quote + {"cq", "’"}, // ISOnum: single closing quote (ASCII 39) + {"aq", "&zerosp;'"}, // apostrophe quote + {"dq", "\""}, // double quote (ASCII 34) + {"Fo", "«"}, // ISOnum + {"Fc", "»"}, // ISOnum + //{"fo", "&fo;"}, + //{"fc", "&fc;"}, + // Punctuation + {"r!", "¡"}, // ISOnum + {"r?", "¿"}, // ISOnum + // Old troff \(em goes here + {"en", "–"}, // ISOpub: en dash + // Old troff \(hy goes here + // Brackets + {"lB", "["}, // ISOnum: left (square) bracket + {"rB", "]"}, // ISOnum: right (square) bracket + {"lC", "{"}, // ISOnum: left (curly) brace + {"rC", "}"}, // ISOnum: right (curly) brace + {"la", "⟨"}, // ISOtech: left angle bracket + {"ra", "⟩"}, // ISOtech: right angle bracket + // Old troff \(bv goes here + // Bracket-pile characters could go here. + // Arrows + // Old troff \(<- and \(-> go here + {"<>", "↔"}, // ISOamsa + {"da", "↓"}, // ISOnum + {"ua", "↑"}, // ISOnum + {"lA", "⇐"}, // ISOtech + {"rA", "⇒"}, // ISOtech + {"hA", "⇔"}, // ISOtech: horizontal double-headed arrow + {"dA", "⇓"}, // ISOamsa + {"uA", "⇑"}, // ISOamsa + {"vA", "⇕"}, // ISOamsa: vertical double-headed double arrow + //{"an", "&an;"}, + // Lines + {"-h", "ℏ"}, // ISOamso: h-bar (Planck's constant) + // Old troff \(or goes here + {"ba", "|"}, // ISOnum + // Old troff \(br, \{u, \(ul, \(bv go here + {"bb", "¦"}, // ISOnum + {"sl", "/"}, + {"rs", "\"}, // ISOnum + // Text markers + // Old troff \(ci, \(bu, \(dd, \(dg go here + {"lz", "◊"}, // ISOpub + // Old troff sq goes here + {"ps", "¶"}, // ISOnum: paragraph or pilcrow sign + {"sc", "§"}, // ISOnum (in old troff) + // Old troff \(lh, \{h go here + {"at", "@"}, // ISOnum + {"sh", "#"}, // ISOnum + //{"CR", "&CR;"}, + {"OK", "✓"}, // ISOpub + // Legalize + // Old troff \(co, \{g go here + {"tm", "™"}, // ISOnum + // Currency symbols + {"Do", "$"}, // ISOnum + {"ct", "¢"}, // ISOnum + {"eu", "€"}, + {"Eu", "€"}, + {"Ye", "¥"}, // ISOnum + {"Po", "£"}, // ISOnum + {"Cs", "¤"}, // ISOnum: currency sign + {"Fn", "&fnof"}, // ISOtech + // Units + // Old troff de goes here + {"%0", "‰"}, // ISOtech: per thousand, per mille sign + // Old troff \(fm goes here + {"sd", "″"}, // ISOtech + {"mc", "µ"}, // ISOnum + {"Of", "ª"}, // ISOnum + {"Om", "º"}, // ISOnum + // Logical symbols + {"AN", "∧"}, // ISOtech + {"OR", "∨"}, // ISOtech + // Old troff \(no goes here + {"te", "∃"}, // ISOtech: there exists, existential quantifier + {"fa", "∀"}, // ISOtech: for all, universal quantifier + {"st", "&bepsi"}, // ISOamsr: such that + {"3d", "∴"}, // ISOtech + {"tf", "∴"}, // ISOtech + // Mathematical symbols + // Old troff "12", "14", "34" goes here + {"S1", "¹"}, // ISOnum + {"S2", "²"}, // ISOnum + {"S3", "³"}, // ISOnum + // Old troff \(pl", \-, \(+- go here + {"t+-", "±"}, // ISOnum + {"-+", "∓"}, // ISOtech + {"pc", "·"}, // ISOnum + {"md", "·"}, // ISOnum + // Old troff \(mu goes here + {"tmu", "×"}, // ISOnum + {"c*", "⊗"}, // ISOamsb: multiply sign in a circle + {"c+", "⊕"}, // ISOamsb: plus sign in a circle + // Old troff \(di goes here + {"tdi", "÷"}, // ISOnum + {"f/", "―"}, // ISOnum: horizintal bar for fractions + // Old troff \(** goes here + {"<=", "≤"}, // ISOtech + {">=", "≥"}, // ISOtech + {"<<", "≪"}, // ISOamsr + {">>", "≫"}, // ISOamsr + {"!=", "≠"}, // ISOtech + // Old troff \(eq and \(== go here + {"=~", "≅"}, // ISOamsr + // Old troff \(ap goes here + {"~~", "≈"}, // ISOtech + // This appears to be an error in the groff table. + // It clashes with the Bell Labs use of ~= for a congruence sign + // {"~=", "≈"}, // ISOamsr + // Old troff \(pt, \(es, \(mo go here + {"nm", "∉"}, // ISOtech + {"nb", "⊄"}, // ISOamsr + {"nc", "⊅"}, // ISOamsn + {"ne", "≢"}, // ISOamsn + // Old troff \(sb, \(sp, \(ib, \(ip, \(ca, \(cu go here + {"/_", "∠"}, // ISOamso + {"pp", "⊥"}, // ISOtech + // Old troff \(is goes here + {"sum", "∑"}, // ISOamsb + {"product", "∏"}, // ISOamsb + {"gr", "∇"}, // ISOtech + // Old troff \(sr. \{n, \(if go here + {"Ah", "ℵ"}, // ISOtech + {"Im", "ℑ"}, // ISOamso: Fraktur I, imaginary + {"Re", "ℜ"}, // ISOamso: Fraktur R, real + {"wp", "℘"}, // ISOamso + {"pd", "∂"}, // ISOtech: partial differentiation sign + // Their table duplicates the Greek letters here. + // We list only the variant forms here, mapping them into + // the ISO Greek 4 variants (which may or may not be correct :-() + {"+f", "&b.phiv;"}, // ISOgrk4: variant phi + {"+h", "&b.thetas;"}, // ISOgrk4: variant theta + {"+p", "&b.omega;"}, // ISOgrk4: variant pi, looking like omega + // Card symbols + {"CL", "♣"}, // ISOpub: club suit + {"SP", "♠"}, // ISOpub: spade suit + {"HE", "♥"}, // ISOpub: heart suit + {"DI", "♦"}, // ISOpub: diamond suit +}; + +const char *special_to_entity(const char *sp) +{ + struct map *mp; + for (mp = entity_table; + mp < entity_table + sizeof(entity_table)/sizeof(entity_table[0]); + mp++) { + if (strcmp(mp->from, sp) == 0) + return mp->to; + } + return NULL; +} + +class char_box : public simple_box { + unsigned char c; + char next_is_italic; + char prev_is_italic; +public: + char_box(unsigned char); + void debug_print(); + void output(); + int is_char(); + int left_is_italic(); + int right_is_italic(); + void hint(unsigned); + void handle_char_type(int, int); +}; + +class special_char_box : public simple_box { + char *s; +public: + special_char_box(const char *); + ~special_char_box(); + void output(); + void debug_print(); + int is_char(); + void handle_char_type(int, int); +}; + +enum spacing_type { + s_ordinary, + s_operator, + s_binary, + s_relation, + s_opening, + s_closing, + s_punctuation, + s_inner, + s_suppress +}; + +const char *spacing_type_table[] = { + "ordinary", + "operator", + "binary", + "relation", + "opening", + "closing", + "punctuation", + "inner", + "suppress", + 0, +}; + +const int DIGIT_TYPE = 0; +const int LETTER_TYPE = 1; + +const char *font_type_table[] = { + "digit", + "letter", + 0, +}; + +struct char_info { + int spacing_type; + int font_type; + char_info(); +}; + +char_info::char_info() +: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE) +{ +} + +static char_info char_table[256]; + +declare_ptable(char_info) +implement_ptable(char_info) + +PTABLE(char_info) special_char_table; + +static int get_special_char_spacing_type(const char *ch) +{ + char_info *p = special_char_table.lookup(ch); + return p ? p->spacing_type : ORDINARY_TYPE; +} + +static int get_special_char_font_type(const char *ch) +{ + char_info *p = special_char_table.lookup(ch); + return p ? p->font_type : DIGIT_TYPE; +} + +static void set_special_char_type(const char *ch, int st, int ft) +{ + char_info *p = special_char_table.lookup(ch); + if (!p) { + p = new char_info[1]; + special_char_table.define(ch, p); + } + if (st >= 0) + p->spacing_type = st; + if (ft >= 0) + p->font_type = ft; +} + +void init_char_table() +{ + set_special_char_type("pl", s_binary, -1); + set_special_char_type("mi", s_binary, -1); + set_special_char_type("eq", s_relation, -1); + set_special_char_type("<=", s_relation, -1); + set_special_char_type(">=", s_relation, -1); + char_table['}'].spacing_type = s_closing; + char_table[')'].spacing_type = s_closing; + char_table[']'].spacing_type = s_closing; + char_table['{'].spacing_type = s_opening; + char_table['('].spacing_type = s_opening; + char_table['['].spacing_type = s_opening; + char_table[','].spacing_type = s_punctuation; + char_table[';'].spacing_type = s_punctuation; + char_table[':'].spacing_type = s_punctuation; + char_table['.'].spacing_type = s_punctuation; + char_table['>'].spacing_type = s_relation; + char_table['<'].spacing_type = s_relation; + char_table['*'].spacing_type = s_binary; + for (int i = 0; i < 256; i++) + if (csalpha(i)) + char_table[i].font_type = LETTER_TYPE; +} + +static int lookup_spacing_type(const char *type) +{ + for (int i = 0; spacing_type_table[i] != 0; i++) + if (strcmp(spacing_type_table[i], type) == 0) + return i; + return -1; +} + +static int lookup_font_type(const char *type) +{ + for (int i = 0; font_type_table[i] != 0; i++) + if (strcmp(font_type_table[i], type) == 0) + return i; + return -1; +} + +void box::set_spacing_type(char *type) +{ + int t = lookup_spacing_type(type); + if (t < 0) + error("unrecognised type '%1'", type); + else + spacing_type = t; + free(type); +} + +char_box::char_box(unsigned char cc) +: c(cc), next_is_italic(0), prev_is_italic(0) +{ + spacing_type = char_table[c].spacing_type; +} + +void char_box::hint(unsigned flags) +{ + if (flags & HINT_PREV_IS_ITALIC) + prev_is_italic = 1; + if (flags & HINT_NEXT_IS_ITALIC) + next_is_italic = 1; +} + +void char_box::output() +{ + if (output_format == troff) { + int font_type = char_table[c].font_type; + if (font_type != LETTER_TYPE) + printf("\\f[%s]", current_roman_font); + if (!prev_is_italic) + fputs("\\,", stdout); + if (c == '\\') + fputs("\\e", stdout); + else + putchar(c); + if (!next_is_italic) + fputs("\\/", stdout); + else + fputs("\\&", stdout); // suppress ligaturing and kerning + if (font_type != LETTER_TYPE) + fputs("\\fP", stdout); + } + else if (output_format == mathml) { + if (isdigit(c)) + printf("<mn>"); + else if (char_table[c].spacing_type) + printf("<mo>"); + else + printf("<mi>"); + if (c == '<') + printf("<"); + else if (c == '>') + printf(">"); + else if (c == '&') + printf("&"); + else + putchar(c); + if (isdigit(c)) + printf("</mn>"); + else if (char_table[c].spacing_type) + printf("</mo>"); + else + printf("</mi>"); + } +} + +int char_box::left_is_italic() +{ + int font_type = char_table[c].font_type; + return font_type == LETTER_TYPE; +} + +int char_box::right_is_italic() +{ + int font_type = char_table[c].font_type; + return font_type == LETTER_TYPE; +} + +int char_box::is_char() +{ + return 1; +} + +void char_box::debug_print() +{ + if (c == '\\') { + putc('\\', stderr); + putc('\\', stderr); + } + else + putc(c, stderr); +} + +special_char_box::special_char_box(const char *t) +{ + s = strsave(t); + spacing_type = get_special_char_spacing_type(s); +} + +special_char_box::~special_char_box() +{ + free(s); +} + +void special_char_box::output() +{ + if (output_format == troff) { + int font_type = get_special_char_font_type(s); + if (font_type != LETTER_TYPE) + printf("\\f[%s]", current_roman_font); + printf("\\,\\[%s]\\/", s); + if (font_type != LETTER_TYPE) + printf("\\fP"); + } + else if (output_format == mathml) { + const char *entity = special_to_entity(s); + if (entity != NULL) + printf("<mo>%s</mo>", entity); + else + printf("<merror>unknown eqn/troff special char %s</merror>", s); + } +} + +int special_char_box::is_char() +{ + return 1; +} + +void special_char_box::debug_print() +{ + fprintf(stderr, "\\[%s]", s); +} + + +void char_box::handle_char_type(int st, int ft) +{ + if (st >= 0) + char_table[c].spacing_type = st; + if (ft >= 0) + char_table[c].font_type = ft; +} + +void special_char_box::handle_char_type(int st, int ft) +{ + set_special_char_type(s, st, ft); +} + +void set_char_type(const char *type, char *ch) +{ + assert(ch != 0); + int st = lookup_spacing_type(type); + int ft = lookup_font_type(type); + if (st < 0 && ft < 0) { + error("bad character type '%1'", type); + delete[] ch; + return; + } + box *b = split_text(ch); + b->handle_char_type(st, ft); + delete b; +} + +/* We give primes special treatment so that in "x' sub 2", the "2" +will be tucked under the prime */ + +class prime_box : public pointer_box { + box *pb; +public: + prime_box(box *); + ~prime_box(); + int compute_metrics(int style); + void output(); + void compute_subscript_kern(); + void debug_print(); + void handle_char_type(int, int); +}; + +box *make_prime_box(box *pp) +{ + return new prime_box(pp); +} + +prime_box::prime_box(box *pp) : pointer_box(pp) +{ + pb = new special_char_box("fm"); +} + +prime_box::~prime_box() +{ + delete pb; +} + +int prime_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + pb->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]" + "+\\n[" WIDTH_FORMAT "]\n", + uid, p->uid, pb->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?\\n[" HEIGHT_FORMAT "]\n", + uid, p->uid, pb->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?\\n[" DEPTH_FORMAT "]\n", + uid, p->uid, pb->uid); + return res; +} + +void prime_box::compute_subscript_kern() +{ + p->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]" + "+\\n[" SUB_KERN_FORMAT "]>?0\n", + uid, pb->uid, p->uid); +} + +void prime_box::output() +{ + p->output(); + pb->output(); +} + +void prime_box::handle_char_type(int st, int ft) +{ + p->handle_char_type(st, ft); + pb->handle_char_type(st, ft); +} + +void prime_box::debug_print() +{ + p->debug_print(); + putc('\'', stderr); +} + +box *split_text(char *text) +{ + list_box *lb = 0; + box *fb = 0; + char *s = text; + while (*s != '\0') { + char c = *s++; + box *b = 0; + switch (c) { + case '+': + b = new special_char_box("pl"); + break; + case '-': + b = new special_char_box("mi"); + break; + case '=': + b = new special_char_box("eq"); + break; + case '\'': + b = new special_char_box("fm"); + break; + case '<': + if (*s == '=') { + b = new special_char_box("<="); + s++; + break; + } + goto normal_char; + case '>': + if (*s == '=') { + b = new special_char_box(">="); + s++; + break; + } + goto normal_char; + case '\\': + if (*s == '\0') { + lex_error("bad escape"); + break; + } + c = *s++; + switch (c) { + case '(': + { + char buf[3]; + if (*s != '\0') { + buf[0] = *s++; + if (*s != '\0') { + buf[1] = *s++; + buf[2] = '\0'; + b = new special_char_box(buf); + } + else { + lex_error("bad escape"); + } + } + else { + lex_error("bad escape"); + } + } + break; + case '[': + { + char *ch = s; + while (*s != ']' && *s != '\0') + s++; + if (*s == '\0') + lex_error("bad escape"); + else { + *s++ = '\0'; + b = new special_char_box(ch); + } + } + break; + case 'f': + case 'g': + case 'k': + case 'n': + case '*': + { + char *escape_start = s - 2; + switch (*s) { + case '(': + if (*++s != '\0') + ++s; + break; + case '[': + for (++s; *s != '\0' && *s != ']'; s++) + ; + break; + } + if (*s == '\0') + lex_error("bad escape"); + else { + ++s; + char *buf = new char[s - escape_start + 1]; + memcpy(buf, escape_start, s - escape_start); + buf[s - escape_start] = '\0'; + b = new quoted_text_box(buf); + } + } + break; + case '-': + case '_': + { + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + b = new special_char_box(buf); + } + break; + case '`': + b = new special_char_box("ga"); + break; + case '\'': + b = new special_char_box("aa"); + break; + case 'e': + case '\\': + b = new char_box('\\'); + break; + case '^': + case '|': + case '0': + { + char buf[3]; + buf[0] = '\\'; + buf[1] = c; + buf[2] = '\0'; + b = new quoted_text_box(strsave(buf)); + break; + } + default: + lex_error("unquoted escape"); + b = new quoted_text_box(strsave(s - 2)); + s = strchr(s, '\0'); + break; + } + break; + default: + normal_char: + b = new char_box(c); + break; + } + while (*s == '\'') { + if (b == 0) + b = new quoted_text_box(0); + b = new prime_box(b); + s++; + } + if (b != 0) { + if (lb != 0) + lb->append(b); + else if (fb != 0) { + lb = new list_box(fb); + lb->append(b); + } + else + fb = b; + } + } + free(text); + if (lb != 0) + return lb; + else if (fb != 0) + return fb; + else + return new quoted_text_box(0); +} + |