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