2 /* Copyright (C) 1999 Free Software Foundation, Inc.
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.
10 This file is part of groff.
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
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
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. */
27 #include "stringclass.h"
31 #include "html_chars.h"
41 #include "ordered_list.h"
50 #define MAX_TEMP_NAME 1024
51 #define MAX_STRING_LENGTH 4096
52 #define MAX_CHAR_SIZE 50 // maximum length of character name
54 #define Y_FUDGE_MARGIN +0.83
55 #define A4_PAGE_LENGTH (11.6944-Y_FUDGE_MARGIN)
56 #define DEFAULT_IMAGE_RES 80
57 #define IMAGE_BOARDER_PIXELS 10
58 #define MAX_WORDS_PER_LINE 1000 // only used for table indentation
59 #define GAP_SPACES 3 // how many spaces needed to guess a gap?
60 #define GAP_WIDTH_ONE_LINE 2 // 1/GAP_WIDTH_ONE_LINE inches required for one line table
61 #define CENTER_TOLERANCE 2 // how many pixels off center will we think a line or region is centered
62 #define MIN_COLUMN 7 // minimum column size pixels for multiple lines
63 #define MIN_COLUMN_FOR_TWO_LINES 20 // minimum column size pixels for a 2 line table
64 #define MIN_TEXT_PERCENT 5 // try and round to this percentage value for used columns
65 #define PERCENT_THRESHOLD 20 // don't bother trying to increase and width greater than this
69 * Only uncomment one of the following to determine default image type.
72 #define IMAGE_DEFAULT_PNG
73 /* #define IMAGE_DEFAULT_GIF */
76 #if defined(IMAGE_DEFAULT_GIF)
77 static enum { gif
, png
} image_type
= gif
;
78 static char *image_device
= "gif";
79 #elif defined(IMAGE_DEFAULT_PNG)
80 static enum { gif
, png
} image_type
= png
;
81 static char *image_device
= "png256";
83 # error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG"
86 static int debug_on
= FALSE
;
87 static int guess_on
= TRUE
;
88 static int margin_on
= FALSE
;
89 static int auto_on
= TRUE
;
90 static int table_on
= TRUE
;
91 static int image_res
= DEFAULT_IMAGE_RES
;
92 static int debug_table_on
= FALSE
;
93 static int table_image_on
= TRUE
; // default is to create images for tbl
95 static int linewidth
= -1;
97 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
98 #define MAX_LINE_LENGTH 72
105 * start with a few favorites
108 static int min (int a
, int b
)
117 static int max (int a
, int b
)
127 * is_subsection - returns TRUE if a1..a2 is within b1..b2
130 static int is_subsection (int a1
, int a2
, int b1
, int b2
)
132 // easier to see whether this is not the case
133 return( !((a1
< b1
) || (a1
> b2
) || (a2
< b1
) || (a2
> b2
)) );
137 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
140 static int is_intersection (int a1
, int a2
, int b1
, int b2
)
142 // again easier to prove NOT outside limits
143 return( ! ((a1
> b2
) || (a2
< b1
)) );
147 * is_digit - returns TRUE if character, ch, is a digit.
150 static int is_digit (char ch
)
152 return( (ch
>= '0') && (ch
<= '9') );
156 * more_than_line_break - returns TRUE should v1 and v2 differ by more than
157 * a simple line break.
160 static int more_than_line_break (int v1
, int v2
, int size
)
162 return( abs(v1
-v2
)>size
);
166 * the class and methods for styles
176 style (font
*, int, int, int, int);
177 int operator == (const style
&) const;
178 int operator != (const style
&) const;
186 style::style(font
*p
, int sz
, int h
, int sl
, int no
)
187 : f(p
), point_size(sz
), font_no(no
), height(h
), slant(sl
)
191 int style::operator==(const style
&s
) const
193 return (f
== s
.f
&& point_size
== s
.point_size
194 && height
== s
.height
&& slant
== s
.slant
);
197 int style::operator!=(const style
&s
) const
199 return !(*this == s
);
204 * the class and methods for retaining ascii text
216 char_block::char_block()
225 char *add_string(char *, unsigned int);
231 char_buffer::char_buffer()
236 char_buffer::~char_buffer()
239 char_block
*temp
= head
;
245 char *char_buffer::add_string (char *s
, unsigned int length
)
248 unsigned int old_used
;
251 tail
= new char_block
;
254 if (tail
->used
+ length
+1 > char_block::SIZE
) {
255 tail
->next
= new char_block
;
259 // at this point we have a tail which is ready for the string.
260 if (tail
->used
+ length
+1 > char_block::SIZE
) {
261 fatal("need to increase char_block::SIZE");
264 old_used
= tail
->used
;
266 tail
->buffer
[tail
->used
] = s
[i
];
272 // add terminating nul character
274 tail
->buffer
[tail
->used
] = '\0';
277 // and return start of new string
279 return( &tail
->buffer
[old_used
] );
283 * the classes and methods for maintaining pages and text positions and graphic regions
288 int is_less (text_glob
*a
, text_glob
*b
);
289 text_glob (style
*s
, char *string
, unsigned int length
,
290 int min_vertical
, int min_horizontal
,
291 int max_vertical
, int max_horizontal
, int is_command
, int is_html
);
297 unsigned int text_length
;
298 int minv
, maxv
, minh
, maxh
;
299 int is_raw_command
; // should the text be sent directly to the device?
300 int is_html_command
; // is the raw command definitely for the html device ie not an eqn?
303 text_glob::text_glob (style
*s
, char *string
, unsigned int length
,
304 int min_vertical
, int min_horizontal
,
305 int max_vertical
, int max_horizontal
, int is_command
, int is_html
)
306 : text_style(*s
), text_string(string
), text_length(length
),
307 minv(min_vertical
), maxv(max_vertical
), minh(min_horizontal
), maxh(max_horizontal
),
308 is_raw_command(is_command
), is_html_command(is_html
)
312 text_glob::text_glob ()
313 : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
314 is_raw_command(FALSE
), is_html_command(FALSE
)
318 text_glob::~text_glob ()
322 int text_glob::is_less (text_glob
*a
, text_glob
*b
)
324 if (is_intersection(a
->minv
+1, a
->maxv
-1, b
->minv
+1, b
->maxv
-1)) {
325 return( a
->minh
< b
->minh
);
327 return( a
->maxv
< b
->maxv
);
338 int is_less (graphic_glob
*a
, graphic_glob
*b
);
339 graphic_glob (int troff_code
);
341 ~graphic_glob (void);
343 int minv
, maxv
, minh
, maxh
;
345 int nopoints
; // number of points allocated in array below
346 struct xycoord
*point
;
352 graphic_glob::graphic_glob ()
353 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(0)
357 graphic_glob::~graphic_glob ()
364 graphic_glob::graphic_glob (int troff_code
)
365 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(troff_code
)
369 int graphic_glob::is_less (graphic_glob
*a
, graphic_glob
*b
)
371 return( (a
->minv
< b
->minv
) || ((a
->minv
== b
->minv
) && (a
->minh
< b
->minh
)) );
378 int is_less (region_glob
*a
, region_glob
*b
);
380 int minv
, maxv
, minh
, maxh
;
383 int region_glob::is_less (region_glob
*a
, region_glob
*b
)
385 return( (a
->minv
< b
->minv
) || ((a
->minv
== b
->minv
) && (a
->minh
< b
->minh
)) );
388 region_glob::region_glob (void)
389 : minv(-1), maxv(-1), minh(-1), maxh(-1)
393 region_glob::~region_glob (void)
400 void add (style
*s
, char *string
, unsigned int length
,
401 int min_vertical
, int min_horizontal
,
402 int max_vertical
, int max_horizontal
);
403 void add_html_command (style
*s
, char *string
, unsigned int length
,
404 int min_vertical
, int min_horizontal
,
405 int max_vertical
, int max_horizontal
);
406 void add_special_char (style
*s
, char *string
, unsigned int length
,
407 int min_vertical
, int min_horizontal
,
408 int max_vertical
, int max_horizontal
);
409 void add_line (int code
, int x1
, int y1
, int x2
, int y2
, int size
, int fill
);
410 void add_arc (int code
, int xc
, int yc
, int *p
, double *c
, int size
, int fill
);
411 void add_polygon (int code
, int np
, int *p
, int oh
, int ov
, int size
, int fill
);
412 void add_spline (int code
, int xc
, int yc
, int np
, int *p
, int size
, int fill
);
413 void calculate_region (void);
414 int is_in_region (graphic_glob
*g
);
415 int can_grow_region (graphic_glob
*g
);
416 void make_new_region (graphic_glob
*g
);
417 int has_line (region_glob
*r
);
418 int has_word (region_glob
*r
);
419 int no_raw_commands (int minv
, int maxv
);
423 ordered_list
<region_glob
> regions
; // squares of bitmapped pics,eqn,tbl's
424 ordered_list
<text_glob
> words
; // position of words on page
425 ordered_list
<graphic_glob
> lines
; // position of lines on page
426 char_buffer buffer
; // all characters for this page
427 int is_in_graphic
; // should graphics and words go below or above
428 ordered_list
<text_glob
> region_words
; // temporary accumulation of words in a region
429 ordered_list
<graphic_glob
> region_lines
; // (as above) and used so that we can determine
430 // the regions vertical limits
434 : is_in_graphic(FALSE
)
438 void page::add (style
*s
, char *string
, unsigned int length
,
439 int min_vertical
, int min_horizontal
,
440 int max_vertical
, int max_horizontal
)
443 text_glob
*g
=new text_glob(s
, buffer
.add_string(string
, length
), length
,
444 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
, FALSE
, FALSE
);
454 * add_html_command - it only makes sense to add html commands when we are not inside
455 * a graphical entity.
458 void page::add_html_command (style
*s
, char *string
, unsigned int length
,
459 int min_vertical
, int min_horizontal
,
460 int max_vertical
, int max_horizontal
)
462 if ((length
> 0) && (! is_in_graphic
)) {
463 text_glob
*g
=new text_glob(s
, buffer
.add_string(string
, length
), length
,
464 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
, TRUE
, TRUE
);
470 * add_special_char - it only makes sense to add special characters when we are inside
471 * a graphical entity.
474 void page::add_special_char (style
*s
, char *string
, unsigned int length
,
475 int min_vertical
, int min_horizontal
,
476 int max_vertical
, int max_horizontal
)
478 if ((length
> 0) && (is_in_graphic
)) {
479 text_glob
*g
=new text_glob(s
, buffer
.add_string(string
, length
), length
,
480 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
, TRUE
, FALSE
);
485 void page::add_line (int code
, int x1
, int y1
, int x2
, int y2
, int size
, int fill
)
487 graphic_glob
*g
= new graphic_glob(code
);
489 g
->minh
= min(x1
, x2
);
490 g
->maxh
= max(x1
, x2
);
491 g
->minv
= min(y1
, y2
);
492 g
->maxv
= max(y1
, y2
);
493 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*2);
512 * assign_min_max_for_arc - works out the smallest box that will encompass an
513 * arc defined by: origin: g->xc, g->xc
514 * and vector (p[0], p[1]) and (p[2], p[3])
517 void assign_min_max_for_arc (graphic_glob
*g
, int *p
, double *c
)
519 int radius
= (int) sqrt(c
[0]*c
[0]+c
[1]*c
[1]);
526 int x2
= g
->xc
+xv1
+xv2
;
527 int y2
= g
->yc
+yv1
+yv2
;
529 // firstly lets use the 'circle' limitation
535 // incidentally I'm sure there is a better way to do this, but I don't know it
536 // please can someone let me know or "improve" this function
538 // now see which min/max can be reduced and increased for the limits of the arc
547 if ((xv1
>=0) && (yv1
>=0)) {
548 // first vector in Q3
549 if ((xv2
>=0) && (yv2
>=0)) {
553 } else if ((xv2
<0) && (yv2
>=0)) {
557 } else if ((xv2
>=0) && (yv2
<0)) {
559 g
->minv
= min(y1
, y2
);
560 } else if ((xv2
<0) && (yv2
<0)) {
565 g
->minv
= min(y1
, y2
);
566 g
->maxv
= max(y1
, y2
);
568 // xv2, yv2 could all be zero?
571 } else if ((xv1
>=0) && (yv1
<0)) {
572 // first vector in Q2
573 if ((xv2
>=0) && (yv2
>=0)) {
575 g
->maxh
= max(x1
, x2
);
576 g
->minh
= min(x1
, x2
);
578 } else if ((xv2
<0) && (yv2
>=0)) {
583 g
->minv
= min(y1
, y2
);
584 g
->maxv
= max(y1
, y2
);
586 // otherwise almost full circle anyway
588 } else if ((xv2
>=0) && (yv2
<0)) {
592 } else if ((xv2
<0) && (yv2
<0)) {
594 g
->minh
= min(x1
, x2
);
596 } else if ((xv1
<0) && (yv1
<0)) {
597 // first vector in Q1
598 if ((xv2
>=0) && (yv2
>=0)) {
603 g
->minv
= min(y1
, y2
);
604 g
->maxv
= max(y1
, y2
);
606 // nearly full circle
608 } else if ((xv2
<0) && (yv2
>=0)) {
610 g
->maxv
= max(y1
, y2
);
611 } else if ((xv2
>=0) && (yv2
<0)) {
613 g
->minv
= min(y1
, y2
);
614 g
->maxv
= max(y1
, y2
);
615 g
->minh
= min(x1
, x2
);
616 } else if ((xv2
<0) && (yv2
<0)) {
621 } else if ((xv1
<0) && (yv1
>=0)) {
622 // first vector in Q4
623 if ((xv2
>=0) && (yv2
>=0)) {
625 g
->maxh
= max(x1
, x2
);
626 } else if ((xv2
<0) && (yv2
>=0)) {
628 g
->maxv
= max(y1
, y2
);
629 g
->maxh
= max(x1
, x2
);
630 } else if ((xv2
>=0) && (yv2
<0)) {
633 g
->minv
= min(y1
, y2
);
634 g
->maxv
= max(y1
, y2
);
635 g
->minh
= min(x1
, x2
);
636 g
->maxh
= max(x2
, x2
);
638 // nearly full circle
640 } else if ((xv2
<0) && (yv2
<0)) {
642 g
->maxv
= max(y1
, y2
);
643 g
->minh
= min(x1
, x2
);
644 g
->maxh
= max(x1
, x2
);
647 // this should *never* happen but if it does it means a case above is wrong..
649 // this code is only present for safety sake
650 if (g
->maxh
< g
->minh
) {
652 fprintf(stderr
, "assert failed minh > maxh\n"); fflush(stderr
);
657 if (g
->maxv
< g
->minv
) {
659 fprintf(stderr
, "assert failed minv > maxv\n"); fflush(stderr
);
666 void page::add_arc (int code
, int xc
, int yc
, int *p
, double *c
, int size
, int fill
)
668 graphic_glob
*g
= new graphic_glob(code
);
670 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*2);
672 g
->point
[0].x
= p
[0] ;
673 g
->point
[0].y
= p
[1] ;
674 g
->point
[1].x
= p
[2] ;
675 g
->point
[1].y
= p
[3] ;
681 assign_min_max_for_arc(g
, p
, c
);
691 void page::add_polygon (int code
, int np
, int *p
, int oh
, int ov
, int size
, int fill
)
693 graphic_glob
*g
= new graphic_glob(code
);
697 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*np
/2);
700 for (i
=0; i
<g
->nopoints
; i
++) {
701 g
->point
[i
].x
= p
[j
];
703 g
->point
[i
].y
= p
[j
];
706 // now calculate min/max
707 g
->minh
= g
->point
[0].x
;
708 g
->minv
= g
->point
[0].y
;
709 g
->maxh
= g
->point
[0].x
;
710 g
->maxv
= g
->point
[0].y
;
711 for (i
=1; i
<g
->nopoints
; i
++) {
712 g
->minh
= min(g
->minh
, g
->point
[i
].x
);
713 g
->minv
= min(g
->minv
, g
->point
[i
].y
);
714 g
->maxh
= max(g
->maxh
, g
->point
[i
].x
);
715 g
->maxv
= max(g
->maxv
, g
->point
[i
].y
);
729 void page::add_spline (int code
, int xc
, int yc
, int np
, int *p
, int size
, int fill
)
731 graphic_glob
*g
= new graphic_glob(code
);
735 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*np
/2);
738 for (i
=0; i
<g
->nopoints
; i
++) {
739 g
->point
[i
].x
= p
[j
];
741 g
->point
[i
].y
= p
[j
];
744 // now calculate min/max
745 g
->minh
= min(g
->point
[0].x
, g
->point
[0].x
/2);
746 g
->minv
= min(g
->point
[0].y
, g
->point
[0].y
/2);
747 g
->maxh
= max(g
->point
[0].x
, g
->point
[0].x
/2);
748 g
->maxv
= max(g
->point
[0].y
, g
->point
[0].y
/2);
750 /* tnum/tden should be between 0 and 1; the closer it is to 1
751 the tighter the curve will be to the guiding lines; 2/3
752 is the standard value */
756 for (i
=1; i
<g
->nopoints
-1; i
++) {
757 g
->minh
= min(g
->minh
, g
->point
[i
].x
*tnum
/(2*tden
));
758 g
->minv
= min(g
->minv
, g
->point
[i
].y
*tnum
/(2*tden
));
759 g
->maxh
= max(g
->maxh
, g
->point
[i
].x
*tnum
/(2*tden
));
760 g
->maxv
= max(g
->maxv
, g
->point
[i
].y
*tnum
/(2*tden
));
762 g
->minh
= min(g
->minh
, g
->point
[i
].x
/2+(g
->point
[i
+1].x
*(tden
-tden
))/(2*tden
));
763 g
->minv
= min(g
->minv
, g
->point
[i
].y
/2+(g
->point
[i
+1].y
*(tden
-tden
))/(2*tden
));
764 g
->maxh
= max(g
->maxh
, g
->point
[i
].x
/2+(g
->point
[i
+1].x
*(tden
-tden
))/(2*tden
));
765 g
->maxv
= max(g
->maxv
, g
->point
[i
].y
/2+(g
->point
[i
+1].y
*(tden
-tden
))/(2*tden
));
767 g
->minh
= min(g
->minh
, (g
->point
[i
].x
-g
->point
[i
].x
/2) + g
->point
[i
+1].x
/2);
768 g
->minv
= min(g
->minv
, (g
->point
[i
].y
-g
->point
[i
].y
/2) + g
->point
[i
+1].y
/2);
769 g
->maxh
= max(g
->maxh
, (g
->point
[i
].x
-g
->point
[i
].x
/2) + g
->point
[i
+1].x
/2);
770 g
->maxv
= max(g
->maxv
, (g
->point
[i
].y
-g
->point
[i
].y
/2) + g
->point
[i
+1].y
/2);
774 g
->minh
= min(g
->minh
, (g
->point
[i
].x
-g
->point
[i
].x
/2)) + xc
;
775 g
->minv
= min(g
->minv
, (g
->point
[i
].y
-g
->point
[i
].y
/2)) + yc
;
776 g
->maxh
= max(g
->maxh
, (g
->point
[i
].x
-g
->point
[i
].x
/2)) + xc
;
777 g
->maxv
= max(g
->maxv
, (g
->point
[i
].y
-g
->point
[i
].y
/2)) + yc
;
791 class html_font
: public font
{
792 html_font(const char *);
796 char *reencoded_name
;
798 void handle_unknown_font_command(const char *command
, const char *arg
,
799 const char *filename
, int lineno
);
800 static html_font
*load_html_font(const char *);
803 html_font
*html_font::load_html_font(const char *s
)
805 html_font
*f
= new html_font(s
);
813 html_font::html_font(const char *nm
)
818 html_font::~html_font()
822 void html_font::handle_unknown_font_command(const char *command
, const char *arg
,
823 const char *filename
, int lineno
)
825 if (strcmp(command
, "encoding") == 0) {
827 error_with_file_and_line(filename
, lineno
,
828 "`encoding' command requires an argument");
830 encoding
= strsave(arg
);
836 * a simple class to contain the header to this document
844 int has_been_written
;
846 char text
[MAX_STRING_LENGTH
];
850 title_desc::title_desc ()
851 : has_been_written(FALSE
), has_been_found(FALSE
)
855 title_desc::~title_desc ()
864 int no_of_headings
; // how many headings have we found?
865 char_buffer headings
; // all the headings used in the document
866 ordered_list
<text_glob
> headers
;
867 int header_level
; // current header level
868 int written_header
; // have we written the header yet?
869 char header_buffer
[MAX_STRING_LENGTH
]; // current header text
871 void write_headings (FILE *f
);
874 header_desc::header_desc ()
875 : no_of_headings(0), header_level(2), written_header(0)
879 header_desc::~header_desc ()
884 * paragraph_type - alignment for a new paragraph
887 typedef enum { left_alignment
, center_alignment
} paragraph_type
;
890 * text_defn - defines the limit of text, initially these are stored in the
891 * column array as words. Later we examine the white space between
892 * the words in successive lines to find out whether we can detect
893 * distinct columns. The columns are generated via html tables.
897 int left
; // the start of a word or text
898 int right
; // the end of the text and beginning of white space
899 int is_used
; // will this this column be used for words or space
900 int right_hits
; // count of the number of words touching right position
901 int percent
; // what percentage width should we use for this cell?
905 * introduce a paragraph class so that we can nest paragraphs
906 * from plain html text and html tables.
909 class html_paragraph
{
911 html_paragraph (int in
, int need
, paragraph_type type
, html_paragraph
*prev
);
916 paragraph_type para_type
;
917 html_paragraph
*previous
;
921 * html_paragraph - constructor, fill in the public fields.
924 html_paragraph::html_paragraph (int in
, int need
, paragraph_type type
, html_paragraph
*prev
)
925 : in_paragraph(in
), need_paragraph(need
),
926 para_type(type
), previous(prev
)
931 * html_paragraph - deconstructor
934 html_paragraph::~html_paragraph ()
939 * note that html_tables are currently only used to provide a better
940 * indentation mechanism for html text (in particular it allows grohtml
941 * to render .IP and .2C together with autoformatting).
949 int no_of_columns
; // how many columns are we using?
950 struct text_defn
*columns
; // left and right margins for each column
951 int vertical_limit
; // the limit of the table
952 int wrap_margin
; // is the current rightmost margin able to wrap words?
955 html_table::html_table ()
956 : no_of_columns(0), columns(0), vertical_limit(0), wrap_margin(0)
960 html_table::~html_table ()
964 class html_printer
: public printer
{
970 int space_char_index
;
971 int no_of_printed_pages
;
973 enum { SBUF_SIZE
= 8192 };
974 char sbuf
[SBUF_SIZE
];
985 int output_draw_point_size
;
987 int output_line_thickness
;
989 unsigned char output_space_code
;
991 char *inside_font_style
;
997 html_table indentation
;
998 int left_margin_indent
;
999 int right_margin_indent
;
1000 int need_one_newline
;
1002 html_paragraph
*current_paragraph
;
1003 char image_name
[MAX_STRING_LENGTH
];
1006 int supress_sub_sup
;
1008 int start_region_vpos
;
1009 int start_region_hpos
;
1010 int end_region_vpos
;
1011 int end_region_hpos
;
1014 struct graphic_glob
*start_graphic
;
1015 struct text_glob
*start_text
;
1018 void set_style (const style
&);
1019 void set_space_code (unsigned char c
);
1020 void do_exec (char *, const environment
*);
1021 void do_import (char *, const environment
*);
1022 void do_def (char *, const environment
*);
1023 void do_mdef (char *, const environment
*);
1024 void do_file (char *, const environment
*);
1025 void set_line_thickness (const environment
*);
1026 void change_font (text_glob
*g
, int is_to_html
);
1027 void terminate_current_font (void);
1028 void flush_font (void);
1029 void flush_page (void);
1030 void add_char_to_sbuf (unsigned char code
);
1031 void add_to_sbuf (char code
, const char *name
);
1032 void display_word (text_glob
*g
, int is_to_html
);
1033 void html_display_word (text_glob
*g
);
1034 void troff_display_word (text_glob
*g
);
1035 void display_line (graphic_glob
*g
, int is_to_html
);
1036 void display_fill (graphic_glob
*g
);
1037 void calculate_margin (void);
1038 void traverse_page_regions (void);
1039 void dump_page (void);
1040 int is_within_region (graphic_glob
*g
);
1041 int is_within_region (text_glob
*t
);
1042 int is_less (graphic_glob
*g
, text_glob
*t
);
1043 void display_globs (int is_to_html
);
1044 void move_horizontal (text_glob
*g
, int left_margin
);
1045 void move_vertical (text_glob
*g
, paragraph_type p
);
1046 void write_html_font_face (const char *fontname
, const char *left
, const char *right
);
1047 void write_html_font_type (const char *fontname
, const char *left
, const char *right
);
1048 void html_change_font (text_glob
*g
, const char *fontname
, int size
);
1049 char *html_position_text (text_glob
*g
, int left_margin
, int right_margin
);
1050 int html_position_region (void);
1051 void troff_change_font (const char *fontname
, int size
, int font_no
);
1052 void troff_position_text (text_glob
*g
);
1053 int pretend_is_on_same_line (text_glob
*g
, int left_margin
, int right_margin
);
1054 int is_on_same_line (text_glob
*g
, int vpos
);
1055 int looks_like_subscript (text_glob
*g
);
1056 int looks_like_superscript (text_glob
*g
);
1057 int looks_like_smaller_font (text_glob
*g
);
1058 int looks_like_larger_font (text_glob
*g
);
1059 void begin_paragraph (paragraph_type p
);
1060 void begin_paragraph_no_height (paragraph_type p
);
1061 void force_begin_paragraph (void);
1062 void end_paragraph (void);
1063 void save_paragraph (void);
1064 void restore_paragraph (void);
1065 void html_newline (void);
1066 void convert_to_image (char *troff_src
, char *image_name
);
1067 void write_title (int in_head
);
1068 void find_title (void);
1069 int is_bold (text_glob
*g
);
1070 void write_header (text_glob
*g
);
1071 void determine_header_level (void);
1072 void build_header (text_glob
*g
);
1073 void make_html_indent (int indent
);
1074 int is_whole_line_bold (text_glob
*g
);
1075 int is_a_header (text_glob
*g
);
1076 int processed_header (text_glob
*g
);
1077 void make_new_image_name (void);
1078 void calculate_region_margins (region_glob
*r
);
1079 void remove_redundant_regions (void);
1080 void remove_duplicate_regions (void);
1081 void move_region_to_page (void);
1082 void calculate_region_range (graphic_glob
*r
);
1083 void flush_graphic (void);
1084 void write_string (graphic_glob
*g
, int is_to_html
);
1085 void prologue (void);
1088 void display_regions (void);
1089 int check_able_to_use_table (text_glob
*g
);
1090 int using_table_for_indent (void);
1091 int collect_columns (struct text_defn
*next_words
, struct text_defn
*next_cols
,
1092 struct text_defn
*last_words
, struct text_defn
*last_cols
,
1094 void include_into_list (struct text_defn
*line
, struct text_defn
*item
);
1095 int is_in_column (struct text_defn
*line
, struct text_defn
*item
, int max_words
);
1096 int is_column_match (struct text_defn
*match
, struct text_defn
*line1
,
1097 struct text_defn
*line2
, int max_words
);
1098 int count_columns (struct text_defn
*line
);
1099 void rewind_text_to (text_glob
*g
);
1100 int found_use_for_table (text_glob
*start
);
1101 void column_display_word (int cell
, int vert
, int left
, int right
, int next
);
1102 void start_table (void);
1103 void end_table (void);
1104 void foreach_column_include_text (text_glob
*start
);
1105 void define_cell (int i
);
1106 int column_calculate_left_margin (int left
, int right
);
1107 int column_calculate_right_margin (int left
, int right
);
1108 void display_columns (const char *word
, const char *name
, text_defn
*line
);
1109 void calculate_right (struct text_defn
*line
, int max_words
);
1110 void determine_right_most_column (struct text_defn
*line
, int max_words
);
1111 int remove_white_using_words (struct text_defn
*next_guess
, struct text_defn
*last_guess
, struct text_defn
*next_line
);
1112 void copy_line (struct text_defn
*dest
, struct text_defn
*src
);
1113 void combine_line (struct text_defn
*dest
, struct text_defn
*src
);
1114 int conflict_with_words (struct text_defn
*column_guess
, struct text_defn
*words
);
1115 void remove_entry_in_line (struct text_defn
*line
, int j
);
1116 void remove_redundant_columns (struct text_defn
*line
);
1117 void add_column_gaps (struct text_defn
*line
);
1118 int continue_searching_column (text_defn
*next_col
, text_defn
*last_col
, text_defn
*all_words
);
1119 void add_right_full_width (struct text_defn
*line
, int mingap
);
1120 int is_continueous_column (text_defn
*last_col
, text_defn
*next_line
);
1121 int is_exact_left (text_defn
*last_col
, text_defn
*next_line
);
1122 int find_column_index_in_line (text_glob
*t
, text_defn
*line
);
1123 void emit_space (text_glob
*g
, int force_space
);
1124 int is_in_middle (int left
, int right
);
1125 int check_able_to_use_center (text_glob
*g
);
1126 void write_centered_line (text_glob
*g
);
1127 int single_centered_line (text_defn
*first
, text_defn
*second
, text_glob
*g
);
1128 int determine_row_limit (text_glob
*start
, int v
);
1129 void assign_used_columns (text_glob
*start
);
1130 int find_column_index (text_glob
*t
);
1131 int large_enough_gap (text_defn
*last_col
);
1132 int is_worth_column (int left
, int right
);
1133 int is_subset_of_columns (text_defn
*a
, text_defn
*b
);
1134 void count_hits (text_defn
*col
, int no_of_columns
, int limit
);
1135 void count_right_hits (text_defn
*col
, int no_of_columns
);
1136 int calculate_min_gap (text_glob
*g
);
1137 int right_indentation (struct text_defn
*last_guess
);
1138 void calculate_percentage_width (text_glob
*start
);
1139 int able_to_steal_width (void);
1140 int need_to_steal_width (void);
1141 int can_distribute_fairly (void);
1142 void utilize_round_off (void);
1143 int will_wrap_text (int i
, text_glob
*start
);
1144 int next_line_on_left_column (int i
, text_glob
*start
);
1145 void remove_table_column (int i
);
1146 void remove_unnecessary_unused (text_glob
*start
);
1147 int is_small_table (int lines
, struct text_defn
*last_guess
,
1148 struct text_defn
*words_1
, struct text_defn
*cols_1
,
1149 struct text_defn
*words_2
, struct text_defn
*cols_2
,
1150 int *limit
, int *limit_1
);
1151 int is_column_subset (struct text_defn
*cols_1
, struct text_defn
*cols_2
);
1152 int is_appropriate_to_start_table (struct text_defn
*cols_1
, struct text_defn
*cols_2
,
1153 struct text_defn
*last_guess
);
1154 int is_a_full_width_column (void);
1155 int right_most_column (struct text_defn
*col
);
1156 int large_enough_gap_for_two (struct text_defn
*col
);
1157 void remove_zero_percentage_column (void);
1158 void translate_to_html (text_glob
*g
);
1159 int html_knows_about (char *troff
);
1160 void determine_diacritical_mark (const char *name
, const environment
*env
);
1161 int sbuf_continuation (unsigned char code
, const char *name
, const environment
*env
, int w
);
1162 char *remove_last_char_from_sbuf ();
1163 const char *check_diacritical_combination (unsigned char code
, const char *name
);
1164 int seen_backwards_escape (char *s
, int l
);
1165 int should_defer_table (int lines
, struct text_glob
*start
, struct text_defn
*cols_1
);
1166 int is_new_exact_right (struct text_defn
*last_guess
, struct text_defn
*last_cols
, struct text_defn
*next_cols
);
1167 void issue_left_paragraph (void);
1168 void adjust_margin_percentages (void);
1169 int total_percentages (void);
1170 int get_left (void);
1171 void can_loose_column (text_glob
*start
, struct text_defn
*last_guess
, int limit
);
1172 int check_lack_of_hits (struct text_defn
*next_guess
, struct text_defn
*last_guess
, text_glob
*start
, int limit
);
1179 void set_char(int i
, font
*f
, const environment
*env
, int w
, const char *name
);
1180 void draw(int code
, int *p
, int np
, const environment
*env
);
1181 void begin_page(int);
1183 void special(char *arg
, const environment
*env
);
1184 font
*make_font(const char *);
1188 html_printer::html_printer()
1189 : html(0, MAX_LINE_LENGTH
),
1190 troff(0, MAX_LINE_LENGTH
),
1191 no_of_printed_pages(0),
1193 sbuf_dmark_hpos(-1),
1198 inside_font_style(0),
1201 left_margin_indent(0),
1202 right_margin_indent(0),
1203 need_one_newline(0),
1207 supress_sub_sup(TRUE
),
1208 start_region_vpos(0),
1209 start_region_hpos(0),
1214 tempfp
= xtmpfile();
1215 html
.set_file(tempfp
);
1217 linewidth
= DEFAULT_LINEWIDTH
;
1219 fatal("horizontal resolution must be 1");
1220 if (font::vert
!= 1)
1221 fatal("vertical resolution must be 1");
1223 // should be sorted html..
1224 if (font::res
% (font::sizescale
*72) != 0)
1225 fatal("res must be a multiple of 72*sizescale");
1229 while (r
% 10 == 0) {
1234 html
.set_fixed_point(point
);
1235 space_char_index
= font::name_to_index("space");
1236 paper_length
= font::paperlength
;
1237 if (paper_length
== 0)
1238 paper_length
= 11*font::res
;
1239 page_contents
= new page
;
1241 postscript_res
= 72000;
1242 current_paragraph
= new html_paragraph(FALSE
, FALSE
, left_alignment
, 0);
1246 * add_char_to_sbuf - adds a single character to the sbuf.
1249 void html_printer::add_char_to_sbuf (unsigned char code
)
1251 if (sbuf_len
< SBUF_SIZE
) {
1252 sbuf
[sbuf_len
] = code
;
1255 fatal("need to increase SBUF_SIZE");
1260 * add_to_sbuf - adds character code or name to the sbuf.
1261 * It escapes \ with \\
1262 * We need to preserve the name of characters if they exist
1263 * because we may need to send this character to two different
1264 * devices: html and postscript.
1267 void html_printer::add_to_sbuf (char code
, const char *name
)
1271 add_char_to_sbuf('\\');
1273 add_char_to_sbuf(code
);
1278 add_char_to_sbuf('\\');
1279 add_char_to_sbuf('(');
1281 if (name
[i
] == '\\') {
1282 add_char_to_sbuf('\\');
1284 add_char_to_sbuf(name
[i
]);
1287 add_char_to_sbuf('\\');
1288 add_char_to_sbuf(')');
1292 int html_printer::sbuf_continuation (unsigned char code
, const char *name
,
1293 const environment
*env
, int w
)
1295 if ((sbuf_end_hpos
== env
->hpos
) || (sbuf_dmark_hpos
== env
->hpos
)) {
1296 name
= check_diacritical_combination(code
, name
);
1297 add_to_sbuf(code
, name
);
1298 determine_diacritical_mark(name
, env
);
1299 sbuf_end_hpos
+= w
+ sbuf_kern
;
1302 if ((sbuf_len
< SBUF_SIZE
-1) && (env
->hpos
>= sbuf_end_hpos
) &&
1303 ((sbuf_kern
== 0) || (sbuf_end_hpos
- sbuf_kern
!= env
->hpos
))) {
1305 * lets see whether a space is needed or not
1307 int space_width
= sbuf_style
.f
->get_space_width(sbuf_style
.point_size
);
1309 if (env
->hpos
-sbuf_end_hpos
< space_width
) {
1310 name
= check_diacritical_combination(code
, name
);
1311 add_to_sbuf(code
, name
);
1312 determine_diacritical_mark(name
, env
);
1313 sbuf_end_hpos
= env
->hpos
+ w
;
1316 } else if ((sbuf_len
> 0) && (sbuf_dmark_hpos
)) {
1318 * check whether the diacritical mark is on the same character
1320 int space_width
= sbuf_style
.f
->get_space_width(sbuf_style
.point_size
);
1322 if (abs(sbuf_dmark_hpos
-env
->hpos
) < space_width
) {
1323 name
= check_diacritical_combination(code
, name
);
1324 add_to_sbuf(code
, name
);
1325 determine_diacritical_mark(name
, env
);
1326 sbuf_end_hpos
= env
->hpos
+ w
;
1335 * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s
1338 int html_printer::seen_backwards_escape (char *s
, int l
)
1341 * this is tricky so it is broken into components for clarity
1342 * (we let the compiler put in all back into a complex expression)
1344 if ((l
>0) && (sbuf
[l
] == '(') && (sbuf
[l
-1] == '\\')) {
1346 * ok seen '\(' but we must now check for '\\('
1348 if ((l
>1) && (sbuf
[l
-2] == '\\')) {
1350 * escaped the escape
1362 * reverse - return reversed string.
1365 char *reverse (char *s
)
1382 * remove_last_char_from_sbuf - removes the last character from sbuf.
1385 char *html_printer::remove_last_char_from_sbuf ()
1388 static char last
[MAX_STRING_LENGTH
];
1392 if ((sbuf
[l
] == ')') && (l
>0) && (sbuf
[l
-1] == '\\')) {
1394 * found terminating escape
1399 while ((l
>0) && (! seen_backwards_escape(sbuf
, l
))) {
1400 if (sbuf
[l
] == '\\') {
1401 if (sbuf
[l
-1] == '\\') {
1414 if (seen_backwards_escape(sbuf
, l
)) {
1417 return( reverse(last
) );
1419 if ((sbuf
[l
] == '\\') && (l
>0) && (sbuf
[l
-1] == '\\')) {
1425 last
[0] = sbuf
[sbuf_len
];
1436 * check_diacriticial_combination - checks to see whether the character code
1437 * if combined with the previous diacriticial mark
1438 * forms a new character.
1441 const char *html_printer::check_diacritical_combination (unsigned char code
, const char *name
)
1443 static char troff_char
[2];
1445 if ((name
== 0) && (sbuf_dmark_hpos
>= 0)) {
1446 // last character was a diacritical mark
1447 char *last
= remove_last_char_from_sbuf();
1452 while (diacritical_table
[i
].mark
!= NULL
) {
1453 if (strcmp(diacritical_table
[i
].mark
, last
) == 0) {
1455 while ((diacritical_table
[i
].second_troff_char
[j
] != (char)0) &&
1456 (diacritical_table
[i
].second_troff_char
[j
] != code
)) {
1459 if (diacritical_table
[i
].second_troff_char
[j
] == code
) {
1460 troff_char
[0] = diacritical_table
[i
].translation
;
1461 troff_char
[1] = code
;
1462 troff_char
[2] = (char)0;
1463 return( troff_char
);
1468 add_to_sbuf(last
[0], last
);
1474 * determine_diacritical_mark - if name is a diacriticial mark the record the position.
1475 * --fixme-- is there a better way of doing this
1476 * this must be done in troff somewhere.
1479 void html_printer::determine_diacritical_mark (const char *name
, const environment
*env
)
1484 while (diacritical_table
[i
].mark
!= NULL
) {
1485 if (strcmp(name
, diacritical_table
[i
].mark
) == 0) {
1486 sbuf_dmark_hpos
= env
->hpos
;
1492 sbuf_dmark_hpos
= -1;
1496 * set_char - adds a character into the sbuf if it is a continuation with the previous
1497 * word otherwise flush the current sbuf and add character anew.
1500 void html_printer::set_char(int i
, font
*f
, const environment
*env
, int w
, const char *name
)
1502 unsigned char code
= f
->get_code(i
);
1509 style
sty(f
, env
->size
, env
->height
, env
->slant
, env
->fontno
);
1510 if (sty
.slant
!= 0) {
1511 if (sty
.slant
> 80 || sty
.slant
< -80) {
1512 error("silly slant `%1' degrees", sty
.slant
);
1516 if ((name
!= 0) && (page_contents
->is_in_graphic
)) {
1518 int r
=font::res
; // resolution of the device
1519 page_contents
->add_special_char(&sty
, (char *)name
, strlen(name
),
1520 env
->vpos
-sty
.point_size
*r
/72, env
->hpos
,
1521 env
->vpos
, env
->hpos
+w
);
1522 sbuf_end_hpos
= env
->hpos
+ w
;
1523 sbuf_start_hpos
= env
->hpos
;
1524 sbuf_vpos
= env
->vpos
;
1528 if ((sbuf_len
> 0) && (sbuf_len
< SBUF_SIZE
) && (sty
== sbuf_style
) &&
1529 (sbuf_vpos
== env
->vpos
) && (sbuf_continuation(code
, name
, env
, w
))) {
1534 add_to_sbuf(code
, name
);
1535 determine_diacritical_mark(name
, env
);
1536 sbuf_end_hpos
= env
->hpos
+ w
;
1537 sbuf_start_hpos
= env
->hpos
;
1538 sbuf_vpos
= env
->vpos
;
1546 * make_new_image_name - creates a new file name ready for a image file.
1549 void html_printer::make_new_image_name (void)
1553 if ((current_filename
== 0) ||
1554 (strcmp(current_filename
, "<standard input>") == 0) ||
1555 (strcmp(current_filename
, "-") == 0) ||
1556 (strchr(current_filename
, '/') != 0)) {
1557 sprintf(image_name
, "grohtml-%d-%ld", image_number
, (long)getpid());
1559 sprintf(image_name
, "%s-%d-%ld", current_filename
, image_number
, (long)getpid());
1564 * write_title - writes the title to this document
1567 void html_printer::write_title (int in_head
)
1569 if (title
.has_been_found
) {
1571 html
.put_string("<title>");
1572 html
.put_string(title
.text
);
1573 html
.put_string("</title>\n");
1575 title
.has_been_written
= TRUE
;
1576 html
.put_string("<h1 align=center>");
1577 html
.put_string(title
.text
);
1578 html
.put_string("</h1>\n");
1584 * get_html_translation - given the position of the character and its name
1585 * return the device encoding for such character.
1588 char *get_html_translation (font
*f
, char *name
)
1592 if ((f
== 0) || (name
== 0) || (strcmp(name
, "") == 0)) {
1595 index
= f
->name_to_index(name
);
1597 error("character `%s' not found", name
);
1600 return( (char *)f
->get_special_device_encoding(index
) );
1606 * str_translate_to_html - converts a string, str, into html text. It places
1607 * the output input buffer, buf. It truncates string, str, if
1608 * there is not enough space in buf.
1609 * It looks up the html character encoding of single characters
1610 * if, and_single, is TRUE. Characters such as < > & etc.
1613 void str_translate_to_html (font
*f
, char *buf
, int buflen
, char *str
, int len
, int and_single
)
1618 char escaped_char
[MAX_STRING_LENGTH
];
1624 if (strcmp(str
, "\\(\\\\-\\)") == 0) {
1628 while (str
[i
] != (char)0) {
1629 if ((str
[i
]=='\\') && (i
+1<len
)) {
1630 i
++; // skip the leading backslash
1631 if (str
[i
] == '(') {
1635 while ((str
[i
] != (char)0) &&
1636 (! ((str
[i
] == '\\') && (i
+1<len
) && (str
[i
+1] == ')')))) {
1637 if (str
[i
] == '\\') {
1640 escaped_char
[e
] = str
[i
];
1644 if ((str
[i
] == '\\') && (i
+1<len
) && (str
[i
+1] == ')')) {
1647 escaped_char
[e
] = (char)0;
1649 translation
= get_html_translation(f
, escaped_char
);
1651 l
= strlen(translation
);
1652 t
= max(0, min(l
, buflen
-b
));
1653 strncpy(&buf
[b
], translation
, t
);
1656 int index
=f
->name_to_index(escaped_char
);
1659 buf
[b
] = f
->get_code(index
);
1671 translation
= get_html_translation(f
, name
);
1673 l
= strlen(translation
);
1674 t
= max(0, min(l
, buflen
-b
));
1675 strncpy(&buf
[b
], translation
, t
);
1685 * do not attempt to encode single characters
1695 buf
[min(b
, buflen
)] = (char)0;
1699 * find_title - finds a title to this document, if it exists.
1702 void html_printer::find_title (void)
1706 int removed_from_head
;
1707 char buf
[MAX_STRING_LENGTH
];
1709 if ((page_number
== 1) && (guess_on
)) {
1710 if (! page_contents
->words
.is_empty()) {
1712 int end_title_hpos
= 0;
1713 int start_title_vpos
= 0;
1714 int found_title_start
= FALSE
;
1716 int start_region
=-1;
1718 if (! page_contents
->regions
.is_empty()) {
1721 page_contents
->regions
.start_from_head();
1722 r
= page_contents
->regions
.get_data();
1724 start_region
= r
->minv
;
1728 page_contents
->words
.start_from_head();
1730 t
= page_contents
->words
.get_data();
1731 removed_from_head
= FALSE
;
1732 if ((found_title_start
) && (start_region
!= -1) && (t
->maxv
>= start_region
)) {
1734 * we have just encountered the first graphic region so
1735 * we stop looking for a title.
1737 title
.has_been_found
= TRUE
;
1739 } else if (t
->is_raw_command
) {
1740 // skip raw commands
1741 page_contents
->words
.move_right(); // move onto next word
1742 } else if ((!found_title_start
) && (t
->minh
> left_margin_indent
) &&
1743 ((start_region
== -1) || (t
->maxv
< start_region
))) {
1744 start_title_vpos
= t
->minv
;
1745 end_title_hpos
= t
->minh
;
1746 str_translate_to_html(t
->text_style
.f
, buf
, MAX_STRING_LENGTH
, t
->text_string
, t
->text_length
, TRUE
);
1747 strcpy((char *)title
.text
, buf
);
1748 height
= t
->text_style
.point_size
*r
/72;
1749 found_title_start
= TRUE
;
1750 page_contents
->words
.sub_move_right();
1751 removed_from_head
= ((!page_contents
->words
.is_empty()) &&
1752 (page_contents
->words
.is_equal_to_head()));
1753 } else if (found_title_start
) {
1754 if ((t
->minv
== start_title_vpos
) ||
1755 ((!more_than_line_break(start_title_vpos
, t
->minv
, (height
*3)/2)) &&
1756 (t
->minh
> left_margin_indent
)) ||
1757 (is_bold(t
) && (t
->minh
> left_margin_indent
))) {
1758 start_title_vpos
= min(t
->minv
, start_title_vpos
);
1759 end_title_hpos
= max(t
->maxh
, end_title_hpos
);
1760 strcat(title
.text
, " ");
1761 str_translate_to_html(t
->text_style
.f
, buf
, MAX_STRING_LENGTH
, t
->text_string
, t
->text_length
, TRUE
);
1762 strcat(title
.text
, buf
);
1763 page_contents
->words
.sub_move_right();
1764 removed_from_head
= ((!page_contents
->words
.is_empty()) &&
1765 (page_contents
->words
.is_equal_to_head()));
1768 title
.has_been_found
= TRUE
;
1771 } else if (t
->minh
<= left_margin_indent
) {
1775 // move onto next word
1776 page_contents
->words
.move_right();
1778 } while ((! page_contents
->words
.is_equal_to_head()) || (removed_from_head
));
1784 * html_newline - generates a newline <br>
1787 void html_printer::html_newline (void)
1790 int height
= output_style
.point_size
*r
/72;
1792 if (current_paragraph
->in_paragraph
) {
1793 // safe to generate a pretty newline
1794 html
.put_string("<br>\n");
1796 html
.put_string("<br>");
1798 output_vpos
+= height
;
1799 issued_newline
= TRUE
;
1803 * issue_left_paragraph - emits a left paragraph together with appropriate
1804 * margin if header_indent is < left_margin_indent.
1807 void html_printer::issue_left_paragraph (void)
1809 if ((header_indent
< left_margin_indent
) && (! using_table_for_indent())) {
1810 html
.put_string("<p style=\"margin-left: ");
1811 html
.put_number(((left_margin_indent
-header_indent
)*100)/(right_margin_indent
-header_indent
));
1812 html
.put_string("%\">");
1814 html
.put_string("<p>");
1819 * force_begin_paragraph - force the begin_paragraph to be emitted.
1822 void html_printer::force_begin_paragraph (void)
1824 if (current_paragraph
->in_paragraph
&& current_paragraph
->need_paragraph
) {
1825 switch (current_paragraph
->para_type
) {
1827 case left_alignment
: issue_left_paragraph();
1829 case center_alignment
: html
.put_string("<p align=center>");
1831 default: fatal("unknown paragraph alignment type");
1833 current_paragraph
->need_paragraph
= FALSE
;
1838 * begin_paragraph - starts a new paragraph. It does nothing if a paragraph
1839 * has already been started.
1842 void html_printer::begin_paragraph (paragraph_type p
)
1844 if (! current_paragraph
->in_paragraph
) {
1846 int height
= output_style
.point_size
*r
/72;
1848 if (output_vpos
>=0) {
1849 // we leave it alone if it is set to the top of page
1850 output_vpos
+= height
;
1852 current_paragraph
->need_paragraph
= TRUE
; // delay the <p> just in case we don't actually emit text
1853 current_paragraph
->in_paragraph
= TRUE
;
1854 current_paragraph
->para_type
= p
;
1855 issued_newline
= TRUE
;
1861 * begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph
1862 * has already been started. Note it does not alter output_vpos.
1865 void html_printer::begin_paragraph_no_height (paragraph_type p
)
1867 if (! current_paragraph
->in_paragraph
) {
1868 current_paragraph
->need_paragraph
= TRUE
; // delay the <p> just in case we don't actually emit text
1869 current_paragraph
->in_paragraph
= TRUE
;
1870 current_paragraph
->para_type
= p
;
1871 issued_newline
= TRUE
;
1876 * end_paragraph - end the current paragraph. It does nothing if a paragraph
1877 * has not been started.
1880 void html_printer::end_paragraph (void)
1882 if (current_paragraph
->in_paragraph
) {
1883 // check whether we have generated any text inbetween the potential paragraph begin end
1884 if (! current_paragraph
->need_paragraph
) {
1886 int height
= output_style
.point_size
*r
/72;
1888 output_vpos
+= height
;
1889 terminate_current_font();
1890 html
.put_string("</p>\n");
1892 terminate_current_font();
1894 current_paragraph
->para_type
= left_alignment
;
1895 current_paragraph
->in_paragraph
= FALSE
;
1900 * save_paragraph - saves the current paragraph state and
1901 * creates new paragraph state.
1904 void html_printer::save_paragraph (void)
1906 if (current_paragraph
== 0) {
1907 fatal("current_paragraph is NULL");
1909 current_paragraph
= new html_paragraph(current_paragraph
->in_paragraph
,
1910 current_paragraph
->need_paragraph
,
1911 current_paragraph
->para_type
,
1913 terminate_current_font();
1917 * restore_paragraph - restores the previous paragraph state.
1920 void html_printer::restore_paragraph (void)
1922 html_paragraph
*old
= current_paragraph
;
1924 current_paragraph
= current_paragraph
->previous
;
1929 * calculate_margin - runs through the words and graphics globs
1930 * and finds the start of the left most margin.
1933 void html_printer::calculate_margin (void)
1941 right_margin_indent
= 0;
1943 if (! page_contents
->words
.is_empty()) {
1945 // firstly check the words to determine the right margin
1947 page_contents
->words
.start_from_head();
1949 w
= page_contents
->words
.get_data();
1950 if ((w
->maxh
>= 0) && (w
->maxh
> right_margin_indent
)) {
1951 right_margin_indent
= w
->maxh
;
1953 if (right_margin_indent
== 758) stop();
1956 page_contents
->words
.move_right();
1957 } while (! page_contents
->words
.is_equal_to_head());
1961 * only examine graphics if no words present
1963 if (! page_contents
->lines
.is_empty()) {
1964 // now check for diagrams for right margin
1965 page_contents
->lines
.start_from_head();
1967 g
= page_contents
->lines
.get_data();
1968 if ((g
->maxh
>= 0) && (g
->maxh
> right_margin_indent
)) {
1969 right_margin_indent
= g
->maxh
;
1971 if (right_margin_indent
== 950) stop();
1974 page_contents
->lines
.move_right();
1975 } while (! page_contents
->lines
.is_equal_to_head());
1979 * now we know the right margin lets do the same to find left margin
1982 if (header_indent
== -1) {
1983 header_indent
= right_margin_indent
;
1985 left_margin_indent
= right_margin_indent
;
1987 if (! page_contents
->words
.is_empty()) {
1989 w
= page_contents
->words
.get_data();
1990 if ((w
->minh
>= 0) && (w
->minh
< left_margin_indent
)) {
1991 if (! is_a_header(w
) && (! w
->is_raw_command
)) {
1992 left_margin_indent
= w
->minh
;
1995 page_contents
->words
.move_right();
1996 } while (! page_contents
->words
.is_equal_to_head());
2000 * only examine graphic for margins if text yields nothing
2003 if (! page_contents
->lines
.is_empty()) {
2004 // now check for diagrams
2005 page_contents
->lines
.start_from_head();
2007 g
= page_contents
->lines
.get_data();
2008 if ((g
->minh
>= 0) && (g
->minh
< left_margin_indent
)) {
2009 left_margin_indent
= g
->minh
;
2011 page_contents
->lines
.move_right();
2012 } while (! page_contents
->lines
.is_equal_to_head());
2018 * calculate_region - runs through the graphics globs and text globs
2019 * and ensures that all graphic routines
2020 * are defined by the region lists.
2021 * This then allows us to easily
2022 * determine the range of vertical and
2023 * horizontal boundaries for pictures,
2028 void page::calculate_region (void)
2032 if (! lines
.is_empty()) {
2033 lines
.start_from_head();
2035 g
= lines
.get_data();
2036 if (! is_in_region(g
)) {
2037 if (! can_grow_region(g
)) {
2042 } while (! lines
.is_equal_to_head());
2047 * remove_redundant_regions - runs through the regions and ensures that
2048 * all are needed. This is required as
2049 * a picture may be empty, or EQ EN pair
2053 void html_printer::remove_redundant_regions (void)
2057 // firstly run through the region making sure that all are needed
2058 // ie all contain a line or word
2059 if (! page_contents
->regions
.is_empty()) {
2060 page_contents
->regions
.start_from_tail();
2062 r
= page_contents
->regions
.get_data();
2063 calculate_region_margins(r
);
2064 if (page_contents
->has_line(r
) || page_contents
->has_word(r
)) {
2065 page_contents
->regions
.move_right();
2067 page_contents
->regions
.sub_move_right();
2069 } while ((! page_contents
->regions
.is_empty()) &&
2070 (! page_contents
->regions
.is_equal_to_tail()));
2074 void html_printer::display_regions (void)
2076 if (debug_table_on
) {
2079 fprintf(stderr
, "==========s t a r t===========\n");
2080 if (! page_contents
->regions
.is_empty()) {
2081 page_contents
->regions
.start_from_head();
2083 r
= page_contents
->regions
.get_data();
2084 fprintf(stderr
, "region minv %d maxv %d\n", r
->minv
, r
->maxv
);
2085 page_contents
->regions
.move_right();
2086 } while (! page_contents
->regions
.is_equal_to_head());
2088 fprintf(stderr
, "============e n d=============\n");
2094 * remove_duplicate_regions - runs through the regions and ensures that
2095 * no duplicates exist.
2098 void html_printer::remove_duplicate_regions (void)
2103 if (! page_contents
->regions
.is_empty()) {
2104 page_contents
->regions
.start_from_head();
2105 l
= page_contents
->regions
.get_data();
2106 page_contents
->regions
.move_right();
2107 r
= page_contents
->regions
.get_data();
2110 r
= page_contents
->regions
.get_data();
2111 // we have a legit region so we check for an intersection
2112 if (is_intersection(r
->minv
, r
->minv
, l
->minv
, l
->maxv
) &&
2113 is_intersection(r
->minh
, r
->maxh
, l
->minh
, l
->maxh
)) {
2114 l
->minv
= min(r
->minv
, l
->minv
);
2115 l
->maxv
= max(r
->maxv
, l
->maxv
);
2116 l
->minh
= min(r
->minh
, l
->minh
);
2117 l
->maxh
= max(r
->maxh
, l
->maxh
);
2118 calculate_region_margins(l
);
2119 page_contents
->regions
.sub_move_right();
2122 page_contents
->regions
.move_right();
2124 } while ((! page_contents
->regions
.is_empty()) &&
2125 (! page_contents
->regions
.is_equal_to_head()));
2130 int page::has_line (region_glob
*r
)
2134 if (! lines
.is_empty()) {
2135 lines
.start_from_head();
2137 g
= lines
.get_data();
2138 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
) &&
2139 is_subsection(g
->minh
, g
->maxh
, r
->minh
, r
->maxh
)) {
2143 } while (! lines
.is_equal_to_head());
2149 int page::has_word (region_glob
*r
)
2153 if (! words
.is_empty()) {
2154 words
.start_from_head();
2156 g
= words
.get_data();
2157 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
) &&
2158 is_subsection(g
->minh
, g
->maxh
, r
->minh
, r
->maxh
)) {
2162 } while (! words
.is_equal_to_head());
2168 void html_printer::calculate_region_margins (region_glob
*r
)
2173 r
->minh
= right_margin_indent
;
2174 r
->maxh
= left_margin_indent
;
2176 if (! page_contents
->lines
.is_empty()) {
2177 page_contents
->lines
.start_from_head();
2179 g
= page_contents
->lines
.get_data();
2180 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
)) {
2181 r
->minh
= min(r
->minh
, g
->minh
);
2182 r
->maxh
= max(r
->maxh
, g
->maxh
);
2184 page_contents
->lines
.move_right();
2185 } while (! page_contents
->lines
.is_equal_to_head());
2187 if (! page_contents
->words
.is_empty()) {
2188 page_contents
->words
.start_from_head();
2190 w
= page_contents
->words
.get_data();
2191 if (is_subsection(w
->minv
, w
->maxv
, r
->minv
, r
->maxv
)) {
2192 r
->minh
= min(r
->minh
, w
->minh
);
2193 r
->maxh
= max(r
->maxh
, w
->maxh
);
2195 page_contents
->words
.move_right();
2196 } while (! page_contents
->words
.is_equal_to_head());
2201 int page::is_in_region (graphic_glob
*g
)
2205 if (! regions
.is_empty()) {
2206 regions
.start_from_head();
2208 r
= regions
.get_data();
2209 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
) &&
2210 is_subsection(g
->minh
, g
->maxh
, r
->minh
, r
->maxh
)) {
2213 regions
.move_right();
2214 } while (! regions
.is_equal_to_head());
2221 * no_raw_commands - returns TRUE if no html raw commands exist between
2225 int page::no_raw_commands (int minv
, int maxv
)
2229 if (! words
.is_empty()) {
2230 words
.start_from_head();
2232 g
= words
.get_data();
2233 if ((g
->is_raw_command
) && (g
->is_html_command
) &&
2234 (is_intersection(g
->minv
, g
->maxv
, minv
, maxv
))) {
2238 } while (! words
.is_equal_to_head());
2244 * can_grow_region - returns TRUE if a region exists which can be extended
2245 * to include graphic_glob *g. The region is extended.
2248 int page::can_grow_region (graphic_glob
*g
)
2251 int quarter_inch
=font::res
/4;
2253 if (! regions
.is_empty()) {
2254 regions
.start_from_head();
2256 r
= regions
.get_data();
2257 // must prevent grohtml from growing a region through a html raw command
2258 if (is_intersection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
+quarter_inch
) &&
2259 (no_raw_commands(r
->minv
, r
->maxv
+quarter_inch
))) {
2260 #if defined(DEBUGGING)
2262 printf("r minh=%d minv=%d maxh=%d maxv=%d\n",
2263 r
->minh
, r
->minv
, r
->maxh
, r
->maxv
);
2264 printf("g minh=%d minv=%d maxh=%d maxv=%d\n",
2265 g
->minh
, g
->minv
, g
->maxh
, g
->maxv
);
2267 r
->minv
= min(r
->minv
, g
->minv
);
2268 r
->maxv
= max(r
->maxv
, g
->maxv
);
2269 r
->minh
= min(r
->minh
, g
->minh
);
2270 r
->maxh
= max(r
->maxh
, g
->maxh
);
2271 #if defined(DEBUGGING)
2272 printf(" r minh=%d minv=%d maxh=%d maxv=%d\n",
2273 r
->minh
, r
->minv
, r
->maxh
, r
->maxv
);
2277 regions
.move_right();
2278 } while (! regions
.is_equal_to_head());
2285 * make_new_region - creates a new region to contain, g.
2288 void page::make_new_region (graphic_glob
*g
)
2290 region_glob
*r
=new region_glob
;
2300 void html_printer::dump_page(void)
2304 printf("\n\ndebugging start\n");
2305 page_contents
->words
.start_from_head();
2307 g
= page_contents
->words
.get_data();
2308 printf("%s ", g
->text_string
);
2309 page_contents
->words
.move_right();
2310 } while (! page_contents
->words
.is_equal_to_head());
2311 printf("\ndebugging end\n\n");
2316 * traverse_page_regions - runs through the regions in current_page
2317 * and generate html for text, and troff output
2321 void html_printer::traverse_page_regions (void)
2325 start_region_vpos
= 0;
2326 start_region_hpos
= 0;
2327 end_region_vpos
= -1;
2328 end_region_hpos
= -1;
2330 if (! page_contents
->regions
.is_empty()) {
2331 page_contents
->regions
.start_from_head();
2333 r
= page_contents
->regions
.get_data();
2335 end_region_vpos
= r
->minv
-1;
2337 end_region_vpos
= 0;
2339 end_region_hpos
= -1;
2340 display_globs(TRUE
);
2341 calculate_region_margins(r
);
2342 start_region_vpos
= end_region_vpos
;
2343 end_region_vpos
= r
->maxv
;
2344 start_region_hpos
= r
->minh
;
2345 end_region_hpos
= r
->maxh
;
2346 display_globs(FALSE
);
2347 start_region_vpos
= end_region_vpos
+1;
2348 start_region_hpos
= 0;
2349 page_contents
->regions
.move_right();
2350 } while (! page_contents
->regions
.is_equal_to_head());
2351 start_region_vpos
= end_region_vpos
+1;
2352 start_region_hpos
= 0;
2353 end_region_vpos
= -1;
2354 end_region_hpos
= -1;
2356 display_globs(TRUE
);
2359 int html_printer::is_within_region (text_glob
*t
)
2363 if (start_region_hpos
== -1) {
2366 hs
= start_region_hpos
;
2368 if (end_region_vpos
== -1) {
2371 ve
= end_region_vpos
;
2373 if (end_region_hpos
== -1) {
2376 he
= end_region_hpos
;
2378 return( is_subsection(t
->minv
, t
->maxv
, start_region_vpos
, ve
) &&
2379 is_subsection(t
->minh
, t
->maxh
, hs
, he
) );
2382 int html_printer::is_within_region (graphic_glob
*g
)
2386 if (start_region_hpos
== -1) {
2389 hs
= start_region_hpos
;
2391 if (end_region_vpos
== -1) {
2394 ve
= end_region_vpos
;
2396 if (end_region_hpos
== -1) {
2399 he
= end_region_hpos
;
2401 return( is_subsection(g
->minv
, g
->maxv
, start_region_vpos
, ve
) &&
2402 is_subsection(g
->minh
, g
->maxh
, hs
, he
) );
2405 int html_printer::is_less (graphic_glob
*g
, text_glob
*t
)
2407 return( (g
->minv
< t
->minv
) || ((g
->minv
== t
->minv
) && (g
->minh
< t
->minh
)) );
2410 void html_printer::convert_to_image (char *troff_src
, char *image_name
)
2413 char *ps_src
= mktemp(xtmptemplate("-ps-"));
2415 sprintf(buffer
, "grops %s > %s\n", troff_src
, ps_src
);
2417 fprintf(stderr
, buffer
);
2421 if (image_type
== gif
) {
2423 "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s - | ppmquant 256 2> /dev/null | ppmtogif 2> /dev/null > %s.gif \n",
2425 (end_region_hpos
-start_region_hpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2426 (end_region_vpos
-start_region_vpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2427 ps_src
, image_name
);
2430 "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s - 2> /dev/null > %s.png \n",
2433 (end_region_hpos
-start_region_hpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2434 (end_region_vpos
-start_region_vpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2435 ps_src
, image_name
);
2438 "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s.ps - > %s.pnm ; pnmtopng -transparent white %s.pnm > %s.png \n",
2441 (end_region_hpos
-start_region_hpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2442 (end_region_vpos
-start_region_vpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2443 name
, name
, name
, image_name
);
2447 fprintf(stderr
, "%s", buffer
);
2454 void html_printer::prologue (void)
2456 troff
.put_string("x T ps\nx res ");
2457 troff
.put_number(postscript_res
);
2458 troff
.put_string(" 1 1\nx init\np1\n");
2461 void html_printer::display_globs (int is_to_html
)
2467 int something
=FALSE
;
2468 int is_center
=FALSE
;
2473 is_center
= html_position_region();
2474 make_new_image_name();
2475 f
= xtmpfile(&troff_src
, "-troff-", FALSE
);
2480 if (! page_contents
->words
.is_empty()) {
2481 page_contents
->words
.start_from_head();
2482 t
= page_contents
->words
.get_data();
2485 if (! page_contents
->lines
.is_empty()) {
2486 page_contents
->lines
.start_from_head();
2487 g
= page_contents
->lines
.get_data();
2492 if ((t
!= 0) && (strcmp(t
->text_string
, "(1.a)") == 0)) {
2496 if ((t
== 0) && (g
!= 0)) {
2497 if (is_within_region(g
)) {
2499 display_line(g
, is_to_html
);
2501 if (page_contents
->lines
.is_empty() || page_contents
->lines
.is_equal_to_tail()) {
2504 g
= page_contents
->lines
.move_right_get_data();
2506 } else if ((g
== 0) && (t
!= 0)) {
2507 if (is_within_region(t
)) {
2508 display_word(t
, is_to_html
);
2511 if (page_contents
->words
.is_empty() || page_contents
->words
.is_equal_to_tail()) {
2514 t
= page_contents
->words
.move_right_get_data();
2517 if ((g
== 0) || (t
== 0)) {
2518 // hmm nothing to print out...
2519 } else if (is_less(g
, t
)) {
2520 if (is_within_region(g
)) {
2521 display_line(g
, is_to_html
);
2524 if (page_contents
->lines
.is_empty() || page_contents
->lines
.is_equal_to_tail()) {
2527 g
= page_contents
->lines
.move_right_get_data();
2530 if (is_within_region(t
)) {
2531 display_word(t
, is_to_html
);
2534 if (page_contents
->words
.is_empty() || page_contents
->words
.is_equal_to_tail()) {
2537 t
= page_contents
->words
.move_right_get_data();
2541 } while ((t
!= 0) || (g
!= 0));
2543 if ((! is_to_html
) && (f
!= 0)) {
2544 fclose(troff
.get_file());
2546 convert_to_image(troff_src
, image_name
);
2550 begin_paragraph(center_alignment
);
2551 force_begin_paragraph();
2553 html
.put_string("<img src=\"");
2554 html
.put_string(image_name
);
2555 if (image_type
== gif
) {
2556 html
.put_string(".gif\"");
2558 html
.put_string(".png\"");
2560 html
.put_string(">\n");
2566 output_vpos
= end_region_vpos
;
2568 need_one_newline
= FALSE
;
2575 void html_printer::flush_page (void)
2579 output_hpos
= get_left();
2580 supress_sub_sup
= TRUE
;
2584 html
.begin_comment("left margin: ").comment_arg(itoa(left_margin_indent
)).end_comment();;
2585 html
.begin_comment("right margin: ").comment_arg(itoa(right_margin_indent
)).end_comment();;
2586 remove_redundant_regions();
2587 page_contents
->calculate_region();
2588 remove_duplicate_regions();
2590 supress_sub_sup
= TRUE
;
2591 traverse_page_regions();
2592 terminate_current_font();
2593 if (need_one_newline
) {
2598 // move onto a new page
2599 delete page_contents
;
2600 page_contents
= new page
;
2603 static int convertSizeToHTML (int size
)
2607 } else if (size
< 8) {
2609 } else if (size
< 10) {
2611 } else if (size
< 12) {
2613 } else if (size
< 14) {
2615 } else if (size
< 16) {
2617 } else if (size
< 18) {
2625 void html_printer::write_html_font_face (const char *fontname
, const char *left
, const char *right
)
2627 switch (fontname
[0]) {
2629 case 'C': html
.put_string(left
) ; html
.put_string("tt"); html
.put_string(right
);
2638 void html_printer::write_html_font_type (const char *fontname
, const char *left
, const char *right
)
2640 if (strcmp(&fontname
[1], "B") == 0) {
2641 html
.put_string(left
) ; html
.put_string("B"); html
.put_string(right
);
2642 } else if (strcmp(&fontname
[1], "I") == 0) {
2643 html
.put_string(left
) ; html
.put_string("I"); html
.put_string(right
);
2644 } else if (strcmp(&fontname
[1], "BI") == 0) {
2645 html
.put_string(left
) ; html
.put_string("EM"); html
.put_string(right
);
2650 void html_printer::html_change_font (text_glob
*g
, const char *fontname
, int size
)
2654 if (output_style
.f
!= 0) {
2655 const char *oldfontname
= output_style
.f
->get_name();
2657 // firstly terminate the current font face and type
2658 if ((oldfontname
!= 0) && (oldfontname
!= fontname
)) {
2659 write_html_font_face(oldfontname
, "</", ">");
2660 write_html_font_type(oldfontname
, "</", ">");
2664 if ((output_style
.point_size
!= size
) && (output_style
.point_size
!= 0)) {
2665 // shutdown the previous font size
2666 html
.put_string("</font>");
2669 if ((output_style
.point_size
!= size
) && (size
!= 0)) {
2670 // now emit the size if it has changed
2671 sprintf(buffer
, "<font size=%d>", convertSizeToHTML(size
));
2672 html
.put_string(buffer
);
2673 output_style
.point_size
= size
; // and remember the size
2675 output_style
.f
= 0; // no style at present
2676 output_style
.point_size
= size
; // remember current font size
2678 if (fontname
!= 0) {
2679 if (! g
->is_raw_command
) {
2680 // now emit the new font
2681 write_html_font_face(fontname
, "<", ">");
2683 // now emit the new font type
2684 write_html_font_type(fontname
, "<", ">");
2686 output_style
= g
->text_style
; // remember style for next time
2692 void html_printer::change_font (text_glob
*g
, int is_to_html
)
2695 if (output_style
!= g
->text_style
) {
2696 const char *fontname
=0;
2699 if (g
->text_style
.f
!= 0) {
2700 fontname
= g
->text_style
.f
->get_name();
2701 size
= (font::res
/(72*font::sizescale
))*g
->text_style
.point_size
;
2703 html_change_font(g
, fontname
, size
);
2707 if (output_style
!= g
->text_style
) {
2708 if (g
->text_style
.f
!= 0) {
2709 const char *fontname
= g
->text_style
.f
->get_name();
2710 int size
= (font::res
/(72*font::sizescale
))*g
->text_style
.point_size
;
2712 if (fontname
== 0) {
2713 fatal("no internalname specified for font");
2716 troff_change_font(fontname
, size
, g
->text_style
.font_no
);
2717 output_style
= g
->text_style
; // remember style for next time
2724 * is_bold - returns TRUE if the text inside, g, is using a bold face.
2725 * It returns FALSE is g contains a raw html command, even if this uses
2729 int html_printer::is_bold (text_glob
*g
)
2731 if (g
->text_style
.f
== 0) {
2734 } else if (g
->is_raw_command
) {
2737 const char *fontname
= g
->text_style
.f
->get_name();
2739 if (strlen(fontname
) >= 2) {
2740 return( fontname
[1] == 'B' );
2747 void html_printer::terminate_current_font (void)
2751 // we create a dummy glob just so we can tell html_change_font not to start up
2753 g
.is_raw_command
= TRUE
;
2754 html_change_font(&g
, 0, 0);
2757 void html_printer::write_header (text_glob
*g
)
2759 if (strlen(header
.header_buffer
) > 0) {
2760 if (header
.header_level
> 7) {
2761 header
.header_level
= 7;
2764 if (cutoff_heading
+2 > header
.header_level
) {
2765 // firstly we must terminate any font and type faces
2766 terminate_current_font();
2769 // secondly we generate a tag
2770 html
.put_string("<a name=\"");
2771 html
.put_string(header
.header_buffer
);
2772 html
.put_string("\"></a>");
2773 // now we save the header so we can issue a list of link
2776 header
.no_of_headings
++;
2778 text_glob
*h
=new text_glob(&st
,
2779 header
.headings
.add_string(header
.header_buffer
, strlen(header
.header_buffer
)),
2780 strlen(header
.header_buffer
),
2781 header
.no_of_headings
, header
.header_level
,
2782 header
.no_of_headings
, header
.header_level
,
2784 header
.headers
.add(h
); // and add this header to the header list
2786 terminate_current_font();
2790 // we adjust the margin if necessary
2792 if (g
->minh
< left_margin_indent
) {
2793 header_indent
= g
->minh
;
2796 // and now we issue the real header
2797 html
.put_string("<h");
2798 html
.put_number(header
.header_level
);
2799 html
.put_string(">");
2800 html
.put_string(header
.header_buffer
);
2801 html
.put_string("</h");
2802 html
.put_number(header
.header_level
);
2803 html
.put_string(">");
2805 need_one_newline
= FALSE
;
2806 begin_paragraph(left_alignment
);
2807 header
.written_header
= TRUE
;
2812 * translate_str_to_html - translates a string, str, into html representation.
2813 * len indicates the string length.
2816 void translate_str_to_html (font
*f
, char *str
, int len
)
2818 char buf
[MAX_STRING_LENGTH
];
2820 str_translate_to_html(f
, buf
, MAX_STRING_LENGTH
, str
, len
, TRUE
);
2821 strncpy(str
, buf
, max(len
, strlen(buf
)+1));
2825 * write_headings - emits a list of links for the headings in this document
2828 void header_desc::write_headings (FILE *f
)
2832 if (! headers
.is_empty()) {
2833 headers
.start_from_head();
2835 g
= headers
.get_data();
2836 fprintf(f
, "<a href=\"#%s\">%s</a><br>\n", g
->text_string
, g
->text_string
);
2837 headers
.move_right();
2838 } while (! headers
.is_equal_to_head());
2842 void html_printer::determine_header_level (void)
2845 int l
=strlen(header
.header_buffer
);
2848 for (i
=0; ((i
<l
) && ((header
.header_buffer
[i
] == '.') || is_digit(header
.header_buffer
[i
]))) ; i
++) {
2849 if (header
.header_buffer
[i
] == '.') {
2854 header
.header_level
= stops
;
2859 void html_printer::build_header (text_glob
*g
)
2863 char buf
[MAX_STRING_LENGTH
];
2865 strcpy(header
.header_buffer
, "");
2868 current_vpos
= g
->minv
;
2869 str_translate_to_html(g
->text_style
.f
, buf
, MAX_STRING_LENGTH
, g
->text_string
, g
->text_length
, TRUE
);
2870 strcat(header
.header_buffer
, (char *)buf
);
2871 page_contents
->words
.move_right();
2872 g
= page_contents
->words
.get_data();
2873 if (g
->minv
== current_vpos
) {
2874 strcat(header
.header_buffer
, " ");
2876 } while ((! page_contents
->words
.is_equal_to_head()) &&
2877 ((g
->minv
== current_vpos
) || (l
->maxh
== right_margin_indent
)));
2879 determine_header_level();
2880 // finally set the output to neutral for after the header
2882 g
= page_contents
->words
.get_data();
2883 output_vpos
= g
->minv
; // set output_vpos to the next line since
2884 output_hpos
= left_margin_indent
; // html header forces a newline anyway
2885 page_contents
->words
.move_left(); // so that next time we use old g
2887 need_one_newline
= FALSE
;
2892 * is_whole_line_bold - returns TRUE if the whole line is bold.
2895 int html_printer::is_whole_line_bold (text_glob
*g
)
2898 int current_vpos
=g
->minv
;
2902 page_contents
->words
.move_right();
2903 n
= page_contents
->words
.get_data();
2905 while (page_contents
->words
.get_data() != g
) {
2906 page_contents
->words
.move_left();
2910 } while ((! page_contents
->words
.is_equal_to_head()) && (is_on_same_line(n
, current_vpos
)));
2911 // was (n->minv == current_vpos)
2912 while (page_contents
->words
.get_data() != g
) {
2913 page_contents
->words
.move_left();
2920 * is_a_header - returns TRUE if the whole sequence of contineous lines are bold.
2921 * It checks to see whether a line is likely to be contineous and
2922 * then checks that all words are bold.
2925 int html_printer::is_a_header (text_glob
*g
)
2933 current_vpos
= n
->minv
;
2935 page_contents
->words
.move_right();
2936 n
= page_contents
->words
.get_data();
2938 while (page_contents
->words
.get_data() != g
) {
2939 page_contents
->words
.move_left();
2943 } while ((! page_contents
->words
.is_equal_to_head()) &&
2944 ((n
->minv
== current_vpos
) || (l
->maxh
== right_margin_indent
)));
2945 while (page_contents
->words
.get_data() != g
) {
2946 page_contents
->words
.move_left();
2952 int html_printer::processed_header (text_glob
*g
)
2954 if ((guess_on
) && (g
->minh
<= left_margin_indent
) && (! using_table_for_indent()) &&
2964 int is_punctuation (char *s
, int length
)
2966 return( (length
== 1) &&
2967 ((s
[0] == '(') || (s
[0] == ')') || (s
[0] == '!') || (s
[0] == '.') || (s
[0] == '[') ||
2968 (s
[0] == ']') || (s
[0] == '?') || (s
[0] == ',') || (s
[0] == ';') || (s
[0] == ':') ||
2969 (s
[0] == '@') || (s
[0] == '#') || (s
[0] == '$') || (s
[0] == '%') || (s
[0] == '^') ||
2970 (s
[0] == '&') || (s
[0] == '*') || (s
[0] == '+') || (s
[0] == '-') || (s
[0] == '=') ||
2971 (s
[0] == '{') || (s
[0] == '}') || (s
[0] == '|') || (s
[0] == '\"') || (s
[0] == '\''))
2976 * move_horizontal - moves right into the position, g->minh.
2979 void html_printer::move_horizontal (text_glob
*g
, int left_margin
)
2981 if (g
->text_style
.f
!= 0) {
2982 int w
= g
->text_style
.f
->get_space_width(g
->text_style
.point_size
);
2985 fatal("space width is zero");
2987 if ((output_hpos
== left_margin
) && (g
->minh
> output_hpos
)) {
2988 make_html_indent(g
->minh
-output_hpos
);
2990 emit_space(g
, FALSE
);
2992 output_hpos
= g
->maxh
;
2993 output_vpos
= g
->minv
;
2995 change_font(g
, TRUE
);
3000 * looks_like_subscript - returns TRUE if, g, looks like a subscript.
3003 int html_printer::looks_like_subscript (text_glob
*g
)
3006 int height
= output_style
.point_size
*r
/72;
3008 /* was return( ((output_vpos < g->minv) && (output_style.point_size != 0) &&
3009 * (output_style.point_size > g->text_style.point_size)) );
3012 return( (output_style
.point_size
!= 0) && (! supress_sub_sup
) && (output_vpos
+height
< g
->maxv
) );
3016 * looks_like_superscript - returns TRUE if, g, looks like a superscript.
3019 int html_printer::looks_like_superscript (text_glob
*g
)
3022 int height
= output_style
.point_size
*r
/72;
3025 * return(((output_vpos > g->minv) && (output_style.point_size != 0) &&
3026 * (output_style.point_size > g->text_style.point_size)));
3029 return( (output_style
.point_size
!= 0) && (! supress_sub_sup
) && (output_vpos
+height
> g
->maxv
) );
3033 * looks_like_larger_font - returns TRUE if, g, can be treated as a larger font.
3034 * g needs to be on the same line
3037 int html_printer::looks_like_larger_font (text_glob
*g
)
3040 int height
= output_style
.point_size
*r
/72;
3042 return( (output_vpos
+height
== g
->maxv
) && (output_style
.point_size
!= 0) &&
3043 (convertSizeToHTML(g
->text_style
.point_size
)+1 == convertSizeToHTML(output_style
.point_size
)) );
3047 * looks_like_smaller_font - returns TRUE if, g, can be treated as a smaller font.
3048 * g needs to be on the same line
3051 int html_printer::looks_like_smaller_font (text_glob
*g
)
3054 int height
= output_style
.point_size
*r
/72;
3056 return( (output_vpos
+height
== g
->maxv
) && (output_style
.point_size
!= 0) &&
3057 (convertSizeToHTML(g
->text_style
.point_size
) == convertSizeToHTML(output_style
.point_size
)+1) );
3061 * pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob.
3062 * Note that it believes a single word spanning the left..right as being
3063 * on a different line.
3066 int html_printer::pretend_is_on_same_line (text_glob
*g
, int left_margin
, int right_margin
)
3068 return( auto_on
&& (right_margin
== output_hpos
) && (left_margin
== g
->minh
) &&
3069 (right_margin
!= g
->maxh
) && ((! is_whole_line_bold(g
)) || (g
->text_style
.f
== output_style
.f
)) &&
3070 (! (using_table_for_indent()) || (indentation
.wrap_margin
)) );
3073 int html_printer::is_on_same_line (text_glob
*g
, int vpos
)
3076 if (g
->is_html_command
) {
3082 (is_intersection(vpos
, vpos
+g
->text_style
.point_size
*font::res
/72-1, g
->minv
, g
->maxv
))
3088 * make_html_indent - creates a relative indentation.
3091 void html_printer::make_html_indent (int indent
)
3094 html
.put_string("<span style=\" text-indent: ");
3095 html
.put_number((indent
*100)/(right_margin_indent
-get_left()));
3096 html
.put_string("%;\"></span>");
3101 * using_table_for_indent - returns TRUE if we currently using a table for indentation
3105 int html_printer::using_table_for_indent (void)
3107 return( indentation
.no_of_columns
!= 0 );
3111 * calculate_min_gap - returns the minimum gap by which we deduce columns.
3112 * This is a rough heuristic.
3115 int html_printer::calculate_min_gap (text_glob
*g
)
3119 while ((t
->is_raw_command
) && (! page_contents
->words
.is_equal_to_tail()) &&
3120 ((t
->minv
< end_region_vpos
) || (end_region_vpos
< 0))) {
3121 page_contents
->words
.move_right();
3122 t
=page_contents
->words
.get_data();
3125 if (t
->is_raw_command
) {
3126 return( font::res
* 10 ); // impossibly large gap width
3128 return( t
->text_style
.f
->get_space_width(t
->text_style
.point_size
)*GAP_SPACES
);
3133 * collect_columns - place html text in a column and return the vertical limit reached.
3136 int html_printer::collect_columns (struct text_defn
*next_words
,
3137 struct text_defn
*next_cols
,
3138 struct text_defn
*last_words
,
3139 struct text_defn
*last_cols
,
3142 text_glob
*start
= page_contents
->words
.get_data();
3143 text_glob
*t
= start
;
3144 int upper_limit
= 0;
3147 * initialize cols and words
3149 next_words
[0].left
= 0;
3150 next_words
[0].right
= 0;
3151 next_cols
[0].left
= 0;
3152 next_cols
[0].right
= 0;
3155 * if we have not reached the end collect the words on the current line
3158 int graphic_limit
= end_region_vpos
;
3160 if (is_whole_line_bold(t
) && (t
->minh
<= left_margin_indent
)) {
3162 * found header therefore terminate indentation table.
3163 * Return a negative number so we know a header has
3164 * stopped the column
3166 upper_limit
= -t
->minv
;
3168 int i
=0; // is the index into next_cols
3169 int j
=0; // is the column index for last_cols
3170 int k
=0; // is the index into next_words
3171 int l
=0; // is the index into next_words
3173 int mingap
=calculate_min_gap(start
);
3176 * while words on the same line record them and any significant gaps
3178 while ((t
!= 0) && (is_on_same_line(t
, start
->minv
) && (i
<max_words
)) &&
3179 ((graphic_limit
== -1) || (graphic_limit
> t
->minv
))) {
3182 * now find column index from the last line which corresponds to, t.
3184 j
= find_column_index_in_line(t
, last_cols
);
3187 * now find word index from the last line which corresponds to, t.
3189 l
= find_column_index_in_line(t
, last_words
);
3192 * Note t->minh might equal t->maxh when we are passing a special device character via \X
3193 * we currently ignore this when considering tables
3195 * if we have found a significant gap then record it
3197 if (((t
->minh
- prevh
>= mingap
) ||
3198 ((last_cols
!= 0) && (last_cols
[j
].left
!= 0) && (t
->minh
== last_cols
[j
].left
))) &&
3199 (t
->minh
!= t
->maxh
)) {
3200 next_cols
[i
].left
= t
->minh
;
3201 next_cols
[i
].right
= t
->maxh
;
3204 * terminate the array
3207 next_cols
[i
].left
= 0;
3208 next_cols
[i
].right
= 0;
3212 * move previous right hand column to align with, t.
3215 if (t
->minh
> next_cols
[i
-1].left
) {
3217 * a simple precaution in case we get globs which are technically on the same line
3218 * (sadly this does occur sometimes - maybe we should be stricter with is_on_same_line)
3221 next_cols
[i
-1].right
= max(next_cols
[i
-1].right
, t
->maxh
);
3225 * remember to record the individual words
3227 next_words
[k
].left
= t
->minh
;
3228 next_words
[k
].right
= t
->maxh
;
3232 * and record the vertical upper limit
3234 upper_limit
= max(t
->minv
, upper_limit
);
3237 * and update prevh - used to detect a when a different line is seen
3242 * get next word into, t, which equals 0, if no word is found
3244 page_contents
->words
.move_right();
3245 t
= page_contents
->words
.get_data();
3246 if (page_contents
->words
.is_equal_to_head()) {
3252 * and terminate the next_words array
3256 next_words
[k
].left
= 0;
3257 next_words
[k
].right
= 0;
3261 * consistency check, next_cols, after removing redundant colums.
3264 remove_redundant_columns(next_cols
);
3267 for (k
=0; k
<count_columns(next_cols
); k
++) {
3268 if (next_cols
[k
].left
> next_cols
[k
].right
) {
3269 fprintf(stderr
, "left > right\n"); fflush(stderr
);
3271 fatal("next_cols has messed up columns");
3273 if ((k
>0) && (k
+1<count_columns(next_cols
)) && (next_cols
[k
].right
> next_cols
[k
+1].left
)) {
3274 fprintf(stderr
, "next_cols[k].right > next_cols[k+1].left\n"); fflush(stderr
);
3276 fatal("next_cols has messed up columns");
3282 return( upper_limit
);
3286 * conflict_with_words - returns TRUE if a word sequence crosses a column.
3289 int html_printer::conflict_with_words (struct text_defn
*column_guess
, struct text_defn
*words
)
3294 while ((column_guess
[i
].left
!= 0) && (i
<MAX_WORDS_PER_LINE
)) {
3296 while ((words
[j
].left
!= 0) && (j
<MAX_WORDS_PER_LINE
)) {
3297 if ((words
[j
].left
<= column_guess
[i
].right
) && (i
+1<MAX_WORDS_PER_LINE
) &&
3298 (column_guess
[i
+1].left
!= 0) && (words
[j
].right
>= column_guess
[i
+1].left
)) {
3299 if (debug_table_on
) {
3300 fprintf(stderr
, "is a conflict with words\n");
3309 if (debug_table_on
) {
3310 fprintf(stderr
, "is NOT a conflict with words\n");
3317 * combine_line - combines dest and src.
3320 void html_printer::combine_line (struct text_defn
*dest
, struct text_defn
*src
)
3324 for (i
=0; (i
<MAX_WORDS_PER_LINE
) && (src
[i
].left
!= 0); i
++) {
3325 include_into_list(dest
, &src
[i
]);
3327 remove_redundant_columns(dest
);
3331 * remove_entry_in_line - removes an entry, j, in, line.
3334 void html_printer::remove_entry_in_line (struct text_defn
*line
, int j
)
3336 while (line
[j
].left
!= 0) {
3337 line
[j
].left
= line
[j
+1].left
;
3338 line
[j
].right
= line
[j
+1].right
;
3344 * remove_redundant_columns - searches through the array columns and removes any redundant entries.
3347 void html_printer::remove_redundant_columns (struct text_defn
*line
)
3352 while (line
[i
].left
!= 0) {
3353 if ((i
<MAX_WORDS_PER_LINE
) && (line
[i
+1].left
!= 0)) {
3355 while ((j
<MAX_WORDS_PER_LINE
) && (line
[j
].left
!= 0)) {
3356 if ((j
!= i
) && (is_intersection(line
[i
].left
, line
[i
].right
, line
[j
].left
, line
[j
].right
))) {
3357 line
[i
].left
= min(line
[i
].left
, line
[j
].left
);
3358 line
[i
].right
= max(line
[i
].right
, line
[j
].right
);
3359 remove_entry_in_line(line
, j
);
3370 * include_into_list - performs an order set inclusion
3373 void html_printer::include_into_list (struct text_defn
*line
, struct text_defn
*item
)
3377 while ((i
<MAX_WORDS_PER_LINE
) && (line
[i
].left
!= 0) && (line
[i
].left
<item
->left
)) {
3381 if (line
[i
].left
== 0) {
3383 if (i
<MAX_WORDS_PER_LINE
) {
3384 if ((i
>0) && (line
[i
-1].left
> item
->left
)) {
3385 fatal("insertion error");
3387 line
[i
].left
= item
->left
;
3388 line
[i
].right
= item
->right
;
3394 if (line
[i
].left
== item
->left
) {
3395 line
[i
].right
= max(item
->right
, line
[i
].right
);
3398 int left
= item
->left
;
3399 int right
= item
->right
;
3400 int l
= line
[i
].left
;
3401 int r
= line
[i
].right
;
3403 while ((i
+1<MAX_WORDS_PER_LINE
) && (line
[i
].left
!= 0)) {
3404 line
[i
].left
= left
;
3405 line
[i
].right
= right
;
3412 if (i
+1<MAX_WORDS_PER_LINE
) {
3413 line
[i
].left
= left
;
3414 line
[i
].right
= right
;
3416 line
[i
+1].right
= 0;
3423 * is_in_column - return TRUE if value is present in line.
3426 int html_printer::is_in_column (struct text_defn
*line
, struct text_defn
*item
, int max_words
)
3430 while ((i
<max_words
) && (line
[i
].left
!= 0)) {
3431 if (line
[i
].left
== item
->left
) {
3441 * calculate_right - calculate the right most margin for each column in line.
3444 void html_printer::calculate_right (struct text_defn
*line
, int max_words
)
3448 while ((i
<max_words
) && (line
[i
].left
!= 0)) {
3450 line
[i
-1].right
= line
[i
].left
;
3457 * add_right_full_width - adds an extra column to the right to bring the table up to
3461 void html_printer::add_right_full_width (struct text_defn
*line
, int mingap
)
3465 while ((i
<MAX_WORDS_PER_LINE
) && (line
[i
].left
!= 0)) {
3469 if ((i
>0) && (line
[i
-1].right
!= right_margin_indent
) && (i
+1<MAX_WORDS_PER_LINE
)) {
3470 line
[i
].left
= min(line
[i
-1].right
+mingap
, right_margin_indent
);
3471 line
[i
].right
= right_margin_indent
;
3473 if (i
<MAX_WORDS_PER_LINE
) {
3481 * determine_right_most_column - works out the right most limit of the right most column.
3482 * Required as we might be performing a .2C and only
3483 * have enough text to fill the left column.
3486 void html_printer::determine_right_most_column (struct text_defn
*line
, int max_words
)
3490 while ((i
<max_words
) && (line
[i
].left
!= 0)) {
3494 // remember right_margin_indent is the right most position for this page
3495 line
[i
-1].right
= column_calculate_right_margin(line
[i
-1].left
, right_margin_indent
);
3500 * is_column_match - returns TRUE if a word is aligned in the same horizontal alignment
3501 * between two lines, line1 and line2. If so then this horizontal
3502 * position is saved in match.
3505 int html_printer::is_column_match (struct text_defn
*match
,
3506 struct text_defn
*line1
, struct text_defn
*line2
, int max_words
)
3511 int first
=(match
[0].left
==0);
3516 t
.left
= left_margin_indent
;
3519 include_into_list(match
, &t
);
3521 while ((line1
[i
].left
!= 0) && (line2
[i
].left
!= 0)) {
3522 if (line1
[i
].left
== line2
[j
].left
) {
3523 // same horizontal alignment found
3524 include_into_list(match
, &line1
[i
]);
3528 } else if (line1
[i
].left
< line2
[j
].left
) {
3534 calculate_right(match
, max_words
);
3539 * check_lack_of_hits - returns TRUE if a column has been moved to a position
3540 * of only one hit from a position of more than one hit.
3543 int html_printer::check_lack_of_hits (struct text_defn
*next_guess
,
3544 struct text_defn
*last_guess
,
3545 text_glob
*start
, int limit
)
3547 text_glob
*current
=page_contents
->words
.get_data();
3548 int n
=count_columns(last_guess
);
3549 int m
=count_columns(next_guess
);
3553 rewind_text_to(start
);
3554 count_hits(last_guess
, n
, limit
);
3555 rewind_text_to(current
);
3558 while ((i
<n
) && (j
<m
) &&
3559 (last_guess
[i
].left
!= 0) && (next_guess
[j
].left
!= 0)) {
3560 if ((is_intersection(last_guess
[i
].left
, last_guess
[i
].right
,
3561 next_guess
[j
].left
, next_guess
[j
].right
)) &&
3562 (next_guess
[j
].left
< last_guess
[i
].left
) &&
3563 (last_guess
[i
].is_used
>= 2)) {
3565 * next_guess has to be = 1 as this position is new
3569 if (last_guess
[i
].left
< next_guess
[j
].left
) {
3580 * remove_white_using_words - remove white space in, last_guess, by examining, next_line
3581 * placing results into next_guess.
3582 * It returns TRUE if the same columns exist in next_guess and last_guess
3583 * we do allow columns to shrink but if a column disappears then we return FALSE.
3586 int html_printer::remove_white_using_words (struct text_defn
*next_guess
,
3587 struct text_defn
*last_guess
, struct text_defn
*next_line
)
3594 while ((last_guess
[j
].left
!= 0) && (next_line
[k
].left
!= 0)) {
3595 if (last_guess
[j
].left
== next_line
[k
].left
) {
3596 // same horizontal alignment found
3597 next_guess
[i
].left
= last_guess
[j
].left
;
3598 next_guess
[i
].right
= max(last_guess
[j
].right
, next_line
[k
].right
);
3602 if ((next_guess
[i
-1].right
> last_guess
[j
].left
) && (last_guess
[j
].left
!= 0)) {
3605 } else if (last_guess
[j
].right
< next_line
[k
].left
) {
3606 next_guess
[i
].left
= last_guess
[j
].left
;
3607 next_guess
[i
].right
= last_guess
[j
].right
;
3610 } else if (last_guess
[j
].left
> next_line
[k
].right
) {
3611 // insert a word sequence from next_line[k]
3612 next_guess
[i
].left
= next_line
[k
].left
;
3613 next_guess
[i
].right
= next_line
[k
].right
;
3616 } else if (is_intersection(last_guess
[j
].left
, last_guess
[j
].right
, next_line
[k
].left
, next_line
[k
].right
)) {
3617 // potential for a column disappearing
3618 next_guess
[i
].left
= min(last_guess
[j
].left
, next_line
[k
].left
);
3619 next_guess
[i
].right
= max(last_guess
[j
].right
, next_line
[k
].right
);
3623 if ((next_guess
[i
-1].right
> last_guess
[j
].left
) && (last_guess
[j
].left
!= 0)) {
3628 while (next_line
[k
].left
!= 0) {
3629 next_guess
[i
].left
= next_line
[k
].left
;
3630 next_guess
[i
].right
= next_line
[k
].right
;
3634 if (i
<MAX_WORDS_PER_LINE
) {
3635 next_guess
[i
].left
= 0;
3636 next_guess
[i
].right
= 0;
3638 if (debug_table_on
) {
3640 fprintf(stderr
, "have removed column\n");
3642 fprintf(stderr
, "have NOT removed column\n");
3646 remove_redundant_columns(next_guess
);
3651 * count_columns - returns the number of elements inside, line.
3654 int html_printer::count_columns (struct text_defn
*line
)
3658 while (line
[i
].left
!= 0) {
3665 * rewind_text_to - moves backwards until page_contents is looking at, g.
3668 void html_printer::rewind_text_to (text_glob
*g
)
3670 while (page_contents
->words
.get_data() != g
) {
3671 if (page_contents
->words
.is_equal_to_head()) {
3672 page_contents
->words
.start_from_tail();
3674 page_contents
->words
.move_left();
3680 * can_loose_column - checks to see whether we should combine two columns.
3681 * This is allowed if there are is only one hit on the
3682 * left hand edge and the previous column is very close.
3685 void html_printer::can_loose_column (text_glob
*start
, struct text_defn
*last_guess
, int limit
)
3687 text_glob
*current
=page_contents
->words
.get_data();
3688 int n
=count_columns(last_guess
);
3691 rewind_text_to(start
);
3692 count_hits(last_guess
, n
, limit
);
3695 if ((last_guess
[i
+1].is_used
== 1) &&
3696 (calculate_min_gap(start
) > (last_guess
[i
+1].left
-last_guess
[i
].right
))) {
3697 last_guess
[i
].right
= last_guess
[i
+1].right
;
3698 remove_entry_in_line(last_guess
, i
+1);
3699 n
= count_columns(last_guess
);
3705 rewind_text_to(current
);
3709 * display_columns - a long overdue debugging function, as this column code is causing me grief :-(
3712 void html_printer::display_columns (const char *word
, const char *name
, text_defn
*line
)
3716 fprintf(stderr
, "[%s:%s]", name
, word
);
3717 while (line
[i
].left
!= 0) {
3718 fprintf(stderr
, " <left=%d right=%d %d%%> ", line
[i
].left
, line
[i
].right
, line
[i
].percent
);
3721 fprintf(stderr
, "\n");
3726 * copy_line - dest = src
3729 void html_printer::copy_line (struct text_defn
*dest
, struct text_defn
*src
)
3733 for (k
=0; ((src
[k
].left
!= 0) && (k
<MAX_WORDS_PER_LINE
)); k
++) {
3734 dest
[k
].left
= src
[k
].left
;
3735 dest
[k
].right
= src
[k
].right
;
3737 if (k
<MAX_WORDS_PER_LINE
) {
3744 * add_column_gaps - adds empty columns between columns which don't exactly align
3747 void html_printer::add_column_gaps (struct text_defn
*line
)
3752 // firstly lets see whether we need an initial column on the left hand side
3753 if ((line
[0].left
!= get_left()) && (line
[0].left
!= 0) &&
3754 (get_left() < line
[0].left
) && (is_worth_column(get_left(), line
[0].left
))) {
3755 t
.left
= get_left();
3756 t
.right
= line
[0].left
;
3757 include_into_list(line
, &t
);
3760 while ((i
<MAX_WORDS_PER_LINE
) && (line
[i
].left
!= 0)) {
3761 if ((i
+1<MAX_WORDS_PER_LINE
) && (line
[i
+1].left
!= 0) && (line
[i
].right
!= line
[i
+1].left
) &&
3762 (is_worth_column(line
[i
].right
, line
[i
+1].left
))) {
3763 t
.left
= line
[i
].right
;
3764 t
.right
= line
[i
+1].left
;
3765 include_into_list(line
, &t
);
3771 // now let us see whether we need a final column on the right hand side
3772 if ((i
>0) && (line
[i
-1].right
!= right_margin_indent
) &&
3773 (is_worth_column(line
[i
-1].right
, right_margin_indent
))) {
3774 t
.left
= line
[i
-1].right
;
3775 t
.right
= right_margin_indent
;
3776 include_into_list(line
, &t
);
3781 * is_continueous_column - returns TRUE if a line has a word on one
3782 * of the last_col right most boundaries.
3785 int html_printer::is_continueous_column (text_defn
*last_col
, text_defn
*next_line
)
3787 int w
= count_columns(next_line
);
3788 int c
= count_columns(last_col
);
3791 for (i
=0; i
<c
; i
++) {
3792 for (j
=0; j
<w
; j
++) {
3793 if (last_col
[i
].right
== next_line
[j
].right
) {
3802 * is_exact_left - returns TRUE if a line has a word on one
3803 * of the last_col left most boundaries.
3806 int html_printer::is_exact_left (text_defn
*last_col
, text_defn
*next_line
)
3808 int w
= count_columns(next_line
);
3809 int c
= count_columns(last_col
);
3812 for (i
=0; i
<c
; i
++) {
3813 for (j
=0; j
<w
; j
++) {
3814 if ((last_col
[i
].left
== next_line
[j
].left
) ||
3815 (last_col
[i
].left
!= left_margin_indent
)) {
3824 * continue_searching_column - decides whether we should carry on searching text for a column.
3827 int html_printer::continue_searching_column (text_defn
*next_col
,
3828 text_defn
*last_col
,
3829 text_defn
*all_words
)
3831 int count
= count_columns(next_col
);
3832 int words
= count_columns(all_words
);
3834 if ((words
== 0) || ((words
== 1) &&
3835 (all_words
[0].left
== left_margin_indent
) &&
3836 (all_words
[0].right
== right_margin_indent
))) {
3837 // no point as we have now seen a full line of contineous text with no gap
3840 return( (count
== count_columns(last_col
)) &&
3841 (last_col
[0].left
!= left_margin_indent
) || (last_col
[0].right
!= right_margin_indent
) );
3845 * is_worth_column - returns TRUE if the size of this column is worth defining.
3848 int html_printer::is_worth_column (int left
, int right
)
3851 return( abs(right
-left
) >= MIN_COLUMN
);
3857 * large_enough_gap - returns TRUE if a large enough gap for one line was seen.
3858 * We need to make sure that a single line definitely warrents
3860 * It also removes other smaller gaps.
3863 int html_printer::large_enough_gap (text_defn
*last_col
)
3868 int gap
=r
/GAP_WIDTH_ONE_LINE
;
3870 if (abs(last_col
[i
].left
- left_margin_indent
) >= gap
) {
3873 while ((last_col
[i
].left
!= 0) && (last_col
[i
+1].left
!= 0)) {
3874 if (abs(last_col
[i
+1].left
-last_col
[i
].right
) >= gap
) {
3878 // not good enough for a single line, remove it
3879 last_col
[i
].right
= last_col
[i
+1].right
;
3880 remove_entry_in_line(last_col
, i
+1);
3887 * is_subset_of_columns - returns TRUE if line, a, is a subset of line, b.
3890 int html_printer::is_subset_of_columns (text_defn
*a
, text_defn
*b
)
3896 while ((i
<MAX_WORDS_PER_LINE
) && (a
[i
].left
!= 0)) {
3898 while ((j
<MAX_WORDS_PER_LINE
) && (b
[j
].left
!= 0) &&
3899 ((b
[j
].left
!= a
[i
].left
) || (b
[j
].right
!= a
[i
].right
))) {
3902 if ((j
==MAX_WORDS_PER_LINE
) || (b
[j
].left
== 0)) {
3903 // found a different column - not a subset
3912 * count_hits - counts the number of hits per column. A left hit
3913 * is when the left hand position of a glob hits
3914 * the left hand column.
3917 void html_printer::count_hits (text_defn
*col
, int no_of_columns
, int limit
)
3920 text_glob
*start
= page_contents
->words
.get_data();
3921 text_glob
*g
= start
;
3923 // firstly reset the used field
3924 for (i
=0; i
<no_of_columns
; i
++) {
3927 // now calculate the left hand hits
3928 while ((g
!= 0) && (g
->minv
<= limit
)) {
3930 while ((i
<no_of_columns
) && (col
[i
].right
< g
->minh
)) {
3933 if ((col
[i
].left
== g
->minh
) && (col
[i
].left
!= 0)) {
3936 page_contents
->words
.move_right();
3937 if (page_contents
->words
.is_equal_to_head()) {
3939 page_contents
->words
.start_from_tail();
3941 g
=page_contents
->words
.get_data();
3947 * count_right_hits - counts the number of right hits per column.
3948 * A right hit is when the left hand position
3949 * of a glob hits the right hand column.
3952 void html_printer::count_right_hits (text_defn
*col
, int no_of_columns
)
3955 text_glob
*start
= page_contents
->words
.get_data();
3956 text_glob
*g
= start
;
3958 // firstly reset the used field
3959 for (i
=0; i
<no_of_columns
; i
++) {
3960 col
[i
].right_hits
= 0;
3962 // now calculate the left hand hits
3963 while ((g
!= 0) && (g
->minv
<= indentation
.vertical_limit
)) {
3965 while ((i
<no_of_columns
) && (col
[i
].right
< g
->minh
)) {
3968 if ((i
<no_of_columns
) && (col
[i
].right
== g
->maxh
)) {
3969 if (debug_table_on
) {
3970 fprintf(stderr
, "found right hit [%s] at %d in %d\n",
3971 g
->text_string
, g
->maxh
, i
);
3974 col
[i
].right_hits
++;
3976 page_contents
->words
.move_right();
3977 if (page_contents
->words
.is_equal_to_head()) {
3979 page_contents
->words
.start_from_tail();
3981 g
=page_contents
->words
.get_data();
3987 * right_indentation - returns TRUE if a single column has been found and
3988 * it resembles an indentation. Ie .RS/.RE or ABSTACT
3991 int html_printer::right_indentation (struct text_defn
*last_guess
)
3993 // it assumes that last_guess contains a single column
3994 return( (last_guess
[0].left
> left_margin_indent
) );
3998 * able_to_steal_width - returns TRUE if we have an unused column which we can steal from.
3999 * It must have more than MIN_TEXT_PERCENT to do this.
4002 int html_printer::able_to_steal_width (void)
4006 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4007 if ((! indentation
.columns
[i
].is_used
) &&
4008 (indentation
.columns
[i
].percent
> MIN_TEXT_PERCENT
)) {
4016 * is_divisible_by - returns TRUE if n is divisible by d leaving no remainder.
4019 static int is_divisible_by (int n
, int d
)
4021 return( (n
% d
) == 0 );
4025 * need_to_steal_width - returns TRUE if a used column need to be
4026 * given a little extra width for safty sake.
4029 int html_printer::need_to_steal_width (void)
4033 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4034 if ((indentation
.columns
[i
].is_used
) &&
4035 (indentation
.columns
[i
].percent
== (((indentation
.columns
[i
].right
- indentation
.columns
[i
].left
) * 100) /
4036 (right_margin_indent
-left_margin_indent
))) &&
4037 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
)) {
4045 * utilize_round_off - utilize the remaining percent width in text columns
4048 void html_printer::utilize_round_off (void)
4050 int total
= total_percentages();
4053 // use up the spare excess
4057 for (i
=0; (i
<indentation
.no_of_columns
) && (excess
>0); i
++) {
4058 if ((indentation
.columns
[i
].is_used
) &&
4059 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
)) {
4060 indentation
.columns
[i
].percent
++;
4064 // we might as well try and keep any numbers simple if possible
4065 for (i
=0; (i
<indentation
.no_of_columns
) && (excess
>0); i
++) {
4066 if ((indentation
.columns
[i
].is_used
) &&
4067 (! is_divisible_by(indentation
.columns
[i
].percent
, MIN_TEXT_PERCENT
))) {
4068 indentation
.columns
[i
].percent
++;
4072 // forget the niceties lets just use excess up now!
4073 for (i
=0; (i
<indentation
.no_of_columns
) && (excess
>0); i
++) {
4074 if (indentation
.columns
[i
].is_used
) {
4075 indentation
.columns
[i
].percent
++;
4082 * can_distribute_fairly - returns TRUE if we can redistribute some of the unused width into
4083 * columns that are used.
4086 int html_printer::can_distribute_fairly (void)
4093 // firstly total up all percentages - so we can use round offs
4094 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4095 total
+= indentation
.columns
[i
].percent
;
4096 if ((indentation
.columns
[i
].is_used
) &&
4097 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
)) {
4103 if (excess
< used
) {
4104 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4105 if (! indentation
.columns
[i
].is_used
) {
4106 if (indentation
.columns
[i
].percent
> MIN_TEXT_PERCENT
) {
4107 indentation
.columns
[i
].percent
--;
4113 if (excess
>= used
) {
4114 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4115 if ((indentation
.columns
[i
].is_used
) &&
4116 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
) &&
4117 (indentation
.columns
[i
].percent
== (((indentation
.columns
[i
].right
- indentation
.columns
[i
].left
) * 100) /
4118 (right_margin_indent
-left_margin_indent
)))) {
4119 indentation
.columns
[i
].percent
++;
4129 * remove_table_column - removes column, i, from the indentation.
4132 void html_printer::remove_table_column (int i
)
4134 while (i
<indentation
.no_of_columns
) {
4135 indentation
.columns
[i
].left
= indentation
.columns
[i
+1].left
;
4136 indentation
.columns
[i
].right
= indentation
.columns
[i
+1].right
;
4137 indentation
.columns
[i
].is_used
= indentation
.columns
[i
+1].is_used
;
4138 indentation
.columns
[i
].percent
= indentation
.columns
[i
+1].percent
;
4141 indentation
.no_of_columns
--;
4145 * next_line_on_left_column - returns TRUE if the next line in
4146 * column, i, has a word on the left margin.
4149 int html_printer::next_line_on_left_column (int i
, text_glob
*start
)
4151 int current_vpos
=start
->minv
;
4153 while ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
) &&
4154 (is_on_same_line(start
, current_vpos
))) {
4155 if (page_contents
->words
.is_equal_to_tail()) {
4158 page_contents
->words
.move_right();
4159 start
= page_contents
->words
.get_data();
4162 if ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
)) {
4163 // onto next line now
4164 current_vpos
=start
->minv
;
4165 while ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
) &&
4166 (is_on_same_line(start
, current_vpos
))) {
4167 if (start
->minh
== indentation
.columns
[i
].left
) {
4170 if (page_contents
->words
.is_equal_to_tail()) {
4173 page_contents
->words
.move_right();
4174 start
= page_contents
->words
.get_data();
4182 * will_wrap_text - returns TRUE if text is wrapped in column, i.
4185 int html_printer::will_wrap_text (int i
, text_glob
*start
)
4187 text_glob
*current
=page_contents
->words
.get_data();
4190 rewind_text_to(start
);
4191 while ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
)) {
4192 if (indentation
.columns
[i
].right
== start
->maxh
) {
4193 // ok right word is on column boarder - check next line
4194 if (next_line_on_left_column(i
, start
)) {
4195 rewind_text_to(current
);
4199 if (page_contents
->words
.is_equal_to_tail()) {
4202 page_contents
->words
.move_right();
4203 start
= page_contents
->words
.get_data();
4207 rewind_text_to(current
);
4212 * remove_unnecessary_unused - runs through a table and decides whether an unused
4213 * column can be removed. This is only true if the
4214 * column to the left does not wrap text.
4217 void html_printer::remove_unnecessary_unused (text_glob
*start
)
4220 int left
=get_left();
4223 while (i
<indentation
.no_of_columns
) {
4224 if ((indentation
.columns
[i
].is_used
) &&
4225 (i
+1<indentation
.no_of_columns
) && (! indentation
.columns
[i
+1].is_used
)) {
4227 * so i+1 is unused and there is a used column to the left.
4228 * Now we check whether we can add the unused column to the column, i.
4229 * This can only be done if column, i, is not wrapping text.
4231 if (! will_wrap_text(i
, start
)) {
4233 if (i
+1 < indentation
.no_of_columns
) {
4234 right
= indentation
.columns
[i
+1].right
;
4236 right
= right_margin_indent
;
4238 indentation
.columns
[i
].percent
= (((right
- indentation
.columns
[i
].left
) * 100) /
4239 (right_margin_indent
-left
));
4241 indentation
.columns
[i
].percent
= (((indentation
.columns
[i
+1].right
- indentation
.columns
[i
].left
) * 100) /
4242 (right_margin_indent
-left
));
4244 remove_table_column(i
+1);
4253 * remove_zero_percentage_column - removes all zero percentage width columns
4256 void html_printer::remove_zero_percentage_column (void)
4260 while (i
<indentation
.no_of_columns
) {
4261 if (indentation
.columns
[i
].percent
== 0) {
4262 remove_table_column(i
);
4271 * get_left - returns the actual left most margin.
4274 int html_printer::get_left (void)
4276 if ((header_indent
< left_margin_indent
) && (header_indent
!= -1)) {
4277 return( header_indent
);
4279 return( left_margin_indent
);
4284 * calculate_percentage_width - calculates the percentage widths,
4285 * this function will be generous to
4286 * columns which have words as some browsers
4287 * produce messy output if the percentage is exactly
4288 * that required for text..
4289 * We try and round up to MIN_TEXT_PERCENT
4290 * of course we can only do this if we can steal from
4294 void html_printer::calculate_percentage_width (text_glob
*start
)
4297 int left
=get_left();
4300 // firstly calculate raw percentages
4301 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4303 indentation
.columns
[i
].percent
= (((indentation
.columns
[i
].right
- indentation
.columns
[i
].left
) * 100) /
4304 (right_margin_indent
-left
));
4306 if (i
+1 < indentation
.no_of_columns
) {
4307 right
= indentation
.columns
[i
+1].left
;
4309 right
= right_margin_indent
;
4311 indentation
.columns
[i
].percent
= (((right
- indentation
.columns
[i
].left
) * 100) /
4312 (right_margin_indent
-left
));
4315 if (debug_table_on
) {
4316 display_columns(start
->text_string
, "[b4 steal] indentation.columns", indentation
.columns
);
4319 // now steal from the unused columns..
4320 remove_unnecessary_unused(start
);
4322 if (debug_table_on
) {
4323 display_columns(start
->text_string
, "[after steal] indentation.columns", indentation
.columns
);
4327 utilize_round_off();
4329 remove_zero_percentage_column();
4334 * is_column_subset - returns TRUE if the columns described by small can be contained in
4335 * the columns in large.
4338 int html_printer::is_column_subset (struct text_defn
*small
, struct text_defn
*large
)
4340 int ns
=count_columns(small
);
4341 int nl
=count_columns(large
);
4350 if (is_intersection(small
[i
].left
, small
[i
].right
, large
[j
].left
, large
[j
].right
)) {
4352 if (! is_subsection(small
[i
].left
, small
[i
].right
, large
[j
].left
, large
[j
].right
)) {
4353 // found column which is not a subset
4364 // small cannot be an empty set
4369 * right_most_column - returns the right most column position.
4372 int html_printer::right_most_column (struct text_defn
*col
)
4374 int i
= count_columns(col
);
4377 return( col
[i
-1].right
);
4384 * large_enough_gap_for_two - returns TRUE if there exists a large enough gap
4388 int html_printer::large_enough_gap_for_two (struct text_defn
*col
)
4392 int gap
=MIN_COLUMN_FOR_TWO_LINES
;
4394 if (abs(col
[i
].left
- left_margin_indent
) >= gap
) {
4397 while ((col
[i
].left
!= 0) && (col
[i
+1].left
!= 0)) {
4398 if (abs(col
[i
+1].left
-col
[i
].right
) >= gap
) {
4402 // not good enough for this table, remove it
4403 col
[i
].right
= col
[i
+1].right
;
4404 remove_entry_in_line(col
, i
+1);
4411 * is_small_table - applies some rigorous rules to test whether we should start this
4412 * table at this point.
4415 int html_printer::is_small_table (int lines
, struct text_defn
*last_guess
,
4416 struct text_defn
*words_1
, struct text_defn
*cols_1
,
4417 struct text_defn
*words_2
, struct text_defn
*cols_2
,
4418 int *limit
, int *limit_1
)
4421 * firstly we check for an indented paragraph
4425 (count_columns(cols_1
) == count_columns(cols_2
)) && (count_columns(cols_1
) == 1) &&
4426 right_indentation(cols_1
) && (! right_indentation(cols_2
)) &&
4427 (cols_1
[0].right
== right_margin_indent
)) {
4433 * as we only have two lines in our table we need to examine in detail whether
4434 * we should construct a table from these two lines.
4435 * For example if the text is the start of an indented paragraph and
4436 * line1 and line2 are contineous then they should form one row in our table but
4437 * if line1 and line2 are not contineous it is safer to treat them separately.
4439 * We are prepared to reduce the table to one line
4441 if (((count_columns(cols_1
) != count_columns(cols_2
)) && (cols_1
[0].left
> cols_2
[0].left
)) ||
4442 (! ((is_column_subset(cols_1
, cols_2
)) ||
4443 (is_column_subset(cols_2
, cols_1
))))) {
4445 * now we must check to see whether line1 and line2 join
4447 if ((right_most_column(cols_1
) == right_margin_indent
) &&
4448 (cols_2
[0].left
== left_margin_indent
)) {
4450 * looks like they join, we don't want a table at all.
4455 * use single line table
4459 copy_line(last_guess
, cols_1
);
4463 if ((count_columns(last_guess
)==1) && (right_indentation(last_guess
))) {
4471 * check for large gap with single line or if multiple lines with more than one column
4475 if (large_enough_gap(last_guess
)) {
4479 } else if (count_columns(last_guess
)>1) {
4481 return( large_enough_gap_for_two(last_guess
) );
4490 * is_appropriate_to_start_table - returns TRUE if it is appropriate to start the table
4494 int html_printer::is_appropriate_to_start_table (struct text_defn
*cols_1
,
4495 struct text_defn
*cols_2
,
4496 struct text_defn
*last_guess
)
4498 if (count_columns(last_guess
) == 1) {
4499 if (debug_table_on
) {
4500 display_columns("", "[is] cols_1" , cols_1
);
4501 display_columns("", "[is] cols_2" , cols_2
);
4502 display_columns("", "[is] last_guess", last_guess
);
4505 if (! ((is_column_subset(cols_1
, cols_2
)) ||
4506 (is_column_subset(cols_2
, cols_1
)))) {
4509 if ((count_columns(cols_1
) == 1) &&
4510 (cols_1
[0].left
> left_margin_indent
) && (cols_1
[0].right
< right_margin_indent
) &&
4511 (cols_1
[0].right
!= cols_2
[0].right
) &&
4512 (count_columns(last_guess
) == 1)) {
4520 * is_a_full_width_column - returns TRUE if there exists a full width column.
4523 int html_printer::is_a_full_width_column (void)
4527 while (i
<indentation
.no_of_columns
) {
4528 if (((indentation
.columns
[i
].left
== get_left()) ||
4529 (indentation
.columns
[i
].left
== left_margin_indent
)) &&
4530 (indentation
.columns
[i
].right
== right_margin_indent
)) {
4539 * should_defer_table - returns TRUE if we should defer this table.
4540 * This can occur if the first line seen indent
4541 * is < than future lines. In which case it
4542 * will cause future lines in this table
4543 * to be indented. The lesser of the evils
4544 * is to treat the first line by itself.
4547 int html_printer::should_defer_table (int lines
, struct text_glob
*start
, struct text_defn
*cols_1
)
4551 int c
=count_columns(cols_1
);
4553 count_hits(cols_1
, count_columns(cols_1
), indentation
.vertical_limit
);
4554 rewind_text_to(start
);
4555 count_right_hits(cols_1
, count_columns(cols_1
));
4556 rewind_text_to(start
);
4558 if ((cols_1
[i
].is_used
> 1) || (cols_1
[i
].right_hits
> 1)) {
4564 * first line (cols_1) is not aligned on any future column, we defer.
4572 * is_new_exact_right - returns TRUE if the, next_cols, has a word sitting
4573 * on the right hand margin of last_guess. But only
4574 * if no exact right word was found in last_cols.
4577 int html_printer::is_new_exact_right (struct text_defn
*last_guess
,
4578 struct text_defn
*last_cols
,
4579 struct text_defn
*next_cols
)
4581 int n
=count_columns(last_guess
)-1;
4584 if ((n
>=0) && (last_guess
[n
].left
!= 0) && (last_cols
[n
].left
!= 0) && (next_cols
[n
].left
!= 0)) {
4585 if ((last_cols
[n
].right
!= last_guess
[n
].right
) &&
4586 ((next_cols
[n
].right
== last_guess
[n
].right
) || (next_cols
[n
].right
== right_margin_indent
))) {
4594 * found_use_for_table - checks whether the some words on one line directly match
4595 * the horizontal alignment of the line below.
4596 * This is rather complex as we need to detect text tables
4597 * such as .2C .IP Abstracts and indentations
4601 * read first line of text and calculate the significant
4602 * gaps between words
4603 * next next line of text and do the same
4604 * if a conflict between these lines exists and
4605 * first line is centered
4607 * return centered line
4608 * elsif start of a table is found
4611 * read next line of text and calculate significant gaps
4612 * until conflict between the gaps is found
4614 * return table found
4616 * return no table found
4620 int html_printer::found_use_for_table (text_glob
*start
)
4623 struct text_defn all_words
[MAX_WORDS_PER_LINE
]; // logical OR of words on each line
4624 struct text_defn words_1
[MAX_WORDS_PER_LINE
]; // actual words found on first line
4625 struct text_defn words_2
[MAX_WORDS_PER_LINE
]; // actual words found on second line
4626 struct text_defn cols_1
[MAX_WORDS_PER_LINE
]; // columns found on line 1
4627 struct text_defn cols_2
[MAX_WORDS_PER_LINE
]; // columns found on line 2
4628 struct text_defn last_words
[MAX_WORDS_PER_LINE
]; // actual words found on last line
4629 struct text_defn last_cols
[MAX_WORDS_PER_LINE
]; // columns found so far
4630 struct text_defn next_words
[MAX_WORDS_PER_LINE
]; // actual words found on last line (new)
4631 struct text_defn next_cols
[MAX_WORDS_PER_LINE
]; // columns found on next line
4632 struct text_defn last_guess
[MAX_WORDS_PER_LINE
]; // columns found on last line
4633 // (logical AND of gaps (treat gaps = true))
4634 struct text_defn next_guess
[MAX_WORDS_PER_LINE
]; // columns found on next line
4635 // (logical AND of gaps (treat gaps = true))
4636 struct text_defn prev_guess
[MAX_WORDS_PER_LINE
]; // temporary copy of last_guess
4638 int lines
=1; // number of lines read
4639 int limit
; // vertical limit reached in our table
4640 int limit_1
; // vertical position after line 1
4643 if (strcmp(start
->text_string
, "This") == 0) {
4649 * get first set of potential columns into last_line, call this last_guess
4651 limit
= collect_columns(words_1
, cols_1
, 0, 0, MAX_WORDS_PER_LINE
);
4653 copy_line(last_guess
, cols_1
);
4656 * initialize the all_words columns - if this should ever equal a complete line
4657 * with no gaps then we terminate the table.
4659 copy_line(all_words
, cols_1
);
4662 * and set the current limit found
4664 indentation
.vertical_limit
= limit
;
4667 * have we reached the end of page?
4669 if (page_contents
->words
.is_equal_to_head() || (limit
== 0)) {
4671 cols_2
[0].right
= 0;
4674 * the answer to the previous question was no.
4675 * So we need to examine the next line
4677 limit
= collect_columns(words_2
, cols_2
, words_1
, cols_1
, MAX_WORDS_PER_LINE
);
4684 * now check to see whether the first line looks like a single centered line
4686 if (single_centered_line(cols_1
, cols_2
, start
)) {
4687 rewind_text_to(start
);
4688 write_centered_line(start
);
4690 * indicate to caller than we have centered text, not found a table.
4692 indentation
.no_of_columns
= 0;
4694 } else if (! table_on
) {
4696 * user does not allow us to find a table (we are allowed to find centered lines (above))
4698 rewind_text_to(start
);
4703 * remove any gaps from all_words
4705 combine_line(all_words
, cols_2
);
4706 if (debug_table_on
) {
4707 display_columns(start
->text_string
, "[1] all_words" , all_words
);
4708 display_columns(start
->text_string
, "[1] cols_1" , cols_1
);
4709 display_columns(start
->text_string
, "[1] words_1" , words_1
);
4710 display_columns(start
->text_string
, "[1] cols_2" , cols_2
);
4711 display_columns(start
->text_string
, "[1] words_2" , words_2
);
4712 display_columns(start
->text_string
, "[1] last_guess", last_guess
);
4716 * next_guess = last_guess AND next_cols (where gap = true)
4719 if (remove_white_using_words(prev_guess
, last_guess
, cols_2
)) {
4721 if (remove_white_using_words(next_guess
, prev_guess
, all_words
)) {
4724 if (debug_table_on
) {
4725 display_columns(start
->text_string
, "[2] next_guess", next_guess
);
4728 copy_line(prev_guess
, cols_1
);
4729 combine_line(prev_guess
, cols_2
);
4732 * if no sequence of words crosses a column and
4733 * both the last column and all_words are not a full solid line of text
4735 if ((! conflict_with_words(next_guess
, all_words
)) &&
4736 (continue_searching_column(next_guess
, next_guess
, all_words
)) &&
4737 (is_appropriate_to_start_table(cols_1
, cols_2
, prev_guess
)) &&
4738 (! page_contents
->words
.is_equal_to_head()) &&
4739 ((end_region_vpos
< 0) || (limit
< end_region_vpos
)) &&
4743 * subtract any columns which are bridged by a sequence of words
4746 copy_line(next_cols
, cols_2
);
4747 copy_line(next_words
, words_2
);
4750 copy_line(prev_guess
, next_guess
); // copy next_guess away so we can compare it later
4751 combine_line(last_guess
, next_guess
);
4753 if (debug_table_on
) {
4754 t
= page_contents
->words
.get_data();
4755 display_columns(t
->text_string
, "[l] last_guess", last_guess
);
4757 indentation
.vertical_limit
= limit
;
4759 copy_line(last_cols
, next_cols
);
4760 copy_line(last_words
, next_words
);
4761 if (page_contents
->words
.is_equal_to_head()) {
4763 * terminate the search
4765 next_cols
[0].left
= 0;
4766 next_cols
[0].right
= 0;
4768 limit
= collect_columns(next_words
, next_cols
, last_words
, last_cols
, MAX_WORDS_PER_LINE
);
4772 combine_line(all_words
, next_cols
);
4773 if (debug_table_on
) {
4774 display_columns(t
->text_string
, "[l] all_words" , all_words
);
4775 display_columns(t
->text_string
, "[l] last_cols" , last_cols
);
4776 display_columns(t
->text_string
, "[l] next_words", next_words
);
4777 display_columns(t
->text_string
, "[l] next_cols" , next_cols
);
4782 * (if limit is < 0 then the table ends anyway.)
4783 * we check to see whether we should combine close columns.
4785 can_loose_column(start
, last_guess
, limit
);
4787 t
= page_contents
->words
.get_data();
4789 if (strcmp(t
->text_string
, "heT") == 0) {
4794 } while ((! remove_white_using_words(next_guess
, last_guess
, next_cols
)) &&
4795 (! conflict_with_words(next_guess
, all_words
)) &&
4796 (continue_searching_column(next_guess
, last_guess
, all_words
)) &&
4797 ((is_continueous_column(prev_guess
, last_cols
)) || (is_exact_left(last_guess
, next_cols
))) &&
4798 (! is_new_exact_right(last_guess
, last_cols
, next_cols
)) &&
4799 (! page_contents
->words
.is_equal_to_head()) &&
4800 (! check_lack_of_hits(next_guess
, last_guess
, start
, limit
)) &&
4801 ((end_region_vpos
<= 0) || (t
->minv
< end_region_vpos
)) &&
4807 indentation
.vertical_limit
= limit
;
4810 if (page_contents
->words
.is_equal_to_head()) {
4811 // end of page check whether we should include everything
4812 if ((! conflict_with_words(next_guess
, all_words
)) &&
4813 (continue_searching_column(next_guess
, last_guess
, all_words
)) &&
4814 ((is_continueous_column(prev_guess
, last_cols
)) || (is_exact_left(last_guess
, next_cols
)))) {
4815 // end of page reached - therefore include everything
4816 page_contents
->words
.start_from_tail();
4817 t
= page_contents
->words
.get_data();
4818 combine_line(last_guess
, next_guess
);
4819 indentation
.vertical_limit
= t
->minv
;
4822 t
= page_contents
->words
.get_data();
4823 if (((! conflict_with_words(last_guess
, all_words
))) &&
4824 (t
->minv
> end_region_vpos
) && (end_region_vpos
> 0)) {
4825 indentation
.vertical_limit
= limit
;
4827 if ((end_region_vpos
> 0) && (t
->minv
> end_region_vpos
)) {
4828 indentation
.vertical_limit
= min(indentation
.vertical_limit
, end_region_vpos
+1);
4829 } else if (indentation
.vertical_limit
< 0) {
4830 // -1 as we don't want to include section heading itself
4831 indentation
.vertical_limit
= -indentation
.vertical_limit
-1;
4835 if (debug_table_on
) {
4836 display_columns(start
->text_string
, "[1] all_words" , all_words
);
4837 display_columns(start
->text_string
, "[1] cols_1" , cols_1
);
4838 display_columns(start
->text_string
, "[1] words_1" , words_1
);
4839 display_columns(start
->text_string
, "[1] cols_2" , cols_2
);
4840 display_columns(start
->text_string
, "[1] words_2" , words_2
);
4841 display_columns(start
->text_string
, "[1] last_guess", last_guess
);
4842 display_columns(start
->text_string
, "[1] next_guess", next_guess
);
4844 rewind_text_to(start
);
4846 i
= count_columns(last_guess
);
4847 if ((i
>1) || (right_indentation(last_guess
))) {
4849 // was (continue_searching_column(last_guess, last_guess, all_words)))) {
4850 if (should_defer_table(lines
, start
, cols_1
)) {
4852 * yes, but let us check for a single line table
4855 copy_line(last_guess
, cols_1
);
4858 if (is_small_table(lines
, last_guess
, words_1
, cols_1
, words_2
, cols_2
,
4859 &indentation
.vertical_limit
, &limit_1
)) {
4861 // copy match into permenant html_table
4863 if (indentation
.columns
!= 0) {
4864 free(indentation
.columns
);
4866 if (debug_table_on
) {
4867 display_columns(start
->text_string
, "[x] last_guess", last_guess
);
4869 add_column_gaps(last_guess
);
4870 if (debug_table_on
) {
4871 display_columns(start
->text_string
, "[g] last_guess", last_guess
);
4875 * +1 for the potential header_margin
4879 indentation
.no_of_columns
= count_columns(last_guess
);
4880 indentation
.columns
= (struct text_defn
*)malloc((indentation
.no_of_columns
+2)*sizeof(struct text_defn
));
4883 while (i
<=indentation
.no_of_columns
) {
4884 indentation
.columns
[i
].left
= last_guess
[i
].left
;
4885 indentation
.columns
[i
].right
= last_guess
[i
].right
;
4889 if (indentation
.no_of_columns
>0) {
4890 assign_used_columns(start
);
4891 rewind_text_to(start
);
4892 calculate_percentage_width(start
);
4894 if (debug_table_on
) {
4895 display_columns(start
->text_string
, "[g] indentation.columns", indentation
.columns
);
4899 * clearly a single column 100% is not worth using a table.
4900 * Also we check to see whether the first line is sensibly
4901 * part of this table.
4903 if (is_a_full_width_column()) {
4904 indentation
.no_of_columns
= 0;
4905 free( indentation
.columns
);
4906 indentation
.columns
= 0;
4917 * define_cell - creates a table cell using the percentage width.
4920 void html_printer::define_cell (int i
)
4922 html
.put_string("<td valign=\"top\" align=\"left\" width=\"");
4923 html
.put_number(indentation
.columns
[i
].percent
);
4924 html
.put_string("%\">\n");
4928 * column_display_word - given a left, right pair and the indentation.vertical_limit
4929 * write out html text within this region.
4932 void html_printer::column_display_word (int cell
, int vert
, int left
, int right
, int next
)
4934 text_glob
*g
=page_contents
->words
.get_data();
4936 supress_sub_sup
= TRUE
;
4939 begin_paragraph_no_height(left_alignment
);
4940 while ((g
!= 0) && (g
->minv
<= vert
)) {
4941 if ((left
<= g
->minh
) && (g
->minh
<right
)) {
4942 char *postword
=html_position_text(g
, left
, right
);
4944 if (header
.written_header
) {
4945 fatal("should never generate a header inside a table");
4947 if (g
->is_raw_command
) {
4948 html
.put_string((char *)g
->text_string
);
4950 translate_to_html(g
);
4952 if (postword
!= 0) {
4953 html
.put_string(postword
);
4955 issued_newline
= FALSE
;
4958 if (page_contents
->words
.is_equal_to_tail()) {
4961 page_contents
->words
.move_right();
4962 g
=page_contents
->words
.get_data();
4966 html
.put_string("</td>\n");
4968 page_contents
->words
.move_left();
4969 // and correct output_vpos
4970 g
=page_contents
->words
.get_data();
4971 output_vpos
= g
->minv
;
4977 * total_percentages - returns the total of all the percentages in the table.
4980 int html_printer::total_percentages ()
4985 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4986 sum
+= indentation
.columns
[i
].percent
;
4992 * start_table - creates a table according with parameters contained within class html_table.
4995 void html_printer::start_table (void)
4998 html
.put_string("\n<table width=\"");
4999 html
.put_number(total_percentages());
5000 html
.put_string("%\" rules=\"none\" frame=\"none\" cols=\"");
5001 html
.put_number(indentation
.no_of_columns
);
5002 html
.put_string("\" cellspacing=\"0\" cellpadding=\"0\">\n");
5006 * end_table - finishes off a table.
5009 void html_printer::end_table (void)
5011 html
.put_string("</table>\n");
5012 indentation
.no_of_columns
= 0;
5013 restore_paragraph();
5014 supress_sub_sup
= TRUE
;
5018 * column_calculate_right_margin - scan through the column and find the right most margin
5021 int html_printer::column_calculate_right_margin (int left
, int right
)
5023 if (left
== right
) {
5028 text_glob
*start
= page_contents
->words
.get_data();
5029 text_glob
*g
= start
;
5031 while ((g
!= 0) && (g
->minv
<= indentation
.vertical_limit
)) {
5032 if ((left
<= g
->minh
) && (g
->minh
<right
)) {
5034 fprintf(stderr
, "right word = %s %d\n", g
->text_string
, g
->maxh
); fflush(stderr
);
5036 if (g
->maxh
== rightmost
) {
5038 } else if (g
->maxh
> rightmost
) {
5040 rightmost
= g
->maxh
;
5042 if (g
->maxh
> right
) {
5044 fprintf(stderr
, "problem as right word = %s %d [%d..%d]\n",
5045 g
->text_string
, right
, g
->minh
, g
->maxh
); fflush(stderr
);
5050 page_contents
->words
.move_right();
5051 if (page_contents
->words
.is_equal_to_head()) {
5053 page_contents
->words
.start_from_tail();
5055 g
=page_contents
->words
.get_data();
5058 rewind_text_to(start
);
5059 if (rightmost
== -1) {
5060 return( right
); // no words in this column
5062 return( rightmost
);
5068 * column_calculate_left_margin - scan through the column and find the left most margin
5071 int html_printer::column_calculate_left_margin (int left
, int right
)
5073 if (left
== right
) {
5077 text_glob
*start
= page_contents
->words
.get_data();
5078 text_glob
*g
= start
;
5080 while ((g
!= 0) && (g
->minv
<= indentation
.vertical_limit
)) {
5081 if ((left
<= g
->minh
) && (g
->minh
<right
)) {
5082 leftmost
= min(g
->minh
, leftmost
);
5084 page_contents
->words
.move_right();
5085 if (page_contents
->words
.is_equal_to_head()) {
5087 page_contents
->words
.start_from_tail();
5089 g
=page_contents
->words
.get_data();
5092 rewind_text_to(start
);
5093 if (leftmost
== right
) {
5094 return( left
); // no words in this column
5102 * find_column_index - returns the index to the column in which glob, t, exists.
5105 int html_printer::find_column_index_in_line (text_glob
*t
, text_defn
*line
)
5109 while ((line
!= 0) && ((line
[i
].left
!= 0) || (line
[i
].right
!= 0)) &&
5110 (! ((line
[i
].left
<=t
->minh
) && (line
[i
].right
>t
->minh
)))) {
5117 * find_column_index - returns the index to the column in which glob, t, exists.
5120 int html_printer::find_column_index (text_glob
*t
)
5124 while ((i
<indentation
.no_of_columns
) &&
5125 (! ((indentation
.columns
[i
].left
<=t
->minh
) &&
5126 (indentation
.columns
[i
].right
>t
->minh
)))) {
5133 * determine_row_limit - checks each row to see if there is a gap in a cell.
5134 * We return the vertical position after the empty cell
5135 * at the start of the next line.
5138 int html_printer::determine_row_limit (text_glob
*start
, int v
)
5142 int vpos
, last
, prev
;
5143 text_glob
*is_gap
[MAX_WORDS_PER_LINE
];
5144 text_glob
zero(&start
->text_style
, 0, 0, 0, 0, 0, 0, 0, 0);
5147 if ((v
== -1) && (strcmp(start
->text_string
, "CASE") == 0)) {
5152 if (v
>= indentation
.vertical_limit
) {
5156 * initially we start with all gaps in our table
5157 * after a gap we start a new row
5158 * here we set the gap array to the previous line
5162 t
= page_contents
->words
.get_data();
5165 page_contents
->words
.move_right();
5166 t
= page_contents
->words
.get_data();
5167 } while ((! page_contents
->words
.is_equal_to_head()) &&
5171 if (page_contents
->words
.is_equal_to_head()) {
5174 page_contents
->words
.move_left();
5175 t
= page_contents
->words
.get_data();
5179 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
5183 if (page_contents
->words
.is_equal_to_tail()) {
5184 rewind_text_to(start
);
5185 return( indentation
.vertical_limit
);
5187 page_contents
->words
.move_right();
5189 t
= page_contents
->words
.get_data();
5192 // now check each row for a gap
5196 if (vpos
> indentation
.vertical_limit
) {
5197 // we have reached the end of the table, quit
5198 rewind_text_to(start
);
5199 return( indentation
.vertical_limit
);
5202 i
= find_column_index(t
);
5203 if (i
>=indentation
.no_of_columns
) {
5204 error("find_column_index has failed");
5207 if (! is_on_same_line(t
, last
)) {
5211 if ((! is_on_same_line(is_gap
[i
], vpos
)) && (! is_on_same_line(is_gap
[i
], prev
)) &&
5212 (indentation
.columns
[i
].is_used
)) {
5213 // no word on previous line - must be a gap - force alignment of row
5214 rewind_text_to(start
);
5219 page_contents
->words
.move_right();
5220 t
= page_contents
->words
.get_data();
5221 } while ((! page_contents
->words
.is_equal_to_head()) &&
5222 (vpos
< indentation
.vertical_limit
) && (vpos
>= last
));
5223 page_contents
->words
.move_left();
5224 t
= page_contents
->words
.get_data();
5225 rewind_text_to(start
);
5226 return( indentation
.vertical_limit
);
5231 * assign_used_columns - sets the is_used field of the column array of records.
5234 void html_printer::assign_used_columns (text_glob
*start
)
5236 text_glob
*t
= start
;
5239 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
5240 indentation
.columns
[i
].is_used
= FALSE
;
5243 rewind_text_to(start
);
5244 if (! page_contents
->words
.is_empty()) {
5246 i
= find_column_index(t
);
5247 if (indentation
.columns
[i
].left
!= 0) {
5248 if (debug_table_on
) {
5249 fprintf(stderr
, "[%s] in column %d at %d..%d limit %d\n", t
->text_string
,
5250 i
, t
->minv
, t
->maxv
, indentation
.vertical_limit
); fflush(stderr
);
5252 indentation
.columns
[i
].is_used
= TRUE
;
5254 page_contents
->words
.move_right();
5255 t
= page_contents
->words
.get_data();
5256 } while ((t
->minv
<indentation
.vertical_limit
) &&
5257 (! page_contents
->words
.is_equal_to_head()));
5259 if (debug_table_on
) {
5260 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
5261 fprintf(stderr
, " <left=%d right=%d is_used=%d> ",
5262 indentation
.columns
[i
].left
,
5263 indentation
.columns
[i
].right
,
5264 indentation
.columns
[i
].is_used
);
5266 fprintf(stderr
, "\n");
5272 * adjust_margin_percentages - so far we have ignored the header_indent
5273 * and just considered left_margin_indent..right_margin_indent.
5274 * (We do this since we can assume 100% is total width for main text).
5275 * However as header_indent can be < left_margin_indent we need to
5276 * recalculate the real percentages in the light of the extended width.
5279 void html_printer::adjust_margin_percentages (void)
5281 if ((header_indent
< left_margin_indent
) && (header_indent
!= -1)) {
5283 * recalculation necessary
5287 while (i
<indentation
.no_of_columns
) {
5288 indentation
.columns
[i
].percent
= (indentation
.columns
[i
].percent
*
5289 (right_margin_indent
- left_margin_indent
)) /
5290 (right_margin_indent
- header_indent
);
5293 // remove_zero_percentage_column();
5298 * foreach_column_include_text - foreach column in a table place the
5299 * appropriate html text.
5302 void html_printer::foreach_column_include_text (text_glob
*start
)
5304 if (indentation
.no_of_columns
>0) {
5310 rewind_text_to(start
);
5311 count_right_hits(indentation
.columns
, indentation
.no_of_columns
);
5312 rewind_text_to(start
);
5315 limit
= determine_row_limit(start
, limit
); // find the bottom of the next row
5316 html
.put_string("<tr valign=\"top\" align=\"left\">\n");
5318 start
= page_contents
->words
.get_data();
5319 while (i
<indentation
.no_of_columns
) {
5320 // reset the output position to the start of column
5321 rewind_text_to(start
);
5322 output_vpos
= start
->minv
;
5323 output_hpos
= indentation
.columns
[i
].left
;
5324 // and display each column until limit
5325 right
= column_calculate_right_margin(indentation
.columns
[i
].left
,
5326 indentation
.columns
[i
].right
);
5327 left
= column_calculate_left_margin(indentation
.columns
[i
].left
,
5328 indentation
.columns
[i
].right
);
5330 if (right
>indentation
.columns
[i
].right
) {
5332 fprintf(stderr
, "assert calculated right column edge is greater than column\n"); fflush(stderr
);
5337 if (left
<indentation
.columns
[i
].left
) {
5339 fprintf(stderr
, "assert calculated left column edge is less than column\n"); fflush(stderr
);
5344 if ((indentation
.columns
[i
].right_hits
== 1) &&
5345 (indentation
.columns
[i
].right
!= right_margin_indent
)) {
5346 indentation
.wrap_margin
= FALSE
;
5348 fprintf(stderr
, "turning auto wrap off during column %d for start word %s\n",
5349 i
, start
->text_string
);
5354 indentation
.wrap_margin
= TRUE
;
5357 column_display_word(i
, limit
, left
, right
, indentation
.columns
[i
].right
);
5361 if (page_contents
->words
.is_equal_to_tail()) {
5364 page_contents
->words
.sub_move_right();
5365 if (page_contents
->words
.is_empty()) {
5368 start
= page_contents
->words
.get_data();
5372 html
.put_string("</tr>\n");
5373 } while (((limit
< indentation
.vertical_limit
) && (start
!= 0) &&
5374 (! page_contents
->words
.is_empty())) || (limit
== -1));
5378 // finished page remove all words
5379 page_contents
->words
.start_from_head();
5380 while (! page_contents
->words
.is_empty()) {
5381 page_contents
->words
.sub_move_right();
5383 } else if (! page_contents
->words
.is_empty()) {
5384 page_contents
->words
.move_left();
5390 * write_centered_line - generates a line of centered text.
5393 void html_printer::write_centered_line (text_glob
*g
)
5395 int current_vpos
=g
->minv
;
5397 move_vertical(g
, center_alignment
);
5399 header
.written_header
= FALSE
;
5400 supress_sub_sup
= TRUE
;
5401 output_vpos
= g
->minv
;
5402 output_hpos
= g
->minh
;
5404 char *postword
=html_position_text(g
, left_margin_indent
, right_margin_indent
);
5406 if (! header
.written_header
) {
5407 if (g
->is_raw_command
) {
5408 html
.put_string((char *)g
->text_string
);
5410 translate_to_html(g
);
5412 if (postword
!= 0) {
5413 html
.put_string(postword
);
5415 need_one_newline
= TRUE
;
5416 issued_newline
= FALSE
;
5418 page_contents
->words
.move_right();
5419 g
= page_contents
->words
.get_data();
5420 } while ((! page_contents
->words
.is_equal_to_head()) && (is_on_same_line(g
, current_vpos
)));
5421 page_contents
->words
.move_left(); // so when we move right we land on the word following this centered line
5422 need_one_newline
= TRUE
;
5426 * is_in_middle - returns TRUE if the text defn, t, is in the middle of the page.
5429 int html_printer::is_in_middle (int left
, int right
)
5431 return( abs(abs(left
-left_margin_indent
) - abs(right_margin_indent
-right
)) <= CENTER_TOLERANCE
);
5435 * single_centered_line - returns TRUE if first is a centered line with a different
5439 int html_printer::single_centered_line (text_defn
*first
, text_defn
*second
, text_glob
*g
)
5442 ((count_columns(first
) == 1) && (first
[0].left
!= left_margin_indent
) &&
5443 (first
[0].left
!= second
[0].left
) && is_in_middle(first
->left
, first
->right
))
5448 * check_able_to_use_center - returns TRUE if we can see a centered line.
5451 int html_printer::check_able_to_use_center (text_glob
*g
)
5453 if (auto_on
&& table_on
&& ((! is_on_same_line(g
, output_vpos
)) || issued_newline
) && (! using_table_for_indent())) {
5454 // we are allowed to check for centered line
5455 // first check to see whether we might be looking at a set of columns
5456 struct text_defn last_guess
[MAX_WORDS_PER_LINE
];
5457 struct text_defn last_words
[MAX_WORDS_PER_LINE
];
5459 collect_columns(last_words
, last_guess
, 0, 0, MAX_WORDS_PER_LINE
);
5462 if ((count_columns(last_guess
) == 1) && (is_in_middle(last_guess
[0].left
, last_guess
[0].right
))) {
5463 write_centered_line(g
);
5471 * check_able_to_use_table - examines forthcoming text to see whether we can
5472 * better format it by using an html transparent table.
5475 int html_printer::check_able_to_use_table (text_glob
*g
)
5477 if (auto_on
&& ((! is_on_same_line(g
, output_vpos
)) || issued_newline
) && (! using_table_for_indent())) {
5478 // we are allowed to check for table
5480 if ((output_hpos
!= right_margin_indent
) && (found_use_for_table(g
))) {
5481 foreach_column_include_text(g
);
5489 * move_vertical - if we are using html auto formatting then decide whether to
5490 * break the line via a <br> or a </p><p> sequence.
5493 void html_printer::move_vertical (text_glob
*g
, paragraph_type p
)
5496 int height
= (g
->text_style
.point_size
+2)*r
/72; // --fixme-- we always assume VS is PS+2 (could do better)
5500 if ((more_than_line_break(output_vpos
, g
->minv
, height
)) || (p
!= current_paragraph
->para_type
)) {
5507 if (output_vpos
== -1) {
5508 temp_vpos
= g
->minv
;
5510 temp_vpos
= output_vpos
;
5513 force_begin_paragraph();
5514 if (need_one_newline
) {
5516 temp_vpos
+= height
;
5518 need_one_newline
= TRUE
;
5521 while ((temp_vpos
< g
->minv
) && (more_than_line_break(temp_vpos
, g
->minv
, height
))) {
5523 temp_vpos
+= height
;
5529 * emit_space - emits a space within html, it checks for the font type and
5530 * will change font depending upon, g. Courier spaces are larger
5531 * than roman so we need consistancy when changing between them.
5534 void html_printer::emit_space (text_glob
*g
, int force_space
)
5536 if (! current_paragraph
->need_paragraph
) {
5537 // only generate a space if we have written a word - as html will ignore it otherwise
5538 if ((output_style
!= g
->text_style
) && (g
->text_style
.f
!= 0)) {
5539 terminate_current_font();
5541 if (force_space
|| (g
->minh
> output_hpos
)) {
5542 html
.put_string(" ");
5544 change_font(g
, TRUE
);
5549 * html_position_text - determine whether the text is subscript/superscript/normal
5553 char *html_printer::html_position_text (text_glob
*g
, int left_margin
, int right_margin
)
5557 begin_paragraph(left_alignment
);
5559 if ((! header
.written_header
) &&
5560 (is_on_same_line(g
, output_vpos
) ||
5561 pretend_is_on_same_line(g
, left_margin
, right_margin
))) {
5564 * check whether we should supress superscripts and subscripts.
5565 * I guess we might be able to do better by examining text on this line
5569 if ((! is_on_same_line(g
, output_vpos
)) && (pretend_is_on_same_line(g
, left_margin
, right_margin
))) {
5570 supress_sub_sup
= TRUE
;
5572 header
.written_header
= FALSE
;
5573 force_begin_paragraph();
5575 // check whether we need to insert white space between words on 'same' line
5576 if (pretend_is_on_same_line(g
, left_margin
, right_margin
)) {
5577 emit_space(g
, TRUE
);
5580 // check whether the font was reset after generating an image
5581 if (output_style
.f
== 0) {
5582 change_font(g
, TRUE
);
5585 if (looks_like_subscript(g
)) {
5587 g
->text_style
.point_size
= output_style
.point_size
;
5588 g
->minv
= output_vpos
; // this ensures that output_vpos doesn't alter
5589 // which allows multiple subscripted words
5590 move_horizontal(g
, left_margin
);
5591 html
.put_string("<sub>");
5592 postword
= "</sub>";
5593 } else if (looks_like_superscript(g
)) {
5595 g
->text_style
.point_size
= output_style
.point_size
;
5596 g
->minv
= output_vpos
;
5598 move_horizontal(g
, left_margin
);
5599 html
.put_string("<sup>");
5600 postword
= "</sup>";
5602 move_horizontal(g
, left_margin
);
5604 supress_sub_sup
= FALSE
;
5606 // we have found a new line
5607 if (! header
.written_header
) {
5608 move_vertical(g
, left_alignment
);
5610 header
.written_header
= FALSE
;
5612 if (processed_header(g
)) {
5613 // we must not alter output_vpos as we have peeped at the next word
5614 // and set vpos to this - to ensure we do not generate a <br> after
5615 // a heading. (The html heading automatically generates a line break)
5616 output_hpos
= left_margin
;
5619 force_begin_paragraph();
5620 if (g
->minh
-left_margin
!= 0) {
5621 make_html_indent(g
->minh
-left_margin
);
5623 change_font(g
, TRUE
);
5624 supress_sub_sup
= FALSE
;
5627 output_vpos
= g
->minv
;
5628 output_hpos
= g
->maxh
;
5633 int html_printer::html_position_region (void)
5636 int height
= output_style
.point_size
*r
/72;
5638 int is_center
= FALSE
;
5640 if (output_style
.point_size
!= 0) {
5641 if (output_vpos
!= start_region_vpos
) {
5643 // graphic starts on a different line
5644 if (output_vpos
== -1) {
5645 temp_vpos
= start_region_vpos
;
5647 temp_vpos
= output_vpos
;
5649 supress_sub_sup
= TRUE
;
5650 if (need_one_newline
) {
5652 temp_vpos
+= height
;
5654 need_one_newline
= TRUE
;
5657 while ((temp_vpos
< start_region_vpos
) &&
5658 (more_than_line_break(temp_vpos
, start_region_vpos
, height
))) {
5660 temp_vpos
+= height
;
5664 if (auto_on
&& (is_in_middle(start_region_hpos
, end_region_hpos
))) {
5667 if (start_region_hpos
> get_left()) {
5668 make_html_indent(start_region_hpos
-get_left());
5671 output_vpos
= start_region_vpos
;
5672 output_hpos
= start_region_hpos
;
5673 return( is_center
);
5677 * gs_x - translate and scale the x axis
5680 int html_printer::gs_x (int x
)
5682 x
+= IMAGE_BOARDER_PIXELS
/2;
5683 return((x
-start_region_hpos
)*postscript_res
/font::res
);
5688 * gs_y - translate and scale the y axis
5691 int html_printer::gs_y (int y
)
5693 int yoffset
=((int)(A4_PAGE_LENGTH
*(double)font::res
))-end_region_vpos
;
5695 y
+= IMAGE_BOARDER_PIXELS
/2;
5696 return( (y
+yoffset
)*postscript_res
/font::res
);
5700 void html_printer::troff_position_text (text_glob
*g
)
5702 change_font(g
, FALSE
);
5704 troff
.put_string("V");
5705 troff
.put_number(gs_y(g
->maxv
));
5706 troff
.put_string("\n");
5708 troff
.put_string("H");
5709 troff
.put_number(gs_x(g
->minh
));
5710 troff
.put_string("\n");
5713 void html_printer::troff_change_font (const char *fontname
, int size
, int font_no
)
5715 troff
.put_string("x font ");
5716 troff
.put_number(font_no
);
5717 troff
.put_string(" ");
5718 troff
.put_string(fontname
);
5719 troff
.put_string("\nf");
5720 troff
.put_number(font_no
);
5721 troff
.put_string("\ns");
5722 troff
.put_number(size
*1000);
5723 troff
.put_string("\n");
5727 void html_printer::set_style(const style
&sty
)
5730 const char *fontname
= sty
.f
->get_name();
5732 fatal("no internalname specified for font");
5734 change_font(fontname
, (font::res
/(72*font::sizescale
))*sty
.point_size
);
5738 void html_printer::end_of_line()
5744 void html_printer::html_display_word (text_glob
*g
)
5747 if (strcmp(g
->text_string
, "ot") == 0) {
5751 if (! check_able_to_use_table(g
)) {
5752 char *postword
=html_position_text(g
, left_margin_indent
, right_margin_indent
);
5754 if (! header
.written_header
) {
5755 if (g
->is_raw_command
) {
5756 html
.put_string((char *)g
->text_string
);
5758 translate_to_html(g
);
5760 if (postword
!= 0) {
5761 html
.put_string(postword
);
5763 need_one_newline
= TRUE
;
5764 issued_newline
= FALSE
;
5769 void html_printer::troff_display_word (text_glob
*g
)
5771 troff_position_text(g
);
5772 if (g
->is_raw_command
) {
5773 int l
=strlen((char *)g
->text_string
);
5775 troff
.put_string("c");
5776 troff
.put_string((char *)g
->text_string
);
5777 troff
.put_string("\n");
5779 troff
.put_string("C");
5780 troff
.put_troffps_char((char *)g
->text_string
);
5781 troff
.put_string("\n");
5784 troff_position_text(g
);
5785 troff
.put_string("t");
5786 troff
.put_translated_string((const char *)g
->text_string
);
5787 troff
.put_string("\n");
5791 void html_printer::display_word (text_glob
*g
, int is_to_html
)
5794 html_display_word(g
);
5795 } else if ((g
->is_raw_command
) && (g
->is_html_command
)) {
5796 // found a raw html command inside a graphic glob.
5797 // We should emit the command to the html device, but of course we
5798 // cannot place it correctly as we are dealing with troff words.
5799 // Remember output_vpos will refer to troff and not html.
5800 html
.put_string((char *)g
->text_string
);
5802 troff_display_word(g
);
5807 * translate_to_html - translates a textual string into html text
5810 void html_printer::translate_to_html (text_glob
*g
)
5812 char buf
[MAX_STRING_LENGTH
];
5814 str_translate_to_html(g
->text_style
.f
, buf
, MAX_STRING_LENGTH
,
5815 g
->text_string
, g
->text_length
, TRUE
);
5816 html
.put_string(buf
);
5820 * html_knows_about - given a character name, troff, return TRUE
5821 * if we know how to display this character using
5825 int html_printer::html_knows_about (char *troff
)
5827 // --fixme-- needs to have similar code as above
5832 * display_fill - generates a troff format fill command
5835 void html_printer::display_fill (graphic_glob
*g
)
5837 troff
.put_string("Df ") ;
5838 troff
.put_number(g
->fill
);
5839 troff
.put_string(" 0\n");
5843 * display_line - displays a line using troff format
5846 void html_printer::display_line (graphic_glob
*g
, int is_to_html
)
5849 fatal("cannot emit lines in html");
5851 if (g
->code
== 'l') {
5854 troff
.put_string("V");
5855 troff
.put_number(gs_y(g
->point
[0].y
));
5856 troff
.put_string("\n");
5858 troff
.put_string("H");
5859 troff
.put_number(gs_x(g
->point
[0].x
));
5860 troff
.put_string("\n");
5864 troff
.put_string("Dl ");
5865 troff
.put_number((g
->point
[1].x
-g
->point
[0].x
)*postscript_res
/font::res
);
5866 troff
.put_string(" ");
5867 troff
.put_number((g
->point
[1].y
-g
->point
[0].y
)*postscript_res
/font::res
);
5868 troff
.put_string("\n");
5869 // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y,
5870 // g->point[1].x, g->point[1].y, g->size);
5871 } else if ((g
->code
== 'c') || (g
->code
== 'C')) {
5874 int xradius
= (g
->maxh
- g
->minh
) / 2;
5875 int yradius
= (g
->maxv
- g
->minv
) / 2;
5876 // center of circle or elipse
5878 troff
.put_string("V");
5879 troff
.put_number(gs_y(g
->minv
+yradius
));
5880 troff
.put_string("\n");
5882 troff
.put_string("H");
5883 troff
.put_number(gs_x(g
->minh
));
5884 troff
.put_string("\n");
5888 if (g
->code
== 'c') {
5889 troff
.put_string("Dc ");
5891 troff
.put_string("DC ");
5894 troff
.put_number(xradius
*2*postscript_res
/font::res
);
5895 troff
.put_string("\n");
5897 } else if ((g
->code
== 'e') || (g
->code
== 'E')) {
5900 int xradius
= (g
->maxh
- g
->minh
) / 2;
5901 int yradius
= (g
->maxv
- g
->minv
) / 2;
5902 // center of elipse - this is untested
5904 troff
.put_string("V");
5905 troff
.put_number(gs_y(g
->minv
+yradius
));
5906 troff
.put_string("\n");
5908 troff
.put_string("H");
5909 troff
.put_number(gs_x(g
->minh
));
5910 troff
.put_string("\n");
5914 if (g
->code
== 'e') {
5915 troff
.put_string("De ");
5917 troff
.put_string("DE ");
5920 troff
.put_number(xradius
*2*postscript_res
/font::res
);
5921 troff
.put_string(" ");
5922 troff
.put_number(yradius
*2*postscript_res
/font::res
);
5923 troff
.put_string("\n");
5924 } else if ((g
->code
== 'p') || (g
->code
== 'P')) {
5926 troff
.put_string("V");
5927 troff
.put_number(gs_y(g
->yc
));
5928 troff
.put_string("\n");
5930 troff
.put_string("H");
5931 troff
.put_number(gs_x(g
->xc
));
5932 troff
.put_string("\n");
5936 if (g
->code
== 'p') {
5937 troff
.put_string("Dp");
5939 troff
.put_string("DP");
5945 for (i
=0; i
<g
->nopoints
; i
++) {
5946 troff
.put_string(" ");
5947 troff
.put_number((g
->point
[i
].x
-xc
)*postscript_res
/font::res
);
5948 troff
.put_string(" ");
5949 troff
.put_number((g
->point
[i
].y
-yc
)*postscript_res
/font::res
);
5953 troff
.put_string("\n");
5954 } else if (g
->code
== 'a') {
5956 troff
.put_string("V");
5957 troff
.put_number(gs_y(g
->yc
));
5958 troff
.put_string("\n");
5960 troff
.put_string("H");
5961 troff
.put_number(gs_x(g
->xc
));
5962 troff
.put_string("\n");
5966 troff
.put_string("Da");
5970 for (i
=0; i
<g
->nopoints
; i
++) {
5971 troff
.put_string(" ");
5972 troff
.put_number(g
->point
[i
].x
*postscript_res
/font::res
);
5973 troff
.put_string(" ");
5974 troff
.put_number(g
->point
[i
].y
*postscript_res
/font::res
);
5976 troff
.put_string("\n");
5977 } else if (g
->code
== '~') {
5979 troff
.put_string("V");
5980 troff
.put_number(gs_y(g
->yc
));
5981 troff
.put_string("\n");
5983 troff
.put_string("H");
5984 troff
.put_number(gs_x(g
->xc
));
5985 troff
.put_string("\n");
5989 troff
.put_string("D~");
5994 for (i
=0; i
<g
->nopoints
; i
++) {
5995 troff
.put_string(" ");
5996 troff
.put_number((g
->point
[i
].x
-xc
)*postscript_res
/font::res
);
5997 troff
.put_string(" ");
5998 troff
.put_number((g
->point
[i
].y
-yc
)*postscript_res
/font::res
);
6002 troff
.put_string("\n");
6008 * flush_sbuf - flushes the current sbuf into the list of glyphs.
6011 void html_printer::flush_sbuf()
6014 int r
=font::res
; // resolution of the device
6015 set_style(sbuf_style
);
6017 page_contents
->add(&sbuf_style
, sbuf
, sbuf_len
,
6018 sbuf_vpos
-sbuf_style
.point_size
*r
/72, sbuf_start_hpos
,
6019 sbuf_vpos
, sbuf_end_hpos
);
6021 output_hpos
= sbuf_end_hpos
;
6022 output_vpos
= sbuf_vpos
;
6024 sbuf_dmark_hpos
= -1;
6029 void html_printer::set_line_thickness(const environment
*env
)
6031 line_thickness
= env
->size
;
6032 printf("line thickness = %d\n", line_thickness
);
6035 void html_printer::draw(int code
, int *p
, int np
, const environment
*env
)
6041 page_contents
->add_line(code
,
6042 env
->hpos
, env
->vpos
, env
->hpos
+p
[0], env
->vpos
+p
[1],
6045 error("2 arguments required for line");
6051 line_thickness
= -1;
6053 // troff gratuitously adds an extra 0
6054 if (np
!= 1 && np
!= 2) {
6055 error("0 or 1 argument required for thickness");
6058 line_thickness
= p
[0];
6068 error("even number of arguments required for polygon");
6072 error("no arguments for polygon");
6075 // firstly lets add our current position to polygon
6087 // now store polygon in page
6088 page_contents
->add_polygon(code
, np
, p
, env
->hpos
, env
->vpos
, env
->size
, fill
);
6095 error("2 arguments required for ellipse");
6098 page_contents
->add_line(code
,
6099 env
->hpos
, env
->vpos
-p
[1]/2, env
->hpos
+p
[0], env
->vpos
+p
[1]/2,
6108 // troff adds an extra argument to C
6109 if (np
!= 1 && !(code
== 'C' && np
== 2)) {
6110 error("1 argument required for circle");
6113 page_contents
->add_line(code
,
6114 env
->hpos
, env
->vpos
-p
[0]/2, env
->hpos
+p
[0], env
->vpos
+p
[0]/2,
6123 if (adjust_arc_center(p
, c
)) {
6124 page_contents
->add_arc('a', env
->hpos
, env
->vpos
, p
, c
, env
->size
, fill
);
6127 page_contents
->add_line('l', env
->hpos
, env
->vpos
, p
[0]+p
[2], p
[1]+p
[3], env
->size
, fill
);
6130 error("4 arguments required for arc");
6137 error("even number of arguments required for spline");
6141 error("no arguments for spline");
6144 // firstly lets add our current position to spline
6156 page_contents
->add_spline('~', env
->hpos
, env
->vpos
, np
, p
, env
->size
, fill
);
6161 if (np
!= 1 && np
!= 2) {
6162 error("1 argument required for fill");
6166 if (fill
< 0 || fill
> FILL_MAX
) {
6167 // This means fill with the current color.
6168 fill
= FILL_MAX
+ 1;
6174 error("unrecognised drawing command `%1'", char(code
));
6180 void html_printer::begin_page(int n
)
6183 html
.begin_comment("Page: ").comment_arg(itoa(page_number
)).end_comment();;
6184 no_of_printed_pages
++;
6187 output_space_code
= 32;
6188 output_draw_point_size
= -1;
6189 output_line_thickness
= -1;
6194 void testing (text_glob
*g
) {}
6196 void html_printer::flush_graphic (void)
6201 page_contents
->is_in_graphic
= FALSE
;
6205 calculate_region_range(&g
);
6207 page_contents
->make_new_region(&g
);
6209 move_region_to_page();
6212 void html_printer::end_page(int)
6219 font
*html_printer::make_font(const char *nm
)
6221 return html_font::load_html_font(nm
);
6224 html_printer::~html_printer()
6226 if (fseek(tempfp
, 0L, 0) < 0)
6227 fatal("fseek on temporary file failed");
6228 html
.set_file(stdout
);
6229 fputs("<html>\n", stdout
);
6230 fputs("<head>\n", stdout
);
6231 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout
);
6233 fputs("</head>\n", stdout
);
6234 fputs("<body>\n", stdout
);
6236 header
.write_headings(stdout
);
6238 extern const char *Version_string
;
6239 html
.begin_comment("Creator : ")
6240 .comment_arg("groff ")
6241 .comment_arg("version ")
6242 .comment_arg(Version_string
)
6246 #ifdef LONG_FOR_TIME_T
6252 html
.begin_comment("CreationDate: ")
6253 .comment_arg(ctime(&t
))
6256 html
.begin_comment("Total number of pages: ").comment_arg(itoa(no_of_printed_pages
)).end_comment();
6258 html
.copy_file(tempfp
);
6259 fputs("</body>\n", stdout
);
6260 fputs("</html>\n", stdout
);
6266 * calculate_region_range - calculates the vertical range for words and lines
6267 * within the region lists.
6270 void html_printer::calculate_region_range (graphic_glob
*r
)
6275 if (! page_contents
->region_lines
.is_empty()) {
6276 page_contents
->region_lines
.start_from_head();
6278 g
= page_contents
->region_lines
.get_data();
6279 if ((r
->minv
== -1) || (g
->minv
< r
->minv
)) {
6282 if ((r
->maxv
== -1) || (g
->maxv
> r
->maxv
)) {
6285 page_contents
->region_lines
.move_right();
6286 } while (! page_contents
->region_lines
.is_equal_to_head());
6288 if (! page_contents
->region_words
.is_empty()) {
6289 page_contents
->region_words
.start_from_head();
6291 w
= page_contents
->region_words
.get_data();
6293 if ((r
->minv
== -1) || (w
->minv
< r
->minv
)) {
6296 if ((r
->maxv
== -1) || (w
->maxv
> r
->maxv
)) {
6299 page_contents
->region_words
.move_right();
6300 } while (! page_contents
->region_words
.is_equal_to_head());
6306 * move_region_to_page - moves lines and words held in the temporary region
6307 * list to the page list.
6310 void html_printer::move_region_to_page (void)
6315 page_contents
->region_lines
.start_from_head();
6316 while (! page_contents
->region_lines
.is_empty()) {
6317 g
= page_contents
->region_lines
.get_data(); // remove from our temporary region list
6318 page_contents
->lines
.add(g
); // and add to the page list
6319 page_contents
->region_lines
.sub_move_right();
6321 page_contents
->region_words
.start_from_head();
6322 while (! page_contents
->region_words
.is_empty()) {
6323 w
= page_contents
->region_words
.get_data(); // remove from our temporary region list
6324 page_contents
->words
.add(w
); // and add to the page list
6325 page_contents
->region_words
.sub_move_right();
6330 * is_graphic_start - returns TRUE if the start of table, pic, eqn was seen.
6333 int is_graphic_start (char *s
)
6335 return( (strcmp(s
, "graphic-start") == 0) ||
6336 ((strcmp(s
, "table-start") == 0) && (table_image_on
)) );
6340 * is_graphic_end - return TRUE if the end of a table, pic, eqn was seen.
6343 int is_graphic_end (char *s
)
6345 return( (strcmp(s
, "graphic-end") == 0) ||
6346 ((strcmp(s
, "table-end") == 0) && (table_image_on
)) );
6350 * special - handle all x X requests from troff. For grohtml they allow users
6351 * to pass raw html commands, turn auto linked headings off/on and
6352 * also allow tbl, eqn & pic say what commands they have generated.
6355 void html_printer::special(char *s
, const environment
*env
)
6358 if (is_graphic_start(s
)) {
6360 if (graphic_level
== 1) {
6361 page_contents
->is_in_graphic
= TRUE
; // add words and lines to temporary region lists
6363 } else if (is_graphic_end(s
) && (graphic_level
> 0)) {
6365 if (graphic_level
== 0) {
6368 } else if (strncmp(s
, "html:", 5) == 0) {
6369 int r
=font::res
; // resolution of the device
6370 char buf
[MAX_STRING_LENGTH
];
6371 font
*f
=sbuf_style
.f
;
6376 f
= font::load_font("TR", &found
);
6378 str_translate_to_html(f
, buf
, MAX_STRING_LENGTH
,
6379 &s
[5], strlen(s
)-5, FALSE
);
6380 page_contents
->add_html_command(&sbuf_style
, buf
, strlen(buf
),
6382 // need to pass rest of string through to html output during flush
6384 env
->vpos
-env
->size
*r
/72, env
->hpos
,
6385 env
->vpos
, env
->hpos
);
6386 // assume that the html command has no width, if it does then we hopefully troff
6387 // will have fudged this in a macro and requested that the formatting move right by
6388 // the appropriate width
6389 } else if (strncmp(s
, "index:", 6) == 0) {
6390 cutoff_heading
= atoi(&s
[6]);
6395 void set_image_type (char *type
)
6397 if (strcmp(type
, "gif") == 0) {
6399 } else if (strcmp(type
, "png") == 0) {
6401 image_device
= "png256";
6402 } else if (strncmp(type
, "png", 3) == 0) {
6404 image_device
= type
;
6408 printer
*make_printer()
6410 return new html_printer
;
6413 static void usage();
6415 int main(int argc
, char **argv
)
6417 program_name
= argv
[0];
6418 static char stderr_buf
[BUFSIZ
];
6419 setbuf(stderr
, stderr_buf
);
6421 while ((c
= getopt(argc
, argv
, "F:atTvdgmx?I:r:")) != EOF
)
6425 extern const char *Version_string
;
6426 fprintf(stderr
, "grohtml version %s\n", Version_string
);
6437 table_image_on
= FALSE
;
6440 font::command_line_font_dir(optarg
);
6443 // user specifying the type of images we should generate
6444 set_image_type(optarg
);
6447 // resolution (dots per inch for an image)
6448 image_res
= atoi(optarg
);
6455 debug_table_on
= TRUE
;
6458 // do not guess title and headings
6462 // leave margins alone
6471 if (optind
>= argc
) {
6474 for (int i
= optind
; i
< argc
; i
++)
6483 fprintf(stderr
, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n",