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"
39 // SunOS 4.1.3 fails to declare this in stdlib.h
46 #include "ordered_list.h"
55 #define MAX_TEMP_NAME 1024
56 #define MAX_STRING_LENGTH 4096
57 #define MAX_CHAR_SIZE 50 // maximum length of character name
59 #define Y_FUDGE_MARGIN +0.83
60 #define A4_PAGE_LENGTH (11.6944-Y_FUDGE_MARGIN)
61 #define DEFAULT_IMAGE_RES 80
62 #define IMAGE_BOARDER_PIXELS 10
63 #define MAX_WORDS_PER_LINE 1000 // only used for table indentation
64 #define GAP_SPACES 3 // how many spaces needed to guess a gap?
65 #define GAP_WIDTH_ONE_LINE 2 // 1/GAP_WIDTH_ONE_LINE inches required for one line table
66 #define CENTER_TOLERANCE 2 // how many pixels off center will we think a line or region is centered
67 #define MIN_COLUMN 7 // minimum column size pixels for multiple lines
68 #define MIN_COLUMN_FOR_TWO_LINES 20 // minimum column size pixels for a 2 line table
69 #define MIN_TEXT_PERCENT 5 // try and round to this percentage value for used columns
70 #define PERCENT_THRESHOLD 20 // don't bother trying to increase and width greater than this
74 * Only uncomment one of the following to determine default image type.
77 #define IMAGE_DEFAULT_PNG
78 /* #define IMAGE_DEFAULT_GIF */
81 #if defined(IMAGE_DEFAULT_GIF)
82 static enum { gif
, png
} image_type
= gif
;
83 static char *image_device
= "gif";
84 #elif defined(IMAGE_DEFAULT_PNG)
85 static enum { gif
, png
} image_type
= png
;
86 static char *image_device
= "png256";
88 # error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG"
91 static int debug_on
= FALSE
;
92 static int guess_on
= TRUE
;
93 static int margin_on
= FALSE
;
94 static int auto_on
= TRUE
;
95 static int table_on
= TRUE
;
96 static int image_res
= DEFAULT_IMAGE_RES
;
97 static int debug_table_on
= FALSE
;
98 static int table_image_on
= TRUE
; // default is to create images for tbl
100 static int linewidth
= -1;
102 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
103 #define MAX_LINE_LENGTH 72
104 #define FILL_MAX 1000
110 * start with a few favorites
113 static int min (int a
, int b
)
122 static int max (int a
, int b
)
132 * is_subsection - returns TRUE if a1..a2 is within b1..b2
135 static int is_subsection (int a1
, int a2
, int b1
, int b2
)
137 // easier to see whether this is not the case
138 return( !((a1
< b1
) || (a1
> b2
) || (a2
< b1
) || (a2
> b2
)) );
142 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
145 static int is_intersection (int a1
, int a2
, int b1
, int b2
)
147 // again easier to prove NOT outside limits
148 return( ! ((a1
> b2
) || (a2
< b1
)) );
152 * is_digit - returns TRUE if character, ch, is a digit.
155 static int is_digit (char ch
)
157 return( (ch
>= '0') && (ch
<= '9') );
161 * more_than_line_break - returns TRUE should v1 and v2 differ by more than
162 * a simple line break.
165 static int more_than_line_break (int v1
, int v2
, int size
)
167 return( abs(v1
-v2
)>size
);
171 * the class and methods for styles
181 style (font
*, int, int, int, int);
182 int operator == (const style
&) const;
183 int operator != (const style
&) const;
191 style::style(font
*p
, int sz
, int h
, int sl
, int no
)
192 : f(p
), point_size(sz
), font_no(no
), height(h
), slant(sl
)
196 int style::operator==(const style
&s
) const
198 return (f
== s
.f
&& point_size
== s
.point_size
199 && height
== s
.height
&& slant
== s
.slant
);
202 int style::operator!=(const style
&s
) const
204 return !(*this == s
);
209 * the class and methods for retaining ascii text
221 char_block::char_block()
230 char *add_string(char *, unsigned int);
236 char_buffer::char_buffer()
241 char_buffer::~char_buffer()
244 char_block
*temp
= head
;
250 char *char_buffer::add_string (char *s
, unsigned int length
)
253 unsigned int old_used
;
256 tail
= new char_block
;
259 if (tail
->used
+ length
+1 > char_block::SIZE
) {
260 tail
->next
= new char_block
;
264 // at this point we have a tail which is ready for the string.
265 if (tail
->used
+ length
+1 > char_block::SIZE
) {
266 fatal("need to increase char_block::SIZE");
269 old_used
= tail
->used
;
271 tail
->buffer
[tail
->used
] = s
[i
];
277 // add terminating nul character
279 tail
->buffer
[tail
->used
] = '\0';
282 // and return start of new string
284 return( &tail
->buffer
[old_used
] );
288 * the classes and methods for maintaining pages and text positions and graphic regions
293 int is_less (text_glob
*a
, text_glob
*b
);
294 text_glob (style
*s
, char *string
, unsigned int length
,
295 int min_vertical
, int min_horizontal
,
296 int max_vertical
, int max_horizontal
, int is_command
, int is_html
);
302 unsigned int text_length
;
303 int minv
, maxv
, minh
, maxh
;
304 int is_raw_command
; // should the text be sent directly to the device?
305 int is_html_command
; // is the raw command definitely for the html device ie not an eqn?
308 text_glob::text_glob (style
*s
, char *string
, unsigned int length
,
309 int min_vertical
, int min_horizontal
,
310 int max_vertical
, int max_horizontal
, int is_command
, int is_html
)
311 : text_style(*s
), text_string(string
), text_length(length
),
312 minv(min_vertical
), maxv(max_vertical
), minh(min_horizontal
), maxh(max_horizontal
),
313 is_raw_command(is_command
), is_html_command(is_html
)
317 text_glob::text_glob ()
318 : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
319 is_raw_command(FALSE
), is_html_command(FALSE
)
323 text_glob::~text_glob ()
327 int text_glob::is_less (text_glob
*a
, text_glob
*b
)
329 if (is_intersection(a
->minv
+1, a
->maxv
-1, b
->minv
+1, b
->maxv
-1)) {
330 return( a
->minh
< b
->minh
);
332 return( a
->maxv
< b
->maxv
);
343 int is_less (graphic_glob
*a
, graphic_glob
*b
);
344 graphic_glob (int troff_code
);
346 ~graphic_glob (void);
348 int minv
, maxv
, minh
, maxh
;
350 int nopoints
; // number of points allocated in array below
351 struct xycoord
*point
;
357 graphic_glob::graphic_glob ()
358 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(0)
362 graphic_glob::~graphic_glob ()
369 graphic_glob::graphic_glob (int troff_code
)
370 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(troff_code
)
374 int graphic_glob::is_less (graphic_glob
*a
, graphic_glob
*b
)
376 return( (a
->minv
< b
->minv
) || ((a
->minv
== b
->minv
) && (a
->minh
< b
->minh
)) );
383 int is_less (region_glob
*a
, region_glob
*b
);
385 int minv
, maxv
, minh
, maxh
;
388 int region_glob::is_less (region_glob
*a
, region_glob
*b
)
390 return( (a
->minv
< b
->minv
) || ((a
->minv
== b
->minv
) && (a
->minh
< b
->minh
)) );
393 region_glob::region_glob (void)
394 : minv(-1), maxv(-1), minh(-1), maxh(-1)
398 region_glob::~region_glob (void)
405 void add (style
*s
, char *string
, unsigned int length
,
406 int min_vertical
, int min_horizontal
,
407 int max_vertical
, int max_horizontal
);
408 void add_html_command (style
*s
, char *string
, unsigned int length
,
409 int min_vertical
, int min_horizontal
,
410 int max_vertical
, int max_horizontal
);
411 void add_special_char (style
*s
, char *string
, unsigned int length
,
412 int min_vertical
, int min_horizontal
,
413 int max_vertical
, int max_horizontal
);
414 void add_line (int code
, int x1
, int y1
, int x2
, int y2
, int size
, int fill
);
415 void add_arc (int code
, int xc
, int yc
, int *p
, double *c
, int size
, int fill
);
416 void add_polygon (int code
, int np
, int *p
, int oh
, int ov
, int size
, int fill
);
417 void add_spline (int code
, int xc
, int yc
, int np
, int *p
, int size
, int fill
);
418 void calculate_region (void);
419 int is_in_region (graphic_glob
*g
);
420 int can_grow_region (graphic_glob
*g
);
421 void make_new_region (graphic_glob
*g
);
422 int has_line (region_glob
*r
);
423 int has_word (region_glob
*r
);
424 int no_raw_commands (int minv
, int maxv
);
428 ordered_list
<region_glob
> regions
; // squares of bitmapped pics,eqn,tbl's
429 ordered_list
<text_glob
> words
; // position of words on page
430 ordered_list
<graphic_glob
> lines
; // position of lines on page
431 char_buffer buffer
; // all characters for this page
432 int is_in_graphic
; // should graphics and words go below or above
433 ordered_list
<text_glob
> region_words
; // temporary accumulation of words in a region
434 ordered_list
<graphic_glob
> region_lines
; // (as above) and used so that we can determine
435 // the regions vertical limits
439 : is_in_graphic(FALSE
)
443 void page::add (style
*s
, char *string
, unsigned int length
,
444 int min_vertical
, int min_horizontal
,
445 int max_vertical
, int max_horizontal
)
448 text_glob
*g
=new text_glob(s
, buffer
.add_string(string
, length
), length
,
449 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
, FALSE
, FALSE
);
459 * add_html_command - it only makes sense to add html commands when we are not inside
460 * a graphical entity.
463 void page::add_html_command (style
*s
, char *string
, unsigned int length
,
464 int min_vertical
, int min_horizontal
,
465 int max_vertical
, int max_horizontal
)
467 if ((length
> 0) && (! is_in_graphic
)) {
468 text_glob
*g
=new text_glob(s
, buffer
.add_string(string
, length
), length
,
469 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
, TRUE
, TRUE
);
475 * add_special_char - it only makes sense to add special characters when we are inside
476 * a graphical entity.
479 void page::add_special_char (style
*s
, char *string
, unsigned int length
,
480 int min_vertical
, int min_horizontal
,
481 int max_vertical
, int max_horizontal
)
483 if ((length
> 0) && (is_in_graphic
)) {
484 text_glob
*g
=new text_glob(s
, buffer
.add_string(string
, length
), length
,
485 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
, TRUE
, FALSE
);
490 void page::add_line (int code
, int x1
, int y1
, int x2
, int y2
, int size
, int fill
)
492 graphic_glob
*g
= new graphic_glob(code
);
494 g
->minh
= min(x1
, x2
);
495 g
->maxh
= max(x1
, x2
);
496 g
->minv
= min(y1
, y2
);
497 g
->maxv
= max(y1
, y2
);
498 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*2);
517 * assign_min_max_for_arc - works out the smallest box that will encompass an
518 * arc defined by: origin: g->xc, g->xc
519 * and vector (p[0], p[1]) and (p[2], p[3])
522 void assign_min_max_for_arc (graphic_glob
*g
, int *p
, double *c
)
524 int radius
= (int) sqrt(c
[0]*c
[0]+c
[1]*c
[1]);
531 int x2
= g
->xc
+xv1
+xv2
;
532 int y2
= g
->yc
+yv1
+yv2
;
534 // firstly lets use the 'circle' limitation
540 // incidentally I'm sure there is a better way to do this, but I don't know it
541 // please can someone let me know or "improve" this function
543 // now see which min/max can be reduced and increased for the limits of the arc
552 if ((xv1
>=0) && (yv1
>=0)) {
553 // first vector in Q3
554 if ((xv2
>=0) && (yv2
>=0)) {
558 } else if ((xv2
<0) && (yv2
>=0)) {
562 } else if ((xv2
>=0) && (yv2
<0)) {
564 g
->minv
= min(y1
, y2
);
565 } else if ((xv2
<0) && (yv2
<0)) {
570 g
->minv
= min(y1
, y2
);
571 g
->maxv
= max(y1
, y2
);
573 // xv2, yv2 could all be zero?
576 } else if ((xv1
>=0) && (yv1
<0)) {
577 // first vector in Q2
578 if ((xv2
>=0) && (yv2
>=0)) {
580 g
->maxh
= max(x1
, x2
);
581 g
->minh
= min(x1
, x2
);
583 } else if ((xv2
<0) && (yv2
>=0)) {
588 g
->minv
= min(y1
, y2
);
589 g
->maxv
= max(y1
, y2
);
591 // otherwise almost full circle anyway
593 } else if ((xv2
>=0) && (yv2
<0)) {
597 } else if ((xv2
<0) && (yv2
<0)) {
599 g
->minh
= min(x1
, x2
);
601 } else if ((xv1
<0) && (yv1
<0)) {
602 // first vector in Q1
603 if ((xv2
>=0) && (yv2
>=0)) {
608 g
->minv
= min(y1
, y2
);
609 g
->maxv
= max(y1
, y2
);
611 // nearly full circle
613 } else if ((xv2
<0) && (yv2
>=0)) {
615 g
->maxv
= max(y1
, y2
);
616 } else if ((xv2
>=0) && (yv2
<0)) {
618 g
->minv
= min(y1
, y2
);
619 g
->maxv
= max(y1
, y2
);
620 g
->minh
= min(x1
, x2
);
621 } else if ((xv2
<0) && (yv2
<0)) {
626 } else if ((xv1
<0) && (yv1
>=0)) {
627 // first vector in Q4
628 if ((xv2
>=0) && (yv2
>=0)) {
630 g
->maxh
= max(x1
, x2
);
631 } else if ((xv2
<0) && (yv2
>=0)) {
633 g
->maxv
= max(y1
, y2
);
634 g
->maxh
= max(x1
, x2
);
635 } else if ((xv2
>=0) && (yv2
<0)) {
638 g
->minv
= min(y1
, y2
);
639 g
->maxv
= max(y1
, y2
);
640 g
->minh
= min(x1
, x2
);
641 g
->maxh
= max(x2
, x2
);
643 // nearly full circle
645 } else if ((xv2
<0) && (yv2
<0)) {
647 g
->maxv
= max(y1
, y2
);
648 g
->minh
= min(x1
, x2
);
649 g
->maxh
= max(x1
, x2
);
652 // this should *never* happen but if it does it means a case above is wrong..
654 // this code is only present for safety sake
655 if (g
->maxh
< g
->minh
) {
657 fprintf(stderr
, "assert failed minh > maxh\n"); fflush(stderr
);
662 if (g
->maxv
< g
->minv
) {
664 fprintf(stderr
, "assert failed minv > maxv\n"); fflush(stderr
);
671 void page::add_arc (int code
, int xc
, int yc
, int *p
, double *c
, int size
, int fill
)
673 graphic_glob
*g
= new graphic_glob(code
);
675 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*2);
677 g
->point
[0].x
= p
[0] ;
678 g
->point
[0].y
= p
[1] ;
679 g
->point
[1].x
= p
[2] ;
680 g
->point
[1].y
= p
[3] ;
686 assign_min_max_for_arc(g
, p
, c
);
696 void page::add_polygon (int code
, int np
, int *p
, int oh
, int ov
, int size
, int fill
)
698 graphic_glob
*g
= new graphic_glob(code
);
702 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*np
/2);
705 for (i
=0; i
<g
->nopoints
; i
++) {
706 g
->point
[i
].x
= p
[j
];
708 g
->point
[i
].y
= p
[j
];
711 // now calculate min/max
712 g
->minh
= g
->point
[0].x
;
713 g
->minv
= g
->point
[0].y
;
714 g
->maxh
= g
->point
[0].x
;
715 g
->maxv
= g
->point
[0].y
;
716 for (i
=1; i
<g
->nopoints
; i
++) {
717 g
->minh
= min(g
->minh
, g
->point
[i
].x
);
718 g
->minv
= min(g
->minv
, g
->point
[i
].y
);
719 g
->maxh
= max(g
->maxh
, g
->point
[i
].x
);
720 g
->maxv
= max(g
->maxv
, g
->point
[i
].y
);
734 void page::add_spline (int code
, int xc
, int yc
, int np
, int *p
, int size
, int fill
)
736 graphic_glob
*g
= new graphic_glob(code
);
740 g
->point
= (struct xycoord
*)malloc(sizeof(xycoord
)*np
/2);
743 for (i
=0; i
<g
->nopoints
; i
++) {
744 g
->point
[i
].x
= p
[j
];
746 g
->point
[i
].y
= p
[j
];
749 // now calculate min/max
750 g
->minh
= min(g
->point
[0].x
, g
->point
[0].x
/2);
751 g
->minv
= min(g
->point
[0].y
, g
->point
[0].y
/2);
752 g
->maxh
= max(g
->point
[0].x
, g
->point
[0].x
/2);
753 g
->maxv
= max(g
->point
[0].y
, g
->point
[0].y
/2);
755 /* tnum/tden should be between 0 and 1; the closer it is to 1
756 the tighter the curve will be to the guiding lines; 2/3
757 is the standard value */
761 for (i
=1; i
<g
->nopoints
-1; i
++) {
762 g
->minh
= min(g
->minh
, g
->point
[i
].x
*tnum
/(2*tden
));
763 g
->minv
= min(g
->minv
, g
->point
[i
].y
*tnum
/(2*tden
));
764 g
->maxh
= max(g
->maxh
, g
->point
[i
].x
*tnum
/(2*tden
));
765 g
->maxv
= max(g
->maxv
, g
->point
[i
].y
*tnum
/(2*tden
));
767 g
->minh
= min(g
->minh
, g
->point
[i
].x
/2+(g
->point
[i
+1].x
*(tden
-tden
))/(2*tden
));
768 g
->minv
= min(g
->minv
, g
->point
[i
].y
/2+(g
->point
[i
+1].y
*(tden
-tden
))/(2*tden
));
769 g
->maxh
= max(g
->maxh
, g
->point
[i
].x
/2+(g
->point
[i
+1].x
*(tden
-tden
))/(2*tden
));
770 g
->maxv
= max(g
->maxv
, g
->point
[i
].y
/2+(g
->point
[i
+1].y
*(tden
-tden
))/(2*tden
));
772 g
->minh
= min(g
->minh
, (g
->point
[i
].x
-g
->point
[i
].x
/2) + g
->point
[i
+1].x
/2);
773 g
->minv
= min(g
->minv
, (g
->point
[i
].y
-g
->point
[i
].y
/2) + g
->point
[i
+1].y
/2);
774 g
->maxh
= max(g
->maxh
, (g
->point
[i
].x
-g
->point
[i
].x
/2) + g
->point
[i
+1].x
/2);
775 g
->maxv
= max(g
->maxv
, (g
->point
[i
].y
-g
->point
[i
].y
/2) + g
->point
[i
+1].y
/2);
779 g
->minh
= min(g
->minh
, (g
->point
[i
].x
-g
->point
[i
].x
/2)) + xc
;
780 g
->minv
= min(g
->minv
, (g
->point
[i
].y
-g
->point
[i
].y
/2)) + yc
;
781 g
->maxh
= max(g
->maxh
, (g
->point
[i
].x
-g
->point
[i
].x
/2)) + xc
;
782 g
->maxv
= max(g
->maxv
, (g
->point
[i
].y
-g
->point
[i
].y
/2)) + yc
;
796 class html_font
: public font
{
797 html_font(const char *);
801 char *reencoded_name
;
803 static html_font
*load_html_font(const char *);
806 html_font
*html_font::load_html_font(const char *s
)
808 html_font
*f
= new html_font(s
);
816 html_font::html_font(const char *nm
)
821 html_font::~html_font()
826 * a simple class to contain the header to this document
834 int has_been_written
;
836 char text
[MAX_STRING_LENGTH
];
840 title_desc::title_desc ()
841 : has_been_written(FALSE
), has_been_found(FALSE
)
845 title_desc::~title_desc ()
854 int no_of_headings
; // how many headings have we found?
855 char_buffer headings
; // all the headings used in the document
856 ordered_list
<text_glob
> headers
;
857 int header_level
; // current header level
858 int written_header
; // have we written the header yet?
859 char header_buffer
[MAX_STRING_LENGTH
]; // current header text
861 void write_headings (FILE *f
);
864 header_desc::header_desc ()
865 : no_of_headings(0), header_level(2), written_header(0)
869 header_desc::~header_desc ()
874 * paragraph_type - alignment for a new paragraph
877 typedef enum { left_alignment
, center_alignment
} paragraph_type
;
880 * text_defn - defines the limit of text, initially these are stored in the
881 * column array as words. Later we examine the white space between
882 * the words in successive lines to find out whether we can detect
883 * distinct columns. The columns are generated via html tables.
887 int left
; // the start of a word or text
888 int right
; // the end of the text and beginning of white space
889 int is_used
; // will this this column be used for words or space
890 int right_hits
; // count of the number of words touching right position
891 int percent
; // what percentage width should we use for this cell?
895 * introduce a paragraph class so that we can nest paragraphs
896 * from plain html text and html tables.
899 class html_paragraph
{
901 html_paragraph (int in
, int need
, paragraph_type type
, html_paragraph
*prev
);
906 paragraph_type para_type
;
907 html_paragraph
*previous
;
911 * html_paragraph - constructor, fill in the public fields.
914 html_paragraph::html_paragraph (int in
, int need
, paragraph_type type
, html_paragraph
*prev
)
915 : in_paragraph(in
), need_paragraph(need
),
916 para_type(type
), previous(prev
)
921 * html_paragraph - deconstructor
924 html_paragraph::~html_paragraph ()
929 * note that html_tables are currently only used to provide a better
930 * indentation mechanism for html text (in particular it allows grohtml
931 * to render .IP and .2C together with autoformatting).
939 int no_of_columns
; // how many columns are we using?
940 struct text_defn
*columns
; // left and right margins for each column
941 int vertical_limit
; // the limit of the table
942 int wrap_margin
; // is the current rightmost margin able to wrap words?
945 html_table::html_table ()
946 : no_of_columns(0), columns(0), vertical_limit(0), wrap_margin(0)
950 html_table::~html_table ()
954 class html_printer
: public printer
{
960 int space_char_index
;
961 int no_of_printed_pages
;
963 enum { SBUF_SIZE
= 8192 };
964 char sbuf
[SBUF_SIZE
];
975 int output_draw_point_size
;
977 int output_line_thickness
;
979 unsigned char output_space_code
;
981 char *inside_font_style
;
987 html_table indentation
;
988 int left_margin_indent
;
989 int right_margin_indent
;
990 int need_one_newline
;
992 html_paragraph
*current_paragraph
;
993 char image_name
[MAX_STRING_LENGTH
];
998 int start_region_vpos
;
999 int start_region_hpos
;
1000 int end_region_vpos
;
1001 int end_region_hpos
;
1004 struct graphic_glob
*start_graphic
;
1005 struct text_glob
*start_text
;
1008 void set_style (const style
&);
1009 void set_space_code (unsigned char c
);
1010 void do_exec (char *, const environment
*);
1011 void do_import (char *, const environment
*);
1012 void do_def (char *, const environment
*);
1013 void do_mdef (char *, const environment
*);
1014 void do_file (char *, const environment
*);
1015 void set_line_thickness (const environment
*);
1016 void change_font (text_glob
*g
, int is_to_html
);
1017 void terminate_current_font (void);
1018 void flush_font (void);
1019 void flush_page (void);
1020 void add_char_to_sbuf (unsigned char code
);
1021 void add_to_sbuf (char code
, const char *name
);
1022 void display_word (text_glob
*g
, int is_to_html
);
1023 void html_display_word (text_glob
*g
);
1024 void troff_display_word (text_glob
*g
);
1025 void display_line (graphic_glob
*g
, int is_to_html
);
1026 void display_fill (graphic_glob
*g
);
1027 void calculate_margin (void);
1028 void traverse_page_regions (void);
1029 void dump_page (void);
1030 int is_within_region (graphic_glob
*g
);
1031 int is_within_region (text_glob
*t
);
1032 int is_less (graphic_glob
*g
, text_glob
*t
);
1033 void display_globs (int is_to_html
);
1034 void move_horizontal (text_glob
*g
, int left_margin
);
1035 void move_vertical (text_glob
*g
, paragraph_type p
);
1036 void write_html_font_face (const char *fontname
, const char *left
, const char *right
);
1037 void write_html_font_type (const char *fontname
, const char *left
, const char *right
);
1038 void html_change_font (text_glob
*g
, const char *fontname
, int size
);
1039 char *html_position_text (text_glob
*g
, int left_margin
, int right_margin
);
1040 int html_position_region (void);
1041 void troff_change_font (const char *fontname
, int size
, int font_no
);
1042 void troff_position_text (text_glob
*g
);
1043 int pretend_is_on_same_line (text_glob
*g
, int left_margin
, int right_margin
);
1044 int is_on_same_line (text_glob
*g
, int vpos
);
1045 int looks_like_subscript (text_glob
*g
);
1046 int looks_like_superscript (text_glob
*g
);
1047 int looks_like_smaller_font (text_glob
*g
);
1048 int looks_like_larger_font (text_glob
*g
);
1049 void begin_paragraph (paragraph_type p
);
1050 void begin_paragraph_no_height (paragraph_type p
);
1051 void force_begin_paragraph (void);
1052 void end_paragraph (void);
1053 void save_paragraph (void);
1054 void restore_paragraph (void);
1055 void html_newline (void);
1056 void convert_to_image (char *troff_src
, char *image_name
);
1057 void write_title (int in_head
);
1058 void find_title (void);
1059 int is_bold (text_glob
*g
);
1060 void write_header (text_glob
*g
);
1061 void determine_header_level (void);
1062 void build_header (text_glob
*g
);
1063 void make_html_indent (int indent
);
1064 int is_whole_line_bold (text_glob
*g
);
1065 int is_a_header (text_glob
*g
);
1066 int processed_header (text_glob
*g
);
1067 void make_new_image_name (void);
1068 void calculate_region_margins (region_glob
*r
);
1069 void remove_redundant_regions (void);
1070 void remove_duplicate_regions (void);
1071 void move_region_to_page (void);
1072 void calculate_region_range (graphic_glob
*r
);
1073 void flush_graphic (void);
1074 void write_string (graphic_glob
*g
, int is_to_html
);
1075 void prologue (void);
1078 void display_regions (void);
1079 int check_able_to_use_table (text_glob
*g
);
1080 int using_table_for_indent (void);
1081 int collect_columns (struct text_defn
*next_words
, struct text_defn
*next_cols
,
1082 struct text_defn
*last_words
, struct text_defn
*last_cols
,
1084 void include_into_list (struct text_defn
*line
, struct text_defn
*item
);
1085 int is_in_column (struct text_defn
*line
, struct text_defn
*item
, int max_words
);
1086 int is_column_match (struct text_defn
*match
, struct text_defn
*line1
,
1087 struct text_defn
*line2
, int max_words
);
1088 int count_columns (struct text_defn
*line
);
1089 void rewind_text_to (text_glob
*g
);
1090 int found_use_for_table (text_glob
*start
);
1091 void column_display_word (int cell
, int vert
, int left
, int right
, int next
);
1092 void start_table (void);
1093 void end_table (void);
1094 void foreach_column_include_text (text_glob
*start
);
1095 void define_cell (int i
);
1096 int column_calculate_left_margin (int left
, int right
);
1097 int column_calculate_right_margin (int left
, int right
);
1098 void display_columns (const char *word
, const char *name
, text_defn
*line
);
1099 void calculate_right (struct text_defn
*line
, int max_words
);
1100 void determine_right_most_column (struct text_defn
*line
, int max_words
);
1101 int remove_white_using_words (struct text_defn
*next_guess
, struct text_defn
*last_guess
, struct text_defn
*next_line
);
1102 void copy_line (struct text_defn
*dest
, struct text_defn
*src
);
1103 void combine_line (struct text_defn
*dest
, struct text_defn
*src
);
1104 int conflict_with_words (struct text_defn
*column_guess
, struct text_defn
*words
);
1105 void remove_entry_in_line (struct text_defn
*line
, int j
);
1106 void remove_redundant_columns (struct text_defn
*line
);
1107 void add_column_gaps (struct text_defn
*line
);
1108 int continue_searching_column (text_defn
*next_col
, text_defn
*last_col
, text_defn
*all_words
);
1109 void add_right_full_width (struct text_defn
*line
, int mingap
);
1110 int is_continueous_column (text_defn
*last_col
, text_defn
*next_line
);
1111 int is_exact_left (text_defn
*last_col
, text_defn
*next_line
);
1112 int find_column_index_in_line (text_glob
*t
, text_defn
*line
);
1113 void emit_space (text_glob
*g
, int force_space
);
1114 int is_in_middle (int left
, int right
);
1115 int check_able_to_use_center (text_glob
*g
);
1116 void write_centered_line (text_glob
*g
);
1117 int single_centered_line (text_defn
*first
, text_defn
*second
, text_glob
*g
);
1118 int determine_row_limit (text_glob
*start
, int v
);
1119 void assign_used_columns (text_glob
*start
);
1120 int find_column_index (text_glob
*t
);
1121 int large_enough_gap (text_defn
*last_col
);
1122 int is_worth_column (int left
, int right
);
1123 int is_subset_of_columns (text_defn
*a
, text_defn
*b
);
1124 void count_hits (text_defn
*col
, int no_of_columns
, int limit
);
1125 void count_right_hits (text_defn
*col
, int no_of_columns
);
1126 int calculate_min_gap (text_glob
*g
);
1127 int right_indentation (struct text_defn
*last_guess
);
1128 void calculate_percentage_width (text_glob
*start
);
1129 int able_to_steal_width (void);
1130 int need_to_steal_width (void);
1131 int can_distribute_fairly (void);
1132 void utilize_round_off (void);
1133 int will_wrap_text (int i
, text_glob
*start
);
1134 int next_line_on_left_column (int i
, text_glob
*start
);
1135 void remove_table_column (int i
);
1136 void remove_unnecessary_unused (text_glob
*start
);
1137 int is_small_table (int lines
, struct text_defn
*last_guess
,
1138 struct text_defn
*words_1
, struct text_defn
*cols_1
,
1139 struct text_defn
*words_2
, struct text_defn
*cols_2
,
1140 int *limit
, int *limit_1
);
1141 int is_column_subset (struct text_defn
*cols_1
, struct text_defn
*cols_2
);
1142 int is_appropriate_to_start_table (struct text_defn
*cols_1
, struct text_defn
*cols_2
,
1143 struct text_defn
*last_guess
);
1144 int is_a_full_width_column (void);
1145 int right_most_column (struct text_defn
*col
);
1146 int large_enough_gap_for_two (struct text_defn
*col
);
1147 void remove_zero_percentage_column (void);
1148 void translate_to_html (text_glob
*g
);
1149 int html_knows_about (char *troff
);
1150 void determine_diacritical_mark (const char *name
, const environment
*env
);
1151 int sbuf_continuation (unsigned char code
, const char *name
, const environment
*env
, int w
);
1152 char *remove_last_char_from_sbuf ();
1153 const char *check_diacritical_combination (unsigned char code
, const char *name
);
1154 int seen_backwards_escape (char *s
, int l
);
1155 int should_defer_table (int lines
, struct text_glob
*start
, struct text_defn
*cols_1
);
1156 int is_new_exact_right (struct text_defn
*last_guess
, struct text_defn
*last_cols
, struct text_defn
*next_cols
);
1157 void issue_left_paragraph (void);
1158 void adjust_margin_percentages (void);
1159 int total_percentages (void);
1160 int get_left (void);
1161 void can_loose_column (text_glob
*start
, struct text_defn
*last_guess
, int limit
);
1162 int check_lack_of_hits (struct text_defn
*next_guess
, struct text_defn
*last_guess
, text_glob
*start
, int limit
);
1163 int is_in_table (void);
1170 void set_char(int i
, font
*f
, const environment
*env
, int w
, const char *name
);
1171 void draw(int code
, int *p
, int np
, const environment
*env
);
1172 void begin_page(int);
1174 void special(char *arg
, const environment
*env
);
1175 font
*make_font(const char *);
1179 html_printer::html_printer()
1180 : html(0, MAX_LINE_LENGTH
),
1181 troff(0, MAX_LINE_LENGTH
),
1182 no_of_printed_pages(0),
1184 sbuf_dmark_hpos(-1),
1189 inside_font_style(0),
1192 left_margin_indent(0),
1193 right_margin_indent(0),
1194 need_one_newline(0),
1198 supress_sub_sup(TRUE
),
1199 start_region_vpos(0),
1200 start_region_hpos(0),
1205 tempfp
= xtmpfile();
1206 html
.set_file(tempfp
);
1208 linewidth
= DEFAULT_LINEWIDTH
;
1210 fatal("horizontal resolution must be 1");
1211 if (font::vert
!= 1)
1212 fatal("vertical resolution must be 1");
1214 // should be sorted html..
1215 if (font::res
% (font::sizescale
*72) != 0)
1216 fatal("res must be a multiple of 72*sizescale");
1220 while (r
% 10 == 0) {
1225 html
.set_fixed_point(point
);
1226 space_char_index
= font::name_to_index("space");
1227 paper_length
= font::paperlength
;
1228 if (paper_length
== 0)
1229 paper_length
= 11*font::res
;
1230 page_contents
= new page
;
1232 postscript_res
= 72000;
1233 current_paragraph
= new html_paragraph(FALSE
, FALSE
, left_alignment
, 0);
1237 * add_char_to_sbuf - adds a single character to the sbuf.
1240 void html_printer::add_char_to_sbuf (unsigned char code
)
1242 if (sbuf_len
< SBUF_SIZE
) {
1243 sbuf
[sbuf_len
] = code
;
1246 fatal("need to increase SBUF_SIZE");
1251 * add_to_sbuf - adds character code or name to the sbuf.
1252 * It escapes \ with \\
1253 * We need to preserve the name of characters if they exist
1254 * because we may need to send this character to two different
1255 * devices: html and postscript.
1258 void html_printer::add_to_sbuf (char code
, const char *name
)
1262 add_char_to_sbuf('\\');
1264 add_char_to_sbuf(code
);
1269 add_char_to_sbuf('\\');
1270 add_char_to_sbuf('(');
1272 if (name
[i
] == '\\') {
1273 add_char_to_sbuf('\\');
1275 add_char_to_sbuf(name
[i
]);
1278 add_char_to_sbuf('\\');
1279 add_char_to_sbuf(')');
1283 int html_printer::sbuf_continuation (unsigned char code
, const char *name
,
1284 const environment
*env
, int w
)
1286 if ((sbuf_end_hpos
== env
->hpos
) || (sbuf_dmark_hpos
== env
->hpos
)) {
1287 name
= check_diacritical_combination(code
, name
);
1288 add_to_sbuf(code
, name
);
1289 determine_diacritical_mark(name
, env
);
1290 sbuf_end_hpos
+= w
+ sbuf_kern
;
1293 if ((sbuf_len
< SBUF_SIZE
-1) && (env
->hpos
>= sbuf_end_hpos
) &&
1294 ((sbuf_kern
== 0) || (sbuf_end_hpos
- sbuf_kern
!= env
->hpos
))) {
1296 * lets see whether a space is needed or not
1298 int space_width
= sbuf_style
.f
->get_space_width(sbuf_style
.point_size
);
1300 if (env
->hpos
-sbuf_end_hpos
< space_width
) {
1301 name
= check_diacritical_combination(code
, name
);
1302 add_to_sbuf(code
, name
);
1303 determine_diacritical_mark(name
, env
);
1304 sbuf_end_hpos
= env
->hpos
+ w
;
1307 } else if ((sbuf_len
> 0) && (sbuf_dmark_hpos
)) {
1309 * check whether the diacritical mark is on the same character
1311 int space_width
= sbuf_style
.f
->get_space_width(sbuf_style
.point_size
);
1313 if (abs(sbuf_dmark_hpos
-env
->hpos
) < space_width
) {
1314 name
= check_diacritical_combination(code
, name
);
1315 add_to_sbuf(code
, name
);
1316 determine_diacritical_mark(name
, env
);
1317 sbuf_end_hpos
= env
->hpos
+ w
;
1326 * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s
1329 int html_printer::seen_backwards_escape (char *s
, int l
)
1332 * this is tricky so it is broken into components for clarity
1333 * (we let the compiler put in all back into a complex expression)
1335 if ((l
>0) && (sbuf
[l
] == '(') && (sbuf
[l
-1] == '\\')) {
1337 * ok seen '\(' but we must now check for '\\('
1339 if ((l
>1) && (sbuf
[l
-2] == '\\')) {
1341 * escaped the escape
1353 * reverse - return reversed string.
1356 char *reverse (char *s
)
1373 * remove_last_char_from_sbuf - removes the last character from sbuf.
1376 char *html_printer::remove_last_char_from_sbuf ()
1379 static char last
[MAX_STRING_LENGTH
];
1383 if ((sbuf
[l
] == ')') && (l
>0) && (sbuf
[l
-1] == '\\')) {
1385 * found terminating escape
1390 while ((l
>0) && (! seen_backwards_escape(sbuf
, l
))) {
1391 if (sbuf
[l
] == '\\') {
1392 if (sbuf
[l
-1] == '\\') {
1405 if (seen_backwards_escape(sbuf
, l
)) {
1408 return( reverse(last
) );
1410 if ((sbuf
[l
] == '\\') && (l
>0) && (sbuf
[l
-1] == '\\')) {
1416 last
[0] = sbuf
[sbuf_len
];
1427 * check_diacriticial_combination - checks to see whether the character code
1428 * if combined with the previous diacriticial mark
1429 * forms a new character.
1432 const char *html_printer::check_diacritical_combination (unsigned char code
, const char *name
)
1434 static char troff_char
[2];
1436 if ((name
== 0) && (sbuf_dmark_hpos
>= 0)) {
1437 // last character was a diacritical mark
1438 char *last
= remove_last_char_from_sbuf();
1443 while (diacritical_table
[i
].mark
!= NULL
) {
1444 if (strcmp(diacritical_table
[i
].mark
, last
) == 0) {
1446 while ((diacritical_table
[i
].second_troff_char
[j
] != (char)0) &&
1447 (diacritical_table
[i
].second_troff_char
[j
] != code
)) {
1450 if (diacritical_table
[i
].second_troff_char
[j
] == code
) {
1451 troff_char
[0] = diacritical_table
[i
].translation
;
1452 troff_char
[1] = code
;
1453 troff_char
[2] = (char)0;
1454 return( troff_char
);
1459 add_to_sbuf(last
[0], last
);
1465 * determine_diacritical_mark - if name is a diacriticial mark the record the position.
1466 * --fixme-- is there a better way of doing this
1467 * this must be done in troff somewhere.
1470 void html_printer::determine_diacritical_mark (const char *name
, const environment
*env
)
1475 while (diacritical_table
[i
].mark
!= NULL
) {
1476 if (strcmp(name
, diacritical_table
[i
].mark
) == 0) {
1477 sbuf_dmark_hpos
= env
->hpos
;
1483 sbuf_dmark_hpos
= -1;
1487 * set_char - adds a character into the sbuf if it is a continuation with the previous
1488 * word otherwise flush the current sbuf and add character anew.
1491 void html_printer::set_char(int i
, font
*f
, const environment
*env
, int w
, const char *name
)
1493 unsigned char code
= f
->get_code(i
);
1500 style
sty(f
, env
->size
, env
->height
, env
->slant
, env
->fontno
);
1501 if (sty
.slant
!= 0) {
1502 if (sty
.slant
> 80 || sty
.slant
< -80) {
1503 error("silly slant `%1' degrees", sty
.slant
);
1507 if ((name
!= 0) && (page_contents
->is_in_graphic
)) {
1509 int r
=font::res
; // resolution of the device
1510 page_contents
->add_special_char(&sty
, (char *)name
, strlen(name
),
1511 env
->vpos
-sty
.point_size
*r
/72, env
->hpos
,
1512 env
->vpos
, env
->hpos
+w
);
1513 sbuf_end_hpos
= env
->hpos
+ w
;
1514 sbuf_start_hpos
= env
->hpos
;
1515 sbuf_vpos
= env
->vpos
;
1519 if ((sbuf_len
> 0) && (sbuf_len
< SBUF_SIZE
) && (sty
== sbuf_style
) &&
1520 (sbuf_vpos
== env
->vpos
) && (sbuf_continuation(code
, name
, env
, w
))) {
1525 add_to_sbuf(code
, name
);
1526 determine_diacritical_mark(name
, env
);
1527 sbuf_end_hpos
= env
->hpos
+ w
;
1528 sbuf_start_hpos
= env
->hpos
;
1529 sbuf_vpos
= env
->vpos
;
1537 * make_new_image_name - creates a new file name ready for a image file.
1540 void html_printer::make_new_image_name (void)
1544 if ((current_filename
== 0) ||
1545 (strcmp(current_filename
, "<standard input>") == 0) ||
1546 (strcmp(current_filename
, "-") == 0) ||
1547 (strchr(current_filename
, '/') != 0)) {
1548 sprintf(image_name
, "grohtml-%d-%ld", image_number
, (long)getpid());
1550 sprintf(image_name
, "%s-%d-%ld", current_filename
, image_number
, (long)getpid());
1555 * write_title - writes the title to this document
1558 void html_printer::write_title (int in_head
)
1560 if (title
.has_been_found
) {
1562 html
.put_string("<title>");
1563 html
.put_string(title
.text
);
1564 html
.put_string("</title>\n");
1566 title
.has_been_written
= TRUE
;
1567 html
.put_string("<h1 align=center>");
1568 html
.put_string(title
.text
);
1569 html
.put_string("</h1>\n");
1575 * get_html_translation - given the position of the character and its name
1576 * return the device encoding for such character.
1579 char *get_html_translation (font
*f
, char *name
)
1583 if ((f
== 0) || (name
== 0) || (strcmp(name
, "") == 0)) {
1586 index
= f
->name_to_index(name
);
1588 error("character `%s' not found", name
);
1591 return( (char *)f
->get_special_device_encoding(index
) );
1597 * str_translate_to_html - converts a string, str, into html text. It places
1598 * the output input buffer, buf. It truncates string, str, if
1599 * there is not enough space in buf.
1600 * It looks up the html character encoding of single characters
1601 * if, and_single, is TRUE. Characters such as < > & etc.
1604 void str_translate_to_html (font
*f
, char *buf
, int buflen
, char *str
, int len
, int and_single
)
1609 char escaped_char
[MAX_STRING_LENGTH
];
1615 if (strcmp(str
, "\\(\\\\-\\)") == 0) {
1619 while (str
[i
] != (char)0) {
1620 if ((str
[i
]=='\\') && (i
+1<len
)) {
1621 i
++; // skip the leading backslash
1622 if (str
[i
] == '(') {
1626 while ((str
[i
] != (char)0) &&
1627 (! ((str
[i
] == '\\') && (i
+1<len
) && (str
[i
+1] == ')')))) {
1628 if (str
[i
] == '\\') {
1631 escaped_char
[e
] = str
[i
];
1635 if ((str
[i
] == '\\') && (i
+1<len
) && (str
[i
+1] == ')')) {
1638 escaped_char
[e
] = (char)0;
1640 translation
= get_html_translation(f
, escaped_char
);
1642 l
= strlen(translation
);
1643 t
= max(0, min(l
, buflen
-b
));
1644 strncpy(&buf
[b
], translation
, t
);
1647 int index
=f
->name_to_index(escaped_char
);
1650 buf
[b
] = f
->get_code(index
);
1662 translation
= get_html_translation(f
, name
);
1664 l
= strlen(translation
);
1665 t
= max(0, min(l
, buflen
-b
));
1666 strncpy(&buf
[b
], translation
, t
);
1676 * do not attempt to encode single characters
1686 buf
[min(b
, buflen
)] = (char)0;
1690 * find_title - finds a title to this document, if it exists.
1693 void html_printer::find_title (void)
1697 int removed_from_head
;
1698 char buf
[MAX_STRING_LENGTH
];
1700 if ((page_number
== 1) && (guess_on
)) {
1701 if (! page_contents
->words
.is_empty()) {
1703 int end_title_hpos
= 0;
1704 int start_title_vpos
= 0;
1705 int found_title_start
= FALSE
;
1707 int start_region
=-1;
1709 if (! page_contents
->regions
.is_empty()) {
1712 page_contents
->regions
.start_from_head();
1713 r
= page_contents
->regions
.get_data();
1715 start_region
= r
->minv
;
1719 page_contents
->words
.start_from_head();
1721 t
= page_contents
->words
.get_data();
1722 removed_from_head
= FALSE
;
1723 if ((found_title_start
) && (start_region
!= -1) && (t
->maxv
>= start_region
)) {
1725 * we have just encountered the first graphic region so
1726 * we stop looking for a title.
1728 title
.has_been_found
= TRUE
;
1730 } else if (t
->is_raw_command
) {
1731 // skip raw commands
1732 page_contents
->words
.move_right(); // move onto next word
1733 } else if ((!found_title_start
) && (t
->minh
> left_margin_indent
) &&
1734 ((start_region
== -1) || (t
->maxv
< start_region
))) {
1735 start_title_vpos
= t
->minv
;
1736 end_title_hpos
= t
->minh
;
1737 str_translate_to_html(t
->text_style
.f
, buf
, MAX_STRING_LENGTH
, t
->text_string
, t
->text_length
, TRUE
);
1738 strcpy((char *)title
.text
, buf
);
1739 height
= t
->text_style
.point_size
*r
/72;
1740 found_title_start
= TRUE
;
1741 page_contents
->words
.sub_move_right();
1742 removed_from_head
= ((!page_contents
->words
.is_empty()) &&
1743 (page_contents
->words
.is_equal_to_head()));
1744 } else if (found_title_start
) {
1745 if ((t
->minv
== start_title_vpos
) ||
1746 ((!more_than_line_break(start_title_vpos
, t
->minv
, (height
*3)/2)) &&
1747 (t
->minh
> left_margin_indent
)) ||
1748 (is_bold(t
) && (t
->minh
> left_margin_indent
))) {
1749 start_title_vpos
= min(t
->minv
, start_title_vpos
);
1750 end_title_hpos
= max(t
->maxh
, end_title_hpos
);
1751 strcat(title
.text
, " ");
1752 str_translate_to_html(t
->text_style
.f
, buf
, MAX_STRING_LENGTH
, t
->text_string
, t
->text_length
, TRUE
);
1753 strcat(title
.text
, buf
);
1754 page_contents
->words
.sub_move_right();
1755 removed_from_head
= ((!page_contents
->words
.is_empty()) &&
1756 (page_contents
->words
.is_equal_to_head()));
1759 title
.has_been_found
= TRUE
;
1762 } else if (t
->minh
<= left_margin_indent
) {
1766 // move onto next word
1767 page_contents
->words
.move_right();
1769 } while ((! page_contents
->words
.is_equal_to_head()) || (removed_from_head
));
1775 * html_newline - generates a newline <br>
1778 void html_printer::html_newline (void)
1781 int height
= output_style
.point_size
*r
/72;
1783 if (current_paragraph
->in_paragraph
) {
1784 // safe to generate a pretty newline
1785 html
.put_string("<br>\n");
1787 html
.put_string("<br>");
1789 output_vpos
+= height
;
1790 issued_newline
= TRUE
;
1794 * issue_left_paragraph - emits a left paragraph together with appropriate
1795 * margin if header_indent is < left_margin_indent.
1798 void html_printer::issue_left_paragraph (void)
1800 if ((header_indent
< left_margin_indent
) && (! using_table_for_indent())) {
1801 html
.put_string("<p style=\"margin-left: ");
1802 html
.put_number(((left_margin_indent
-header_indent
)*100)/(right_margin_indent
-header_indent
));
1803 html
.put_string("%\">");
1805 html
.put_string("<p>");
1810 * force_begin_paragraph - force the begin_paragraph to be emitted.
1813 void html_printer::force_begin_paragraph (void)
1815 if (current_paragraph
->in_paragraph
&& current_paragraph
->need_paragraph
) {
1816 switch (current_paragraph
->para_type
) {
1818 case left_alignment
: issue_left_paragraph();
1820 case center_alignment
: html
.put_string("<p align=center>");
1822 default: fatal("unknown paragraph alignment type");
1824 current_paragraph
->need_paragraph
= FALSE
;
1829 * begin_paragraph - starts a new paragraph. It does nothing if a paragraph
1830 * has already been started.
1833 void html_printer::begin_paragraph (paragraph_type p
)
1835 if (! current_paragraph
->in_paragraph
) {
1837 int height
= output_style
.point_size
*r
/72;
1839 if (output_vpos
>=0) {
1840 // we leave it alone if it is set to the top of page
1841 output_vpos
+= height
;
1843 current_paragraph
->need_paragraph
= TRUE
; // delay the <p> just in case we don't actually emit text
1844 current_paragraph
->in_paragraph
= TRUE
;
1845 current_paragraph
->para_type
= p
;
1846 issued_newline
= TRUE
;
1852 * begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph
1853 * has already been started. Note it does not alter output_vpos.
1856 void html_printer::begin_paragraph_no_height (paragraph_type p
)
1858 if (! current_paragraph
->in_paragraph
) {
1859 current_paragraph
->need_paragraph
= TRUE
; // delay the <p> just in case we don't actually emit text
1860 current_paragraph
->in_paragraph
= TRUE
;
1861 current_paragraph
->para_type
= p
;
1862 issued_newline
= TRUE
;
1867 * end_paragraph - end the current paragraph. It does nothing if a paragraph
1868 * has not been started.
1871 void html_printer::end_paragraph (void)
1873 if (current_paragraph
->in_paragraph
) {
1874 // check whether we have generated any text inbetween the potential paragraph begin end
1875 if (! current_paragraph
->need_paragraph
) {
1877 int height
= output_style
.point_size
*r
/72;
1879 output_vpos
+= height
;
1880 terminate_current_font();
1881 html
.put_string("</p>\n");
1883 terminate_current_font();
1885 current_paragraph
->para_type
= left_alignment
;
1886 current_paragraph
->in_paragraph
= FALSE
;
1891 * save_paragraph - saves the current paragraph state and
1892 * creates new paragraph state.
1895 void html_printer::save_paragraph (void)
1897 if (current_paragraph
== 0) {
1898 fatal("current_paragraph is NULL");
1900 current_paragraph
= new html_paragraph(current_paragraph
->in_paragraph
,
1901 current_paragraph
->need_paragraph
,
1902 current_paragraph
->para_type
,
1904 terminate_current_font();
1908 * restore_paragraph - restores the previous paragraph state.
1911 void html_printer::restore_paragraph (void)
1913 html_paragraph
*old
= current_paragraph
;
1915 current_paragraph
= current_paragraph
->previous
;
1920 * calculate_margin - runs through the words and graphics globs
1921 * and finds the start of the left most margin.
1924 void html_printer::calculate_margin (void)
1931 right_margin_indent
= 0;
1933 if (! page_contents
->words
.is_empty()) {
1935 // firstly check the words to determine the right margin
1937 page_contents
->words
.start_from_head();
1939 w
= page_contents
->words
.get_data();
1940 if ((w
->maxh
>= 0) && (w
->maxh
> right_margin_indent
)) {
1941 right_margin_indent
= w
->maxh
;
1943 if (right_margin_indent
== 758) stop();
1946 page_contents
->words
.move_right();
1947 } while (! page_contents
->words
.is_equal_to_head());
1950 * only examine graphics if no words present
1952 if (! page_contents
->lines
.is_empty()) {
1953 // now check for diagrams for right margin
1954 page_contents
->lines
.start_from_head();
1956 g
= page_contents
->lines
.get_data();
1957 if ((g
->maxh
>= 0) && (g
->maxh
> right_margin_indent
)) {
1958 right_margin_indent
= g
->maxh
;
1960 if (right_margin_indent
== 950) stop();
1963 page_contents
->lines
.move_right();
1964 } while (! page_contents
->lines
.is_equal_to_head());
1969 * now we know the right margin lets do the same to find left margin
1972 if (header_indent
== -1) {
1973 header_indent
= right_margin_indent
;
1975 left_margin_indent
= right_margin_indent
;
1977 if (! page_contents
->words
.is_empty()) {
1979 w
= page_contents
->words
.get_data();
1980 if ((w
->minh
>= 0) && (w
->minh
< left_margin_indent
)) {
1981 if (! is_a_header(w
) && (! w
->is_raw_command
)) {
1982 left_margin_indent
= w
->minh
;
1985 page_contents
->words
.move_right();
1986 } while (! page_contents
->words
.is_equal_to_head());
1990 * only examine graphic for margins if text yields nothing
1993 if (! page_contents
->lines
.is_empty()) {
1994 // now check for diagrams
1995 page_contents
->lines
.start_from_head();
1997 g
= page_contents
->lines
.get_data();
1998 if ((g
->minh
>= 0) && (g
->minh
< left_margin_indent
)) {
1999 left_margin_indent
= g
->minh
;
2001 page_contents
->lines
.move_right();
2002 } while (! page_contents
->lines
.is_equal_to_head());
2008 * calculate_region - runs through the graphics globs and text globs
2009 * and ensures that all graphic routines
2010 * are defined by the region lists.
2011 * This then allows us to easily
2012 * determine the range of vertical and
2013 * horizontal boundaries for pictures,
2018 void page::calculate_region (void)
2022 if (! lines
.is_empty()) {
2023 lines
.start_from_head();
2025 g
= lines
.get_data();
2026 if (! is_in_region(g
)) {
2027 if (! can_grow_region(g
)) {
2032 } while (! lines
.is_equal_to_head());
2037 * remove_redundant_regions - runs through the regions and ensures that
2038 * all are needed. This is required as
2039 * a picture may be empty, or EQ EN pair
2043 void html_printer::remove_redundant_regions (void)
2047 // firstly run through the region making sure that all are needed
2048 // ie all contain a line or word
2049 if (! page_contents
->regions
.is_empty()) {
2050 page_contents
->regions
.start_from_tail();
2052 r
= page_contents
->regions
.get_data();
2053 calculate_region_margins(r
);
2054 if (page_contents
->has_line(r
) || page_contents
->has_word(r
)) {
2055 page_contents
->regions
.move_right();
2057 page_contents
->regions
.sub_move_right();
2059 } while ((! page_contents
->regions
.is_empty()) &&
2060 (! page_contents
->regions
.is_equal_to_tail()));
2064 void html_printer::display_regions (void)
2066 if (debug_table_on
) {
2069 fprintf(stderr
, "==========s t a r t===========\n");
2070 if (! page_contents
->regions
.is_empty()) {
2071 page_contents
->regions
.start_from_head();
2073 r
= page_contents
->regions
.get_data();
2074 fprintf(stderr
, "region minv %d maxv %d\n", r
->minv
, r
->maxv
);
2075 page_contents
->regions
.move_right();
2076 } while (! page_contents
->regions
.is_equal_to_head());
2078 fprintf(stderr
, "============e n d=============\n");
2084 * remove_duplicate_regions - runs through the regions and ensures that
2085 * no duplicates exist.
2088 void html_printer::remove_duplicate_regions (void)
2093 if (! page_contents
->regions
.is_empty()) {
2094 page_contents
->regions
.start_from_head();
2095 l
= page_contents
->regions
.get_data();
2096 page_contents
->regions
.move_right();
2097 r
= page_contents
->regions
.get_data();
2100 r
= page_contents
->regions
.get_data();
2101 // we have a legit region so we check for an intersection
2102 if (is_intersection(r
->minv
, r
->minv
, l
->minv
, l
->maxv
) &&
2103 is_intersection(r
->minh
, r
->maxh
, l
->minh
, l
->maxh
)) {
2104 l
->minv
= min(r
->minv
, l
->minv
);
2105 l
->maxv
= max(r
->maxv
, l
->maxv
);
2106 l
->minh
= min(r
->minh
, l
->minh
);
2107 l
->maxh
= max(r
->maxh
, l
->maxh
);
2108 calculate_region_margins(l
);
2109 page_contents
->regions
.sub_move_right();
2112 page_contents
->regions
.move_right();
2114 } while ((! page_contents
->regions
.is_empty()) &&
2115 (! page_contents
->regions
.is_equal_to_head()));
2120 int page::has_line (region_glob
*r
)
2124 if (! lines
.is_empty()) {
2125 lines
.start_from_head();
2127 g
= lines
.get_data();
2128 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
) &&
2129 is_subsection(g
->minh
, g
->maxh
, r
->minh
, r
->maxh
)) {
2133 } while (! lines
.is_equal_to_head());
2139 int page::has_word (region_glob
*r
)
2143 if (! words
.is_empty()) {
2144 words
.start_from_head();
2146 g
= words
.get_data();
2147 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
) &&
2148 is_subsection(g
->minh
, g
->maxh
, r
->minh
, r
->maxh
)) {
2152 } while (! words
.is_equal_to_head());
2158 void html_printer::calculate_region_margins (region_glob
*r
)
2163 r
->minh
= right_margin_indent
;
2164 r
->maxh
= left_margin_indent
;
2166 if (! page_contents
->lines
.is_empty()) {
2167 page_contents
->lines
.start_from_head();
2169 g
= page_contents
->lines
.get_data();
2170 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
)) {
2171 r
->minh
= min(r
->minh
, g
->minh
);
2172 r
->maxh
= max(r
->maxh
, g
->maxh
);
2174 page_contents
->lines
.move_right();
2175 } while (! page_contents
->lines
.is_equal_to_head());
2177 if (! page_contents
->words
.is_empty()) {
2178 page_contents
->words
.start_from_head();
2180 w
= page_contents
->words
.get_data();
2181 if (is_subsection(w
->minv
, w
->maxv
, r
->minv
, r
->maxv
)) {
2182 r
->minh
= min(r
->minh
, w
->minh
);
2183 r
->maxh
= max(r
->maxh
, w
->maxh
);
2185 page_contents
->words
.move_right();
2186 } while (! page_contents
->words
.is_equal_to_head());
2191 int page::is_in_region (graphic_glob
*g
)
2195 if (! regions
.is_empty()) {
2196 regions
.start_from_head();
2198 r
= regions
.get_data();
2199 if (is_subsection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
) &&
2200 is_subsection(g
->minh
, g
->maxh
, r
->minh
, r
->maxh
)) {
2203 regions
.move_right();
2204 } while (! regions
.is_equal_to_head());
2211 * no_raw_commands - returns TRUE if no html raw commands exist between
2215 int page::no_raw_commands (int minv
, int maxv
)
2219 if (! words
.is_empty()) {
2220 words
.start_from_head();
2222 g
= words
.get_data();
2223 if ((g
->is_raw_command
) && (g
->is_html_command
) &&
2224 (is_intersection(g
->minv
, g
->maxv
, minv
, maxv
))) {
2228 } while (! words
.is_equal_to_head());
2234 * can_grow_region - returns TRUE if a region exists which can be extended
2235 * to include graphic_glob *g. The region is extended.
2238 int page::can_grow_region (graphic_glob
*g
)
2241 int quarter_inch
=font::res
/4;
2243 if (! regions
.is_empty()) {
2244 regions
.start_from_head();
2246 r
= regions
.get_data();
2247 // must prevent grohtml from growing a region through a html raw command
2248 if (is_intersection(g
->minv
, g
->maxv
, r
->minv
, r
->maxv
+quarter_inch
) &&
2249 (no_raw_commands(r
->minv
, r
->maxv
+quarter_inch
))) {
2250 #if defined(DEBUGGING)
2252 printf("r minh=%d minv=%d maxh=%d maxv=%d\n",
2253 r
->minh
, r
->minv
, r
->maxh
, r
->maxv
);
2254 printf("g minh=%d minv=%d maxh=%d maxv=%d\n",
2255 g
->minh
, g
->minv
, g
->maxh
, g
->maxv
);
2257 r
->minv
= min(r
->minv
, g
->minv
);
2258 r
->maxv
= max(r
->maxv
, g
->maxv
);
2259 r
->minh
= min(r
->minh
, g
->minh
);
2260 r
->maxh
= max(r
->maxh
, g
->maxh
);
2261 #if defined(DEBUGGING)
2262 printf(" r minh=%d minv=%d maxh=%d maxv=%d\n",
2263 r
->minh
, r
->minv
, r
->maxh
, r
->maxv
);
2267 regions
.move_right();
2268 } while (! regions
.is_equal_to_head());
2275 * make_new_region - creates a new region to contain, g.
2278 void page::make_new_region (graphic_glob
*g
)
2280 region_glob
*r
=new region_glob
;
2290 void html_printer::dump_page(void)
2294 printf("\n\ndebugging start\n");
2295 page_contents
->words
.start_from_head();
2297 g
= page_contents
->words
.get_data();
2298 printf("%s ", g
->text_string
);
2299 page_contents
->words
.move_right();
2300 } while (! page_contents
->words
.is_equal_to_head());
2301 printf("\ndebugging end\n\n");
2306 * traverse_page_regions - runs through the regions in current_page
2307 * and generate html for text, and troff output
2311 void html_printer::traverse_page_regions (void)
2315 start_region_vpos
= 0;
2316 start_region_hpos
= 0;
2317 end_region_vpos
= -1;
2318 end_region_hpos
= -1;
2320 if (! page_contents
->regions
.is_empty()) {
2321 page_contents
->regions
.start_from_head();
2323 r
= page_contents
->regions
.get_data();
2325 end_region_vpos
= r
->minv
-1;
2327 end_region_vpos
= 0;
2329 end_region_hpos
= -1;
2330 display_globs(TRUE
);
2331 calculate_region_margins(r
);
2332 start_region_vpos
= end_region_vpos
;
2333 end_region_vpos
= r
->maxv
;
2334 start_region_hpos
= r
->minh
;
2335 end_region_hpos
= r
->maxh
;
2336 display_globs(FALSE
);
2337 start_region_vpos
= end_region_vpos
+1;
2338 start_region_hpos
= 0;
2339 page_contents
->regions
.move_right();
2340 } while (! page_contents
->regions
.is_equal_to_head());
2341 start_region_vpos
= end_region_vpos
+1;
2342 start_region_hpos
= 0;
2343 end_region_vpos
= -1;
2344 end_region_hpos
= -1;
2346 display_globs(TRUE
);
2349 int html_printer::is_within_region (text_glob
*t
)
2353 if (start_region_hpos
== -1) {
2356 hs
= start_region_hpos
;
2358 if (end_region_vpos
== -1) {
2361 ve
= end_region_vpos
;
2363 if (end_region_hpos
== -1) {
2366 he
= end_region_hpos
;
2368 return( is_subsection(t
->minv
, t
->maxv
, start_region_vpos
, ve
) &&
2369 is_subsection(t
->minh
, t
->maxh
, hs
, he
) );
2372 int html_printer::is_within_region (graphic_glob
*g
)
2376 if (start_region_hpos
== -1) {
2379 hs
= start_region_hpos
;
2381 if (end_region_vpos
== -1) {
2384 ve
= end_region_vpos
;
2386 if (end_region_hpos
== -1) {
2389 he
= end_region_hpos
;
2391 return( is_subsection(g
->minv
, g
->maxv
, start_region_vpos
, ve
) &&
2392 is_subsection(g
->minh
, g
->maxh
, hs
, he
) );
2395 int html_printer::is_less (graphic_glob
*g
, text_glob
*t
)
2397 return( (g
->minv
< t
->minv
) || ((g
->minv
== t
->minv
) && (g
->minh
< t
->minh
)) );
2400 void html_printer::convert_to_image (char *troff_src
, char *image_name
)
2403 char *ps_src
= mktemp(xtmptemplate("-ps-"));
2405 sprintf(buffer
, "grops %s > %s\n", troff_src
, ps_src
);
2407 fprintf(stderr
, buffer
);
2411 if (image_type
== gif
) {
2413 "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",
2415 (end_region_hpos
-start_region_hpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2416 (end_region_vpos
-start_region_vpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2417 ps_src
, image_name
);
2420 "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s - 2> /dev/null > %s.png \n",
2423 (end_region_hpos
-start_region_hpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2424 (end_region_vpos
-start_region_vpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2425 ps_src
, image_name
);
2428 "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",
2431 (end_region_hpos
-start_region_hpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2432 (end_region_vpos
-start_region_vpos
)*image_res
/font::res
+IMAGE_BOARDER_PIXELS
,
2433 name
, name
, name
, image_name
);
2437 fprintf(stderr
, "%s", buffer
);
2444 void html_printer::prologue (void)
2446 troff
.put_string("x T ps\nx res ");
2447 troff
.put_number(postscript_res
);
2448 troff
.put_string(" 1 1\nx init\np1\n");
2451 void html_printer::display_globs (int is_to_html
)
2457 int something
=FALSE
;
2458 int is_center
=FALSE
;
2463 is_center
= html_position_region();
2464 make_new_image_name();
2465 f
= xtmpfile(&troff_src
, "-troff-", FALSE
);
2470 if (! page_contents
->words
.is_empty()) {
2471 page_contents
->words
.start_from_head();
2472 t
= page_contents
->words
.get_data();
2475 if (! page_contents
->lines
.is_empty()) {
2476 page_contents
->lines
.start_from_head();
2477 g
= page_contents
->lines
.get_data();
2482 if ((t
!= 0) && (strcmp(t
->text_string
, "(1.a)") == 0)) {
2486 if ((t
== 0) && (g
!= 0)) {
2487 if (is_within_region(g
)) {
2489 display_line(g
, is_to_html
);
2491 if (page_contents
->lines
.is_empty() || page_contents
->lines
.is_equal_to_tail()) {
2494 g
= page_contents
->lines
.move_right_get_data();
2496 } else if ((g
== 0) && (t
!= 0)) {
2497 if (is_within_region(t
)) {
2498 display_word(t
, is_to_html
);
2501 if (page_contents
->words
.is_empty() || page_contents
->words
.is_equal_to_tail()) {
2504 t
= page_contents
->words
.move_right_get_data();
2507 if ((g
== 0) || (t
== 0)) {
2508 // hmm nothing to print out...
2509 } else if (is_less(g
, t
)) {
2510 if (is_within_region(g
)) {
2511 display_line(g
, is_to_html
);
2514 if (page_contents
->lines
.is_empty() || page_contents
->lines
.is_equal_to_tail()) {
2517 g
= page_contents
->lines
.move_right_get_data();
2520 if (is_within_region(t
)) {
2521 display_word(t
, is_to_html
);
2524 if (page_contents
->words
.is_empty() || page_contents
->words
.is_equal_to_tail()) {
2527 t
= page_contents
->words
.move_right_get_data();
2531 } while ((t
!= 0) || (g
!= 0));
2533 if ((! is_to_html
) && (f
!= 0)) {
2534 fclose(troff
.get_file());
2536 convert_to_image(troff_src
, image_name
);
2540 begin_paragraph(center_alignment
);
2541 force_begin_paragraph();
2543 html
.put_string("<img src=\"");
2544 html
.put_string(image_name
);
2545 if (image_type
== gif
) {
2546 html
.put_string(".gif\"");
2548 html
.put_string(".png\"");
2550 html
.put_string(">\n");
2556 output_vpos
= end_region_vpos
;
2558 need_one_newline
= FALSE
;
2565 void html_printer::flush_page (void)
2569 output_hpos
= get_left();
2570 supress_sub_sup
= TRUE
;
2574 html
.begin_comment("left margin: ").comment_arg(i_to_a(left_margin_indent
)).end_comment();;
2575 html
.begin_comment("right margin: ").comment_arg(i_to_a(right_margin_indent
)).end_comment();;
2576 remove_redundant_regions();
2577 page_contents
->calculate_region();
2578 remove_duplicate_regions();
2580 supress_sub_sup
= TRUE
;
2581 traverse_page_regions();
2582 terminate_current_font();
2583 if (need_one_newline
) {
2588 // move onto a new page
2589 delete page_contents
;
2590 page_contents
= new page
;
2593 static int convertSizeToHTML (int size
)
2597 } else if (size
< 8) {
2599 } else if (size
< 10) {
2601 } else if (size
< 12) {
2603 } else if (size
< 14) {
2605 } else if (size
< 16) {
2607 } else if (size
< 18) {
2615 void html_printer::write_html_font_face (const char *fontname
, const char *left
, const char *right
)
2617 switch (fontname
[0]) {
2619 case 'C': html
.put_string(left
) ; html
.put_string("tt"); html
.put_string(right
);
2628 void html_printer::write_html_font_type (const char *fontname
, const char *left
, const char *right
)
2630 if (strcmp(&fontname
[1], "B") == 0) {
2631 html
.put_string(left
) ; html
.put_string("B"); html
.put_string(right
);
2632 } else if (strcmp(&fontname
[1], "I") == 0) {
2633 html
.put_string(left
) ; html
.put_string("I"); html
.put_string(right
);
2634 } else if (strcmp(&fontname
[1], "BI") == 0) {
2635 html
.put_string(left
) ; html
.put_string("EM"); html
.put_string(right
);
2640 void html_printer::html_change_font (text_glob
*g
, const char *fontname
, int size
)
2644 if (output_style
.f
!= 0) {
2645 const char *oldfontname
= output_style
.f
->get_name();
2647 // firstly terminate the current font face and type
2648 if ((oldfontname
!= 0) && (oldfontname
!= fontname
)) {
2649 write_html_font_face(oldfontname
, "</", ">");
2650 write_html_font_type(oldfontname
, "</", ">");
2654 if ((output_style
.point_size
!= size
) && (output_style
.point_size
!= 0)) {
2655 // shutdown the previous font size
2656 html
.put_string("</font>");
2659 if ((output_style
.point_size
!= size
) && (size
!= 0)) {
2660 // now emit the size if it has changed
2661 sprintf(buffer
, "<font size=%d>", convertSizeToHTML(size
));
2662 html
.put_string(buffer
);
2663 output_style
.point_size
= size
; // and remember the size
2665 output_style
.f
= 0; // no style at present
2666 output_style
.point_size
= size
; // remember current font size
2668 if (fontname
!= 0) {
2669 if (! g
->is_raw_command
) {
2670 // now emit the new font
2671 write_html_font_face(fontname
, "<", ">");
2673 // now emit the new font type
2674 write_html_font_type(fontname
, "<", ">");
2676 output_style
= g
->text_style
; // remember style for next time
2682 void html_printer::change_font (text_glob
*g
, int is_to_html
)
2685 if (output_style
!= g
->text_style
) {
2686 const char *fontname
=0;
2689 if (g
->text_style
.f
!= 0) {
2690 fontname
= g
->text_style
.f
->get_name();
2691 size
= (font::res
/(72*font::sizescale
))*g
->text_style
.point_size
;
2693 html_change_font(g
, fontname
, size
);
2697 if (output_style
!= g
->text_style
) {
2698 if (g
->text_style
.f
!= 0) {
2699 const char *fontname
= g
->text_style
.f
->get_name();
2700 int size
= (font::res
/(72*font::sizescale
))*g
->text_style
.point_size
;
2702 if (fontname
== 0) {
2703 fatal("no internalname specified for font");
2706 troff_change_font(fontname
, size
, g
->text_style
.font_no
);
2707 output_style
= g
->text_style
; // remember style for next time
2714 * is_bold - returns TRUE if the text inside, g, is using a bold face.
2715 * It returns FALSE is g contains a raw html command, even if this uses
2719 int html_printer::is_bold (text_glob
*g
)
2721 if (g
->text_style
.f
== 0) {
2724 } else if (g
->is_raw_command
) {
2727 const char *fontname
= g
->text_style
.f
->get_name();
2729 if (strlen(fontname
) >= 2) {
2730 return( fontname
[1] == 'B' );
2737 void html_printer::terminate_current_font (void)
2741 // we create a dummy glob just so we can tell html_change_font not to start up
2743 g
.is_raw_command
= TRUE
;
2744 html_change_font(&g
, 0, 0);
2747 void html_printer::write_header (text_glob
*g
)
2749 if (strlen(header
.header_buffer
) > 0) {
2750 if (header
.header_level
> 7) {
2751 header
.header_level
= 7;
2754 if (cutoff_heading
+2 > header
.header_level
) {
2755 // firstly we must terminate any font and type faces
2756 terminate_current_font();
2759 // secondly we generate a tag
2760 html
.put_string("<a name=\"");
2761 html
.put_string(header
.header_buffer
);
2762 html
.put_string("\"></a>");
2763 // now we save the header so we can issue a list of link
2766 header
.no_of_headings
++;
2768 text_glob
*h
=new text_glob(&st
,
2769 header
.headings
.add_string(header
.header_buffer
, strlen(header
.header_buffer
)),
2770 strlen(header
.header_buffer
),
2771 header
.no_of_headings
, header
.header_level
,
2772 header
.no_of_headings
, header
.header_level
,
2774 header
.headers
.add(h
); // and add this header to the header list
2776 terminate_current_font();
2780 // we adjust the margin if necessary
2782 if (g
->minh
< left_margin_indent
) {
2783 header_indent
= g
->minh
;
2786 // and now we issue the real header
2787 html
.put_string("<h");
2788 html
.put_number(header
.header_level
);
2789 html
.put_string(">");
2790 html
.put_string(header
.header_buffer
);
2791 html
.put_string("</h");
2792 html
.put_number(header
.header_level
);
2793 html
.put_string(">");
2795 need_one_newline
= FALSE
;
2796 begin_paragraph(left_alignment
);
2797 header
.written_header
= TRUE
;
2802 * translate_str_to_html - translates a string, str, into html representation.
2803 * len indicates the string length.
2806 void translate_str_to_html (font
*f
, char *str
, int len
)
2808 char buf
[MAX_STRING_LENGTH
];
2810 str_translate_to_html(f
, buf
, MAX_STRING_LENGTH
, str
, len
, TRUE
);
2811 strncpy(str
, buf
, max(len
, strlen(buf
)+1));
2815 * write_headings - emits a list of links for the headings in this document
2818 void header_desc::write_headings (FILE *f
)
2822 if (! headers
.is_empty()) {
2823 headers
.start_from_head();
2825 g
= headers
.get_data();
2826 fprintf(f
, "<a href=\"#%s\">%s</a><br>\n", g
->text_string
, g
->text_string
);
2827 headers
.move_right();
2828 } while (! headers
.is_equal_to_head());
2832 void html_printer::determine_header_level (void)
2835 int l
=strlen(header
.header_buffer
);
2838 for (i
=0; ((i
<l
) && ((header
.header_buffer
[i
] == '.') || is_digit(header
.header_buffer
[i
]))) ; i
++) {
2839 if (header
.header_buffer
[i
] == '.') {
2844 header
.header_level
= stops
;
2849 void html_printer::build_header (text_glob
*g
)
2853 char buf
[MAX_STRING_LENGTH
];
2855 strcpy(header
.header_buffer
, "");
2858 current_vpos
= g
->minv
;
2859 str_translate_to_html(g
->text_style
.f
, buf
, MAX_STRING_LENGTH
, g
->text_string
, g
->text_length
, TRUE
);
2860 strcat(header
.header_buffer
, (char *)buf
);
2861 page_contents
->words
.move_right();
2862 g
= page_contents
->words
.get_data();
2863 if (g
->minv
== current_vpos
) {
2864 strcat(header
.header_buffer
, " ");
2866 } while ((! page_contents
->words
.is_equal_to_head()) &&
2867 ((g
->minv
== current_vpos
) || (l
->maxh
== right_margin_indent
)));
2869 determine_header_level();
2870 // finally set the output to neutral for after the header
2872 g
= page_contents
->words
.get_data();
2873 output_vpos
= g
->minv
; // set output_vpos to the next line since
2874 output_hpos
= left_margin_indent
; // html header forces a newline anyway
2875 page_contents
->words
.move_left(); // so that next time we use old g
2877 need_one_newline
= FALSE
;
2882 * is_whole_line_bold - returns TRUE if the whole line is bold.
2885 int html_printer::is_whole_line_bold (text_glob
*g
)
2888 int current_vpos
=g
->minv
;
2892 page_contents
->words
.move_right();
2893 n
= page_contents
->words
.get_data();
2895 while (page_contents
->words
.get_data() != g
) {
2896 page_contents
->words
.move_left();
2900 } while ((! page_contents
->words
.is_equal_to_head()) && (is_on_same_line(n
, current_vpos
)));
2901 // was (n->minv == current_vpos)
2902 while (page_contents
->words
.get_data() != g
) {
2903 page_contents
->words
.move_left();
2910 * is_a_header - returns TRUE if the whole sequence of contineous lines are bold.
2911 * It checks to see whether a line is likely to be contineous and
2912 * then checks that all words are bold.
2915 int html_printer::is_a_header (text_glob
*g
)
2923 current_vpos
= n
->minv
;
2925 page_contents
->words
.move_right();
2926 n
= page_contents
->words
.get_data();
2928 while (page_contents
->words
.get_data() != g
) {
2929 page_contents
->words
.move_left();
2933 } while ((! page_contents
->words
.is_equal_to_head()) &&
2934 ((n
->minv
== current_vpos
) || (l
->maxh
== right_margin_indent
)));
2935 while (page_contents
->words
.get_data() != g
) {
2936 page_contents
->words
.move_left();
2942 int html_printer::processed_header (text_glob
*g
)
2944 if ((guess_on
) && (g
->minh
<= left_margin_indent
) && (! using_table_for_indent()) &&
2954 int is_punctuation (char *s
, int length
)
2956 return( (length
== 1) &&
2957 ((s
[0] == '(') || (s
[0] == ')') || (s
[0] == '!') || (s
[0] == '.') || (s
[0] == '[') ||
2958 (s
[0] == ']') || (s
[0] == '?') || (s
[0] == ',') || (s
[0] == ';') || (s
[0] == ':') ||
2959 (s
[0] == '@') || (s
[0] == '#') || (s
[0] == '$') || (s
[0] == '%') || (s
[0] == '^') ||
2960 (s
[0] == '&') || (s
[0] == '*') || (s
[0] == '+') || (s
[0] == '-') || (s
[0] == '=') ||
2961 (s
[0] == '{') || (s
[0] == '}') || (s
[0] == '|') || (s
[0] == '\"') || (s
[0] == '\''))
2966 * move_horizontal - moves right into the position, g->minh.
2969 void html_printer::move_horizontal (text_glob
*g
, int left_margin
)
2971 if (g
->text_style
.f
!= 0) {
2972 int w
= g
->text_style
.f
->get_space_width(g
->text_style
.point_size
);
2975 fatal("space width is zero");
2977 if ((output_hpos
== left_margin
) && (g
->minh
> output_hpos
)) {
2978 make_html_indent(g
->minh
-output_hpos
);
2980 emit_space(g
, FALSE
);
2982 output_hpos
= g
->maxh
;
2983 output_vpos
= g
->minv
;
2985 change_font(g
, TRUE
);
2990 * looks_like_subscript - returns TRUE if, g, looks like a subscript.
2993 int html_printer::looks_like_subscript (text_glob
*g
)
2996 int height
= output_style
.point_size
*r
/72;
2998 /* was return( ((output_vpos < g->minv) && (output_style.point_size != 0) &&
2999 * (output_style.point_size > g->text_style.point_size)) );
3002 return( (output_style
.point_size
!= 0) && (! supress_sub_sup
) && (output_vpos
+height
< g
->maxv
) );
3006 * looks_like_superscript - returns TRUE if, g, looks like a superscript.
3009 int html_printer::looks_like_superscript (text_glob
*g
)
3012 int height
= output_style
.point_size
*r
/72;
3015 * return(((output_vpos > g->minv) && (output_style.point_size != 0) &&
3016 * (output_style.point_size > g->text_style.point_size)));
3019 return( (output_style
.point_size
!= 0) && (! supress_sub_sup
) && (output_vpos
+height
> g
->maxv
) );
3023 * looks_like_larger_font - returns TRUE if, g, can be treated as a larger font.
3024 * g needs to be on the same line
3027 int html_printer::looks_like_larger_font (text_glob
*g
)
3030 int height
= output_style
.point_size
*r
/72;
3032 return( (output_vpos
+height
== g
->maxv
) && (output_style
.point_size
!= 0) &&
3033 (convertSizeToHTML(g
->text_style
.point_size
)+1 == convertSizeToHTML(output_style
.point_size
)) );
3037 * looks_like_smaller_font - returns TRUE if, g, can be treated as a smaller font.
3038 * g needs to be on the same line
3041 int html_printer::looks_like_smaller_font (text_glob
*g
)
3044 int height
= output_style
.point_size
*r
/72;
3046 return( (output_vpos
+height
== g
->maxv
) && (output_style
.point_size
!= 0) &&
3047 (convertSizeToHTML(g
->text_style
.point_size
) == convertSizeToHTML(output_style
.point_size
)+1) );
3051 * pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob.
3052 * Note that it believes a single word spanning the left..right as being
3053 * on a different line.
3056 int html_printer::pretend_is_on_same_line (text_glob
*g
, int left_margin
, int right_margin
)
3058 return( auto_on
&& (right_margin
== output_hpos
) && (left_margin
== g
->minh
) &&
3059 (right_margin
!= g
->maxh
) && ((! is_whole_line_bold(g
)) || (g
->text_style
.f
== output_style
.f
)) &&
3060 (! (using_table_for_indent()) || (indentation
.wrap_margin
)) );
3063 int html_printer::is_on_same_line (text_glob
*g
, int vpos
)
3066 if (g
->is_html_command
) {
3072 (is_intersection(vpos
, vpos
+g
->text_style
.point_size
*font::res
/72-1, g
->minv
, g
->maxv
))
3078 * make_html_indent - creates a relative indentation.
3081 void html_printer::make_html_indent (int indent
)
3083 if ((indent
> 0) && ((right_margin_indent
-get_left()) > 0) &&
3084 ((indent
*100)/(right_margin_indent
-get_left()))) {
3085 html
.put_string("<span style=\" text-indent: ");
3086 html
.put_number((indent
*100)/(right_margin_indent
-get_left()));
3087 html
.put_string("%;\"></span>");
3092 * using_table_for_indent - returns TRUE if we currently using a table for indentation
3096 int html_printer::using_table_for_indent (void)
3098 return( indentation
.no_of_columns
!= 0 );
3102 * calculate_min_gap - returns the minimum gap by which we deduce columns.
3103 * This is a rough heuristic.
3106 int html_printer::calculate_min_gap (text_glob
*g
)
3110 while ((t
->is_raw_command
) && (! page_contents
->words
.is_equal_to_tail()) &&
3111 ((t
->minv
< end_region_vpos
) || (end_region_vpos
< 0))) {
3112 page_contents
->words
.move_right();
3113 t
=page_contents
->words
.get_data();
3116 if (t
->is_raw_command
) {
3117 return( font::res
* 10 ); // impossibly large gap width
3119 return( t
->text_style
.f
->get_space_width(t
->text_style
.point_size
)*GAP_SPACES
);
3124 * collect_columns - place html text in a column and return the vertical limit reached.
3127 int html_printer::collect_columns (struct text_defn
*next_words
,
3128 struct text_defn
*next_cols
,
3129 struct text_defn
*last_words
,
3130 struct text_defn
*last_cols
,
3133 text_glob
*start
= page_contents
->words
.get_data();
3134 text_glob
*t
= start
;
3135 int upper_limit
= 0;
3138 * initialize cols and words
3140 next_words
[0].left
= 0;
3141 next_words
[0].right
= 0;
3142 next_cols
[0].left
= 0;
3143 next_cols
[0].right
= 0;
3146 * if we have not reached the end collect the words on the current line
3149 int graphic_limit
= end_region_vpos
;
3151 if (is_whole_line_bold(t
) && (t
->minh
<= left_margin_indent
) && (guess_on
)) {
3153 * found header therefore terminate indentation table.
3154 * Return a negative number so we know a header has
3155 * stopped the column
3157 upper_limit
= -t
->minv
;
3159 int i
=0; // is the index into next_cols
3160 int j
=0; // is the column index for last_cols
3161 int k
=0; // is the index into next_words
3162 int l
=0; // is the index into next_words
3164 int mingap
=calculate_min_gap(start
);
3167 * while words on the same line record them and any significant gaps
3169 while ((t
!= 0) && (is_on_same_line(t
, start
->minv
) && (i
<max_words
)) &&
3170 ((graphic_limit
== -1) || (graphic_limit
> t
->minv
))) {
3173 * now find column index from the last line which corresponds to, t.
3175 j
= find_column_index_in_line(t
, last_cols
);
3178 * now find word index from the last line which corresponds to, t.
3180 l
= find_column_index_in_line(t
, last_words
);
3183 * Note t->minh might equal t->maxh when we are passing a special device character via \X
3184 * we currently ignore this when considering tables
3186 * if we have found a significant gap then record it
3188 if (((t
->minh
- prevh
>= mingap
) ||
3189 ((last_cols
!= 0) && (last_cols
[j
].right
!= 0) && (t
->minh
== last_cols
[j
].left
))) &&
3190 (t
->minh
!= t
->maxh
)) {
3191 next_cols
[i
].left
= t
->minh
;
3192 next_cols
[i
].right
= t
->maxh
;
3195 * terminate the array
3198 next_cols
[i
].left
= 0;
3199 next_cols
[i
].right
= 0;
3203 * move previous right hand column to align with, t.
3206 if (t
->minh
> next_cols
[i
-1].left
) {
3208 * a simple precaution in case we get globs which are technically on the same line
3209 * (sadly this does occur sometimes - maybe we should be stricter with is_on_same_line)
3212 next_cols
[i
-1].right
= max(next_cols
[i
-1].right
, t
->maxh
);
3216 * remember to record the individual words
3218 next_words
[k
].left
= t
->minh
;
3219 next_words
[k
].right
= t
->maxh
;
3223 * and record the vertical upper limit
3225 upper_limit
= max(t
->minv
, upper_limit
);
3228 * and update prevh - used to detect a when a different line is seen
3233 * get next word into, t, which equals 0, if no word is found
3235 page_contents
->words
.move_right();
3236 t
= page_contents
->words
.get_data();
3237 if (page_contents
->words
.is_equal_to_head()) {
3243 * and terminate the next_words array
3247 next_words
[k
].left
= 0;
3248 next_words
[k
].right
= 0;
3252 * consistency check, next_cols, after removing redundant colums.
3255 remove_redundant_columns(next_cols
);
3258 for (k
=0; k
<count_columns(next_cols
); k
++) {
3259 if (next_cols
[k
].left
> next_cols
[k
].right
) {
3260 fprintf(stderr
, "left > right\n"); fflush(stderr
);
3262 fatal("next_cols has messed up columns");
3264 if ((k
>0) && (k
+1<count_columns(next_cols
)) && (next_cols
[k
].right
> next_cols
[k
+1].left
)) {
3265 fprintf(stderr
, "next_cols[k].right > next_cols[k+1].left\n"); fflush(stderr
);
3267 fatal("next_cols has messed up columns");
3273 return( upper_limit
);
3277 * conflict_with_words - returns TRUE if a word sequence crosses a column.
3280 int html_printer::conflict_with_words (struct text_defn
*column_guess
, struct text_defn
*words
)
3285 while ((column_guess
[i
].right
!= 0) && (i
<MAX_WORDS_PER_LINE
)) {
3287 while ((words
[j
].right
!= 0) && (j
<MAX_WORDS_PER_LINE
)) {
3288 if ((words
[j
].left
<= column_guess
[i
].right
) && (i
+1<MAX_WORDS_PER_LINE
) &&
3289 (column_guess
[i
+1].right
!= 0) && (words
[j
].right
>= column_guess
[i
+1].left
)) {
3290 if (debug_table_on
) {
3291 fprintf(stderr
, "is a conflict with words\n");
3300 if (debug_table_on
) {
3301 fprintf(stderr
, "is NOT a conflict with words\n");
3308 * combine_line - combines dest and src.
3311 void html_printer::combine_line (struct text_defn
*dest
, struct text_defn
*src
)
3315 for (i
=0; (i
<MAX_WORDS_PER_LINE
) && (src
[i
].right
!= 0); i
++) {
3316 include_into_list(dest
, &src
[i
]);
3318 remove_redundant_columns(dest
);
3322 * remove_entry_in_line - removes an entry, j, in, line.
3325 void html_printer::remove_entry_in_line (struct text_defn
*line
, int j
)
3327 while (line
[j
].right
!= 0) {
3328 line
[j
].left
= line
[j
+1].left
;
3329 line
[j
].right
= line
[j
+1].right
;
3335 * remove_redundant_columns - searches through the array columns and removes any redundant entries.
3338 void html_printer::remove_redundant_columns (struct text_defn
*line
)
3343 while (line
[i
].right
!= 0) {
3344 if ((i
<MAX_WORDS_PER_LINE
) && (line
[i
+1].right
!= 0)) {
3346 while ((j
<MAX_WORDS_PER_LINE
) && (line
[j
].right
!= 0)) {
3347 if ((j
!= i
) && (is_intersection(line
[i
].left
, line
[i
].right
, line
[j
].left
, line
[j
].right
))) {
3348 line
[i
].left
= min(line
[i
].left
, line
[j
].left
);
3349 line
[i
].right
= max(line
[i
].right
, line
[j
].right
);
3350 remove_entry_in_line(line
, j
);
3361 * include_into_list - performs an order set inclusion
3364 void html_printer::include_into_list (struct text_defn
*line
, struct text_defn
*item
)
3368 while ((i
<MAX_WORDS_PER_LINE
) && (line
[i
].right
!= 0) && (line
[i
].left
<item
->left
)) {
3372 if (line
[i
].right
== 0) {
3374 if (i
<MAX_WORDS_PER_LINE
) {
3375 if ((i
>0) && (line
[i
-1].left
> item
->left
)) {
3376 fatal("insertion error");
3378 line
[i
].left
= item
->left
;
3379 line
[i
].right
= item
->right
;
3385 if (line
[i
].left
== item
->left
) {
3386 line
[i
].right
= max(item
->right
, line
[i
].right
);
3389 int left
= item
->left
;
3390 int right
= item
->right
;
3391 int l
= line
[i
].left
;
3392 int r
= line
[i
].right
;
3394 while ((i
+1<MAX_WORDS_PER_LINE
) && (line
[i
].right
!= 0)) {
3395 line
[i
].left
= left
;
3396 line
[i
].right
= right
;
3403 if (i
+1<MAX_WORDS_PER_LINE
) {
3404 line
[i
].left
= left
;
3405 line
[i
].right
= right
;
3407 line
[i
+1].right
= 0;
3414 * is_in_column - return TRUE if value is present in line.
3417 int html_printer::is_in_column (struct text_defn
*line
, struct text_defn
*item
, int max_words
)
3421 while ((i
<max_words
) && (line
[i
].right
!= 0)) {
3422 if (line
[i
].left
== item
->left
) {
3432 * calculate_right - calculate the right most margin for each column in line.
3435 void html_printer::calculate_right (struct text_defn
*line
, int max_words
)
3439 while ((i
<max_words
) && (line
[i
].right
!= 0)) {
3441 line
[i
-1].right
= line
[i
].left
;
3448 * add_right_full_width - adds an extra column to the right to bring the table up to
3452 void html_printer::add_right_full_width (struct text_defn
*line
, int mingap
)
3456 while ((i
<MAX_WORDS_PER_LINE
) && (line
[i
].right
!= 0)) {
3460 if ((i
>0) && (line
[i
-1].right
!= right_margin_indent
) && (i
+1<MAX_WORDS_PER_LINE
)) {
3461 line
[i
].left
= min(line
[i
-1].right
+mingap
, right_margin_indent
);
3462 line
[i
].right
= right_margin_indent
;
3464 if (i
<MAX_WORDS_PER_LINE
) {
3472 * determine_right_most_column - works out the right most limit of the right most column.
3473 * Required as we might be performing a .2C and only
3474 * have enough text to fill the left column.
3477 void html_printer::determine_right_most_column (struct text_defn
*line
, int max_words
)
3481 while ((i
<max_words
) && (line
[i
].right
!= 0)) {
3485 // remember right_margin_indent is the right most position for this page
3486 line
[i
-1].right
= column_calculate_right_margin(line
[i
-1].left
, right_margin_indent
);
3491 * is_column_match - returns TRUE if a word is aligned in the same horizontal alignment
3492 * between two lines, line1 and line2. If so then this horizontal
3493 * position is saved in match.
3496 int html_printer::is_column_match (struct text_defn
*match
,
3497 struct text_defn
*line1
, struct text_defn
*line2
, int max_words
)
3502 int first
=(match
[0].left
==0);
3507 t
.left
= left_margin_indent
;
3510 include_into_list(match
, &t
);
3512 while ((line1
[i
].right
!= 0) && (line2
[i
].right
!= 0)) {
3513 if (line1
[i
].left
== line2
[j
].left
) {
3514 // same horizontal alignment found
3515 include_into_list(match
, &line1
[i
]);
3519 } else if (line1
[i
].left
< line2
[j
].left
) {
3525 calculate_right(match
, max_words
);
3530 * check_lack_of_hits - returns TRUE if a column has been moved to a position
3531 * of only one hit from a position of more than one hit.
3534 int html_printer::check_lack_of_hits (struct text_defn
*next_guess
,
3535 struct text_defn
*last_guess
,
3536 text_glob
*start
, int limit
)
3538 text_glob
*current
=page_contents
->words
.get_data();
3539 int n
=count_columns(last_guess
);
3540 int m
=count_columns(next_guess
);
3544 rewind_text_to(start
);
3545 count_hits(last_guess
, n
, limit
);
3546 rewind_text_to(current
);
3549 while ((i
<n
) && (j
<m
) &&
3550 (last_guess
[i
].right
!= 0) && (next_guess
[j
].right
!= 0)) {
3551 if ((is_intersection(last_guess
[i
].left
, last_guess
[i
].right
,
3552 next_guess
[j
].left
, next_guess
[j
].right
)) &&
3553 (next_guess
[j
].left
< last_guess
[i
].left
) &&
3554 (last_guess
[i
].is_used
>= 2)) {
3556 * next_guess has to be = 1 as this position is new
3560 if (last_guess
[i
].left
< next_guess
[j
].left
) {
3571 * remove_white_using_words - remove white space in, last_guess, by examining, next_line
3572 * placing results into next_guess.
3573 * It returns TRUE if the same columns exist in next_guess and last_guess
3574 * we do allow columns to shrink but if a column disappears then we return FALSE.
3577 int html_printer::remove_white_using_words (struct text_defn
*next_guess
,
3578 struct text_defn
*last_guess
, struct text_defn
*next_line
)
3585 while ((last_guess
[j
].right
!= 0) && (next_line
[k
].right
!= 0)) {
3586 if (last_guess
[j
].left
== next_line
[k
].left
) {
3587 // same horizontal alignment found
3588 next_guess
[i
].left
= last_guess
[j
].left
;
3589 next_guess
[i
].right
= max(last_guess
[j
].right
, next_line
[k
].right
);
3593 if ((next_guess
[i
-1].right
> last_guess
[j
].left
) && (last_guess
[j
].right
!= 0)) {
3596 } else if (last_guess
[j
].right
< next_line
[k
].left
) {
3597 next_guess
[i
].left
= last_guess
[j
].left
;
3598 next_guess
[i
].right
= last_guess
[j
].right
;
3601 } else if (last_guess
[j
].left
> next_line
[k
].right
) {
3602 // insert a word sequence from next_line[k]
3603 next_guess
[i
].left
= next_line
[k
].left
;
3604 next_guess
[i
].right
= next_line
[k
].right
;
3607 } else if (is_intersection(last_guess
[j
].left
, last_guess
[j
].right
, next_line
[k
].left
, next_line
[k
].right
)) {
3608 // potential for a column disappearing
3609 next_guess
[i
].left
= min(last_guess
[j
].left
, next_line
[k
].left
);
3610 next_guess
[i
].right
= max(last_guess
[j
].right
, next_line
[k
].right
);
3614 if ((next_guess
[i
-1].right
> last_guess
[j
].left
) && (last_guess
[j
].right
!= 0)) {
3619 while (next_line
[k
].right
!= 0) {
3620 next_guess
[i
].left
= next_line
[k
].left
;
3621 next_guess
[i
].right
= next_line
[k
].right
;
3625 if (i
<MAX_WORDS_PER_LINE
) {
3626 next_guess
[i
].left
= 0;
3627 next_guess
[i
].right
= 0;
3629 if (debug_table_on
) {
3631 fprintf(stderr
, "have removed column\n");
3633 fprintf(stderr
, "have NOT removed column\n");
3637 remove_redundant_columns(next_guess
);
3642 * count_columns - returns the number of elements inside, line.
3645 int html_printer::count_columns (struct text_defn
*line
)
3649 while (line
[i
].right
!= 0) {
3656 * rewind_text_to - moves backwards until page_contents is looking at, g.
3659 void html_printer::rewind_text_to (text_glob
*g
)
3661 while (page_contents
->words
.get_data() != g
) {
3662 if (page_contents
->words
.is_equal_to_head()) {
3663 page_contents
->words
.start_from_tail();
3665 page_contents
->words
.move_left();
3671 * can_loose_column - checks to see whether we should combine two columns.
3672 * This is allowed if there are is only one hit on the
3673 * left hand edge and the previous column is very close.
3676 void html_printer::can_loose_column (text_glob
*start
, struct text_defn
*last_guess
, int limit
)
3678 text_glob
*current
=page_contents
->words
.get_data();
3679 int n
=count_columns(last_guess
);
3682 rewind_text_to(start
);
3683 count_hits(last_guess
, n
, limit
);
3686 if ((last_guess
[i
+1].is_used
== 1) &&
3687 (calculate_min_gap(start
) > (last_guess
[i
+1].left
-last_guess
[i
].right
))) {
3688 last_guess
[i
].right
= last_guess
[i
+1].right
;
3689 remove_entry_in_line(last_guess
, i
+1);
3690 n
= count_columns(last_guess
);
3696 rewind_text_to(current
);
3700 * display_columns - a long overdue debugging function, as this column code is causing me grief :-(
3703 void html_printer::display_columns (const char *word
, const char *name
, text_defn
*line
)
3707 fprintf(stderr
, "[%s:%s]", name
, word
);
3708 while (line
[i
].right
!= 0) {
3709 fprintf(stderr
, " <left=%d right=%d %d%%> ", line
[i
].left
, line
[i
].right
, line
[i
].percent
);
3712 fprintf(stderr
, "\n");
3717 * copy_line - dest = src
3720 void html_printer::copy_line (struct text_defn
*dest
, struct text_defn
*src
)
3724 for (k
=0; ((src
[k
].right
!= 0) && (k
<MAX_WORDS_PER_LINE
)); k
++) {
3725 dest
[k
].left
= src
[k
].left
;
3726 dest
[k
].right
= src
[k
].right
;
3728 if (k
<MAX_WORDS_PER_LINE
) {
3735 * add_column_gaps - adds empty columns between columns which don't exactly align
3738 void html_printer::add_column_gaps (struct text_defn
*line
)
3743 // firstly lets see whether we need an initial column on the left hand side
3744 if ((line
[0].left
!= get_left()) && (line
[0].right
!= 0) &&
3745 (get_left() < line
[0].left
) && (is_worth_column(get_left(), line
[0].left
))) {
3746 t
.left
= get_left();
3747 t
.right
= line
[0].left
;
3748 include_into_list(line
, &t
);
3751 while ((i
<MAX_WORDS_PER_LINE
) && (line
[i
].right
!= 0)) {
3752 if ((i
+1<MAX_WORDS_PER_LINE
) && (line
[i
+1].right
!= 0) && (line
[i
].right
!= line
[i
+1].left
) &&
3753 (is_worth_column(line
[i
].right
, line
[i
+1].left
))) {
3754 t
.left
= line
[i
].right
;
3755 t
.right
= line
[i
+1].left
;
3756 include_into_list(line
, &t
);
3762 // now let us see whether we need a final column on the right hand side
3763 if ((i
>0) && (line
[i
-1].right
!= right_margin_indent
) &&
3764 (is_worth_column(line
[i
-1].right
, right_margin_indent
))) {
3765 t
.left
= line
[i
-1].right
;
3766 t
.right
= right_margin_indent
;
3767 include_into_list(line
, &t
);
3772 * is_continueous_column - returns TRUE if a line has a word on one
3773 * of the last_col right most boundaries.
3776 int html_printer::is_continueous_column (text_defn
*last_col
, text_defn
*next_line
)
3778 int w
= count_columns(next_line
);
3779 int c
= count_columns(last_col
);
3782 for (i
=0; i
<c
; i
++) {
3783 for (j
=0; j
<w
; j
++) {
3784 if (last_col
[i
].right
== next_line
[j
].right
) {
3793 * is_exact_left - returns TRUE if a line has a word on one
3794 * of the last_col left most boundaries.
3797 int html_printer::is_exact_left (text_defn
*last_col
, text_defn
*next_line
)
3799 int w
= count_columns(next_line
);
3800 int c
= count_columns(last_col
);
3803 for (i
=0; i
<c
; i
++) {
3804 for (j
=0; j
<w
; j
++) {
3805 if ((last_col
[i
].left
== next_line
[j
].left
) ||
3806 (last_col
[i
].left
!= left_margin_indent
)) {
3815 * continue_searching_column - decides whether we should carry on searching text for a column.
3818 int html_printer::continue_searching_column (text_defn
*next_col
,
3819 text_defn
*last_col
,
3820 text_defn
*all_words
)
3822 int count
= count_columns(next_col
);
3823 int words
= count_columns(all_words
);
3825 if ((words
== 0) || ((words
== 1) &&
3826 (all_words
[0].left
== left_margin_indent
) &&
3827 (all_words
[0].right
== right_margin_indent
))) {
3828 // no point as we have now seen a full line of contineous text with no gap
3831 return( (count
== count_columns(last_col
)) &&
3832 (last_col
[0].left
!= left_margin_indent
) || (last_col
[0].right
!= right_margin_indent
) );
3836 * is_worth_column - returns TRUE if the size of this column is worth defining.
3839 int html_printer::is_worth_column (int left
, int right
)
3842 return( abs(right
-left
) >= MIN_COLUMN
);
3848 * large_enough_gap - returns TRUE if a large enough gap for one line was seen.
3849 * We need to make sure that a single line definitely warrents
3851 * It also removes other smaller gaps.
3854 int html_printer::large_enough_gap (text_defn
*last_col
)
3859 int gap
=r
/GAP_WIDTH_ONE_LINE
;
3861 if (abs(last_col
[i
].left
- left_margin_indent
) >= gap
) {
3864 while ((last_col
[i
].right
!= 0) && (last_col
[i
+1].right
!= 0)) {
3865 if (abs(last_col
[i
+1].left
-last_col
[i
].right
) >= gap
) {
3869 // not good enough for a single line, remove it
3870 last_col
[i
].right
= last_col
[i
+1].right
;
3871 remove_entry_in_line(last_col
, i
+1);
3878 * is_subset_of_columns - returns TRUE if line, a, is a subset of line, b.
3881 int html_printer::is_subset_of_columns (text_defn
*a
, text_defn
*b
)
3887 while ((i
<MAX_WORDS_PER_LINE
) && (a
[i
].right
!= 0)) {
3889 while ((j
<MAX_WORDS_PER_LINE
) && (b
[j
].right
!= 0) &&
3890 ((b
[j
].left
!= a
[i
].left
) || (b
[j
].right
!= a
[i
].right
))) {
3893 if ((j
==MAX_WORDS_PER_LINE
) || (b
[j
].right
== 0)) {
3894 // found a different column - not a subset
3903 * count_hits - counts the number of hits per column. A left hit
3904 * is when the left hand position of a glob hits
3905 * the left hand column.
3908 void html_printer::count_hits (text_defn
*col
, int no_of_columns
, int limit
)
3911 text_glob
*start
= page_contents
->words
.get_data();
3912 text_glob
*g
= start
;
3914 // firstly reset the used field
3915 for (i
=0; i
<no_of_columns
; i
++) {
3918 // now calculate the left hand hits
3919 while ((g
!= 0) && (g
->minv
<= limit
)) {
3921 while ((i
<no_of_columns
) && (col
[i
].right
< g
->minh
)) {
3924 if ((col
[i
].left
== g
->minh
) && (col
[i
].right
!= 0)) {
3927 page_contents
->words
.move_right();
3928 if (page_contents
->words
.is_equal_to_head()) {
3930 page_contents
->words
.start_from_tail();
3932 g
=page_contents
->words
.get_data();
3938 * count_right_hits - counts the number of right hits per column.
3939 * A right hit is when the left hand position
3940 * of a glob hits the right hand column.
3943 void html_printer::count_right_hits (text_defn
*col
, int no_of_columns
)
3946 text_glob
*start
= page_contents
->words
.get_data();
3947 text_glob
*g
= start
;
3949 // firstly reset the used field
3950 for (i
=0; i
<no_of_columns
; i
++) {
3951 col
[i
].right_hits
= 0;
3953 // now calculate the left hand hits
3954 while ((g
!= 0) && (g
->minv
<= indentation
.vertical_limit
)) {
3956 while ((i
<no_of_columns
) && (col
[i
].right
< g
->minh
)) {
3959 if ((i
<no_of_columns
) && (col
[i
].right
== g
->maxh
)) {
3960 if (debug_table_on
) {
3961 fprintf(stderr
, "found right hit [%s] at %d in %d\n",
3962 g
->text_string
, g
->maxh
, i
);
3965 col
[i
].right_hits
++;
3967 page_contents
->words
.move_right();
3968 if (page_contents
->words
.is_equal_to_head()) {
3970 page_contents
->words
.start_from_tail();
3972 g
=page_contents
->words
.get_data();
3978 * right_indentation - returns TRUE if a single column has been found and
3979 * it resembles an indentation. Ie .RS/.RE or ABSTACT
3982 int html_printer::right_indentation (struct text_defn
*last_guess
)
3984 // it assumes that last_guess contains a single column
3985 return( (last_guess
[0].left
> left_margin_indent
) );
3989 * able_to_steal_width - returns TRUE if we have an unused column which we can steal from.
3990 * It must have more than MIN_TEXT_PERCENT to do this.
3993 int html_printer::able_to_steal_width (void)
3997 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
3998 if ((! indentation
.columns
[i
].is_used
) &&
3999 (indentation
.columns
[i
].percent
> MIN_TEXT_PERCENT
)) {
4007 * is_divisible_by - returns TRUE if n is divisible by d leaving no remainder.
4010 static int is_divisible_by (int n
, int d
)
4012 return( (n
% d
) == 0 );
4016 * need_to_steal_width - returns TRUE if a used column need to be
4017 * given a little extra width for safty sake.
4020 int html_printer::need_to_steal_width (void)
4024 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4025 if ((indentation
.columns
[i
].is_used
) &&
4026 (indentation
.columns
[i
].percent
== (((indentation
.columns
[i
].right
- indentation
.columns
[i
].left
) * 100) /
4027 (right_margin_indent
-left_margin_indent
))) &&
4028 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
)) {
4036 * utilize_round_off - utilize the remaining percent width in text columns
4039 void html_printer::utilize_round_off (void)
4041 int total
= total_percentages();
4044 // use up the spare excess
4048 for (i
=0; (i
<indentation
.no_of_columns
) && (excess
>0); i
++) {
4049 if ((indentation
.columns
[i
].is_used
) &&
4050 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
)) {
4051 indentation
.columns
[i
].percent
++;
4055 // we might as well try and keep any numbers simple if possible
4056 for (i
=0; (i
<indentation
.no_of_columns
) && (excess
>0); i
++) {
4057 if ((indentation
.columns
[i
].is_used
) &&
4058 (! is_divisible_by(indentation
.columns
[i
].percent
, MIN_TEXT_PERCENT
))) {
4059 indentation
.columns
[i
].percent
++;
4063 // forget the niceties lets just use excess up now!
4064 for (i
=0; (i
<indentation
.no_of_columns
) && (excess
>0); i
++) {
4065 if (indentation
.columns
[i
].is_used
) {
4066 indentation
.columns
[i
].percent
++;
4073 * can_distribute_fairly - returns TRUE if we can redistribute some of the unused width into
4074 * columns that are used.
4077 int html_printer::can_distribute_fairly (void)
4084 // firstly total up all percentages - so we can use round offs
4085 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4086 total
+= indentation
.columns
[i
].percent
;
4087 if ((indentation
.columns
[i
].is_used
) &&
4088 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
)) {
4094 if (excess
< used
) {
4095 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4096 if (! indentation
.columns
[i
].is_used
) {
4097 if (indentation
.columns
[i
].percent
> MIN_TEXT_PERCENT
) {
4098 indentation
.columns
[i
].percent
--;
4104 if (excess
>= used
) {
4105 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4106 if ((indentation
.columns
[i
].is_used
) &&
4107 (indentation
.columns
[i
].percent
< PERCENT_THRESHOLD
) &&
4108 (indentation
.columns
[i
].percent
== (((indentation
.columns
[i
].right
- indentation
.columns
[i
].left
) * 100) /
4109 (right_margin_indent
-left_margin_indent
)))) {
4110 indentation
.columns
[i
].percent
++;
4120 * remove_table_column - removes column, i, from the indentation.
4123 void html_printer::remove_table_column (int i
)
4125 while (i
<indentation
.no_of_columns
) {
4126 indentation
.columns
[i
].left
= indentation
.columns
[i
+1].left
;
4127 indentation
.columns
[i
].right
= indentation
.columns
[i
+1].right
;
4128 indentation
.columns
[i
].is_used
= indentation
.columns
[i
+1].is_used
;
4129 indentation
.columns
[i
].percent
= indentation
.columns
[i
+1].percent
;
4132 indentation
.no_of_columns
--;
4136 * next_line_on_left_column - returns TRUE if the next line in
4137 * column, i, has a word on the left margin.
4140 int html_printer::next_line_on_left_column (int i
, text_glob
*start
)
4142 int current_vpos
=start
->minv
;
4144 while ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
) &&
4145 (is_on_same_line(start
, current_vpos
))) {
4146 if (page_contents
->words
.is_equal_to_tail()) {
4149 page_contents
->words
.move_right();
4150 start
= page_contents
->words
.get_data();
4153 if ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
)) {
4154 // onto next line now
4155 current_vpos
=start
->minv
;
4156 while ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
) &&
4157 (is_on_same_line(start
, current_vpos
))) {
4158 if (start
->minh
== indentation
.columns
[i
].left
) {
4161 if (page_contents
->words
.is_equal_to_tail()) {
4164 page_contents
->words
.move_right();
4165 start
= page_contents
->words
.get_data();
4173 * will_wrap_text - returns TRUE if text is wrapped in column, i.
4176 int html_printer::will_wrap_text (int i
, text_glob
*start
)
4178 text_glob
*current
=page_contents
->words
.get_data();
4181 rewind_text_to(start
);
4182 while ((start
!= 0) && (start
->minv
< indentation
.vertical_limit
)) {
4183 if (indentation
.columns
[i
].right
== start
->maxh
) {
4184 // ok right word is on column boarder - check next line
4185 if (next_line_on_left_column(i
, start
)) {
4186 rewind_text_to(current
);
4190 if (page_contents
->words
.is_equal_to_tail()) {
4193 page_contents
->words
.move_right();
4194 start
= page_contents
->words
.get_data();
4198 rewind_text_to(current
);
4203 * remove_unnecessary_unused - runs through a table and decides whether an unused
4204 * column can be removed. This is only true if the
4205 * column to the left does not wrap text.
4208 void html_printer::remove_unnecessary_unused (text_glob
*start
)
4211 int left
=get_left();
4214 while (i
<indentation
.no_of_columns
) {
4215 if ((indentation
.columns
[i
].is_used
) &&
4216 (i
+1<indentation
.no_of_columns
) && (! indentation
.columns
[i
+1].is_used
)) {
4218 * so i+1 is unused and there is a used column to the left.
4219 * Now we check whether we can add the unused column to the column, i.
4220 * This can only be done if column, i, is not wrapping text.
4222 if (! will_wrap_text(i
, start
)) {
4224 if (i
+1 < indentation
.no_of_columns
) {
4225 right
= indentation
.columns
[i
+1].right
;
4227 right
= right_margin_indent
;
4229 indentation
.columns
[i
].percent
= (((right
- indentation
.columns
[i
].left
) * 100) /
4230 (right_margin_indent
-left
));
4232 indentation
.columns
[i
].percent
= (((indentation
.columns
[i
+1].right
- indentation
.columns
[i
].left
) * 100) /
4233 (right_margin_indent
-left
));
4235 remove_table_column(i
+1);
4244 * remove_zero_percentage_column - removes all zero percentage width columns
4247 void html_printer::remove_zero_percentage_column (void)
4251 while (i
<indentation
.no_of_columns
) {
4252 if (indentation
.columns
[i
].percent
== 0) {
4253 remove_table_column(i
);
4262 * get_left - returns the actual left most margin.
4265 int html_printer::get_left (void)
4267 if ((header_indent
< left_margin_indent
) && (header_indent
!= -1)) {
4268 return( header_indent
);
4273 return( left_margin_indent
);
4279 * calculate_percentage_width - calculates the percentage widths,
4280 * this function will be generous to
4281 * columns which have words as some browsers
4282 * produce messy output if the percentage is exactly
4283 * that required for text..
4284 * We try and round up to MIN_TEXT_PERCENT
4285 * of course we can only do this if we can steal from
4289 void html_printer::calculate_percentage_width (text_glob
*start
)
4292 int left
=get_left();
4295 // firstly calculate raw percentages
4296 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4298 indentation
.columns
[i
].percent
= (((indentation
.columns
[i
].right
- indentation
.columns
[i
].left
) * 100) /
4299 (right_margin_indent
-left
));
4301 if (i
+1 < indentation
.no_of_columns
) {
4302 right
= indentation
.columns
[i
+1].left
;
4304 right
= right_margin_indent
;
4306 indentation
.columns
[i
].percent
= (((right
- indentation
.columns
[i
].left
) * 100) /
4307 (right_margin_indent
-left
));
4310 if (debug_table_on
) {
4311 display_columns(start
->text_string
, "[b4 steal] indentation.columns", indentation
.columns
);
4314 // now steal from the unused columns..
4315 remove_unnecessary_unused(start
);
4317 if (debug_table_on
) {
4318 display_columns(start
->text_string
, "[after steal] indentation.columns", indentation
.columns
);
4322 utilize_round_off();
4324 remove_zero_percentage_column();
4329 * is_column_subset - returns TRUE if the columns described by small can be contained in
4330 * the columns in large.
4333 int html_printer::is_column_subset (struct text_defn
*small
, struct text_defn
*large
)
4335 int ns
=count_columns(small
);
4336 int nl
=count_columns(large
);
4345 if (is_intersection(small
[i
].left
, small
[i
].right
, large
[j
].left
, large
[j
].right
)) {
4347 if (! is_subsection(small
[i
].left
, small
[i
].right
, large
[j
].left
, large
[j
].right
)) {
4348 // found column which is not a subset
4359 // small cannot be an empty set
4364 * right_most_column - returns the right most column position.
4367 int html_printer::right_most_column (struct text_defn
*col
)
4369 int i
= count_columns(col
);
4372 return( col
[i
-1].right
);
4379 * large_enough_gap_for_two - returns TRUE if there exists a large enough gap
4383 int html_printer::large_enough_gap_for_two (struct text_defn
*col
)
4387 int gap
=MIN_COLUMN_FOR_TWO_LINES
;
4389 if (abs(col
[i
].left
- left_margin_indent
) >= gap
) {
4392 while ((col
[i
].right
!= 0) && (col
[i
+1].right
!= 0)) {
4393 if (abs(col
[i
+1].left
-col
[i
].right
) >= gap
) {
4397 // not good enough for this table, remove it
4398 col
[i
].right
= col
[i
+1].right
;
4399 remove_entry_in_line(col
, i
+1);
4406 * is_small_table - applies some rigorous rules to test whether we should start this
4407 * table at this point.
4410 int html_printer::is_small_table (int lines
, struct text_defn
*last_guess
,
4411 struct text_defn
*words_1
, struct text_defn
*cols_1
,
4412 struct text_defn
*words_2
, struct text_defn
*cols_2
,
4413 int *limit
, int *limit_1
)
4416 * firstly we check for an indented paragraph
4420 (count_columns(cols_1
) == count_columns(cols_2
)) && (count_columns(cols_1
) == 1) &&
4421 right_indentation(cols_1
) && (! right_indentation(cols_2
)) &&
4422 (cols_1
[0].right
== right_margin_indent
)) {
4428 * as we only have two lines in our table we need to examine in detail whether
4429 * we should construct a table from these two lines.
4430 * For example if the text is the start of an indented paragraph and
4431 * line1 and line2 are contineous then they should form one row in our table but
4432 * if line1 and line2 are not contineous it is safer to treat them separately.
4434 * We are prepared to reduce the table to one line
4436 if (((count_columns(cols_1
) != count_columns(cols_2
)) && (cols_1
[0].left
> cols_2
[0].left
)) ||
4437 (! ((is_column_subset(cols_1
, cols_2
)) ||
4438 (is_column_subset(cols_2
, cols_1
))))) {
4440 * now we must check to see whether line1 and line2 join
4442 if ((right_most_column(cols_1
) == right_margin_indent
) &&
4443 (cols_2
[0].left
== left_margin_indent
)) {
4445 * looks like they join, we don't want a table at all.
4450 * use single line table
4454 copy_line(last_guess
, cols_1
);
4458 if ((count_columns(last_guess
)==1) && (right_indentation(last_guess
))) {
4466 * check for large gap with single line or if multiple lines with more than one column
4470 if (large_enough_gap(last_guess
)) {
4474 } else if (count_columns(last_guess
)>1) {
4476 return( large_enough_gap_for_two(last_guess
) );
4485 * is_appropriate_to_start_table - returns TRUE if it is appropriate to start the table
4489 int html_printer::is_appropriate_to_start_table (struct text_defn
*cols_1
,
4490 struct text_defn
*cols_2
,
4491 struct text_defn
*last_guess
)
4493 if (count_columns(last_guess
) == 1) {
4494 if (debug_table_on
) {
4495 display_columns("", "[is] cols_1" , cols_1
);
4496 display_columns("", "[is] cols_2" , cols_2
);
4497 display_columns("", "[is] last_guess", last_guess
);
4500 if (! ((is_column_subset(cols_1
, cols_2
)) ||
4501 (is_column_subset(cols_2
, cols_1
)))) {
4504 if ((count_columns(cols_1
) == 1) &&
4505 (cols_1
[0].left
> left_margin_indent
) && (cols_1
[0].right
< right_margin_indent
) &&
4506 (cols_1
[0].right
!= cols_2
[0].right
) &&
4507 (count_columns(last_guess
) == 1)) {
4515 * is_a_full_width_column - returns TRUE if there exists a full width column.
4518 int html_printer::is_a_full_width_column (void)
4522 while (i
<indentation
.no_of_columns
) {
4523 if (((indentation
.columns
[i
].left
== get_left()) ||
4524 (indentation
.columns
[i
].left
== left_margin_indent
)) &&
4525 (indentation
.columns
[i
].right
== right_margin_indent
)) {
4534 * should_defer_table - returns TRUE if we should defer this table.
4535 * This can occur if the first line seen indent
4536 * is < than future lines. In which case it
4537 * will cause future lines in this table
4538 * to be indented. The lesser of the evils
4539 * is to treat the first line by itself.
4542 int html_printer::should_defer_table (int lines
, struct text_glob
*start
, struct text_defn
*cols_1
)
4546 int c
=count_columns(cols_1
);
4548 count_hits(cols_1
, count_columns(cols_1
), indentation
.vertical_limit
);
4549 rewind_text_to(start
);
4550 count_right_hits(cols_1
, count_columns(cols_1
));
4551 rewind_text_to(start
);
4553 if ((cols_1
[i
].is_used
> 1) || (cols_1
[i
].right_hits
> 1)) {
4559 * first line (cols_1) is not aligned on any future column, we defer.
4567 * is_new_exact_right - returns TRUE if the, next_cols, has a word sitting
4568 * on the right hand margin of last_guess. But only
4569 * if no exact right word was found in last_cols.
4572 int html_printer::is_new_exact_right (struct text_defn
*last_guess
,
4573 struct text_defn
*last_cols
,
4574 struct text_defn
*next_cols
)
4576 int n
=count_columns(last_guess
)-1;
4579 if ((n
>=0) && (last_guess
[n
].right
!= 0) && (last_cols
[n
].right
!= 0) && (next_cols
[n
].right
!= 0)) {
4580 if ((last_cols
[n
].right
!= last_guess
[n
].right
) &&
4581 ((next_cols
[n
].right
== last_guess
[n
].right
) || (next_cols
[n
].right
== right_margin_indent
))) {
4589 * found_use_for_table - checks whether the some words on one line directly match
4590 * the horizontal alignment of the line below.
4591 * This is rather complex as we need to detect text tables
4592 * such as .2C .IP Abstracts and indentations
4596 * read first line of text and calculate the significant
4597 * gaps between words
4598 * next next line of text and do the same
4599 * if a conflict between these lines exists and
4600 * first line is centered
4602 * return centered line
4603 * elsif start of a table is found
4606 * read next line of text and calculate significant gaps
4607 * until conflict between the gaps is found
4609 * return table found
4611 * return no table found
4615 int html_printer::found_use_for_table (text_glob
*start
)
4618 struct text_defn all_words
[MAX_WORDS_PER_LINE
]; // logical OR of words on each line
4619 struct text_defn words_1
[MAX_WORDS_PER_LINE
]; // actual words found on first line
4620 struct text_defn words_2
[MAX_WORDS_PER_LINE
]; // actual words found on second line
4621 struct text_defn cols_1
[MAX_WORDS_PER_LINE
]; // columns found on line 1
4622 struct text_defn cols_2
[MAX_WORDS_PER_LINE
]; // columns found on line 2
4623 struct text_defn last_words
[MAX_WORDS_PER_LINE
]; // actual words found on last line
4624 struct text_defn last_cols
[MAX_WORDS_PER_LINE
]; // columns found so far
4625 struct text_defn next_words
[MAX_WORDS_PER_LINE
]; // actual words found on last line (new)
4626 struct text_defn next_cols
[MAX_WORDS_PER_LINE
]; // columns found on next line
4627 struct text_defn last_guess
[MAX_WORDS_PER_LINE
]; // columns found on last line
4628 // (logical AND of gaps (treat gaps = true))
4629 struct text_defn next_guess
[MAX_WORDS_PER_LINE
]; // columns found on next line
4630 // (logical AND of gaps (treat gaps = true))
4631 struct text_defn prev_guess
[MAX_WORDS_PER_LINE
]; // temporary copy of last_guess
4633 int lines
=1; // number of lines read
4634 int limit
; // vertical limit reached in our table
4635 int limit_1
; // vertical position after line 1
4638 if (strcmp(start
->text_string
, "<hr>") == 0) {
4644 * get first set of potential columns into last_line, call this last_guess
4646 limit
= collect_columns(words_1
, cols_1
, 0, 0, MAX_WORDS_PER_LINE
);
4648 copy_line(last_guess
, cols_1
);
4651 * initialize the all_words columns - if this should ever equal a complete line
4652 * with no gaps then we terminate the table.
4654 copy_line(all_words
, cols_1
);
4657 * and set the current limit found
4659 indentation
.vertical_limit
= limit
;
4662 * have we reached the end of page?
4664 if (page_contents
->words
.is_equal_to_head() || (limit
== 0)) {
4666 cols_2
[0].right
= 0;
4669 * the answer to the previous question was no.
4670 * So we need to examine the next line
4672 limit
= collect_columns(words_2
, cols_2
, words_1
, cols_1
, MAX_WORDS_PER_LINE
);
4679 * now check to see whether the first line looks like a single centered line
4681 if (single_centered_line(cols_1
, cols_2
, start
)) {
4682 rewind_text_to(start
);
4683 write_centered_line(start
);
4685 * indicate to caller than we have centered text, not found a table.
4687 indentation
.no_of_columns
= 0;
4689 } else if (! table_on
) {
4691 * user does not allow us to find a table (we are allowed to find centered lines (above))
4693 rewind_text_to(start
);
4698 * remove any gaps from all_words
4700 combine_line(all_words
, cols_2
);
4701 if (debug_table_on
) {
4702 display_columns(start
->text_string
, "[1] all_words" , all_words
);
4703 display_columns(start
->text_string
, "[1] cols_1" , cols_1
);
4704 display_columns(start
->text_string
, "[1] words_1" , words_1
);
4705 display_columns(start
->text_string
, "[1] cols_2" , cols_2
);
4706 display_columns(start
->text_string
, "[1] words_2" , words_2
);
4707 display_columns(start
->text_string
, "[1] last_guess", last_guess
);
4711 * next_guess = last_guess AND next_cols (where gap = true)
4714 if (remove_white_using_words(prev_guess
, last_guess
, cols_2
)) {
4716 if (remove_white_using_words(next_guess
, prev_guess
, all_words
)) {
4719 if (debug_table_on
) {
4720 display_columns(start
->text_string
, "[2] next_guess", next_guess
);
4723 copy_line(prev_guess
, cols_1
);
4724 combine_line(prev_guess
, cols_2
);
4727 * if no sequence of words crosses a column and
4728 * both the last column and all_words are not a full solid line of text
4730 if ((! conflict_with_words(next_guess
, all_words
)) &&
4731 (continue_searching_column(next_guess
, next_guess
, all_words
)) &&
4732 (is_appropriate_to_start_table(cols_1
, cols_2
, prev_guess
)) &&
4733 (! page_contents
->words
.is_equal_to_head()) &&
4734 ((end_region_vpos
< 0) || (limit
< end_region_vpos
)) &&
4738 * subtract any columns which are bridged by a sequence of words
4741 copy_line(next_cols
, cols_2
);
4742 copy_line(next_words
, words_2
);
4745 copy_line(prev_guess
, next_guess
); // copy next_guess away so we can compare it later
4746 combine_line(last_guess
, next_guess
);
4748 if (debug_table_on
) {
4749 t
= page_contents
->words
.get_data();
4750 display_columns(t
->text_string
, "[l] last_guess", last_guess
);
4752 indentation
.vertical_limit
= limit
;
4754 copy_line(last_cols
, next_cols
);
4755 copy_line(last_words
, next_words
);
4756 if (page_contents
->words
.is_equal_to_head()) {
4758 * terminate the search
4760 next_cols
[0].left
= 0;
4761 next_cols
[0].right
= 0;
4763 limit
= collect_columns(next_words
, next_cols
, last_words
, last_cols
, MAX_WORDS_PER_LINE
);
4767 combine_line(all_words
, next_cols
);
4768 if (debug_table_on
) {
4769 display_columns(t
->text_string
, "[l] all_words" , all_words
);
4770 display_columns(t
->text_string
, "[l] last_cols" , last_cols
);
4771 display_columns(t
->text_string
, "[l] next_words", next_words
);
4772 display_columns(t
->text_string
, "[l] next_cols" , next_cols
);
4777 * (if limit is < 0 then the table ends anyway.)
4778 * we check to see whether we should combine close columns.
4780 can_loose_column(start
, last_guess
, limit
);
4782 t
= page_contents
->words
.get_data();
4784 if (strcmp(t
->text_string
, "heT") == 0) {
4789 } while ((! remove_white_using_words(next_guess
, last_guess
, next_cols
)) &&
4790 (! conflict_with_words(next_guess
, all_words
)) &&
4791 (continue_searching_column(next_guess
, last_guess
, all_words
)) &&
4792 ((is_continueous_column(prev_guess
, last_cols
)) || (is_exact_left(last_guess
, next_cols
))) &&
4793 (! is_new_exact_right(last_guess
, last_cols
, next_cols
)) &&
4794 (! page_contents
->words
.is_equal_to_head()) &&
4795 (! check_lack_of_hits(next_guess
, last_guess
, start
, limit
)) &&
4796 ((end_region_vpos
<= 0) || (t
->minv
< end_region_vpos
)) &&
4802 indentation
.vertical_limit
= limit
;
4805 if (page_contents
->words
.is_equal_to_head()) {
4806 // end of page check whether we should include everything
4807 if ((! conflict_with_words(next_guess
, all_words
)) &&
4808 (continue_searching_column(next_guess
, last_guess
, all_words
)) &&
4809 ((is_continueous_column(prev_guess
, last_cols
)) || (is_exact_left(last_guess
, next_cols
)))) {
4810 // end of page reached - therefore include everything
4811 page_contents
->words
.start_from_tail();
4812 t
= page_contents
->words
.get_data();
4813 combine_line(last_guess
, next_guess
);
4814 indentation
.vertical_limit
= t
->minv
;
4817 t
= page_contents
->words
.get_data();
4818 if (((! conflict_with_words(last_guess
, all_words
))) &&
4819 (t
->minv
> end_region_vpos
) && (end_region_vpos
> 0)) {
4820 indentation
.vertical_limit
= limit
;
4822 if ((end_region_vpos
> 0) && (t
->minv
> end_region_vpos
)) {
4823 indentation
.vertical_limit
= min(indentation
.vertical_limit
, end_region_vpos
+1);
4824 } else if (indentation
.vertical_limit
< 0) {
4825 // -1 as we don't want to include section heading itself
4826 indentation
.vertical_limit
= -indentation
.vertical_limit
-1;
4830 if (debug_table_on
) {
4831 display_columns(start
->text_string
, "[1] all_words" , all_words
);
4832 display_columns(start
->text_string
, "[1] cols_1" , cols_1
);
4833 display_columns(start
->text_string
, "[1] words_1" , words_1
);
4834 display_columns(start
->text_string
, "[1] cols_2" , cols_2
);
4835 display_columns(start
->text_string
, "[1] words_2" , words_2
);
4836 display_columns(start
->text_string
, "[1] last_guess", last_guess
);
4837 display_columns(start
->text_string
, "[1] next_guess", next_guess
);
4839 rewind_text_to(start
);
4841 i
= count_columns(last_guess
);
4842 if ((i
>1) || (right_indentation(last_guess
))) {
4844 // was (continue_searching_column(last_guess, last_guess, all_words)))) {
4845 if (should_defer_table(lines
, start
, cols_1
)) {
4847 * yes, but let us check for a single line table
4850 copy_line(last_guess
, cols_1
);
4853 if (is_small_table(lines
, last_guess
, words_1
, cols_1
, words_2
, cols_2
,
4854 &indentation
.vertical_limit
, &limit_1
)) {
4856 // copy match into permenant html_table
4858 if (indentation
.columns
!= 0) {
4859 free(indentation
.columns
);
4861 if (debug_table_on
) {
4862 display_columns(start
->text_string
, "[x] last_guess", last_guess
);
4864 add_column_gaps(last_guess
);
4865 if (debug_table_on
) {
4866 display_columns(start
->text_string
, "[g] last_guess", last_guess
);
4870 * +1 for the potential header_margin
4874 indentation
.no_of_columns
= count_columns(last_guess
);
4875 indentation
.columns
= (struct text_defn
*)malloc((indentation
.no_of_columns
+2)*sizeof(struct text_defn
));
4878 while (i
<=indentation
.no_of_columns
) {
4879 indentation
.columns
[i
].left
= last_guess
[i
].left
;
4880 indentation
.columns
[i
].right
= last_guess
[i
].right
;
4884 if (indentation
.no_of_columns
>0) {
4885 assign_used_columns(start
);
4886 rewind_text_to(start
);
4887 calculate_percentage_width(start
);
4889 if (debug_table_on
) {
4890 display_columns(start
->text_string
, "[g] indentation.columns", indentation
.columns
);
4894 * clearly a single column 100% is not worth using a table.
4895 * Also we check to see whether the first line is sensibly
4896 * part of this table.
4898 if (is_a_full_width_column()) {
4899 indentation
.no_of_columns
= 0;
4900 free( indentation
.columns
);
4901 indentation
.columns
= 0;
4912 * define_cell - creates a table cell using the percentage width.
4915 void html_printer::define_cell (int i
)
4917 html
.put_string("<td valign=\"top\" align=\"left\" width=\"");
4918 html
.put_number(indentation
.columns
[i
].percent
);
4919 html
.put_string("%\">\n");
4923 * column_display_word - given a left, right pair and the indentation.vertical_limit
4924 * write out html text within this region.
4927 void html_printer::column_display_word (int cell
, int vert
, int left
, int right
, int next
)
4929 text_glob
*g
=page_contents
->words
.get_data();
4931 supress_sub_sup
= TRUE
;
4934 begin_paragraph_no_height(left_alignment
);
4935 while ((g
!= 0) && (g
->minv
<= vert
)) {
4936 if ((left
<= g
->minh
) && (g
->minh
<right
)) {
4937 char *postword
=html_position_text(g
, left
, right
);
4939 if (header
.written_header
) {
4940 fatal("should never generate a header inside a table");
4942 if (g
->is_raw_command
) {
4943 html
.put_string((char *)g
->text_string
);
4945 translate_to_html(g
);
4947 if (postword
!= 0) {
4948 html
.put_string(postword
);
4950 issued_newline
= FALSE
;
4953 if (page_contents
->words
.is_equal_to_tail()) {
4956 page_contents
->words
.move_right();
4957 g
=page_contents
->words
.get_data();
4961 html
.put_string("</td>\n");
4963 page_contents
->words
.move_left();
4964 // and correct output_vpos
4965 g
=page_contents
->words
.get_data();
4966 output_vpos
= g
->minv
;
4972 * total_percentages - returns the total of all the percentages in the table.
4975 int html_printer::total_percentages ()
4980 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
4981 sum
+= indentation
.columns
[i
].percent
;
4987 * start_table - creates a table according with parameters contained within class html_table.
4990 void html_printer::start_table (void)
4993 html
.put_string("\n<table width=\"");
4994 html
.put_number(total_percentages());
4995 html
.put_string("%\" rules=\"none\" frame=\"none\" cols=\"");
4996 html
.put_number(indentation
.no_of_columns
);
4997 html
.put_string("\" cellspacing=\"0\" cellpadding=\"0\">\n");
5001 * end_table - finishes off a table.
5004 void html_printer::end_table (void)
5006 html
.put_string("</table>\n");
5007 indentation
.no_of_columns
= 0;
5008 restore_paragraph();
5009 supress_sub_sup
= TRUE
;
5014 * is_in_table - returns TRUE if we are inside an html table.
5017 int html_printer::is_in_table (void)
5019 return( indentation
.no_of_columns
!= 0 );
5024 * column_calculate_right_margin - scan through the column and find the right most margin
5027 int html_printer::column_calculate_right_margin (int left
, int right
)
5029 if (left
== right
) {
5034 text_glob
*start
= page_contents
->words
.get_data();
5035 text_glob
*g
= start
;
5037 while ((g
!= 0) && (g
->minv
<= indentation
.vertical_limit
)) {
5038 if ((left
<= g
->minh
) && (g
->minh
<right
)) {
5040 fprintf(stderr
, "right word = %s %d\n", g
->text_string
, g
->maxh
); fflush(stderr
);
5042 if (g
->maxh
== rightmost
) {
5044 } else if (g
->maxh
> rightmost
) {
5046 rightmost
= g
->maxh
;
5048 if (g
->maxh
> right
) {
5050 fprintf(stderr
, "problem as right word = %s %d [%d..%d]\n",
5051 g
->text_string
, right
, g
->minh
, g
->maxh
); fflush(stderr
);
5056 page_contents
->words
.move_right();
5057 if (page_contents
->words
.is_equal_to_head()) {
5059 page_contents
->words
.start_from_tail();
5061 g
=page_contents
->words
.get_data();
5064 rewind_text_to(start
);
5065 if (rightmost
== -1) {
5066 return( right
); // no words in this column
5068 return( rightmost
);
5074 * column_calculate_left_margin - scan through the column and find the left most margin
5077 int html_printer::column_calculate_left_margin (int left
, int right
)
5079 if (left
== right
) {
5083 text_glob
*start
= page_contents
->words
.get_data();
5084 text_glob
*g
= start
;
5086 while ((g
!= 0) && (g
->minv
<= indentation
.vertical_limit
)) {
5087 if ((left
<= g
->minh
) && (g
->minh
<right
)) {
5088 leftmost
= min(g
->minh
, leftmost
);
5090 page_contents
->words
.move_right();
5091 if (page_contents
->words
.is_equal_to_head()) {
5093 page_contents
->words
.start_from_tail();
5095 g
=page_contents
->words
.get_data();
5098 rewind_text_to(start
);
5099 if (leftmost
== right
) {
5100 return( left
); // no words in this column
5108 * find_column_index - returns the index to the column in which glob, t, exists.
5111 int html_printer::find_column_index_in_line (text_glob
*t
, text_defn
*line
)
5115 while ((line
!= 0) && ((line
[i
].right
!= 0) || (line
[i
].right
!= 0)) &&
5116 (! ((line
[i
].left
<=t
->minh
) && (line
[i
].right
>t
->minh
)))) {
5123 * find_column_index - returns the index to the column in which glob, t, exists.
5126 int html_printer::find_column_index (text_glob
*t
)
5130 while ((i
<indentation
.no_of_columns
) &&
5131 (! ((indentation
.columns
[i
].left
<=t
->minh
) &&
5132 (indentation
.columns
[i
].right
>t
->minh
)))) {
5139 * determine_row_limit - checks each row to see if there is a gap in a cell.
5140 * We return the vertical position after the empty cell
5141 * at the start of the next line.
5144 int html_printer::determine_row_limit (text_glob
*start
, int v
)
5148 int vpos
, last
, prev
;
5149 text_glob
*is_gap
[MAX_WORDS_PER_LINE
];
5150 text_glob
zero(&start
->text_style
, 0, 0, 0, 0, 0, 0, 0, 0);
5153 if ((v
== -1) && (strcmp(start
->text_string
, "CASE") == 0)) {
5158 if (v
>= indentation
.vertical_limit
) {
5162 * initially we start with all gaps in our table
5163 * after a gap we start a new row
5164 * here we set the gap array to the previous line
5168 t
= page_contents
->words
.get_data();
5171 page_contents
->words
.move_right();
5172 t
= page_contents
->words
.get_data();
5173 } while ((! page_contents
->words
.is_equal_to_head()) &&
5177 if (page_contents
->words
.is_equal_to_head()) {
5180 page_contents
->words
.move_left();
5181 t
= page_contents
->words
.get_data();
5185 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
5189 if (page_contents
->words
.is_equal_to_tail()) {
5190 rewind_text_to(start
);
5191 return( indentation
.vertical_limit
);
5193 page_contents
->words
.move_right();
5195 t
= page_contents
->words
.get_data();
5198 // now check each row for a gap
5202 if (vpos
> indentation
.vertical_limit
) {
5203 // we have reached the end of the table, quit
5204 rewind_text_to(start
);
5205 return( indentation
.vertical_limit
);
5208 i
= find_column_index(t
);
5209 if (i
>=indentation
.no_of_columns
) {
5210 error("find_column_index has failed");
5213 if (! is_on_same_line(t
, last
)) {
5217 if ((! is_on_same_line(is_gap
[i
], vpos
)) && (! is_on_same_line(is_gap
[i
], prev
)) &&
5218 (indentation
.columns
[i
].is_used
)) {
5219 // no word on previous line - must be a gap - force alignment of row
5220 rewind_text_to(start
);
5225 page_contents
->words
.move_right();
5226 t
= page_contents
->words
.get_data();
5227 } while ((! page_contents
->words
.is_equal_to_head()) &&
5228 (vpos
< indentation
.vertical_limit
) && (vpos
>= last
));
5229 page_contents
->words
.move_left();
5230 t
= page_contents
->words
.get_data();
5231 rewind_text_to(start
);
5232 return( indentation
.vertical_limit
);
5237 * assign_used_columns - sets the is_used field of the column array of records.
5240 void html_printer::assign_used_columns (text_glob
*start
)
5242 text_glob
*t
= start
;
5245 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
5246 indentation
.columns
[i
].is_used
= FALSE
;
5249 rewind_text_to(start
);
5250 if (! page_contents
->words
.is_empty()) {
5252 i
= find_column_index(t
);
5253 if (indentation
.columns
[i
].right
!= 0) {
5254 if (debug_table_on
) {
5255 fprintf(stderr
, "[%s] in column %d at %d..%d limit %d\n", t
->text_string
,
5256 i
, t
->minv
, t
->maxv
, indentation
.vertical_limit
); fflush(stderr
);
5258 indentation
.columns
[i
].is_used
= TRUE
;
5260 page_contents
->words
.move_right();
5261 t
= page_contents
->words
.get_data();
5262 } while ((t
->minv
<indentation
.vertical_limit
) &&
5263 (! page_contents
->words
.is_equal_to_head()));
5265 if (debug_table_on
) {
5266 for (i
=0; i
<indentation
.no_of_columns
; i
++) {
5267 fprintf(stderr
, " <left=%d right=%d is_used=%d> ",
5268 indentation
.columns
[i
].left
,
5269 indentation
.columns
[i
].right
,
5270 indentation
.columns
[i
].is_used
);
5272 fprintf(stderr
, "\n");
5278 * adjust_margin_percentages - so far we have ignored the header_indent
5279 * and just considered left_margin_indent..right_margin_indent.
5280 * (We do this since we can assume 100% is total width for main text).
5281 * However as header_indent can be < left_margin_indent we need to
5282 * recalculate the real percentages in the light of the extended width.
5285 void html_printer::adjust_margin_percentages (void)
5287 if ((header_indent
< left_margin_indent
) && (header_indent
!= -1)) {
5289 * recalculation necessary
5293 while (i
<indentation
.no_of_columns
) {
5294 indentation
.columns
[i
].percent
= (indentation
.columns
[i
].percent
*
5295 (right_margin_indent
- left_margin_indent
)) /
5296 (right_margin_indent
- header_indent
);
5299 // remove_zero_percentage_column();
5304 * foreach_column_include_text - foreach column in a table place the
5305 * appropriate html text.
5308 void html_printer::foreach_column_include_text (text_glob
*start
)
5310 if (indentation
.no_of_columns
>0) {
5316 rewind_text_to(start
);
5317 count_right_hits(indentation
.columns
, indentation
.no_of_columns
);
5318 rewind_text_to(start
);
5321 limit
= determine_row_limit(start
, limit
); // find the bottom of the next row
5322 html
.put_string("<tr valign=\"top\" align=\"left\">\n");
5324 start
= page_contents
->words
.get_data();
5325 while (i
<indentation
.no_of_columns
) {
5326 // reset the output position to the start of column
5327 rewind_text_to(start
);
5328 output_vpos
= start
->minv
;
5329 output_hpos
= indentation
.columns
[i
].left
;
5330 // and display each column until limit
5331 right
= column_calculate_right_margin(indentation
.columns
[i
].left
,
5332 indentation
.columns
[i
].right
);
5333 left
= column_calculate_left_margin(indentation
.columns
[i
].left
,
5334 indentation
.columns
[i
].right
);
5336 if (right
>indentation
.columns
[i
].right
) {
5338 fprintf(stderr
, "assert calculated right column edge is greater than column\n"); fflush(stderr
);
5343 if (left
<indentation
.columns
[i
].left
) {
5345 fprintf(stderr
, "assert calculated left column edge is less than column\n"); fflush(stderr
);
5350 if ((indentation
.columns
[i
].right_hits
== 1) &&
5351 (indentation
.columns
[i
].right
!= right_margin_indent
)) {
5352 indentation
.wrap_margin
= FALSE
;
5354 fprintf(stderr
, "turning auto wrap off during column %d for start word %s\n",
5355 i
, start
->text_string
);
5360 indentation
.wrap_margin
= TRUE
;
5363 column_display_word(i
, limit
, left
, right
, indentation
.columns
[i
].right
);
5367 if (page_contents
->words
.is_equal_to_tail()) {
5370 page_contents
->words
.sub_move_right();
5371 if (page_contents
->words
.is_empty()) {
5374 start
= page_contents
->words
.get_data();
5378 html
.put_string("</tr>\n");
5379 } while (((limit
< indentation
.vertical_limit
) && (start
!= 0) &&
5380 (! page_contents
->words
.is_empty())) || (limit
== -1));
5384 // finished page remove all words
5385 page_contents
->words
.start_from_head();
5386 while (! page_contents
->words
.is_empty()) {
5387 page_contents
->words
.sub_move_right();
5389 } else if (! page_contents
->words
.is_empty()) {
5390 page_contents
->words
.move_left();
5396 * write_centered_line - generates a line of centered text.
5399 void html_printer::write_centered_line (text_glob
*g
)
5401 int current_vpos
=g
->minv
;
5403 move_vertical(g
, center_alignment
);
5405 header
.written_header
= FALSE
;
5406 supress_sub_sup
= TRUE
;
5407 output_vpos
= g
->minv
;
5408 output_hpos
= g
->minh
;
5410 char *postword
=html_position_text(g
, left_margin_indent
, right_margin_indent
);
5412 if (! header
.written_header
) {
5413 if (g
->is_raw_command
) {
5414 html
.put_string((char *)g
->text_string
);
5416 translate_to_html(g
);
5418 if (postword
!= 0) {
5419 html
.put_string(postword
);
5421 need_one_newline
= TRUE
;
5422 issued_newline
= FALSE
;
5424 page_contents
->words
.move_right();
5425 g
= page_contents
->words
.get_data();
5426 } while ((! page_contents
->words
.is_equal_to_head()) && (is_on_same_line(g
, current_vpos
)));
5427 page_contents
->words
.move_left(); // so when we move right we land on the word following this centered line
5428 need_one_newline
= TRUE
;
5432 * is_in_middle - returns TRUE if the text defn, t, is in the middle of the page.
5435 int html_printer::is_in_middle (int left
, int right
)
5437 return( abs(abs(left
-left_margin_indent
) - abs(right_margin_indent
-right
)) <= CENTER_TOLERANCE
);
5441 * single_centered_line - returns TRUE if first is a centered line with a different
5445 int html_printer::single_centered_line (text_defn
*first
, text_defn
*second
, text_glob
*g
)
5448 ((count_columns(first
) == 1) && (first
[0].left
!= left_margin_indent
) &&
5449 (first
[0].left
!= second
[0].left
) && is_in_middle(first
->left
, first
->right
))
5454 * check_able_to_use_center - returns TRUE if we can see a centered line.
5457 int html_printer::check_able_to_use_center (text_glob
*g
)
5459 if (auto_on
&& table_on
&& ((! is_on_same_line(g
, output_vpos
)) || issued_newline
) && (! using_table_for_indent())) {
5460 // we are allowed to check for centered line
5461 // first check to see whether we might be looking at a set of columns
5462 struct text_defn last_guess
[MAX_WORDS_PER_LINE
];
5463 struct text_defn last_words
[MAX_WORDS_PER_LINE
];
5465 collect_columns(last_words
, last_guess
, 0, 0, MAX_WORDS_PER_LINE
);
5468 if ((count_columns(last_guess
) == 1) && (is_in_middle(last_guess
[0].left
, last_guess
[0].right
))) {
5469 write_centered_line(g
);
5477 * check_able_to_use_table - examines forthcoming text to see whether we can
5478 * better format it by using an html transparent table.
5481 int html_printer::check_able_to_use_table (text_glob
*g
)
5483 if (auto_on
&& ((! is_on_same_line(g
, output_vpos
)) || issued_newline
) && (! using_table_for_indent())) {
5484 // we are allowed to check for table
5486 if ((output_hpos
!= right_margin_indent
) && (found_use_for_table(g
))) {
5487 foreach_column_include_text(g
);
5495 * move_vertical - if we are using html auto formatting then decide whether to
5496 * break the line via a <br> or a </p><p> sequence.
5499 void html_printer::move_vertical (text_glob
*g
, paragraph_type p
)
5502 int height
= (g
->text_style
.point_size
+2)*r
/72; // --fixme-- we always assume VS is PS+2 (could do better)
5506 if ((more_than_line_break(output_vpos
, g
->minv
, height
)) || (p
!= current_paragraph
->para_type
)) {
5513 if (output_vpos
== -1) {
5514 temp_vpos
= g
->minv
;
5516 temp_vpos
= output_vpos
;
5519 force_begin_paragraph();
5520 if (need_one_newline
) {
5522 temp_vpos
+= height
;
5524 need_one_newline
= TRUE
;
5527 while ((temp_vpos
< g
->minv
) && (more_than_line_break(temp_vpos
, g
->minv
, height
))) {
5529 temp_vpos
+= height
;
5535 * emit_space - emits a space within html, it checks for the font type and
5536 * will change font depending upon, g. Courier spaces are larger
5537 * than roman so we need consistancy when changing between them.
5540 void html_printer::emit_space (text_glob
*g
, int force_space
)
5542 if (! current_paragraph
->need_paragraph
) {
5543 // only generate a space if we have written a word - as html will ignore it otherwise
5544 if ((output_style
!= g
->text_style
) && (g
->text_style
.f
!= 0)) {
5545 terminate_current_font();
5547 if (force_space
|| (g
->minh
> output_hpos
)) {
5548 html
.put_string(" ");
5550 change_font(g
, TRUE
);
5555 * html_position_text - determine whether the text is subscript/superscript/normal
5559 char *html_printer::html_position_text (text_glob
*g
, int left_margin
, int right_margin
)
5563 begin_paragraph(left_alignment
);
5565 if ((! header
.written_header
) &&
5566 (is_on_same_line(g
, output_vpos
) ||
5567 pretend_is_on_same_line(g
, left_margin
, right_margin
))) {
5570 * check whether we should supress superscripts and subscripts.
5571 * I guess we might be able to do better by examining text on this line
5575 if ((! is_on_same_line(g
, output_vpos
)) && (pretend_is_on_same_line(g
, left_margin
, right_margin
))) {
5576 supress_sub_sup
= TRUE
;
5578 header
.written_header
= FALSE
;
5579 force_begin_paragraph();
5581 // check whether we need to insert white space between words on 'same' line
5582 if (pretend_is_on_same_line(g
, left_margin
, right_margin
)) {
5583 emit_space(g
, TRUE
);
5586 // check whether the font was reset after generating an image
5587 if (output_style
.f
== 0) {
5588 change_font(g
, TRUE
);
5591 if (looks_like_subscript(g
)) {
5593 g
->text_style
.point_size
= output_style
.point_size
;
5594 g
->minv
= output_vpos
; // this ensures that output_vpos doesn't alter
5595 // which allows multiple subscripted words
5596 move_horizontal(g
, left_margin
);
5597 html
.put_string("<sub>");
5598 postword
= "</sub>";
5599 } else if (looks_like_superscript(g
)) {
5601 g
->text_style
.point_size
= output_style
.point_size
;
5602 g
->minv
= output_vpos
;
5604 move_horizontal(g
, left_margin
);
5605 html
.put_string("<sup>");
5606 postword
= "</sup>";
5608 move_horizontal(g
, left_margin
);
5610 supress_sub_sup
= FALSE
;
5612 // we have found a new line
5613 if (! header
.written_header
) {
5614 move_vertical(g
, left_alignment
);
5616 header
.written_header
= FALSE
;
5618 if (processed_header(g
)) {
5619 // we must not alter output_vpos as we have peeped at the next word
5620 // and set vpos to this - to ensure we do not generate a <br> after
5621 // a heading. (The html heading automatically generates a line break)
5622 output_hpos
= left_margin
;
5625 force_begin_paragraph();
5626 if ((! is_in_table()) && (margin_on
)) {
5627 make_html_indent(left_margin
);
5629 if (g
->minh
-left_margin
!= 0) {
5630 make_html_indent(g
->minh
-left_margin
);
5632 change_font(g
, TRUE
);
5633 supress_sub_sup
= FALSE
;
5636 output_vpos
= g
->minv
;
5637 output_hpos
= g
->maxh
;
5642 int html_printer::html_position_region (void)
5645 int height
= output_style
.point_size
*r
/72;
5647 int is_center
= FALSE
;
5649 if (output_style
.point_size
!= 0) {
5650 if (output_vpos
!= start_region_vpos
) {
5652 // graphic starts on a different line
5653 if (output_vpos
== -1) {
5654 temp_vpos
= start_region_vpos
;
5656 temp_vpos
= output_vpos
;
5658 supress_sub_sup
= TRUE
;
5659 if (need_one_newline
) {
5661 temp_vpos
+= height
;
5663 need_one_newline
= TRUE
;
5666 while ((temp_vpos
< start_region_vpos
) &&
5667 (more_than_line_break(temp_vpos
, start_region_vpos
, height
))) {
5669 temp_vpos
+= height
;
5673 if (auto_on
&& (is_in_middle(start_region_hpos
, end_region_hpos
))) {
5676 if (start_region_hpos
> get_left()) {
5677 make_html_indent(start_region_hpos
-get_left());
5680 output_vpos
= start_region_vpos
;
5681 output_hpos
= start_region_hpos
;
5682 return( is_center
);
5686 * gs_x - translate and scale the x axis
5689 int html_printer::gs_x (int x
)
5691 x
+= IMAGE_BOARDER_PIXELS
/2;
5692 return((x
-start_region_hpos
)*postscript_res
/font::res
);
5697 * gs_y - translate and scale the y axis
5700 int html_printer::gs_y (int y
)
5702 int yoffset
=((int)(A4_PAGE_LENGTH
*(double)font::res
))-end_region_vpos
;
5704 y
+= IMAGE_BOARDER_PIXELS
/2;
5705 return( (y
+yoffset
)*postscript_res
/font::res
);
5709 void html_printer::troff_position_text (text_glob
*g
)
5711 change_font(g
, FALSE
);
5713 troff
.put_string("V");
5714 troff
.put_number(gs_y(g
->maxv
));
5715 troff
.put_string("\n");
5717 troff
.put_string("H");
5718 troff
.put_number(gs_x(g
->minh
));
5719 troff
.put_string("\n");
5722 void html_printer::troff_change_font (const char *fontname
, int size
, int font_no
)
5724 troff
.put_string("x font ");
5725 troff
.put_number(font_no
);
5726 troff
.put_string(" ");
5727 troff
.put_string(fontname
);
5728 troff
.put_string("\nf");
5729 troff
.put_number(font_no
);
5730 troff
.put_string("\ns");
5731 troff
.put_number(size
*1000);
5732 troff
.put_string("\n");
5736 void html_printer::set_style(const style
&sty
)
5739 const char *fontname
= sty
.f
->get_name();
5741 fatal("no internalname specified for font");
5743 change_font(fontname
, (font::res
/(72*font::sizescale
))*sty
.point_size
);
5747 void html_printer::end_of_line()
5753 void html_printer::html_display_word (text_glob
*g
)
5756 if (strcmp(g
->text_string
, "ot") == 0) {
5760 if (! check_able_to_use_table(g
)) {
5761 char *postword
=html_position_text(g
, left_margin_indent
, right_margin_indent
);
5763 if (! header
.written_header
) {
5764 if (g
->is_raw_command
) {
5765 html
.put_string((char *)g
->text_string
);
5767 translate_to_html(g
);
5769 if (postword
!= 0) {
5770 html
.put_string(postword
);
5772 need_one_newline
= TRUE
;
5773 issued_newline
= FALSE
;
5778 void html_printer::troff_display_word (text_glob
*g
)
5780 troff_position_text(g
);
5781 if (g
->is_raw_command
) {
5782 int l
=strlen((char *)g
->text_string
);
5784 troff
.put_string("c");
5785 troff
.put_string((char *)g
->text_string
);
5786 troff
.put_string("\n");
5788 troff
.put_string("C");
5789 troff
.put_troffps_char((char *)g
->text_string
);
5790 troff
.put_string("\n");
5793 troff_position_text(g
);
5794 troff
.put_string("t");
5795 troff
.put_translated_string((const char *)g
->text_string
);
5796 troff
.put_string("\n");
5800 void html_printer::display_word (text_glob
*g
, int is_to_html
)
5803 html_display_word(g
);
5804 } else if ((g
->is_raw_command
) && (g
->is_html_command
)) {
5805 // found a raw html command inside a graphic glob.
5806 // We should emit the command to the html device, but of course we
5807 // cannot place it correctly as we are dealing with troff words.
5808 // Remember output_vpos will refer to troff and not html.
5809 html
.put_string((char *)g
->text_string
);
5811 troff_display_word(g
);
5816 * translate_to_html - translates a textual string into html text
5819 void html_printer::translate_to_html (text_glob
*g
)
5821 char buf
[MAX_STRING_LENGTH
];
5823 str_translate_to_html(g
->text_style
.f
, buf
, MAX_STRING_LENGTH
,
5824 g
->text_string
, g
->text_length
, TRUE
);
5825 html
.put_string(buf
);
5829 * html_knows_about - given a character name, troff, return TRUE
5830 * if we know how to display this character using
5834 int html_printer::html_knows_about (char *troff
)
5836 // --fixme-- needs to have similar code as above
5841 * display_fill - generates a troff format fill command
5844 void html_printer::display_fill (graphic_glob
*g
)
5846 troff
.put_string("Df ") ;
5847 troff
.put_number(g
->fill
);
5848 troff
.put_string(" 0\n");
5852 * display_line - displays a line using troff format
5855 void html_printer::display_line (graphic_glob
*g
, int is_to_html
)
5858 fatal("cannot emit lines in html");
5860 if (g
->code
== 'l') {
5863 troff
.put_string("V");
5864 troff
.put_number(gs_y(g
->point
[0].y
));
5865 troff
.put_string("\n");
5867 troff
.put_string("H");
5868 troff
.put_number(gs_x(g
->point
[0].x
));
5869 troff
.put_string("\n");
5873 troff
.put_string("Dl ");
5874 troff
.put_number((g
->point
[1].x
-g
->point
[0].x
)*postscript_res
/font::res
);
5875 troff
.put_string(" ");
5876 troff
.put_number((g
->point
[1].y
-g
->point
[0].y
)*postscript_res
/font::res
);
5877 troff
.put_string("\n");
5878 // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y,
5879 // g->point[1].x, g->point[1].y, g->size);
5880 } else if ((g
->code
== 'c') || (g
->code
== 'C')) {
5883 int xradius
= (g
->maxh
- g
->minh
) / 2;
5884 int yradius
= (g
->maxv
- g
->minv
) / 2;
5885 // center of circle or elipse
5887 troff
.put_string("V");
5888 troff
.put_number(gs_y(g
->minv
+yradius
));
5889 troff
.put_string("\n");
5891 troff
.put_string("H");
5892 troff
.put_number(gs_x(g
->minh
));
5893 troff
.put_string("\n");
5897 if (g
->code
== 'c') {
5898 troff
.put_string("Dc ");
5900 troff
.put_string("DC ");
5903 troff
.put_number(xradius
*2*postscript_res
/font::res
);
5904 troff
.put_string("\n");
5906 } else if ((g
->code
== 'e') || (g
->code
== 'E')) {
5909 int xradius
= (g
->maxh
- g
->minh
) / 2;
5910 int yradius
= (g
->maxv
- g
->minv
) / 2;
5911 // center of elipse - this is untested
5913 troff
.put_string("V");
5914 troff
.put_number(gs_y(g
->minv
+yradius
));
5915 troff
.put_string("\n");
5917 troff
.put_string("H");
5918 troff
.put_number(gs_x(g
->minh
));
5919 troff
.put_string("\n");
5923 if (g
->code
== 'e') {
5924 troff
.put_string("De ");
5926 troff
.put_string("DE ");
5929 troff
.put_number(xradius
*2*postscript_res
/font::res
);
5930 troff
.put_string(" ");
5931 troff
.put_number(yradius
*2*postscript_res
/font::res
);
5932 troff
.put_string("\n");
5933 } else if ((g
->code
== 'p') || (g
->code
== 'P')) {
5935 troff
.put_string("V");
5936 troff
.put_number(gs_y(g
->yc
));
5937 troff
.put_string("\n");
5939 troff
.put_string("H");
5940 troff
.put_number(gs_x(g
->xc
));
5941 troff
.put_string("\n");
5945 if (g
->code
== 'p') {
5946 troff
.put_string("Dp");
5948 troff
.put_string("DP");
5954 for (i
=0; i
<g
->nopoints
; i
++) {
5955 troff
.put_string(" ");
5956 troff
.put_number((g
->point
[i
].x
-xc
)*postscript_res
/font::res
);
5957 troff
.put_string(" ");
5958 troff
.put_number((g
->point
[i
].y
-yc
)*postscript_res
/font::res
);
5962 troff
.put_string("\n");
5963 } else if (g
->code
== 'a') {
5965 troff
.put_string("V");
5966 troff
.put_number(gs_y(g
->yc
));
5967 troff
.put_string("\n");
5969 troff
.put_string("H");
5970 troff
.put_number(gs_x(g
->xc
));
5971 troff
.put_string("\n");
5975 troff
.put_string("Da");
5979 for (i
=0; i
<g
->nopoints
; i
++) {
5980 troff
.put_string(" ");
5981 troff
.put_number(g
->point
[i
].x
*postscript_res
/font::res
);
5982 troff
.put_string(" ");
5983 troff
.put_number(g
->point
[i
].y
*postscript_res
/font::res
);
5985 troff
.put_string("\n");
5986 } else if (g
->code
== '~') {
5988 troff
.put_string("V");
5989 troff
.put_number(gs_y(g
->yc
));
5990 troff
.put_string("\n");
5992 troff
.put_string("H");
5993 troff
.put_number(gs_x(g
->xc
));
5994 troff
.put_string("\n");
5998 troff
.put_string("D~");
6003 for (i
=0; i
<g
->nopoints
; i
++) {
6004 troff
.put_string(" ");
6005 troff
.put_number((g
->point
[i
].x
-xc
)*postscript_res
/font::res
);
6006 troff
.put_string(" ");
6007 troff
.put_number((g
->point
[i
].y
-yc
)*postscript_res
/font::res
);
6011 troff
.put_string("\n");
6017 * flush_sbuf - flushes the current sbuf into the list of glyphs.
6020 void html_printer::flush_sbuf()
6023 int r
=font::res
; // resolution of the device
6024 set_style(sbuf_style
);
6026 page_contents
->add(&sbuf_style
, sbuf
, sbuf_len
,
6027 sbuf_vpos
-sbuf_style
.point_size
*r
/72, sbuf_start_hpos
,
6028 sbuf_vpos
, sbuf_end_hpos
);
6030 output_hpos
= sbuf_end_hpos
;
6031 output_vpos
= sbuf_vpos
;
6033 sbuf_dmark_hpos
= -1;
6038 void html_printer::set_line_thickness(const environment
*env
)
6040 line_thickness
= env
->size
;
6041 printf("line thickness = %d\n", line_thickness
);
6044 void html_printer::draw(int code
, int *p
, int np
, const environment
*env
)
6050 page_contents
->add_line(code
,
6051 env
->hpos
, env
->vpos
, env
->hpos
+p
[0], env
->vpos
+p
[1],
6054 error("2 arguments required for line");
6060 line_thickness
= -1;
6062 // troff gratuitously adds an extra 0
6063 if (np
!= 1 && np
!= 2) {
6064 error("0 or 1 argument required for thickness");
6067 line_thickness
= p
[0];
6077 error("even number of arguments required for polygon");
6081 error("no arguments for polygon");
6084 // firstly lets add our current position to polygon
6096 // now store polygon in page
6097 page_contents
->add_polygon(code
, np
, p
, env
->hpos
, env
->vpos
, env
->size
, fill
);
6104 error("2 arguments required for ellipse");
6107 page_contents
->add_line(code
,
6108 env
->hpos
, env
->vpos
-p
[1]/2, env
->hpos
+p
[0], env
->vpos
+p
[1]/2,
6117 // troff adds an extra argument to C
6118 if (np
!= 1 && !(code
== 'C' && np
== 2)) {
6119 error("1 argument required for circle");
6122 page_contents
->add_line(code
,
6123 env
->hpos
, env
->vpos
-p
[0]/2, env
->hpos
+p
[0], env
->vpos
+p
[0]/2,
6132 if (adjust_arc_center(p
, c
)) {
6133 page_contents
->add_arc('a', env
->hpos
, env
->vpos
, p
, c
, env
->size
, fill
);
6136 page_contents
->add_line('l', env
->hpos
, env
->vpos
, p
[0]+p
[2], p
[1]+p
[3], env
->size
, fill
);
6139 error("4 arguments required for arc");
6146 error("even number of arguments required for spline");
6150 error("no arguments for spline");
6153 // firstly lets add our current position to spline
6165 page_contents
->add_spline('~', env
->hpos
, env
->vpos
, np
, p
, env
->size
, fill
);
6170 if (np
!= 1 && np
!= 2) {
6171 error("1 argument required for fill");
6175 if (fill
< 0 || fill
> FILL_MAX
) {
6176 // This means fill with the current color.
6177 fill
= FILL_MAX
+ 1;
6183 error("unrecognised drawing command `%1'", char(code
));
6189 void html_printer::begin_page(int n
)
6192 html
.begin_comment("Page: ").comment_arg(i_to_a(page_number
)).end_comment();;
6193 no_of_printed_pages
++;
6196 output_space_code
= 32;
6197 output_draw_point_size
= -1;
6198 output_line_thickness
= -1;
6203 void testing (text_glob
*g
) {}
6205 void html_printer::flush_graphic (void)
6210 page_contents
->is_in_graphic
= FALSE
;
6214 calculate_region_range(&g
);
6216 page_contents
->make_new_region(&g
);
6218 move_region_to_page();
6221 void html_printer::end_page(int)
6228 font
*html_printer::make_font(const char *nm
)
6230 return html_font::load_html_font(nm
);
6233 html_printer::~html_printer()
6235 if (fseek(tempfp
, 0L, 0) < 0)
6236 fatal("fseek on temporary file failed");
6237 html
.set_file(stdout
);
6238 fputs("<html>\n", stdout
);
6239 fputs("<head>\n", stdout
);
6240 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout
);
6242 fputs("</head>\n", stdout
);
6243 fputs("<body>\n", stdout
);
6245 header
.write_headings(stdout
);
6247 extern const char *Version_string
;
6248 html
.begin_comment("Creator : ")
6249 .comment_arg("groff ")
6250 .comment_arg("version ")
6251 .comment_arg(Version_string
)
6255 #ifdef LONG_FOR_TIME_T
6261 html
.begin_comment("CreationDate: ")
6262 .comment_arg(ctime(&t
))
6265 html
.begin_comment("Total number of pages: ").comment_arg(i_to_a(no_of_printed_pages
)).end_comment();
6267 html
.copy_file(tempfp
);
6268 fputs("</body>\n", stdout
);
6269 fputs("</html>\n", stdout
);
6275 * calculate_region_range - calculates the vertical range for words and lines
6276 * within the region lists.
6279 void html_printer::calculate_region_range (graphic_glob
*r
)
6284 if (! page_contents
->region_lines
.is_empty()) {
6285 page_contents
->region_lines
.start_from_head();
6287 g
= page_contents
->region_lines
.get_data();
6288 if ((r
->minv
== -1) || (g
->minv
< r
->minv
)) {
6291 if ((r
->maxv
== -1) || (g
->maxv
> r
->maxv
)) {
6294 page_contents
->region_lines
.move_right();
6295 } while (! page_contents
->region_lines
.is_equal_to_head());
6297 if (! page_contents
->region_words
.is_empty()) {
6298 page_contents
->region_words
.start_from_head();
6300 w
= page_contents
->region_words
.get_data();
6302 if ((r
->minv
== -1) || (w
->minv
< r
->minv
)) {
6305 if ((r
->maxv
== -1) || (w
->maxv
> r
->maxv
)) {
6308 page_contents
->region_words
.move_right();
6309 } while (! page_contents
->region_words
.is_equal_to_head());
6315 * move_region_to_page - moves lines and words held in the temporary region
6316 * list to the page list.
6319 void html_printer::move_region_to_page (void)
6324 page_contents
->region_lines
.start_from_head();
6325 while (! page_contents
->region_lines
.is_empty()) {
6326 g
= page_contents
->region_lines
.get_data(); // remove from our temporary region list
6327 page_contents
->lines
.add(g
); // and add to the page list
6328 page_contents
->region_lines
.sub_move_right();
6330 page_contents
->region_words
.start_from_head();
6331 while (! page_contents
->region_words
.is_empty()) {
6332 w
= page_contents
->region_words
.get_data(); // remove from our temporary region list
6333 page_contents
->words
.add(w
); // and add to the page list
6334 page_contents
->region_words
.sub_move_right();
6339 * is_graphic_start - returns TRUE if the start of table, pic, eqn was seen.
6342 int is_graphic_start (char *s
)
6344 return( (strcmp(s
, "graphic-start") == 0) ||
6345 ((strcmp(s
, "table-start") == 0) && (table_image_on
)) );
6349 * is_graphic_end - return TRUE if the end of a table, pic, eqn was seen.
6352 int is_graphic_end (char *s
)
6354 return( (strcmp(s
, "graphic-end") == 0) ||
6355 ((strcmp(s
, "table-end") == 0) && (table_image_on
)) );
6359 * special - handle all x X requests from troff. For grohtml they allow users
6360 * to pass raw html commands, turn auto linked headings off/on and
6361 * also allow tbl, eqn & pic say what commands they have generated.
6364 void html_printer::special(char *s
, const environment
*env
)
6367 if (is_graphic_start(s
)) {
6369 if (graphic_level
== 1) {
6370 page_contents
->is_in_graphic
= TRUE
; // add words and lines to temporary region lists
6372 } else if (is_graphic_end(s
) && (graphic_level
> 0)) {
6374 if (graphic_level
== 0) {
6377 } else if (strncmp(s
, "html:", 5) == 0) {
6378 int r
=font::res
; // resolution of the device
6379 char buf
[MAX_STRING_LENGTH
];
6380 font
*f
=sbuf_style
.f
;
6385 f
= font::load_font("TR", &found
);
6387 str_translate_to_html(f
, buf
, MAX_STRING_LENGTH
,
6388 &s
[5], strlen(s
)-5, FALSE
);
6389 page_contents
->add_html_command(&sbuf_style
, buf
, strlen(buf
),
6391 // need to pass rest of string through to html output during flush
6393 env
->vpos
-env
->size
*r
/72, env
->hpos
,
6394 env
->vpos
, env
->hpos
);
6395 // assume that the html command has no width, if it does then we hopefully troff
6396 // will have fudged this in a macro and requested that the formatting move right by
6397 // the appropriate width
6398 } else if (strncmp(s
, "index:", 6) == 0) {
6399 cutoff_heading
= atoi(&s
[6]);
6404 void set_image_type (char *type
)
6406 if (strcmp(type
, "gif") == 0) {
6408 } else if (strcmp(type
, "png") == 0) {
6410 image_device
= "png256";
6411 } else if (strncmp(type
, "png", 3) == 0) {
6413 image_device
= type
;
6417 printer
*make_printer()
6419 return new html_printer
;
6422 static void usage();
6424 int main(int argc
, char **argv
)
6426 program_name
= argv
[0];
6427 static char stderr_buf
[BUFSIZ
];
6428 setbuf(stderr
, stderr_buf
);
6430 while ((c
= getopt(argc
, argv
, "F:atTvdgmx?I:r:")) != EOF
)
6434 extern const char *Version_string
;
6435 fprintf(stderr
, "grohtml version %s\n", Version_string
);
6446 table_image_on
= FALSE
;
6449 font::command_line_font_dir(optarg
);
6452 // user specifying the type of images we should generate
6453 set_image_type(optarg
);
6456 // resolution (dots per inch for an image)
6457 image_res
= atoi(optarg
);
6464 debug_table_on
= TRUE
;
6467 // do not guess title and headings
6471 // leave margins alone
6480 if (optind
>= argc
) {
6483 for (int i
= optind
; i
< argc
; i
++)
6492 fprintf(stderr
, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n",