1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. 3 Written by James Clark (jjc@jclark.com) 4 5This file is part of groff. 6 7groff is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 2, or (at your option) any later 10version. 11 12groff is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License along 18with groff; see the file COPYING. If not, write to the Free Software 19Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21#ifdef COLUMN 22 23#include "troff.h" 24#include "symbol.h" 25#include "dictionary.h" 26#include "hvunits.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 "stringclass.h" 34 35void output_file::vjustify(vunits, symbol) 36{ 37 // do nothing 38} 39 40struct justification_spec; 41struct output_line; 42 43class column : public output_file { 44private: 45 output_file *out; 46 vunits bottom; 47 output_line *col; 48 output_line **tail; 49 void add_output_line(output_line *); 50 void begin_page(int pageno, vunits page_length); 51 void flush(); 52 void print_line(hunits, vunits, node *, vunits, vunits); 53 void vjustify(vunits, symbol); 54 void transparent_char(unsigned char c); 55 void copy_file(hunits, vunits, const char *); 56 int is_printing(); 57 void check_bottom(); 58public: 59 column(); 60 ~column(); 61 void start(); 62 void output(); 63 void justify(const justification_spec &); 64 void trim(); 65 void reset(); 66 vunits get_bottom(); 67 vunits get_last_extra_space(); 68 int is_active() { return out != 0; } 69}; 70 71column *the_column = 0; 72 73struct transparent_output_line; 74struct vjustify_output_line; 75 76class output_line { 77 output_line *next; 78public: 79 output_line(); 80 virtual ~output_line(); 81 virtual void output(output_file *, vunits); 82 virtual transparent_output_line *as_transparent_output_line(); 83 virtual vjustify_output_line *as_vjustify_output_line(); 84 virtual vunits distance(); 85 virtual vunits height(); 86 virtual void reset(); 87 virtual vunits extra_space(); // post line 88 friend class column; 89 friend class justification_spec; 90}; 91 92class position_output_line : public output_line { 93 vunits dist; 94public: 95 position_output_line(vunits); 96 vunits distance(); 97}; 98 99class node_output_line : public position_output_line { 100 node *nd; 101 hunits page_offset; 102 vunits before; 103 vunits after; 104public: 105 node_output_line(vunits, node *, hunits, vunits, vunits); 106 ~node_output_line(); 107 void output(output_file *, vunits); 108 vunits height(); 109 vunits extra_space(); 110}; 111 112class vjustify_output_line : public position_output_line { 113 vunits current; 114 symbol typ; 115public: 116 vjustify_output_line(vunits dist, symbol); 117 vunits height(); 118 vjustify_output_line *as_vjustify_output_line(); 119 void vary(vunits amount); 120 void reset(); 121 symbol type(); 122}; 123 124inline symbol vjustify_output_line::type() 125{ 126 return typ; 127} 128 129class copy_file_output_line : public position_output_line { 130 symbol filename; 131 hunits hpos; 132public: 133 copy_file_output_line(vunits, const char *, hunits); 134 void output(output_file *, vunits); 135}; 136 137class transparent_output_line : public output_line { 138 string buf; 139public: 140 transparent_output_line(); 141 void output(output_file *, vunits); 142 void append_char(unsigned char c); 143 transparent_output_line *as_transparent_output_line(); 144}; 145 146output_line::output_line() : next(0) 147{ 148} 149 150output_line::~output_line() 151{ 152} 153 154void output_line::reset() 155{ 156} 157 158transparent_output_line *output_line::as_transparent_output_line() 159{ 160 return 0; 161} 162 163vjustify_output_line *output_line::as_vjustify_output_line() 164{ 165 return 0; 166} 167 168void output_line::output(output_file *, vunits) 169{ 170} 171 172vunits output_line::distance() 173{ 174 return V0; 175} 176 177vunits output_line::height() 178{ 179 return V0; 180} 181 182vunits output_line::extra_space() 183{ 184 return V0; 185} 186 187position_output_line::position_output_line(vunits d) 188: dist(d) 189{ 190} 191 192vunits position_output_line::distance() 193{ 194 return dist; 195} 196 197node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a) 198: position_output_line(d), nd(n), page_offset(po), before(b), after(a) 199{ 200} 201 202node_output_line::~node_output_line() 203{ 204 delete_node_list(nd); 205} 206 207void node_output_line::output(output_file *out, vunits pos) 208{ 209 out->print_line(page_offset, pos, nd, before, after); 210 nd = 0; 211} 212 213vunits node_output_line::height() 214{ 215 return after; 216} 217 218vunits node_output_line::extra_space() 219{ 220 return after; 221} 222 223vjustify_output_line::vjustify_output_line(vunits d, symbol t) 224: position_output_line(d), typ(t) 225{ 226} 227 228void vjustify_output_line::reset() 229{ 230 current = V0; 231} 232 233vunits vjustify_output_line::height() 234{ 235 return current; 236} 237 238vjustify_output_line *vjustify_output_line::as_vjustify_output_line() 239{ 240 return this; 241} 242 243inline void vjustify_output_line::vary(vunits amount) 244{ 245 current += amount; 246} 247 248transparent_output_line::transparent_output_line() 249{ 250} 251 252transparent_output_line *transparent_output_line::as_transparent_output_line() 253{ 254 return this; 255} 256 257void transparent_output_line::append_char(unsigned char c) 258{ 259 assert(c != 0); 260 buf += c; 261} 262 263void transparent_output_line::output(output_file *out, vunits) 264{ 265 int len = buf.length(); 266 for (int i = 0; i < len; i++) 267 out->transparent_char(buf[i]); 268} 269 270copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h) 271: position_output_line(d), hpos(h), filename(f) 272{ 273} 274 275void copy_file_output_line::output(output_file *out, vunits pos) 276{ 277 out->copy_file(hpos, pos, filename.contents()); 278} 279 280column::column() 281: bottom(V0), col(0), tail(&col), out(0) 282{ 283} 284 285column::~column() 286{ 287 assert(out != 0); 288 error("automatically outputting column before exiting"); 289 output(); 290 delete the_output; 291} 292 293void column::start() 294{ 295 assert(out == 0); 296 if (!the_output) 297 init_output(); 298 assert(the_output != 0); 299 out = the_output; 300 the_output = this; 301} 302 303void column::begin_page(int pageno, vunits page_length) 304{ 305 assert(out != 0); 306 if (col) { 307 error("automatically outputting column before beginning next page"); 308 output(); 309 the_output->begin_page(pageno, page_length); 310 } 311 else 312 out->begin_page(pageno, page_length); 313 314} 315 316void column::flush() 317{ 318 assert(out != 0); 319 out->flush(); 320} 321 322int column::is_printing() 323{ 324 assert(out != 0); 325 return out->is_printing(); 326} 327 328vunits column::get_bottom() 329{ 330 return bottom; 331} 332 333void column::add_output_line(output_line *ln) 334{ 335 *tail = ln; 336 bottom += ln->distance(); 337 bottom += ln->height(); 338 ln->next = 0; 339 tail = &(*tail)->next; 340} 341 342void column::print_line(hunits page_offset, vunits pos, node *nd, 343 vunits before, vunits after) 344{ 345 assert(out != 0); 346 add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after)); 347} 348 349void column::vjustify(vunits pos, symbol typ) 350{ 351 assert(out != 0); 352 add_output_line(new vjustify_output_line(pos - bottom, typ)); 353} 354 355void column::transparent_char(unsigned char c) 356{ 357 assert(out != 0); 358 transparent_output_line *tl = 0; 359 if (*tail) 360 tl = (*tail)->as_transparent_output_line(); 361 if (!tl) { 362 tl = new transparent_output_line; 363 add_output_line(tl); 364 } 365 tl->append_char(c); 366} 367 368void column::copy_file(hunits page_offset, vunits pos, const char *filename) 369{ 370 assert(out != 0); 371 add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset)); 372} 373 374void column::trim() 375{ 376 output_line **spp = 0; 377 for (output_line **pp = &col; *pp; pp = &(*pp)->next) 378 if ((*pp)->as_vjustify_output_line() == 0) 379 spp = 0; 380 else if (!spp) 381 spp = pp; 382 if (spp) { 383 output_line *ln = *spp; 384 *spp = 0; 385 tail = spp; 386 while (ln) { 387 output_line *tem = ln->next; 388 bottom -= ln->distance(); 389 bottom -= ln->height(); 390 delete ln; 391 ln = tem; 392 } 393 } 394} 395 396void column::reset() 397{ 398 bottom = V0; 399 for (output_line *ln = col; ln; ln = ln->next) { 400 bottom += ln->distance(); 401 ln->reset(); 402 bottom += ln->height(); 403 } 404} 405 406void column::check_bottom() 407{ 408 vunits b; 409 for (output_line *ln = col; ln; ln = ln->next) { 410 b += ln->distance(); 411 b += ln->height(); 412 } 413 assert(b == bottom); 414} 415 416void column::output() 417{ 418 assert(out != 0); 419 vunits vpos(V0); 420 output_line *ln = col; 421 while (ln) { 422 vpos += ln->distance(); 423 ln->output(out, vpos); 424 vpos += ln->height(); 425 output_line *tem = ln->next; 426 delete ln; 427 ln = tem; 428 } 429 tail = &col; 430 bottom = V0; 431 col = 0; 432 the_output = out; 433 out = 0; 434} 435 436vunits column::get_last_extra_space() 437{ 438 if (!col) 439 return V0; 440 for (output_line *p = col; p->next; p = p->next) 441 ; 442 return p->extra_space(); 443} 444 445class justification_spec { 446 vunits height; 447 symbol *type; 448 vunits *amount; 449 int n; 450 int maxn; 451public: 452 justification_spec(vunits); 453 ~justification_spec(); 454 void append(symbol t, vunits v); 455 void justify(output_line *, vunits *bottomp) const; 456}; 457 458justification_spec::justification_spec(vunits h) 459: height(h), n(0), maxn(10) 460{ 461 type = new symbol[maxn]; 462 amount = new vunits[maxn]; 463} 464 465justification_spec::~justification_spec() 466{ 467 a_delete type; 468 a_delete amount; 469} 470 471void justification_spec::append(symbol t, vunits v) 472{ 473 if (v <= V0) { 474 if (v < V0) 475 warning(WARN_RANGE, 476 "maximum space for vertical justification must not be negative"); 477 else 478 warning(WARN_RANGE, 479 "maximum space for vertical justification must not be zero"); 480 return; 481 } 482 if (n >= maxn) { 483 maxn *= 2; 484 symbol *old_type = type; 485 type = new symbol[maxn]; 486 int i; 487 for (i = 0; i < n; i++) 488 type[i] = old_type[i]; 489 a_delete old_type; 490 vunits *old_amount = amount; 491 amount = new vunits[maxn]; 492 for (i = 0; i < n; i++) 493 amount[i] = old_amount[i]; 494 a_delete old_amount; 495 } 496 assert(n < maxn); 497 type[n] = t; 498 amount[n] = v; 499 n++; 500} 501 502void justification_spec::justify(output_line *col, vunits *bottomp) const 503{ 504 if (*bottomp >= height) 505 return; 506 vunits total; 507 output_line *p; 508 for (p = col; p; p = p->next) { 509 vjustify_output_line *sp = p->as_vjustify_output_line(); 510 if (sp) { 511 symbol t = sp->type(); 512 for (int i = 0; i < n; i++) { 513 if (t == type[i]) 514 total += amount[i]; 515 } 516 } 517 } 518 vunits gap = height - *bottomp; 519 for (p = col; p; p = p->next) { 520 vjustify_output_line *sp = p->as_vjustify_output_line(); 521 if (sp) { 522 symbol t = sp->type(); 523 for (int i = 0; i < n; i++) { 524 if (t == type[i]) { 525 if (total <= gap) { 526 sp->vary(amount[i]); 527 gap -= amount[i]; 528 } 529 else { 530 // gap < total 531 vunits v = scale(amount[i], gap, total); 532 sp->vary(v); 533 gap -= v; 534 } 535 total -= amount[i]; 536 } 537 } 538 } 539 } 540 assert(total == V0); 541 *bottomp = height - gap; 542} 543 544void column::justify(const justification_spec &js) 545{ 546 check_bottom(); 547 js.justify(col, &bottom); 548 check_bottom(); 549} 550 551void column_justify() 552{ 553 vunits height; 554 if (!the_column->is_active()) 555 error("can't justify column - column not active"); 556 else if (get_vunits(&height, 'v')) { 557 justification_spec js(height); 558 symbol nm = get_long_name(1); 559 if (!nm.is_null()) { 560 vunits v; 561 if (get_vunits(&v, 'v')) { 562 js.append(nm, v); 563 int err = 0; 564 while (has_arg()) { 565 nm = get_long_name(1); 566 if (nm.is_null()) { 567 err = 1; 568 break; 569 } 570 if (!get_vunits(&v, 'v')) { 571 err = 1; 572 break; 573 } 574 js.append(nm, v); 575 } 576 if (!err) 577 the_column->justify(js); 578 } 579 } 580 } 581 skip_line(); 582} 583 584void column_start() 585{ 586 if (the_column->is_active()) 587 error("can't start column - column already active"); 588 else 589 the_column->start(); 590 skip_line(); 591} 592 593void column_output() 594{ 595 if (!the_column->is_active()) 596 error("can't output column - column not active"); 597 else 598 the_column->output(); 599 skip_line(); 600} 601 602void column_trim() 603{ 604 if (!the_column->is_active()) 605 error("can't trim column - column not active"); 606 else 607 the_column->trim(); 608 skip_line(); 609} 610 611void column_reset() 612{ 613 if (!the_column->is_active()) 614 error("can't reset column - column not active"); 615 else 616 the_column->reset(); 617 skip_line(); 618} 619 620class column_bottom_reg : public reg { 621public: 622 const char *get_string(); 623}; 624 625const char *column_bottom_reg::get_string() 626{ 627 return i_to_a(the_column->get_bottom().to_units()); 628} 629 630class column_extra_space_reg : public reg { 631public: 632 const char *get_string(); 633}; 634 635const char *column_extra_space_reg::get_string() 636{ 637 return i_to_a(the_column->get_last_extra_space().to_units()); 638} 639 640class column_active_reg : public reg { 641public: 642 const char *get_string(); 643}; 644 645const char *column_active_reg::get_string() 646{ 647 return the_column->is_active() ? "1" : "0"; 648} 649 650static int no_vjustify_mode = 0; 651 652class vjustify_node : public node { 653 symbol typ; 654public: 655 vjustify_node(symbol); 656 int reread(int *); 657 const char *type(); 658 int same(node *); 659 node *copy(); 660}; 661 662vjustify_node::vjustify_node(symbol t) 663: typ(t) 664{ 665} 666 667node *vjustify_node::copy() 668{ 669 return new vjustify_node(typ, div_nest_level); 670} 671 672const char *vjustify_node::type() 673{ 674 return "vjustify_node"; 675} 676 677int vjustify_node::same(node *nd) 678{ 679 return typ == ((vjustify_node *)nd)->typ; 680} 681 682int vjustify_node::reread(int *bolp) 683{ 684 curdiv->vjustify(typ); 685 *bolp = 1; 686 return 1; 687} 688 689void macro_diversion::vjustify(symbol type) 690{ 691 if (!no_vjustify_mode) 692 mac->append(new vjustify_node(type)); 693} 694 695void top_level_diversion::vjustify(symbol type) 696{ 697 if (no_space_mode || no_vjustify_mode) 698 return; 699 assert(first_page_begun); // I'm not sure about this. 700 the_output->vjustify(vertical_position, type); 701} 702 703void no_vjustify() 704{ 705 skip_line(); 706 no_vjustify_mode = 1; 707} 708 709void restore_vjustify() 710{ 711 skip_line(); 712 no_vjustify_mode = 0; 713} 714 715void init_column_requests() 716{ 717 the_column = new column; 718 init_request("cols", column_start); 719 init_request("colo", column_output); 720 init_request("colj", column_justify); 721 init_request("colr", column_reset); 722 init_request("colt", column_trim); 723 init_request("nvj", no_vjustify); 724 init_request("rvj", restore_vjustify); 725 number_reg_dictionary.define(".colb", new column_bottom_reg); 726 number_reg_dictionary.define(".colx", new column_extra_space_reg); 727 number_reg_dictionary.define(".cola", new column_active_reg); 728 number_reg_dictionary.define(".nvj", 729 new constant_int_reg(&no_vjustify_mode)); 730} 731 732#endif /* COLUMN */ 733