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