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