1 /* $NetBSD: html-table.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $ */
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 /*
15 This file is part of groff.
16
17 groff is free software; you can redistribute it and/or modify it under
18 the terms of the GNU General Public License as published by the Free
19 Software Foundation; either version 2, or (at your option) any later
20 version.
21
22 groff is distributed in the hope that it will be useful, but WITHOUT ANY
23 WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 for more details.
26
27 You should have received a copy of the GNU General Public License along
28 with groff; see the file COPYING. If not, write to the Free Software
29 Foundation, 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
tabs()46 tabs::tabs ()
47 : tab(NULL)
48 {
49 }
50
~tabs()51 tabs::~tabs ()
52 {
53 delete_list();
54 }
55
56 /*
57 * delete_list - frees the tab list and sets tab to NULL.
58 */
59
delete_list(void)60 void 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
clear(void)73 void 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
compatible(const char * s)85 int 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
init(const char * s)126 void 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
check_init(const char * s)171 void 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
find_tab(int pos)181 int 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
get_tab_pos(int n)198 int 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
get_tab_align(int n)211 char 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
dump_tabs(void)228 void 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
html_table(simple_output * op,int linelen)243 html_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
~html_table()249 html_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
remove_cols(cols * c)267 void 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
set_linelength(int linelen)284 void 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
get_effective_linelength(void)307 int 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
add_indent(int indent)319 void 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
emit_table_header(int space)329 void 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
get_right(cols * c)360 int 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
set_space(int space)374 void html_table::set_space (int space)
375 {
376 start_space = space;
377 }
378
379 /*
380 * emit_col - moves onto column, n.
381 */
382
emit_col(int n)383 void 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
finish_row(void)471 void 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
emit_new_row(void)490 void 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
emit_finish_table(void)505 void 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
add_column(int coln,int hstart,int hend,char align)516 int 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
get_column(int coln)530 cols *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
insert_column(int coln,int hstart,int hend,char align)549 int 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
modify_column(cols * c,int hstart,int hend,char align)598 int 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
find_tab_column(int pos)627 int 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
find_column(int pos)638 int 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
no_columns(void)655 int 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
is_gap(cols * c)669 int 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
no_gaps(void)686 int 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
get_tab_pos(int n)701 int html_table::get_tab_pos (int n)
702 {
703 return tab_stops->get_tab_pos(n);
704 }
705
get_tab_align(int n)706 char html_table::get_tab_align (int n)
707 {
708 return tab_stops->get_tab_align(n);
709 }
710
711
dump_table(void)712 void 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
html_indent(simple_output * op,int ind,int pageoffset,int linelength)728 html_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
~html_indent(void)739 html_indent::~html_indent (void)
740 {
741 end();
742 delete table;
743 }
744
begin(int space)745 void 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
end(void)772 void html_indent::end (void)
773 {
774 }
775
776 /*
777 * get_reg - collects the registers as supplied during initialization.
778 */
779
get_reg(int * ind,int * pageoffset,int * linelength)780 void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
781 {
782 *ind = in;
783 *pageoffset = pg;
784 *linelength = ll;
785 }
786