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