Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / devices / grohtml / post-html.cc
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
3  *
4  *  Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc
5  *  but it owes a huge amount of ideas and raw code from
6  *  James Clark (jjc@jclark.com) grops/ps.cc.
7  */
8
9 /*
10 This file is part of groff.
11
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
16
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 for more details.
21
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING.  If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25
26 #include "driver.h"
27 #include "stringclass.h"
28 #include "cset.h"
29 #include "html.h"
30 #include "html-text.h"
31 #include "html-table.h"
32
33 #include <time.h>
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #include <stdio.h>
40 #include <fcntl.h>
41
42 extern "C" const char *Version_string;
43
44 #if !defined(TRUE)
45 #   define TRUE  (1==1)
46 #endif
47 #if !defined(FALSE)
48 #   define FALSE (1==0)
49 #endif
50
51 #define MAX_LINE_LENGTH                60            /* maximum characters we want in a line      */
52 #define SIZE_INCREMENT                  2            /* font size increment <big> = +2            */
53 #define BASE_POINT_SIZE                10            /* 10 points is the base size ie html size 3 */
54 #define CENTER_TOLERANCE                2            /* how many pixels off center will we still  */
55 #define ANCHOR_TEMPLATE         "heading"            /* if simple anchor is set we use this       */
56 #define UNICODE_DESC_START           0x80            /* all character entities above this are     */
57                                                      /* either encoded by their glyph names or if */
58                                                      /* there is no name then we use &#nnn;       */
59 typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
60 typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
61
62 #undef DEBUG_TABLES
63
64
65 /*
66  *  prototypes
67  */
68
69 char *get_html_translation (font *f, const string &name);
70 int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
71
72
73 static int auto_links = TRUE;                        /* by default we enable automatic links at  */
74                                                      /* top of the document.                     */
75 static int auto_rule  = TRUE;                        /* by default we enable an automatic rule   */
76                                                      /* at the top and bottom of the document    */
77 static int simple_anchors = FALSE;                   /* default to anchors with heading text     */
78 static int manufacture_headings = FALSE;             /* default is to use the Hn html headings,  */
79                                                      /* rather than manufacture our own.         */
80 static color *default_background = NULL;             /* has user requested initial bg color?     */
81
82
83 /*
84  *  start with a few favorites
85  */
86
87 void stop () {}
88
89 static int min (int a, int b)
90 {
91   if (a < b)
92     return a;
93   else
94     return b;
95 }
96
97 static int max (int a, int b)
98 {
99   if (a > b)
100     return a;
101   else
102     return b;
103 }
104
105 /*
106  *  is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
107  */
108
109 static int is_intersection (int a1, int a2, int b1, int b2)
110 {
111   // easier to prove NOT outside limits
112   return( ! ((a1 > b2) || (a2 < b1)) );
113 }
114
115 /*
116  *  is_digit - returns TRUE if character, ch, is a digit.
117  */
118
119 static int is_digit (char ch)
120 {
121   return( (ch >= '0') && (ch <= '9') );
122 }
123
124 /*
125  *  the classes and methods for maintaining a list of files.
126  */
127
128 struct file {
129   FILE    *fp;
130   file    *next;
131
132   file     (FILE *f);
133 };
134
135 /*
136  *  file - initialize all fields to NULL
137  */
138
139 file::file (FILE *f)
140   : fp(f), next(0)
141 {
142 }
143
144 class files {
145 public:
146             files         ();
147   FILE     *get_file      (void);
148   void      start_of_list (void);
149   void      move_next     (void);
150   void      add_new_file  (FILE *f);
151 private:
152   file     *head;
153   file     *tail;
154   file     *ptr;
155 };
156
157 /*
158  *  files - create an empty list of files.
159  */
160
161 files::files ()
162   : head(0), tail(0), ptr(0)
163 {
164 }
165
166 /*
167  *  get_file - returns the FILE associated with ptr.
168  */
169
170 FILE *files::get_file (void)
171 {
172   if (ptr) {
173     return( ptr->fp );
174   } else {
175     return( 0 );
176   }
177 }
178
179 /*
180  *  start_of_list - reset the ptr to the start of the list.
181  */
182
183 void files::start_of_list (void)
184 {
185   ptr = head;
186 }
187
188 /*
189  *  move_next - moves the ptr to the next element on the list.
190  */
191
192 void files::move_next (void)
193 {
194   if (ptr != 0)
195     ptr = ptr->next;
196 }
197
198 /*
199  *  add_new_file - adds a new file, f, to the list.
200  */
201
202 void files::add_new_file (FILE *f)
203 {
204   if (head == 0) {
205     head = new file(f);
206     tail = head;
207   } else {
208     tail->next = new file(f);
209     tail       = tail->next;
210   }
211   ptr = tail;
212 }
213
214 /*
215  *  the class and methods for styles
216  */
217
218 struct style {
219   font        *f;
220   int          point_size;
221   int          font_no;
222   int          height;
223   int          slant;
224   color        col;
225                style       ();
226                style       (font *, int, int, int, int, color);
227   int          operator == (const style &) const;
228   int          operator != (const style &) const;
229 };
230
231 style::style()
232   : f(0)
233 {
234 }
235
236 style::style(font *p, int sz, int h, int sl, int no, color c)
237   : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
238 {
239 }
240
241 int style::operator==(const style &s) const
242 {
243   return (f == s.f && point_size == s.point_size
244           && height == s.height && slant == s.slant && col == s.col);
245 }
246
247 int style::operator!=(const style &s) const
248 {
249   return !(*this == s);
250 }
251
252 /*
253  *  the class and methods for retaining ascii text
254  */
255
256 struct char_block {
257   enum { SIZE = 256 };
258   char         *buffer;
259   int           used;
260   char_block   *next;
261
262   char_block();
263   char_block::char_block(int length);
264 };
265
266 char_block::char_block()
267 : buffer(NULL), used(0), next(0)
268 {
269 }
270
271 char_block::char_block(int length)
272 : used(0), next(0)
273 {
274   buffer = (char *)malloc(max(length, char_block::SIZE));
275   if (buffer == NULL)
276     fatal("out of memory error");
277 }
278
279 class char_buffer {
280 public:
281   char_buffer();
282   ~char_buffer();
283   char  *add_string(const char *, unsigned int);
284   char  *add_string(const string &);
285 private:
286   char_block *head;
287   char_block *tail;
288 };
289
290 char_buffer::char_buffer()
291 : head(0), tail(0)
292 {
293 }
294
295 char_buffer::~char_buffer()
296 {
297   while (head != 0) {
298     char_block *temp = head;
299     head = head->next;
300     delete temp;
301   }
302 }
303
304 char *char_buffer::add_string (const char *s, unsigned int length)
305 {
306   int i=0;
307   unsigned int old_used;
308
309   if (s == NULL || length == 0)
310     return NULL;
311
312   if (tail == 0) {
313     tail = new char_block(length+1);
314     head = tail;
315   } else {
316     if (tail->used + length+1 > char_block::SIZE) {
317       tail->next  = new char_block(length+1);
318       tail        = tail->next;
319     }
320   }
321
322   old_used = tail->used;
323   do {
324     tail->buffer[tail->used] = s[i];
325     tail->used++;
326     i++;
327     length--;
328   } while (length>0);
329
330   // add terminating nul character
331
332   tail->buffer[tail->used] = '\0';
333   tail->used++;
334
335   // and return start of new string
336
337   return( &tail->buffer[old_used] );
338 }
339
340 char *char_buffer::add_string (const string &s)
341 {
342   return add_string(s.contents(), s.length());
343 }
344
345 /*
346  *  the classes and methods for maintaining glyph positions.
347  */
348
349 class text_glob {
350 public:
351   void text_glob_html      (style *s, char *str, int length,
352                             int min_vertical, int min_horizontal,
353                             int max_vertical, int max_horizontal);
354   void text_glob_special   (style *s, char *str, int length,
355                             int min_vertical, int min_horizontal,
356                             int max_vertical, int max_horizontal);
357   void text_glob_line      (style *s,
358                             int min_vertical, int min_horizontal,
359                             int max_vertical, int max_horizontal,
360                             int thickness);
361   void text_glob_auto_image(style *s, char *str, int length,
362                             int min_vertical, int min_horizontal,
363                             int max_vertical, int max_horizontal);
364   void text_glob_tag       (style *s, char *str, int length,
365                             int min_vertical, int min_horizontal,
366                             int max_vertical, int max_horizontal);
367                        
368   text_glob                (void);
369   ~text_glob               (void);
370   int  is_a_line           (void);
371   int  is_a_tag            (void);
372   int  is_eol              (void);
373   int  is_auto_img         (void);
374   int  is_br               (void);
375   int  is_in               (void);
376   int  is_po               (void);
377   int  is_ti               (void);
378   int  is_ce               (void);
379   int  is_eol_ce           (void);
380   int  is_col              (void);
381   int  is_tab              (void);
382   int  is_tab0             (void);
383   int  is_ta               (void);
384   int  is_tab_ts           (void);
385   int  is_tab_te           (void);
386   int  is_nf               (void);
387   int  is_fi               (void);
388   int  get_arg             (void);
389   int  get_tab_args        (char *align);
390
391   void        remember_table (html_table *t);
392   html_table *get_table      (void);
393
394   style           text_style;
395   const char     *text_string;
396   unsigned int    text_length;
397   int             minv, minh, maxv, maxh;
398   int             is_tag;               // is this a .br, .sp, .tl etc
399   int             is_img_auto;          // image created by eqn delim
400   int             is_special;           // text has come via 'x X html:'
401   int             is_line;              // is the command a <line>?
402   int             thickness;            // the thickness of a line
403   html_table     *tab;                  // table description
404
405 private:
406   text_glob           (style *s, char *str, int length,
407                        int min_vertical , int min_horizontal,
408                        int max_vertical , int max_horizontal,
409                        bool is_troff_command,
410                        bool is_auto_image, bool is_special_command,
411                        bool is_a_line    , int  thickness);
412 };
413
414 text_glob::text_glob (style *s, char *str, int length,
415                       int min_vertical, int min_horizontal,
416                       int max_vertical, int max_horizontal,
417                       bool is_troff_command,
418                       bool is_auto_image, bool is_special_command,
419                       bool is_a_line, int line_thickness)
420   : text_style(*s), text_string(str), text_length(length),
421     minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
422     is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
423     is_line(is_a_line), thickness(line_thickness), tab(NULL)
424 {
425 }
426
427 text_glob::text_glob ()
428   : text_string(0), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
429     is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
430 {
431 }
432
433 text_glob::~text_glob ()
434 {
435   if (tab != NULL)
436     delete tab;
437 }
438
439 /*
440  *  text_glob_html - used to place html text into the glob buffer.
441  */
442
443 void text_glob::text_glob_html (style *s, char *str, int length,
444                                 int min_vertical , int min_horizontal,
445                                 int max_vertical , int max_horizontal)
446 {
447   text_glob *g = new text_glob(s, str, length,
448                                min_vertical, min_horizontal, max_vertical, max_horizontal,
449                                FALSE, FALSE, FALSE, FALSE, 0);
450   *this = *g;
451 }
452
453 /*
454  *  text_glob_html - used to place html specials into the glob buffer.
455  *                   This text is essentially html commands coming through
456  *                   from the macro sets, with special designated sequences of
457  *                   characters translated into html. See add_and_encode.
458  */
459
460 void text_glob::text_glob_special (style *s, char *str, int length,
461                                    int min_vertical , int min_horizontal,
462                                    int max_vertical , int max_horizontal)
463 {
464   text_glob *g = new text_glob(s, str, length,
465                                min_vertical, min_horizontal, max_vertical, max_horizontal,
466                                FALSE, FALSE, TRUE, FALSE, 0);
467   *this = *g;
468 }
469
470 /*
471  *  text_glob_line - record horizontal draw line commands.
472  */
473
474 void text_glob::text_glob_line (style *s,
475                                 int min_vertical , int min_horizontal,
476                                 int max_vertical , int max_horizontal,
477                                 int thickness)
478 {
479   text_glob *g = new text_glob(s, "", 0,
480                                min_vertical, min_horizontal, max_vertical, max_horizontal,
481                                FALSE, FALSE, FALSE, TRUE, thickness);
482   *this = *g;
483 }
484
485 /*
486  *  text_glob_auto_image - record the presence of a .auto-image tag command.
487  *                         Used to mark that an image has been created automatically
488  *                         by a preprocessor and (pre-grohtml/troff) combination.
489  *                         Under some circumstances images may not be created.
490  *                         (consider .EQ
491  *                                   delim $$
492  *                                   .EN
493  *                                   .TS
494  *                                   tab(!), center;
495  *                                   l!l.
496  *                                   $1 over x$!recripical of x
497  *                                   .TE
498  *
499  *                          the first auto-image marker is created via .EQ/.EN pair
500  *                          and no image is created.
501  *                          The second auto-image marker occurs at $1 over x$
502  *                          Currently this image will not be created
503  *                          as the whole of the table is created as an image.
504  *                          (Once html tables are handled by grohtml this will change.
505  *                           Shortly this will be the case).
506  */
507
508 void text_glob::text_glob_auto_image(style *s, char *str, int length,
509                                      int min_vertical, int min_horizontal,
510                                      int max_vertical, int max_horizontal)
511 {
512   text_glob *g = new text_glob(s, str, length,
513                                min_vertical, min_horizontal, max_vertical, max_horizontal,
514                                TRUE, TRUE, FALSE, FALSE, 0);
515   *this = *g;
516 }
517
518 /*
519  *  text_glob_tag - records a troff tag.
520  */
521
522 void text_glob::text_glob_tag (style *s, char *str, int length,
523                                int min_vertical, int min_horizontal,
524                                int max_vertical, int max_horizontal)
525 {
526   text_glob *g = new text_glob(s, str, length,
527                                min_vertical, min_horizontal, max_vertical, max_horizontal,
528                                TRUE, FALSE, FALSE, FALSE, 0);
529   *this = *g;
530 }
531
532 /*
533  *  is_a_line - returns TRUE if glob should be converted into an <hr>
534  */
535
536 int text_glob::is_a_line (void)
537 {
538   return is_line;
539 }
540
541 /*
542  *  is_a_tag - returns TRUE if glob contains a troff directive.
543  */
544
545 int text_glob::is_a_tag (void)
546 {
547   return is_tag;
548 }
549
550 /*
551  *  is_eol - returns TRUE if glob contains the tag eol
552  */
553
554 int text_glob::is_eol (void)
555 {
556   return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) );
557 }
558
559 /*
560  *  is_eol_ce - returns TRUE if glob contains the tag eol.ce
561  */
562
563 int text_glob::is_eol_ce (void)
564 {
565   return( is_tag && (strcmp(text_string, "html-tag:eol.ce") == 0) );
566 }
567
568
569 /*
570  *  is_nf - returns TRUE if glob contains the tag .nf
571  */
572
573 int text_glob::is_nf (void)
574 {
575   return( is_tag && (strcmp(text_string, "html-tag:.nf") == 0) );
576 }
577
578 /*
579  *  is_fi - returns TRUE if glob contains the tag .fi
580  */
581
582 int text_glob::is_fi (void)
583 {
584   return( is_tag && (strcmp(text_string, "html-tag:.fi") == 0) );
585 }
586
587 /*
588  *  is_ce - returns TRUE if glob contains the tag .ce
589  */
590
591 int text_glob::is_ce (void)
592 {
593   return( is_tag && (strcmp(text_string, "html-tag:.ce") == 0) );
594 }
595
596 /*
597  *  is_in - returns TRUE if glob contains the tag .in
598  */
599
600 int text_glob::is_in (void)
601 {
602   return( is_tag && (strncmp(text_string, "html-tag:.in ", strlen("html-tag:.in ")) == 0) );
603 }
604
605 /*
606  *  is_po - returns TRUE if glob contains the tag .po
607  */
608
609 int text_glob::is_po (void)
610 {
611   return( is_tag && (strncmp(text_string, "html-tag:.po ", strlen("html-tag:.po ")) == 0) );
612 }
613
614 /*
615  *  is_ti - returns TRUE if glob contains the tag .ti
616  */
617
618 int text_glob::is_ti (void)
619 {
620   return( is_tag && (strncmp(text_string, "html-tag:.ti ", strlen("html-tag:.ti ")) == 0) );
621 }
622
623 /*
624  *  is_col - returns TRUE if glob contains the tag .col
625  */
626
627 int text_glob::is_col (void)
628 {
629   return( is_tag && (strncmp(text_string, "html-tag:.col", strlen("html-tag:.col")) == 0) );
630 }
631
632 /*
633  *  is_tab_ts - returns TRUE if glob contains the tag .tab_ts
634  */
635
636 int text_glob::is_tab_ts (void)
637 {
638   return( is_tag && (strcmp(text_string, "html-tag:.tab-ts") == 0) );
639 }
640
641 /*
642  *  is_tab_te - returns TRUE if glob contains the tag .tab_te
643  */
644
645 int text_glob::is_tab_te (void)
646 {
647   return( is_tag && (strcmp(text_string, "html-tag:.tab-te") == 0) );
648 }
649
650 /*
651  *  is_ta - returns TRUE if glob contains the tag .ta
652  */
653
654 int text_glob::is_ta (void)
655 {
656   return( is_tag && (strncmp(text_string, "html-tag:.ta ", strlen("html-tag:.ta ")) == 0) );
657 }
658
659 /*
660  *  is_tab - returns TRUE if glob contains the tag tab
661  */
662
663 int text_glob::is_tab (void)
664 {
665   return( is_tag && (strncmp(text_string, "html-tag:tab ", strlen("html-tag:tab ")) == 0) );
666 }
667
668 /*
669  *  is_tab0 - returns TRUE if glob contains the tag tab0
670  */
671
672 int text_glob::is_tab0 (void)
673 {
674   return( is_tag && (strncmp(text_string, "html-tag:tab0", strlen("html-tag:tab0")) == 0) );
675 }
676
677 /*
678  *  is_auto_img - returns TRUE if the glob contains an automatically
679  *                generated image.
680  */
681
682 int text_glob::is_auto_img (void)
683 {
684   return is_img_auto;
685 }
686
687 /*
688  *  is_br - returns TRUE if the glob is a tag containing a .br
689  *          or an implied .br. Note that we do not include .nf or .fi
690  *          as grohtml will place a .br after these commands if they
691  *          should break the line.
692  */
693
694 int text_glob::is_br (void)
695 {
696   return( is_a_tag() && ((strcmp ("html-tag:.br", text_string) == 0) ||
697                          (strncmp("html-tag:.sp", text_string, 11) == 0) ||
698                          (strcmp ("html-tag:.ce", text_string) == 0)) );
699 }
700
701 int text_glob::get_arg (void)
702 {
703   if (strncmp("html-tag:", text_string, strlen("html-tag:")) == 0) {
704     const char *p = text_string;
705
706     while ((*p != (char)0) && (!isspace(*p)))
707       p++;
708     while ((*p != (char)0) && (isspace(*p)))
709       p++;
710     if (*p == (char)0)
711       return -1;
712     return atoi(p);
713   }
714   return -1;
715 }
716
717 /*
718  *  get_tab_args - returns the tab position and alignment of the tab tag
719  */
720
721 int text_glob::get_tab_args (char *align)
722 {
723   if (strncmp("html-tag:", text_string, strlen("html-tag:")) == 0) {
724     const char *p = text_string;
725
726     // firstly the alignment C|R|L
727     while ((*p != (char)0) && (!isspace(*p)))
728       p++;
729     while ((*p != (char)0) && (isspace(*p)))
730       p++;
731     *align = *p;
732     // now the int value
733     while ((*p != (char)0) && (!isspace(*p)))
734       p++;
735     while ((*p != (char)0) && (isspace(*p)))
736       p++;
737     if (*p == (char)0)
738       return -1;
739     return atoi(p);
740   }
741   return -1;
742 }
743
744 /*
745  *  remember_table - saves table, t, in the text_glob.
746  */
747
748 void text_glob::remember_table (html_table *t)
749 {
750   tab = t;
751 }
752
753 /*
754  *  get_table - returns the stored table description.
755  */
756
757 html_table *text_glob::get_table (void)
758 {
759   return tab;
760 }
761
762 /*
763  *  the class and methods used to construct ordered double linked lists.
764  *  In a previous implementation we used templates via #include "ordered-list.h",
765  *  but this does assume that all C++ compilers can handle this feature. Pragmatically
766  *  it is safer to assume this is not the case.
767  */
768
769 struct element_list {
770   element_list *right;
771   element_list *left;
772   text_glob    *datum;
773   int           lineno;
774   int           minv, minh, maxv, maxh;
775
776   element_list  (text_glob *d,
777                  int line_number,
778                  int min_vertical, int min_horizontal,
779                  int max_vertical, int max_horizontal);
780   element_list  ();
781 };
782
783 element_list::element_list ()
784   : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
785 {
786 }
787
788 /*
789  *  element_list - create a list element assigning the datum and region parameters.
790  */
791
792 element_list::element_list (text_glob *in,
793                             int line_number,
794                             int min_vertical, int min_horizontal,
795                             int max_vertical, int max_horizontal)
796   : right(0), left(0), datum(in), lineno(line_number),
797     minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
798 {
799 }
800
801 class list {
802 public:
803        list             ();
804       ~list             ();
805   int  is_less          (element_list *a, element_list *b);
806   void add              (text_glob *in,
807                          int line_number,
808                          int min_vertical, int min_horizontal,
809                          int max_vertical, int max_horizontal);
810   void                  sub_move_right      (void);
811   void                  move_right          (void);
812   void                  move_left           (void);
813   int                   is_empty            (void);
814   int                   is_equal_to_tail    (void);
815   int                   is_equal_to_head    (void);
816   void                  start_from_head     (void);
817   void                  start_from_tail     (void);
818   void                  insert              (text_glob *in);
819   void                  move_to             (text_glob *in);
820   text_glob            *move_right_get_data (void);
821   text_glob            *move_left_get_data  (void);
822   text_glob            *get_data            (void);
823 private:
824   element_list *head;
825   element_list *tail;
826   element_list *ptr;
827 };
828
829 /*
830  *  list - construct an empty list.
831  */
832
833 list::list ()
834   : head(0), tail(0), ptr(0)
835 {
836 }
837
838 /*
839  *  ~list - destroy a complete list.
840  */
841
842 list::~list()
843 {
844   element_list *temp=head;
845
846   do {
847     temp = head;
848     if (temp != 0) {
849       head = head->right;
850       delete temp;
851     }
852   } while ((head != 0) && (head != tail));
853 }
854
855 /*
856  *  is_less - returns TRUE if a is left of b if on the same line or
857  *            if a is higher up the page than b.
858  */
859
860 int list::is_less (element_list *a, element_list *b)
861 {
862   // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
863   if (a->lineno < b->lineno) {
864     return( TRUE );
865   } else if (a->lineno > b->lineno) {
866     return( FALSE );
867   } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
868     return( a->minh < b->minh );
869   } else {
870     return( a->maxv < b->maxv );
871   }
872 }
873
874 /*
875  *  add - adds a datum to the list in the order specified by the region position.
876  */
877
878 void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
879 {
880   // create a new list element with datum and position fields initialized
881   element_list *t    = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
882   element_list *last;
883
884   if (head == 0) {
885     head     = t;
886     tail     = t;
887     ptr      = t;
888     t->left  = t;
889     t->right = t;
890   } else {
891     last = tail;
892
893     while ((last != head) && (is_less(t, last))) {
894       last = last->left;
895     }
896
897     if (is_less(t, last)) {
898       t->right          = last;
899       last->left->right = t;
900       t->left           = last->left;
901       last->left        = t;
902       // now check for a new head
903       if (last == head) {
904         head = t;
905       }
906     } else {
907       // add t beyond last
908       t->right          = last->right;
909       t->left           = last;
910       last->right->left = t;
911       last->right       = t;
912       // now check for a new tail
913       if (last == tail) {
914         tail = t;
915       }
916     }
917   }
918 }
919
920 /*
921  *  sub_move_right - removes the element which is currently pointed to by ptr
922  *                   from the list and moves ptr to the right.
923  */
924
925 void list::sub_move_right (void)
926 {
927   element_list *t=ptr->right;
928
929   if (head == tail) {
930     head = 0;
931     if (tail != 0) {
932       delete tail;
933     }
934     tail = 0;
935     ptr  = 0;
936   } else {
937     if (head == ptr) {
938       head = head->right;
939     }
940     if (tail == ptr) {
941       tail = tail->left;
942     }
943     ptr->left->right = ptr->right;
944     ptr->right->left = ptr->left;
945     ptr=t;
946   }
947 }
948
949 /*
950  *  start_from_head - assigns ptr to the head.
951  */
952
953 void list::start_from_head (void)
954 {
955   ptr = head;
956 }
957
958 /*
959  *  start_from_tail - assigns ptr to the tail.
960  */
961
962 void list::start_from_tail (void)
963 {
964   ptr = tail;
965 }
966
967 /*
968  *  is_empty - returns TRUE if the list has no elements.
969  */
970
971 int list::is_empty (void)
972 {
973   return( head == 0 );
974 }
975
976 /*
977  *  is_equal_to_tail - returns TRUE if the ptr equals the tail.
978  */
979
980 int list::is_equal_to_tail (void)
981 {
982   return( ptr == tail );
983 }
984
985 /*
986  *  is_equal_to_head - returns TRUE if the ptr equals the head.
987  */
988
989 int list::is_equal_to_head (void)
990 {
991   return( ptr == head );
992 }
993
994 /*
995  *  move_left - moves the ptr left.
996  */
997
998 void list::move_left (void)
999 {
1000   ptr = ptr->left;
1001 }
1002
1003 /*
1004  *  move_right - moves the ptr right.
1005  */
1006
1007 void list::move_right (void)
1008 {
1009   ptr = ptr->right;
1010 }
1011
1012 /*
1013  *  get_datum - returns the datum referenced via ptr.
1014  */
1015
1016 text_glob* list::get_data (void)
1017 {
1018   return( ptr->datum );
1019 }
1020
1021 /*
1022  *  move_right_get_data - returns the datum referenced via ptr and moves
1023  *                        ptr right.
1024  */
1025
1026 text_glob* list::move_right_get_data (void)
1027 {
1028   ptr = ptr->right;
1029   if (ptr == head) {
1030     return( 0 );
1031   } else {
1032     return( ptr->datum );
1033   }
1034 }
1035
1036 /*
1037  *  move_left_get_data - returns the datum referenced via ptr and moves
1038  *                       ptr right.
1039  */
1040
1041 text_glob* list::move_left_get_data (void)
1042 {
1043   ptr = ptr->left;
1044   if (ptr == tail) {
1045     return( 0 );
1046   } else {
1047     return( ptr->datum );
1048   }
1049 }
1050
1051 /*
1052  *  insert - inserts data after the current position.
1053  */
1054
1055 void list::insert (text_glob *in)
1056 {
1057   if (is_empty())
1058     fatal("list must not be empty if we are inserting data");
1059   else {
1060     if (ptr == 0)
1061       ptr = head;
1062     
1063     element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1064     if (ptr == tail)
1065       tail = t;
1066     ptr->right->left = t;
1067     t->right = ptr->right;
1068     ptr->right = t;
1069     t->left = ptr;
1070   }
1071 }
1072
1073 /*
1074  *  move_to - moves the current position to the point where data, in, exists.
1075  *            This is an expensive method and should be used sparingly.
1076  */
1077
1078 void list::move_to (text_glob *in)
1079 {
1080   ptr = head;
1081   while (ptr != tail && ptr->datum != in)
1082     ptr = ptr->right;
1083 }
1084
1085 /*
1086  *  page class and methods
1087  */
1088
1089 class page {
1090 public:
1091                               page            (void);
1092   void                        add             (style *s, const string &str,
1093                                                int line_number,
1094                                                int min_vertical, int min_horizontal,
1095                                                int max_vertical, int max_horizontal);
1096   void                        add_tag         (style *s, const string &str,
1097                                                int line_number,
1098                                                int min_vertical, int min_horizontal,
1099                                                int max_vertical, int max_horizontal);
1100   void                        add_and_encode  (style *s, const string &str,
1101                                                int line_number,
1102                                                int min_vertical, int min_horizontal,
1103                                                int max_vertical, int max_horizontal);
1104   void                        add_line        (style *s,
1105                                                int line_number,
1106                                                int x1, int y1, int x2, int y2,
1107                                                int thickness);
1108   void                        insert_tag      (const string &str);
1109   void                        dump_page       (void);   // debugging method
1110
1111   // and the data
1112
1113   list                        glyphs;         // position of glyphs and specials on page
1114   char_buffer                 buffer;         // all characters for this page
1115 };
1116
1117 page::page()
1118 {
1119 }
1120
1121 /*
1122  *  insert_tag - inserts a tag after the current position.
1123  */
1124
1125 void page::insert_tag (const string &str)
1126 {
1127   if (str.length() > 0) {
1128     text_glob *g=new text_glob();
1129     text_glob *f=glyphs.get_data();
1130     g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1131                      f->minv, f->minh, f->maxv, f->maxh);
1132     glyphs.insert(g);
1133   }
1134 }
1135
1136 /*
1137  *  add - add html text to the list of glyphs.
1138  */
1139
1140 void page::add (style *s, const string &str,
1141                 int line_number,
1142                 int min_vertical, int min_horizontal,
1143                 int max_vertical, int max_horizontal)
1144 {
1145   if (str.length() > 0) {
1146     text_glob *g=new text_glob();
1147     g->text_glob_html(s, buffer.add_string(str), str.length(),
1148                       min_vertical, min_horizontal, max_vertical, max_horizontal);
1149     glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1150   }
1151 }
1152
1153 /*
1154  *  add_tag - adds a troff tag, for example: .tl .sp .br
1155  */
1156
1157 void page::add_tag (style *s, const string &str,
1158                     int line_number,
1159                     int min_vertical, int min_horizontal,
1160                     int max_vertical, int max_horizontal)
1161 {
1162   if (str.length() > 0) {
1163     text_glob *g;
1164
1165     if (strncmp((str+'\0').contents(), "html-tag:.auto-image", 20) == 0) {
1166       g = new text_glob();
1167       g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1168                               min_vertical, min_horizontal, max_vertical, max_horizontal);
1169     } else {
1170       g = new text_glob();
1171       g->text_glob_tag(s, buffer.add_string(str), str.length(),
1172                        min_vertical, min_horizontal, max_vertical, max_horizontal);
1173     }
1174     glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1175   }
1176 }
1177
1178 /*
1179  *  add_line - adds the <line> primitive providing that y1==y2
1180  */
1181
1182 void page::add_line (style *s,
1183                      int line_number,
1184                      int x1, int y1, int x2, int y2,
1185                      int thickness)
1186 {
1187   if (y1 == y2) {
1188     text_glob *g = new text_glob();
1189     g->text_glob_line(s,
1190                       min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2),
1191                       thickness);
1192     glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2));
1193   }
1194 }
1195
1196 /*
1197  *  to_unicode - returns a unicode translation of int, ch.
1198  */
1199
1200 static char *to_unicode (unsigned int ch)
1201 {
1202   static char buf[30];
1203
1204   sprintf(buf, "&#%u;", ch);
1205   return buf;
1206 }
1207
1208 /*
1209  *  add_and_encode - adds a special string to the page, it translates the string
1210  *                   into html glyphs. The special string will have come from x X html:
1211  *                   and can contain troff character encodings which appear as
1212  *                   \(char\). A sequence of \\ represents \.
1213  *                   So for example we can write:
1214  *                      "cost = \(Po\)3.00 file = \\foo\\bar"
1215  *                   which is translated into:
1216  *                      "cost = &pound;3.00 file = \foo\bar"
1217  */
1218
1219 void page::add_and_encode (style *s, const string &str,
1220                            int line_number,
1221                            int min_vertical, int min_horizontal,
1222                            int max_vertical, int max_horizontal)
1223 {
1224   string html_string;
1225   char *html_glyph;
1226   int i=0;
1227
1228   if (s->f == NULL)
1229     return;
1230   while (i < str.length()) {
1231     if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
1232       // start of escape
1233       i += 2; // move over \(
1234       int a = i;
1235       while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
1236         i++;
1237       }
1238       int n = i;
1239       if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
1240         i++;
1241       else
1242         n = -1;
1243       if (n > 0) {
1244         string troff_charname = str.substring(a, n-a);
1245         html_glyph = get_html_translation(s->f, troff_charname);
1246         if (html_glyph)
1247           html_string += html_glyph;
1248         else {
1249           int index=s->f->name_to_index((troff_charname + '\0').contents());
1250           
1251           if (s->f->contains(index) && (index != 0))
1252             html_string += s->f->get_code(index);
1253         }
1254       }
1255     } else
1256       html_string += str[i];
1257     i++;
1258   }
1259   if (html_string.length() > 0) {
1260     text_glob *g=new text_glob();
1261     g->text_glob_special(s, buffer.add_string(html_string), html_string.length(),
1262                          min_vertical, min_horizontal, max_vertical, max_horizontal);
1263     glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1264   }
1265 }
1266
1267 /*
1268  *  dump_page - dump the page contents for debugging purposes.
1269  */
1270
1271 void page::dump_page(void)
1272 {
1273 #if defined(DEBUG_TABLES)
1274   text_glob *old_pos = glyphs.get_data();
1275   text_glob *g;
1276
1277   printf("\n<!--\n");
1278   printf("\n\ndebugging start\n");
1279   glyphs.start_from_head();
1280   do {
1281     g = glyphs.get_data();
1282     if (g->is_tab_ts()) {
1283       printf("\n\n");
1284       if (g->get_table() != NULL)
1285         g->get_table()->dump_table();
1286     }
1287     printf("%s ", g->text_string);
1288     if (g->is_tab_te())
1289       printf("\n\n");
1290     glyphs.move_right();
1291   } while (! glyphs.is_equal_to_head());
1292   glyphs.move_to(old_pos);
1293   printf("\ndebugging end\n\n");
1294   printf("\n-->\n");
1295   fflush(stdout);
1296 #endif
1297 }
1298
1299 /*
1300  *  font classes and methods
1301  */
1302
1303 class html_font : public font {
1304   html_font(const char *);
1305 public:
1306   int encoding_index;
1307   char *encoding;
1308   char *reencoded_name;
1309   ~html_font();
1310   static html_font *load_html_font(const char *);
1311 };
1312
1313 html_font *html_font::load_html_font(const char *s)
1314 {
1315   html_font *f = new html_font(s);
1316   if (!f->load()) {
1317     delete f;
1318     return 0;
1319   }
1320   return f;
1321 }
1322
1323 html_font::html_font(const char *nm)
1324 : font(nm)
1325 {
1326 }
1327
1328 html_font::~html_font()
1329 {
1330 }
1331
1332 /*
1333  *  a simple class to contain the header to this document
1334  */
1335
1336 class title_desc {
1337 public:
1338           title_desc ();
1339          ~title_desc ();
1340
1341   int     has_been_written;
1342   int     has_been_found;
1343   int     with_h1;
1344   string  text;
1345 };
1346
1347
1348 title_desc::title_desc ()
1349   : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1350 {
1351 }
1352
1353 title_desc::~title_desc ()
1354 {
1355 }
1356
1357 class header_desc {
1358 public:
1359                             header_desc ();
1360                            ~header_desc ();
1361
1362   int                       no_of_headings;      // how many headings have we found?
1363   char_buffer               headings;            // all the headings used in the document
1364   list                      headers;             // list of headers built from .NH and .SH
1365   int                       header_level;        // current header level
1366   int                       written_header;      // have we written the header yet?
1367   string                    header_buffer;       // current header text
1368
1369   void                      write_headings (FILE *f, int force);
1370 };
1371
1372 header_desc::header_desc ()
1373   :   no_of_headings(0), header_level(2), written_header(0)
1374 {
1375 }
1376
1377 header_desc::~header_desc ()
1378 {
1379 }
1380
1381 /*
1382  *  write_headings - emits a list of links for the headings in this document
1383  */
1384
1385 void header_desc::write_headings (FILE *f, int force)
1386 {
1387   text_glob *g;
1388
1389   if (auto_links || force) {
1390     if (! headers.is_empty()) {
1391       int h=1;
1392
1393       headers.start_from_head();
1394       do {
1395         g = headers.get_data();
1396         fputs("<a href=\"#", f);
1397         if (simple_anchors) {
1398           string buffer(ANCHOR_TEMPLATE);
1399
1400           buffer += as_string(h);
1401           buffer += '\0';
1402           fprintf(f, buffer.contents());
1403         } else
1404           fputs(g->text_string, f);
1405         h++;
1406         fputs("\">", f);
1407         fputs(g->text_string, f);
1408         fputs("</a><br>\n", f);
1409         headers.move_right();
1410       } while (! headers.is_equal_to_head());
1411       fputs("\n", f);
1412     }
1413   }
1414 }
1415
1416 class html_printer : public printer {
1417   files                file_list;
1418   simple_output        html;
1419   int                  res;
1420   int                  space_char_index;
1421   int                  space_width;
1422   int                  no_of_printed_pages;
1423   int                  paper_length;
1424   string               sbuf;
1425   int                  sbuf_start_hpos;
1426   int                  sbuf_vpos;
1427   int                  sbuf_end_hpos;
1428   int                  sbuf_prev_hpos;
1429   int                  sbuf_kern;
1430   style                sbuf_style;
1431   int                  last_sbuf_length;
1432   int                  overstrike_detected;
1433   style                output_style;
1434   int                  output_hpos;
1435   int                  output_vpos;
1436   int                  output_vpos_max;
1437   int                  output_draw_point_size;
1438   int                  line_thickness;
1439   int                  output_line_thickness;
1440   unsigned char        output_space_code;
1441   char                *inside_font_style;
1442   int                  page_number;
1443   title_desc           title;
1444   header_desc          header;
1445   int                  header_indent;
1446   int                  supress_sub_sup;
1447   int                  cutoff_heading;
1448   page                *page_contents;
1449   html_text           *current_paragraph;
1450   html_indent         *indent;
1451   html_table          *table;
1452   int                  end_center;
1453   int                  end_tempindent;
1454   TAG_ALIGNMENT        next_tag;
1455   int                  fill_on;
1456   int                  max_linelength;
1457   int                  linelength;
1458   int                  pageoffset;
1459   int                  indentation;
1460   int                  prev_indent;
1461   int                  pointsize;
1462   int                  vertical_spacing;
1463   int                  line_number;
1464   color               *background;
1465
1466   void  flush_sbuf                    ();
1467   void  set_style                     (const style &);
1468   void  set_space_code                (unsigned char c);
1469   void  do_exec                       (char *, const environment *);
1470   void  do_import                     (char *, const environment *);
1471   void  do_def                        (char *, const environment *);
1472   void  do_mdef                       (char *, const environment *);
1473   void  do_file                       (char *, const environment *);
1474   void  set_line_thickness            (const environment *);
1475   void  terminate_current_font        (void);
1476   void  flush_font                    (void);
1477   void  add_to_sbuf                   (int index, const string &s);
1478   void  write_title                   (int in_head);
1479   int   sbuf_continuation             (int index, const char *name, const environment *env, int w);
1480   void  flush_page                    (void);
1481   void  troff_tag                     (text_glob *g);
1482   void  flush_globs                   (void);
1483   void  emit_line                     (text_glob *g);
1484   void  emit_raw                      (text_glob *g);
1485   void  emit_html                     (text_glob *g);
1486   void  determine_space               (text_glob *g);
1487   void  start_font                    (const char *name);
1488   void  end_font                      (const char *name);
1489   int   is_font_courier               (font *f);
1490   int   is_courier_until_eol          (void);
1491   void  start_size                    (int from, int to);
1492   void  do_font                       (text_glob *g);
1493   void  do_center                     (char *arg);
1494   void  do_break                      (void);
1495   void  do_eol                        (void);
1496   void  do_eol_ce                     (void);
1497   void  do_title                      (void);
1498   void  do_fill                       (int on);
1499   void  do_heading                    (char *arg);
1500   void  write_header                  (void);
1501   void  determine_header_level        (int level);
1502   void  do_linelength                 (char *arg);
1503   void  do_pageoffset                 (char *arg);
1504   void  do_indentation                (char *arg);
1505   void  do_tempindent                 (char *arg);
1506   void  do_indentedparagraph          (void);
1507   void  do_verticalspacing            (char *arg);
1508   void  do_pointsize                  (char *arg);
1509   void  do_centered_image             (void);
1510   void  do_left_image                 (void);
1511   void  do_right_image                (void);
1512   void  do_auto_image                 (text_glob *g, const char *filename);
1513   void  do_links                      (void);
1514   void  do_flush                      (void);
1515   int   is_in_middle                  (int left, int right);
1516   void  do_sup_or_sub                 (text_glob *g);
1517   int   start_subscript               (text_glob *g);
1518   int   end_subscript                 (text_glob *g);
1519   int   start_superscript             (text_glob *g);
1520   int   end_superscript               (text_glob *g);
1521   void  outstanding_eol               (int n);
1522   int   is_bold                       (font *f);
1523   font *make_bold                     (font *f);
1524   int   overstrike                    (int index, const char *name, const environment *env, int w);
1525   void  do_body                       (void);
1526   int   next_horiz_pos                (text_glob *g, int nf);
1527   void  lookahead_for_tables          (void);
1528   void  insert_tab_te                 (void);
1529   text_glob *insert_tab_ts            (text_glob *where);
1530   void insert_tab0_foreach_tab        (void);
1531   void insert_tab_0                   (text_glob *where);
1532   void do_indent                      (int in, int pageoff, int linelen);
1533   void shutdown_table                 (void);
1534   void do_tab_ts                      (text_glob *g);
1535   void do_tab_te                      (void);
1536   void do_col                         (char *s);
1537   void do_tab                         (char *s);
1538   void do_tab0                        (void);
1539   int  calc_nf                        (text_glob *g, int nf);
1540   void calc_po_in                     (text_glob *g, int nf);
1541   void remove_tabs                    (void);
1542   void remove_courier_tabs            (void);
1543   void update_min_max                 (colType type_of_col, int *minimum, int *maximum, text_glob *g);
1544   void add_table_end                  (char *debug_string);
1545   // ADD HERE
1546
1547 public:
1548   html_printer         ();
1549   ~html_printer        ();
1550   void set_char        (int i, font *f, const environment *env, int w, const char *name);
1551   void draw            (int code, int *p, int np, const environment *env);
1552   void begin_page      (int);
1553   void end_page        (int);
1554   void special         (char *arg, const environment *env, char type);
1555   font *make_font      (const char *);
1556   void end_of_line     ();
1557 };
1558
1559 printer *make_printer()
1560 {
1561   return new html_printer;
1562 }
1563
1564 static void usage(FILE *stream);
1565
1566 void html_printer::set_style(const style &sty)
1567 {
1568   const char *fontname = sty.f->get_name();
1569   if (fontname == NULL)
1570     fatal("no internalname specified for font");
1571
1572 #if 0
1573   change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
1574 #endif
1575 }
1576
1577 /*
1578  *  is_bold - returns TRUE if font, f, is bold.
1579  */
1580
1581 int html_printer::is_bold (font *f)
1582 {
1583   const char *fontname = f->get_name();
1584   return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
1585 }
1586
1587 /*
1588  *  make_bold - if a bold font of, f, exists then return it.
1589  */
1590
1591 font *html_printer::make_bold (font *f)
1592 {
1593   const char *fontname = f->get_name();
1594
1595   if (strcmp(fontname, "B") == 0)
1596     return f;
1597   if (strcmp(fontname, "I") == 0)
1598     return font::load_font("BI");
1599   if (strcmp(fontname, "BI") == 0)
1600     return f;
1601   return NULL;
1602 }
1603
1604 void html_printer::end_of_line()
1605 {
1606   flush_sbuf();
1607   line_number++;
1608 }
1609
1610 /*
1611  *  emit_line - writes out a horizontal rule.
1612  */
1613
1614 void html_printer::emit_line (text_glob *g)
1615 {
1616   // --fixme-- needs to know the length in percentage
1617   html.put_string("<hr>");
1618 }
1619
1620 /*
1621  *  emit_raw - writes the raw html information directly to the device.
1622  */
1623
1624 void html_printer::emit_raw (text_glob *g)
1625 {
1626   do_font(g);
1627   if (next_tag == INLINE) {
1628     determine_space(g);
1629     current_paragraph->do_emittext(g->text_string, g->text_length);
1630   } else {
1631     current_paragraph->done_para();
1632     switch (next_tag) {
1633
1634     case CENTERED:
1635       current_paragraph->do_para("align=center");
1636       break;
1637     case LEFT:
1638       current_paragraph->do_para(&html, "align=left", indentation, pageoffset, linelength);
1639       break;
1640     case RIGHT:
1641       current_paragraph->do_para(&html, "align=right", indentation, pageoffset, linelength);
1642       break;
1643     default:
1644       fatal("unknown enumeration");
1645     }
1646     current_paragraph->do_emittext(g->text_string, g->text_length);
1647     current_paragraph->done_para();
1648     next_tag        = INLINE;
1649     supress_sub_sup = TRUE;
1650     if (indentation > 0) {
1651       /*
1652        *  restore indentation
1653        */
1654       int newin = indentation;
1655       indentation = 0;
1656       do_indent(newin, pageoffset, linelength);
1657     }
1658   }
1659 }
1660
1661 /*
1662  *  do_center - handle the .ce commands from troff.
1663  */
1664
1665 void html_printer::do_center (char *arg)
1666 {
1667   int n = atoi(arg);
1668   current_paragraph->do_break();
1669
1670   if (n > 0) {
1671     current_paragraph->done_para();
1672     supress_sub_sup = TRUE;
1673     current_paragraph->do_para("align=center");
1674     end_center += n;
1675   } else {
1676     end_center = 0;
1677     current_paragraph->remove_para_align();
1678   }
1679 }
1680
1681 /*
1682  *  do_centered_image - set a flag such that the next html-tag is
1683  *                      placed inside a centered paragraph.
1684  */
1685
1686 void html_printer::do_centered_image (void)
1687 {
1688   next_tag = CENTERED;
1689 }
1690
1691 /*
1692  *  do_right_image - set a flag such that the next html-tag is
1693  *                   placed inside a right aligned paragraph.
1694  */
1695
1696 void html_printer::do_right_image (void)
1697 {
1698   next_tag = RIGHT;
1699 }
1700
1701 /*
1702  *  do_left_image - set a flag such that the next html-tag is
1703  *                  placed inside a left aligned paragraph.
1704  */
1705
1706 void html_printer::do_left_image (void)
1707 {
1708   next_tag = LEFT;
1709 }
1710
1711 /*
1712  *  exists - returns TRUE if filename exists.
1713  */
1714
1715 static int exists (const char *filename)
1716 {
1717   FILE *fp = fopen(filename, "r");
1718
1719   if (fp == 0) {
1720     return( FALSE );
1721   } else {
1722     fclose(fp);
1723     return( TRUE );
1724   }
1725 }
1726
1727 /*
1728  *  generate_img_src - returns a html image tag for the filename
1729  *                     providing that the image exists.
1730  */
1731
1732 static string &generate_img_src (const char *filename)
1733 {
1734   string *s = new string("");
1735
1736   while (filename && (filename[0] == ' ')) {
1737     filename++;
1738   }
1739   if (exists(filename))
1740     *s += string("<img src=\"") + filename + "\">";
1741   return *s;
1742 }
1743
1744 /*
1745  *  do_auto_image - tests whether the image, indicated by filename,
1746  *                  is present, if so then it emits an html image tag.
1747  *                  An image tag may be passed through from pic, eqn
1748  *                  but the corresponding image might not be created.
1749  *                  Consider .EQ delim $$ .EN  or an empty .PS .PE.
1750  */
1751
1752 void html_printer::do_auto_image (text_glob *g, const char *filename)
1753 {
1754   string buffer = generate_img_src(filename);
1755   
1756   if (! buffer.empty()) {
1757     /*
1758      *  utilize emit_raw by creating a new text_glob.
1759      */
1760     text_glob h = *g;
1761
1762     h.text_string = buffer.contents();
1763     h.text_length = buffer.length();
1764     emit_raw(&h);
1765   } else
1766     next_tag = INLINE;
1767 }
1768
1769 /*
1770  *  outstanding_eol - call do_eol, n, times.
1771  */
1772
1773 void html_printer::outstanding_eol (int n)
1774 {
1775   while (n > 0) {
1776     do_eol();
1777     n--;
1778   }
1779 }
1780
1781 /*
1782  *  do_title - handle the .tl commands from troff.
1783  */
1784
1785 void html_printer::do_title (void)
1786 {
1787   text_glob    *t;
1788   int           removed_from_head;
1789   int           eol_ce = 0;
1790
1791   if (page_number == 1) {
1792     int found_title_start  = FALSE;
1793     if (! page_contents->glyphs.is_empty()) {
1794       page_contents->glyphs.sub_move_right();       /* move onto next word */
1795       do {
1796         t = page_contents->glyphs.get_data();
1797         removed_from_head = FALSE;
1798         if (t->is_auto_img()) {
1799           string img = generate_img_src((char *)(t->text_string + 20));
1800
1801           if (! img.empty()) {
1802             if (found_title_start)
1803               title.text += " ";
1804             found_title_start = TRUE;
1805             title.has_been_found = TRUE;
1806             title.text += img;
1807           }
1808           page_contents->glyphs.sub_move_right();         /* move onto next word */
1809           removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1810                                (page_contents->glyphs.is_equal_to_head()));
1811         } else if (t->is_eol_ce()) {
1812           /*  process the eol associated with .ce
1813            */
1814           eol_ce++;
1815           page_contents->glyphs.sub_move_right();         /* move onto next word */
1816         } else if (t->is_eol()) {
1817           /* end of title found
1818            */
1819           title.has_been_found = TRUE;
1820           outstanding_eol(eol_ce);
1821           return;
1822         } else if (t->is_a_tag()) {
1823           /* end of title found, but move back so that we read this tag and process it
1824            */
1825           page_contents->glyphs.move_left();           /* move backwards to last word */
1826           title.has_been_found = TRUE;
1827           outstanding_eol(eol_ce);
1828           return;
1829         } else if (found_title_start) {
1830           title.text += " " + string(t->text_string, t->text_length);
1831           page_contents->glyphs.sub_move_right();         /* move onto next word */
1832           removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1833                                (page_contents->glyphs.is_equal_to_head()));
1834         } else {
1835           title.text += string(t->text_string, t->text_length);
1836           found_title_start    = TRUE;
1837           title.has_been_found = TRUE;
1838           page_contents->glyphs.sub_move_right();         /* move onto next word */
1839           removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1840                                (page_contents->glyphs.is_equal_to_head()));
1841         }
1842       } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
1843     }
1844     outstanding_eol(eol_ce);
1845   }
1846 }
1847
1848 void html_printer::write_header (void)
1849 {
1850   if (! header.header_buffer.empty()) {
1851     if (header.header_level > 7) {
1852       header.header_level = 7;
1853     }
1854
1855     // firstly we must terminate any font and type faces
1856     current_paragraph->done_para();
1857     supress_sub_sup = TRUE;
1858
1859     if (cutoff_heading+2 > header.header_level) {
1860       // now we save the header so we can issue a list of links
1861       header.no_of_headings++;
1862       style st;
1863
1864       text_glob *h=new text_glob();
1865       h->text_glob_html(&st,
1866                         header.headings.add_string(header.header_buffer),
1867                         header.header_buffer.length(),
1868                         header.no_of_headings, header.header_level,
1869                         header.no_of_headings, header.header_level);
1870
1871       header.headers.add(h,
1872                          header.no_of_headings,
1873                          header.no_of_headings, header.no_of_headings,
1874                          header.no_of_headings, header.no_of_headings);   // and add this header to the header list
1875
1876       // lastly we generate a tag
1877
1878       html.nl().put_string("<a name=\"");
1879       if (simple_anchors) {
1880         string buffer(ANCHOR_TEMPLATE);
1881
1882         buffer += as_string(header.no_of_headings);
1883         buffer += '\0';
1884         html.put_string(buffer.contents());
1885       } else {
1886         html.put_string(header.header_buffer);
1887       }
1888       html.put_string("\"></a>").nl();
1889     }
1890
1891     if (manufacture_headings) {
1892       // line break before a header
1893       if (!current_paragraph->emitted_text())
1894         current_paragraph->do_space();
1895       // user wants manufactured headings which look better than <Hn></Hn>
1896       if (header.header_level<4) {
1897         html.put_string("<b><font size=\"+1\">");
1898         html.put_string(header.header_buffer);
1899         html.put_string("</font></b>").nl();
1900       }
1901       else {
1902         html.put_string("<b>");
1903         html.put_string(header.header_buffer);
1904         html.put_string("</b>").nl();
1905       }
1906     }
1907     else {
1908       // and now we issue the real header
1909       html.put_string("<h");
1910       html.put_number(header.header_level);
1911       html.put_string(">");
1912       html.put_string(header.header_buffer);
1913       html.put_string("</h");
1914       html.put_number(header.header_level);
1915       html.put_string(">").nl();
1916     }
1917
1918     current_paragraph->do_para(&html, "", indentation, pageoffset, linelength);
1919   }
1920 }
1921
1922 void html_printer::determine_header_level (int level)
1923 {
1924   if (level == 0) {
1925     int i;
1926
1927     for (i=0; ((i<header.header_buffer.length())
1928                && ((header.header_buffer[i] == '.')
1929                    || is_digit(header.header_buffer[i]))) ; i++) {
1930       if (header.header_buffer[i] == '.') {
1931         level++;
1932       }
1933     }
1934   }
1935   header.header_level = level+1;
1936 }
1937
1938 /*
1939  *  do_heading - handle the .SH and .NH and equivalent commands from troff.
1940  */
1941
1942 void html_printer::do_heading (char *arg)
1943 {
1944   text_glob *g;
1945   text_glob *l = 0;
1946   int  level=atoi(arg);
1947
1948   header.header_buffer.clear();
1949   page_contents->glyphs.move_right();
1950   if (! page_contents->glyphs.is_equal_to_head()) {
1951     g = page_contents->glyphs.get_data();
1952     do {
1953       if (g->is_auto_img()) {
1954         string img=generate_img_src((char *)(g->text_string + 20));
1955
1956         if (! img.empty()) {
1957           simple_anchors = TRUE;  // we cannot use full heading anchors with images
1958           if (l != 0)
1959             header.header_buffer += " ";
1960           
1961           l = g;
1962           header.header_buffer += img;
1963         }
1964       } else if (! (g->is_a_line() || g->is_a_tag())) {
1965         /*
1966          *  we ignore tags commands when constructing a heading
1967          */
1968         if (l != 0)
1969           header.header_buffer += " ";
1970         l = g;
1971
1972         header.header_buffer += string(g->text_string, g->text_length);
1973       }
1974       page_contents->glyphs.move_right();
1975       g = page_contents->glyphs.get_data();
1976     } while ((! page_contents->glyphs.is_equal_to_head()) &&
1977              (! g->is_br()));
1978   }
1979
1980   determine_header_level(level);
1981   write_header();
1982
1983   // finally set the output to neutral for after the header
1984   g = page_contents->glyphs.get_data();
1985   page_contents->glyphs.move_left();     // so that next time we use old g
1986 }
1987
1988 /*
1989  *  is_courier_until_eol - returns TRUE if we can see a whole line which is courier
1990  */
1991
1992 int html_printer::is_courier_until_eol (void)
1993 {
1994   text_glob *orig = page_contents->glyphs.get_data();
1995   int result      = TRUE;
1996   text_glob *g;
1997
1998   if (! page_contents->glyphs.is_equal_to_tail()) {
1999     page_contents->glyphs.move_right();
2000     do {
2001       g = page_contents->glyphs.get_data();
2002       if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2003         result = FALSE;
2004       page_contents->glyphs.move_right();
2005     } while (result &&
2006              (! page_contents->glyphs.is_equal_to_head()) &&
2007              (! g->is_fi()) && (! g->is_eol()));
2008     
2009     /*
2010      *  now restore our previous position.
2011      */
2012     while (page_contents->glyphs.get_data() != orig)
2013       page_contents->glyphs.move_left();
2014   }
2015   return result;
2016 }
2017
2018 /*
2019  *  do_linelength - handle the .ll command from troff.
2020  */
2021
2022 void html_printer::do_linelength (char *arg)
2023 {
2024   if (max_linelength == -1)
2025     max_linelength = atoi(arg);
2026
2027   if (fill_on)
2028     do_indent(indentation, pageoffset, atoi(arg));
2029 }
2030
2031 /*
2032  *  do_pageoffset - handle the .po command from troff.
2033  */
2034
2035 void html_printer::do_pageoffset (char *arg)
2036 {
2037   if (fill_on)
2038     do_indent(indentation, atoi(arg), linelength);
2039 }
2040
2041 /*
2042  *  do_indentation - handle the .in command from troff.
2043  */
2044
2045 void html_printer::do_indentation (char *arg)
2046 {
2047   if (fill_on)
2048     do_indent(atoi(arg), pageoffset, linelength);
2049 }
2050
2051 /*
2052  *  do_tempindent - handle the .ti command from troff.
2053  */
2054
2055 void html_printer::do_tempindent (char *arg)
2056 {
2057   if (fill_on) {
2058     end_tempindent = 1;
2059     prev_indent    = indentation;
2060     do_indent(atoi(arg), pageoffset, linelength);
2061   }
2062 }
2063
2064 /*
2065  *  shutdown_table - shuts down the current table.
2066  */
2067
2068 void html_printer::shutdown_table (void)
2069 {
2070   if (table != NULL) {
2071     current_paragraph->done_para();
2072     table->emit_finish_table();
2073     // dont delete this table as it will be deleted when we destroy the text_glob
2074     table = NULL;
2075   }
2076 }
2077
2078 /*
2079  *  do_indent - remember the indent parameters and if
2080  *              indent is > pageoff and indent has changed
2081  *              then we start a html table to implement the indentation.
2082  */
2083
2084 void html_printer::do_indent (int in, int pageoff, int linelen)
2085 {
2086   if ((indentation != -1) &&
2087       (pageoffset+indentation != in+pageoff)) {
2088     
2089     current_paragraph->done_para();
2090       
2091     indentation = in;
2092     pageoffset  = pageoff;
2093     if (linelen <= max_linelength)
2094       linelength  = linelen;
2095
2096     current_paragraph->do_para(&html, "", indentation, pageoffset, max_linelength);
2097   }
2098 }
2099
2100 /*
2101  *  do_verticalspacing - handle the .vs command from troff.
2102  */
2103
2104 void html_printer::do_verticalspacing (char *arg)
2105 {
2106   vertical_spacing = atoi(arg);
2107 }
2108
2109 /*
2110  *  do_pointsize - handle the .ps command from troff.
2111  */
2112
2113 void html_printer::do_pointsize (char *arg)
2114 {
2115   pointsize = atoi(arg);
2116 }
2117
2118 /*
2119  *  do_fill - records whether troff has requested that text be filled.
2120  */
2121
2122 void html_printer::do_fill (int on)
2123 {
2124   current_paragraph->do_break();
2125   output_hpos = indentation+pageoffset;
2126   supress_sub_sup = TRUE;
2127
2128   if (fill_on != on) {
2129     if (on)
2130       current_paragraph->do_para("");
2131     else
2132       current_paragraph->do_pre();
2133     fill_on = on;
2134   }
2135 }
2136
2137 /*
2138  *  do_eol - handle the end of line
2139  */
2140
2141 void html_printer::do_eol (void)
2142 {
2143   if (! fill_on) {
2144     if (current_paragraph->ever_emitted_text()) {
2145       current_paragraph->do_newline();
2146       current_paragraph->do_break();
2147     }
2148   }
2149   output_hpos = indentation+pageoffset;
2150 }
2151
2152 /*
2153  *  do_eol_ce - handle end of line specifically for a .ce
2154  */
2155
2156 void html_printer::do_eol_ce (void)
2157 {
2158   if (end_center > 0) {
2159     if (end_center > 1)
2160       if (current_paragraph->emitted_text())
2161         current_paragraph->do_break();
2162     
2163     end_center--;
2164     if (end_center == 0) {
2165       current_paragraph->done_para();
2166       supress_sub_sup = TRUE;
2167     }
2168   }
2169 }
2170
2171 /*
2172  *  do_flush - flushes all output and tags.
2173  */
2174
2175 void html_printer::do_flush (void)
2176 {
2177   current_paragraph->done_para();
2178 }
2179
2180 /*
2181  *  do_links - moves onto a new temporary file and sets auto_links to FALSE.
2182  */
2183
2184 void html_printer::do_links (void)
2185 {
2186   current_paragraph->done_para();
2187   auto_links = FALSE;   /* from now on only emit under user request */
2188   file_list.add_new_file(xtmpfile());
2189   html.set_file(file_list.get_file());
2190 }
2191
2192 /*
2193  *  do_break - handles the ".br" request and also
2194  *             undoes an outstanding ".ti" command.
2195  */
2196
2197 void html_printer::do_break (void)
2198 {
2199   current_paragraph->do_break();
2200   if (end_tempindent > 0) {
2201     end_tempindent--;
2202     if (end_tempindent == 0)
2203       do_indent(prev_indent, pageoffset, linelength);
2204   }
2205   output_hpos = indentation+pageoffset;
2206   supress_sub_sup = TRUE;
2207 }
2208
2209 /*
2210  *  do_tab_ts - start a table, which will have already been defined.
2211  */
2212
2213 void html_printer::do_tab_ts (text_glob *g)
2214 {
2215   html_table *t = g->get_table();
2216
2217   if (t != NULL) {
2218     current_paragraph->done_pre();
2219     current_paragraph->done_para();
2220
2221     html.simple_comment("TABS");
2222
2223     t->set_linelength(max_linelength);
2224     t->add_indent(pageoffset);
2225     t->emit_table_header(FALSE);
2226   }
2227
2228   table = t;
2229 }
2230
2231 /*
2232  *  do_tab_te - finish a table.
2233  */
2234
2235 void html_printer::do_tab_te (void)
2236 {
2237   if (table) {
2238     current_paragraph->done_para();
2239     table->emit_finish_table();
2240   }
2241
2242   table = NULL;
2243
2244   if (indentation > 0) {
2245     /*
2246      *  restore indentation
2247      */
2248     int newin = indentation;
2249     indentation = 0;
2250     do_indent(newin, pageoffset, linelength);
2251   }
2252 }
2253
2254 /*
2255  *  do_tab - handle the "html-tag:tab" tag
2256  */
2257
2258 void html_printer::do_tab (char *s)
2259 {
2260   if (table) {
2261     while (isspace(*s))
2262       s++;
2263     s++;
2264     int col = table->find_column(atoi(s) + pageoffset + indentation);
2265     if (col > 0) {
2266       current_paragraph->done_para();
2267       table->emit_col(col);
2268     }
2269   }
2270 }
2271
2272 /*
2273  *  do_tab0 - handle the "html-tag:tab0" tag
2274  */
2275
2276 void html_printer::do_tab0 (void)
2277 {
2278   if (table) {
2279     int col = table->find_column(pageoffset+indentation);
2280     if (col > 0) {
2281       current_paragraph->done_para();
2282       table->emit_col(col);
2283     }
2284   }
2285 }
2286
2287 /*
2288  *  do_col - start column, s.
2289  */
2290
2291 void html_printer::do_col (char *s)
2292 {
2293   if (table) {
2294     current_paragraph->done_para();
2295     table->emit_col(atoi(s));
2296   }
2297 }
2298
2299 /*
2300  *  troff_tag - processes the troff tag and manipulates the troff state machine.
2301  */
2302
2303 void html_printer::troff_tag (text_glob *g)
2304 {
2305   /*
2306    *  firstly skip over html-tag:
2307    */
2308   char *t=(char *)g->text_string+9;
2309
2310   if (g->is_eol()) {
2311     do_eol();
2312   } else if (g->is_eol_ce()) {
2313     do_eol_ce();
2314   } else if (strncmp(t, ".sp", 3) == 0) {
2315     if (g->get_arg() > 0)
2316       current_paragraph->do_space();
2317     else
2318       current_paragraph->do_break();
2319     supress_sub_sup = TRUE;
2320   } else if (strncmp(t, ".br", 3) == 0) {
2321     do_break();
2322   } else if (strcmp(t, ".centered-image") == 0) {
2323     do_centered_image();
2324   } else if (strcmp(t, ".right-image") == 0) {
2325     do_right_image();
2326   } else if (strcmp(t, ".left-image") == 0) {
2327     do_left_image();
2328   } else if (strncmp(t, ".auto-image", 11) == 0) {
2329     char *a = (char *)t+11;
2330     do_auto_image(g, a);
2331   } else if (strncmp(t, ".ce", 3) == 0) {
2332     char *a = (char *)t+3;
2333     supress_sub_sup = TRUE;
2334     do_center(a);
2335   } else if (strncmp(t, ".tl", 3) == 0) {
2336     supress_sub_sup = TRUE;
2337     title.with_h1 = TRUE;
2338     do_title();
2339   } else if (strncmp(t, ".html-tl", 8) == 0) {
2340     supress_sub_sup = TRUE;
2341     title.with_h1 = FALSE;
2342     do_title();
2343   } else if (strncmp(t, ".fi", 3) == 0) {
2344     do_fill(TRUE);
2345   } else if (strncmp(t, ".nf", 3) == 0) {
2346     do_fill(FALSE);
2347   } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
2348     char *a = (char *)t+3;
2349     do_heading(a);
2350   } else if (strncmp(t, ".ll", 3) == 0) {
2351     char *a = (char *)t+3;
2352     do_linelength(a);
2353   } else if (strncmp(t, ".po", 3) == 0) {
2354     char *a = (char *)t+3;
2355     do_pageoffset(a);
2356   } else if (strncmp(t, ".in", 3) == 0) {
2357     char *a = (char *)t+3;
2358     do_indentation(a);
2359   } else if (strncmp(t, ".ti", 3) == 0) {
2360     char *a = (char *)t+3;
2361     do_tempindent(a);
2362   } else if (strncmp(t, ".vs", 3) == 0) {
2363     char *a = (char *)t+3;
2364     do_verticalspacing(a);
2365   } else if (strncmp(t, ".ps", 3) == 0) {
2366     char *a = (char *)t+3;
2367     do_pointsize(a);
2368   } else if (strcmp(t, ".links") == 0) {
2369     do_links();
2370   } else if (strcmp(t, ".no-auto-rule") == 0) {
2371     auto_rule = FALSE;
2372   } else if (strcmp(t, ".tab-ts") == 0) {
2373     do_tab_ts(g);
2374   } else if (strcmp(t, ".tab-te") == 0) {
2375     do_tab_te();
2376   } else if (strncmp(t, ".col ", 5) == 0) {
2377     char *a = (char *)t+4;
2378     do_col(a);
2379   } else if (strncmp(t, "tab ", 4) == 0) {
2380     char *a = (char *)t+3;
2381     do_tab(a);
2382   } else if (strncmp(t, "tab0", 4) == 0) {
2383     do_tab0();
2384   }
2385 }
2386
2387 /*
2388  *  is_in_middle - returns TRUE if the positions left..right are in the center of the page.
2389  */
2390
2391 int html_printer::is_in_middle (int left, int right)
2392 {
2393   return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE );
2394 }
2395
2396 /*
2397  *  flush_globs - runs through the text glob list and emits html.
2398  */
2399
2400 void html_printer::flush_globs (void)
2401 {
2402   text_glob *g;
2403
2404   if (! page_contents->glyphs.is_empty()) {
2405     page_contents->glyphs.start_from_head();
2406     do {
2407       g = page_contents->glyphs.get_data();
2408
2409       if (strcmp(g->text_string, "XXXXXXX") == 0)
2410         stop();
2411
2412       if (g->is_a_tag()) {
2413         troff_tag(g);
2414       } else if (g->is_a_line()) {
2415         emit_line(g);
2416       } else {
2417         emit_html(g);
2418       }
2419       /*
2420        *  after processing the title (and removing it) the glyph list might be empty
2421        */
2422       if (! page_contents->glyphs.is_empty()) {
2423         page_contents->glyphs.move_right();
2424       }
2425     } while (! page_contents->glyphs.is_equal_to_head());
2426   }
2427 }
2428
2429 /*
2430  *  calc_nf - calculates the _no_ format flag, given the
2431  *            text glob, g.
2432  */
2433
2434 int html_printer::calc_nf (text_glob *g, int nf)
2435 {
2436   if (g != NULL) {
2437     if (g->is_fi())
2438       return FALSE;
2439     if (g->is_nf())
2440       return TRUE;
2441   }
2442   return nf;
2443 }
2444
2445 /*
2446  *  calc_po_in - calculates the, in, po, registers
2447  */
2448
2449 void html_printer::calc_po_in (text_glob *g, int nf)
2450 {
2451   if (g->is_in())
2452     indentation = g->get_arg();
2453   else if (g->is_po())
2454     pageoffset = g->get_arg();
2455   else if (g->is_ti()) {
2456     prev_indent = indentation;
2457     indentation = g->get_arg();
2458     end_tempindent = 1;
2459   } else if (g->is_br() && ((end_tempindent > 0) || (nf && g->is_eol()))) {
2460     end_tempindent = 0;
2461     indentation = prev_indent;
2462   }
2463 }
2464
2465 /*
2466  *  next_horiz_pos - returns the next horiz position.
2467  *                   -1 is returned if it doesn't exist.
2468  */
2469
2470 int html_printer::next_horiz_pos (text_glob *g, int nf)
2471 {
2472   int next     = -1;
2473
2474   if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
2475     if (! page_contents->glyphs.is_empty()) {
2476       page_contents->glyphs.move_right_get_data();
2477       if (g == NULL)
2478         page_contents->glyphs.start_from_head();
2479       else {
2480         next = g->minh;
2481         page_contents->glyphs.move_left();
2482       }
2483     }
2484   return next;
2485 }
2486
2487 /*
2488  *  insert_tab_ts - inserts a tab-ts before, where.
2489  */
2490
2491 text_glob *html_printer::insert_tab_ts (text_glob *where)
2492 {
2493   text_glob *start_of_table;
2494   text_glob *old_pos = page_contents->glyphs.get_data();
2495
2496   page_contents->glyphs.move_to(where);
2497   page_contents->glyphs.move_left();
2498   page_contents->insert_tag(string("html-tag:.tab-ts"));  // tab table start
2499   page_contents->glyphs.move_right();
2500   start_of_table = page_contents->glyphs.get_data();
2501   page_contents->glyphs.move_to(old_pos);
2502   return start_of_table;
2503 }
2504
2505 /*
2506  *  insert_tab_te - inserts a tab-te before the current position
2507  *                  (it skips backwards over .sp/.br)
2508  */
2509
2510 void html_printer::insert_tab_te (void)
2511 {
2512   text_glob *g = page_contents->glyphs.get_data();
2513   page_contents->dump_page();
2514
2515   while (page_contents->glyphs.get_data()->is_a_tag())
2516     page_contents->glyphs.move_left();
2517
2518   page_contents->insert_tag(string("html-tag:.tab-te"));  // tab table end
2519   while (g != page_contents->glyphs.get_data())
2520     page_contents->glyphs.move_right();
2521   page_contents->dump_page();
2522 }
2523
2524 /*
2525  *  insert_tab_0 - inserts a tab0 before, where.
2526  */
2527
2528 void html_printer::insert_tab_0 (text_glob *where)
2529 {
2530   text_glob *old_pos = page_contents->glyphs.get_data();
2531
2532   page_contents->glyphs.move_to(where);
2533   page_contents->glyphs.move_left();
2534   page_contents->insert_tag(string("html-tag:tab0"));  // tab0 start of line
2535   page_contents->glyphs.move_right();
2536   page_contents->glyphs.move_to(old_pos);
2537 }
2538
2539 /*
2540  *  remove_tabs - removes the tabs tags on this line.
2541  */
2542
2543 void html_printer::remove_tabs (void)
2544 {
2545   text_glob *orig = page_contents->glyphs.get_data();
2546   text_glob *g;
2547
2548   if (! page_contents->glyphs.is_equal_to_tail()) {
2549     do {
2550       g = page_contents->glyphs.get_data();
2551       if (g->is_tab()) {
2552         page_contents->glyphs.sub_move_right();
2553         if (g == orig)
2554           orig = page_contents->glyphs.get_data();
2555       } else
2556         page_contents->glyphs.move_right();
2557     } while ((! page_contents->glyphs.is_equal_to_head()) &&
2558              (! g->is_eol()));
2559     
2560     /*
2561      *  now restore our previous position.
2562      */
2563     while (page_contents->glyphs.get_data() != orig)
2564       page_contents->glyphs.move_left();
2565   }
2566 }
2567
2568 void html_printer::remove_courier_tabs (void)
2569 {
2570   text_glob  *g;
2571   int line_start = TRUE;
2572   int nf         = FALSE;
2573
2574   if (! page_contents->glyphs.is_empty()) {
2575     page_contents->glyphs.start_from_head();
2576     line_start = TRUE;
2577     do {
2578       g = page_contents->glyphs.get_data();
2579       
2580       nf = calc_nf(g, nf);
2581
2582       if (line_start) {
2583         if (line_start && nf && is_courier_until_eol()) {
2584           remove_tabs();
2585           g = page_contents->glyphs.get_data();
2586         }
2587       }
2588
2589       line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
2590       page_contents->glyphs.move_right();
2591     } while (! page_contents->glyphs.is_equal_to_head());
2592   }
2593 }
2594
2595 void html_printer::insert_tab0_foreach_tab (void)
2596 {
2597   text_glob  *start_of_line  = NULL;
2598   text_glob  *g              = NULL;
2599   int seen_tab               = FALSE;
2600   int seen_col               = FALSE;
2601   int nf                     = FALSE;
2602
2603   if (! page_contents->glyphs.is_empty()) {
2604     page_contents->glyphs.start_from_head();
2605     start_of_line = page_contents->glyphs.get_data();
2606     do {
2607       g = page_contents->glyphs.get_data();
2608       
2609       nf = calc_nf(g, nf);
2610
2611       if (g->is_tab())
2612         seen_tab = TRUE;
2613       
2614       if (g->is_col())
2615         seen_col = TRUE;
2616
2617       if (g->is_br() || (nf && g->is_eol())) {
2618         do {
2619           page_contents->glyphs.move_right();
2620           g = page_contents->glyphs.get_data();
2621           nf = calc_nf(g, nf);
2622           if (page_contents->glyphs.is_equal_to_head()) {
2623             if (seen_tab && !seen_col)
2624               insert_tab_0(start_of_line);
2625             return;
2626           }
2627         } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
2628         // printf("\nstart_of_line is: %s\n", g->text_string);
2629         if (seen_tab && !seen_col) {
2630           insert_tab_0(start_of_line);
2631           page_contents->glyphs.move_to(g);
2632         }
2633
2634         seen_tab = FALSE;
2635         seen_col = FALSE;
2636         start_of_line = g;
2637       }
2638       page_contents->glyphs.move_right();
2639     } while (! page_contents->glyphs.is_equal_to_head());
2640     if (seen_tab && !seen_col)
2641       insert_tab_0(start_of_line);
2642
2643   }
2644 }
2645
2646 /*
2647  *  update_min_max - updates the extent of a column, given the left and right
2648  *                   extents of a glyph, g.
2649  */
2650
2651 void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
2652 {
2653   switch (type_of_col) {
2654     
2655   case tab_tag:
2656     break;
2657   case tab0_tag:
2658     *minimum = g->minh;
2659     break;
2660   case col_tag:
2661     *minimum = g->minh;
2662     *maximum = g->maxh;
2663     break;
2664   default:
2665     break;
2666   }
2667 }
2668
2669 /*
2670  *  add_table_end - moves left one glyph, adds a table end tag and adds a
2671  *                  debugging string.
2672  */
2673
2674 void html_printer::add_table_end (char *debug_string)
2675 {
2676   page_contents->glyphs.move_left();
2677   insert_tab_te();
2678 #if defined(DEBUG_TABLES)
2679   page_contents->insert_tag(string(debug_string));
2680 #endif
2681 }
2682
2683 /*
2684  *  lookahead_for_tables - checks for .col tags and inserts table start/end tags
2685  */
2686
2687 void html_printer::lookahead_for_tables (void)
2688 {
2689   text_glob  *g;
2690   text_glob  *start_of_line  = NULL;
2691   text_glob  *start_of_table = NULL;
2692   text_glob  *last           = NULL;
2693   colType     type_of_col    = none;
2694   int         left           = 0;
2695   int         found_col      = FALSE;
2696   int         seen_text      = FALSE;
2697   int         ncol           = 0;
2698   int         colmin;
2699   int         colmax;
2700   html_table *table          = new html_table(&html, -1);
2701   const char *tab_defs       = NULL;
2702   char        align          = 'L';
2703   int         nf             = FALSE;
2704   int         old_pageoffset = pageoffset;
2705
2706   remove_courier_tabs();
2707   page_contents->dump_page();
2708   insert_tab0_foreach_tab();
2709   page_contents->dump_page();
2710   if (! page_contents->glyphs.is_empty()) {
2711     page_contents->glyphs.start_from_head();
2712     g = page_contents->glyphs.get_data();
2713     do {
2714 #if defined(DEBUG_TABLES)
2715       fprintf(stderr, " [") ;
2716       fprintf(stderr, g->text_string) ;
2717       fprintf(stderr, "] ") ;
2718       fflush(stderr);
2719       if (strcmp(g->text_string, "XXXXXXX") == 0)
2720         stop();
2721 #endif
2722
2723       nf = calc_nf(g, nf);
2724       calc_po_in(g, nf);
2725       if (g->is_col()) {
2726         if (type_of_col == tab_tag && start_of_table != NULL) {
2727           page_contents->glyphs.move_left();
2728           insert_tab_te();
2729           start_of_table->remember_table(table);
2730           table = new html_table(&html, -1);
2731           page_contents->insert_tag(string("*** TAB -> COL ***"));
2732           if (tab_defs != NULL)
2733             table->tab_stops->init(tab_defs);
2734           start_of_table = NULL;
2735           last = NULL;
2736         }
2737         type_of_col = col_tag;
2738         found_col = TRUE;
2739         ncol = g->get_arg();
2740         align = 'L';
2741         colmin = 0;
2742         colmax = 0;
2743       } else if (g->is_tab()) {
2744         type_of_col = tab_tag;
2745         colmin = g->get_tab_args(&align);
2746         align = 'L'; // for now as 'C' and 'R' are broken
2747         ncol = table->find_tab_column(colmin);
2748         colmin += pageoffset + indentation;
2749         colmax = table->get_tab_pos(ncol+1);
2750         if (colmax > 0)
2751           colmax += pageoffset + indentation;
2752       } else if (g->is_tab0()) {
2753         if (type_of_col == col_tag && start_of_table != NULL) {
2754           page_contents->glyphs.move_left();
2755           insert_tab_te();
2756           start_of_table->remember_table(table);
2757           table = new html_table(&html, -1);
2758           page_contents->insert_tag(string("*** COL -> TAB ***"));
2759           start_of_table = NULL;
2760           last = NULL;
2761         }
2762         if (tab_defs != NULL)
2763           table->tab_stops->init(tab_defs);
2764
2765         type_of_col = tab0_tag;
2766         ncol = 1;
2767         colmin = 0;
2768         colmax = table->get_tab_pos(2) + pageoffset + indentation;
2769       } else if (! g->is_a_tag())
2770         update_min_max(type_of_col, &colmin, &colmax, g);
2771
2772       if ((! g->is_a_tag()) || g->is_tab())
2773         seen_text = TRUE;
2774
2775       if ((g->is_col() || g->is_tab() || g->is_tab0())
2776           && (start_of_line != NULL) && (start_of_table == NULL)) {
2777         start_of_table = insert_tab_ts(start_of_line);
2778         start_of_line = NULL;
2779         seen_text = FALSE;
2780       } else if (g->is_ce() && (start_of_table != NULL)) {
2781         add_table_end("*** CE ***");
2782         start_of_table->remember_table(table);
2783         start_of_table = NULL;
2784         last = NULL;
2785       } else if (g->is_ta()) {
2786         tab_defs = g->text_string;
2787         if (!table->tab_stops->compatible(tab_defs)) {
2788           if (start_of_table != NULL) {
2789             add_table_end("*** TABS ***");
2790             start_of_table->remember_table(table);
2791             table = new html_table(&html, -1);
2792             start_of_table = NULL;
2793             type_of_col = none;
2794             last = NULL;
2795           }
2796           table->tab_stops->init(tab_defs);
2797         }
2798       }
2799
2800       if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
2801         // we are in a table and have a glyph
2802         if ((ncol == 0) || (! table->add_column(ncol, colmin, colmax, align))) {
2803           if (ncol == 0)
2804             add_table_end("*** NCOL == 0 ***");
2805           else
2806             add_table_end("*** CROSSED COLS ***");
2807
2808           start_of_table->remember_table(table);
2809           table = new html_table(&html, -1);
2810           start_of_table = NULL;
2811           type_of_col = none;
2812           last = NULL;
2813         }
2814       }
2815       
2816       /*
2817        *  move onto next glob, check whether we are starting a new line
2818        */
2819       g = page_contents->glyphs.move_right_get_data();
2820
2821       if (g == NULL) {
2822         if (found_col) {
2823           page_contents->glyphs.start_from_head();
2824           last = g;
2825           found_col = FALSE;
2826         }
2827       } else if (g->is_br() || (nf && g->is_eol())) {
2828         do {
2829           g = page_contents->glyphs.move_right_get_data();
2830           nf = calc_nf(g, nf);
2831         } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
2832         start_of_line = g;
2833         seen_text = FALSE;
2834         ncol = 0;
2835         left = next_horiz_pos(g, nf);
2836         if (found_col)
2837           last = g;
2838         found_col = FALSE;
2839       }
2840     } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
2841
2842 #if defined(DEBUG_TABLES)
2843     fprintf(stderr, "finished scanning for tables\n");
2844 #endif
2845
2846     page_contents->glyphs.start_from_head();
2847     if (start_of_table != NULL) {
2848       if (last != NULL)
2849         while (last != page_contents->glyphs.get_data())
2850           page_contents->glyphs.move_left();
2851
2852       insert_tab_te();
2853       start_of_table->remember_table(table);
2854       table = NULL;
2855       page_contents->insert_tag(string("*** LAST ***"));      
2856     }
2857   }
2858   if (table != NULL)
2859     delete table;
2860
2861   // and reset the registers
2862   pageoffset = old_pageoffset;
2863   indentation = 0;
2864   prev_indent = 0;
2865   end_tempindent = 0;
2866 }
2867
2868 void html_printer::flush_page (void)
2869 {
2870   supress_sub_sup = TRUE;
2871   flush_sbuf();
2872   page_contents->dump_page();
2873   lookahead_for_tables();
2874   page_contents->dump_page();
2875
2876   flush_globs();
2877   current_paragraph->done_para();
2878   
2879   // move onto a new page
2880   delete page_contents;
2881 #if defined(DEBUG_TABLES)
2882   fprintf(stderr, "\n\n*** flushed page ***\n\n");
2883
2884   html.simple_comment("new page called");
2885 #endif
2886   page_contents = new page;
2887 }
2888
2889 /*
2890  *  determine_space - works out whether we need to write a space.
2891  *                    If last glyph is ajoining then no space emitted.
2892  */
2893
2894 void html_printer::determine_space (text_glob *g)
2895 {
2896   if (current_paragraph->is_in_pre()) {
2897     /*
2898      *  .nf has been specified
2899      */
2900     while (output_hpos < g->minh) {
2901       output_hpos += space_width;
2902       current_paragraph->emit_space();
2903     }
2904   } else {
2905     if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
2906       current_paragraph->emit_space();
2907     }
2908   }
2909 }
2910
2911 /*
2912  *  is_font_courier - returns TRUE if the font, f, is courier.
2913  */
2914
2915 int html_printer::is_font_courier (font *f)
2916 {
2917   if (f != 0) {
2918     const char *fontname = f->get_name();
2919
2920     return( (fontname != 0) && (fontname[0] == 'C') );
2921   }
2922   return( FALSE );
2923 }
2924
2925 /*
2926  *  end_font - shuts down the font corresponding to fontname.
2927  */
2928
2929 void html_printer::end_font (const char *fontname)
2930 {
2931   if (strcmp(fontname, "B") == 0) {
2932     current_paragraph->done_bold();
2933   } else if (strcmp(fontname, "I") == 0) {
2934     current_paragraph->done_italic();
2935   } else if (strcmp(fontname, "BI") == 0) {
2936     current_paragraph->done_bold();
2937     current_paragraph->done_italic();
2938   } else if (strcmp(fontname, "CR") == 0) {
2939     current_paragraph->done_tt();
2940   } else if (strcmp(fontname, "CI") == 0) {
2941     current_paragraph->done_italic();
2942     current_paragraph->done_tt();
2943   } else if (strcmp(fontname, "CB") == 0) {
2944     current_paragraph->done_bold();
2945     current_paragraph->done_tt();
2946   } else if (strcmp(fontname, "CBI") == 0) {
2947     current_paragraph->done_bold();
2948     current_paragraph->done_italic();
2949     current_paragraph->done_tt();
2950   }
2951 }
2952
2953 /*
2954  *  start_font - starts the font corresponding to name.
2955  */
2956
2957 void html_printer::start_font (const char *fontname)
2958 {
2959   if (strcmp(fontname, "R") == 0) {
2960     current_paragraph->done_bold();
2961     current_paragraph->done_italic();
2962     current_paragraph->done_tt();
2963   } else if (strcmp(fontname, "B") == 0) {
2964     current_paragraph->do_bold();
2965   } else if (strcmp(fontname, "I") == 0) {
2966     current_paragraph->do_italic();
2967   } else if (strcmp(fontname, "BI") == 0) {
2968     current_paragraph->do_bold();
2969     current_paragraph->do_italic();
2970   } else if (strcmp(fontname, "CR") == 0) {
2971     if ((! fill_on) && (is_courier_until_eol())) {
2972       current_paragraph->do_pre();
2973     }
2974     current_paragraph->do_tt();
2975   } else if (strcmp(fontname, "CI") == 0) {
2976     if ((! fill_on) && (is_courier_until_eol())) {
2977       current_paragraph->do_pre();
2978     }
2979     current_paragraph->do_tt();
2980     current_paragraph->do_italic();
2981   } else if (strcmp(fontname, "CB") == 0) {
2982     if ((! fill_on) && (is_courier_until_eol())) {
2983       current_paragraph->do_pre();
2984     }
2985     current_paragraph->do_tt();
2986     current_paragraph->do_bold();
2987   } else if (strcmp(fontname, "CBI") == 0) {
2988     if ((! fill_on) && (is_courier_until_eol())) {
2989       current_paragraph->do_pre();
2990     }
2991     current_paragraph->do_tt();
2992     current_paragraph->do_italic();
2993     current_paragraph->do_bold();
2994   }
2995 }
2996
2997 /*
2998  *  start_size - from is old font size, to is the new font size.
2999  *               The html increase <big> and <small> decrease alters the
3000  *               font size by 20%. We try and map these onto glyph sizes.
3001  */
3002
3003 void html_printer::start_size (int from, int to)
3004 {
3005   if (from < to) {
3006     while (from < to) {
3007       current_paragraph->do_big();
3008       from += SIZE_INCREMENT;
3009     }
3010   } else if (from > to) {
3011     while (from > to) {
3012       current_paragraph->do_small();
3013       from -= SIZE_INCREMENT;
3014     }
3015   }
3016 }
3017
3018 /*
3019  *  do_font - checks to see whether we need to alter the html font.
3020  */
3021
3022 void html_printer::do_font (text_glob *g)
3023 {
3024   /*
3025    *  check if the output_style.point_size has not been set yet
3026    *  this allow users to place .ps at the top of their troff files
3027    *  and grohtml can then treat the .ps value as the base font size (3)
3028    */
3029   if (output_style.point_size == -1) {
3030     output_style.point_size = pointsize;
3031   }
3032
3033   if (g->text_style.f != output_style.f) {
3034     if (output_style.f != 0) {
3035       end_font(output_style.f->get_name());
3036     }
3037     output_style.f = g->text_style.f;
3038     if (output_style.f != 0) {
3039       start_font(output_style.f->get_name());
3040     }
3041   }
3042   if (output_style.point_size != g->text_style.point_size) {
3043     do_sup_or_sub(g);
3044     if ((output_style.point_size > 0) &&
3045         (g->text_style.point_size > 0)) {
3046       start_size(output_style.point_size, g->text_style.point_size);
3047     }
3048     if (g->text_style.point_size > 0) {
3049       output_style.point_size = g->text_style.point_size;
3050     }
3051   }
3052   if (output_style.col != g->text_style.col) {
3053     current_paragraph->done_color();
3054     output_style.col = g->text_style.col;
3055     current_paragraph->do_color(&output_style.col);
3056   }
3057 }
3058
3059 /*
3060  *  start_subscript - returns TRUE if, g, looks like a subscript start.
3061  */
3062
3063 int html_printer::start_subscript (text_glob *g)
3064 {
3065   int r        = font::res;
3066   int height   = output_style.point_size*r/72;
3067
3068   return( (output_style.point_size != 0) &&
3069           (output_vpos < g->minv) &&
3070           (output_vpos-height > g->maxv) &&
3071           (output_style.point_size > g->text_style.point_size) );
3072 }
3073
3074 /*
3075  *  start_superscript - returns TRUE if, g, looks like a superscript start.
3076  */
3077
3078 int html_printer::start_superscript (text_glob *g)
3079 {
3080   int r        = font::res;
3081   int height   = output_style.point_size*r/72;
3082
3083   return( (output_style.point_size != 0) &&
3084           (output_vpos > g->minv) &&
3085           (output_vpos-height < g->maxv) &&
3086           (output_style.point_size > g->text_style.point_size) );
3087 }
3088
3089 /*
3090  *  end_subscript - returns TRUE if, g, looks like the end of a subscript.
3091  */
3092
3093 int html_printer::end_subscript (text_glob *g)
3094 {
3095   int r        = font::res;
3096   int height   = output_style.point_size*r/72;
3097
3098   return( (output_style.point_size != 0) &&
3099           (g->minv < output_vpos) &&
3100           (output_vpos-height > g->maxv) &&
3101           (output_style.point_size < g->text_style.point_size) );
3102 }
3103
3104 /*
3105  *  end_superscript - returns TRUE if, g, looks like the end of a superscript.
3106  */
3107
3108 int html_printer::end_superscript (text_glob *g)
3109 {
3110   int r        = font::res;
3111   int height   = output_style.point_size*r/72;
3112
3113   return( (output_style.point_size != 0) &&
3114           (g->minv > output_vpos) &&
3115           (output_vpos-height < g->maxv) &&
3116           (output_style.point_size < g->text_style.point_size) );
3117 }
3118
3119 /*
3120  *  do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
3121  *                  start/end and it calls the services of html-text to issue the
3122  *                  appropriate tags.
3123  */
3124
3125 void html_printer::do_sup_or_sub (text_glob *g)
3126 {
3127   if (! supress_sub_sup) {
3128     if (start_subscript(g)) {
3129       current_paragraph->do_sub();
3130     } else if (start_superscript(g)) {
3131       current_paragraph->do_sup();
3132     } else if (end_subscript(g)) {
3133       current_paragraph->done_sub();
3134     } else if (end_superscript(g)) {
3135       current_paragraph->done_sup();
3136     }
3137   }
3138 }
3139
3140 /*
3141  *  emit_html - write out the html text
3142  */
3143
3144 void html_printer::emit_html (text_glob *g)
3145 {
3146   do_font(g);
3147   determine_space(g);
3148   current_paragraph->do_emittext(g->text_string, g->text_length);
3149   output_vpos     = g->minv;
3150   output_hpos     = g->maxh;
3151   output_vpos_max = g->maxv;
3152   supress_sub_sup = FALSE;
3153 }
3154
3155 /*
3156  *  flush_sbuf - flushes the current sbuf into the list of glyphs.
3157  */
3158
3159 void html_printer::flush_sbuf()
3160 {
3161   if (sbuf.length() > 0) {
3162     int r=font::res;   // resolution of the device
3163     set_style(sbuf_style);
3164     if (overstrike_detected && (! is_bold(sbuf_style.f))) {
3165       font *bold_font = make_bold(sbuf_style.f);
3166       if (bold_font != NULL)
3167         sbuf_style.f = bold_font;
3168     }
3169
3170     page_contents->add(&sbuf_style, sbuf,
3171                        line_number,
3172                        sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
3173                        sbuf_vpos                           , sbuf_end_hpos);
3174              
3175     output_hpos = sbuf_end_hpos;
3176     output_vpos = sbuf_vpos;
3177     last_sbuf_length = 0;
3178     sbuf_prev_hpos = sbuf_end_hpos;
3179     overstrike_detected = FALSE;
3180     sbuf.clear();
3181   }
3182 }
3183
3184 void html_printer::set_line_thickness(const environment *env)
3185 {
3186   line_thickness = env->size;
3187 }
3188
3189 void html_printer::draw(int code, int *p, int np, const environment *env)
3190 {
3191   switch (code) {
3192
3193   case 'l':
3194 # if 0
3195     if (np == 2) {
3196       page_contents->add_line(&sbuf_style,
3197                               line_number,
3198                               env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
3199     } else {
3200       error("2 arguments required for line");
3201     }
3202 # endif
3203     break;
3204   case 't':
3205     {
3206       if (np == 0) {
3207         line_thickness = -1;
3208       } else {
3209         // troff gratuitously adds an extra 0
3210         if (np != 1 && np != 2) {
3211           error("0 or 1 argument required for thickness");
3212           break;
3213         }
3214         line_thickness = p[0];
3215       }
3216       break;
3217     }
3218
3219   case 'P':
3220     break;
3221   case 'p':
3222     break;
3223   case 'E':
3224     break;
3225   case 'e':
3226     break;
3227   case 'C':
3228     break;
3229   case 'c':
3230     break;
3231   case 'a':
3232     break;
3233   case '~':
3234     break;
3235   case 'f':
3236     break;
3237   case 'F':
3238     // fill with color env->fill
3239     if (background != NULL)
3240       delete background;
3241     background = new color;
3242     *background = *env->fill;
3243     break;
3244
3245   default:
3246     error("unrecognised drawing command `%1'", char(code));
3247     break;
3248   }
3249 }
3250
3251 html_printer::html_printer()
3252 : html(0, MAX_LINE_LENGTH),
3253   no_of_printed_pages(0),
3254   last_sbuf_length(0),
3255   overstrike_detected(FALSE),
3256   output_hpos(-1),
3257   output_vpos(-1),
3258   output_vpos_max(-1),
3259   line_thickness(-1),
3260   inside_font_style(0),
3261   page_number(0),
3262   header_indent(-1),
3263   supress_sub_sup(TRUE),
3264   cutoff_heading(100),
3265   indent(NULL),
3266   table(NULL),
3267   end_center(0),
3268   end_tempindent(0),
3269   next_tag(INLINE),
3270   fill_on(TRUE),
3271   max_linelength(-1),
3272   linelength(0),
3273   pageoffset(0),
3274   indentation(0),
3275   prev_indent(0),
3276   pointsize(0),
3277   line_number(0),
3278   background(default_background)
3279 {
3280   file_list.add_new_file(xtmpfile());
3281   html.set_file(file_list.get_file());
3282   if (font::hor != 24)
3283     fatal("horizontal resolution must be 24");
3284   if (font::vert != 40)
3285     fatal("vertical resolution must be 40");
3286 #if 0
3287   // should be sorted html..
3288   if (font::res % (font::sizescale*72) != 0)
3289     fatal("res must be a multiple of 72*sizescale");
3290 #endif
3291   int r = font::res;
3292   int point = 0;
3293   while (r % 10 == 0) {
3294     r /= 10;
3295     point++;
3296   }
3297   res               = r;
3298   html.set_fixed_point(point);
3299   space_char_index  = font::name_to_index("space");
3300   space_width       = font::hor;
3301   paper_length      = font::paperlength;
3302   linelength        = font::res*13/2;
3303   if (paper_length == 0)
3304     paper_length    = 11*font::res;
3305
3306   page_contents = new page();
3307 }
3308
3309 /*
3310  *  add_to_sbuf - adds character code or name to the sbuf.
3311  */
3312
3313 void html_printer::add_to_sbuf (int index, const string &s)
3314 {
3315   if (sbuf_style.f == NULL)
3316     return;
3317
3318   char *html_glyph = NULL;
3319   unsigned int code = sbuf_style.f->get_code(index);
3320
3321   if (s.empty()) {
3322     if (sbuf_style.f->contains(index))
3323       html_glyph = (char *)sbuf_style.f->get_special_device_encoding(index);
3324     else
3325       html_glyph = NULL;
3326     
3327     if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
3328       html_glyph = to_unicode(code);
3329   } else 
3330     html_glyph = get_html_translation(sbuf_style.f, s);
3331
3332   last_sbuf_length = sbuf.length();
3333   if (html_glyph == NULL)
3334     sbuf += ((char)code);
3335   else
3336     sbuf += html_glyph;
3337 }
3338
3339 int html_printer::sbuf_continuation (int index, const char *name,
3340                                      const environment *env, int w)
3341 {
3342   /*
3343    *  lets see whether the glyph is closer to the end of sbuf
3344    */
3345   if ((sbuf_end_hpos == env->hpos)
3346       || ((sbuf_prev_hpos < sbuf_end_hpos)
3347           && (env->hpos < sbuf_end_hpos)
3348           && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
3349     add_to_sbuf(index, name);
3350     sbuf_prev_hpos = sbuf_end_hpos;
3351     sbuf_end_hpos += w + sbuf_kern;
3352     return TRUE;
3353   } else {
3354     if ((env->hpos >= sbuf_end_hpos) &&
3355         ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
3356       /*
3357        *  lets see whether a space is needed or not
3358        */
3359
3360       if (env->hpos-sbuf_end_hpos < space_width) {
3361         add_to_sbuf(index, name);
3362         sbuf_prev_hpos = sbuf_end_hpos;
3363         sbuf_end_hpos = env->hpos + w;
3364         return TRUE;
3365       }
3366     }
3367   }
3368   return FALSE ;
3369 }
3370
3371 /*
3372  *  get_html_translation - given the position of the character and its name
3373  *                         return the device encoding for such character.
3374  */
3375
3376 char *get_html_translation (font *f, const string &name)
3377 {
3378   int index;
3379
3380   if ((f == 0) || name.empty())
3381     return NULL;
3382   else {
3383     index = f->name_to_index((char *)(name + '\0').contents());
3384     if (index == 0) {
3385       error("character `%s' not found", (name + '\0').contents());
3386       return NULL;
3387     } else
3388       if (f->contains(index))
3389         return (char *)f->get_special_device_encoding(index);
3390       else
3391         return NULL;
3392   }
3393 }
3394
3395 /*
3396  *  overstrike - returns TRUE if the glyph (i, name) is going to overstrike
3397  *               a previous glyph in sbuf.
3398  *               If TRUE the font is changed to bold and the previous sbuf
3399  *               is flushed.
3400  */
3401
3402 int html_printer::overstrike(int index, const char *name, const environment *env, int w)
3403 {
3404   if ((env->hpos < sbuf_end_hpos)
3405       || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
3406     /*
3407      *  at this point we have detected an overlap
3408      */
3409     if (overstrike_detected) {
3410       /* already detected, remove previous glyph and use this glyph */
3411       sbuf.set_length(last_sbuf_length);
3412       add_to_sbuf(index, name);
3413       sbuf_end_hpos = env->hpos + w;
3414       return TRUE;
3415     } else {
3416       /* first time we have detected an overstrike in the sbuf */
3417       sbuf.set_length(last_sbuf_length); /* remove previous glyph */
3418       if (! is_bold(sbuf_style.f))
3419         flush_sbuf();
3420       overstrike_detected = TRUE;
3421       add_to_sbuf(index, name);
3422       sbuf_end_hpos = env->hpos + w;
3423       return TRUE;
3424     }
3425   }
3426   return FALSE ;
3427 }
3428
3429 /*
3430  *  set_char - adds a character into the sbuf if it is a continuation with the previous
3431  *             word otherwise flush the current sbuf and add character anew.
3432  */
3433
3434 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
3435 {
3436   style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
3437   if (sty.slant != 0) {
3438     if (sty.slant > 80 || sty.slant < -80) {
3439       error("silly slant `%1' degrees", sty.slant);
3440       sty.slant = 0;
3441     }
3442   }
3443   if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
3444       && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
3445     return;
3446   
3447   flush_sbuf();
3448   add_to_sbuf(i, name);
3449   sbuf_end_hpos = env->hpos + w;
3450   sbuf_start_hpos = env->hpos;
3451   sbuf_prev_hpos = env->hpos;
3452   sbuf_vpos = env->vpos;
3453   sbuf_style = sty;
3454   sbuf_kern = 0;
3455 }
3456
3457 /*
3458  *  write_title - writes the title to this document
3459  */
3460
3461 void html_printer::write_title (int in_head)
3462 {
3463   if (title.has_been_found) {
3464     if (in_head) {
3465       html.put_string("<title>");
3466       html.put_string(title.text);
3467       html.put_string("</title>").nl().nl();
3468     } else {
3469       title.has_been_written = TRUE;
3470       if (title.with_h1) {
3471         html.put_string("<h1 align=center>");
3472         html.put_string(title.text);
3473         html.put_string("</h1>").nl().nl();
3474       }
3475     }
3476   } else if (in_head) {
3477     // place empty title tags to help conform to `tidy'
3478     html.put_string("<title></title>").nl();
3479   }
3480 }
3481
3482 /*
3483  *  write_rule - emits a html rule tag, if the auto_rule boolean is true.
3484  */
3485
3486 static void write_rule (void)
3487 {
3488   if (auto_rule)
3489     fputs("<hr>\n", stdout);
3490 }
3491
3492 void html_printer::begin_page(int n)
3493 {
3494   page_number            =  n;
3495 #if defined(DEBUGGING)
3496   html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
3497 #endif
3498   no_of_printed_pages++;
3499
3500   output_style.f         =  0;
3501   output_style.point_size= -1;
3502   output_space_code      = 32;
3503   output_draw_point_size = -1;
3504   output_line_thickness  = -1;
3505   output_hpos            = -1;
3506   output_vpos            = -1;
3507   output_vpos_max        = -1;
3508   current_paragraph      = new html_text(&html);
3509   do_indent(indentation, pageoffset, linelength);
3510   current_paragraph->do_para("");
3511 }
3512
3513 void html_printer::end_page(int)
3514 {
3515   flush_sbuf();
3516   flush_page();
3517 }
3518
3519 font *html_printer::make_font(const char *nm)
3520 {
3521   return html_font::load_html_font(nm);
3522 }
3523
3524 void html_printer::do_body (void)
3525 {
3526   if (background == NULL)
3527     fputs("<body>\n\n", stdout);
3528   else {
3529     unsigned int r, g, b;
3530     char buf[6+1];
3531
3532     background->get_rgb(&r, &g, &b);
3533     // we have to scale 0..0xFFFF to 0..0xFF
3534     sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
3535
3536     fputs("<body bgcolor=\"#", stdout);
3537     fputs(buf, stdout);
3538     fputs("\">\n\n", stdout);
3539   }
3540 }
3541
3542 html_printer::~html_printer()
3543 {
3544 #ifdef LONG_FOR_TIME_T
3545   long t;
3546 #else
3547   time_t t;
3548 #endif
3549
3550   current_paragraph->flush_text();
3551   html.end_line();
3552   html.set_file(stdout);
3553   html.begin_comment("Creator     : ")
3554     .put_string("groff ")
3555     .put_string("version ")
3556     .put_string(Version_string)
3557     .end_comment();
3558
3559   t = time(0);
3560   html.begin_comment("CreationDate: ")
3561     .put_string(ctime(&t), strlen(ctime(&t))-1)
3562     .end_comment();
3563
3564   /*
3565    *  'HTML: The definitive guide', O'Reilly, p47. advises against specifying
3566    *         the dtd.
3567    */
3568   // fputs("<!doctype html public \"-//IETF//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n", stdout);
3569   fputs("<html>\n", stdout);
3570   fputs("<head>\n", stdout);
3571   fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
3572   fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
3573   write_title(TRUE);
3574   fputs("</head>\n", stdout);
3575   do_body();
3576
3577   write_title(FALSE);
3578   header.write_headings(stdout, FALSE);
3579   write_rule();
3580 #if defined(DEBUGGING)
3581   html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
3582 #endif
3583   html.end_line();
3584   html.end_line();
3585   /*
3586    *  now run through the file list copying each temporary file in turn and emitting the links.
3587    */
3588   file_list.start_of_list();
3589   while (file_list.get_file() != 0) {
3590     if (fseek(file_list.get_file(), 0L, 0) < 0)
3591       fatal("fseek on temporary file failed");
3592     html.copy_file(file_list.get_file());
3593     fclose(file_list.get_file());
3594     file_list.move_next();
3595     if (file_list.get_file() != 0)
3596       header.write_headings(stdout, TRUE);
3597   }
3598   write_rule();
3599   fputs("</body>\n", stdout);
3600   fputs("</html>\n", stdout);
3601 }
3602
3603 /*
3604  *  special - handle all x X requests from troff. For post-html they allow users
3605  *            to pass raw html commands, turn auto linked headings off/on and
3606  *            also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
3607  */
3608
3609 void html_printer::special(char *s, const environment *env, char type)
3610 {
3611   if (type != 'p')
3612     return;
3613   if (s != 0) {
3614     flush_sbuf();
3615     if (env->fontno >= 0) {
3616       style sty(get_font_from_index(env->fontno), env->size, env->height,
3617                 env->slant, env->fontno, *env->col);
3618       sbuf_style = sty;
3619     }
3620
3621     if (strncmp(s, "html:", 5) == 0) {
3622       int r=font::res;   /* resolution of the device */
3623       font *f=sbuf_style.f;
3624
3625       if (f == NULL) {
3626         int found=FALSE;
3627
3628         f = font::load_font("TR", &found);
3629       }
3630
3631       /*
3632        *  need to pass rest of string through to html output during flush
3633        */
3634       page_contents->add_and_encode(&sbuf_style, string(&s[5]),
3635                                     line_number,
3636                                     env->vpos-env->size*r/72, env->hpos,
3637                                     env->vpos               , env->hpos);
3638
3639       /*
3640        * assume that the html command has no width, if it does then hopefully troff
3641        * will have fudged this in a macro by requesting that the formatting move right by
3642        * the appropriate amount.
3643        */
3644     } else if (strncmp(s, "index:", 6) == 0) {
3645       cutoff_heading = atoi(&s[6]);
3646     } else if (strncmp(s, "html-tag:", 9) == 0) {
3647       int r=font::res;   /* resolution of the device */
3648
3649       page_contents->add_tag(&sbuf_style, string(s),
3650                              line_number,
3651                              env->vpos-env->size*r/72, env->hpos,
3652                              env->vpos               , env->hpos);
3653     }
3654   }
3655 }
3656
3657 int main(int argc, char **argv)
3658 {
3659   program_name = argv[0];
3660   static char stderr_buf[BUFSIZ];
3661   setbuf(stderr, stderr_buf);
3662   int c;
3663   static const struct option long_options[] = {
3664     { "help", no_argument, 0, CHAR_MAX + 1 },
3665     { "version", no_argument, 0, 'v' },
3666     { NULL, 0, 0, 0 }
3667   };
3668   while ((c = getopt_long(argc, argv, "a:g:o:i:I:D:F:vbdhlrnp", long_options, NULL))
3669          != EOF)
3670     switch(c) {
3671     case 'v':
3672       printf("GNU post-grohtml (groff) version %s\n", Version_string);
3673       exit(0);
3674       break;
3675     case 'a':
3676       /* text antialiasing bits - handled by pre-html */
3677       break;
3678     case 'g':
3679       /* graphic antialiasing bits - handled by pre-html */
3680       break;
3681     case 'b':
3682       // set background color to white
3683       default_background = new color;
3684       default_background->set_gray(color::MAX_COLOR_VAL);
3685       break;
3686     case 'F':
3687       font::command_line_font_dir(optarg);
3688       break;
3689     case 'l':
3690       auto_links = FALSE;
3691       break;
3692     case 'r':
3693       auto_rule = FALSE;
3694       break;
3695     case 'd':
3696       /* handled by pre-html */
3697       break;
3698     case 'h':
3699       /* do not use the Hn headings of html, but manufacture our own */
3700       manufacture_headings = TRUE;
3701       break;
3702     case 'o':
3703       /* handled by pre-html */
3704       break;
3705     case 'p':
3706       /* handled by pre-html */
3707       break;
3708     case 'i':
3709       /* handled by pre-html */
3710       break;
3711     case 'I':
3712       /* handled by pre-html */
3713       break;
3714     case 'D':
3715       /* handled by pre-html */
3716       break;
3717     case 'n':
3718       simple_anchors = TRUE;
3719       break;
3720     case CHAR_MAX + 1: // --help
3721       usage(stdout);
3722       exit(0);
3723       break;
3724     case '?':
3725       usage(stderr);
3726       exit(1);
3727       break;
3728     default:
3729       assert(0);
3730     }
3731   if (optind >= argc) {
3732     do_file("-");
3733   } else {
3734     for (int i = optind; i < argc; i++)
3735       do_file(argv[i]);
3736   }
3737   delete pr;
3738   return 0;
3739 }
3740
3741 static void usage(FILE *stream)
3742 {
3743   fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
3744           program_name);
3745 }