| Commit | Line | Data |
|---|---|---|
| 92d0a6a6 | 1 | // -*- C++ -*- |
| 4d3e9548 JL |
2 | /* Copyright (C) 1989, 1990, 1991, 1992, 2003, 2007, 2009 |
| 3 | Free Software Foundation, Inc. | |
| 92d0a6a6 JR |
4 | Written by James Clark (jjc@jclark.com) |
| 5 | ||
| 6 | This file is part of groff. | |
| 7 | ||
| 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 | |
| 4d3e9548 JL |
10 | Software Foundation, either version 3 of the License, or |
| 11 | (at your option) any later version. | |
| 92d0a6a6 JR |
12 | |
| 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. | |
| 17 | ||
| 4d3e9548 JL |
18 | You should have received a copy of the GNU General Public License |
| 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| 92d0a6a6 JR |
20 | |
| 21 | #include "eqn.h" | |
| 22 | #include "pbox.h" | |
| 23 | ||
| 24 | enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 }; | |
| 25 | ||
| 26 | // Small must be none-zero and must exist in each device. | |
| 27 | // Small will be put in the roman font, others are assumed to be | |
| 28 | // on the special font (so no font change will be necessary.) | |
| 29 | ||
| 30 | struct delimiter { | |
| 31 | const char *name; | |
| 32 | int flags; | |
| 33 | const char *small; | |
| 34 | const char *chain_format; | |
| 35 | const char *ext; | |
| 36 | const char *top; | |
| 37 | const char *mid; | |
| 38 | const char *bot; | |
| 39 | } delim_table[] = { | |
| 40 | { | |
| 41 | "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]", | |
| 42 | "\\[parenleftex]", | |
| 43 | "\\[parenlefttp]", | |
| 44 | 0, | |
| 45 | "\\[parenleftbt]", | |
| 46 | }, | |
| 47 | { | |
| 48 | ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]", | |
| 49 | "\\[parenrightex]", | |
| 50 | "\\[parenrighttp]", | |
| 51 | 0, | |
| 52 | "\\[parenrightbt]", | |
| 53 | }, | |
| 54 | { | |
| 55 | "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]", | |
| 56 | "\\[bracketleftex]", | |
| 57 | "\\[bracketlefttp]", | |
| 58 | 0, | |
| 59 | "\\[bracketleftbt]", | |
| 60 | }, | |
| 61 | { | |
| 62 | "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]", | |
| 63 | "\\[bracketrightex]", | |
| 64 | "\\[bracketrighttp]", | |
| 65 | 0, | |
| 66 | "\\[bracketrightbt]", | |
| 67 | }, | |
| 68 | { | |
| 69 | "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]", | |
| 70 | "\\[braceleftex]", | |
| 71 | "\\[bracelefttp]", | |
| 72 | "\\[braceleftmid]", | |
| 73 | "\\[braceleftbt]", | |
| 74 | }, | |
| 75 | { | |
| 76 | "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]", | |
| 77 | "\\[bracerightex]", | |
| 78 | "\\[bracerighttp]", | |
| 79 | "\\[bracerightmid]", | |
| 80 | "\\[bracerightbt]", | |
| 81 | }, | |
| 82 | { | |
| 83 | "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]", | |
| 84 | "\\[barex]", | |
| 85 | 0, | |
| 86 | 0, | |
| 87 | 0, | |
| 88 | }, | |
| 89 | { | |
| 90 | "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]", | |
| 91 | "\\[bracketleftex]", | |
| 92 | 0, | |
| 93 | 0, | |
| 94 | "\\[bracketleftbt]", | |
| 95 | }, | |
| 96 | { | |
| 97 | "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]", | |
| 98 | "\\[bracketrightex]", | |
| 99 | 0, | |
| 100 | 0, | |
| 101 | "\\[bracketrightbt]", | |
| 102 | }, | |
| 103 | { | |
| 104 | "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]", | |
| 105 | "\\[bracketleftex]", | |
| 106 | "\\[bracketlefttp]", | |
| 107 | 0, | |
| 108 | 0, | |
| 109 | }, | |
| 110 | { | |
| 111 | "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]", | |
| 112 | "\\[bracketrightex]", | |
| 113 | "\\[bracketrighttp]", | |
| 114 | 0, | |
| 115 | 0, | |
| 116 | }, | |
| 117 | { | |
| 118 | "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]", | |
| 119 | "\\[bardblex]", | |
| 120 | 0, | |
| 121 | 0, | |
| 122 | 0, | |
| 123 | }, | |
| 124 | { | |
| 125 | "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]", | |
| 126 | 0, | |
| 127 | 0, | |
| 128 | 0, | |
| 129 | 0, | |
| 130 | }, | |
| 131 | { | |
| 132 | ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]", | |
| 133 | 0, | |
| 134 | 0, | |
| 135 | 0, | |
| 136 | 0, | |
| 137 | }, | |
| 138 | { | |
| 139 | "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]", | |
| 140 | "\\[arrowvertex]", | |
| 141 | "\\[arrowverttp]", | |
| 142 | 0, | |
| 143 | 0, | |
| 144 | }, | |
| 145 | { | |
| 146 | "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]", | |
| 147 | "\\[arrowvertex]", | |
| 148 | 0, | |
| 149 | 0, | |
| 150 | "\\[arrowvertbt]", | |
| 151 | }, | |
| 152 | { | |
| 153 | "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]", | |
| 154 | "\\[arrowvertex]", | |
| 155 | "\\[arrowverttp]", | |
| 156 | 0, | |
| 157 | "\\[arrowvertbt]", | |
| 158 | }, | |
| 159 | }; | |
| 160 | ||
| 161 | const int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0])); | |
| 162 | ||
| 163 | class delim_box : public box { | |
| 164 | private: | |
| 165 | char *left; | |
| 166 | char *right; | |
| 167 | box *p; | |
| 168 | public: | |
| 169 | delim_box(char *, box *, char *); | |
| 170 | ~delim_box(); | |
| 171 | int compute_metrics(int); | |
| 172 | void output(); | |
| 173 | void check_tabs(int); | |
| 174 | void debug_print(); | |
| 175 | }; | |
| 176 | ||
| 177 | box *make_delim_box(char *l, box *pp, char *r) | |
| 178 | { | |
| 179 | if (l != 0 && *l == '\0') { | |
| 180 | a_delete l; | |
| 181 | l = 0; | |
| 182 | } | |
| 183 | if (r != 0 && *r == '\0') { | |
| 184 | a_delete r; | |
| 185 | r = 0; | |
| 186 | } | |
| 187 | return new delim_box(l, pp, r); | |
| 188 | } | |
| 189 | ||
| 190 | delim_box::delim_box(char *l, box *pp, char *r) | |
| 191 | : left(l), right(r), p(pp) | |
| 192 | { | |
| 193 | } | |
| 194 | ||
| 195 | delim_box::~delim_box() | |
| 196 | { | |
| 197 | a_delete left; | |
| 198 | a_delete right; | |
| 199 | delete p; | |
| 200 | } | |
| 201 | ||
| 202 | static void build_extensible(const char *ext, const char *top, const char *mid, | |
| 203 | const char *bot) | |
| 204 | { | |
| 205 | assert(ext != 0); | |
| 206 | printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", | |
| 207 | ext); | |
| 208 | printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n"); | |
| 209 | printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n"); | |
| 210 | if (top) { | |
| 211 | printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" | |
| 212 | ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", | |
| 213 | top); | |
| 214 | printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n"); | |
| 215 | printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n"); | |
| 216 | } | |
| 217 | if (mid) { | |
| 218 | printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" | |
| 219 | ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", | |
| 220 | mid); | |
| 221 | printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n"); | |
| 222 | printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n"); | |
| 223 | } | |
| 224 | if (bot) { | |
| 225 | printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" | |
| 226 | ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", | |
| 227 | bot); | |
| 228 | printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n"); | |
| 229 | printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n"); | |
| 230 | } | |
| 231 | printf(".nr " TOTAL_HEIGHT_REG " 0"); | |
| 232 | if (top) | |
| 233 | printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]"); | |
| 234 | if (bot) | |
| 235 | printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]"); | |
| 236 | if (mid) | |
| 237 | printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]"); | |
| 238 | printf("\n"); | |
| 239 | // determine how many extensible characters we need | |
| 240 | printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]"); | |
| 241 | if (mid) | |
| 242 | printf("/2"); | |
| 243 | printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n[" | |
| 244 | EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n"); | |
| 245 | ||
| 246 | printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n[" | |
| 247 | EXT_DEPTH_REG "]*\\n[" TEMP_REG "]"); | |
| 248 | if (mid) | |
| 249 | printf("*2"); | |
| 250 | printf(")\n"); | |
| 251 | printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR | |
| 252 | "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n", | |
| 253 | axis_height); | |
| 254 | if (top) | |
| 255 | printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'" | |
| 256 | "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR | |
| 257 | "\\v'\\n[" TOP_DEPTH_REG "]u'\n", | |
| 258 | top); | |
| 259 | ||
| 260 | // this macro appends $2 copies of $3 to string $1 | |
| 261 | printf(".de " REPEAT_APPEND_STRING_MACRO "\n" | |
| 262 | ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n" | |
| 263 | "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n" | |
| 264 | ".\\}\n" | |
| 265 | "..\n"); | |
| 266 | ||
| 267 | printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] " | |
| 268 | "\\v'\\n[" EXT_HEIGHT_REG "]u'" | |
| 269 | "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR | |
| 270 | "\\v'\\n[" EXT_DEPTH_REG "]u'\n", | |
| 271 | ext); | |
| 272 | ||
| 273 | if (mid) { | |
| 274 | printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'" | |
| 275 | "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR | |
| 276 | "\\v'\\n[" MID_DEPTH_REG "]u'\n", | |
| 277 | mid); | |
| 278 | printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING | |
| 279 | " \\n[" TEMP_REG "] " | |
| 280 | "\\v'\\n[" EXT_HEIGHT_REG "]u'" | |
| 281 | "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR | |
| 282 | "\\v'\\n[" EXT_DEPTH_REG "]u'\n", | |
| 283 | ext); | |
| 284 | } | |
| 285 | if (bot) | |
| 286 | printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'" | |
| 287 | "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR | |
| 288 | "\\v'\\n[" BOT_DEPTH_REG "]u'\n", | |
| 289 | bot); | |
| 290 | printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n"); | |
| 291 | } | |
| 292 | ||
| 293 | static void define_extensible_string(char *delim, int uid, | |
| 294 | left_or_right_t left_or_right) | |
| 295 | { | |
| 296 | printf(".ds " DELIM_STRING "\n"); | |
| 297 | delimiter *d = delim_table; | |
| 298 | int delim_len = strlen(delim); | |
| 299 | int i; | |
| 300 | for (i = 0; i < DELIM_TABLE_SIZE; i++, d++) | |
| 301 | if (strncmp(delim, d->name, delim_len) == 0 | |
| 302 | && (left_or_right & d->flags) != 0) | |
| 303 | break; | |
| 304 | if (i >= DELIM_TABLE_SIZE) { | |
| 305 | error("there is no `%1' delimiter", delim); | |
| 306 | printf(".nr " DELIM_WIDTH_REG " 0\n"); | |
| 307 | return; | |
| 308 | } | |
| 309 | ||
| 310 | printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n" | |
| 311 | ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR | |
| 312 | "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n" | |
| 313 | ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n" | |
| 314 | ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] " | |
| 315 | "\\{", | |
| 316 | current_roman_font, d->small, axis_height, | |
| 317 | current_roman_font, d->small); | |
| 318 | ||
| 319 | char buf[256]; | |
| 320 | sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]"); | |
| 321 | printf(".nr " INDEX_REG " 0\n" | |
| 322 | ".de " TEMP_MACRO "\n" | |
| 323 | ".ie c%s \\{\\\n" | |
| 324 | ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n" | |
| 325 | ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR | |
| 326 | "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n" | |
| 327 | ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n" | |
| 328 | ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] " | |
| 329 | "\\{.nr " INDEX_REG " +1\n" | |
| 330 | "." TEMP_MACRO "\n" | |
| 331 | ".\\}\\}\n" | |
| 332 | ".el .nr " INDEX_REG " 0-1\n" | |
| 333 | "..\n" | |
| 334 | "." TEMP_MACRO "\n", | |
| 335 | buf, buf, axis_height, buf); | |
| 336 | if (d->ext) { | |
| 337 | printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext); | |
| 338 | build_extensible(d->ext, d->top, d->mid, d->bot); | |
| 339 | printf(".\\}\\}\n"); | |
| 340 | } | |
| 341 | printf(".\\}\n"); | |
| 342 | printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n"); | |
| 343 | printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid); | |
| 344 | printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" | |
| 345 | ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n", | |
| 346 | uid, uid, axis_height); | |
| 347 | printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" | |
| 348 | ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n", | |
| 349 | uid, uid, axis_height); | |
| 350 | } | |
| 351 | ||
| 352 | int delim_box::compute_metrics(int style) | |
| 353 | { | |
| 354 | int r = p->compute_metrics(style); | |
| 355 | printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); | |
| 356 | printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); | |
| 357 | printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); | |
| 358 | printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM" | |
| 359 | ">?(\\n[" DEPTH_FORMAT "]+%dM)\n", | |
| 360 | p->uid, axis_height, p->uid, axis_height); | |
| 361 | printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500" | |
| 362 | ">?(\\n[" DELTA_REG "]*2-%dM)\n", | |
| 363 | delimiter_factor, delimiter_shortfall); | |
| 364 | if (left) { | |
| 365 | define_extensible_string(left, uid, LEFT_DELIM); | |
| 366 | printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n", | |
| 367 | uid); | |
| 368 | if (r) | |
| 369 | printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n"); | |
| 370 | } | |
| 371 | if (right) { | |
| 372 | define_extensible_string(right, uid, RIGHT_DELIM); | |
| 373 | printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n", | |
| 374 | uid); | |
| 375 | } | |
| 376 | return r; | |
| 377 | } | |
| 378 | ||
| 379 | void delim_box::output() | |
| 380 | { | |
| 4d3e9548 JL |
381 | if (output_format == troff) { |
| 382 | if (left) | |
| 383 | printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid); | |
| 384 | p->output(); | |
| 385 | if (right) | |
| 386 | printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid); | |
| 387 | } | |
| 388 | else if (output_format == mathml) { | |
| 389 | printf("<mrow><mo>%s</mo>", left); | |
| 390 | p->output(); | |
| 391 | printf("<mo>%s</mo></mrow>", right); | |
| 392 | } | |
| 92d0a6a6 JR |
393 | } |
| 394 | ||
| 395 | void delim_box::check_tabs(int level) | |
| 396 | { | |
| 397 | p->check_tabs(level); | |
| 398 | } | |
| 399 | ||
| 400 | void delim_box::debug_print() | |
| 401 | { | |
| 402 | fprintf(stderr, "left \"%s\" { ", left ? left : ""); | |
| 403 | p->debug_print(); | |
| 404 | fprintf(stderr, " }"); | |
| 405 | if (right) | |
| 406 | fprintf(stderr, " right \"%s\"", right); | |
| 407 | } | |
| 408 |