2 /* Copyright (C) 2000-2014 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
5 * but it owes a huge amount of ideas and raw code from
6 * James Clark (jjc@jclark.com) grops/ps.cpp.
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 3 of the License, or
15 (at your option) any later version.
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
23 along with this program. If not, see <http://www.gnu.org/licenses/>. */
26 #include "stringclass.h"
29 #include "html-text.h"
30 #include "html-table.h"
42 extern "C" const char *Version_string;
51 #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
52 #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
53 #define CENTER_TOLERANCE 2 /* how many pixels off center do we allow */
54 #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
55 #define UNICODE_DESC_START 0x80 /* all character entities above this are */
56 /* either encoded by their glyph names or if */
57 /* there is no name then we use &#nnn; */
58 typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
59 typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
62 // #define DEBUG_TABLES
68 const char *get_html_translation (font *f, const string &name);
69 static const char *get_html_entity(unsigned int code);
70 int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
73 static int auto_links = TRUE; /* by default we enable automatic links at */
74 /* top of the document. */
75 static int auto_rule = TRUE; /* by default we enable an automatic rule */
76 /* at the top and bottom of the document */
77 static int simple_anchors = FALSE; /* default to anchors with heading text */
78 static int manufacture_headings = FALSE; /* default is to use the Hn html headings, */
79 /* rather than manufacture our own. */
80 static color *default_background = NULL; /* has user requested initial bg color? */
81 static string job_name; /* if set then the output is split into */
82 /* multiple files with `job_name'-%d.html */
83 static int multiple_files = FALSE; /* must we the output be divided into */
84 /* multiple html files, one for each */
86 static int base_point_size = 0; /* which troff font size maps onto html */
88 static int split_level = 2; /* what heading level to split at? */
89 static string head_info; /* user supplied information to be placed */
90 /* into <head> </head> */
91 static int valid_flag = FALSE; /* has user requested a valid flag at the */
92 /* end of each page? */
93 static int groff_sig = FALSE; /* "This document was produced using" */
94 html_dialect dialect = html4; /* which html dialect should grohtml output */
98 * start with a few favorites
103 static int min (int a, int b)
111 static int max (int a, int b)
120 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
123 static int is_intersection (int a1, int a2, int b1, int b2)
125 // easier to prove NOT outside limits
126 return ! ((a1 > b2) || (a2 < b1));
130 * is_digit - returns TRUE if character, ch, is a digit.
133 static int is_digit (char ch)
135 return (ch >= '0') && (ch <= '9');
139 * the classes and methods for maintaining a list of files.
147 string output_file_name;
153 * file - initialize all fields to NULL
157 : fp(f), next(NULL), new_output_file(FALSE),
158 require_links(FALSE), output_file_name("")
165 FILE *get_file (void);
166 void start_of_list (void);
167 void move_next (void);
168 void add_new_file (FILE *f);
169 void set_file_name (string name);
170 void set_links_required (void);
171 int are_links_required (void);
172 int is_new_output_file (void);
173 string file_name (void);
174 string next_file_name (void);
182 * files - create an empty list of files.
186 : head(NULL), tail(NULL), ptr(NULL)
191 * get_file - returns the FILE associated with ptr.
194 FILE *files::get_file (void)
203 * start_of_list - reset the ptr to the start of the list.
206 void files::start_of_list (void)
212 * move_next - moves the ptr to the next element on the list.
215 void files::move_next (void)
222 * add_new_file - adds a new file, f, to the list.
225 void files::add_new_file (FILE *f)
231 tail->next = new file(f);
238 * set_file_name - sets the final file name to contain the html
242 void files::set_file_name (string name)
245 ptr->output_file_name = name;
246 ptr->new_output_file = TRUE;
251 * set_links_required - issue links when processing this component
255 void files::set_links_required (void)
258 ptr->require_links = TRUE;
262 * are_links_required - returns TRUE if this section of the file
263 * requires that links should be issued.
266 int files::are_links_required (void)
269 return ptr->require_links;
274 * is_new_output_file - returns TRUE if this component of the file
275 * is the start of a new output file.
278 int files::is_new_output_file (void)
281 return ptr->new_output_file;
286 * file_name - returns the name of the file.
289 string files::file_name (void)
292 return ptr->output_file_name;
297 * next_file_name - returns the name of the next file.
300 string files::next_file_name (void)
302 if (ptr != NULL && ptr->next != NULL)
303 return ptr->next->output_file_name;
308 * the class and methods for styles
319 style (font *, int, int, int, int, color);
320 int operator == (const style &) const;
321 int operator != (const style &) const;
329 style::style(font *p, int sz, int h, int sl, int no, color c)
330 : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
334 int style::operator==(const style &s) const
336 return (f == s.f && point_size == s.point_size
337 && height == s.height && slant == s.slant && col == s.col);
340 int style::operator!=(const style &s) const
342 return !(*this == s);
346 * the class and methods for retaining ascii text
356 char_block(int length);
360 char_block::char_block()
361 : buffer(NULL), used(0), next(NULL)
365 char_block::char_block(int length)
366 : used(0), next(NULL)
368 buffer = new char[max(length, char_block::SIZE)];
370 fatal("out of memory error");
373 char_block::~char_block()
383 char *add_string(const char *, unsigned int);
384 char *add_string(const string &);
390 char_buffer::char_buffer()
391 : head(NULL), tail(NULL)
395 char_buffer::~char_buffer()
397 while (head != NULL) {
398 char_block *temp = head;
404 char *char_buffer::add_string (const char *s, unsigned int length)
407 unsigned int old_used;
409 if (s == NULL || length == 0)
413 tail = new char_block(length+1);
416 if (tail->used + length+1 > char_block::SIZE) {
417 tail->next = new char_block(length+1);
422 old_used = tail->used;
424 tail->buffer[tail->used] = s[i];
430 // add terminating nul character
432 tail->buffer[tail->used] = '\0';
435 // and return start of new string
437 return &tail->buffer[old_used];
440 char *char_buffer::add_string (const string &s)
442 return add_string(s.contents(), s.length());
446 * the classes and methods for maintaining glyph positions.
451 void text_glob_html (style *s, char *str, int length,
452 int min_vertical, int min_horizontal,
453 int max_vertical, int max_horizontal);
454 void text_glob_special (style *s, char *str, int length,
455 int min_vertical, int min_horizontal,
456 int max_vertical, int max_horizontal);
457 void text_glob_line (style *s,
458 int min_vertical, int min_horizontal,
459 int max_vertical, int max_horizontal,
461 void text_glob_auto_image(style *s, char *str, int length,
462 int min_vertical, int min_horizontal,
463 int max_vertical, int max_horizontal);
464 void text_glob_tag (style *s, char *str, int length,
465 int min_vertical, int min_horizontal,
466 int max_vertical, int max_horizontal);
470 int is_a_line (void);
473 int is_auto_img (void);
482 int is_eol_ce (void);
487 int is_tab_ts (void);
488 int is_tab_te (void);
493 int get_tab_args (char *align);
495 void remember_table (html_table *t);
496 html_table *get_table (void);
499 const char *text_string;
500 unsigned int text_length;
501 int minv, minh, maxv, maxh;
502 int is_tag; // is this a .br, .sp, .tl etc
503 int is_img_auto; // image created by eqn delim
504 int is_special; // text has come via 'x X html:'
505 int is_line; // is the command a <line>?
506 int thickness; // the thickness of a line
507 html_table *tab; // table description
510 text_glob (style *s, const char *str, int length,
511 int min_vertical , int min_horizontal,
512 int max_vertical , int max_horizontal,
513 bool is_troff_command,
514 bool is_auto_image, bool is_special_command,
515 bool is_a_line , int thickness);
518 text_glob::text_glob (style *s, const char *str, int length,
519 int min_vertical, int min_horizontal,
520 int max_vertical, int max_horizontal,
521 bool is_troff_command,
522 bool is_auto_image, bool is_special_command,
523 bool is_a_line_flag, int line_thickness)
524 : text_style(*s), text_string(str), text_length(length),
525 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
526 is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
527 is_line(is_a_line_flag), thickness(line_thickness), tab(NULL)
531 text_glob::text_glob ()
532 : text_string(NULL), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
533 is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
537 text_glob::~text_glob ()
544 * text_glob_html - used to place html text into the glob buffer.
547 void text_glob::text_glob_html (style *s, char *str, int length,
548 int min_vertical , int min_horizontal,
549 int max_vertical , int max_horizontal)
551 text_glob *g = new text_glob(s, str, length,
552 min_vertical, min_horizontal, max_vertical, max_horizontal,
553 FALSE, FALSE, FALSE, FALSE, 0);
559 * text_glob_html - used to place html specials into the glob buffer.
560 * This text is essentially html commands coming through
561 * from the macro sets, with special designated sequences of
562 * characters translated into html. See add_and_encode.
565 void text_glob::text_glob_special (style *s, char *str, int length,
566 int min_vertical , int min_horizontal,
567 int max_vertical , int max_horizontal)
569 text_glob *g = new text_glob(s, str, length,
570 min_vertical, min_horizontal, max_vertical, max_horizontal,
571 FALSE, FALSE, TRUE, FALSE, 0);
577 * text_glob_line - record horizontal draw line commands.
580 void text_glob::text_glob_line (style *s,
581 int min_vertical , int min_horizontal,
582 int max_vertical , int max_horizontal,
585 text_glob *g = new text_glob(s, "", 0,
586 min_vertical, min_horizontal, max_vertical, max_horizontal,
587 FALSE, FALSE, FALSE, TRUE, thickness_value);
593 * text_glob_auto_image - record the presence of a .auto-image tag command.
594 * Used to mark that an image has been created automatically
595 * by a preprocessor and (pre-grohtml/troff) combination.
596 * Under some circumstances images may not be created.
603 * $1 over x$!recripical of x
606 * the first auto-image marker is created via .EQ/.EN pair
607 * and no image is created.
608 * The second auto-image marker occurs at $1 over x$
609 * Currently this image will not be created
610 * as the whole of the table is created as an image.
611 * (Once html tables are handled by grohtml this will change.
612 * Shortly this will be the case).
615 void text_glob::text_glob_auto_image(style *s, char *str, int length,
616 int min_vertical, int min_horizontal,
617 int max_vertical, int max_horizontal)
619 text_glob *g = new text_glob(s, str, length,
620 min_vertical, min_horizontal, max_vertical, max_horizontal,
621 TRUE, TRUE, FALSE, FALSE, 0);
627 * text_glob_tag - records a troff tag.
630 void text_glob::text_glob_tag (style *s, char *str, int length,
631 int min_vertical, int min_horizontal,
632 int max_vertical, int max_horizontal)
634 text_glob *g = new text_glob(s, str, length,
635 min_vertical, min_horizontal, max_vertical, max_horizontal,
636 TRUE, FALSE, FALSE, FALSE, 0);
642 * is_a_line - returns TRUE if glob should be converted into an <hr>
645 int text_glob::is_a_line (void)
651 * is_a_tag - returns TRUE if glob contains a troff directive.
654 int text_glob::is_a_tag (void)
660 * is_eol - returns TRUE if glob contains the tag eol
663 int text_glob::is_eol (void)
665 return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
669 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
672 int text_glob::is_eol_ce (void)
674 return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
678 * is_tl - returns TRUE if glob contains the tag .tl
681 int text_glob::is_tl (void)
683 return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
687 * is_eo_tl - returns TRUE if glob contains the tag eo.tl
690 int text_glob::is_eo_tl (void)
692 return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
696 * is_nf - returns TRUE if glob contains the tag .fi 0
699 int text_glob::is_nf (void)
701 return is_tag && (strncmp(text_string, "devtag:.fi",
702 strlen("devtag:.fi")) == 0) &&
707 * is_fi - returns TRUE if glob contains the tag .fi 1
710 int text_glob::is_fi (void)
712 return( is_tag && (strncmp(text_string, "devtag:.fi",
713 strlen("devtag:.fi")) == 0) &&
718 * is_eo_h - returns TRUE if glob contains the tag .eo.h
721 int text_glob::is_eo_h (void)
723 return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
727 * is_ce - returns TRUE if glob contains the tag .ce
730 int text_glob::is_ce (void)
732 return is_tag && (strncmp(text_string, "devtag:.ce",
733 strlen("devtag:.ce")) == 0);
737 * is_in - returns TRUE if glob contains the tag .in
740 int text_glob::is_in (void)
742 return is_tag && (strncmp(text_string, "devtag:.in ",
743 strlen("devtag:.in ")) == 0);
747 * is_po - returns TRUE if glob contains the tag .po
750 int text_glob::is_po (void)
752 return is_tag && (strncmp(text_string, "devtag:.po ",
753 strlen("devtag:.po ")) == 0);
757 * is_ti - returns TRUE if glob contains the tag .ti
760 int text_glob::is_ti (void)
762 return is_tag && (strncmp(text_string, "devtag:.ti ",
763 strlen("devtag:.ti ")) == 0);
767 * is_ll - returns TRUE if glob contains the tag .ll
770 int text_glob::is_ll (void)
772 return is_tag && (strncmp(text_string, "devtag:.ll ",
773 strlen("devtag:.ll ")) == 0);
777 * is_col - returns TRUE if glob contains the tag .col
780 int text_glob::is_col (void)
782 return is_tag && (strncmp(text_string, "devtag:.col",
783 strlen("devtag:.col")) == 0);
787 * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
790 int text_glob::is_tab_ts (void)
792 return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
796 * is_tab_te - returns TRUE if glob contains the tag .tab_te
799 int text_glob::is_tab_te (void)
801 return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
805 * is_ta - returns TRUE if glob contains the tag .ta
808 int text_glob::is_ta (void)
810 return is_tag && (strncmp(text_string, "devtag:.ta ",
811 strlen("devtag:.ta ")) == 0);
815 * is_tab - returns TRUE if glob contains the tag tab
818 int text_glob::is_tab (void)
820 return is_tag && (strncmp(text_string, "devtag:tab ",
821 strlen("devtag:tab ")) == 0);
825 * is_tab0 - returns TRUE if glob contains the tag tab0
828 int text_glob::is_tab0 (void)
830 return is_tag && (strncmp(text_string, "devtag:tab0",
831 strlen("devtag:tab0")) == 0);
835 * is_auto_img - returns TRUE if the glob contains an automatically
839 int text_glob::is_auto_img (void)
845 * is_br - returns TRUE if the glob is a tag containing a .br
846 * or an implied .br. Note that we do not include .nf or .fi
847 * as grohtml will place a .br after these commands if they
848 * should break the line.
851 int text_glob::is_br (void)
853 return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0) ||
854 (strncmp("devtag:.sp", text_string,
855 strlen("devtag:.sp")) == 0));
858 int text_glob::get_arg (void)
860 if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
861 const char *p = text_string;
863 while ((*p != (char)0) && (!isspace(*p)))
865 while ((*p != (char)0) && (isspace(*p)))
875 * get_tab_args - returns the tab position and alignment of the tab tag
878 int text_glob::get_tab_args (char *align)
880 if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
881 const char *p = text_string;
883 // firstly the alignment C|R|L
884 while ((*p != (char)0) && (!isspace(*p)))
886 while ((*p != (char)0) && (isspace(*p)))
890 while ((*p != (char)0) && (!isspace(*p)))
892 while ((*p != (char)0) && (isspace(*p)))
902 * remember_table - saves table, t, in the text_glob.
905 void text_glob::remember_table (html_table *t)
913 * get_table - returns the stored table description.
916 html_table *text_glob::get_table (void)
922 * the class and methods used to construct ordered double linked
923 * lists. In a previous implementation we used templates via
924 * #include "ordered-list.h", but this does assume that all C++
925 * compilers can handle this feature. Pragmatically it is safer to
926 * assume this is not the case.
929 struct element_list {
934 int minv, minh, maxv, maxh;
936 element_list (text_glob *d,
938 int min_vertical, int min_horizontal,
939 int max_vertical, int max_horizontal);
944 element_list::element_list ()
945 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
950 * element_list - create a list element assigning the datum and region parameters.
953 element_list::element_list (text_glob *in,
955 int min_vertical, int min_horizontal,
956 int max_vertical, int max_horizontal)
957 : right(0), left(0), datum(in), lineno(line_number),
958 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
962 element_list::~element_list ()
972 int is_less (element_list *a, element_list *b);
973 void add (text_glob *in,
975 int min_vertical, int min_horizontal,
976 int max_vertical, int max_horizontal);
977 void sub_move_right (void);
978 void move_right (void);
979 void move_left (void);
981 int is_equal_to_tail (void);
982 int is_equal_to_head (void);
983 void start_from_head (void);
984 void start_from_tail (void);
985 void insert (text_glob *in);
986 void move_to (text_glob *in);
987 text_glob *move_right_get_data (void);
988 text_glob *move_left_get_data (void);
989 text_glob *get_data (void);
997 * list - construct an empty list.
1001 : head(NULL), tail(NULL), ptr(NULL)
1006 * ~list - destroy a complete list.
1011 element_list *temp=head;
1019 } while ((head != NULL) && (head != tail));
1023 * is_less - returns TRUE if a is left of b if on the same line or
1024 * if a is higher up the page than b.
1027 int list::is_less (element_list *a, element_list *b)
1029 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1030 if (a->lineno < b->lineno) {
1032 } else if (a->lineno > b->lineno) {
1034 } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
1035 return( a->minh < b->minh );
1037 return( a->maxv < b->maxv );
1042 * add - adds a datum to the list in the order specified by the
1046 void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
1048 // create a new list element with datum and position fields initialized
1049 element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1053 fprintf(stderr, "[%s %d,%d,%d,%d] ",
1054 in->text_string, min_vertical, min_horizontal, max_vertical, max_horizontal);
1067 while ((last != head) && (is_less(t, last)))
1070 if (is_less(t, last)) {
1072 last->left->right = t;
1073 t->left = last->left;
1075 // now check for a new head
1079 // add t beyond last
1080 t->right = last->right;
1082 last->right->left = t;
1084 // now check for a new tail
1092 * sub_move_right - removes the element which is currently pointed to by ptr
1093 * from the list and moves ptr to the right.
1096 void list::sub_move_right (void)
1098 element_list *t=ptr->right;
1112 ptr->left->right = ptr->right;
1113 ptr->right->left = ptr->left;
1119 * start_from_head - assigns ptr to the head.
1122 void list::start_from_head (void)
1128 * start_from_tail - assigns ptr to the tail.
1131 void list::start_from_tail (void)
1137 * is_empty - returns TRUE if the list has no elements.
1140 int list::is_empty (void)
1142 return head == NULL;
1146 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
1149 int list::is_equal_to_tail (void)
1155 * is_equal_to_head - returns TRUE if the ptr equals the head.
1158 int list::is_equal_to_head (void)
1164 * move_left - moves the ptr left.
1167 void list::move_left (void)
1173 * move_right - moves the ptr right.
1176 void list::move_right (void)
1182 * get_datum - returns the datum referenced via ptr.
1185 text_glob* list::get_data (void)
1191 * move_right_get_data - returns the datum referenced via ptr and moves
1195 text_glob* list::move_right_get_data (void)
1205 * move_left_get_data - returns the datum referenced via ptr and moves
1209 text_glob* list::move_left_get_data (void)
1219 * insert - inserts data after the current position.
1222 void list::insert (text_glob *in)
1225 fatal("list must not be empty if we are inserting data");
1230 element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1233 ptr->right->left = t;
1234 t->right = ptr->right;
1241 * move_to - moves the current position to the point where data, in, exists.
1242 * This is an expensive method and should be used sparingly.
1245 void list::move_to (text_glob *in)
1248 while (ptr != tail && ptr->datum != in)
1253 * page class and methods
1259 void add (style *s, const string &str,
1261 int min_vertical, int min_horizontal,
1262 int max_vertical, int max_horizontal);
1263 void add_tag (style *s, const string &str,
1265 int min_vertical, int min_horizontal,
1266 int max_vertical, int max_horizontal);
1267 void add_and_encode (style *s, const string &str,
1269 int min_vertical, int min_horizontal,
1270 int max_vertical, int max_horizontal,
1272 void add_line (style *s,
1274 int x1, int y1, int x2, int y2,
1276 void insert_tag (const string &str);
1277 void dump_page (void); // debugging method
1281 list glyphs; // position of glyphs and specials on page
1282 char_buffer buffer; // all characters for this page
1290 * insert_tag - inserts a tag after the current position.
1293 void page::insert_tag (const string &str)
1295 if (str.length() > 0) {
1296 text_glob *g=new text_glob();
1297 text_glob *f=glyphs.get_data();
1298 g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1299 f->minv, f->minh, f->maxv, f->maxh);
1305 * add - add html text to the list of glyphs.
1308 void page::add (style *s, const string &str,
1310 int min_vertical, int min_horizontal,
1311 int max_vertical, int max_horizontal)
1313 if (str.length() > 0) {
1314 text_glob *g=new text_glob();
1315 g->text_glob_html(s, buffer.add_string(str), str.length(),
1316 min_vertical, min_horizontal, max_vertical, max_horizontal);
1317 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1322 * add_tag - adds a troff tag, for example: .tl .sp .br
1325 void page::add_tag (style *s, const string &str,
1327 int min_vertical, int min_horizontal,
1328 int max_vertical, int max_horizontal)
1330 if (str.length() > 0) {
1333 if (strncmp((str+'\0').contents(), "devtag:.auto-image",
1334 strlen("devtag:.auto-image")) == 0) {
1335 g = new text_glob();
1336 g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1337 min_vertical, min_horizontal, max_vertical, max_horizontal);
1339 g = new text_glob();
1340 g->text_glob_tag(s, buffer.add_string(str), str.length(),
1341 min_vertical, min_horizontal, max_vertical, max_horizontal);
1343 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1348 * add_line - adds the <line> primitive providing that y1==y2
1351 void page::add_line (style *s,
1353 int x_1, int y_1, int x_2, int y_2,
1357 text_glob *g = new text_glob();
1358 g->text_glob_line(s,
1359 min(y_1, y_2), min(x_1, x_2),
1360 max(y_1, y_2), max(x_1, x_2),
1362 glyphs.add(g, line_number,
1363 min(y_1, y_2), min(x_1, x_2),
1364 max(y_1, y_2), max(x_1, x_2));
1369 * to_unicode - returns a unicode translation of int, ch.
1372 static char *to_unicode (unsigned int ch)
1374 static char buf[30];
1376 sprintf(buf, "&#%u;", ch);
1381 * add_and_encode - adds a special string to the page, it translates the string
1382 * into html glyphs. The special string will have come from x X html:
1383 * and can contain troff character encodings which appear as
1384 * \[char]. A sequence of \\ represents \.
1385 * So for example we can write:
1386 * "cost = \[Po]3.00 file = \\foo\\bar"
1387 * which is translated into:
1388 * "cost = £3.00 file = \foo\bar"
1391 void page::add_and_encode (style *s, const string &str,
1393 int min_vertical, int min_horizontal,
1394 int max_vertical, int max_horizontal,
1398 const char *html_glyph;
1400 const int len = str.length();
1405 if ((i + 1 < len) && (str.substring(i, 2) == string("\\["))) {
1407 i += 2; // move over \[
1409 while ((i < len) && (str[i] != ']'))
1412 string troff_charname = str.substring(a, i - a);
1413 html_glyph = get_html_translation(s->f, troff_charname);
1415 html_string += html_glyph;
1417 glyph *g = name_to_glyph((troff_charname + '\0').contents());
1418 if (s->f->contains(g))
1419 html_string += s->f->get_code(g);
1424 html_string += str[i];
1427 if (html_string.length() > 0) {
1428 text_glob *g=new text_glob();
1430 g->text_glob_tag(s, buffer.add_string(html_string),
1431 html_string.length(),
1432 min_vertical, min_horizontal,
1433 max_vertical, max_horizontal);
1435 g->text_glob_special(s, buffer.add_string(html_string),
1436 html_string.length(),
1437 min_vertical, min_horizontal,
1438 max_vertical, max_horizontal);
1439 glyphs.add(g, line_number, min_vertical,
1440 min_horizontal, max_vertical, max_horizontal);
1445 * dump_page - dump the page contents for debugging purposes.
1448 void page::dump_page(void)
1450 #if defined(DEBUG_TABLES)
1451 text_glob *old_pos = glyphs.get_data();
1455 printf("\n\ndebugging start\n");
1456 glyphs.start_from_head();
1458 g = glyphs.get_data();
1459 if (g->is_tab_ts()) {
1461 if (g->get_table() != NULL)
1462 g->get_table()->dump_table();
1464 printf("%s ", g->text_string);
1467 glyphs.move_right();
1468 } while (! glyphs.is_equal_to_head());
1469 glyphs.move_to(old_pos);
1470 printf("\ndebugging end\n\n");
1477 * font classes and methods
1480 class html_font : public font {
1481 html_font(const char *);
1485 char *reencoded_name;
1487 static html_font *load_html_font(const char *);
1490 html_font *html_font::load_html_font(const char *s)
1492 html_font *f = new html_font(s);
1500 html_font::html_font(const char *nm)
1505 html_font::~html_font()
1510 * a simple class to contain the header to this document
1518 int has_been_written;
1525 title_desc::title_desc ()
1526 : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1530 title_desc::~title_desc ()
1539 int no_of_level_one_headings; // how many .SH or .NH 1 have we found?
1540 int no_of_headings; // how many headings have we found?
1541 char_buffer headings; // all the headings used in the document
1542 list headers; // list of headers built from .NH and .SH
1543 list header_filename; // in which file is this header?
1544 int header_level; // current header level
1545 int written_header; // have we written the header yet?
1546 string header_buffer; // current header text
1548 void write_headings (FILE *f, int force);
1551 header_desc::header_desc ()
1552 : no_of_level_one_headings(0), no_of_headings(0),
1553 header_level(2), written_header(0)
1557 header_desc::~header_desc ()
1562 * write_headings - emits a list of links for the headings in this document
1565 void header_desc::write_headings (FILE *f, int force)
1569 if (auto_links || force) {
1570 if (! headers.is_empty()) {
1573 headers.start_from_head();
1574 header_filename.start_from_head();
1575 if (dialect == xhtml)
1578 g = headers.get_data();
1579 fputs("<a href=\"", f);
1580 if (multiple_files && (! header_filename.is_empty())) {
1581 text_glob *fn = header_filename.get_data();
1582 fputs(fn->text_string, f);
1585 if (simple_anchors) {
1586 string buffer(ANCHOR_TEMPLATE);
1588 buffer += as_string(h);
1590 fprintf(f, "%s", buffer.contents());
1592 fputs(g->text_string, f);
1595 fputs(g->text_string, f);
1597 if (dialect == xhtml)
1598 fputs("<br/>\n", f);
1601 headers.move_right();
1602 if (multiple_files && (! header_filename.is_empty()))
1603 header_filename.move_right();
1604 } while (! headers.is_equal_to_head());
1606 if (dialect == xhtml)
1618 class assert_state {
1623 void addx (const char *c, const char *i, const char *v,
1624 const char *f, const char *l);
1625 void addy (const char *c, const char *i, const char *v,
1626 const char *f, const char *l);
1627 void build(const char *c, const char *v,
1628 const char *f, const char *l);
1629 void check_br (int br);
1630 void check_ce (int ce);
1631 void check_fi (int fi);
1632 void check_sp (int sp);
1644 const char *file_br;
1645 const char *file_ce;
1646 const char *file_fi;
1647 const char *file_sp;
1648 const char *line_br;
1649 const char *line_ce;
1650 const char *line_fi;
1651 const char *line_sp;
1656 void add (assert_pos **h,
1657 const char *c, const char *i, const char *v,
1658 const char *f, const char *l);
1659 void compare(assert_pos *t,
1660 const char *v, const char *f, const char *l);
1661 void close (const char *c);
1662 void set (const char *c, const char *v,
1663 const char *f, const char *l);
1664 void check_value (const char *s, int v, const char *name,
1665 const char *f, const char *l, int *flag);
1666 int check_value_error (int c, int v, const char *s,
1668 const char *f, const char *l, int flag);
1671 assert_state::assert_state ()
1690 assert_state::~assert_state ()
1694 while (xhead != NULL) {
1696 xhead = xhead->next;
1697 a_delete (char *)t->val;
1698 a_delete (char *)t->id;
1701 while (yhead != NULL) {
1703 yhead = yhead->next;
1704 a_delete (char *)t->val;
1705 a_delete (char *)t->id;
1710 void assert_state::reset (void)
1718 void assert_state::add (assert_pos **h,
1719 const char *c, const char *i, const char *v,
1720 const char *f, const char *l)
1725 if (strcmp(t->id, i) == 0)
1729 if (t != NULL && v != NULL && (v[0] != '='))
1730 compare(t, v, f, l);
1737 if (v == NULL || v[0] != '=') {
1743 v = "no value at all";
1744 fprintf(stderr, "%s:%s:error in assert format of id=%s expecting value to be prefixed with an `=' got %s\n",
1755 void assert_state::addx (const char *c, const char *i, const char *v,
1756 const char *f, const char *l)
1758 add(&xhead, c, i, v, f, l);
1761 void assert_state::addy (const char *c, const char *i, const char *v,
1762 const char *f, const char *l)
1764 add(&yhead, c, i, v, f, l);
1767 void assert_state::compare(assert_pos *t,
1768 const char *v, const char *f, const char *l)
1770 const char *s=t->val;
1777 if (strcmp(v, s) != 0) {
1782 fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %s and was given %s\n",
1787 void assert_state::close (const char *c)
1789 if (strcmp(c, "sp") == 0)
1791 else if (strcmp(c, "br") == 0)
1793 else if (strcmp(c, "fi") == 0)
1795 else if (strcmp(c, "nf") == 0)
1797 else if (strcmp(c, "ce") == 0)
1800 fprintf(stderr, "internal error: unrecognised tag in grohtml (%s)\n", c);
1803 const char *replace_negate_str (const char *before, char *after)
1806 a_delete (char *)before;
1808 if (strlen(after) > 0) {
1809 int d = atoi(after);
1811 if (d < 0 || d > 1) {
1812 fprintf(stderr, "expecting nf/fi value to be 0 or 1 not %d\n", d);
1824 const char *replace_str (const char *before, const char *after)
1827 a_delete (char *)before;
1831 void assert_state::set (const char *c, const char *v,
1832 const char *f, const char *l)
1839 // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1840 if (strcmp(c, "sp") == 0) {
1842 val_sp = replace_str(val_sp, strsave(v));
1843 file_sp = replace_str(file_sp, strsave(f));
1844 line_sp = replace_str(line_sp, strsave(l));
1845 } else if (strcmp(c, "br") == 0) {
1847 val_br = replace_str(val_br, strsave(v));
1848 file_br = replace_str(file_br, strsave(f));
1849 line_br = replace_str(line_br, strsave(l));
1850 } else if (strcmp(c, "fi") == 0) {
1852 val_fi = replace_str(val_fi, strsave(v));
1853 file_fi = replace_str(file_fi, strsave(f));
1854 line_fi = replace_str(line_fi, strsave(l));
1855 } else if (strcmp(c, "nf") == 0) {
1857 val_fi = replace_negate_str(val_fi, strsave(v));
1858 file_fi = replace_str(file_fi, strsave(f));
1859 line_fi = replace_str(line_fi, strsave(l));
1860 } else if (strcmp(c, "ce") == 0) {
1862 val_ce = replace_str(val_ce, strsave(v));
1863 file_ce = replace_str(file_ce, strsave(f));
1864 line_ce = replace_str(line_ce, strsave(l));
1869 * build - builds the troff state assertion.
1870 * see tmac/www.tmac for cmd examples.
1873 void assert_state::build (const char *c, const char *v,
1874 const char *f, const char *l)
1877 set(&c[1], v, f, l);
1882 int assert_state::check_value_error (int c, int v, const char *s,
1884 const char *f, const char *l, int flag)
1891 fprintf(stderr, "%s:%s:grohtml (troff state) assertion failed, expected %s to be %s but found it to contain %d\n",
1898 void assert_state::check_value (const char *s, int v, const char *name,
1899 const char *f, const char *l, int *flag)
1901 if (strncmp(s, "<=", 2) == 0)
1902 *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
1903 else if (strncmp(s, ">=", 2) == 0)
1904 *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
1905 else if (strncmp(s, "==", 2) == 0)
1906 *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
1907 else if (strncmp(s, "!=", 2) == 0)
1908 *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
1909 else if (strncmp(s, "<", 1) == 0)
1910 *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
1911 else if (strncmp(s, ">", 1) == 0)
1912 *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
1913 else if (strncmp(s, "=", 1) == 0)
1914 *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
1916 *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
1919 void assert_state::check_sp (int sp)
1922 check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
1925 void assert_state::check_fi (int fi)
1928 check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
1931 void assert_state::check_br (int br)
1934 check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
1937 void assert_state::check_ce (int ce)
1940 check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
1943 class html_printer : public printer {
1949 int no_of_printed_pages;
1952 int sbuf_start_hpos;
1958 int last_sbuf_length;
1959 int overstrike_detected;
1963 int output_vpos_max;
1964 int output_draw_point_size;
1966 int output_line_thickness;
1967 unsigned char output_space_code;
1968 char *inside_font_style;
1973 int supress_sub_sup;
1975 page *page_contents;
1976 html_text *current_paragraph;
1977 html_indent *indent;
1981 TAG_ALIGNMENT next_tag;
1990 int vertical_spacing;
1995 int seen_pageoffset;
1996 int next_pageoffset;
1997 int seen_linelength;
1998 int next_linelength;
2008 void set_style (const style &);
2009 void set_space_code (unsigned char c);
2010 void do_exec (char *, const environment *);
2011 void do_import (char *, const environment *);
2012 void do_def (char *, const environment *);
2013 void do_mdef (char *, const environment *);
2014 void do_file (char *, const environment *);
2015 void set_line_thickness (const environment *);
2016 void terminate_current_font (void);
2017 void flush_font (void);
2018 void add_to_sbuf (glyph *g, const string &s);
2019 void write_title (int in_head);
2020 int sbuf_continuation (glyph *g, const char *name, const environment *env, int w);
2021 void flush_page (void);
2022 void troff_tag (text_glob *g);
2023 void flush_globs (void);
2024 void emit_line (text_glob *g);
2025 void emit_raw (text_glob *g);
2026 void emit_html (text_glob *g);
2027 void determine_space (text_glob *g);
2028 void start_font (const char *name);
2029 void end_font (const char *name);
2030 int is_font_courier (font *f);
2031 int is_line_start (int nf);
2032 int is_courier_until_eol (void);
2033 void start_size (int from, int to);
2034 void do_font (text_glob *g);
2035 void do_center (char *arg);
2036 void do_check_center (void);
2037 void do_break (void);
2038 void do_space (char *arg);
2040 void do_eol_ce (void);
2041 void do_title (void);
2042 void do_fill (char *arg);
2043 void do_heading (char *arg);
2044 void write_header (void);
2045 void determine_header_level (int level);
2046 void do_linelength (char *arg);
2047 void do_pageoffset (char *arg);
2048 void do_indentation (char *arg);
2049 void do_tempindent (char *arg);
2050 void do_indentedparagraph (void);
2051 void do_verticalspacing (char *arg);
2052 void do_pointsize (char *arg);
2053 void do_centered_image (void);
2054 void do_left_image (void);
2055 void do_right_image (void);
2056 void do_auto_image (text_glob *g, const char *filename);
2057 void do_links (void);
2058 void do_flush (void);
2059 void do_job_name (char *name);
2060 void do_head (char *name);
2061 void insert_split_file (void);
2062 int is_in_middle (int left, int right);
2063 void do_sup_or_sub (text_glob *g);
2064 int start_subscript (text_glob *g);
2065 int end_subscript (text_glob *g);
2066 int start_superscript (text_glob *g);
2067 int end_superscript (text_glob *g);
2068 void outstanding_eol (int n);
2069 int is_bold (font *f);
2070 font *make_bold (font *f);
2071 int overstrike (glyph *g, const char *name, const environment *env, int w);
2072 void do_body (void);
2073 int next_horiz_pos (text_glob *g, int nf);
2074 void lookahead_for_tables (void);
2075 void insert_tab_te (void);
2076 text_glob *insert_tab_ts (text_glob *where);
2077 void insert_tab0_foreach_tab (void);
2078 void insert_tab_0 (text_glob *where);
2079 void do_indent (int in, int pageoff, int linelen);
2080 void shutdown_table (void);
2081 void do_tab_ts (text_glob *g);
2082 void do_tab_te (void);
2083 void do_col (char *s);
2084 void do_tab (char *s);
2085 void do_tab0 (void);
2086 int calc_nf (text_glob *g, int nf);
2087 void calc_po_in (text_glob *g, int nf);
2088 void remove_tabs (void);
2089 void remove_courier_tabs (void);
2090 void update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g);
2091 void add_table_end (const char *);
2092 void do_file_components (void);
2093 void write_navigation (const string &top, const string &prev,
2094 const string &next, const string ¤t);
2095 void emit_link (const string &to, const char *name);
2096 int get_troff_indent (void);
2097 void restore_troff_indent (void);
2098 void handle_assertion (int minv, int minh, int maxv, int maxh, const char *s);
2099 void handle_state_assertion (text_glob *g);
2100 void do_end_para (text_glob *g);
2101 int round_width (int x);
2102 void handle_tag_within_title (text_glob *g);
2103 void writeHeadMetaStyle (void);
2104 void handle_valid_flag (int needs_para);
2105 void do_math (text_glob *g);
2106 void write_html_anchor (text_glob *h);
2107 void write_xhtml_anchor (text_glob *h);
2113 void set_char (glyph *g, font *f, const environment *env, int w, const char *name);
2114 void set_numbered_char(int num, const environment *env, int *widthp);
2115 glyph *set_char_and_width(const char *nm, const environment *env,
2116 int *widthp, font **f);
2117 void draw (int code, int *p, int np, const environment *env);
2118 void begin_page (int);
2119 void end_page (int);
2120 void special (char *arg, const environment *env, char type);
2121 void devtag (char *arg, const environment *env, char type);
2122 font *make_font (const char *);
2123 void end_of_line ();
2126 printer *make_printer()
2128 return new html_printer;
2131 static void usage(FILE *stream);
2133 void html_printer::set_style(const style &sty)
2135 const char *fontname = sty.f->get_name();
2136 if (fontname == NULL)
2137 fatal("no internalname specified for font");
2140 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
2145 * is_bold - returns TRUE if font, f, is bold.
2148 int html_printer::is_bold (font *f)
2150 const char *fontname = f->get_name();
2151 return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
2155 * make_bold - if a bold font of, f, exists then return it.
2158 font *html_printer::make_bold (font *f)
2160 const char *fontname = f->get_name();
2162 if (strcmp(fontname, "B") == 0)
2164 if (strcmp(fontname, "I") == 0)
2165 return font::load_font("BI");
2166 if (strcmp(fontname, "BI") == 0)
2171 void html_printer::end_of_line()
2178 * emit_line - writes out a horizontal rule.
2181 void html_printer::emit_line (text_glob *)
2183 // --fixme-- needs to know the length in percentage
2184 if (dialect == xhtml)
2185 html.put_string("<hr/>");
2187 html.put_string("<hr>");
2191 * restore_troff_indent - is called when we have temporarily shutdown
2192 * indentation (typically done when we have
2193 * centered an image).
2196 void html_printer::restore_troff_indent (void)
2198 troff_indent = next_indent;
2199 if (troff_indent > 0) {
2201 * force device indentation
2204 do_indent(get_troff_indent(), pageoffset, linelength);
2209 * emit_raw - writes the raw html information directly to the device.
2212 void html_printer::emit_raw (text_glob *g)
2215 if (next_tag == INLINE) {
2217 current_paragraph->do_emittext(g->text_string, g->text_length);
2219 int space = current_paragraph->retrieve_para_space() || seen_space;
2221 current_paragraph->done_para();
2226 if (dialect == html4)
2227 current_paragraph->do_para("align=\"center\"", space);
2229 current_paragraph->do_para("class=\"center\"", space);
2232 if (dialect == html4)
2233 current_paragraph->do_para(&html, "align=\"left\"", get_troff_indent(), pageoffset, linelength, space);
2235 current_paragraph->do_para(&html, "class=\"left\"", get_troff_indent(), pageoffset, linelength, space);
2238 if (dialect == html4)
2239 current_paragraph->do_para(&html, "align=\"right\"", get_troff_indent(), pageoffset, linelength, space);
2241 current_paragraph->do_para(&html, "class=\"right\"", get_troff_indent(), pageoffset, linelength, space);
2244 fatal("unknown enumeration");
2246 current_paragraph->do_emittext(g->text_string, g->text_length);
2247 current_paragraph->done_para();
2249 supress_sub_sup = TRUE;
2251 restore_troff_indent();
2256 * handle_tag_within_title - handle a limited number of tags within
2257 * the context of a table. Those tags which
2258 * set values rather than generate spaces
2262 void html_printer::handle_tag_within_title (text_glob *g)
2264 if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll()
2265 || g->is_fi() || g->is_nf())
2270 * do_center - handle the .ce commands from troff.
2273 void html_printer::do_center (char *arg)
2275 next_center = atoi(arg);
2280 * do_centered_image - set a flag such that the next devtag is
2281 * placed inside a centered paragraph.
2284 void html_printer::do_centered_image (void)
2286 next_tag = CENTERED;
2290 * do_right_image - set a flag such that the next devtag is
2291 * placed inside a right aligned paragraph.
2294 void html_printer::do_right_image (void)
2300 * do_left_image - set a flag such that the next devtag is
2301 * placed inside a left aligned paragraph.
2304 void html_printer::do_left_image (void)
2310 * exists - returns TRUE if filename exists.
2313 static int exists (const char *filename)
2315 FILE *fp = fopen(filename, "r");
2326 * generate_img_src - returns a html image tag for the filename
2327 * providing that the image exists.
2330 static string &generate_img_src (const char *filename)
2332 string *s = new string("");
2334 while (filename && (filename[0] == ' ')) {
2337 if (exists(filename)) {
2338 *s += string("<img src=\"") + filename + "\" "
2339 + "alt=\"Image " + filename + "\">";
2340 if (dialect == xhtml)
2347 * do_auto_image - tests whether the image, indicated by filename,
2348 * is present, if so then it emits an html image tag.
2349 * An image tag may be passed through from pic, eqn
2350 * but the corresponding image might not be created.
2351 * Consider .EQ delim $$ .EN or an empty .PS .PE.
2354 void html_printer::do_auto_image (text_glob *g, const char *filename)
2356 string buffer = generate_img_src(filename);
2358 if (! buffer.empty()) {
2360 * utilize emit_raw by creating a new text_glob.
2364 h.text_string = buffer.contents();
2365 h.text_length = buffer.length();
2372 * outstanding_eol - call do_eol, n, times.
2375 void html_printer::outstanding_eol (int n)
2384 * do_title - handle the .tl commands from troff.
2387 void html_printer::do_title (void)
2390 int removed_from_head;
2392 if (page_number == 1) {
2393 int found_title_start = FALSE;
2394 if (! page_contents->glyphs.is_empty()) {
2395 page_contents->glyphs.sub_move_right(); /* move onto next word */
2397 t = page_contents->glyphs.get_data();
2398 removed_from_head = FALSE;
2399 if (t->is_auto_img()) {
2400 string img = generate_img_src((char *)(t->text_string + 20));
2402 if (! img.empty()) {
2403 if (found_title_start)
2405 found_title_start = TRUE;
2406 title.has_been_found = TRUE;
2409 page_contents->glyphs.sub_move_right(); /* move onto next word */
2410 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2411 (page_contents->glyphs.is_equal_to_head()));
2412 } else if (t->is_eo_tl()) {
2413 /* end of title found
2415 title.has_been_found = TRUE;
2417 } else if (t->is_a_tag()) {
2418 handle_tag_within_title(t);
2419 page_contents->glyphs.sub_move_right(); /* move onto next word */
2420 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2421 (page_contents->glyphs.is_equal_to_head()));
2422 } else if (found_title_start) {
2423 title.text += " " + string(t->text_string, t->text_length);
2424 page_contents->glyphs.sub_move_right(); /* move onto next word */
2425 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2426 (page_contents->glyphs.is_equal_to_head()));
2428 title.text += string(t->text_string, t->text_length);
2429 found_title_start = TRUE;
2430 title.has_been_found = TRUE;
2431 page_contents->glyphs.sub_move_right(); /* move onto next word */
2432 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2433 (page_contents->glyphs.is_equal_to_head()));
2435 } while ((! page_contents->glyphs.is_equal_to_head()) ||
2436 (removed_from_head));
2442 * write_html_anchor - writes out an anchor. The style of the anchor
2443 * dependent upon simple_anchor.
2446 void html_printer::write_html_anchor (text_glob *h)
2448 if (dialect == html4) {
2450 html.put_string("<a name=\"");
2451 if (simple_anchors) {
2452 string buffer(ANCHOR_TEMPLATE);
2454 buffer += as_string(header.no_of_headings);
2456 html.put_string(buffer.contents());
2458 html.put_string(header.header_buffer);
2459 html.put_string("\"></a>").nl();
2465 * write_xhtml_anchor - writes out an anchor. The style of the anchor
2466 * dependent upon simple_anchor.
2469 void html_printer::write_xhtml_anchor (text_glob *h)
2471 if (dialect == xhtml) {
2473 html.put_string(" id=\"");
2474 if (simple_anchors) {
2475 string buffer(ANCHOR_TEMPLATE);
2477 buffer += as_string(header.no_of_headings);
2479 html.put_string(buffer.contents());
2481 html.put_string(header.header_buffer);
2482 html.put_string("\"");
2487 void html_printer::write_header (void)
2489 if (! header.header_buffer.empty()) {
2490 text_glob *a = NULL;
2491 int space = current_paragraph->retrieve_para_space() || seen_space;
2493 if (header.header_level > 7)
2494 header.header_level = 7;
2496 // firstly we must terminate any font and type faces
2497 current_paragraph->done_para();
2498 supress_sub_sup = TRUE;
2500 if (cutoff_heading+2 > header.header_level) {
2501 // now we save the header so we can issue a list of links
2502 header.no_of_headings++;
2505 a = new text_glob();
2506 a->text_glob_html(&st,
2507 header.headings.add_string(header.header_buffer),
2508 header.header_buffer.length(),
2509 header.no_of_headings, header.header_level,
2510 header.no_of_headings, header.header_level);
2512 // and add this header to the header list
2513 header.headers.add(a,
2514 header.no_of_headings,
2515 header.no_of_headings, header.no_of_headings,
2516 header.no_of_headings, header.no_of_headings);
2521 if (manufacture_headings) {
2522 // line break before a header
2523 if (!current_paragraph->emitted_text())
2524 current_paragraph->do_space();
2525 // user wants manufactured headings which look better than <Hn></Hn>
2526 if (header.header_level<4) {
2527 html.put_string("<b><font size=\"+1\">");
2528 html.put_string(header.header_buffer);
2529 html.put_string("</font>").nl();
2530 write_html_anchor(a);
2531 html.put_string("</b>").nl();
2534 html.put_string("<b>");
2535 html.put_string(header.header_buffer).nl();
2536 write_html_anchor(a);
2537 html.put_string("</b>").nl();
2541 // and now we issue the real header
2542 html.put_string("<h");
2543 html.put_number(header.header_level);
2544 write_xhtml_anchor(a);
2545 html.put_string(">");
2546 html.put_string(header.header_buffer).nl();
2547 write_html_anchor(a);
2548 html.put_string("</h");
2549 html.put_number(header.header_level);
2550 html.put_string(">").nl();
2553 /* and now we save the file name in which this header will occur */
2555 style st; // fake style to enable us to use the list data structure
2557 text_glob *h=new text_glob();
2558 h->text_glob_html(&st,
2559 header.headings.add_string(file_list.file_name()),
2560 file_list.file_name().length(),
2561 header.no_of_headings, header.header_level,
2562 header.no_of_headings, header.header_level);
2564 header.header_filename.add(h,
2565 header.no_of_headings,
2566 header.no_of_headings, header.no_of_headings,
2567 header.no_of_headings, header.no_of_headings);
2569 current_paragraph->do_para(&html, "", get_troff_indent(), pageoffset, linelength, space);
2573 void html_printer::determine_header_level (int level)
2578 for (i=0; ((i<header.header_buffer.length())
2579 && ((header.header_buffer[i] == '.')
2580 || is_digit(header.header_buffer[i]))) ; i++) {
2581 if (header.header_buffer[i] == '.') {
2586 header.header_level = level+1;
2587 if (header.header_level >= 2 && header.header_level <= split_level) {
2588 header.no_of_level_one_headings++;
2589 insert_split_file();
2594 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2597 void html_printer::do_heading (char *arg)
2600 int level=atoi(arg);
2603 header.header_buffer.clear();
2604 page_contents->glyphs.move_right();
2605 if (! page_contents->glyphs.is_equal_to_head()) {
2606 g = page_contents->glyphs.get_data();
2609 if (g->is_auto_img()) {
2610 string img=generate_img_src((char *)(g->text_string + 20));
2612 if (! img.empty()) {
2613 simple_anchors = TRUE; // we cannot use full heading anchors with images
2614 if (horiz < g->minh)
2615 header.header_buffer += " ";
2617 header.header_buffer += img;
2620 else if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll())
2622 else if (g->is_fi())
2624 else if (g->is_nf())
2626 else if (! (g->is_a_line() || g->is_a_tag())) {
2628 * we ignore the other tag commands when constructing a heading
2630 if (horiz < g->minh)
2631 header.header_buffer += " ";
2634 header.header_buffer += string(g->text_string, g->text_length);
2636 page_contents->glyphs.move_right();
2637 g = page_contents->glyphs.get_data();
2638 } while ((! page_contents->glyphs.is_equal_to_head()) &&
2642 determine_header_level(level);
2646 * finally set the output font to uninitialized, thus forcing
2647 * the new paragraph to start a new font block.
2650 output_style.f = NULL;
2651 g = page_contents->glyphs.get_data();
2652 page_contents->glyphs.move_left(); // so that next time we use old g
2656 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
2659 int html_printer::is_courier_until_eol (void)
2661 text_glob *orig = page_contents->glyphs.get_data();
2665 if (! page_contents->glyphs.is_equal_to_tail()) {
2666 page_contents->glyphs.move_right();
2668 g = page_contents->glyphs.get_data();
2669 if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2671 page_contents->glyphs.move_right();
2673 (! page_contents->glyphs.is_equal_to_head()) &&
2674 (! g->is_fi()) && (! g->is_eol()));
2677 * now restore our previous position.
2679 while (page_contents->glyphs.get_data() != orig)
2680 page_contents->glyphs.move_left();
2686 * do_linelength - handle the .ll command from troff.
2689 void html_printer::do_linelength (char *arg)
2691 if (max_linelength == -1)
2692 max_linelength = atoi(arg);
2694 next_linelength = atoi(arg);
2695 seen_linelength = TRUE;
2699 * do_pageoffset - handle the .po command from troff.
2702 void html_printer::do_pageoffset (char *arg)
2704 next_pageoffset = atoi(arg);
2705 seen_pageoffset = TRUE;
2709 * get_troff_indent - returns the indent value.
2712 int html_printer::get_troff_indent (void)
2714 if (end_tempindent > 0)
2717 return troff_indent;
2721 * do_indentation - handle the .in command from troff.
2724 void html_printer::do_indentation (char *arg)
2726 next_indent = atoi(arg);
2731 * do_tempindent - handle the .ti command from troff.
2734 void html_printer::do_tempindent (char *arg)
2738 * we set the end_tempindent to 2 as the first .br
2739 * activates the .ti and the second terminates it.
2742 temp_indent = atoi(arg);
2747 * shutdown_table - shuts down the current table.
2750 void html_printer::shutdown_table (void)
2752 if (table != NULL) {
2753 current_paragraph->done_para();
2754 table->emit_finish_table();
2755 // dont delete this table as it will be deleted when we destroy the text_glob
2761 * do_indent - remember the indent parameters and if
2762 * indent is > pageoff and indent has changed
2763 * then we start a html table to implement the indentation.
2766 void html_printer::do_indent (int in, int pageoff, int linelen)
2768 if ((device_indent != -1) &&
2769 (pageoffset+device_indent != in+pageoff)) {
2771 int space = current_paragraph->retrieve_para_space() || seen_space;
2772 current_paragraph->done_para();
2775 pageoffset = pageoff;
2776 if (linelen <= max_linelength)
2777 linelength = linelen;
2779 current_paragraph->do_para(&html, "", device_indent,
2780 pageoffset, max_linelength, space);
2785 * do_verticalspacing - handle the .vs command from troff.
2788 void html_printer::do_verticalspacing (char *arg)
2790 vertical_spacing = atoi(arg);
2794 * do_pointsize - handle the .ps command from troff.
2797 void html_printer::do_pointsize (char *arg)
2800 * firstly check to see whether this point size is really associated with a .tl tag
2803 if (! page_contents->glyphs.is_empty()) {
2804 text_glob *g = page_contents->glyphs.get_data();
2805 text_glob *t = page_contents->glyphs.get_data();
2807 while (t->is_a_tag() && (! page_contents->glyphs.is_equal_to_head())) {
2810 * found title therefore ignore this .ps tag
2813 page_contents->glyphs.move_left();
2814 t = page_contents->glyphs.get_data();
2818 page_contents->glyphs.move_right();
2819 t = page_contents->glyphs.get_data();
2822 * move back to original position
2825 page_contents->glyphs.move_left();
2826 t = page_contents->glyphs.get_data();
2829 * collect valid pointsize
2831 pointsize = atoi(arg);
2836 * do_fill - records whether troff has requested that text be filled.
2839 void html_printer::do_fill (char *arg)
2843 output_hpos = get_troff_indent()+pageoffset;
2844 supress_sub_sup = TRUE;
2846 if (fill_on != on) {
2848 current_paragraph->do_para("", seen_space);
2854 * do_eol - handle the end of line
2857 void html_printer::do_eol (void)
2860 if (current_paragraph->ever_emitted_text()) {
2861 current_paragraph->do_newline();
2862 current_paragraph->do_break();
2865 output_hpos = get_troff_indent()+pageoffset;
2869 * do_check_center - checks to see whether we have seen a `.ce' tag
2870 * during the previous line.
2873 void html_printer::do_check_center(void)
2876 seen_center = FALSE;
2877 if (next_center > 0) {
2878 if (end_center == 0) {
2879 int space = current_paragraph->retrieve_para_space() || seen_space;
2880 current_paragraph->done_para();
2881 supress_sub_sup = TRUE;
2882 if (dialect == html4)
2883 current_paragraph->do_para("align=\"center\"", space);
2885 current_paragraph->do_para("class=\"center\"", space);
2887 if ((strcmp("align=\"center\"",
2888 current_paragraph->get_alignment()) != 0) &&
2889 (strcmp("class=\"center\"",
2890 current_paragraph->get_alignment()) != 0)) {
2892 * different alignment, so shutdown paragraph and open
2895 int space = current_paragraph->retrieve_para_space() || seen_space;
2896 current_paragraph->done_para();
2897 supress_sub_sup = TRUE;
2898 if (dialect == html4)
2899 current_paragraph->do_para("align=\"center\"", space);
2901 current_paragraph->do_para("class=\"center\"", space);
2904 * same alignment, if we have emitted text then issue a break.
2906 if (current_paragraph->emitted_text())
2907 current_paragraph->do_break();
2912 if (end_center > 0) {
2913 seen_space = seen_space || current_paragraph->retrieve_para_space();
2914 current_paragraph->done_para();
2915 supress_sub_sup = TRUE;
2916 current_paragraph->do_para("", seen_space);
2918 end_center = next_center;
2923 * do_eol_ce - handle end of line specifically for a .ce
2926 void html_printer::do_eol_ce (void)
2928 if (end_center > 0) {
2930 if (current_paragraph->emitted_text())
2931 current_paragraph->do_break();
2934 if (end_center == 0) {
2935 current_paragraph->done_para();
2936 supress_sub_sup = TRUE;
2942 * do_flush - flushes all output and tags.
2945 void html_printer::do_flush (void)
2947 current_paragraph->done_para();
2951 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
2954 void html_printer::do_links (void)
2956 html.end_line(); // flush line
2957 auto_links = FALSE; /* from now on only emit under user request */
2958 file_list.add_new_file(xtmpfile());
2959 file_list.set_links_required();
2960 html.set_file(file_list.get_file());
2964 * insert_split_file -
2967 void html_printer::insert_split_file (void)
2969 if (multiple_files) {
2970 current_paragraph->done_para(); // flush paragraph
2971 html.end_line(); // flush line
2972 html.set_file(file_list.get_file()); // flush current file
2973 file_list.add_new_file(xtmpfile());
2974 string split_file = job_name;
2976 split_file += string("-");
2977 split_file += as_string(header.no_of_level_one_headings);
2978 if (dialect == xhtml)
2979 split_file += string(".xhtml");
2981 split_file += string(".html");
2984 file_list.set_file_name(split_file);
2985 html.set_file(file_list.get_file());
2990 * do_job_name - assigns the job_name to name.
2993 void html_printer::do_job_name (char *name)
2995 if (! multiple_files) {
2996 multiple_files = TRUE;
2997 while (name != NULL && (*name != (char)0) && (*name == ' '))
3004 * do_head - adds a string to head_info which is to be included into
3005 * the <head> </head> section of the html document.
3008 void html_printer::do_head (char *name)
3010 head_info += string(name);
3015 * do_break - handles the ".br" request and also
3016 * undoes an outstanding ".ti" command
3017 * and calls indent if the indentation
3018 * related registers have changed.
3021 void html_printer::do_break (void)
3023 int seen_temp_indent = FALSE;
3025 current_paragraph->do_break();
3026 if (end_tempindent > 0) {
3028 if (end_tempindent > 0)
3029 seen_temp_indent = TRUE;
3031 if (seen_indent || seen_pageoffset || seen_linelength || seen_temp_indent) {
3032 if (seen_indent && (! seen_temp_indent))
3033 troff_indent = next_indent;
3034 if (! seen_pageoffset)
3035 next_pageoffset = pageoffset;
3036 if (! seen_linelength)
3037 next_linelength = linelength;
3038 do_indent(get_troff_indent(), next_pageoffset, next_linelength);
3040 seen_indent = seen_temp_indent;
3041 seen_linelength = FALSE;
3042 seen_pageoffset = FALSE;
3044 output_hpos = get_troff_indent()+pageoffset;
3045 supress_sub_sup = TRUE;
3048 void html_printer::do_space (char *arg)
3052 seen_space = atoi(arg);
3053 as.check_sp(seen_space);
3056 table->set_space(TRUE);
3060 current_paragraph->do_space();
3063 supress_sub_sup = TRUE;
3067 * do_tab_ts - start a table, which will have already been defined.
3070 void html_printer::do_tab_ts (text_glob *g)
3072 html_table *t = g->get_table();
3076 current_paragraph->done_pre();
3077 current_paragraph->done_para();
3078 current_paragraph->remove_para_space();
3080 #if defined(DEBUG_TABLES)
3081 html.simple_comment("TABS");
3084 t->set_linelength(max_linelength);
3085 t->add_indent(pageoffset);
3087 t->emit_table_header(seen_space);
3089 t->emit_table_header(FALSE);
3090 row_space = current_paragraph->retrieve_para_space() || seen_space;
3099 * do_tab_te - finish a table.
3102 void html_printer::do_tab_te (void)
3105 current_paragraph->done_para();
3106 current_paragraph->remove_para_space();
3107 table->emit_finish_table();
3111 restore_troff_indent();
3115 * do_tab - handle the "devtag:tab" tag
3118 void html_printer::do_tab (char *s)
3124 int col = table->find_column(atoi(s) + pageoffset + get_troff_indent());
3126 current_paragraph->done_para();
3127 table->emit_col(col);
3133 * do_tab0 - handle the "devtag:tab0" tag
3136 void html_printer::do_tab0 (void)
3139 int col = table->find_column(pageoffset+get_troff_indent());
3141 current_paragraph->done_para();
3142 table->emit_col(col);
3148 * do_col - start column, s.
3151 void html_printer::do_col (char *s)
3154 if (atoi(s) < current_column)
3155 row_space = seen_space;
3157 current_column = atoi(s);
3158 current_paragraph->done_para();
3159 table->emit_col(current_column);
3160 current_paragraph->do_para("", row_space);
3165 * troff_tag - processes the troff tag and manipulates the troff
3169 void html_printer::troff_tag (text_glob *g)
3172 * firstly skip over devtag:
3174 char *t=(char *)g->text_string+strlen("devtag:");
3175 if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
3177 } else if (strncmp(g->text_string, "html<?p>:", strlen("html<?p>:")) == 0) {
3178 if (current_paragraph->emitted_text())
3179 html.put_string(g->text_string+9);
3182 } else if (strncmp(g->text_string, "math<?p>:", strlen("math<?p>:")) == 0) {
3184 } else if (g->is_eol()) {
3186 } else if (g->is_eol_ce()) {
3188 } else if (strncmp(t, ".sp", 3) == 0) {
3189 char *a = (char *)t+3;
3191 } else if (strncmp(t, ".br", 3) == 0) {
3195 } else if (strcmp(t, ".centered-image") == 0) {
3196 do_centered_image();
3197 } else if (strcmp(t, ".right-image") == 0) {
3199 } else if (strcmp(t, ".left-image") == 0) {
3201 } else if (strncmp(t, ".auto-image", 11) == 0) {
3202 char *a = (char *)t+11;
3203 do_auto_image(g, a);
3204 } else if (strncmp(t, ".ce", 3) == 0) {
3205 char *a = (char *)t+3;
3206 supress_sub_sup = TRUE;
3208 } else if (g->is_tl()) {
3209 supress_sub_sup = TRUE;
3210 title.with_h1 = TRUE;
3212 } else if (strncmp(t, ".html-tl", 8) == 0) {
3213 supress_sub_sup = TRUE;
3214 title.with_h1 = FALSE;
3216 } else if (strncmp(t, ".fi", 3) == 0) {
3217 char *a = (char *)t+3;
3219 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
3220 char *a = (char *)t+3;
3222 } else if (strncmp(t, ".ll", 3) == 0) {
3223 char *a = (char *)t+3;
3225 } else if (strncmp(t, ".po", 3) == 0) {
3226 char *a = (char *)t+3;
3228 } else if (strncmp(t, ".in", 3) == 0) {
3229 char *a = (char *)t+3;
3231 } else if (strncmp(t, ".ti", 3) == 0) {
3232 char *a = (char *)t+3;
3234 } else if (strncmp(t, ".vs", 3) == 0) {
3235 char *a = (char *)t+3;
3236 do_verticalspacing(a);
3237 } else if (strncmp(t, ".ps", 3) == 0) {
3238 char *a = (char *)t+3;
3240 } else if (strcmp(t, ".links") == 0) {
3242 } else if (strncmp(t, ".job-name", 9) == 0) {
3243 char *a = (char *)t+9;
3245 } else if (strncmp(t, ".head", 5) == 0) {
3246 char *a = (char *)t+5;
3248 } else if (strcmp(t, ".no-auto-rule") == 0) {
3250 } else if (strcmp(t, ".tab-ts") == 0) {
3252 } else if (strcmp(t, ".tab-te") == 0) {
3254 } else if (strncmp(t, ".col ", 5) == 0) {
3255 char *a = (char *)t+4;
3257 } else if (strncmp(t, "tab ", 4) == 0) {
3258 char *a = (char *)t+3;
3260 } else if (strncmp(t, "tab0", 4) == 0) {
3266 * do_math - prints out the equation
3269 void html_printer::do_math (text_glob *g)
3272 if (current_paragraph->emitted_text())
3273 html.put_string(g->text_string+9);
3279 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
3282 int html_printer::is_in_middle (int left, int right)
3284 return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
3285 <= CENTER_TOLERANCE );
3289 * flush_globs - runs through the text glob list and emits html.
3292 void html_printer::flush_globs (void)
3296 if (! page_contents->glyphs.is_empty()) {
3297 page_contents->glyphs.start_from_head();
3299 g = page_contents->glyphs.get_data();
3301 fprintf(stderr, "[%s:%d:%d:%d:%d]",
3302 g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
3306 handle_state_assertion(g);
3308 if (strcmp(g->text_string, "XXXXXXX") == 0)
3313 else if (g->is_a_line())
3316 as.check_sp(seen_space);
3317 as.check_br(seen_break);
3323 as.check_fi(fill_on);
3324 as.check_ce(end_center);
3326 * after processing the title (and removing it) the glyph list might be empty
3328 if (! page_contents->glyphs.is_empty()) {
3329 page_contents->glyphs.move_right();
3331 } while (! page_contents->glyphs.is_equal_to_head());
3336 * calc_nf - calculates the _no_ format flag, given the
3340 int html_printer::calc_nf (text_glob *g, int nf)
3357 * calc_po_in - calculates the, in, po, registers
3360 void html_printer::calc_po_in (text_glob *g, int nf)
3363 troff_indent = g->get_arg();
3364 else if (g->is_po())
3365 pageoffset = g->get_arg();
3366 else if (g->is_ti()) {
3367 temp_indent = g->get_arg();
3369 } else if (g->is_br() || (nf && g->is_eol())) {
3370 if (end_tempindent > 0)
3376 * next_horiz_pos - returns the next horiz position.
3377 * -1 is returned if it doesn't exist.
3380 int html_printer::next_horiz_pos (text_glob *g, int nf)
3384 if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
3385 if (! page_contents->glyphs.is_empty()) {
3386 page_contents->glyphs.move_right_get_data();
3388 page_contents->glyphs.start_from_head();
3393 page_contents->glyphs.move_left();
3400 * insert_tab_ts - inserts a tab-ts before, where.
3403 text_glob *html_printer::insert_tab_ts (text_glob *where)
3405 text_glob *start_of_table;
3406 text_glob *old_pos = page_contents->glyphs.get_data();
3408 page_contents->glyphs.move_to(where);
3409 page_contents->glyphs.move_left();
3410 page_contents->insert_tag(string("devtag:.tab-ts")); // tab table start
3411 page_contents->glyphs.move_right();
3412 start_of_table = page_contents->glyphs.get_data();
3413 page_contents->glyphs.move_to(old_pos);
3414 return start_of_table;
3418 * insert_tab_te - inserts a tab-te before the current position
3419 * (it skips backwards over .sp/.br)
3422 void html_printer::insert_tab_te (void)
3424 text_glob *g = page_contents->glyphs.get_data();
3425 page_contents->dump_page();
3427 while (page_contents->glyphs.get_data()->is_a_tag())
3428 page_contents->glyphs.move_left();
3430 page_contents->insert_tag(string("devtag:.tab-te")); // tab table end
3431 while (g != page_contents->glyphs.get_data())
3432 page_contents->glyphs.move_right();
3433 page_contents->dump_page();
3437 * insert_tab_0 - inserts a tab0 before, where.
3440 void html_printer::insert_tab_0 (text_glob *where)
3442 text_glob *old_pos = page_contents->glyphs.get_data();
3444 page_contents->glyphs.move_to(where);
3445 page_contents->glyphs.move_left();
3446 page_contents->insert_tag(string("devtag:tab0")); // tab0 start of line
3447 page_contents->glyphs.move_right();
3448 page_contents->glyphs.move_to(old_pos);
3452 * remove_tabs - removes the tabs tags on this line.
3455 void html_printer::remove_tabs (void)
3457 text_glob *orig = page_contents->glyphs.get_data();
3460 if (! page_contents->glyphs.is_equal_to_tail()) {
3462 g = page_contents->glyphs.get_data();
3464 page_contents->glyphs.sub_move_right();
3466 orig = page_contents->glyphs.get_data();
3468 page_contents->glyphs.move_right();
3469 } while ((! page_contents->glyphs.is_equal_to_head()) &&
3473 * now restore our previous position.
3475 while (page_contents->glyphs.get_data() != orig)
3476 page_contents->glyphs.move_left();
3480 void html_printer::remove_courier_tabs (void)
3483 int line_start = TRUE;
3486 if (! page_contents->glyphs.is_empty()) {
3487 page_contents->glyphs.start_from_head();
3491 g = page_contents->glyphs.get_data();
3492 handle_state_assertion(g);
3493 nf = calc_nf(g, nf);
3496 if (line_start && nf && is_courier_until_eol()) {
3498 g = page_contents->glyphs.get_data();
3502 // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
3503 line_start = g->is_br() || (nf && g->is_eol());
3504 page_contents->glyphs.move_right();
3505 } while (! page_contents->glyphs.is_equal_to_head());
3509 void html_printer::insert_tab0_foreach_tab (void)
3511 text_glob *start_of_line = NULL;
3512 text_glob *g = NULL;
3513 int seen_tab = FALSE;
3514 int seen_col = FALSE;
3517 if (! page_contents->glyphs.is_empty()) {
3518 page_contents->glyphs.start_from_head();
3520 start_of_line = page_contents->glyphs.get_data();
3522 g = page_contents->glyphs.get_data();
3523 handle_state_assertion(g);
3524 nf = calc_nf(g, nf);
3532 if (g->is_br() || (nf && g->is_eol())) {
3534 page_contents->glyphs.move_right();
3535 g = page_contents->glyphs.get_data();
3536 handle_state_assertion(g);
3537 nf = calc_nf(g, nf);
3538 if (page_contents->glyphs.is_equal_to_head()) {
3539 if (seen_tab && !seen_col)
3540 insert_tab_0(start_of_line);
3543 } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
3544 // printf("\nstart_of_line is: %s\n", g->text_string);
3545 if (seen_tab && !seen_col) {
3546 insert_tab_0(start_of_line);
3547 page_contents->glyphs.move_to(g);
3554 page_contents->glyphs.move_right();
3555 } while (! page_contents->glyphs.is_equal_to_head());
3556 if (seen_tab && !seen_col)
3557 insert_tab_0(start_of_line);
3563 * update_min_max - updates the extent of a column, given the left and right
3564 * extents of a glyph, g.
3567 void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
3569 switch (type_of_col) {
3586 * add_table_end - moves left one glyph, adds a table end tag and adds a
3590 void html_printer::add_table_end (const char *
3591 #if defined(DEBUG_TABLES)
3596 page_contents->glyphs.move_left();
3598 #if defined(DEBUG_TABLES)
3599 page_contents->insert_tag(string(debug_string));
3604 * lookahead_for_tables - checks for .col tags and inserts table
3608 void html_printer::lookahead_for_tables (void)
3611 text_glob *start_of_line = NULL;
3612 text_glob *start_of_table = NULL;
3613 text_glob *last = NULL;
3614 colType type_of_col = none;
3615 int found_col = FALSE;
3617 int colmin = 0; // pacify compiler
3618 int colmax = 0; // pacify compiler
3619 html_table *tbl = new html_table(&html, -1);
3620 const char *tab_defs = NULL;
3623 int old_pageoffset = pageoffset;
3625 remove_courier_tabs();
3626 page_contents->dump_page();
3627 insert_tab0_foreach_tab();
3628 page_contents->dump_page();
3629 if (! page_contents->glyphs.is_empty()) {
3630 page_contents->glyphs.start_from_head();
3632 g = page_contents->glyphs.get_data();
3634 g = page_contents->glyphs.move_right_get_data();
3635 handle_state_assertion(g);
3636 if (page_contents->glyphs.is_equal_to_head()) {
3652 #if defined(DEBUG_TABLES)
3653 fprintf(stderr, " [") ;
3654 fprintf(stderr, g->text_string) ;
3655 fprintf(stderr, "] ") ;
3657 if (strcmp(g->text_string, "XXXXXXX") == 0)
3661 nf = calc_nf(g, nf);
3664 if (type_of_col == tab_tag && start_of_table != NULL) {
3665 page_contents->glyphs.move_left();
3667 start_of_table->remember_table(tbl);
3668 tbl = new html_table(&html, -1);
3669 page_contents->insert_tag(string("*** TAB -> COL ***"));
3670 if (tab_defs != NULL)
3671 tbl->tab_stops->init(tab_defs);
3672 start_of_table = NULL;
3675 type_of_col = col_tag;
3677 ncol = g->get_arg();
3681 } else if (g->is_tab()) {
3682 type_of_col = tab_tag;
3683 colmin = g->get_tab_args(&align);
3684 align = 'L'; // for now as 'C' and 'R' are broken
3685 ncol = tbl->find_tab_column(colmin);
3686 colmin += pageoffset + get_troff_indent();
3687 colmax = tbl->get_tab_pos(ncol+1);
3689 colmax += pageoffset + get_troff_indent();
3690 } else if (g->is_tab0()) {
3691 if (type_of_col == col_tag && start_of_table != NULL) {
3692 page_contents->glyphs.move_left();
3694 start_of_table->remember_table(tbl);
3695 tbl = new html_table(&html, -1);
3696 page_contents->insert_tag(string("*** COL -> TAB ***"));
3697 start_of_table = NULL;
3700 if (tab_defs != NULL)
3701 tbl->tab_stops->init(tab_defs);
3703 type_of_col = tab0_tag;
3706 colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
3707 } else if (! g->is_a_tag())
3708 update_min_max(type_of_col, &colmin, &colmax, g);
3710 if ((g->is_col() || g->is_tab() || g->is_tab0())
3711 && (start_of_line != NULL) && (start_of_table == NULL)) {
3712 start_of_table = insert_tab_ts(start_of_line);
3713 start_of_line = NULL;
3714 } else if (g->is_ce() && (start_of_table != NULL)) {
3715 add_table_end("*** CE ***");
3716 start_of_table->remember_table(tbl);
3717 tbl = new html_table(&html, -1);
3718 start_of_table = NULL;
3720 } else if (g->is_ta()) {
3721 tab_defs = g->text_string;
3723 if (type_of_col == col_tag)
3724 tbl->tab_stops->check_init(tab_defs);
3726 if (!tbl->tab_stops->compatible(tab_defs)) {
3727 if (start_of_table != NULL) {
3728 add_table_end("*** TABS ***");
3729 start_of_table->remember_table(tbl);
3730 tbl = new html_table(&html, -1);
3731 start_of_table = NULL;
3735 tbl->tab_stops->init(tab_defs);
3739 if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
3740 // we are in a table and have a glyph
3741 if ((ncol == 0) || (! tbl->add_column(ncol, colmin, colmax, align))) {
3743 add_table_end("*** NCOL == 0 ***");
3745 add_table_end("*** CROSSED COLS ***");
3747 start_of_table->remember_table(tbl);
3748 tbl = new html_table(&html, -1);
3749 start_of_table = NULL;
3756 * move onto next glob, check whether we are starting a new line
3758 g = page_contents->glyphs.move_right_get_data();
3759 handle_state_assertion(g);
3763 page_contents->glyphs.start_from_head();
3768 } else if (g->is_br() || (nf && g->is_eol())) {
3770 g = page_contents->glyphs.move_right_get_data();
3771 handle_state_assertion(g);
3772 nf = calc_nf(g, nf);
3773 } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
3780 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3782 #if defined(DEBUG_TABLES)
3783 fprintf(stderr, "finished scanning for tables\n");
3786 page_contents->glyphs.start_from_head();
3787 if (start_of_table != NULL) {
3789 while (last != page_contents->glyphs.get_data())
3790 page_contents->glyphs.move_left();
3793 start_of_table->remember_table(tbl);
3795 page_contents->insert_tag(string("*** LAST ***"));
3803 // and reset the registers
3804 pageoffset = old_pageoffset;
3810 void html_printer::flush_page (void)
3812 supress_sub_sup = TRUE;
3814 page_contents->dump_page();
3815 lookahead_for_tables();
3816 page_contents->dump_page();
3819 current_paragraph->done_para();
3820 current_paragraph->flush_text();
3822 // move onto a new page
3823 delete page_contents;
3824 #if defined(DEBUG_TABLES)
3825 fprintf(stderr, "\n\n*** flushed page ***\n\n");
3827 html.simple_comment("new page called");
3829 page_contents = new page;
3833 * determine_space - works out whether we need to write a space.
3834 * If last glyph is ajoining then no space emitted.
3837 void html_printer::determine_space (text_glob *g)
3839 if (current_paragraph->is_in_pre()) {
3841 * .nf has been specified
3843 while (output_hpos < g->minh) {
3844 output_hpos += space_width;
3845 current_paragraph->emit_space();
3848 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
3849 current_paragraph->emit_space();
3855 * is_line_start - returns TRUE if we are at the start of a line.
3858 int html_printer::is_line_start (int nf)
3860 int line_start = FALSE;
3862 text_glob *orig = page_contents->glyphs.get_data();
3865 if (! page_contents->glyphs.is_equal_to_head()) {
3867 page_contents->glyphs.move_left();
3868 g = page_contents->glyphs.get_data();
3869 result = g->is_a_tag();
3872 else if (g->is_nf())
3874 line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
3875 } while ((!line_start) && (result));
3877 * now restore our previous position.
3879 while (page_contents->glyphs.get_data() != orig)
3880 page_contents->glyphs.move_right();
3886 * is_font_courier - returns TRUE if the font, f, is courier.
3889 int html_printer::is_font_courier (font *f)
3892 const char *fontname = f->get_name();
3894 return( (fontname != 0) && (fontname[0] == 'C') );
3900 * end_font - shuts down the font corresponding to fontname.
3903 void html_printer::end_font (const char *fontname)
3905 if (strcmp(fontname, "B") == 0) {
3906 current_paragraph->done_bold();
3907 } else if (strcmp(fontname, "I") == 0) {
3908 current_paragraph->done_italic();
3909 } else if (strcmp(fontname, "BI") == 0) {
3910 current_paragraph->done_bold();
3911 current_paragraph->done_italic();
3912 } else if (strcmp(fontname, "CR") == 0) {
3913 current_paragraph->done_tt();
3914 } else if (strcmp(fontname, "CI") == 0) {
3915 current_paragraph->done_italic();
3916 current_paragraph->done_tt();
3917 } else if (strcmp(fontname, "CB") == 0) {
3918 current_paragraph->done_bold();
3919 current_paragraph->done_tt();
3920 } else if (strcmp(fontname, "CBI") == 0) {
3921 current_paragraph->done_bold();
3922 current_paragraph->done_italic();
3923 current_paragraph->done_tt();
3928 * start_font - starts the font corresponding to name.
3931 void html_printer::start_font (const char *fontname)
3933 if (strcmp(fontname, "R") == 0) {
3934 current_paragraph->done_bold();
3935 current_paragraph->done_italic();
3936 current_paragraph->done_tt();
3937 } else if (strcmp(fontname, "B") == 0) {
3938 current_paragraph->do_bold();
3939 } else if (strcmp(fontname, "I") == 0) {
3940 current_paragraph->do_italic();
3941 } else if (strcmp(fontname, "BI") == 0) {
3942 current_paragraph->do_bold();
3943 current_paragraph->do_italic();
3944 } else if (strcmp(fontname, "CR") == 0) {
3945 if ((! fill_on) && (is_courier_until_eol()) &&
3946 is_line_start(! fill_on)) {
3947 current_paragraph->do_pre();
3949 current_paragraph->do_tt();
3950 } else if (strcmp(fontname, "CI") == 0) {
3951 if ((! fill_on) && (is_courier_until_eol()) &&
3952 is_line_start(! fill_on)) {
3953 current_paragraph->do_pre();
3955 current_paragraph->do_tt();
3956 current_paragraph->do_italic();
3957 } else if (strcmp(fontname, "CB") == 0) {
3958 if ((! fill_on) && (is_courier_until_eol()) &&
3959 is_line_start(! fill_on)) {
3960 current_paragraph->do_pre();
3962 current_paragraph->do_tt();
3963 current_paragraph->do_bold();
3964 } else if (strcmp(fontname, "CBI") == 0) {
3965 if ((! fill_on) && (is_courier_until_eol()) &&
3966 is_line_start(! fill_on)) {
3967 current_paragraph->do_pre();
3969 current_paragraph->do_tt();
3970 current_paragraph->do_italic();
3971 current_paragraph->do_bold();
3976 * start_size - from is old font size, to is the new font size.
3977 * The html increase <big> and <small> decrease alters the
3978 * font size by 20%. We try and map these onto glyph sizes.
3981 void html_printer::start_size (int from, int to)
3985 current_paragraph->do_big();
3986 from += SIZE_INCREMENT;
3988 } else if (from > to) {
3990 current_paragraph->do_small();
3991 from -= SIZE_INCREMENT;
3997 * do_font - checks to see whether we need to alter the html font.
4000 void html_printer::do_font (text_glob *g)
4003 * check if the output_style.point_size has not been set yet
4004 * this allow users to place .ps at the top of their troff files
4005 * and grohtml can then treat the .ps value as the base font size (3)
4007 if (output_style.point_size == -1) {
4008 output_style.point_size = pointsize;
4011 if (g->text_style.f != output_style.f) {
4012 if (output_style.f != 0) {
4013 end_font(output_style.f->get_name());
4015 output_style.f = g->text_style.f;
4016 if (output_style.f != 0) {
4017 start_font(output_style.f->get_name());
4020 if (output_style.point_size != g->text_style.point_size) {
4022 if ((output_style.point_size > 0) &&
4023 (g->text_style.point_size > 0)) {
4024 start_size(output_style.point_size, g->text_style.point_size);
4026 if (g->text_style.point_size > 0) {
4027 output_style.point_size = g->text_style.point_size;
4030 if (output_style.col != g->text_style.col) {
4031 current_paragraph->done_color();
4032 output_style.col = g->text_style.col;
4033 current_paragraph->do_color(&output_style.col);
4038 * start_subscript - returns TRUE if, g, looks like a subscript start.
4041 int html_printer::start_subscript (text_glob *g)
4044 int height = output_style.point_size*r/72;
4046 return( (output_style.point_size != 0) &&
4047 (output_vpos < g->minv) &&
4048 (output_vpos-height > g->maxv) &&
4049 (output_style.point_size > g->text_style.point_size) );
4053 * start_superscript - returns TRUE if, g, looks like a superscript start.
4056 int html_printer::start_superscript (text_glob *g)
4059 int height = output_style.point_size*r/72;
4061 return( (output_style.point_size != 0) &&
4062 (output_vpos > g->minv) &&
4063 (output_vpos-height < g->maxv) &&
4064 (output_style.point_size > g->text_style.point_size) );
4068 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
4071 int html_printer::end_subscript (text_glob *g)
4074 int height = output_style.point_size*r/72;
4076 return( (output_style.point_size != 0) &&
4077 (g->minv < output_vpos) &&
4078 (output_vpos-height > g->maxv) &&
4079 (output_style.point_size < g->text_style.point_size) );
4083 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
4086 int html_printer::end_superscript (text_glob *g)
4089 int height = output_style.point_size*r/72;
4091 return( (output_style.point_size != 0) &&
4092 (g->minv > output_vpos) &&
4093 (output_vpos-height < g->maxv) &&
4094 (output_style.point_size < g->text_style.point_size) );
4098 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4099 * start/end and it calls the services of html-text to issue the
4103 void html_printer::do_sup_or_sub (text_glob *g)
4105 if (! supress_sub_sup) {
4106 if (start_subscript(g)) {
4107 current_paragraph->do_sub();
4108 } else if (start_superscript(g)) {
4109 current_paragraph->do_sup();
4110 } else if (end_subscript(g)) {
4111 current_paragraph->done_sub();
4112 } else if (end_superscript(g)) {
4113 current_paragraph->done_sup();
4119 * do_end_para - writes out the html text after shutting down the
4120 * current paragraph.
4123 void html_printer::do_end_para (text_glob *g)
4126 current_paragraph->done_para();
4127 current_paragraph->remove_para_space();
4128 html.put_string(g->text_string+9);
4129 output_vpos = g->minv;
4130 output_hpos = g->maxh;
4131 output_vpos_max = g->maxv;
4132 supress_sub_sup = FALSE;
4136 * emit_html - write out the html text
4139 void html_printer::emit_html (text_glob *g)
4143 current_paragraph->do_emittext(g->text_string, g->text_length);
4144 output_vpos = g->minv;
4145 output_hpos = g->maxh;
4146 output_vpos_max = g->maxv;
4147 supress_sub_sup = FALSE;
4151 * flush_sbuf - flushes the current sbuf into the list of glyphs.
4154 void html_printer::flush_sbuf()
4156 if (sbuf.length() > 0) {
4157 int r=font::res; // resolution of the device
4158 set_style(sbuf_style);
4160 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
4161 font *bold_font = make_bold(sbuf_style.f);
4162 if (bold_font != NULL)
4163 sbuf_style.f = bold_font;
4166 page_contents->add(&sbuf_style, sbuf,
4168 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4169 sbuf_vpos , sbuf_end_hpos);
4171 output_hpos = sbuf_end_hpos;
4172 output_vpos = sbuf_vpos;
4173 last_sbuf_length = 0;
4174 sbuf_prev_hpos = sbuf_end_hpos;
4175 overstrike_detected = FALSE;
4180 void html_printer::set_line_thickness(const environment *env)
4182 line_thickness = env->size;
4185 void html_printer::draw(int code, int *p, int np, const environment *env)
4192 page_contents->add_line(&sbuf_style,
4194 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
4196 error("2 arguments required for line");
4203 line_thickness = -1;
4205 // troff gratuitously adds an extra 0
4206 if (np != 1 && np != 2) {
4207 error("0 or 1 argument required for thickness");
4210 line_thickness = p[0];
4234 // fill with color env->fill
4235 if (background != NULL)
4237 background = new color;
4238 *background = *env->fill;
4242 error("unrecognised drawing command `%1'", char(code));
4247 html_printer::html_printer()
4248 : html(0, MAX_LINE_LENGTH),
4249 no_of_printed_pages(0),
4250 last_sbuf_length(0),
4251 overstrike_detected(FALSE),
4254 output_vpos_max(-1),
4256 inside_font_style(0),
4259 supress_sub_sup(TRUE),
4260 cutoff_heading(100),
4273 pointsize(base_point_size),
4275 background(default_background),
4278 seen_pageoffset(FALSE),
4280 seen_linelength(FALSE),
4289 file_list.add_new_file(xtmpfile());
4290 html.set_file(file_list.get_file());
4291 if (font::hor != 24)
4292 fatal("horizontal resolution must be 24");
4293 if (font::vert != 40)
4294 fatal("vertical resolution must be 40");
4296 // should be sorted html..
4297 if (font::res % (font::sizescale*72) != 0)
4298 fatal("res must be a multiple of 72*sizescale");
4302 while (r % 10 == 0) {
4307 html.set_fixed_point(point);
4308 space_glyph = name_to_glyph("space");
4309 space_width = font::hor;
4310 paper_length = font::paperlength;
4311 linelength = font::res*13/2;
4312 if (paper_length == 0)
4313 paper_length = 11*font::res;
4315 page_contents = new page();
4319 * add_to_sbuf - adds character code or name to the sbuf.
4322 void html_printer::add_to_sbuf (glyph *g, const string &s)
4324 if (sbuf_style.f == NULL)
4327 const char *html_glyph = NULL;
4328 unsigned int code = sbuf_style.f->get_code(g);
4331 if (sbuf_style.f->contains(g))
4332 html_glyph = get_html_entity(sbuf_style.f->get_code(g));
4336 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
4337 html_glyph = to_unicode(code);
4339 html_glyph = get_html_translation(sbuf_style.f, s);
4341 last_sbuf_length = sbuf.length();
4342 if (html_glyph == NULL)
4343 sbuf += ((char)code);
4348 int html_printer::sbuf_continuation (glyph *g, const char *name,
4349 const environment *env, int w)
4352 * lets see whether the glyph is closer to the end of sbuf
4354 if ((sbuf_end_hpos == env->hpos)
4355 || ((sbuf_prev_hpos < sbuf_end_hpos)
4356 && (env->hpos < sbuf_end_hpos)
4357 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
4358 add_to_sbuf(g, name);
4359 sbuf_prev_hpos = sbuf_end_hpos;
4360 sbuf_end_hpos += w + sbuf_kern;
4363 if ((env->hpos >= sbuf_end_hpos) &&
4364 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
4366 * lets see whether a space is needed or not
4369 if (env->hpos-sbuf_end_hpos < space_width) {
4370 add_to_sbuf(g, name);
4371 sbuf_prev_hpos = sbuf_end_hpos;
4372 sbuf_end_hpos = env->hpos + w;
4381 * get_html_translation - given the position of the character and its name
4382 * return the device encoding for such character.
4385 const char *get_html_translation (font *f, const string &name)
4387 if ((f == 0) || name.empty())
4390 glyph *g = name_to_glyph((char *)(name + '\0').contents());
4392 return get_html_entity(f->get_code(g));
4399 * get_html_entity - given a Unicode character's code point, return a
4400 * HTML entity that represents the character, if the
4401 * character cannot represent itself in all contexts.
4402 * The return value, if non-NULL, is allocated in a static buffer and is
4403 * only valid until the next call of this function.
4405 static const char *get_html_entity (unsigned int code)
4407 if (code < UNICODE_DESC_START) {
4409 case 0x0022: return """;
4410 case 0x0026: return "&";
4411 case 0x003C: return "<";
4412 case 0x003E: return ">";
4413 default: return NULL;
4417 case 0x00A0: return " ";
4418 case 0x00A1: return "¡";
4419 case 0x00A2: return "¢";
4420 case 0x00A3: return "£";
4421 case 0x00A4: return "¤";
4422 case 0x00A5: return "¥";
4423 case 0x00A6: return "¦";
4424 case 0x00A7: return "§";
4425 case 0x00A8: return "¨";
4426 case 0x00A9: return "©";
4427 case 0x00AA: return "ª";
4428 case 0x00AB: return "«";
4429 case 0x00AC: return "¬";
4430 case 0x00AE: return "®";
4431 case 0x00AF: return "¯";
4432 case 0x00B0: return "°";
4433 case 0x00B1: return "±";
4434 case 0x00B2: return "²";
4435 case 0x00B3: return "³";
4436 case 0x00B4: return "´";
4437 case 0x00B5: return "µ";
4438 case 0x00B6: return "¶";
4439 case 0x00B7: return "·";
4440 case 0x00B8: return "¸";
4441 case 0x00B9: return "¹";
4442 case 0x00BA: return "º";
4443 case 0x00BB: return "»";
4444 case 0x00BC: return "¼";
4445 case 0x00BD: return "½";
4446 case 0x00BE: return "¾";
4447 case 0x00BF: return "¿";
4448 case 0x00C0: return "À";
4449 case 0x00C1: return "Á";
4450 case 0x00C2: return "Â";
4451 case 0x00C3: return "Ã";
4452 case 0x00C4: return "Ä";
4453 case 0x00C5: return "Å";
4454 case 0x00C6: return "Æ";
4455 case 0x00C7: return "Ç";
4456 case 0x00C8: return "È";
4457 case 0x00C9: return "É";
4458 case 0x00CA: return "Ê";
4459 case 0x00CB: return "Ë";
4460 case 0x00CC: return "Ì";
4461 case 0x00CD: return "Í";
4462 case 0x00CE: return "Î";
4463 case 0x00CF: return "Ï";
4464 case 0x00D0: return "Ð";
4465 case 0x00D1: return "Ñ";
4466 case 0x00D2: return "Ò";
4467 case 0x00D3: return "Ó";
4468 case 0x00D4: return "Ô";
4469 case 0x00D5: return "Õ";
4470 case 0x00D6: return "Ö";
4471 case 0x00D7: return "×";
4472 case 0x00D8: return "Ø";
4473 case 0x00D9: return "Ù";
4474 case 0x00DA: return "Ú";
4475 case 0x00DB: return "Û";
4476 case 0x00DC: return "Ü";
4477 case 0x00DD: return "Ý";
4478 case 0x00DE: return "Þ";
4479 case 0x00DF: return "ß";
4480 case 0x00E0: return "à";
4481 case 0x00E1: return "á";
4482 case 0x00E2: return "â";
4483 case 0x00E3: return "ã";
4484 case 0x00E4: return "ä";
4485 case 0x00E5: return "å";
4486 case 0x00E6: return "æ";
4487 case 0x00E7: return "ç";
4488 case 0x00E8: return "è";
4489 case 0x00E9: return "é";
4490 case 0x00EA: return "ê";
4491 case 0x00EB: return "ë";
4492 case 0x00EC: return "ì";
4493 case 0x00ED: return "í";
4494 case 0x00EE: return "î";
4495 case 0x00EF: return "ï";
4496 case 0x00F0: return "ð";
4497 case 0x00F1: return "ñ";
4498 case 0x00F2: return "ò";
4499 case 0x00F3: return "ó";
4500 case 0x00F4: return "ô";
4501 case 0x00F5: return "õ";
4502 case 0x00F6: return "ö";
4503 case 0x00F7: return "÷";
4504 case 0x00F8: return "ø";
4505 case 0x00F9: return "ù";
4506 case 0x00FA: return "ú";
4507 case 0x00FB: return "û";
4508 case 0x00FC: return "ü";
4509 case 0x00FD: return "ý";
4510 case 0x00FE: return "þ";
4511 case 0x00FF: return "ÿ";
4512 case 0x0152: return "Œ";
4513 case 0x0153: return "œ";
4514 case 0x0160: return "Š";
4515 case 0x0161: return "š";
4516 case 0x0178: return "Ÿ";
4517 case 0x0192: return "ƒ";
4518 case 0x0391: return "Α";
4519 case 0x0392: return "Β";
4520 case 0x0393: return "Γ";
4521 case 0x0394: return "Δ";
4522 case 0x0395: return "Ε";
4523 case 0x0396: return "Ζ";
4524 case 0x0397: return "Η";
4525 case 0x0398: return "Θ";
4526 case 0x0399: return "Ι";
4527 case 0x039A: return "Κ";
4528 case 0x039B: return "Λ";
4529 case 0x039C: return "Μ";
4530 case 0x039D: return "Ν";
4531 case 0x039E: return "Ξ";
4532 case 0x039F: return "Ο";
4533 case 0x03A0: return "Π";
4534 case 0x03A1: return "Ρ";
4535 case 0x03A3: return "Σ";
4536 case 0x03A4: return "Τ";
4537 case 0x03A5: return "Υ";
4538 case 0x03A6: return "Φ";
4539 case 0x03A7: return "Χ";
4540 case 0x03A8: return "Ψ";
4541 case 0x03A9: return "Ω";
4542 case 0x03B1: return "α";
4543 case 0x03B2: return "β";
4544 case 0x03B3: return "γ";
4545 case 0x03B4: return "δ";
4546 case 0x03B5: return "ε";
4547 case 0x03B6: return "ζ";
4548 case 0x03B7: return "η";
4549 case 0x03B8: return "θ";
4550 case 0x03B9: return "ι";
4551 case 0x03BA: return "κ";
4552 case 0x03BB: return "λ";
4553 case 0x03BC: return "μ";
4554 case 0x03BD: return "ν";
4555 case 0x03BE: return "ξ";
4556 case 0x03BF: return "ο";
4557 case 0x03C0: return "π";
4558 case 0x03C1: return "ρ";
4559 case 0x03C2: return "ς";
4560 case 0x03C3: return "σ";
4561 case 0x03C4: return "τ";
4562 case 0x03C5: return "υ";
4563 case 0x03C6: return "φ";
4564 case 0x03C7: return "χ";
4565 case 0x03C8: return "ψ";
4566 case 0x03C9: return "ω";
4567 case 0x03D1: return "ϑ";
4568 case 0x03D6: return "ϖ";
4569 case 0x2013: return "–";
4570 case 0x2014: return "—";
4571 case 0x2018: return "‘";
4572 case 0x2019: return "’";
4573 case 0x201A: return "‚";
4574 case 0x201C: return "“";
4575 case 0x201D: return "”";
4576 case 0x201E: return "„";
4577 case 0x2020: return "†";
4578 case 0x2021: return "‡";
4579 case 0x2022: return "•";
4580 case 0x2030: return "‰";
4581 case 0x2032: return "′";
4582 case 0x2033: return "″";
4583 case 0x2039: return "‹";
4584 case 0x203A: return "›";
4585 case 0x203E: return "‾";
4586 case 0x2044: return "⁄";
4587 case 0x20AC: return "€";
4588 case 0x2111: return "ℑ";
4589 case 0x2118: return "℘";
4590 case 0x211C: return "ℜ";
4591 case 0x2122: return "™";
4592 case 0x2135: return "ℵ";
4593 case 0x2190: return "←";
4594 case 0x2191: return "↑";
4595 case 0x2192: return "→";
4596 case 0x2193: return "↓";
4597 case 0x2194: return "↔";
4598 case 0x21D0: return "⇐";
4599 case 0x21D1: return "⇑";
4600 case 0x21D2: return "⇒";
4601 case 0x21D3: return "⇓";
4602 case 0x21D4: return "⇔";
4603 case 0x2200: return "∀";
4604 case 0x2202: return "∂";
4605 case 0x2203: return "∃";
4606 case 0x2205: return "∅";
4607 case 0x2207: return "∇";
4608 case 0x2208: return "∈";
4609 case 0x2209: return "∉";
4610 case 0x220B: return "∋";
4611 case 0x220F: return "∏";
4612 case 0x2211: return "∑";
4613 case 0x2212: return "−";
4614 case 0x2217: return "∗";
4615 case 0x221A: return "√";
4616 case 0x221D: return "∝";
4617 case 0x221E: return "∞";
4618 case 0x2220: return "∠";
4619 case 0x2227: return "∧";
4620 case 0x2228: return "∨";
4621 case 0x2229: return "∩";
4622 case 0x222A: return "∪";
4623 case 0x222B: return "∫";
4624 case 0x2234: return "∴";
4625 case 0x223C: return "∼";
4626 case 0x2245: return "≅";
4627 case 0x2248: return "≈";
4628 case 0x2260: return "≠";
4629 case 0x2261: return "≡";
4630 case 0x2264: return "≤";
4631 case 0x2265: return "≥";
4632 case 0x2282: return "⊂";
4633 case 0x2283: return "⊃";
4634 case 0x2284: return "⊄";
4635 case 0x2286: return "⊆";
4636 case 0x2287: return "⊇";
4637 case 0x2295: return "⊕";
4638 case 0x2297: return "⊗";
4639 case 0x22A5: return "⊥";
4640 case 0x22C5: return "⋅";
4641 case 0x2308: return "⌈";
4642 case 0x2309: return "⌉";
4643 case 0x230A: return "⌊";
4644 case 0x230B: return "⌋";
4645 case 0x2329: return "⟨";
4646 case 0x232A: return "⟩";
4647 case 0x25CA: return "◊";
4648 case 0x2660: return "♠";
4649 case 0x2663: return "♣";
4650 case 0x2665: return "♥";
4651 case 0x2666: return "♦";
4652 case 0x27E8: return "⟨";
4653 case 0x27E9: return "⟩";
4654 default: return to_unicode(code);
4660 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
4661 * a previous glyph in sbuf.
4662 * If TRUE the font is changed to bold and the previous sbuf
4666 int html_printer::overstrike(glyph *g, const char *name, const environment *env, int w)
4668 if ((env->hpos < sbuf_end_hpos)
4669 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
4671 * at this point we have detected an overlap
4673 if (overstrike_detected) {
4674 /* already detected, remove previous glyph and use this glyph */
4675 sbuf.set_length(last_sbuf_length);
4676 add_to_sbuf(g, name);
4677 sbuf_end_hpos = env->hpos + w;
4680 /* first time we have detected an overstrike in the sbuf */
4681 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
4682 if (! is_bold(sbuf_style.f))
4684 overstrike_detected = TRUE;
4685 add_to_sbuf(g, name);
4686 sbuf_end_hpos = env->hpos + w;
4694 * set_char - adds a character into the sbuf if it is a continuation
4695 * with the previous word otherwise flush the current sbuf
4696 * and add character anew.
4699 void html_printer::set_char(glyph *g, font *f, const environment *env,
4700 int w, const char *name)
4702 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
4703 if (sty.slant != 0) {
4704 if (sty.slant > 80 || sty.slant < -80) {
4705 error("silly slant `%1' degrees", sty.slant);
4709 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
4710 && (sbuf_continuation(g, name, env, w)
4711 || overstrike(g, name, env, w)))
4715 if (sbuf_style.f == NULL)
4717 add_to_sbuf(g, name);
4718 sbuf_end_hpos = env->hpos + w;
4719 sbuf_start_hpos = env->hpos;
4720 sbuf_prev_hpos = env->hpos;
4721 sbuf_vpos = env->vpos;
4727 * set_numbered_char - handle numbered characters.
4728 * Negative values are interpreted as unbreakable spaces;
4729 * the value (taken positive) gives the width.
4732 void html_printer::set_numbered_char(int num, const environment *env,
4738 num = 160; //
4740 glyph *g = number_to_glyph(num);
4741 int fn = env->fontno;
4742 if (fn < 0 || fn >= nfonts) {
4743 error("bad font position `%1'", fn);
4746 font *f = font_table[fn];
4748 error("no font mounted at `%1'", fn);
4751 if (!f->contains(g)) {
4752 error("font `%1' does not contain numbered character %2",
4761 w = f->get_width(g, env->size);
4765 set_char(g, f, env, w, 0);
4768 glyph *html_printer::set_char_and_width(const char *nm, const environment *env,
4769 int *widthp, font **f)
4771 glyph *g = name_to_glyph(nm);
4772 int fn = env->fontno;
4773 if (fn < 0 || fn >= nfonts) {
4774 error("bad font position `%1'", fn);
4775 return UNDEFINED_GLYPH;
4777 *f = font_table[fn];
4779 error("no font mounted at `%1'", fn);
4780 return UNDEFINED_GLYPH;
4782 if (!(*f)->contains(g)) {
4783 if (nm[0] != '\0' && nm[1] == '\0')
4784 error("font `%1' does not contain ascii character `%2'",
4788 error("font `%1' does not contain special character `%2'",
4791 return UNDEFINED_GLYPH;
4793 int w = (*f)->get_width(g, env->size);
4801 * write_title - writes the title to this document
4804 void html_printer::write_title (int in_head)
4806 if (title.has_been_found) {
4808 html.put_string("<title>");
4809 html.put_string(title.text);
4810 html.put_string("</title>").nl().nl();
4812 title.has_been_written = TRUE;
4813 if (title.with_h1) {
4814 if (dialect == xhtml)
4815 html.put_string("<h1>");
4817 html.put_string("<h1 align=\"center\">");
4818 html.put_string(title.text);
4819 html.put_string("</h1>").nl().nl();
4822 } else if (in_head) {
4823 // place empty title tags to help conform to `tidy'
4824 html.put_string("<title></title>").nl();
4829 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
4832 static void write_rule (void)
4835 if (dialect == xhtml)
4836 fputs("<hr/>\n", stdout);
4838 fputs("<hr>\n", stdout);
4842 void html_printer::begin_page(int n)
4845 #if defined(DEBUGGING)
4846 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
4848 no_of_printed_pages++;
4851 output_style.point_size= -1;
4852 output_space_code = 32;
4853 output_draw_point_size = -1;
4854 output_line_thickness = -1;
4857 output_vpos_max = -1;
4858 current_paragraph = new html_text(&html, dialect);
4859 do_indent(get_troff_indent(), pageoffset, linelength);
4860 current_paragraph->do_para("", FALSE);
4863 void html_printer::end_page(int)
4869 font *html_printer::make_font(const char *nm)
4871 return html_font::load_html_font(nm);
4874 void html_printer::do_body (void)
4876 if (background == NULL)
4877 fputs("<body>\n\n", stdout);
4879 unsigned int r, g, b;
4882 background->get_rgb(&r, &g, &b);
4883 // we have to scale 0..0xFFFF to 0..0xFF
4884 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
4886 fputs("<body bgcolor=\"#", stdout);
4888 fputs("\">\n\n", stdout);
4893 * emit_link - generates: <a href="to">name</a>
4896 void html_printer::emit_link (const string &to, const char *name)
4898 fputs("<a href=\"", stdout);
4899 fputs(to.contents(), stdout);
4900 fputs("\">", stdout);
4901 fputs(name, stdout);
4902 fputs("</a>", stdout);
4906 * write_navigation - writes out the links which navigate between
4910 void html_printer::write_navigation (const string &top, const string &prev,
4911 const string &next, const string ¤t)
4913 int need_bar = FALSE;
4915 if (multiple_files) {
4916 current_paragraph->done_para();
4919 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
4920 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
4921 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
4922 "<tr><td class=\"left\">", stdout);
4923 handle_valid_flag(FALSE);
4924 fputs("[ ", stdout);
4925 if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
4926 emit_link(prev, "prev");
4929 if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
4931 fputs(" | ", stdout);
4932 emit_link(next, "next");
4935 if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
4937 fputs(" | ", stdout);
4938 emit_link(top, "top");
4940 fputs(" ]\n", stdout);
4943 fputs("</td><td class=\"right\"><i><small>"
4944 "This document was produced using "
4945 "<a href=\"http://www.gnu.org/software/groff/\">"
4947 fputs(Version_string, stdout);
4948 fputs("</a>.</small></i></td></tr></table>\n", stdout);
4955 * do_file_components - scan the file list copying each temporary
4956 * file in turn. This is used twofold:
4958 * firstly to emit section heading links,
4959 * between file fragments if required and
4960 * secondly to generate jobname file fragments
4964 void html_printer::do_file_components (void)
4966 int fragment_no = 1;
4972 file_list.start_of_list();
4973 top = string(job_name);
4974 if (dialect == xhtml)
4975 top += string(".xhtml");
4977 top += string(".html");
4979 next = file_list.next_file_name();
4982 while (file_list.get_file() != 0) {
4983 if (fseek(file_list.get_file(), 0L, 0) < 0)
4984 fatal("fseek on temporary file failed");
4985 html.copy_file(file_list.get_file());
4986 fclose(file_list.get_file());
4988 file_list.move_next();
4989 if (file_list.is_new_output_file()) {
4990 #ifdef LONG_FOR_TIME_T
4996 if (fragment_no > 1)
4997 write_navigation(top, prev, next, current);
5000 next = file_list.next_file_name();
5002 string split_file = file_list.file_name();
5005 freopen(split_file.contents(), "w", stdout);
5007 if (dialect == xhtml)
5008 writeHeadMetaStyle();
5010 html.begin_comment("Creator : ")
5011 .put_string("groff ")
5012 .put_string("version ")
5013 .put_string(Version_string)
5017 html.begin_comment("CreationDate: ")
5018 .put_string(ctime(&t), strlen(ctime(&t))-1)
5021 if (dialect == html4)
5022 writeHeadMetaStyle();
5024 html.put_string("<title>");
5025 html.put_string(split_file.contents());
5026 html.put_string("</title>").nl().nl();
5028 fputs(head_info.contents(), stdout);
5029 fputs("</head>\n", stdout);
5030 write_navigation(top, prev, next, current);
5032 if (file_list.are_links_required())
5033 header.write_headings(stdout, TRUE);
5035 if (fragment_no > 1)
5036 write_navigation(top, prev, next, current);
5038 current_paragraph->done_para();
5042 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
5043 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
5044 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
5045 "<tr><td class=\"left\">", stdout);
5046 handle_valid_flag(TRUE);
5048 fputs("</td><td class=\"right\"><i><small>"
5049 "This document was produced using "
5050 "<a href=\"http://www.gnu.org/software/groff/\">"
5052 fputs(Version_string, stdout);
5053 fputs("</a>.</small></i></td></tr></table>\n", stdout);
5061 * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
5062 * related information.
5065 void html_printer::writeHeadMetaStyle (void)
5067 if (dialect == html4) {
5068 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
5069 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
5070 fputs("<html>\n", stdout);
5071 fputs("<head>\n", stdout);
5072 fputs("<meta name=\"generator\" "
5073 "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
5074 fputs("<meta http-equiv=\"Content-Type\" "
5075 "content=\"text/html; charset=US-ASCII\">\n", stdout);
5076 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
5077 fputs("<style type=\"text/css\">\n", stdout);
5080 fputs("<?xml version=\"1.0\" encoding=\"us-ascii\"?>\n", stdout);
5081 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\"\n", stdout);
5082 fputs(" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\"\n", stdout);
5083 fputs(" [<!ENTITY mathml \"http://www.w3.org/1998/Math/MathML\">]>\n", stdout);
5085 fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n",
5087 fputs("<head>\n", stdout);
5088 fputs("<meta name=\"generator\" "
5089 "content=\"groff -Txhtml, see www.gnu.org\"/>\n", stdout);
5090 fputs("<meta http-equiv=\"Content-Type\" "
5091 "content=\"text/html; charset=US-ASCII\"/>\n", stdout);
5092 fputs("<meta name=\"Content-Style\" content=\"text/css\"/>\n", stdout);
5093 fputs("<style type=\"text/css\">\n", stdout);
5094 fputs(" .center { text-align: center }\n", stdout);
5095 fputs(" .right { text-align: right }\n", stdout);
5097 fputs(" p { margin-top: 0; margin-bottom: 0; "
5098 "vertical-align: top }\n", stdout);
5099 fputs(" pre { margin-top: 0; margin-bottom: 0; "
5100 "vertical-align: top }\n", stdout);
5101 fputs(" table { margin-top: 0; margin-bottom: 0; "
5102 "vertical-align: top }\n", stdout);
5103 fputs(" h1 { text-align: center }\n", stdout);
5104 fputs("</style>\n", stdout);
5107 html_printer::~html_printer()
5109 #ifdef LONG_FOR_TIME_T
5115 if (current_paragraph)
5116 current_paragraph->flush_text();
5118 html.set_file(stdout);
5120 if (dialect == xhtml)
5121 writeHeadMetaStyle();
5123 html.begin_comment("Creator : ")
5124 .put_string("groff ")
5125 .put_string("version ")
5126 .put_string(Version_string)
5130 html.begin_comment("CreationDate: ")
5131 .put_string(ctime(&t), strlen(ctime(&t))-1)
5134 if (dialect == html4)
5135 writeHeadMetaStyle();
5139 fputs(head_info.contents(), stdout);
5140 fputs("</head>\n", stdout);
5144 header.write_headings(stdout, FALSE);
5146 #if defined(DEBUGGING)
5147 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
5152 if (multiple_files) {
5153 fputs("</body>\n", stdout);
5154 fputs("</html>\n", stdout);
5155 do_file_components();
5157 do_file_components();
5158 fputs("</body>\n", stdout);
5159 fputs("</html>\n", stdout);
5164 * get_str - returns a dupicate of string, s. The duplicate
5165 * string is terminated at the next ',' or ']'.
5168 static char *get_str (const char *s, char **n)
5173 while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
5180 (*n) = (char *)&s[i+1];
5182 (*n) = (char *)&s[i];
5186 (*n) = (char *)&s[1];
5193 * make_val - creates a string from if s is NULL.
5196 char *make_val (char *s, int v, char *id, char *f, char *l)
5201 sprintf(buf, "%d", v);
5202 return strsave(buf);
5206 * check that value, s, is the same as, v.
5214 f = (char *)"stdin";
5216 l = (char *)"<none>";
5217 fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %d and was given %s\n",
5225 * handle_assertion - handles the assertions created via .www:ASSERT
5226 * in www.tmac. See www.tmac for examples.
5227 * This method should be called as we are
5228 * parsing the ditroff input. It checks the x, y
5229 * position assertions. It does _not_ check the
5230 * troff state assertions as these are unknown at this
5234 void html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
5237 char *cmd = get_str(s, &n);
5238 char *id = get_str(n, &n);
5239 char *val = get_str(n, &n);
5240 char *file= get_str(n, &n);
5241 char *line= get_str(n, &n);
5243 if (strcmp(cmd, "assertion:[x") == 0)
5244 as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
5245 else if (strcmp(cmd, "assertion:[y") == 0)
5246 as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
5248 if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
5249 page_contents->add_tag(&sbuf_style, string(s),
5250 line_number, minv, minh, maxv, maxh);
5254 * build_state_assertion - builds the troff state assertions.
5257 void html_printer::handle_state_assertion (text_glob *g)
5259 if (g != NULL && g->is_a_tag() &&
5260 (strncmp(g->text_string, "assertion:[", 11) == 0)) {
5261 char *n = (char *)&g->text_string[11];
5262 char *cmd = get_str(n, &n);
5263 char *val = get_str(n, &n);
5264 (void)get_str(n, &n); // unused
5265 char *file= get_str(n, &n);
5266 char *line= get_str(n, &n);
5268 as.build(cmd, val, file, line);
5273 * special - handle all x X requests from troff. For post-html they
5274 * allow users to pass raw html commands, turn auto linked
5275 * headings off/on etc.
5278 void html_printer::special(char *s, const environment *env, char type)
5284 if (env->fontno >= 0) {
5285 style sty(get_font_from_index(env->fontno), env->size, env->height,
5286 env->slant, env->fontno, *env->col);
5290 if (strncmp(s, "html:", 5) == 0) {
5291 int r=font::res; /* resolution of the device */
5292 font *f=sbuf_style.f;
5297 f = font::load_font("TR", &found);
5301 * need to pass rest of string through to html output during flush
5303 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
5305 env->vpos-env->size*r/72, env->hpos,
5306 env->vpos , env->hpos,
5310 * assume that the html command has no width, if it does then
5311 * hopefully troff will have fudged this in a macro by
5312 * requesting that the formatting move right by the appropriate
5315 } else if ((strncmp(s, "html</p>:", 9) == 0) ||
5316 (strncmp(s, "html<?p>:", 9) == 0) ||
5317 (strncmp(s, "math<?p>:", 9) == 0)) {
5318 int r=font::res; /* resolution of the device */
5319 font *f=sbuf_style.f;
5325 f = font::load_font("TR", &found);
5328 if (strncmp(s, "math<?p>:", 9) == 0) {
5329 if (strncmp((char *)&s[9], "<math>", 6) == 0) {
5332 t += "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
5333 t += (char *)&s[15];
5340 * need to pass all of string through to html output during flush
5342 page_contents->add_and_encode(&sbuf_style, string(s),
5344 env->vpos-env->size*r/72, env->hpos,
5345 env->vpos , env->hpos,
5349 * assume that the html command has no width, if it does then
5350 * hopefully troff will have fudged this in a macro by
5351 * requesting that the formatting move right by the appropriate
5355 } else if (strncmp(s, "index:", 6) == 0) {
5356 cutoff_heading = atoi(&s[6]);
5357 } else if (strncmp(s, "assertion:[", 11) == 0) {
5358 int r=font::res; /* resolution of the device */
5360 handle_assertion(env->vpos-env->size*r/72, env->hpos,
5361 env->vpos, env->hpos, s);
5367 * devtag - handles device troff tags sent from the `troff'.
5368 * These include the troff state machine tags:
5369 * .br, .sp, .in, .tl, .ll etc
5371 * (see man 5 grohtml_tags).
5374 void html_printer::devtag (char *s, const environment *env, char type)
5381 if (env->fontno >= 0) {
5382 style sty(get_font_from_index(env->fontno), env->size, env->height,
5383 env->slant, env->fontno, *env->col);
5387 if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
5388 int r=font::res; /* resolution of the device */
5390 page_contents->add_tag(&sbuf_style, string(s),
5392 env->vpos-env->size*r/72, env->hpos,
5393 env->vpos , env->hpos);
5400 * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
5403 int html_printer::round_width(int x)
5408 // don't depend on the rounding direction for division of negative integers
5413 ? -((-x + r/2 - 1)/r)
5419 * handle_valid_flag - emits a valid xhtml 1.1 or html-4.01 button, provided -V
5420 * was supplied on the command line.
5423 void html_printer::handle_valid_flag (int needs_para)
5427 fputs("<p>", stdout);
5428 if (dialect == xhtml)
5429 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5430 "src=\"http://www.w3.org/Icons/valid-xhtml11-blue\" "
5431 "alt=\"Valid XHTML 1.1 Transitional\" height=\"31\" width=\"88\" /></a>\n",
5434 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5435 "src=\"http://www.w3.org/Icons/valid-html401-blue\" "
5436 "alt=\"Valid HTML 4.01 Transitional\" height=\"31\" width=\"88\"></a>\n",
5439 fputs("</p>", stdout);
5443 int main(int argc, char **argv)
5445 program_name = argv[0];
5446 static char stderr_buf[BUFSIZ];
5447 setbuf(stderr, stderr_buf);
5449 static const struct option long_options[] = {
5450 { "help", no_argument, 0, CHAR_MAX + 1 },
5451 { "version", no_argument, 0, 'v' },
5454 while ((c = getopt_long(argc, argv, "a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
5455 long_options, NULL))
5459 /* text antialiasing bits - handled by pre-html */
5462 // set background color to white
5463 default_background = new color;
5464 default_background->set_gray(color::MAX_COLOR_VAL);
5467 /* handled by pre-html */
5470 /* handled by pre-html */
5473 /* handled by pre-html */
5476 font::command_line_font_dir(optarg);
5479 /* graphic antialiasing bits - handled by pre-html */
5482 /* do not use the Hn headings of html, but manufacture our own */
5483 manufacture_headings = TRUE;
5486 /* handled by pre-html */
5489 /* handled by pre-html */
5492 multiple_files = TRUE;
5499 simple_anchors = TRUE;
5502 /* handled by pre-html */
5505 /* handled by pre-html */
5511 base_point_size = atoi(optarg);
5514 split_level = atoi(optarg) + 1;
5517 printf("GNU post-grohtml (groff) version %s\n", Version_string);
5524 if (strcmp(optarg, "x") == 0) {
5526 simple_anchors = TRUE;
5527 } else if (strcmp(optarg, "4") == 0)
5530 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
5535 case CHAR_MAX + 1: // --help
5546 if (optind >= argc) {
5549 for (int i = optind; i < argc; i++)
5555 static void usage(FILE *stream)
5557 fprintf(stream, "usage: %s [-vbelnhVy] [-D dir] [-I image_stem] [-F dir] [-x x] [files ...]\n",