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"
41 extern "C" const char *Version_string
;
50 #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
51 #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
52 #define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */
53 #define CENTER_TOLERANCE 2 /* how many pixels off center will we still */
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 #define INDENTATION /* #undef INDENTATION to remove .in handling */
60 typedef enum {CENTERED
, LEFT
, RIGHT
, INLINE
} TAG_ALIGNMENT
;
66 char *get_html_translation (font
*f
, const string
&name
);
67 int char_translate_to_html (font
*f
, char *buf
, int buflen
, unsigned char ch
, int b
, int and_single
);
70 static int auto_links
= TRUE
; /* by default we enable automatic links at */
71 /* top of the document. */
72 static int auto_rule
= TRUE
; /* by default we enable an automatic rule */
73 /* at the top and bottom of the document */
74 static int simple_anchors
= FALSE
; /* default to anchors with heading text */
75 static int manufacture_headings
= FALSE
; /* default is to use the Hn html headings, */
76 /* rather than manufacture our own. */
77 static color
*default_background
= NULL
; /* has user requested initial bg color? */
81 * start with a few favorites
86 static int min (int a
, int b
)
95 static int max (int a
, int b
)
105 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
108 static int is_intersection (int a1
, int a2
, int b1
, int b2
)
110 // again easier to prove NOT outside limits
111 return( ! ((a1
> b2
) || (a2
< b1
)) );
115 * is_digit - returns TRUE if character, ch, is a digit.
118 static int is_digit (char ch
)
120 return( (ch
>= '0') && (ch
<= '9') );
124 * the classes and methods for maintaining a list of files.
135 * file - initialize all fields to NULL
146 FILE *get_file (void);
147 void start_of_list (void);
148 void move_next (void);
149 void add_new_file (FILE *f
);
157 * files - create an empty list of files.
161 : head(0), tail(0), ptr(0)
166 * get_file - returns the FILE associated with ptr.
169 FILE *files::get_file (void)
179 * start_of_list - reset the ptr to the start of the list.
182 void files::start_of_list (void)
188 * move_next - moves the ptr to the next element on the list.
191 void files::move_next (void)
198 * add_new_file - adds a new file, f, to the list.
201 void files::add_new_file (FILE *f
)
207 tail
->next
= new file(f
);
214 * the class and methods for styles
225 style (font
*, int, int, int, int, color
);
226 int operator == (const style
&) const;
227 int operator != (const style
&) const;
235 style::style(font
*p
, int sz
, int h
, int sl
, int no
, color c
)
236 : f(p
), point_size(sz
), font_no(no
), height(h
), slant(sl
), col(c
)
240 int style::operator==(const style
&s
) const
242 return (f
== s
.f
&& point_size
== s
.point_size
243 && height
== s
.height
&& slant
== s
.slant
&& col
== s
.col
);
246 int style::operator!=(const style
&s
) const
248 return !(*this == s
);
252 * the class and methods for retaining ascii text
264 char_block::char_block()
273 char *add_string(const char *, unsigned int);
274 char *add_string(const string
&);
280 char_buffer::char_buffer()
285 char_buffer::~char_buffer()
288 char_block
*temp
= head
;
294 char *char_buffer::add_string (const char *s
, unsigned int length
)
297 unsigned int old_used
;
299 if (s
== NULL
|| length
== 0)
303 tail
= new char_block
;
306 if (tail
->used
+ length
+1 > char_block::SIZE
) {
307 tail
->next
= new char_block
;
311 // at this point we have a tail which is ready for the string.
312 if (tail
->used
+ length
+1 > char_block::SIZE
) {
313 fatal("need to increase char_block::SIZE");
316 old_used
= tail
->used
;
318 tail
->buffer
[tail
->used
] = s
[i
];
324 // add terminating nul character
326 tail
->buffer
[tail
->used
] = '\0';
329 // and return start of new string
331 return( &tail
->buffer
[old_used
] );
334 char *char_buffer::add_string (const string
&s
)
336 return add_string(s
.contents(), s
.length());
340 * the classes and methods for maintaining glyph positions.
345 void text_glob_html (style
*s
, char *str
, int length
,
346 int min_vertical
, int min_horizontal
,
347 int max_vertical
, int max_horizontal
);
348 void text_glob_special (style
*s
, char *str
, int length
,
349 int min_vertical
, int min_horizontal
,
350 int max_vertical
, int max_horizontal
);
351 void text_glob_line (style
*s
,
352 int min_vertical
, int min_horizontal
,
353 int max_vertical
, int max_horizontal
,
355 void text_glob_auto_image(style
*s
, char *str
, int length
,
356 int min_vertical
, int min_horizontal
,
357 int max_vertical
, int max_horizontal
);
358 void text_glob_tag (style
*s
, char *str
, int length
,
359 int min_vertical
, int min_horizontal
,
360 int max_vertical
, int max_horizontal
);
364 int is_a_line (void);
367 int is_auto_img (void);
369 int is_eol_ce (void);
372 const char *text_string
;
373 unsigned int text_length
;
374 int minv
, minh
, maxv
, maxh
;
375 int is_tag
; // is this a .br, .sp, .tl etc
376 int is_img_auto
; // image created by eqn delim
377 int is_special
; // text has come via 'x X html:'
378 int is_line
; // is the command a <line>?
379 int thickness
; // the thickness of a line
382 text_glob (style
*s
, char *str
, int length
,
383 int min_vertical
, int min_horizontal
,
384 int max_vertical
, int max_horizontal
,
385 bool is_troff_command
,
386 bool is_auto_image
, bool is_special_command
,
387 bool is_a_line
, int thickness
);
390 text_glob::text_glob (style
*s
, char *str
, int length
,
391 int min_vertical
, int min_horizontal
,
392 int max_vertical
, int max_horizontal
,
393 bool is_troff_command
,
394 bool is_auto_image
, bool is_special_command
,
395 bool is_a_line
, int line_thickness
)
396 : text_style(*s
), text_string(str
), text_length(length
),
397 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
),
398 is_tag(is_troff_command
), is_img_auto(is_auto_image
), is_special(is_special_command
),
399 is_line(is_a_line
), thickness(line_thickness
)
403 text_glob::text_glob ()
404 : text_string(0), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
405 is_tag(FALSE
), is_special(FALSE
), is_line(FALSE
), thickness(0)
409 text_glob::~text_glob ()
414 * text_glob_html - used to place html text into the glob buffer.
417 void text_glob::text_glob_html (style
*s
, char *str
, int length
,
418 int min_vertical
, int min_horizontal
,
419 int max_vertical
, int max_horizontal
)
421 text_glob
*g
= new text_glob(s
, str
, length
,
422 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
423 FALSE
, FALSE
, FALSE
, FALSE
, 0);
428 * text_glob_html - used to place html specials into the glob buffer.
429 * This text is essentially html commands coming through
430 * from the macro sets, with special designated sequences of
431 * characters translated into html. See add_and_encode.
434 void text_glob::text_glob_special (style
*s
, char *str
, int length
,
435 int min_vertical
, int min_horizontal
,
436 int max_vertical
, int max_horizontal
)
438 text_glob
*g
= new text_glob(s
, str
, length
,
439 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
440 FALSE
, FALSE
, TRUE
, FALSE
, 0);
445 * text_glob_line - record horizontal draw line commands.
448 void text_glob::text_glob_line (style
*s
,
449 int min_vertical
, int min_horizontal
,
450 int max_vertical
, int max_horizontal
,
453 text_glob
*g
= new text_glob(s
, "", 0,
454 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
455 FALSE
, FALSE
, FALSE
, TRUE
, thickness
);
460 * text_glob_auto_image - record the presence of a .auto-image tag command.
461 * Used to mark that an image has been created automatically
462 * by a preprocessor and (pre-grohtml/troff) combination.
463 * Under some circumstances images may not be created.
470 * $1 over x$!recripical of x
473 * the first auto-image marker is created via .EQ/.EN pair
474 * and no image is created.
475 * The second auto-image marker occurs at $1 over x$
476 * Currently this image will not be created
477 * as the whole of the table is created as an image.
478 * (Once html tables are handled by grohtml this will change.
479 * Shortly this will be the case).
482 void text_glob::text_glob_auto_image(style
*s
, char *str
, int length
,
483 int min_vertical
, int min_horizontal
,
484 int max_vertical
, int max_horizontal
)
486 text_glob
*g
= new text_glob(s
, str
, length
,
487 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
488 TRUE
, TRUE
, FALSE
, FALSE
, 0);
493 * text_glob_tag - records a troff tag.
496 void text_glob::text_glob_tag (style
*s
, char *str
, int length
,
497 int min_vertical
, int min_horizontal
,
498 int max_vertical
, int max_horizontal
)
500 text_glob
*g
= new text_glob(s
, str
, length
,
501 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
502 TRUE
, FALSE
, FALSE
, FALSE
, 0);
507 * is_a_line - returns TRUE if glob should be converted into an <hr>
510 int text_glob::is_a_line (void)
516 * is_a_tag - returns TRUE if glob contains a troff directive.
519 int text_glob::is_a_tag (void)
525 * is_eol - returns TRUE if glob contains the tag eol
528 int text_glob::is_eol (void)
530 return( is_tag
&& (strcmp(text_string
, "html-tag:eol") == 0) );
534 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
537 int text_glob::is_eol_ce (void)
539 return( is_tag
&& (strcmp(text_string
, "html-tag:eol.ce") == 0) );
543 * is_auto_img - returns TRUE if the glob contains an automatically
547 int text_glob::is_auto_img (void)
553 * is_br - returns TRUE if the glob is a tag containing a .br
557 int text_glob::is_br (void)
559 return( is_a_tag() && ((strcmp ("html-tag:.br", text_string
) == 0) ||
560 (strncmp("html-tag:.sp", text_string
, 11) == 0) ||
561 (strcmp ("html-tag:.ce", text_string
) == 0) ||
562 (strcmp ("html-tag:.nf", text_string
) == 0)) );
566 * the class and methods used to construct ordered double linked lists.
567 * In a previous implementation we used templates via #include "ordered-list.h",
568 * but this does assume that all C++ compilers can handle this feature. Pragmatically
569 * it is safer to assume this is not the case.
572 struct element_list
{
577 int minv
, minh
, maxv
, maxh
;
579 element_list (text_glob
*d
,
581 int min_vertical
, int min_horizontal
,
582 int max_vertical
, int max_horizontal
);
586 element_list::element_list ()
587 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
592 * element_list - create a list element assigning the datum and region parameters.
595 element_list::element_list (text_glob
*in
,
597 int min_vertical
, int min_horizontal
,
598 int max_vertical
, int max_horizontal
)
599 : right(0), left(0), datum(in
), lineno(line_number
),
600 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
)
608 int is_less (element_list
*a
, element_list
*b
);
609 void add (text_glob
*in
,
611 int min_vertical
, int min_horizontal
,
612 int max_vertical
, int max_horizontal
);
613 void sub_move_right (void);
614 void move_right (void);
615 void move_left (void);
617 int is_equal_to_tail (void);
618 int is_equal_to_head (void);
619 void start_from_head (void);
620 void start_from_tail (void);
621 text_glob
*move_right_get_data (void);
622 text_glob
*move_left_get_data (void);
623 text_glob
*get_data (void);
631 * list - construct an empty list.
635 : head(0), tail(0), ptr(0)
640 * ~list - destroy a complete list.
645 element_list
*temp
=head
;
653 } while ((head
!= 0) && (head
!= tail
));
657 * is_less - returns TRUE if a is left of b if on the same line or
658 * if a is higher up the page than b.
661 int list::is_less (element_list
*a
, element_list
*b
)
663 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
664 if (a
->lineno
< b
->lineno
) {
666 } else if (a
->lineno
> b
->lineno
) {
668 } else if (is_intersection(a
->minv
, a
->maxv
, b
->minv
, b
->maxv
)) {
669 return( a
->minh
< b
->minh
);
671 return( a
->maxv
< b
->maxv
);
676 * add - adds a datum to the list in the order specified by the region position.
679 void list::add (text_glob
*in
, int line_number
, int min_vertical
, int min_horizontal
, int max_vertical
, int max_horizontal
)
681 // create a new list element with datum and position fields initialized
682 element_list
*t
= new element_list(in
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
693 while ((last
!= head
) && (is_less(t
, last
))) {
697 if (is_less(t
, last
)) {
699 last
->left
->right
= t
;
700 t
->left
= last
->left
;
702 // now check for a new head
708 t
->right
= last
->right
;
710 last
->right
->left
= t
;
712 // now check for a new tail
721 * sub_move_right - removes the element which is currently pointed to by ptr
722 * from the list and moves ptr to the right.
725 void list::sub_move_right (void)
727 element_list
*t
=ptr
->right
;
743 ptr
->left
->right
= ptr
->right
;
744 ptr
->right
->left
= ptr
->left
;
750 * start_from_head - assigns ptr to the head.
753 void list::start_from_head (void)
759 * start_from_tail - assigns ptr to the tail.
762 void list::start_from_tail (void)
768 * is_empty - returns TRUE if the list has no elements.
771 int list::is_empty (void)
777 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
780 int list::is_equal_to_tail (void)
782 return( ptr
== tail
);
786 * is_equal_to_head - returns TRUE if the ptr equals the head.
789 int list::is_equal_to_head (void)
791 return( ptr
== head
);
795 * move_left - moves the ptr left.
798 void list::move_left (void)
804 * move_right - moves the ptr right.
807 void list::move_right (void)
813 * get_datum - returns the datum referenced via ptr.
816 text_glob
* list::get_data (void)
818 return( ptr
->datum
);
822 * move_right_get_data - returns the datum referenced via ptr and moves
826 text_glob
* list::move_right_get_data (void)
832 return( ptr
->datum
);
837 * move_left_get_data - returns the datum referenced via ptr and moves
841 text_glob
* list::move_left_get_data (void)
847 return( ptr
->datum
);
852 * page class and methods
858 void add (style
*s
, const string
&str
,
860 int min_vertical
, int min_horizontal
,
861 int max_vertical
, int max_horizontal
);
862 void add_tag (style
*s
, const string
&str
,
864 int min_vertical
, int min_horizontal
,
865 int max_vertical
, int max_horizontal
);
866 void add_and_encode (style
*s
, const string
&str
,
868 int min_vertical
, int min_horizontal
,
869 int max_vertical
, int max_horizontal
);
870 void add_line (style
*s
,
872 int x1
, int y1
, int x2
, int y2
,
874 void dump_page (void); // debugging method
878 list glyphs
; // position of glyphs and specials on page
879 char_buffer buffer
; // all characters for this page
887 * add - add html text to the list of glyphs.
890 void page::add (style
*s
, const string
&str
,
892 int min_vertical
, int min_horizontal
,
893 int max_vertical
, int max_horizontal
)
895 if (str
.length() > 0) {
896 text_glob
*g
=new text_glob();
897 g
->text_glob_html(s
, buffer
.add_string(str
), str
.length(),
898 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
899 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
904 * add_tag - adds a troff tag, for example: .tl .sp .br
907 void page::add_tag (style
*s
, const string
&str
,
909 int min_vertical
, int min_horizontal
,
910 int max_vertical
, int max_horizontal
)
912 if (str
.length() > 0) {
915 if (strncmp((str
+'\0').contents(), "html-tag:.auto-image", 20) == 0) {
917 g
->text_glob_auto_image(s
, buffer
.add_string(str
), str
.length(),
918 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
921 g
->text_glob_tag(s
, buffer
.add_string(str
), str
.length(),
922 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
924 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
929 * add_line - adds the <line> primitive providing that y1==y2
932 void page::add_line (style
*s
,
934 int x1
, int y1
, int x2
, int y2
,
938 text_glob
*g
= new text_glob();
940 min(y1
, y2
), min(x1
, y2
), max(y1
, y2
), max(x1
, x2
),
942 glyphs
.add(g
, line_number
, min(y1
, y2
), min(x1
, y2
), max(y1
, y2
), max(x1
, x2
));
947 * to_unicode - returns a unicode translation of char, ch.
950 static char *to_unicode (unsigned char ch
)
954 sprintf(buf
, "&#%u;", (unsigned int)ch
);
959 * add_and_encode - adds a special string to the page, it translates the string
960 * into html glyphs. The special string will have come from x X html:
961 * and can contain troff character encodings which appear as
962 * \(char\). A sequence of \\ represents \.
963 * So for example we can write:
964 * "cost = \(Po\)3.00 file = \\foo\\bar"
965 * which is translated into:
966 * "cost = £3.00 file = \foo\bar"
969 void page::add_and_encode (style
*s
, const string
&str
,
971 int min_vertical
, int min_horizontal
,
972 int max_vertical
, int max_horizontal
)
980 while (i
< str
.length()) {
981 if ((i
+1<str
.length()) && (str
.substring(i
, 2) == string("\\("))) {
983 i
+= 2; // move over \(
985 while ((i
+1<str
.length()) && (str
.substring(i
, 2) != string("\\)"))) {
989 if ((i
+1<str
.length()) && (str
.substring(i
, 2) == string("\\)")))
994 string troff_charname
= str
.substring(a
, n
-a
);
995 html_glyph
= get_html_translation(s
->f
, troff_charname
);
997 html_string
+= html_glyph
;
999 int index
=s
->f
->name_to_index((troff_charname
+ '\0').contents());
1001 if (s
->f
->contains(index
) && (index
!= 0))
1002 html_string
+= s
->f
->get_code(index
);
1006 html_string
+= str
[i
];
1009 if (html_string
.length() > 0) {
1010 text_glob
*g
=new text_glob();
1011 g
->text_glob_special(s
, buffer
.add_string(html_string
), html_string
.length(),
1012 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1013 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1018 * dump_page - dump the page contents for debugging purposes.
1021 void page::dump_page(void)
1025 printf("\n\ndebugging start\n");
1026 glyphs
.start_from_head();
1028 g
= glyphs
.get_data();
1029 printf("%s ", g
->text_string
);
1030 glyphs
.move_right();
1031 } while (! glyphs
.is_equal_to_head());
1032 printf("\ndebugging end\n\n");
1036 * font classes and methods
1039 class html_font
: public font
{
1040 html_font(const char *);
1044 char *reencoded_name
;
1046 static html_font
*load_html_font(const char *);
1049 html_font
*html_font::load_html_font(const char *s
)
1051 html_font
*f
= new html_font(s
);
1059 html_font::html_font(const char *nm
)
1064 html_font::~html_font()
1069 * a simple class to contain the header to this document
1077 int has_been_written
;
1084 title_desc::title_desc ()
1085 : has_been_written(FALSE
), has_been_found(FALSE
), with_h1(FALSE
)
1089 title_desc::~title_desc ()
1098 int no_of_headings
; // how many headings have we found?
1099 char_buffer headings
; // all the headings used in the document
1100 list headers
; // list of headers built from .NH and .SH
1101 int header_level
; // current header level
1102 int written_header
; // have we written the header yet?
1103 string header_buffer
; // current header text
1105 void write_headings (FILE *f
, int force
);
1108 header_desc::header_desc ()
1109 : no_of_headings(0), header_level(2), written_header(0)
1113 header_desc::~header_desc ()
1118 * write_headings - emits a list of links for the headings in this document
1121 void header_desc::write_headings (FILE *f
, int force
)
1125 if (auto_links
|| force
) {
1126 if (! headers
.is_empty()) {
1129 headers
.start_from_head();
1131 g
= headers
.get_data();
1132 fputs("<a href=\"#", f
);
1133 if (simple_anchors
) {
1134 string
buffer(ANCHOR_TEMPLATE
);
1136 buffer
+= as_string(h
);
1138 fprintf(f
, buffer
.contents());
1140 fputs(g
->text_string
, f
);
1143 fputs(g
->text_string
, f
);
1144 fputs("</a><br>\n", f
);
1145 headers
.move_right();
1146 } while (! headers
.is_equal_to_head());
1152 class html_printer
: public printer
{
1156 int space_char_index
;
1158 int no_of_printed_pages
;
1161 int sbuf_start_hpos
;
1167 int last_sbuf_length
;
1168 int overstrike_detected
;
1172 int output_vpos_max
;
1173 int output_draw_point_size
;
1175 int output_line_thickness
;
1176 unsigned char output_space_code
;
1177 char *inside_font_style
;
1180 title_desc indent
; // use title class to remember $1 of .ip
1183 int supress_sub_sup
;
1185 page
*page_contents
;
1186 html_text
*current_paragraph
;
1189 TAG_ALIGNMENT next_tag
;
1196 int vertical_spacing
;
1201 void set_style (const style
&);
1202 void set_space_code (unsigned char c
);
1203 void do_exec (char *, const environment
*);
1204 void do_import (char *, const environment
*);
1205 void do_def (char *, const environment
*);
1206 void do_mdef (char *, const environment
*);
1207 void do_file (char *, const environment
*);
1208 void set_line_thickness (const environment
*);
1209 void terminate_current_font (void);
1210 void flush_font (void);
1211 void add_to_sbuf (unsigned char code
, const string
&s
);
1212 void write_title (int in_head
);
1213 int sbuf_continuation (unsigned char code
, const char *name
, const environment
*env
, int w
);
1214 void flush_page (void);
1215 void troff_tag (text_glob
*g
);
1216 void flush_globs (void);
1217 void emit_line (text_glob
*g
);
1218 void emit_raw (text_glob
*g
);
1219 void emit_html (text_glob
*g
);
1220 void determine_space (text_glob
*g
);
1221 void start_font (const char *name
);
1222 void end_font (const char *name
);
1223 int is_font_courier (font
*f
);
1224 int is_courier_until_eol (void);
1225 void start_size (int from
, int to
);
1226 void do_font (text_glob
*g
);
1227 void do_center (char *arg
);
1228 void do_break (void);
1230 void do_title (void);
1231 void do_fill (int on
);
1232 void do_heading (char *arg
);
1233 void write_header (void);
1234 void determine_header_level (int level
);
1235 void do_linelength (char *arg
);
1236 void do_pageoffset (char *arg
);
1237 void do_indentation (char *arg
);
1238 void do_tempindent (char *arg
);
1239 void do_indentedparagraph (void);
1240 void do_verticalspacing (char *arg
);
1241 void do_pointsize (char *arg
);
1242 void do_centered_image (void);
1243 void do_left_image (void);
1244 void do_right_image (void);
1245 void do_auto_image (text_glob
*g
, const char *filename
);
1246 void do_links (void);
1247 void do_flush (void);
1248 int is_in_middle (int left
, int right
);
1249 void do_sup_or_sub (text_glob
*g
);
1250 int start_subscript (text_glob
*g
);
1251 int end_subscript (text_glob
*g
);
1252 int start_superscript (text_glob
*g
);
1253 int end_superscript (text_glob
*g
);
1254 void outstanding_eol (int n
);
1255 int is_bold (font
*f
);
1256 font
*make_bold (font
*f
);
1257 int overstrike (unsigned char code
, const char *name
, const environment
*env
, int w
);
1258 void do_body (void);
1264 void set_char (int i
, font
*f
, const environment
*env
, int w
, const char *name
);
1265 void draw (int code
, int *p
, int np
, const environment
*env
);
1266 void begin_page (int);
1267 void end_page (int);
1268 void special (char *arg
, const environment
*env
, char type
);
1269 font
*make_font (const char *);
1270 void end_of_line ();
1273 printer
*make_printer()
1275 return new html_printer
;
1278 static void usage(FILE *stream
);
1280 void html_printer::set_style(const style
&sty
)
1282 const char *fontname
= sty
.f
->get_name();
1283 if (fontname
== NULL
)
1284 fatal("no internalname specified for font");
1287 change_font(fontname
, (font::res
/(72*font::sizescale
))*sty
.point_size
);
1292 * is_bold - returns TRUE if font, f, is bold.
1295 int html_printer::is_bold (font
*f
)
1297 const char *fontname
= f
->get_name();
1298 return (strcmp(fontname
, "B") == 0) || (strcmp(fontname
, "BI") == 0);
1302 * make_bold - if a bold font of, f, exists then return it.
1305 font
*html_printer::make_bold (font
*f
)
1307 const char *fontname
= f
->get_name();
1309 if (strcmp(fontname
, "B") == 0)
1311 if (strcmp(fontname
, "I") == 0)
1312 return font::load_font("BI");
1313 if (strcmp(fontname
, "BI") == 0)
1318 void html_printer::end_of_line()
1325 * emit_line - writes out a horizontal rule.
1328 void html_printer::emit_line (text_glob
*g
)
1330 // --fixme-- needs to know the length in percentage
1331 html
.put_string("<hr>");
1335 * emit_raw - writes the raw html information directly to the device.
1338 void html_printer::emit_raw (text_glob
*g
)
1341 if (next_tag
== INLINE
) {
1343 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
1345 int in_table
=current_paragraph
->is_in_table();
1347 current_paragraph
->done_para();
1351 current_paragraph
->do_para("align=center");
1354 current_paragraph
->do_para("align=left");
1357 current_paragraph
->do_para("align=right");
1360 fatal("unknown enumeration");
1362 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
1363 current_paragraph
->done_para();
1365 supress_sub_sup
= TRUE
;
1366 #if defined(INDENTATION)
1368 current_paragraph
->do_indent(NULL
, 0, pageoffset
, linelength
);
1369 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
1370 indent
.text
.clear();
1377 * do_center - handle the .ce commands from troff.
1380 void html_printer::do_center (char *arg
)
1384 current_paragraph
->do_break();
1385 current_paragraph
->done_para();
1386 supress_sub_sup
= TRUE
;
1389 current_paragraph
->do_para("align=center");
1397 * do_centered_image - set a flag such that the next html-tag is
1398 * placed inside a centered paragraph.
1401 void html_printer::do_centered_image (void)
1403 next_tag
= CENTERED
;
1407 * do_right_image - set a flag such that the next html-tag is
1408 * placed inside a right aligned paragraph.
1411 void html_printer::do_right_image (void)
1417 * do_left_image - set a flag such that the next html-tag is
1418 * placed inside a left aligned paragraph.
1421 void html_printer::do_left_image (void)
1427 * exists - returns TRUE if filename exists.
1430 static int exists (const char *filename
)
1432 FILE *fp
= fopen(filename
, "r");
1443 * generate_img_src - returns a html image tag for the filename
1444 * providing that the image exists.
1447 static string
&generate_img_src (const char *filename
)
1449 string
*s
= new string("");
1451 while (filename
&& (filename
[0] == ' ')) {
1454 if (exists(filename
))
1455 *s
+= string("<img src=\"") + filename
+ "\">";
1460 * do_auto_image - tests whether the image, indicated by filename,
1461 * is present, if so then it emits an html image tag.
1462 * An image tag may be passed through from pic, eqn
1463 * but the corresponding image might not be created.
1464 * Consider .EQ delim $$ .EN or an empty .PS .PE.
1467 void html_printer::do_auto_image (text_glob
*g
, const char *filename
)
1469 string buffer
= generate_img_src(filename
);
1471 if (! buffer
.empty()) {
1473 * utilize emit_raw by creating a new text_glob.
1477 h
.text_string
= buffer
.contents();
1478 h
.text_length
= buffer
.length();
1485 * outstanding_eol - call do_eol, n, times.
1488 void html_printer::outstanding_eol (int n
)
1497 * do_title - handle the .tl commands from troff.
1500 void html_printer::do_title (void)
1503 int removed_from_head
;
1506 if (page_number
== 1) {
1507 int found_title_start
= FALSE
;
1508 if (! page_contents
->glyphs
.is_empty()) {
1509 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1511 t
= page_contents
->glyphs
.get_data();
1512 removed_from_head
= FALSE
;
1513 if (t
->is_auto_img()) {
1514 string img
= generate_img_src((char *)(t
->text_string
+ 20));
1516 if (! img
.empty()) {
1517 if (found_title_start
)
1519 found_title_start
= TRUE
;
1520 title
.has_been_found
= TRUE
;
1523 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1524 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1525 (page_contents
->glyphs
.is_equal_to_head()));
1526 } else if (t
->is_eol_ce()) {
1527 /* process the eol associated with .ce
1530 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1531 } else if (t
->is_eol()) {
1532 /* end of title found
1534 title
.has_been_found
= TRUE
;
1535 outstanding_eol(eol_ce
);
1537 } else if (t
->is_a_tag()) {
1538 /* end of title found, but move back so that we read this tag and process it
1540 page_contents
->glyphs
.move_left(); /* move backwards to last word */
1541 title
.has_been_found
= TRUE
;
1542 outstanding_eol(eol_ce
);
1544 } else if (found_title_start
) {
1545 title
.text
+= " " + string(t
->text_string
, t
->text_length
);
1546 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1547 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1548 (page_contents
->glyphs
.is_equal_to_head()));
1550 title
.text
+= string(t
->text_string
, t
->text_length
);
1551 found_title_start
= TRUE
;
1552 title
.has_been_found
= TRUE
;
1553 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1554 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1555 (page_contents
->glyphs
.is_equal_to_head()));
1557 } while ((! page_contents
->glyphs
.is_equal_to_head()) || (removed_from_head
));
1559 outstanding_eol(eol_ce
);
1563 void html_printer::write_header (void)
1565 if (! header
.header_buffer
.empty()) {
1566 if (header
.header_level
> 7) {
1567 header
.header_level
= 7;
1570 // firstly we must terminate any font and type faces
1571 current_paragraph
->done_para();
1572 current_paragraph
->done_table();
1573 supress_sub_sup
= TRUE
;
1575 if (cutoff_heading
+2 > header
.header_level
) {
1576 // now we save the header so we can issue a list of links
1577 header
.no_of_headings
++;
1580 text_glob
*h
=new text_glob();
1581 h
->text_glob_html(&st
,
1582 header
.headings
.add_string(header
.header_buffer
),
1583 header
.header_buffer
.length(),
1584 header
.no_of_headings
, header
.header_level
,
1585 header
.no_of_headings
, header
.header_level
);
1587 header
.headers
.add(h
,
1588 header
.no_of_headings
,
1589 header
.no_of_headings
, header
.no_of_headings
,
1590 header
.no_of_headings
, header
.no_of_headings
); // and add this header to the header list
1592 // lastly we generate a tag
1594 html
.nl().put_string("<a name=\"");
1595 if (simple_anchors
) {
1596 string
buffer(ANCHOR_TEMPLATE
);
1598 buffer
+= as_string(header
.no_of_headings
);
1600 html
.put_string(buffer
.contents());
1602 html
.put_string(header
.header_buffer
);
1604 html
.put_string("\"></a>").nl();
1607 if (manufacture_headings
) {
1608 // line break before a header
1609 if (!current_paragraph
->emitted_text())
1610 current_paragraph
->do_space();
1611 // user wants manufactured headings which look better than <Hn></Hn>
1612 if (header
.header_level
<4) {
1613 html
.put_string("<b><font size=\"+1\">");
1614 html
.put_string(header
.header_buffer
);
1615 html
.put_string("</font></b>").nl();
1618 html
.put_string("<b>");
1619 html
.put_string(header
.header_buffer
);
1620 html
.put_string("</b>").nl();
1624 // and now we issue the real header
1625 html
.put_string("<h");
1626 html
.put_number(header
.header_level
);
1627 html
.put_string(">");
1628 html
.put_string(header
.header_buffer
);
1629 html
.put_string("</h");
1630 html
.put_number(header
.header_level
);
1631 html
.put_string(">").nl();
1634 current_paragraph
->do_para("");
1638 void html_printer::determine_header_level (int level
)
1643 for (i
=0; ((i
<header
.header_buffer
.length())
1644 && ((header
.header_buffer
[i
] == '.')
1645 || is_digit(header
.header_buffer
[i
]))) ; i
++) {
1646 if (header
.header_buffer
[i
] == '.') {
1651 header
.header_level
= level
+1;
1655 * do_heading - handle the .SH and .NH and equivalent commands from troff.
1658 void html_printer::do_heading (char *arg
)
1662 int level
=atoi(arg
);
1664 header
.header_buffer
.clear();
1665 page_contents
->glyphs
.move_right();
1666 if (! page_contents
->glyphs
.is_equal_to_head()) {
1667 g
= page_contents
->glyphs
.get_data();
1669 if (g
->is_auto_img()) {
1670 string img
=generate_img_src((char *)(g
->text_string
+ 20));
1672 if (! img
.empty()) {
1673 simple_anchors
= TRUE
; // we cannot use full heading anchors with images
1675 header
.header_buffer
+= " ";
1678 header
.header_buffer
+= img
;
1680 } else if (! (g
->is_a_line() || g
->is_a_tag())) {
1682 * we ignore tags commands when constructing a heading
1685 header
.header_buffer
+= " ";
1688 header
.header_buffer
+= string(g
->text_string
, g
->text_length
);
1690 page_contents
->glyphs
.move_right();
1691 g
= page_contents
->glyphs
.get_data();
1692 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
1696 determine_header_level(level
);
1699 // finally set the output to neutral for after the header
1700 g
= page_contents
->glyphs
.get_data();
1701 page_contents
->glyphs
.move_left(); // so that next time we use old g
1705 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
1708 int html_printer::is_courier_until_eol (void)
1710 text_glob
*orig
= page_contents
->glyphs
.get_data();
1714 if (! page_contents
->glyphs
.is_equal_to_tail()) {
1715 page_contents
->glyphs
.move_right();
1717 g
= page_contents
->glyphs
.get_data();
1718 if (! is_font_courier(g
->text_style
.f
)) {
1721 page_contents
->glyphs
.move_right();
1722 } while ((result
) &&
1723 (! page_contents
->glyphs
.is_equal_to_head()) &&
1727 * now restore our previous position.
1729 while (page_contents
->glyphs
.get_data() != orig
) {
1730 page_contents
->glyphs
.move_left();
1737 * do_linelength - handle the .ll command from troff.
1740 void html_printer::do_linelength (char *arg
)
1742 #if defined(INDENTATION)
1744 linelength
= atoi(arg
);
1745 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
1746 indent
.text
.clear();
1752 * do_pageoffset - handle the .po command from troff.
1755 void html_printer::do_pageoffset (char *arg
)
1757 #if defined(INDENTATION)
1758 pageoffset
= atoi(arg
);
1760 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
1761 indent
.text
.clear();
1767 * do_indentation - handle the .in command from troff.
1770 void html_printer::do_indentation (char *arg
)
1772 #if defined(INDENTATION)
1774 indentation
= atoi(arg
);
1775 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
1776 indent
.text
.clear();
1782 * do_tempindent - handle the .ti command from troff.
1785 void html_printer::do_tempindent (char *arg
)
1787 #if defined(INDENTATION)
1790 prev_indent
= indentation
;
1791 indentation
= atoi(arg
);
1792 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
1793 indent
.text
.clear();
1799 * do_indentedparagraph - handle the .ip tag, this buffers the next line
1800 * and passes this to text-text as the left hand
1801 * column table entry.
1804 void html_printer::do_indentedparagraph (void)
1806 #if defined(INDENTATION)
1808 int removed_from_head
;
1809 int found_indent_start
= FALSE
;
1811 indent
.has_been_found
= FALSE
;
1812 indent
.text
.clear();
1814 if (! page_contents
->glyphs
.is_empty()) {
1815 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1817 t
= page_contents
->glyphs
.get_data();
1818 removed_from_head
= FALSE
;
1819 if (t
->is_auto_img()) {
1820 string img
= generate_img_src((char *)(t
->text_string
+ 20));
1822 if (! img
.empty()) {
1823 if (found_indent_start
)
1826 found_indent_start
= TRUE
;
1829 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1830 } else if (t
->is_a_tag() && (strncmp(t
->text_string
, "html-tag:.br", 12) == 0)) {
1831 /* end of indented para found, but move back so that we read this tag and process it
1833 page_contents
->glyphs
.move_left(); /* move backwards to last word */
1834 indent
.has_been_found
= TRUE
;
1836 } else if (t
->is_a_tag()) {
1837 if (strncmp(t
->text_string
, "html-tag:.ti", 12) == 0) {
1838 char *a
= (char *)t
->text_string
+12;
1841 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1842 } else if (found_indent_start
) {
1843 indent
.text
+= " " + string(t
->text_string
, t
->text_length
);
1844 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1845 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1846 (page_contents
->glyphs
.is_equal_to_head()));
1848 indent
.text
+= string(t
->text_string
, t
->text_length
);
1849 found_indent_start
= TRUE
;
1850 indent
.has_been_found
= TRUE
;
1851 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1852 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1853 (page_contents
->glyphs
.is_equal_to_head()));
1855 } while ((! page_contents
->glyphs
.is_equal_to_head()) || (removed_from_head
));
1857 // page_contents->glyphs.move_left(); /* move backwards to last word */
1862 * do_verticalspacing - handle the .vs command from troff.
1865 void html_printer::do_verticalspacing (char *arg
)
1867 vertical_spacing
= atoi(arg
);
1871 * do_pointsize - handle the .ps command from troff.
1874 void html_printer::do_pointsize (char *arg
)
1876 pointsize
= atoi(arg
);
1880 * do_fill - records whether troff has requested that text be filled.
1883 void html_printer::do_fill (int on
)
1885 current_paragraph
->do_break();
1886 output_hpos
= indentation
+pageoffset
;
1887 supress_sub_sup
= TRUE
;
1889 if (fill_on
!= on
) {
1891 current_paragraph
->done_pre();
1893 current_paragraph
->do_pre();
1900 * do_eol - handle the end of line
1903 void html_printer::do_eol (void)
1906 if (current_paragraph
->emitted_text()) {
1907 current_paragraph
->do_newline();
1908 current_paragraph
->do_break();
1911 output_hpos
= indentation
+pageoffset
;
1912 if (end_center
> 0) {
1914 if (current_paragraph
->emitted_text())
1915 current_paragraph
->do_break();
1918 if (end_center
== 0) {
1919 current_paragraph
->done_para();
1920 supress_sub_sup
= TRUE
;
1926 * do_flush - flushes all output and tags.
1929 void html_printer::do_flush (void)
1931 current_paragraph
->done_para();
1932 current_paragraph
->done_table();
1936 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
1939 void html_printer::do_links (void)
1941 current_paragraph
->done_para();
1942 current_paragraph
->done_table();
1943 auto_links
= FALSE
; /* from now on only emit under user request */
1944 file_list
.add_new_file(xtmpfile());
1945 html
.set_file(file_list
.get_file());
1949 * do_break - handles the ".br" request and also
1950 * undoes an outstanding ".ti" command.
1953 void html_printer::do_break (void)
1955 current_paragraph
->do_break();
1956 #if defined(INDENTATION)
1957 if (end_tempindent
> 0) {
1959 if (end_tempindent
== 0) {
1960 indentation
= prev_indent
;
1961 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
1962 indent
.text
.clear();
1966 output_hpos
= indentation
+pageoffset
;
1967 supress_sub_sup
= TRUE
;
1971 * troff_tag - processes the troff tag and manipulates the troff state machine.
1974 void html_printer::troff_tag (text_glob
*g
)
1977 * firstly skip over html-tag:
1979 char *t
=(char *)g
->text_string
+9;
1983 } else if (g
->is_eol_ce()) {
1985 } else if (strncmp(t
, ".sp", 3) == 0) {
1986 current_paragraph
->do_space();
1987 supress_sub_sup
= TRUE
;
1988 } else if (strncmp(t
, ".br", 3) == 0) {
1990 } else if (strcmp(t
, ".centered-image") == 0) {
1991 do_centered_image();
1992 } else if (strcmp(t
, ".right-image") == 0) {
1994 } else if (strcmp(t
, ".left-image") == 0) {
1996 } else if (strncmp(t
, ".auto-image", 11) == 0) {
1997 char *a
= (char *)t
+11;
1998 do_auto_image(g
, a
);
1999 } else if (strncmp(t
, ".ce", 3) == 0) {
2000 char *a
= (char *)t
+3;
2001 supress_sub_sup
= TRUE
;
2003 } else if (strncmp(t
, ".tl", 3) == 0) {
2004 supress_sub_sup
= TRUE
;
2005 title
.with_h1
= TRUE
;
2007 } else if (strncmp(t
, ".html-tl", 8) == 0) {
2008 supress_sub_sup
= TRUE
;
2009 title
.with_h1
= FALSE
;
2011 } else if (strncmp(t
, ".fi", 3) == 0) {
2013 } else if (strncmp(t
, ".nf", 3) == 0) {
2015 } else if ((strncmp(t
, ".SH", 3) == 0) || (strncmp(t
, ".NH", 3) == 0)) {
2016 char *a
= (char *)t
+3;
2018 } else if (strncmp(t
, ".ll", 3) == 0) {
2019 char *a
= (char *)t
+3;
2021 } else if (strncmp(t
, ".po", 3) == 0) {
2022 char *a
= (char *)t
+3;
2024 } else if (strncmp(t
, ".in", 3) == 0) {
2025 char *a
= (char *)t
+3;
2027 } else if (strncmp(t
, ".ti", 3) == 0) {
2028 char *a
= (char *)t
+3;
2030 } else if (strncmp(t
, ".vs", 3) == 0) {
2031 char *a
= (char *)t
+3;
2032 do_verticalspacing(a
);
2033 } else if (strncmp(t
, ".ip", 3) == 0) {
2034 do_indentedparagraph();
2035 } else if (strcmp(t
, ".links") == 0) {
2037 } else if (strcmp(t
, ".no-auto-rule") == 0) {
2043 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
2046 int html_printer::is_in_middle (int left
, int right
)
2048 return( abs(abs(left
-pageoffset
) - abs(pageoffset
+linelength
-right
)) <= CENTER_TOLERANCE
);
2052 * flush_globs - runs through the text glob list and emits html.
2055 void html_printer::flush_globs (void)
2059 if (! page_contents
->glyphs
.is_empty()) {
2060 page_contents
->glyphs
.start_from_head();
2062 g
= page_contents
->glyphs
.get_data();
2064 if (strcmp(g
->text_string
, "XXXXXXX") == 0) {
2068 if (g
->is_a_tag()) {
2070 } else if (g
->is_a_line()) {
2076 * after processing the title (and removing it) the glyph list might be empty
2078 if (! page_contents
->glyphs
.is_empty()) {
2079 page_contents
->glyphs
.move_right();
2081 } while (! page_contents
->glyphs
.is_equal_to_head());
2085 void html_printer::flush_page (void)
2087 supress_sub_sup
= TRUE
;
2089 // page_contents->dump_page();
2091 current_paragraph
->done_para();
2092 current_paragraph
->done_table();
2094 // move onto a new page
2095 delete page_contents
;
2096 page_contents
= new page
;
2100 * determine_space - works out whether we need to write a space.
2101 * If last glyph is ajoining then no space emitted.
2104 void html_printer::determine_space (text_glob
*g
)
2106 if (current_paragraph
->is_in_pre()) {
2108 * .nf has been specified
2110 while (output_hpos
< g
->minh
) {
2111 output_hpos
+= space_width
;
2112 current_paragraph
->emit_space();
2115 if ((output_vpos
!= g
->minv
) || (output_hpos
< g
->minh
)) {
2116 current_paragraph
->emit_space();
2122 * is_font_courier - returns TRUE if the font, f, is courier.
2125 int html_printer::is_font_courier (font
*f
)
2128 const char *fontname
= f
->get_name();
2130 return( (fontname
!= 0) && (fontname
[0] == 'C') );
2136 * end_font - shuts down the font corresponding to fontname.
2139 void html_printer::end_font (const char *fontname
)
2141 if (strcmp(fontname
, "B") == 0) {
2142 current_paragraph
->done_bold();
2143 } else if (strcmp(fontname
, "I") == 0) {
2144 current_paragraph
->done_italic();
2145 } else if (strcmp(fontname
, "BI") == 0) {
2146 current_paragraph
->done_bold();
2147 current_paragraph
->done_italic();
2148 } else if (strcmp(fontname
, "CR") == 0) {
2149 current_paragraph
->done_tt();
2154 * start_font - starts the font corresponding to name.
2157 void html_printer::start_font (const char *fontname
)
2159 if (strcmp(fontname
, "R") == 0) {
2160 current_paragraph
->done_bold();
2161 current_paragraph
->done_italic();
2162 current_paragraph
->done_tt();
2163 } else if (strcmp(fontname
, "B") == 0) {
2164 current_paragraph
->do_bold();
2165 } else if (strcmp(fontname
, "I") == 0) {
2166 current_paragraph
->do_italic();
2167 } else if (strcmp(fontname
, "BI") == 0) {
2168 current_paragraph
->do_bold();
2169 current_paragraph
->do_italic();
2170 } else if (strcmp(fontname
, "CR") == 0) {
2171 if ((! fill_on
) && (is_courier_until_eol())) {
2172 current_paragraph
->do_pre();
2174 current_paragraph
->do_tt();
2179 * start_size - from is old font size, to is the new font size.
2180 * The html increase <big> and <small> decrease alters the
2181 * font size by 20%. We try and map these onto glyph sizes.
2184 void html_printer::start_size (int from
, int to
)
2188 current_paragraph
->do_big();
2189 from
+= SIZE_INCREMENT
;
2191 } else if (from
> to
) {
2193 current_paragraph
->do_small();
2194 from
-= SIZE_INCREMENT
;
2200 * do_font - checks to see whether we need to alter the html font.
2203 void html_printer::do_font (text_glob
*g
)
2206 * check if the output_style.point_size has not been set yet
2207 * this allow users to place .ps at the top of their troff files
2208 * and grohtml can then treat the .ps value as the base font size (3)
2210 if (output_style
.point_size
== -1) {
2211 output_style
.point_size
= pointsize
;
2214 if (g
->text_style
.f
!= output_style
.f
) {
2215 if (output_style
.f
!= 0) {
2216 end_font(output_style
.f
->get_name());
2218 output_style
.f
= g
->text_style
.f
;
2219 if (output_style
.f
!= 0) {
2220 start_font(output_style
.f
->get_name());
2223 if (output_style
.point_size
!= g
->text_style
.point_size
) {
2225 if ((output_style
.point_size
> 0) &&
2226 (g
->text_style
.point_size
> 0)) {
2227 start_size(output_style
.point_size
, g
->text_style
.point_size
);
2229 if (g
->text_style
.point_size
> 0) {
2230 output_style
.point_size
= g
->text_style
.point_size
;
2233 if (output_style
.col
!= g
->text_style
.col
) {
2234 current_paragraph
->done_color();
2235 output_style
.col
= g
->text_style
.col
;
2236 current_paragraph
->do_color(&output_style
.col
);
2241 * start_subscript - returns TRUE if, g, looks like a subscript start.
2244 int html_printer::start_subscript (text_glob
*g
)
2247 int height
= output_style
.point_size
*r
/72;
2249 return( (output_style
.point_size
!= 0) &&
2250 (output_vpos
< g
->minv
) &&
2251 (output_vpos
-height
> g
->maxv
) &&
2252 (output_style
.point_size
> g
->text_style
.point_size
) );
2256 * start_superscript - returns TRUE if, g, looks like a superscript start.
2259 int html_printer::start_superscript (text_glob
*g
)
2262 int height
= output_style
.point_size
*r
/72;
2264 return( (output_style
.point_size
!= 0) &&
2265 (output_vpos
> g
->minv
) &&
2266 (output_vpos
-height
< g
->maxv
) &&
2267 (output_style
.point_size
> g
->text_style
.point_size
) );
2271 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
2274 int html_printer::end_subscript (text_glob
*g
)
2277 int height
= output_style
.point_size
*r
/72;
2279 return( (output_style
.point_size
!= 0) &&
2280 (g
->minv
< output_vpos
) &&
2281 (output_vpos
-height
> g
->maxv
) &&
2282 (output_style
.point_size
< g
->text_style
.point_size
) );
2286 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
2289 int html_printer::end_superscript (text_glob
*g
)
2292 int height
= output_style
.point_size
*r
/72;
2294 return( (output_style
.point_size
!= 0) &&
2295 (g
->minv
> output_vpos
) &&
2296 (output_vpos
-height
< g
->maxv
) &&
2297 (output_style
.point_size
< g
->text_style
.point_size
) );
2301 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
2302 * start/end and it calls the services of html-text to issue the
2306 void html_printer::do_sup_or_sub (text_glob
*g
)
2308 if (! supress_sub_sup
) {
2309 if (start_subscript(g
)) {
2310 current_paragraph
->do_sub();
2311 } else if (start_superscript(g
)) {
2312 current_paragraph
->do_sup();
2313 } else if (end_subscript(g
)) {
2314 current_paragraph
->done_sub();
2315 } else if (end_superscript(g
)) {
2316 current_paragraph
->done_sup();
2322 * emit_html - write out the html text
2325 void html_printer::emit_html (text_glob
*g
)
2329 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
2330 output_vpos
= g
->minv
;
2331 output_hpos
= g
->maxh
;
2332 output_vpos_max
= g
->maxv
;
2333 supress_sub_sup
= FALSE
;
2337 * flush_sbuf - flushes the current sbuf into the list of glyphs.
2340 void html_printer::flush_sbuf()
2342 if (sbuf
.length() > 0) {
2343 int r
=font::res
; // resolution of the device
2344 set_style(sbuf_style
);
2345 if (overstrike_detected
&& (! is_bold(sbuf_style
.f
))) {
2346 font
*bold_font
= make_bold(sbuf_style
.f
);
2347 if (bold_font
!= NULL
)
2348 sbuf_style
.f
= bold_font
;
2351 page_contents
->add(&sbuf_style
, sbuf
,
2353 sbuf_vpos
-sbuf_style
.point_size
*r
/72, sbuf_start_hpos
,
2354 sbuf_vpos
, sbuf_end_hpos
);
2356 output_hpos
= sbuf_end_hpos
;
2357 output_vpos
= sbuf_vpos
;
2358 last_sbuf_length
= 0;
2359 sbuf_prev_hpos
= sbuf_end_hpos
;
2360 overstrike_detected
= FALSE
;
2365 void html_printer::set_line_thickness(const environment
*env
)
2367 line_thickness
= env
->size
;
2370 void html_printer::draw(int code
, int *p
, int np
, const environment
*env
)
2377 page_contents
->add_line(&sbuf_style
,
2379 env
->hpos
, env
->vpos
, env
->hpos
+p
[0], env
->vpos
+p
[1], line_thickness
);
2381 error("2 arguments required for line");
2388 line_thickness
= -1;
2390 // troff gratuitously adds an extra 0
2391 if (np
!= 1 && np
!= 2) {
2392 error("0 or 1 argument required for thickness");
2395 line_thickness
= p
[0];
2419 // fill with color env->fill
2420 if (background
!= NULL
)
2422 background
= new color
;
2423 *background
= *env
->fill
;
2427 error("unrecognised drawing command `%1'", char(code
));
2432 html_printer::html_printer()
2433 : html(0, MAX_LINE_LENGTH
),
2434 no_of_printed_pages(0),
2435 last_sbuf_length(0),
2436 overstrike_detected(FALSE
),
2439 output_vpos_max(-1),
2441 inside_font_style(0),
2444 supress_sub_sup(TRUE
),
2445 cutoff_heading(100),
2455 background(default_background
)
2457 file_list
.add_new_file(xtmpfile());
2458 html
.set_file(file_list
.get_file());
2459 if (font::hor
!= 24)
2460 fatal("horizontal resolution must be 24");
2461 if (font::vert
!= 40)
2462 fatal("vertical resolution must be 40");
2464 // should be sorted html..
2465 if (font::res
% (font::sizescale
*72) != 0)
2466 fatal("res must be a multiple of 72*sizescale");
2470 while (r
% 10 == 0) {
2475 html
.set_fixed_point(point
);
2476 space_char_index
= font::name_to_index("space");
2477 space_width
= font::hor
;
2478 paper_length
= font::paperlength
;
2479 linelength
= font::res
*13/2;
2480 if (paper_length
== 0)
2481 paper_length
= 11*font::res
;
2483 page_contents
= new page();
2487 * add_to_sbuf - adds character code or name to the sbuf.
2490 void html_printer::add_to_sbuf (unsigned char code
, const string
&s
)
2495 html_glyph
= get_html_translation(sbuf_style
.f
, string(code
));
2496 if ((html_glyph
== NULL
) && (code
>= UNICODE_DESC_START
))
2497 html_glyph
= to_unicode(code
);
2499 if (sbuf_style
.f
!= NULL
)
2500 html_glyph
= get_html_translation(sbuf_style
.f
, s
);
2502 last_sbuf_length
= sbuf
.length();
2503 if (html_glyph
== NULL
)
2509 int html_printer::sbuf_continuation (unsigned char code
, const char *name
,
2510 const environment
*env
, int w
)
2513 * lets see whether the glyph is closer to the end of sbuf
2515 if ((sbuf_end_hpos
== env
->hpos
)
2516 || ((sbuf_prev_hpos
< sbuf_end_hpos
)
2517 && (env
->hpos
< sbuf_end_hpos
)
2518 && ((sbuf_end_hpos
-env
->hpos
< env
->hpos
-sbuf_prev_hpos
)))) {
2519 add_to_sbuf(code
, name
);
2520 sbuf_prev_hpos
= sbuf_end_hpos
;
2521 sbuf_end_hpos
+= w
+ sbuf_kern
;
2524 if ((env
->hpos
>= sbuf_end_hpos
) &&
2525 ((sbuf_kern
== 0) || (sbuf_end_hpos
- sbuf_kern
!= env
->hpos
))) {
2527 * lets see whether a space is needed or not
2530 if (env
->hpos
-sbuf_end_hpos
< space_width
) {
2531 add_to_sbuf(code
, name
);
2532 sbuf_prev_hpos
= sbuf_end_hpos
;
2533 sbuf_end_hpos
= env
->hpos
+ w
;
2542 * get_html_translation - given the position of the character and its name
2543 * return the device encoding for such character.
2546 char *get_html_translation (font
*f
, const string
&name
)
2550 if ((f
== 0) || name
.empty())
2553 index
= f
->name_to_index((char *)(name
+ '\0').contents());
2555 error("character `%s' not found", (name
+ '\0').contents());
2558 if (f
->contains(index
))
2559 return (char *)f
->get_special_device_encoding(index
);
2566 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
2567 * a previous glyph in sbuf.
2568 * If TRUE the font is changed to bold and the previous sbuf
2572 int html_printer::overstrike(unsigned char code
, const char *name
, const environment
*env
, int w
)
2574 if ((env
->hpos
< sbuf_end_hpos
)
2575 || ((sbuf_kern
!= 0) && (sbuf_end_hpos
- sbuf_kern
< env
->hpos
))) {
2577 * at this point we have detected an overlap
2579 if (overstrike_detected
) {
2580 /* already detected, remove previous glyph and use this glyph */
2581 sbuf
.set_length(last_sbuf_length
);
2582 add_to_sbuf(code
, name
);
2583 sbuf_end_hpos
= env
->hpos
+ w
;
2586 /* first time we have detected an overstrike in the sbuf */
2587 sbuf
.set_length(last_sbuf_length
); /* remove previous glyph */
2588 if (! is_bold(sbuf_style
.f
))
2590 overstrike_detected
= TRUE
;
2591 add_to_sbuf(code
, name
);
2592 sbuf_end_hpos
= env
->hpos
+ w
;
2600 * set_char - adds a character into the sbuf if it is a continuation with the previous
2601 * word otherwise flush the current sbuf and add character anew.
2604 void html_printer::set_char(int i
, font
*f
, const environment
*env
, int w
, const char *name
)
2606 unsigned char code
= f
->get_code(i
);
2608 style
sty(f
, env
->size
, env
->height
, env
->slant
, env
->fontno
, *env
->col
);
2609 if (sty
.slant
!= 0) {
2610 if (sty
.slant
> 80 || sty
.slant
< -80) {
2611 error("silly slant `%1' degrees", sty
.slant
);
2615 if (((! sbuf
.empty()) && (sty
== sbuf_style
) && (sbuf_vpos
== env
->vpos
))
2616 && (sbuf_continuation(code
, name
, env
, w
) || overstrike(code
, name
, env
, w
)))
2620 add_to_sbuf(code
, name
);
2621 sbuf_end_hpos
= env
->hpos
+ w
;
2622 sbuf_start_hpos
= env
->hpos
;
2623 sbuf_prev_hpos
= env
->hpos
;
2624 sbuf_vpos
= env
->vpos
;
2630 * write_title - writes the title to this document
2633 void html_printer::write_title (int in_head
)
2635 if (title
.has_been_found
) {
2637 html
.put_string("<title>");
2638 html
.put_string(title
.text
);
2639 html
.put_string("</title>").nl().nl();
2641 title
.has_been_written
= TRUE
;
2642 if (title
.with_h1
) {
2643 html
.put_string("<h1 align=center>");
2644 html
.put_string(title
.text
);
2645 html
.put_string("</h1>").nl().nl();
2648 } else if (in_head
) {
2649 // place empty title tags to help conform to `tidy'
2650 html
.put_string("<title></title>").nl();
2655 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
2658 static void write_rule (void)
2661 fputs("<hr>\n", stdout
);
2664 void html_printer::begin_page(int n
)
2667 #if defined(DEBUGGING)
2668 html
.begin_comment("Page: ").put_string(i_to_a(page_number
)).end_comment();;
2670 no_of_printed_pages
++;
2673 output_style
.point_size
= -1;
2674 output_space_code
= 32;
2675 output_draw_point_size
= -1;
2676 output_line_thickness
= -1;
2679 output_vpos_max
= -1;
2680 current_paragraph
= new html_text(&html
);
2681 #if defined(INDENTATION)
2682 current_paragraph
->do_indent(page_contents
->buffer
.add_string(indent
.text
), indentation
, pageoffset
, linelength
);
2683 indent
.text
.clear();
2685 current_paragraph
->do_para("");
2688 void html_printer::end_page(int)
2694 font
*html_printer::make_font(const char *nm
)
2696 return html_font::load_html_font(nm
);
2699 void html_printer::do_body (void)
2701 if (background
== NULL
)
2702 fputs("<body>\n\n", stdout
);
2704 unsigned int r
, g
, b
;
2707 background
->get_rgb(&r
, &g
, &b
);
2708 // we have to scale 0..0xFFFF to 0..0xFF
2709 sprintf(buf
, "%.2X%.2X%.2X", r
/0x101, g
/0x101, b
/0x101);
2711 fputs("<body bgcolor=\"#", stdout
);
2713 fputs("\">\n\n", stdout
);
2717 html_printer::~html_printer()
2719 current_paragraph
->flush_text();
2721 html
.set_file(stdout
);
2723 * 'HTML: The definitive guide', O'Reilly, p47. advises against specifying
2724 * the dtd, so for the moment I'll leave this commented out.
2725 * If requested we could always emit it if a command line switch
2728 * fputs("<!doctype html public \"-//IETF//DTD HTML 4.0//EN\">\n", stdout);
2730 fputs("<html>\n", stdout
);
2731 fputs("<head>\n", stdout
);
2732 fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout
);
2733 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout
);
2735 fputs("</head>\n", stdout
);
2739 header
.write_headings(stdout
, FALSE
);
2742 html
.begin_comment("Creator : ")
2743 .put_string("groff ")
2744 .put_string("version ")
2745 .put_string(Version_string
)
2749 #ifdef LONG_FOR_TIME_T
2755 html
.begin_comment("CreationDate: ")
2756 .put_string(ctime(&t
), strlen(ctime(&t
))-1)
2759 #if defined(DEBUGGING)
2760 html
.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages
)).end_comment();
2765 * now run through the file list copying each temporary file in turn and emitting the links.
2767 file_list
.start_of_list();
2768 while (file_list
.get_file() != 0) {
2769 if (fseek(file_list
.get_file(), 0L, 0) < 0)
2770 fatal("fseek on temporary file failed");
2771 html
.copy_file(file_list
.get_file());
2772 fclose(file_list
.get_file());
2773 file_list
.move_next();
2774 if (file_list
.get_file() != 0)
2775 header
.write_headings(stdout
, TRUE
);
2778 fputs("</body>\n", stdout
);
2779 fputs("</html>\n", stdout
);
2783 * special - handle all x X requests from troff. For post-html they allow users
2784 * to pass raw html commands, turn auto linked headings off/on and
2785 * also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
2788 void html_printer::special(char *s
, const environment
*env
, char type
)
2794 if (env
->fontno
>= 0) {
2795 style
sty(get_font_from_index(env
->fontno
), env
->size
, env
->height
,
2796 env
->slant
, env
->fontno
, *env
->col
);
2800 if (strncmp(s
, "html:", 5) == 0) {
2801 int r
=font::res
; /* resolution of the device */
2802 font
*f
=sbuf_style
.f
;
2807 f
= font::load_font("TR", &found
);
2811 * need to pass rest of string through to html output during flush
2813 page_contents
->add_and_encode(&sbuf_style
, string(&s
[5]),
2815 env
->vpos
-env
->size
*r
/72, env
->hpos
,
2816 env
->vpos
, env
->hpos
);
2819 * assume that the html command has no width, if it does then hopefully troff
2820 * will have fudged this in a macro by requesting that the formatting move right by
2821 * the appropriate amount.
2823 } else if (strncmp(s
, "index:", 6) == 0) {
2824 cutoff_heading
= atoi(&s
[6]);
2825 } else if (strncmp(s
, "html-tag:", 9) == 0) {
2826 int r
=font::res
; /* resolution of the device */
2828 page_contents
->add_tag(&sbuf_style
, string(s
),
2830 env
->vpos
-env
->size
*r
/72, env
->hpos
,
2831 env
->vpos
, env
->hpos
);
2836 int main(int argc
, char **argv
)
2838 program_name
= argv
[0];
2839 static char stderr_buf
[BUFSIZ
];
2840 setbuf(stderr
, stderr_buf
);
2842 static const struct option long_options
[] = {
2843 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
2844 { "version", no_argument
, 0, 'v' },
2847 while ((c
= getopt_long(argc
, argv
, "a:g:o:i:I:D:F:vbdhlrnp", long_options
, NULL
))
2851 printf("GNU post-grohtml (groff) version %s\n", Version_string
);
2855 /* text antialiasing bits - handled by pre-html */
2858 /* graphic antialiasing bits - handled by pre-html */
2861 // set background color to white
2862 default_background
= new color
;
2863 default_background
->set_gray(color::MAX_COLOR_VAL
);
2866 font::command_line_font_dir(optarg
);
2875 /* handled by pre-html */
2878 /* do not use the Hn headings of html, but manufacture our own */
2879 manufacture_headings
= TRUE
;
2882 /* handled by pre-html */
2885 /* handled by pre-html */
2888 /* handled by pre-html */
2891 /* handled by pre-html */
2894 /* handled by pre-html */
2897 simple_anchors
= TRUE
;
2899 case CHAR_MAX
+ 1: // --help
2910 if (optind
>= argc
) {
2913 for (int i
= optind
; i
< argc
; i
++)
2920 static void usage(FILE *stream
)
2922 fprintf(stream
, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",