column.cpp revision 259065
119304Speter// -*- C++ -*- 219304Speter/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc. 319304Speter Written by James Clark (jjc@jclark.com) 419304Speter 519304SpeterThis file is part of groff. 619304Speter 719304Spetergroff is free software; you can redistribute it and/or modify it under 819304Speterthe terms of the GNU General Public License as published by the Free 919304SpeterSoftware Foundation; either version 2, or (at your option) any later 1019304Speterversion. 1119304Speter 1219304Spetergroff is distributed in the hope that it will be useful, but WITHOUT ANY 13254225SpeterWARRANTY; without even the implied warranty of MERCHANTABILITY or 1419304SpeterFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1519304Speterfor more details. 1619304Speter 1719304SpeterYou should have received a copy of the GNU General Public License along 1819304Speterwith groff; see the file COPYING. If not, write to the Free Software 1919304SpeterFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 2019304Speter 2119304Speter#ifdef COLUMN 2219304Speter 2319304Speter#include "troff.h" 2419304Speter#include "symbol.h" 2519304Speter#include "dictionary.h" 2619304Speter#include "hvunits.h" 2719304Speter#include "env.h" 2819304Speter#include "request.h" 2919304Speter#include "node.h" 3019304Speter#include "token.h" 3119304Speter#include "div.h" 3219304Speter#include "reg.h" 3319304Speter#include "stringclass.h" 3419304Speter 3519304Spetervoid output_file::vjustify(vunits, symbol) 3619304Speter{ 3719304Speter // do nothing 3819304Speter} 3919304Speter 4019304Speterstruct justification_spec; 4119304Speterstruct output_line; 4219304Speter 4319304Speterclass column : public output_file { 4419304Speterprivate: 4519304Speter output_file *out; 4619304Speter vunits bottom; 4719304Speter output_line *col; 4819304Speter output_line **tail; 4919304Speter void add_output_line(output_line *); 5019304Speter void begin_page(int pageno, vunits page_length); 5119304Speter void flush(); 5219304Speter void print_line(hunits, vunits, node *, vunits, vunits); 5319304Speter void vjustify(vunits, symbol); 5419304Speter void transparent_char(unsigned char c); 5519304Speter void copy_file(hunits, vunits, const char *); 5619304Speter int is_printing(); 5719304Speter void check_bottom(); 5819304Speterpublic: 5919304Speter column(); 6019304Speter ~column(); 6119304Speter void start(); 6219304Speter void output(); 6319304Speter void justify(const justification_spec &); 6419304Speter void trim(); 65254225Speter void reset(); 6619304Speter vunits get_bottom(); 6719304Speter vunits get_last_extra_space(); 6819304Speter int is_active() { return out != 0; } 69254225Speter}; 70254225Speter 7119304Spetercolumn *the_column = 0; 7219304Speter 7319304Speterstruct transparent_output_line; 7419304Speterstruct vjustify_output_line; 7519304Speter 7619304Speterclass output_line { 7719304Speter output_line *next; 7819304Speterpublic: 7919304Speter output_line(); 8019304Speter virtual ~output_line(); 8119304Speter virtual void output(output_file *, vunits); 8219304Speter virtual transparent_output_line *as_transparent_output_line(); 8319304Speter virtual vjustify_output_line *as_vjustify_output_line(); 8419304Speter virtual vunits distance(); 8519304Speter virtual vunits height(); 8619304Speter virtual void reset(); 8719304Speter virtual vunits extra_space(); // post line 8819304Speter friend class column; 8919304Speter friend class justification_spec; 9019304Speter}; 9119304Speter 9219304Speterclass position_output_line : public output_line { 9319304Speter vunits dist; 9419304Speterpublic: 9519304Speter position_output_line(vunits); 9619304Speter vunits distance(); 9719304Speter}; 9819304Speter 9919304Speterclass node_output_line : public position_output_line { 100254225Speter node *nd; 10119304Speter hunits page_offset; 10219304Speter vunits before; 10319304Speter vunits after; 10419304Speterpublic: 10519304Speter node_output_line(vunits, node *, hunits, vunits, vunits); 10619304Speter ~node_output_line(); 10719304Speter void output(output_file *, vunits); 10819304Speter vunits height(); 10919304Speter vunits extra_space(); 11019304Speter}; 11119304Speter 11219304Speterclass vjustify_output_line : public position_output_line { 11319304Speter vunits current; 11419304Speter symbol typ; 11519304Speterpublic: 11619304Speter vjustify_output_line(vunits dist, symbol); 11719304Speter vunits height(); 11819304Speter vjustify_output_line *as_vjustify_output_line(); 11919304Speter void vary(vunits amount); 12019304Speter void reset(); 12119304Speter symbol type(); 12219304Speter}; 12319304Speter 124254225Speterinline symbol vjustify_output_line::type() 12519304Speter{ 12619304Speter return typ; 12719304Speter} 12819304Speter 12919304Speterclass copy_file_output_line : public position_output_line { 13019304Speter symbol filename; 13119304Speter hunits hpos; 13219304Speterpublic: 13319304Speter copy_file_output_line(vunits, const char *, hunits); 13419304Speter void output(output_file *, vunits); 13519304Speter}; 13619304Speter 13719304Speterclass transparent_output_line : public output_line { 13819304Speter string buf; 13919304Speterpublic: 14019304Speter transparent_output_line(); 14119304Speter void output(output_file *, vunits); 14219304Speter void append_char(unsigned char c); 14319304Speter transparent_output_line *as_transparent_output_line(); 14419304Speter}; 14519304Speter 14619304Speteroutput_line::output_line() : next(0) 14719304Speter{ 14819304Speter} 14919304Speter 15019304Speteroutput_line::~output_line() 15119304Speter{ 15219304Speter} 15319304Speter 15419304Spetervoid output_line::reset() 15519304Speter{ 15619304Speter} 15719304Speter 15819304Spetertransparent_output_line *output_line::as_transparent_output_line() 15919304Speter{ 16019304Speter return 0; 16119304Speter} 16219304Speter 16319304Spetervjustify_output_line *output_line::as_vjustify_output_line() 16419304Speter{ 16519304Speter return 0; 16619304Speter} 16719304Speter 16819304Spetervoid output_line::output(output_file *, vunits) 16919304Speter{ 17019304Speter} 17119304Speter 172254225Spetervunits output_line::distance() 17319304Speter{ 17419304Speter return V0; 17519304Speter} 176254225Speter 177254225Spetervunits output_line::height() 17819304Speter{ 17919304Speter return V0; 18019304Speter} 18119304Speter 18219304Spetervunits output_line::extra_space() 18319304Speter{ 18419304Speter return V0; 18519304Speter} 18619304Speter 18719304Speterposition_output_line::position_output_line(vunits d) 18819304Speter: dist(d) 18919304Speter{ 19019304Speter} 19119304Speter 19219304Spetervunits position_output_line::distance() 19319304Speter{ 19419304Speter return dist; 19519304Speter} 19619304Speter 19719304Speternode_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a) 19819304Speter: position_output_line(d), nd(n), page_offset(po), before(b), after(a) 19919304Speter{ 20019304Speter} 20119304Speter 20219304Speternode_output_line::~node_output_line() 20319304Speter{ 20419304Speter delete_node_list(nd); 20519304Speter} 20619304Speter 20719304Spetervoid node_output_line::output(output_file *out, vunits pos) 20819304Speter{ 20919304Speter out->print_line(page_offset, pos, nd, before, after); 21019304Speter nd = 0; 21119304Speter} 21219304Speter 21319304Spetervunits node_output_line::height() 214254225Speter{ 21519304Speter return after; 21619304Speter} 21719304Speter 21819304Spetervunits node_output_line::extra_space() 21919304Speter{ 22019304Speter return after; 22119304Speter} 22219304Speter 22319304Spetervjustify_output_line::vjustify_output_line(vunits d, symbol t) 22419304Speter: position_output_line(d), typ(t) 22519304Speter{ 22619304Speter} 22719304Speter 22819304Spetervoid vjustify_output_line::reset() 22919304Speter{ 23019304Speter current = V0; 23119304Speter} 23219304Speter 23319304Spetervunits vjustify_output_line::height() 23419304Speter{ 23519304Speter return current; 23619304Speter} 23719304Speter 23819304Spetervjustify_output_line *vjustify_output_line::as_vjustify_output_line() 23919304Speter{ 24019304Speter return this; 24119304Speter} 24219304Speter 24319304Speterinline void vjustify_output_line::vary(vunits amount) 24419304Speter{ 24519304Speter current += amount; 24619304Speter} 24719304Speter 24819304Spetertransparent_output_line::transparent_output_line() 24919304Speter{ 25019304Speter} 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