2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
3 * Free Software Foundation, Inc.
5 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
6 * but it owes a huge amount of ideas and raw code from
7 * James Clark (jjc@jclark.com) grops/ps.cpp.
11 This file is part of groff.
13 groff is free software; you can redistribute it and/or modify it under
14 the terms of the GNU General Public License as published by the Free
15 Software Foundation; either version 2, or (at your option) any later
18 groff is distributed in the hope that it will be useful, but WITHOUT ANY
19 WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 You should have received a copy of the GNU General Public License along
24 with groff; see the file COPYING. If not, write to the Free Software
25 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
28 #include "stringclass.h"
31 #include "html-text.h"
32 #include "html-table.h"
44 extern "C" const char *Version_string
;
53 #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
54 #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
55 #define CENTER_TOLERANCE 2 /* how many pixels off center do we allow */
56 #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
57 #define UNICODE_DESC_START 0x80 /* all character entities above this are */
58 /* either encoded by their glyph names or if */
59 /* there is no name then we use &#nnn; */
60 typedef enum {CENTERED
, LEFT
, RIGHT
, INLINE
} TAG_ALIGNMENT
;
61 typedef enum {col_tag
, tab_tag
, tab0_tag
, none
} colType
;
64 // #define DEBUG_TABLES
70 char *get_html_translation (font
*f
, const string
&name
);
71 int char_translate_to_html (font
*f
, char *buf
, int buflen
, unsigned char ch
, int b
, int and_single
);
74 static int auto_links
= TRUE
; /* by default we enable automatic links at */
75 /* top of the document. */
76 static int auto_rule
= TRUE
; /* by default we enable an automatic rule */
77 /* at the top and bottom of the document */
78 static int simple_anchors
= FALSE
; /* default to anchors with heading text */
79 static int manufacture_headings
= FALSE
; /* default is to use the Hn html headings, */
80 /* rather than manufacture our own. */
81 static color
*default_background
= NULL
; /* has user requested initial bg color? */
82 static string job_name
; /* if set then the output is split into */
83 /* multiple files with `job_name'-%d.html */
84 static int multiple_files
= FALSE
; /* must we the output be divided into */
85 /* multiple html files, one for each */
87 static int base_point_size
= 0; /* which troff font size maps onto html */
89 static int split_level
= 2; /* what heading level to split at? */
90 static string head_info
; /* user supplied information to be placed */
91 /* into <head> </head> */
95 * start with a few favorites
100 static int min (int a
, int b
)
108 static int max (int a
, int b
)
117 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
120 static int is_intersection (int a1
, int a2
, int b1
, int b2
)
122 // easier to prove NOT outside limits
123 return ! ((a1
> b2
) || (a2
< b1
));
127 * is_digit - returns TRUE if character, ch, is a digit.
130 static int is_digit (char ch
)
132 return (ch
>= '0') && (ch
<= '9');
136 * the classes and methods for maintaining a list of files.
144 string output_file_name
;
150 * file - initialize all fields to NULL
154 : fp(f
), next(NULL
), new_output_file(FALSE
),
155 require_links(FALSE
), output_file_name("")
162 FILE *get_file (void);
163 void start_of_list (void);
164 void move_next (void);
165 void add_new_file (FILE *f
);
166 void set_file_name (string name
);
167 void set_links_required (void);
168 int are_links_required (void);
169 int is_new_output_file (void);
170 string
file_name (void);
171 string
next_file_name (void);
179 * files - create an empty list of files.
183 : head(NULL
), tail(NULL
), ptr(NULL
)
188 * get_file - returns the FILE associated with ptr.
191 FILE *files::get_file (void)
200 * start_of_list - reset the ptr to the start of the list.
203 void files::start_of_list (void)
209 * move_next - moves the ptr to the next element on the list.
212 void files::move_next (void)
219 * add_new_file - adds a new file, f, to the list.
222 void files::add_new_file (FILE *f
)
228 tail
->next
= new file(f
);
235 * set_file_name - sets the final file name to contain the html
239 void files::set_file_name (string name
)
242 ptr
->output_file_name
= name
;
243 ptr
->new_output_file
= TRUE
;
248 * set_links_required - issue links when processing this component
252 void files::set_links_required (void)
255 ptr
->require_links
= TRUE
;
259 * are_links_required - returns TRUE if this section of the file
260 * requires that links should be issued.
263 int files::are_links_required (void)
266 return ptr
->require_links
;
271 * is_new_output_file - returns TRUE if this component of the file
272 * is the start of a new output file.
275 int files::is_new_output_file (void)
278 return ptr
->new_output_file
;
283 * file_name - returns the name of the file.
286 string
files::file_name (void)
289 return ptr
->output_file_name
;
294 * next_file_name - returns the name of the next file.
297 string
files::next_file_name (void)
299 if (ptr
!= NULL
&& ptr
->next
!= NULL
)
300 return ptr
->next
->output_file_name
;
305 * the class and methods for styles
316 style (font
*, int, int, int, int, color
);
317 int operator == (const style
&) const;
318 int operator != (const style
&) const;
326 style::style(font
*p
, int sz
, int h
, int sl
, int no
, color c
)
327 : f(p
), point_size(sz
), font_no(no
), height(h
), slant(sl
), col(c
)
331 int style::operator==(const style
&s
) const
333 return (f
== s
.f
&& point_size
== s
.point_size
334 && height
== s
.height
&& slant
== s
.slant
&& col
== s
.col
);
337 int style::operator!=(const style
&s
) const
339 return !(*this == s
);
343 * the class and methods for retaining ascii text
353 char_block(int length
);
357 char_block::char_block()
358 : buffer(NULL
), used(0), next(NULL
)
362 char_block::char_block(int length
)
363 : used(0), next(NULL
)
365 buffer
= new char[max(length
, char_block::SIZE
)];
367 fatal("out of memory error");
370 char_block::~char_block()
380 char *add_string(const char *, unsigned int);
381 char *add_string(const string
&);
387 char_buffer::char_buffer()
388 : head(NULL
), tail(NULL
)
392 char_buffer::~char_buffer()
394 while (head
!= NULL
) {
395 char_block
*temp
= head
;
401 char *char_buffer::add_string (const char *s
, unsigned int length
)
404 unsigned int old_used
;
406 if (s
== NULL
|| length
== 0)
410 tail
= new char_block(length
+1);
413 if (tail
->used
+ length
+1 > char_block::SIZE
) {
414 tail
->next
= new char_block(length
+1);
419 old_used
= tail
->used
;
421 tail
->buffer
[tail
->used
] = s
[i
];
427 // add terminating nul character
429 tail
->buffer
[tail
->used
] = '\0';
432 // and return start of new string
434 return &tail
->buffer
[old_used
];
437 char *char_buffer::add_string (const string
&s
)
439 return add_string(s
.contents(), s
.length());
443 * the classes and methods for maintaining glyph positions.
448 void text_glob_html (style
*s
, char *str
, int length
,
449 int min_vertical
, int min_horizontal
,
450 int max_vertical
, int max_horizontal
);
451 void text_glob_special (style
*s
, char *str
, int length
,
452 int min_vertical
, int min_horizontal
,
453 int max_vertical
, int max_horizontal
);
454 void text_glob_line (style
*s
,
455 int min_vertical
, int min_horizontal
,
456 int max_vertical
, int max_horizontal
,
458 void text_glob_auto_image(style
*s
, char *str
, int length
,
459 int min_vertical
, int min_horizontal
,
460 int max_vertical
, int max_horizontal
);
461 void text_glob_tag (style
*s
, char *str
, int length
,
462 int min_vertical
, int min_horizontal
,
463 int max_vertical
, int max_horizontal
);
467 int is_a_line (void);
470 int is_auto_img (void);
479 int is_eol_ce (void);
484 int is_tab_ts (void);
485 int is_tab_te (void);
490 int get_tab_args (char *align
);
492 void remember_table (html_table
*t
);
493 html_table
*get_table (void);
496 const char *text_string
;
497 unsigned int text_length
;
498 int minv
, minh
, maxv
, maxh
;
499 int is_tag
; // is this a .br, .sp, .tl etc
500 int is_img_auto
; // image created by eqn delim
501 int is_special
; // text has come via 'x X html:'
502 int is_line
; // is the command a <line>?
503 int thickness
; // the thickness of a line
504 html_table
*tab
; // table description
507 text_glob (style
*s
, const char *str
, int length
,
508 int min_vertical
, int min_horizontal
,
509 int max_vertical
, int max_horizontal
,
510 bool is_troff_command
,
511 bool is_auto_image
, bool is_special_command
,
512 bool is_a_line
, int thickness
);
515 text_glob::text_glob (style
*s
, const char *str
, int length
,
516 int min_vertical
, int min_horizontal
,
517 int max_vertical
, int max_horizontal
,
518 bool is_troff_command
,
519 bool is_auto_image
, bool is_special_command
,
520 bool is_a_line_flag
, int line_thickness
)
521 : text_style(*s
), text_string(str
), text_length(length
),
522 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
),
523 is_tag(is_troff_command
), is_img_auto(is_auto_image
), is_special(is_special_command
),
524 is_line(is_a_line_flag
), thickness(line_thickness
), tab(NULL
)
528 text_glob::text_glob ()
529 : text_string(NULL
), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
530 is_tag(FALSE
), is_special(FALSE
), is_line(FALSE
), thickness(0), tab(NULL
)
534 text_glob::~text_glob ()
541 * text_glob_html - used to place html text into the glob buffer.
544 void text_glob::text_glob_html (style
*s
, char *str
, int length
,
545 int min_vertical
, int min_horizontal
,
546 int max_vertical
, int max_horizontal
)
548 text_glob
*g
= new text_glob(s
, str
, length
,
549 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
550 FALSE
, FALSE
, FALSE
, FALSE
, 0);
556 * text_glob_html - used to place html specials into the glob buffer.
557 * This text is essentially html commands coming through
558 * from the macro sets, with special designated sequences of
559 * characters translated into html. See add_and_encode.
562 void text_glob::text_glob_special (style
*s
, char *str
, int length
,
563 int min_vertical
, int min_horizontal
,
564 int max_vertical
, int max_horizontal
)
566 text_glob
*g
= new text_glob(s
, str
, length
,
567 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
568 FALSE
, FALSE
, TRUE
, FALSE
, 0);
574 * text_glob_line - record horizontal draw line commands.
577 void text_glob::text_glob_line (style
*s
,
578 int min_vertical
, int min_horizontal
,
579 int max_vertical
, int max_horizontal
,
582 text_glob
*g
= new text_glob(s
, "", 0,
583 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
584 FALSE
, FALSE
, FALSE
, TRUE
, thickness_value
);
590 * text_glob_auto_image - record the presence of a .auto-image tag command.
591 * Used to mark that an image has been created automatically
592 * by a preprocessor and (pre-grohtml/troff) combination.
593 * Under some circumstances images may not be created.
600 * $1 over x$!recripical of x
603 * the first auto-image marker is created via .EQ/.EN pair
604 * and no image is created.
605 * The second auto-image marker occurs at $1 over x$
606 * Currently this image will not be created
607 * as the whole of the table is created as an image.
608 * (Once html tables are handled by grohtml this will change.
609 * Shortly this will be the case).
612 void text_glob::text_glob_auto_image(style
*s
, char *str
, int length
,
613 int min_vertical
, int min_horizontal
,
614 int max_vertical
, int max_horizontal
)
616 text_glob
*g
= new text_glob(s
, str
, length
,
617 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
618 TRUE
, TRUE
, FALSE
, FALSE
, 0);
624 * text_glob_tag - records a troff tag.
627 void text_glob::text_glob_tag (style
*s
, char *str
, int length
,
628 int min_vertical
, int min_horizontal
,
629 int max_vertical
, int max_horizontal
)
631 text_glob
*g
= new text_glob(s
, str
, length
,
632 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
633 TRUE
, FALSE
, FALSE
, FALSE
, 0);
639 * is_a_line - returns TRUE if glob should be converted into an <hr>
642 int text_glob::is_a_line (void)
648 * is_a_tag - returns TRUE if glob contains a troff directive.
651 int text_glob::is_a_tag (void)
657 * is_eol - returns TRUE if glob contains the tag eol
660 int text_glob::is_eol (void)
662 return is_tag
&& (strcmp(text_string
, "devtag:.eol") == 0);
666 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
669 int text_glob::is_eol_ce (void)
671 return is_tag
&& (strcmp(text_string
, "devtag:eol.ce") == 0);
675 * is_tl - returns TRUE if glob contains the tag .tl
678 int text_glob::is_tl (void)
680 return is_tag
&& (strcmp(text_string
, "devtag:.tl") == 0);
684 * is_eo_tl - returns TRUE if glob contains the tag eo.tl
687 int text_glob::is_eo_tl (void)
689 return is_tag
&& (strcmp(text_string
, "devtag:.eo.tl") == 0);
693 * is_nf - returns TRUE if glob contains the tag .fi 0
696 int text_glob::is_nf (void)
698 return is_tag
&& (strncmp(text_string
, "devtag:.fi",
699 strlen("devtag:.fi")) == 0) &&
704 * is_fi - returns TRUE if glob contains the tag .fi 1
707 int text_glob::is_fi (void)
709 return( is_tag
&& (strncmp(text_string
, "devtag:.fi",
710 strlen("devtag:.fi")) == 0) &&
715 * is_eo_h - returns TRUE if glob contains the tag .eo.h
718 int text_glob::is_eo_h (void)
720 return is_tag
&& (strcmp(text_string
, "devtag:.eo.h") == 0);
724 * is_ce - returns TRUE if glob contains the tag .ce
727 int text_glob::is_ce (void)
729 return is_tag
&& (strncmp(text_string
, "devtag:.ce",
730 strlen("devtag:.ce")) == 0);
734 * is_in - returns TRUE if glob contains the tag .in
737 int text_glob::is_in (void)
739 return is_tag
&& (strncmp(text_string
, "devtag:.in ",
740 strlen("devtag:.in ")) == 0);
744 * is_po - returns TRUE if glob contains the tag .po
747 int text_glob::is_po (void)
749 return is_tag
&& (strncmp(text_string
, "devtag:.po ",
750 strlen("devtag:.po ")) == 0);
754 * is_ti - returns TRUE if glob contains the tag .ti
757 int text_glob::is_ti (void)
759 return is_tag
&& (strncmp(text_string
, "devtag:.ti ",
760 strlen("devtag:.ti ")) == 0);
764 * is_ll - returns TRUE if glob contains the tag .ll
767 int text_glob::is_ll (void)
769 return is_tag
&& (strncmp(text_string
, "devtag:.ll ",
770 strlen("devtag:.ll ")) == 0);
774 * is_col - returns TRUE if glob contains the tag .col
777 int text_glob::is_col (void)
779 return is_tag
&& (strncmp(text_string
, "devtag:.col",
780 strlen("devtag:.col")) == 0);
784 * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
787 int text_glob::is_tab_ts (void)
789 return is_tag
&& (strcmp(text_string
, "devtag:.tab-ts") == 0);
793 * is_tab_te - returns TRUE if glob contains the tag .tab_te
796 int text_glob::is_tab_te (void)
798 return is_tag
&& (strcmp(text_string
, "devtag:.tab-te") == 0);
802 * is_ta - returns TRUE if glob contains the tag .ta
805 int text_glob::is_ta (void)
807 return is_tag
&& (strncmp(text_string
, "devtag:.ta ",
808 strlen("devtag:.ta ")) == 0);
812 * is_tab - returns TRUE if glob contains the tag tab
815 int text_glob::is_tab (void)
817 return is_tag
&& (strncmp(text_string
, "devtag:tab ",
818 strlen("devtag:tab ")) == 0);
822 * is_tab0 - returns TRUE if glob contains the tag tab0
825 int text_glob::is_tab0 (void)
827 return is_tag
&& (strncmp(text_string
, "devtag:tab0",
828 strlen("devtag:tab0")) == 0);
832 * is_auto_img - returns TRUE if the glob contains an automatically
836 int text_glob::is_auto_img (void)
842 * is_br - returns TRUE if the glob is a tag containing a .br
843 * or an implied .br. Note that we do not include .nf or .fi
844 * as grohtml will place a .br after these commands if they
845 * should break the line.
848 int text_glob::is_br (void)
850 return is_a_tag() && ((strcmp ("devtag:.br", text_string
) == 0) ||
851 (strncmp("devtag:.sp", text_string
,
852 strlen("devtag:.sp")) == 0));
855 int text_glob::get_arg (void)
857 if (strncmp("devtag:", text_string
, strlen("devtag:")) == 0) {
858 const char *p
= text_string
;
860 while ((*p
!= (char)0) && (!isspace(*p
)))
862 while ((*p
!= (char)0) && (isspace(*p
)))
872 * get_tab_args - returns the tab position and alignment of the tab tag
875 int text_glob::get_tab_args (char *align
)
877 if (strncmp("devtag:", text_string
, strlen("devtag:")) == 0) {
878 const char *p
= text_string
;
880 // firstly the alignment C|R|L
881 while ((*p
!= (char)0) && (!isspace(*p
)))
883 while ((*p
!= (char)0) && (isspace(*p
)))
887 while ((*p
!= (char)0) && (!isspace(*p
)))
889 while ((*p
!= (char)0) && (isspace(*p
)))
899 * remember_table - saves table, t, in the text_glob.
902 void text_glob::remember_table (html_table
*t
)
910 * get_table - returns the stored table description.
913 html_table
*text_glob::get_table (void)
919 * the class and methods used to construct ordered double linked
920 * lists. In a previous implementation we used templates via
921 * #include "ordered-list.h", but this does assume that all C++
922 * compilers can handle this feature. Pragmatically it is safer to
923 * assume this is not the case.
926 struct element_list
{
931 int minv
, minh
, maxv
, maxh
;
933 element_list (text_glob
*d
,
935 int min_vertical
, int min_horizontal
,
936 int max_vertical
, int max_horizontal
);
941 element_list::element_list ()
942 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
947 * element_list - create a list element assigning the datum and region parameters.
950 element_list::element_list (text_glob
*in
,
952 int min_vertical
, int min_horizontal
,
953 int max_vertical
, int max_horizontal
)
954 : right(0), left(0), datum(in
), lineno(line_number
),
955 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
)
959 element_list::~element_list ()
969 int is_less (element_list
*a
, element_list
*b
);
970 void add (text_glob
*in
,
972 int min_vertical
, int min_horizontal
,
973 int max_vertical
, int max_horizontal
);
974 void sub_move_right (void);
975 void move_right (void);
976 void move_left (void);
978 int is_equal_to_tail (void);
979 int is_equal_to_head (void);
980 void start_from_head (void);
981 void start_from_tail (void);
982 void insert (text_glob
*in
);
983 void move_to (text_glob
*in
);
984 text_glob
*move_right_get_data (void);
985 text_glob
*move_left_get_data (void);
986 text_glob
*get_data (void);
994 * list - construct an empty list.
998 : head(NULL
), tail(NULL
), ptr(NULL
)
1003 * ~list - destroy a complete list.
1008 element_list
*temp
=head
;
1016 } while ((head
!= NULL
) && (head
!= tail
));
1020 * is_less - returns TRUE if a is left of b if on the same line or
1021 * if a is higher up the page than b.
1024 int list::is_less (element_list
*a
, element_list
*b
)
1026 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1027 if (a
->lineno
< b
->lineno
) {
1029 } else if (a
->lineno
> b
->lineno
) {
1031 } else if (is_intersection(a
->minv
, a
->maxv
, b
->minv
, b
->maxv
)) {
1032 return( a
->minh
< b
->minh
);
1034 return( a
->maxv
< b
->maxv
);
1039 * add - adds a datum to the list in the order specified by the
1043 void list::add (text_glob
*in
, int line_number
, int min_vertical
, int min_horizontal
, int max_vertical
, int max_horizontal
)
1045 // create a new list element with datum and position fields initialized
1046 element_list
*t
= new element_list(in
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1050 fprintf(stderr
, "[%s %d,%d,%d,%d] ",
1051 in
->text_string
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1064 while ((last
!= head
) && (is_less(t
, last
)))
1067 if (is_less(t
, last
)) {
1069 last
->left
->right
= t
;
1070 t
->left
= last
->left
;
1072 // now check for a new head
1076 // add t beyond last
1077 t
->right
= last
->right
;
1079 last
->right
->left
= t
;
1081 // now check for a new tail
1089 * sub_move_right - removes the element which is currently pointed to by ptr
1090 * from the list and moves ptr to the right.
1093 void list::sub_move_right (void)
1095 element_list
*t
=ptr
->right
;
1109 ptr
->left
->right
= ptr
->right
;
1110 ptr
->right
->left
= ptr
->left
;
1116 * start_from_head - assigns ptr to the head.
1119 void list::start_from_head (void)
1125 * start_from_tail - assigns ptr to the tail.
1128 void list::start_from_tail (void)
1134 * is_empty - returns TRUE if the list has no elements.
1137 int list::is_empty (void)
1139 return head
== NULL
;
1143 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
1146 int list::is_equal_to_tail (void)
1152 * is_equal_to_head - returns TRUE if the ptr equals the head.
1155 int list::is_equal_to_head (void)
1161 * move_left - moves the ptr left.
1164 void list::move_left (void)
1170 * move_right - moves the ptr right.
1173 void list::move_right (void)
1179 * get_datum - returns the datum referenced via ptr.
1182 text_glob
* list::get_data (void)
1188 * move_right_get_data - returns the datum referenced via ptr and moves
1192 text_glob
* list::move_right_get_data (void)
1202 * move_left_get_data - returns the datum referenced via ptr and moves
1206 text_glob
* list::move_left_get_data (void)
1216 * insert - inserts data after the current position.
1219 void list::insert (text_glob
*in
)
1222 fatal("list must not be empty if we are inserting data");
1227 element_list
*t
= new element_list(in
, ptr
->lineno
, ptr
->minv
, ptr
->minh
, ptr
->maxv
, ptr
->maxh
);
1230 ptr
->right
->left
= t
;
1231 t
->right
= ptr
->right
;
1238 * move_to - moves the current position to the point where data, in, exists.
1239 * This is an expensive method and should be used sparingly.
1242 void list::move_to (text_glob
*in
)
1245 while (ptr
!= tail
&& ptr
->datum
!= in
)
1250 * page class and methods
1256 void add (style
*s
, const string
&str
,
1258 int min_vertical
, int min_horizontal
,
1259 int max_vertical
, int max_horizontal
);
1260 void add_tag (style
*s
, const string
&str
,
1262 int min_vertical
, int min_horizontal
,
1263 int max_vertical
, int max_horizontal
);
1264 void add_and_encode (style
*s
, const string
&str
,
1266 int min_vertical
, int min_horizontal
,
1267 int max_vertical
, int max_horizontal
,
1269 void add_line (style
*s
,
1271 int x1
, int y1
, int x2
, int y2
,
1273 void insert_tag (const string
&str
);
1274 void dump_page (void); // debugging method
1278 list glyphs
; // position of glyphs and specials on page
1279 char_buffer buffer
; // all characters for this page
1287 * insert_tag - inserts a tag after the current position.
1290 void page::insert_tag (const string
&str
)
1292 if (str
.length() > 0) {
1293 text_glob
*g
=new text_glob();
1294 text_glob
*f
=glyphs
.get_data();
1295 g
->text_glob_tag(&f
->text_style
, buffer
.add_string(str
), str
.length(),
1296 f
->minv
, f
->minh
, f
->maxv
, f
->maxh
);
1302 * add - add html text to the list of glyphs.
1305 void page::add (style
*s
, const string
&str
,
1307 int min_vertical
, int min_horizontal
,
1308 int max_vertical
, int max_horizontal
)
1310 if (str
.length() > 0) {
1311 text_glob
*g
=new text_glob();
1312 g
->text_glob_html(s
, buffer
.add_string(str
), str
.length(),
1313 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1314 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1319 * add_tag - adds a troff tag, for example: .tl .sp .br
1322 void page::add_tag (style
*s
, const string
&str
,
1324 int min_vertical
, int min_horizontal
,
1325 int max_vertical
, int max_horizontal
)
1327 if (str
.length() > 0) {
1330 if (strncmp((str
+'\0').contents(), "devtag:.auto-image",
1331 strlen("devtag:.auto-image")) == 0) {
1332 g
= new text_glob();
1333 g
->text_glob_auto_image(s
, buffer
.add_string(str
), str
.length(),
1334 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1336 g
= new text_glob();
1337 g
->text_glob_tag(s
, buffer
.add_string(str
), str
.length(),
1338 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1340 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1345 * add_line - adds the <line> primitive providing that y1==y2
1348 void page::add_line (style
*s
,
1350 int x_1
, int y_1
, int x_2
, int y_2
,
1354 text_glob
*g
= new text_glob();
1355 g
->text_glob_line(s
,
1356 min(y_1
, y_2
), min(x_1
, x_2
),
1357 max(y_1
, y_2
), max(x_1
, x_2
),
1359 glyphs
.add(g
, line_number
,
1360 min(y_1
, y_2
), min(x_1
, x_2
),
1361 max(y_1
, y_2
), max(x_1
, x_2
));
1366 * to_unicode - returns a unicode translation of int, ch.
1369 static char *to_unicode (unsigned int ch
)
1371 static char buf
[30];
1373 sprintf(buf
, "&#%u;", ch
);
1378 * add_and_encode - adds a special string to the page, it translates the string
1379 * into html glyphs. The special string will have come from x X html:
1380 * and can contain troff character encodings which appear as
1381 * \(char\). A sequence of \\ represents \.
1382 * So for example we can write:
1383 * "cost = \(Po\)3.00 file = \\foo\\bar"
1384 * which is translated into:
1385 * "cost = £3.00 file = \foo\bar"
1388 void page::add_and_encode (style
*s
, const string
&str
,
1390 int min_vertical
, int min_horizontal
,
1391 int max_vertical
, int max_horizontal
,
1400 while (i
< str
.length()) {
1401 if ((i
+1<str
.length()) && (str
.substring(i
, 2) == string("\\("))) {
1403 i
+= 2; // move over \(
1405 while ((i
+1<str
.length()) && (str
.substring(i
, 2) != string("\\)"))) {
1409 if ((i
+1<str
.length()) && (str
.substring(i
, 2) == string("\\)")))
1414 string troff_charname
= str
.substring(a
, n
-a
);
1415 html_glyph
= get_html_translation(s
->f
, troff_charname
);
1417 html_string
+= html_glyph
;
1419 int idx
=s
->f
->name_to_index((troff_charname
+ '\0').contents());
1421 if (s
->f
->contains(idx
) && (idx
!= 0))
1422 html_string
+= s
->f
->get_code(idx
);
1426 html_string
+= str
[i
];
1429 if (html_string
.length() > 0) {
1430 text_glob
*g
=new text_glob();
1432 g
->text_glob_tag(s
, buffer
.add_string(html_string
),
1433 html_string
.length(),
1434 min_vertical
, min_horizontal
,
1435 max_vertical
, max_horizontal
);
1437 g
->text_glob_special(s
, buffer
.add_string(html_string
),
1438 html_string
.length(),
1439 min_vertical
, min_horizontal
,
1440 max_vertical
, max_horizontal
);
1441 glyphs
.add(g
, line_number
, min_vertical
,
1442 min_horizontal
, max_vertical
, max_horizontal
);
1447 * dump_page - dump the page contents for debugging purposes.
1450 void page::dump_page(void)
1452 #if defined(DEBUG_TABLES)
1453 text_glob
*old_pos
= glyphs
.get_data();
1457 printf("\n\ndebugging start\n");
1458 glyphs
.start_from_head();
1460 g
= glyphs
.get_data();
1461 if (g
->is_tab_ts()) {
1463 if (g
->get_table() != NULL
)
1464 g
->get_table()->dump_table();
1466 printf("%s ", g
->text_string
);
1469 glyphs
.move_right();
1470 } while (! glyphs
.is_equal_to_head());
1471 glyphs
.move_to(old_pos
);
1472 printf("\ndebugging end\n\n");
1479 * font classes and methods
1482 class html_font
: public font
{
1483 html_font(const char *);
1487 char *reencoded_name
;
1489 static html_font
*load_html_font(const char *);
1492 html_font
*html_font::load_html_font(const char *s
)
1494 html_font
*f
= new html_font(s
);
1502 html_font::html_font(const char *nm
)
1507 html_font::~html_font()
1512 * a simple class to contain the header to this document
1520 int has_been_written
;
1527 title_desc::title_desc ()
1528 : has_been_written(FALSE
), has_been_found(FALSE
), with_h1(FALSE
)
1532 title_desc::~title_desc ()
1541 int no_of_level_one_headings
; // how many .SH or .NH 1 have we found?
1542 int no_of_headings
; // how many headings have we found?
1543 char_buffer headings
; // all the headings used in the document
1544 list headers
; // list of headers built from .NH and .SH
1545 list header_filename
; // in which file is this header?
1546 int header_level
; // current header level
1547 int written_header
; // have we written the header yet?
1548 string header_buffer
; // current header text
1550 void write_headings (FILE *f
, int force
);
1553 header_desc::header_desc ()
1554 : no_of_level_one_headings(0), no_of_headings(0),
1555 header_level(2), written_header(0)
1559 header_desc::~header_desc ()
1564 * write_headings - emits a list of links for the headings in this document
1567 void header_desc::write_headings (FILE *f
, int force
)
1571 if (auto_links
|| force
) {
1572 if (! headers
.is_empty()) {
1575 headers
.start_from_head();
1576 header_filename
.start_from_head();
1578 g
= headers
.get_data();
1579 fputs("<a href=\"", f
);
1580 if (multiple_files
&& (! header_filename
.is_empty())) {
1581 text_glob
*fn
= header_filename
.get_data();
1582 fputs(fn
->text_string
, f
);
1585 if (simple_anchors
) {
1586 string
buffer(ANCHOR_TEMPLATE
);
1588 buffer
+= as_string(h
);
1590 fprintf(f
, buffer
.contents());
1592 fputs(g
->text_string
, f
);
1595 fputs(g
->text_string
, f
);
1596 fputs("</a><br>\n", f
);
1597 headers
.move_right();
1598 if (multiple_files
&& (! header_filename
.is_empty()))
1599 header_filename
.move_right();
1600 } while (! headers
.is_equal_to_head());
1612 class assert_state
{
1617 void addx (const char *c
, const char *i
, const char *v
,
1618 const char *f
, const char *l
);
1619 void addy (const char *c
, const char *i
, const char *v
,
1620 const char *f
, const char *l
);
1621 void build(const char *c
, const char *v
,
1622 const char *f
, const char *l
);
1623 void check_br (int br
);
1624 void check_ce (int ce
);
1625 void check_fi (int fi
);
1626 void check_sp (int sp
);
1638 const char *file_br
;
1639 const char *file_ce
;
1640 const char *file_fi
;
1641 const char *file_sp
;
1642 const char *line_br
;
1643 const char *line_ce
;
1644 const char *line_fi
;
1645 const char *line_sp
;
1650 void add (assert_pos
**h
,
1651 const char *c
, const char *i
, const char *v
,
1652 const char *f
, const char *l
);
1653 void compare(assert_pos
*t
,
1654 const char *v
, const char *f
, const char *l
);
1655 void close (const char *c
);
1656 void set (const char *c
, const char *v
,
1657 const char *f
, const char *l
);
1658 void check_value (const char *s
, int v
, const char *name
,
1659 const char *f
, const char *l
, int *flag
);
1660 int check_value_error (int c
, int v
, const char *s
,
1662 const char *f
, const char *l
, int flag
);
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) {
1776 fprintf(stderr
, "%s:%s: grohtml assertion failed at id%s expecting %s and was given %s\n",
1781 void assert_state::close (const char *c
)
1783 if (strcmp(c
, "sp") == 0)
1785 else if (strcmp(c
, "br") == 0)
1787 else if (strcmp(c
, "fi") == 0)
1789 else if (strcmp(c
, "nf") == 0)
1791 else if (strcmp(c
, "ce") == 0)
1794 fprintf(stderr
, "internal error: unrecognised tag in grohtml (%s)\n", c
);
1797 const char *replace_negate_str (const char *before
, char *after
)
1800 a_delete (char *)before
;
1802 if (strlen(after
) > 0) {
1803 int d
= atoi(after
);
1805 if (d
< 0 || d
> 1) {
1806 fprintf(stderr
, "expecting nf/fi value to be 0 or 1 not %d\n", d
);
1818 const char *replace_str (const char *before
, const char *after
)
1821 a_delete (char *)before
;
1825 void assert_state::set (const char *c
, const char *v
,
1826 const char *f
, const char *l
)
1833 // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1834 if (strcmp(c
, "sp") == 0) {
1836 val_sp
= replace_str(val_sp
, strsave(v
));
1837 file_sp
= replace_str(file_sp
, strsave(f
));
1838 line_sp
= replace_str(line_sp
, strsave(l
));
1839 } else if (strcmp(c
, "br") == 0) {
1841 val_br
= replace_str(val_br
, strsave(v
));
1842 file_br
= replace_str(file_br
, strsave(f
));
1843 line_br
= replace_str(line_br
, strsave(l
));
1844 } else if (strcmp(c
, "fi") == 0) {
1846 val_fi
= replace_str(val_fi
, strsave(v
));
1847 file_fi
= replace_str(file_fi
, strsave(f
));
1848 line_fi
= replace_str(line_fi
, strsave(l
));
1849 } else if (strcmp(c
, "nf") == 0) {
1851 val_fi
= replace_negate_str(val_fi
, strsave(v
));
1852 file_fi
= replace_str(file_fi
, strsave(f
));
1853 line_fi
= replace_str(line_fi
, strsave(l
));
1854 } else if (strcmp(c
, "ce") == 0) {
1856 val_ce
= replace_str(val_ce
, strsave(v
));
1857 file_ce
= replace_str(file_ce
, strsave(f
));
1858 line_ce
= replace_str(line_ce
, strsave(l
));
1863 * build - builds the troff state assertion.
1864 * see tmac/www.tmac for cmd examples.
1867 void assert_state::build (const char *c
, const char *v
,
1868 const char *f
, const char *l
)
1871 set(&c
[1], v
, f
, l
);
1876 int assert_state::check_value_error (int c
, int v
, const char *s
,
1878 const char *f
, const char *l
, int flag
)
1885 fprintf(stderr
, "%s:%s:grohtml (troff state) assertion failed, expected %s to be %s but found it to contain %d\n",
1892 void assert_state::check_value (const char *s
, int v
, const char *name
,
1893 const char *f
, const char *l
, int *flag
)
1895 if (strncmp(s
, "<=", 2) == 0)
1896 *flag
= check_value_error(v
<= atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1897 else if (strncmp(s
, ">=", 2) == 0)
1898 *flag
= check_value_error(v
>= atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1899 else if (strncmp(s
, "==", 2) == 0)
1900 *flag
= check_value_error(v
== atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1901 else if (strncmp(s
, "!=", 2) == 0)
1902 *flag
= check_value_error(v
!= atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1903 else if (strncmp(s
, "<", 1) == 0)
1904 *flag
= check_value_error(v
< atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1905 else if (strncmp(s
, ">", 1) == 0)
1906 *flag
= check_value_error(v
> atoi(&s
[2]), v
, s
, name
, f
, l
, *flag
);
1907 else if (strncmp(s
, "=", 1) == 0)
1908 *flag
= check_value_error(v
== atoi(&s
[1]), v
, s
, name
, f
, l
, *flag
);
1910 *flag
= check_value_error(v
== atoi(s
), v
, s
, name
, f
, l
, *flag
);
1913 void assert_state::check_sp (int sp
)
1916 check_value(val_sp
, sp
, "sp", file_sp
, line_sp
, &check_sp_flag
);
1919 void assert_state::check_fi (int fi
)
1922 check_value(val_fi
, fi
, "fi", file_fi
, line_fi
, &check_fi_flag
);
1925 void assert_state::check_br (int br
)
1928 check_value(val_br
, br
, "br", file_br
, line_br
, &check_br_flag
);
1931 void assert_state::check_ce (int ce
)
1934 check_value(val_ce
, ce
, "ce", file_ce
, line_ce
, &check_ce_flag
);
1937 class html_printer
: public printer
{
1941 int space_char_index
;
1943 int no_of_printed_pages
;
1946 int sbuf_start_hpos
;
1952 int last_sbuf_length
;
1953 int overstrike_detected
;
1957 int output_vpos_max
;
1958 int output_draw_point_size
;
1960 int output_line_thickness
;
1961 unsigned char output_space_code
;
1962 char *inside_font_style
;
1967 int supress_sub_sup
;
1969 page
*page_contents
;
1970 html_text
*current_paragraph
;
1971 html_indent
*indent
;
1975 TAG_ALIGNMENT next_tag
;
1984 int vertical_spacing
;
1989 int seen_pageoffset
;
1990 int next_pageoffset
;
1991 int seen_linelength
;
1992 int next_linelength
;
2002 void set_style (const style
&);
2003 void set_space_code (unsigned char c
);
2004 void do_exec (char *, const environment
*);
2005 void do_import (char *, const environment
*);
2006 void do_def (char *, const environment
*);
2007 void do_mdef (char *, const environment
*);
2008 void do_file (char *, const environment
*);
2009 void set_line_thickness (const environment
*);
2010 void terminate_current_font (void);
2011 void flush_font (void);
2012 void add_to_sbuf (int index
, const string
&s
);
2013 void write_title (int in_head
);
2014 int sbuf_continuation (int index
, const char *name
, const environment
*env
, int w
);
2015 void flush_page (void);
2016 void troff_tag (text_glob
*g
);
2017 void flush_globs (void);
2018 void emit_line (text_glob
*g
);
2019 void emit_raw (text_glob
*g
);
2020 void emit_html (text_glob
*g
);
2021 void determine_space (text_glob
*g
);
2022 void start_font (const char *name
);
2023 void end_font (const char *name
);
2024 int is_font_courier (font
*f
);
2025 int is_line_start (int nf
);
2026 int is_courier_until_eol (void);
2027 void start_size (int from
, int to
);
2028 void do_font (text_glob
*g
);
2029 void do_center (char *arg
);
2030 void do_check_center (void);
2031 void do_break (void);
2032 void do_space (char *arg
);
2034 void do_eol_ce (void);
2035 void do_title (void);
2036 void do_fill (char *arg
);
2037 void do_heading (char *arg
);
2038 void write_header (void);
2039 void determine_header_level (int level
);
2040 void do_linelength (char *arg
);
2041 void do_pageoffset (char *arg
);
2042 void do_indentation (char *arg
);
2043 void do_tempindent (char *arg
);
2044 void do_indentedparagraph (void);
2045 void do_verticalspacing (char *arg
);
2046 void do_pointsize (char *arg
);
2047 void do_centered_image (void);
2048 void do_left_image (void);
2049 void do_right_image (void);
2050 void do_auto_image (text_glob
*g
, const char *filename
);
2051 void do_links (void);
2052 void do_flush (void);
2053 void do_job_name (char *name
);
2054 void do_head (char *name
);
2055 void insert_split_file (void);
2056 int is_in_middle (int left
, int right
);
2057 void do_sup_or_sub (text_glob
*g
);
2058 int start_subscript (text_glob
*g
);
2059 int end_subscript (text_glob
*g
);
2060 int start_superscript (text_glob
*g
);
2061 int end_superscript (text_glob
*g
);
2062 void outstanding_eol (int n
);
2063 int is_bold (font
*f
);
2064 font
*make_bold (font
*f
);
2065 int overstrike (int index
, const char *name
, const environment
*env
, int w
);
2066 void do_body (void);
2067 int next_horiz_pos (text_glob
*g
, int nf
);
2068 void lookahead_for_tables (void);
2069 void insert_tab_te (void);
2070 text_glob
*insert_tab_ts (text_glob
*where
);
2071 void insert_tab0_foreach_tab (void);
2072 void insert_tab_0 (text_glob
*where
);
2073 void do_indent (int in
, int pageoff
, int linelen
);
2074 void shutdown_table (void);
2075 void do_tab_ts (text_glob
*g
);
2076 void do_tab_te (void);
2077 void do_col (char *s
);
2078 void do_tab (char *s
);
2079 void do_tab0 (void);
2080 int calc_nf (text_glob
*g
, int nf
);
2081 void calc_po_in (text_glob
*g
, int nf
);
2082 void remove_tabs (void);
2083 void remove_courier_tabs (void);
2084 void update_min_max (colType type_of_col
, int *minimum
, int *maximum
, text_glob
*g
);
2085 void add_table_end (const char *);
2086 void do_file_components (void);
2087 void write_navigation (const string
&top
, const string
&prev
,
2088 const string
&next
, const string
¤t
);
2089 void emit_link (const string
&to
, const char *name
);
2090 int get_troff_indent (void);
2091 void restore_troff_indent (void);
2092 void handle_assertion (int minv
, int minh
, int maxv
, int maxh
, const char *s
);
2093 void handle_state_assertion (text_glob
*g
);
2094 void do_end_para (text_glob
*g
);
2095 int round_width (int x
);
2096 void handle_tag_within_title (text_glob
*g
);
2097 void writeHeadMetaStyle (void);
2103 void set_char (int i
, font
*f
, const environment
*env
, int w
, const char *name
);
2104 void set_numbered_char(int num
, const environment
*env
, int *widthp
);
2105 int set_char_and_width(const char *nm
, const environment
*env
,
2106 int *widthp
, font
**f
);
2107 void draw (int code
, int *p
, int np
, const environment
*env
);
2108 void begin_page (int);
2109 void end_page (int);
2110 void special (char *arg
, const environment
*env
, char type
);
2111 void devtag (char *arg
, const environment
*env
, char type
);
2112 font
*make_font (const char *);
2113 void end_of_line ();
2116 printer
*make_printer()
2118 return new html_printer
;
2121 static void usage(FILE *stream
);
2123 void html_printer::set_style(const style
&sty
)
2125 const char *fontname
= sty
.f
->get_name();
2126 if (fontname
== NULL
)
2127 fatal("no internalname specified for font");
2130 change_font(fontname
, (font::res
/(72*font::sizescale
))*sty
.point_size
);
2135 * is_bold - returns TRUE if font, f, is bold.
2138 int html_printer::is_bold (font
*f
)
2140 const char *fontname
= f
->get_name();
2141 return (strcmp(fontname
, "B") == 0) || (strcmp(fontname
, "BI") == 0);
2145 * make_bold - if a bold font of, f, exists then return it.
2148 font
*html_printer::make_bold (font
*f
)
2150 const char *fontname
= f
->get_name();
2152 if (strcmp(fontname
, "B") == 0)
2154 if (strcmp(fontname
, "I") == 0)
2155 return font::load_font("BI");
2156 if (strcmp(fontname
, "BI") == 0)
2161 void html_printer::end_of_line()
2168 * emit_line - writes out a horizontal rule.
2171 void html_printer::emit_line (text_glob
*)
2173 // --fixme-- needs to know the length in percentage
2174 html
.put_string("<hr>");
2178 * restore_troff_indent - is called when we have temporarily shutdown
2179 * indentation (typically done when we have
2180 * centered an image).
2183 void html_printer::restore_troff_indent (void)
2185 troff_indent
= next_indent
;
2186 if (troff_indent
> 0) {
2188 * force device indentation
2191 do_indent(get_troff_indent(), pageoffset
, linelength
);
2196 * emit_raw - writes the raw html information directly to the device.
2199 void html_printer::emit_raw (text_glob
*g
)
2202 if (next_tag
== INLINE
) {
2204 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
2206 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2208 current_paragraph
->done_para();
2213 current_paragraph
->do_para("align=center", space
);
2216 current_paragraph
->do_para(&html
, "align=left", get_troff_indent(), pageoffset
, linelength
, space
);
2219 current_paragraph
->do_para(&html
, "align=right", get_troff_indent(), pageoffset
, linelength
, space
);
2222 fatal("unknown enumeration");
2224 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
2225 current_paragraph
->done_para();
2227 supress_sub_sup
= TRUE
;
2229 restore_troff_indent();
2234 * handle_tag_within_title - handle a limited number of tags within
2235 * the context of a table. Those tags which
2236 * set values rather than generate spaces
2240 void html_printer::handle_tag_within_title (text_glob
*g
)
2242 if (g
->is_in() || g
->is_ti() || g
->is_po() || g
->is_ce() || g
->is_ll()
2243 || g
->is_fi() || g
->is_nf())
2248 * do_center - handle the .ce commands from troff.
2251 void html_printer::do_center (char *arg
)
2253 next_center
= atoi(arg
);
2258 * do_centered_image - set a flag such that the next devtag is
2259 * placed inside a centered paragraph.
2262 void html_printer::do_centered_image (void)
2264 next_tag
= CENTERED
;
2268 * do_right_image - set a flag such that the next devtag is
2269 * placed inside a right aligned paragraph.
2272 void html_printer::do_right_image (void)
2278 * do_left_image - set a flag such that the next devtag is
2279 * placed inside a left aligned paragraph.
2282 void html_printer::do_left_image (void)
2288 * exists - returns TRUE if filename exists.
2291 static int exists (const char *filename
)
2293 FILE *fp
= fopen(filename
, "r");
2304 * generate_img_src - returns a html image tag for the filename
2305 * providing that the image exists.
2308 static string
&generate_img_src (const char *filename
)
2310 string
*s
= new string("");
2312 while (filename
&& (filename
[0] == ' ')) {
2315 if (exists(filename
))
2316 *s
+= string("<img src=\"") + filename
+ "\" "
2317 + "alt=\"Image " + filename
+ "\">";
2322 * do_auto_image - tests whether the image, indicated by filename,
2323 * is present, if so then it emits an html image tag.
2324 * An image tag may be passed through from pic, eqn
2325 * but the corresponding image might not be created.
2326 * Consider .EQ delim $$ .EN or an empty .PS .PE.
2329 void html_printer::do_auto_image (text_glob
*g
, const char *filename
)
2331 string buffer
= generate_img_src(filename
);
2333 if (! buffer
.empty()) {
2335 * utilize emit_raw by creating a new text_glob.
2339 h
.text_string
= buffer
.contents();
2340 h
.text_length
= buffer
.length();
2347 * outstanding_eol - call do_eol, n, times.
2350 void html_printer::outstanding_eol (int n
)
2359 * do_title - handle the .tl commands from troff.
2362 void html_printer::do_title (void)
2365 int removed_from_head
;
2367 if (page_number
== 1) {
2368 int found_title_start
= FALSE
;
2369 if (! page_contents
->glyphs
.is_empty()) {
2370 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2372 t
= page_contents
->glyphs
.get_data();
2373 removed_from_head
= FALSE
;
2374 if (t
->is_auto_img()) {
2375 string img
= generate_img_src((char *)(t
->text_string
+ 20));
2377 if (! img
.empty()) {
2378 if (found_title_start
)
2380 found_title_start
= TRUE
;
2381 title
.has_been_found
= TRUE
;
2384 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2385 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2386 (page_contents
->glyphs
.is_equal_to_head()));
2387 } else if (t
->is_eo_tl()) {
2388 /* end of title found
2390 title
.has_been_found
= TRUE
;
2392 } else if (t
->is_a_tag()) {
2393 handle_tag_within_title(t
);
2394 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2395 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2396 (page_contents
->glyphs
.is_equal_to_head()));
2397 } else if (found_title_start
) {
2398 title
.text
+= " " + string(t
->text_string
, t
->text_length
);
2399 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2400 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2401 (page_contents
->glyphs
.is_equal_to_head()));
2403 title
.text
+= string(t
->text_string
, t
->text_length
);
2404 found_title_start
= TRUE
;
2405 title
.has_been_found
= TRUE
;
2406 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
2407 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
2408 (page_contents
->glyphs
.is_equal_to_head()));
2410 } while ((! page_contents
->glyphs
.is_equal_to_head()) ||
2411 (removed_from_head
));
2416 void html_printer::write_header (void)
2418 if (! header
.header_buffer
.empty()) {
2419 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2421 if (header
.header_level
> 7) {
2422 header
.header_level
= 7;
2425 // firstly we must terminate any font and type faces
2426 current_paragraph
->done_para();
2427 supress_sub_sup
= TRUE
;
2429 if (cutoff_heading
+2 > header
.header_level
) {
2430 // now we save the header so we can issue a list of links
2431 header
.no_of_headings
++;
2434 text_glob
*h
=new text_glob();
2435 h
->text_glob_html(&st
,
2436 header
.headings
.add_string(header
.header_buffer
),
2437 header
.header_buffer
.length(),
2438 header
.no_of_headings
, header
.header_level
,
2439 header
.no_of_headings
, header
.header_level
);
2441 header
.headers
.add(h
,
2442 header
.no_of_headings
,
2443 header
.no_of_headings
, header
.no_of_headings
,
2444 header
.no_of_headings
, header
.no_of_headings
); // and add this header to the header list
2446 // lastly we generate a tag
2448 html
.nl().nl().put_string("<a name=\"");
2449 if (simple_anchors
) {
2450 string
buffer(ANCHOR_TEMPLATE
);
2452 buffer
+= as_string(header
.no_of_headings
);
2454 html
.put_string(buffer
.contents());
2456 html
.put_string(header
.header_buffer
);
2458 html
.put_string("\"></a>").nl();
2461 if (manufacture_headings
) {
2462 // line break before a header
2463 if (!current_paragraph
->emitted_text())
2464 current_paragraph
->do_space();
2465 // user wants manufactured headings which look better than <Hn></Hn>
2466 if (header
.header_level
<4) {
2467 html
.put_string("<b><font size=\"+1\">");
2468 html
.put_string(header
.header_buffer
);
2469 html
.put_string("</font></b>").nl();
2472 html
.put_string("<b>");
2473 html
.put_string(header
.header_buffer
);
2474 html
.put_string("</b>").nl();
2478 // and now we issue the real header
2479 html
.put_string("<h");
2480 html
.put_number(header
.header_level
);
2481 html
.put_string(">");
2482 html
.put_string(header
.header_buffer
);
2483 html
.put_string("</h");
2484 html
.put_number(header
.header_level
);
2485 html
.put_string(">").nl();
2488 /* and now we save the file name in which this header will occur */
2490 style st
; // fake style to enable us to use the list data structure
2492 text_glob
*h
=new text_glob();
2493 h
->text_glob_html(&st
,
2494 header
.headings
.add_string(file_list
.file_name()),
2495 file_list
.file_name().length(),
2496 header
.no_of_headings
, header
.header_level
,
2497 header
.no_of_headings
, header
.header_level
);
2499 header
.header_filename
.add(h
,
2500 header
.no_of_headings
,
2501 header
.no_of_headings
, header
.no_of_headings
,
2502 header
.no_of_headings
, header
.no_of_headings
);
2504 current_paragraph
->do_para(&html
, "", get_troff_indent(), pageoffset
, linelength
, space
);
2508 void html_printer::determine_header_level (int level
)
2513 for (i
=0; ((i
<header
.header_buffer
.length())
2514 && ((header
.header_buffer
[i
] == '.')
2515 || is_digit(header
.header_buffer
[i
]))) ; i
++) {
2516 if (header
.header_buffer
[i
] == '.') {
2521 header
.header_level
= level
+1;
2522 if (header
.header_level
>= 2 && header
.header_level
<= split_level
) {
2523 header
.no_of_level_one_headings
++;
2524 insert_split_file();
2529 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2532 void html_printer::do_heading (char *arg
)
2535 int level
=atoi(arg
);
2538 header
.header_buffer
.clear();
2539 page_contents
->glyphs
.move_right();
2540 if (! page_contents
->glyphs
.is_equal_to_head()) {
2541 g
= page_contents
->glyphs
.get_data();
2544 if (g
->is_auto_img()) {
2545 string img
=generate_img_src((char *)(g
->text_string
+ 20));
2547 if (! img
.empty()) {
2548 simple_anchors
= TRUE
; // we cannot use full heading anchors with images
2549 if (horiz
< g
->minh
)
2550 header
.header_buffer
+= " ";
2552 header
.header_buffer
+= img
;
2555 else if (g
->is_in() || g
->is_ti() || g
->is_po() || g
->is_ce() || g
->is_ll())
2557 else if (g
->is_fi())
2559 else if (g
->is_nf())
2561 else if (! (g
->is_a_line() || g
->is_a_tag())) {
2563 * we ignore the other tag commands when constructing a heading
2565 if (horiz
< g
->minh
)
2566 header
.header_buffer
+= " ";
2569 header
.header_buffer
+= string(g
->text_string
, g
->text_length
);
2571 page_contents
->glyphs
.move_right();
2572 g
= page_contents
->glyphs
.get_data();
2573 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
2577 determine_header_level(level
);
2580 // finally set the output to neutral for after the header
2581 g
= page_contents
->glyphs
.get_data();
2582 page_contents
->glyphs
.move_left(); // so that next time we use old g
2586 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
2589 int html_printer::is_courier_until_eol (void)
2591 text_glob
*orig
= page_contents
->glyphs
.get_data();
2595 if (! page_contents
->glyphs
.is_equal_to_tail()) {
2596 page_contents
->glyphs
.move_right();
2598 g
= page_contents
->glyphs
.get_data();
2599 if (! g
->is_a_tag() && (! is_font_courier(g
->text_style
.f
)))
2601 page_contents
->glyphs
.move_right();
2603 (! page_contents
->glyphs
.is_equal_to_head()) &&
2604 (! g
->is_fi()) && (! g
->is_eol()));
2607 * now restore our previous position.
2609 while (page_contents
->glyphs
.get_data() != orig
)
2610 page_contents
->glyphs
.move_left();
2616 * do_linelength - handle the .ll command from troff.
2619 void html_printer::do_linelength (char *arg
)
2621 if (max_linelength
== -1)
2622 max_linelength
= atoi(arg
);
2624 next_linelength
= atoi(arg
);
2625 seen_linelength
= TRUE
;
2629 * do_pageoffset - handle the .po command from troff.
2632 void html_printer::do_pageoffset (char *arg
)
2634 next_pageoffset
= atoi(arg
);
2635 seen_pageoffset
= TRUE
;
2639 * get_troff_indent - returns the indent value.
2642 int html_printer::get_troff_indent (void)
2644 if (end_tempindent
> 0)
2647 return troff_indent
;
2651 * do_indentation - handle the .in command from troff.
2654 void html_printer::do_indentation (char *arg
)
2656 next_indent
= atoi(arg
);
2661 * do_tempindent - handle the .ti command from troff.
2664 void html_printer::do_tempindent (char *arg
)
2668 * we set the end_tempindent to 2 as the first .br
2669 * activates the .ti and the second terminates it.
2672 temp_indent
= atoi(arg
);
2677 * shutdown_table - shuts down the current table.
2680 void html_printer::shutdown_table (void)
2682 if (table
!= NULL
) {
2683 current_paragraph
->done_para();
2684 table
->emit_finish_table();
2685 // dont delete this table as it will be deleted when we destroy the text_glob
2691 * do_indent - remember the indent parameters and if
2692 * indent is > pageoff and indent has changed
2693 * then we start a html table to implement the indentation.
2696 void html_printer::do_indent (int in
, int pageoff
, int linelen
)
2698 if ((device_indent
!= -1) &&
2699 (pageoffset
+device_indent
!= in
+pageoff
)) {
2701 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2702 current_paragraph
->done_para();
2705 pageoffset
= pageoff
;
2706 if (linelen
<= max_linelength
)
2707 linelength
= linelen
;
2709 current_paragraph
->do_para(&html
, "", device_indent
,
2710 pageoffset
, max_linelength
, space
);
2715 * do_verticalspacing - handle the .vs command from troff.
2718 void html_printer::do_verticalspacing (char *arg
)
2720 vertical_spacing
= atoi(arg
);
2724 * do_pointsize - handle the .ps command from troff.
2727 void html_printer::do_pointsize (char *arg
)
2730 * firstly check to see whether this point size is really associated with a .tl tag
2733 if (! page_contents
->glyphs
.is_empty()) {
2734 text_glob
*g
= page_contents
->glyphs
.get_data();
2735 text_glob
*t
= page_contents
->glyphs
.get_data();
2737 while (t
->is_a_tag() && (! page_contents
->glyphs
.is_equal_to_head())) {
2740 * found title therefore ignore this .ps tag
2743 page_contents
->glyphs
.move_left();
2744 t
= page_contents
->glyphs
.get_data();
2748 page_contents
->glyphs
.move_right();
2749 t
= page_contents
->glyphs
.get_data();
2752 * move back to original position
2755 page_contents
->glyphs
.move_left();
2756 t
= page_contents
->glyphs
.get_data();
2759 * collect legal pointsize
2761 pointsize
= atoi(arg
);
2766 * do_fill - records whether troff has requested that text be filled.
2769 void html_printer::do_fill (char *arg
)
2773 output_hpos
= get_troff_indent()+pageoffset
;
2774 supress_sub_sup
= TRUE
;
2776 if (fill_on
!= on
) {
2778 current_paragraph
->do_para("", seen_space
);
2784 * do_eol - handle the end of line
2787 void html_printer::do_eol (void)
2790 if (current_paragraph
->ever_emitted_text()) {
2791 current_paragraph
->do_newline();
2792 current_paragraph
->do_break();
2795 output_hpos
= get_troff_indent()+pageoffset
;
2799 * do_check_center - checks to see whether we have seen a `.ce' tag
2800 * during the previous line.
2803 void html_printer::do_check_center(void)
2806 seen_center
= FALSE
;
2807 if (next_center
> 0) {
2808 if (end_center
== 0) {
2809 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2810 current_paragraph
->done_para();
2811 supress_sub_sup
= TRUE
;
2812 current_paragraph
->do_para("align=center", space
);
2814 if (strcmp("align=center",
2815 current_paragraph
->get_alignment()) != 0) {
2817 * different alignment, so shutdown paragraph and open
2820 int space
= current_paragraph
->retrieve_para_space() || seen_space
;
2821 current_paragraph
->done_para();
2822 supress_sub_sup
= TRUE
;
2823 current_paragraph
->do_para("align=center", space
);
2826 * same alignment, if we have emitted text then issue a break.
2828 if (current_paragraph
->emitted_text())
2829 current_paragraph
->do_break();
2834 if (end_center
> 0) {
2835 seen_space
= seen_space
|| current_paragraph
->retrieve_para_space();
2836 current_paragraph
->done_para();
2837 supress_sub_sup
= TRUE
;
2838 current_paragraph
->do_para("", seen_space
);
2840 end_center
= next_center
;
2845 * do_eol_ce - handle end of line specifically for a .ce
2848 void html_printer::do_eol_ce (void)
2850 if (end_center
> 0) {
2852 if (current_paragraph
->emitted_text())
2853 current_paragraph
->do_break();
2856 if (end_center
== 0) {
2857 current_paragraph
->done_para();
2858 supress_sub_sup
= TRUE
;
2864 * do_flush - flushes all output and tags.
2867 void html_printer::do_flush (void)
2869 current_paragraph
->done_para();
2873 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
2876 void html_printer::do_links (void)
2878 html
.end_line(); // flush line
2879 auto_links
= FALSE
; /* from now on only emit under user request */
2880 file_list
.add_new_file(xtmpfile());
2881 file_list
.set_links_required();
2882 html
.set_file(file_list
.get_file());
2886 * insert_split_file -
2889 void html_printer::insert_split_file (void)
2891 if (multiple_files
) {
2892 current_paragraph
->done_para(); // flush paragraph
2893 html
.end_line(); // flush line
2894 html
.set_file(file_list
.get_file()); // flush current file
2895 file_list
.add_new_file(xtmpfile());
2896 string split_file
= job_name
;
2898 split_file
+= string("-");
2899 split_file
+= as_string(header
.no_of_level_one_headings
);
2900 split_file
+= string(".html");
2903 file_list
.set_file_name(split_file
);
2904 html
.set_file(file_list
.get_file());
2909 * do_job_name - assigns the job_name to name.
2912 void html_printer::do_job_name (char *name
)
2914 if (! multiple_files
) {
2915 multiple_files
= TRUE
;
2916 while (name
!= NULL
&& (*name
!= (char)0) && (*name
== ' '))
2923 * do_head - adds a string to head_info which is to be included into
2924 * the <head> </head> section of the html document.
2927 void html_printer::do_head (char *name
)
2929 head_info
+= string(name
);
2934 * do_break - handles the ".br" request and also
2935 * undoes an outstanding ".ti" command
2936 * and calls indent if the indentation
2937 * related registers have changed.
2940 void html_printer::do_break (void)
2942 int seen_temp_indent
= FALSE
;
2944 current_paragraph
->do_break();
2945 if (end_tempindent
> 0) {
2947 if (end_tempindent
> 0)
2948 seen_temp_indent
= TRUE
;
2950 if (seen_indent
|| seen_pageoffset
|| seen_linelength
|| seen_temp_indent
) {
2951 if (seen_indent
&& (! seen_temp_indent
))
2952 troff_indent
= next_indent
;
2953 if (! seen_pageoffset
)
2954 next_pageoffset
= pageoffset
;
2955 if (! seen_linelength
)
2956 next_linelength
= linelength
;
2957 do_indent(get_troff_indent(), next_pageoffset
, next_linelength
);
2959 seen_indent
= seen_temp_indent
;
2960 seen_linelength
= FALSE
;
2961 seen_pageoffset
= FALSE
;
2963 output_hpos
= get_troff_indent()+pageoffset
;
2964 supress_sub_sup
= TRUE
;
2967 void html_printer::do_space (char *arg
)
2971 seen_space
= atoi(arg
);
2972 as
.check_sp(seen_space
);
2975 table
->set_space(TRUE
);
2979 current_paragraph
->do_space();
2982 supress_sub_sup
= TRUE
;
2986 * do_tab_ts - start a table, which will have already been defined.
2989 void html_printer::do_tab_ts (text_glob
*g
)
2991 html_table
*t
= g
->get_table();
2995 current_paragraph
->done_pre();
2996 current_paragraph
->done_para();
2997 current_paragraph
->remove_para_space();
2999 #if defined(DEBUG_TABLES)
3000 html
.simple_comment("TABS");
3003 t
->set_linelength(max_linelength
);
3004 t
->add_indent(pageoffset
);
3006 t
->emit_table_header(seen_space
);
3008 t
->emit_table_header(FALSE
);
3009 row_space
= current_paragraph
->retrieve_para_space() || seen_space
;
3018 * do_tab_te - finish a table.
3021 void html_printer::do_tab_te (void)
3024 current_paragraph
->done_para();
3025 current_paragraph
->remove_para_space();
3026 table
->emit_finish_table();
3030 restore_troff_indent();
3034 * do_tab - handle the "devtag:tab" tag
3037 void html_printer::do_tab (char *s
)
3043 int col
= table
->find_column(atoi(s
) + pageoffset
+ get_troff_indent());
3045 current_paragraph
->done_para();
3046 table
->emit_col(col
);
3052 * do_tab0 - handle the "devtag:tab0" tag
3055 void html_printer::do_tab0 (void)
3058 int col
= table
->find_column(pageoffset
+get_troff_indent());
3060 current_paragraph
->done_para();
3061 table
->emit_col(col
);
3067 * do_col - start column, s.
3070 void html_printer::do_col (char *s
)
3073 if (atoi(s
) < current_column
)
3074 row_space
= seen_space
;
3076 current_column
= atoi(s
);
3077 current_paragraph
->done_para();
3078 table
->emit_col(current_column
);
3079 current_paragraph
->do_para("", row_space
);
3084 * troff_tag - processes the troff tag and manipulates the troff
3088 void html_printer::troff_tag (text_glob
*g
)
3091 * firstly skip over devtag:
3093 char *t
=(char *)g
->text_string
+strlen("devtag:");
3095 if (strncmp(g
->text_string
, "html</p>:", strlen("html</p>:")) == 0) {
3097 } else if (g
->is_eol()) {
3099 } else if (g
->is_eol_ce()) {
3101 } else if (strncmp(t
, ".sp", 3) == 0) {
3102 char *a
= (char *)t
+3;
3104 } else if (strncmp(t
, ".br", 3) == 0) {
3108 } else if (strcmp(t
, ".centered-image") == 0) {
3109 do_centered_image();
3110 } else if (strcmp(t
, ".right-image") == 0) {
3112 } else if (strcmp(t
, ".left-image") == 0) {
3114 } else if (strncmp(t
, ".auto-image", 11) == 0) {
3115 char *a
= (char *)t
+11;
3116 do_auto_image(g
, a
);
3117 } else if (strncmp(t
, ".ce", 3) == 0) {
3118 char *a
= (char *)t
+3;
3119 supress_sub_sup
= TRUE
;
3121 } else if (g
->is_tl()) {
3122 supress_sub_sup
= TRUE
;
3123 title
.with_h1
= TRUE
;
3125 } else if (strncmp(t
, ".html-tl", 8) == 0) {
3126 supress_sub_sup
= TRUE
;
3127 title
.with_h1
= FALSE
;
3129 } else if (strncmp(t
, ".fi", 3) == 0) {
3130 char *a
= (char *)t
+3;
3132 } else if ((strncmp(t
, ".SH", 3) == 0) || (strncmp(t
, ".NH", 3) == 0)) {
3133 char *a
= (char *)t
+3;
3135 } else if (strncmp(t
, ".ll", 3) == 0) {
3136 char *a
= (char *)t
+3;
3138 } else if (strncmp(t
, ".po", 3) == 0) {
3139 char *a
= (char *)t
+3;
3141 } else if (strncmp(t
, ".in", 3) == 0) {
3142 char *a
= (char *)t
+3;
3144 } else if (strncmp(t
, ".ti", 3) == 0) {
3145 char *a
= (char *)t
+3;
3147 } else if (strncmp(t
, ".vs", 3) == 0) {
3148 char *a
= (char *)t
+3;
3149 do_verticalspacing(a
);
3150 } else if (strncmp(t
, ".ps", 3) == 0) {
3151 char *a
= (char *)t
+3;
3153 } else if (strcmp(t
, ".links") == 0) {
3155 } else if (strncmp(t
, ".job-name", 9) == 0) {
3156 char *a
= (char *)t
+9;
3158 } else if (strncmp(t
, ".head", 5) == 0) {
3159 char *a
= (char *)t
+5;
3161 } else if (strcmp(t
, ".no-auto-rule") == 0) {
3163 } else if (strcmp(t
, ".tab-ts") == 0) {
3165 } else if (strcmp(t
, ".tab-te") == 0) {
3167 } else if (strncmp(t
, ".col ", 5) == 0) {
3168 char *a
= (char *)t
+4;
3170 } else if (strncmp(t
, "tab ", 4) == 0) {
3171 char *a
= (char *)t
+3;
3173 } else if (strncmp(t
, "tab0", 4) == 0) {
3179 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
3182 int html_printer::is_in_middle (int left
, int right
)
3184 return( abs(abs(left
-pageoffset
) - abs(pageoffset
+linelength
-right
))
3185 <= CENTER_TOLERANCE
);
3189 * flush_globs - runs through the text glob list and emits html.
3192 void html_printer::flush_globs (void)
3196 if (! page_contents
->glyphs
.is_empty()) {
3197 page_contents
->glyphs
.start_from_head();
3199 g
= page_contents
->glyphs
.get_data();
3201 fprintf(stderr
, "[%s:%d:%d:%d:%d]",
3202 g
->text_string
, g
->minv
, g
->minh
, g
->maxv
, g
->maxh
) ;
3206 handle_state_assertion(g
);
3208 if (strcmp(g
->text_string
, "XXXXXXX") == 0)
3213 else if (g
->is_a_line())
3216 as
.check_sp(seen_space
);
3217 as
.check_br(seen_break
);
3223 as
.check_fi(fill_on
);
3224 as
.check_ce(end_center
);
3226 * after processing the title (and removing it) the glyph list might be empty
3228 if (! page_contents
->glyphs
.is_empty()) {
3229 page_contents
->glyphs
.move_right();
3231 } while (! page_contents
->glyphs
.is_equal_to_head());
3236 * calc_nf - calculates the _no_ format flag, given the
3240 int html_printer::calc_nf (text_glob
*g
, int nf
)
3257 * calc_po_in - calculates the, in, po, registers
3260 void html_printer::calc_po_in (text_glob
*g
, int nf
)
3263 troff_indent
= g
->get_arg();
3264 else if (g
->is_po())
3265 pageoffset
= g
->get_arg();
3266 else if (g
->is_ti()) {
3267 temp_indent
= g
->get_arg();
3269 } else if (g
->is_br() || (nf
&& g
->is_eol())) {
3270 if (end_tempindent
> 0)
3276 * next_horiz_pos - returns the next horiz position.
3277 * -1 is returned if it doesn't exist.
3280 int html_printer::next_horiz_pos (text_glob
*g
, int nf
)
3284 if ((g
!= NULL
) && (g
->is_br() || (nf
&& g
->is_eol())))
3285 if (! page_contents
->glyphs
.is_empty()) {
3286 page_contents
->glyphs
.move_right_get_data();
3288 page_contents
->glyphs
.start_from_head();
3293 page_contents
->glyphs
.move_left();
3300 * insert_tab_ts - inserts a tab-ts before, where.
3303 text_glob
*html_printer::insert_tab_ts (text_glob
*where
)
3305 text_glob
*start_of_table
;
3306 text_glob
*old_pos
= page_contents
->glyphs
.get_data();
3308 page_contents
->glyphs
.move_to(where
);
3309 page_contents
->glyphs
.move_left();
3310 page_contents
->insert_tag(string("devtag:.tab-ts")); // tab table start
3311 page_contents
->glyphs
.move_right();
3312 start_of_table
= page_contents
->glyphs
.get_data();
3313 page_contents
->glyphs
.move_to(old_pos
);
3314 return start_of_table
;
3318 * insert_tab_te - inserts a tab-te before the current position
3319 * (it skips backwards over .sp/.br)
3322 void html_printer::insert_tab_te (void)
3324 text_glob
*g
= page_contents
->glyphs
.get_data();
3325 page_contents
->dump_page();
3327 while (page_contents
->glyphs
.get_data()->is_a_tag())
3328 page_contents
->glyphs
.move_left();
3330 page_contents
->insert_tag(string("devtag:.tab-te")); // tab table end
3331 while (g
!= page_contents
->glyphs
.get_data())
3332 page_contents
->glyphs
.move_right();
3333 page_contents
->dump_page();
3337 * insert_tab_0 - inserts a tab0 before, where.
3340 void html_printer::insert_tab_0 (text_glob
*where
)
3342 text_glob
*old_pos
= page_contents
->glyphs
.get_data();
3344 page_contents
->glyphs
.move_to(where
);
3345 page_contents
->glyphs
.move_left();
3346 page_contents
->insert_tag(string("devtag:tab0")); // tab0 start of line
3347 page_contents
->glyphs
.move_right();
3348 page_contents
->glyphs
.move_to(old_pos
);
3352 * remove_tabs - removes the tabs tags on this line.
3355 void html_printer::remove_tabs (void)
3357 text_glob
*orig
= page_contents
->glyphs
.get_data();
3360 if (! page_contents
->glyphs
.is_equal_to_tail()) {
3362 g
= page_contents
->glyphs
.get_data();
3364 page_contents
->glyphs
.sub_move_right();
3366 orig
= page_contents
->glyphs
.get_data();
3368 page_contents
->glyphs
.move_right();
3369 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
3373 * now restore our previous position.
3375 while (page_contents
->glyphs
.get_data() != orig
)
3376 page_contents
->glyphs
.move_left();
3380 void html_printer::remove_courier_tabs (void)
3383 int line_start
= TRUE
;
3386 if (! page_contents
->glyphs
.is_empty()) {
3387 page_contents
->glyphs
.start_from_head();
3391 g
= page_contents
->glyphs
.get_data();
3392 handle_state_assertion(g
);
3393 nf
= calc_nf(g
, nf
);
3396 if (line_start
&& nf
&& is_courier_until_eol()) {
3398 g
= page_contents
->glyphs
.get_data();
3402 // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
3403 line_start
= g
->is_br() || (nf
&& g
->is_eol());
3404 page_contents
->glyphs
.move_right();
3405 } while (! page_contents
->glyphs
.is_equal_to_head());
3409 void html_printer::insert_tab0_foreach_tab (void)
3411 text_glob
*start_of_line
= NULL
;
3412 text_glob
*g
= NULL
;
3413 int seen_tab
= FALSE
;
3414 int seen_col
= FALSE
;
3417 if (! page_contents
->glyphs
.is_empty()) {
3418 page_contents
->glyphs
.start_from_head();
3420 start_of_line
= page_contents
->glyphs
.get_data();
3422 g
= page_contents
->glyphs
.get_data();
3423 handle_state_assertion(g
);
3424 nf
= calc_nf(g
, nf
);
3432 if (g
->is_br() || (nf
&& g
->is_eol())) {
3434 page_contents
->glyphs
.move_right();
3435 g
= page_contents
->glyphs
.get_data();
3436 handle_state_assertion(g
);
3437 nf
= calc_nf(g
, nf
);
3438 if (page_contents
->glyphs
.is_equal_to_head()) {
3439 if (seen_tab
&& !seen_col
)
3440 insert_tab_0(start_of_line
);
3443 } while (g
->is_br() || (nf
&& g
->is_eol()) || g
->is_ta());
3444 // printf("\nstart_of_line is: %s\n", g->text_string);
3445 if (seen_tab
&& !seen_col
) {
3446 insert_tab_0(start_of_line
);
3447 page_contents
->glyphs
.move_to(g
);
3454 page_contents
->glyphs
.move_right();
3455 } while (! page_contents
->glyphs
.is_equal_to_head());
3456 if (seen_tab
&& !seen_col
)
3457 insert_tab_0(start_of_line
);
3463 * update_min_max - updates the extent of a column, given the left and right
3464 * extents of a glyph, g.
3467 void html_printer::update_min_max (colType type_of_col
, int *minimum
, int *maximum
, text_glob
*g
)
3469 switch (type_of_col
) {
3486 * add_table_end - moves left one glyph, adds a table end tag and adds a
3490 void html_printer::add_table_end (const char *
3491 #if defined(DEBUG_TABLES)
3496 page_contents
->glyphs
.move_left();
3498 #if defined(DEBUG_TABLES)
3499 page_contents
->insert_tag(string(debug_string
));
3504 * lookahead_for_tables - checks for .col tags and inserts table
3508 void html_printer::lookahead_for_tables (void)
3511 text_glob
*start_of_line
= NULL
;
3512 text_glob
*start_of_table
= NULL
;
3513 text_glob
*last
= NULL
;
3514 colType type_of_col
= none
;
3516 int found_col
= FALSE
;
3517 int seen_text
= FALSE
;
3519 int colmin
= 0; // pacify compiler
3520 int colmax
= 0; // pacify compiler
3521 html_table
*tbl
= new html_table(&html
, -1);
3522 const char *tab_defs
= NULL
;
3525 int old_pageoffset
= pageoffset
;
3527 remove_courier_tabs();
3528 page_contents
->dump_page();
3529 insert_tab0_foreach_tab();
3530 page_contents
->dump_page();
3531 if (! page_contents
->glyphs
.is_empty()) {
3532 page_contents
->glyphs
.start_from_head();
3534 g
= page_contents
->glyphs
.get_data();
3536 g
= page_contents
->glyphs
.move_right_get_data();
3537 handle_state_assertion(g
);
3538 if (page_contents
->glyphs
.is_equal_to_head()) {
3549 left
= next_horiz_pos(g
, nf
);
3556 #if defined(DEBUG_TABLES)
3557 fprintf(stderr
, " [") ;
3558 fprintf(stderr
, g
->text_string
) ;
3559 fprintf(stderr
, "] ") ;
3561 if (strcmp(g
->text_string
, "XXXXXXX") == 0)
3565 nf
= calc_nf(g
, nf
);
3568 if (type_of_col
== tab_tag
&& start_of_table
!= NULL
) {
3569 page_contents
->glyphs
.move_left();
3571 start_of_table
->remember_table(tbl
);
3572 tbl
= new html_table(&html
, -1);
3573 page_contents
->insert_tag(string("*** TAB -> COL ***"));
3574 if (tab_defs
!= NULL
)
3575 tbl
->tab_stops
->init(tab_defs
);
3576 start_of_table
= NULL
;
3579 type_of_col
= col_tag
;
3581 ncol
= g
->get_arg();
3585 } else if (g
->is_tab()) {
3586 type_of_col
= tab_tag
;
3587 colmin
= g
->get_tab_args(&align
);
3588 align
= 'L'; // for now as 'C' and 'R' are broken
3589 ncol
= tbl
->find_tab_column(colmin
);
3590 colmin
+= pageoffset
+ get_troff_indent();
3591 colmax
= tbl
->get_tab_pos(ncol
+1);
3593 colmax
+= pageoffset
+ get_troff_indent();
3594 } else if (g
->is_tab0()) {
3595 if (type_of_col
== col_tag
&& start_of_table
!= NULL
) {
3596 page_contents
->glyphs
.move_left();
3598 start_of_table
->remember_table(tbl
);
3599 tbl
= new html_table(&html
, -1);
3600 page_contents
->insert_tag(string("*** COL -> TAB ***"));
3601 start_of_table
= NULL
;
3604 if (tab_defs
!= NULL
)
3605 tbl
->tab_stops
->init(tab_defs
);
3607 type_of_col
= tab0_tag
;
3610 colmax
= tbl
->get_tab_pos(2) + pageoffset
+ get_troff_indent();
3611 } else if (! g
->is_a_tag())
3612 update_min_max(type_of_col
, &colmin
, &colmax
, g
);
3614 if ((! g
->is_a_tag()) || g
->is_tab())
3617 if ((g
->is_col() || g
->is_tab() || g
->is_tab0())
3618 && (start_of_line
!= NULL
) && (start_of_table
== NULL
)) {
3619 start_of_table
= insert_tab_ts(start_of_line
);
3620 start_of_line
= NULL
;
3622 } else if (g
->is_ce() && (start_of_table
!= NULL
)) {
3623 add_table_end("*** CE ***");
3624 start_of_table
->remember_table(tbl
);
3625 tbl
= new html_table(&html
, -1);
3626 start_of_table
= NULL
;
3628 } else if (g
->is_ta()) {
3629 tab_defs
= g
->text_string
;
3631 if (type_of_col
== col_tag
)
3632 tbl
->tab_stops
->check_init(tab_defs
);
3634 if (!tbl
->tab_stops
->compatible(tab_defs
)) {
3635 if (start_of_table
!= NULL
) {
3636 add_table_end("*** TABS ***");
3637 start_of_table
->remember_table(tbl
);
3638 tbl
= new html_table(&html
, -1);
3639 start_of_table
= NULL
;
3643 tbl
->tab_stops
->init(tab_defs
);
3647 if (((! g
->is_a_tag()) || g
->is_tab()) && (start_of_table
!= NULL
)) {
3648 // we are in a table and have a glyph
3649 if ((ncol
== 0) || (! tbl
->add_column(ncol
, colmin
, colmax
, align
))) {
3651 add_table_end("*** NCOL == 0 ***");
3653 add_table_end("*** CROSSED COLS ***");
3655 start_of_table
->remember_table(tbl
);
3656 tbl
= new html_table(&html
, -1);
3657 start_of_table
= NULL
;
3664 * move onto next glob, check whether we are starting a new line
3666 g
= page_contents
->glyphs
.move_right_get_data();
3667 handle_state_assertion(g
);
3671 page_contents
->glyphs
.start_from_head();
3676 } else if (g
->is_br() || (nf
&& g
->is_eol())) {
3678 g
= page_contents
->glyphs
.move_right_get_data();
3679 handle_state_assertion(g
);
3680 nf
= calc_nf(g
, nf
);
3681 } while ((g
!= NULL
) && (g
->is_br() || (nf
&& g
->is_eol())));
3685 left
= next_horiz_pos(g
, nf
);
3690 } while ((g
!= NULL
) && (! page_contents
->glyphs
.is_equal_to_head()));
3692 #if defined(DEBUG_TABLES)
3693 fprintf(stderr
, "finished scanning for tables\n");
3696 page_contents
->glyphs
.start_from_head();
3697 if (start_of_table
!= NULL
) {
3699 while (last
!= page_contents
->glyphs
.get_data())
3700 page_contents
->glyphs
.move_left();
3703 start_of_table
->remember_table(tbl
);
3705 page_contents
->insert_tag(string("*** LAST ***"));
3713 // and reset the registers
3714 pageoffset
= old_pageoffset
;
3720 void html_printer::flush_page (void)
3722 supress_sub_sup
= TRUE
;
3724 page_contents
->dump_page();
3725 lookahead_for_tables();
3726 page_contents
->dump_page();
3729 current_paragraph
->done_para();
3731 // move onto a new page
3732 delete page_contents
;
3733 #if defined(DEBUG_TABLES)
3734 fprintf(stderr
, "\n\n*** flushed page ***\n\n");
3736 html
.simple_comment("new page called");
3738 page_contents
= new page
;
3742 * determine_space - works out whether we need to write a space.
3743 * If last glyph is ajoining then no space emitted.
3746 void html_printer::determine_space (text_glob
*g
)
3748 if (current_paragraph
->is_in_pre()) {
3750 * .nf has been specified
3752 while (output_hpos
< g
->minh
) {
3753 output_hpos
+= space_width
;
3754 current_paragraph
->emit_space();
3757 if ((output_vpos
!= g
->minv
) || (output_hpos
< g
->minh
)) {
3758 current_paragraph
->emit_space();
3764 * is_line_start - returns TRUE if we are at the start of a line.
3767 int html_printer::is_line_start (int nf
)
3769 int line_start
= FALSE
;
3771 text_glob
*orig
= page_contents
->glyphs
.get_data();
3774 if (! page_contents
->glyphs
.is_equal_to_head()) {
3776 page_contents
->glyphs
.move_left();
3777 g
= page_contents
->glyphs
.get_data();
3778 result
= g
->is_a_tag();
3781 else if (g
->is_nf())
3783 line_start
= g
->is_col() || g
->is_br() || (nf
&& g
->is_eol());
3784 } while ((!line_start
) && (result
));
3786 * now restore our previous position.
3788 while (page_contents
->glyphs
.get_data() != orig
)
3789 page_contents
->glyphs
.move_right();
3795 * is_font_courier - returns TRUE if the font, f, is courier.
3798 int html_printer::is_font_courier (font
*f
)
3801 const char *fontname
= f
->get_name();
3803 return( (fontname
!= 0) && (fontname
[0] == 'C') );
3809 * end_font - shuts down the font corresponding to fontname.
3812 void html_printer::end_font (const char *fontname
)
3814 if (strcmp(fontname
, "B") == 0) {
3815 current_paragraph
->done_bold();
3816 } else if (strcmp(fontname
, "I") == 0) {
3817 current_paragraph
->done_italic();
3818 } else if (strcmp(fontname
, "BI") == 0) {
3819 current_paragraph
->done_bold();
3820 current_paragraph
->done_italic();
3821 } else if (strcmp(fontname
, "CR") == 0) {
3822 current_paragraph
->done_tt();
3823 } else if (strcmp(fontname
, "CI") == 0) {
3824 current_paragraph
->done_italic();
3825 current_paragraph
->done_tt();
3826 } else if (strcmp(fontname
, "CB") == 0) {
3827 current_paragraph
->done_bold();
3828 current_paragraph
->done_tt();
3829 } else if (strcmp(fontname
, "CBI") == 0) {
3830 current_paragraph
->done_bold();
3831 current_paragraph
->done_italic();
3832 current_paragraph
->done_tt();
3837 * start_font - starts the font corresponding to name.
3840 void html_printer::start_font (const char *fontname
)
3842 if (strcmp(fontname
, "R") == 0) {
3843 current_paragraph
->done_bold();
3844 current_paragraph
->done_italic();
3845 current_paragraph
->done_tt();
3846 } else if (strcmp(fontname
, "B") == 0) {
3847 current_paragraph
->do_bold();
3848 } else if (strcmp(fontname
, "I") == 0) {
3849 current_paragraph
->do_italic();
3850 } else if (strcmp(fontname
, "BI") == 0) {
3851 current_paragraph
->do_bold();
3852 current_paragraph
->do_italic();
3853 } else if (strcmp(fontname
, "CR") == 0) {
3854 if ((! fill_on
) && (is_courier_until_eol()) &&
3855 is_line_start(! fill_on
)) {
3856 current_paragraph
->do_pre();
3858 current_paragraph
->do_tt();
3859 } else if (strcmp(fontname
, "CI") == 0) {
3860 if ((! fill_on
) && (is_courier_until_eol()) &&
3861 is_line_start(! fill_on
)) {
3862 current_paragraph
->do_pre();
3864 current_paragraph
->do_tt();
3865 current_paragraph
->do_italic();
3866 } else if (strcmp(fontname
, "CB") == 0) {
3867 if ((! fill_on
) && (is_courier_until_eol()) &&
3868 is_line_start(! fill_on
)) {
3869 current_paragraph
->do_pre();
3871 current_paragraph
->do_tt();
3872 current_paragraph
->do_bold();
3873 } else if (strcmp(fontname
, "CBI") == 0) {
3874 if ((! fill_on
) && (is_courier_until_eol()) &&
3875 is_line_start(! fill_on
)) {
3876 current_paragraph
->do_pre();
3878 current_paragraph
->do_tt();
3879 current_paragraph
->do_italic();
3880 current_paragraph
->do_bold();
3885 * start_size - from is old font size, to is the new font size.
3886 * The html increase <big> and <small> decrease alters the
3887 * font size by 20%. We try and map these onto glyph sizes.
3890 void html_printer::start_size (int from
, int to
)
3894 current_paragraph
->do_big();
3895 from
+= SIZE_INCREMENT
;
3897 } else if (from
> to
) {
3899 current_paragraph
->do_small();
3900 from
-= SIZE_INCREMENT
;
3906 * do_font - checks to see whether we need to alter the html font.
3909 void html_printer::do_font (text_glob
*g
)
3912 * check if the output_style.point_size has not been set yet
3913 * this allow users to place .ps at the top of their troff files
3914 * and grohtml can then treat the .ps value as the base font size (3)
3916 if (output_style
.point_size
== -1) {
3917 output_style
.point_size
= pointsize
;
3920 if (g
->text_style
.f
!= output_style
.f
) {
3921 if (output_style
.f
!= 0) {
3922 end_font(output_style
.f
->get_name());
3924 output_style
.f
= g
->text_style
.f
;
3925 if (output_style
.f
!= 0) {
3926 start_font(output_style
.f
->get_name());
3929 if (output_style
.point_size
!= g
->text_style
.point_size
) {
3931 if ((output_style
.point_size
> 0) &&
3932 (g
->text_style
.point_size
> 0)) {
3933 start_size(output_style
.point_size
, g
->text_style
.point_size
);
3935 if (g
->text_style
.point_size
> 0) {
3936 output_style
.point_size
= g
->text_style
.point_size
;
3939 if (output_style
.col
!= g
->text_style
.col
) {
3940 current_paragraph
->done_color();
3941 output_style
.col
= g
->text_style
.col
;
3942 current_paragraph
->do_color(&output_style
.col
);
3947 * start_subscript - returns TRUE if, g, looks like a subscript start.
3950 int html_printer::start_subscript (text_glob
*g
)
3953 int height
= output_style
.point_size
*r
/72;
3955 return( (output_style
.point_size
!= 0) &&
3956 (output_vpos
< g
->minv
) &&
3957 (output_vpos
-height
> g
->maxv
) &&
3958 (output_style
.point_size
> g
->text_style
.point_size
) );
3962 * start_superscript - returns TRUE if, g, looks like a superscript start.
3965 int html_printer::start_superscript (text_glob
*g
)
3968 int height
= output_style
.point_size
*r
/72;
3970 return( (output_style
.point_size
!= 0) &&
3971 (output_vpos
> g
->minv
) &&
3972 (output_vpos
-height
< g
->maxv
) &&
3973 (output_style
.point_size
> g
->text_style
.point_size
) );
3977 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
3980 int html_printer::end_subscript (text_glob
*g
)
3983 int height
= output_style
.point_size
*r
/72;
3985 return( (output_style
.point_size
!= 0) &&
3986 (g
->minv
< output_vpos
) &&
3987 (output_vpos
-height
> g
->maxv
) &&
3988 (output_style
.point_size
< g
->text_style
.point_size
) );
3992 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
3995 int html_printer::end_superscript (text_glob
*g
)
3998 int height
= output_style
.point_size
*r
/72;
4000 return( (output_style
.point_size
!= 0) &&
4001 (g
->minv
> output_vpos
) &&
4002 (output_vpos
-height
< g
->maxv
) &&
4003 (output_style
.point_size
< g
->text_style
.point_size
) );
4007 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4008 * start/end and it calls the services of html-text to issue the
4012 void html_printer::do_sup_or_sub (text_glob
*g
)
4014 if (! supress_sub_sup
) {
4015 if (start_subscript(g
)) {
4016 current_paragraph
->do_sub();
4017 } else if (start_superscript(g
)) {
4018 current_paragraph
->do_sup();
4019 } else if (end_subscript(g
)) {
4020 current_paragraph
->done_sub();
4021 } else if (end_superscript(g
)) {
4022 current_paragraph
->done_sup();
4028 * do_end_para - writes out the html text after shutting down the
4029 * current paragraph.
4032 void html_printer::do_end_para (text_glob
*g
)
4035 current_paragraph
->done_para();
4036 current_paragraph
->remove_para_space();
4037 html
.put_string(g
->text_string
+9);
4038 output_vpos
= g
->minv
;
4039 output_hpos
= g
->maxh
;
4040 output_vpos_max
= g
->maxv
;
4041 supress_sub_sup
= FALSE
;
4045 * emit_html - write out the html text
4048 void html_printer::emit_html (text_glob
*g
)
4052 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
4053 output_vpos
= g
->minv
;
4054 output_hpos
= g
->maxh
;
4055 output_vpos_max
= g
->maxv
;
4056 supress_sub_sup
= FALSE
;
4060 * flush_sbuf - flushes the current sbuf into the list of glyphs.
4063 void html_printer::flush_sbuf()
4065 if (sbuf
.length() > 0) {
4066 int r
=font::res
; // resolution of the device
4067 set_style(sbuf_style
);
4069 if (overstrike_detected
&& (! is_bold(sbuf_style
.f
))) {
4070 font
*bold_font
= make_bold(sbuf_style
.f
);
4071 if (bold_font
!= NULL
)
4072 sbuf_style
.f
= bold_font
;
4075 page_contents
->add(&sbuf_style
, sbuf
,
4077 sbuf_vpos
-sbuf_style
.point_size
*r
/72, sbuf_start_hpos
,
4078 sbuf_vpos
, sbuf_end_hpos
);
4080 output_hpos
= sbuf_end_hpos
;
4081 output_vpos
= sbuf_vpos
;
4082 last_sbuf_length
= 0;
4083 sbuf_prev_hpos
= sbuf_end_hpos
;
4084 overstrike_detected
= FALSE
;
4089 void html_printer::set_line_thickness(const environment
*env
)
4091 line_thickness
= env
->size
;
4094 void html_printer::draw(int code
, int *p
, int np
, const environment
*env
)
4101 page_contents
->add_line(&sbuf_style
,
4103 env
->hpos
, env
->vpos
, env
->hpos
+p
[0], env
->vpos
+p
[1], line_thickness
);
4105 error("2 arguments required for line");
4112 line_thickness
= -1;
4114 // troff gratuitously adds an extra 0
4115 if (np
!= 1 && np
!= 2) {
4116 error("0 or 1 argument required for thickness");
4119 line_thickness
= p
[0];
4143 // fill with color env->fill
4144 if (background
!= NULL
)
4146 background
= new color
;
4147 *background
= *env
->fill
;
4151 error("unrecognised drawing command `%1'", char(code
));
4156 html_printer::html_printer()
4157 : html(0, MAX_LINE_LENGTH
),
4158 no_of_printed_pages(0),
4159 last_sbuf_length(0),
4160 overstrike_detected(FALSE
),
4163 output_vpos_max(-1),
4165 inside_font_style(0),
4168 supress_sub_sup(TRUE
),
4169 cutoff_heading(100),
4182 pointsize(base_point_size
),
4184 background(default_background
),
4187 seen_pageoffset(FALSE
),
4189 seen_linelength(FALSE
),
4198 file_list
.add_new_file(xtmpfile());
4199 html
.set_file(file_list
.get_file());
4200 if (font::hor
!= 24)
4201 fatal("horizontal resolution must be 24");
4202 if (font::vert
!= 40)
4203 fatal("vertical resolution must be 40");
4205 // should be sorted html..
4206 if (font::res
% (font::sizescale
*72) != 0)
4207 fatal("res must be a multiple of 72*sizescale");
4211 while (r
% 10 == 0) {
4216 html
.set_fixed_point(point
);
4217 space_char_index
= font::name_to_index("space");
4218 space_width
= font::hor
;
4219 paper_length
= font::paperlength
;
4220 linelength
= font::res
*13/2;
4221 if (paper_length
== 0)
4222 paper_length
= 11*font::res
;
4224 page_contents
= new page();
4228 * add_to_sbuf - adds character code or name to the sbuf.
4231 void html_printer::add_to_sbuf (int idx
, const string
&s
)
4233 if (sbuf_style
.f
== NULL
)
4236 char *html_glyph
= NULL
;
4237 unsigned int code
= sbuf_style
.f
->get_code(idx
);
4240 if (sbuf_style
.f
->contains(idx
))
4241 html_glyph
= (char *)sbuf_style
.f
->get_special_device_encoding(idx
);
4245 if ((html_glyph
== NULL
) && (code
>= UNICODE_DESC_START
))
4246 html_glyph
= to_unicode(code
);
4248 html_glyph
= get_html_translation(sbuf_style
.f
, s
);
4250 last_sbuf_length
= sbuf
.length();
4251 if (html_glyph
== NULL
)
4252 sbuf
+= ((char)code
);
4257 int html_printer::sbuf_continuation (int idx
, const char *name
,
4258 const environment
*env
, int w
)
4261 * lets see whether the glyph is closer to the end of sbuf
4263 if ((sbuf_end_hpos
== env
->hpos
)
4264 || ((sbuf_prev_hpos
< sbuf_end_hpos
)
4265 && (env
->hpos
< sbuf_end_hpos
)
4266 && ((sbuf_end_hpos
-env
->hpos
< env
->hpos
-sbuf_prev_hpos
)))) {
4267 add_to_sbuf(idx
, name
);
4268 sbuf_prev_hpos
= sbuf_end_hpos
;
4269 sbuf_end_hpos
+= w
+ sbuf_kern
;
4272 if ((env
->hpos
>= sbuf_end_hpos
) &&
4273 ((sbuf_kern
== 0) || (sbuf_end_hpos
- sbuf_kern
!= env
->hpos
))) {
4275 * lets see whether a space is needed or not
4278 if (env
->hpos
-sbuf_end_hpos
< space_width
) {
4279 add_to_sbuf(idx
, name
);
4280 sbuf_prev_hpos
= sbuf_end_hpos
;
4281 sbuf_end_hpos
= env
->hpos
+ w
;
4290 * get_html_translation - given the position of the character and its name
4291 * return the device encoding for such character.
4294 char *get_html_translation (font
*f
, const string
&name
)
4298 if ((f
== 0) || name
.empty())
4301 idx
= f
->name_to_index((char *)(name
+ '\0').contents());
4303 error("character `%s' not found", (name
+ '\0').contents());
4306 if (f
->contains(idx
))
4307 return (char *)f
->get_special_device_encoding(idx
);
4314 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
4315 * a previous glyph in sbuf.
4316 * If TRUE the font is changed to bold and the previous sbuf
4320 int html_printer::overstrike(int idx
, const char *name
, const environment
*env
, int w
)
4322 if ((env
->hpos
< sbuf_end_hpos
)
4323 || ((sbuf_kern
!= 0) && (sbuf_end_hpos
- sbuf_kern
< env
->hpos
))) {
4325 * at this point we have detected an overlap
4327 if (overstrike_detected
) {
4328 /* already detected, remove previous glyph and use this glyph */
4329 sbuf
.set_length(last_sbuf_length
);
4330 add_to_sbuf(idx
, name
);
4331 sbuf_end_hpos
= env
->hpos
+ w
;
4334 /* first time we have detected an overstrike in the sbuf */
4335 sbuf
.set_length(last_sbuf_length
); /* remove previous glyph */
4336 if (! is_bold(sbuf_style
.f
))
4338 overstrike_detected
= TRUE
;
4339 add_to_sbuf(idx
, name
);
4340 sbuf_end_hpos
= env
->hpos
+ w
;
4348 * set_char - adds a character into the sbuf if it is a continuation
4349 * with the previous word otherwise flush the current sbuf
4350 * and add character anew.
4353 void html_printer::set_char(int i
, font
*f
, const environment
*env
,
4354 int w
, const char *name
)
4356 style
sty(f
, env
->size
, env
->height
, env
->slant
, env
->fontno
, *env
->col
);
4357 if (sty
.slant
!= 0) {
4358 if (sty
.slant
> 80 || sty
.slant
< -80) {
4359 error("silly slant `%1' degrees", sty
.slant
);
4363 if (((! sbuf
.empty()) && (sty
== sbuf_style
) && (sbuf_vpos
== env
->vpos
))
4364 && (sbuf_continuation(i
, name
, env
, w
) || overstrike(i
, name
, env
, w
)))
4368 if (sbuf_style
.f
== NULL
)
4370 add_to_sbuf(i
, name
);
4371 sbuf_end_hpos
= env
->hpos
+ w
;
4372 sbuf_start_hpos
= env
->hpos
;
4373 sbuf_prev_hpos
= env
->hpos
;
4374 sbuf_vpos
= env
->vpos
;
4380 * set_numbered_char - handle numbered characters.
4381 * Negative values are interpreted as unbreakable spaces;
4382 * the value (taken positive) gives the width.
4385 void html_printer::set_numbered_char(int num
, const environment
*env
,
4391 num
= 160; //
4393 int i
= font::number_to_index(num
);
4394 int fn
= env
->fontno
;
4395 if (fn
< 0 || fn
>= nfonts
) {
4396 error("bad font position `%1'", fn
);
4399 font
*f
= font_table
[fn
];
4401 error("no font mounted at `%1'", fn
);
4404 if (!f
->contains(i
)) {
4405 error("font `%1' does not contain numbered character %2",
4414 w
= f
->get_width(i
, env
->size
);
4418 set_char(i
, f
, env
, w
, 0);
4421 int html_printer::set_char_and_width(const char *nm
, const environment
*env
,
4422 int *widthp
, font
**f
)
4424 int i
= font::name_to_index(nm
);
4425 int fn
= env
->fontno
;
4426 if (fn
< 0 || fn
>= nfonts
) {
4427 error("bad font position `%1'", fn
);
4430 *f
= font_table
[fn
];
4432 error("no font mounted at `%1'", fn
);
4435 if (!(*f
)->contains(i
)) {
4436 if (nm
[0] != '\0' && nm
[1] == '\0')
4437 error("font `%1' does not contain ascii character `%2'",
4441 error("font `%1' does not contain special character `%2'",
4446 int w
= (*f
)->get_width(i
, env
->size
);
4454 * write_title - writes the title to this document
4457 void html_printer::write_title (int in_head
)
4459 if (title
.has_been_found
) {
4461 html
.put_string("<title>");
4462 html
.put_string(title
.text
);
4463 html
.put_string("</title>").nl().nl();
4465 title
.has_been_written
= TRUE
;
4466 if (title
.with_h1
) {
4467 html
.put_string("<h1 align=center>");
4468 html
.put_string(title
.text
);
4469 html
.put_string("</h1>").nl().nl();
4472 } else if (in_head
) {
4473 // place empty title tags to help conform to `tidy'
4474 html
.put_string("<title></title>").nl();
4479 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
4482 static void write_rule (void)
4485 fputs("<hr>\n", stdout
);
4488 void html_printer::begin_page(int n
)
4491 #if defined(DEBUGGING)
4492 html
.begin_comment("Page: ").put_string(i_to_a(page_number
)).end_comment();;
4494 no_of_printed_pages
++;
4497 output_style
.point_size
= -1;
4498 output_space_code
= 32;
4499 output_draw_point_size
= -1;
4500 output_line_thickness
= -1;
4503 output_vpos_max
= -1;
4504 current_paragraph
= new html_text(&html
);
4505 do_indent(get_troff_indent(), pageoffset
, linelength
);
4506 current_paragraph
->do_para("", FALSE
);
4509 void html_printer::end_page(int)
4515 font
*html_printer::make_font(const char *nm
)
4517 return html_font::load_html_font(nm
);
4520 void html_printer::do_body (void)
4522 if (background
== NULL
)
4523 fputs("<body>\n\n", stdout
);
4525 unsigned int r
, g
, b
;
4528 background
->get_rgb(&r
, &g
, &b
);
4529 // we have to scale 0..0xFFFF to 0..0xFF
4530 sprintf(buf
, "%.2X%.2X%.2X", r
/0x101, g
/0x101, b
/0x101);
4532 fputs("<body bgcolor=\"#", stdout
);
4534 fputs("\">\n\n", stdout
);
4539 * emit_link - generates: <a href="to">name</a>
4542 void html_printer::emit_link (const string
&to
, const char *name
)
4544 fputs("<a href=\"", stdout
);
4545 fputs(to
.contents(), stdout
);
4546 fputs("\">", stdout
);
4547 fputs(name
, stdout
);
4548 fputs("</a>", stdout
);
4552 * write_navigation - writes out the links which navigate between
4556 void html_printer::write_navigation (const string
&top
, const string
&prev
,
4557 const string
&next
, const string
¤t
)
4559 int need_bar
= FALSE
;
4561 if (multiple_files
) {
4563 fputs("[ ", stdout
);
4564 if ((strcmp(prev
.contents(), "") != 0) && prev
!= top
&& prev
!= current
) {
4565 emit_link(prev
, "prev");
4568 if ((strcmp(next
.contents(), "") != 0) && next
!= top
&& next
!= current
) {
4570 fputs(" | ", stdout
);
4571 emit_link(next
, "next");
4574 if (top
!= "<standard input>" && (strcmp(top
.contents(), "") != 0) && top
!= current
) {
4576 fputs(" | ", stdout
);
4577 emit_link(top
, "top");
4579 fputs(" ]\n", stdout
);
4585 * do_file_components - scan the file list copying each temporary
4586 * file in turn. This is used twofold:
4588 * firstly to emit section heading links,
4589 * between file fragments if required and
4590 * secondly to generate jobname file fragments
4594 void html_printer::do_file_components (void)
4596 int fragment_no
= 1;
4602 file_list
.start_of_list();
4603 top
= string(job_name
);
4604 top
+= string(".html");
4606 next
= file_list
.next_file_name();
4609 while (file_list
.get_file() != 0) {
4610 if (fseek(file_list
.get_file(), 0L, 0) < 0)
4611 fatal("fseek on temporary file failed");
4612 html
.copy_file(file_list
.get_file());
4613 fclose(file_list
.get_file());
4615 file_list
.move_next();
4616 if (file_list
.is_new_output_file()) {
4617 if (fragment_no
> 1)
4618 write_navigation(top
, prev
, next
, current
);
4621 next
= file_list
.next_file_name();
4623 string split_file
= file_list
.file_name();
4626 freopen(split_file
.contents(), "w", stdout
);
4628 writeHeadMetaStyle();
4629 write_navigation(top
, prev
, next
, current
);
4631 if (file_list
.are_links_required())
4632 header
.write_headings(stdout
, TRUE
);
4634 if (fragment_no
> 1)
4635 write_navigation(top
, prev
, next
, current
);
4641 * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
4642 * related information.
4645 void html_printer::writeHeadMetaStyle (void)
4647 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout
);
4648 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout
);
4650 fputs("<html>\n", stdout
);
4651 fputs("<head>\n", stdout
);
4652 fputs("<meta name=\"generator\" "
4653 "content=\"groff -Thtml, see www.gnu.org\">\n", stdout
);
4654 fputs("<meta http-equiv=\"Content-Type\" "
4655 "content=\"text/html; charset=US-ASCII\">\n", stdout
);
4656 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout
);
4658 fputs("<style type=\"text/css\">\n", stdout
);
4659 fputs(" p { margin-top: 0; margin-bottom: 0; }\n", stdout
);
4660 fputs(" pre { margin-top: 0; margin-bottom: 0; }\n", stdout
);
4661 fputs(" table { margin-top: 0; margin-bottom: 0; }\n", stdout
);
4662 fputs("</style>\n", stdout
);
4665 html_printer::~html_printer()
4667 #ifdef LONG_FOR_TIME_T
4673 current_paragraph
->flush_text();
4675 html
.set_file(stdout
);
4676 html
.begin_comment("Creator : ")
4677 .put_string("groff ")
4678 .put_string("version ")
4679 .put_string(Version_string
)
4683 html
.begin_comment("CreationDate: ")
4684 .put_string(ctime(&t
), strlen(ctime(&t
))-1)
4687 writeHeadMetaStyle();
4691 fputs(head_info
.contents(), stdout
);
4692 fputs("</head>\n", stdout
);
4696 header
.write_headings(stdout
, FALSE
);
4698 #if defined(DEBUGGING)
4699 html
.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages
)).end_comment();
4704 if (multiple_files
) {
4705 fputs("</body>\n", stdout
);
4706 fputs("</html>\n", stdout
);
4707 do_file_components();
4709 do_file_components();
4710 fputs("</body>\n", stdout
);
4711 fputs("</html>\n", stdout
);
4716 * get_str - returns a dupicate of string, s. The duplicate
4717 * string is terminated at the next ',' or ']'.
4720 static char *get_str (const char *s
, char **n
)
4725 while ((s
[i
] != (char)0) && (s
[i
] != ',') && (s
[i
] != ']'))
4732 (*n
) = (char *)&s
[i
+1];
4734 (*n
) = (char *)&s
[i
];
4738 (*n
) = (char *)&s
[1];
4745 * make_val - creates a string from if s is NULL.
4748 char *make_val (char *s
, int v
, char *id
, char *f
, char *l
)
4753 sprintf(buf
, "%d", v
);
4754 return strsave(buf
);
4758 * check that value, s, is the same as, v.
4766 f
= (char *)"stdin";
4768 l
= (char *)"<none>";
4769 fprintf(stderr
, "%s:%s: grohtml assertion failed at id%s expecting %d and was given %s\n",
4777 * handle_assertion - handles the assertions created via .www:ASSERT
4778 * in www.tmac. See www.tmac for examples.
4779 * This method should be called as we are
4780 * parsing the ditroff input. It checks the x, y
4781 * position assertions. It does _not_ check the
4782 * troff state assertions as these are unknown at this
4786 void html_printer::handle_assertion (int minv
, int minh
, int maxv
, int maxh
, const char *s
)
4789 char *cmd
= get_str(s
, &n
);
4790 char *id
= get_str(n
, &n
);
4791 char *val
= get_str(n
, &n
);
4792 char *file
= get_str(n
, &n
);
4793 char *line
= get_str(n
, &n
);
4795 if (strcmp(cmd
, "assertion:[x") == 0)
4796 as
.addx(cmd
, id
, make_val(val
, minh
, id
, file
, line
), file
, line
);
4797 else if (strcmp(cmd
, "assertion:[y") == 0)
4798 as
.addy(cmd
, id
, make_val(val
, minv
, id
, file
, line
), file
, line
);
4800 if (strncmp(cmd
, "assertion:[", strlen("assertion:[")) == 0)
4801 page_contents
->add_tag(&sbuf_style
, string(s
),
4802 line_number
, minv
, minh
, maxv
, maxh
);
4806 * build_state_assertion - builds the troff state assertions.
4809 void html_printer::handle_state_assertion (text_glob
*g
)
4811 if (g
!= NULL
&& g
->is_a_tag() &&
4812 (strncmp(g
->text_string
, "assertion:[", 11) == 0)) {
4813 char *n
= (char *)&g
->text_string
[11];
4814 char *cmd
= get_str(n
, &n
);
4815 char *val
= get_str(n
, &n
);
4816 (void)get_str(n
, &n
); // unused
4817 char *file
= get_str(n
, &n
);
4818 char *line
= get_str(n
, &n
);
4820 as
.build(cmd
, val
, file
, line
);
4825 * special - handle all x X requests from troff. For post-html they
4826 * allow users to pass raw html commands, turn auto linked
4827 * headings off/on etc.
4830 void html_printer::special(char *s
, const environment
*env
, char type
)
4836 if (env
->fontno
>= 0) {
4837 style
sty(get_font_from_index(env
->fontno
), env
->size
, env
->height
,
4838 env
->slant
, env
->fontno
, *env
->col
);
4842 if (strncmp(s
, "html:", 5) == 0) {
4843 int r
=font::res
; /* resolution of the device */
4844 font
*f
=sbuf_style
.f
;
4849 f
= font::load_font("TR", &found
);
4853 * need to pass rest of string through to html output during flush
4855 page_contents
->add_and_encode(&sbuf_style
, string(&s
[5]),
4857 env
->vpos
-env
->size
*r
/72, env
->hpos
,
4858 env
->vpos
, env
->hpos
,
4862 * assume that the html command has no width, if it does then
4863 * hopefully troff will have fudged this in a macro by
4864 * requesting that the formatting move right by the appropriate
4867 } else if (strncmp(s
, "html</p>:", 9) == 0) {
4868 int r
=font::res
; /* resolution of the device */
4869 font
*f
=sbuf_style
.f
;
4874 f
= font::load_font("TR", &found
);
4878 * need to pass all of string through to html output during flush
4880 page_contents
->add_and_encode(&sbuf_style
, string(s
),
4882 env
->vpos
-env
->size
*r
/72, env
->hpos
,
4883 env
->vpos
, env
->hpos
,
4887 * assume that the html command has no width, if it does then
4888 * hopefully troff will have fudged this in a macro by
4889 * requesting that the formatting move right by the appropriate
4892 } else if (strncmp(s
, "index:", 6) == 0) {
4893 cutoff_heading
= atoi(&s
[6]);
4894 } else if (strncmp(s
, "assertion:[", 11) == 0) {
4895 int r
=font::res
; /* resolution of the device */
4897 handle_assertion(env
->vpos
-env
->size
*r
/72, env
->hpos
,
4898 env
->vpos
, env
->hpos
, s
);
4904 * devtag - handles device troff tags sent from the `troff'.
4905 * These include the troff state machine tags:
4906 * .br, .sp, .in, .tl, .ll etc
4908 * (see man 5 grohtml_tags).
4911 void html_printer::devtag (char *s
, const environment
*env
, char type
)
4918 if (env
->fontno
>= 0) {
4919 style
sty(get_font_from_index(env
->fontno
), env
->size
, env
->height
,
4920 env
->slant
, env
->fontno
, *env
->col
);
4924 if (strncmp(s
, "devtag:", strlen("devtag:")) == 0) {
4925 int r
=font::res
; /* resolution of the device */
4927 page_contents
->add_tag(&sbuf_style
, string(s
),
4929 env
->vpos
-env
->size
*r
/72, env
->hpos
,
4930 env
->vpos
, env
->hpos
);
4937 * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
4940 int html_printer::round_width(int x
)
4945 // don't depend on the rounding direction for division of negative integers
4950 ? -((-x
+ r
/2 - 1)/r
)
4955 int main(int argc
, char **argv
)
4957 program_name
= argv
[0];
4958 static char stderr_buf
[BUFSIZ
];
4959 setbuf(stderr
, stderr_buf
);
4961 static const struct option long_options
[] = {
4962 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
4963 { "version", no_argument
, 0, 'v' },
4966 while ((c
= getopt_long(argc
, argv
, "a:bdD:F:g:hi:I:j:lno:prs:S:v",
4967 long_options
, NULL
))
4971 /* text antialiasing bits - handled by pre-html */
4974 // set background color to white
4975 default_background
= new color
;
4976 default_background
->set_gray(color::MAX_COLOR_VAL
);
4979 /* handled by pre-html */
4982 /* handled by pre-html */
4985 font::command_line_font_dir(optarg
);
4988 /* graphic antialiasing bits - handled by pre-html */
4991 /* do not use the Hn headings of html, but manufacture our own */
4992 manufacture_headings
= TRUE
;
4995 /* handled by pre-html */
4998 /* handled by pre-html */
5001 multiple_files
= TRUE
;
5008 simple_anchors
= TRUE
;
5011 /* handled by pre-html */
5014 /* handled by pre-html */
5020 base_point_size
= atoi(optarg
);
5023 split_level
= atoi(optarg
) + 1;
5026 printf("GNU post-grohtml (groff) version %s\n", Version_string
);
5029 case CHAR_MAX
+ 1: // --help
5040 if (optind
>= argc
) {
5043 for (int i
= optind
; i
< argc
; i
++)
5049 static void usage(FILE *stream
)
5051 fprintf(stream
, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",