2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 2000 - 2007
5 * Free Software Foundation, Inc.
7 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
8 * but it owes a huge amount of ideas and raw code from
9 * James Clark (jjc@jclark.com) grops/ps.cpp.
12 * This 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 * This 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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
28 #include "html-config.h"
41 #include "stringclass.h"
44 #include "html-text.h"
45 #include "html-table.h"
47 typedef enum {CENTERED
, LEFT
, RIGHT
, INLINE
} TAG_ALIGNMENT
;
48 typedef enum {col_tag
, tab_tag
, tab0_tag
, none
} colType
;
50 const char *get_html_translation (font
*f
, const string
&name
);
51 static const char *get_html_entity(unsigned int code
);
52 int char_translate_to_html (font
*f
, char *buf
, int buflen
, unsigned char ch
, int b
, int and_single
);
54 static int auto_links
= true; /* by default we enable automatic links at */
55 /* top of the document. */
56 static int auto_rule
= true; /* by default we enable an automatic rule */
57 /* at the top and bottom of the document */
58 static int simple_anchors
= false; /* default to anchors with heading text */
59 static int manufacture_headings
= false; /* default is to use the Hn html headings, */
60 /* rather than manufacture our own. */
61 static color
*default_background
= NULL
; /* has user requested initial bg color? */
62 static string job_name
; /* if set then the output is split into */
63 /* multiple files with `job_name'-%d.html */
64 static int multiple_files
= false; /* must we the output be divided into */
65 /* multiple html files, one for each */
67 static int base_point_size
= 0; /* which troff font size maps onto html */
69 static int split_level
= 2; /* what heading level to split at? */
70 static string head_info
; /* user supplied information to be placed */
71 /* into <head> </head> */
72 static int valid_flag
= false; /* has user requested a valid flag at the */
73 /* end of each page? */
74 static int roff_sig
= false; /* "This document was produced using" */
75 html_dialect dialect
= html4
; /* which html dialect should grohtml output */
78 * start with a few favorites
83 static int min (int a
, int b
)
91 static int max (int a
, int b
)
100 * is_intersection - returns true if range a1..a2 intersects with b1..b2
103 static int is_intersection (int a1
, int a2
, int b1
, int b2
)
105 // easier to prove NOT outside limits
106 return ! ((a1
> b2
) || (a2
< b1
));
110 * is_digit - returns true if character, ch, is a digit.
113 static int is_digit (char ch
)
115 return (ch
>= '0') && (ch
<= '9');
119 * the classes and methods for maintaining a list of files.
127 string output_file_name
;
133 * file - initialize all fields to NULL
137 : fp(f
), next(NULL
), new_output_file(false),
138 require_links(false), output_file_name("")
150 FILE *get_file (void);
151 void start_of_list (void);
152 void move_next (void);
153 void add_new_file (FILE *f
);
154 void set_file_name (string name
);
155 void set_links_required (void);
156 int are_links_required (void);
157 int is_new_output_file (void);
158 string
file_name (void);
159 string
next_file_name (void);
163 * files - create an empty list of files.
167 : head(NULL
), tail(NULL
), ptr(NULL
)
172 * get_file - returns the FILE associated with ptr.
175 FILE *files::get_file (void)
184 * start_of_list - reset the ptr to the start of the list.
187 void files::start_of_list (void)
193 * move_next - moves the ptr to the next element on the list.
196 void files::move_next (void)
203 * add_new_file - adds a new file, f, to the list.
206 void files::add_new_file (FILE *f
)
212 tail
->next
= new file(f
);
219 * set_file_name - sets the final file name to contain the html
223 void files::set_file_name (string name
)
226 ptr
->output_file_name
= name
;
227 ptr
->new_output_file
= true;
232 * set_links_required - issue links when processing this component
236 void files::set_links_required (void)
239 ptr
->require_links
= true;
243 * are_links_required - returns true if this section of the file
244 * requires that links should be issued.
247 int files::are_links_required (void)
250 return ptr
->require_links
;
255 * is_new_output_file - returns true if this component of the file
256 * is the start of a new output file.
259 int files::is_new_output_file (void)
262 return ptr
->new_output_file
;
267 * file_name - returns the name of the file.
270 string
files::file_name (void)
273 return ptr
->output_file_name
;
278 * next_file_name - returns the name of the next file.
281 string
files::next_file_name (void)
283 if (ptr
!= NULL
&& ptr
->next
!= NULL
)
284 return ptr
->next
->output_file_name
;
289 * the class and methods for styles
302 style (font
*, int, int, int, int, color
);
303 int operator == (const style
&) const;
304 int operator != (const style
&) const;
312 style::style(font
*p
, int sz
, int h
, int sl
, int no
, color c
)
313 : f(p
), point_size(sz
), font_no(no
), height(h
), slant(sl
), col(c
)
317 int style::operator==(const style
&s
) const
319 return (f
== s
.f
&& point_size
== s
.point_size
320 && height
== s
.height
&& slant
== s
.slant
&& col
== s
.col
);
323 int style::operator!=(const style
&s
) const
325 return !(*this == s
);
329 * the class and methods for retaining ascii text
341 char_block(int length
);
345 char_block::char_block()
346 : buffer(NULL
), used(0), next(NULL
)
350 char_block::char_block(int length
)
351 : used(0), next(NULL
)
353 buffer
= new char[max(length
, char_block::SIZE
)];
355 fatal("out of memory error");
358 char_block::~char_block()
368 char *add_string(const char *, unsigned int);
369 char *add_string(const string
&);
375 char_buffer::char_buffer()
376 : head(NULL
), tail(NULL
)
380 char_buffer::~char_buffer()
382 while (head
!= NULL
) {
383 char_block
*temp
= head
;
389 char *char_buffer::add_string (const char *s
, unsigned int length
)
392 unsigned int old_used
;
394 if (s
== NULL
|| length
== 0)
398 tail
= new char_block(length
+1);
401 if (tail
->used
+ length
+1 > char_block::SIZE
) {
402 tail
->next
= new char_block(length
+1);
407 old_used
= tail
->used
;
409 tail
->buffer
[tail
->used
] = s
[i
];
415 // add terminating nul character
417 tail
->buffer
[tail
->used
] = '\0';
420 // and return start of new string
422 return &tail
->buffer
[old_used
];
425 char *char_buffer::add_string (const string
&s
)
427 return add_string(s
.contents(), s
.length());
431 * the classes and methods for maintaining glyph positions.
436 text_glob (style
*s
, const char *str
, int length
,
437 int min_vertical
, int min_horizontal
,
438 int max_vertical
, int max_horizontal
,
439 bool is_troff_command
,
440 bool is_auto_image
, bool is_special_command
,
441 bool is_a_line
, int thickness
);
444 void text_glob_html (style
*s
, char *str
, int length
,
445 int min_vertical
, int min_horizontal
,
446 int max_vertical
, int max_horizontal
);
447 void text_glob_special (style
*s
, char *str
, int length
,
448 int min_vertical
, int min_horizontal
,
449 int max_vertical
, int max_horizontal
);
450 void text_glob_line (style
*s
,
451 int min_vertical
, int min_horizontal
,
452 int max_vertical
, int max_horizontal
,
454 void text_glob_auto_image(style
*s
, char *str
, int length
,
455 int min_vertical
, int min_horizontal
,
456 int max_vertical
, int max_horizontal
);
457 void text_glob_tag (style
*s
, char *str
, int length
,
458 int min_vertical
, int min_horizontal
,
459 int max_vertical
, int max_horizontal
);
463 int is_a_line (void);
466 int is_auto_img (void);
475 int is_eol_ce (void);
480 int is_tab_ts (void);
481 int is_tab_te (void);
486 int get_tab_args (char *align
);
488 void remember_table (html_table
*t
);
489 html_table
*get_table (void);
492 const char *text_string
;
493 unsigned int text_length
;
494 int minv
, minh
, maxv
, maxh
;
495 int is_tag
; // is this a .br, .sp, .tl etc
496 int is_img_auto
; // image created by eqn delim
497 int is_special
; // text has come via 'x X html:'
498 int is_line
; // is the command a <line>?
499 int thickness
; // the thickness of a line
500 html_table
*tab
; // table description
503 text_glob::text_glob (style
*s
, const char *str
, int length
,
504 int min_vertical
, int min_horizontal
,
505 int max_vertical
, int max_horizontal
,
506 bool is_troff_command
,
507 bool is_auto_image
, bool is_special_command
,
508 bool is_a_line_flag
, int line_thickness
)
509 : text_style(*s
), text_string(str
), text_length(length
),
510 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
),
511 is_tag(is_troff_command
), is_img_auto(is_auto_image
), is_special(is_special_command
),
512 is_line(is_a_line_flag
), thickness(line_thickness
), tab(NULL
)
516 text_glob::text_glob ()
517 : text_string(NULL
), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
518 is_tag(false), is_special(false), is_line(false), thickness(0), tab(NULL
)
522 text_glob::~text_glob ()
529 * text_glob_html - used to place html text into the glob buffer.
532 void text_glob::text_glob_html (style
*s
, char *str
, int length
,
533 int min_vertical
, int min_horizontal
,
534 int max_vertical
, int max_horizontal
)
536 text_glob
*g
= new text_glob(s
, str
, length
,
537 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
538 false, false, false, false, 0);
544 * text_glob_html - used to place html specials into the glob buffer.
545 * This text is essentially html commands coming through
546 * from the macro sets, with special designated sequences of
547 * characters translated into html. See add_and_encode.
550 void text_glob::text_glob_special (style
*s
, char *str
, int length
,
551 int min_vertical
, int min_horizontal
,
552 int max_vertical
, int max_horizontal
)
554 text_glob
*g
= new text_glob(s
, str
, length
,
555 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
556 false, false, true, false, 0);
562 * text_glob_line - record horizontal draw line commands.
565 void text_glob::text_glob_line (style
*s
,
566 int min_vertical
, int min_horizontal
,
567 int max_vertical
, int max_horizontal
,
570 text_glob
*g
= new text_glob(s
, "", 0,
571 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
572 false, false, false, true, thickness_value
);
578 * text_glob_auto_image - record the presence of a .auto-image tag command.
579 * Used to mark that an image has been created automatically
580 * by a preprocessor and (pre-grohtml/troff) combination.
581 * Under some circumstances images may not be created.
588 * $1 over x$!recripical of x
591 * the first auto-image marker is created via .EQ/.EN pair
592 * and no image is created.
593 * The second auto-image marker occurs at $1 over x$
594 * Currently this image will not be created
595 * as the whole of the table is created as an image.
596 * (Once html tables are handled by grohtml this will change.
597 * Shortly this will be the case).
600 void text_glob::text_glob_auto_image(style
*s
, char *str
, int length
,
601 int min_vertical
, int min_horizontal
,
602 int max_vertical
, int max_horizontal
)
604 text_glob
*g
= new text_glob(s
, str
, length
,
605 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
606 true, true, false, false, 0);
612 * text_glob_tag - records a troff tag.
615 void text_glob::text_glob_tag (style
*s
, char *str
, int length
,
616 int min_vertical
, int min_horizontal
,
617 int max_vertical
, int max_horizontal
)
619 text_glob
*g
= new text_glob(s
, str
, length
,
620 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
621 true, false, false, false, 0);
627 * is_a_line - returns true if glob should be converted into an <hr>
630 int text_glob::is_a_line (void)
636 * is_a_tag - returns true if glob contains a troff directive.
639 int text_glob::is_a_tag (void)
645 * is_eol - returns true if glob contains the tag eol
648 int text_glob::is_eol (void)
650 return is_tag
&& (strcmp(text_string
, "devtag:.eol") == 0);
654 * is_eol_ce - returns true if glob contains the tag eol.ce
657 int text_glob::is_eol_ce (void)
659 return is_tag
&& (strcmp(text_string
, "devtag:eol.ce") == 0);
663 * is_tl - returns true if glob contains the tag .tl
666 int text_glob::is_tl (void)
668 return is_tag
&& (strcmp(text_string
, "devtag:.tl") == 0);
672 * is_eo_tl - returns true if glob contains the tag eo.tl
675 int text_glob::is_eo_tl (void)
677 return is_tag
&& (strcmp(text_string
, "devtag:.eo.tl") == 0);
681 * is_nf - returns true if glob contains the tag .fi 0
684 int text_glob::is_nf (void)
686 return is_tag
&& (strncmp(text_string
, "devtag:.fi",
687 strlen("devtag:.fi")) == 0) &&
692 * is_fi - returns true if glob contains the tag .fi 1
695 int text_glob::is_fi (void)
697 return( is_tag
&& (strncmp(text_string
, "devtag:.fi",
698 strlen("devtag:.fi")) == 0) &&
703 * is_eo_h - returns true if glob contains the tag .eo.h
706 int text_glob::is_eo_h (void)
708 return is_tag
&& (strcmp(text_string
, "devtag:.eo.h") == 0);
712 * is_ce - returns true if glob contains the tag .ce
715 int text_glob::is_ce (void)
717 return is_tag
&& (strncmp(text_string
, "devtag:.ce",
718 strlen("devtag:.ce")) == 0);
722 * is_in - returns true if glob contains the tag .in
725 int text_glob::is_in (void)
727 return is_tag
&& (strncmp(text_string
, "devtag:.in ",
728 strlen("devtag:.in ")) == 0);
732 * is_po - returns true if glob contains the tag .po
735 int text_glob::is_po (void)
737 return is_tag
&& (strncmp(text_string
, "devtag:.po ",
738 strlen("devtag:.po ")) == 0);
742 * is_ti - returns true if glob contains the tag .ti
745 int text_glob::is_ti (void)
747 return is_tag
&& (strncmp(text_string
, "devtag:.ti ",
748 strlen("devtag:.ti ")) == 0);
752 * is_ll - returns true if glob contains the tag .ll
755 int text_glob::is_ll (void)
757 return is_tag
&& (strncmp(text_string
, "devtag:.ll ",
758 strlen("devtag:.ll ")) == 0);
762 * is_col - returns true if glob contains the tag .col
765 int text_glob::is_col (void)
767 return is_tag
&& (strncmp(text_string
, "devtag:.col",
768 strlen("devtag:.col")) == 0);
772 * is_tab_ts - returns true if glob contains the tag .tab_ts
775 int text_glob::is_tab_ts (void)
777 return is_tag
&& (strcmp(text_string
, "devtag:.tab-ts") == 0);
781 * is_tab_te - returns true if glob contains the tag .tab_te
784 int text_glob::is_tab_te (void)
786 return is_tag
&& (strcmp(text_string
, "devtag:.tab-te") == 0);
790 * is_ta - returns true if glob contains the tag .ta
793 int text_glob::is_ta (void)
795 return is_tag
&& (strncmp(text_string
, "devtag:.ta ",
796 strlen("devtag:.ta ")) == 0);
800 * is_tab - returns true if glob contains the tag tab
803 int text_glob::is_tab (void)
805 return is_tag
&& (strncmp(text_string
, "devtag:tab ",
806 strlen("devtag:tab ")) == 0);
810 * is_tab0 - returns true if glob contains the tag tab0
813 int text_glob::is_tab0 (void)
815 return is_tag
&& (strncmp(text_string
, "devtag:tab0",
816 strlen("devtag:tab0")) == 0);
820 * is_auto_img - returns true if the glob contains an automatically
824 int text_glob::is_auto_img (void)
830 * is_br - returns true if the glob is a tag containing a .br
831 * or an implied .br. Note that we do not include .nf or .fi
832 * as grohtml will place a .br after these commands if they
833 * should break the line.
836 int text_glob::is_br (void)
838 return is_a_tag() && ((strcmp ("devtag:.br", text_string
) == 0) ||
839 (strncmp("devtag:.sp", text_string
,
840 strlen("devtag:.sp")) == 0));
843 int text_glob::get_arg (void)
845 if (strncmp("devtag:", text_string
, strlen("devtag:")) == 0) {
846 const char *p
= text_string
;
848 while ((*p
!= (char)0) && (!isspace(*p
)))
850 while ((*p
!= (char)0) && (isspace(*p
)))
860 * get_tab_args - returns the tab position and alignment of the tab tag
863 int text_glob::get_tab_args (char *align
)
865 if (strncmp("devtag:", text_string
, strlen("devtag:")) == 0) {
866 const char *p
= text_string
;
868 // firstly the alignment C|R|L
869 while ((*p
!= (char)0) && (!isspace(*p
)))
871 while ((*p
!= (char)0) && (isspace(*p
)))
875 while ((*p
!= (char)0) && (!isspace(*p
)))
877 while ((*p
!= (char)0) && (isspace(*p
)))
887 * remember_table - saves table, t, in the text_glob.
890 void text_glob::remember_table (html_table
*t
)
898 * get_table - returns the stored table description.
901 html_table
*text_glob::get_table (void)
907 * the class and methods used to construct ordered double linked
908 * lists. In a previous implementation we used templates via
909 * #include "ordered-list.h", but this does assume that all C++
910 * compilers can handle this feature. Pragmatically it is safer to
911 * assume this is not the case.
921 int minv
, minh
, maxv
, maxh
;
923 element_list (text_glob
*d
,
925 int min_vertical
, int min_horizontal
,
926 int max_vertical
, int max_horizontal
);
931 element_list::element_list ()
932 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
937 * element_list - create a list element assigning the datum and region parameters.
940 element_list::element_list (text_glob
*in
,
942 int min_vertical
, int min_horizontal
,
943 int max_vertical
, int max_horizontal
)
944 : right(0), left(0), datum(in
), lineno(line_number
),
945 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
)
949 element_list::~element_list ()
964 int is_less (element_list
*a
, element_list
*b
);
965 void add (text_glob
*in
,
967 int min_vertical
, int min_horizontal
,
968 int max_vertical
, int max_horizontal
);
969 void sub_move_right (void);
970 void move_right (void);
971 void move_left (void);
973 int is_equal_to_tail (void);
974 int is_equal_to_head (void);
975 void start_from_head (void);
976 void start_from_tail (void);
977 void insert (text_glob
*in
);
978 void move_to (text_glob
*in
);
979 text_glob
*move_right_get_data (void);
980 text_glob
*move_left_get_data (void);
981 text_glob
*get_data (void);
985 * list - construct an empty list.
989 : head(NULL
), tail(NULL
), ptr(NULL
)
994 * ~list - destroy a complete list.
999 element_list
*temp
=head
;
1007 } while ((head
!= NULL
) && (head
!= tail
));
1011 * is_less - returns true if a is left of b if on the same line or
1012 * if a is higher up the page than b.
1015 int list::is_less (element_list
*a
, element_list
*b
)
1017 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1018 if (a
->lineno
< b
->lineno
) {
1020 } else if (a
->lineno
> b
->lineno
) {
1022 } else if (is_intersection(a
->minv
, a
->maxv
, b
->minv
, b
->maxv
)) {
1023 return( a
->minh
< b
->minh
);
1025 return( a
->maxv
< b
->maxv
);
1030 * add - adds a datum to the list in the order specified by the
1034 void list::add (text_glob
*in
, int line_number
, int min_vertical
, int min_horizontal
, int max_vertical
, int max_horizontal
)
1036 // create a new list element with datum and position fields initialized
1037 element_list
*t
= new element_list(in
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1041 fprintf(stderr
, "[%s %d,%d,%d,%d] ",
1042 in
->text_string
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1055 while ((last
!= head
) && (is_less(t
, last
)))
1058 if (is_less(t
, last
)) {
1060 last
->left
->right
= t
;
1061 t
->left
= last
->left
;
1063 // now check for a new head
1067 // add t beyond last
1068 t
->right
= last
->right
;
1070 last
->right
->left
= t
;
1072 // now check for a new tail
1080 * sub_move_right - removes the element which is currently pointed to by ptr
1081 * from the list and moves ptr to the right.
1084 void list::sub_move_right (void)
1086 element_list
*t
=ptr
->right
;
1100 ptr
->left
->right
= ptr
->right
;
1101 ptr
->right
->left
= ptr
->left
;
1107 * start_from_head - assigns ptr to the head.
1110 void list::start_from_head (void)
1116 * start_from_tail - assigns ptr to the tail.
1119 void list::start_from_tail (void)
1125 * is_empty - returns true if the list has no elements.
1128 int list::is_empty (void)
1130 return head
== NULL
;
1134 * is_equal_to_tail - returns true if the ptr equals the tail.
1137 int list::is_equal_to_tail (void)
1143 * is_equal_to_head - returns true if the ptr equals the head.
1146 int list::is_equal_to_head (void)
1152 * move_left - moves the ptr left.
1155 void list::move_left (void)
1161 * move_right - moves the ptr right.
1164 void list::move_right (void)
1170 * get_datum - returns the datum referenced via ptr.
1173 text_glob
* list::get_data (void)
1179 * move_right_get_data - returns the datum referenced via ptr and moves
1183 text_glob
* list::move_right_get_data (void)
1193 * move_left_get_data - returns the datum referenced via ptr and moves
1197 text_glob
* list::move_left_get_data (void)
1207 * insert - inserts data after the current position.
1210 void list::insert (text_glob
*in
)
1213 fatal("list must not be empty if we are inserting data");
1218 element_list
*t
= new element_list(in
, ptr
->lineno
, ptr
->minv
, ptr
->minh
, ptr
->maxv
, ptr
->maxh
);
1221 ptr
->right
->left
= t
;
1222 t
->right
= ptr
->right
;
1229 * move_to - moves the current position to the point where data, in, exists.
1230 * This is an expensive method and should be used sparingly.
1233 void list::move_to (text_glob
*in
)
1236 while (ptr
!= tail
&& ptr
->datum
!= in
)
1241 * page class and methods
1248 void add (style
*s
, const string
&str
,
1250 int min_vertical
, int min_horizontal
,
1251 int max_vertical
, int max_horizontal
);
1252 void add_tag (style
*s
, const string
&str
,
1254 int min_vertical
, int min_horizontal
,
1255 int max_vertical
, int max_horizontal
);
1256 void add_and_encode (style
*s
, const string
&str
,
1258 int min_vertical
, int min_horizontal
,
1259 int max_vertical
, int max_horizontal
,
1261 void add_line (style
*s
,
1263 int x1
, int y1
, int x2
, int y2
,
1265 void insert_tag (const string
&str
);
1266 void dump_page (void); // debugging method
1270 list glyphs
; // position of glyphs and specials on page
1271 char_buffer buffer
; // all characters for this page
1279 * insert_tag - inserts a tag after the current position.
1282 void page::insert_tag (const string
&str
)
1284 if (str
.length() > 0) {
1285 text_glob
*g
=new text_glob();
1286 text_glob
*f
=glyphs
.get_data();
1287 g
->text_glob_tag(&f
->text_style
, buffer
.add_string(str
), str
.length(),
1288 f
->minv
, f
->minh
, f
->maxv
, f
->maxh
);
1294 * add - add html text to the list of glyphs.
1297 void page::add (style
*s
, const string
&str
,
1299 int min_vertical
, int min_horizontal
,
1300 int max_vertical
, int max_horizontal
)
1302 if (str
.length() > 0) {
1303 text_glob
*g
=new text_glob();
1304 g
->text_glob_html(s
, buffer
.add_string(str
), str
.length(),
1305 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1306 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1311 * add_tag - adds a troff tag, for example: .tl .sp .br
1314 void page::add_tag (style
*s
, const string
&str
,
1316 int min_vertical
, int min_horizontal
,
1317 int max_vertical
, int max_horizontal
)
1319 if (str
.length() > 0) {
1322 if (strncmp((str
+'\0').contents(), "devtag:.auto-image",
1323 strlen("devtag:.auto-image")) == 0) {
1324 g
= new text_glob();
1325 g
->text_glob_auto_image(s
, buffer
.add_string(str
), str
.length(),
1326 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1328 g
= new text_glob();
1329 g
->text_glob_tag(s
, buffer
.add_string(str
), str
.length(),
1330 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1332 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1337 * add_line - adds the <line> primitive providing that y1==y2
1340 void page::add_line (style
*s
,
1342 int x_1
, int y_1
, int x_2
, int y_2
,
1346 text_glob
*g
= new text_glob();
1347 g
->text_glob_line(s
,
1348 min(y_1
, y_2
), min(x_1
, x_2
),
1349 max(y_1
, y_2
), max(x_1
, x_2
),
1351 glyphs
.add(g
, line_number
,
1352 min(y_1
, y_2
), min(x_1
, x_2
),
1353 max(y_1
, y_2
), max(x_1
, x_2
));
1358 * to_unicode - returns a unicode translation of int, ch.
1361 static char *to_unicode (unsigned int ch
)
1363 static char buf
[30];
1365 sprintf(buf
, "&#%u;", ch
);
1370 * add_and_encode - adds a special string to the page, it translates the string
1371 * into html glyphs. The special string will have come from x X html:
1372 * and can contain troff character encodings which appear as
1373 * \[char]. A sequence of \\ represents \.
1374 * So for example we can write:
1375 * "cost = \[Po]3.00 file = \\foo\\bar"
1376 * which is translated into:
1377 * "cost = £3.00 file = \foo\bar"
1380 void page::add_and_encode (style
*s
, const string
&str
,
1382 int min_vertical
, int min_horizontal
,
1383 int max_vertical
, int max_horizontal
,
1387 const char *html_glyph
;
1389 const int len
= str
.length();
1394 if ((i
+ 1 < len
) && (str
.substring(i
, 2) == string("\\["))) {
1396 i
+= 2; // move over \[
1398 while ((i
< len
) && (str
[i
] != ']'))
1401 string troff_charname
= str
.substring(a
, i
- a
);
1402 html_glyph
= get_html_translation(s
->f
, troff_charname
);
1404 html_string
+= html_glyph
;
1406 glyph
*g
= name_to_glyph((troff_charname
+ '\0').contents());
1407 if (s
->f
->contains(g
))
1408 html_string
+= s
->f
->get_code(g
);
1413 html_string
+= str
[i
];
1416 if (html_string
.length() > 0) {
1417 text_glob
*g
=new text_glob();
1419 g
->text_glob_tag(s
, buffer
.add_string(html_string
),
1420 html_string
.length(),
1421 min_vertical
, min_horizontal
,
1422 max_vertical
, max_horizontal
);
1424 g
->text_glob_special(s
, buffer
.add_string(html_string
),
1425 html_string
.length(),
1426 min_vertical
, min_horizontal
,
1427 max_vertical
, max_horizontal
);
1428 glyphs
.add(g
, line_number
, min_vertical
,
1429 min_horizontal
, max_vertical
, max_horizontal
);
1434 * dump_page - dump the page contents for debugging purposes.
1437 void page::dump_page(void)
1439 #if defined(DEBUG_TABLES)
1440 text_glob
*old_pos
= glyphs
.get_data();
1444 printf("\n\ndebugging start\n");
1445 glyphs
.start_from_head();
1447 g
= glyphs
.get_data();
1448 if (g
->is_tab_ts()) {
1450 if (g
->get_table() != NULL
)
1451 g
->get_table()->dump_table();
1453 printf("%s ", g
->text_string
);
1456 glyphs
.move_right();
1457 } while (! glyphs
.is_equal_to_head());
1458 glyphs
.move_to(old_pos
);
1459 printf("\ndebugging end\n\n");
1466 * font classes and methods
1472 html_font(const char *);
1477 char *reencoded_name
;
1479 static html_font
*load_html_font(const char *);
1482 html_font
*html_font::load_html_font(const char *s
)
1484 html_font
*f
= new html_font(s
);
1492 html_font::html_font(const char *nm
)
1497 html_font::~html_font()
1502 * a simple class to contain the header to this document
1511 int has_been_written
;
1518 title_desc::title_desc ()
1519 : has_been_written(false), has_been_found(false), with_h1(false)
1523 title_desc::~title_desc ()
1533 int no_of_level_one_headings
; // how many .SH or .NH 1 have we found?
1534 int no_of_headings
; // how many headings have we found?
1535 char_buffer headings
; // all the headings used in the document
1536 list headers
; // list of headers built from .NH and .SH
1537 list header_filename
; // in which file is this header?
1538 int header_level
; // current header level
1539 int written_header
; // have we written the header yet?
1540 string header_buffer
; // current header text
1542 void write_headings (FILE *f
, int force
);
1545 header_desc::header_desc ()
1546 : no_of_level_one_headings(0), no_of_headings(0),
1547 header_level(2), written_header(0)
1551 header_desc::~header_desc ()
1556 * write_headings - emits a list of links for the headings in this document
1559 void header_desc::write_headings (FILE *f
, int force
)
1563 if (auto_links
|| force
) {
1564 if (! headers
.is_empty()) {
1567 headers
.start_from_head();
1568 header_filename
.start_from_head();
1569 if (dialect
== xhtml
)
1572 g
= headers
.get_data();
1573 fputs("<a href=\"", f
);
1574 if (multiple_files
&& (! header_filename
.is_empty())) {
1575 text_glob
*fn
= header_filename
.get_data();
1576 fputs(fn
->text_string
, f
);
1579 if (simple_anchors
) {
1580 string
buffer(ANCHOR_TEMPLATE
);
1582 buffer
+= as_string(h
);
1584 fprintf(f
, "%s", buffer
.contents());
1586 fputs(g
->text_string
, f
);
1589 fputs(g
->text_string
, f
);
1591 if (dialect
== xhtml
)
1592 fputs("<br/>\n", f
);
1595 headers
.move_right();
1596 if (multiple_files
&& (! header_filename
.is_empty()))
1597 header_filename
.move_right();
1598 } while (! headers
.is_equal_to_head());
1600 if (dialect
== xhtml
)
1622 const char *file_br
;
1623 const char *file_ce
;
1624 const char *file_fi
;
1625 const char *file_sp
;
1626 const char *line_br
;
1627 const char *line_ce
;
1628 const char *line_fi
;
1629 const char *line_sp
;
1634 void add (assert_pos
**h
,
1635 const char *c
, const char *i
, const char *v
,
1636 const char *f
, const char *l
);
1637 void compare(assert_pos
*t
,
1638 const char *v
, const char *f
, const char *l
);
1639 void close (const char *c
);
1640 void set (const char *c
, const char *v
,
1641 const char *f
, const char *l
);
1642 void check_value (const char *s
, int v
, const char *name
,
1643 const char *f
, const char *l
, int *flag
);
1644 int check_value_error (int c
, int v
, const char *s
,
1646 const char *f
, const char *l
, int flag
);
1652 void addx (const char *c
, const char *i
, const char *v
,
1653 const char *f
, const char *l
);
1654 void addy (const char *c
, const char *i
, const char *v
,
1655 const char *f
, const char *l
);
1656 void build(const char *c
, const char *v
,
1657 const char *f
, const char *l
);
1658 void check_br (int br
);
1659 void check_ce (int ce
);
1660 void check_fi (int fi
);
1661 void check_sp (int sp
);
1665 assert_state::assert_state ()
1684 assert_state::~assert_state ()
1688 while (xhead
!= NULL
) {
1690 xhead
= xhead
->next
;
1691 a_delete (char *)t
->val
;
1692 a_delete (char *)t
->id
;
1695 while (yhead
!= NULL
) {
1697 yhead
= yhead
->next
;
1698 a_delete (char *)t
->val
;
1699 a_delete (char *)t
->id
;
1704 void assert_state::reset (void)
1712 void assert_state::add (assert_pos
**h
,
1713 const char *c
, const char *i
, const char *v
,
1714 const char *f
, const char *l
)
1719 if (strcmp(t
->id
, i
) == 0)
1723 if (t
!= NULL
&& v
!= NULL
&& (v
[0] != '='))
1724 compare(t
, v
, f
, l
);
1731 if (v
== NULL
|| v
[0] != '=') {
1737 v
= "no value at all";
1738 fprintf(stderr
, "%s:%s:error in assert format of id=%s expecting value to be prefixed with an `=' got %s\n",
1749 void assert_state::addx (const char *c
, const char *i
, const char *v
,
1750 const char *f
, const char *l
)
1752 add(&xhead
, c
, i
, v
, f
, l
);
1755 void assert_state::addy (const char *c
, const char *i
, const char *v
,
1756 const char *f
, const char *l
)
1758 add(&yhead
, c
, i
, v
, f
, l
);
1761 void assert_state::compare(assert_pos
*t
,
1762 const char *v
, const char *f
, const char *l
)
1764 const char *s
=t
->val
;
1771 if (strcmp(v
, s
) != 0) {
1777 "%s:%s: " L_D_HTML
" assertion failed at id%s: should %s, got %s\n",
1782 void assert_state::close (const char *c
)
1784 if (strcmp(c
, "sp") == 0)
1786 else if (strcmp(c
, "br") == 0)
1788 else if (strcmp(c
, "fi") == 0)
1790 else if (strcmp(c
, "nf") == 0)
1792 else if (strcmp(c
, "ce") == 0)
1795 fprintf(stderr
, "internal error: unrecognised tag in " L_D_HTML
" (%s)\n",
1799 const char *replace_negate_str (const char *before
, char *after
)
1802 a_delete (char *)before
;
1804 if (strlen(after
) > 0) {
1805 int d
= atoi(after
);
1807 if (d
< 0 || d
> 1) {
1808 fprintf(stderr
, "expecting nf/fi value to be 0 or 1 not %d\n", d
);
1820 const char *replace_str (const char *before
, const char *after
)
1823 a_delete (char *)before
;
1827 void assert_state::set (const char *c
, const char *v
,
1828 const char *f
, const char *l
)
1835 // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1836 if (strcmp(c
, "sp") == 0) {
1838 val_sp
= replace_str(val_sp
, strsave(v
));
1839 file_sp
= replace_str(file_sp
, strsave(f
));
1840 line_sp
= replace_str(line_sp
, strsave(l
));
1841 } else if (strcmp(c
, "br") == 0) {
1843 val_br
= replace_str(val_br
, strsave(v
));
1844 file_br
= replace_str(file_br
, strsave(f
));
1845 line_br
= replace_str(line_br
, strsave(l
));
1846 } else if (strcmp(c
, "fi") == 0) {
1848 val_fi
= replace_str(val_fi
, strsave(v
));
1849 file_fi
= replace_str(file_fi
, strsave(f
));
1850 line_fi
= replace_str(line_fi
, strsave(l
));
1851 } else if (strcmp(c
, "nf") == 0) {
1853 val_fi
= replace_negate_str(val_fi
, strsave(v
));
1854 file_fi
= replace_str(file_fi
, strsave(f
));
1855 line_fi
= replace_str(line_fi
, strsave(l
));
1856 } else if (strcmp(c
, "ce") == 0) {
1858 val_ce
= replace_str(val_ce
, strsave(v
));
1859 file_ce
= replace_str(file_ce
, strsave(f
));
1860 line_ce
= replace_str(line_ce
, strsave(l
));
1865 * build - builds the troff state assertion.
1866 * see tmac/www.tmac for cmd examples.
1869 void assert_state::build (const char *c
, const char *v
,
1870 const char *f
, const char *l
)
1873 set(&c
[1], v
, f
, l
);
1878 int assert_state::check_value_error (int c
, int v
, const char *s
,
1880 const char *f
, const char *l
, int flag
)
1888 "%s:%s: " L_D_HTML
" assertion failed, <%s>: should %s, got %d\n",
1895 void assert_state::check_value (const char *s
, int v
, const char *name
,
1896 const char *f
, const char *l
, int *flag
)
1898 if (strncmp(s
, "<=", 2) == 0)
1899 *flag
= check_value_error(v
<= atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1900 else if (strncmp(s
, ">=", 2) == 0)
1901 *flag
= check_value_error(v
>= atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1902 else if (strncmp(s
, "==", 2) == 0)
1903 *flag
= check_value_error(v
== atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1904 else if (strncmp(s
, "!=", 2) == 0)
1905 *flag
= check_value_error(v
!= atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1906 else if (strncmp(s
, "<", 1) == 0)
1907 *flag
= check_value_error(v
< atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1908 else if (strncmp(s
, ">", 1) == 0)
1909 *flag
= check_value_error(v
> atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1910 else if (strncmp(s
, "=", 1) == 0)
1911 *flag
= check_value_error(v
== atoi(&s
[1]), v
, s
, name
, f
, l
, *flag
);
1913 *flag
= check_value_error(v
== atoi(s
), v
, s
, name
, f
, l
, *flag
);
1916 void assert_state::check_sp (int sp
)
1919 check_value(val_sp
, sp
, "sp", file_sp
, line_sp
, &check_sp_flag
);
1922 void assert_state::check_fi (int fi
)
1925 check_value(val_fi
, fi
, "fi", file_fi
, line_fi
, &check_fi_flag
);
1928 void assert_state::check_br (int br
)
1931 check_value(val_br
, br
, "br", file_br
, line_br
, &check_br_flag
);
1934 void assert_state::check_ce (int ce
)
1937 check_value(val_ce
, ce
, "ce", file_ce
, line_ce
, &check_ce_flag
);
1948 int no_of_printed_pages
;
1951 int sbuf_start_hpos
;
1957 int last_sbuf_length
;
1958 int overstrike_detected
;
1962 int output_vpos_max
;
1963 int output_draw_point_size
;
1965 int output_line_thickness
;
1966 unsigned char output_space_code
;
1967 char *inside_font_style
;
1972 int supress_sub_sup
;
1974 page
*page_contents
;
1975 html_text
*current_paragraph
;
1976 html_indent
*indent
;
1980 TAG_ALIGNMENT next_tag
;
1989 int vertical_spacing
;
1994 int seen_pageoffset
;
1995 int next_pageoffset
;
1996 int seen_linelength
;
1997 int next_linelength
;
2007 void set_style (const style
&);
2008 void set_space_code (unsigned char c
);
2009 void do_exec (char *, const environment
*);
2010 void do_import (char *, const environment
*);
2011 void do_def (char *, const environment
*);
2012 void do_mdef (char *, const environment
*);
2013 void do_file (char *, const environment
*);
2014 void set_line_thickness (const environment
*);
2015 void terminate_current_font (void);
2016 void flush_font (void);
2017 void add_to_sbuf (glyph
*g
, const string
&s
);
2018 void write_title (int in_head
);
2019 int sbuf_continuation (glyph
*g
, const char *name
, const environment
*env
, int w
);
2020 void flush_page (void);
2021 void troff_tag (text_glob
*g
);
2022 void flush_globs (void);
2023 void emit_line (text_glob
*g
);
2024 void emit_raw (text_glob
*g
);
2025 void emit_html (text_glob
*g
);
2026 void determine_space (text_glob
*g
);
2027 void start_font (const char *name
);
2028 void end_font (const char *name
);
2029 int is_font_courier (font
*f
);
2030 int is_line_start (int nf
);
2031 int is_courier_until_eol (void);
2032 void start_size (int from
, int to
);
2033 void do_font (text_glob
*g
);
2034 void do_center (char *arg
);
2035 void do_check_center (void);
2036 void do_break (void);
2037 void do_space (char *arg
);
2039 void do_eol_ce (void);
2040 void do_title (void);
2041 void do_fill (char *arg
);
2042 void do_heading (char *arg
);
2043 void write_header (void);
2044 void determine_header_level (int level
);
2045 void do_linelength (char *arg
);
2046 void do_pageoffset (char *arg
);
2047 void do_indentation (char *arg
);
2048 void do_tempindent (char *arg
);
2049 void do_indentedparagraph (void);
2050 void do_verticalspacing (char *arg
);
2051 void do_pointsize (char *arg
);
2052 void do_centered_image (void);
2053 void do_left_image (void);
2054 void do_right_image (void);
2055 void do_auto_image (text_glob
*g
, const char *filename
);
2056 void do_links (void);
2057 void do_flush (void);
2058 void do_job_name (char *name
);
2059 void do_head (char *name
);
2060 void insert_split_file (void);
2061 int is_in_middle (int left
, int right
);
2062 void do_sup_or_sub (text_glob
*g
);
2063 int start_subscript (text_glob
*g
);
2064 int end_subscript (text_glob
*g
);
2065 int start_superscript (text_glob
*g
);
2066 int end_superscript (text_glob
*g
);
2067 void outstanding_eol (int n
);
2068 int is_bold (font
*f
);
2069 font
*make_bold (font
*f
);
2070 int overstrike (glyph
*g
, const char *name
, const environment
*env
, int w
);
2071 void do_body (void);
2072 int next_horiz_pos (text_glob
*g
, int nf
);
2073 void lookahead_for_tables (void);
2074 void insert_tab_te (void);
2075 text_glob
*insert_tab_ts (text_glob
*where
);
2076 void insert_tab0_foreach_tab (void);
2077 void insert_tab_0 (text_glob
*where
);
2078 void do_indent (int in
, int pageoff
, int linelen
);
2079 void shutdown_table (void);
2080 void do_tab_ts (text_glob
*g
);
2081 void do_tab_te (void);
2082 void do_col (char *s
);
2083 void do_tab (char *s
);
2084 void do_tab0 (void);
2085 int calc_nf (text_glob
*g
, int nf
);
2086 void calc_po_in (text_glob
*g
, int nf
);
2087 void remove_tabs (void);
2088 void remove_courier_tabs (void);
2089 void update_min_max (colType type_of_col
, int *minimum
, int *maximum
, text_glob
*g
);
2090 void add_table_end (const char *);
2091 void do_file_components (void);
2092 void write_navigation (const string
&top
, const string
&prev
,
2093 const string
&next
, const string
¤t
);
2094 void emit_link (const string
&to
, const char *name
);
2095 int get_troff_indent (void);
2096 void restore_troff_indent (void);
2097 void handle_assertion (int minv
, int minh
, int maxv
, int maxh
, const char *s
);
2098 void handle_state_assertion (text_glob
*g
);
2099 void do_end_para (text_glob
*g
);
2100 int round_width (int x
);
2101 void handle_tag_within_title (text_glob
*g
);
2102 void writeHeadMetaStyle (void);
2103 void handle_valid_flag (int needs_para
);
2104 void do_math (text_glob
*g
);
2105 void write_html_anchor (text_glob
*h
);
2106 void write_xhtml_anchor (text_glob
*h
);
2112 void set_char (glyph
*g
, font
*f
, const environment
*env
, int w
, const char *name
);
2113 void set_numbered_char(int num
, const environment
*env
, int *widthp
);
2114 glyph
*set_char_and_width(const char *nm
, const environment
*env
,
2115 int *widthp
, font
**f
);
2116 void draw (int code
, int *p
, int np
, const environment
*env
);
2117 void begin_page (int);
2118 void end_page (int);
2119 void special (char *arg
, const environment
*env
, char type
);
2120 void devtag (char *arg
, const environment
*env
, char type
);
2121 font
*make_font (const char *);
2122 void end_of_line ();
2125 printer
*make_printer()
2127 return new html_printer
;
2130 static void usage(FILE *stream
);
2132 void html_printer::set_style(const style
&sty
)
2134 const char *fontname
= sty
.f
->get_name();
2135 if (fontname
== NULL
)
2136 fatal("no internalname specified for font");
2139 change_font(fontname
, (font::res
/(72*font::sizescale
))*sty
.point_size
);
2144 * is_bold - returns true if font, f, is bold.
2147 int html_printer::is_bold (font
*f
)
2149 const char *fontname
= f
->get_name();
2150 return (strcmp(fontname
, "B") == 0) || (strcmp(fontname
, "BI") == 0);
2154 * make_bold - if a bold font of, f, exists then return it.
2157 font
*html_printer::make_bold (font
*f
)
2159 const char *fontname
= f
->get_name();
2161 if (strcmp(fontname
, "B") == 0)
2163 if (strcmp(fontname
, "I") == 0)
2164 return font::load_font("BI");
2165 if (strcmp(fontname
, "BI") == 0)
2170 void html_printer::end_of_line()
2177 * emit_line - writes out a horizontal rule.
2180 void html_printer::emit_line (text_glob
*)
2182 // --fixme-- needs to know the length in percentage
2183 if (dialect
== xhtml
)
2184 html
.put_string("<hr/>");
2186 html
.put_string("<hr>");
2190 * restore_troff_indent - is called when we have temporarily shutdown
2191 * indentation (typically done when we have
2192 * centered an image).
2195 void html_printer::restore_troff_indent (void)
2197 troff_indent
= next_indent
;
2198 if (troff_indent
> 0) {
2200 * force device indentation
2203 do_indent(get_troff_indent(), pageoffset
, linelength
);
2208 * emit_raw - writes the raw html information directly to the device.
2211 void html_printer::emit_raw (text_glob
*g
)
2214 if (next_tag
== INLINE
) {
2216 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
2218 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2220 current_paragraph
->done_para();
2225 if (dialect
== html4
)
2226 current_paragraph
->do_para("align=\"center\"", space
);
2228 current_paragraph
->do_para("class=\"center\"", space
);
2231 if (dialect
== html4
)
2232 current_paragraph
->do_para(&html
, "align=\"left\"", get_troff_indent(), pageoffset
, linelength
, space
);
2234 current_paragraph
->do_para(&html
, "class=\"left\"", get_troff_indent(), pageoffset
, linelength
, space
);
2237 if (dialect
== html4
)
2238 current_paragraph
->do_para(&html
, "align=\"right\"", get_troff_indent(), pageoffset
, linelength
, space
);
2240 current_paragraph
->do_para(&html
, "class=\"right\"", get_troff_indent(), pageoffset
, linelength
, space
);
2243 fatal("unknown enumeration");
2245 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
2246 current_paragraph
->done_para();
2248 supress_sub_sup
= true;
2250 restore_troff_indent();
2255 * handle_tag_within_title - handle a limited number of tags within
2256 * the context of a table. Those tags which
2257 * set values rather than generate spaces
2261 void html_printer::handle_tag_within_title (text_glob
*g
)
2263 if (g
->is_in() || g
->is_ti() || g
->is_po() || g
->is_ce() || g
->is_ll()
2264 || g
->is_fi() || g
->is_nf())
2269 * do_center - handle the .ce commands from troff.
2272 void html_printer::do_center (char *arg
)
2274 next_center
= atoi(arg
);
2279 * do_centered_image - set a flag such that the next devtag is
2280 * placed inside a centered paragraph.
2283 void html_printer::do_centered_image (void)
2285 next_tag
= CENTERED
;
2289 * do_right_image - set a flag such that the next devtag is
2290 * placed inside a right aligned paragraph.
2293 void html_printer::do_right_image (void)
2299 * do_left_image - set a flag such that the next devtag is
2300 * placed inside a left aligned paragraph.
2303 void html_printer::do_left_image (void)
2309 * exists - returns true if filename exists.
2312 static int exists (const char *filename
)
2314 FILE *fp
= fopen(filename
, "r");
2325 * generate_img_src - returns a html image tag for the filename
2326 * providing that the image exists.
2329 static string
&generate_img_src (const char *filename
)
2331 string
*s
= new string("");
2333 while (filename
&& (filename
[0] == ' ')) {
2336 if (exists(filename
)) {
2337 *s
+= string("<img src=\"") + filename
+ "\" "
2338 + "alt=\"Image " + filename
+ "\">";
2339 if (dialect
== xhtml
)
2346 * do_auto_image - tests whether the image, indicated by filename,
2347 * is present, if so then it emits an html image tag.
2348 * An image tag may be passed through from pic, eqn
2349 * but the corresponding image might not be created.
2350 * Consider .EQ delim $$ .EN or an empty .PS .PE.
2353 void html_printer::do_auto_image (text_glob
*g
, const char *filename
)
2355 string buffer
= generate_img_src(filename
);
2357 if (! buffer
.empty()) {
2359 * utilize emit_raw by creating a new text_glob.
2363 h
.text_string
= buffer
.contents();
2364 h
.text_length
= buffer
.length();
2371 * outstanding_eol - call do_eol, n, times.
2374 void html_printer::outstanding_eol (int n
)
2383 * do_title - handle the .tl commands from troff.
2386 void html_printer::do_title (void)
2389 int removed_from_head
;
2391 if (page_number
== 1) {
2392 int found_title_start
= false;
2393 if (! page_contents
->glyphs
.is_empty()) {
2394 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2396 t
= page_contents
->glyphs
.get_data();
2397 removed_from_head
= false;
2398 if (t
->is_auto_img()) {
2399 string img
= generate_img_src((char *)(t
->text_string
+ 20));
2401 if (! img
.empty()) {
2402 if (found_title_start
)
2404 found_title_start
= true;
2405 title
.has_been_found
= true;
2408 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2409 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2410 (page_contents
->glyphs
.is_equal_to_head()));
2411 } else if (t
->is_eo_tl()) {
2412 /* end of title found
2414 title
.has_been_found
= true;
2416 } else if (t
->is_a_tag()) {
2417 handle_tag_within_title(t
);
2418 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2419 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2420 (page_contents
->glyphs
.is_equal_to_head()));
2421 } else if (found_title_start
) {
2422 title
.text
+= " " + string(t
->text_string
, t
->text_length
);
2423 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2424 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2425 (page_contents
->glyphs
.is_equal_to_head()));
2427 title
.text
+= string(t
->text_string
, t
->text_length
);
2428 found_title_start
= true;
2429 title
.has_been_found
= true;
2430 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2431 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2432 (page_contents
->glyphs
.is_equal_to_head()));
2434 } while ((! page_contents
->glyphs
.is_equal_to_head()) ||
2435 (removed_from_head
));
2441 * write_html_anchor - writes out an anchor. The style of the anchor
2442 * dependent upon simple_anchor.
2445 void html_printer::write_html_anchor (text_glob
*h
)
2447 if (dialect
== html4
) {
2449 html
.put_string("<a name=\"");
2450 if (simple_anchors
) {
2451 string
buffer(ANCHOR_TEMPLATE
);
2453 buffer
+= as_string(header
.no_of_headings
);
2455 html
.put_string(buffer
.contents());
2457 html
.put_string(header
.header_buffer
);
2458 html
.put_string("\"></a>").nl();
2464 * write_xhtml_anchor - writes out an anchor. The style of the anchor
2465 * dependent upon simple_anchor.
2468 void html_printer::write_xhtml_anchor (text_glob
*h
)
2470 if (dialect
== xhtml
) {
2472 html
.put_string(" id=\"");
2473 if (simple_anchors
) {
2474 string
buffer(ANCHOR_TEMPLATE
);
2476 buffer
+= as_string(header
.no_of_headings
);
2478 html
.put_string(buffer
.contents());
2480 html
.put_string(header
.header_buffer
);
2481 html
.put_string("\"");
2486 void html_printer::write_header (void)
2488 if (! header
.header_buffer
.empty()) {
2489 text_glob
*a
= NULL
;
2490 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2492 if (header
.header_level
> 7)
2493 header
.header_level
= 7;
2495 // firstly we must terminate any font and type faces
2496 current_paragraph
->done_para();
2497 supress_sub_sup
= true;
2499 if (cutoff_heading
+2 > header
.header_level
) {
2500 // now we save the header so we can issue a list of links
2501 header
.no_of_headings
++;
2504 a
= new text_glob();
2505 a
->text_glob_html(&st
,
2506 header
.headings
.add_string(header
.header_buffer
),
2507 header
.header_buffer
.length(),
2508 header
.no_of_headings
, header
.header_level
,
2509 header
.no_of_headings
, header
.header_level
);
2511 // and add this header to the header list
2512 header
.headers
.add(a
,
2513 header
.no_of_headings
,
2514 header
.no_of_headings
, header
.no_of_headings
,
2515 header
.no_of_headings
, header
.no_of_headings
);
2520 if (manufacture_headings
) {
2521 // line break before a header
2522 if (!current_paragraph
->emitted_text())
2523 current_paragraph
->do_space();
2524 // user wants manufactured headings which look better than <Hn></Hn>
2525 if (header
.header_level
<4) {
2526 html
.put_string("<b><font size=\"+1\">");
2527 html
.put_string(header
.header_buffer
);
2528 html
.put_string("</font>").nl();
2529 write_html_anchor(a
);
2530 html
.put_string("</b>").nl();
2533 html
.put_string("<b>");
2534 html
.put_string(header
.header_buffer
).nl();
2535 write_html_anchor(a
);
2536 html
.put_string("</b>").nl();
2540 // and now we issue the real header
2541 html
.put_string("<h");
2542 html
.put_number(header
.header_level
);
2543 write_xhtml_anchor(a
);
2544 html
.put_string(">");
2545 html
.put_string(header
.header_buffer
).nl();
2546 write_html_anchor(a
);
2547 html
.put_string("</h");
2548 html
.put_number(header
.header_level
);
2549 html
.put_string(">").nl();
2552 /* and now we save the file name in which this header will occur */
2554 style st
; // fake style to enable us to use the list data structure
2556 text_glob
*h
=new text_glob();
2557 h
->text_glob_html(&st
,
2558 header
.headings
.add_string(file_list
.file_name()),
2559 file_list
.file_name().length(),
2560 header
.no_of_headings
, header
.header_level
,
2561 header
.no_of_headings
, header
.header_level
);
2563 header
.header_filename
.add(h
,
2564 header
.no_of_headings
,
2565 header
.no_of_headings
, header
.no_of_headings
,
2566 header
.no_of_headings
, header
.no_of_headings
);
2568 current_paragraph
->do_para(&html
, "", get_troff_indent(), pageoffset
, linelength
, space
);
2572 void html_printer::determine_header_level (int level
)
2577 for (i
=0; ((i
<header
.header_buffer
.length())
2578 && ((header
.header_buffer
[i
] == '.')
2579 || is_digit(header
.header_buffer
[i
]))) ; i
++) {
2580 if (header
.header_buffer
[i
] == '.') {
2585 header
.header_level
= level
+1;
2586 if (header
.header_level
>= 2 && header
.header_level
<= split_level
) {
2587 header
.no_of_level_one_headings
++;
2588 insert_split_file();
2593 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2596 void html_printer::do_heading (char *arg
)
2599 int level
=atoi(arg
);
2602 header
.header_buffer
.clear();
2603 page_contents
->glyphs
.move_right();
2604 if (! page_contents
->glyphs
.is_equal_to_head()) {
2605 g
= page_contents
->glyphs
.get_data();
2608 if (g
->is_auto_img()) {
2609 string img
=generate_img_src((char *)(g
->text_string
+ 20));
2611 if (! img
.empty()) {
2612 simple_anchors
= true; // we cannot use full heading anchors with images
2613 if (horiz
< g
->minh
)
2614 header
.header_buffer
+= " ";
2616 header
.header_buffer
+= img
;
2619 else if (g
->is_in() || g
->is_ti() || g
->is_po() || g
->is_ce() || g
->is_ll())
2621 else if (g
->is_fi())
2623 else if (g
->is_nf())
2625 else if (! (g
->is_a_line() || g
->is_a_tag())) {
2627 * we ignore the other tag commands when constructing a heading
2629 if (horiz
< g
->minh
)
2630 header
.header_buffer
+= " ";
2633 header
.header_buffer
+= string(g
->text_string
, g
->text_length
);
2635 page_contents
->glyphs
.move_right();
2636 g
= page_contents
->glyphs
.get_data();
2637 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
2641 determine_header_level(level
);
2645 * finally set the output font to uninitialized, thus forcing
2646 * the new paragraph to start a new font block.
2649 output_style
.f
= NULL
;
2650 g
= page_contents
->glyphs
.get_data();
2651 page_contents
->glyphs
.move_left(); // so that next time we use old g
2655 * is_courier_until_eol - returns true if we can see a whole line which is courier
2658 int html_printer::is_courier_until_eol (void)
2660 text_glob
*orig
= page_contents
->glyphs
.get_data();
2664 if (! page_contents
->glyphs
.is_equal_to_tail()) {
2665 page_contents
->glyphs
.move_right();
2667 g
= page_contents
->glyphs
.get_data();
2668 if (! g
->is_a_tag() && (! is_font_courier(g
->text_style
.f
)))
2670 page_contents
->glyphs
.move_right();
2672 (! page_contents
->glyphs
.is_equal_to_head()) &&
2673 (! g
->is_fi()) && (! g
->is_eol()));
2676 * now restore our previous position.
2678 while (page_contents
->glyphs
.get_data() != orig
)
2679 page_contents
->glyphs
.move_left();
2685 * do_linelength - handle the .ll command from troff.
2688 void html_printer::do_linelength (char *arg
)
2690 if (max_linelength
== -1)
2691 max_linelength
= atoi(arg
);
2693 next_linelength
= atoi(arg
);
2694 seen_linelength
= true;
2698 * do_pageoffset - handle the .po command from troff.
2701 void html_printer::do_pageoffset (char *arg
)
2703 next_pageoffset
= atoi(arg
);
2704 seen_pageoffset
= true;
2708 * get_troff_indent - returns the indent value.
2711 int html_printer::get_troff_indent (void)
2713 if (end_tempindent
> 0)
2716 return troff_indent
;
2720 * do_indentation - handle the .in command from troff.
2723 void html_printer::do_indentation (char *arg
)
2725 next_indent
= atoi(arg
);
2730 * do_tempindent - handle the .ti command from troff.
2733 void html_printer::do_tempindent (char *arg
)
2737 * we set the end_tempindent to 2 as the first .br
2738 * activates the .ti and the second terminates it.
2741 temp_indent
= atoi(arg
);
2746 * shutdown_table - shuts down the current table.
2749 void html_printer::shutdown_table (void)
2751 if (table
!= NULL
) {
2752 current_paragraph
->done_para();
2753 table
->emit_finish_table();
2754 // dont delete this table as it will be deleted when we destroy the text_glob
2760 * do_indent - remember the indent parameters and if
2761 * indent is > pageoff and indent has changed
2762 * then we start a html table to implement the indentation.
2765 void html_printer::do_indent (int in
, int pageoff
, int linelen
)
2767 if ((device_indent
!= -1) &&
2768 (pageoffset
+device_indent
!= in
+pageoff
)) {
2770 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2771 current_paragraph
->done_para();
2774 pageoffset
= pageoff
;
2775 if (linelen
<= max_linelength
)
2776 linelength
= linelen
;
2778 current_paragraph
->do_para(&html
, "", device_indent
,
2779 pageoffset
, max_linelength
, space
);
2784 * do_verticalspacing - handle the .vs command from troff.
2787 void html_printer::do_verticalspacing (char *arg
)
2789 vertical_spacing
= atoi(arg
);
2793 * do_pointsize - handle the .ps command from troff.
2796 void html_printer::do_pointsize (char *arg
)
2799 * firstly check to see whether this point size is really associated with a .tl tag
2802 if (! page_contents
->glyphs
.is_empty()) {
2803 text_glob
*g
= page_contents
->glyphs
.get_data();
2804 text_glob
*t
= page_contents
->glyphs
.get_data();
2806 while (t
->is_a_tag() && (! page_contents
->glyphs
.is_equal_to_head())) {
2809 * found title therefore ignore this .ps tag
2812 page_contents
->glyphs
.move_left();
2813 t
= page_contents
->glyphs
.get_data();
2817 page_contents
->glyphs
.move_right();
2818 t
= page_contents
->glyphs
.get_data();
2821 * move back to original position
2824 page_contents
->glyphs
.move_left();
2825 t
= page_contents
->glyphs
.get_data();
2828 * collect valid pointsize
2830 pointsize
= atoi(arg
);
2835 * do_fill - records whether troff has requested that text be filled.
2838 void html_printer::do_fill (char *arg
)
2842 output_hpos
= get_troff_indent()+pageoffset
;
2843 supress_sub_sup
= true;
2845 if (fill_on
!= on
) {
2847 current_paragraph
->do_para("", seen_space
);
2853 * do_eol - handle the end of line
2856 void html_printer::do_eol (void)
2859 if (current_paragraph
->ever_emitted_text()) {
2860 current_paragraph
->do_newline();
2861 current_paragraph
->do_break();
2864 output_hpos
= get_troff_indent()+pageoffset
;
2868 * do_check_center - checks to see whether we have seen a `.ce' tag
2869 * during the previous line.
2872 void html_printer::do_check_center(void)
2875 seen_center
= false;
2876 if (next_center
> 0) {
2877 if (end_center
== 0) {
2878 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2879 current_paragraph
->done_para();
2880 supress_sub_sup
= true;
2881 if (dialect
== html4
)
2882 current_paragraph
->do_para("align=\"center\"", space
);
2884 current_paragraph
->do_para("class=\"center\"", space
);
2886 if ((strcmp("align=\"center\"",
2887 current_paragraph
->get_alignment()) != 0) &&
2888 (strcmp("class=\"center\"",
2889 current_paragraph
->get_alignment()) != 0)) {
2891 * different alignment, so shutdown paragraph and open
2894 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2895 current_paragraph
->done_para();
2896 supress_sub_sup
= true;
2897 if (dialect
== html4
)
2898 current_paragraph
->do_para("align=\"center\"", space
);
2900 current_paragraph
->do_para("class=\"center\"", space
);
2903 * same alignment, if we have emitted text then issue a break.
2905 if (current_paragraph
->emitted_text())
2906 current_paragraph
->do_break();
2911 if (end_center
> 0) {
2912 seen_space
= seen_space
|| current_paragraph
->retrieve_para_space();
2913 current_paragraph
->done_para();
2914 supress_sub_sup
= true;
2915 current_paragraph
->do_para("", seen_space
);
2917 end_center
= next_center
;
2922 * do_eol_ce - handle end of line specifically for a .ce
2925 void html_printer::do_eol_ce (void)
2927 if (end_center
> 0) {
2929 if (current_paragraph
->emitted_text())
2930 current_paragraph
->do_break();
2933 if (end_center
== 0) {
2934 current_paragraph
->done_para();
2935 supress_sub_sup
= true;
2941 * do_flush - flushes all output and tags.
2944 void html_printer::do_flush (void)
2946 current_paragraph
->done_para();
2950 * do_links - moves onto a new temporary file and sets auto_links to false.
2953 void html_printer::do_links (void)
2955 html
.end_line(); // flush line
2956 auto_links
= false; /* from now on only emit under user request */
2957 file_list
.add_new_file(xtmpfile());
2958 file_list
.set_links_required();
2959 html
.set_file(file_list
.get_file());
2963 * insert_split_file -
2966 void html_printer::insert_split_file (void)
2968 if (multiple_files
) {
2969 current_paragraph
->done_para(); // flush paragraph
2970 html
.end_line(); // flush line
2971 html
.set_file(file_list
.get_file()); // flush current file
2972 file_list
.add_new_file(xtmpfile());
2973 string split_file
= job_name
;
2975 split_file
+= string("-");
2976 split_file
+= as_string(header
.no_of_level_one_headings
);
2977 if (dialect
== xhtml
)
2978 split_file
+= string(".xhtml");
2980 split_file
+= string(".html");
2983 file_list
.set_file_name(split_file
);
2984 html
.set_file(file_list
.get_file());
2989 * do_job_name - assigns the job_name to name.
2992 void html_printer::do_job_name (char *name
)
2994 if (! multiple_files
) {
2995 multiple_files
= true;
2996 while (name
!= NULL
&& (*name
!= (char)0) && (*name
== ' '))
3003 * do_head - adds a string to head_info which is to be included into
3004 * the <head> </head> section of the html document.
3007 void html_printer::do_head (char *name
)
3009 head_info
+= string(name
);
3014 * do_break - handles the ".br" request and also
3015 * undoes an outstanding ".ti" command
3016 * and calls indent if the indentation
3017 * related registers have changed.
3020 void html_printer::do_break (void)
3022 int seen_temp_indent
= false;
3024 current_paragraph
->do_break();
3025 if (end_tempindent
> 0) {
3027 if (end_tempindent
> 0)
3028 seen_temp_indent
= true;
3030 if (seen_indent
|| seen_pageoffset
|| seen_linelength
|| seen_temp_indent
) {
3031 if (seen_indent
&& (! seen_temp_indent
))
3032 troff_indent
= next_indent
;
3033 if (! seen_pageoffset
)
3034 next_pageoffset
= pageoffset
;
3035 if (! seen_linelength
)
3036 next_linelength
= linelength
;
3037 do_indent(get_troff_indent(), next_pageoffset
, next_linelength
);
3039 seen_indent
= seen_temp_indent
;
3040 seen_linelength
= false;
3041 seen_pageoffset
= false;
3043 output_hpos
= get_troff_indent()+pageoffset
;
3044 supress_sub_sup
= true;
3047 void html_printer::do_space (char *arg
)
3051 seen_space
= atoi(arg
);
3052 as
.check_sp(seen_space
);
3055 table
->set_space(true);
3059 current_paragraph
->do_space();
3062 supress_sub_sup
= true;
3066 * do_tab_ts - start a table, which will have already been defined.
3069 void html_printer::do_tab_ts (text_glob
*g
)
3071 html_table
*t
= g
->get_table();
3075 current_paragraph
->done_pre();
3076 current_paragraph
->done_para();
3077 current_paragraph
->remove_para_space();
3079 #if defined(DEBUG_TABLES)
3080 html
.simple_comment("TABS");
3083 t
->set_linelength(max_linelength
);
3084 t
->add_indent(pageoffset
);
3086 t
->emit_table_header(seen_space
);
3088 t
->emit_table_header(false);
3089 row_space
= current_paragraph
->retrieve_para_space() || seen_space
;
3098 * do_tab_te - finish a table.
3101 void html_printer::do_tab_te (void)
3104 current_paragraph
->done_para();
3105 current_paragraph
->remove_para_space();
3106 table
->emit_finish_table();
3110 restore_troff_indent();
3114 * do_tab - handle the "devtag:tab" tag
3117 void html_printer::do_tab (char *s
)
3123 int col
= table
->find_column(atoi(s
) + pageoffset
+ get_troff_indent());
3125 current_paragraph
->done_para();
3126 table
->emit_col(col
);
3132 * do_tab0 - handle the "devtag:tab0" tag
3135 void html_printer::do_tab0 (void)
3138 int col
= table
->find_column(pageoffset
+get_troff_indent());
3140 current_paragraph
->done_para();
3141 table
->emit_col(col
);
3147 * do_col - start column, s.
3150 void html_printer::do_col (char *s
)
3153 if (atoi(s
) < current_column
)
3154 row_space
= seen_space
;
3156 current_column
= atoi(s
);
3157 current_paragraph
->done_para();
3158 table
->emit_col(current_column
);
3159 current_paragraph
->do_para("", row_space
);
3164 * troff_tag - processes the troff tag and manipulates the troff
3168 void html_printer::troff_tag (text_glob
*g
)
3171 * firstly skip over devtag:
3173 char *t
=(char *)g
->text_string
+strlen("devtag:");
3174 if (strncmp(g
->text_string
, "html</p>:", strlen("html</p>:")) == 0) {
3176 } else if (strncmp(g
->text_string
, "html<?p>:", strlen("html<?p>:")) == 0) {
3177 if (current_paragraph
->emitted_text())
3178 html
.put_string(g
->text_string
+9);
3181 } else if (strncmp(g
->text_string
, "math<?p>:", strlen("math<?p>:")) == 0) {
3183 } else if (g
->is_eol()) {
3185 } else if (g
->is_eol_ce()) {
3187 } else if (strncmp(t
, ".sp", 3) == 0) {
3188 char *a
= (char *)t
+3;
3190 } else if (strncmp(t
, ".br", 3) == 0) {
3194 } else if (strcmp(t
, ".centered-image") == 0) {
3195 do_centered_image();
3196 } else if (strcmp(t
, ".right-image") == 0) {
3198 } else if (strcmp(t
, ".left-image") == 0) {
3200 } else if (strncmp(t
, ".auto-image", 11) == 0) {
3201 char *a
= (char *)t
+11;
3202 do_auto_image(g
, a
);
3203 } else if (strncmp(t
, ".ce", 3) == 0) {
3204 char *a
= (char *)t
+3;
3205 supress_sub_sup
= true;
3207 } else if (g
->is_tl()) {
3208 supress_sub_sup
= true;
3209 title
.with_h1
= true;
3211 } else if (strncmp(t
, ".html-tl", 8) == 0) {
3212 supress_sub_sup
= true;
3213 title
.with_h1
= false;
3215 } else if (strncmp(t
, ".fi", 3) == 0) {
3216 char *a
= (char *)t
+3;
3218 } else if ((strncmp(t
, ".SH", 3) == 0) || (strncmp(t
, ".NH", 3) == 0)) {
3219 char *a
= (char *)t
+3;
3221 } else if (strncmp(t
, ".ll", 3) == 0) {
3222 char *a
= (char *)t
+3;
3224 } else if (strncmp(t
, ".po", 3) == 0) {
3225 char *a
= (char *)t
+3;
3227 } else if (strncmp(t
, ".in", 3) == 0) {
3228 char *a
= (char *)t
+3;
3230 } else if (strncmp(t
, ".ti", 3) == 0) {
3231 char *a
= (char *)t
+3;
3233 } else if (strncmp(t
, ".vs", 3) == 0) {
3234 char *a
= (char *)t
+3;
3235 do_verticalspacing(a
);
3236 } else if (strncmp(t
, ".ps", 3) == 0) {
3237 char *a
= (char *)t
+3;
3239 } else if (strcmp(t
, ".links") == 0) {
3241 } else if (strncmp(t
, ".job-name", 9) == 0) {
3242 char *a
= (char *)t
+9;
3244 } else if (strncmp(t
, ".head", 5) == 0) {
3245 char *a
= (char *)t
+5;
3247 } else if (strcmp(t
, ".no-auto-rule") == 0) {
3249 } else if (strcmp(t
, ".tab-ts") == 0) {
3251 } else if (strcmp(t
, ".tab-te") == 0) {
3253 } else if (strncmp(t
, ".col ", 5) == 0) {
3254 char *a
= (char *)t
+4;
3256 } else if (strncmp(t
, "tab ", 4) == 0) {
3257 char *a
= (char *)t
+3;
3259 } else if (strncmp(t
, "tab0", 4) == 0) {
3265 * do_math - prints out the equation
3268 void html_printer::do_math (text_glob
*g
)
3271 if (current_paragraph
->emitted_text())
3272 html
.put_string(g
->text_string
+9);
3278 * is_in_middle - returns true if the positions left..right are in the center of the page.
3281 int html_printer::is_in_middle (int left
, int right
)
3283 return( abs(abs(left
-pageoffset
) - abs(pageoffset
+linelength
-right
))
3284 <= CENTER_TOLERANCE
);
3288 * flush_globs - runs through the text glob list and emits html.
3291 void html_printer::flush_globs (void)
3295 if (! page_contents
->glyphs
.is_empty()) {
3296 page_contents
->glyphs
.start_from_head();
3298 g
= page_contents
->glyphs
.get_data();
3300 fprintf(stderr
, "[%s:%d:%d:%d:%d]",
3301 g
->text_string
, g
->minv
, g
->minh
, g
->maxv
, g
->maxh
) ;
3305 handle_state_assertion(g
);
3307 if (strcmp(g
->text_string
, "XXXXXXX") == 0)
3312 else if (g
->is_a_line())
3315 as
.check_sp(seen_space
);
3316 as
.check_br(seen_break
);
3322 as
.check_fi(fill_on
);
3323 as
.check_ce(end_center
);
3325 * after processing the title (and removing it) the glyph list might be empty
3327 if (! page_contents
->glyphs
.is_empty()) {
3328 page_contents
->glyphs
.move_right();
3330 } while (! page_contents
->glyphs
.is_equal_to_head());
3335 * calc_nf - calculates the _no_ format flag, given the
3339 int html_printer::calc_nf (text_glob
*g
, int nf
)
3356 * calc_po_in - calculates the, in, po, registers
3359 void html_printer::calc_po_in (text_glob
*g
, int nf
)
3362 troff_indent
= g
->get_arg();
3363 else if (g
->is_po())
3364 pageoffset
= g
->get_arg();
3365 else if (g
->is_ti()) {
3366 temp_indent
= g
->get_arg();
3368 } else if (g
->is_br() || (nf
&& g
->is_eol())) {
3369 if (end_tempindent
> 0)
3375 * next_horiz_pos - returns the next horiz position.
3376 * -1 is returned if it doesn't exist.
3379 int html_printer::next_horiz_pos (text_glob
*g
, int nf
)
3383 if ((g
!= NULL
) && (g
->is_br() || (nf
&& g
->is_eol())))
3384 if (! page_contents
->glyphs
.is_empty()) {
3385 page_contents
->glyphs
.move_right_get_data();
3387 page_contents
->glyphs
.start_from_head();
3392 page_contents
->glyphs
.move_left();
3399 * insert_tab_ts - inserts a tab-ts before, where.
3402 text_glob
*html_printer::insert_tab_ts (text_glob
*where
)
3404 text_glob
*start_of_table
;
3405 text_glob
*old_pos
= page_contents
->glyphs
.get_data();
3407 page_contents
->glyphs
.move_to(where
);
3408 page_contents
->glyphs
.move_left();
3409 page_contents
->insert_tag(string("devtag:.tab-ts")); // tab table start
3410 page_contents
->glyphs
.move_right();
3411 start_of_table
= page_contents
->glyphs
.get_data();
3412 page_contents
->glyphs
.move_to(old_pos
);
3413 return start_of_table
;
3417 * insert_tab_te - inserts a tab-te before the current position
3418 * (it skips backwards over .sp/.br)
3421 void html_printer::insert_tab_te (void)
3423 text_glob
*g
= page_contents
->glyphs
.get_data();
3424 page_contents
->dump_page();
3426 while (page_contents
->glyphs
.get_data()->is_a_tag())
3427 page_contents
->glyphs
.move_left();
3429 page_contents
->insert_tag(string("devtag:.tab-te")); // tab table end
3430 while (g
!= page_contents
->glyphs
.get_data())
3431 page_contents
->glyphs
.move_right();
3432 page_contents
->dump_page();
3436 * insert_tab_0 - inserts a tab0 before, where.
3439 void html_printer::insert_tab_0 (text_glob
*where
)
3441 text_glob
*old_pos
= page_contents
->glyphs
.get_data();
3443 page_contents
->glyphs
.move_to(where
);
3444 page_contents
->glyphs
.move_left();
3445 page_contents
->insert_tag(string("devtag:tab0")); // tab0 start of line
3446 page_contents
->glyphs
.move_right();
3447 page_contents
->glyphs
.move_to(old_pos
);
3451 * remove_tabs - removes the tabs tags on this line.
3454 void html_printer::remove_tabs (void)
3456 text_glob
*orig
= page_contents
->glyphs
.get_data();
3459 if (! page_contents
->glyphs
.is_equal_to_tail()) {
3461 g
= page_contents
->glyphs
.get_data();
3463 page_contents
->glyphs
.sub_move_right();
3465 orig
= page_contents
->glyphs
.get_data();
3467 page_contents
->glyphs
.move_right();
3468 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
3472 * now restore our previous position.
3474 while (page_contents
->glyphs
.get_data() != orig
)
3475 page_contents
->glyphs
.move_left();
3479 void html_printer::remove_courier_tabs (void)
3482 int line_start
= true;
3485 if (! page_contents
->glyphs
.is_empty()) {
3486 page_contents
->glyphs
.start_from_head();
3490 g
= page_contents
->glyphs
.get_data();
3491 handle_state_assertion(g
);
3492 nf
= calc_nf(g
, nf
);
3495 if (line_start
&& nf
&& is_courier_until_eol()) {
3497 g
= page_contents
->glyphs
.get_data();
3501 // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
3502 line_start
= g
->is_br() || (nf
&& g
->is_eol());
3503 page_contents
->glyphs
.move_right();
3504 } while (! page_contents
->glyphs
.is_equal_to_head());
3508 void html_printer::insert_tab0_foreach_tab (void)
3510 text_glob
*start_of_line
= NULL
;
3511 text_glob
*g
= NULL
;
3512 int seen_tab
= false;
3513 int seen_col
= false;
3516 if (! page_contents
->glyphs
.is_empty()) {
3517 page_contents
->glyphs
.start_from_head();
3519 start_of_line
= page_contents
->glyphs
.get_data();
3521 g
= page_contents
->glyphs
.get_data();
3522 handle_state_assertion(g
);
3523 nf
= calc_nf(g
, nf
);
3531 if (g
->is_br() || (nf
&& g
->is_eol())) {
3533 page_contents
->glyphs
.move_right();
3534 g
= page_contents
->glyphs
.get_data();
3535 handle_state_assertion(g
);
3536 nf
= calc_nf(g
, nf
);
3537 if (page_contents
->glyphs
.is_equal_to_head()) {
3538 if (seen_tab
&& !seen_col
)
3539 insert_tab_0(start_of_line
);
3542 } while (g
->is_br() || (nf
&& g
->is_eol()) || g
->is_ta());
3543 // printf("\nstart_of_line is: %s\n", g->text_string);
3544 if (seen_tab
&& !seen_col
) {
3545 insert_tab_0(start_of_line
);
3546 page_contents
->glyphs
.move_to(g
);
3553 page_contents
->glyphs
.move_right();
3554 } while (! page_contents
->glyphs
.is_equal_to_head());
3555 if (seen_tab
&& !seen_col
)
3556 insert_tab_0(start_of_line
);
3562 * update_min_max - updates the extent of a column, given the left and right
3563 * extents of a glyph, g.
3566 void html_printer::update_min_max (colType type_of_col
, int *minimum
, int *maximum
, text_glob
*g
)
3568 switch (type_of_col
) {
3585 * add_table_end - moves left one glyph, adds a table end tag and adds a
3589 void html_printer::add_table_end (const char *
3590 #if defined(DEBUG_TABLES)
3595 page_contents
->glyphs
.move_left();
3597 #if defined(DEBUG_TABLES)
3598 page_contents
->insert_tag(string(debug_string
));
3603 * lookahead_for_tables - checks for .col tags and inserts table
3607 void html_printer::lookahead_for_tables (void)
3610 text_glob
*start_of_line
= NULL
;
3611 text_glob
*start_of_table
= NULL
;
3612 text_glob
*last
= NULL
;
3613 colType type_of_col
= none
;
3615 int found_col
= false;
3617 int colmin
= 0; // pacify compiler
3618 int colmax
= 0; // pacify compiler
3619 html_table
*tbl
= new html_table(&html
, -1);
3620 const char *tab_defs
= NULL
;
3623 int old_pageoffset
= pageoffset
;
3625 remove_courier_tabs();
3626 page_contents
->dump_page();
3627 insert_tab0_foreach_tab();
3628 page_contents
->dump_page();
3629 if (! page_contents
->glyphs
.is_empty()) {
3630 page_contents
->glyphs
.start_from_head();
3632 g
= page_contents
->glyphs
.get_data();
3634 g
= page_contents
->glyphs
.move_right_get_data();
3635 handle_state_assertion(g
);
3636 if (page_contents
->glyphs
.is_equal_to_head()) {
3646 left
= next_horiz_pos(g
, nf
);
3653 #if defined(DEBUG_TABLES)
3654 fprintf(stderr
, " [") ;
3655 fprintf(stderr
, g
->text_string
) ;
3656 fprintf(stderr
, "] ") ;
3658 if (strcmp(g
->text_string
, "XXXXXXX") == 0)
3662 nf
= calc_nf(g
, nf
);
3665 if (type_of_col
== tab_tag
&& start_of_table
!= NULL
) {
3666 page_contents
->glyphs
.move_left();
3668 start_of_table
->remember_table(tbl
);
3669 tbl
= new html_table(&html
, -1);
3670 page_contents
->insert_tag(string("*** TAB -> COL ***"));
3671 if (tab_defs
!= NULL
)
3672 tbl
->tab_stops
->init(tab_defs
);
3673 start_of_table
= NULL
;
3676 type_of_col
= col_tag
;
3678 ncol
= g
->get_arg();
3682 } else if (g
->is_tab()) {
3683 type_of_col
= tab_tag
;
3684 colmin
= g
->get_tab_args(&align
);
3685 align
= 'L'; // for now as 'C' and 'R' are broken
3686 ncol
= tbl
->find_tab_column(colmin
);
3687 colmin
+= pageoffset
+ get_troff_indent();
3688 colmax
= tbl
->get_tab_pos(ncol
+1);
3690 colmax
+= pageoffset
+ get_troff_indent();
3691 } else if (g
->is_tab0()) {
3692 if (type_of_col
== col_tag
&& start_of_table
!= NULL
) {
3693 page_contents
->glyphs
.move_left();
3695 start_of_table
->remember_table(tbl
);
3696 tbl
= new html_table(&html
, -1);
3697 page_contents
->insert_tag(string("*** COL -> TAB ***"));
3698 start_of_table
= NULL
;
3701 if (tab_defs
!= NULL
)
3702 tbl
->tab_stops
->init(tab_defs
);
3704 type_of_col
= tab0_tag
;
3707 colmax
= tbl
->get_tab_pos(2) + pageoffset
+ get_troff_indent();
3708 } else if (! g
->is_a_tag())
3709 update_min_max(type_of_col
, &colmin
, &colmax
, g
);
3711 if ((g
->is_col() || g
->is_tab() || g
->is_tab0())
3712 && (start_of_line
!= NULL
) && (start_of_table
== NULL
)) {
3713 start_of_table
= insert_tab_ts(start_of_line
);
3714 start_of_line
= NULL
;
3715 } else if (g
->is_ce() && (start_of_table
!= NULL
)) {
3716 add_table_end("*** CE ***");
3717 start_of_table
->remember_table(tbl
);
3718 tbl
= new html_table(&html
, -1);
3719 start_of_table
= NULL
;
3721 } else if (g
->is_ta()) {
3722 tab_defs
= g
->text_string
;
3724 if (type_of_col
== col_tag
)
3725 tbl
->tab_stops
->check_init(tab_defs
);
3727 if (!tbl
->tab_stops
->compatible(tab_defs
)) {
3728 if (start_of_table
!= NULL
) {
3729 add_table_end("*** TABS ***");
3730 start_of_table
->remember_table(tbl
);
3731 tbl
= new html_table(&html
, -1);
3732 start_of_table
= NULL
;
3736 tbl
->tab_stops
->init(tab_defs
);
3740 if (((! g
->is_a_tag()) || g
->is_tab()) && (start_of_table
!= NULL
)) {
3741 // we are in a table and have a glyph
3742 if ((ncol
== 0) || (! tbl
->add_column(ncol
, colmin
, colmax
, align
))) {
3744 add_table_end("*** NCOL == 0 ***");
3746 add_table_end("*** CROSSED COLS ***");
3748 start_of_table
->remember_table(tbl
);
3749 tbl
= new html_table(&html
, -1);
3750 start_of_table
= NULL
;
3757 * move onto next glob, check whether we are starting a new line
3759 g
= page_contents
->glyphs
.move_right_get_data();
3760 handle_state_assertion(g
);
3764 page_contents
->glyphs
.start_from_head();
3769 } else if (g
->is_br() || (nf
&& g
->is_eol())) {
3771 g
= page_contents
->glyphs
.move_right_get_data();
3772 handle_state_assertion(g
);
3773 nf
= calc_nf(g
, nf
);
3774 } while ((g
!= NULL
) && (g
->is_br() || (nf
&& g
->is_eol())));
3777 left
= next_horiz_pos(g
, nf
);
3782 } while ((g
!= NULL
) && (! page_contents
->glyphs
.is_equal_to_head()));
3784 #if defined(DEBUG_TABLES)
3785 fprintf(stderr
, "finished scanning for tables\n");
3788 page_contents
->glyphs
.start_from_head();
3789 if (start_of_table
!= NULL
) {
3791 while (last
!= page_contents
->glyphs
.get_data())
3792 page_contents
->glyphs
.move_left();
3795 start_of_table
->remember_table(tbl
);
3797 page_contents
->insert_tag(string("*** LAST ***"));
3805 // and reset the registers
3806 pageoffset
= old_pageoffset
;
3812 void html_printer::flush_page (void)
3814 supress_sub_sup
= true;
3816 page_contents
->dump_page();
3817 lookahead_for_tables();
3818 page_contents
->dump_page();
3821 current_paragraph
->done_para();
3822 current_paragraph
->flush_text();
3824 // move onto a new page
3825 delete page_contents
;
3826 #if defined(DEBUG_TABLES)
3827 fprintf(stderr
, "\n\n*** flushed page ***\n\n");
3829 html
.simple_comment("new page called");
3831 page_contents
= new page
;
3835 * determine_space - works out whether we need to write a space.
3836 * If last glyph is ajoining then no space emitted.
3839 void html_printer::determine_space (text_glob
*g
)
3841 if (current_paragraph
->is_in_pre()) {
3843 * .nf has been specified
3845 while (output_hpos
< g
->minh
) {
3846 output_hpos
+= space_width
;
3847 current_paragraph
->emit_space();
3850 if ((output_vpos
!= g
->minv
) || (output_hpos
< g
->minh
)) {
3851 current_paragraph
->emit_space();
3857 * is_line_start - returns true if we are at the start of a line.
3860 int html_printer::is_line_start (int nf
)
3862 int line_start
= false;
3864 text_glob
*orig
= page_contents
->glyphs
.get_data();
3867 if (! page_contents
->glyphs
.is_equal_to_head()) {
3869 page_contents
->glyphs
.move_left();
3870 g
= page_contents
->glyphs
.get_data();
3871 result
= g
->is_a_tag();
3874 else if (g
->is_nf())
3876 line_start
= g
->is_col() || g
->is_br() || (nf
&& g
->is_eol());
3877 } while ((!line_start
) && (result
));
3879 * now restore our previous position.
3881 while (page_contents
->glyphs
.get_data() != orig
)
3882 page_contents
->glyphs
.move_right();
3888 * is_font_courier - returns true if the font, f, is courier.
3891 int html_printer::is_font_courier (font
*f
)
3894 const char *fontname
= f
->get_name();
3896 return( (fontname
!= 0) && (fontname
[0] == 'C') );
3902 * end_font - shuts down the font corresponding to fontname.
3905 void html_printer::end_font (const char *fontname
)
3907 if (strcmp(fontname
, "B") == 0) {
3908 current_paragraph
->done_bold();
3909 } else if (strcmp(fontname
, "I") == 0) {
3910 current_paragraph
->done_italic();
3911 } else if (strcmp(fontname
, "BI") == 0) {
3912 current_paragraph
->done_bold();
3913 current_paragraph
->done_italic();
3914 } else if (strcmp(fontname
, "CR") == 0) {
3915 current_paragraph
->done_tt();
3916 } else if (strcmp(fontname
, "CI") == 0) {
3917 current_paragraph
->done_italic();
3918 current_paragraph
->done_tt();
3919 } else if (strcmp(fontname
, "CB") == 0) {
3920 current_paragraph
->done_bold();
3921 current_paragraph
->done_tt();
3922 } else if (strcmp(fontname
, "CBI") == 0) {
3923 current_paragraph
->done_bold();
3924 current_paragraph
->done_italic();
3925 current_paragraph
->done_tt();
3930 * start_font - starts the font corresponding to name.
3933 void html_printer::start_font (const char *fontname
)
3935 if (strcmp(fontname
, "R") == 0) {
3936 current_paragraph
->done_bold();
3937 current_paragraph
->done_italic();
3938 current_paragraph
->done_tt();
3939 } else if (strcmp(fontname
, "B") == 0) {
3940 current_paragraph
->do_bold();
3941 } else if (strcmp(fontname
, "I") == 0) {
3942 current_paragraph
->do_italic();
3943 } else if (strcmp(fontname
, "BI") == 0) {
3944 current_paragraph
->do_bold();
3945 current_paragraph
->do_italic();
3946 } else if (strcmp(fontname
, "CR") == 0) {
3947 if ((! fill_on
) && (is_courier_until_eol()) &&
3948 is_line_start(! fill_on
)) {
3949 current_paragraph
->do_pre();
3951 current_paragraph
->do_tt();
3952 } else if (strcmp(fontname
, "CI") == 0) {
3953 if ((! fill_on
) && (is_courier_until_eol()) &&
3954 is_line_start(! fill_on
)) {
3955 current_paragraph
->do_pre();
3957 current_paragraph
->do_tt();
3958 current_paragraph
->do_italic();
3959 } else if (strcmp(fontname
, "CB") == 0) {
3960 if ((! fill_on
) && (is_courier_until_eol()) &&
3961 is_line_start(! fill_on
)) {
3962 current_paragraph
->do_pre();
3964 current_paragraph
->do_tt();
3965 current_paragraph
->do_bold();
3966 } else if (strcmp(fontname
, "CBI") == 0) {
3967 if ((! fill_on
) && (is_courier_until_eol()) &&
3968 is_line_start(! fill_on
)) {
3969 current_paragraph
->do_pre();
3971 current_paragraph
->do_tt();
3972 current_paragraph
->do_italic();
3973 current_paragraph
->do_bold();
3978 * start_size - from is old font size, to is the new font size.
3979 * The html increase <big> and <small> decrease alters the
3980 * font size by 20%. We try and map these onto glyph sizes.
3983 void html_printer::start_size (int from
, int to
)
3987 current_paragraph
->do_big();
3988 from
+= SIZE_INCREMENT
;
3990 } else if (from
> to
) {
3992 current_paragraph
->do_small();
3993 from
-= SIZE_INCREMENT
;
3999 * do_font - checks to see whether we need to alter the html font.
4002 void html_printer::do_font (text_glob
*g
)
4005 * check if the output_style.point_size has not been set yet
4006 * this allow users to place .ps at the top of their troff files
4007 * and grohtml can then treat the .ps value as the base font size (3)
4009 if (output_style
.point_size
== -1) {
4010 output_style
.point_size
= pointsize
;
4013 if (g
->text_style
.f
!= output_style
.f
) {
4014 if (output_style
.f
!= 0) {
4015 end_font(output_style
.f
->get_name());
4017 output_style
.f
= g
->text_style
.f
;
4018 if (output_style
.f
!= 0) {
4019 start_font(output_style
.f
->get_name());
4022 if (output_style
.point_size
!= g
->text_style
.point_size
) {
4024 if ((output_style
.point_size
> 0) &&
4025 (g
->text_style
.point_size
> 0)) {
4026 start_size(output_style
.point_size
, g
->text_style
.point_size
);
4028 if (g
->text_style
.point_size
> 0) {
4029 output_style
.point_size
= g
->text_style
.point_size
;
4032 if (output_style
.col
!= g
->text_style
.col
) {
4033 current_paragraph
->done_color();
4034 output_style
.col
= g
->text_style
.col
;
4035 current_paragraph
->do_color(&output_style
.col
);
4040 * start_subscript - returns true if, g, looks like a subscript start.
4043 int html_printer::start_subscript (text_glob
*g
)
4046 int height
= output_style
.point_size
*r
/72;
4048 return( (output_style
.point_size
!= 0) &&
4049 (output_vpos
< g
->minv
) &&
4050 (output_vpos
-height
> g
->maxv
) &&
4051 (output_style
.point_size
> g
->text_style
.point_size
) );
4055 * start_superscript - returns true if, g, looks like a superscript start.
4058 int html_printer::start_superscript (text_glob
*g
)
4061 int height
= output_style
.point_size
*r
/72;
4063 return( (output_style
.point_size
!= 0) &&
4064 (output_vpos
> g
->minv
) &&
4065 (output_vpos
-height
< g
->maxv
) &&
4066 (output_style
.point_size
> g
->text_style
.point_size
) );
4070 * end_subscript - returns true if, g, looks like the end of a subscript.
4073 int html_printer::end_subscript (text_glob
*g
)
4076 int height
= output_style
.point_size
*r
/72;
4078 return( (output_style
.point_size
!= 0) &&
4079 (g
->minv
< output_vpos
) &&
4080 (output_vpos
-height
> g
->maxv
) &&
4081 (output_style
.point_size
< g
->text_style
.point_size
) );
4085 * end_superscript - returns true if, g, looks like the end of a superscript.
4088 int html_printer::end_superscript (text_glob
*g
)
4091 int height
= output_style
.point_size
*r
/72;
4093 return( (output_style
.point_size
!= 0) &&
4094 (g
->minv
> output_vpos
) &&
4095 (output_vpos
-height
< g
->maxv
) &&
4096 (output_style
.point_size
< g
->text_style
.point_size
) );
4100 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4101 * start/end and it calls the services of html-text to issue the
4105 void html_printer::do_sup_or_sub (text_glob
*g
)
4107 if (! supress_sub_sup
) {
4108 if (start_subscript(g
)) {
4109 current_paragraph
->do_sub();
4110 } else if (start_superscript(g
)) {
4111 current_paragraph
->do_sup();
4112 } else if (end_subscript(g
)) {
4113 current_paragraph
->done_sub();
4114 } else if (end_superscript(g
)) {
4115 current_paragraph
->done_sup();
4121 * do_end_para - writes out the html text after shutting down the
4122 * current paragraph.
4125 void html_printer::do_end_para (text_glob
*g
)
4128 current_paragraph
->done_para();
4129 current_paragraph
->remove_para_space();
4130 html
.put_string(g
->text_string
+9);
4131 output_vpos
= g
->minv
;
4132 output_hpos
= g
->maxh
;
4133 output_vpos_max
= g
->maxv
;
4134 supress_sub_sup
= false;
4138 * emit_html - write out the html text
4141 void html_printer::emit_html (text_glob
*g
)
4145 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
4146 output_vpos
= g
->minv
;
4147 output_hpos
= g
->maxh
;
4148 output_vpos_max
= g
->maxv
;
4149 supress_sub_sup
= false;
4153 * flush_sbuf - flushes the current sbuf into the list of glyphs.
4156 void html_printer::flush_sbuf()
4158 if (sbuf
.length() > 0) {
4159 int r
=font::res
; // resolution of the device
4160 set_style(sbuf_style
);
4162 if (overstrike_detected
&& (! is_bold(sbuf_style
.f
))) {
4163 font
*bold_font
= make_bold(sbuf_style
.f
);
4164 if (bold_font
!= NULL
)
4165 sbuf_style
.f
= bold_font
;
4168 page_contents
->add(&sbuf_style
, sbuf
,
4170 sbuf_vpos
-sbuf_style
.point_size
*r
/72, sbuf_start_hpos
,
4171 sbuf_vpos
, sbuf_end_hpos
);
4173 output_hpos
= sbuf_end_hpos
;
4174 output_vpos
= sbuf_vpos
;
4175 last_sbuf_length
= 0;
4176 sbuf_prev_hpos
= sbuf_end_hpos
;
4177 overstrike_detected
= false;
4182 void html_printer::set_line_thickness(const environment
*env
)
4184 line_thickness
= env
->size
;
4187 void html_printer::draw(int code
, int *p
, int np
, const environment
*env
)
4194 page_contents
->add_line(&sbuf_style
,
4196 env
->hpos
, env
->vpos
, env
->hpos
+p
[0], env
->vpos
+p
[1], line_thickness
);
4198 error("2 arguments required for line");
4205 line_thickness
= -1;
4207 // troff gratuitously adds an extra 0
4208 if (np
!= 1 && np
!= 2) {
4209 error("0 or 1 argument required for thickness");
4212 line_thickness
= p
[0];
4236 // fill with color env->fill
4237 if (background
!= NULL
)
4239 background
= new color
;
4240 *background
= *env
->fill
;
4244 error("unrecognised drawing command `%1'", char(code
));
4249 html_printer::html_printer()
4250 : html(0, MAX_LINE_LENGTH
),
4251 no_of_printed_pages(0),
4252 last_sbuf_length(0),
4253 overstrike_detected(false),
4256 output_vpos_max(-1),
4258 inside_font_style(0),
4261 supress_sub_sup(true),
4262 cutoff_heading(100),
4275 pointsize(base_point_size
),
4277 background(default_background
),
4280 seen_pageoffset(false),
4282 seen_linelength(false),
4291 file_list
.add_new_file(xtmpfile());
4292 html
.set_file(file_list
.get_file());
4293 if (font::hor
!= 24)
4294 fatal("horizontal resolution must be 24");
4295 if (font::vert
!= 40)
4296 fatal("vertical resolution must be 40");
4298 // should be sorted html..
4299 if (font::res
% (font::sizescale
*72) != 0)
4300 fatal("res must be a multiple of 72*sizescale");
4304 while (r
% 10 == 0) {
4309 html
.set_fixed_point(point
);
4310 space_glyph
= name_to_glyph("space");
4311 space_width
= font::hor
;
4312 paper_length
= font::paperlength
;
4313 linelength
= font::res
*13/2;
4314 if (paper_length
== 0)
4315 paper_length
= 11*font::res
;
4317 page_contents
= new page();
4321 * add_to_sbuf - adds character code or name to the sbuf.
4324 void html_printer::add_to_sbuf (glyph
*g
, const string
&s
)
4326 if (sbuf_style
.f
== NULL
)
4329 const char *html_glyph
= NULL
;
4330 unsigned int code
= sbuf_style
.f
->get_code(g
);
4333 if (sbuf_style
.f
->contains(g
))
4334 html_glyph
= get_html_entity(sbuf_style
.f
->get_code(g
));
4338 if ((html_glyph
== NULL
) && (code
>= UNICODE_DESC_START
))
4339 html_glyph
= to_unicode(code
);
4341 html_glyph
= get_html_translation(sbuf_style
.f
, s
);
4343 last_sbuf_length
= sbuf
.length();
4344 if (html_glyph
== NULL
)
4345 sbuf
+= ((char)code
);
4350 int html_printer::sbuf_continuation (glyph
*g
, const char *name
,
4351 const environment
*env
, int w
)
4354 * lets see whether the glyph is closer to the end of sbuf
4356 if ((sbuf_end_hpos
== env
->hpos
)
4357 || ((sbuf_prev_hpos
< sbuf_end_hpos
)
4358 && (env
->hpos
< sbuf_end_hpos
)
4359 && ((sbuf_end_hpos
-env
->hpos
< env
->hpos
-sbuf_prev_hpos
)))) {
4360 add_to_sbuf(g
, name
);
4361 sbuf_prev_hpos
= sbuf_end_hpos
;
4362 sbuf_end_hpos
+= w
+ sbuf_kern
;
4365 if ((env
->hpos
>= sbuf_end_hpos
) &&
4366 ((sbuf_kern
== 0) || (sbuf_end_hpos
- sbuf_kern
!= env
->hpos
))) {
4368 * lets see whether a space is needed or not
4371 if (env
->hpos
-sbuf_end_hpos
< space_width
) {
4372 add_to_sbuf(g
, name
);
4373 sbuf_prev_hpos
= sbuf_end_hpos
;
4374 sbuf_end_hpos
= env
->hpos
+ w
;
4383 * get_html_translation - given the position of the character and its name
4384 * return the device encoding for such character.
4387 const char *get_html_translation (font
*f
, const string
&name
)
4389 if ((f
== 0) || name
.empty())
4392 glyph
*g
= name_to_glyph((char *)(name
+ '\0').contents());
4394 return get_html_entity(f
->get_code(g
));
4401 * get_html_entity - given a Unicode character's code point, return a
4402 * HTML entity that represents the character, if the
4403 * character cannot represent itself in all contexts.
4404 * The return value, if non-NULL, is allocated in a static buffer and is
4405 * only valid until the next call of this function.
4407 static const char *get_html_entity (unsigned int code
)
4409 if (code
< UNICODE_DESC_START
) {
4411 case 0x0022: return """;
4412 case 0x0026: return "&";
4413 case 0x003C: return "<";
4414 case 0x003E: return ">";
4415 default: return NULL
;
4419 case 0x00A0: return " ";
4420 case 0x00A1: return "¡";
4421 case 0x00A2: return "¢";
4422 case 0x00A3: return "£";
4423 case 0x00A4: return "¤";
4424 case 0x00A5: return "¥";
4425 case 0x00A6: return "¦";
4426 case 0x00A7: return "§";
4427 case 0x00A8: return "¨";
4428 case 0x00A9: return "©";
4429 case 0x00AA: return "ª";
4430 case 0x00AB: return "«";
4431 case 0x00AC: return "¬";
4432 case 0x00AE: return "®";
4433 case 0x00AF: return "¯";
4434 case 0x00B0: return "°";
4435 case 0x00B1: return "±";
4436 case 0x00B2: return "²";
4437 case 0x00B3: return "³";
4438 case 0x00B4: return "´";
4439 case 0x00B5: return "µ";
4440 case 0x00B6: return "¶";
4441 case 0x00B7: return "·";
4442 case 0x00B8: return "¸";
4443 case 0x00B9: return "¹";
4444 case 0x00BA: return "º";
4445 case 0x00BB: return "»";
4446 case 0x00BC: return "¼";
4447 case 0x00BD: return "½";
4448 case 0x00BE: return "¾";
4449 case 0x00BF: return "¿";
4450 case 0x00C0: return "À";
4451 case 0x00C1: return "Á";
4452 case 0x00C2: return "Â";
4453 case 0x00C3: return "Ã";
4454 case 0x00C4: return "Ä";
4455 case 0x00C5: return "Å";
4456 case 0x00C6: return "Æ";
4457 case 0x00C7: return "Ç";
4458 case 0x00C8: return "È";
4459 case 0x00C9: return "É";
4460 case 0x00CA: return "Ê";
4461 case 0x00CB: return "Ë";
4462 case 0x00CC: return "Ì";
4463 case 0x00CD: return "Í";
4464 case 0x00CE: return "Î";
4465 case 0x00CF: return "Ï";
4466 case 0x00D0: return "Ð";
4467 case 0x00D1: return "Ñ";
4468 case 0x00D2: return "Ò";
4469 case 0x00D3: return "Ó";
4470 case 0x00D4: return "Ô";
4471 case 0x00D5: return "Õ";
4472 case 0x00D6: return "Ö";
4473 case 0x00D7: return "×";
4474 case 0x00D8: return "Ø";
4475 case 0x00D9: return "Ù";
4476 case 0x00DA: return "Ú";
4477 case 0x00DB: return "Û";
4478 case 0x00DC: return "Ü";
4479 case 0x00DD: return "Ý";
4480 case 0x00DE: return "Þ";
4481 case 0x00DF: return "ß";
4482 case 0x00E0: return "à";
4483 case 0x00E1: return "á";
4484 case 0x00E2: return "â";
4485 case 0x00E3: return "ã";
4486 case 0x00E4: return "ä";
4487 case 0x00E5: return "å";
4488 case 0x00E6: return "æ";
4489 case 0x00E7: return "ç";
4490 case 0x00E8: return "è";
4491 case 0x00E9: return "é";
4492 case 0x00EA: return "ê";
4493 case 0x00EB: return "ë";
4494 case 0x00EC: return "ì";
4495 case 0x00ED: return "í";
4496 case 0x00EE: return "î";
4497 case 0x00EF: return "ï";
4498 case 0x00F0: return "ð";
4499 case 0x00F1: return "ñ";
4500 case 0x00F2: return "ò";
4501 case 0x00F3: return "ó";
4502 case 0x00F4: return "ô";
4503 case 0x00F5: return "õ";
4504 case 0x00F6: return "ö";
4505 case 0x00F7: return "÷";
4506 case 0x00F8: return "ø";
4507 case 0x00F9: return "ù";
4508 case 0x00FA: return "ú";
4509 case 0x00FB: return "û";
4510 case 0x00FC: return "ü";
4511 case 0x00FD: return "ý";
4512 case 0x00FE: return "þ";
4513 case 0x00FF: return "ÿ";
4514 case 0x0152: return "Œ";
4515 case 0x0153: return "œ";
4516 case 0x0160: return "Š";
4517 case 0x0161: return "š";
4518 case 0x0178: return "Ÿ";
4519 case 0x0192: return "ƒ";
4520 case 0x0391: return "Α";
4521 case 0x0392: return "Β";
4522 case 0x0393: return "Γ";
4523 case 0x0394: return "Δ";
4524 case 0x0395: return "Ε";
4525 case 0x0396: return "Ζ";
4526 case 0x0397: return "Η";
4527 case 0x0398: return "Θ";
4528 case 0x0399: return "Ι";
4529 case 0x039A: return "Κ";
4530 case 0x039B: return "Λ";
4531 case 0x039C: return "Μ";
4532 case 0x039D: return "Ν";
4533 case 0x039E: return "Ξ";
4534 case 0x039F: return "Ο";
4535 case 0x03A0: return "Π";
4536 case 0x03A1: return "Ρ";
4537 case 0x03A3: return "Σ";
4538 case 0x03A4: return "Τ";
4539 case 0x03A5: return "Υ";
4540 case 0x03A6: return "Φ";
4541 case 0x03A7: return "Χ";
4542 case 0x03A8: return "Ψ";
4543 case 0x03A9: return "Ω";
4544 case 0x03B1: return "α";
4545 case 0x03B2: return "β";
4546 case 0x03B3: return "γ";
4547 case 0x03B4: return "δ";
4548 case 0x03B5: return "ε";
4549 case 0x03B6: return "ζ";
4550 case 0x03B7: return "η";
4551 case 0x03B8: return "θ";
4552 case 0x03B9: return "ι";
4553 case 0x03BA: return "κ";
4554 case 0x03BB: return "λ";
4555 case 0x03BC: return "μ";
4556 case 0x03BD: return "ν";
4557 case 0x03BE: return "ξ";
4558 case 0x03BF: return "ο";
4559 case 0x03C0: return "π";
4560 case 0x03C1: return "ρ";
4561 case 0x03C2: return "ς";
4562 case 0x03C3: return "σ";
4563 case 0x03C4: return "τ";
4564 case 0x03C5: return "υ";
4565 case 0x03C6: return "φ";
4566 case 0x03C7: return "χ";
4567 case 0x03C8: return "ψ";
4568 case 0x03C9: return "ω";
4569 case 0x03D1: return "ϑ";
4570 case 0x03D6: return "ϖ";
4571 case 0x2013: return "–";
4572 case 0x2014: return "—";
4573 case 0x2018: return "‘";
4574 case 0x2019: return "’";
4575 case 0x201A: return "‚";
4576 case 0x201C: return "“";
4577 case 0x201D: return "”";
4578 case 0x201E: return "„";
4579 case 0x2020: return "†";
4580 case 0x2021: return "‡";
4581 case 0x2022: return "•";
4582 case 0x2030: return "‰";
4583 case 0x2032: return "′";
4584 case 0x2033: return "″";
4585 case 0x2039: return "‹";
4586 case 0x203A: return "›";
4587 case 0x203E: return "‾";
4588 case 0x2044: return "⁄";
4589 case 0x20AC: return "€";
4590 case 0x2111: return "ℑ";
4591 case 0x2118: return "℘";
4592 case 0x211C: return "ℜ";
4593 case 0x2122: return "™";
4594 case 0x2135: return "ℵ";
4595 case 0x2190: return "←";
4596 case 0x2191: return "↑";
4597 case 0x2192: return "→";
4598 case 0x2193: return "↓";
4599 case 0x2194: return "↔";
4600 case 0x21D0: return "⇐";
4601 case 0x21D1: return "⇑";
4602 case 0x21D2: return "⇒";
4603 case 0x21D3: return "⇓";
4604 case 0x21D4: return "⇔";
4605 case 0x2200: return "∀";
4606 case 0x2202: return "∂";
4607 case 0x2203: return "∃";
4608 case 0x2205: return "∅";
4609 case 0x2207: return "∇";
4610 case 0x2208: return "∈";
4611 case 0x2209: return "∉";
4612 case 0x220B: return "∋";
4613 case 0x220F: return "∏";
4614 case 0x2211: return "∑";
4615 case 0x2212: return "−";
4616 case 0x2217: return "∗";
4617 case 0x221A: return "√";
4618 case 0x221D: return "∝";
4619 case 0x221E: return "∞";
4620 case 0x2220: return "∠";
4621 case 0x2227: return "∧";
4622 case 0x2228: return "∨";
4623 case 0x2229: return "∩";
4624 case 0x222A: return "∪";
4625 case 0x222B: return "∫";
4626 case 0x2234: return "∴";
4627 case 0x223C: return "∼";
4628 case 0x2245: return "≅";
4629 case 0x2248: return "≈";
4630 case 0x2260: return "≠";
4631 case 0x2261: return "≡";
4632 case 0x2264: return "≤";
4633 case 0x2265: return "≥";
4634 case 0x2282: return "⊂";
4635 case 0x2283: return "⊃";
4636 case 0x2284: return "⊄";
4637 case 0x2286: return "⊆";
4638 case 0x2287: return "⊇";
4639 case 0x2295: return "⊕";
4640 case 0x2297: return "⊗";
4641 case 0x22A5: return "⊥";
4642 case 0x22C5: return "⋅";
4643 case 0x2308: return "⌈";
4644 case 0x2309: return "⌉";
4645 case 0x230A: return "⌊";
4646 case 0x230B: return "⌋";
4647 case 0x2329: return "⟨";
4648 case 0x232A: return "⟩";
4649 case 0x25CA: return "◊";
4650 case 0x2660: return "♠";
4651 case 0x2663: return "♣";
4652 case 0x2665: return "♥";
4653 case 0x2666: return "♦";
4654 case 0x27E8: return "⟨";
4655 case 0x27E9: return "⟩";
4656 default: return to_unicode(code
);
4662 * overstrike - returns true if the glyph (i, name) is going to overstrike
4663 * a previous glyph in sbuf.
4664 * If true the font is changed to bold and the previous sbuf
4668 int html_printer::overstrike(glyph
*g
, const char *name
, const environment
*env
, int w
)
4670 if ((env
->hpos
< sbuf_end_hpos
)
4671 || ((sbuf_kern
!= 0) && (sbuf_end_hpos
- sbuf_kern
< env
->hpos
))) {
4673 * at this point we have detected an overlap
4675 if (overstrike_detected
) {
4676 /* already detected, remove previous glyph and use this glyph */
4677 sbuf
.set_length(last_sbuf_length
);
4678 add_to_sbuf(g
, name
);
4679 sbuf_end_hpos
= env
->hpos
+ w
;
4682 /* first time we have detected an overstrike in the sbuf */
4683 sbuf
.set_length(last_sbuf_length
); /* remove previous glyph */
4684 if (! is_bold(sbuf_style
.f
))
4686 overstrike_detected
= true;
4687 add_to_sbuf(g
, name
);
4688 sbuf_end_hpos
= env
->hpos
+ w
;
4696 * set_char - adds a character into the sbuf if it is a continuation
4697 * with the previous word otherwise flush the current sbuf
4698 * and add character anew.
4701 void html_printer::set_char(glyph
*g
, font
*f
, const environment
*env
,
4702 int w
, const char *name
)
4704 style
sty(f
, env
->size
, env
->height
, env
->slant
, env
->fontno
, *env
->col
);
4705 if (sty
.slant
!= 0) {
4706 if (sty
.slant
> 80 || sty
.slant
< -80) {
4707 error("silly slant `%1' degrees", sty
.slant
);
4711 if (((! sbuf
.empty()) && (sty
== sbuf_style
) && (sbuf_vpos
== env
->vpos
))
4712 && (sbuf_continuation(g
, name
, env
, w
)
4713 || overstrike(g
, name
, env
, w
)))
4717 if (sbuf_style
.f
== NULL
)
4719 add_to_sbuf(g
, name
);
4720 sbuf_end_hpos
= env
->hpos
+ w
;
4721 sbuf_start_hpos
= env
->hpos
;
4722 sbuf_prev_hpos
= env
->hpos
;
4723 sbuf_vpos
= env
->vpos
;
4729 * set_numbered_char - handle numbered characters.
4730 * Negative values are interpreted as unbreakable spaces;
4731 * the value (taken positive) gives the width.
4734 void html_printer::set_numbered_char(int num
, const environment
*env
,
4740 num
= 160; //
4742 glyph
*g
= number_to_glyph(num
);
4743 int fn
= env
->fontno
;
4744 if (fn
< 0 || fn
>= nfonts
) {
4745 error("bad font position `%1'", fn
);
4748 font
*f
= font_table
[fn
];
4750 error("no font mounted at `%1'", fn
);
4753 if (!f
->contains(g
)) {
4754 error("font `%1' does not contain numbered character %2",
4763 w
= f
->get_width(g
, env
->size
);
4767 set_char(g
, f
, env
, w
, 0);
4770 glyph
*html_printer::set_char_and_width(const char *nm
, const environment
*env
,
4771 int *widthp
, font
**f
)
4773 glyph
*g
= name_to_glyph(nm
);
4774 int fn
= env
->fontno
;
4775 if (fn
< 0 || fn
>= nfonts
) {
4776 error("bad font position `%1'", fn
);
4777 return UNDEFINED_GLYPH
;
4779 *f
= font_table
[fn
];
4781 error("no font mounted at `%1'", fn
);
4782 return UNDEFINED_GLYPH
;
4784 if (!(*f
)->contains(g
)) {
4785 if (nm
[0] != '\0' && nm
[1] == '\0')
4786 error("font `%1' does not contain ascii character `%2'",
4790 error("font `%1' does not contain special character `%2'",
4793 return UNDEFINED_GLYPH
;
4795 int w
= (*f
)->get_width(g
, env
->size
);
4803 * write_title - writes the title to this document
4806 void html_printer::write_title (int in_head
)
4808 if (title
.has_been_found
) {
4810 html
.put_string("<title>");
4811 html
.put_string(title
.text
);
4812 html
.put_string("</title>").nl().nl();
4814 title
.has_been_written
= true;
4815 if (title
.with_h1
) {
4816 if (dialect
== xhtml
)
4817 html
.put_string("<h1>");
4819 html
.put_string("<h1 align=\"center\">");
4820 html
.put_string(title
.text
);
4821 html
.put_string("</h1>").nl().nl();
4824 } else if (in_head
) {
4825 // place empty title tags to help conform to `tidy'
4826 html
.put_string("<title></title>").nl();
4831 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
4834 static void write_rule (void)
4837 if (dialect
== xhtml
)
4838 fputs("<hr/>\n", stdout
);
4840 fputs("<hr>\n", stdout
);
4844 void html_printer::begin_page(int n
)
4847 #if defined(DEBUGGING)
4848 html
.begin_comment("Page: ").put_string(i_to_a(page_number
)).end_comment();;
4850 no_of_printed_pages
++;
4853 output_style
.point_size
= -1;
4854 output_space_code
= 32;
4855 output_draw_point_size
= -1;
4856 output_line_thickness
= -1;
4859 output_vpos_max
= -1;
4860 current_paragraph
= new html_text(&html
, dialect
);
4861 do_indent(get_troff_indent(), pageoffset
, linelength
);
4862 current_paragraph
->do_para("", false);
4865 void html_printer::end_page(int)
4871 font
*html_printer::make_font(const char *nm
)
4873 return html_font::load_html_font(nm
);
4876 void html_printer::do_body (void)
4878 if (background
== NULL
)
4879 fputs("<body>\n\n", stdout
);
4881 unsigned int r
, g
, b
;
4884 background
->get_rgb(&r
, &g
, &b
);
4885 // we have to scale 0..0xFFFF to 0..0xFF
4886 sprintf(buf
, "%.2X%.2X%.2X", r
/0x101, g
/0x101, b
/0x101);
4888 fputs("<body bgcolor=\"#", stdout
);
4890 fputs("\">\n\n", stdout
);
4895 * emit_link - generates: <a href="to">name</a>
4898 void html_printer::emit_link (const string
&to
, const char *name
)
4900 fputs("<a href=\"", stdout
);
4901 fputs(to
.contents(), stdout
);
4902 fputs("\">", stdout
);
4903 fputs(name
, stdout
);
4904 fputs("</a>", stdout
);
4908 * write_navigation - writes out the links which navigate between
4912 void html_printer::write_navigation (const string
&top
, const string
&prev
,
4913 const string
&next
, const string
¤t
)
4915 int need_bar
= false;
4917 if (multiple_files
) {
4918 current_paragraph
->done_para();
4921 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
4922 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
4923 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
4924 "<tr><td class=\"left\">", stdout
);
4925 handle_valid_flag(false);
4926 fputs("[ ", stdout
);
4927 if ((strcmp(prev
.contents(), "") != 0) && prev
!= top
&& prev
!= current
) {
4928 emit_link(prev
, "prev");
4931 if ((strcmp(next
.contents(), "") != 0) && next
!= top
&& next
!= current
) {
4933 fputs(" | ", stdout
);
4934 emit_link(next
, "next");
4937 if (top
!= "<standard input>" && (strcmp(top
.contents(), "") != 0) && top
!= current
) {
4939 fputs(" | ", stdout
);
4940 emit_link(top
, "top");
4942 fputs(" ]\n", stdout
);
4945 fputs("</td><td class=\"right\"><small>"
4946 "This document was produced using "
4947 "<a href=\"" ROFF_URL
"\">" T_ROFF
" v" VERSION
4948 "</a>.</small></td></tr></table>\n",
4955 * do_file_components - scan the file list copying each temporary
4956 * file in turn. This is used twofold:
4958 * firstly to emit section heading links,
4959 * between file fragments if required and
4960 * secondly to generate jobname file fragments
4964 void html_printer::do_file_components (void)
4966 int fragment_no
= 1;
4972 file_list
.start_of_list();
4973 top
= string(job_name
);
4974 if (dialect
== xhtml
)
4975 top
+= string(".xhtml");
4977 top
+= string(".html");
4979 next
= file_list
.next_file_name();
4982 while (file_list
.get_file() != 0) {
4983 if (fseek(file_list
.get_file(), 0L, 0) < 0)
4984 fatal("fseek on temporary file failed");
4985 html
.copy_file(file_list
.get_file());
4986 fclose(file_list
.get_file());
4988 file_list
.move_next();
4989 if (file_list
.is_new_output_file()) {
4990 #ifdef LONG_FOR_TIME_T
4996 if (fragment_no
> 1)
4997 write_navigation(top
, prev
, next
, current
);
5000 next
= file_list
.next_file_name();
5002 string split_file
= file_list
.file_name();
5005 freopen(split_file
.contents(), "w", stdout
);
5007 if (dialect
== xhtml
)
5008 writeHeadMetaStyle();
5010 html
.begin_comment("Creator : " T_ROFF
" version " VERSION
).end_comment();
5013 html
.begin_comment("CreationDate: ")
5014 .put_string(ctime(&t
), strlen(ctime(&t
))-1)
5017 if (dialect
== html4
)
5018 writeHeadMetaStyle();
5020 html
.put_string("<title>");
5021 html
.put_string(split_file
.contents());
5022 html
.put_string("</title>").nl().nl();
5024 fputs(head_info
.contents(), stdout
);
5025 fputs("</head>\n", stdout
);
5026 write_navigation(top
, prev
, next
, current
);
5028 if (file_list
.are_links_required())
5029 header
.write_headings(stdout
, true);
5031 if (fragment_no
> 1)
5032 write_navigation(top
, prev
, next
, current
);
5034 current_paragraph
->done_para();
5038 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
5039 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
5040 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
5041 "<tr><td class=\"left\">", stdout
);
5042 handle_valid_flag(true);
5044 fputs("</td><td class=\"right\"><small>"
5045 "This document was produced using "
5046 "<a href=\"" ROFF_URL
"\">" T_ROFF
" v" VERSION
5047 "</a>.</small></td></tr></table>\n",
5055 * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
5056 * related information.
5059 void html_printer::writeHeadMetaStyle (void)
5061 if (dialect
== html4
) {
5062 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout
);
5063 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout
);
5064 fputs("<html>\n", stdout
);
5065 fputs("<head>\n", stdout
);
5066 fputs("<meta name=\"generator\" "
5067 "content=\"" L_ROFF
" -Thtml, see " ROFF_URL
"\">\n", stdout
);
5068 fputs("<meta http-equiv=\"Content-Type\" "
5069 "content=\"text/html; charset=US-ASCII\">\n", stdout
);
5070 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout
);
5071 fputs("<style type=\"text/css\">\n", stdout
);
5074 fputs("<?xml version=\"1.0\" encoding=\"us-ascii\"?>\n", stdout
);
5075 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\"\n", stdout
);
5076 fputs(" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\"\n", stdout
);
5077 fputs(" [<!ENTITY mathml \"http://www.w3.org/1998/Math/MathML\">]>\n", stdout
);
5079 fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n",
5081 fputs("<head>\n", stdout
);
5082 fputs("<meta name=\"generator\" "
5083 "content=\"" L_ROFF
" -Txhtml, see " ROFF_URL
"\"/>\n", stdout
);
5084 fputs("<meta http-equiv=\"Content-Type\" "
5085 "content=\"text/html; charset=US-ASCII\"/>\n", stdout
);
5086 fputs("<meta name=\"Content-Style\" content=\"text/css\"/>\n", stdout
);
5087 fputs("<style type=\"text/css\">\n", stdout
);
5088 fputs(" .center { text-align: center }\n", stdout
);
5089 fputs(" .right { text-align: right }\n", stdout
);
5091 fputs(" p { margin-top: 0; margin-bottom: 0; "
5092 "vertical-align: top }\n", stdout
);
5093 fputs(" pre { margin-top: 0; margin-bottom: 0; "
5094 "vertical-align: top }\n", stdout
);
5095 fputs(" table { margin-top: 0; margin-bottom: 0; "
5096 "vertical-align: top }\n", stdout
);
5097 fputs(" h1 { text-align: center }\n", stdout
);
5098 fputs("</style>\n", stdout
);
5101 html_printer::~html_printer()
5103 #ifdef LONG_FOR_TIME_T
5109 if (current_paragraph
)
5110 current_paragraph
->flush_text();
5112 html
.set_file(stdout
);
5114 if (dialect
== xhtml
)
5115 writeHeadMetaStyle();
5117 html
.begin_comment("Creator : " T_ROFF
" version " VERSION
).end_comment();
5120 html
.begin_comment("CreationDate: ")
5121 .put_string(ctime(&t
), strlen(ctime(&t
))-1)
5124 if (dialect
== html4
)
5125 writeHeadMetaStyle();
5129 fputs(head_info
.contents(), stdout
);
5130 fputs("</head>\n", stdout
);
5134 header
.write_headings(stdout
, false);
5136 #if defined(DEBUGGING)
5137 html
.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages
)).end_comment();
5142 if (multiple_files
) {
5143 fputs("</body>\n", stdout
);
5144 fputs("</html>\n", stdout
);
5145 do_file_components();
5147 do_file_components();
5148 fputs("</body>\n", stdout
);
5149 fputs("</html>\n", stdout
);
5154 * get_str - returns a dupicate of string, s. The duplicate
5155 * string is terminated at the next ',' or ']'.
5158 static char *get_str (const char *s
, char **n
)
5163 while ((s
[i
] != (char)0) && (s
[i
] != ',') && (s
[i
] != ']'))
5170 (*n
) = (char *)&s
[i
+1];
5172 (*n
) = (char *)&s
[i
];
5176 (*n
) = (char *)&s
[1];
5183 * make_val - creates a string from if s is NULL.
5186 char *make_val (char *s
, int v
, char *id
, char *f
, char *l
)
5191 sprintf(buf
, "%d", v
);
5192 return strsave(buf
);
5196 * check that value, s, is the same as, v.
5204 f
= (char *)"stdin";
5206 l
= (char *)"<none>";
5208 "%s:%s: " L_D_HTML
" assertion failed at id%s; should: %d; got: %s\n",
5216 * handle_assertion - handles the assertions created via .www:ASSERT
5217 * in www.tmac. See www.tmac for examples.
5218 * This method should be called as we are
5219 * parsing the ditroff input. It checks the x, y
5220 * position assertions. It does _not_ check the
5221 * troff state assertions as these are unknown at this
5225 void html_printer::handle_assertion (int minv
, int minh
, int maxv
, int maxh
, const char *s
)
5228 char *cmd
= get_str(s
, &n
);
5229 char *id
= get_str(n
, &n
);
5230 char *val
= get_str(n
, &n
);
5231 char *file
= get_str(n
, &n
);
5232 char *line
= get_str(n
, &n
);
5234 if (strcmp(cmd
, "assertion:[x") == 0)
5235 as
.addx(cmd
, id
, make_val(val
, minh
, id
, file
, line
), file
, line
);
5236 else if (strcmp(cmd
, "assertion:[y") == 0)
5237 as
.addy(cmd
, id
, make_val(val
, minv
, id
, file
, line
), file
, line
);
5239 if (strncmp(cmd
, "assertion:[", strlen("assertion:[")) == 0)
5240 page_contents
->add_tag(&sbuf_style
, string(s
),
5241 line_number
, minv
, minh
, maxv
, maxh
);
5245 * build_state_assertion - builds the troff state assertions.
5248 void html_printer::handle_state_assertion (text_glob
*g
)
5250 if (g
!= NULL
&& g
->is_a_tag() &&
5251 (strncmp(g
->text_string
, "assertion:[", 11) == 0)) {
5252 char *n
= (char *)&g
->text_string
[11];
5253 char *cmd
= get_str(n
, &n
);
5254 char *val
= get_str(n
, &n
);
5255 (void)get_str(n
, &n
); // unused
5256 char *file
= get_str(n
, &n
);
5257 char *line
= get_str(n
, &n
);
5259 as
.build(cmd
, val
, file
, line
);
5264 * special - handle all x X requests from troff. For post-html they
5265 * allow users to pass raw html commands, turn auto linked
5266 * headings off/on etc.
5269 void html_printer::special(char *s
, const environment
*env
, char type
)
5275 if (env
->fontno
>= 0) {
5276 style
sty(get_font_from_index(env
->fontno
), env
->size
, env
->height
,
5277 env
->slant
, env
->fontno
, *env
->col
);
5281 if (strncmp(s
, "html:", 5) == 0) {
5282 int r
=font::res
; /* resolution of the device */
5283 font
*f
=sbuf_style
.f
;
5288 f
= font::load_font("TR", &found
);
5292 * need to pass rest of string through to html output during flush
5294 page_contents
->add_and_encode(&sbuf_style
, string(&s
[5]),
5296 env
->vpos
-env
->size
*r
/72, env
->hpos
,
5297 env
->vpos
, env
->hpos
,
5301 * assume that the html command has no width, if it does then
5302 * hopefully troff will have fudged this in a macro by
5303 * requesting that the formatting move right by the appropriate
5306 } else if ((strncmp(s
, "html</p>:", 9) == 0) ||
5307 (strncmp(s
, "html<?p>:", 9) == 0) ||
5308 (strncmp(s
, "math<?p>:", 9) == 0)) {
5309 int r
=font::res
; /* resolution of the device */
5310 font
*f
=sbuf_style
.f
;
5316 f
= font::load_font("TR", &found
);
5319 if (strncmp(s
, "math<?p>:", 9) == 0) {
5320 if (strncmp((char *)&s
[9], "<math>", 6) == 0) {
5323 t
+= "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
5324 t
+= (char *)&s
[15];
5331 * need to pass all of string through to html output during flush
5333 page_contents
->add_and_encode(&sbuf_style
, string(s
),
5335 env
->vpos
-env
->size
*r
/72, env
->hpos
,
5336 env
->vpos
, env
->hpos
,
5340 * assume that the html command has no width, if it does then
5341 * hopefully troff will have fudged this in a macro by
5342 * requesting that the formatting move right by the appropriate
5346 } else if (strncmp(s
, "index:", 6) == 0) {
5347 cutoff_heading
= atoi(&s
[6]);
5348 } else if (strncmp(s
, "assertion:[", 11) == 0) {
5349 int r
=font::res
; /* resolution of the device */
5351 handle_assertion(env
->vpos
-env
->size
*r
/72, env
->hpos
,
5352 env
->vpos
, env
->hpos
, s
);
5358 * devtag - handles device troff tags sent from the `troff'.
5359 * These include the troff state machine tags:
5360 * .br, .sp, .in, .tl, .ll etc
5362 * (see man 5 grohtml_tags).
5365 void html_printer::devtag (char *s
, const environment
*env
, char type
)
5372 if (env
->fontno
>= 0) {
5373 style
sty(get_font_from_index(env
->fontno
), env
->size
, env
->height
,
5374 env
->slant
, env
->fontno
, *env
->col
);
5378 if (strncmp(s
, "devtag:", strlen("devtag:")) == 0) {
5379 int r
=font::res
; /* resolution of the device */
5381 page_contents
->add_tag(&sbuf_style
, string(s
),
5383 env
->vpos
-env
->size
*r
/72, env
->hpos
,
5384 env
->vpos
, env
->hpos
);
5391 * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
5394 int html_printer::round_width(int x
)
5399 // don't depend on the rounding direction for division of negative integers
5404 ? -((-x
+ r
/2 - 1)/r
)
5410 * handle_valid_flag - emits a valid xhtml 1.1 or html-4.01 button, provided -V
5411 * was supplied on the command line.
5414 void html_printer::handle_valid_flag (int needs_para
)
5418 fputs("<p>", stdout
);
5419 if (dialect
== xhtml
)
5420 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5421 "src=\"http://www.w3.org/Icons/valid-xhtml11-blue\" "
5422 "alt=\"Valid XHTML 1.1 Transitional\" height=\"31\" width=\"88\" /></a>\n",
5425 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5426 "src=\"http://www.w3.org/Icons/valid-html401-blue\" "
5427 "alt=\"Valid HTML 4.01 Transitional\" height=\"31\" width=\"88\"></a>\n",
5430 fputs("</p>", stdout
);
5434 int main(int argc
, char **argv
)
5436 program_name
= argv
[0];
5437 static char stderr_buf
[BUFSIZ
];
5438 setbuf(stderr
, stderr_buf
);
5440 static const struct option long_options
[] = {
5441 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
5442 { "version", no_argument
, 0, 'v' },
5445 while ((c
= getopt_long(argc
, argv
, "a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
5446 long_options
, NULL
))
5450 /* text antialiasing bits - handled by pre-html */
5453 // set background color to white
5454 default_background
= new color
;
5455 default_background
->set_gray(color::MAX_COLOR_VAL
);
5458 /* handled by pre-html */
5461 /* handled by pre-html */
5464 /* handled by pre-html */
5467 font::command_line_font_dir(optarg
);
5470 /* graphic antialiasing bits - handled by pre-html */
5473 /* do not use the Hn headings of html, but manufacture our own */
5474 manufacture_headings
= true;
5477 /* handled by pre-html */
5480 /* handled by pre-html */
5483 multiple_files
= true;
5490 simple_anchors
= true;
5493 /* handled by pre-html */
5496 /* handled by pre-html */
5502 base_point_size
= atoi(optarg
);
5505 split_level
= atoi(optarg
) + 1;
5508 puts(L_D_HTML
" (" T_ROFF
") v" VERSION
);
5515 if (strcmp(optarg
, "x") == 0) {
5517 simple_anchors
= true;
5518 } else if (strcmp(optarg
, "4") == 0)
5521 printf("unsupported html dialect %s (defaulting to html4)\n", optarg
);
5526 case CHAR_MAX
+ 1: // --help
5537 if (optind
>= argc
) {
5540 for (int i
= optind
; i
< argc
; i
++)
5546 static void usage(FILE *stream
)
5549 "Synopsis: %s [-vbelnhVy] [-D dir] "
5550 "[-I image_stem] [-F dir] [-x x] [files ...]\n",