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"
37 #include "ordered_list.h"
46 #define MAX_TEMP_NAME 1024
47 #define MAX_STRING_LENGTH 4096
49 #define Y_FUDGE_MARGIN +0.83
50 #define A4_PAGE_LENGTH (11.6944-Y_FUDGE_MARGIN)
51 #define DEFAULT_IMAGE_RES 80
52 #define IMAGE_BOARDER_PIXELS 10
53 #define MAX_WORDS_PER_LINE 1000 // only used for table indentation
54 #define GAP_SPACES 3 // how many spaces needed to guess a gap?
55 #define GAP_WIDTH_ONE_LINE 2 // 1/GAP_WIDTH_ONE_LINE inches required for one line table
56 #define CENTER_TOLERANCE 2 // how many pixels off center will we think a line or region is centered
57 #define MIN_COLUMN 7 // minimum column size pixels
61 * Only uncomment one of the following to determine default image type.
64 #define IMAGE_DEFAULT_PNG
65 /* #define IMAGE_DEFAULT_GIF */
68 #if defined(IMAGE_DEFAULT_GIF)
69 static enum { gif, png } image_type = gif;
70 static char *image_device = "gif";
71 #elif defined(IMAGE_DEFAULT_PNG)
72 static enum { gif, png } image_type = png;
73 static char *image_device = "png256";
75 # error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG"
78 static int debug_on = FALSE;
79 static int guess_on = TRUE;
80 static int margin_on = FALSE;
81 static int auto_on = TRUE;
82 static int table_on = TRUE;
83 static int image_res = DEFAULT_IMAGE_RES;
84 static int debug_table_on = FALSE;
86 static int linewidth = -1;
88 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
89 #define MAX_LINE_LENGTH 72
96 * start with a few favorites
99 static int min (int a, int b)
108 static int max (int a, int b)
119 * is_subsection - returns TRUE if a1..a2 is within b1..b2
122 static int is_subsection (int a1, int a2, int b1, int b2)
124 // easier to see whether this is not the case
125 return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) );
130 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
133 static int is_intersection (int a1, int a2, int b1, int b2)
135 // again easier to prove NOT outside limits
136 return( ! ((a1 > b2) || (a2 < b1)) );
141 * is_digit - returns TRUE if character, ch, is a digit.
144 static int is_digit (char ch)
146 return( (ch >= '0') && (ch <= '9') );
151 * more_than_line_break - returns TRUE should v1 and v2 differ by more than
152 * a simple line break.
155 static int more_than_line_break (int v1, int v2, int size)
157 return( abs(v1-v2)>size );
162 * the class and methods for styles
172 style (font *, int, int, int, int);
173 int operator == (const style &) const;
174 int operator != (const style &) const;
182 style::style(font *p, int sz, int h, int sl, int no)
183 : f(p), point_size(sz), height(h), slant(sl), font_no(no)
187 int style::operator==(const style &s) const
189 return (f == s.f && point_size == s.point_size
190 && height == s.height && slant == s.slant);
193 int style::operator!=(const style &s) const
195 return !(*this == s);
200 * the class and methods for retaining ascii text
212 char_block::char_block()
221 char *add_string(char *, unsigned int);
227 char_buffer::char_buffer()
232 char_buffer::~char_buffer()
235 char_block *temp = head;
241 char *char_buffer::add_string (char *s, unsigned int length)
244 unsigned int old_used;
247 tail = new char_block;
250 if (tail->used + length+1 > char_block::SIZE) {
251 tail->next = new char_block;
255 // at this point we have a tail which is ready for the string.
256 if (tail->used + length+1 > char_block::SIZE) {
257 fatal("need to increase char_block::SIZE");
260 old_used = tail->used;
262 tail->buffer[tail->used] = s[i];
268 // add terminating nul character
270 tail->buffer[tail->used] = '\0';
273 // and return start of new string
275 return( &tail->buffer[old_used] );
279 * the classes and methods for maintaining pages and text positions and graphic regions
284 int is_less (text_glob *a, text_glob *b);
285 text_glob (style *s, char *string, unsigned int length,
286 int min_vertical, int min_horizontal,
287 int max_vertical, int max_horizontal, int is_command, int is_html);
293 unsigned int text_length;
294 int minv, maxv, minh, maxh;
295 int is_raw_command; // should the text be sent directly to the device?
296 int is_html_command; // is the raw command definitely for the html device ie not an eqn?
299 text_glob::text_glob (style *s, char *string, unsigned int length,
300 int min_vertical, int min_horizontal,
301 int max_vertical, int max_horizontal, int is_command, int is_html)
302 : text_style(*s), text_string(string), text_length(length),
303 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
304 is_raw_command(is_command), is_html_command(is_html)
308 text_glob::text_glob ()
309 : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
310 is_raw_command(FALSE), is_html_command(FALSE)
314 text_glob::~text_glob ()
318 int text_glob::is_less (text_glob *a, text_glob *b)
320 if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
321 return( a->minh < b->minh );
323 return( a->maxv < b->maxv );
334 int is_less (graphic_glob *a, graphic_glob *b);
335 graphic_glob (int troff_code);
337 ~graphic_glob (void);
339 int minv, maxv, minh, maxh;
341 int nopoints; // number of points allocated in array below
342 struct xycoord *point;
348 graphic_glob::graphic_glob ()
349 : minv(-1), maxv(-1), minh(-1), maxh(-1), code(0), size(0), nopoints(0), point(0)
353 graphic_glob::~graphic_glob ()
360 graphic_glob::graphic_glob (int troff_code)
361 : minv(-1), maxv(-1), minh(-1), maxh(-1), code(troff_code), size(0), nopoints(0), point(0)
365 int graphic_glob::is_less (graphic_glob *a, graphic_glob *b)
367 return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
374 int is_less (region_glob *a, region_glob *b);
376 int minv, maxv, minh, maxh;
379 int region_glob::is_less (region_glob *a, region_glob *b)
381 return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
384 region_glob::region_glob (void)
385 : minv(-1), maxv(-1), minh(-1), maxh(-1)
389 region_glob::~region_glob (void)
396 void add (style *s, char *string, unsigned int length,
397 int min_vertical, int min_horizontal,
398 int max_vertical, int max_horizontal);
399 void add_html_command (style *s, char *string, unsigned int length,
400 int min_vertical, int min_horizontal,
401 int max_vertical, int max_horizontal);
402 void add_special_char (style *s, char *string, unsigned int length,
403 int min_vertical, int min_horizontal,
404 int max_vertical, int max_horizontal);
405 void add_line (int code, int x1, int y1, int x2, int y2, int size, int fill);
406 void add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill);
407 void add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill);
408 void add_spline (int code, int xc, int yc, int np, int *p, int size, int fill);
409 void calculate_region (void);
410 int is_in_region (graphic_glob *g);
411 int can_grow_region (graphic_glob *g);
412 void make_new_region (graphic_glob *g);
413 int has_line (region_glob *r);
414 int has_word (region_glob *r);
415 int no_raw_commands (int minv, int maxv);
419 ordered_list <region_glob> regions; // squares of bitmapped pics,eqn,tbl's
420 ordered_list <text_glob> words; // position of words on page
421 ordered_list <graphic_glob> lines; // position of lines on page
422 char_buffer buffer; // all characters for this page
423 int is_in_graphic; // should graphics and words go below or above
424 ordered_list <text_glob> region_words; // temporary accumulation of words in a region
425 ordered_list <graphic_glob> region_lines; // (as above) and used so that we can determine
426 // the regions vertical limits
430 : is_in_graphic(FALSE)
434 void page::add (style *s, char *string, unsigned int length,
435 int min_vertical, int min_horizontal,
436 int max_vertical, int max_horizontal)
439 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
440 min_vertical, min_horizontal, max_vertical, max_horizontal, FALSE, FALSE);
450 * add_html_command - it only makes sense to add html commands when we are not inside
451 * a graphical entity.
454 void page::add_html_command (style *s, char *string, unsigned int length,
455 int min_vertical, int min_horizontal,
456 int max_vertical, int max_horizontal)
458 if ((length > 0) && (! is_in_graphic)) {
459 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
460 min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, TRUE);
466 * add_special_char - it only makes sense to add special characters when we are inside
467 * a graphical entity.
470 void page::add_special_char (style *s, char *string, unsigned int length,
471 int min_vertical, int min_horizontal,
472 int max_vertical, int max_horizontal)
474 if ((length > 0) && (is_in_graphic)) {
475 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
476 min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, FALSE);
481 void page::add_line (int code, int x1, int y1, int x2, int y2, int size, int fill)
483 graphic_glob *g = new graphic_glob(code);
485 g->minh = min(x1, x2);
486 g->maxh = max(x1, x2);
487 g->minv = min(y1, y2);
488 g->maxv = max(y1, y2);
489 g->point = (struct xycoord *)malloc(sizeof(xycoord)*2);
508 * assign_min_max_for_arc - works out the smallest box that will encompass an
509 * arc defined by: origin: g->xc, g->xc
510 * and vector (p[0], p[1]) and (p[2], p[3])
513 void assign_min_max_for_arc (graphic_glob *g, int *p, double *c)
515 int radius = (int) sqrt(c[0]*c[0]+c[1]*c[1]);
522 int x2 = g->xc+xv1+xv2;
523 int y2 = g->yc+yv1+yv2;
525 // firstly lets use the 'circle' limitation
531 // incidentally I'm sure there is a better way to do this, but I don't know it
532 // please can someone let me know or "improve" this function
534 // now see which min/max can be reduced and increased for the limits of the arc
543 if ((xv1>=0) && (yv1>=0)) {
544 // first vector in Q3
545 if ((xv2>=0) && (yv2>=0)) {
549 } else if ((xv2<0) && (yv2>=0)) {
553 } else if ((xv2>=0) && (yv2<0)) {
555 g->minv = min(y1, y2);
556 } else if ((xv2<0) && (yv2<0)) {
561 g->minv = min(y1, y2);
562 g->maxv = max(y1, y2);
564 // xv2, yv2 could all be zero?
567 } else if ((xv1>=0) && (yv1<0)) {
568 // first vector in Q2
569 if ((xv2>=0) && (yv2>=0)) {
571 g->maxh = max(x1, x2);
572 g->minh = min(x1, x2);
574 } else if ((xv2<0) && (yv2>=0)) {
579 g->minv = min(y1, y2);
580 g->maxv = max(y1, y2);
582 // otherwise almost full circle anyway
584 } else if ((xv2>=0) && (yv2<0)) {
588 } else if ((xv2<0) && (yv2<0)) {
590 g->minh = min(x1, x2);
592 } else if ((xv1<0) && (yv1<0)) {
593 // first vector in Q1
594 if ((xv2>=0) && (yv2>=0)) {
599 g->minv = min(y1, y2);
600 g->maxv = max(y1, y2);
602 // nearly full circle
604 } else if ((xv2<0) && (yv2>=0)) {
606 g->maxv = max(y1, y2);
607 } else if ((xv2>=0) && (yv2<0)) {
609 g->minv = min(y1, y2);
610 g->maxv = max(y1, y2);
611 g->minh = min(x1, x2);
612 } else if ((xv2<0) && (yv2<0)) {
617 } else if ((xv1<0) && (yv1>=0)) {
618 // first vector in Q4
619 if ((xv2>=0) && (yv2>=0)) {
621 g->maxh = max(x1, x2);
622 } else if ((xv2<0) && (yv2>=0)) {
624 g->maxv = max(y1, y2);
625 g->maxh = max(x1, x2);
626 } else if ((xv2>=0) && (yv2<0)) {
629 g->minv = min(y1, y2);
630 g->maxv = max(y1, y2);
631 g->minh = min(x1, x2);
632 g->maxh = max(x2, x2);
634 // nearly full circle
636 } else if ((xv2<0) && (yv2<0)) {
638 g->maxv = max(y1, y2);
639 g->minh = min(x1, x2);
640 g->maxh = max(x1, x2);
643 // this should *never* happen but if it does it means a case above is wrong..
645 // this code is only present for safety sake
646 if (g->maxh < g->minh) {
648 fprintf(stderr, "assert failed minh > maxh\n"); fflush(stderr);
653 if (g->maxv < g->minv) {
655 fprintf(stderr, "assert failed minv > maxv\n"); fflush(stderr);
662 void page::add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill)
664 graphic_glob *g = new graphic_glob(code);
666 g->point = (struct xycoord *)malloc(sizeof(xycoord)*2);
668 g->point[0].x = p[0] ;
669 g->point[0].y = p[1] ;
670 g->point[1].x = p[2] ;
671 g->point[1].y = p[3] ;
677 assign_min_max_for_arc(g, p, c);
687 void page::add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill)
689 graphic_glob *g = new graphic_glob(code);
693 g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
696 for (i=0; i<g->nopoints; i++) {
697 g->point[i].x = p[j];
699 g->point[i].y = p[j];
702 // now calculate min/max
703 g->minh = g->point[0].x;
704 g->minv = g->point[0].y;
705 g->maxh = g->point[0].x;
706 g->maxv = g->point[0].y;
707 for (i=1; i<g->nopoints; i++) {
708 g->minh = min(g->minh, g->point[i].x);
709 g->minv = min(g->minv, g->point[i].y);
710 g->maxh = max(g->maxh, g->point[i].x);
711 g->maxv = max(g->maxv, g->point[i].y);
725 void page::add_spline (int code, int xc, int yc, int np, int *p, int size, int fill)
727 graphic_glob *g = new graphic_glob(code);
731 g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
734 for (i=0; i<g->nopoints; i++) {
735 g->point[i].x = p[j];
737 g->point[i].y = p[j];
740 // now calculate min/max
741 g->minh = min(g->point[0].x, g->point[0].x/2);
742 g->minv = min(g->point[0].y, g->point[0].y/2);
743 g->maxh = max(g->point[0].x, g->point[0].x/2);
744 g->maxv = max(g->point[0].y, g->point[0].y/2);
746 /* tnum/tden should be between 0 and 1; the closer it is to 1
747 the tighter the curve will be to the guiding lines; 2/3
748 is the standard value */
752 for (i=1; i<g->nopoints-1; i++) {
753 g->minh = min(g->minh, g->point[i].x*tnum/(2*tden));
754 g->minv = min(g->minv, g->point[i].y*tnum/(2*tden));
755 g->maxh = max(g->maxh, g->point[i].x*tnum/(2*tden));
756 g->maxv = max(g->maxv, g->point[i].y*tnum/(2*tden));
758 g->minh = min(g->minh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
759 g->minv = min(g->minv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
760 g->maxh = max(g->maxh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
761 g->maxv = max(g->maxv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
763 g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
764 g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
765 g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
766 g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
770 g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2)) + xc;
771 g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2)) + yc;
772 g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2)) + xc;
773 g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2)) + yc;
788 * the classes and methods for simple_output manipulation
791 simple_output::simple_output(FILE *f, int n)
792 : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
796 simple_output &simple_output::set_file(FILE *f)
803 simple_output &simple_output::copy_file(FILE *infp)
806 while ((c = getc(infp)) != EOF)
811 simple_output &simple_output::end_line()
821 simple_output &simple_output::special(const char *s)
826 simple_output &simple_output::simple_comment(const char *s)
838 simple_output &simple_output::begin_comment(const char *s)
848 simple_output &simple_output::end_comment()
859 simple_output &simple_output::comment_arg(const char *s)
863 if (col + len + 1 > max_line_length) {
872 simple_output &simple_output::set_fixed_point(int n)
874 assert(n >= 0 && n <= 10);
879 simple_output &simple_output::put_delimiter(char c)
887 simple_output &simple_output::put_string(const char *s, int n)
899 simple_output &simple_output::put_translated_string(const char *s)
903 while (s[i] != (char)0) {
904 if ((s[i] & 0x7f) == s[i]) {
913 simple_output &simple_output::put_string(const char *s)
917 while (s[i] != '\0') {
925 struct html_2_postscript {
927 char *postscript_char;
930 static struct html_2_postscript char_conversions[] = {
938 // this is an aweful hack which attempts to translate html characters onto
939 // postscript characters. Can this be done inside the devhtml files?
941 // or should we read the devps files and find out the translations?
944 simple_output &simple_output::put_translated_char (const char *s)
948 while (char_conversions[i].html_char != NULL) {
949 if (strcmp(s, char_conversions[i].html_char) == 0) {
950 put_string(char_conversions[i].postscript_char);
960 simple_output &simple_output::put_number(int n)
962 char buf[1 + INT_DIGITS + 1];
963 sprintf(buf, "%d", n);
964 int len = strlen(buf);
965 put_string(buf, len);
970 simple_output &simple_output::put_float(double d)
974 sprintf(buf, "%.4f", d);
975 int len = strlen(buf);
976 put_string(buf, len);
982 simple_output &simple_output::put_symbol(const char *s)
996 class html_font : public font {
997 html_font(const char *);
1001 char *reencoded_name;
1003 void handle_unknown_font_command(const char *command, const char *arg,
1004 const char *filename, int lineno);
1005 static html_font *load_html_font(const char *);
1008 html_font *html_font::load_html_font(const char *s)
1010 html_font *f = new html_font(s);
1018 html_font::html_font(const char *nm)
1023 html_font::~html_font()
1027 void html_font::handle_unknown_font_command(const char *command, const char *arg,
1028 const char *filename, int lineno)
1030 if (strcmp(command, "encoding") == 0) {
1032 error_with_file_and_line(filename, lineno,
1033 "`encoding' command requires an argument");
1035 encoding = strsave(arg);
1041 * a simple class to contain the header to this document
1049 int has_been_written;
1051 char text[MAX_STRING_LENGTH];
1055 title_desc::title_desc ()
1056 : has_been_found(FALSE), has_been_written(FALSE)
1060 title_desc::~title_desc ()
1069 int no_of_headings; // how many headings have we found?
1070 char_buffer headings; // all the headings used in the document
1071 ordered_list <text_glob> headers;
1072 int header_level; // current header level
1073 int written_header; // have we written the header yet?
1074 char header_buffer[MAX_STRING_LENGTH]; // current header text
1076 void write_headings (FILE *f);
1079 header_desc::header_desc ()
1080 : no_of_headings(0), header_level(2), written_header(0)
1084 header_desc::~header_desc ()
1089 * paragraph_type - alignment for a new paragraph
1092 typedef enum { left_alignment, center_alignment } paragraph_type;
1095 * text_defn - defines the limit of text, initially these are stored in the
1096 * column array as words. Later we examine the white space between
1097 * the words in successive lines to find out whether we can detect
1098 * distinct columns. The columns are generated via html tables.
1102 int left; // the start of a word or text
1103 int right; // the end of the text and beginning of white space
1104 int is_used; // will this this column be used for words or space
1109 * note that html_tables are currently only used to provide a better
1110 * indentation mechanism for html text (in particular it allows grohtml
1111 * to render .IP and .2C together with autoformatting).
1119 int no_of_columns; // how many columns are we using?
1120 struct text_defn *columns; // left and right margins for each column
1121 int vertical_limit; // the limit of the table
1124 html_table::html_table ()
1125 : no_of_columns(0), columns(0), vertical_limit(0)
1129 html_table::~html_table ()
1133 class html_printer : public printer {
1136 simple_output troff;
1139 int space_char_index;
1140 int no_of_printed_pages;
1142 enum { SBUF_SIZE = 256 };
1143 char sbuf[SBUF_SIZE];
1145 int sbuf_start_hpos;
1148 int sbuf_space_width;
1149 int sbuf_space_count;
1150 int sbuf_space_diff_count;
1151 int sbuf_space_code;
1157 int output_draw_point_size;
1159 int output_line_thickness;
1161 unsigned char output_space_code;
1163 char *inside_font_style;
1167 page *page_contents;
1168 html_table indentation;
1169 int left_margin_indent;
1170 int right_margin_indent;
1171 int need_one_newline;
1175 paragraph_type para_type;
1176 char image_name[MAX_STRING_LENGTH];
1180 int start_region_vpos;
1181 int start_region_hpos;
1182 int end_region_vpos;
1183 int end_region_hpos;
1186 struct graphic_glob *start_graphic;
1187 struct text_glob *start_text;
1191 void set_style (const style &);
1192 void set_space_code (unsigned char c);
1193 void do_exec (char *, const environment *);
1194 void do_import (char *, const environment *);
1195 void do_def (char *, const environment *);
1196 void do_mdef (char *, const environment *);
1197 void do_file (char *, const environment *);
1198 void set_line_thickness (const environment *);
1199 void change_font (text_glob *g, int is_to_html);
1200 void terminate_current_font (void);
1201 void flush_font (void);
1202 void flush_page (void);
1203 void display_word (text_glob *g, int is_to_html);
1204 void html_display_word (text_glob *g);
1205 void troff_display_word (text_glob *g);
1206 void display_line (graphic_glob *g, int is_to_html);
1207 void display_fill (graphic_glob *g);
1208 void calculate_margin (void);
1209 void traverse_page_regions (void);
1210 void dump_page (void);
1211 int is_within_region (graphic_glob *g);
1212 int is_within_region (text_glob *t);
1213 int is_less (graphic_glob *g, text_glob *t);
1214 void display_globs (int is_to_html);
1215 void move_horizontal (text_glob *g, int left_margin);
1216 void move_vertical (text_glob *g, paragraph_type p);
1217 void write_html_font_face (const char *fontname, const char *left, const char *right);
1218 void write_html_font_type (const char *fontname, const char *left, const char *right);
1219 void html_change_font (text_glob *g, const char *fontname, int size);
1220 char *html_position_text (text_glob *g, int left_margin, int right_margin);
1221 int html_position_region (void);
1222 void troff_change_font (const char *fontname, int size, int font_no);
1223 void troff_position_text (text_glob *g);
1224 int pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin);
1225 int is_on_same_line (text_glob *g, int vpos);
1226 int looks_like_subscript (text_glob *g);
1227 int looks_like_superscript (text_glob *g);
1228 void begin_paragraph (paragraph_type p);
1229 void begin_paragraph_no_height (paragraph_type p);
1230 void force_begin_paragraph (void);
1231 void end_paragraph (void);
1232 void html_newline (void);
1233 void convert_to_image (char *name);
1234 void write_title (int in_head);
1235 void find_title (void);
1236 int is_bold (text_glob *g);
1237 void write_header (void);
1238 void determine_header_level (void);
1239 void build_header (text_glob *g);
1240 void make_html_indent (int indent);
1241 int is_whole_line_bold (text_glob *g);
1242 int is_a_header (text_glob *g);
1243 int processed_header (text_glob *g);
1244 void make_new_image_name (void);
1245 void create_temp_name (char *name, char *extension);
1246 void calculate_region_margins (region_glob *r);
1247 void remove_redundant_regions (void);
1248 void remove_duplicate_regions (void);
1249 void move_region_to_page (void);
1250 void calculate_region_range (graphic_glob *r);
1251 void flush_graphic (void);
1252 void write_string (graphic_glob *g, int is_to_html);
1253 void prologue (void);
1256 void display_regions (void);
1257 int check_able_to_use_table (text_glob *g);
1258 int using_table_for_indent (void);
1259 int collect_columns (struct text_defn *line, struct text_defn *last, int max_words);
1260 void include_into_list (struct text_defn *line, struct text_defn *item);
1261 int is_in_column (struct text_defn *line, struct text_defn *item, int max_words);
1262 int is_column_match (struct text_defn *match, struct text_defn *line1, struct text_defn *line2, int max_words);
1263 int count_columns (struct text_defn *line);
1264 void rewind_text_to (text_glob *g);
1265 int found_use_for_table (text_glob *start);
1266 void column_display_word (int vert, int left, int right, int next);
1267 void start_table (void);
1268 void end_table (void);
1269 void foreach_column_include_text (text_glob *start);
1270 void define_cell (int left, int right);
1271 int column_calculate_left_margin (int left, int right);
1272 int column_calculate_right_margin (int left, int right);
1273 void display_columns (const char *word, const char *name, text_defn *line);
1274 void calculate_right (struct text_defn *line, int max_words);
1275 void determine_right_most_column (struct text_defn *line, int max_words);
1276 int remove_white_using_words (struct text_defn *next_guess, struct text_defn *last_guess, struct text_defn *next_line);
1277 int copy_line (struct text_defn *dest, struct text_defn *src);
1278 void combine_line (struct text_defn *dest, struct text_defn *src);
1279 int conflict_with_words (struct text_defn *column_guess, struct text_defn *words);
1280 void remove_entry_in_line (struct text_defn *line, int j);
1281 void remove_redundant_columns (struct text_defn *line);
1282 void add_column_gaps (struct text_defn *line);
1283 int continue_searching_column (text_defn *next_col, text_defn *last_col, text_defn *all_words);
1284 void add_right_full_width (struct text_defn *line, int mingap);
1285 int is_continueous_column (text_defn *last_col, text_defn *next_line);
1286 int is_exact_left (text_defn *last_col, text_defn *next_line);
1287 void emit_space (text_glob *g, int force_space);
1288 int is_in_middle (int left, int right);
1289 int check_able_to_use_center (text_glob *g);
1290 void write_centered_line (text_glob *g);
1291 int single_centered_line (text_defn *first, text_defn *second, text_glob *g);
1292 int determine_row_limit (text_glob *start, int v);
1293 void assign_used_columns (text_glob *start);
1294 int find_column_index (text_glob *t);
1295 int large_enough_gap (text_defn *last_col);
1296 int is_worth_column (int left, int right);
1297 int is_subset_of_columns (text_defn *a, text_defn *b);
1298 void count_hits (text_defn *col);
1299 int calculate_min_gap (text_glob *g);
1304 void set_char(int i, font *f, const environment *env, int w, const char *name);
1305 void draw(int code, int *p, int np, const environment *env);
1306 void begin_page(int);
1308 void special(char *arg, const environment *env);
1309 font *make_font(const char *);
1313 html_printer::html_printer()
1314 : no_of_printed_pages(0),
1318 html(0, MAX_LINE_LENGTH),
1319 troff(0, MAX_LINE_LENGTH),
1321 inside_font_style(0),
1324 left_margin_indent(0),
1325 right_margin_indent(0),
1326 start_region_vpos(0),
1327 start_region_hpos(0),
1330 need_one_newline(0),
1334 cutoff_heading(100),
1337 para_type(left_alignment)
1339 tempfp = xtmpfile();
1340 html.set_file(tempfp);
1342 linewidth = DEFAULT_LINEWIDTH;
1344 fatal("horizontal resolution must be 1");
1345 if (font::vert != 1)
1346 fatal("vertical resolution must be 1");
1348 // should be sorted html..
1349 if (font::res % (font::sizescale*72) != 0)
1350 fatal("res must be a multiple of 72*sizescale");
1354 while (r % 10 == 0) {
1359 html.set_fixed_point(point);
1360 space_char_index = font::name_to_index("space");
1361 paper_length = font::paperlength;
1362 if (paper_length == 0)
1363 paper_length = 11*font::res;
1364 page_contents = new page;
1366 postscript_res = 72000;
1370 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
1372 unsigned char code = f->get_code(i);
1374 style sty(f, env->size, env->height, env->slant, env->fontno);
1375 if (sty.slant != 0) {
1376 if (sty.slant > 80 || sty.slant < -80) {
1377 error("silly slant `%1' degrees", sty.slant);
1381 if ((name != 0) && (page_contents->is_in_graphic)) {
1383 int r=font::res; // resolution of the device actually
1384 page_contents->add_special_char(&sty, (char *)name, strlen(name),
1385 env->vpos-sty.point_size*r/72, env->hpos,
1386 env->vpos, env->hpos+w);
1387 sbuf_end_hpos = env->hpos + w;
1388 sbuf_start_hpos = env->hpos;
1389 sbuf_vpos = env->vpos;
1396 if (sbuf_len < SBUF_SIZE
1397 && sty == sbuf_style
1398 && sbuf_vpos == env->vpos) {
1399 if (sbuf_end_hpos == env->hpos) {
1400 sbuf[sbuf_len++] = code;
1401 sbuf_end_hpos += w + sbuf_kern;
1404 /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
1405 starting a new string. */
1406 if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
1407 && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
1408 if (sbuf_space_code < 0) {
1410 sbuf_space_code = ' ';
1412 sbuf_space_width = env->hpos - sbuf_end_hpos;
1413 sbuf_end_hpos = env->hpos + w + sbuf_kern;
1414 sbuf[sbuf_len++] = ' ';
1415 sbuf[sbuf_len++] = code;
1419 int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
1421 sbuf_end_hpos = env->hpos + w + sbuf_kern;
1422 sbuf[sbuf_len++] = sbuf_space_code;
1423 sbuf[sbuf_len++] = code;
1426 sbuf_space_diff_count++;
1427 else if (diff == -1)
1428 sbuf_space_diff_count--;
1438 sbuf_end_hpos = env->hpos + w;
1439 sbuf_start_hpos = env->hpos;
1440 sbuf_vpos = env->vpos;
1442 sbuf_space_code = -1;
1443 sbuf_space_width = 0;
1444 sbuf_space_count = sbuf_space_diff_count = 0;
1450 * make_new_image_name - creates a new file name ready for a image file.
1451 * it leaves the extension off.
1454 void html_printer::make_new_image_name (void)
1457 sprintf(image_name, "groff-html-%d-%d", image_number, getpid());
1461 * write_title - writes the title to this document
1464 void html_printer::write_title (int in_head)
1466 if (title.has_been_found) {
1468 html.put_string("<title>");
1469 html.put_string(title.text);
1470 html.put_string("</title>\n");
1472 title.has_been_written = TRUE;
1473 html.put_string("<h1 align=center>");
1474 html.put_string(title.text);
1475 html.put_string("</h1>\n");
1482 * find_title - finds a title to this document, if it exists.
1485 void html_printer::find_title (void)
1489 int removed_from_head;
1491 if ((page_number == 1) && (guess_on)) {
1492 if (! page_contents->words.is_empty()) {
1494 int end_title_hpos = 0;
1495 int start_title_hpos = 0;
1496 int start_title_vpos = 0;
1497 int found_title_start = FALSE;
1499 int start_region =-1;
1501 if (! page_contents->regions.is_empty()) {
1504 page_contents->regions.start_from_head();
1505 r = page_contents->regions.get_data();
1507 start_region = r->minv;
1511 page_contents->words.start_from_head();
1513 t = page_contents->words.get_data();
1514 removed_from_head = FALSE;
1515 if ((found_title_start) && (start_region != -1) && (t->maxv >= start_region)) {
1517 * we have just encountered the first graphic region so
1518 * we stop looking for a title.
1520 title.has_been_found = TRUE;
1522 } else if (t->is_raw_command) {
1523 // skip raw commands
1524 } else if ((!found_title_start) && (t->minh > left_margin_indent) &&
1525 ((start_region == -1) || (t->maxv < start_region))) {
1526 start_title_vpos = t->minv;
1527 end_title_hpos = t->minh;
1528 strcpy((char *)title.text, (char *)t->text_string);
1529 height = t->text_style.point_size*r/72;
1530 found_title_start = TRUE;
1531 page_contents->words.sub_move_right();
1532 removed_from_head = ((!page_contents->words.is_empty()) &&
1533 (page_contents->words.is_equal_to_head()));
1534 } else if (found_title_start) {
1535 if ((t->minv == start_title_vpos) ||
1536 ((!more_than_line_break(start_title_vpos, t->minv, (height*3)/2)) &&
1537 (t->minh > left_margin_indent)) ||
1538 (is_bold(t) && (t->minh > left_margin_indent))) {
1539 start_title_vpos = min(t->minv, start_title_vpos);
1540 end_title_hpos = max(t->maxh, end_title_hpos);
1541 strcat(title.text, " ");
1542 strcat(title.text, (char *)t->text_string);
1543 page_contents->words.sub_move_right();
1544 removed_from_head = ((!page_contents->words.is_empty()) &&
1545 (page_contents->words.is_equal_to_head()));
1548 title.has_been_found = TRUE;
1551 } else if (t->minh == left_margin_indent) {
1555 // move onto next word
1556 page_contents->words.move_right();
1558 } while ((! page_contents->words.is_equal_to_head()) || (removed_from_head));
1564 * html_newline - generates a newline <br>
1567 void html_printer::html_newline (void)
1570 int height = output_style.point_size*r/72;
1573 // safe to generate a pretty newline
1574 html.put_string("<br>\n");
1576 html.put_string("<br>");
1578 output_vpos += height;
1579 issued_newline = TRUE;
1583 * force_begin_paragraph - force the begin_paragraph to be emitted.
1586 void html_printer::force_begin_paragraph (void)
1588 if (in_paragraph && need_paragraph) {
1589 switch (para_type) {
1591 case left_alignment: html.put_string("<p>");
1593 case center_alignment: html.put_string("<p align=center>");
1595 default: fatal("unknown paragraph alignment type");
1597 need_paragraph = FALSE;
1602 * begin_paragraph - starts a new paragraph. It does nothing if a paragraph
1603 * has already been started.
1606 void html_printer::begin_paragraph (paragraph_type p)
1608 if (! in_paragraph) {
1610 int height = output_style.point_size*r/72;
1612 if (output_vpos >=0) {
1613 // we leave it alone if it is set to the top of page
1614 output_vpos += height;
1616 need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text
1617 in_paragraph = TRUE;
1618 issued_newline = TRUE;
1625 * begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph
1626 * has already been started. Note it does not alter output_vpos.
1629 void html_printer::begin_paragraph_no_height (paragraph_type p)
1631 if (! in_paragraph) {
1632 need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text
1633 in_paragraph = TRUE;
1634 issued_newline = TRUE;
1640 * end_paragraph - end the current paragraph. It does nothing if a paragraph
1641 * has not been started.
1644 void html_printer::end_paragraph (void)
1647 // check whether we have generated any text inbetween the potential paragraph begin end
1648 if (! need_paragraph) {
1650 int height = output_style.point_size*r/72;
1652 output_vpos += height;
1653 html.put_string("</p>\n");
1655 terminate_current_font();
1656 para_type = left_alignment;
1657 in_paragraph = FALSE;
1662 * calculate_margin - runs through the words and graphics globs
1663 * and finds the start of the left most margin.
1666 void html_printer::calculate_margin (void)
1674 right_margin_indent = 0;
1676 if (! page_contents->words.is_empty()) {
1678 // firstly check the words right margin
1680 page_contents->words.start_from_head();
1682 w = page_contents->words.get_data();
1683 if ((w->maxh >= 0) && (w->maxh > right_margin_indent)) {
1684 right_margin_indent = w->maxh;
1686 if (right_margin_indent == 950) stop();
1689 page_contents->words.move_right();
1690 } while (! page_contents->words.is_equal_to_head());
1693 if (! page_contents->lines.is_empty()) {
1694 // now check for diagrams for right margin
1695 page_contents->lines.start_from_head();
1697 g = page_contents->lines.get_data();
1698 if ((g->maxh >= 0) && (g->maxh > right_margin_indent)) {
1699 right_margin_indent = g->maxh;
1701 if (right_margin_indent == 950) stop();
1704 page_contents->lines.move_right();
1705 } while (! page_contents->lines.is_equal_to_head());
1708 // now we know the right margin lets do the same to find left margin
1710 left_margin_indent = right_margin_indent;
1712 if (! page_contents->words.is_empty()) {
1714 w = page_contents->words.get_data();
1715 if ((w->minh >= 0) && (w->minh < left_margin_indent)) {
1716 left_margin_indent = w->minh;
1718 page_contents->words.move_right();
1719 } while (! page_contents->words.is_equal_to_head());
1722 if (! page_contents->lines.is_empty()) {
1723 // now check for diagrams
1724 page_contents->lines.start_from_head();
1726 g = page_contents->lines.get_data();
1727 if ((g->minh >= 0) && (g->minh < left_margin_indent)) {
1728 left_margin_indent = g->minh;
1730 page_contents->lines.move_right();
1731 } while (! page_contents->lines.is_equal_to_head());
1738 * calculate_region - runs through the graphics globs and text globs
1739 * and ensures that all graphic routines
1740 * are defined by the region lists.
1741 * This then allows us to easily
1742 * determine the range of vertical and
1743 * horizontal boundaries for pictures,
1748 void page::calculate_region (void)
1752 if (! lines.is_empty()) {
1753 lines.start_from_head();
1755 g = lines.get_data();
1756 if (! is_in_region(g)) {
1757 if (! can_grow_region(g)) {
1762 } while (! lines.is_equal_to_head());
1767 * remove_redundant_regions - runs through the regions and ensures that
1768 * all are needed. This is required as
1769 * a picture may be empty, or EQ EN pair
1773 void html_printer::remove_redundant_regions (void)
1778 // firstly run through the region making sure that all are needed
1779 // ie all contain a line or word
1780 if (! page_contents->regions.is_empty()) {
1781 page_contents->regions.start_from_tail();
1783 r = page_contents->regions.get_data();
1784 calculate_region_margins(r);
1785 if (page_contents->has_line(r) || page_contents->has_word(r)) {
1786 page_contents->regions.move_right();
1788 page_contents->regions.sub_move_right();
1790 } while ((! page_contents->regions.is_empty()) &&
1791 (! page_contents->regions.is_equal_to_tail()));
1795 void html_printer::display_regions (void)
1797 if (debug_table_on) {
1800 fprintf(stderr, "==========s t a r t===========\n");
1801 if (! page_contents->regions.is_empty()) {
1802 page_contents->regions.start_from_head();
1804 r = page_contents->regions.get_data();
1805 fprintf(stderr, "region minv %d maxv %d\n", r->minv, r->maxv);
1806 page_contents->regions.move_right();
1807 } while (! page_contents->regions.is_equal_to_head());
1809 fprintf(stderr, "============e n d=============\n");
1815 * remove_duplicate_regions - runs through the regions and ensures that
1816 * no duplicates exist.
1819 void html_printer::remove_duplicate_regions (void)
1824 if (! page_contents->regions.is_empty()) {
1825 page_contents->regions.start_from_head();
1826 l = page_contents->regions.get_data();
1827 page_contents->regions.move_right();
1828 r = page_contents->regions.get_data();
1831 r = page_contents->regions.get_data();
1832 // we have a legit region so we check for an intersection
1833 if (is_intersection(r->minv, r->minv, l->minv, l->maxv) &&
1834 is_intersection(r->minh, r->maxh, l->minh, l->maxh)) {
1835 l->minv = min(r->minv, l->minv);
1836 l->maxv = max(r->maxv, l->maxv);
1837 l->minh = min(r->minh, l->minh);
1838 l->maxh = max(r->maxh, l->maxh);
1839 calculate_region_margins(l);
1840 page_contents->regions.sub_move_right();
1843 page_contents->regions.move_right();
1845 } while ((! page_contents->regions.is_empty()) &&
1846 (! page_contents->regions.is_equal_to_head()));
1851 int page::has_line (region_glob *r)
1855 if (! lines.is_empty()) {
1856 lines.start_from_head();
1858 g = lines.get_data();
1859 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
1860 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
1864 } while (! lines.is_equal_to_head());
1870 int page::has_word (region_glob *r)
1874 if (! words.is_empty()) {
1875 words.start_from_head();
1877 g = words.get_data();
1878 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
1879 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
1883 } while (! words.is_equal_to_head());
1889 void html_printer::calculate_region_margins (region_glob *r)
1894 r->minh = right_margin_indent;
1895 r->maxh = left_margin_indent;
1897 if (! page_contents->lines.is_empty()) {
1898 page_contents->lines.start_from_head();
1900 g = page_contents->lines.get_data();
1901 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv)) {
1902 r->minh = min(r->minh, g->minh);
1903 r->maxh = max(r->maxh, g->maxh);
1905 page_contents->lines.move_right();
1906 } while (! page_contents->lines.is_equal_to_head());
1908 if (! page_contents->words.is_empty()) {
1909 page_contents->words.start_from_head();
1911 w = page_contents->words.get_data();
1912 if (is_subsection(w->minv, w->maxv, r->minv, r->maxv)) {
1913 r->minh = min(r->minh, w->minh);
1914 r->maxh = max(r->maxh, w->maxh);
1916 page_contents->words.move_right();
1917 } while (! page_contents->words.is_equal_to_head());
1922 int page::is_in_region (graphic_glob *g)
1926 if (! regions.is_empty()) {
1927 regions.start_from_head();
1929 r = regions.get_data();
1930 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
1931 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
1934 regions.move_right();
1935 } while (! regions.is_equal_to_head());
1942 * no_raw_commands - returns TRUE if no html raw commands exist between
1946 int page::no_raw_commands (int minv, int maxv)
1950 if (! words.is_empty()) {
1951 words.start_from_head();
1953 g = words.get_data();
1954 if ((g->is_raw_command) && (g->is_html_command) &&
1955 (is_intersection(g->minv, g->maxv, minv, maxv))) {
1959 } while (! words.is_equal_to_head());
1965 * can_grow_region - returns TRUE if a region exists which can be extended
1966 * to include graphic_glob *g. The region is extended.
1969 int page::can_grow_region (graphic_glob *g)
1972 int quarter_inch=font::res/4;
1974 if (! regions.is_empty()) {
1975 regions.start_from_head();
1977 r = regions.get_data();
1978 // must prevent grohtml from growing a region through a html raw command
1979 if (is_intersection(g->minv, g->maxv, r->minv, r->maxv+quarter_inch) &&
1980 (no_raw_commands(r->minv, r->maxv+quarter_inch))) {
1981 #if defined(DEBUGGING)
1983 printf("r minh=%d minv=%d maxh=%d maxv=%d\n",
1984 r->minh, r->minv, r->maxh, r->maxv);
1985 printf("g minh=%d minv=%d maxh=%d maxv=%d\n",
1986 g->minh, g->minv, g->maxh, g->maxv);
1988 r->minv = min(r->minv, g->minv);
1989 r->maxv = max(r->maxv, g->maxv);
1990 r->minh = min(r->minh, g->minh);
1991 r->maxh = max(r->maxh, g->maxh);
1992 #if defined(DEBUGGING)
1993 printf(" r minh=%d minv=%d maxh=%d maxv=%d\n",
1994 r->minh, r->minv, r->maxh, r->maxv);
1998 regions.move_right();
1999 } while (! regions.is_equal_to_head());
2006 * make_new_region - creates a new region to contain, g.
2009 void page::make_new_region (graphic_glob *g)
2011 region_glob *r=new region_glob;
2021 void html_printer::dump_page(void)
2025 printf("\n\ndebugging start\n");
2026 page_contents->words.start_from_head();
2028 g = page_contents->words.get_data();
2029 printf("%s ", g->text_string);
2030 page_contents->words.move_right();
2031 } while (! page_contents->words.is_equal_to_head());
2032 printf("\ndebugging end\n\n");
2037 * traverse_page_regions - runs through the regions in current_page
2038 * and generate html for text, and troff output
2042 void html_printer::traverse_page_regions (void)
2046 start_region_vpos = 0;
2047 start_region_hpos = 0;
2048 end_region_vpos = -1;
2049 end_region_hpos = -1;
2051 if (! page_contents->regions.is_empty()) {
2052 page_contents->regions.start_from_head();
2054 r = page_contents->regions.get_data();
2056 end_region_vpos = r->minv-1;
2058 end_region_vpos = 0;
2060 end_region_hpos = -1;
2061 display_globs(TRUE);
2062 calculate_region_margins(r);
2063 start_region_vpos = end_region_vpos;
2064 end_region_vpos = r->maxv;
2065 start_region_hpos = r->minh;
2066 end_region_hpos = r->maxh;
2067 display_globs(FALSE);
2068 start_region_vpos = end_region_vpos+1;
2069 start_region_hpos = 0;
2070 page_contents->regions.move_right();
2071 } while (! page_contents->regions.is_equal_to_head());
2072 start_region_vpos = end_region_vpos+1;
2073 start_region_hpos = 0;
2074 end_region_vpos = -1;
2075 end_region_hpos = -1;
2077 display_globs(TRUE);
2080 int html_printer::is_within_region (text_glob *t)
2084 if (start_region_hpos == -1) {
2087 hs = start_region_hpos;
2089 if (end_region_vpos == -1) {
2092 ve = end_region_vpos;
2094 if (end_region_hpos == -1) {
2097 he = end_region_hpos;
2099 return( is_subsection(t->minv, t->maxv, start_region_vpos, ve) &&
2100 is_subsection(t->minh, t->maxh, hs, he) );
2103 int html_printer::is_within_region (graphic_glob *g)
2107 if (start_region_hpos == -1) {
2110 hs = start_region_hpos;
2112 if (end_region_vpos == -1) {
2115 ve = end_region_vpos;
2117 if (end_region_hpos == -1) {
2120 he = end_region_hpos;
2122 return( is_subsection(g->minv, g->maxv, start_region_vpos, ve) &&
2123 is_subsection(g->minh, g->maxh, hs, he) );
2126 int html_printer::is_less (graphic_glob *g, text_glob *t)
2128 return( (g->minv < t->minv) || ((g->minv == t->minv) && (g->minh < t->minh)) );
2131 static FILE *create_file (char *filename)
2136 f = fopen(filename, "w");
2138 error("can't create `%1'", filename);
2145 void html_printer::convert_to_image (char *name)
2149 sprintf(buffer, "grops %s > %s.ps\n", name, name);
2151 fprintf(stderr, "%s", buffer);
2155 if (image_type == gif) {
2157 "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s.ps - | ppmquant 256 2> /dev/null | ppmtogif 2> /dev/null > %s.gif \n",
2159 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2160 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2164 "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s.ps - 2> /dev/null > %s.png \n",
2167 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2168 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2172 fprintf(stderr, "%s", buffer);
2175 sprintf(buffer, "/bin/rm -f %s %s.ps\n", name, name);
2177 fprintf(stderr, "%s", buffer);
2183 void html_printer::prologue (void)
2185 troff.put_string("x T ps\nx res ");
2186 troff.put_number(postscript_res);
2187 troff.put_string(" 1 1\nx init\np1\n");
2190 void html_printer::create_temp_name (char *name, char *extension)
2192 make_new_image_name();
2193 sprintf(name, "/tmp/%s.%s", image_name, extension);
2196 void html_printer::display_globs (int is_to_html)
2201 char name[MAX_TEMP_NAME];
2204 int something=FALSE;
2205 int is_center=FALSE;
2210 is_center = html_position_region();
2211 create_temp_name(name, "troff");
2212 f = create_file(name);
2217 if (! page_contents->words.is_empty()) {
2218 page_contents->words.start_from_head();
2219 t = page_contents->words.get_data();
2222 if (! page_contents->lines.is_empty()) {
2223 page_contents->lines.start_from_head();
2224 g = page_contents->lines.get_data();
2229 if ((t != 0) && (strcmp(t->text_string, "(1.a)") == 0)) {
2233 if ((t == 0) && (g != 0)) {
2234 if (is_within_region(g)) {
2236 display_line(g, is_to_html);
2238 if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2241 g = page_contents->lines.move_right_get_data();
2243 } else if ((g == 0) && (t != 0)) {
2244 if (is_within_region(t)) {
2245 display_word(t, is_to_html);
2248 if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2251 t = page_contents->words.move_right_get_data();
2254 if ((g == 0) || (t == 0)) {
2255 // hmm nothing to print out...
2256 } else if (is_less(g, t)) {
2257 if (is_within_region(g)) {
2258 display_line(g, is_to_html);
2261 if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2264 g = page_contents->lines.move_right_get_data();
2267 if (is_within_region(t)) {
2268 display_word(t, is_to_html);
2271 if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2274 t = page_contents->words.move_right_get_data();
2278 } while ((t != 0) || (g != 0));
2280 if ((! is_to_html) && (f != 0)) {
2281 fclose(troff.get_file());
2283 convert_to_image(name);
2286 begin_paragraph(center_alignment);
2288 begin_paragraph(left_alignment);
2290 force_begin_paragraph();
2291 html.put_string("<img src=\"");
2292 html.put_string(image_name);
2293 if (image_type == gif) {
2294 html.put_string(".gif\"");
2296 html.put_string(".png\"");
2299 html.put_string(" align=\"middle\"");
2301 html.put_string(">\n");
2305 output_vpos = end_region_vpos;
2307 need_one_newline = FALSE;
2310 // unlink(name); // remove troff file
2314 void html_printer::flush_page (void)
2318 output_hpos = left_margin_indent;
2322 html.begin_comment("left margin: ").comment_arg(itoa(left_margin_indent)).end_comment();;
2323 html.begin_comment("right margin: ").comment_arg(itoa(right_margin_indent)).end_comment();;
2324 remove_redundant_regions();
2325 page_contents->calculate_region();
2326 remove_duplicate_regions();
2329 traverse_page_regions();
2330 terminate_current_font();
2331 if (need_one_newline) {
2336 // move onto a new page
2337 delete page_contents;
2338 page_contents = new page;
2341 static int convertSizeToHTML (int size)
2345 } else if (size < 8) {
2347 } else if (size < 10) {
2349 } else if (size < 12) {
2351 } else if (size < 14) {
2353 } else if (size < 16) {
2355 } else if (size < 18) {
2363 void html_printer::write_html_font_face (const char *fontname, const char *left, const char *right)
2365 switch (fontname[0]) {
2367 case 'C': html.put_string(left) ; html.put_string("tt"); html.put_string(right);
2376 void html_printer::write_html_font_type (const char *fontname, const char *left, const char *right)
2378 if (strcmp(&fontname[1], "B") == 0) {
2379 html.put_string(left) ; html.put_string("B"); html.put_string(right);
2380 } else if (strcmp(&fontname[1], "I") == 0) {
2381 html.put_string(left) ; html.put_string("I"); html.put_string(right);
2382 } else if (strcmp(&fontname[1], "BI") == 0) {
2383 html.put_string(left) ; html.put_string("EM"); html.put_string(right);
2388 void html_printer::html_change_font (text_glob *g, const char *fontname, int size)
2392 if (output_style.f != 0) {
2393 const char *oldfontname = output_style.f->get_name();
2395 // firstly terminate the current font face and type
2396 if ((oldfontname != 0) && (oldfontname != fontname)) {
2397 write_html_font_face(oldfontname, "</", ">");
2398 write_html_font_type(oldfontname, "</", ">");
2401 if (fontname != 0) {
2402 // now emit the size if it has changed
2403 if (((output_style.f == 0) || (output_style.point_size != size)) && (size != 0)) {
2404 sprintf(buffer, "<font size=%d>", convertSizeToHTML(size));
2405 html.put_string(buffer);
2406 output_style.point_size = size; // and remember the size
2409 if (! g->is_raw_command) {
2410 // now emit the new font
2411 write_html_font_face(fontname, "<", ">");
2413 // now emit the new font type
2414 write_html_font_type(fontname, "<", ">");
2416 output_style = g->text_style; // remember style for next time
2419 output_style.f = 0; // no style at present
2424 void html_printer::change_font (text_glob *g, int is_to_html)
2427 if (output_style != g->text_style) {
2428 const char *fontname=0;
2431 if (g->text_style.f != 0) {
2432 fontname = g->text_style.f->get_name();
2433 size = (font::res/(72*font::sizescale))*g->text_style.point_size;
2435 html_change_font(g, fontname, size);
2437 html_change_font(g, fontname, size);
2442 if (output_style != g->text_style) {
2443 if (g->text_style.f != 0) {
2444 const char *fontname = g->text_style.f->get_name();
2445 int size = (font::res/(72*font::sizescale))*g->text_style.point_size;
2447 if (fontname == 0) {
2448 fatal("no internalname specified for font");
2451 troff_change_font(fontname, size, g->text_style.font_no);
2452 output_style = g->text_style; // remember style for next time
2460 * is_bold - returns TRUE if the text inside, g, is using a bold face.
2461 * It returns FALSE is g contains a raw html command, even if this uses
2465 int html_printer::is_bold (text_glob *g)
2467 if (g->text_style.f == 0) {
2470 } else if (g->is_raw_command) {
2473 const char *fontname = g->text_style.f->get_name();
2475 if (strlen(fontname) >= 2) {
2476 return( fontname[1] == 'B' );
2483 void html_printer::terminate_current_font (void)
2487 // we create a dummy glob just so we can tell html_change_font not to start up
2489 g.is_raw_command = TRUE;
2490 html_change_font(&g, 0, 0);
2493 void html_printer::write_header (void)
2495 if (strlen(header.header_buffer) > 0) {
2496 if (header.header_level > 7) {
2497 header.header_level = 7;
2500 if (cutoff_heading+2 > header.header_level) {
2501 // firstly we must terminate any font and type faces
2502 terminate_current_font();
2505 // secondly we generate a tag
2506 html.put_string("<a name=\"");
2507 html.put_string(header.header_buffer);
2508 html.put_string("\"></a>");
2509 // now we save the header so we can issue a list of link
2512 header.no_of_headings++;
2514 text_glob *g=new text_glob(&st,
2515 header.headings.add_string(header.header_buffer, strlen(header.header_buffer)),
2516 strlen(header.header_buffer),
2517 header.no_of_headings, header.header_level,
2518 header.no_of_headings, header.header_level,
2520 header.headers.add(g); // and add this header to the header list
2524 // and now we issue the real header
2525 html.put_string("<h");
2526 html.put_number(header.header_level);
2527 html.put_string(">");
2528 html.put_string(header.header_buffer);
2529 html.put_string("</h");
2530 html.put_number(header.header_level);
2531 html.put_string(">");
2532 need_one_newline = FALSE;
2533 begin_paragraph(left_alignment);
2534 header.written_header = TRUE;
2539 * write_headings - emits a list of links for the headings in this document
2542 void header_desc::write_headings (FILE *f)
2546 if (! headers.is_empty()) {
2547 headers.start_from_head();
2549 g = headers.get_data();
2550 fprintf(f, "<a href=\"#%s\">%s</a><br>\n", g->text_string, g->text_string);
2551 headers.move_right();
2552 } while (! headers.is_equal_to_head());
2556 void html_printer::determine_header_level (void)
2559 int l=strlen(header.header_buffer);
2562 for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) {
2563 if (header.header_buffer[i] == '.') {
2568 header.header_level = stops;
2573 void html_printer::build_header (text_glob *g)
2576 int height = g->text_style.point_size*r/72;
2580 strcpy(header.header_buffer, "");
2583 current_vpos = g->minv;
2584 strcat(header.header_buffer, (char *)g->text_string);
2585 page_contents->words.move_right();
2586 g = page_contents->words.get_data();
2587 if (g->minv == current_vpos) {
2588 strcat(header.header_buffer, " ");
2590 } while ((! page_contents->words.is_equal_to_head()) &&
2591 ((g->minv == current_vpos) || (l->maxh == right_margin_indent)));
2593 determine_header_level();
2594 // finally set the output to neutral for after the header
2596 g = page_contents->words.get_data();
2597 output_vpos = g->minv; // set output_vpos to the next line since
2598 output_hpos = left_margin_indent; // html header forces a newline anyway
2599 page_contents->words.move_left(); // so that next time we use old g
2601 need_one_newline = FALSE;
2606 * is_whole_line_bold - returns TRUE if the whole line is bold.
2609 int html_printer::is_whole_line_bold (text_glob *g)
2612 int current_vpos=g->minv;
2616 page_contents->words.move_right();
2617 n = page_contents->words.get_data();
2619 while (page_contents->words.get_data() != g) {
2620 page_contents->words.move_left();
2624 } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(n, current_vpos)));
2625 // was (n->minv == current_vpos)
2626 while (page_contents->words.get_data() != g) {
2627 page_contents->words.move_left();
2634 * is_a_header - returns TRUE if the whole sequence of contineous lines are bold.
2635 * It checks to see whether a line is likely to be contineous and
2636 * then checks that all words are bold.
2639 int html_printer::is_a_header (text_glob *g)
2647 current_vpos = n->minv;
2649 page_contents->words.move_right();
2650 n = page_contents->words.get_data();
2652 while (page_contents->words.get_data() != g) {
2653 page_contents->words.move_left();
2657 } while ((! page_contents->words.is_equal_to_head()) &&
2658 ((n->minv == current_vpos) || (l->maxh == right_margin_indent)));
2659 while (page_contents->words.get_data() != g) {
2660 page_contents->words.move_left();
2666 int html_printer::processed_header (text_glob *g)
2668 if ((guess_on) && (g->minh == left_margin_indent) && (! using_table_for_indent()) &&
2678 int is_punctuation (char *s, int length)
2680 return( (length == 1) &&
2681 ((s[0] == '(') || (s[0] == ')') || (s[0] == '!') || (s[0] == '.') || (s[0] == '[') ||
2682 (s[0] == ']') || (s[0] == '?') || (s[0] == ',') || (s[0] == ';') || (s[0] == ':') ||
2683 (s[0] == '@') || (s[0] == '#') || (s[0] == '$') || (s[0] == '%') || (s[0] == '^') ||
2684 (s[0] == '&') || (s[0] == '*') || (s[0] == '+') || (s[0] == '-') || (s[0] == '=') ||
2685 (s[0] == '{') || (s[0] == '}') || (s[0] == '|') || (s[0] == '\"') || (s[0] == '\''))
2690 * move_horizontal - moves right into the position, g->minh.
2693 void html_printer::move_horizontal (text_glob *g, int left_margin)
2695 if (g->text_style.f != 0) {
2696 int w = g->text_style.f->get_space_width(g->text_style.point_size);
2699 fatal("space width is zero");
2701 if ((output_hpos == left_margin) && (g->minh > output_hpos)) {
2702 make_html_indent(g->minh-output_hpos);
2704 emit_space(g, FALSE);
2706 output_hpos = g->maxh;
2707 output_vpos = g->minv;
2709 change_font(g, TRUE);
2713 int html_printer::looks_like_subscript (text_glob *g)
2715 return(((output_vpos < g->minv) && (output_style.point_size != 0) &&
2716 (output_style.point_size > g->text_style.point_size)));
2720 int html_printer::looks_like_superscript (text_glob *g)
2722 return(((output_vpos > g->minv) && (output_style.point_size != 0) &&
2723 (output_style.point_size > g->text_style.point_size)));
2727 * pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob.
2728 * Note that it believes a single word spanning the left..right as being
2729 * on a different line.
2732 int html_printer::pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin)
2734 return( auto_on && (right_margin == output_hpos) && (left_margin == g->minh) &&
2735 (right_margin != g->maxh) && ((! is_whole_line_bold(g)) || (g->text_style.f == output_style.f)) );
2738 int html_printer::is_on_same_line (text_glob *g, int vpos)
2742 is_intersection(vpos, vpos+g->text_style.point_size*font::res/72-1, g->minv, g->maxv)
2748 * make_html_indent - creates a relative indentation.
2751 void html_printer::make_html_indent (int indent)
2755 html.put_string("<span style=\" text-indent: ");
2756 html.put_float(((double)(indent)/((double)r)));
2757 html.put_string("in;\"></span>");
2761 * using_table_for_indent - returns TRUE if we currently using a table for indentation
2765 int html_printer::using_table_for_indent (void)
2767 return( indentation.no_of_columns != 0 );
2771 * calculate_min_gap - returns the minimum gap by which we deduce columns.
2772 * This is a rough heuristic.
2775 int html_printer::calculate_min_gap (text_glob *g)
2777 return( g->text_style.f->get_space_width(g->text_style.point_size)*GAP_SPACES );
2781 * collect_columns - place html text in a column and return the vertical limit reached.
2784 int html_printer::collect_columns (struct text_defn *line, struct text_defn *last, int max_words)
2786 text_glob *start = page_contents->words.get_data();
2787 text_glob *t = start;
2788 int upper_limit = 0;
2793 int graphic_limit = end_region_vpos;
2795 if (is_whole_line_bold(t) && (t->minh == left_margin_indent)) {
2796 // found header therefore terminate indentation table
2797 upper_limit = -t->minv; // so we know a header has stopped the column
2802 int mingap =calculate_min_gap(start);
2804 while ((t != 0) && (is_on_same_line(t, start->minv) && (i<max_words)) &&
2805 ((graphic_limit == -1) || (graphic_limit > t->minv))) {
2806 while ((last != 0) && (j<max_words) && (last[j].left != 0) && (last[j].left < t->minh)) {
2809 // t->minh might equal t->maxh when we are passing a special device character via \X
2810 // we currently ignore these when considering tables
2811 if (((t->minh - prevh >= mingap) || ((last != 0) && (last[j].left != 0) && (t->minh == last[j].left))) &&
2812 (t->minh != t->maxh)) {
2813 line[i].left = t->minh;
2814 line[i].right = t->maxh;
2817 line[i-1].right = t->maxh;
2820 // and record the vertical upper limit
2821 upper_limit = max(t->minv, upper_limit);
2824 page_contents->words.move_right();
2825 t = page_contents->words.get_data();
2826 if (page_contents->words.is_equal_to_head()) {
2837 return( upper_limit );
2841 * conflict_with_words - returns TRUE if a word sequence crosses a column.
2844 int html_printer::conflict_with_words (struct text_defn *column_guess, struct text_defn *words)
2849 while ((column_guess[i].left != 0) && (i<MAX_WORDS_PER_LINE)) {
2851 while ((words[j].left != 0) && (j<MAX_WORDS_PER_LINE)) {
2852 if ((words[j].left <= column_guess[i].right) && (i+1<MAX_WORDS_PER_LINE) &&
2853 (column_guess[i+1].left != 0) && (words[j].right >= column_guess[i+1].left)) {
2854 if (debug_table_on) {
2855 fprintf(stderr, "is a conflict with words\n");
2864 if (debug_table_on) {
2865 fprintf(stderr, "is NOT a conflict with words\n");
2872 * combine_line - combines dest and src.
2875 void html_printer::combine_line (struct text_defn *dest, struct text_defn *src)
2879 for (i=0; (i<MAX_WORDS_PER_LINE) && (src[i].left != 0); i++) {
2880 include_into_list(dest, &src[i]);
2882 remove_redundant_columns(dest);
2886 * remove_entry_in_line - removes an entry, j, in, line.
2889 void html_printer::remove_entry_in_line (struct text_defn *line, int j)
2891 while (line[j].left != 0) {
2892 line[j].left = line[j+1].left;
2893 line[j].right = line[j+1].right;
2899 * remove_redundant_columns - searches through the array columns and removes any redundant entries.
2902 void html_printer::remove_redundant_columns (struct text_defn *line)
2907 while (line[i].left != 0) {
2908 if ((i<MAX_WORDS_PER_LINE) && (line[i+1].left != 0)) {
2910 while ((j<MAX_WORDS_PER_LINE) && (line[j].left != 0)) {
2911 if ((j != i) && (is_intersection(line[i].left, line[i].right, line[j].left, line[j].right))) {
2912 line[i].left = min(line[i].left , line[j].left);
2913 line[i].right = max(line[i].right, line[j].right);
2914 remove_entry_in_line(line, j);
2925 * include_into_list - performs an order set inclusion
2928 void html_printer::include_into_list (struct text_defn *line, struct text_defn *item)
2932 while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0) && (line[i].left<item->left)) {
2936 if (line[i].left == 0) {
2938 if (i<MAX_WORDS_PER_LINE) {
2939 if ((i>0) && (line[i-1].left > item->left)) {
2940 fatal("insertion error");
2942 line[i].left = item->left;
2943 line[i].right = item->right;
2949 if (line[i].left == item->left) {
2950 line[i].right = max(item->right, line[i].right);
2953 int left = item->left;
2954 int right = item->right;
2955 int l = line[i].left;
2956 int r = line[i].right;
2958 while ((i+1<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
2959 line[i].left = left;
2960 line[i].right = right;
2967 if (i+1<MAX_WORDS_PER_LINE) {
2968 line[i].left = left;
2969 line[i].right = right;
2971 line[i+1].right = 0;
2978 * is_in_column - return TRUE if value is present in line.
2981 int html_printer::is_in_column (struct text_defn *line, struct text_defn *item, int max_words)
2985 while ((i<max_words) && (line[i].left != 0)) {
2986 if (line[i].left == item->left) {
2996 * calculate_right - calculate the right most margin for each column in line.
2999 void html_printer::calculate_right (struct text_defn *line, int max_words)
3003 while ((i<max_words) && (line[i].left != 0)) {
3005 line[i-1].right = line[i].left;
3012 * add_right_full_width - adds an extra column to the right to bring the table up to
3016 void html_printer::add_right_full_width (struct text_defn *line, int mingap)
3020 while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3024 if ((i>0) && (line[i-1].right != right_margin_indent) && (i+1<MAX_WORDS_PER_LINE)) {
3025 line[i].left = min(line[i-1].right+mingap, right_margin_indent);
3026 line[i].right = right_margin_indent;
3028 if (i<MAX_WORDS_PER_LINE) {
3036 * determine_right_most_column - works out the right most limit of the right most column.
3037 * Required as we might be performing a .2C and only
3038 * have enough text to fill the left column.
3041 void html_printer::determine_right_most_column (struct text_defn *line, int max_words)
3045 while ((i<max_words) && (line[i].left != 0)) {
3049 // remember right_margin_indent is the right most position for this page
3050 line[i-1].right = column_calculate_right_margin(line[i-1].left, right_margin_indent);
3055 * is_column_match - returns TRUE if a word is aligned in the same horizontal alignment
3056 * between two lines, line1 and line2. If so then this horizontal
3057 * position is saved in match.
3060 int html_printer::is_column_match (struct text_defn *match,
3061 struct text_defn *line1, struct text_defn *line2, int max_words)
3066 int first=(match[0].left==0);
3071 t.left = left_margin_indent;
3074 include_into_list(match, &t);
3076 while ((line1[i].left != 0) && (line2[i].left != 0)) {
3077 if (line1[i].left == line2[j].left) {
3078 // same horizontal alignment found
3079 include_into_list(match, &line1[i]);
3083 } else if (line1[i].left < line2[j].left) {
3089 calculate_right(match, max_words);
3095 * remove_white_using_words - remove white space in, last_guess, by examining, next_line
3096 * placing results into next_guess.
3097 * It returns TRUE if the same columns exist in next_guess and last_guess
3098 * we do allow columns to shrink but if a column disappears then we return FALSE.
3101 int html_printer::remove_white_using_words (struct text_defn *next_guess,
3102 struct text_defn *last_guess, struct text_defn *next_line)
3109 while ((last_guess[j].left != 0) && (next_line[k].left != 0)) {
3110 if (last_guess[j].left == next_line[k].left) {
3111 // same horizontal alignment found
3112 next_guess[i].left = last_guess[j].left;
3113 next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3117 if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].left != 0)) {
3120 } else if (last_guess[j].right < next_line[k].left) {
3121 next_guess[i].left = last_guess[j].left;
3122 next_guess[i].right = last_guess[j].right;
3125 } else if (last_guess[j].left > next_line[k].right) {
3126 // insert a word sequence from next_line[k]
3127 next_guess[i].left = next_line[k].left;
3128 next_guess[i].right = next_line[k].right;
3131 } else if (is_intersection(last_guess[j].left, last_guess[j].right, next_line[k].left, next_line[k].right)) {
3132 // potential for a column disappearing
3133 next_guess[i].left = min(last_guess[j].left , next_line[k].left);
3134 next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3138 if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].left != 0)) {
3143 if (i<MAX_WORDS_PER_LINE) {
3144 next_guess[i].left = 0;
3145 next_guess[i].right = 0;
3147 if (debug_table_on) {
3149 fprintf(stderr, "have removed column\n");
3151 fprintf(stderr, "have NOT removed column\n");
3155 remove_redundant_columns(next_guess);
3160 * count_columns - returns the number of elements inside, line.
3163 int html_printer::count_columns (struct text_defn *line)
3167 while (line[i].left != 0) {
3174 * rewind_text_to - moves backwards until page_contents is looking at, g.
3177 void html_printer::rewind_text_to (text_glob *g)
3179 while (page_contents->words.get_data() != g) {
3180 if (page_contents->words.is_equal_to_head()) {
3181 page_contents->words.start_from_tail();
3183 page_contents->words.move_left();
3189 * display_columns - a long overdue debugging function, as this column code is causing me grief :-(
3192 void html_printer::display_columns (const char *word, const char *name, text_defn *line)
3196 fprintf(stderr, "[%s:%s]", name, word);
3197 while (line[i].left != 0) {
3198 fprintf(stderr, " <left=%d right=%d> ", line[i].left, line[i].right);
3201 fprintf(stderr, "\n");
3206 * copy_line - dest = src
3209 int html_printer::copy_line (struct text_defn *dest, struct text_defn *src)
3213 for (k=0; ((src[k].left != 0) && (k<MAX_WORDS_PER_LINE)); k++) {
3214 dest[k].left = src[k].left;
3215 dest[k].right = src[k].right;
3217 if (k<MAX_WORDS_PER_LINE) {
3224 * add_column_gaps - adds empty columns between columns which don't exactly align
3227 void html_printer::add_column_gaps (struct text_defn *line)
3232 // firstly lets see whether we need an initial column on the left hand side
3233 if ((line[0].left != left_margin_indent) && (line[0].left != 0) &&
3234 (left_margin_indent < line[0].left) && (is_worth_column(left_margin_indent, line[0].left))) {
3235 t.left = left_margin_indent;
3236 t.right = line[0].left;
3237 include_into_list(line, &t);
3240 while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3241 if ((i+1<MAX_WORDS_PER_LINE) && (line[i+1].left != 0) && (line[i].right != line[i+1].left) &&
3242 (is_worth_column(line[i].right, line[i+1].left))) {
3243 t.left = line[i].right;
3244 t.right = line[i+1].left;
3245 include_into_list(line, &t);
3251 // lastly lets see whether we need a final column on the right hand side
3252 if ((i>0) && (line[i-1].right != right_margin_indent) &&
3253 (is_worth_column(line[i-1].right, right_margin_indent))) {
3254 t.left = line[i-1].right;
3255 t.right = right_margin_indent;
3256 include_into_list(line, &t);
3261 * is_continueous_column - returns TRUE if a line has a word on one
3262 * of the last_col right most boundaries.
3265 int html_printer::is_continueous_column (text_defn *last_col, text_defn *next_line)
3267 int w = count_columns(next_line);
3268 int c = count_columns(last_col);
3271 for (i=0; i<c; i++) {
3272 for (j=0; j<w; j++) {
3273 if (last_col[i].right == next_line[j].right) {
3282 * is_exact_left - returns TRUE if a line has a word on one
3283 * of the last_col left most boundaries.
3286 int html_printer::is_exact_left (text_defn *last_col, text_defn *next_line)
3288 int w = count_columns(next_line);
3289 int c = count_columns(last_col);
3292 for (i=0; i<c; i++) {
3293 for (j=0; j<w; j++) {
3294 if ((last_col[i].left == next_line[j].left) ||
3295 (last_col[i].left != left_margin_indent)) {
3304 * continue_searching_column - decides whether we should carry on searching text for a column.
3307 int html_printer::continue_searching_column (text_defn *next_col,
3308 text_defn *last_col,
3309 text_defn *all_words)
3311 int count = count_columns(next_col);
3312 int words = count_columns(all_words);
3314 if ((words == 0) || ((words == 1) &&
3315 (all_words[0].left == left_margin_indent) &&
3316 (all_words[0].right == right_margin_indent))) {
3317 // no point as we have now seen a full line of contineous text
3320 return( (count == count_columns(last_col)) &&
3321 (last_col[0].left != left_margin_indent) || (last_col[0].right != right_margin_indent) );
3325 * is_worth_column - returns TRUE if the size of this column is worth defining.
3328 int html_printer::is_worth_column (int left, int right)
3331 return( abs(right-left) >= MIN_COLUMN );
3337 * large_enough_gap - returns TRUE if a large enough gap for one line was seen.
3338 * We need to make sure that a single line definitely warrents
3340 * It also removes other smaller gaps.
3343 int html_printer::large_enough_gap (text_defn *last_col)
3348 int gap=r/GAP_WIDTH_ONE_LINE;
3350 if (abs(last_col[i].left - left_margin_indent) >= gap) {
3353 while ((last_col[i].left != 0) && (last_col[i+1].left != 0)) {
3354 if (abs(last_col[i+1].left-last_col[i].right) >= gap) {
3358 // not good enough for a single line, remove it
3360 last_col[i-1].right = last_col[i].right;
3362 remove_entry_in_line(last_col, i);
3369 * is_subset_of_columns - returns TRUE if line, a, is a subset of line, b.
3372 int html_printer::is_subset_of_columns (text_defn *a, text_defn *b)
3378 while ((i<MAX_WORDS_PER_LINE) && (a[i].left != 0)) {
3380 while ((j<MAX_WORDS_PER_LINE) && (b[j].left != 0) &&
3381 ((b[j].left != a[i].left) || (b[j].right != a[i].right))) {
3384 if ((j==MAX_WORDS_PER_LINE) || (b[j].left == 0)) {
3385 // found a different column - not a subset
3394 * count_hits - counts the number of hits per column. A hit is when the
3395 * left hand position of a glob hits the left hand column.
3398 void html_printer::count_hits (text_defn *col)
3401 text_glob *start = page_contents->words.get_data();
3402 text_glob *g = start;
3404 int gap = r/GAP_WIDTH_ONE_LINE;
3405 int n = count_columns(col);
3408 // firstly reset the used field
3409 for (i=0; i<n; i++) {
3412 // now calculate the left hand hits
3413 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3415 while ((col[i].left < g->minh) && (col[i].left != 0)) {
3418 if ((col[i].left == g->minh) && (col[i].left != 0)) {
3421 page_contents->words.move_right();
3422 if (page_contents->words.is_equal_to_head()) {
3424 page_contents->words.start_from_tail();
3426 g=page_contents->words.get_data();
3429 // now remove any column which is less than the
3430 // minimal gap for one hit.
3431 // column 0 is excempt
3435 while (i<count_columns(col)) {
3436 if (col[i].is_used == 1) {
3437 if (col[i].left - left < gap) {
3438 col[i-1].right = col[i].right;
3439 remove_entry_in_line(col, i);
3453 * found_use_for_table - checks whether the some words on one line directly match
3454 * the horizontal alignment of the line below.
3457 int html_printer::found_use_for_table (text_glob *start)
3460 struct text_defn all_words [MAX_WORDS_PER_LINE];
3461 struct text_defn last_raw [MAX_WORDS_PER_LINE];
3462 struct text_defn next_line [MAX_WORDS_PER_LINE];
3463 struct text_defn prev_guess[MAX_WORDS_PER_LINE];
3464 struct text_defn last_guess[MAX_WORDS_PER_LINE];
3465 struct text_defn next_guess[MAX_WORDS_PER_LINE];
3468 int mingap=calculate_min_gap(start);
3472 if (strcmp(start->text_string, "man") == 0) {
3477 // get first set of potential columns into line1
3478 limit = collect_columns(last_guess, 0, MAX_WORDS_PER_LINE);
3479 copy_line(last_raw, last_guess);
3480 // add_right_full_width(last_guess, mingap); // adds extra right column to bring table to full width
3482 copy_line(all_words, last_guess);
3483 indentation.vertical_limit = limit;
3485 if (page_contents->words.is_equal_to_head() || (limit == 0)) {
3486 next_line[0].left = 0;
3487 next_line[0].right = 0;
3489 // and get the next line for finding columns
3490 limit = collect_columns(next_line, last_guess, MAX_WORDS_PER_LINE);
3494 // now check to see whether the first line looks like a single centered line
3496 if (single_centered_line(last_raw, next_line, start)) {
3497 rewind_text_to(start);
3498 write_centered_line(start);
3499 indentation.no_of_columns = 0; // center instead
3501 } else if (! table_on) {
3502 rewind_text_to(start);
3506 combine_line(all_words, next_line);
3507 if (debug_table_on) {
3508 display_columns(start->text_string, "[b] all_words", all_words);
3511 if ((! remove_white_using_words(next_guess, last_guess, next_line))) {
3514 if ((! conflict_with_words(next_guess, all_words)) &&
3515 (continue_searching_column(next_guess, next_guess, all_words)) &&
3516 (! page_contents->words.is_equal_to_head()) &&
3517 ((end_region_vpos < 0) || (limit < end_region_vpos)) &&
3520 combine_line(last_guess, next_line);
3521 // subtract any columns which are bridged by a sequence of words
3523 copy_line(prev_guess, next_guess);
3524 combine_line(last_guess, next_guess);
3526 if (debug_table_on) {
3527 t = page_contents->words.get_data();
3528 display_columns(t->text_string, "[l] last_guess", last_guess);
3530 indentation.vertical_limit = limit;
3532 copy_line(last_raw, next_line);
3533 if (page_contents->words.is_equal_to_head()) {
3534 next_line[0].left = 0;
3535 next_line[0].right = 0;
3537 limit = collect_columns(next_line, last_guess, MAX_WORDS_PER_LINE);
3541 combine_line(all_words, next_line);
3542 if (debug_table_on) {
3543 display_columns(t->text_string, "[l] all_words", all_words);
3544 display_columns(t->text_string, "[l] last_raw ", last_raw);
3547 if (debug_table_on) {
3548 display_columns(t->text_string, "[l] next_line", next_line);
3550 t = page_contents->words.get_data();
3552 if (strcmp(t->text_string, "market,") == 0) {
3557 } while ((! remove_white_using_words(next_guess, last_guess, next_line)) &&
3558 (! conflict_with_words(next_guess, all_words)) &&
3559 (continue_searching_column(next_guess, last_guess, all_words)) &&
3560 ((is_continueous_column(prev_guess, last_raw)) || (is_exact_left(last_guess, next_line))) &&
3561 (! page_contents->words.is_equal_to_head()) &&
3562 ((end_region_vpos <= 0) || (t->minv < end_region_vpos)) &&
3568 indentation.vertical_limit = limit;
3571 if (page_contents->words.is_equal_to_head()) {
3572 // end of page check whether we should include everything
3573 if ((! conflict_with_words(next_guess, all_words)) &&
3574 (continue_searching_column(next_guess, last_guess, all_words)) &&
3575 ((is_continueous_column(prev_guess, last_raw)) || (is_exact_left(last_guess, next_line)))) {
3576 // end of page reached - therefore include everything
3577 page_contents->words.start_from_tail();
3578 t = page_contents->words.get_data();
3579 indentation.vertical_limit = t->minv;
3582 t = page_contents->words.get_data();
3583 if ((end_region_vpos > 0) && (t->minv > end_region_vpos)) {
3584 indentation.vertical_limit = min(indentation.vertical_limit, end_region_vpos+1);
3585 } else if (indentation.vertical_limit < 0) {
3586 // -1 as we don't want to include section heading itself
3587 indentation.vertical_limit = -indentation.vertical_limit-1;
3591 if (debug_table_on) {
3592 display_columns(start->text_string, "[x] last_guess", last_guess);
3594 rewind_text_to(start);
3596 i = count_columns(last_guess);
3597 if (((lines > 2) && ((i>1) || (continue_searching_column(last_guess, last_guess, all_words)))) ||
3598 ((lines == 1) && (large_enough_gap(last_guess)))) {
3599 // copy match into permenant html_table
3601 if (indentation.columns != 0) {
3602 free(indentation.columns);
3604 if (debug_table_on) {
3605 display_columns(start->text_string, "[x] last_guess", last_guess);
3607 add_column_gaps(last_guess);
3608 if (debug_table_on) {
3609 display_columns(start->text_string, "[g] last_guess", last_guess);
3612 indentation.no_of_columns = count_columns(last_guess);
3613 indentation.columns = (struct text_defn *)malloc(indentation.no_of_columns*sizeof(struct text_defn));
3616 while (i<indentation.no_of_columns) {
3617 indentation.columns[i].left = last_guess[i].left;
3618 indentation.columns[i].right = last_guess[i].right;
3627 void html_printer::define_cell (int left, int right)
3629 float f=((float)(right-left))/((float)(right_margin_indent-left_margin_indent))*100.0;
3631 html.put_string("<td valign=\"top\" align=\"left\" width=\"");
3635 html.put_float(1.0);
3637 html.put_string("%\">\n");
3641 * column_display_word - given a left, right pair and the indentation.vertical_limit
3642 * write out html text within this region.
3645 void html_printer::column_display_word (int vert, int left, int right, int next)
3647 text_glob *g=page_contents->words.get_data();
3650 define_cell(left, next);
3651 begin_paragraph_no_height(left_alignment);
3652 while ((g != 0) && (g->minv <= vert)) {
3653 if ((left <= g->minh) && (g->minh<right)) {
3654 char *postword=html_position_text(g, left, right);
3656 if (header.written_header) {
3657 fatal("should never generate a header inside a table");
3659 if (g->is_raw_command) {
3660 html.put_string((char *)g->text_string);
3662 html.html_write_string((char *)g->text_string);
3664 if (postword != 0) {
3665 html.put_string(postword);
3667 issued_newline = FALSE;
3670 if (page_contents->words.is_equal_to_tail()) {
3673 page_contents->words.move_right();
3674 g=page_contents->words.get_data();
3677 if (page_contents->words.is_equal_to_head()) {
3679 page_contents->words.start_from_tail();
3686 html.put_string("</td>\n");
3688 page_contents->words.move_left();
3689 // and correct output_vpos
3690 g=page_contents->words.get_data();
3691 output_vpos = g->minv;
3697 * start_table - creates a table according with parameters contained within class html_table.
3700 void html_printer::start_table (void)
3705 html.put_string("\n<table width=\"100%\" rules=\"none\" frame=\"none\" cols=\"");
3706 html.put_number(indentation.no_of_columns);
3707 html.put_string("\">\n");
3711 * end_table - finishes off a table.
3714 void html_printer::end_table (void)
3716 html.put_string("</table>\n");
3717 indentation.no_of_columns = 0;
3721 * column_calculate_right_margin - scan through the column and find the right most margin
3724 int html_printer::column_calculate_right_margin (int left, int right)
3726 if (left == right) {
3731 text_glob *start = page_contents->words.get_data();
3732 text_glob *g = start;
3734 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3735 if ((left <= g->minh) && (g->minh<right)) {
3737 fprintf(stderr, "right word = %s %d\n", g->text_string, g->maxh); fflush(stderr);
3739 if (g->maxh == rightmost) {
3741 } else if (g->maxh > rightmost) {
3743 rightmost = g->maxh;
3745 if (g->maxh > right) {
3747 fprintf(stderr, "problem as right word = %s %d [%d..%d]\n",
3748 g->text_string, right, g->minh, g->maxh); fflush(stderr);
3753 page_contents->words.move_right();
3754 if (page_contents->words.is_equal_to_head()) {
3756 page_contents->words.start_from_tail();
3758 g=page_contents->words.get_data();
3761 rewind_text_to(start);
3762 if (rightmost == -1) {
3763 return( right ); // no words in this column
3766 return( rightmost+1 );
3768 return( rightmost );
3775 * column_calculate_left_margin - scan through the column and find the left most margin
3778 int html_printer::column_calculate_left_margin (int left, int right)
3780 if (left == right) {
3784 text_glob *start = page_contents->words.get_data();
3785 text_glob *g = start;
3787 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3788 if ((left <= g->minh) && (g->minh<right)) {
3789 leftmost = min(g->minh, leftmost);
3791 page_contents->words.move_right();
3792 if (page_contents->words.is_equal_to_head()) {
3794 page_contents->words.start_from_tail();
3796 g=page_contents->words.get_data();
3799 rewind_text_to(start);
3800 if (leftmost == right) {
3801 return( left ); // no words in this column
3809 * find_column_index - returns the index to the column in which glob, t, exists.
3812 int html_printer::find_column_index (text_glob *t)
3816 while ((i<indentation.no_of_columns) &&
3817 (! ((indentation.columns[i].left<=t->minh) &&
3818 (indentation.columns[i].right>t->minh)))) {
3825 * determine_row_limit - checks each row to see if there is a gap in a cell.
3826 * We return the vertical position after the empty cell
3827 * at the start of the next line.
3830 int html_printer::determine_row_limit (text_glob *start, int v)
3834 int vpos, prev, last;
3835 int is_gap[MAX_WORDS_PER_LINE];
3837 if (v >= indentation.vertical_limit) {
3840 // initially we start with all gaps in our table
3841 // after a gap we start a new row
3842 // here we set the gap array to the previous line
3845 t = page_contents->words.get_data();
3848 page_contents->words.move_right();
3849 t = page_contents->words.get_data();
3850 } while ((! page_contents->words.is_equal_to_head()) &&
3854 if (! page_contents->words.is_equal_to_head()) {
3855 page_contents->words.move_left();
3857 t = page_contents->words.get_data();
3859 for (i=0; i<indentation.no_of_columns; i++) {
3863 if (! page_contents->words.is_equal_to_tail()) {
3864 page_contents->words.move_right();
3866 t = page_contents->words.get_data();
3869 // now check each row for a gap
3873 i = find_column_index(t);
3874 if (! is_on_same_line(t, last)) {
3878 if ((is_gap[i] != vpos) && (is_gap[i] != prev) &&
3879 (indentation.columns[i].is_used)) {
3880 // no word on previous line - must be a gap - force alignment of row
3881 rewind_text_to(start);
3885 page_contents->words.move_right();
3886 t = page_contents->words.get_data();
3887 } while ((! page_contents->words.is_equal_to_head()) &&
3888 (vpos < indentation.vertical_limit) && (vpos >= last));
3889 page_contents->words.move_left();
3890 t = page_contents->words.get_data();
3891 rewind_text_to(start);
3892 return( indentation.vertical_limit );
3897 * assign_used_columns - sets the is_used field of the column array of records.
3900 void html_printer::assign_used_columns (text_glob *start)
3902 text_glob *t = start;
3905 for (i=0; i<indentation.no_of_columns; i++) {
3906 indentation.columns[i].is_used = FALSE;
3909 rewind_text_to(start);
3910 if (! page_contents->words.is_empty()) {
3912 i = find_column_index(t);
3913 if (indentation.columns[i].left != 0) {
3914 if (debug_table_on) {
3915 fprintf(stderr, "[%s] in column %d at %d..%d limit %d\n", t->text_string,
3916 i, t->minv, t->maxv, indentation.vertical_limit); fflush(stderr);
3918 indentation.columns[i].is_used = TRUE;
3920 page_contents->words.move_right();
3921 t = page_contents->words.get_data();
3922 } while ((t->minv<indentation.vertical_limit) &&
3923 (! page_contents->words.is_equal_to_head()));
3925 if (debug_table_on) {
3926 for (i=0; i<indentation.no_of_columns; i++) {
3927 fprintf(stderr, " <left=%d right=%d is_used=%d> ",
3928 indentation.columns[i].left,
3929 indentation.columns[i].right,
3930 indentation.columns[i].is_used);
3932 fprintf(stderr, "\n");
3938 * foreach_column_include_text - foreach column in a table place the
3939 * appropriate html text.
3942 void html_printer::foreach_column_include_text (text_glob *start)
3944 if (indentation.no_of_columns>0) {
3949 assign_used_columns(start);
3951 rewind_text_to(start);
3954 limit = determine_row_limit(start, limit); // find the bottom of the next row
3955 html.put_string("<tr valign=\"top\" align=\"left\">\n");
3957 start = page_contents->words.get_data();
3958 while (i<indentation.no_of_columns) {
3959 // reset the output position to the start of column
3960 rewind_text_to(start);
3961 output_vpos = start->minv;
3962 output_hpos = indentation.columns[i].left;
3963 // and display each column until limit
3964 right = column_calculate_right_margin(indentation.columns[i].left,
3965 indentation.columns[i].right);
3966 left = column_calculate_left_margin(indentation.columns[i].left,
3967 indentation.columns[i].right);
3969 if (right>indentation.columns[i].right) {
3971 fprintf(stderr, "assert calculated right column edge is greater than column\n"); fflush(stderr);
3976 if (left<indentation.columns[i].left) {
3978 fprintf(stderr, "assert calculated left column edge is less than column\n"); fflush(stderr);
3983 column_display_word(limit, left, right, indentation.columns[i].right);
3987 if (page_contents->words.is_equal_to_tail()) {
3990 page_contents->words.sub_move_right();
3991 if (page_contents->words.is_empty()) {
3994 start = page_contents->words.get_data();
3998 html.put_string("</tr>\n");
3999 } while ((limit < indentation.vertical_limit) && (start != 0) &&
4000 (! page_contents->words.is_empty()));
4004 // finished page remove all words
4005 page_contents->words.start_from_head();
4006 while (! page_contents->words.is_empty()) {
4007 page_contents->words.sub_move_right();
4009 } else if (! page_contents->words.is_empty()) {
4010 page_contents->words.move_left();
4016 * write_centered_line - generates a line of centered text.
4019 void html_printer::write_centered_line (text_glob *g)
4021 int current_vpos=g->minv;
4023 move_vertical(g, center_alignment);
4025 header.written_header = FALSE;
4026 output_vpos = g->minv;
4027 output_hpos = g->minh;
4029 char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
4031 if (! header.written_header) {
4032 if (g->is_raw_command) {
4033 html.put_string((char *)g->text_string);
4035 html.html_write_string((char *)g->text_string);
4037 if (postword != 0) {
4038 html.put_string(postword);
4040 need_one_newline = TRUE;
4041 issued_newline = FALSE;
4043 page_contents->words.move_right();
4044 g = page_contents->words.get_data();
4045 } while ((! page_contents->words.is_equal_to_head()) && (g->minv == current_vpos));
4046 page_contents->words.move_left(); // so when we move right we land on the word following this centered line
4047 need_one_newline = TRUE;
4051 * is_in_middle - returns TRUE if the text defn, t, is in the middle of the page.
4054 int html_printer::is_in_middle (int left, int right)
4056 return( abs(abs(left-left_margin_indent) - abs(right_margin_indent-right)) <= CENTER_TOLERANCE );
4060 * single_centered_line - returns TRUE if first is a centered line with a different
4064 int html_printer::single_centered_line (text_defn *first, text_defn *second, text_glob *g)
4067 ((count_columns(first) == 1) && (first[0].left != left_margin_indent) &&
4068 (first[0].left != second[0].left) && is_in_middle(first->left, first->right))
4073 * check_able_to_use_center - returns TRUE if we can see a centered line.
4076 int html_printer::check_able_to_use_center (text_glob *g)
4078 if (auto_on && table_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
4079 // we are allowed to check for centered line
4080 // first check to see whether we might be looking at a set of columns
4081 struct text_defn last_guess[MAX_WORDS_PER_LINE];
4082 int limit = collect_columns(last_guess, 0, MAX_WORDS_PER_LINE);
4085 if ((count_columns(last_guess) == 1) && (is_in_middle(last_guess[0].left, last_guess[0].right))) {
4086 write_centered_line(g);
4094 * check_able_to_use_table - examines forthcoming text to see whether we can
4095 * better format it by using an html transparent table.
4098 int html_printer::check_able_to_use_table (text_glob *g)
4100 if (auto_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
4101 // we are allowed to check for table
4103 if ((output_hpos != right_margin_indent) && (found_use_for_table(g))) {
4104 foreach_column_include_text(g);
4112 * move_vertical - if we are using html auto formatting then decide whether to
4113 * break the line via a <br> or a </p><p> sequence.
4116 void html_printer::move_vertical (text_glob *g, paragraph_type p)
4119 int height = (g->text_style.point_size+2)*r/72; // --fixme-- we always assume VS is PS+2 (could do better)
4123 if ((more_than_line_break(output_vpos, g->minv, height)) || (p != para_type)) {
4130 if (output_vpos == -1) {
4131 temp_vpos = g->minv;
4133 temp_vpos = output_vpos;
4136 force_begin_paragraph();
4137 if (need_one_newline) {
4139 temp_vpos += height;
4141 need_one_newline = TRUE;
4144 while ((temp_vpos < g->minv) && (more_than_line_break(temp_vpos, g->minv, height))) {
4146 temp_vpos += height;
4152 * emit_space - emits a space within html, it checks for the font type and
4153 * will change font depending upon, g. Courier spaces are larger
4154 * than roman so we need consistancy when changing between them.
4157 void html_printer::emit_space (text_glob *g, int force_space)
4159 if (! need_paragraph) {
4160 // only generate a space if we have written a word - as html will ignore it otherwise
4161 if ((output_style != g->text_style) && (g->text_style.f != 0)) {
4162 terminate_current_font();
4164 if (force_space || (g->minh > output_hpos)) {
4165 html.put_string(" ");
4167 change_font(g, TRUE);
4172 * html_position_text - determine whether the text is subscript/superscript/normal
4176 char *html_printer::html_position_text (text_glob *g, int left_margin, int right_margin)
4181 if (strcmp(g->text_string, "increased.") == 0) {
4185 begin_paragraph(left_alignment);
4187 if ((! header.written_header) &&
4188 (is_on_same_line(g, output_vpos) ||
4189 pretend_is_on_same_line(g, left_margin, right_margin))) {
4190 // check whether the font was reset after generating an image
4192 header.written_header = FALSE;
4193 force_begin_paragraph();
4195 // check whether we need to insert white space between words on 'same' line
4196 if (pretend_is_on_same_line(g, left_margin, right_margin)) {
4197 emit_space(g, TRUE);
4200 if (output_style.f == 0) {
4201 change_font(g, TRUE);
4204 if (looks_like_subscript(g)) {
4206 g->text_style.point_size = output_style.point_size;
4207 g->minv = output_vpos; // this ensures that output_vpos doesn't alter
4208 // which allows multiple subscripted words
4209 move_horizontal(g, left_margin);
4210 html.put_string("<sub>");
4211 postword = "</sub>";
4212 } else if (looks_like_superscript(g)) {
4214 g->text_style.point_size = output_style.point_size;
4215 g->minv = output_vpos;
4217 move_horizontal(g, left_margin);
4218 html.put_string("<sup>");
4219 postword = "</sup>";
4221 move_horizontal(g, left_margin);
4224 // we have found a new line
4225 if (! header.written_header) {
4226 move_vertical(g, left_alignment);
4228 header.written_header = FALSE;
4230 if (processed_header(g)) {
4231 // we must not alter output_vpos as we have peeped at the next word
4232 // and set vpos to this - to ensure we do not generate a <br> after
4233 // a heading. (The html heading automatically generates a line break)
4234 output_hpos = left_margin;
4237 force_begin_paragraph();
4238 if (g->minh-left_margin != 0) {
4239 make_html_indent(g->minh-left_margin);
4241 change_font(g, TRUE);
4244 output_vpos = g->minv;
4245 output_hpos = g->maxh;
4250 int html_printer::html_position_region (void)
4253 int height = output_style.point_size*r/72;
4255 int is_center = FALSE;
4257 if (output_style.point_size != 0) {
4258 if (output_vpos != start_region_vpos) {
4260 // graphic starts on a different line
4261 if (output_vpos == -1) {
4262 temp_vpos = start_region_vpos;
4264 temp_vpos = output_vpos;
4268 if (need_one_newline) {
4270 temp_vpos += height;
4272 need_one_newline = TRUE;
4276 temp_vpos += height;
4279 while ((temp_vpos < start_region_vpos) &&
4280 (more_than_line_break(temp_vpos, start_region_vpos, height))) {
4282 temp_vpos += height;
4286 if (auto_on && (is_in_middle(start_region_hpos, end_region_hpos))) {
4289 if (start_region_hpos > left_margin_indent) {
4290 html.put_string("<span style=\" text-indent: ");
4291 html.put_float(((double)(start_region_hpos-left_margin_indent)/((double)r)));
4292 html.put_string("in;\"></span>");
4298 if (start_region_hpos > output_hpos) {
4299 html.put_string("<span style=\" text-indent: ");
4300 html.put_float(((double)(start_region_hpos-output_hpos)/((double)r)));
4301 html.put_string("in;\"></span>");
4306 output_vpos = start_region_vpos;
4307 output_hpos = start_region_hpos;
4308 return( is_center );
4313 * gs_x - translate and scale the x axis
4316 int html_printer::gs_x (int x)
4318 x += IMAGE_BOARDER_PIXELS/2;
4319 return((x-start_region_hpos)*postscript_res/font::res);
4324 * gs_y - translate and scale the y axis
4327 int html_printer::gs_y (int y)
4329 int yoffset=((int)(A4_PAGE_LENGTH*(double)font::res))-end_region_vpos;
4331 y += IMAGE_BOARDER_PIXELS/2;
4332 return( (y+yoffset)*postscript_res/font::res );
4336 void html_printer::troff_position_text (text_glob *g)
4338 change_font(g, FALSE);
4340 troff.put_string("V");
4341 troff.put_number(gs_y(g->maxv));
4342 troff.put_string("\n");
4344 troff.put_string("H");
4345 troff.put_number(gs_x(g->minh));
4346 troff.put_string("\n");
4349 void html_printer::troff_change_font (const char *fontname, int size, int font_no)
4351 troff.put_string("x font ");
4352 troff.put_number(font_no);
4353 troff.put_string(" ");
4354 troff.put_string(fontname);
4355 troff.put_string("\nf");
4356 troff.put_number(font_no);
4357 troff.put_string("\ns");
4358 troff.put_number(size*1000);
4359 troff.put_string("\n");
4363 void html_printer::set_style(const style &sty)
4366 const char *fontname = sty.f->get_name();
4368 fatal("no internalname specified for font");
4370 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
4374 void html_printer::end_of_line()
4380 void html_printer::html_display_word (text_glob *g)
4383 if (strcmp(g->text_string, "increased.") == 0) {
4387 if (! check_able_to_use_table(g)) {
4388 char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
4390 if (! header.written_header) {
4391 if (g->is_raw_command) {
4392 html.put_string((char *)g->text_string);
4394 html.html_write_string((char *)g->text_string);
4396 if (postword != 0) {
4397 html.put_string(postword);
4399 need_one_newline = TRUE;
4400 issued_newline = FALSE;
4405 void html_printer::troff_display_word (text_glob *g)
4407 troff_position_text(g);
4408 if (g->is_raw_command) {
4409 int l=strlen((char *)g->text_string);
4411 troff.put_string("c");
4412 troff.put_string((char *)g->text_string);
4413 troff.put_string("\n");
4415 troff.put_string("C");
4416 troff.put_translated_char((char *)g->text_string);
4417 troff.put_string("\n");
4420 troff_position_text(g);
4421 troff.put_string("t");
4422 troff.put_translated_string((const char *)g->text_string);
4423 troff.put_string("\n");
4427 void html_printer::display_word (text_glob *g, int is_to_html)
4430 html_display_word(g);
4431 } else if ((g->is_raw_command) && (g->is_html_command)) {
4432 // found a raw html command inside a graphic glob.
4433 // We should emit the command to the html device, but of course we
4434 // cannot place it correctly as we are dealing with troff words.
4435 // Remember output_vpos will refer to troff and not html.
4436 html.put_string((char *)g->text_string);
4438 troff_display_word(g);
4444 * this information may be better placed inside some of the font files
4445 * in devhtml - however one must bare in mind that we need the ability
4446 * to write out to TWO devices (image and html) and image
4447 * invokes ghostscript.
4450 simple_output &simple_output::html_write_string (const char *s)
4454 while (s[i] != (char)0) {
4457 } else if (s[i] == '>') {
4469 * display_fill - generates a troff format fill command
4472 void html_printer::display_fill (graphic_glob *g)
4474 troff.put_string("Df ") ;
4475 troff.put_number(g->fill);
4476 troff.put_string(" 0\n");
4480 * display_line - displays a line using troff format
4483 void html_printer::display_line (graphic_glob *g, int is_to_html)
4486 fatal("cannot emit lines in html");
4488 if (g->code == 'l') {
4491 troff.put_string("V");
4492 troff.put_number(gs_y(g->point[0].y));
4493 troff.put_string("\n");
4495 troff.put_string("H");
4496 troff.put_number(gs_x(g->point[0].x));
4497 troff.put_string("\n");
4501 troff.put_string("Dl ");
4502 troff.put_number((g->point[1].x-g->point[0].x)*postscript_res/font::res);
4503 troff.put_string(" ");
4504 troff.put_number((g->point[1].y-g->point[0].y)*postscript_res/font::res);
4505 troff.put_string("\n");
4506 // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y,
4507 // g->point[1].x, g->point[1].y, g->size);
4508 } else if ((g->code == 'c') || (g->code == 'C')) {
4511 int xradius = (g->maxh - g->minh) / 2;
4512 int yradius = (g->maxv - g->minv) / 2;
4513 // center of circle or elipse
4515 troff.put_string("V");
4516 troff.put_number(gs_y(g->minv+yradius));
4517 troff.put_string("\n");
4519 troff.put_string("H");
4520 troff.put_number(gs_x(g->minh));
4521 troff.put_string("\n");
4525 if (g->code == 'c') {
4526 troff.put_string("Dc ");
4528 troff.put_string("DC ");
4531 troff.put_number(xradius*2*postscript_res/font::res);
4532 troff.put_string("\n");
4534 } else if ((g->code == 'e') || (g->code == 'E')) {
4537 int xradius = (g->maxh - g->minh) / 2;
4538 int yradius = (g->maxv - g->minv) / 2;
4539 // center of elipse - this is untested
4541 troff.put_string("V");
4542 troff.put_number(gs_y(g->minv+yradius));
4543 troff.put_string("\n");
4545 troff.put_string("H");
4546 troff.put_number(gs_x(g->minh));
4547 troff.put_string("\n");
4551 if (g->code == 'e') {
4552 troff.put_string("De ");
4554 troff.put_string("DE ");
4557 troff.put_number(xradius*2*postscript_res/font::res);
4558 troff.put_string(" ");
4559 troff.put_number(yradius*2*postscript_res/font::res);
4560 troff.put_string("\n");
4561 } else if ((g->code == 'p') || (g->code == 'P')) {
4563 troff.put_string("V");
4564 troff.put_number(gs_y(g->yc));
4565 troff.put_string("\n");
4567 troff.put_string("H");
4568 troff.put_number(gs_x(g->xc));
4569 troff.put_string("\n");
4573 if (g->code == 'p') {
4574 troff.put_string("Dp");
4576 troff.put_string("DP");
4582 for (i=0; i<g->nopoints; i++) {
4583 troff.put_string(" ");
4584 troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
4585 troff.put_string(" ");
4586 troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
4590 troff.put_string("\n");
4591 } else if (g->code == 'a') {
4593 troff.put_string("V");
4594 troff.put_number(gs_y(g->yc));
4595 troff.put_string("\n");
4597 troff.put_string("H");
4598 troff.put_number(gs_x(g->xc));
4599 troff.put_string("\n");
4603 troff.put_string("Da");
4607 for (i=0; i<g->nopoints; i++) {
4608 troff.put_string(" ");
4609 troff.put_number(g->point[i].x*postscript_res/font::res);
4610 troff.put_string(" ");
4611 troff.put_number(g->point[i].y*postscript_res/font::res);
4613 troff.put_string("\n");
4614 } else if (g->code == '~') {
4616 troff.put_string("V");
4617 troff.put_number(gs_y(g->yc));
4618 troff.put_string("\n");
4620 troff.put_string("H");
4621 troff.put_number(gs_x(g->xc));
4622 troff.put_string("\n");
4626 troff.put_string("D~");
4631 for (i=0; i<g->nopoints; i++) {
4632 troff.put_string(" ");
4633 troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
4634 troff.put_string(" ");
4635 troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
4639 troff.put_string("\n");
4644 void html_printer::flush_sbuf()
4647 int r=font::res; // resolution of the device actually
4648 set_style(sbuf_style);
4650 page_contents->add(&sbuf_style, sbuf, sbuf_len,
4651 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4652 sbuf_vpos, sbuf_end_hpos);
4654 output_hpos = sbuf_end_hpos;
4655 output_vpos = sbuf_vpos;
4671 if (output_style != sbuf_style) {
4672 set_style(sbuf_style);
4673 output_style = sbuf_style;
4676 int extra_space = 0;
4677 if (output_hpos < 0 || output_vpos < 0)
4680 if (output_hpos != sbuf_start_hpos)
4681 motion = RELATIVE_H;
4682 if (output_vpos != sbuf_vpos) {
4684 motion = RELATIVE_HV;
4686 motion = RELATIVE_V;
4689 if (sbuf_space_code >= 0) {
4690 int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
4691 if (w + sbuf_kern != sbuf_space_width) {
4692 if (sbuf_space_code != output_space_code) {
4693 output_space_code = sbuf_space_code;
4696 extra_space = sbuf_space_width - w - sbuf_kern;
4697 if (sbuf_space_diff_count > sbuf_space_count/2)
4699 else if (sbuf_space_diff_count < -(sbuf_space_count/2))
4705 html.put_number(extra_space);
4707 html.put_number(sbuf_kern);
4709 html.put_string(sbuf, sbuf_len);
4712 sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
4718 html.put_number(sbuf_start_hpos)
4719 .put_number(sbuf_vpos);
4722 html.put_number(sbuf_start_hpos - output_hpos);
4725 html.put_number(sbuf_vpos - output_vpos);
4728 html.put_number(sbuf_start_hpos - output_hpos)
4729 .put_number(sbuf_vpos - output_vpos);
4735 output_hpos = sbuf_end_hpos;
4736 output_vpos = sbuf_vpos;
4742 void html_printer::set_line_thickness(const environment *env)
4744 line_thickness = env->size;
4745 printf("line thickness = %d\n", line_thickness);
4748 void html_printer::draw(int code, int *p, int np, const environment *env)
4754 page_contents->add_line(code,
4755 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1],
4758 error("2 arguments required for line");
4764 line_thickness = -1;
4766 // troff gratuitously adds an extra 0
4767 if (np != 1 && np != 2) {
4768 error("0 or 1 argument required for thickness");
4771 line_thickness = p[0];
4781 error("even number of arguments required for polygon");
4785 error("no arguments for polygon");
4788 // firstly lets add our current position to polygon
4800 // now store polygon in page
4801 page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill);
4808 error("2 arguments required for ellipse");
4811 page_contents->add_line(code,
4812 env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2,
4821 // troff adds an extra argument to C
4822 if (np != 1 && !(code == 'C' && np == 2)) {
4823 error("1 argument required for circle");
4826 page_contents->add_line(code,
4827 env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2,
4836 if (adjust_arc_center(p, c)) {
4837 page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill);
4840 page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill);
4843 error("4 arguments required for arc");
4850 error("even number of arguments required for spline");
4854 error("no arguments for spline");
4857 // firstly lets add our current position to spline
4869 page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill);
4874 if (np != 1 && np != 2) {
4875 error("1 argument required for fill");
4879 if (fill < 0 || fill > FILL_MAX) {
4880 // This means fill with the current color.
4881 fill = FILL_MAX + 1;
4887 error("unrecognised drawing command `%1'", char(code));
4893 void html_printer::begin_page(int n)
4896 html.begin_comment("Page: ").comment_arg(itoa(page_number)).end_comment();;
4897 no_of_printed_pages++;
4900 output_space_code = 32;
4901 output_draw_point_size = -1;
4902 output_line_thickness = -1;
4907 void testing (text_glob *g) {}
4909 void html_printer::flush_graphic (void)
4914 page_contents->is_in_graphic = FALSE;
4918 calculate_region_range(&g);
4920 page_contents->make_new_region(&g);
4922 move_region_to_page();
4925 void html_printer::end_page(int)
4932 font *html_printer::make_font(const char *nm)
4934 return html_font::load_html_font(nm);
4937 html_printer::~html_printer()
4939 if (fseek(tempfp, 0L, 0) < 0)
4940 fatal("fseek on temporary file failed");
4941 html.set_file(stdout);
4942 fputs("<html>\n", stdout);
4943 fputs("<head>\n", stdout);
4944 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
4946 fputs("</head>\n", stdout);
4947 fputs("<body>\n", stdout);
4949 header.write_headings(stdout);
4951 extern const char *version_string;
4952 html.begin_comment("Creator : ")
4953 .comment_arg("groff ")
4954 .comment_arg("version ")
4955 .comment_arg(version_string)
4959 #ifdef LONG_FOR_TIME_T
4965 html.begin_comment("CreationDate: ")
4966 .comment_arg(ctime(&t))
4969 for (font_pointer_list *f = font_list; f; f = f->next) {
4970 html_font *psf = (html_font *)(f->p);
4972 html.begin_comment("Total number of pages: ").comment_arg(itoa(no_of_printed_pages)).end_comment();
4974 html.copy_file(tempfp);
4975 fputs("</body>\n", stdout);
4976 fputs("</html>\n", stdout);
4982 * calculate_region_range - calculates the vertical range for words and lines
4983 * within the region lists.
4986 void html_printer::calculate_region_range (graphic_glob *r)
4991 if (! page_contents->region_lines.is_empty()) {
4992 page_contents->region_lines.start_from_head();
4994 g = page_contents->region_lines.get_data();
4995 if ((r->minv == -1) || (g->minv < r->minv)) {
4998 if ((r->maxv == -1) || (g->maxv > r->maxv)) {
5001 page_contents->region_lines.move_right();
5002 } while (! page_contents->region_lines.is_equal_to_head());
5004 if (! page_contents->region_words.is_empty()) {
5005 page_contents->region_words.start_from_head();
5007 w = page_contents->region_words.get_data();
5009 if ((r->minv == -1) || (w->minv < r->minv)) {
5012 if ((r->maxv == -1) || (w->maxv > r->maxv)) {
5015 page_contents->region_words.move_right();
5016 } while (! page_contents->region_words.is_equal_to_head());
5022 * move_region_to_page - moves lines and words held in the temporary region
5023 * list to the page list.
5026 void html_printer::move_region_to_page (void)
5031 page_contents->region_lines.start_from_head();
5032 while (! page_contents->region_lines.is_empty()) {
5033 g = page_contents->region_lines.get_data(); // remove from our temporary region list
5034 page_contents->lines.add(g); // and add to the page list
5035 page_contents->region_lines.sub_move_right();
5037 page_contents->region_words.start_from_head();
5038 while (! page_contents->region_words.is_empty()) {
5039 w = page_contents->region_words.get_data(); // remove from our temporary region list
5040 page_contents->words.add(w); // and add to the page list
5041 page_contents->region_words.sub_move_right();
5046 void html_printer::special(char *s, const environment *env)
5049 if (strcmp(s, "graphic-start") == 0) {
5051 if (graphic_level == 1) {
5052 page_contents->is_in_graphic = TRUE; // add words and lines to temporary region lists
5054 } else if ((strcmp(s, "graphic-end") == 0) && (graphic_level > 0)) {
5056 if (graphic_level == 0) {
5059 } else if (strncmp(s, "html:", 5) == 0) {
5060 int r=font::res; // resolution of the device actually
5062 page_contents->add_html_command(&sbuf_style, &s[5], strlen(s)-5,
5064 // need to pass rest of string through to html output during flush
5066 env->vpos-env->size*r/72, env->hpos,
5067 env->vpos , env->hpos);
5068 // assume that the html command has no width, if it does then we hopefully troff
5069 // will have fudged this in a macro and requested that the formatting move right by
5070 // the appropriate width
5071 } else if (strncmp(s, "index:", 6) == 0) {
5072 cutoff_heading = atoi(&s[6]);
5077 void set_image_type (char *type)
5079 if (strcmp(type, "gif") == 0) {
5081 } else if (strcmp(type, "png") == 0) {
5083 image_device = "png256";
5084 } else if (strncmp(type, "png", 3) == 0) {
5086 image_device = type;
5090 // A conforming PostScript document must not have lines longer
5091 // than 255 characters (excluding line termination characters).
5093 static int check_line_lengths(const char *p)
5096 const char *end = strchr(p, '\n');
5098 end = strchr(p, '\0');
5108 printer *make_printer()
5110 return new html_printer;
5113 static void usage();
5115 int main(int argc, char **argv)
5117 program_name = argv[0];
5118 static char stderr_buf[BUFSIZ];
5119 setbuf(stderr, stderr_buf);
5121 while ((c = getopt(argc, argv, "F:atvdgmx?I:r:")) != EOF)
5125 extern const char *version_string;
5126 fprintf(stderr, "grohtml version %s\n", version_string);
5137 font::command_line_font_dir(optarg);
5140 // user specifying the type of images we should generate
5141 set_image_type(optarg);
5144 // resolution (dots per inch for an image)
5145 image_res = atoi(optarg);
5152 debug_table_on = TRUE;
5155 // do not guess title and headings
5159 // leave margins alone
5168 if (optind >= argc) {
5171 for (int i = optind; i < argc; i++)
5180 fprintf(stderr, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n",