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 /*
13 This file is part of groff.
14
15 groff is free software; you can redistribute it and/or modify it under
16 the terms of the GNU General Public License as published by the Free
17 Software Foundation; either version 2, or (at your option) any later
18 version.
19
20 groff is distributed in the hope that it will be useful, but WITHOUT ANY
21 WARRANTY; without even the implied warranty of MERCHANTABILITY or
22 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24
25 You should have received a copy of the GNU General Public License along
26 with groff; see the file COPYING. If not, write to the Free Software
27 Foundation, 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
tabs()44 tabs::tabs ()
45 : tab(NULL)
46 {
47 }
48
~tabs()49 tabs::~tabs ()
50 {
51 delete_list();
52 }
53
54 /*
55 * delete_list - frees the tab list and sets tab to NULL.
56 */
57
delete_list(void)58 void 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
clear(void)71 void 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
compatible(const char * s)83 int 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
init(const char * s)124 void 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
check_init(const char * s)169 void 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
find_tab(int pos)179 int 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
get_tab_pos(int n)196 int 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
get_tab_align(int n)209 char 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
dump_tabs(void)226 void 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
html_table(simple_output * op,int linelen)241 html_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
~html_table()247 html_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
remove_cols(cols * c)265 void 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
set_linelength(int linelen)282 void 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
get_effective_linelength(void)305 int 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
add_indent(int indent)317 void 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
emit_table_header(int space)327 void 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
get_right(cols * c)358 int 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
set_space(int space)372 void html_table::set_space (int space)
373 {
374 start_space = space;
375 }
376
377 /*
378 * emit_col - moves onto column, n.
379 */
380
emit_col(int n)381 void 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
finish_row(void)469 void 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
emit_new_row(void)488 void 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
emit_finish_table(void)503 void 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
add_column(int coln,int hstart,int hend,char align)514 int 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
get_column(int coln)528 cols *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
insert_column(int coln,int hstart,int hend,char align)547 int 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
modify_column(cols * c,int hstart,int hend,char align)596 int 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
find_tab_column(int pos)625 int 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
find_column(int pos)636 int 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
no_columns(void)653 int 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
is_gap(cols * c)667 int 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
no_gaps(void)684 int 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
get_tab_pos(int n)699 int html_table::get_tab_pos (int n)
700 {
701 return tab_stops->get_tab_pos(n);
702 }
703
get_tab_align(int n)704 char html_table::get_tab_align (int n)
705 {
706 return tab_stops->get_tab_align(n);
707 }
708
709
dump_table(void)710 void 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
html_indent(simple_output * op,int ind,int pageoffset,int linelength)726 html_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
~html_indent(void)737 html_indent::~html_indent (void)
738 {
739 end();
740 delete table;
741 }
742
begin(int space)743 void 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
end(void)770 void html_indent::end (void)
771 {
772 }
773
774 /*
775 * get_reg - collects the registers as supplied during initialization.
776 */
777
get_reg(int * ind,int * pageoffset,int * linelength)778 void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
779 {
780 *ind = in;
781 *pageoffset = pg;
782 *linelength = ll;
783 }
784