1 /* $NetBSD: text.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $ */ 2 3 // -*- C++ -*- 4 /* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc. 5 Written by James Clark (jjc@jclark.com) 6 7 This file is part of groff. 8 9 groff is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free 11 Software Foundation; either version 2, or (at your option) any later 12 version. 13 14 groff is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 for more details. 18 19 You should have received a copy of the GNU General Public License along 20 with groff; see the file COPYING. If not, write to the Free Software 21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 22 23 #include "eqn.h" 24 #include "pbox.h" 25 #include "ptable.h" 26 27 class char_box : public simple_box { 28 unsigned char c; 29 char next_is_italic; 30 char prev_is_italic; 31 public: 32 char_box(unsigned char); 33 void debug_print(); 34 void output(); 35 int is_char(); 36 int left_is_italic(); 37 int right_is_italic(); 38 void hint(unsigned); 39 void handle_char_type(int, int); 40 }; 41 42 class special_char_box : public simple_box { 43 char *s; 44 public: 45 special_char_box(const char *); 46 ~special_char_box(); 47 void output(); 48 void debug_print(); 49 int is_char(); 50 void handle_char_type(int, int); 51 }; 52 53 const char *spacing_type_table[] = { 54 "ordinary", 55 "operator", 56 "binary", 57 "relation", 58 "opening", 59 "closing", 60 "punctuation", 61 "inner", 62 "suppress", 63 0, 64 }; 65 66 const int DIGIT_TYPE = 0; 67 const int LETTER_TYPE = 1; 68 69 const char *font_type_table[] = { 70 "digit", 71 "letter", 72 0, 73 }; 74 75 struct char_info { 76 int spacing_type; 77 int font_type; 78 char_info(); 79 }; 80 81 char_info::char_info() 82 : spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE) 83 { 84 } 85 86 static char_info char_table[256]; 87 88 declare_ptable(char_info) 89 implement_ptable(char_info) 90 91 PTABLE(char_info) special_char_table; 92 93 static int get_special_char_spacing_type(const char *ch) 94 { 95 char_info *p = special_char_table.lookup(ch); 96 return p ? p->spacing_type : ORDINARY_TYPE; 97 } 98 99 static int get_special_char_font_type(const char *ch) 100 { 101 char_info *p = special_char_table.lookup(ch); 102 return p ? p->font_type : DIGIT_TYPE; 103 } 104 105 static void set_special_char_type(const char *ch, int st, int ft) 106 { 107 char_info *p = special_char_table.lookup(ch); 108 if (!p) { 109 p = new char_info[1]; 110 special_char_table.define(ch, p); 111 } 112 if (st >= 0) 113 p->spacing_type = st; 114 if (ft >= 0) 115 p->font_type = ft; 116 } 117 118 void init_char_table() 119 { 120 set_special_char_type("pl", 2, -1); // binary 121 set_special_char_type("mi", 2, -1); 122 set_special_char_type("eq", 3, -1); // relation 123 set_special_char_type("<=", 3, -1); 124 set_special_char_type(">=", 3, -1); 125 char_table['}'].spacing_type = 5; // closing 126 char_table[')'].spacing_type = 5; 127 char_table[']'].spacing_type = 5; 128 char_table['{'].spacing_type = 4; // opening 129 char_table['('].spacing_type = 4; 130 char_table['['].spacing_type = 4; 131 char_table[','].spacing_type = 6; // punctuation 132 char_table[';'].spacing_type = 6; 133 char_table[':'].spacing_type = 6; 134 char_table['.'].spacing_type = 6; 135 char_table['>'].spacing_type = 3; 136 char_table['<'].spacing_type = 3; 137 char_table['*'].spacing_type = 2; // binary 138 for (int i = 0; i < 256; i++) 139 if (csalpha(i)) 140 char_table[i].font_type = LETTER_TYPE; 141 } 142 143 static int lookup_spacing_type(const char *type) 144 { 145 for (int i = 0; spacing_type_table[i] != 0; i++) 146 if (strcmp(spacing_type_table[i], type) == 0) 147 return i; 148 return -1; 149 } 150 151 static int lookup_font_type(const char *type) 152 { 153 for (int i = 0; font_type_table[i] != 0; i++) 154 if (strcmp(font_type_table[i], type) == 0) 155 return i; 156 return -1; 157 } 158 159 void box::set_spacing_type(char *type) 160 { 161 int t = lookup_spacing_type(type); 162 if (t < 0) 163 error("unrecognised type `%1'", type); 164 else 165 spacing_type = t; 166 a_delete type; 167 } 168 169 char_box::char_box(unsigned char cc) 170 : c(cc), next_is_italic(0), prev_is_italic(0) 171 { 172 spacing_type = char_table[c].spacing_type; 173 } 174 175 void char_box::hint(unsigned flags) 176 { 177 if (flags & HINT_PREV_IS_ITALIC) 178 prev_is_italic = 1; 179 if (flags & HINT_NEXT_IS_ITALIC) 180 next_is_italic = 1; 181 } 182 183 void char_box::output() 184 { 185 int font_type = char_table[c].font_type; 186 if (font_type != LETTER_TYPE) 187 printf("\\f[%s]", current_roman_font); 188 if (!prev_is_italic) 189 fputs("\\,", stdout); 190 if (c == '\\') 191 fputs("\\e", stdout); 192 else 193 putchar(c); 194 if (!next_is_italic) 195 fputs("\\/", stdout); 196 else 197 fputs("\\&", stdout); // suppress ligaturing and kerning 198 if (font_type != LETTER_TYPE) 199 fputs("\\fP", stdout); 200 } 201 202 int char_box::left_is_italic() 203 { 204 int font_type = char_table[c].font_type; 205 return font_type == LETTER_TYPE; 206 } 207 208 int char_box::right_is_italic() 209 { 210 int font_type = char_table[c].font_type; 211 return font_type == LETTER_TYPE; 212 } 213 214 int char_box::is_char() 215 { 216 return 1; 217 } 218 219 void char_box::debug_print() 220 { 221 if (c == '\\') { 222 putc('\\', stderr); 223 putc('\\', stderr); 224 } 225 else 226 putc(c, stderr); 227 } 228 229 special_char_box::special_char_box(const char *t) 230 { 231 s = strsave(t); 232 spacing_type = get_special_char_spacing_type(s); 233 } 234 235 special_char_box::~special_char_box() 236 { 237 a_delete s; 238 } 239 240 void special_char_box::output() 241 { 242 int font_type = get_special_char_font_type(s); 243 if (font_type != LETTER_TYPE) 244 printf("\\f[%s]", current_roman_font); 245 printf("\\,\\[%s]\\/", s); 246 if (font_type != LETTER_TYPE) 247 printf("\\fP"); 248 } 249 250 int special_char_box::is_char() 251 { 252 return 1; 253 } 254 255 void special_char_box::debug_print() 256 { 257 fprintf(stderr, "\\[%s]", s); 258 } 259 260 261 void char_box::handle_char_type(int st, int ft) 262 { 263 if (st >= 0) 264 char_table[c].spacing_type = st; 265 if (ft >= 0) 266 char_table[c].font_type = ft; 267 } 268 269 void special_char_box::handle_char_type(int st, int ft) 270 { 271 set_special_char_type(s, st, ft); 272 } 273 274 void set_char_type(const char *type, char *ch) 275 { 276 assert(ch != 0); 277 int st = lookup_spacing_type(type); 278 int ft = lookup_font_type(type); 279 if (st < 0 && ft < 0) { 280 error("bad character type `%1'", type); 281 a_delete ch; 282 return; 283 } 284 box *b = split_text(ch); 285 b->handle_char_type(st, ft); 286 delete b; 287 } 288 289 /* We give primes special treatment so that in ``x' sub 2'', the ``2'' 290 will be tucked under the prime */ 291 292 class prime_box : public pointer_box { 293 box *pb; 294 public: 295 prime_box(box *); 296 ~prime_box(); 297 int compute_metrics(int style); 298 void output(); 299 void compute_subscript_kern(); 300 void debug_print(); 301 void handle_char_type(int, int); 302 }; 303 304 box *make_prime_box(box *pp) 305 { 306 return new prime_box(pp); 307 } 308 309 prime_box::prime_box(box *pp) : pointer_box(pp) 310 { 311 pb = new special_char_box("fm"); 312 } 313 314 prime_box::~prime_box() 315 { 316 delete pb; 317 } 318 319 int prime_box::compute_metrics(int style) 320 { 321 int res = p->compute_metrics(style); 322 pb->compute_metrics(style); 323 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]" 324 "+\\n[" WIDTH_FORMAT "]\n", 325 uid, p->uid, pb->uid); 326 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" 327 ">?\\n[" HEIGHT_FORMAT "]\n", 328 uid, p->uid, pb->uid); 329 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" 330 ">?\\n[" DEPTH_FORMAT "]\n", 331 uid, p->uid, pb->uid); 332 return res; 333 } 334 335 void prime_box::compute_subscript_kern() 336 { 337 p->compute_subscript_kern(); 338 printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]" 339 "+\\n[" SUB_KERN_FORMAT "]>?0\n", 340 uid, pb->uid, p->uid); 341 } 342 343 void prime_box::output() 344 { 345 p->output(); 346 pb->output(); 347 } 348 349 void prime_box::handle_char_type(int st, int ft) 350 { 351 p->handle_char_type(st, ft); 352 pb->handle_char_type(st, ft); 353 } 354 355 void prime_box::debug_print() 356 { 357 p->debug_print(); 358 putc('\'', stderr); 359 } 360 361 box *split_text(char *text) 362 { 363 list_box *lb = 0; 364 box *fb = 0; 365 char *s = text; 366 while (*s != '\0') { 367 char c = *s++; 368 box *b = 0; 369 switch (c) { 370 case '+': 371 b = new special_char_box("pl"); 372 break; 373 case '-': 374 b = new special_char_box("mi"); 375 break; 376 case '=': 377 b = new special_char_box("eq"); 378 break; 379 case '\'': 380 b = new special_char_box("fm"); 381 break; 382 case '<': 383 if (*s == '=') { 384 b = new special_char_box("<="); 385 s++; 386 break; 387 } 388 goto normal_char; 389 case '>': 390 if (*s == '=') { 391 b = new special_char_box(">="); 392 s++; 393 break; 394 } 395 goto normal_char; 396 case '\\': 397 if (*s == '\0') { 398 lex_error("bad escape"); 399 break; 400 } 401 c = *s++; 402 switch (c) { 403 case '(': 404 { 405 char buf[3]; 406 if (*s != '\0') { 407 buf[0] = *s++; 408 if (*s != '\0') { 409 buf[1] = *s++; 410 buf[2] = '\0'; 411 b = new special_char_box(buf); 412 } 413 else { 414 lex_error("bad escape"); 415 } 416 } 417 else { 418 lex_error("bad escape"); 419 } 420 } 421 break; 422 case '[': 423 { 424 char *ch = s; 425 while (*s != ']' && *s != '\0') 426 s++; 427 if (*s == '\0') 428 lex_error("bad escape"); 429 else { 430 *s++ = '\0'; 431 b = new special_char_box(ch); 432 } 433 } 434 break; 435 case 'f': 436 case 'g': 437 case 'k': 438 case 'n': 439 case '*': 440 { 441 char *escape_start = s - 2; 442 switch (*s) { 443 case '(': 444 if (*++s != '\0') 445 ++s; 446 break; 447 case '[': 448 for (++s; *s != '\0' && *s != ']'; s++) 449 ; 450 break; 451 } 452 if (*s == '\0') 453 lex_error("bad escape"); 454 else { 455 ++s; 456 char *buf = new char[s - escape_start + 1]; 457 memcpy(buf, escape_start, s - escape_start); 458 buf[s - escape_start] = '\0'; 459 b = new quoted_text_box(buf); 460 } 461 } 462 break; 463 case '-': 464 case '_': 465 { 466 char buf[2]; 467 buf[0] = c; 468 buf[1] = '\0'; 469 b = new special_char_box(buf); 470 } 471 break; 472 case '`': 473 b = new special_char_box("ga"); 474 break; 475 case '\'': 476 b = new special_char_box("aa"); 477 break; 478 case 'e': 479 case '\\': 480 b = new char_box('\\'); 481 break; 482 case '^': 483 case '|': 484 case '0': 485 { 486 char buf[3]; 487 buf[0] = '\\'; 488 buf[1] = c; 489 buf[2] = '\0'; 490 b = new quoted_text_box(strsave(buf)); 491 break; 492 } 493 default: 494 lex_error("unquoted escape"); 495 b = new quoted_text_box(strsave(s - 2)); 496 s = strchr(s, '\0'); 497 break; 498 } 499 break; 500 default: 501 normal_char: 502 b = new char_box(c); 503 break; 504 } 505 while (*s == '\'') { 506 if (b == 0) 507 b = new quoted_text_box(0); 508 b = new prime_box(b); 509 s++; 510 } 511 if (b != 0) { 512 if (lb != 0) 513 lb->append(b); 514 else if (fb != 0) { 515 lb = new list_box(fb); 516 lb->append(b); 517 } 518 else 519 fb = b; 520 } 521 } 522 a_delete text; 523 if (lb != 0) 524 return lb; 525 else if (fb != 0) 526 return fb; 527 else 528 return new quoted_text_box(0); 529 } 530 531