1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2002, 2004 3 Free Software Foundation, Inc. 4 Written by James Clark (jjc@jclark.com) 5 6This file is part of groff. 7 8groff is free software; you can redistribute it and/or modify it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 2, or (at your option) any later 11version. 12 13groff is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18You should have received a copy of the GNU General Public License along 19with groff; see the file COPYING. If not, write to the Free Software 20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21 22#include "eqn.h" 23#include "pbox.h" 24 25const char *current_roman_font; 26 27char *gfont = 0; 28char *grfont = 0; 29char *gbfont = 0; 30int gsize = 0; 31 32int script_size_reduction = -1; // negative means reduce by a percentage 33 34int positive_space = -1; 35int negative_space = -1; 36 37int minimum_size = 5; 38 39int fat_offset = 4; 40int body_height = 85; 41int body_depth = 35; 42 43int over_hang = 0; 44int accent_width = 31; 45int delimiter_factor = 900; 46int delimiter_shortfall = 50; 47 48int null_delimiter_space = 12; 49int script_space = 5; 50int thin_space = 17; 51int medium_space = 22; 52int thick_space = 28; 53 54int num1 = 70; 55int num2 = 40; 56// we don't use num3, because we don't have \atop 57int denom1 = 70; 58int denom2 = 36; 59int axis_height = 26; // in 100ths of an em 60int sup1 = 42; 61int sup2 = 37; 62int sup3 = 28; 63int default_rule_thickness = 4; 64int sub1 = 20; 65int sub2 = 23; 66int sup_drop = 38; 67int sub_drop = 5; 68int x_height = 45; 69int big_op_spacing1 = 11; 70int big_op_spacing2 = 17; 71int big_op_spacing3 = 20; 72int big_op_spacing4 = 60; 73int big_op_spacing5 = 10; 74 75// These are for piles and matrices. 76 77int baseline_sep = 140; // = num1 + denom1 78int shift_down = 26; // = axis_height 79int column_sep = 100; // = em space 80int matrix_side_sep = 17; // = thin space 81 82int nroff = 0; // should we grok ndefine or tdefine? 83 84struct S { 85 const char *name; 86 int *ptr; 87} param_table[] = { 88 { "fat_offset", &fat_offset }, 89 { "over_hang", &over_hang }, 90 { "accent_width", &accent_width }, 91 { "delimiter_factor", &delimiter_factor }, 92 { "delimiter_shortfall", &delimiter_shortfall }, 93 { "null_delimiter_space", &null_delimiter_space }, 94 { "script_space", &script_space }, 95 { "thin_space", &thin_space }, 96 { "medium_space", &medium_space }, 97 { "thick_space", &thick_space }, 98 { "num1", &num1 }, 99 { "num2", &num2 }, 100 { "denom1", &denom1 }, 101 { "denom2", &denom2 }, 102 { "axis_height", &axis_height }, 103 { "sup1", ¹ }, 104 { "sup2", ² }, 105 { "sup3", ³ }, 106 { "default_rule_thickness", &default_rule_thickness }, 107 { "sub1", &sub1 }, 108 { "sub2", &sub2 }, 109 { "sup_drop", &sup_drop }, 110 { "sub_drop", &sub_drop }, 111 { "x_height", &x_height }, 112 { "big_op_spacing1", &big_op_spacing1 }, 113 { "big_op_spacing2", &big_op_spacing2 }, 114 { "big_op_spacing3", &big_op_spacing3 }, 115 { "big_op_spacing4", &big_op_spacing4 }, 116 { "big_op_spacing5", &big_op_spacing5 }, 117 { "minimum_size", &minimum_size }, 118 { "baseline_sep", &baseline_sep }, 119 { "shift_down", &shift_down }, 120 { "column_sep", &column_sep }, 121 { "matrix_side_sep", &matrix_side_sep }, 122 { "draw_lines", &draw_flag }, 123 { "body_height", &body_height }, 124 { "body_depth", &body_depth }, 125 { "nroff", &nroff }, 126 { 0, 0 } 127}; 128 129void set_param(const char *name, int value) 130{ 131 for (int i = 0; param_table[i].name != 0; i++) 132 if (strcmp(param_table[i].name, name) == 0) { 133 *param_table[i].ptr = value; 134 return; 135 } 136 error("unrecognised parameter `%1'", name); 137} 138 139int script_style(int style) 140{ 141 return style > SCRIPT_STYLE ? style - 2 : style; 142} 143 144int cramped_style(int style) 145{ 146 return (style & 1) ? style - 1 : style; 147} 148 149void set_space(int n) 150{ 151 if (n < 0) 152 negative_space = -n; 153 else 154 positive_space = n; 155} 156 157// Return 0 if the specified size is bad. 158// The caller is responsible for giving the error message. 159 160int set_gsize(const char *s) 161{ 162 const char *p = (*s == '+' || *s == '-') ? s + 1 : s; 163 char *end; 164 long n = strtol(p, &end, 10); 165 if (n <= 0 || *end != '\0' || n > INT_MAX) 166 return 0; 167 if (p > s) { 168 if (!gsize) 169 gsize = 10; 170 if (*s == '+') { 171 if (gsize > INT_MAX - n) 172 return 0; 173 gsize += int(n); 174 } 175 else { 176 if (gsize - n <= 0) 177 return 0; 178 gsize -= int(n); 179 } 180 } 181 else 182 gsize = int(n); 183 return 1; 184} 185 186void set_script_reduction(int n) 187{ 188 script_size_reduction = n; 189} 190 191const char *get_gfont() 192{ 193 return gfont ? gfont : "I"; 194} 195 196const char *get_grfont() 197{ 198 return grfont ? grfont : "R"; 199} 200 201const char *get_gbfont() 202{ 203 return gbfont ? gbfont : "B"; 204} 205 206void set_gfont(const char *s) 207{ 208 a_delete gfont; 209 gfont = strsave(s); 210} 211 212void set_grfont(const char *s) 213{ 214 a_delete grfont; 215 grfont = strsave(s); 216} 217 218void set_gbfont(const char *s) 219{ 220 a_delete gbfont; 221 gbfont = strsave(s); 222} 223 224// this must be precisely 2 characters in length 225#define COMPATIBLE_REG "0C" 226 227void start_string() 228{ 229 printf(".nr " COMPATIBLE_REG " \\n(.C\n"); 230 printf(".cp 0\n"); 231 printf(".ds " LINE_STRING "\n"); 232} 233 234void output_string() 235{ 236 printf("\\*(" LINE_STRING "\n"); 237} 238 239void restore_compatibility() 240{ 241 printf(".cp \\n(" COMPATIBLE_REG "\n"); 242} 243 244void do_text(const char *s) 245{ 246 printf(".eo\n"); 247 printf(".as " LINE_STRING " \"%s\n", s); 248 printf(".ec\n"); 249} 250 251void set_minimum_size(int n) 252{ 253 minimum_size = n; 254} 255 256void set_script_size() 257{ 258 if (minimum_size < 0) 259 minimum_size = 0; 260 if (script_size_reduction >= 0) 261 printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size); 262 else 263 printf(".ps (u;\\n[.ps]*7+5/10>?%d)\n", minimum_size); 264} 265 266int box::next_uid = 0; 267 268box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++) 269{ 270} 271 272box::~box() 273{ 274} 275 276void box::top_level() 277{ 278 // debug_print(); 279 // putc('\n', stderr); 280 box *b = this; 281 printf(".nr " SAVED_FONT_REG " \\n[.f]\n"); 282 printf(".ft\n"); 283 printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n"); 284 printf(".ft %s\n", get_gfont()); 285 printf(".nr " SAVED_SIZE_REG " \\n[.ps]\n"); 286 if (gsize > 0) { 287 char buf[INT_DIGITS + 1]; 288 sprintf(buf, "%d", gsize); 289 b = new size_box(strsave(buf), b); 290 } 291 current_roman_font = get_grfont(); 292 // This catches tabs used within \Z (which aren't allowed). 293 b->check_tabs(0); 294 int r = b->compute_metrics(DISPLAY_STYLE); 295 printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n"); 296 printf(".ft \\n[" SAVED_FONT_REG "]\n"); 297 printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r); 298 if (r == FOUND_MARK) { 299 printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n"); 300 printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid); 301 } 302 else if (r == FOUND_LINEUP) 303 printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n[" 304 SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n"); 305 else 306 assert(r == FOUND_NOTHING); 307 // The problem here is that the argument to \f is read in copy mode, 308 // so we cannot use \E there; so we hide it in a string instead. 309 // Another problem is that if we use \R directly, then the space will 310 // prevent it working in a macro argument. 311 printf(".ds " SAVE_FONT_STRING " " 312 "\\R'" SAVED_INLINE_FONT_REG " \\\\n[.f]'" 313 "\\fP" 314 "\\R'" SAVED_INLINE_PREV_FONT_REG " \\\\n[.f]'" 315 "\\R'" SAVED_INLINE_SIZE_REG " \\\\n[.ps]'" 316 "\\s0" 317 "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.ps]'" 318 "\n" 319 ".ds " RESTORE_FONT_STRING " " 320 "\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]" 321 "\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]" 322 "\\s'\\\\n[" SAVED_INLINE_PREV_SIZE_REG "]u'" 323 "\\s'\\\\n[" SAVED_INLINE_SIZE_REG "]u'" 324 "\n"); 325 printf(".as1 " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]"); 326 printf("\\f[%s]", get_gfont()); 327 printf("\\s'\\En[" SAVED_SIZE_REG "]u'"); 328 current_roman_font = get_grfont(); 329 b->output(); 330 printf("\\E*[" RESTORE_FONT_STRING "]\n"); 331 if (r == FOUND_LINEUP) 332 printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n[" 333 MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n[" 334 WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n", 335 b->uid); 336 b->extra_space(); 337 if (!inline_flag) 338 printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n[" 339 DEPTH_FORMAT "]u-%dM>?0)\n", 340 b->uid, body_height, b->uid, body_depth); 341 delete b; 342 next_uid = 0; 343} 344 345// gpic defines this register so as to make geqn not produce `\x's 346#define EQN_NO_EXTRA_SPACE_REG "0x" 347 348void box::extra_space() 349{ 350 printf(".if !r" EQN_NO_EXTRA_SPACE_REG " " 351 ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); 352 if (positive_space >= 0 || negative_space >= 0) { 353 if (positive_space > 0) 354 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 355 ".as1 " LINE_STRING " \\x'-%dM'\n", positive_space); 356 if (negative_space > 0) 357 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 358 ".as1 " LINE_STRING " \\x'%dM'\n", negative_space); 359 positive_space = negative_space = -1; 360 } 361 else { 362 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 363 ".if \\n[" HEIGHT_FORMAT "]>%dM .as1 " LINE_STRING 364 " \\x'-(\\n[" HEIGHT_FORMAT 365 "]u-%dM)'\n", 366 uid, body_height, uid, body_height); 367 printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 368 ".if \\n[" DEPTH_FORMAT "]>%dM .as1 " LINE_STRING 369 " \\x'\\n[" DEPTH_FORMAT 370 "]u-%dM'\n", 371 uid, body_depth, uid, body_depth); 372 } 373} 374 375int box::compute_metrics(int) 376{ 377 printf(".nr " WIDTH_FORMAT " 0\n", uid); 378 printf(".nr " HEIGHT_FORMAT " 0\n", uid); 379 printf(".nr " DEPTH_FORMAT " 0\n", uid); 380 return FOUND_NOTHING; 381} 382 383void box::compute_subscript_kern() 384{ 385 printf(".nr " SUB_KERN_FORMAT " 0\n", uid); 386} 387 388void box::compute_skew() 389{ 390 printf(".nr " SKEW_FORMAT " 0\n", uid); 391} 392 393void box::output() 394{ 395} 396 397void box::check_tabs(int) 398{ 399} 400 401int box::is_char() 402{ 403 return 0; 404} 405 406int box::left_is_italic() 407{ 408 return 0; 409} 410 411int box::right_is_italic() 412{ 413 return 0; 414} 415 416void box::hint(unsigned) 417{ 418} 419 420void box::handle_char_type(int, int) 421{ 422} 423 424 425box_list::box_list(box *pp) 426{ 427 p = new box*[10]; 428 for (int i = 0; i < 10; i++) 429 p[i] = 0; 430 maxlen = 10; 431 len = 1; 432 p[0] = pp; 433} 434 435void box_list::append(box *pp) 436{ 437 if (len + 1 > maxlen) { 438 box **oldp = p; 439 maxlen *= 2; 440 p = new box*[maxlen]; 441 memcpy(p, oldp, sizeof(box*)*len); 442 a_delete oldp; 443 } 444 p[len++] = pp; 445} 446 447box_list::~box_list() 448{ 449 for (int i = 0; i < len; i++) 450 delete p[i]; 451 a_delete p; 452} 453 454void box_list::list_check_tabs(int level) 455{ 456 for (int i = 0; i < len; i++) 457 p[i]->check_tabs(level); 458} 459 460 461pointer_box::pointer_box(box *pp) : p(pp) 462{ 463 spacing_type = p->spacing_type; 464} 465 466pointer_box::~pointer_box() 467{ 468 delete p; 469} 470 471int pointer_box::compute_metrics(int style) 472{ 473 int r = p->compute_metrics(style); 474 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); 475 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); 476 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); 477 return r; 478} 479 480void pointer_box::compute_subscript_kern() 481{ 482 p->compute_subscript_kern(); 483 printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid); 484} 485 486void pointer_box::compute_skew() 487{ 488 p->compute_skew(); 489 printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n", 490 uid, p->uid); 491} 492 493void pointer_box::check_tabs(int level) 494{ 495 p->check_tabs(level); 496} 497 498int simple_box::compute_metrics(int) 499{ 500 printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid); 501 output(); 502 printf(DELIMITER_CHAR "\n"); 503 printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid); 504 printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid); 505 printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid); 506 printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid); 507 return FOUND_NOTHING; 508} 509 510void simple_box::compute_subscript_kern() 511{ 512 // do nothing, we already computed it in do_metrics 513} 514 515void simple_box::compute_skew() 516{ 517 // do nothing, we already computed it in do_metrics 518} 519 520int box::is_simple() 521{ 522 return 0; 523} 524 525int simple_box::is_simple() 526{ 527 return 1; 528} 529 530quoted_text_box::quoted_text_box(char *s) : text(s) 531{ 532} 533 534quoted_text_box::~quoted_text_box() 535{ 536 a_delete text; 537} 538 539void quoted_text_box::output() 540{ 541 if (text) 542 fputs(text, stdout); 543} 544 545tab_box::tab_box() : disabled(0) 546{ 547} 548 549// We treat a tab_box as having width 0 for width computations. 550 551void tab_box::output() 552{ 553 if (!disabled) 554 printf("\\t"); 555} 556 557void tab_box::check_tabs(int level) 558{ 559 if (level > 0) { 560 error("tabs allowed only at outermost level"); 561 disabled = 1; 562 } 563} 564 565space_box::space_box() 566{ 567 spacing_type = SUPPRESS_TYPE; 568} 569 570void space_box::output() 571{ 572 printf("\\h'%dM'", thick_space); 573} 574 575half_space_box::half_space_box() 576{ 577 spacing_type = SUPPRESS_TYPE; 578} 579 580void half_space_box::output() 581{ 582 printf("\\h'%dM'", thin_space); 583} 584 585void box_list::list_debug_print(const char *sep) 586{ 587 p[0]->debug_print(); 588 for (int i = 1; i < len; i++) { 589 fprintf(stderr, "%s", sep); 590 p[i]->debug_print(); 591 } 592} 593 594void quoted_text_box::debug_print() 595{ 596 fprintf(stderr, "\"%s\"", (text ? text : "")); 597} 598 599void half_space_box::debug_print() 600{ 601 fprintf(stderr, "^"); 602} 603 604void space_box::debug_print() 605{ 606 fprintf(stderr, "~"); 607} 608 609void tab_box::debug_print() 610{ 611 fprintf(stderr, "<tab>"); 612} 613