2 /* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc
5 * but it owes a huge amount of ideas and raw code from
6 * James Clark (jjc@jclark.com) grops/ps.cc.
10 This file is part of groff.
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING. If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include "stringclass.h"
30 #include "html-text.h"
31 #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 BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */
54 #define CENTER_TOLERANCE 2 /* how many pixels off center will we still */
55 #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
56 #define UNICODE_DESC_START 0x80 /* all character entities above this are */
57 /* either encoded by their glyph names or if */
58 /* there is no name then we use &#nnn; */
59 typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
60 typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
69 char *get_html_translation (font *f, const string &name);
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? */
84 * start with a few favorites
89 static int min (int a, int b)
97 static int max (int a, int b)
106 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
109 static int is_intersection (int a1, int a2, int b1, int b2)
111 // easier to prove NOT outside limits
112 return( ! ((a1 > b2) || (a2 < b1)) );
116 * is_digit - returns TRUE if character, ch, is a digit.
119 static int is_digit (char ch)
121 return( (ch >= '0') && (ch <= '9') );
125 * the classes and methods for maintaining a list of files.
136 * file - initialize all fields to NULL
147 FILE *get_file (void);
148 void start_of_list (void);
149 void move_next (void);
150 void add_new_file (FILE *f);
158 * files - create an empty list of files.
162 : head(0), tail(0), ptr(0)
167 * get_file - returns the FILE associated with ptr.
170 FILE *files::get_file (void)
180 * start_of_list - reset the ptr to the start of the list.
183 void files::start_of_list (void)
189 * move_next - moves the ptr to the next element on the list.
192 void files::move_next (void)
199 * add_new_file - adds a new file, f, to the list.
202 void files::add_new_file (FILE *f)
208 tail->next = new file(f);
215 * the class and methods for styles
226 style (font *, int, int, int, int, color);
227 int operator == (const style &) const;
228 int operator != (const style &) const;
236 style::style(font *p, int sz, int h, int sl, int no, color c)
237 : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
241 int style::operator==(const style &s) const
243 return (f == s.f && point_size == s.point_size
244 && height == s.height && slant == s.slant && col == s.col);
247 int style::operator!=(const style &s) const
249 return !(*this == s);
253 * the class and methods for retaining ascii text
263 char_block::char_block(int length);
266 char_block::char_block()
267 : buffer(NULL), used(0), next(0)
271 char_block::char_block(int length)
274 buffer = (char *)malloc(max(length, char_block::SIZE));
276 fatal("out of memory error");
283 char *add_string(const char *, unsigned int);
284 char *add_string(const string &);
290 char_buffer::char_buffer()
295 char_buffer::~char_buffer()
298 char_block *temp = head;
304 char *char_buffer::add_string (const char *s, unsigned int length)
307 unsigned int old_used;
309 if (s == NULL || length == 0)
313 tail = new char_block(length+1);
316 if (tail->used + length+1 > char_block::SIZE) {
317 tail->next = new char_block(length+1);
322 old_used = tail->used;
324 tail->buffer[tail->used] = s[i];
330 // add terminating nul character
332 tail->buffer[tail->used] = '\0';
335 // and return start of new string
337 return( &tail->buffer[old_used] );
340 char *char_buffer::add_string (const string &s)
342 return add_string(s.contents(), s.length());
346 * the classes and methods for maintaining glyph positions.
351 void text_glob_html (style *s, char *str, int length,
352 int min_vertical, int min_horizontal,
353 int max_vertical, int max_horizontal);
354 void text_glob_special (style *s, char *str, int length,
355 int min_vertical, int min_horizontal,
356 int max_vertical, int max_horizontal);
357 void text_glob_line (style *s,
358 int min_vertical, int min_horizontal,
359 int max_vertical, int max_horizontal,
361 void text_glob_auto_image(style *s, char *str, int length,
362 int min_vertical, int min_horizontal,
363 int max_vertical, int max_horizontal);
364 void text_glob_tag (style *s, char *str, int length,
365 int min_vertical, int min_horizontal,
366 int max_vertical, int max_horizontal);
370 int is_a_line (void);
373 int is_auto_img (void);
379 int is_eol_ce (void);
384 int is_tab_ts (void);
385 int is_tab_te (void);
389 int get_tab_args (char *align);
391 void remember_table (html_table *t);
392 html_table *get_table (void);
395 const char *text_string;
396 unsigned int text_length;
397 int minv, minh, maxv, maxh;
398 int is_tag; // is this a .br, .sp, .tl etc
399 int is_img_auto; // image created by eqn delim
400 int is_special; // text has come via 'x X html:'
401 int is_line; // is the command a <line>?
402 int thickness; // the thickness of a line
403 html_table *tab; // table description
406 text_glob (style *s, char *str, int length,
407 int min_vertical , int min_horizontal,
408 int max_vertical , int max_horizontal,
409 bool is_troff_command,
410 bool is_auto_image, bool is_special_command,
411 bool is_a_line , int thickness);
414 text_glob::text_glob (style *s, char *str, int length,
415 int min_vertical, int min_horizontal,
416 int max_vertical, int max_horizontal,
417 bool is_troff_command,
418 bool is_auto_image, bool is_special_command,
419 bool is_a_line, int line_thickness)
420 : text_style(*s), text_string(str), text_length(length),
421 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
422 is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
423 is_line(is_a_line), thickness(line_thickness), tab(NULL)
427 text_glob::text_glob ()
428 : text_string(0), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
429 is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
433 text_glob::~text_glob ()
440 * text_glob_html - used to place html text into the glob buffer.
443 void text_glob::text_glob_html (style *s, char *str, int length,
444 int min_vertical , int min_horizontal,
445 int max_vertical , int max_horizontal)
447 text_glob *g = new text_glob(s, str, length,
448 min_vertical, min_horizontal, max_vertical, max_horizontal,
449 FALSE, FALSE, FALSE, FALSE, 0);
454 * text_glob_html - used to place html specials into the glob buffer.
455 * This text is essentially html commands coming through
456 * from the macro sets, with special designated sequences of
457 * characters translated into html. See add_and_encode.
460 void text_glob::text_glob_special (style *s, char *str, int length,
461 int min_vertical , int min_horizontal,
462 int max_vertical , int max_horizontal)
464 text_glob *g = new text_glob(s, str, length,
465 min_vertical, min_horizontal, max_vertical, max_horizontal,
466 FALSE, FALSE, TRUE, FALSE, 0);
471 * text_glob_line - record horizontal draw line commands.
474 void text_glob::text_glob_line (style *s,
475 int min_vertical , int min_horizontal,
476 int max_vertical , int max_horizontal,
479 text_glob *g = new text_glob(s, "", 0,
480 min_vertical, min_horizontal, max_vertical, max_horizontal,
481 FALSE, FALSE, FALSE, TRUE, thickness);
486 * text_glob_auto_image - record the presence of a .auto-image tag command.
487 * Used to mark that an image has been created automatically
488 * by a preprocessor and (pre-grohtml/troff) combination.
489 * Under some circumstances images may not be created.
496 * $1 over x$!recripical of x
499 * the first auto-image marker is created via .EQ/.EN pair
500 * and no image is created.
501 * The second auto-image marker occurs at $1 over x$
502 * Currently this image will not be created
503 * as the whole of the table is created as an image.
504 * (Once html tables are handled by grohtml this will change.
505 * Shortly this will be the case).
508 void text_glob::text_glob_auto_image(style *s, char *str, int length,
509 int min_vertical, int min_horizontal,
510 int max_vertical, int max_horizontal)
512 text_glob *g = new text_glob(s, str, length,
513 min_vertical, min_horizontal, max_vertical, max_horizontal,
514 TRUE, TRUE, FALSE, FALSE, 0);
519 * text_glob_tag - records a troff tag.
522 void text_glob::text_glob_tag (style *s, char *str, int length,
523 int min_vertical, int min_horizontal,
524 int max_vertical, int max_horizontal)
526 text_glob *g = new text_glob(s, str, length,
527 min_vertical, min_horizontal, max_vertical, max_horizontal,
528 TRUE, FALSE, FALSE, FALSE, 0);
533 * is_a_line - returns TRUE if glob should be converted into an <hr>
536 int text_glob::is_a_line (void)
542 * is_a_tag - returns TRUE if glob contains a troff directive.
545 int text_glob::is_a_tag (void)
551 * is_eol - returns TRUE if glob contains the tag eol
554 int text_glob::is_eol (void)
556 return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) );
560 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
563 int text_glob::is_eol_ce (void)
565 return( is_tag && (strcmp(text_string, "html-tag:eol.ce") == 0) );
570 * is_nf - returns TRUE if glob contains the tag .nf
573 int text_glob::is_nf (void)
575 return( is_tag && (strcmp(text_string, "html-tag:.nf") == 0) );
579 * is_fi - returns TRUE if glob contains the tag .fi
582 int text_glob::is_fi (void)
584 return( is_tag && (strcmp(text_string, "html-tag:.fi") == 0) );
588 * is_ce - returns TRUE if glob contains the tag .ce
591 int text_glob::is_ce (void)
593 return( is_tag && (strcmp(text_string, "html-tag:.ce") == 0) );
597 * is_in - returns TRUE if glob contains the tag .in
600 int text_glob::is_in (void)
602 return( is_tag && (strncmp(text_string, "html-tag:.in ", strlen("html-tag:.in ")) == 0) );
606 * is_po - returns TRUE if glob contains the tag .po
609 int text_glob::is_po (void)
611 return( is_tag && (strncmp(text_string, "html-tag:.po ", strlen("html-tag:.po ")) == 0) );
615 * is_ti - returns TRUE if glob contains the tag .ti
618 int text_glob::is_ti (void)
620 return( is_tag && (strncmp(text_string, "html-tag:.ti ", strlen("html-tag:.ti ")) == 0) );
624 * is_col - returns TRUE if glob contains the tag .col
627 int text_glob::is_col (void)
629 return( is_tag && (strncmp(text_string, "html-tag:.col", strlen("html-tag:.col")) == 0) );
633 * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
636 int text_glob::is_tab_ts (void)
638 return( is_tag && (strcmp(text_string, "html-tag:.tab-ts") == 0) );
642 * is_tab_te - returns TRUE if glob contains the tag .tab_te
645 int text_glob::is_tab_te (void)
647 return( is_tag && (strcmp(text_string, "html-tag:.tab-te") == 0) );
651 * is_ta - returns TRUE if glob contains the tag .ta
654 int text_glob::is_ta (void)
656 return( is_tag && (strncmp(text_string, "html-tag:.ta ", strlen("html-tag:.ta ")) == 0) );
660 * is_tab - returns TRUE if glob contains the tag tab
663 int text_glob::is_tab (void)
665 return( is_tag && (strncmp(text_string, "html-tag:tab ", strlen("html-tag:tab ")) == 0) );
669 * is_tab0 - returns TRUE if glob contains the tag tab0
672 int text_glob::is_tab0 (void)
674 return( is_tag && (strncmp(text_string, "html-tag:tab0", strlen("html-tag:tab0")) == 0) );
678 * is_auto_img - returns TRUE if the glob contains an automatically
682 int text_glob::is_auto_img (void)
688 * is_br - returns TRUE if the glob is a tag containing a .br
689 * or an implied .br. Note that we do not include .nf or .fi
690 * as grohtml will place a .br after these commands if they
691 * should break the line.
694 int text_glob::is_br (void)
696 return( is_a_tag() && ((strcmp ("html-tag:.br", text_string) == 0) ||
697 (strncmp("html-tag:.sp", text_string, 11) == 0) ||
698 (strcmp ("html-tag:.ce", text_string) == 0)) );
701 int text_glob::get_arg (void)
703 if (strncmp("html-tag:", text_string, strlen("html-tag:")) == 0) {
704 const char *p = text_string;
706 while ((*p != (char)0) && (!isspace(*p)))
708 while ((*p != (char)0) && (isspace(*p)))
718 * get_tab_args - returns the tab position and alignment of the tab tag
721 int text_glob::get_tab_args (char *align)
723 if (strncmp("html-tag:", text_string, strlen("html-tag:")) == 0) {
724 const char *p = text_string;
726 // firstly the alignment C|R|L
727 while ((*p != (char)0) && (!isspace(*p)))
729 while ((*p != (char)0) && (isspace(*p)))
733 while ((*p != (char)0) && (!isspace(*p)))
735 while ((*p != (char)0) && (isspace(*p)))
745 * remember_table - saves table, t, in the text_glob.
748 void text_glob::remember_table (html_table *t)
754 * get_table - returns the stored table description.
757 html_table *text_glob::get_table (void)
763 * the class and methods used to construct ordered double linked lists.
764 * In a previous implementation we used templates via #include "ordered-list.h",
765 * but this does assume that all C++ compilers can handle this feature. Pragmatically
766 * it is safer to assume this is not the case.
769 struct element_list {
774 int minv, minh, maxv, maxh;
776 element_list (text_glob *d,
778 int min_vertical, int min_horizontal,
779 int max_vertical, int max_horizontal);
783 element_list::element_list ()
784 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
789 * element_list - create a list element assigning the datum and region parameters.
792 element_list::element_list (text_glob *in,
794 int min_vertical, int min_horizontal,
795 int max_vertical, int max_horizontal)
796 : right(0), left(0), datum(in), lineno(line_number),
797 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
805 int is_less (element_list *a, element_list *b);
806 void add (text_glob *in,
808 int min_vertical, int min_horizontal,
809 int max_vertical, int max_horizontal);
810 void sub_move_right (void);
811 void move_right (void);
812 void move_left (void);
814 int is_equal_to_tail (void);
815 int is_equal_to_head (void);
816 void start_from_head (void);
817 void start_from_tail (void);
818 void insert (text_glob *in);
819 void move_to (text_glob *in);
820 text_glob *move_right_get_data (void);
821 text_glob *move_left_get_data (void);
822 text_glob *get_data (void);
830 * list - construct an empty list.
834 : head(0), tail(0), ptr(0)
839 * ~list - destroy a complete list.
844 element_list *temp=head;
852 } while ((head != 0) && (head != tail));
856 * is_less - returns TRUE if a is left of b if on the same line or
857 * if a is higher up the page than b.
860 int list::is_less (element_list *a, element_list *b)
862 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
863 if (a->lineno < b->lineno) {
865 } else if (a->lineno > b->lineno) {
867 } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
868 return( a->minh < b->minh );
870 return( a->maxv < b->maxv );
875 * add - adds a datum to the list in the order specified by the region position.
878 void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
880 // create a new list element with datum and position fields initialized
881 element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
893 while ((last != head) && (is_less(t, last))) {
897 if (is_less(t, last)) {
899 last->left->right = t;
900 t->left = last->left;
902 // now check for a new head
908 t->right = last->right;
910 last->right->left = t;
912 // now check for a new tail
921 * sub_move_right - removes the element which is currently pointed to by ptr
922 * from the list and moves ptr to the right.
925 void list::sub_move_right (void)
927 element_list *t=ptr->right;
943 ptr->left->right = ptr->right;
944 ptr->right->left = ptr->left;
950 * start_from_head - assigns ptr to the head.
953 void list::start_from_head (void)
959 * start_from_tail - assigns ptr to the tail.
962 void list::start_from_tail (void)
968 * is_empty - returns TRUE if the list has no elements.
971 int list::is_empty (void)
977 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
980 int list::is_equal_to_tail (void)
982 return( ptr == tail );
986 * is_equal_to_head - returns TRUE if the ptr equals the head.
989 int list::is_equal_to_head (void)
991 return( ptr == head );
995 * move_left - moves the ptr left.
998 void list::move_left (void)
1004 * move_right - moves the ptr right.
1007 void list::move_right (void)
1013 * get_datum - returns the datum referenced via ptr.
1016 text_glob* list::get_data (void)
1018 return( ptr->datum );
1022 * move_right_get_data - returns the datum referenced via ptr and moves
1026 text_glob* list::move_right_get_data (void)
1032 return( ptr->datum );
1037 * move_left_get_data - returns the datum referenced via ptr and moves
1041 text_glob* list::move_left_get_data (void)
1047 return( ptr->datum );
1052 * insert - inserts data after the current position.
1055 void list::insert (text_glob *in)
1058 fatal("list must not be empty if we are inserting data");
1063 element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1066 ptr->right->left = t;
1067 t->right = ptr->right;
1074 * move_to - moves the current position to the point where data, in, exists.
1075 * This is an expensive method and should be used sparingly.
1078 void list::move_to (text_glob *in)
1081 while (ptr != tail && ptr->datum != in)
1086 * page class and methods
1092 void add (style *s, const string &str,
1094 int min_vertical, int min_horizontal,
1095 int max_vertical, int max_horizontal);
1096 void add_tag (style *s, const string &str,
1098 int min_vertical, int min_horizontal,
1099 int max_vertical, int max_horizontal);
1100 void add_and_encode (style *s, const string &str,
1102 int min_vertical, int min_horizontal,
1103 int max_vertical, int max_horizontal);
1104 void add_line (style *s,
1106 int x1, int y1, int x2, int y2,
1108 void insert_tag (const string &str);
1109 void dump_page (void); // debugging method
1113 list glyphs; // position of glyphs and specials on page
1114 char_buffer buffer; // all characters for this page
1122 * insert_tag - inserts a tag after the current position.
1125 void page::insert_tag (const string &str)
1127 if (str.length() > 0) {
1128 text_glob *g=new text_glob();
1129 text_glob *f=glyphs.get_data();
1130 g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1131 f->minv, f->minh, f->maxv, f->maxh);
1137 * add - add html text to the list of glyphs.
1140 void page::add (style *s, const string &str,
1142 int min_vertical, int min_horizontal,
1143 int max_vertical, int max_horizontal)
1145 if (str.length() > 0) {
1146 text_glob *g=new text_glob();
1147 g->text_glob_html(s, buffer.add_string(str), str.length(),
1148 min_vertical, min_horizontal, max_vertical, max_horizontal);
1149 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1154 * add_tag - adds a troff tag, for example: .tl .sp .br
1157 void page::add_tag (style *s, const string &str,
1159 int min_vertical, int min_horizontal,
1160 int max_vertical, int max_horizontal)
1162 if (str.length() > 0) {
1165 if (strncmp((str+'\0').contents(), "html-tag:.auto-image", 20) == 0) {
1166 g = new text_glob();
1167 g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1168 min_vertical, min_horizontal, max_vertical, max_horizontal);
1170 g = new text_glob();
1171 g->text_glob_tag(s, buffer.add_string(str), str.length(),
1172 min_vertical, min_horizontal, max_vertical, max_horizontal);
1174 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1179 * add_line - adds the <line> primitive providing that y1==y2
1182 void page::add_line (style *s,
1184 int x1, int y1, int x2, int y2,
1188 text_glob *g = new text_glob();
1189 g->text_glob_line(s,
1190 min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2),
1192 glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2));
1197 * to_unicode - returns a unicode translation of int, ch.
1200 static char *to_unicode (unsigned int ch)
1202 static char buf[30];
1204 sprintf(buf, "&#%u;", ch);
1209 * add_and_encode - adds a special string to the page, it translates the string
1210 * into html glyphs. The special string will have come from x X html:
1211 * and can contain troff character encodings which appear as
1212 * \(char\). A sequence of \\ represents \.
1213 * So for example we can write:
1214 * "cost = \(Po\)3.00 file = \\foo\\bar"
1215 * which is translated into:
1216 * "cost = £3.00 file = \foo\bar"
1219 void page::add_and_encode (style *s, const string &str,
1221 int min_vertical, int min_horizontal,
1222 int max_vertical, int max_horizontal)
1230 while (i < str.length()) {
1231 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
1233 i += 2; // move over \(
1235 while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
1239 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
1244 string troff_charname = str.substring(a, n-a);
1245 html_glyph = get_html_translation(s->f, troff_charname);
1247 html_string += html_glyph;
1249 int index=s->f->name_to_index((troff_charname + '\0').contents());
1251 if (s->f->contains(index) && (index != 0))
1252 html_string += s->f->get_code(index);
1256 html_string += str[i];
1259 if (html_string.length() > 0) {
1260 text_glob *g=new text_glob();
1261 g->text_glob_special(s, buffer.add_string(html_string), html_string.length(),
1262 min_vertical, min_horizontal, max_vertical, max_horizontal);
1263 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1268 * dump_page - dump the page contents for debugging purposes.
1271 void page::dump_page(void)
1273 #if defined(DEBUG_TABLES)
1274 text_glob *old_pos = glyphs.get_data();
1278 printf("\n\ndebugging start\n");
1279 glyphs.start_from_head();
1281 g = glyphs.get_data();
1282 if (g->is_tab_ts()) {
1284 if (g->get_table() != NULL)
1285 g->get_table()->dump_table();
1287 printf("%s ", g->text_string);
1290 glyphs.move_right();
1291 } while (! glyphs.is_equal_to_head());
1292 glyphs.move_to(old_pos);
1293 printf("\ndebugging end\n\n");
1300 * font classes and methods
1303 class html_font : public font {
1304 html_font(const char *);
1308 char *reencoded_name;
1310 static html_font *load_html_font(const char *);
1313 html_font *html_font::load_html_font(const char *s)
1315 html_font *f = new html_font(s);
1323 html_font::html_font(const char *nm)
1328 html_font::~html_font()
1333 * a simple class to contain the header to this document
1341 int has_been_written;
1348 title_desc::title_desc ()
1349 : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1353 title_desc::~title_desc ()
1362 int no_of_headings; // how many headings have we found?
1363 char_buffer headings; // all the headings used in the document
1364 list headers; // list of headers built from .NH and .SH
1365 int header_level; // current header level
1366 int written_header; // have we written the header yet?
1367 string header_buffer; // current header text
1369 void write_headings (FILE *f, int force);
1372 header_desc::header_desc ()
1373 : no_of_headings(0), header_level(2), written_header(0)
1377 header_desc::~header_desc ()
1382 * write_headings - emits a list of links for the headings in this document
1385 void header_desc::write_headings (FILE *f, int force)
1389 if (auto_links || force) {
1390 if (! headers.is_empty()) {
1393 headers.start_from_head();
1395 g = headers.get_data();
1396 fputs("<a href=\"#", f);
1397 if (simple_anchors) {
1398 string buffer(ANCHOR_TEMPLATE);
1400 buffer += as_string(h);
1402 fprintf(f, buffer.contents());
1404 fputs(g->text_string, f);
1407 fputs(g->text_string, f);
1408 fputs("</a><br>\n", f);
1409 headers.move_right();
1410 } while (! headers.is_equal_to_head());
1416 class html_printer : public printer {
1420 int space_char_index;
1422 int no_of_printed_pages;
1425 int sbuf_start_hpos;
1431 int last_sbuf_length;
1432 int overstrike_detected;
1436 int output_vpos_max;
1437 int output_draw_point_size;
1439 int output_line_thickness;
1440 unsigned char output_space_code;
1441 char *inside_font_style;
1446 int supress_sub_sup;
1448 page *page_contents;
1449 html_text *current_paragraph;
1450 html_indent *indent;
1454 TAG_ALIGNMENT next_tag;
1462 int vertical_spacing;
1467 void set_style (const style &);
1468 void set_space_code (unsigned char c);
1469 void do_exec (char *, const environment *);
1470 void do_import (char *, const environment *);
1471 void do_def (char *, const environment *);
1472 void do_mdef (char *, const environment *);
1473 void do_file (char *, const environment *);
1474 void set_line_thickness (const environment *);
1475 void terminate_current_font (void);
1476 void flush_font (void);
1477 void add_to_sbuf (int index, const string &s);
1478 void write_title (int in_head);
1479 int sbuf_continuation (int index, const char *name, const environment *env, int w);
1480 void flush_page (void);
1481 void troff_tag (text_glob *g);
1482 void flush_globs (void);
1483 void emit_line (text_glob *g);
1484 void emit_raw (text_glob *g);
1485 void emit_html (text_glob *g);
1486 void determine_space (text_glob *g);
1487 void start_font (const char *name);
1488 void end_font (const char *name);
1489 int is_font_courier (font *f);
1490 int is_courier_until_eol (void);
1491 void start_size (int from, int to);
1492 void do_font (text_glob *g);
1493 void do_center (char *arg);
1494 void do_break (void);
1496 void do_eol_ce (void);
1497 void do_title (void);
1498 void do_fill (int on);
1499 void do_heading (char *arg);
1500 void write_header (void);
1501 void determine_header_level (int level);
1502 void do_linelength (char *arg);
1503 void do_pageoffset (char *arg);
1504 void do_indentation (char *arg);
1505 void do_tempindent (char *arg);
1506 void do_indentedparagraph (void);
1507 void do_verticalspacing (char *arg);
1508 void do_pointsize (char *arg);
1509 void do_centered_image (void);
1510 void do_left_image (void);
1511 void do_right_image (void);
1512 void do_auto_image (text_glob *g, const char *filename);
1513 void do_links (void);
1514 void do_flush (void);
1515 int is_in_middle (int left, int right);
1516 void do_sup_or_sub (text_glob *g);
1517 int start_subscript (text_glob *g);
1518 int end_subscript (text_glob *g);
1519 int start_superscript (text_glob *g);
1520 int end_superscript (text_glob *g);
1521 void outstanding_eol (int n);
1522 int is_bold (font *f);
1523 font *make_bold (font *f);
1524 int overstrike (int index, const char *name, const environment *env, int w);
1525 void do_body (void);
1526 int next_horiz_pos (text_glob *g, int nf);
1527 void lookahead_for_tables (void);
1528 void insert_tab_te (void);
1529 text_glob *insert_tab_ts (text_glob *where);
1530 void insert_tab0_foreach_tab (void);
1531 void insert_tab_0 (text_glob *where);
1532 void do_indent (int in, int pageoff, int linelen);
1533 void shutdown_table (void);
1534 void do_tab_ts (text_glob *g);
1535 void do_tab_te (void);
1536 void do_col (char *s);
1537 void do_tab (char *s);
1538 void do_tab0 (void);
1539 int calc_nf (text_glob *g, int nf);
1540 void calc_po_in (text_glob *g, int nf);
1541 void remove_tabs (void);
1542 void remove_courier_tabs (void);
1543 void update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g);
1544 void add_table_end (char *debug_string);
1550 void set_char (int i, font *f, const environment *env, int w, const char *name);
1551 void draw (int code, int *p, int np, const environment *env);
1552 void begin_page (int);
1553 void end_page (int);
1554 void special (char *arg, const environment *env, char type);
1555 font *make_font (const char *);
1556 void end_of_line ();
1559 printer *make_printer()
1561 return new html_printer;
1564 static void usage(FILE *stream);
1566 void html_printer::set_style(const style &sty)
1568 const char *fontname = sty.f->get_name();
1569 if (fontname == NULL)
1570 fatal("no internalname specified for font");
1573 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
1578 * is_bold - returns TRUE if font, f, is bold.
1581 int html_printer::is_bold (font *f)
1583 const char *fontname = f->get_name();
1584 return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
1588 * make_bold - if a bold font of, f, exists then return it.
1591 font *html_printer::make_bold (font *f)
1593 const char *fontname = f->get_name();
1595 if (strcmp(fontname, "B") == 0)
1597 if (strcmp(fontname, "I") == 0)
1598 return font::load_font("BI");
1599 if (strcmp(fontname, "BI") == 0)
1604 void html_printer::end_of_line()
1611 * emit_line - writes out a horizontal rule.
1614 void html_printer::emit_line (text_glob *g)
1616 // --fixme-- needs to know the length in percentage
1617 html.put_string("<hr>");
1621 * emit_raw - writes the raw html information directly to the device.
1624 void html_printer::emit_raw (text_glob *g)
1627 if (next_tag == INLINE) {
1629 current_paragraph->do_emittext(g->text_string, g->text_length);
1631 current_paragraph->done_para();
1635 current_paragraph->do_para("align=center");
1638 current_paragraph->do_para(&html, "align=left", indentation, pageoffset, linelength);
1641 current_paragraph->do_para(&html, "align=right", indentation, pageoffset, linelength);
1644 fatal("unknown enumeration");
1646 current_paragraph->do_emittext(g->text_string, g->text_length);
1647 current_paragraph->done_para();
1649 supress_sub_sup = TRUE;
1650 if (indentation > 0) {
1652 * restore indentation
1654 int newin = indentation;
1656 do_indent(newin, pageoffset, linelength);
1662 * do_center - handle the .ce commands from troff.
1665 void html_printer::do_center (char *arg)
1668 current_paragraph->do_break();
1671 current_paragraph->done_para();
1672 supress_sub_sup = TRUE;
1673 current_paragraph->do_para("align=center");
1677 current_paragraph->remove_para_align();
1682 * do_centered_image - set a flag such that the next html-tag is
1683 * placed inside a centered paragraph.
1686 void html_printer::do_centered_image (void)
1688 next_tag = CENTERED;
1692 * do_right_image - set a flag such that the next html-tag is
1693 * placed inside a right aligned paragraph.
1696 void html_printer::do_right_image (void)
1702 * do_left_image - set a flag such that the next html-tag is
1703 * placed inside a left aligned paragraph.
1706 void html_printer::do_left_image (void)
1712 * exists - returns TRUE if filename exists.
1715 static int exists (const char *filename)
1717 FILE *fp = fopen(filename, "r");
1728 * generate_img_src - returns a html image tag for the filename
1729 * providing that the image exists.
1732 static string &generate_img_src (const char *filename)
1734 string *s = new string("");
1736 while (filename && (filename[0] == ' ')) {
1739 if (exists(filename))
1740 *s += string("<img src=\"") + filename + "\">";
1745 * do_auto_image - tests whether the image, indicated by filename,
1746 * is present, if so then it emits an html image tag.
1747 * An image tag may be passed through from pic, eqn
1748 * but the corresponding image might not be created.
1749 * Consider .EQ delim $$ .EN or an empty .PS .PE.
1752 void html_printer::do_auto_image (text_glob *g, const char *filename)
1754 string buffer = generate_img_src(filename);
1756 if (! buffer.empty()) {
1758 * utilize emit_raw by creating a new text_glob.
1762 h.text_string = buffer.contents();
1763 h.text_length = buffer.length();
1770 * outstanding_eol - call do_eol, n, times.
1773 void html_printer::outstanding_eol (int n)
1782 * do_title - handle the .tl commands from troff.
1785 void html_printer::do_title (void)
1788 int removed_from_head;
1791 if (page_number == 1) {
1792 int found_title_start = FALSE;
1793 if (! page_contents->glyphs.is_empty()) {
1794 page_contents->glyphs.sub_move_right(); /* move onto next word */
1796 t = page_contents->glyphs.get_data();
1797 removed_from_head = FALSE;
1798 if (t->is_auto_img()) {
1799 string img = generate_img_src((char *)(t->text_string + 20));
1801 if (! img.empty()) {
1802 if (found_title_start)
1804 found_title_start = TRUE;
1805 title.has_been_found = TRUE;
1808 page_contents->glyphs.sub_move_right(); /* move onto next word */
1809 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1810 (page_contents->glyphs.is_equal_to_head()));
1811 } else if (t->is_eol_ce()) {
1812 /* process the eol associated with .ce
1815 page_contents->glyphs.sub_move_right(); /* move onto next word */
1816 } else if (t->is_eol()) {
1817 /* end of title found
1819 title.has_been_found = TRUE;
1820 outstanding_eol(eol_ce);
1822 } else if (t->is_a_tag()) {
1823 /* end of title found, but move back so that we read this tag and process it
1825 page_contents->glyphs.move_left(); /* move backwards to last word */
1826 title.has_been_found = TRUE;
1827 outstanding_eol(eol_ce);
1829 } else if (found_title_start) {
1830 title.text += " " + string(t->text_string, t->text_length);
1831 page_contents->glyphs.sub_move_right(); /* move onto next word */
1832 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1833 (page_contents->glyphs.is_equal_to_head()));
1835 title.text += string(t->text_string, t->text_length);
1836 found_title_start = TRUE;
1837 title.has_been_found = TRUE;
1838 page_contents->glyphs.sub_move_right(); /* move onto next word */
1839 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1840 (page_contents->glyphs.is_equal_to_head()));
1842 } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
1844 outstanding_eol(eol_ce);
1848 void html_printer::write_header (void)
1850 if (! header.header_buffer.empty()) {
1851 if (header.header_level > 7) {
1852 header.header_level = 7;
1855 // firstly we must terminate any font and type faces
1856 current_paragraph->done_para();
1857 supress_sub_sup = TRUE;
1859 if (cutoff_heading+2 > header.header_level) {
1860 // now we save the header so we can issue a list of links
1861 header.no_of_headings++;
1864 text_glob *h=new text_glob();
1865 h->text_glob_html(&st,
1866 header.headings.add_string(header.header_buffer),
1867 header.header_buffer.length(),
1868 header.no_of_headings, header.header_level,
1869 header.no_of_headings, header.header_level);
1871 header.headers.add(h,
1872 header.no_of_headings,
1873 header.no_of_headings, header.no_of_headings,
1874 header.no_of_headings, header.no_of_headings); // and add this header to the header list
1876 // lastly we generate a tag
1878 html.nl().put_string("<a name=\"");
1879 if (simple_anchors) {
1880 string buffer(ANCHOR_TEMPLATE);
1882 buffer += as_string(header.no_of_headings);
1884 html.put_string(buffer.contents());
1886 html.put_string(header.header_buffer);
1888 html.put_string("\"></a>").nl();
1891 if (manufacture_headings) {
1892 // line break before a header
1893 if (!current_paragraph->emitted_text())
1894 current_paragraph->do_space();
1895 // user wants manufactured headings which look better than <Hn></Hn>
1896 if (header.header_level<4) {
1897 html.put_string("<b><font size=\"+1\">");
1898 html.put_string(header.header_buffer);
1899 html.put_string("</font></b>").nl();
1902 html.put_string("<b>");
1903 html.put_string(header.header_buffer);
1904 html.put_string("</b>").nl();
1908 // and now we issue the real header
1909 html.put_string("<h");
1910 html.put_number(header.header_level);
1911 html.put_string(">");
1912 html.put_string(header.header_buffer);
1913 html.put_string("</h");
1914 html.put_number(header.header_level);
1915 html.put_string(">").nl();
1918 current_paragraph->do_para(&html, "", indentation, pageoffset, linelength);
1922 void html_printer::determine_header_level (int level)
1927 for (i=0; ((i<header.header_buffer.length())
1928 && ((header.header_buffer[i] == '.')
1929 || is_digit(header.header_buffer[i]))) ; i++) {
1930 if (header.header_buffer[i] == '.') {
1935 header.header_level = level+1;
1939 * do_heading - handle the .SH and .NH and equivalent commands from troff.
1942 void html_printer::do_heading (char *arg)
1946 int level=atoi(arg);
1948 header.header_buffer.clear();
1949 page_contents->glyphs.move_right();
1950 if (! page_contents->glyphs.is_equal_to_head()) {
1951 g = page_contents->glyphs.get_data();
1953 if (g->is_auto_img()) {
1954 string img=generate_img_src((char *)(g->text_string + 20));
1956 if (! img.empty()) {
1957 simple_anchors = TRUE; // we cannot use full heading anchors with images
1959 header.header_buffer += " ";
1962 header.header_buffer += img;
1964 } else if (! (g->is_a_line() || g->is_a_tag())) {
1966 * we ignore tags commands when constructing a heading
1969 header.header_buffer += " ";
1972 header.header_buffer += string(g->text_string, g->text_length);
1974 page_contents->glyphs.move_right();
1975 g = page_contents->glyphs.get_data();
1976 } while ((! page_contents->glyphs.is_equal_to_head()) &&
1980 determine_header_level(level);
1983 // finally set the output to neutral for after the header
1984 g = page_contents->glyphs.get_data();
1985 page_contents->glyphs.move_left(); // so that next time we use old g
1989 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
1992 int html_printer::is_courier_until_eol (void)
1994 text_glob *orig = page_contents->glyphs.get_data();
1998 if (! page_contents->glyphs.is_equal_to_tail()) {
1999 page_contents->glyphs.move_right();
2001 g = page_contents->glyphs.get_data();
2002 if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2004 page_contents->glyphs.move_right();
2006 (! page_contents->glyphs.is_equal_to_head()) &&
2007 (! g->is_fi()) && (! g->is_eol()));
2010 * now restore our previous position.
2012 while (page_contents->glyphs.get_data() != orig)
2013 page_contents->glyphs.move_left();
2019 * do_linelength - handle the .ll command from troff.
2022 void html_printer::do_linelength (char *arg)
2024 if (max_linelength == -1)
2025 max_linelength = atoi(arg);
2028 do_indent(indentation, pageoffset, atoi(arg));
2032 * do_pageoffset - handle the .po command from troff.
2035 void html_printer::do_pageoffset (char *arg)
2038 do_indent(indentation, atoi(arg), linelength);
2042 * do_indentation - handle the .in command from troff.
2045 void html_printer::do_indentation (char *arg)
2048 do_indent(atoi(arg), pageoffset, linelength);
2052 * do_tempindent - handle the .ti command from troff.
2055 void html_printer::do_tempindent (char *arg)
2059 prev_indent = indentation;
2060 do_indent(atoi(arg), pageoffset, linelength);
2065 * shutdown_table - shuts down the current table.
2068 void html_printer::shutdown_table (void)
2070 if (table != NULL) {
2071 current_paragraph->done_para();
2072 table->emit_finish_table();
2073 // dont delete this table as it will be deleted when we destroy the text_glob
2079 * do_indent - remember the indent parameters and if
2080 * indent is > pageoff and indent has changed
2081 * then we start a html table to implement the indentation.
2084 void html_printer::do_indent (int in, int pageoff, int linelen)
2086 if ((indentation != -1) &&
2087 (pageoffset+indentation != in+pageoff)) {
2089 current_paragraph->done_para();
2092 pageoffset = pageoff;
2093 if (linelen <= max_linelength)
2094 linelength = linelen;
2096 current_paragraph->do_para(&html, "", indentation, pageoffset, max_linelength);
2101 * do_verticalspacing - handle the .vs command from troff.
2104 void html_printer::do_verticalspacing (char *arg)
2106 vertical_spacing = atoi(arg);
2110 * do_pointsize - handle the .ps command from troff.
2113 void html_printer::do_pointsize (char *arg)
2115 pointsize = atoi(arg);
2119 * do_fill - records whether troff has requested that text be filled.
2122 void html_printer::do_fill (int on)
2124 current_paragraph->do_break();
2125 output_hpos = indentation+pageoffset;
2126 supress_sub_sup = TRUE;
2128 if (fill_on != on) {
2130 current_paragraph->do_para("");
2132 current_paragraph->do_pre();
2138 * do_eol - handle the end of line
2141 void html_printer::do_eol (void)
2144 if (current_paragraph->ever_emitted_text()) {
2145 current_paragraph->do_newline();
2146 current_paragraph->do_break();
2149 output_hpos = indentation+pageoffset;
2153 * do_eol_ce - handle end of line specifically for a .ce
2156 void html_printer::do_eol_ce (void)
2158 if (end_center > 0) {
2160 if (current_paragraph->emitted_text())
2161 current_paragraph->do_break();
2164 if (end_center == 0) {
2165 current_paragraph->done_para();
2166 supress_sub_sup = TRUE;
2172 * do_flush - flushes all output and tags.
2175 void html_printer::do_flush (void)
2177 current_paragraph->done_para();
2181 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
2184 void html_printer::do_links (void)
2186 current_paragraph->done_para();
2187 auto_links = FALSE; /* from now on only emit under user request */
2188 file_list.add_new_file(xtmpfile());
2189 html.set_file(file_list.get_file());
2193 * do_break - handles the ".br" request and also
2194 * undoes an outstanding ".ti" command.
2197 void html_printer::do_break (void)
2199 current_paragraph->do_break();
2200 if (end_tempindent > 0) {
2202 if (end_tempindent == 0)
2203 do_indent(prev_indent, pageoffset, linelength);
2205 output_hpos = indentation+pageoffset;
2206 supress_sub_sup = TRUE;
2210 * do_tab_ts - start a table, which will have already been defined.
2213 void html_printer::do_tab_ts (text_glob *g)
2215 html_table *t = g->get_table();
2218 current_paragraph->done_pre();
2219 current_paragraph->done_para();
2221 html.simple_comment("TABS");
2223 t->set_linelength(max_linelength);
2224 t->add_indent(pageoffset);
2225 t->emit_table_header(FALSE);
2232 * do_tab_te - finish a table.
2235 void html_printer::do_tab_te (void)
2238 current_paragraph->done_para();
2239 table->emit_finish_table();
2244 if (indentation > 0) {
2246 * restore indentation
2248 int newin = indentation;
2250 do_indent(newin, pageoffset, linelength);
2255 * do_tab - handle the "html-tag:tab" tag
2258 void html_printer::do_tab (char *s)
2264 int col = table->find_column(atoi(s) + pageoffset + indentation);
2266 current_paragraph->done_para();
2267 table->emit_col(col);
2273 * do_tab0 - handle the "html-tag:tab0" tag
2276 void html_printer::do_tab0 (void)
2279 int col = table->find_column(pageoffset+indentation);
2281 current_paragraph->done_para();
2282 table->emit_col(col);
2288 * do_col - start column, s.
2291 void html_printer::do_col (char *s)
2294 current_paragraph->done_para();
2295 table->emit_col(atoi(s));
2300 * troff_tag - processes the troff tag and manipulates the troff state machine.
2303 void html_printer::troff_tag (text_glob *g)
2306 * firstly skip over html-tag:
2308 char *t=(char *)g->text_string+9;
2312 } else if (g->is_eol_ce()) {
2314 } else if (strncmp(t, ".sp", 3) == 0) {
2315 if (g->get_arg() > 0)
2316 current_paragraph->do_space();
2318 current_paragraph->do_break();
2319 supress_sub_sup = TRUE;
2320 } else if (strncmp(t, ".br", 3) == 0) {
2322 } else if (strcmp(t, ".centered-image") == 0) {
2323 do_centered_image();
2324 } else if (strcmp(t, ".right-image") == 0) {
2326 } else if (strcmp(t, ".left-image") == 0) {
2328 } else if (strncmp(t, ".auto-image", 11) == 0) {
2329 char *a = (char *)t+11;
2330 do_auto_image(g, a);
2331 } else if (strncmp(t, ".ce", 3) == 0) {
2332 char *a = (char *)t+3;
2333 supress_sub_sup = TRUE;
2335 } else if (strncmp(t, ".tl", 3) == 0) {
2336 supress_sub_sup = TRUE;
2337 title.with_h1 = TRUE;
2339 } else if (strncmp(t, ".html-tl", 8) == 0) {
2340 supress_sub_sup = TRUE;
2341 title.with_h1 = FALSE;
2343 } else if (strncmp(t, ".fi", 3) == 0) {
2345 } else if (strncmp(t, ".nf", 3) == 0) {
2347 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
2348 char *a = (char *)t+3;
2350 } else if (strncmp(t, ".ll", 3) == 0) {
2351 char *a = (char *)t+3;
2353 } else if (strncmp(t, ".po", 3) == 0) {
2354 char *a = (char *)t+3;
2356 } else if (strncmp(t, ".in", 3) == 0) {
2357 char *a = (char *)t+3;
2359 } else if (strncmp(t, ".ti", 3) == 0) {
2360 char *a = (char *)t+3;
2362 } else if (strncmp(t, ".vs", 3) == 0) {
2363 char *a = (char *)t+3;
2364 do_verticalspacing(a);
2365 } else if (strncmp(t, ".ps", 3) == 0) {
2366 char *a = (char *)t+3;
2368 } else if (strcmp(t, ".links") == 0) {
2370 } else if (strcmp(t, ".no-auto-rule") == 0) {
2372 } else if (strcmp(t, ".tab-ts") == 0) {
2374 } else if (strcmp(t, ".tab-te") == 0) {
2376 } else if (strncmp(t, ".col ", 5) == 0) {
2377 char *a = (char *)t+4;
2379 } else if (strncmp(t, "tab ", 4) == 0) {
2380 char *a = (char *)t+3;
2382 } else if (strncmp(t, "tab0", 4) == 0) {
2388 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
2391 int html_printer::is_in_middle (int left, int right)
2393 return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE );
2397 * flush_globs - runs through the text glob list and emits html.
2400 void html_printer::flush_globs (void)
2404 if (! page_contents->glyphs.is_empty()) {
2405 page_contents->glyphs.start_from_head();
2407 g = page_contents->glyphs.get_data();
2409 if (strcmp(g->text_string, "XXXXXXX") == 0)
2412 if (g->is_a_tag()) {
2414 } else if (g->is_a_line()) {
2420 * after processing the title (and removing it) the glyph list might be empty
2422 if (! page_contents->glyphs.is_empty()) {
2423 page_contents->glyphs.move_right();
2425 } while (! page_contents->glyphs.is_equal_to_head());
2430 * calc_nf - calculates the _no_ format flag, given the
2434 int html_printer::calc_nf (text_glob *g, int nf)
2446 * calc_po_in - calculates the, in, po, registers
2449 void html_printer::calc_po_in (text_glob *g, int nf)
2452 indentation = g->get_arg();
2453 else if (g->is_po())
2454 pageoffset = g->get_arg();
2455 else if (g->is_ti()) {
2456 prev_indent = indentation;
2457 indentation = g->get_arg();
2459 } else if (g->is_br() && ((end_tempindent > 0) || (nf && g->is_eol()))) {
2461 indentation = prev_indent;
2466 * next_horiz_pos - returns the next horiz position.
2467 * -1 is returned if it doesn't exist.
2470 int html_printer::next_horiz_pos (text_glob *g, int nf)
2474 if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
2475 if (! page_contents->glyphs.is_empty()) {
2476 page_contents->glyphs.move_right_get_data();
2478 page_contents->glyphs.start_from_head();
2481 page_contents->glyphs.move_left();
2488 * insert_tab_ts - inserts a tab-ts before, where.
2491 text_glob *html_printer::insert_tab_ts (text_glob *where)
2493 text_glob *start_of_table;
2494 text_glob *old_pos = page_contents->glyphs.get_data();
2496 page_contents->glyphs.move_to(where);
2497 page_contents->glyphs.move_left();
2498 page_contents->insert_tag(string("html-tag:.tab-ts")); // tab table start
2499 page_contents->glyphs.move_right();
2500 start_of_table = page_contents->glyphs.get_data();
2501 page_contents->glyphs.move_to(old_pos);
2502 return start_of_table;
2506 * insert_tab_te - inserts a tab-te before the current position
2507 * (it skips backwards over .sp/.br)
2510 void html_printer::insert_tab_te (void)
2512 text_glob *g = page_contents->glyphs.get_data();
2513 page_contents->dump_page();
2515 while (page_contents->glyphs.get_data()->is_a_tag())
2516 page_contents->glyphs.move_left();
2518 page_contents->insert_tag(string("html-tag:.tab-te")); // tab table end
2519 while (g != page_contents->glyphs.get_data())
2520 page_contents->glyphs.move_right();
2521 page_contents->dump_page();
2525 * insert_tab_0 - inserts a tab0 before, where.
2528 void html_printer::insert_tab_0 (text_glob *where)
2530 text_glob *old_pos = page_contents->glyphs.get_data();
2532 page_contents->glyphs.move_to(where);
2533 page_contents->glyphs.move_left();
2534 page_contents->insert_tag(string("html-tag:tab0")); // tab0 start of line
2535 page_contents->glyphs.move_right();
2536 page_contents->glyphs.move_to(old_pos);
2540 * remove_tabs - removes the tabs tags on this line.
2543 void html_printer::remove_tabs (void)
2545 text_glob *orig = page_contents->glyphs.get_data();
2548 if (! page_contents->glyphs.is_equal_to_tail()) {
2550 g = page_contents->glyphs.get_data();
2552 page_contents->glyphs.sub_move_right();
2554 orig = page_contents->glyphs.get_data();
2556 page_contents->glyphs.move_right();
2557 } while ((! page_contents->glyphs.is_equal_to_head()) &&
2561 * now restore our previous position.
2563 while (page_contents->glyphs.get_data() != orig)
2564 page_contents->glyphs.move_left();
2568 void html_printer::remove_courier_tabs (void)
2571 int line_start = TRUE;
2574 if (! page_contents->glyphs.is_empty()) {
2575 page_contents->glyphs.start_from_head();
2578 g = page_contents->glyphs.get_data();
2580 nf = calc_nf(g, nf);
2583 if (line_start && nf && is_courier_until_eol()) {
2585 g = page_contents->glyphs.get_data();
2589 line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
2590 page_contents->glyphs.move_right();
2591 } while (! page_contents->glyphs.is_equal_to_head());
2595 void html_printer::insert_tab0_foreach_tab (void)
2597 text_glob *start_of_line = NULL;
2598 text_glob *g = NULL;
2599 int seen_tab = FALSE;
2600 int seen_col = FALSE;
2603 if (! page_contents->glyphs.is_empty()) {
2604 page_contents->glyphs.start_from_head();
2605 start_of_line = page_contents->glyphs.get_data();
2607 g = page_contents->glyphs.get_data();
2609 nf = calc_nf(g, nf);
2617 if (g->is_br() || (nf && g->is_eol())) {
2619 page_contents->glyphs.move_right();
2620 g = page_contents->glyphs.get_data();
2621 nf = calc_nf(g, nf);
2622 if (page_contents->glyphs.is_equal_to_head()) {
2623 if (seen_tab && !seen_col)
2624 insert_tab_0(start_of_line);
2627 } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
2628 // printf("\nstart_of_line is: %s\n", g->text_string);
2629 if (seen_tab && !seen_col) {
2630 insert_tab_0(start_of_line);
2631 page_contents->glyphs.move_to(g);
2638 page_contents->glyphs.move_right();
2639 } while (! page_contents->glyphs.is_equal_to_head());
2640 if (seen_tab && !seen_col)
2641 insert_tab_0(start_of_line);
2647 * update_min_max - updates the extent of a column, given the left and right
2648 * extents of a glyph, g.
2651 void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
2653 switch (type_of_col) {
2670 * add_table_end - moves left one glyph, adds a table end tag and adds a
2674 void html_printer::add_table_end (char *debug_string)
2676 page_contents->glyphs.move_left();
2678 #if defined(DEBUG_TABLES)
2679 page_contents->insert_tag(string(debug_string));
2684 * lookahead_for_tables - checks for .col tags and inserts table start/end tags
2687 void html_printer::lookahead_for_tables (void)
2690 text_glob *start_of_line = NULL;
2691 text_glob *start_of_table = NULL;
2692 text_glob *last = NULL;
2693 colType type_of_col = none;
2695 int found_col = FALSE;
2696 int seen_text = FALSE;
2700 html_table *table = new html_table(&html, -1);
2701 const char *tab_defs = NULL;
2704 int old_pageoffset = pageoffset;
2706 remove_courier_tabs();
2707 page_contents->dump_page();
2708 insert_tab0_foreach_tab();
2709 page_contents->dump_page();
2710 if (! page_contents->glyphs.is_empty()) {
2711 page_contents->glyphs.start_from_head();
2712 g = page_contents->glyphs.get_data();
2714 #if defined(DEBUG_TABLES)
2715 fprintf(stderr, " [") ;
2716 fprintf(stderr, g->text_string) ;
2717 fprintf(stderr, "] ") ;
2719 if (strcmp(g->text_string, "XXXXXXX") == 0)
2723 nf = calc_nf(g, nf);
2726 if (type_of_col == tab_tag && start_of_table != NULL) {
2727 page_contents->glyphs.move_left();
2729 start_of_table->remember_table(table);
2730 table = new html_table(&html, -1);
2731 page_contents->insert_tag(string("*** TAB -> COL ***"));
2732 if (tab_defs != NULL)
2733 table->tab_stops->init(tab_defs);
2734 start_of_table = NULL;
2737 type_of_col = col_tag;
2739 ncol = g->get_arg();
2743 } else if (g->is_tab()) {
2744 type_of_col = tab_tag;
2745 colmin = g->get_tab_args(&align);
2746 align = 'L'; // for now as 'C' and 'R' are broken
2747 ncol = table->find_tab_column(colmin);
2748 colmin += pageoffset + indentation;
2749 colmax = table->get_tab_pos(ncol+1);
2751 colmax += pageoffset + indentation;
2752 } else if (g->is_tab0()) {
2753 if (type_of_col == col_tag && start_of_table != NULL) {
2754 page_contents->glyphs.move_left();
2756 start_of_table->remember_table(table);
2757 table = new html_table(&html, -1);
2758 page_contents->insert_tag(string("*** COL -> TAB ***"));
2759 start_of_table = NULL;
2762 if (tab_defs != NULL)
2763 table->tab_stops->init(tab_defs);
2765 type_of_col = tab0_tag;
2768 colmax = table->get_tab_pos(2) + pageoffset + indentation;
2769 } else if (! g->is_a_tag())
2770 update_min_max(type_of_col, &colmin, &colmax, g);
2772 if ((! g->is_a_tag()) || g->is_tab())
2775 if ((g->is_col() || g->is_tab() || g->is_tab0())
2776 && (start_of_line != NULL) && (start_of_table == NULL)) {
2777 start_of_table = insert_tab_ts(start_of_line);
2778 start_of_line = NULL;
2780 } else if (g->is_ce() && (start_of_table != NULL)) {
2781 add_table_end("*** CE ***");
2782 start_of_table->remember_table(table);
2783 start_of_table = NULL;
2785 } else if (g->is_ta()) {
2786 tab_defs = g->text_string;
2787 if (!table->tab_stops->compatible(tab_defs)) {
2788 if (start_of_table != NULL) {
2789 add_table_end("*** TABS ***");
2790 start_of_table->remember_table(table);
2791 table = new html_table(&html, -1);
2792 start_of_table = NULL;
2796 table->tab_stops->init(tab_defs);
2800 if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
2801 // we are in a table and have a glyph
2802 if ((ncol == 0) || (! table->add_column(ncol, colmin, colmax, align))) {
2804 add_table_end("*** NCOL == 0 ***");
2806 add_table_end("*** CROSSED COLS ***");
2808 start_of_table->remember_table(table);
2809 table = new html_table(&html, -1);
2810 start_of_table = NULL;
2817 * move onto next glob, check whether we are starting a new line
2819 g = page_contents->glyphs.move_right_get_data();
2823 page_contents->glyphs.start_from_head();
2827 } else if (g->is_br() || (nf && g->is_eol())) {
2829 g = page_contents->glyphs.move_right_get_data();
2830 nf = calc_nf(g, nf);
2831 } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
2835 left = next_horiz_pos(g, nf);
2840 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
2842 #if defined(DEBUG_TABLES)
2843 fprintf(stderr, "finished scanning for tables\n");
2846 page_contents->glyphs.start_from_head();
2847 if (start_of_table != NULL) {
2849 while (last != page_contents->glyphs.get_data())
2850 page_contents->glyphs.move_left();
2853 start_of_table->remember_table(table);
2855 page_contents->insert_tag(string("*** LAST ***"));
2861 // and reset the registers
2862 pageoffset = old_pageoffset;
2868 void html_printer::flush_page (void)
2870 supress_sub_sup = TRUE;
2872 page_contents->dump_page();
2873 lookahead_for_tables();
2874 page_contents->dump_page();
2877 current_paragraph->done_para();
2879 // move onto a new page
2880 delete page_contents;
2881 #if defined(DEBUG_TABLES)
2882 fprintf(stderr, "\n\n*** flushed page ***\n\n");
2884 html.simple_comment("new page called");
2886 page_contents = new page;
2890 * determine_space - works out whether we need to write a space.
2891 * If last glyph is ajoining then no space emitted.
2894 void html_printer::determine_space (text_glob *g)
2896 if (current_paragraph->is_in_pre()) {
2898 * .nf has been specified
2900 while (output_hpos < g->minh) {
2901 output_hpos += space_width;
2902 current_paragraph->emit_space();
2905 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
2906 current_paragraph->emit_space();
2912 * is_font_courier - returns TRUE if the font, f, is courier.
2915 int html_printer::is_font_courier (font *f)
2918 const char *fontname = f->get_name();
2920 return( (fontname != 0) && (fontname[0] == 'C') );
2926 * end_font - shuts down the font corresponding to fontname.
2929 void html_printer::end_font (const char *fontname)
2931 if (strcmp(fontname, "B") == 0) {
2932 current_paragraph->done_bold();
2933 } else if (strcmp(fontname, "I") == 0) {
2934 current_paragraph->done_italic();
2935 } else if (strcmp(fontname, "BI") == 0) {
2936 current_paragraph->done_bold();
2937 current_paragraph->done_italic();
2938 } else if (strcmp(fontname, "CR") == 0) {
2939 current_paragraph->done_tt();
2940 } else if (strcmp(fontname, "CI") == 0) {
2941 current_paragraph->done_italic();
2942 current_paragraph->done_tt();
2943 } else if (strcmp(fontname, "CB") == 0) {
2944 current_paragraph->done_bold();
2945 current_paragraph->done_tt();
2946 } else if (strcmp(fontname, "CBI") == 0) {
2947 current_paragraph->done_bold();
2948 current_paragraph->done_italic();
2949 current_paragraph->done_tt();
2954 * start_font - starts the font corresponding to name.
2957 void html_printer::start_font (const char *fontname)
2959 if (strcmp(fontname, "R") == 0) {
2960 current_paragraph->done_bold();
2961 current_paragraph->done_italic();
2962 current_paragraph->done_tt();
2963 } else if (strcmp(fontname, "B") == 0) {
2964 current_paragraph->do_bold();
2965 } else if (strcmp(fontname, "I") == 0) {
2966 current_paragraph->do_italic();
2967 } else if (strcmp(fontname, "BI") == 0) {
2968 current_paragraph->do_bold();
2969 current_paragraph->do_italic();
2970 } else if (strcmp(fontname, "CR") == 0) {
2971 if ((! fill_on) && (is_courier_until_eol())) {
2972 current_paragraph->do_pre();
2974 current_paragraph->do_tt();
2975 } else if (strcmp(fontname, "CI") == 0) {
2976 if ((! fill_on) && (is_courier_until_eol())) {
2977 current_paragraph->do_pre();
2979 current_paragraph->do_tt();
2980 current_paragraph->do_italic();
2981 } else if (strcmp(fontname, "CB") == 0) {
2982 if ((! fill_on) && (is_courier_until_eol())) {
2983 current_paragraph->do_pre();
2985 current_paragraph->do_tt();
2986 current_paragraph->do_bold();
2987 } else if (strcmp(fontname, "CBI") == 0) {
2988 if ((! fill_on) && (is_courier_until_eol())) {
2989 current_paragraph->do_pre();
2991 current_paragraph->do_tt();
2992 current_paragraph->do_italic();
2993 current_paragraph->do_bold();
2998 * start_size - from is old font size, to is the new font size.
2999 * The html increase <big> and <small> decrease alters the
3000 * font size by 20%. We try and map these onto glyph sizes.
3003 void html_printer::start_size (int from, int to)
3007 current_paragraph->do_big();
3008 from += SIZE_INCREMENT;
3010 } else if (from > to) {
3012 current_paragraph->do_small();
3013 from -= SIZE_INCREMENT;
3019 * do_font - checks to see whether we need to alter the html font.
3022 void html_printer::do_font (text_glob *g)
3025 * check if the output_style.point_size has not been set yet
3026 * this allow users to place .ps at the top of their troff files
3027 * and grohtml can then treat the .ps value as the base font size (3)
3029 if (output_style.point_size == -1) {
3030 output_style.point_size = pointsize;
3033 if (g->text_style.f != output_style.f) {
3034 if (output_style.f != 0) {
3035 end_font(output_style.f->get_name());
3037 output_style.f = g->text_style.f;
3038 if (output_style.f != 0) {
3039 start_font(output_style.f->get_name());
3042 if (output_style.point_size != g->text_style.point_size) {
3044 if ((output_style.point_size > 0) &&
3045 (g->text_style.point_size > 0)) {
3046 start_size(output_style.point_size, g->text_style.point_size);
3048 if (g->text_style.point_size > 0) {
3049 output_style.point_size = g->text_style.point_size;
3052 if (output_style.col != g->text_style.col) {
3053 current_paragraph->done_color();
3054 output_style.col = g->text_style.col;
3055 current_paragraph->do_color(&output_style.col);
3060 * start_subscript - returns TRUE if, g, looks like a subscript start.
3063 int html_printer::start_subscript (text_glob *g)
3066 int height = output_style.point_size*r/72;
3068 return( (output_style.point_size != 0) &&
3069 (output_vpos < g->minv) &&
3070 (output_vpos-height > g->maxv) &&
3071 (output_style.point_size > g->text_style.point_size) );
3075 * start_superscript - returns TRUE if, g, looks like a superscript start.
3078 int html_printer::start_superscript (text_glob *g)
3081 int height = output_style.point_size*r/72;
3083 return( (output_style.point_size != 0) &&
3084 (output_vpos > g->minv) &&
3085 (output_vpos-height < g->maxv) &&
3086 (output_style.point_size > g->text_style.point_size) );
3090 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
3093 int html_printer::end_subscript (text_glob *g)
3096 int height = output_style.point_size*r/72;
3098 return( (output_style.point_size != 0) &&
3099 (g->minv < output_vpos) &&
3100 (output_vpos-height > g->maxv) &&
3101 (output_style.point_size < g->text_style.point_size) );
3105 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
3108 int html_printer::end_superscript (text_glob *g)
3111 int height = output_style.point_size*r/72;
3113 return( (output_style.point_size != 0) &&
3114 (g->minv > output_vpos) &&
3115 (output_vpos-height < g->maxv) &&
3116 (output_style.point_size < g->text_style.point_size) );
3120 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
3121 * start/end and it calls the services of html-text to issue the
3125 void html_printer::do_sup_or_sub (text_glob *g)
3127 if (! supress_sub_sup) {
3128 if (start_subscript(g)) {
3129 current_paragraph->do_sub();
3130 } else if (start_superscript(g)) {
3131 current_paragraph->do_sup();
3132 } else if (end_subscript(g)) {
3133 current_paragraph->done_sub();
3134 } else if (end_superscript(g)) {
3135 current_paragraph->done_sup();
3141 * emit_html - write out the html text
3144 void html_printer::emit_html (text_glob *g)
3148 current_paragraph->do_emittext(g->text_string, g->text_length);
3149 output_vpos = g->minv;
3150 output_hpos = g->maxh;
3151 output_vpos_max = g->maxv;
3152 supress_sub_sup = FALSE;
3156 * flush_sbuf - flushes the current sbuf into the list of glyphs.
3159 void html_printer::flush_sbuf()
3161 if (sbuf.length() > 0) {
3162 int r=font::res; // resolution of the device
3163 set_style(sbuf_style);
3164 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
3165 font *bold_font = make_bold(sbuf_style.f);
3166 if (bold_font != NULL)
3167 sbuf_style.f = bold_font;
3170 page_contents->add(&sbuf_style, sbuf,
3172 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
3173 sbuf_vpos , sbuf_end_hpos);
3175 output_hpos = sbuf_end_hpos;
3176 output_vpos = sbuf_vpos;
3177 last_sbuf_length = 0;
3178 sbuf_prev_hpos = sbuf_end_hpos;
3179 overstrike_detected = FALSE;
3184 void html_printer::set_line_thickness(const environment *env)
3186 line_thickness = env->size;
3189 void html_printer::draw(int code, int *p, int np, const environment *env)
3196 page_contents->add_line(&sbuf_style,
3198 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
3200 error("2 arguments required for line");
3207 line_thickness = -1;
3209 // troff gratuitously adds an extra 0
3210 if (np != 1 && np != 2) {
3211 error("0 or 1 argument required for thickness");
3214 line_thickness = p[0];
3238 // fill with color env->fill
3239 if (background != NULL)
3241 background = new color;
3242 *background = *env->fill;
3246 error("unrecognised drawing command `%1'", char(code));
3251 html_printer::html_printer()
3252 : html(0, MAX_LINE_LENGTH),
3253 no_of_printed_pages(0),
3254 last_sbuf_length(0),
3255 overstrike_detected(FALSE),
3258 output_vpos_max(-1),
3260 inside_font_style(0),
3263 supress_sub_sup(TRUE),
3264 cutoff_heading(100),
3278 background(default_background)
3280 file_list.add_new_file(xtmpfile());
3281 html.set_file(file_list.get_file());
3282 if (font::hor != 24)
3283 fatal("horizontal resolution must be 24");
3284 if (font::vert != 40)
3285 fatal("vertical resolution must be 40");
3287 // should be sorted html..
3288 if (font::res % (font::sizescale*72) != 0)
3289 fatal("res must be a multiple of 72*sizescale");
3293 while (r % 10 == 0) {
3298 html.set_fixed_point(point);
3299 space_char_index = font::name_to_index("space");
3300 space_width = font::hor;
3301 paper_length = font::paperlength;
3302 linelength = font::res*13/2;
3303 if (paper_length == 0)
3304 paper_length = 11*font::res;
3306 page_contents = new page();
3310 * add_to_sbuf - adds character code or name to the sbuf.
3313 void html_printer::add_to_sbuf (int index, const string &s)
3315 if (sbuf_style.f == NULL)
3318 char *html_glyph = NULL;
3319 unsigned int code = sbuf_style.f->get_code(index);
3322 if (sbuf_style.f->contains(index))
3323 html_glyph = (char *)sbuf_style.f->get_special_device_encoding(index);
3327 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
3328 html_glyph = to_unicode(code);
3330 html_glyph = get_html_translation(sbuf_style.f, s);
3332 last_sbuf_length = sbuf.length();
3333 if (html_glyph == NULL)
3334 sbuf += ((char)code);
3339 int html_printer::sbuf_continuation (int index, const char *name,
3340 const environment *env, int w)
3343 * lets see whether the glyph is closer to the end of sbuf
3345 if ((sbuf_end_hpos == env->hpos)
3346 || ((sbuf_prev_hpos < sbuf_end_hpos)
3347 && (env->hpos < sbuf_end_hpos)
3348 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
3349 add_to_sbuf(index, name);
3350 sbuf_prev_hpos = sbuf_end_hpos;
3351 sbuf_end_hpos += w + sbuf_kern;
3354 if ((env->hpos >= sbuf_end_hpos) &&
3355 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
3357 * lets see whether a space is needed or not
3360 if (env->hpos-sbuf_end_hpos < space_width) {
3361 add_to_sbuf(index, name);
3362 sbuf_prev_hpos = sbuf_end_hpos;
3363 sbuf_end_hpos = env->hpos + w;
3372 * get_html_translation - given the position of the character and its name
3373 * return the device encoding for such character.
3376 char *get_html_translation (font *f, const string &name)
3380 if ((f == 0) || name.empty())
3383 index = f->name_to_index((char *)(name + '\0').contents());
3385 error("character `%s' not found", (name + '\0').contents());
3388 if (f->contains(index))
3389 return (char *)f->get_special_device_encoding(index);
3396 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
3397 * a previous glyph in sbuf.
3398 * If TRUE the font is changed to bold and the previous sbuf
3402 int html_printer::overstrike(int index, const char *name, const environment *env, int w)
3404 if ((env->hpos < sbuf_end_hpos)
3405 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
3407 * at this point we have detected an overlap
3409 if (overstrike_detected) {
3410 /* already detected, remove previous glyph and use this glyph */
3411 sbuf.set_length(last_sbuf_length);
3412 add_to_sbuf(index, name);
3413 sbuf_end_hpos = env->hpos + w;
3416 /* first time we have detected an overstrike in the sbuf */
3417 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
3418 if (! is_bold(sbuf_style.f))
3420 overstrike_detected = TRUE;
3421 add_to_sbuf(index, name);
3422 sbuf_end_hpos = env->hpos + w;
3430 * set_char - adds a character into the sbuf if it is a continuation with the previous
3431 * word otherwise flush the current sbuf and add character anew.
3434 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
3436 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
3437 if (sty.slant != 0) {
3438 if (sty.slant > 80 || sty.slant < -80) {
3439 error("silly slant `%1' degrees", sty.slant);
3443 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
3444 && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
3448 add_to_sbuf(i, name);
3449 sbuf_end_hpos = env->hpos + w;
3450 sbuf_start_hpos = env->hpos;
3451 sbuf_prev_hpos = env->hpos;
3452 sbuf_vpos = env->vpos;
3458 * write_title - writes the title to this document
3461 void html_printer::write_title (int in_head)
3463 if (title.has_been_found) {
3465 html.put_string("<title>");
3466 html.put_string(title.text);
3467 html.put_string("</title>").nl().nl();
3469 title.has_been_written = TRUE;
3470 if (title.with_h1) {
3471 html.put_string("<h1 align=center>");
3472 html.put_string(title.text);
3473 html.put_string("</h1>").nl().nl();
3476 } else if (in_head) {
3477 // place empty title tags to help conform to `tidy'
3478 html.put_string("<title></title>").nl();
3483 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
3486 static void write_rule (void)
3489 fputs("<hr>\n", stdout);
3492 void html_printer::begin_page(int n)
3495 #if defined(DEBUGGING)
3496 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
3498 no_of_printed_pages++;
3501 output_style.point_size= -1;
3502 output_space_code = 32;
3503 output_draw_point_size = -1;
3504 output_line_thickness = -1;
3507 output_vpos_max = -1;
3508 current_paragraph = new html_text(&html);
3509 do_indent(indentation, pageoffset, linelength);
3510 current_paragraph->do_para("");
3513 void html_printer::end_page(int)
3519 font *html_printer::make_font(const char *nm)
3521 return html_font::load_html_font(nm);
3524 void html_printer::do_body (void)
3526 if (background == NULL)
3527 fputs("<body>\n\n", stdout);
3529 unsigned int r, g, b;
3532 background->get_rgb(&r, &g, &b);
3533 // we have to scale 0..0xFFFF to 0..0xFF
3534 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
3536 fputs("<body bgcolor=\"#", stdout);
3538 fputs("\">\n\n", stdout);
3542 html_printer::~html_printer()
3544 #ifdef LONG_FOR_TIME_T
3550 current_paragraph->flush_text();
3552 html.set_file(stdout);
3553 html.begin_comment("Creator : ")
3554 .put_string("groff ")
3555 .put_string("version ")
3556 .put_string(Version_string)
3560 html.begin_comment("CreationDate: ")
3561 .put_string(ctime(&t), strlen(ctime(&t))-1)
3565 * 'HTML: The definitive guide', O'Reilly, p47. advises against specifying
3568 // fputs("<!doctype html public \"-//IETF//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n", stdout);
3569 fputs("<html>\n", stdout);
3570 fputs("<head>\n", stdout);
3571 fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
3572 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
3574 fputs("</head>\n", stdout);
3578 header.write_headings(stdout, FALSE);
3580 #if defined(DEBUGGING)
3581 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
3586 * now run through the file list copying each temporary file in turn and emitting the links.
3588 file_list.start_of_list();
3589 while (file_list.get_file() != 0) {
3590 if (fseek(file_list.get_file(), 0L, 0) < 0)
3591 fatal("fseek on temporary file failed");
3592 html.copy_file(file_list.get_file());
3593 fclose(file_list.get_file());
3594 file_list.move_next();
3595 if (file_list.get_file() != 0)
3596 header.write_headings(stdout, TRUE);
3599 fputs("</body>\n", stdout);
3600 fputs("</html>\n", stdout);
3604 * special - handle all x X requests from troff. For post-html they allow users
3605 * to pass raw html commands, turn auto linked headings off/on and
3606 * also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
3609 void html_printer::special(char *s, const environment *env, char type)
3615 if (env->fontno >= 0) {
3616 style sty(get_font_from_index(env->fontno), env->size, env->height,
3617 env->slant, env->fontno, *env->col);
3621 if (strncmp(s, "html:", 5) == 0) {
3622 int r=font::res; /* resolution of the device */
3623 font *f=sbuf_style.f;
3628 f = font::load_font("TR", &found);
3632 * need to pass rest of string through to html output during flush
3634 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
3636 env->vpos-env->size*r/72, env->hpos,
3637 env->vpos , env->hpos);
3640 * assume that the html command has no width, if it does then hopefully troff
3641 * will have fudged this in a macro by requesting that the formatting move right by
3642 * the appropriate amount.
3644 } else if (strncmp(s, "index:", 6) == 0) {
3645 cutoff_heading = atoi(&s[6]);
3646 } else if (strncmp(s, "html-tag:", 9) == 0) {
3647 int r=font::res; /* resolution of the device */
3649 page_contents->add_tag(&sbuf_style, string(s),
3651 env->vpos-env->size*r/72, env->hpos,
3652 env->vpos , env->hpos);
3657 int main(int argc, char **argv)
3659 program_name = argv[0];
3660 static char stderr_buf[BUFSIZ];
3661 setbuf(stderr, stderr_buf);
3663 static const struct option long_options[] = {
3664 { "help", no_argument, 0, CHAR_MAX + 1 },
3665 { "version", no_argument, 0, 'v' },
3668 while ((c = getopt_long(argc, argv, "a:g:o:i:I:D:F:vbdhlrnp", long_options, NULL))
3672 printf("GNU post-grohtml (groff) version %s\n", Version_string);
3676 /* text antialiasing bits - handled by pre-html */
3679 /* graphic antialiasing bits - handled by pre-html */
3682 // set background color to white
3683 default_background = new color;
3684 default_background->set_gray(color::MAX_COLOR_VAL);
3687 font::command_line_font_dir(optarg);
3696 /* handled by pre-html */
3699 /* do not use the Hn headings of html, but manufacture our own */
3700 manufacture_headings = TRUE;
3703 /* handled by pre-html */
3706 /* handled by pre-html */
3709 /* handled by pre-html */
3712 /* handled by pre-html */
3715 /* handled by pre-html */
3718 simple_anchors = TRUE;
3720 case CHAR_MAX + 1: // --help
3731 if (optind >= argc) {
3734 for (int i = optind; i < argc; i++)
3741 static void usage(FILE *stream)
3743 fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",