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