xref: /netbsd-src/external/gpl2/groff/dist/src/devices/grohtml/html-table.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
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