groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / devices / grohtml / html-table.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2002, 2003, 2004, 2005, 2007, 2009
3 *    Free Software Foundation, Inc.
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 /*
14 This file is part of groff.
15
16 groff is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free
18 Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 groff is distributed in the hope that it will be useful, but WITHOUT ANY
22 WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24 for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>. */
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
44 extern html_dialect dialect;
45
46
47 tabs::tabs ()
48   : tab(NULL)
49 {
50 }
51
52 tabs::~tabs ()
53 {
54   delete_list();
55 }
56
57 /*
58  *  delete_list - frees the tab list and sets tab to NULL.
59  */
60
61 void 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;
69     delete q;
70   }
71   tab = NULL;
72 }
73
74 void 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
86 int 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
111     total = atoi(s);
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
127 void 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) {
156       tab = new tab_position;
157       last = tab;
158     } else {
159       last->next = new tab_position;
160       last = last->next;
161     }
162     last->alignment = align;
163     last->position = total;
164     last->next = NULL;    
165   }
166 }
167
168 /*
169  *  check_init - define tab stops using, s, providing none already exist.
170  */
171
172 void tabs::check_init (const char *s)
173 {
174   if (tab == NULL)
175     init(s);
176 }
177
178 /*
179  *  find_tab - returns the tab number corresponding to the position, pos.
180  */
181
182 int 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
199 int 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
212 char 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
229 void 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
244 html_table::html_table (simple_output *op, int linelen)
245   : out(op), columns(NULL), linelength(linelen), last_col(NULL), start_space(FALSE)
246 {
247   tab_stops = new tabs();
248 }
249
250 html_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;
259     delete c;
260     c = columns;
261   }
262 }
263
264 /*
265  *  remove_cols - remove a list of columns as defined by, c.
266  */
267
268 void html_table::remove_cols (cols *c)
269 {
270   cols *p;
271
272   while (c != NULL) {
273     p = c;
274     c = c->next;
275     delete p;
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
285 void 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   }
300   if (p != NULL && p->right > 0 && linelength > p->right)
301     add_column(p->no+1, p->right, linelength, 'L');
302 }
303
304 /*
305  *  get_effective_linelength -
306  */
307
308 int 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
320 void 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
330 void 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) {
339     out->nl();
340     out->nl();
341
342     out->put_string("<table width=\"100%\"")
343       .put_string(" border=\"0\" rules=\"none\" frame=\"void\"\n")
344       .put_string("       cellspacing=\"0\" cellpadding=\"0\"");
345     out->put_string(">")
346       .nl();
347     if (dialect == xhtml)
348       emit_colspan();
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();
356   }
357 }
358
359 /*
360  *  get_right - returns the right most position of this column.
361  */
362
363 int 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 /*
373  *  set_space - assigns start_space. Used to determine the
374  *              vertical alignment when generating the next table row.
375  */
376
377 void html_table::set_space (int space)
378 {
379   start_space = space;
380 }
381
382 /*
383  *  emit_colspan - emits a series of colspan entries defining the
384  *                 table columns.
385  */
386
387 void 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
438 void 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 /*
459  *  emit_col - moves onto column, n.
460  */
461
462 void 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) {
489       emit_td(is_gap(b), "></td>");
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();
501       emit_td(width, "></td>");
502       // have we a gap?
503       emit_td(is_gap(b), "></td>");
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':
512       emit_td(width, " align=center>");
513       break;
514     case 'R':
515       emit_td(width, " align=right>");
516       break;
517     default:
518       emit_td(width);
519     }
520     // remember column, b
521     last_col = b;
522   }
523 }
524
525 /*
526  *  finish_row -
527  */
528
529 void 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);
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();
547   }
548 }
549
550 /*
551  *  emit_new_row - move to the next row.
552  */
553
554 void html_table::emit_new_row (void)
555 {
556   finish_row();
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;
566   last_col = NULL;
567 }
568
569 void html_table::emit_finish_table (void)
570 {
571   finish_row();
572   out->put_string("</table>");
573 }
574
575 /*
576  *  add_column - adds a column. It returns FALSE if hstart..hend
577  *               crosses into a different columns.
578  */
579
580 int 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
594 cols *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
613 int html_table::insert_column (int coln, int hstart, int hend, char align)
614 {
615   cols *c = columns;
616   cols *l = columns;
617   cols *n = NULL;
618
619   while (c != NULL && c->no < coln) {
620     l = c;
621     c = c->next;
622   }
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
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
640   n = new cols;
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
662 int 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
691 int 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
702 int 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
719 int 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
733 int 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
750 int 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
765 int html_table::get_tab_pos (int n)
766 {
767   return tab_stops->get_tab_pos(n);
768 }
769
770 char html_table::get_tab_align (int n)
771 {
772   return tab_stops->get_tab_align(n);
773 }
774
775
776 void 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
792 html_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;
801 }
802
803 html_indent::~html_indent (void)
804 {
805   end();
806   delete table;
807 }
808
809 void html_indent::begin (int space)
810 {
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("\"");
833   }
834 }
835
836 void html_indent::end (void)
837 {
838 }
839
840 /*
841  *  get_reg - collects the registers as supplied during initialization.
842  */
843
844 void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
845 {
846   *ind = in;
847   *pageoffset = pg;
848   *linelength = ll;
849 }