Imported Upstream version 1.22.4
[platform/upstream/groff.git] / src / devices / grohtml / html-table.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 2002-2018  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 3 of the License, or
18 (at your option) any later 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
26 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
27
28 #include "driver.h"
29 #include "stringclass.h"
30 #include "cset.h"
31 #include "html-table.h"
32 #include "ctype.h"
33 #include "html.h"
34 #include "html-text.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
43 extern html_dialect dialect;
44
45
46 tabs::tabs ()
47   : tab(NULL)
48 {
49 }
50
51 tabs::~tabs ()
52 {
53   delete_list();
54 }
55
56 /*
57  *  delete_list - frees the tab list and sets tab to NULL.
58  */
59
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
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
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
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
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
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
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
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
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
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
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
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
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 && linelength > p->right)
300     add_column(p->no+1, p->right, linelength, 'L');
301 }
302
303 /*
304  *  get_effective_linelength -
305  */
306
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
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
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     if (dialect == xhtml)
347       emit_colspan();
348     out->put_string("<tr valign=\"top\" align=\"left\"");
349     if (space) {
350       out->put_string(" style=\"margin-top: ");
351       out->put_string(STYLE_VERTICAL_SPACE);
352       out->put_string("\"");
353     }
354     out->put_string(">").nl();
355   }
356 }
357
358 /*
359  *  get_right - returns the right most position of this column.
360  */
361
362 int html_table::get_right (cols *c)
363 {
364   if (c != NULL && c->right > 0)
365     return c->right;
366   if (c->next != NULL)
367     return c->left;
368   return linelength;
369 }
370
371 /*
372  *  set_space - assigns start_space. Used to determine the
373  *              vertical alignment when generating the next table row.
374  */
375
376 void html_table::set_space (int space)
377 {
378   start_space = space;
379 }
380
381 /*
382  *  emit_colspan - emits a series of colspan entries defining the
383  *                 table columns.
384  */
385
386 void html_table::emit_colspan (void)
387 {
388   cols *b = columns;
389   cols *c = columns;
390   int   width = 0;
391
392   out->put_string("<colgroup>");
393   while (c != NULL) {
394     if (b != NULL && b != c && is_gap(b))
395       /*
396        *   blank column for gap
397        */
398       out->put_string("<col width=\"")
399         .put_number(is_gap(b))
400         .put_string("%\" class=\"center\"></col>")
401         .nl();
402     
403     width = (get_right(c)*100 + get_effective_linelength()/2)
404               / get_effective_linelength()
405              - (c->left*100 + get_effective_linelength()/2)
406                 /get_effective_linelength();
407     switch (c->alignment) {
408     case 'C':
409       out->put_string("<col width=\"")
410           .put_number(width)
411           .put_string("%\" class=\"center\"></col>")
412           .nl();
413       break;
414     case 'R':
415       out->put_string("<col width=\"")
416           .put_number(width)
417           .put_string("%\" class=\"right\"></col>")
418           .nl();
419       break;
420     default:
421       out->put_string("<col width=\"")
422           .put_number(width)
423           .put_string("%\"></col>")
424           .nl();
425     }
426     b = c;
427     c = c->next;
428   }
429   out->put_string("</colgroup>").nl();
430 }
431
432 /*
433  *  emit_td - writes out a <td> tag with a corresponding width
434  *            if the dialect is html4.
435  */
436
437 void html_table::emit_td (int percentage, const char *s)
438 {
439   if (percentage) {
440     if (dialect == html4) {
441       out->put_string("<td width=\"")
442         .put_number(percentage)
443         .put_string("%\"");
444       if (s != NULL)
445         out->put_string(s);
446       out->nl();
447     }
448     else {
449       out->put_string("<td");
450       if (s != NULL)
451         out->put_string(s);
452       out->nl();
453     }
454   }
455 }
456
457 /*
458  *  emit_col - moves onto column, n.
459  */
460
461 void html_table::emit_col (int n)
462 {
463   cols *c = columns;
464   cols *b = columns;
465   int   width = 0;
466
467   // must be a different row
468   if (last_col != NULL && n <= last_col->no)
469     emit_new_row();
470
471   while (c != NULL && c->no < n)
472     c = c->next;
473
474   // can we find column, n?
475   if (c != NULL && c->no == n) {
476     // shutdown previous column
477     if (last_col != NULL)
478       out->put_string("</td>").nl();
479
480     // find previous column
481     if (last_col == NULL)
482       b = columns;
483     else
484       b = last_col;
485     
486     // have we a gap?
487     if (last_col != NULL) {
488       emit_td(is_gap(b), "></td>");
489       b = b->next;
490     }
491
492     // move across to column n
493     while (b != c) {
494       // we compute the difference after converting positions
495       // to avoid rounding errors
496       width = (get_right(b)*100 + get_effective_linelength()/2)
497                 / get_effective_linelength()
498               - (b->left*100 + get_effective_linelength()/2)
499                   /get_effective_linelength();
500       emit_td(width, "></td>");
501       // have we a gap?
502       emit_td(is_gap(b), "></td>");
503       b = b->next;
504     }
505     width = (get_right(b)*100 + get_effective_linelength()/2)
506               / get_effective_linelength()
507             - (b->left*100 + get_effective_linelength()/2)
508                 /get_effective_linelength();
509     switch (b->alignment) {
510     case 'C':
511       emit_td(width, " align=center>");
512       break;
513     case 'R':
514       emit_td(width, " align=right>");
515       break;
516     default:
517       emit_td(width);
518     }
519     // remember column, b
520     last_col = b;
521   }
522 }
523
524 /*
525  *  finish_row -
526  */
527
528 void html_table::finish_row (void)
529 {
530   int n = 0;
531   cols *c;
532
533   if (last_col != NULL) {
534     for (c = last_col->next; c != NULL; c = c->next)
535       n = c->no;
536     
537     if (n > 0)
538       emit_col(n);
539 #if 1
540     if (last_col != NULL) {
541       out->put_string("</td>");
542       last_col = NULL;
543     }
544 #endif
545     out->put_string("</tr>").nl();
546   }
547 }
548
549 /*
550  *  emit_new_row - move to the next row.
551  */
552
553 void html_table::emit_new_row (void)
554 {
555   finish_row();
556
557   out->put_string("<tr valign=\"top\" align=\"left\"");
558   if (start_space) {
559     out->put_string(" style=\"margin-top: ");
560     out->put_string(STYLE_VERTICAL_SPACE);
561     out->put_string("\"");
562   }
563   out->put_string(">").nl();
564   start_space = FALSE;
565   last_col = NULL;
566 }
567
568 void html_table::emit_finish_table (void)
569 {
570   finish_row();
571   out->put_string("</table>");
572 }
573
574 /*
575  *  add_column - adds a column. It returns FALSE if hstart..hend
576  *               crosses into a different columns.
577  */
578
579 int html_table::add_column (int coln, int hstart, int hend, char align)
580 {
581   cols *c = get_column(coln);
582
583   if (c == NULL)
584     return insert_column(coln, hstart, hend, align);
585   else
586     return modify_column(c, hstart, hend, align);
587 }
588
589 /*
590  *  get_column - returns the column, coln.
591  */
592
593 cols *html_table::get_column (int coln)
594 {
595   cols *c = columns;
596
597   while (c != NULL && coln != c->no)
598     c = c->next;
599
600   if (c != NULL && coln == c->no)
601     return c;
602   else
603     return NULL;
604 }
605
606 /*
607  *  insert_column - inserts a column, coln.
608  *                  It returns TRUE if it does not bump into
609  *                  another column.
610  */
611
612 int html_table::insert_column (int coln, int hstart, int hend, char align)
613 {
614   cols *c = columns;
615   cols *l = columns;
616   cols *n = NULL;
617
618   while (c != NULL && c->no < coln) {
619     l = c;
620     c = c->next;
621   }
622   if (l != NULL && l->no>coln && hend > l->left)
623     return FALSE;       // new column bumps into previous one
624
625   l = NULL;
626   c = columns;
627   while (c != NULL && c->no < coln) {
628     l = c;
629     c = c->next;
630   }
631
632   if ((l != NULL) && (hstart < l->right))
633     return FALSE;       // new column bumps into previous one
634   
635   if ((l != NULL) && (l->next != NULL) &&
636       (l->next->left < hend))
637     return FALSE;  // new column bumps into next one
638
639   n = new cols;
640   if (l == NULL) {
641     n->next = columns;
642     columns = n;
643   } else {
644     n->next = l->next;
645     l->next = n;
646   }
647   n->left = hstart;
648   n->right = hend;
649   n->no = coln;
650   n->alignment = align;
651   return TRUE;
652 }
653
654 /*
655  *  modify_column - given a column, c, modify the width to
656  *                  contain hstart..hend.
657  *                  It returns TRUE if it does not clash with
658  *                  the next or previous column.
659  */
660
661 int html_table::modify_column (cols *c, int hstart, int hend, char align)
662 {
663   cols *l = columns;
664
665   while (l != NULL && l->next != c)
666     l = l->next;
667
668   if ((l != NULL) && (hstart < l->right))
669     return FALSE;       // new column bumps into previous one
670   
671   if ((c->next != NULL) && (c->next->left < hend))
672     return FALSE;  // new column bumps into next one
673
674   if (c->left > hstart)
675     c->left = hstart;
676
677   if (c->right < hend)
678     c->right = hend;
679
680   c->alignment = align;
681
682   return TRUE;
683 }
684
685 /*
686  *  find_tab_column - finds the column number for position, pos.
687  *                    It searches through the list tab stops.
688  */
689
690 int html_table::find_tab_column (int pos)
691 {
692   // remember the first column is reserved for untabbed glyphs
693   return tab_stops->find_tab(pos)+1;
694 }
695
696 /*
697  *  find_column - find the column number for position, pos.
698  *                It searches through the list of columns.
699  */
700
701 int html_table::find_column (int pos)
702 {
703   int   p=0;
704   cols *c;
705
706   for (c = columns; c != NULL; c = c->next) {
707     if (c->left > pos)
708       return p;
709     p = c->no;
710   }
711   return p;
712 }
713
714 /*
715  *  no_columns - returns the number of table columns (rather than tabs)
716  */
717
718 int html_table::no_columns (void)
719 {
720   int n=0;
721   cols *c;
722
723   for (c = columns; c != NULL; c = c->next)
724     n++;
725   return n;
726 }
727
728 /*
729  *  is_gap - returns the gap between column, c, and the next column.
730  */
731
732 int html_table::is_gap (cols *c)
733 {
734   if (c == NULL || c->right <= 0 || c->next == NULL)
735     return 0;
736   else
737     // we compute the difference after converting positions
738     // to avoid rounding errors
739     return (c->next->left*100 + get_effective_linelength()/2)
740              / get_effective_linelength()
741            - (c->right*100 + get_effective_linelength()/2)
742                / get_effective_linelength();
743 }
744
745 /*
746  *  no_gaps - returns the number of table gaps between the columns
747  */
748
749 int html_table::no_gaps (void)
750 {
751   int n=0;
752   cols *c;
753
754   for (c = columns; c != NULL; c = c->next)
755     if (is_gap(c))
756       n++;
757   return n;
758 }
759
760 /*
761  *  get_tab_pos - returns the, nth, tab position
762  */
763
764 int html_table::get_tab_pos (int n)
765 {
766   return tab_stops->get_tab_pos(n);
767 }
768
769 char html_table::get_tab_align (int n)
770 {
771   return tab_stops->get_tab_align(n);
772 }
773
774
775 void html_table::dump_table (void)
776 {
777   if (columns != NULL) {
778     cols *c;
779     for (c = columns; c != NULL; c = c->next) {
780       printf("column %d  %d..%d  %c\n", c->no, c->left, c->right, c->alignment);
781     }
782   } else
783     tab_stops->dump_tabs();
784 }
785
786 /*
787  *  html_indent - creates an indent with indentation, ind, given
788  *                a line length of linelength.
789  */
790
791 html_indent::html_indent (simple_output *op, int ind, int pageoffset, int linelength)
792 {
793   table = new html_table(op, linelength);
794
795   table->add_column(1, ind+pageoffset, linelength, 'L');
796   table->add_indent(pageoffset);
797   in = ind;
798   pg = pageoffset;
799   ll = linelength;
800 }
801
802 html_indent::~html_indent (void)
803 {
804   end();
805   delete table;
806 }
807
808 void html_indent::begin (int space)
809 {
810   if (in + pg == 0) {
811     if (space) {
812       table->out->put_string(" style=\"margin-top: ");
813       table->out->put_string(STYLE_VERTICAL_SPACE);
814       table->out->put_string("\"");
815     }
816   }
817   else {
818     //
819     // we use exactly the same mechanism for calculating
820     // indentation as html_table::emit_col
821     //
822     table->out->put_string(" style=\"margin-left:")
823       .put_number(((in + pg) * 100 + ll/2) / ll -
824                   (ll/2)/ll)
825       .put_string("%;");
826
827     if (space) {
828       table->out->put_string(" margin-top: ");
829       table->out->put_string(STYLE_VERTICAL_SPACE);
830     }
831     table->out->put_string("\"");
832   }
833 }
834
835 void html_indent::end (void)
836 {
837 }
838
839 /*
840  *  get_reg - collects the registers as supplied during initialization.
841  */
842
843 void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
844 {
845   *ind = in;
846   *pageoffset = pg;
847   *linelength = ll;
848 }