/contrib/groff/src/roff/troff/env.cpp
C++ | 3893 lines | 3496 code | 263 blank | 134 comment | 897 complexity | c07b344fa412b4f06e5daa000c8d6c87 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 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 "troff.h" 23#include "dictionary.h" 24#include "hvunits.h" 25#include "stringclass.h" 26#include "mtsm.h" 27#include "env.h" 28#include "request.h" 29#include "node.h" 30#include "token.h" 31#include "div.h" 32#include "reg.h" 33#include "charinfo.h" 34#include "macropath.h" 35#include "input.h" 36#include <math.h> 37 38symbol default_family("T"); 39 40enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 }; 41 42enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 }; 43 44struct env_list { 45 environment *env; 46 env_list *next; 47 env_list(environment *e, env_list *p) : env(e), next(p) {} 48}; 49 50env_list *env_stack; 51const int NENVIRONMENTS = 10; 52environment *env_table[NENVIRONMENTS]; 53dictionary env_dictionary(10); 54environment *curenv; 55static int next_line_number = 0; 56extern int suppress_push; 57extern statem *get_diversion_state(); 58 59charinfo *field_delimiter_char; 60charinfo *padding_indicator_char; 61 62int translate_space_to_dummy = 0; 63 64class pending_output_line { 65 node *nd; 66 int no_fill; 67 int was_centered; 68 vunits vs; 69 vunits post_vs; 70 hunits width; 71#ifdef WIDOW_CONTROL 72 int last_line; // Is it the last line of the paragraph? 73#endif /* WIDOW_CONTROL */ 74public: 75 pending_output_line *next; 76 77 pending_output_line(node *, int, vunits, vunits, hunits, int, 78 pending_output_line * = 0); 79 ~pending_output_line(); 80 int output(); 81 82#ifdef WIDOW_CONTROL 83 friend void environment::mark_last_line(); 84 friend void environment::output(node *, int, vunits, vunits, hunits, int); 85#endif /* WIDOW_CONTROL */ 86}; 87 88pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv, 89 hunits w, int ce, 90 pending_output_line *p) 91: nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w), 92#ifdef WIDOW_CONTROL 93 last_line(0), 94#endif /* WIDOW_CONTROL */ 95 next(p) 96{ 97} 98 99pending_output_line::~pending_output_line() 100{ 101 delete_node_list(nd); 102} 103 104int pending_output_line::output() 105{ 106 if (trap_sprung_flag) 107 return 0; 108#ifdef WIDOW_CONTROL 109 if (next && next->last_line && !no_fill) { 110 curdiv->need(vs + post_vs + vunits(vresolution)); 111 if (trap_sprung_flag) { 112 next->last_line = 0; // Try to avoid infinite loops. 113 return 0; 114 } 115 } 116#endif 117 curenv->construct_format_state(nd, was_centered, !no_fill); 118 curdiv->output(nd, no_fill, vs, post_vs, width); 119 nd = 0; 120 return 1; 121} 122 123void environment::output(node *nd, int no_fill_flag, 124 vunits vs, vunits post_vs, 125 hunits width, int was_centered) 126{ 127#ifdef WIDOW_CONTROL 128 while (pending_lines) { 129 if (widow_control && !pending_lines->no_fill && !pending_lines->next) 130 break; 131 if (!pending_lines->output()) 132 break; 133 pending_output_line *tem = pending_lines; 134 pending_lines = pending_lines->next; 135 delete tem; 136 } 137#else /* WIDOW_CONTROL */ 138 output_pending_lines(); 139#endif /* WIDOW_CONTROL */ 140 if (!trap_sprung_flag && !pending_lines 141#ifdef WIDOW_CONTROL 142 && (!widow_control || no_fill_flag) 143#endif /* WIDOW_CONTROL */ 144 ) { 145 curenv->construct_format_state(nd, was_centered, !no_fill_flag); 146 curdiv->output(nd, no_fill_flag, vs, post_vs, width); 147 } else { 148 pending_output_line **p; 149 for (p = &pending_lines; *p; p = &(*p)->next) 150 ; 151 *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width, 152 was_centered); 153 } 154} 155 156// a line from .tl goes at the head of the queue 157 158void environment::output_title(node *nd, int no_fill_flag, 159 vunits vs, vunits post_vs, 160 hunits width) 161{ 162 if (!trap_sprung_flag) 163 curdiv->output(nd, no_fill_flag, vs, post_vs, width); 164 else 165 pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs, 166 width, 0, pending_lines); 167} 168 169void environment::output_pending_lines() 170{ 171 while (pending_lines && pending_lines->output()) { 172 pending_output_line *tem = pending_lines; 173 pending_lines = pending_lines->next; 174 delete tem; 175 } 176} 177 178#ifdef WIDOW_CONTROL 179 180void environment::mark_last_line() 181{ 182 if (!widow_control || !pending_lines) 183 return; 184 pending_output_line *p; 185 for (p = pending_lines; p->next; p = p->next) 186 ; 187 if (!p->no_fill) 188 p->last_line = 1; 189} 190 191void widow_control_request() 192{ 193 int n; 194 if (has_arg() && get_integer(&n)) 195 curenv->widow_control = n != 0; 196 else 197 curenv->widow_control = 1; 198 skip_line(); 199} 200 201#endif /* WIDOW_CONTROL */ 202 203/* font_size functions */ 204 205size_range *font_size::size_table = 0; 206int font_size::nranges = 0; 207 208extern "C" { 209 210int compare_ranges(const void *p1, const void *p2) 211{ 212 return ((size_range *)p1)->min - ((size_range *)p2)->min; 213} 214 215} 216 217void font_size::init_size_table(int *sizes) 218{ 219 nranges = 0; 220 while (sizes[nranges*2] != 0) 221 nranges++; 222 assert(nranges > 0); 223 size_table = new size_range[nranges]; 224 for (int i = 0; i < nranges; i++) { 225 size_table[i].min = sizes[i*2]; 226 size_table[i].max = sizes[i*2 + 1]; 227 } 228 qsort(size_table, nranges, sizeof(size_range), compare_ranges); 229} 230 231font_size::font_size(int sp) 232{ 233 for (int i = 0; i < nranges; i++) { 234 if (sp < size_table[i].min) { 235 if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max) 236 p = size_table[i - 1].max; 237 else 238 p = size_table[i].min; 239 return; 240 } 241 if (sp <= size_table[i].max) { 242 p = sp; 243 return; 244 } 245 } 246 p = size_table[nranges - 1].max; 247} 248 249int font_size::to_units() 250{ 251 return scale(p, units_per_inch, sizescale*72); 252} 253 254// we can't do this in a static constructor because various dictionaries 255// have to get initialized first 256 257void init_environments() 258{ 259 curenv = env_table[0] = new environment("0"); 260} 261 262void tab_character() 263{ 264 curenv->tab_char = get_optional_char(); 265 skip_line(); 266} 267 268void leader_character() 269{ 270 curenv->leader_char = get_optional_char(); 271 skip_line(); 272} 273 274void environment::add_char(charinfo *ci) 275{ 276 int s; 277 node *gc_np = 0; 278 if (interrupted) 279 ; 280 // don't allow fields in dummy environments 281 else if (ci == field_delimiter_char && !dummy) { 282 if (current_field) 283 wrap_up_field(); 284 else 285 start_field(); 286 } 287 else if (current_field && ci == padding_indicator_char) 288 add_padding(); 289 else if (current_tab) { 290 if (tab_contents == 0) 291 tab_contents = new line_start_node; 292 if (ci != hyphen_indicator_char) 293 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np); 294 else 295 tab_contents = tab_contents->add_discretionary_hyphen(); 296 } 297 else { 298 if (line == 0) 299 start_line(); 300#if 0 301 fprintf(stderr, "current line is\n"); 302 line->debug_node_list(); 303#endif 304 if (ci != hyphen_indicator_char) 305 line = line->add_char(ci, this, &width_total, &space_total, &gc_np); 306 else 307 line = line->add_discretionary_hyphen(); 308 } 309#if 0 310 fprintf(stderr, "now after we have added character the line is\n"); 311 line->debug_node_list(); 312#endif 313 if ((!suppress_push) && gc_np) { 314 if (gc_np && (gc_np->state == 0)) { 315 gc_np->state = construct_state(0); 316 gc_np->push_state = get_diversion_state(); 317 } 318 else if (line && (line->state == 0)) { 319 line->state = construct_state(0); 320 line->push_state = get_diversion_state(); 321 } 322 } 323#if 0 324 fprintf(stderr, "now we have possibly added the state the line is\n"); 325 line->debug_node_list(); 326#endif 327} 328 329node *environment::make_char_node(charinfo *ci) 330{ 331 return make_node(ci, this); 332} 333 334void environment::add_node(node *n) 335{ 336 if (n == 0) 337 return; 338 if (!suppress_push) { 339 if (n->is_special && n->state == NULL) 340 n->state = construct_state(0); 341 n->push_state = get_diversion_state(); 342 } 343 344 if (current_tab || current_field) 345 n->freeze_space(); 346 if (interrupted) { 347 delete n; 348 } 349 else if (current_tab) { 350 n->next = tab_contents; 351 tab_contents = n; 352 tab_width += n->width(); 353 } 354 else { 355 if (line == 0) { 356 if (discarding && n->discardable()) { 357 // XXX possibly: input_line_start -= n->width(); 358 delete n; 359 return; 360 } 361 start_line(); 362 } 363 width_total += n->width(); 364 space_total += n->nspaces(); 365 n->next = line; 366 line = n; 367 construct_new_line_state(line); 368 } 369} 370 371void environment::add_hyphen_indicator() 372{ 373 if (current_tab || interrupted || current_field 374 || hyphen_indicator_char != 0) 375 return; 376 if (line == 0) 377 start_line(); 378 line = line->add_discretionary_hyphen(); 379} 380 381int environment::get_hyphenation_flags() 382{ 383 return hyphenation_flags; 384} 385 386int environment::get_hyphen_line_max() 387{ 388 return hyphen_line_max; 389} 390 391int environment::get_hyphen_line_count() 392{ 393 return hyphen_line_count; 394} 395 396int environment::get_center_lines() 397{ 398 return center_lines; 399} 400 401int environment::get_right_justify_lines() 402{ 403 return right_justify_lines; 404} 405 406void environment::add_italic_correction() 407{ 408 if (current_tab) { 409 if (tab_contents) 410 tab_contents = tab_contents->add_italic_correction(&tab_width); 411 } 412 else if (line) 413 line = line->add_italic_correction(&width_total); 414} 415 416void environment::space_newline() 417{ 418 assert(!current_tab && !current_field); 419 if (interrupted) 420 return; 421 hunits x = H0; 422 hunits sw = env_space_width(this); 423 hunits ssw = env_sentence_space_width(this); 424 if (!translate_space_to_dummy) { 425 x = sw; 426 if (node_list_ends_sentence(line) == 1) 427 x += ssw; 428 } 429 width_list *w = new width_list(sw, ssw); 430 if (node_list_ends_sentence(line) == 1) 431 w->next = new width_list(sw, ssw); 432 if (line != 0 && line->merge_space(x, sw, ssw)) { 433 width_total += x; 434 return; 435 } 436 add_node(new word_space_node(x, get_fill_color(), w)); 437 possibly_break_line(0, spread_flag); 438 spread_flag = 0; 439} 440 441void environment::space() 442{ 443 space(env_space_width(this), env_sentence_space_width(this)); 444} 445 446void environment::space(hunits space_width, hunits sentence_space_width) 447{ 448 if (interrupted) 449 return; 450 if (current_field && padding_indicator_char == 0) { 451 add_padding(); 452 return; 453 } 454 hunits x = translate_space_to_dummy ? H0 : space_width; 455 node *p = current_tab ? tab_contents : line; 456 hunits *tp = current_tab ? &tab_width : &width_total; 457 if (p && p->nspaces() == 1 && p->width() == x 458 && node_list_ends_sentence(p->next) == 1) { 459 hunits xx = translate_space_to_dummy ? H0 : sentence_space_width; 460 if (p->merge_space(xx, space_width, sentence_space_width)) { 461 *tp += xx; 462 return; 463 } 464 } 465 if (p && p->merge_space(x, space_width, sentence_space_width)) { 466 *tp += x; 467 return; 468 } 469 add_node(new word_space_node(x, 470 get_fill_color(), 471 new width_list(space_width, 472 sentence_space_width))); 473 possibly_break_line(0, spread_flag); 474 spread_flag = 0; 475} 476 477node *do_underline_special(int); 478 479void environment::set_font(symbol nm) 480{ 481 if (interrupted) 482 return; 483 if (nm == symbol("P") || nm.is_empty()) { 484 if (family->make_definite(prev_fontno) < 0) 485 return; 486 int tem = fontno; 487 fontno = prev_fontno; 488 prev_fontno = tem; 489 } 490 else { 491 prev_fontno = fontno; 492 int n = symbol_fontno(nm); 493 if (n < 0) { 494 n = next_available_font_position(); 495 if (!mount_font(n, nm)) 496 return; 497 } 498 if (family->make_definite(n) < 0) 499 return; 500 fontno = n; 501 } 502 if (underline_spaces && fontno != prev_fontno) { 503 if (fontno == get_underline_fontno()) 504 add_node(do_underline_special(1)); 505 if (prev_fontno == get_underline_fontno()) 506 add_node(do_underline_special(0)); 507 } 508} 509 510void environment::set_font(int n) 511{ 512 if (interrupted) 513 return; 514 if (is_good_fontno(n)) { 515 prev_fontno = fontno; 516 fontno = n; 517 } 518 else 519 warning(WARN_FONT, "bad font number"); 520} 521 522void environment::set_family(symbol fam) 523{ 524 if (interrupted) 525 return; 526 if (fam.is_null() || fam.is_empty()) { 527 if (prev_family->make_definite(fontno) < 0) 528 return; 529 font_family *tem = family; 530 family = prev_family; 531 prev_family = tem; 532 } 533 else { 534 font_family *f = lookup_family(fam); 535 if (f->make_definite(fontno) < 0) 536 return; 537 prev_family = family; 538 family = f; 539 } 540} 541 542void environment::set_size(int n) 543{ 544 if (interrupted) 545 return; 546 if (n == 0) { 547 font_size temp = prev_size; 548 prev_size = size; 549 size = temp; 550 int temp2 = prev_requested_size; 551 prev_requested_size = requested_size; 552 requested_size = temp2; 553 } 554 else { 555 prev_size = size; 556 size = font_size(n); 557 prev_requested_size = requested_size; 558 requested_size = n; 559 } 560} 561 562void environment::set_char_height(int n) 563{ 564 if (interrupted) 565 return; 566 if (n == requested_size || n <= 0) 567 char_height = 0; 568 else 569 char_height = n; 570} 571 572void environment::set_char_slant(int n) 573{ 574 if (interrupted) 575 return; 576 char_slant = n; 577} 578 579color *environment::get_prev_glyph_color() 580{ 581 return prev_glyph_color; 582} 583 584color *environment::get_glyph_color() 585{ 586 return glyph_color; 587} 588 589color *environment::get_prev_fill_color() 590{ 591 return prev_fill_color; 592} 593 594color *environment::get_fill_color() 595{ 596 return fill_color; 597} 598 599void environment::set_glyph_color(color *c) 600{ 601 if (interrupted) 602 return; 603 curenv->prev_glyph_color = curenv->glyph_color; 604 curenv->glyph_color = c; 605} 606 607void environment::set_fill_color(color *c) 608{ 609 if (interrupted) 610 return; 611 curenv->prev_fill_color = curenv->fill_color; 612 curenv->fill_color = c; 613} 614 615environment::environment(symbol nm) 616: dummy(0), 617 prev_line_length((units_per_inch*13)/2), 618 line_length((units_per_inch*13)/2), 619 prev_title_length((units_per_inch*13)/2), 620 title_length((units_per_inch*13)/2), 621 prev_size(sizescale*10), 622 size(sizescale*10), 623 requested_size(sizescale*10), 624 prev_requested_size(sizescale*10), 625 char_height(0), 626 char_slant(0), 627 space_size(12), 628 sentence_space_size(12), 629 adjust_mode(ADJUST_BOTH), 630 fill(1), 631 interrupted(0), 632 prev_line_interrupted(0), 633 center_lines(0), 634 right_justify_lines(0), 635 prev_vertical_spacing(points_to_units(12)), 636 vertical_spacing(points_to_units(12)), 637 prev_post_vertical_spacing(0), 638 post_vertical_spacing(0), 639 prev_line_spacing(1), 640 line_spacing(1), 641 prev_indent(0), 642 indent(0), 643 temporary_indent(0), 644 have_temporary_indent(0), 645 underline_lines(0), 646 underline_spaces(0), 647 input_trap_count(0), 648 continued_input_trap(0), 649 line(0), 650 prev_text_length(0), 651 width_total(0), 652 space_total(0), 653 input_line_start(0), 654 line_tabs(0), 655 current_tab(TAB_NONE), 656 leader_node(0), 657 tab_char(0), 658 leader_char(charset_table['.']), 659 current_field(0), 660 discarding(0), 661 spread_flag(0), 662 margin_character_flags(0), 663 margin_character_node(0), 664 margin_character_distance(points_to_units(10)), 665 numbering_nodes(0), 666 number_text_separation(1), 667 line_number_indent(0), 668 line_number_multiple(1), 669 no_number_count(0), 670 hyphenation_flags(1), 671 hyphen_line_count(0), 672 hyphen_line_max(-1), 673 hyphenation_space(H0), 674 hyphenation_margin(H0), 675 composite(0), 676 pending_lines(0), 677#ifdef WIDOW_CONTROL 678 widow_control(0), 679#endif /* WIDOW_CONTROL */ 680 glyph_color(&default_color), 681 prev_glyph_color(&default_color), 682 fill_color(&default_color), 683 prev_fill_color(&default_color), 684 seen_space(0), 685 seen_eol(0), 686 suppress_next_eol(0), 687 seen_break(0), 688 tabs(units_per_inch/2, TAB_LEFT), 689 name(nm), 690 control_char('.'), 691 no_break_control_char('\''), 692 hyphen_indicator_char(0) 693{ 694 prev_family = family = lookup_family(default_family); 695 prev_fontno = fontno = 1; 696 if (!is_good_fontno(1)) 697 fatal("font number 1 not a valid font"); 698 if (family->make_definite(1) < 0) 699 fatal("invalid default family `%1'", default_family.contents()); 700 prev_fontno = fontno; 701} 702 703environment::environment(const environment *e) 704: dummy(1), 705 prev_line_length(e->prev_line_length), 706 line_length(e->line_length), 707 prev_title_length(e->prev_title_length), 708 title_length(e->title_length), 709 prev_size(e->prev_size), 710 size(e->size), 711 requested_size(e->requested_size), 712 prev_requested_size(e->prev_requested_size), 713 char_height(e->char_height), 714 char_slant(e->char_slant), 715 prev_fontno(e->prev_fontno), 716 fontno(e->fontno), 717 prev_family(e->prev_family), 718 family(e->family), 719 space_size(e->space_size), 720 sentence_space_size(e->sentence_space_size), 721 adjust_mode(e->adjust_mode), 722 fill(e->fill), 723 interrupted(0), 724 prev_line_interrupted(0), 725 center_lines(0), 726 right_justify_lines(0), 727 prev_vertical_spacing(e->prev_vertical_spacing), 728 vertical_spacing(e->vertical_spacing), 729 prev_post_vertical_spacing(e->prev_post_vertical_spacing), 730 post_vertical_spacing(e->post_vertical_spacing), 731 prev_line_spacing(e->prev_line_spacing), 732 line_spacing(e->line_spacing), 733 prev_indent(e->prev_indent), 734 indent(e->indent), 735 temporary_indent(0), 736 have_temporary_indent(0), 737 underline_lines(0), 738 underline_spaces(0), 739 input_trap_count(0), 740 continued_input_trap(0), 741 line(0), 742 prev_text_length(e->prev_text_length), 743 width_total(0), 744 space_total(0), 745 input_line_start(0), 746 line_tabs(e->line_tabs), 747 current_tab(TAB_NONE), 748 leader_node(0), 749 tab_char(e->tab_char), 750 leader_char(e->leader_char), 751 current_field(0), 752 discarding(0), 753 spread_flag(0), 754 margin_character_flags(e->margin_character_flags), 755 margin_character_node(e->margin_character_node), 756 margin_character_distance(e->margin_character_distance), 757 numbering_nodes(0), 758 number_text_separation(e->number_text_separation), 759 line_number_indent(e->line_number_indent), 760 line_number_multiple(e->line_number_multiple), 761 no_number_count(e->no_number_count), 762 hyphenation_flags(e->hyphenation_flags), 763 hyphen_line_count(0), 764 hyphen_line_max(e->hyphen_line_max), 765 hyphenation_space(e->hyphenation_space), 766 hyphenation_margin(e->hyphenation_margin), 767 composite(0), 768 pending_lines(0), 769#ifdef WIDOW_CONTROL 770 widow_control(e->widow_control), 771#endif /* WIDOW_CONTROL */ 772 glyph_color(e->glyph_color), 773 prev_glyph_color(e->prev_glyph_color), 774 fill_color(e->fill_color), 775 prev_fill_color(e->prev_fill_color), 776 seen_space(e->seen_space), 777 seen_eol(e->seen_eol), 778 suppress_next_eol(e->suppress_next_eol), 779 seen_break(e->seen_break), 780 tabs(e->tabs), 781 name(e->name), // so that eg `.if "\n[.ev]"0"' works 782 control_char(e->control_char), 783 no_break_control_char(e->no_break_control_char), 784 hyphen_indicator_char(e->hyphen_indicator_char) 785{ 786} 787 788void environment::copy(const environment *e) 789{ 790 prev_line_length = e->prev_line_length; 791 line_length = e->line_length; 792 prev_title_length = e->prev_title_length; 793 title_length = e->title_length; 794 prev_size = e->prev_size; 795 size = e->size; 796 prev_requested_size = e->prev_requested_size; 797 requested_size = e->requested_size; 798 char_height = e->char_height; 799 char_slant = e->char_slant; 800 space_size = e->space_size; 801 sentence_space_size = e->sentence_space_size; 802 adjust_mode = e->adjust_mode; 803 fill = e->fill; 804 interrupted = 0; 805 prev_line_interrupted = 0; 806 center_lines = 0; 807 right_justify_lines = 0; 808 prev_vertical_spacing = e->prev_vertical_spacing; 809 vertical_spacing = e->vertical_spacing; 810 prev_post_vertical_spacing = e->prev_post_vertical_spacing, 811 post_vertical_spacing = e->post_vertical_spacing, 812 prev_line_spacing = e->prev_line_spacing; 813 line_spacing = e->line_spacing; 814 prev_indent = e->prev_indent; 815 indent = e->indent; 816 have_temporary_indent = 0; 817 temporary_indent = 0; 818 underline_lines = 0; 819 underline_spaces = 0; 820 input_trap_count = 0; 821 continued_input_trap = 0; 822 prev_text_length = e->prev_text_length; 823 width_total = 0; 824 space_total = 0; 825 input_line_start = 0; 826 control_char = e->control_char; 827 no_break_control_char = e->no_break_control_char; 828 hyphen_indicator_char = e->hyphen_indicator_char; 829 spread_flag = 0; 830 line = 0; 831 pending_lines = 0; 832 discarding = 0; 833 tabs = e->tabs; 834 line_tabs = e->line_tabs; 835 current_tab = TAB_NONE; 836 current_field = 0; 837 margin_character_flags = e->margin_character_flags; 838 margin_character_node = e->margin_character_node; 839 margin_character_distance = e->margin_character_distance; 840 numbering_nodes = 0; 841 number_text_separation = e->number_text_separation; 842 line_number_multiple = e->line_number_multiple; 843 line_number_indent = e->line_number_indent; 844 no_number_count = e->no_number_count; 845 tab_char = e->tab_char; 846 leader_char = e->leader_char; 847 hyphenation_flags = e->hyphenation_flags; 848 fontno = e->fontno; 849 prev_fontno = e->prev_fontno; 850 dummy = e->dummy; 851 family = e->family; 852 prev_family = e->prev_family; 853 leader_node = 0; 854#ifdef WIDOW_CONTROL 855 widow_control = e->widow_control; 856#endif /* WIDOW_CONTROL */ 857 hyphen_line_max = e->hyphen_line_max; 858 hyphen_line_count = 0; 859 hyphenation_space = e->hyphenation_space; 860 hyphenation_margin = e->hyphenation_margin; 861 composite = 0; 862 glyph_color= e->glyph_color; 863 prev_glyph_color = e->prev_glyph_color; 864 fill_color = e->fill_color; 865 prev_fill_color = e->prev_fill_color; 866} 867 868environment::~environment() 869{ 870 delete leader_node; 871 delete_node_list(line); 872 delete_node_list(numbering_nodes); 873} 874 875hunits environment::get_input_line_position() 876{ 877 hunits n; 878 if (line == 0) 879 n = -input_line_start; 880 else 881 n = width_total - input_line_start; 882 if (current_tab) 883 n += tab_width; 884 return n; 885} 886 887void environment::set_input_line_position(hunits n) 888{ 889 input_line_start = line == 0 ? -n : width_total - n; 890 if (current_tab) 891 input_line_start += tab_width; 892} 893 894hunits environment::get_line_length() 895{ 896 return line_length; 897} 898 899hunits environment::get_saved_line_length() 900{ 901 if (line) 902 return target_text_length + saved_indent; 903 else 904 return line_length; 905} 906 907vunits environment::get_vertical_spacing() 908{ 909 return vertical_spacing; 910} 911 912vunits environment::get_post_vertical_spacing() 913{ 914 return post_vertical_spacing; 915} 916 917int environment::get_line_spacing() 918{ 919 return line_spacing; 920} 921 922vunits environment::total_post_vertical_spacing() 923{ 924 vunits tem(post_vertical_spacing); 925 if (line_spacing > 1) 926 tem += (line_spacing - 1)*vertical_spacing; 927 return tem; 928} 929 930int environment::get_bold() 931{ 932 return get_bold_fontno(fontno); 933} 934 935hunits environment::get_digit_width() 936{ 937 return env_digit_width(this); 938} 939 940int environment::get_adjust_mode() 941{ 942 return adjust_mode; 943} 944 945int environment::get_fill() 946{ 947 return fill; 948} 949 950hunits environment::get_indent() 951{ 952 return indent; 953} 954 955hunits environment::get_saved_indent() 956{ 957 if (line) 958 return saved_indent; 959 else if (have_temporary_indent) 960 return temporary_indent; 961 else 962 return indent; 963} 964 965hunits environment::get_temporary_indent() 966{ 967 return temporary_indent; 968} 969 970hunits environment::get_title_length() 971{ 972 return title_length; 973} 974 975node *environment::get_prev_char() 976{ 977 for (node *n = current_tab ? tab_contents : line; n; n = n->next) { 978 node *last = n->last_char_node(); 979 if (last) 980 return last; 981 } 982 return 0; 983} 984 985hunits environment::get_prev_char_width() 986{ 987 node *last = get_prev_char(); 988 if (!last) 989 return H0; 990 return last->width(); 991} 992 993hunits environment::get_prev_char_skew() 994{ 995 node *last = get_prev_char(); 996 if (!last) 997 return H0; 998 return last->skew(); 999} 1000 1001vunits environment::get_prev_char_height() 1002{ 1003 node *last = get_prev_char(); 1004 if (!last) 1005 return V0; 1006 vunits min, max; 1007 last->vertical_extent(&min, &max); 1008 return -min; 1009} 1010 1011vunits environment::get_prev_char_depth() 1012{ 1013 node *last = get_prev_char(); 1014 if (!last) 1015 return V0; 1016 vunits min, max; 1017 last->vertical_extent(&min, &max); 1018 return max; 1019} 1020 1021hunits environment::get_text_length() 1022{ 1023 hunits n = line == 0 ? H0 : width_total; 1024 if (current_tab) 1025 n += tab_width; 1026 return n; 1027} 1028 1029hunits environment::get_prev_text_length() 1030{ 1031 return prev_text_length; 1032} 1033 1034 1035static int sb_reg_contents = 0; 1036static int st_reg_contents = 0; 1037static int ct_reg_contents = 0; 1038static int rsb_reg_contents = 0; 1039static int rst_reg_contents = 0; 1040static int skw_reg_contents = 0; 1041static int ssc_reg_contents = 0; 1042 1043void environment::width_registers() 1044{ 1045 // this is used to implement \w; it sets the st, sb, ct registers 1046 vunits min = 0, max = 0, cur = 0; 1047 int character_type = 0; 1048 ssc_reg_contents = line ? line->subscript_correction().to_units() : 0; 1049 skw_reg_contents = line ? line->skew().to_units() : 0; 1050 line = reverse_node_list(line); 1051 vunits real_min = V0; 1052 vunits real_max = V0; 1053 vunits v1, v2; 1054 for (node *tem = line; tem; tem = tem->next) { 1055 tem->vertical_extent(&v1, &v2); 1056 v1 += cur; 1057 if (v1 < real_min) 1058 real_min = v1; 1059 v2 += cur; 1060 if (v2 > real_max) 1061 real_max = v2; 1062 if ((cur += tem->vertical_width()) < min) 1063 min = cur; 1064 else if (cur > max) 1065 max = cur; 1066 character_type |= tem->character_type(); 1067 } 1068 line = reverse_node_list(line); 1069 st_reg_contents = -min.to_units(); 1070 sb_reg_contents = -max.to_units(); 1071 rst_reg_contents = -real_min.to_units(); 1072 rsb_reg_contents = -real_max.to_units(); 1073 ct_reg_contents = character_type; 1074} 1075 1076node *environment::extract_output_line() 1077{ 1078 if (current_tab) 1079 wrap_up_tab(); 1080 node *n = line; 1081 line = 0; 1082 return n; 1083} 1084 1085/* environment related requests */ 1086 1087void environment_switch() 1088{ 1089 int pop = 0; // 1 means pop, 2 means pop but no error message on underflow 1090 if (curenv->is_dummy()) 1091 error("can't switch environments when current environment is dummy"); 1092 else if (!has_arg()) 1093 pop = 1; 1094 else { 1095 symbol nm; 1096 if (!tok.delimiter()) { 1097 // It looks like a number. 1098 int n; 1099 if (get_integer(&n)) { 1100 if (n >= 0 && n < NENVIRONMENTS) { 1101 env_stack = new env_list(curenv, env_stack); 1102 if (env_table[n] == 0) 1103 env_table[n] = new environment(i_to_a(n)); 1104 curenv = env_table[n]; 1105 } 1106 else 1107 nm = i_to_a(n); 1108 } 1109 else 1110 pop = 2; 1111 } 1112 else { 1113 nm = get_long_name(1); 1114 if (nm.is_null()) 1115 pop = 2; 1116 } 1117 if (!nm.is_null()) { 1118 environment *e = (environment *)env_dictionary.lookup(nm); 1119 if (!e) { 1120 e = new environment(nm); 1121 (void)env_dictionary.lookup(nm, e); 1122 } 1123 env_stack = new env_list(curenv, env_stack); 1124 curenv = e; 1125 } 1126 } 1127 if (pop) { 1128 if (env_stack == 0) { 1129 if (pop == 1) 1130 error("environment stack underflow"); 1131 } 1132 else { 1133 int seen_space = curenv->seen_space; 1134 int seen_eol = curenv->seen_eol; 1135 int suppress_next_eol = curenv->suppress_next_eol; 1136 curenv = env_stack->env; 1137 curenv->seen_space = seen_space; 1138 curenv->seen_eol = seen_eol; 1139 curenv->suppress_next_eol = suppress_next_eol; 1140 env_list *tem = env_stack; 1141 env_stack = env_stack->next; 1142 delete tem; 1143 } 1144 } 1145 skip_line(); 1146} 1147 1148void environment_copy() 1149{ 1150 symbol nm; 1151 environment *e=0; 1152 tok.skip(); 1153 if (!tok.delimiter()) { 1154 // It looks like a number. 1155 int n; 1156 if (get_integer(&n)) { 1157 if (n >= 0 && n < NENVIRONMENTS) 1158 e = env_table[n]; 1159 else 1160 nm = i_to_a(n); 1161 } 1162 } 1163 else 1164 nm = get_long_name(1); 1165 if (!e && !nm.is_null()) 1166 e = (environment *)env_dictionary.lookup(nm); 1167 if (e == 0) { 1168 error("No environment to copy from"); 1169 return; 1170 } 1171 else 1172 curenv->copy(e); 1173 skip_line(); 1174} 1175 1176void fill_color_change() 1177{ 1178 symbol s = get_name(); 1179 if (s.is_null()) 1180 curenv->set_fill_color(curenv->get_prev_fill_color()); 1181 else 1182 do_fill_color(s); 1183 skip_line(); 1184} 1185 1186void glyph_color_change() 1187{ 1188 symbol s = get_name(); 1189 if (s.is_null()) 1190 curenv->set_glyph_color(curenv->get_prev_glyph_color()); 1191 else 1192 do_glyph_color(s); 1193 skip_line(); 1194} 1195 1196static symbol P_symbol("P"); 1197 1198void font_change() 1199{ 1200 symbol s = get_name(); 1201 int is_number = 1; 1202 if (s.is_null() || s == P_symbol) { 1203 s = P_symbol; 1204 is_number = 0; 1205 } 1206 else { 1207 for (const char *p = s.contents(); p != 0 && *p != 0; p++) 1208 if (!csdigit(*p)) { 1209 is_number = 0; 1210 break; 1211 } 1212 } 1213 if (is_number) 1214 curenv->set_font(atoi(s.contents())); 1215 else 1216 curenv->set_font(s); 1217 skip_line(); 1218} 1219 1220void family_change() 1221{ 1222 symbol s = get_name(); 1223 curenv->set_family(s); 1224 skip_line(); 1225} 1226 1227void point_size() 1228{ 1229 int n; 1230 if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) { 1231 if (n <= 0) 1232 n = 1; 1233 curenv->set_size(n); 1234 } 1235 else 1236 curenv->set_size(0); 1237 skip_line(); 1238} 1239 1240void override_sizes() 1241{ 1242 int n = 16; 1243 int *sizes = new int[n]; 1244 int i = 0; 1245 char *buf = read_string(); 1246 if (!buf) 1247 return; 1248 char *p = strtok(buf, " \t"); 1249 for (;;) { 1250 if (!p) 1251 break; 1252 int lower, upper; 1253 switch (sscanf(p, "%d-%d", &lower, &upper)) { 1254 case 1: 1255 upper = lower; 1256 // fall through 1257 case 2: 1258 if (lower <= upper && lower >= 0) 1259 break; 1260 // fall through 1261 default: 1262 warning(WARN_RANGE, "bad size range `%1'", p); 1263 return; 1264 } 1265 if (i + 2 > n) { 1266 int *old_sizes = sizes; 1267 sizes = new int[n*2]; 1268 memcpy(sizes, old_sizes, n*sizeof(int)); 1269 n *= 2; 1270 a_delete old_sizes; 1271 } 1272 sizes[i++] = lower; 1273 if (lower == 0) 1274 break; 1275 sizes[i++] = upper; 1276 p = strtok(0, " \t"); 1277 } 1278 font_size::init_size_table(sizes); 1279} 1280 1281void space_size() 1282{ 1283 int n; 1284 if (get_integer(&n)) { 1285 curenv->space_size = n; 1286 if (has_arg() && get_integer(&n)) 1287 curenv->sentence_space_size = n; 1288 else 1289 curenv->sentence_space_size = curenv->space_size; 1290 } 1291 skip_line(); 1292} 1293 1294void fill() 1295{ 1296 while (!tok.newline() && !tok.eof()) 1297 tok.next(); 1298 if (break_flag) 1299 curenv->do_break(); 1300 curenv->fill = 1; 1301 tok.next(); 1302} 1303 1304void no_fill() 1305{ 1306 while (!tok.newline() && !tok.eof()) 1307 tok.next(); 1308 if (break_flag) 1309 curenv->do_break(); 1310 curenv->fill = 0; 1311 curenv->suppress_next_eol = 1; 1312 tok.next(); 1313} 1314 1315void center() 1316{ 1317 int n; 1318 if (!has_arg() || !get_integer(&n)) 1319 n = 1; 1320 else if (n < 0) 1321 n = 0; 1322 while (!tok.newline() && !tok.eof()) 1323 tok.next(); 1324 if (break_flag) 1325 curenv->do_break(); 1326 curenv->right_justify_lines = 0; 1327 curenv->center_lines = n; 1328 curdiv->modified_tag.incl(MTSM_CE); 1329 tok.next(); 1330} 1331 1332void right_justify() 1333{ 1334 int n; 1335 if (!has_arg() || !get_integer(&n)) 1336 n = 1; 1337 else if (n < 0) 1338 n = 0; 1339 while (!tok.newline() && !tok.eof()) 1340 tok.next(); 1341 if (break_flag) 1342 curenv->do_break(); 1343 curenv->center_lines = 0; 1344 curenv->right_justify_lines = n; 1345 curdiv->modified_tag.incl(MTSM_RJ); 1346 tok.next(); 1347} 1348 1349void line_length() 1350{ 1351 hunits temp; 1352 if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) { 1353 if (temp < H0) { 1354 warning(WARN_RANGE, "bad line length %1u", temp.to_units()); 1355 temp = H0; 1356 } 1357 } 1358 else 1359 temp = curenv->prev_line_length; 1360 curenv->prev_line_length = curenv->line_length; 1361 curenv->line_length = temp; 1362 curdiv->modified_tag.incl(MTSM_LL); 1363 skip_line(); 1364} 1365 1366void title_length() 1367{ 1368 hunits temp; 1369 if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) { 1370 if (temp < H0) { 1371 warning(WARN_RANGE, "bad title length %1u", temp.to_units()); 1372 temp = H0; 1373 } 1374 } 1375 else 1376 temp = curenv->prev_title_length; 1377 curenv->prev_title_length = curenv->title_length; 1378 curenv->title_length = temp; 1379 skip_line(); 1380} 1381 1382void vertical_spacing() 1383{ 1384 vunits temp; 1385 if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) { 1386 if (temp < V0) { 1387 warning(WARN_RANGE, "vertical spacing must not be negative"); 1388 temp = vresolution; 1389 } 1390 } 1391 else 1392 temp = curenv->prev_vertical_spacing; 1393 curenv->prev_vertical_spacing = curenv->vertical_spacing; 1394 curenv->vertical_spacing = temp; 1395 skip_line(); 1396} 1397 1398void post_vertical_spacing() 1399{ 1400 vunits temp; 1401 if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) { 1402 if (temp < V0) { 1403 warning(WARN_RANGE, 1404 "post vertical spacing must be greater than or equal to 0"); 1405 temp = V0; 1406 } 1407 } 1408 else 1409 temp = curenv->prev_post_vertical_spacing; 1410 curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing; 1411 curenv->post_vertical_spacing = temp; 1412 skip_line(); 1413} 1414 1415void line_spacing() 1416{ 1417 int temp; 1418 if (has_arg() && get_integer(&temp)) { 1419 if (temp < 1) { 1420 warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp); 1421 temp = 1; 1422 } 1423 } 1424 else 1425 temp = curenv->prev_line_spacing; 1426 curenv->prev_line_spacing = curenv->line_spacing; 1427 curenv->line_spacing = temp; 1428 skip_line(); 1429} 1430 1431void indent() 1432{ 1433 hunits temp; 1434 if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) { 1435 if (temp < H0) { 1436 warning(WARN_RANGE, "indent cannot be negative"); 1437 temp = H0; 1438 } 1439 } 1440 else 1441 temp = curenv->prev_indent; 1442 while (!tok.newline() && !tok.eof()) 1443 tok.next(); 1444 if (break_flag) 1445 curenv->do_break(); 1446 curenv->have_temporary_indent = 0; 1447 curenv->prev_indent = curenv->indent; 1448 curenv->indent = temp; 1449 curdiv->modified_tag.incl(MTSM_IN); 1450 tok.next(); 1451} 1452 1453void temporary_indent() 1454{ 1455 int err = 0; 1456 hunits temp; 1457 if (!get_hunits(&temp, 'm', curenv->get_indent())) 1458 err = 1; 1459 while (!tok.newline() && !tok.eof()) 1460 tok.next(); 1461 if (break_flag) 1462 curenv->do_break(); 1463 if (temp < H0) { 1464 warning(WARN_RANGE, "total indent cannot be negative"); 1465 temp = H0; 1466 } 1467 if (!err) { 1468 curenv->temporary_indent = temp; 1469 curenv->have_temporary_indent = 1; 1470 curdiv->modified_tag.incl(MTSM_TI); 1471 } 1472 tok.next(); 1473} 1474 1475node *do_underline_special(int underline_spaces) 1476{ 1477 macro m; 1478 m.append_str("x u "); 1479 m.append(underline_spaces + '0'); 1480 return new special_node(m, 1); 1481} 1482 1483void do_underline(int underline_spaces) 1484{ 1485 int n; 1486 if (!has_arg() || !get_integer(&n)) 1487 n = 1; 1488 if (n <= 0) { 1489 if (curenv->underline_lines > 0) { 1490 curenv->prev_fontno = curenv->fontno; 1491 curenv->fontno = curenv->pre_underline_fontno; 1492 if (underline_spaces) { 1493 curenv->underline_spaces = 0; 1494 curenv->add_node(do_underline_special(0)); 1495 } 1496 } 1497 curenv->underline_lines = 0; 1498 } 1499 else { 1500 curenv->underline_lines = n; 1501 curenv->pre_underline_fontno = curenv->fontno; 1502 curenv->fontno = get_underline_fontno(); 1503 if (underline_spaces) { 1504 curenv->underline_spaces = 1; 1505 curenv->add_node(do_underline_special(1)); 1506 } 1507 } 1508 skip_line(); 1509} 1510 1511void continuous_underline() 1512{ 1513 do_underline(1); 1514} 1515 1516void underline() 1517{ 1518 do_underline(0); 1519} 1520 1521void control_char() 1522{ 1523 curenv->control_char = '.'; 1524 if (has_arg()) { 1525 if (tok.ch() == 0) 1526 error("bad control character"); 1527 else 1528 curenv->control_char = tok.ch(); 1529 } 1530 skip_line(); 1531} 1532 1533void no_break_control_char() 1534{ 1535 curenv->no_break_control_char = '\''; 1536 if (has_arg()) { 1537 if (tok.ch() == 0) 1538 error("bad control character"); 1539 else 1540 curenv->no_break_control_char = tok.ch(); 1541 } 1542 skip_line(); 1543} 1544 1545void margin_character() 1546{ 1547 while (tok.space()) 1548 tok.next(); 1549 charinfo *ci = tok.get_char(); 1550 if (ci) { 1551 // Call tok.next() only after making the node so that 1552 // .mc \s+9\(br\s0 works. 1553 node *nd = curenv->make_char_node(ci); 1554 tok.next(); 1555 if (nd) { 1556 delete curenv->margin_character_node; 1557 curenv->margin_character_node = nd; 1558 curenv->margin_character_flags = (MARGIN_CHARACTER_ON 1559 |MARGIN_CHARACTER_NEXT); 1560 hunits d; 1561 if (has_arg() && get_hunits(&d, 'm')) 1562 curenv->margin_character_distance = d; 1563 } 1564 } 1565 else { 1566 check_missing_character(); 1567 curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON; 1568 if (curenv->margin_character_flags == 0) { 1569 delete curenv->margin_character_node; 1570 curenv->margin_character_node = 0; 1571 } 1572 } 1573 skip_line(); 1574} 1575 1576void number_lines() 1577{ 1578 delete_node_list(curenv->numbering_nodes); 1579 curenv->numbering_nodes = 0; 1580 if (has_arg()) { 1581 node *nd = 0; 1582 for (int i = '9'; i >= '0'; i--) { 1583 node *tem = make_node(charset_table[i], curenv); 1584 if (!tem) { 1585 skip_line(); 1586 return; 1587 } 1588 tem->next = nd; 1589 nd = tem; 1590 } 1591 curenv->numbering_nodes = nd; 1592 curenv->line_number_digit_width = env_digit_width(curenv); 1593 int n; 1594 if (!tok.delimiter()) { 1595 if (get_integer(&n, next_line_number)) { 1596 next_line_number = n; 1597 if (next_line_number < 0) { 1598 warning(WARN_RANGE, "negative line number"); 1599 next_line_number = 0; 1600 } 1601 } 1602 } 1603 else 1604 while (!tok.space() && !tok.newline() && !tok.eof()) 1605 tok.next(); 1606 if (has_arg()) { 1607 if (!tok.delimiter()) { 1608 if (get_integer(&n)) { 1609 if (n <= 0) { 1610 warning(WARN_RANGE, "negative or zero line number multiple"); 1611 } 1612 else 1613 curenv->line_number_multiple = n; 1614 } 1615 } 1616 else 1617 while (!tok.space() && !tok.newline() && !tok.eof()) 1618 tok.next(); 1619 if (has_arg()) { 1620 if (!tok.delimiter()) { 1621 if (get_integer(&n)) 1622 curenv->number_text_separation = n; 1623 } 1624 else 1625 while (!tok.space() && !tok.newline() && !tok.eof()) 1626 tok.next(); 1627 if (has_arg() && !tok.delimiter() && get_integer(&n)) 1628 curenv->line_number_indent = n; 1629 } 1630 } 1631 } 1632 skip_line(); 1633} 1634 1635void no_number() 1636{ 1637 int n; 1638 if (has_arg() && get_integer(&n)) 1639 curenv->no_number_count = n > 0 ? n : 0; 1640 else 1641 curenv->no_number_count = 1; 1642 skip_line(); 1643} 1644 1645void no_hyphenate() 1646{ 1647 curenv->hyphenation_flags = 0; 1648 skip_line(); 1649} 1650 1651void hyphenate_request() 1652{ 1653 int n; 1654 if (has_arg() && get_integer(&n)) 1655 curenv->hyphenation_flags = n; 1656 else 1657 curenv->hyphenation_flags = 1; 1658 skip_line(); 1659} 1660 1661void hyphen_char() 1662{ 1663 curenv->hyphen_indicator_char = get_optional_char(); 1664 skip_line(); 1665} 1666 1667void hyphen_line_max_request() 1668{ 1669 int n; 1670 if (has_arg() && get_integer(&n)) 1671 curenv->hyphen_line_max = n; 1672 else 1673 curenv->hyphen_line_max = -1; 1674 skip_line(); 1675} 1676 1677void environment::interrupt() 1678{ 1679 if (!dummy) { 1680 add_node(new transparent_dummy_node); 1681 interrupted = 1; 1682 } 1683} 1684 1685void environment::newline() 1686{ 1687 int was_centered = 0; 1688 if (underline_lines > 0) { 1689 if (--underline_lines == 0) { 1690 prev_fontno = fontno; 1691 fontno = pre_underline_fontno; 1692 if (underline_spaces) { 1693 underline_spaces = 0; 1694 add_node(do_underline_special(0)); 1695 } 1696 } 1697 } 1698 if (current_field) 1699 wrap_up_field(); 1700 if (current_tab) 1701 wrap_up_tab(); 1702 // strip trailing spaces 1703 while (line != 0 && line->discardable()) { 1704 width_total -= line->width(); 1705 space_total -= line->nspaces(); 1706 node *tem = line; 1707 line = line->next; 1708 delete tem; 1709 } 1710 node *to_be_output = 0; 1711 hunits to_be_output_width; 1712 prev_line_interrupted = 0; 1713 if (dummy) 1714 space_newline(); 1715 else if (interrupted) { 1716 interrupted = 0; 1717 // see environment::final_break 1718 prev_line_interrupted = exit_started ? 2 : 1; 1719 } 1720 else if (center_lines > 0) { 1721 --center_lines; 1722 hunits x = target_text_length - width_total; 1723 if (x > H0) 1724 saved_indent += x/2; 1725 to_be_output = line; 1726 was_centered = 1; 1727 to_be_output_width = width_total; 1728 line = 0; 1729 } 1730 else if (right_justify_lines > 0) { 1731 --right_justify_lines; 1732 hunits x = target_text_length - width_total; 1733 if (x > H0) 1734 saved_indent += x; 1735 to_be_output = line; 1736 to_be_output_width = width_total; 1737 line = 0; 1738 } 1739 else if (fill) 1740 space_newline(); 1741 else { 1742 to_be_output = line; 1743 to_be_output_width = width_total; 1744 line = 0; 1745 } 1746 input_line_start = line == 0 ? H0 : width_total; 1747 if (to_be_output) { 1748 if (is_html && !fill) { 1749 curdiv->modified_tag.incl(MTSM_EOL); 1750 if (suppress_next_eol) 1751 suppress_next_eol = 0; 1752 else 1753 seen_eol = 1; 1754 } 1755 1756 output_line(to_be_output, to_be_output_width, was_centered); 1757 hyphen_line_count = 0; 1758 } 1759 if (input_trap_count > 0) { 1760 if (!(continued_input_trap && prev_line_interrupted)) 1761 if (--input_trap_count == 0) 1762 spring_trap(input_trap); 1763 } 1764} 1765 1766void environment::output_line(node *n, hunits width, int was_centered) 1767{ 1768 prev_text_length = width; 1769 if (margin_character_flags) { 1770 hunits d = line_length + margin_character_distance - saved_indent - width; 1771 if (d > 0) { 1772 n = new hmotion_node(d, get_fill_color(), n); 1773 width += d; 1774 } 1775 margin_character_flags &= ~MARGIN_CHARACTER_NEXT; 1776 node *tem; 1777 if (!margin_character_flags) { 1778 tem = margin_character_node; 1779 margin_character_node = 0; 1780 } 1781 else 1782 tem = margin_character_node->copy(); 1783 tem->next = n; 1784 n = tem; 1785 width += tem->width(); 1786 } 1787 node *nn = 0; 1788 while (n != 0) { 1789 node *tem = n->next; 1790 n->next = nn; 1791 nn = n; 1792 n = tem; 1793 } 1794 if (!saved_indent.is_zero()) 1795 nn = new hmotion_node(saved_indent, get_fill_color(), nn); 1796 width += saved_indent; 1797 if (no_number_count > 0) 1798 --no_number_count; 1799 else if (numbering_nodes) { 1800 hunits w = (line_number_digit_width 1801 *(3+line_number_indent+number_text_separation)); 1802 if (next_line_number % line_number_multiple != 0) 1803 nn = new hmotion_node(w, get_fill_color(), nn); 1804 else { 1805 hunits x = w; 1806 nn = new hmotion_node(number_text_separation * line_number_digit_width, 1807 get_fill_color(), nn); 1808 x -= number_text_separation*line_number_digit_width; 1809 char buf[30]; 1810 sprintf(buf, "%3d", next_line_number); 1811 for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) { 1812 node *gn = numbering_nodes; 1813 for (int count = *p - '0'; count > 0; count--) 1814 gn = gn->next; 1815 gn = gn->copy(); 1816 x -= gn->width(); 1817 gn->next = nn; 1818 nn = gn; 1819 } 1820 nn = new hmotion_node(x, get_fill_color(), nn); 1821 } 1822 width += w; 1823 ++next_line_number; 1824 } 1825 output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width, 1826 was_centered); 1827} 1828 1829void environment::start_line() 1830{ 1831 assert(line == 0); 1832 discarding = 0; 1833 line = new line_start_node; 1834 if (have_temporary_indent) { 1835 saved_indent = temporary_indent; 1836 have_temporary_indent = 0; 1837 } 1838 else 1839 saved_indent = indent; 1840 target_text_length = line_length - saved_indent; 1841 width_total = H0; 1842 space_total = 0; 1843} 1844 1845hunits environment::get_hyphenation_space() 1846{ 1847 return hyphenation_space; 1848} 1849 1850void hyphenation_space_request() 1851{ 1852 hunits n; 1853 if (get_hunits(&n, 'm')) { 1854 if (n < H0) { 1855 warning(WARN_RANGE, "hyphenation space cannot be negative"); 1856 n = H0; 1857 } 1858 curenv->hyphenation_space = n; 1859 } 1860 skip_line(); 1861} 1862 1863hunits environment::get_hyphenation_margin() 1864{ 1865 return hyphenation_margin; 1866} 1867 1868void hyphenation_margin_request() 1869{ 1870 hunits n; 1871 if (get_hunits(&n, 'm')) { 1872 if (n < H0) { 1873 warning(WARN_RANGE, "hyphenation margin cannot be negative"); 1874 n = H0; 1875 } 1876 curenv->hyphenation_margin = n; 1877 } 1878 skip_line(); 1879} 1880 1881breakpoint *environment::choose_breakpoint() 1882{ 1883 hunits x = width_total; 1884 int s = space_total; 1885 node *n = line; 1886 breakpoint *best_bp = 0; // the best breakpoint so far 1887 int best_bp_fits = 0; 1888 while (n != 0) { 1889 x -= n->width(); 1890 s -= n->nspaces(); 1891 breakpoint *bp = n->get_breakpoints(x, s); 1892 while (bp != 0) { 1893 if (bp->width <= target_text_length) { 1894 if (!bp->hyphenated) { 1895 breakpoint *tem = bp->next; 1896 bp->next = 0; 1897 while (tem != 0) { 1898 breakpoint *tem1 = tem; 1899 tem = tem->next; 1900 delete tem1; 1901 } 1902 if (best_bp_fits 1903 // Decide whether to use the hyphenated breakpoint. 1904 && (hyphen_line_max < 0 1905 // Only choose the hyphenated breakpoint if it would not 1906 // exceed the maximum number of consecutive hyphenated 1907 // lines. 1908 || hyphen_line_count + 1 <= hyphen_line_max) 1909 && !(adjust_mode == ADJUST_BOTH 1910 // Don't choose the hyphenated breakpoint if the line 1911 // can be justified by adding no more than 1912 // hyphenation_space to any word space. 1913 ? (bp->nspaces > 0 1914 && (((target_text_length - bp->width 1915 + (bp->nspaces - 1)*hresolution)/bp->nspaces) 1916 <= hyphenation_space)) 1917 // Don't choose the hyphenated breakpoint if the line 1918 // is no more than hyphenation_margin short. 1919 : target_text_length - bp->width <= hyphenation_margin)) { 1920 delete bp; 1921 return best_bp; 1922 } 1923 if (best_bp) 1924 delete best_bp; 1925 return bp; 1926 } 1927 else { 1928 if ((adjust_mode == ADJUST_BOTH 1929 ? hyphenation_space == H0 1930 : hyphenation_margin == H0) 1931 && (hyphen_line_max < 0 1932 || hyphen_line_count + 1 <= hyphen_line_max)) { 1933 // No need to consider a non-hyphenated breakpoint. 1934 if (best_bp) 1935 delete best_bp; 1936 breakpoint *tem = bp->next; 1937 bp->next = 0; 1938 while (tem != 0) { 1939 breakpoint *tem1 = tem; 1940 tem = tem->next; 1941 delete tem1; 1942 } 1943 return bp; 1944 } 1945 // It fits but it's hyphenated. 1946 if (!best_bp_fits) { 1947 if (best_bp) 1948 delete best_bp; 1949 best_bp = bp; 1950 bp = bp->next; 1951 best_bp_fits = 1; 1952 } 1953 else { 1954 breakpoint *tem = bp; 1955 bp = bp->next; 1956 delete tem; 1957 } 1958 } 1959 } 1960 else { 1961 if (best_bp) 1962 delete best_bp; 1963 best_bp = bp; 1964 bp = bp->next; 1965 } 1966 } 1967 n = n->next; 1968 } 1969 if (best_bp) { 1970 if (!best_bp_fits) 1971 output_warning(WARN_BREAK, "can't break line"); 1972 return best_bp; 1973 } 1974 return 0; 1975} 1976 1977void environment::hyphenate_line(int start_here) 1978{ 1979 assert(line != 0); 1980 hyphenation_type prev_type = line->get_hyphenation_type(); 1981 node **startp; 1982 if (start_here) 1983 startp = &line; 1984 else 1985 for (startp = &line->next; *startp != 0; startp = &(*startp)->next) { 1986 hyphenation_type this_type = (*startp)->get_hyphenation_type(); 1987 if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE) 1988 break; 1989 prev_type = this_type; 1990 } 1991 if (*startp == 0) 1992 return; 1993 node *tem = *startp; 1994 do { 1995 tem = tem->next; 1996 } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE); 1997 int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT); 1998 node *end = tem; 1999 hyphen_list *sl = 0; 2000 tem = *startp; 2001 node *forward = 0; 2002 int i = 0; 2003 while (tem != end) { 2004 sl = tem->get_hyphen_list(sl, &i); 2005 node *tem1 = tem; 2006 tem = tem->next; 2007 tem1->next = forward; 2008 forward = tem1; 2009 } 2010 if (!inhibit) { 2011 // this is for characters like hyphen and emdash 2012 int prev_code = 0; 2013 for (hyphen_list *h = sl; h; h = h->next) { 2014 h->breakable = (prev_code != 0 2015 && h->next != 0 2016 && h->next->hyphenation_code != 0); 2017 prev_code = h->hyphenation_code; 2018 } 2019 } 2020 if (hyphenation_flags != 0 2021 && !inhibit 2022 // this may not be right if we have extra space on this line 2023 && !((hyphenation_flags & HYPHEN_LAST_LINE) 2024 && (curdiv->distance_to_next_trap() 2025 <= vertical_spacing + total_post_vertical_spacing())) 2026 && i >= 4) 2027 hyphenate(sl, hyphenation_flags); 2028 while (forward != 0) { 2029 node *tem1 = forward; 2030 forward = forward->next; 2031 tem1->next = 0; 2032 tem = tem1->add_self(tem, &sl); 2033 } 2034 *startp = tem; 2035} 2036 2037static node *node_list_reverse(node *n) 2038{ 2039 node *res = 0; 2040 while (n) { 2041 node *tem = n; 2042 n = n->next; 2043 tem->next = res; 2044 res = tem; 2045 } 2046 return res; 2047} 2048 2049static void distribute_space(node *n, int nspaces, hunits desired_space, 2050 int force_reverse = 0) 2051{ 2052 static int reverse = 0; 2053 if (force_reverse || reverse) 2054 n = node_list_reverse(n); 2055 if (!force_reverse && nspaces > 0 && spread_limit >= 0 2056 && desired_space.to_units() > 0) { 2057 hunits em = curenv->get_size(); 2058 double Ems = (double)desired_space.to_units() / nspaces 2059 / (em.is_zero() ? hresolution : em.to_units()); 2060 if (Ems > spread_limit) 2061 output_warning(WARN_BREAK, "spreading %1m per space", Ems); 2062 } 2063 for (node *tem = n; tem; tem = tem->next) 2064 tem->spread_space(&nspaces, &desired_space); 2065 if (force_reverse || reverse) 2066 (void)node_list_reverse(n); 2067 if (!force_reverse) 2068 reverse = !reverse; 2069 assert(desired_space.is_zero() && nspaces == 0); 2070} 2071 2072void environment::possibly_break_line(int start_here, int forced) 2073{ 2074 int was_centered = center_lines > 0; 2075 if (!fill || current_tab || current_field || dummy) 2076 return; 2077 while (line != 0 2078 && (forced 2079 // When a macro follows a paragraph in fill mode, the 2080 // current line should not be empty. 2081 || (width_total - line->width()) > target_text_length)) { 2082 hyphenate_line(start_here); 2083 breakpoint *bp = choose_breakpoint(); 2084 if (bp == 0) 2085 // we'll find one eventually 2086 return; 2087 node *pre, *post; 2088 node **ndp = &line; 2089 while (*ndp != bp->nd) 2090 ndp = &(*ndp)->next; 2091 bp->nd->split(bp->index, &pre, &post); 2092 *ndp = post; 2093 hunits extra_space_width = H0; 2094 switch(adjust_mode) { 2095 case ADJUST_BOTH: 2096 if (bp->nspaces != 0) 2097 extra_space_width = target_text_length - bp->width; 2098 else if (bp->width > 0 && target_text_length > 0 2099 && target_text_length > bp->width) 2100 output_warning(WARN_BREAK, "cannot adjust line"); 2101 break; 2102 case ADJUST_CENTER: 2103 saved_indent += (target_text_length - bp->width)/2; 2104 was_centered = 1; 2105 break; 2106 case ADJUST_RIGHT: 2107 saved_indent += target_text_length - bp->width; 2108 break; 2109 } 2110 distribute_space(pre, bp->nspaces, extra_space_width); 2111 hunits output_width = bp->width + extra_space_width; 2112 input_line_start -= output_width; 2113 if (bp->hyphenated) 2114 hyphen_line_count++; 2115 else 2116 hyphen_line_count = 0; 2117 delete bp; 2118 space_total = 0; 2119 width_total = 0; 2120 node *first_non_discardable = 0; 2121 node *tem; 2122 for (tem = line; tem != 0; tem = tem->next) 2123 if (!tem->discardable()) 2124 first_non_discardable = tem; 2125 node *to_be_discarded; 2126 if (first_non_discardable) { 2127 to_be_discarded = first_non_discardable->next; 2128 first_non_discardable->next = 0; 2129 for (tem = line; tem != 0; tem = tem->next) { 2130 width_total += tem->width(); 2131 space_total += tem->nspaces(); 2132 } 2133 discarding = 0; 2134 } 2135 else { 2136 discarding = 1; 2137 to_be_discarded = line; 2138 line = 0; 2139 } 2140 // Do output_line() here so that line will be 0 iff the 2141 // the environment will be empty. 2142 output_line(pre, output_width, was_centered); 2143 while (to_be_discarded != 0) { 2144 tem = to_be_discarded; 2145 to_be_discarded = to_be_discarded->next; 2146 input_line_start -= tem->width(); 2147 …
Large files files are truncated, but you can click here to view the full file