]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/groff/grohtml/html.cc
This commit was generated by cvs2svn to compensate for changes in r56893,
[FreeBSD/FreeBSD.git] / contrib / groff / grohtml / html.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1999 Free Software Foundation, Inc.
3  *
4  *  Gaius Mulley (gaius@glam.ac.uk) wrote grohtml
5  *  but it owes a huge amount of ideas and raw code from
6  *  James Clark (jjc@jclark.com) grops/ps.cc.
7  */
8
9 /*
10 This file is part of groff.
11
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
16
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 for more details.
21
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING.  If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25
26 #include "driver.h"
27 #include "stringclass.h"
28 #include "cset.h"
29
30 #include "html.h"
31 #include <time.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include "ordered_list.h"
38
39 #if !defined(TRUE)
40 #   define TRUE  (1==1)
41 #endif
42 #if !defined(FALSE)
43 #   define FALSE (1==0)
44 #endif
45
46 #define MAX_TEMP_NAME                1024
47 #define MAX_STRING_LENGTH            4096
48
49 #define Y_FUDGE_MARGIN              +0.83
50 #define A4_PAGE_LENGTH              (11.6944-Y_FUDGE_MARGIN)
51 #define DEFAULT_IMAGE_RES              80
52 #define IMAGE_BOARDER_PIXELS           10
53 #define MAX_WORDS_PER_LINE           1000        // only used for table indentation
54 #define GAP_SPACES                      3        // how many spaces needed to guess a gap?
55 #define GAP_WIDTH_ONE_LINE              2        // 1/GAP_WIDTH_ONE_LINE inches required for one line table
56 #define CENTER_TOLERANCE                2        // how many pixels off center will we think a line or region is centered
57 #define MIN_COLUMN                      7        // minimum column size pixels
58
59
60 /*
61  *  Only uncomment one of the following to determine default image type.
62  */
63
64 #define IMAGE_DEFAULT_PNG
65 /* #define IMAGE_DEFAULT_GIF */
66
67
68 #if defined(IMAGE_DEFAULT_GIF)
69 static enum { gif, png } image_type = gif;
70 static char *image_device           = "gif";
71 #elif defined(IMAGE_DEFAULT_PNG)
72 static enum { gif, png } image_type = png;
73 static char *image_device           = "png256";
74 #else
75 #   error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG"
76 #endif
77
78 static int debug_on                 = FALSE;
79 static int guess_on                 =  TRUE;
80 static int margin_on                = FALSE;
81 static int auto_on                  =  TRUE;
82 static int table_on                 =  TRUE;
83 static int image_res                = DEFAULT_IMAGE_RES;
84 static int debug_table_on           = FALSE;
85
86 static int linewidth = -1;
87
88 #define DEFAULT_LINEWIDTH 40    /* in ems/1000 */
89 #define MAX_LINE_LENGTH 72
90 #define FILL_MAX 1000
91
92 void stop () {}
93
94
95 /*
96  *  start with a few favorites
97  */
98
99 static int min (int a, int b)
100 {
101   if (a < b) {
102     return( a );
103   } else {
104     return( b );
105   }
106 }
107
108 static int max (int a, int b)
109 {
110   if (a > b) {
111     return( a );
112   } else {
113     return( b );
114   }
115 }
116
117
118 /*
119  *  is_subsection - returns TRUE if a1..a2 is within b1..b2
120  */
121
122 static int is_subsection (int a1, int a2, int b1, int b2)
123 {
124   // easier to see whether this is not the case
125   return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) );
126 }
127
128
129 /*
130  *  is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
131  */
132
133 static int is_intersection (int a1, int a2, int b1, int b2)
134 {
135   // again easier to prove NOT outside limits
136   return( ! ((a1 > b2) || (a2 < b1)) );
137 }
138
139
140 /*
141  *  is_digit - returns TRUE if character, ch, is a digit.
142  */
143
144 static int is_digit (char ch)
145 {
146   return( (ch >= '0') && (ch <= '9') );
147 }
148
149
150 /*
151  * more_than_line_break - returns TRUE should v1 and v2 differ by more than
152  *                        a simple line break.
153  */
154
155 static int more_than_line_break (int v1, int v2, int size)
156 {
157   return( abs(v1-v2)>size );
158 }
159
160
161 /*
162  *  the class and methods for styles
163  */
164
165 struct style {
166   font        *f;
167   int          point_size;
168   int          font_no;
169   int          height;
170   int          slant;
171                style       ();
172                style       (font *, int, int, int, int);
173   int          operator == (const style &) const;
174   int          operator != (const style &) const;
175 };
176
177 style::style()
178   : f(0)
179 {
180 }
181
182 style::style(font *p, int sz, int h, int sl, int no)
183   : f(p), point_size(sz), height(h), slant(sl), font_no(no)
184 {
185 }
186
187 int style::operator==(const style &s) const
188 {
189   return (f == s.f && point_size == s.point_size
190           && height == s.height && slant == s.slant);
191 }
192
193 int style::operator!=(const style &s) const
194 {
195   return !(*this == s);
196 }
197
198
199 /*
200  *  the class and methods for retaining ascii text
201  */
202
203 struct char_block {
204   enum { SIZE = 256 };
205   char          buffer[SIZE];
206   int           used;
207   char_block   *next;
208
209   char_block();
210 };
211
212 char_block::char_block()
213 : next(0), used(0)
214 {
215 }
216
217 class char_buffer {
218 public:
219   char_buffer();
220   ~char_buffer();
221   char  *add_string(char *, unsigned int);
222 private:
223   char_block *head;
224   char_block *tail;
225 };
226
227 char_buffer::char_buffer()
228 : head(0), tail(0)
229 {
230 }
231
232 char_buffer::~char_buffer()
233 {
234   while (head != 0) {
235     char_block *temp = head;
236     head = head->next;
237     delete temp;
238   }
239 }
240
241 char *char_buffer::add_string (char *s, unsigned int length)
242 {
243   int i=0;
244   unsigned int old_used;
245
246   if (tail == 0) {
247     tail = new char_block;
248     head = tail;
249   } else {
250     if (tail->used + length+1 > char_block::SIZE) {
251       tail->next = new char_block;
252       tail       = tail->next;
253     }
254   }
255   // at this point we have a tail which is ready for the string.
256   if (tail->used + length+1 > char_block::SIZE) {
257     fatal("need to increase char_block::SIZE");
258   }
259
260   old_used = tail->used;
261   do {
262     tail->buffer[tail->used] = s[i];
263     tail->used++;
264     i++;
265     length--;
266   } while (length>0);
267
268   // add terminating nul character
269
270   tail->buffer[tail->used] = '\0';
271   tail->used++;
272
273   // and return start of new string
274
275   return( &tail->buffer[old_used] );
276 }
277
278 /*
279  *  the classes and methods for maintaining pages and text positions and graphic regions
280  */
281
282 class text_glob {
283 public:
284   int        is_less (text_glob *a, text_glob *b);
285   text_glob  (style *s, char *string, unsigned int length,
286               int min_vertical, int min_horizontal,
287               int max_vertical, int max_horizontal, int is_command, int is_html);
288   text_glob  (void);
289   ~text_glob (void);
290
291   style           text_style;
292   char           *text_string;
293   unsigned int    text_length;
294   int             minv, maxv, minh, maxh;
295   int             is_raw_command;       // should the text be sent directly to the device?
296   int             is_html_command;      // is the raw command definitely for the html device ie not an eqn?
297 };
298
299 text_glob::text_glob (style *s, char *string, unsigned int length,
300                       int min_vertical, int min_horizontal,
301                       int max_vertical, int max_horizontal, int is_command, int is_html)
302   : text_style(*s), text_string(string), text_length(length),
303     minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
304     is_raw_command(is_command), is_html_command(is_html)
305 {
306 }
307
308 text_glob::text_glob ()
309   : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
310     is_raw_command(FALSE), is_html_command(FALSE)
311 {
312 }
313
314 text_glob::~text_glob ()
315 {
316 }
317
318 int text_glob::is_less (text_glob *a, text_glob *b)
319 {
320   if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
321     return( a->minh < b->minh );
322   } else {
323     return( a->maxv < b->maxv );
324   }
325 }
326
327 struct xycoord {
328   int x;
329   int y;
330 };
331
332 class graphic_glob {
333 public:
334   int             is_less (graphic_glob *a, graphic_glob *b);
335   graphic_glob    (int troff_code);
336   graphic_glob    (void);
337   ~graphic_glob   (void);
338
339   int             minv, maxv, minh, maxh;
340   int             xc, yc;
341   int             nopoints;           // number of points allocated in array below
342   struct xycoord *point;
343   int             size;
344   int             fill;
345   int             code;
346 };
347
348 graphic_glob::graphic_glob ()
349   : minv(-1), maxv(-1), minh(-1), maxh(-1), code(0), size(0), nopoints(0), point(0)
350 {
351 }
352
353 graphic_glob::~graphic_glob ()
354 {
355   if (point != 0) {
356     free(point);
357   }
358 }
359
360 graphic_glob::graphic_glob (int troff_code)
361   : minv(-1), maxv(-1), minh(-1), maxh(-1), code(troff_code), size(0), nopoints(0), point(0)
362 {
363 }
364
365 int graphic_glob::is_less (graphic_glob *a, graphic_glob *b)
366 {
367   return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
368 }
369
370 class region_glob {
371 public:
372                   region_glob (void);
373                  ~region_glob (void);
374   int             is_less     (region_glob *a, region_glob *b);
375
376   int minv, maxv, minh, maxh;
377 };
378
379 int region_glob::is_less (region_glob *a, region_glob *b)
380 {
381   return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
382 }
383
384 region_glob::region_glob (void)
385   : minv(-1), maxv(-1), minh(-1), maxh(-1)
386 {
387 }
388
389 region_glob::~region_glob (void)
390 {
391 }
392
393 class page {
394 public:
395                               page                     (void);
396   void                        add                      (style *s, char *string, unsigned int length,
397                                                         int min_vertical, int min_horizontal,
398                                                         int max_vertical, int max_horizontal);
399   void                        add_html_command         (style *s, char *string, unsigned int length,
400                                                         int min_vertical, int min_horizontal,
401                                                         int max_vertical, int max_horizontal);
402   void                        add_special_char         (style *s, char *string, unsigned int length,
403                                                         int min_vertical, int min_horizontal,
404                                                         int max_vertical, int max_horizontal);
405   void                        add_line                 (int code, int x1, int y1, int x2, int y2, int size, int fill);
406   void                        add_arc                  (int code, int xc, int yc, int *p, double *c, int size, int fill);
407   void                        add_polygon              (int code, int np, int *p, int oh, int ov, int size, int fill);
408   void                        add_spline               (int code, int xc, int yc, int np, int *p, int size, int fill);
409   void                        calculate_region         (void);
410   int                         is_in_region             (graphic_glob *g);
411   int                         can_grow_region          (graphic_glob *g);
412   void                        make_new_region          (graphic_glob *g);
413   int                         has_line                 (region_glob *r);
414   int                         has_word                 (region_glob *r);
415   int                         no_raw_commands          (int minv, int maxv);
416
417   // and the data
418
419   ordered_list <region_glob>  regions;       // squares of bitmapped pics,eqn,tbl's
420   ordered_list <text_glob>    words;         // position of words on page
421   ordered_list <graphic_glob> lines;         // position of lines on page
422   char_buffer                 buffer;        // all characters for this page
423   int                         is_in_graphic; // should graphics and words go below or above
424   ordered_list <text_glob>    region_words;  // temporary accumulation of words in a region
425   ordered_list <graphic_glob> region_lines;  // (as above) and used so that we can determine
426                                              // the regions vertical limits
427 };
428
429 page::page()
430   : is_in_graphic(FALSE)
431 {
432 }
433
434 void page::add (style *s, char *string, unsigned int length,
435                 int min_vertical, int min_horizontal,
436                 int max_vertical, int max_horizontal)
437 {
438   if (length > 0) {
439     text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
440                                min_vertical, min_horizontal, max_vertical, max_horizontal, FALSE, FALSE);
441     if (is_in_graphic) {
442       region_words.add(g);
443     } else {
444       words.add(g);
445     }
446   }
447 }
448
449 /*
450  *  add_html_command - it only makes sense to add html commands when we are not inside
451  *                     a graphical entity.
452  */
453
454 void page::add_html_command (style *s, char *string, unsigned int length,
455                              int min_vertical, int min_horizontal,
456                              int max_vertical, int max_horizontal)
457 {
458   if ((length > 0) && (! is_in_graphic)) {
459     text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
460                                min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, TRUE);
461     words.add(g);
462   }
463 }
464
465 /*
466  *  add_special_char - it only makes sense to add special characters when we are inside
467  *                     a graphical entity.
468  */
469
470 void page::add_special_char (style *s, char *string, unsigned int length,
471                              int min_vertical, int min_horizontal,
472                              int max_vertical, int max_horizontal)
473 {
474   if ((length > 0) && (is_in_graphic)) {
475     text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
476                                min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, FALSE);
477     region_words.add(g);
478   }
479 }
480
481 void page::add_line (int code, int x1, int y1, int x2, int y2, int size, int fill)
482 {
483   graphic_glob *g = new graphic_glob(code);
484
485   g->minh       = min(x1, x2);
486   g->maxh       = max(x1, x2);
487   g->minv       = min(y1, y2);
488   g->maxv       = max(y1, y2);
489   g->point      = (struct xycoord *)malloc(sizeof(xycoord)*2);
490   g->nopoints   = 2;
491   g->point[0].x = x1 ;
492   g->point[0].y = y1 ;
493   g->point[1].x = x2 ;
494   g->point[1].y = y2 ;
495   g->xc         = 0;
496   g->yc         = 0;
497   g->size       = size;
498   g->fill       = fill;
499
500   if (is_in_graphic) {
501     region_lines.add(g);
502   } else {
503     lines.add(g);
504   }
505 }
506
507 /*
508  *  assign_min_max_for_arc - works out the smallest box that will encompass an
509  *                           arc defined by:  origin: g->xc, g->xc
510  *                           and vector (p[0], p[1]) and (p[2], p[3])
511  */
512
513 void assign_min_max_for_arc (graphic_glob *g, int *p, double *c)
514 {
515   int radius = (int) sqrt(c[0]*c[0]+c[1]*c[1]);
516   int xv1    = p[0];
517   int yv1    = p[1];
518   int xv2    = p[2];
519   int yv2    = p[3];
520   int x1     = g->xc+xv1;
521   int y1     = g->yc+yv1;
522   int x2     = g->xc+xv1+xv2;
523   int y2     = g->yc+yv1+yv2;
524
525   // firstly lets use the 'circle' limitation
526   g->minh = x1-radius;
527   g->maxh = x1+radius;
528   g->minv = y1-radius;
529   g->maxv = y1+radius;
530
531   // incidentally I'm sure there is a better way to do this, but I don't know it
532   // please can someone let me know or "improve" this function
533
534   // now see which min/max can be reduced and increased for the limits of the arc
535   //
536   //
537   //       Q2   |   Q1
538   //       -----+-----
539   //       Q3   |   Q4
540   //
541
542
543   if ((xv1>=0) && (yv1>=0)) {
544     // first vector in Q3
545     if ((xv2>=0) && (yv2>=0)) {
546       // second in Q1
547       g->maxh = x2;
548       g->minv = y1;
549     } else if ((xv2<0) && (yv2>=0)) {
550       // second in Q2
551       g->maxh = x2;
552       g->minv = y1;
553     } else if ((xv2>=0) && (yv2<0)) {
554       // second in Q4
555       g->minv = min(y1, y2);
556     } else if ((xv2<0) && (yv2<0)) {
557       // second in Q3
558       if (x1>=x2) {
559         g->minh = x2;
560         g->maxh = x1;
561         g->minv = min(y1, y2);
562         g->maxv = max(y1, y2);
563       } else {
564         // xv2, yv2 could all be zero?
565       }
566     }
567   } else if ((xv1>=0) && (yv1<0)) {
568     // first vector in Q2
569     if ((xv2>=0) && (yv2>=0)) {
570       // second in Q1
571       g->maxh = max(x1, x2);
572       g->minh = min(x1, x2);
573       g->minv = y1;
574     } else if ((xv2<0) && (yv2>=0)) {
575       // second in Q2
576       if (x1<x2) {
577         g->maxh = x2;
578         g->minh = x1;
579         g->minv = min(y1, y2);
580         g->maxv = max(y1, y2);
581       } else {
582         // otherwise almost full circle anyway
583       }
584     } else if ((xv2>=0) && (yv2<0)) {
585       // second in Q4
586       g->minv = y2;
587       g->minh = x1;
588     } else if ((xv2<0) && (yv2<0)) {
589       // second in Q3
590       g->minh = min(x1, x2);
591     }
592   } else if ((xv1<0) && (yv1<0)) {
593     // first vector in Q1
594     if ((xv2>=0) && (yv2>=0)) {
595       // second in Q1
596       if (x1<x2) {
597         g->minh = x1;
598         g->maxh = x2;
599         g->minv = min(y1, y2);
600         g->maxv = max(y1, y2);
601       } else {
602         // nearly full circle
603       }
604     } else if ((xv2<0) && (yv2>=0)) {
605       // second in Q2
606       g->maxv = max(y1, y2);
607     } else if ((xv2>=0) && (yv2<0)) {
608       // second in Q4
609       g->minv = min(y1, y2);
610       g->maxv = max(y1, y2);
611       g->minh = min(x1, x2);
612     } else if ((xv2<0) && (yv2<0)) {
613       // second in Q3
614       g->minh = x2;
615       g->maxv = y1;
616     }
617   } else if ((xv1<0) && (yv1>=0)) {
618     // first vector in Q4
619     if ((xv2>=0) && (yv2>=0)) {
620       // second in Q1
621       g->maxh = max(x1, x2);
622     } else if ((xv2<0) && (yv2>=0)) {
623       // second in Q2
624       g->maxv = max(y1, y2);
625       g->maxh = max(x1, x2);
626     } else if ((xv2>=0) && (yv2<0)) {
627       // second in Q4
628       if (x1>=x2) {
629         g->minv = min(y1, y2);
630         g->maxv = max(y1, y2);
631         g->minh = min(x1, x2);
632         g->maxh = max(x2, x2);
633       } else {
634         // nearly full circle
635       }
636     } else if ((xv2<0) && (yv2<0)) {
637       // second in Q3
638       g->maxv = max(y1, y2);
639       g->minh = min(x1, x2);
640       g->maxh = max(x1, x2);
641     }
642   }
643   // this should *never* happen but if it does it means a case above is wrong..
644
645   // this code is only present for safety sake
646   if (g->maxh < g->minh) {
647     if (debug_on) {
648       fprintf(stderr, "assert failed minh > maxh\n"); fflush(stderr);
649       stop();
650     }
651     g->maxh = g->minh;
652   }
653   if (g->maxv < g->minv) {
654     if (debug_on) {
655       fprintf(stderr, "assert failed minv > maxv\n"); fflush(stderr);
656       stop();
657     }
658     g->maxv = g->minv;
659   }
660 }
661
662 void page::add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill)
663 {
664   graphic_glob *g = new graphic_glob(code);
665
666   g->point      = (struct xycoord *)malloc(sizeof(xycoord)*2);
667   g->nopoints   = 2;
668   g->point[0].x = p[0] ;
669   g->point[0].y = p[1] ;
670   g->point[1].x = p[2] ;
671   g->point[1].y = p[3] ;
672   g->xc         = xc;
673   g->yc         = yc;
674   g->size       = size;
675   g->fill       = fill;
676
677   assign_min_max_for_arc(g, p, c);
678
679   if (is_in_graphic) {
680     region_lines.add(g);
681   } else {
682     lines.add(g);
683   }
684 }
685
686
687 void page::add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill)
688 {
689   graphic_glob *g = new graphic_glob(code);
690   int           j = 0;
691   int           i;
692
693   g->point      = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
694   g->nopoints   = np/2;
695
696   for (i=0; i<g->nopoints; i++) {
697     g->point[i].x = p[j];
698     j++;
699     g->point[i].y = p[j];
700     j++;
701   }
702   // now calculate min/max
703   g->minh = g->point[0].x;
704   g->minv = g->point[0].y;
705   g->maxh = g->point[0].x;
706   g->maxv = g->point[0].y;
707   for (i=1; i<g->nopoints; i++) {
708     g->minh = min(g->minh, g->point[i].x);
709     g->minv = min(g->minv, g->point[i].y);
710     g->maxh = max(g->maxh, g->point[i].x);
711     g->maxv = max(g->maxv, g->point[i].y);
712   }
713   g->size = size;
714   g->xc   = oh;
715   g->yc   = ov;
716   g->fill = fill;
717
718   if (is_in_graphic) {
719     region_lines.add(g);
720   } else {
721     lines.add(g);
722   }
723 }
724
725 void page::add_spline (int code, int xc, int yc, int np, int *p, int size, int fill)
726 {
727   graphic_glob *g = new graphic_glob(code);
728   int           j = 0;
729   int           i;
730
731   g->point      = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
732   g->nopoints   = np/2;
733
734   for (i=0; i<g->nopoints; i++) {
735     g->point[i].x = p[j];
736     j++;
737     g->point[i].y = p[j];
738     j++;
739   }
740   // now calculate min/max
741   g->minh = min(g->point[0].x, g->point[0].x/2);
742   g->minv = min(g->point[0].y, g->point[0].y/2);
743   g->maxh = max(g->point[0].x, g->point[0].x/2);
744   g->maxv = max(g->point[0].y, g->point[0].y/2);
745
746   /* tnum/tden should be between 0 and 1; the closer it is to 1
747      the tighter the curve will be to the guiding lines; 2/3
748      is the standard value */
749   const int tnum = 2;
750   const int tden = 3;
751
752   for (i=1; i<g->nopoints-1; i++) {
753     g->minh = min(g->minh, g->point[i].x*tnum/(2*tden));
754     g->minv = min(g->minv, g->point[i].y*tnum/(2*tden));
755     g->maxh = max(g->maxh, g->point[i].x*tnum/(2*tden));
756     g->maxv = max(g->maxv, g->point[i].y*tnum/(2*tden));
757
758     g->minh = min(g->minh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
759     g->minv = min(g->minv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
760     g->maxh = max(g->maxh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
761     g->maxv = max(g->maxv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
762
763     g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
764     g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
765     g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
766     g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
767   }
768   i = g->nopoints-1;
769
770   g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2)) + xc;
771   g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2)) + yc;
772   g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2)) + xc;
773   g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2)) + yc;
774
775   g->size = size;
776   g->xc   = xc;
777   g->yc   = yc;
778   g->fill = fill;
779
780   if (is_in_graphic) {
781     region_lines.add(g);
782   } else {
783     lines.add(g);
784   }
785 }
786
787 /*
788  *  the classes and methods for simple_output manipulation
789  */
790
791 simple_output::simple_output(FILE *f, int n)
792 : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
793 {
794 }
795
796 simple_output &simple_output::set_file(FILE *f)
797 {
798   fp = f;
799   col = 0;
800   return *this;
801 }
802
803 simple_output &simple_output::copy_file(FILE *infp)
804 {
805   int c;
806   while ((c = getc(infp)) != EOF)
807     putc(c, fp);
808   return *this;
809 }
810
811 simple_output &simple_output::end_line()
812 {
813   if (col != 0) {
814     putc('\n', fp);
815     col = 0;
816     need_space = 0;
817   }
818   return *this;
819 }
820
821 simple_output &simple_output::special(const char *s)
822 {
823   return *this;
824 }
825
826 simple_output &simple_output::simple_comment(const char *s)
827 {
828   if (col != 0)
829     putc('\n', fp);
830   fputs("<!-- ", fp);
831   fputs(s, fp);
832   fputs(" -->\n", fp);
833   col = 0;
834   need_space = 0;
835   return *this;
836 }
837
838 simple_output &simple_output::begin_comment(const char *s)
839 {
840   if (col != 0)
841     putc('\n', fp);
842   fputs("<!-- ", fp);
843   fputs(s, fp);
844   col = 5 + strlen(s);
845   return *this;
846 }
847
848 simple_output &simple_output::end_comment()
849 {
850   if (need_space) {
851     putc(' ', fp);
852   }
853   fputs(" -->\n", fp);
854   col = 0;
855   need_space = 0;
856   return *this;
857 }
858
859 simple_output &simple_output::comment_arg(const char *s)
860 {
861   int len = strlen(s);
862
863   if (col + len + 1 > max_line_length) {
864     fputs("\n ", fp);
865     col = 1;
866   }
867   fputs(s, fp);
868   col += len + 1;
869   return *this;
870 }
871
872 simple_output &simple_output::set_fixed_point(int n)
873 {
874   assert(n >= 0 && n <= 10);
875   fixed_point = n;
876   return *this;
877 }
878
879 simple_output &simple_output::put_delimiter(char c)
880 {
881   putc(c, fp);
882   col++;
883   need_space = 0;
884   return *this;
885 }
886
887 simple_output &simple_output::put_string(const char *s, int n)
888 {
889   int i=0;
890
891   while (i<n) {
892     fputc(s[i], fp);
893     i++;
894   }
895   col += n;
896   return *this;
897 }
898
899 simple_output &simple_output::put_translated_string(const char *s)
900 {
901   int i=0;
902
903   while (s[i] != (char)0) {
904     if ((s[i] & 0x7f) == s[i]) {
905       fputc(s[i], fp);
906     }
907     i++;
908   }
909   col += i;
910   return *this;
911 }
912
913 simple_output &simple_output::put_string(const char *s)
914 {
915   int i=0;
916
917   while (s[i] != '\0') {
918     fputc(s[i], fp);
919     i++;
920   }
921   col += i;
922   return *this;
923 }
924
925 struct html_2_postscript {
926   char *html_char;
927   char *postscript_char;
928 };
929
930 static struct html_2_postscript char_conversions[] = {
931   "+-", "char177",
932   "eq", "=",
933   "mu", "char215",
934   NULL, NULL,
935 };
936
937
938 // this is an aweful hack which attempts to translate html characters onto
939 // postscript characters. Can this be done inside the devhtml files?
940 //
941 // or should we read the devps files and find out the translations?
942 //
943
944 simple_output &simple_output::put_translated_char (const char *s)
945 {
946   int i=0;
947
948   while (char_conversions[i].html_char != NULL) {
949     if (strcmp(s, char_conversions[i].html_char) == 0) {
950       put_string(char_conversions[i].postscript_char);
951       return *this;
952     } else {
953       i++;
954     }
955   }
956   put_string(s);
957   return *this;
958 }
959
960 simple_output &simple_output::put_number(int n)
961 {
962   char buf[1 + INT_DIGITS + 1];
963   sprintf(buf, "%d", n);
964   int len = strlen(buf);
965   put_string(buf, len);
966   need_space = 1;
967   return *this;
968 }
969
970 simple_output &simple_output::put_float(double d)
971 {
972   char buf[128];
973
974   sprintf(buf, "%.4f", d);
975   int len = strlen(buf);
976   put_string(buf, len);
977   need_space = 1;
978   return *this;
979 }
980
981
982 simple_output &simple_output::put_symbol(const char *s)
983 {
984   int len = strlen(s);
985
986   if (need_space) {
987     putc(' ', fp);
988     col++;
989   }
990   fputs(s, fp);
991   col += len;
992   need_space = 1;
993   return *this;
994 }
995
996 class html_font : public font {
997   html_font(const char *);
998 public:
999   int encoding_index;
1000   char *encoding;
1001   char *reencoded_name;
1002   ~html_font();
1003   void handle_unknown_font_command(const char *command, const char *arg,
1004                                    const char *filename, int lineno);
1005   static html_font *load_html_font(const char *);
1006 };
1007
1008 html_font *html_font::load_html_font(const char *s)
1009 {
1010   html_font *f = new html_font(s);
1011   if (!f->load()) {
1012     delete f;
1013     return 0;
1014   }
1015   return f;
1016 }
1017
1018 html_font::html_font(const char *nm)
1019 : font(nm)
1020 {
1021 }
1022
1023 html_font::~html_font()
1024 {
1025 }
1026
1027 void html_font::handle_unknown_font_command(const char *command, const char *arg,
1028                                             const char *filename, int lineno)
1029 {
1030   if (strcmp(command, "encoding") == 0) {
1031     if (arg == 0)
1032       error_with_file_and_line(filename, lineno,
1033                                "`encoding' command requires an argument");
1034     else
1035       encoding = strsave(arg);
1036   }
1037 }
1038
1039
1040 /*
1041  *  a simple class to contain the header to this document
1042  */
1043
1044 class title_desc {
1045 public:
1046           title_desc ();
1047          ~title_desc ();
1048
1049   int     has_been_written;
1050   int     has_been_found;
1051   char    text[MAX_STRING_LENGTH];
1052 };
1053
1054
1055 title_desc::title_desc ()
1056   : has_been_found(FALSE), has_been_written(FALSE)
1057 {
1058 }
1059
1060 title_desc::~title_desc ()
1061 {
1062 }
1063
1064 class header_desc {
1065 public:
1066                             header_desc ();
1067                            ~header_desc ();
1068
1069   int                       no_of_headings;      // how many headings have we found?
1070   char_buffer               headings;            // all the headings used in the document
1071   ordered_list  <text_glob> headers;
1072   int                       header_level;        // current header level
1073   int                       written_header;      // have we written the header yet?
1074   char                      header_buffer[MAX_STRING_LENGTH];  // current header text
1075
1076   void                      write_headings (FILE *f);
1077 };
1078
1079 header_desc::header_desc ()
1080   :   no_of_headings(0), header_level(2), written_header(0)
1081 {
1082 }
1083
1084 header_desc::~header_desc ()
1085 {
1086 }
1087
1088 /*
1089  *  paragraph_type - alignment for a new paragraph
1090  */
1091
1092 typedef enum { left_alignment, center_alignment } paragraph_type;
1093
1094 /*
1095  *  text_defn - defines the limit of text, initially these are stored in the
1096  *              column array as words. Later we examine the white space between
1097  *              the words in successive lines to find out whether we can detect
1098  *              distinct columns. The columns are generated via html tables.
1099  */
1100
1101 struct text_defn {
1102   int left;       // the start of a word or text
1103   int right;      // the end of the text and beginning of white space
1104   int is_used;    // will this this column be used for words or space
1105 };
1106
1107
1108 /*
1109  * note that html_tables are currently only used to provide a better
1110  * indentation mechanism for html text (in particular it allows grohtml
1111  * to render .IP and .2C together with autoformatting).
1112  */
1113
1114 class html_table {
1115 public:
1116                             html_table ();
1117                            ~html_table ();
1118
1119   int                       no_of_columns;     // how many columns are we using?
1120   struct text_defn         *columns;           // left and right margins for each column
1121   int                       vertical_limit;    // the limit of the table
1122 };
1123
1124 html_table::html_table ()
1125   : no_of_columns(0), columns(0), vertical_limit(0)
1126 {
1127 }
1128
1129 html_table::~html_table ()
1130 {
1131 }
1132
1133 class html_printer : public printer {
1134   FILE                *tempfp;
1135   simple_output        html;
1136   simple_output        troff;
1137   int                  res;
1138   int                  postscript_res;
1139   int                  space_char_index;
1140   int                  no_of_printed_pages;
1141   int                  paper_length;
1142   enum                 { SBUF_SIZE = 256 };
1143   char                 sbuf[SBUF_SIZE];
1144   int                  sbuf_len;
1145   int                  sbuf_start_hpos;
1146   int                  sbuf_vpos;
1147   int                  sbuf_end_hpos;
1148   int                  sbuf_space_width;
1149   int                  sbuf_space_count;
1150   int                  sbuf_space_diff_count;
1151   int                  sbuf_space_code;
1152   int                  sbuf_kern;
1153   style                sbuf_style;
1154   style                output_style;
1155   int                  output_hpos;
1156   int                  output_vpos;
1157   int                  output_draw_point_size;
1158   int                  line_thickness;
1159   int                  output_line_thickness;
1160   int                  fill;
1161   unsigned char        output_space_code;
1162   string               defs;
1163   char                *inside_font_style;
1164   int                  page_number;
1165   title_desc           title;
1166   header_desc          header;
1167   page                *page_contents;
1168   html_table           indentation;
1169   int                  left_margin_indent;
1170   int                  right_margin_indent;
1171   int                  need_one_newline;
1172   int                  issued_newline;
1173   int                  in_paragraph;
1174   int                  need_paragraph;
1175   paragraph_type       para_type;
1176   char                 image_name[MAX_STRING_LENGTH];
1177   int                  image_number;
1178   int                  graphic_level;
1179
1180   int                  start_region_vpos;
1181   int                  start_region_hpos;
1182   int                  end_region_vpos;
1183   int                  end_region_hpos;
1184   int                  cutoff_heading;
1185
1186   struct graphic_glob *start_graphic;
1187   struct text_glob    *start_text;
1188
1189
1190   void  flush_sbuf                    ();
1191   void  set_style                     (const style &);
1192   void  set_space_code                (unsigned char c);
1193   void  do_exec                       (char *, const environment *);
1194   void  do_import                     (char *, const environment *);
1195   void  do_def                        (char *, const environment *);
1196   void  do_mdef                       (char *, const environment *);
1197   void  do_file                       (char *, const environment *);
1198   void  set_line_thickness            (const environment *);
1199   void  change_font                   (text_glob *g, int is_to_html);
1200   void  terminate_current_font        (void);
1201   void  flush_font                    (void);
1202   void  flush_page                    (void);
1203   void  display_word                  (text_glob *g, int is_to_html);
1204   void  html_display_word             (text_glob *g);
1205   void  troff_display_word            (text_glob *g);
1206   void  display_line                  (graphic_glob *g, int is_to_html);
1207   void  display_fill                  (graphic_glob *g);
1208   void  calculate_margin              (void);
1209   void  traverse_page_regions         (void);
1210   void  dump_page                     (void);
1211   int   is_within_region              (graphic_glob *g);
1212   int   is_within_region              (text_glob *t);
1213   int   is_less                       (graphic_glob *g, text_glob *t);
1214   void  display_globs                 (int is_to_html);
1215   void  move_horizontal               (text_glob *g, int left_margin);
1216   void  move_vertical                 (text_glob *g, paragraph_type p);
1217   void  write_html_font_face          (const char *fontname, const char *left, const char *right);
1218   void  write_html_font_type          (const char *fontname, const char *left, const char *right);
1219   void  html_change_font              (text_glob *g, const char *fontname, int size);
1220   char *html_position_text            (text_glob *g, int left_margin, int right_margin);
1221   int   html_position_region          (void);
1222   void  troff_change_font             (const char *fontname, int size, int font_no);
1223   void  troff_position_text           (text_glob *g);
1224   int   pretend_is_on_same_line       (text_glob *g, int left_margin, int right_margin);
1225   int   is_on_same_line               (text_glob *g, int vpos);
1226   int   looks_like_subscript          (text_glob *g);
1227   int   looks_like_superscript        (text_glob *g);
1228   void  begin_paragraph               (paragraph_type p);
1229   void  begin_paragraph_no_height     (paragraph_type p);
1230   void  force_begin_paragraph         (void);
1231   void  end_paragraph                 (void);
1232   void  html_newline                  (void);
1233   void  convert_to_image              (char *name);
1234   void  write_title                   (int in_head);
1235   void  find_title                    (void);
1236   int   is_bold                       (text_glob *g);
1237   void  write_header                  (void);
1238   void  determine_header_level        (void);
1239   void  build_header                  (text_glob *g);
1240   void  make_html_indent              (int indent);
1241   int   is_whole_line_bold            (text_glob *g);
1242   int   is_a_header                   (text_glob *g);
1243   int   processed_header              (text_glob *g);
1244   void  make_new_image_name           (void);
1245   void  create_temp_name              (char *name, char *extension);
1246   void  calculate_region_margins      (region_glob *r);
1247   void  remove_redundant_regions      (void);
1248   void  remove_duplicate_regions      (void);
1249   void  move_region_to_page           (void);
1250   void  calculate_region_range        (graphic_glob *r);
1251   void  flush_graphic                 (void);
1252   void  write_string                  (graphic_glob *g, int is_to_html);
1253   void  prologue                      (void);
1254   int   gs_x                          (int x);
1255   int   gs_y                          (int y);
1256   void  display_regions               (void);
1257   int   check_able_to_use_table       (text_glob *g);
1258   int   using_table_for_indent        (void);
1259   int   collect_columns               (struct text_defn *line, struct text_defn *last, int max_words);
1260   void  include_into_list             (struct text_defn *line, struct text_defn *item);
1261   int   is_in_column                  (struct text_defn *line, struct text_defn *item, int max_words);
1262   int   is_column_match               (struct text_defn *match, struct text_defn *line1, struct text_defn *line2, int max_words);
1263   int   count_columns                 (struct text_defn *line);
1264   void  rewind_text_to                (text_glob *g);
1265   int   found_use_for_table           (text_glob *start);
1266   void  column_display_word           (int vert, int left, int right, int next);
1267   void  start_table                   (void);
1268   void  end_table                     (void);
1269   void  foreach_column_include_text   (text_glob *start);
1270   void  define_cell                   (int left, int right);
1271   int   column_calculate_left_margin  (int left, int right);
1272   int   column_calculate_right_margin (int left, int right);
1273   void  display_columns               (const char *word, const char *name, text_defn *line);
1274   void  calculate_right               (struct text_defn *line, int max_words);
1275   void  determine_right_most_column   (struct text_defn *line, int max_words);
1276   int   remove_white_using_words      (struct text_defn *next_guess, struct text_defn *last_guess, struct text_defn *next_line);
1277   int   copy_line                     (struct text_defn *dest, struct text_defn *src);
1278   void  combine_line                  (struct text_defn *dest, struct text_defn *src);
1279   int   conflict_with_words           (struct text_defn *column_guess, struct text_defn *words);
1280   void  remove_entry_in_line          (struct text_defn *line, int j);
1281   void  remove_redundant_columns      (struct text_defn *line);
1282   void  add_column_gaps               (struct text_defn *line);
1283   int   continue_searching_column     (text_defn *next_col, text_defn *last_col, text_defn *all_words);
1284   void  add_right_full_width          (struct text_defn *line, int mingap);
1285   int   is_continueous_column         (text_defn *last_col, text_defn *next_line);
1286   int   is_exact_left                 (text_defn *last_col, text_defn *next_line);
1287   void  emit_space                    (text_glob *g, int force_space);
1288   int   is_in_middle                  (int left, int right);
1289   int   check_able_to_use_center      (text_glob *g);
1290   void  write_centered_line           (text_glob *g);
1291   int   single_centered_line          (text_defn *first, text_defn *second, text_glob *g);
1292   int   determine_row_limit           (text_glob *start, int v);
1293   void  assign_used_columns           (text_glob *start);
1294   int   find_column_index             (text_glob *t);
1295   int   large_enough_gap              (text_defn *last_col);
1296   int   is_worth_column               (int left, int right);
1297   int   is_subset_of_columns          (text_defn *a, text_defn *b);
1298   void  count_hits                    (text_defn *col);
1299   int   calculate_min_gap             (text_glob *g);
1300
1301 public:
1302   html_printer();
1303   ~html_printer();
1304   void set_char(int i, font *f, const environment *env, int w, const char *name);
1305   void draw(int code, int *p, int np, const environment *env);
1306   void begin_page(int);
1307   void end_page(int);
1308   void special(char *arg, const environment *env);
1309   font *make_font(const char *);
1310   void end_of_line();
1311 };
1312
1313 html_printer::html_printer()
1314 : no_of_printed_pages(0),
1315   sbuf_len(0),
1316   output_hpos(-1),
1317   output_vpos(-1),
1318   html(0, MAX_LINE_LENGTH),
1319   troff(0, MAX_LINE_LENGTH),
1320   line_thickness(-1),
1321   inside_font_style(0),
1322   fill(FILL_MAX + 1),
1323   page_number(0),
1324   left_margin_indent(0),
1325   right_margin_indent(0),
1326   start_region_vpos(0),
1327   start_region_hpos(0),
1328   end_region_vpos(0),
1329   end_region_hpos(0),
1330   need_one_newline(0),
1331   issued_newline(0),
1332   image_number(0),
1333   graphic_level(0),
1334   cutoff_heading(100),
1335   in_paragraph(0),
1336   need_paragraph(0),
1337   para_type(left_alignment)
1338 {
1339   tempfp = xtmpfile();
1340   html.set_file(tempfp);
1341   if (linewidth < 0)
1342     linewidth = DEFAULT_LINEWIDTH;
1343   if (font::hor != 1)
1344     fatal("horizontal resolution must be 1");
1345   if (font::vert != 1)
1346     fatal("vertical resolution must be 1");
1347 #if 0
1348   // should be sorted html..
1349   if (font::res % (font::sizescale*72) != 0)
1350     fatal("res must be a multiple of 72*sizescale");
1351 #endif
1352   int r = font::res;
1353   int point = 0;
1354   while (r % 10 == 0) {
1355     r /= 10;
1356     point++;
1357   }
1358   res = r;
1359   html.set_fixed_point(point);
1360   space_char_index = font::name_to_index("space");
1361   paper_length = font::paperlength;
1362   if (paper_length == 0)
1363     paper_length = 11*font::res;
1364   page_contents   = new page;
1365
1366   postscript_res = 72000;
1367 }
1368
1369
1370 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
1371 {
1372   unsigned char code = f->get_code(i);
1373
1374   style sty(f, env->size, env->height, env->slant, env->fontno);
1375   if (sty.slant != 0) {
1376     if (sty.slant > 80 || sty.slant < -80) {
1377       error("silly slant `%1' degrees", sty.slant);
1378       sty.slant = 0;
1379     }
1380   }
1381   if ((name != 0) && (page_contents->is_in_graphic)) {
1382     flush_sbuf();
1383     int r=font::res;   // resolution of the device actually
1384     page_contents->add_special_char(&sty, (char *)name, strlen(name),
1385                                     env->vpos-sty.point_size*r/72, env->hpos,
1386                                     env->vpos, env->hpos+w);
1387     sbuf_end_hpos   = env->hpos + w;
1388     sbuf_start_hpos = env->hpos;
1389     sbuf_vpos       = env->vpos;
1390     sbuf_style      = sty;
1391     sbuf_kern       = 0;
1392     return;
1393   }
1394
1395   if (sbuf_len > 0) {
1396     if (sbuf_len < SBUF_SIZE
1397         && sty == sbuf_style
1398         && sbuf_vpos == env->vpos) {
1399       if (sbuf_end_hpos == env->hpos) {
1400         sbuf[sbuf_len++] = code;
1401         sbuf_end_hpos += w + sbuf_kern;
1402         return;
1403       }
1404       /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
1405          starting a new string. */
1406       if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
1407           && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
1408         if (sbuf_space_code < 0) {
1409 #if 0
1410           sbuf_space_code = ' ';
1411           sbuf_space_count++;
1412           sbuf_space_width = env->hpos - sbuf_end_hpos;
1413           sbuf_end_hpos = env->hpos + w + sbuf_kern;
1414           sbuf[sbuf_len++] = ' ';
1415           sbuf[sbuf_len++] = code;
1416           return;
1417 #endif
1418         } else {
1419           int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
1420           if (diff == 0) {
1421             sbuf_end_hpos = env->hpos + w + sbuf_kern;
1422             sbuf[sbuf_len++] = sbuf_space_code;
1423             sbuf[sbuf_len++] = code;
1424             sbuf_space_count++;
1425             if (diff == 1)
1426               sbuf_space_diff_count++;
1427             else if (diff == -1)
1428               sbuf_space_diff_count--;
1429             return;
1430           }
1431         }
1432       }
1433     }
1434     flush_sbuf();
1435   }
1436   sbuf_len = 1;
1437   sbuf[0] = code;
1438   sbuf_end_hpos = env->hpos + w;
1439   sbuf_start_hpos = env->hpos;
1440   sbuf_vpos = env->vpos;
1441   sbuf_style = sty;
1442   sbuf_space_code = -1;
1443   sbuf_space_width = 0;
1444   sbuf_space_count = sbuf_space_diff_count = 0;
1445   sbuf_kern = 0;
1446 }
1447
1448
1449 /*
1450  *  make_new_image_name - creates a new file name ready for a image file.
1451  *                        it leaves the extension off.
1452  */
1453
1454 void html_printer::make_new_image_name (void)
1455 {
1456   image_number++;
1457   sprintf(image_name, "groff-html-%d-%d", image_number, getpid());
1458 }
1459
1460 /*
1461  *  write_title - writes the title to this document
1462  */
1463
1464 void html_printer::write_title (int in_head)
1465 {
1466   if (title.has_been_found) {
1467     if (in_head) {
1468       html.put_string("<title>");
1469       html.put_string(title.text);
1470       html.put_string("</title>\n");
1471     } else {
1472       title.has_been_written = TRUE;
1473       html.put_string("<h1 align=center>");
1474       html.put_string(title.text);
1475       html.put_string("</h1>\n");
1476     }
1477   }
1478 }
1479
1480
1481 /*
1482  *  find_title - finds a title to this document, if it exists.
1483  */
1484
1485 void html_printer::find_title (void)
1486 {
1487   text_glob    *t;
1488   int           r=font::res;
1489   int           removed_from_head;
1490
1491   if ((page_number == 1) && (guess_on)) {
1492     if (! page_contents->words.is_empty()) {
1493
1494       int end_title_hpos     = 0;
1495       int start_title_hpos   = 0;
1496       int start_title_vpos   = 0;
1497       int found_title_start  = FALSE;
1498       int height             = 0;
1499       int start_region       =-1;
1500
1501       if (! page_contents->regions.is_empty()) {
1502         region_glob *r;
1503
1504         page_contents->regions.start_from_head();
1505         r = page_contents->regions.get_data();
1506         if (r->minv > 0) {
1507           start_region = r->minv;
1508         }
1509       }
1510       
1511       page_contents->words.start_from_head();
1512       do {
1513         t = page_contents->words.get_data();
1514         removed_from_head = FALSE;
1515         if ((found_title_start) && (start_region != -1) && (t->maxv >= start_region)) {
1516           /*
1517            * we have just encountered the first graphic region so
1518            * we stop looking for a title.
1519            */
1520           title.has_been_found = TRUE;
1521           return;
1522         } else if (t->is_raw_command) {
1523           // skip raw commands
1524         } else if ((!found_title_start) && (t->minh > left_margin_indent) &&
1525                    ((start_region == -1) || (t->maxv < start_region))) {
1526           start_title_vpos     = t->minv;
1527           end_title_hpos       = t->minh;
1528           strcpy((char *)title.text, (char *)t->text_string);
1529           height               = t->text_style.point_size*r/72;
1530           found_title_start    = TRUE;
1531           page_contents->words.sub_move_right();
1532           removed_from_head = ((!page_contents->words.is_empty()) &&
1533                                (page_contents->words.is_equal_to_head()));
1534         } else if (found_title_start) {
1535           if ((t->minv == start_title_vpos) ||
1536               ((!more_than_line_break(start_title_vpos, t->minv, (height*3)/2)) &&
1537                (t->minh > left_margin_indent)) ||
1538               (is_bold(t) && (t->minh > left_margin_indent))) {
1539             start_title_vpos = min(t->minv, start_title_vpos);
1540             end_title_hpos   = max(t->maxh, end_title_hpos);
1541             strcat(title.text, " ");
1542             strcat(title.text, (char *)t->text_string);
1543             page_contents->words.sub_move_right();
1544             removed_from_head = ((!page_contents->words.is_empty()) &&
1545                                  (page_contents->words.is_equal_to_head()));
1546           } else {
1547             // end of title
1548             title.has_been_found = TRUE;
1549             return;
1550           }
1551         } else if (t->minh == left_margin_indent) {
1552           // no margin exists
1553           return;
1554         } else {
1555           // move onto next word
1556           page_contents->words.move_right();
1557         }
1558       } while ((! page_contents->words.is_equal_to_head()) || (removed_from_head));
1559     }
1560   }
1561 }
1562
1563 /*
1564  *  html_newline - generates a newline <br>
1565  */
1566
1567 void html_printer::html_newline (void)
1568 {
1569   int r        = font::res;
1570   int height   = output_style.point_size*r/72;
1571
1572   if (in_paragraph) {
1573     // safe to generate a pretty newline
1574     html.put_string("<br>\n");
1575   } else {
1576     html.put_string("<br>");
1577   }
1578   output_vpos += height;
1579   issued_newline = TRUE;
1580 }
1581
1582 /*
1583  *  force_begin_paragraph - force the begin_paragraph to be emitted.
1584  */
1585
1586 void html_printer::force_begin_paragraph (void)
1587 {
1588   if (in_paragraph && need_paragraph) {
1589     switch (para_type) {
1590
1591     case left_alignment:   html.put_string("<p>");
1592                            break;
1593     case center_alignment: html.put_string("<p align=center>");
1594                            break;
1595     default:               fatal("unknown paragraph alignment type");
1596     }
1597     need_paragraph   = FALSE;
1598   }
1599 }
1600
1601 /*
1602  *  begin_paragraph - starts a new paragraph. It does nothing if a paragraph
1603  *                    has already been started.
1604  */
1605
1606 void html_printer::begin_paragraph (paragraph_type p)
1607 {
1608   if (! in_paragraph) {
1609     int r        = font::res;
1610     int height   = output_style.point_size*r/72;
1611
1612     if (output_vpos >=0) {
1613       // we leave it alone if it is set to the top of page
1614       output_vpos += height;
1615     }
1616     need_paragraph = TRUE;   // delay the <p> just in case we don't actually emit text
1617     in_paragraph   = TRUE;
1618     issued_newline = TRUE;
1619     para_type      = p;
1620   }
1621 }
1622
1623
1624 /*
1625  *  begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph
1626  *                              has already been started. Note it does not alter output_vpos.
1627  */
1628
1629 void html_printer::begin_paragraph_no_height (paragraph_type p)
1630 {
1631   if (! in_paragraph) {
1632     need_paragraph = TRUE;   // delay the <p> just in case we don't actually emit text
1633     in_paragraph   = TRUE;
1634     issued_newline = TRUE;
1635     para_type      = p;
1636   }
1637 }
1638
1639 /*
1640  *  end_paragraph - end the current paragraph. It does nothing if a paragraph
1641  *                  has not been started.
1642  */
1643
1644 void html_printer::end_paragraph (void)
1645 {
1646   if (in_paragraph) {
1647     // check whether we have generated any text inbetween the potential paragraph begin end
1648     if (! need_paragraph) {
1649       int r        = font::res;
1650       int height   = output_style.point_size*r/72;
1651
1652       output_vpos += height;
1653       html.put_string("</p>\n");
1654     }
1655     terminate_current_font();
1656     para_type    = left_alignment;
1657     in_paragraph = FALSE;
1658   }
1659 }
1660
1661 /*
1662  *  calculate_margin - runs through the words and graphics globs
1663  *                     and finds the start of the left most margin.
1664  */
1665
1666 void html_printer::calculate_margin (void)
1667 {
1668   if (! margin_on) {
1669     text_glob    *w;
1670     graphic_glob *g;
1671
1672     // remove margin
1673
1674     right_margin_indent = 0;
1675
1676     if (! page_contents->words.is_empty()) {
1677
1678       // firstly check the words right margin
1679
1680       page_contents->words.start_from_head();
1681       do {
1682         w = page_contents->words.get_data();
1683         if ((w->maxh >= 0) && (w->maxh > right_margin_indent)) {
1684           right_margin_indent = w->maxh;
1685 #if 0
1686           if (right_margin_indent == 950) stop();
1687 #endif
1688         }
1689         page_contents->words.move_right();
1690       } while (! page_contents->words.is_equal_to_head());
1691     }
1692
1693     if (! page_contents->lines.is_empty()) {
1694       // now check for diagrams for right margin
1695       page_contents->lines.start_from_head();
1696       do {
1697         g = page_contents->lines.get_data();
1698         if ((g->maxh >= 0) && (g->maxh > right_margin_indent)) {
1699           right_margin_indent = g->maxh;
1700 #if 0
1701           if (right_margin_indent == 950) stop();
1702 #endif
1703         }
1704         page_contents->lines.move_right();
1705       } while (! page_contents->lines.is_equal_to_head());
1706     }
1707
1708     // now we know the right margin lets do the same to find left margin
1709
1710     left_margin_indent  = right_margin_indent;
1711
1712     if (! page_contents->words.is_empty()) {
1713       do {
1714         w = page_contents->words.get_data();
1715         if ((w->minh >= 0) && (w->minh < left_margin_indent)) {
1716           left_margin_indent = w->minh;
1717         }
1718         page_contents->words.move_right();
1719       } while (! page_contents->words.is_equal_to_head());
1720     }
1721
1722     if (! page_contents->lines.is_empty()) {
1723       // now check for diagrams
1724       page_contents->lines.start_from_head();
1725       do {
1726         g = page_contents->lines.get_data();
1727         if ((g->minh >= 0) && (g->minh < left_margin_indent)) {
1728           left_margin_indent = g->minh;
1729         }
1730         page_contents->lines.move_right();
1731       } while (! page_contents->lines.is_equal_to_head());
1732     }
1733   }
1734 }
1735
1736
1737 /*
1738  *  calculate_region - runs through the graphics globs and text globs
1739  *                     and ensures that all graphic routines
1740  *                     are defined by the region lists.
1741  *                     This then allows us to easily
1742  *                     determine the range of vertical and
1743  *                     horizontal boundaries for pictures,
1744  *                     tbl's and eqn's.
1745  *
1746  */
1747
1748 void page::calculate_region (void)
1749 {
1750   graphic_glob *g;
1751
1752   if (! lines.is_empty()) {
1753     lines.start_from_head();
1754     do {
1755       g = lines.get_data();
1756       if (! is_in_region(g)) {
1757         if (! can_grow_region(g)) {
1758           make_new_region(g);
1759         }
1760       }
1761       lines.move_right();
1762     } while (! lines.is_equal_to_head());
1763   }
1764 }
1765
1766 /*
1767  *  remove_redundant_regions - runs through the regions and ensures that
1768  *                             all are needed. This is required as
1769  *                             a picture may be empty, or EQ EN pair
1770  *                             maybe empty.
1771  */
1772
1773 void html_printer::remove_redundant_regions (void)
1774 {
1775   region_glob  *r;
1776   graphic_glob *g;
1777   
1778   // firstly run through the region making sure that all are needed
1779   // ie all contain a line or word
1780   if (! page_contents->regions.is_empty()) {
1781     page_contents->regions.start_from_tail();
1782     do {
1783       r = page_contents->regions.get_data();
1784       calculate_region_margins(r);
1785       if (page_contents->has_line(r) || page_contents->has_word(r)) {
1786         page_contents->regions.move_right();
1787       } else {
1788         page_contents->regions.sub_move_right();
1789       }
1790     } while ((! page_contents->regions.is_empty()) &&
1791              (! page_contents->regions.is_equal_to_tail()));
1792   }
1793 }
1794
1795 void html_printer::display_regions (void)
1796 {
1797   if (debug_table_on) {
1798     region_glob  *r;  
1799
1800     fprintf(stderr, "==========s t a r t===========\n");
1801     if (! page_contents->regions.is_empty()) {
1802       page_contents->regions.start_from_head();
1803       do {
1804         r = page_contents->regions.get_data();
1805         fprintf(stderr, "region minv %d  maxv %d\n", r->minv, r->maxv);
1806         page_contents->regions.move_right();      
1807       } while (! page_contents->regions.is_equal_to_head());
1808     }
1809     fprintf(stderr, "============e n d=============\n");
1810     fflush(stderr);
1811   }
1812 }
1813
1814 /*
1815  *  remove_duplicate_regions - runs through the regions and ensures that
1816  *                             no duplicates exist.
1817  */
1818
1819 void html_printer::remove_duplicate_regions (void)
1820 {
1821   region_glob  *r;
1822   region_glob  *l=0;
1823
1824   if (! page_contents->regions.is_empty()) {
1825     page_contents->regions.start_from_head();
1826     l = page_contents->regions.get_data();
1827     page_contents->regions.move_right();
1828     r = page_contents->regions.get_data();
1829     if (l != r) {
1830       do {
1831         r = page_contents->regions.get_data();
1832         // we have a legit region so we check for an intersection
1833         if (is_intersection(r->minv, r->minv, l->minv, l->maxv) &&
1834             is_intersection(r->minh, r->maxh, l->minh, l->maxh)) {
1835           l->minv = min(r->minv, l->minv);
1836           l->maxv = max(r->maxv, l->maxv);
1837           l->minh = min(r->minh, l->minh);
1838           l->maxh = max(r->maxh, l->maxh);
1839           calculate_region_margins(l);
1840           page_contents->regions.sub_move_right();
1841         } else {
1842           l = r;
1843           page_contents->regions.move_right();
1844         }
1845       } while ((! page_contents->regions.is_empty()) &&
1846                (! page_contents->regions.is_equal_to_head()));
1847     }
1848   }
1849 }
1850
1851 int page::has_line (region_glob *r)
1852 {
1853   graphic_glob *g;
1854
1855   if (! lines.is_empty()) {
1856     lines.start_from_head();
1857     do {
1858       g = lines.get_data();
1859       if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
1860           is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
1861         return( TRUE );
1862       }
1863       lines.move_right();
1864     } while (! lines.is_equal_to_head());
1865   }
1866   return( FALSE );
1867 }
1868
1869
1870 int page::has_word (region_glob *r)
1871 {
1872   text_glob *g;
1873
1874   if (! words.is_empty()) {
1875     words.start_from_head();
1876     do {
1877       g = words.get_data();
1878       if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
1879           is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
1880         return( TRUE );
1881       }
1882       words.move_right();
1883     } while (! words.is_equal_to_head());
1884   }
1885   return( FALSE );
1886 }
1887
1888
1889 void html_printer::calculate_region_margins (region_glob *r)
1890 {
1891   text_glob    *w;
1892   graphic_glob *g;
1893
1894   r->minh = right_margin_indent;
1895   r->maxh = left_margin_indent;
1896   
1897   if (! page_contents->lines.is_empty()) {
1898     page_contents->lines.start_from_head();
1899     do {
1900       g = page_contents->lines.get_data();
1901       if (is_subsection(g->minv, g->maxv, r->minv, r->maxv)) {
1902         r->minh = min(r->minh, g->minh);
1903         r->maxh = max(r->maxh, g->maxh);
1904       }
1905       page_contents->lines.move_right();
1906     } while (! page_contents->lines.is_equal_to_head());
1907   }
1908   if (! page_contents->words.is_empty()) {
1909     page_contents->words.start_from_head();
1910     do {
1911       w = page_contents->words.get_data();
1912       if (is_subsection(w->minv, w->maxv, r->minv, r->maxv)) {
1913         r->minh = min(r->minh, w->minh);
1914         r->maxh = max(r->maxh, w->maxh);
1915       }
1916       page_contents->words.move_right();
1917     } while (! page_contents->words.is_equal_to_head());
1918   }
1919 }
1920
1921
1922 int page::is_in_region (graphic_glob *g)
1923 {
1924   region_glob *r;
1925
1926   if (! regions.is_empty()) {
1927     regions.start_from_head();
1928     do {
1929       r = regions.get_data();
1930       if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
1931           is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
1932         return( TRUE );
1933       }
1934       regions.move_right();
1935     } while (! regions.is_equal_to_head());
1936   }
1937   return( FALSE );
1938 }
1939
1940
1941 /*
1942  *  no_raw_commands - returns TRUE if no html raw commands exist between
1943  *                    minv and maxv.
1944  */
1945
1946 int page::no_raw_commands (int minv, int maxv)
1947 {
1948   text_glob *g;
1949
1950   if (! words.is_empty()) {
1951     words.start_from_head();
1952     do {
1953       g = words.get_data();
1954       if ((g->is_raw_command) && (g->is_html_command) &&
1955           (is_intersection(g->minv, g->maxv, minv, maxv))) {
1956         return( FALSE );
1957       }
1958       words.move_right();
1959     } while (! words.is_equal_to_head());
1960   }
1961   return( TRUE );
1962 }
1963
1964 /*
1965  *  can_grow_region - returns TRUE if a region exists which can be extended
1966  *                    to include graphic_glob *g. The region is extended.
1967  */
1968
1969 int page::can_grow_region (graphic_glob *g)
1970 {
1971   region_glob *r;
1972   int          quarter_inch=font::res/4;
1973
1974   if (! regions.is_empty()) {
1975     regions.start_from_head();
1976     do {
1977       r = regions.get_data();
1978       // must prevent grohtml from growing a region through a html raw command
1979       if (is_intersection(g->minv, g->maxv, r->minv, r->maxv+quarter_inch) &&
1980           (no_raw_commands(r->minv, r->maxv+quarter_inch))) {
1981 #if defined(DEBUGGING)
1982         stop();
1983         printf("r minh=%d  minv=%d  maxh=%d  maxv=%d\n",
1984                r->minh, r->minv, r->maxh, r->maxv);
1985         printf("g minh=%d  minv=%d  maxh=%d  maxv=%d\n",
1986                g->minh, g->minv, g->maxh, g->maxv);
1987 #endif
1988         r->minv = min(r->minv, g->minv);
1989         r->maxv = max(r->maxv, g->maxv);
1990         r->minh = min(r->minh, g->minh);
1991         r->maxh = max(r->maxh, g->maxh);
1992 #if defined(DEBUGGING)
1993         printf("           r minh=%d  minv=%d  maxh=%d  maxv=%d\n",
1994                r->minh, r->minv, r->maxh, r->maxv);
1995 #endif
1996         return( TRUE );
1997       }
1998       regions.move_right();
1999     } while (! regions.is_equal_to_head());
2000   }
2001   return( FALSE );
2002 }
2003
2004
2005 /*
2006  *  make_new_region - creates a new region to contain, g.
2007  */
2008
2009 void page::make_new_region (graphic_glob *g)
2010 {
2011   region_glob *r=new region_glob;
2012
2013   r->minv = g->minv;
2014   r->maxv = g->maxv;
2015   r->minh = g->minh;
2016   r->maxv = g->maxv;
2017   regions.add(r);
2018 }
2019
2020
2021 void html_printer::dump_page(void)
2022 {
2023   text_glob *g;
2024
2025   printf("\n\ndebugging start\n");
2026   page_contents->words.start_from_head();
2027   do {
2028     g = page_contents->words.get_data();
2029     printf("%s ", g->text_string);
2030     page_contents->words.move_right();
2031   } while (! page_contents->words.is_equal_to_head());
2032   printf("\ndebugging end\n\n");
2033 }
2034
2035
2036 /*
2037  *  traverse_page_regions - runs through the regions in current_page
2038  *                          and generate html for text, and troff output
2039  *                          for all graphics.
2040  */
2041
2042 void html_printer::traverse_page_regions (void)
2043 {
2044   region_glob *r;
2045
2046   start_region_vpos =  0;
2047   start_region_hpos =  0;
2048   end_region_vpos   = -1;
2049   end_region_hpos   = -1;
2050
2051   if (! page_contents->regions.is_empty()) {
2052     page_contents->regions.start_from_head();
2053     do {
2054       r = page_contents->regions.get_data();
2055       if (r->minv > 0) {
2056         end_region_vpos = r->minv-1;
2057       } else {
2058         end_region_vpos = 0;
2059       }
2060       end_region_hpos = -1;
2061       display_globs(TRUE);
2062       calculate_region_margins(r);
2063       start_region_vpos = end_region_vpos;
2064       end_region_vpos   = r->maxv;
2065       start_region_hpos = r->minh;
2066       end_region_hpos   = r->maxh;
2067       display_globs(FALSE);
2068       start_region_vpos = end_region_vpos+1;
2069       start_region_hpos = 0;
2070       page_contents->regions.move_right();
2071     } while (! page_contents->regions.is_equal_to_head());
2072     start_region_vpos = end_region_vpos+1;
2073     start_region_hpos = 0;
2074     end_region_vpos = -1;
2075     end_region_hpos = -1;
2076   }
2077   display_globs(TRUE);
2078 }
2079
2080 int html_printer::is_within_region (text_glob *t)
2081 {
2082   int he, ve, hs;
2083
2084   if (start_region_hpos == -1) {
2085     hs = t->minh;
2086   } else {
2087     hs = start_region_hpos;
2088   }
2089   if (end_region_vpos == -1) {
2090     ve = t->maxv;
2091   } else {
2092     ve = end_region_vpos;
2093   }
2094   if (end_region_hpos == -1) {
2095     he = t->maxh;
2096   } else {
2097     he = end_region_hpos;
2098   }
2099   return( is_subsection(t->minv, t->maxv, start_region_vpos, ve) &&
2100           is_subsection(t->minh, t->maxh, hs, he) );
2101 }
2102
2103 int html_printer::is_within_region (graphic_glob *g)
2104 {
2105   int he, ve, hs;
2106
2107   if (start_region_hpos == -1) {
2108     hs = g->minh;
2109   } else {
2110     hs = start_region_hpos;
2111   }
2112   if (end_region_vpos == -1) {
2113     ve = g->maxv;
2114   } else {
2115     ve = end_region_vpos;
2116   }
2117   if (end_region_hpos == -1) {
2118     he = g->maxh;
2119   } else {
2120     he = end_region_hpos;
2121   }
2122   return( is_subsection(g->minv, g->maxv, start_region_vpos, ve) &&
2123           is_subsection(g->minh, g->maxh, hs, he) );
2124 }
2125
2126 int html_printer::is_less (graphic_glob *g, text_glob *t)
2127 {
2128   return( (g->minv < t->minv) || ((g->minv == t->minv) && (g->minh < t->minh)) );
2129 }
2130
2131 static FILE *create_file (char *filename)
2132 {
2133   FILE *f;
2134
2135   errno = 0;
2136   f = fopen(filename, "w");
2137   if (f == 0) {
2138     error("can't create `%1'", filename);
2139     return( 0 );
2140   } else {
2141     return( f );
2142   }
2143 }
2144
2145 void html_printer::convert_to_image (char *name)
2146 {
2147   char buffer[1024];
2148
2149   sprintf(buffer, "grops %s > %s.ps\n", name, name);
2150   if (debug_on) {
2151     fprintf(stderr, "%s", buffer);
2152   }
2153   system(buffer);
2154
2155   if (image_type == gif) {
2156     sprintf(buffer,
2157             "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s.ps - | ppmquant 256  2> /dev/null | ppmtogif  2> /dev/null > %s.gif \n",
2158             image_res,
2159             (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2160             (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2161             name, image_name);
2162   } else {
2163     sprintf(buffer,
2164             "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s.ps - 2> /dev/null > %s.png \n",
2165             image_device,
2166             image_res,
2167             (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2168             (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2169             name, image_name);
2170   }
2171   if (debug_on) {
2172     fprintf(stderr, "%s", buffer);
2173   }
2174   system(buffer);
2175   sprintf(buffer, "/bin/rm -f %s %s.ps\n", name, name);
2176   if (debug_on) {
2177     fprintf(stderr, "%s", buffer);
2178   } else {
2179     system(buffer);
2180   }
2181 }
2182
2183 void html_printer::prologue (void)
2184 {
2185   troff.put_string("x T ps\nx res ");
2186   troff.put_number(postscript_res);
2187   troff.put_string(" 1 1\nx init\np1\n");
2188 }
2189
2190 void html_printer::create_temp_name (char *name, char *extension)
2191 {
2192   make_new_image_name();
2193   sprintf(name, "/tmp/%s.%s", image_name, extension);
2194 }
2195
2196 void html_printer::display_globs (int is_to_html)
2197 {
2198   text_glob    *t=0;
2199   graphic_glob *g=0;
2200   FILE         *f=0;
2201   char          name[MAX_TEMP_NAME];
2202   char          buffer[1024];
2203   int           r=font::res;
2204   int           something=FALSE;
2205   int           is_center=FALSE;
2206
2207   end_paragraph();
2208
2209   if (! is_to_html) {
2210     is_center = html_position_region();
2211     create_temp_name(name, "troff");
2212     f = create_file(name);
2213     troff.set_file(f);
2214     prologue();
2215     output_style.f = 0;
2216   }
2217   if (! page_contents->words.is_empty()) {
2218     page_contents->words.start_from_head();
2219     t = page_contents->words.get_data();
2220   }
2221
2222   if (! page_contents->lines.is_empty()) {
2223     page_contents->lines.start_from_head();
2224     g = page_contents->lines.get_data();
2225   }
2226
2227   do {
2228 #if 0
2229     if ((t != 0) && (strcmp(t->text_string, "(1.a)") == 0)) {
2230       stop();
2231     }
2232 #endif
2233     if ((t == 0) && (g != 0)) {
2234       if (is_within_region(g)) {
2235         something = TRUE;
2236         display_line(g, is_to_html);
2237       }
2238       if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2239         g = 0;
2240       } else {
2241         g = page_contents->lines.move_right_get_data();
2242       }
2243     } else if ((g == 0) && (t != 0)) {
2244       if (is_within_region(t)) {
2245         display_word(t, is_to_html);
2246         something = TRUE;
2247       }
2248       if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2249         t = 0;
2250       } else {
2251         t = page_contents->words.move_right_get_data();
2252       }
2253     } else {
2254       if ((g == 0) || (t == 0)) {
2255         // hmm nothing to print out...
2256       } else if (is_less(g, t)) {
2257         if (is_within_region(g)) {
2258           display_line(g, is_to_html);
2259           something = TRUE;
2260         }
2261         if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2262           g = 0;
2263         } else {
2264           g = page_contents->lines.move_right_get_data();
2265         }
2266       } else {
2267         if (is_within_region(t)) {
2268           display_word(t, is_to_html);
2269           something = TRUE;
2270         }
2271         if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2272           t = 0;
2273         } else {
2274           t = page_contents->words.move_right_get_data();
2275         }
2276       }
2277     }
2278   } while ((t != 0) || (g != 0));
2279
2280   if ((! is_to_html) && (f != 0)) {
2281     fclose(troff.get_file());
2282     if (something) {
2283       convert_to_image(name);
2284
2285       if (is_center) {
2286         begin_paragraph(center_alignment);
2287       } else {
2288         begin_paragraph(left_alignment);
2289       }
2290       force_begin_paragraph();
2291       html.put_string("<img src=\"");
2292       html.put_string(image_name);
2293       if (image_type == gif) {
2294         html.put_string(".gif\"");
2295       } else {
2296         html.put_string(".png\"");
2297       }
2298       if (is_center) {
2299         html.put_string(" align=\"middle\"");
2300       }
2301       html.put_string(">\n");
2302       html_newline();
2303       end_paragraph();
2304
2305       output_vpos      = end_region_vpos;
2306       output_hpos      = 0;
2307       need_one_newline = FALSE;
2308       output_style.f   = 0;
2309     }
2310     // unlink(name);  // remove troff file
2311   }
2312 }
2313
2314 void html_printer::flush_page (void)
2315 {
2316   calculate_margin();
2317   output_vpos = -1;
2318   output_hpos = left_margin_indent;
2319 #if 0
2320   dump_page();
2321 #endif
2322   html.begin_comment("left  margin: ").comment_arg(itoa(left_margin_indent)).end_comment();;
2323   html.begin_comment("right margin: ").comment_arg(itoa(right_margin_indent)).end_comment();;
2324   remove_redundant_regions();
2325   page_contents->calculate_region();
2326   remove_duplicate_regions();
2327   find_title();
2328
2329   traverse_page_regions();
2330   terminate_current_font();
2331   if (need_one_newline) {
2332     html_newline();
2333   }
2334   end_paragraph();
2335   
2336   // move onto a new page
2337   delete page_contents;
2338   page_contents = new page;
2339 }
2340
2341 static int convertSizeToHTML (int size)
2342 {
2343   if (size < 6) {
2344     return( 0 );
2345   } else if (size < 8) {
2346     return( 1 );
2347   } else if (size < 10) {
2348     return( 2 );
2349   } else if (size < 12) {
2350     return( 3 );
2351   } else if (size < 14) {
2352     return( 4 );
2353   } else if (size < 16) {
2354     return( 5 );
2355   } else if (size < 18) {
2356     return( 6 );
2357   } else {
2358     return( 7 );
2359   }
2360 }
2361
2362
2363 void html_printer::write_html_font_face (const char *fontname, const char *left, const char *right)
2364 {
2365   switch (fontname[0]) {
2366
2367   case 'C':  html.put_string(left) ; html.put_string("tt"); html.put_string(right);
2368              break;
2369   case 'H':  break;
2370   case 'T':  break;
2371   default:   break;
2372   }
2373 }
2374
2375
2376 void html_printer::write_html_font_type (const char *fontname, const char *left, const char *right)
2377 {
2378   if (strcmp(&fontname[1], "B") == 0) {
2379     html.put_string(left) ; html.put_string("B"); html.put_string(right);
2380   } else if (strcmp(&fontname[1], "I") == 0) {
2381     html.put_string(left) ; html.put_string("I"); html.put_string(right);
2382   } else if (strcmp(&fontname[1], "BI") == 0) {
2383     html.put_string(left) ; html.put_string("EM"); html.put_string(right);
2384   }
2385 }
2386
2387
2388 void html_printer::html_change_font (text_glob *g, const char *fontname, int size)
2389 {
2390   char        buffer[1024];
2391
2392   if (output_style.f != 0) {
2393     const char       *oldfontname = output_style.f->get_name();
2394
2395     // firstly terminate the current font face and type
2396     if ((oldfontname != 0) && (oldfontname != fontname)) {
2397       write_html_font_face(oldfontname, "</", ">");
2398       write_html_font_type(oldfontname, "</", ">");
2399     }
2400   }
2401   if (fontname != 0) {
2402     // now emit the size if it has changed
2403     if (((output_style.f == 0) || (output_style.point_size != size)) && (size != 0)) {
2404       sprintf(buffer, "<font size=%d>", convertSizeToHTML(size));
2405       html.put_string(buffer);
2406       output_style.point_size = size;  // and remember the size
2407     }
2408
2409     if (! g->is_raw_command) {
2410       // now emit the new font
2411       write_html_font_face(fontname, "<", ">");
2412   
2413       // now emit the new font type
2414       write_html_font_type(fontname, "<", ">");
2415
2416       output_style = g->text_style;  // remember style for next time
2417     }
2418   } else {
2419     output_style.f = 0;   // no style at present
2420   }
2421 }
2422
2423
2424 void html_printer::change_font (text_glob *g, int is_to_html)
2425 {
2426   if (is_to_html) {
2427     if (output_style != g->text_style) {
2428       const char *fontname=0;
2429       int   size=0;
2430
2431       if (g->text_style.f != 0) {
2432         fontname = g->text_style.f->get_name();
2433         size     = (font::res/(72*font::sizescale))*g->text_style.point_size;
2434
2435         html_change_font(g, fontname, size);
2436       } else {
2437         html_change_font(g, fontname, size);
2438       }
2439     }
2440   } else {
2441     // is to troff
2442     if (output_style != g->text_style) {
2443       if (g->text_style.f != 0) {
2444         const char *fontname = g->text_style.f->get_name();
2445         int size             = (font::res/(72*font::sizescale))*g->text_style.point_size;
2446
2447         if (fontname == 0) {
2448           fatal("no internalname specified for font");
2449         }
2450
2451         troff_change_font(fontname, size, g->text_style.font_no);
2452         output_style = g->text_style;  // remember style for next time
2453       }
2454     }
2455   }
2456 }
2457
2458
2459 /*
2460  *  is_bold - returns TRUE if the text inside, g, is using a bold face.
2461  *            It returns FALSE is g contains a raw html command, even if this uses
2462  *            a bold font.
2463  */
2464
2465 int html_printer::is_bold (text_glob *g)
2466 {
2467   if (g->text_style.f == 0) {
2468     // unknown font
2469     return( FALSE );
2470   } else if (g->is_raw_command) {
2471     return( FALSE );
2472   } else {
2473     const char *fontname = g->text_style.f->get_name();
2474     
2475     if (strlen(fontname) >= 2) {
2476       return( fontname[1] == 'B' );
2477     } else {
2478       return( FALSE );
2479     }
2480   }
2481 }
2482
2483 void html_printer::terminate_current_font (void)
2484 {
2485   text_glob g;
2486
2487   // we create a dummy glob just so we can tell html_change_font not to start up
2488   // a new font
2489   g.is_raw_command = TRUE;
2490   html_change_font(&g, 0, 0);   
2491 }
2492
2493 void html_printer::write_header (void)
2494 {
2495   if (strlen(header.header_buffer) > 0) {
2496     if (header.header_level > 7) {
2497       header.header_level = 7;
2498     }
2499
2500     if (cutoff_heading+2 > header.header_level) {
2501       // firstly we must terminate any font and type faces
2502       terminate_current_font();
2503       end_paragraph();
2504
2505       // secondly we generate a tag
2506       html.put_string("<a name=\"");
2507       html.put_string(header.header_buffer);
2508       html.put_string("\"></a>");
2509       // now we save the header so we can issue a list of link
2510       style st;
2511
2512       header.no_of_headings++;
2513
2514       text_glob *g=new text_glob(&st,
2515                                  header.headings.add_string(header.header_buffer, strlen(header.header_buffer)),
2516                                  strlen(header.header_buffer),
2517                                  header.no_of_headings, header.header_level,
2518                                  header.no_of_headings, header.header_level,
2519                                  FALSE, FALSE);
2520       header.headers.add(g);   // and add this header to the header list
2521     }
2522
2523     end_paragraph();
2524     // and now we issue the real header
2525     html.put_string("<h");
2526     html.put_number(header.header_level);
2527     html.put_string(">");
2528     html.put_string(header.header_buffer);
2529     html.put_string("</h");
2530     html.put_number(header.header_level);
2531     html.put_string(">");
2532     need_one_newline = FALSE;
2533     begin_paragraph(left_alignment);
2534     header.written_header = TRUE;
2535   }
2536 }
2537
2538 /*
2539  *  write_headings - emits a list of links for the headings in this document
2540  */
2541
2542 void header_desc::write_headings (FILE *f)
2543 {
2544   text_glob *g;
2545
2546   if (! headers.is_empty()) {
2547     headers.start_from_head();
2548     do {
2549       g = headers.get_data();
2550       fprintf(f, "<a href=\"#%s\">%s</a><br>\n", g->text_string, g->text_string);
2551       headers.move_right();
2552     } while (! headers.is_equal_to_head());
2553   }
2554 }
2555
2556 void html_printer::determine_header_level (void)
2557 {
2558   int i;
2559   int l=strlen(header.header_buffer);
2560   int stops=0;
2561
2562   for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) {
2563     if (header.header_buffer[i] == '.') {
2564       stops++;
2565     }
2566   }
2567   if (stops > 0) {
2568     header.header_level = stops;
2569   }
2570 }
2571
2572
2573 void html_printer::build_header (text_glob *g)
2574 {
2575   int r            = font::res;
2576   int height       = g->text_style.point_size*r/72;
2577   text_glob *l;
2578   int current_vpos;
2579
2580   strcpy(header.header_buffer, "");
2581   do {
2582     l = g;
2583     current_vpos = g->minv;
2584     strcat(header.header_buffer, (char *)g->text_string);
2585     page_contents->words.move_right();
2586     g = page_contents->words.get_data();
2587     if (g->minv == current_vpos) {
2588       strcat(header.header_buffer, " ");
2589     }
2590   } while ((! page_contents->words.is_equal_to_head()) &&
2591            ((g->minv == current_vpos) || (l->maxh == right_margin_indent)));
2592
2593   determine_header_level();
2594   // finally set the output to neutral for after the header
2595
2596   g = page_contents->words.get_data();
2597   output_vpos = g->minv;                // set output_vpos to the next line since
2598   output_hpos = left_margin_indent;     // html header forces a newline anyway
2599   page_contents->words.move_left();     // so that next time we use old g
2600
2601   need_one_newline = FALSE;
2602 }
2603
2604
2605 /*
2606  *  is_whole_line_bold - returns TRUE if the whole line is bold.
2607  */
2608
2609 int html_printer::is_whole_line_bold (text_glob *g)
2610 {
2611   text_glob *n=g;
2612   int        current_vpos=g->minv;
2613
2614   do {
2615     if (is_bold(n)) {
2616       page_contents->words.move_right();
2617       n = page_contents->words.get_data();
2618     } else {
2619       while (page_contents->words.get_data() != g) {
2620         page_contents->words.move_left();
2621       }
2622       return( FALSE );
2623     }
2624   } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(n, current_vpos)));
2625   // was (n->minv == current_vpos)
2626   while (page_contents->words.get_data() != g) {
2627     page_contents->words.move_left();
2628   }
2629   return( TRUE );
2630 }
2631
2632
2633 /*
2634  *  is_a_header - returns TRUE if the whole sequence of contineous lines are bold.
2635  *                It checks to see whether a line is likely to be contineous and
2636  *                then checks that all words are bold.
2637  */
2638
2639 int html_printer::is_a_header (text_glob *g)
2640 {
2641   text_glob *l;
2642   text_glob *n=g;
2643   int        current_vpos;
2644
2645   do {
2646     l = n;
2647     current_vpos = n->minv;
2648     if (is_bold(n)) {
2649       page_contents->words.move_right();
2650       n = page_contents->words.get_data();
2651     } else {
2652       while (page_contents->words.get_data() != g) {
2653         page_contents->words.move_left();
2654       }
2655       return( FALSE );
2656     }
2657   } while ((! page_contents->words.is_equal_to_head()) &&
2658            ((n->minv == current_vpos) || (l->maxh == right_margin_indent)));
2659   while (page_contents->words.get_data() != g) {
2660     page_contents->words.move_left();
2661   }
2662   return( TRUE );
2663 }
2664
2665
2666 int html_printer::processed_header (text_glob *g)
2667 {
2668   if ((guess_on) && (g->minh == left_margin_indent) && (! using_table_for_indent()) &&
2669       (is_a_header(g))) {
2670     build_header(g);
2671     write_header();
2672     return( TRUE );
2673   } else {
2674     return( FALSE );
2675   }
2676 }
2677
2678 int is_punctuation (char *s, int length)
2679 {
2680   return( (length == 1) &&
2681           ((s[0] == '(') || (s[0] == ')') || (s[0] == '!') || (s[0] == '.') || (s[0] == '[') ||
2682            (s[0] == ']') || (s[0] == '?') || (s[0] == ',') || (s[0] == ';') || (s[0] == ':') ||
2683            (s[0] == '@') || (s[0] == '#') || (s[0] == '$') || (s[0] == '%') || (s[0] == '^') ||
2684            (s[0] == '&') || (s[0] == '*') || (s[0] == '+') || (s[0] == '-') || (s[0] == '=') ||
2685            (s[0] == '{') || (s[0] == '}') || (s[0] == '|') || (s[0] == '\"') || (s[0] == '\''))
2686           );
2687 }
2688
2689 /*
2690  *  move_horizontal - moves right into the position, g->minh.
2691  */
2692
2693 void html_printer::move_horizontal (text_glob *g, int left_margin)
2694 {
2695   if (g->text_style.f != 0) {
2696     int w = g->text_style.f->get_space_width(g->text_style.point_size);
2697
2698     if (w == 0) {
2699       fatal("space width is zero");
2700     }
2701     if ((output_hpos == left_margin) && (g->minh > output_hpos)) {
2702       make_html_indent(g->minh-output_hpos);
2703     } else {
2704       emit_space(g, FALSE);
2705     }
2706     output_hpos = g->maxh;
2707     output_vpos = g->minv;
2708
2709     change_font(g, TRUE);
2710   }
2711 }
2712
2713 int html_printer::looks_like_subscript (text_glob *g)
2714 {
2715   return(((output_vpos < g->minv) && (output_style.point_size != 0) &&
2716           (output_style.point_size > g->text_style.point_size)));
2717 }
2718
2719
2720 int html_printer::looks_like_superscript (text_glob *g)
2721 {
2722   return(((output_vpos > g->minv) && (output_style.point_size != 0) &&
2723           (output_style.point_size > g->text_style.point_size)));
2724 }
2725
2726 /*
2727  *  pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob.
2728  *                            Note that it believes a single word spanning the left..right as being
2729  *                            on a different line.
2730  */
2731
2732 int html_printer::pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin)
2733 {
2734   return( auto_on && (right_margin == output_hpos) && (left_margin == g->minh) &&
2735           (right_margin != g->maxh) && ((! is_whole_line_bold(g)) || (g->text_style.f == output_style.f)) );
2736 }
2737
2738 int html_printer::is_on_same_line (text_glob *g, int vpos)
2739 {
2740   return(
2741          (vpos >= 0) &&
2742          is_intersection(vpos, vpos+g->text_style.point_size*font::res/72-1, g->minv, g->maxv)
2743         );
2744 }
2745
2746
2747 /*
2748  *  make_html_indent - creates a relative indentation.
2749  */
2750
2751 void html_printer::make_html_indent (int indent)
2752 {
2753   int  r=font::res;
2754
2755   html.put_string("<span style=\" text-indent: ");
2756   html.put_float(((double)(indent)/((double)r)));
2757   html.put_string("in;\"></span>");
2758 }
2759
2760 /*
2761  *  using_table_for_indent - returns TRUE if we currently using a table for indentation
2762  *                           purposes.
2763  */
2764
2765 int html_printer::using_table_for_indent (void)
2766 {
2767   return( indentation.no_of_columns != 0 );
2768 }
2769
2770 /*
2771  *  calculate_min_gap - returns the minimum gap by which we deduce columns.
2772  *                      This is a rough heuristic.
2773  */
2774
2775 int html_printer::calculate_min_gap (text_glob *g)
2776 {
2777   return( g->text_style.f->get_space_width(g->text_style.point_size)*GAP_SPACES );
2778 }
2779
2780 /*
2781  *  collect_columns - place html text in a column and return the vertical limit reached.
2782  */
2783
2784 int html_printer::collect_columns (struct text_defn *line, struct text_defn *last, int max_words)
2785 {
2786   text_glob *start = page_contents->words.get_data();
2787   text_glob *t     = start;
2788   int  upper_limit = 0;
2789
2790   line[0].left  = 0;
2791   line[0].right = 0;
2792   if (start != 0) {
2793     int graphic_limit = end_region_vpos;
2794
2795     if (is_whole_line_bold(t) && (t->minh == left_margin_indent)) {
2796       // found header therefore terminate indentation table
2797       upper_limit = -t->minv;  // so we know a header has stopped the column
2798     } else {
2799       int i      =0;
2800       int j      =0;
2801       int prevh  =0;
2802       int mingap =calculate_min_gap(start);
2803
2804       while ((t != 0) && (is_on_same_line(t, start->minv) && (i<max_words)) &&
2805              ((graphic_limit == -1) || (graphic_limit > t->minv))) {
2806         while ((last != 0) && (j<max_words) && (last[j].left != 0) && (last[j].left < t->minh)) {
2807           j++;
2808         }
2809         // t->minh might equal t->maxh when we are passing a special device character via \X
2810         // we currently ignore these when considering tables
2811         if (((t->minh - prevh >= mingap) || ((last != 0) && (last[j].left != 0) && (t->minh == last[j].left))) &&
2812             (t->minh != t->maxh)) {
2813           line[i].left    = t->minh;
2814           line[i].right   = t->maxh;
2815           i++;
2816         } else if (i>0) {
2817           line[i-1].right = t->maxh;
2818         }
2819
2820         // and record the vertical upper limit
2821         upper_limit = max(t->minv, upper_limit);
2822
2823         prevh = t->maxh;
2824         page_contents->words.move_right();
2825         t = page_contents->words.get_data();
2826         if (page_contents->words.is_equal_to_head()) {
2827           t = 0;
2828         }
2829       }
2830
2831       if (i<max_words) {
2832         line[i].left  = 0;
2833         line[i].right = 0;
2834       }
2835     }
2836   }
2837   return( upper_limit );
2838 }
2839
2840 /*
2841  *  conflict_with_words - returns TRUE if a word sequence crosses a column.
2842  */
2843
2844 int html_printer::conflict_with_words (struct text_defn *column_guess, struct text_defn *words)
2845 {
2846   int i=0;
2847   int j;
2848
2849   while ((column_guess[i].left != 0) && (i<MAX_WORDS_PER_LINE)) {
2850     j=0;
2851     while ((words[j].left != 0) && (j<MAX_WORDS_PER_LINE)) {
2852       if ((words[j].left <= column_guess[i].right) && (i+1<MAX_WORDS_PER_LINE) &&
2853           (column_guess[i+1].left != 0) && (words[j].right >= column_guess[i+1].left)) {
2854         if (debug_table_on) {
2855           fprintf(stderr, "is a conflict with words\n");
2856           fflush(stderr);
2857         }
2858         return( TRUE );
2859       }
2860       j++;
2861     }
2862     i++;
2863   }
2864   if (debug_table_on) {
2865     fprintf(stderr, "is NOT a conflict with words\n");
2866     fflush(stderr);
2867   }
2868   return( FALSE );
2869 }
2870
2871 /*
2872  *  combine_line - combines dest and src.
2873  */
2874
2875 void html_printer::combine_line (struct text_defn *dest, struct text_defn *src)
2876 {
2877   int i;
2878
2879   for (i=0; (i<MAX_WORDS_PER_LINE) && (src[i].left != 0); i++) {
2880     include_into_list(dest, &src[i]);
2881   }
2882   remove_redundant_columns(dest);
2883 }
2884
2885 /*
2886  *  remove_entry_in_line - removes an entry, j, in, line.
2887  */
2888
2889 void html_printer::remove_entry_in_line (struct text_defn *line, int j)
2890 {
2891   while (line[j].left != 0) {
2892     line[j].left  = line[j+1].left;
2893     line[j].right = line[j+1].right;
2894     j++;
2895   }
2896 }
2897
2898 /*
2899  *  remove_redundant_columns - searches through the array columns and removes any redundant entries.
2900  */
2901
2902 void html_printer::remove_redundant_columns (struct text_defn *line)
2903 {
2904   int i=0;
2905   int j=0;
2906
2907   while (line[i].left != 0) {
2908     if ((i<MAX_WORDS_PER_LINE) && (line[i+1].left != 0)) {
2909       j = 0;
2910       while ((j<MAX_WORDS_PER_LINE) && (line[j].left != 0)) {
2911         if ((j != i) && (is_intersection(line[i].left, line[i].right, line[j].left, line[j].right))) {
2912           line[i].left  = min(line[i].left , line[j].left);
2913           line[i].right = max(line[i].right, line[j].right);
2914           remove_entry_in_line(line, j);
2915         } else {
2916           j++;
2917         }
2918       }
2919     }
2920     i++;
2921   }
2922 }
2923
2924 /*
2925  *  include_into_list - performs an order set inclusion
2926  */
2927
2928 void html_printer::include_into_list (struct text_defn *line, struct text_defn *item)
2929 {
2930   int i=0;
2931
2932   while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0) && (line[i].left<item->left)) {
2933     i++;
2934   }
2935
2936   if (line[i].left == 0) {
2937     // add to the end
2938     if (i<MAX_WORDS_PER_LINE) {
2939       if ((i>0) && (line[i-1].left > item->left)) {
2940         fatal("insertion error");
2941       }
2942       line[i].left  = item->left;
2943       line[i].right = item->right;
2944       i++;
2945       line[i].left  = 0;
2946       line[i].right = 0;
2947     }
2948   } else {
2949     if (line[i].left == item->left) {
2950       line[i].right = max(item->right, line[i].right);
2951     } else {
2952       // insert
2953       int left  = item->left;
2954       int right = item->right;
2955       int l     = line[i].left;
2956       int r     = line[i].right;
2957
2958       while ((i+1<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
2959         line[i].left  = left;
2960         line[i].right = right;
2961         i++;
2962         left          = l;
2963         right         = r;
2964         l             = line[i].left;
2965         r             = line[i].right;
2966       }
2967       if (i+1<MAX_WORDS_PER_LINE) {
2968         line[i].left    = left;
2969         line[i].right   = right;
2970         line[i+1].left  = 0;
2971         line[i+1].right = 0;
2972       }
2973     }
2974   }
2975 }
2976
2977 /*
2978  *  is_in_column - return TRUE if value is present in line.
2979  */
2980
2981 int html_printer::is_in_column (struct text_defn *line, struct text_defn *item, int max_words)
2982 {
2983   int i=0;
2984
2985   while ((i<max_words) && (line[i].left != 0)) {
2986     if (line[i].left == item->left) {
2987       return( TRUE );
2988     } else {
2989       i++;
2990     }
2991   }
2992   return( FALSE );
2993 }
2994
2995 /*
2996  *  calculate_right - calculate the right most margin for each column in line.
2997  */
2998
2999 void html_printer::calculate_right (struct text_defn *line, int max_words)
3000 {
3001   int i=0;
3002
3003   while ((i<max_words) && (line[i].left != 0)) {
3004     if (i>0) {
3005       line[i-1].right = line[i].left;
3006     }
3007     i++;
3008   }
3009 }
3010
3011 /*
3012  *  add_right_full_width - adds an extra column to the right to bring the table up to
3013  *                         full width.
3014  */
3015
3016 void html_printer::add_right_full_width (struct text_defn *line, int mingap)
3017 {
3018   int i=0;
3019
3020   while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3021     i++;
3022   }
3023
3024   if ((i>0) && (line[i-1].right != right_margin_indent) && (i+1<MAX_WORDS_PER_LINE)) {
3025     line[i].left    = min(line[i-1].right+mingap, right_margin_indent);
3026     line[i].right   = right_margin_indent;
3027     i++;
3028     if (i<MAX_WORDS_PER_LINE) {
3029       line[i].left  = 0;
3030       line[i].right = 0;
3031     }
3032   }
3033 }
3034
3035 /*
3036  *  determine_right_most_column - works out the right most limit of the right most column.
3037  *                                Required as we might be performing a .2C and only
3038  *                                have enough text to fill the left column.
3039  */
3040
3041 void html_printer::determine_right_most_column (struct text_defn *line, int max_words)
3042 {
3043   int i=0;
3044
3045   while ((i<max_words) && (line[i].left != 0)) {
3046     i++;
3047   }
3048   if (i>0) {
3049     // remember right_margin_indent is the right most position for this page
3050     line[i-1].right = column_calculate_right_margin(line[i-1].left, right_margin_indent);
3051   }
3052 }
3053
3054 /*
3055  *  is_column_match - returns TRUE if a word is aligned in the same horizontal alignment
3056  *                    between two lines, line1 and line2. If so then this horizontal
3057  *                    position is saved in match.
3058  */
3059
3060 int html_printer::is_column_match (struct text_defn *match,
3061                                    struct text_defn *line1, struct text_defn *line2, int max_words)
3062 {
3063   int i=0;
3064   int j=0;
3065   int found=FALSE;
3066   int first=(match[0].left==0);
3067
3068   if (first) {
3069     struct text_defn   t;
3070
3071     t.left  = left_margin_indent;
3072     t.right = 0;
3073
3074     include_into_list(match, &t);
3075   }
3076   while ((line1[i].left != 0) && (line2[i].left != 0)) {
3077     if (line1[i].left == line2[j].left) {
3078       // same horizontal alignment found
3079       include_into_list(match, &line1[i]);
3080       i++;
3081       j++;
3082       found = TRUE;
3083     } else if (line1[i].left < line2[j].left) {
3084       i++;
3085     } else {
3086       j++;
3087     }
3088   }
3089   calculate_right(match, max_words);
3090   return( found );
3091 }
3092
3093
3094 /*
3095  *  remove_white_using_words - remove white space in, last_guess, by examining, next_line
3096  *                             placing results into next_guess.
3097  *                             It returns TRUE if the same columns exist in next_guess and last_guess
3098  *                             we do allow columns to shrink but if a column disappears then we return FALSE.
3099  */
3100
3101 int html_printer::remove_white_using_words (struct text_defn *next_guess,
3102                                             struct text_defn *last_guess, struct text_defn *next_line)
3103 {
3104   int i=0;
3105   int j=0;
3106   int k=0;
3107   int removed=FALSE;
3108
3109   while ((last_guess[j].left != 0) && (next_line[k].left != 0)) {
3110     if (last_guess[j].left == next_line[k].left) {
3111       // same horizontal alignment found
3112       next_guess[i].left  = last_guess[j].left;
3113       next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3114       i++;
3115       j++;
3116       k++;
3117       if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].left != 0)) {
3118         removed = TRUE;
3119       }
3120     } else if (last_guess[j].right < next_line[k].left) {
3121       next_guess[i].left  = last_guess[j].left;
3122       next_guess[i].right = last_guess[j].right;
3123       i++;
3124       j++;
3125     } else if (last_guess[j].left > next_line[k].right) {
3126       // insert a word sequence from next_line[k]
3127       next_guess[i].left  = next_line[k].left;
3128       next_guess[i].right = next_line[k].right;
3129       i++;
3130       k++;
3131     } else if (is_intersection(last_guess[j].left, last_guess[j].right, next_line[k].left, next_line[k].right)) {
3132       // potential for a column disappearing
3133       next_guess[i].left  = min(last_guess[j].left , next_line[k].left);
3134       next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3135       i++;
3136       j++;
3137       k++;
3138       if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].left != 0)) {
3139         removed = TRUE;
3140       }
3141     }
3142   }
3143   if (i<MAX_WORDS_PER_LINE) {
3144     next_guess[i].left  = 0;
3145     next_guess[i].right = 0;
3146   }
3147   if (debug_table_on) {
3148     if (removed) {
3149       fprintf(stderr, "have removed column\n");
3150     } else {
3151       fprintf(stderr, "have NOT removed column\n");
3152     }
3153     fflush(stderr);
3154   }
3155   remove_redundant_columns(next_guess);
3156   return( removed );
3157 }
3158
3159 /*
3160  *  count_columns - returns the number of elements inside, line.
3161  */
3162
3163 int html_printer::count_columns (struct text_defn *line)
3164 {
3165   int i=0;
3166
3167   while (line[i].left != 0) {
3168     i++;
3169   }
3170   return( i );
3171 }
3172
3173 /*
3174  *  rewind_text_to - moves backwards until page_contents is looking at, g.
3175  */
3176
3177 void html_printer::rewind_text_to (text_glob *g)
3178 {
3179   while (page_contents->words.get_data() != g) {
3180     if (page_contents->words.is_equal_to_head()) {
3181       page_contents->words.start_from_tail();
3182     } else {
3183       page_contents->words.move_left();
3184     }
3185   }
3186 }
3187
3188 /*
3189  *  display_columns - a long overdue debugging function, as this column code is causing me grief :-(
3190  */
3191
3192 void html_printer::display_columns (const char *word, const char *name, text_defn *line)
3193 {
3194   int i=0;
3195
3196   fprintf(stderr, "[%s:%s]", name, word);
3197   while (line[i].left != 0) {
3198     fprintf(stderr, " <left=%d right=%d> ", line[i].left, line[i].right);
3199     i++;
3200   }
3201   fprintf(stderr, "\n");
3202   fflush(stderr);
3203 }
3204
3205 /*
3206  *  copy_line - dest = src
3207  */
3208
3209 int html_printer::copy_line (struct text_defn *dest, struct text_defn *src)
3210
3211   int k;
3212
3213   for (k=0; ((src[k].left != 0) && (k<MAX_WORDS_PER_LINE)); k++) {
3214     dest[k].left  = src[k].left;
3215     dest[k].right = src[k].right;
3216   }
3217   if (k<MAX_WORDS_PER_LINE) {
3218     dest[k].left  = 0;
3219     dest[k].right = 0;
3220   }
3221 }
3222
3223 /*
3224  *  add_column_gaps - adds empty columns between columns which don't exactly align
3225  */
3226
3227 void html_printer::add_column_gaps (struct text_defn *line)
3228 {
3229   int i=0;
3230   struct text_defn t;
3231
3232   // firstly lets see whether we need an initial column on the left hand side
3233   if ((line[0].left != left_margin_indent) && (line[0].left != 0) &&
3234       (left_margin_indent < line[0].left) && (is_worth_column(left_margin_indent, line[0].left))) {
3235     t.left  = left_margin_indent;
3236     t.right = line[0].left;
3237     include_into_list(line, &t);
3238   }
3239
3240   while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3241     if ((i+1<MAX_WORDS_PER_LINE) && (line[i+1].left != 0) && (line[i].right != line[i+1].left) &&
3242         (is_worth_column(line[i].right, line[i+1].left))) {
3243       t.left  = line[i].right;
3244       t.right = line[i+1].left;
3245       include_into_list(line, &t);
3246       i=0;
3247     } else {
3248       i++;
3249     }
3250   }
3251   // lastly lets see whether we need a final column on the right hand side
3252   if ((i>0) && (line[i-1].right != right_margin_indent) &&
3253       (is_worth_column(line[i-1].right, right_margin_indent))) {
3254     t.left  = line[i-1].right;
3255     t.right = right_margin_indent;
3256     include_into_list(line, &t);
3257   }
3258 }
3259
3260 /*
3261  *  is_continueous_column - returns TRUE if a line has a word on one
3262  *                          of the last_col right most boundaries.
3263  */
3264
3265 int html_printer::is_continueous_column (text_defn *last_col, text_defn *next_line)
3266 {
3267   int w = count_columns(next_line);
3268   int c = count_columns(last_col);
3269   int i, j;
3270
3271   for (i=0; i<c; i++) {
3272     for (j=0; j<w; j++) {
3273       if (last_col[i].right == next_line[j].right) {
3274         return( TRUE );
3275       }
3276     }
3277   }
3278   return( FALSE );
3279 }
3280
3281 /*
3282  *  is_exact_left - returns TRUE if a line has a word on one
3283  *                  of the last_col left most boundaries.
3284  */
3285
3286 int html_printer::is_exact_left (text_defn *last_col, text_defn *next_line)
3287 {
3288   int w = count_columns(next_line);
3289   int c = count_columns(last_col);
3290   int i, j;
3291
3292   for (i=0; i<c; i++) {
3293     for (j=0; j<w; j++) {
3294       if ((last_col[i].left == next_line[j].left) ||
3295           (last_col[i].left != left_margin_indent)) {
3296         return( TRUE );
3297       }
3298     }
3299   }
3300   return( FALSE );
3301 }
3302
3303 /*
3304  *  continue_searching_column - decides whether we should carry on searching text for a column.
3305  */
3306
3307 int html_printer::continue_searching_column (text_defn *next_col,
3308                                              text_defn *last_col,
3309                                              text_defn *all_words)
3310 {
3311   int count = count_columns(next_col);
3312   int words = count_columns(all_words);
3313
3314   if ((words == 0) || ((words == 1) &&
3315                        (all_words[0].left == left_margin_indent) &&
3316                        (all_words[0].right == right_margin_indent))) {
3317     // no point as we have now seen a full line of contineous text
3318     return( FALSE );
3319   }
3320   return( (count == count_columns(last_col)) &&
3321           (last_col[0].left != left_margin_indent) || (last_col[0].right != right_margin_indent) );
3322 }
3323
3324 /*
3325  *  is_worth_column - returns TRUE if the size of this column is worth defining.
3326  */
3327
3328 int html_printer::is_worth_column (int left, int right)
3329 {
3330 #if 0
3331   return( abs(right-left) >= MIN_COLUMN );
3332 #endif
3333   return( TRUE );
3334 }
3335
3336 /*
3337  *  large_enough_gap - returns TRUE if a large enough gap for one line was seen.
3338  *                     We need to make sure that a single line definitely warrents
3339  *                     a table.
3340  *                     It also removes other smaller gaps.
3341  */
3342
3343 int html_printer::large_enough_gap (text_defn *last_col)
3344 {
3345   int i=0;
3346   int found=FALSE;
3347   int r=font::res;
3348   int gap=r/GAP_WIDTH_ONE_LINE;
3349
3350   if (abs(last_col[i].left - left_margin_indent) >= gap) {
3351     found = TRUE;
3352   }
3353   while ((last_col[i].left != 0) && (last_col[i+1].left != 0)) {
3354     if (abs(last_col[i+1].left-last_col[i].right) >= gap) {
3355       found = TRUE;
3356       i++;
3357     } else {
3358       // not good enough for a single line, remove it
3359       if (i>0) {
3360         last_col[i-1].right = last_col[i].right;
3361       }
3362       remove_entry_in_line(last_col, i);
3363     }
3364   }
3365   return( found );
3366 }
3367
3368 /*
3369  *  is_subset_of_columns - returns TRUE if line, a, is a subset of line, b.
3370  */
3371
3372 int html_printer::is_subset_of_columns (text_defn *a, text_defn *b)
3373 {
3374   int i;
3375   int j;
3376
3377   i=0;
3378   while ((i<MAX_WORDS_PER_LINE) && (a[i].left != 0)) {
3379     j=0;
3380     while ((j<MAX_WORDS_PER_LINE) && (b[j].left != 0) &&
3381            ((b[j].left != a[i].left) || (b[j].right != a[i].right))) {
3382       j++;
3383     }
3384     if ((j==MAX_WORDS_PER_LINE) || (b[j].left == 0)) {
3385       // found a different column - not a subset
3386       return( FALSE );
3387     }
3388     i++;
3389   }
3390   return( TRUE );
3391 }
3392
3393 /*
3394  *  count_hits - counts the number of hits per column. A hit is when the
3395  *               left hand position of a glob hits the left hand column.
3396  */
3397
3398 void html_printer::count_hits (text_defn *col)
3399 {
3400   int        i;
3401   text_glob *start = page_contents->words.get_data();
3402   text_glob *g     = start;
3403   int        r     = font::res;
3404   int        gap   = r/GAP_WIDTH_ONE_LINE;
3405   int        n     = count_columns(col);
3406   int        left;
3407
3408   // firstly reset the used field
3409   for (i=0; i<n; i++) {
3410     col[i].is_used = 0;
3411   }
3412   // now calculate the left hand hits
3413   while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3414     i=0;
3415     while ((col[i].left < g->minh) && (col[i].left != 0)) {
3416       i++;
3417     }
3418     if ((col[i].left == g->minh) && (col[i].left != 0)) {
3419       col[i].is_used++;
3420     }
3421     page_contents->words.move_right();
3422     if (page_contents->words.is_equal_to_head()) {
3423       g = 0;
3424       page_contents->words.start_from_tail();
3425     } else {
3426       g=page_contents->words.get_data();
3427     }
3428   }
3429   // now remove any column which is less than the
3430   // minimal gap for one hit.
3431   // column 0 is excempt
3432
3433   left = col[0].left;
3434   i=1;
3435   while (i<count_columns(col)) {
3436     if (col[i].is_used == 1) {
3437       if (col[i].left - left < gap) {
3438         col[i-1].right = col[i].right;
3439         remove_entry_in_line(col, i);
3440         left = col[i].left;
3441       } else {
3442         left = col[i].left;
3443         i++;
3444       }
3445     } else {
3446       left = col[i].left;
3447       i++;
3448     }
3449   }
3450 }
3451
3452 /*
3453  *  found_use_for_table - checks whether the some words on one line directly match
3454  *                        the horizontal alignment of the line below.
3455  */
3456
3457 int html_printer::found_use_for_table (text_glob *start)
3458 {
3459   text_glob         *t;
3460   struct text_defn   all_words [MAX_WORDS_PER_LINE];
3461   struct text_defn   last_raw  [MAX_WORDS_PER_LINE];
3462   struct text_defn   next_line [MAX_WORDS_PER_LINE];
3463   struct text_defn   prev_guess[MAX_WORDS_PER_LINE];
3464   struct text_defn   last_guess[MAX_WORDS_PER_LINE];
3465   struct text_defn   next_guess[MAX_WORDS_PER_LINE];
3466   int                i     =0;
3467   int                lines =0;
3468   int                mingap=calculate_min_gap(start);
3469   int                limit;
3470
3471 #if 0
3472   if (strcmp(start->text_string, "man") == 0) {
3473     stop();
3474   }
3475 #endif
3476
3477   // get first set of potential columns into line1
3478   limit = collect_columns(last_guess, 0, MAX_WORDS_PER_LINE);
3479   copy_line(last_raw, last_guess);
3480   // add_right_full_width(last_guess, mingap);   // adds extra right column to bring table to full width
3481
3482   copy_line(all_words, last_guess);
3483   indentation.vertical_limit = limit;
3484
3485   if (page_contents->words.is_equal_to_head() || (limit == 0)) {
3486     next_line[0].left  = 0;
3487     next_line[0].right = 0;
3488   } else {
3489     // and get the next line for finding columns
3490     limit = collect_columns(next_line, last_guess, MAX_WORDS_PER_LINE);
3491     lines++;
3492   }
3493
3494   // now check to see whether the first line looks like a single centered line
3495
3496   if (single_centered_line(last_raw, next_line, start)) {
3497     rewind_text_to(start);
3498     write_centered_line(start);
3499     indentation.no_of_columns = 0;   // center instead
3500     return( TRUE );
3501   } else if (! table_on) {
3502     rewind_text_to(start);
3503     return( FALSE );
3504   }
3505
3506   combine_line(all_words, next_line);
3507   if (debug_table_on) {
3508     display_columns(start->text_string, "[b] all_words", all_words);
3509   }
3510
3511   if ((! remove_white_using_words(next_guess, last_guess, next_line))) {
3512   }
3513
3514   if ((! conflict_with_words(next_guess, all_words)) &&
3515       (continue_searching_column(next_guess, next_guess, all_words)) &&
3516       (! page_contents->words.is_equal_to_head()) &&
3517       ((end_region_vpos < 0) || (limit < end_region_vpos)) &&
3518       (limit > 0)) {
3519
3520     combine_line(last_guess, next_line);
3521     // subtract any columns which are bridged by a sequence of words
3522     do {
3523       copy_line(prev_guess, next_guess);
3524       combine_line(last_guess, next_guess);
3525
3526       if (debug_table_on) {
3527         t = page_contents->words.get_data();
3528         display_columns(t->text_string, "[l] last_guess", last_guess);
3529       }
3530       indentation.vertical_limit = limit;
3531
3532       copy_line(last_raw, next_line);
3533       if (page_contents->words.is_equal_to_head()) {
3534         next_line[0].left  = 0;
3535         next_line[0].right = 0;
3536       } else {
3537         limit = collect_columns(next_line, last_guess, MAX_WORDS_PER_LINE);
3538         lines++;
3539       }
3540
3541       combine_line(all_words, next_line);
3542       if (debug_table_on) {
3543         display_columns(t->text_string, "[l] all_words", all_words);
3544         display_columns(t->text_string, "[l] last_raw ", last_raw);
3545       }
3546
3547       if (debug_table_on) {
3548         display_columns(t->text_string, "[l] next_line", next_line);
3549       }
3550       t = page_contents->words.get_data();
3551 #if 0
3552       if (strcmp(t->text_string, "market,") == 0) {
3553         stop();
3554       }
3555 #endif
3556
3557     } while ((! remove_white_using_words(next_guess, last_guess, next_line)) &&
3558              (! conflict_with_words(next_guess, all_words)) &&
3559              (continue_searching_column(next_guess, last_guess, all_words)) &&
3560              ((is_continueous_column(prev_guess, last_raw)) || (is_exact_left(last_guess, next_line))) &&
3561              (! page_contents->words.is_equal_to_head()) &&
3562              ((end_region_vpos <= 0) || (t->minv < end_region_vpos)) &&
3563              (limit >= 0));
3564   }
3565   lines--;
3566
3567   if (limit < 0) {
3568     indentation.vertical_limit = limit;
3569   }
3570
3571   if (page_contents->words.is_equal_to_head()) {
3572     // end of page check whether we should include everything
3573     if ((! conflict_with_words(next_guess, all_words)) &&
3574         (continue_searching_column(next_guess, last_guess, all_words)) &&
3575         ((is_continueous_column(prev_guess, last_raw)) || (is_exact_left(last_guess, next_line)))) {
3576       // end of page reached - therefore include everything
3577       page_contents->words.start_from_tail();
3578       t = page_contents->words.get_data();
3579       indentation.vertical_limit = t->minv;
3580     }
3581   } else {
3582     t = page_contents->words.get_data();
3583     if ((end_region_vpos > 0) && (t->minv > end_region_vpos)) {
3584       indentation.vertical_limit = min(indentation.vertical_limit, end_region_vpos+1);
3585     } else if (indentation.vertical_limit < 0) {
3586       // -1 as we don't want to include section heading itself
3587       indentation.vertical_limit = -indentation.vertical_limit-1;
3588     }
3589   }
3590
3591   if (debug_table_on) {
3592     display_columns(start->text_string, "[x] last_guess", last_guess);
3593   }
3594   rewind_text_to(start);
3595
3596   i = count_columns(last_guess);
3597   if (((lines > 2) && ((i>1) || (continue_searching_column(last_guess, last_guess, all_words)))) ||
3598       ((lines == 1) && (large_enough_gap(last_guess)))) {
3599     // copy match into permenant html_table
3600
3601     if (indentation.columns != 0) {
3602       free(indentation.columns);
3603     }
3604     if (debug_table_on) {
3605       display_columns(start->text_string, "[x] last_guess", last_guess);
3606     }
3607     add_column_gaps(last_guess);
3608     if (debug_table_on) {
3609       display_columns(start->text_string, "[g] last_guess", last_guess);
3610     }
3611
3612     indentation.no_of_columns = count_columns(last_guess);
3613     indentation.columns       = (struct text_defn *)malloc(indentation.no_of_columns*sizeof(struct text_defn));
3614
3615     i=0;
3616     while (i<indentation.no_of_columns) {
3617       indentation.columns[i].left  = last_guess[i].left;
3618       indentation.columns[i].right = last_guess[i].right;
3619       i++;
3620     }
3621     return( TRUE );
3622   } else {
3623     return( FALSE );
3624   }
3625 }
3626
3627 void html_printer::define_cell (int left, int right)
3628 {
3629   float f=((float)(right-left))/((float)(right_margin_indent-left_margin_indent))*100.0;
3630
3631   html.put_string("<td valign=\"top\" align=\"left\"  width=\"");
3632   if (f > 1.0) {
3633     html.put_float(f);
3634   } else {
3635     html.put_float(1.0);
3636   }
3637   html.put_string("%\">\n");
3638 }
3639
3640 /*
3641  *  column_display_word - given a left, right pair and the indentation.vertical_limit
3642  *                        write out html text within this region.
3643  */
3644
3645 void html_printer::column_display_word (int vert, int left, int right, int next)
3646 {
3647   text_glob *g=page_contents->words.get_data();
3648
3649   if (left != next) {
3650     define_cell(left, next);
3651     begin_paragraph_no_height(left_alignment);
3652     while ((g != 0) && (g->minv <= vert)) {
3653       if ((left <= g->minh) && (g->minh<right)) {
3654         char *postword=html_position_text(g, left, right);
3655         
3656         if (header.written_header) {
3657           fatal("should never generate a header inside a table");
3658         } else {
3659           if (g->is_raw_command) {
3660             html.put_string((char *)g->text_string);
3661           } else {
3662             html.html_write_string((char *)g->text_string);
3663           }
3664           if (postword != 0) {
3665             html.put_string(postword);
3666           }
3667           issued_newline = FALSE;
3668         }
3669       }
3670       if (page_contents->words.is_equal_to_tail()) {
3671         g = 0;
3672       } else {
3673         page_contents->words.move_right();
3674         g=page_contents->words.get_data();
3675       }
3676 #if 0
3677       if (page_contents->words.is_equal_to_head()) {
3678         g = 0;
3679         page_contents->words.start_from_tail();
3680       } else {
3681
3682       }
3683 #endif
3684     }
3685     end_paragraph();
3686     html.put_string("</td>\n");
3687     if (g != 0) {
3688       page_contents->words.move_left();
3689       // and correct output_vpos
3690       g=page_contents->words.get_data();
3691       output_vpos = g->minv;
3692     }
3693   }
3694 }
3695
3696 /*
3697  *  start_table - creates a table according with parameters contained within class html_table.
3698  */
3699
3700 void html_printer::start_table (void)
3701 {
3702   int i;
3703
3704   end_paragraph();
3705   html.put_string("\n<table width=\"100%\"  rules=\"none\"  frame=\"none\"  cols=\"");
3706   html.put_number(indentation.no_of_columns);
3707   html.put_string("\">\n");
3708 }
3709
3710 /*
3711  *  end_table - finishes off a table.
3712  */
3713
3714 void html_printer::end_table (void)
3715 {
3716   html.put_string("</table>\n");
3717   indentation.no_of_columns = 0;
3718 }
3719
3720 /*
3721  *  column_calculate_right_margin - scan through the column and find the right most margin
3722  */
3723
3724 int html_printer::column_calculate_right_margin (int left, int right)
3725 {
3726   if (left == right) {
3727     return( right );
3728   } else {
3729     int rightmost    =-1;
3730     int count        = 0;
3731     text_glob *start = page_contents->words.get_data();
3732     text_glob *g     = start;
3733
3734     while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3735       if ((left <= g->minh) && (g->minh<right)) {
3736         if (debug_on) {
3737           fprintf(stderr, "right word = %s      %d\n", g->text_string, g->maxh); fflush(stderr);
3738         }
3739         if (g->maxh == rightmost) {
3740           count++;
3741         } else if (g->maxh > rightmost) {
3742           count = 1;
3743           rightmost = g->maxh;
3744         }
3745         if (g->maxh > right) {
3746           if (debug_on) {
3747             fprintf(stderr, "problem as right word = %s      %d    [%d..%d]\n",
3748                     g->text_string, right, g->minh, g->maxh); fflush(stderr);
3749             stop();
3750           }
3751         }
3752       }
3753       page_contents->words.move_right();
3754       if (page_contents->words.is_equal_to_head()) {
3755         g = 0;
3756         page_contents->words.start_from_tail();
3757       } else {
3758         g=page_contents->words.get_data();
3759       }
3760     }
3761     rewind_text_to(start);
3762     if (rightmost == -1) {
3763       return( right );  // no words in this column
3764     } else {
3765       if (count == 1) {
3766         return( rightmost+1 );
3767       } else {
3768         return( rightmost );
3769       }
3770     }
3771   }
3772 }
3773
3774 /*
3775  *  column_calculate_left_margin - scan through the column and find the left most margin
3776  */
3777
3778 int html_printer::column_calculate_left_margin (int left, int right)
3779 {
3780   if (left == right) {
3781     return( left );
3782   } else {
3783     int leftmost=right;
3784     text_glob *start = page_contents->words.get_data();
3785     text_glob *g     = start;
3786
3787     while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3788       if ((left <= g->minh) && (g->minh<right)) {
3789         leftmost = min(g->minh, leftmost);
3790       }
3791       page_contents->words.move_right();
3792       if (page_contents->words.is_equal_to_head()) {
3793         g = 0;
3794         page_contents->words.start_from_tail();
3795       } else {
3796         g=page_contents->words.get_data();
3797       }
3798     }
3799     rewind_text_to(start);
3800     if (leftmost == right) {
3801       return( left );  // no words in this column
3802     } else {
3803       return( leftmost );
3804     }
3805   }
3806 }
3807
3808 /*
3809  *  find_column_index - returns the index to the column in which glob, t, exists.
3810  */
3811
3812 int html_printer::find_column_index (text_glob *t)
3813 {
3814   int i=0;
3815
3816   while ((i<indentation.no_of_columns) &&
3817          (! ((indentation.columns[i].left<=t->minh) &&
3818              (indentation.columns[i].right>t->minh)))) {
3819     i++;
3820   }
3821   return( i );
3822 }
3823
3824 /*
3825  *  determine_row_limit - checks each row to see if there is a gap in a cell.
3826  *                        We return the vertical position after the empty cell
3827  *                        at the start of the next line.
3828  */
3829
3830 int html_printer::determine_row_limit (text_glob *start, int v)
3831 {
3832   text_glob *t;
3833   int        i;
3834   int        vpos, prev, last;
3835   int is_gap[MAX_WORDS_PER_LINE];
3836
3837   if (v >= indentation.vertical_limit) {
3838     return( v+1 );
3839   } else {
3840     // initially we start with all gaps in our table
3841     // after a gap we start a new row
3842     // here we set the gap array to the previous line
3843
3844     if (v>=0) {
3845       t = page_contents->words.get_data();
3846       if (t->minv < v) {
3847         do {
3848           page_contents->words.move_right();
3849           t = page_contents->words.get_data();
3850         } while ((! page_contents->words.is_equal_to_head()) &&
3851                  (t->minv <= v));
3852       }
3853     }
3854     if (! page_contents->words.is_equal_to_head()) {
3855       page_contents->words.move_left();
3856     }
3857     t = page_contents->words.get_data();
3858     prev = t->minv;
3859     for (i=0; i<indentation.no_of_columns; i++) {
3860       is_gap[i] = prev;
3861     }
3862
3863     if (! page_contents->words.is_equal_to_tail()) {
3864       page_contents->words.move_right();
3865     }
3866     t = page_contents->words.get_data();
3867     vpos = t->minv;
3868
3869     // now check each row for a gap
3870     do {
3871       last = vpos;
3872       vpos = t->minv;
3873       i = find_column_index(t);
3874       if (! is_on_same_line(t, last)) {
3875         prev = last;
3876       }
3877
3878       if ((is_gap[i] != vpos) && (is_gap[i] != prev) &&
3879           (indentation.columns[i].is_used)) {
3880         // no word on previous line - must be a gap - force alignment of row
3881         rewind_text_to(start);
3882         return( last );
3883       }
3884       is_gap[i] = vpos;
3885       page_contents->words.move_right();
3886       t = page_contents->words.get_data();
3887     } while ((! page_contents->words.is_equal_to_head()) &&
3888              (vpos < indentation.vertical_limit) && (vpos >= last));
3889     page_contents->words.move_left();
3890     t = page_contents->words.get_data();
3891     rewind_text_to(start);
3892     return( indentation.vertical_limit );
3893   }
3894 }
3895
3896 /*
3897  *  assign_used_columns - sets the is_used field of the column array of records.
3898  */
3899
3900 void html_printer::assign_used_columns (text_glob *start)
3901 {
3902   text_glob *t = start;
3903   int        i;
3904
3905   for (i=0; i<indentation.no_of_columns; i++) {
3906     indentation.columns[i].is_used = FALSE;
3907   }
3908
3909   rewind_text_to(start);
3910   if (! page_contents->words.is_empty()) {
3911     do {
3912       i = find_column_index(t);
3913       if (indentation.columns[i].left != 0) {
3914         if (debug_table_on) {
3915           fprintf(stderr, "[%s] in column %d at %d..%d  limit %d\n", t->text_string,
3916                   i, t->minv, t->maxv, indentation.vertical_limit); fflush(stderr);
3917         }
3918         indentation.columns[i].is_used = TRUE;
3919       }
3920       page_contents->words.move_right();
3921       t = page_contents->words.get_data();
3922     } while ((t->minv<indentation.vertical_limit) &&
3923              (! page_contents->words.is_equal_to_head()));
3924   }
3925   if (debug_table_on) {
3926     for (i=0; i<indentation.no_of_columns; i++) {
3927       fprintf(stderr, " <left=%d right=%d is_used=%d> ",
3928               indentation.columns[i].left,
3929               indentation.columns[i].right,
3930               indentation.columns[i].is_used);
3931     }
3932     fprintf(stderr, "\n");
3933     fflush(stderr);
3934   }
3935 }
3936
3937 /*
3938  *  foreach_column_include_text - foreach column in a table place the
3939  *                                appropriate html text.
3940  */
3941
3942 void html_printer::foreach_column_include_text (text_glob *start)
3943 {
3944   if (indentation.no_of_columns>0) {
3945     int i;
3946     int left, right;
3947     int limit=-1;
3948
3949     assign_used_columns(start);
3950     start_table();
3951     rewind_text_to(start);
3952
3953     do {
3954       limit = determine_row_limit(start, limit);   // find the bottom of the next row
3955       html.put_string("<tr valign=\"top\" align=\"left\">\n");
3956       i=0;
3957       start = page_contents->words.get_data();
3958       while (i<indentation.no_of_columns) {
3959         // reset the output position to the start of column
3960         rewind_text_to(start);
3961         output_vpos = start->minv;
3962         output_hpos = indentation.columns[i].left;
3963         // and display each column until limit
3964         right = column_calculate_right_margin(indentation.columns[i].left,
3965                                               indentation.columns[i].right);
3966         left  = column_calculate_left_margin(indentation.columns[i].left,
3967                                              indentation.columns[i].right);
3968
3969         if (right>indentation.columns[i].right) {
3970           if (debug_on) {
3971             fprintf(stderr, "assert calculated right column edge is greater than column\n"); fflush(stderr);
3972             stop();
3973           }
3974         }
3975
3976         if (left<indentation.columns[i].left) {
3977           if (debug_on) {
3978             fprintf(stderr, "assert calculated left column edge is less than column\n"); fflush(stderr);
3979             stop();
3980           }
3981         }
3982
3983         column_display_word(limit, left, right, indentation.columns[i].right);
3984         i++;
3985       }
3986
3987       if (page_contents->words.is_equal_to_tail()) {
3988         start = 0;
3989       } else {
3990         page_contents->words.sub_move_right();
3991         if (page_contents->words.is_empty()) {
3992           start = 0;
3993         } else {
3994           start = page_contents->words.get_data();
3995         }
3996       }
3997
3998       html.put_string("</tr>\n");
3999     } while ((limit < indentation.vertical_limit) && (start != 0) &&
4000              (! page_contents->words.is_empty()));
4001     end_table();
4002
4003     if (start == 0) {
4004       // finished page remove all words
4005       page_contents->words.start_from_head();
4006       while (! page_contents->words.is_empty()) {
4007         page_contents->words.sub_move_right();
4008       }
4009     } else if (! page_contents->words.is_empty()) {
4010       page_contents->words.move_left();
4011     }
4012   }
4013 }
4014
4015 /*
4016  *  write_centered_line - generates a line of centered text.
4017  */
4018
4019 void html_printer::write_centered_line (text_glob *g)
4020 {
4021   int current_vpos=g->minv;
4022
4023   move_vertical(g, center_alignment);
4024
4025   header.written_header = FALSE;
4026   output_vpos = g->minv;
4027   output_hpos = g->minh;
4028   do {
4029     char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
4030
4031     if (! header.written_header) {
4032       if (g->is_raw_command) {
4033         html.put_string((char *)g->text_string);
4034       } else {
4035         html.html_write_string((char *)g->text_string);
4036       }
4037       if (postword != 0) {
4038         html.put_string(postword);
4039       }
4040       need_one_newline = TRUE;
4041       issued_newline   = FALSE;
4042     }    
4043     page_contents->words.move_right();
4044     g = page_contents->words.get_data();
4045   } while ((! page_contents->words.is_equal_to_head()) && (g->minv == current_vpos));
4046   page_contents->words.move_left();  // so when we move right we land on the word following this centered line
4047   need_one_newline = TRUE;
4048 }
4049
4050 /*
4051  *  is_in_middle - returns TRUE if the text defn, t, is in the middle of the page.
4052  */
4053
4054 int html_printer::is_in_middle (int left, int right)
4055 {
4056   return( abs(abs(left-left_margin_indent) - abs(right_margin_indent-right)) <= CENTER_TOLERANCE );
4057 }
4058
4059 /*
4060  *  single_centered_line - returns TRUE if first is a centered line with a different
4061  *                         margin to second.
4062  */
4063
4064 int html_printer::single_centered_line (text_defn *first, text_defn *second, text_glob *g)
4065 {
4066   return(
4067          ((count_columns(first) == 1) && (first[0].left != left_margin_indent) &&
4068           (first[0].left != second[0].left) && is_in_middle(first->left, first->right))
4069          );
4070 }
4071
4072 /*
4073  *  check_able_to_use_center - returns TRUE if we can see a centered line.
4074  */
4075
4076 int html_printer::check_able_to_use_center (text_glob *g)
4077 {
4078   if (auto_on && table_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
4079     // we are allowed to check for centered line
4080     // first check to see whether we might be looking at a set of columns
4081     struct text_defn   last_guess[MAX_WORDS_PER_LINE];
4082     int                limit = collect_columns(last_guess, 0, MAX_WORDS_PER_LINE);
4083
4084     rewind_text_to(g);    
4085     if ((count_columns(last_guess) == 1) && (is_in_middle(last_guess[0].left, last_guess[0].right))) {
4086       write_centered_line(g);
4087       return( TRUE );
4088     }
4089   }
4090   return( FALSE );
4091 }
4092
4093 /*
4094  *  check_able_to_use_table - examines forthcoming text to see whether we can
4095  *                            better format it by using an html transparent table.
4096  */
4097
4098 int html_printer::check_able_to_use_table (text_glob *g)
4099 {
4100   if (auto_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
4101     // we are allowed to check for table
4102     
4103     if ((output_hpos != right_margin_indent) && (found_use_for_table(g))) {
4104       foreach_column_include_text(g);
4105       return( TRUE );
4106     }
4107   }
4108   return( FALSE );
4109 }
4110
4111 /*
4112  *  move_vertical - if we are using html auto formatting then decide whether to
4113  *                  break the line via a <br> or a </p><p> sequence.
4114  */
4115
4116 void html_printer::move_vertical (text_glob *g, paragraph_type p)
4117 {
4118   int  r        =font::res;
4119   int  height   = (g->text_style.point_size+2)*r/72;   // --fixme-- we always assume VS is PS+2 (could do better)
4120   int  temp_vpos;
4121
4122   if (auto_on) {
4123     if ((more_than_line_break(output_vpos, g->minv, height)) || (p != para_type)) {
4124       end_paragraph();
4125       begin_paragraph(p);
4126     } else {
4127       html_newline();
4128     }
4129   } else {
4130     if (output_vpos == -1) {
4131       temp_vpos = g->minv;
4132     } else {
4133       temp_vpos = output_vpos;
4134     }
4135
4136     force_begin_paragraph();
4137     if (need_one_newline) {
4138       html_newline();
4139       temp_vpos += height;
4140     } else {
4141       need_one_newline = TRUE;
4142     }
4143     
4144     while ((temp_vpos < g->minv) && (more_than_line_break(temp_vpos, g->minv, height))) {
4145       html_newline();
4146       temp_vpos += height;
4147     }
4148   }
4149 }
4150
4151 /*
4152  *  emit_space - emits a space within html, it checks for the font type and
4153  *               will change font depending upon, g. Courier spaces are larger
4154  *               than roman so we need consistancy when changing between them.
4155  */
4156
4157 void html_printer::emit_space (text_glob *g, int force_space)
4158 {
4159   if (! need_paragraph) {
4160     // only generate a space if we have written a word - as html will ignore it otherwise
4161     if ((output_style != g->text_style) && (g->text_style.f != 0)) {
4162       terminate_current_font();
4163     }
4164     if (force_space || (g->minh > output_hpos)) {
4165       html.put_string(" ");
4166     }
4167     change_font(g, TRUE);      
4168   }
4169 }
4170
4171 /*
4172  *  html_position_text - determine whether the text is subscript/superscript/normal
4173  *                       or a header.
4174  */
4175
4176 char *html_printer::html_position_text (text_glob *g, int left_margin, int right_margin)
4177 {
4178   char *postword=0;
4179
4180 #if 0
4181   if (strcmp(g->text_string, "increased.") == 0) {
4182     stop();
4183   }
4184 #endif
4185   begin_paragraph(left_alignment);
4186
4187   if ((! header.written_header) &&
4188       (is_on_same_line(g, output_vpos) ||
4189        pretend_is_on_same_line(g, left_margin, right_margin))) {
4190     // check whether the font was reset after generating an image
4191
4192     header.written_header = FALSE;
4193     force_begin_paragraph();
4194
4195     // check whether we need to insert white space between words on 'same' line
4196     if (pretend_is_on_same_line(g, left_margin, right_margin)) {
4197       emit_space(g, TRUE);
4198     }
4199
4200     if (output_style.f == 0) {
4201       change_font(g, TRUE);
4202     }
4203
4204     if (looks_like_subscript(g)) {
4205
4206       g->text_style.point_size = output_style.point_size;
4207       g->minv                  = output_vpos;   // this ensures that output_vpos doesn't alter
4208                                                 // which allows multiple subscripted words
4209       move_horizontal(g, left_margin);
4210       html.put_string("<sub>");
4211       postword = "</sub>";
4212     } else if (looks_like_superscript(g)) {
4213
4214       g->text_style.point_size = output_style.point_size;
4215       g->minv                  = output_vpos;
4216
4217       move_horizontal(g, left_margin);
4218       html.put_string("<sup>");
4219       postword = "</sup>";
4220     } else {
4221       move_horizontal(g, left_margin);
4222     }
4223   } else {
4224     // we have found a new line
4225     if (! header.written_header) {
4226       move_vertical(g, left_alignment);
4227     }
4228     header.written_header = FALSE;
4229
4230     if (processed_header(g)) {
4231       // we must not alter output_vpos as we have peeped at the next word
4232       // and set vpos to this - to ensure we do not generate a <br> after
4233       // a heading. (The html heading automatically generates a line break)
4234       output_hpos = left_margin;
4235       return( postword );
4236     } else {
4237       force_begin_paragraph();
4238       if (g->minh-left_margin != 0) {
4239         make_html_indent(g->minh-left_margin);
4240       }
4241       change_font(g, TRUE);
4242     }
4243   }
4244   output_vpos = g->minv;
4245   output_hpos = g->maxh;
4246   return( postword );
4247 }
4248
4249
4250 int html_printer::html_position_region (void)
4251 {
4252   int  r         = font::res;
4253   int  height    = output_style.point_size*r/72;
4254   int  temp_vpos;
4255   int  is_center = FALSE;
4256
4257   if (output_style.point_size != 0) {
4258     if (output_vpos != start_region_vpos) {
4259
4260       // graphic starts on a different line
4261       if (output_vpos == -1) {
4262         temp_vpos = start_region_vpos;
4263       } else {
4264         temp_vpos = output_vpos;
4265       }
4266
4267 #if 1
4268       if (need_one_newline) {
4269         html_newline();
4270         temp_vpos += height;
4271       } else {
4272         need_one_newline = TRUE;
4273       }
4274 #else
4275       html_newline();
4276       temp_vpos += height;
4277 #endif
4278
4279       while ((temp_vpos < start_region_vpos) &&
4280              (more_than_line_break(temp_vpos, start_region_vpos, height))) {
4281         html_newline();
4282         temp_vpos += height;
4283       }
4284     }
4285   }
4286   if (auto_on && (is_in_middle(start_region_hpos, end_region_hpos))) {
4287     is_center = TRUE;
4288   } else {
4289     if (start_region_hpos > left_margin_indent) {
4290       html.put_string("<span style=\" text-indent: ");
4291       html.put_float(((double)(start_region_hpos-left_margin_indent)/((double)r)));
4292       html.put_string("in;\"></span>");
4293     }
4294   }
4295 #if 0
4296    } else {
4297       // on the same line
4298       if (start_region_hpos > output_hpos) {
4299         html.put_string("<span style=\" text-indent: ");
4300         html.put_float(((double)(start_region_hpos-output_hpos)/((double)r)));
4301         html.put_string("in;\"></span>");
4302       }
4303     }
4304   }
4305 #endif
4306   output_vpos = start_region_vpos;
4307   output_hpos = start_region_hpos;
4308   return( is_center );
4309 }
4310
4311
4312 /*
4313  *  gs_x - translate and scale the x axis
4314  */
4315
4316 int html_printer::gs_x (int x)
4317 {
4318   x += IMAGE_BOARDER_PIXELS/2;
4319   return((x-start_region_hpos)*postscript_res/font::res);
4320 }
4321
4322
4323 /*
4324  *  gs_y - translate and scale the y axis
4325  */
4326
4327 int html_printer::gs_y (int y)
4328 {
4329   int yoffset=((int)(A4_PAGE_LENGTH*(double)font::res))-end_region_vpos;
4330
4331   y += IMAGE_BOARDER_PIXELS/2;
4332   return( (y+yoffset)*postscript_res/font::res );
4333 }
4334
4335
4336 void html_printer::troff_position_text (text_glob *g)
4337 {
4338   change_font(g, FALSE);
4339
4340   troff.put_string("V");
4341   troff.put_number(gs_y(g->maxv));
4342   troff.put_string("\n");
4343
4344   troff.put_string("H");
4345   troff.put_number(gs_x(g->minh));
4346   troff.put_string("\n");
4347 }
4348
4349 void html_printer::troff_change_font (const char *fontname, int size, int font_no)
4350 {
4351   troff.put_string("x font ");
4352   troff.put_number(font_no);
4353   troff.put_string(" ");
4354   troff.put_string(fontname);
4355   troff.put_string("\nf");
4356   troff.put_number(font_no);
4357   troff.put_string("\ns");
4358   troff.put_number(size*1000);
4359   troff.put_string("\n");
4360 }
4361
4362
4363 void html_printer::set_style(const style &sty)
4364 {
4365 #if 0
4366   const char *fontname = sty.f->get_name();
4367   if (fontname == 0)
4368     fatal("no internalname specified for font");
4369
4370   change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
4371 #endif
4372 }
4373
4374 void html_printer::end_of_line()
4375 {
4376   flush_sbuf();
4377   output_hpos = -1;
4378 }
4379
4380 void html_printer::html_display_word (text_glob *g)
4381 {
4382 #if 0
4383   if (strcmp(g->text_string, "increased.") == 0) {
4384     stop();
4385   }
4386 #endif
4387   if (! check_able_to_use_table(g)) {
4388     char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
4389
4390     if (! header.written_header) {
4391       if (g->is_raw_command) {
4392         html.put_string((char *)g->text_string);
4393       } else {
4394         html.html_write_string((char *)g->text_string);
4395       }
4396       if (postword != 0) {
4397         html.put_string(postword);
4398       }
4399       need_one_newline = TRUE;
4400       issued_newline   = FALSE;
4401     }
4402   }
4403 }
4404
4405 void html_printer::troff_display_word (text_glob *g)
4406 {
4407   troff_position_text(g);
4408   if (g->is_raw_command) {
4409     int l=strlen((char *)g->text_string);
4410     if (l == 1) {
4411       troff.put_string("c");
4412       troff.put_string((char *)g->text_string);
4413       troff.put_string("\n");
4414     } else if (l > 1) {
4415       troff.put_string("C");
4416       troff.put_translated_char((char *)g->text_string);
4417       troff.put_string("\n");
4418     }
4419   } else {
4420     troff_position_text(g);
4421     troff.put_string("t");
4422     troff.put_translated_string((const char *)g->text_string);
4423     troff.put_string("\n");
4424   }
4425 }
4426
4427 void html_printer::display_word (text_glob *g, int is_to_html)
4428 {
4429   if (is_to_html) {
4430     html_display_word(g);
4431   } else if ((g->is_raw_command) && (g->is_html_command)) {
4432     // found a raw html command inside a graphic glob.
4433     // We should emit the command to the html device, but of course we
4434     // cannot place it correctly as we are dealing with troff words.
4435     // Remember output_vpos will refer to troff and not html.
4436     html.put_string((char *)g->text_string);
4437   } else {
4438     troff_display_word(g);
4439   }
4440 }
4441
4442
4443 /*
4444  *  this information may be better placed inside some of the font files
4445  *  in devhtml - however one must bare in mind that we need the ability
4446  *  to write out to TWO devices (image and html) and image
4447  *  invokes ghostscript.
4448  */
4449
4450 simple_output &simple_output::html_write_string (const char *s)
4451 {
4452   int i=0;
4453
4454   while (s[i] != (char)0) {
4455     if (s[i] == '<') {
4456       put_string("&lt;");
4457     } else if (s[i] == '>') {
4458       put_string("&gt;");
4459     } else {
4460       fputc(s[i], fp);
4461       col++;
4462     }
4463     i++;
4464   }
4465   return *this;
4466 }
4467
4468 /*
4469  *  display_fill - generates a troff format fill command
4470  */
4471
4472 void html_printer::display_fill (graphic_glob *g)
4473 {
4474   troff.put_string("Df ") ;
4475   troff.put_number(g->fill);
4476   troff.put_string(" 0\n");
4477 }
4478
4479 /*
4480  *  display_line - displays a line using troff format
4481  */
4482
4483 void html_printer::display_line (graphic_glob *g, int is_to_html)
4484 {
4485   if (is_to_html) {
4486     fatal("cannot emit lines in html");
4487   }
4488   if (g->code == 'l') {
4489     // straight line
4490
4491     troff.put_string("V");
4492     troff.put_number(gs_y(g->point[0].y));
4493     troff.put_string("\n");
4494
4495     troff.put_string("H");
4496     troff.put_number(gs_x(g->point[0].x));
4497     troff.put_string("\n");
4498
4499     display_fill(g);
4500
4501     troff.put_string("Dl ");
4502     troff.put_number((g->point[1].x-g->point[0].x)*postscript_res/font::res);
4503     troff.put_string(" ");
4504     troff.put_number((g->point[1].y-g->point[0].y)*postscript_res/font::res);
4505     troff.put_string("\n");
4506     // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y,
4507     //                                         g->point[1].x, g->point[1].y, g->size);
4508   } else if ((g->code == 'c') || (g->code == 'C')) {
4509     // circle
4510
4511     int xradius = (g->maxh - g->minh) / 2;
4512     int yradius = (g->maxv - g->minv) / 2;
4513     // center of circle or elipse
4514
4515     troff.put_string("V");
4516     troff.put_number(gs_y(g->minv+yradius));
4517     troff.put_string("\n");
4518
4519     troff.put_string("H");
4520     troff.put_number(gs_x(g->minh));
4521     troff.put_string("\n");
4522
4523     display_fill(g);
4524
4525     if (g->code == 'c') {
4526       troff.put_string("Dc ");
4527     } else {
4528       troff.put_string("DC ");
4529     }
4530
4531     troff.put_number(xradius*2*postscript_res/font::res);
4532     troff.put_string("\n");
4533
4534   } else if ((g->code == 'e') || (g->code == 'E')) {
4535     // ellipse
4536
4537     int xradius = (g->maxh - g->minh) / 2;
4538     int yradius = (g->maxv - g->minv) / 2;
4539     // center of elipse - this is untested
4540
4541     troff.put_string("V");
4542     troff.put_number(gs_y(g->minv+yradius));
4543     troff.put_string("\n");
4544
4545     troff.put_string("H");
4546     troff.put_number(gs_x(g->minh));
4547     troff.put_string("\n");
4548
4549     display_fill(g);
4550
4551     if (g->code == 'e') {
4552       troff.put_string("De ");
4553     } else {
4554       troff.put_string("DE ");
4555     }
4556
4557     troff.put_number(xradius*2*postscript_res/font::res);
4558     troff.put_string(" ");
4559     troff.put_number(yradius*2*postscript_res/font::res);
4560     troff.put_string("\n");
4561   } else if ((g->code == 'p') || (g->code == 'P')) {
4562     // polygon
4563     troff.put_string("V");
4564     troff.put_number(gs_y(g->yc));
4565     troff.put_string("\n");
4566
4567     troff.put_string("H");
4568     troff.put_number(gs_x(g->xc));
4569     troff.put_string("\n");
4570
4571     display_fill(g);
4572
4573     if (g->code == 'p') {
4574       troff.put_string("Dp");
4575     } else {
4576       troff.put_string("DP");
4577     }
4578
4579     int i;
4580     int xc=g->xc;
4581     int yc=g->yc;
4582     for (i=0; i<g->nopoints; i++) {
4583       troff.put_string(" ");
4584       troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
4585       troff.put_string(" ");
4586       troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
4587       xc = g->point[i].x;
4588       yc = g->point[i].y;
4589     }
4590     troff.put_string("\n");
4591   } else if (g->code == 'a') {
4592     // arc
4593     troff.put_string("V");
4594     troff.put_number(gs_y(g->yc));
4595     troff.put_string("\n");
4596
4597     troff.put_string("H");
4598     troff.put_number(gs_x(g->xc));
4599     troff.put_string("\n");
4600
4601     display_fill(g);
4602
4603     troff.put_string("Da");
4604
4605     int i;
4606
4607     for (i=0; i<g->nopoints; i++) {
4608       troff.put_string(" ");
4609       troff.put_number(g->point[i].x*postscript_res/font::res);
4610       troff.put_string(" ");
4611       troff.put_number(g->point[i].y*postscript_res/font::res);
4612     }
4613     troff.put_string("\n");
4614   } else if (g->code == '~') {
4615     // spline
4616     troff.put_string("V");
4617     troff.put_number(gs_y(g->yc));
4618     troff.put_string("\n");
4619
4620     troff.put_string("H");
4621     troff.put_number(gs_x(g->xc));
4622     troff.put_string("\n");
4623
4624     display_fill(g);
4625
4626     troff.put_string("D~");
4627
4628     int i;
4629     int xc=g->xc;
4630     int yc=g->yc;
4631     for (i=0; i<g->nopoints; i++) {
4632       troff.put_string(" ");
4633       troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
4634       troff.put_string(" ");
4635       troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
4636       xc = g->point[i].x;
4637       yc = g->point[i].y;
4638     }
4639     troff.put_string("\n");
4640   }
4641 }
4642
4643
4644 void html_printer::flush_sbuf()
4645 {
4646   if (sbuf_len > 0) {
4647     int r=font::res;   // resolution of the device actually
4648     set_style(sbuf_style);
4649
4650     page_contents->add(&sbuf_style, sbuf, sbuf_len,
4651                        sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4652                        sbuf_vpos,                            sbuf_end_hpos);
4653              
4654     output_hpos = sbuf_end_hpos;
4655     output_vpos = sbuf_vpos;
4656     sbuf_len = 0;
4657   }
4658
4659 #if 0
4660   enum {
4661     NONE,
4662     RELATIVE_H,
4663     RELATIVE_V,
4664     RELATIVE_HV,
4665     ABSOLUTE
4666     } motion = NONE;
4667   int space_flag = 0;
4668   if (sbuf_len == 0)
4669     return;
4670
4671   if (output_style != sbuf_style) {
4672     set_style(sbuf_style);
4673     output_style = sbuf_style;
4674   }
4675
4676   int extra_space = 0;
4677   if (output_hpos < 0 || output_vpos < 0)
4678     motion = ABSOLUTE;
4679   else {
4680     if (output_hpos != sbuf_start_hpos)
4681       motion = RELATIVE_H;
4682     if (output_vpos != sbuf_vpos) {
4683       if  (motion != NONE)
4684         motion = RELATIVE_HV;
4685       else
4686         motion = RELATIVE_V;
4687     }
4688   }
4689   if (sbuf_space_code >= 0) {
4690     int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
4691     if (w + sbuf_kern != sbuf_space_width) {
4692       if (sbuf_space_code != output_space_code) {
4693         output_space_code = sbuf_space_code;
4694       }
4695       space_flag = 1;
4696       extra_space = sbuf_space_width - w - sbuf_kern;
4697       if (sbuf_space_diff_count > sbuf_space_count/2)
4698         extra_space++;
4699       else if (sbuf_space_diff_count < -(sbuf_space_count/2))
4700         extra_space--;
4701     }
4702   }
4703
4704   if (space_flag)
4705     html.put_number(extra_space);
4706   if (sbuf_kern != 0)
4707     html.put_number(sbuf_kern);
4708
4709   html.put_string(sbuf, sbuf_len);
4710
4711   char sym[2];
4712   sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
4713   sym[1] = '\0';
4714   switch (motion) {
4715   case NONE:
4716     break;
4717   case ABSOLUTE:
4718     html.put_number(sbuf_start_hpos)
4719        .put_number(sbuf_vpos);
4720     break;
4721   case RELATIVE_H:
4722     html.put_number(sbuf_start_hpos - output_hpos);
4723     break;
4724   case RELATIVE_V:
4725     html.put_number(sbuf_vpos - output_vpos);
4726     break;
4727   case RELATIVE_HV:
4728     html.put_number(sbuf_start_hpos - output_hpos)
4729        .put_number(sbuf_vpos - output_vpos);
4730     break;
4731   default:
4732     assert(0);
4733   }
4734
4735   output_hpos = sbuf_end_hpos;
4736   output_vpos = sbuf_vpos;
4737   sbuf_len = 0;
4738 #endif
4739 }
4740
4741
4742 void html_printer::set_line_thickness(const environment *env)
4743 {
4744   line_thickness = env->size;
4745   printf("line thickness = %d\n", line_thickness);
4746 }
4747
4748 void html_printer::draw(int code, int *p, int np, const environment *env)
4749 {
4750   switch (code) {
4751
4752   case 'l':
4753     if (np == 2) {
4754       page_contents->add_line(code,
4755                               env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1],
4756                               env->size, fill);
4757     } else {
4758       error("2 arguments required for line");
4759     }
4760     break;
4761   case 't':
4762     {
4763       if (np == 0) {
4764         line_thickness = -1;
4765       } else {
4766         // troff gratuitously adds an extra 0
4767         if (np != 1 && np != 2) {
4768           error("0 or 1 argument required for thickness");
4769           break;
4770         }
4771         line_thickness = p[0];
4772       }
4773       break;
4774     }
4775
4776   case 'P':
4777     // fall through
4778   case 'p':
4779     {
4780       if (np & 1) {
4781         error("even number of arguments required for polygon");
4782         break;
4783       }
4784       if (np == 0) {
4785         error("no arguments for polygon");
4786         break;
4787       }
4788       // firstly lets add our current position to polygon
4789       int oh=env->hpos;
4790       int ov=env->vpos;
4791       int i=0;
4792
4793       while (i<np) {
4794         p[i+0] += oh;
4795         p[i+1] += ov;
4796         oh      = p[i+0];
4797         ov      = p[i+1];
4798         i      += 2;
4799       }
4800       // now store polygon in page
4801       page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill);
4802     }
4803     break;
4804   case 'E':
4805     // fall through
4806   case 'e':
4807     if (np != 2) {
4808       error("2 arguments required for ellipse");
4809       break;
4810     }
4811     page_contents->add_line(code,
4812                             env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2,
4813                             env->size, fill);
4814
4815     break;
4816   case 'C':
4817     // fill circle
4818
4819   case 'c':
4820     {
4821       // troff adds an extra argument to C
4822       if (np != 1 && !(code == 'C' && np == 2)) {
4823         error("1 argument required for circle");
4824         break;
4825       }
4826       page_contents->add_line(code,
4827                               env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2,
4828                               env->size, fill);
4829     }
4830     break;
4831   case 'a':
4832     {
4833       if (np == 4) {
4834         double c[2];
4835
4836         if (adjust_arc_center(p, c)) {
4837           page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill);
4838         } else {
4839           // a straignt line
4840           page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill);
4841         }
4842       } else {
4843         error("4 arguments required for arc");
4844       }
4845     }
4846     break;
4847   case '~':
4848     {
4849       if (np & 1) {
4850         error("even number of arguments required for spline");
4851         break;
4852       }
4853       if (np == 0) {
4854         error("no arguments for spline");
4855         break;
4856       }
4857       // firstly lets add our current position to spline
4858       int oh=env->hpos;
4859       int ov=env->vpos;
4860       int i=0;
4861
4862       while (i<np) {
4863         p[i+0] += oh;
4864         p[i+1] += ov;
4865         oh      = p[i+0];
4866         ov      = p[i+1];
4867         i      += 2;
4868       }
4869       page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill);
4870     }
4871     break;
4872   case 'f':
4873     {
4874       if (np != 1 && np != 2) {
4875         error("1 argument required for fill");
4876         break;
4877       }
4878       fill = p[0];
4879       if (fill < 0 || fill > FILL_MAX) {
4880         // This means fill with the current color.
4881         fill = FILL_MAX + 1;
4882       }
4883       break;
4884     }
4885
4886   default:
4887     error("unrecognised drawing command `%1'", char(code));
4888     break;
4889   }
4890 }
4891
4892
4893 void html_printer::begin_page(int n)
4894 {
4895   page_number            =  n;
4896   html.begin_comment("Page: ").comment_arg(itoa(page_number)).end_comment();;
4897   no_of_printed_pages++;
4898   
4899   output_style.f         =  0;
4900   output_space_code      = 32;
4901   output_draw_point_size = -1;
4902   output_line_thickness  = -1;
4903   output_hpos            = -1;
4904   output_vpos            = -1;
4905 }
4906
4907 void testing (text_glob *g) {}
4908
4909 void html_printer::flush_graphic (void)
4910 {
4911   graphic_glob g;
4912
4913   graphic_level = 0;
4914   page_contents->is_in_graphic = FALSE;
4915
4916   g.minv = -1;
4917   g.maxv = -1;
4918   calculate_region_range(&g);
4919   if (g.minv != -1) {
4920     page_contents->make_new_region(&g);
4921   }
4922   move_region_to_page();
4923 }
4924
4925 void html_printer::end_page(int)
4926 {
4927   flush_sbuf();
4928   flush_graphic();
4929   flush_page();
4930 }
4931
4932 font *html_printer::make_font(const char *nm)
4933 {
4934   return html_font::load_html_font(nm);
4935 }
4936
4937 html_printer::~html_printer()
4938 {
4939   if (fseek(tempfp, 0L, 0) < 0)
4940     fatal("fseek on temporary file failed");
4941   html.set_file(stdout);
4942   fputs("<html>\n", stdout);
4943   fputs("<head>\n", stdout);
4944   fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
4945   write_title(TRUE);
4946   fputs("</head>\n", stdout);
4947   fputs("<body>\n", stdout);
4948   write_title(FALSE);
4949   header.write_headings(stdout);
4950   {
4951     extern const char *version_string;
4952     html.begin_comment("Creator     : ")
4953        .comment_arg("groff ")
4954        .comment_arg("version ")
4955        .comment_arg(version_string)
4956        .end_comment();
4957   }
4958   {
4959 #ifdef LONG_FOR_TIME_T
4960     long
4961 #else
4962     time_t
4963 #endif
4964     t = time(0);
4965     html.begin_comment("CreationDate: ")
4966        .comment_arg(ctime(&t))
4967        .end_comment();
4968   }
4969   for (font_pointer_list *f = font_list; f; f = f->next) {
4970     html_font *psf = (html_font *)(f->p);
4971   }
4972   html.begin_comment("Total number of pages: ").comment_arg(itoa(no_of_printed_pages)).end_comment();
4973   html.end_line();
4974   html.copy_file(tempfp);
4975   fputs("</body>\n", stdout);
4976   fputs("</html>\n", stdout);
4977   fclose(tempfp);
4978 }
4979
4980
4981 /*
4982  *  calculate_region_range - calculates the vertical range for words and lines
4983  *                           within the region lists.
4984  */
4985
4986 void html_printer::calculate_region_range (graphic_glob *r)
4987 {
4988   text_glob    *w;
4989   graphic_glob *g;
4990
4991   if (! page_contents->region_lines.is_empty()) {
4992     page_contents->region_lines.start_from_head();
4993     do {
4994       g = page_contents->region_lines.get_data();
4995       if ((r->minv == -1) || (g->minv < r->minv)) {
4996         r->minv = g->minv;
4997       }
4998       if ((r->maxv == -1) || (g->maxv > r->maxv)) {
4999         r->maxv = g->maxv;
5000       }
5001       page_contents->region_lines.move_right();
5002     } while (! page_contents->region_lines.is_equal_to_head());
5003   }
5004   if (! page_contents->region_words.is_empty()) {
5005     page_contents->region_words.start_from_head();
5006     do {
5007       w = page_contents->region_words.get_data();
5008
5009       if ((r->minv == -1) || (w->minv < r->minv)) {
5010         r->minv = w->minv;
5011       }
5012       if ((r->maxv == -1) || (w->maxv > r->maxv)) {
5013         r->maxv = w->maxv;
5014       }
5015       page_contents->region_words.move_right();
5016     } while (! page_contents->region_words.is_equal_to_head());
5017   }
5018 }
5019
5020
5021 /*
5022  *  move_region_to_page - moves lines and words held in the temporary region
5023  *                        list to the page list.
5024  */
5025
5026 void html_printer::move_region_to_page (void)
5027 {
5028   text_glob    *w;
5029   graphic_glob *g;
5030
5031   page_contents->region_lines.start_from_head();
5032   while (! page_contents->region_lines.is_empty()) {
5033     g = page_contents->region_lines.get_data();   // remove from our temporary region list
5034     page_contents->lines.add(g);                  // and add to the page list
5035     page_contents->region_lines.sub_move_right();
5036   }
5037   page_contents->region_words.start_from_head();
5038   while (! page_contents->region_words.is_empty()) {
5039     w = page_contents->region_words.get_data();   // remove from our temporary region list
5040     page_contents->words.add(w);                  // and add to the page list
5041     page_contents->region_words.sub_move_right();
5042   }
5043 }
5044
5045
5046 void html_printer::special(char *s, const environment *env)
5047 {
5048   if (s != 0) {
5049     if (strcmp(s, "graphic-start") == 0) {
5050       graphic_level++;
5051       if (graphic_level == 1) {
5052         page_contents->is_in_graphic = TRUE;    // add words and lines to temporary region lists
5053       }
5054     } else if ((strcmp(s, "graphic-end") == 0) && (graphic_level > 0)) {
5055       graphic_level--;
5056       if (graphic_level == 0) {
5057         flush_graphic();
5058       }
5059     } else if (strncmp(s, "html:", 5) == 0) {
5060       int r=font::res;   // resolution of the device actually
5061
5062       page_contents->add_html_command(&sbuf_style, &s[5], strlen(s)-5,
5063
5064       // need to pass rest of string through to html output during flush
5065
5066                                       env->vpos-env->size*r/72, env->hpos,
5067                                       env->vpos            , env->hpos);
5068       // assume that the html command has no width, if it does then we hopefully troff
5069       // will have fudged this in a macro and requested that the formatting move right by
5070       // the appropriate width
5071     } else if (strncmp(s, "index:", 6) == 0) {
5072       cutoff_heading = atoi(&s[6]);
5073     }
5074   }
5075 }
5076
5077 void set_image_type (char *type)
5078 {
5079   if (strcmp(type, "gif") == 0) {
5080     image_type = gif;
5081   } else if (strcmp(type, "png") == 0) {
5082     image_type = png;
5083     image_device = "png256";
5084   } else if (strncmp(type, "png", 3) == 0) {
5085     image_type = png;
5086     image_device = type;
5087   }
5088 }
5089
5090 // A conforming PostScript document must not have lines longer
5091 // than 255 characters (excluding line termination characters).
5092
5093 static int check_line_lengths(const char *p)
5094 {
5095   for (;;) {
5096     const char *end = strchr(p, '\n');
5097     if (end == 0)
5098       end = strchr(p, '\0');
5099     if (end - p > 255)
5100       return 0;
5101     if (*end == '\0')
5102       break;
5103     p = end + 1;
5104   }
5105   return 1;
5106 }
5107
5108 printer *make_printer()
5109 {
5110   return new html_printer;
5111 }
5112
5113 static void usage();
5114
5115 int main(int argc, char **argv)
5116 {
5117   program_name = argv[0];
5118   static char stderr_buf[BUFSIZ];
5119   setbuf(stderr, stderr_buf);
5120   int c;
5121   while ((c = getopt(argc, argv, "F:atvdgmx?I:r:")) != EOF)
5122     switch(c) {
5123     case 'v':
5124       {
5125         extern const char *version_string;
5126         fprintf(stderr, "grohtml version %s\n", version_string);
5127         fflush(stderr);
5128         break;
5129       }
5130     case 'a':
5131       auto_on = FALSE;
5132       break;
5133     case 't':
5134       table_on = FALSE;
5135       break;
5136     case 'F':
5137       font::command_line_font_dir(optarg);
5138       break;
5139     case 'I':
5140       // user specifying the type of images we should generate
5141       set_image_type(optarg);
5142       break;
5143     case 'r':
5144       // resolution (dots per inch for an image)
5145       image_res = atoi(optarg);
5146       break;
5147     case 'd':
5148       // debugging on
5149       debug_on       = TRUE;
5150       break;
5151     case 'x':
5152       debug_table_on = TRUE;
5153       break;
5154     case 'g':
5155       // do not guess title and headings
5156       guess_on = FALSE;
5157       break;
5158     case 'm':
5159       // leave margins alone
5160       margin_on = TRUE;
5161       break;
5162     case '?':
5163       usage();
5164       break;
5165     default:
5166       assert(0);
5167     }
5168   if (optind >= argc) {
5169     do_file("-");
5170   } else {
5171     for (int i = optind; i < argc; i++)
5172       do_file(argv[i]);
5173   }
5174   delete pr;
5175   return 0;
5176 }
5177
5178 static void usage()
5179 {
5180   fprintf(stderr, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n",
5181           program_name);
5182   exit(1);
5183 }