1/* $NetBSD$ */ 2 3// -*- C++ -*- 4/* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 5 * 6 * Gaius Mulley (gaius@glam.ac.uk) wrote html-table.cpp 7 * 8 * html-table.h 9 * 10 * provides the methods necessary to handle indentation and tab 11 * positions using html tables. 12 */ 13 14/* 15This file is part of groff. 16 17groff is free software; you can redistribute it and/or modify it under 18the terms of the GNU General Public License as published by the Free 19Software Foundation; either version 2, or (at your option) any later 20version. 21 22groff is distributed in the hope that it will be useful, but WITHOUT ANY 23WARRANTY; without even the implied warranty of MERCHANTABILITY or 24FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 25for more details. 26 27You should have received a copy of the GNU General Public License along 28with groff; see the file COPYING. If not, write to the Free Software 29Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 30 31#include "driver.h" 32#include "stringclass.h" 33#include "cset.h" 34#include "html-table.h" 35#include "ctype.h" 36#include "html.h" 37#include "html-text.h" 38 39#if !defined(TRUE) 40# define TRUE (1==1) 41#endif 42#if !defined(FALSE) 43# define FALSE (1==0) 44#endif 45 46tabs::tabs () 47 : tab(NULL) 48{ 49} 50 51tabs::~tabs () 52{ 53 delete_list(); 54} 55 56/* 57 * delete_list - frees the tab list and sets tab to NULL. 58 */ 59 60void tabs::delete_list (void) 61{ 62 tab_position *p = tab; 63 tab_position *q; 64 65 while (p != NULL) { 66 q = p; 67 p = p->next; 68 delete q; 69 } 70 tab = NULL; 71} 72 73void tabs::clear (void) 74{ 75 delete_list(); 76} 77 78/* 79 * compatible - returns TRUE if the tab stops in, s, do 80 * not conflict with the current tab stops. 81 * The new tab stops are _not_ placed into 82 * this class. 83 */ 84 85int tabs::compatible (const char *s) 86{ 87 char align; 88 int total=0; 89 tab_position *last = tab; 90 91 if (last == NULL) 92 return FALSE; // no tab stops defined 93 94 // move over tag name 95 while ((*s != (char)0) && !isspace(*s)) 96 s++; 97 98 while (*s != (char)0 && last != NULL) { 99 // move over white space 100 while ((*s != (char)0) && isspace(*s)) 101 s++; 102 // collect alignment 103 align = *s; 104 // move over alignment 105 s++; 106 // move over white space 107 while ((*s != (char)0) && isspace(*s)) 108 s++; 109 // collect tab position 110 total = atoi(s); 111 // move over tab position 112 while ((*s != (char)0) && !isspace(*s)) 113 s++; 114 if (last->alignment != align || last->position != total) 115 return FALSE; 116 117 last = last->next; 118 } 119 return TRUE; 120} 121 122/* 123 * init - scans the string, s, and initializes the tab stops. 124 */ 125 126void tabs::init (const char *s) 127{ 128 char align; 129 int total=0; 130 tab_position *last = NULL; 131 132 clear(); // remove any tab stops 133 134 // move over tag name 135 while ((*s != (char)0) && !isspace(*s)) 136 s++; 137 138 while (*s != (char)0) { 139 // move over white space 140 while ((*s != (char)0) && isspace(*s)) 141 s++; 142 // collect alignment 143 align = *s; 144 // move over alignment 145 s++; 146 // move over white space 147 while ((*s != (char)0) && isspace(*s)) 148 s++; 149 // collect tab position 150 total = atoi(s); 151 // move over tab position 152 while ((*s != (char)0) && !isspace(*s)) 153 s++; 154 if (last == NULL) { 155 tab = new tab_position; 156 last = tab; 157 } else { 158 last->next = new tab_position; 159 last = last->next; 160 } 161 last->alignment = align; 162 last->position = total; 163 last->next = NULL; 164 } 165} 166 167/* 168 * check_init - define tab stops using, s, providing none already exist. 169 */ 170 171void tabs::check_init (const char *s) 172{ 173 if (tab == NULL) 174 init(s); 175} 176 177/* 178 * find_tab - returns the tab number corresponding to the position, pos. 179 */ 180 181int tabs::find_tab (int pos) 182{ 183 tab_position *p; 184 int i=0; 185 186 for (p = tab; p != NULL; p = p->next) { 187 i++; 188 if (p->position == pos) 189 return i; 190 } 191 return 0; 192} 193 194/* 195 * get_tab_pos - returns the, nth, tab position 196 */ 197 198int tabs::get_tab_pos (int n) 199{ 200 tab_position *p; 201 202 n--; 203 for (p = tab; (p != NULL) && (n>0); p = p->next) { 204 n--; 205 if (n == 0) 206 return p->position; 207 } 208 return 0; 209} 210 211char tabs::get_tab_align (int n) 212{ 213 tab_position *p; 214 215 n--; 216 for (p = tab; (p != NULL) && (n>0); p = p->next) { 217 n--; 218 if (n == 0) 219 return p->alignment; 220 } 221 return 'L'; 222} 223 224/* 225 * dump_tab - display tab positions 226 */ 227 228void tabs::dump_tabs (void) 229{ 230 int i=1; 231 tab_position *p; 232 233 for (p = tab; p != NULL; p = p->next) { 234 printf("tab %d is %d\n", i, p->position); 235 i++; 236 } 237} 238 239/* 240 * html_table - methods 241 */ 242 243html_table::html_table (simple_output *op, int linelen) 244 : out(op), columns(NULL), linelength(linelen), last_col(NULL), start_space(FALSE) 245{ 246 tab_stops = new tabs(); 247} 248 249html_table::~html_table () 250{ 251 cols *c; 252 if (tab_stops != NULL) 253 delete tab_stops; 254 255 c = columns; 256 while (columns != NULL) { 257 columns = columns->next; 258 delete c; 259 c = columns; 260 } 261} 262 263/* 264 * remove_cols - remove a list of columns as defined by, c. 265 */ 266 267void html_table::remove_cols (cols *c) 268{ 269 cols *p; 270 271 while (c != NULL) { 272 p = c; 273 c = c->next; 274 delete p; 275 } 276} 277 278/* 279 * set_linelength - sets the line length value in this table. 280 * It also adds an extra blank column to the 281 * table should linelen exceed the last column. 282 */ 283 284void html_table::set_linelength (int linelen) 285{ 286 cols *p = NULL; 287 cols *c; 288 linelength = linelen; 289 290 for (c = columns; c != NULL; c = c->next) { 291 if (c->right > linelength) { 292 c->right = linelength; 293 remove_cols(c->next); 294 c->next = NULL; 295 return; 296 } 297 p = c; 298 } 299 if (p != NULL && p->right > 0) 300 add_column(p->no+1, p->right, linelength, 'L'); 301} 302 303/* 304 * get_effective_linelength - 305 */ 306 307int html_table::get_effective_linelength (void) 308{ 309 if (columns != NULL) 310 return linelength - columns->left; 311 else 312 return linelength; 313} 314 315/* 316 * add_indent - adds the indent to a table. 317 */ 318 319void html_table::add_indent (int indent) 320{ 321 if (columns != NULL && columns->left > indent) 322 add_column(0, indent, columns->left, 'L'); 323} 324 325/* 326 * emit_table_header - emits the html header for this table. 327 */ 328 329void html_table::emit_table_header (int space) 330{ 331 if (columns == NULL) 332 return; 333 334 // dump_table(); 335 336 last_col = NULL; 337 if (linelength > 0) { 338 out->nl(); 339 out->nl(); 340 341 out->put_string("<table width=\"100%\"") 342 .put_string(" border=0 rules=\"none\" frame=\"void\"\n") 343 .put_string(" cellspacing=\"0\" cellpadding=\"0\""); 344 out->put_string(">") 345 .nl(); 346 out->put_string("<tr valign=\"top\" align=\"left\""); 347 if (space) { 348 out->put_string(" style=\"margin-top: "); 349 out->put_string(STYLE_VERTICAL_SPACE); 350 out->put_string("\""); 351 } 352 out->put_string(">").nl(); 353 } 354} 355 356/* 357 * get_right - returns the right most position of this column. 358 */ 359 360int html_table::get_right (cols *c) 361{ 362 if (c != NULL && c->right > 0) 363 return c->right; 364 if (c->next != NULL) 365 return c->left; 366 return linelength; 367} 368 369/* 370 * set_space - assigns start_space. Used to determine the 371 * vertical alignment when generating the next table row. 372 */ 373 374void html_table::set_space (int space) 375{ 376 start_space = space; 377} 378 379/* 380 * emit_col - moves onto column, n. 381 */ 382 383void html_table::emit_col (int n) 384{ 385 cols *c = columns; 386 cols *b = columns; 387 int width = 0; 388 389 // must be a different row 390 if (last_col != NULL && n <= last_col->no) 391 emit_new_row(); 392 393 while (c != NULL && c->no < n) 394 c = c->next; 395 396 // can we find column, n? 397 if (c != NULL && c->no == n) { 398 // shutdown previous column 399 if (last_col != NULL) 400 out->put_string("</td>").nl(); 401 402 // find previous column 403 if (last_col == NULL) 404 b = columns; 405 else 406 b = last_col; 407 408 // have we a gap? 409 if (last_col != NULL) { 410 if (is_gap(b)) 411 out->put_string("<td width=\"") 412 .put_number(is_gap(b)) 413 .put_string("%\"></td>") 414 .nl(); 415 b = b->next; 416 } 417 418 // move across to column n 419 while (b != c) { 420 // we compute the difference after converting positions 421 // to avoid rounding errors 422 width = (get_right(b)*100 + get_effective_linelength()/2) 423 / get_effective_linelength() 424 - (b->left*100 + get_effective_linelength()/2) 425 /get_effective_linelength(); 426 if (width) 427 out->put_string("<td width=\"") 428 .put_number(width) 429 .put_string("%\"></td>") 430 .nl(); 431 // have we a gap? 432 if (is_gap(b)) 433 out->put_string("<td width=\"") 434 .put_number(is_gap(b)) 435 .put_string("%\"></td>") 436 .nl(); 437 b = b->next; 438 } 439 width = (get_right(b)*100 + get_effective_linelength()/2) 440 / get_effective_linelength() 441 - (b->left*100 + get_effective_linelength()/2) 442 /get_effective_linelength(); 443 switch (b->alignment) { 444 case 'C': 445 out->put_string("<td width=\"") 446 .put_number(width) 447 .put_string("%\" align=center>") 448 .nl(); 449 break; 450 case 'R': 451 out->put_string("<td width=\"") 452 .put_number(width) 453 .put_string("%\" align=right>") 454 .nl(); 455 break; 456 default: 457 out->put_string("<td width=\"") 458 .put_number(width) 459 .put_string("%\">") 460 .nl(); 461 } 462 // remember column, b 463 last_col = b; 464 } 465} 466 467/* 468 * finish_row - 469 */ 470 471void html_table::finish_row (void) 472{ 473 int n = 0; 474 cols *c; 475 476 if (last_col != NULL) { 477 for (c = last_col->next; c != NULL; c = c->next) 478 n = c->no; 479 480 if (n > 0) 481 emit_col(n); 482 out->put_string("</td>").nl(); 483 } 484} 485 486/* 487 * emit_new_row - move to the next row. 488 */ 489 490void html_table::emit_new_row (void) 491{ 492 finish_row(); 493 494 out->put_string("<tr valign=\"top\" align=\"left\""); 495 if (start_space) { 496 out->put_string(" style=\"margin-top: "); 497 out->put_string(STYLE_VERTICAL_SPACE); 498 out->put_string("\""); 499 } 500 out->put_string(">").nl(); 501 start_space = FALSE; 502 last_col = NULL; 503} 504 505void html_table::emit_finish_table (void) 506{ 507 finish_row(); 508 out->put_string("</table>"); 509} 510 511/* 512 * add_column - adds a column. It returns FALSE if hstart..hend 513 * crosses into a different columns. 514 */ 515 516int html_table::add_column (int coln, int hstart, int hend, char align) 517{ 518 cols *c = get_column(coln); 519 520 if (c == NULL) 521 return insert_column(coln, hstart, hend, align); 522 else 523 return modify_column(c, hstart, hend, align); 524} 525 526/* 527 * get_column - returns the column, coln. 528 */ 529 530cols *html_table::get_column (int coln) 531{ 532 cols *c = columns; 533 534 while (c != NULL && coln != c->no) 535 c = c->next; 536 537 if (c != NULL && coln == c->no) 538 return c; 539 else 540 return NULL; 541} 542 543/* 544 * insert_column - inserts a column, coln. 545 * It returns TRUE if it does not bump into 546 * another column. 547 */ 548 549int html_table::insert_column (int coln, int hstart, int hend, char align) 550{ 551 cols *c = columns; 552 cols *l = columns; 553 cols *n = NULL; 554 555 while (c != NULL && c->no < coln) { 556 l = c; 557 c = c->next; 558 } 559 if (l != NULL && l->no>coln && hend > l->left) 560 return FALSE; // new column bumps into previous one 561 562 l = NULL; 563 c = columns; 564 while (c != NULL && c->no < coln) { 565 l = c; 566 c = c->next; 567 } 568 569 if ((l != NULL) && (hstart < l->right)) 570 return FALSE; // new column bumps into previous one 571 572 if ((l != NULL) && (l->next != NULL) && 573 (l->next->left < hend)) 574 return FALSE; // new column bumps into next one 575 576 n = new cols; 577 if (l == NULL) { 578 n->next = columns; 579 columns = n; 580 } else { 581 n->next = l->next; 582 l->next = n; 583 } 584 n->left = hstart; 585 n->right = hend; 586 n->no = coln; 587 n->alignment = align; 588 return TRUE; 589} 590 591/* 592 * modify_column - given a column, c, modify the width to 593 * contain hstart..hend. 594 * It returns TRUE if it does not clash with 595 * the next or previous column. 596 */ 597 598int html_table::modify_column (cols *c, int hstart, int hend, char align) 599{ 600 cols *l = columns; 601 602 while (l != NULL && l->next != c) 603 l = l->next; 604 605 if ((l != NULL) && (hstart < l->right)) 606 return FALSE; // new column bumps into previous one 607 608 if ((c->next != NULL) && (c->next->left < hend)) 609 return FALSE; // new column bumps into next one 610 611 if (c->left > hstart) 612 c->left = hstart; 613 614 if (c->right < hend) 615 c->right = hend; 616 617 c->alignment = align; 618 619 return TRUE; 620} 621 622/* 623 * find_tab_column - finds the column number for position, pos. 624 * It searches through the list tab stops. 625 */ 626 627int html_table::find_tab_column (int pos) 628{ 629 // remember the first column is reserved for untabbed glyphs 630 return tab_stops->find_tab(pos)+1; 631} 632 633/* 634 * find_column - find the column number for position, pos. 635 * It searches through the list of columns. 636 */ 637 638int html_table::find_column (int pos) 639{ 640 int p=0; 641 cols *c; 642 643 for (c = columns; c != NULL; c = c->next) { 644 if (c->left > pos) 645 return p; 646 p = c->no; 647 } 648 return p; 649} 650 651/* 652 * no_columns - returns the number of table columns (rather than tabs) 653 */ 654 655int html_table::no_columns (void) 656{ 657 int n=0; 658 cols *c; 659 660 for (c = columns; c != NULL; c = c->next) 661 n++; 662 return n; 663} 664 665/* 666 * is_gap - returns the gap between column, c, and the next column. 667 */ 668 669int html_table::is_gap (cols *c) 670{ 671 if (c == NULL || c->right <= 0 || c->next == NULL) 672 return 0; 673 else 674 // we compute the difference after converting positions 675 // to avoid rounding errors 676 return (c->next->left*100 + get_effective_linelength()/2) 677 / get_effective_linelength() 678 - (c->right*100 + get_effective_linelength()/2) 679 / get_effective_linelength(); 680} 681 682/* 683 * no_gaps - returns the number of table gaps between the columns 684 */ 685 686int html_table::no_gaps (void) 687{ 688 int n=0; 689 cols *c; 690 691 for (c = columns; c != NULL; c = c->next) 692 if (is_gap(c)) 693 n++; 694 return n; 695} 696 697/* 698 * get_tab_pos - returns the, nth, tab position 699 */ 700 701int html_table::get_tab_pos (int n) 702{ 703 return tab_stops->get_tab_pos(n); 704} 705 706char html_table::get_tab_align (int n) 707{ 708 return tab_stops->get_tab_align(n); 709} 710 711 712void html_table::dump_table (void) 713{ 714 if (columns != NULL) { 715 cols *c; 716 for (c = columns; c != NULL; c = c->next) { 717 printf("column %d %d..%d %c\n", c->no, c->left, c->right, c->alignment); 718 } 719 } else 720 tab_stops->dump_tabs(); 721} 722 723/* 724 * html_indent - creates an indent with indentation, ind, given 725 * a line length of linelength. 726 */ 727 728html_indent::html_indent (simple_output *op, int ind, int pageoffset, int linelength) 729{ 730 table = new html_table(op, linelength); 731 732 table->add_column(1, ind+pageoffset, linelength, 'L'); 733 table->add_indent(pageoffset); 734 in = ind; 735 pg = pageoffset; 736 ll = linelength; 737} 738 739html_indent::~html_indent (void) 740{ 741 end(); 742 delete table; 743} 744 745void html_indent::begin (int space) 746{ 747 if (in + pg == 0) { 748 if (space) { 749 table->out->put_string(" style=\"margin-top: "); 750 table->out->put_string(STYLE_VERTICAL_SPACE); 751 table->out->put_string("\""); 752 } 753 } 754 else { 755 // 756 // we use exactly the same mechanism for calculating 757 // indentation as html_table::emit_col 758 // 759 table->out->put_string(" style=\"margin-left:") 760 .put_number(((in + pg) * 100 + ll/2) / ll - 761 (ll/2)/ll) 762 .put_string("%;"); 763 764 if (space) { 765 table->out->put_string(" margin-top: "); 766 table->out->put_string(STYLE_VERTICAL_SPACE); 767 } 768 table->out->put_string("\""); 769 } 770} 771 772void html_indent::end (void) 773{ 774} 775 776/* 777 * get_reg - collects the registers as supplied during initialization. 778 */ 779 780void html_indent::get_reg (int *ind, int *pageoffset, int *linelength) 781{ 782 *ind = in; 783 *pageoffset = pg; 784 *linelength = ll; 785} 786