2 /* Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
5 * but it owes a huge amount of ideas and raw code from
6 * James Clark (jjc@jclark.com) grops/ps.cpp.
10 This file is part of groff.
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING. If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include "stringclass.h"
30 #include "html-text.h"
31 #include "html-table.h"
42 extern "C" const char *Version_string
;
51 #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
52 #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
53 #define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */
54 #define CENTER_TOLERANCE 2 /* how many pixels off center will we still */
55 #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
56 #define UNICODE_DESC_START 0x80 /* all character entities above this are */
57 /* either encoded by their glyph names or if */
58 /* there is no name then we use &#nnn; */
59 typedef enum {CENTERED
, LEFT
, RIGHT
, INLINE
} TAG_ALIGNMENT
;
60 typedef enum {col_tag
, tab_tag
, tab0_tag
, none
} colType
;
69 char *get_html_translation (font
*f
, const string
&name
);
70 int char_translate_to_html (font
*f
, char *buf
, int buflen
, unsigned char ch
, int b
, int and_single
);
73 static int auto_links
= TRUE
; /* by default we enable automatic links at */
74 /* top of the document. */
75 static int auto_rule
= TRUE
; /* by default we enable an automatic rule */
76 /* at the top and bottom of the document */
77 static int simple_anchors
= FALSE
; /* default to anchors with heading text */
78 static int manufacture_headings
= FALSE
; /* default is to use the Hn html headings, */
79 /* rather than manufacture our own. */
80 static color
*default_background
= NULL
; /* has user requested initial bg color? */
81 static string job_name
; /* if set then the output is split into */
82 /* multiple files with `job_name'-%d.html */
83 static int multiple_files
= FALSE
; /* must we the output be divided into */
84 /* multiple html files, one for each */
89 * start with a few favorites
94 static int min (int a
, int b
)
102 static int max (int a
, int b
)
111 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
114 static int is_intersection (int a1
, int a2
, int b1
, int b2
)
116 // easier to prove NOT outside limits
117 return( ! ((a1
> b2
) || (a2
< b1
)) );
121 * is_digit - returns TRUE if character, ch, is a digit.
124 static int is_digit (char ch
)
126 return( (ch
>= '0') && (ch
<= '9') );
130 * the classes and methods for maintaining a list of files.
138 string output_file_name
;
144 * file - initialize all fields to NULL
148 : fp(f
), next(0), new_output_file(FALSE
),
149 require_links(FALSE
), output_file_name("")
156 FILE *get_file (void);
157 void start_of_list (void);
158 void move_next (void);
159 void add_new_file (FILE *f
);
160 void set_file_name (string name
);
161 void set_links_required (void);
162 int are_links_required (void);
163 int is_new_output_file (void);
164 string
file_name (void);
165 string
next_file_name (void);
173 * files - create an empty list of files.
177 : head(0), tail(0), ptr(0)
182 * get_file - returns the FILE associated with ptr.
185 FILE *files::get_file (void)
194 * start_of_list - reset the ptr to the start of the list.
197 void files::start_of_list (void)
203 * move_next - moves the ptr to the next element on the list.
206 void files::move_next (void)
213 * add_new_file - adds a new file, f, to the list.
216 void files::add_new_file (FILE *f
)
222 tail
->next
= new file(f
);
229 * set_file_name - sets the final file name to contain the html
233 void files::set_file_name (string name
)
236 ptr
->output_file_name
= name
;
237 ptr
->new_output_file
= TRUE
;
242 * set_links_required - issue links when processing this component
246 void files::set_links_required (void)
249 ptr
->require_links
= TRUE
;
253 * are_links_required - returns TRUE if this section of the file
254 * requires that links should be issued.
257 int files::are_links_required (void)
260 return ptr
->require_links
;
265 * is_new_output_file - returns TRUE if this component of the file
266 * is the start of a new output file.
269 int files::is_new_output_file (void)
272 return ptr
->new_output_file
;
277 * file_name - returns the name of the file.
280 string
files::file_name (void)
283 return ptr
->output_file_name
;
288 * next_file_name - returns the name of the next file.
291 string
files::next_file_name (void)
293 if (ptr
!= NULL
&& ptr
->next
!= NULL
)
294 return ptr
->next
->output_file_name
;
299 * the class and methods for styles
310 style (font
*, int, int, int, int, color
);
311 int operator == (const style
&) const;
312 int operator != (const style
&) const;
320 style::style(font
*p
, int sz
, int h
, int sl
, int no
, color c
)
321 : f(p
), point_size(sz
), font_no(no
), height(h
), slant(sl
), col(c
)
325 int style::operator==(const style
&s
) const
327 return (f
== s
.f
&& point_size
== s
.point_size
328 && height
== s
.height
&& slant
== s
.slant
&& col
== s
.col
);
331 int style::operator!=(const style
&s
) const
333 return !(*this == s
);
337 * the class and methods for retaining ascii text
347 char_block(int length
);
351 char_block::char_block()
352 : buffer(NULL
), used(0), next(0)
356 char_block::char_block(int length
)
359 buffer
= (char *)malloc(max(length
, char_block::SIZE
));
361 fatal("out of memory error");
364 char_block::~char_block()
374 char *add_string(const char *, unsigned int);
375 char *add_string(const string
&);
381 char_buffer::char_buffer()
386 char_buffer::~char_buffer()
389 char_block
*temp
= head
;
395 char *char_buffer::add_string (const char *s
, unsigned int length
)
398 unsigned int old_used
;
400 if (s
== NULL
|| length
== 0)
404 tail
= new char_block(length
+1);
407 if (tail
->used
+ length
+1 > char_block::SIZE
) {
408 tail
->next
= new char_block(length
+1);
413 old_used
= tail
->used
;
415 tail
->buffer
[tail
->used
] = s
[i
];
421 // add terminating nul character
423 tail
->buffer
[tail
->used
] = '\0';
426 // and return start of new string
428 return( &tail
->buffer
[old_used
] );
431 char *char_buffer::add_string (const string
&s
)
433 return add_string(s
.contents(), s
.length());
437 * the classes and methods for maintaining glyph positions.
442 void text_glob_html (style
*s
, char *str
, int length
,
443 int min_vertical
, int min_horizontal
,
444 int max_vertical
, int max_horizontal
);
445 void text_glob_special (style
*s
, char *str
, int length
,
446 int min_vertical
, int min_horizontal
,
447 int max_vertical
, int max_horizontal
);
448 void text_glob_line (style
*s
,
449 int min_vertical
, int min_horizontal
,
450 int max_vertical
, int max_horizontal
,
452 void text_glob_auto_image(style
*s
, char *str
, int length
,
453 int min_vertical
, int min_horizontal
,
454 int max_vertical
, int max_horizontal
);
455 void text_glob_tag (style
*s
, char *str
, int length
,
456 int min_vertical
, int min_horizontal
,
457 int max_vertical
, int max_horizontal
);
461 int is_a_line (void);
464 int is_auto_img (void);
470 int is_eol_ce (void);
475 int is_tab_ts (void);
476 int is_tab_te (void);
480 int get_tab_args (char *align
);
482 void remember_table (html_table
*t
);
483 html_table
*get_table (void);
486 const char *text_string
;
487 unsigned int text_length
;
488 int minv
, minh
, maxv
, maxh
;
489 int is_tag
; // is this a .br, .sp, .tl etc
490 int is_img_auto
; // image created by eqn delim
491 int is_special
; // text has come via 'x X html:'
492 int is_line
; // is the command a <line>?
493 int thickness
; // the thickness of a line
494 html_table
*tab
; // table description
497 text_glob (style
*s
, const char *str
, int length
,
498 int min_vertical
, int min_horizontal
,
499 int max_vertical
, int max_horizontal
,
500 bool is_troff_command
,
501 bool is_auto_image
, bool is_special_command
,
502 bool is_a_line
, int thickness
);
505 text_glob::text_glob (style
*s
, const char *str
, int length
,
506 int min_vertical
, int min_horizontal
,
507 int max_vertical
, int max_horizontal
,
508 bool is_troff_command
,
509 bool is_auto_image
, bool is_special_command
,
510 bool is_a_line
, int line_thickness
)
511 : text_style(*s
), text_string(str
), text_length(length
),
512 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
),
513 is_tag(is_troff_command
), is_img_auto(is_auto_image
), is_special(is_special_command
),
514 is_line(is_a_line
), thickness(line_thickness
), tab(NULL
)
518 text_glob::text_glob ()
519 : text_string(0), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
520 is_tag(FALSE
), is_special(FALSE
), is_line(FALSE
), thickness(0), tab(NULL
)
524 text_glob::~text_glob ()
531 * text_glob_html - used to place html text into the glob buffer.
534 void text_glob::text_glob_html (style
*s
, char *str
, int length
,
535 int min_vertical
, int min_horizontal
,
536 int max_vertical
, int max_horizontal
)
538 text_glob
*g
= new text_glob(s
, str
, length
,
539 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
540 FALSE
, FALSE
, FALSE
, FALSE
, 0);
546 * text_glob_html - used to place html specials into the glob buffer.
547 * This text is essentially html commands coming through
548 * from the macro sets, with special designated sequences of
549 * characters translated into html. See add_and_encode.
552 void text_glob::text_glob_special (style
*s
, char *str
, int length
,
553 int min_vertical
, int min_horizontal
,
554 int max_vertical
, int max_horizontal
)
556 text_glob
*g
= new text_glob(s
, str
, length
,
557 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
558 FALSE
, FALSE
, TRUE
, FALSE
, 0);
564 * text_glob_line - record horizontal draw line commands.
567 void text_glob::text_glob_line (style
*s
,
568 int min_vertical
, int min_horizontal
,
569 int max_vertical
, int max_horizontal
,
572 text_glob
*g
= new text_glob(s
, "", 0,
573 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
574 FALSE
, FALSE
, FALSE
, TRUE
, thickness
);
580 * text_glob_auto_image - record the presence of a .auto-image tag command.
581 * Used to mark that an image has been created automatically
582 * by a preprocessor and (pre-grohtml/troff) combination.
583 * Under some circumstances images may not be created.
590 * $1 over x$!recripical of x
593 * the first auto-image marker is created via .EQ/.EN pair
594 * and no image is created.
595 * The second auto-image marker occurs at $1 over x$
596 * Currently this image will not be created
597 * as the whole of the table is created as an image.
598 * (Once html tables are handled by grohtml this will change.
599 * Shortly this will be the case).
602 void text_glob::text_glob_auto_image(style
*s
, char *str
, int length
,
603 int min_vertical
, int min_horizontal
,
604 int max_vertical
, int max_horizontal
)
606 text_glob
*g
= new text_glob(s
, str
, length
,
607 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
608 TRUE
, TRUE
, FALSE
, FALSE
, 0);
614 * text_glob_tag - records a troff tag.
617 void text_glob::text_glob_tag (style
*s
, char *str
, int length
,
618 int min_vertical
, int min_horizontal
,
619 int max_vertical
, int max_horizontal
)
621 text_glob
*g
= new text_glob(s
, str
, length
,
622 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
,
623 TRUE
, FALSE
, FALSE
, FALSE
, 0);
629 * is_a_line - returns TRUE if glob should be converted into an <hr>
632 int text_glob::is_a_line (void)
638 * is_a_tag - returns TRUE if glob contains a troff directive.
641 int text_glob::is_a_tag (void)
647 * is_eol - returns TRUE if glob contains the tag eol
650 int text_glob::is_eol (void)
652 return( is_tag
&& (strcmp(text_string
, "html-tag:eol") == 0) );
656 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
659 int text_glob::is_eol_ce (void)
661 return( is_tag
&& (strcmp(text_string
, "html-tag:eol.ce") == 0) );
666 * is_nf - returns TRUE if glob contains the tag .nf
669 int text_glob::is_nf (void)
671 return( is_tag
&& (strcmp(text_string
, "html-tag:.nf") == 0) );
675 * is_fi - returns TRUE if glob contains the tag .fi
678 int text_glob::is_fi (void)
680 return( is_tag
&& (strcmp(text_string
, "html-tag:.fi") == 0) );
684 * is_ce - returns TRUE if glob contains the tag .ce
687 int text_glob::is_ce (void)
689 return( is_tag
&& (strcmp(text_string
, "html-tag:.ce") == 0) );
693 * is_in - returns TRUE if glob contains the tag .in
696 int text_glob::is_in (void)
698 return( is_tag
&& (strncmp(text_string
, "html-tag:.in ", strlen("html-tag:.in ")) == 0) );
702 * is_po - returns TRUE if glob contains the tag .po
705 int text_glob::is_po (void)
707 return( is_tag
&& (strncmp(text_string
, "html-tag:.po ", strlen("html-tag:.po ")) == 0) );
711 * is_ti - returns TRUE if glob contains the tag .ti
714 int text_glob::is_ti (void)
716 return( is_tag
&& (strncmp(text_string
, "html-tag:.ti ", strlen("html-tag:.ti ")) == 0) );
720 * is_col - returns TRUE if glob contains the tag .col
723 int text_glob::is_col (void)
725 return( is_tag
&& (strncmp(text_string
, "html-tag:.col", strlen("html-tag:.col")) == 0) );
729 * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
732 int text_glob::is_tab_ts (void)
734 return( is_tag
&& (strcmp(text_string
, "html-tag:.tab-ts") == 0) );
738 * is_tab_te - returns TRUE if glob contains the tag .tab_te
741 int text_glob::is_tab_te (void)
743 return( is_tag
&& (strcmp(text_string
, "html-tag:.tab-te") == 0) );
747 * is_ta - returns TRUE if glob contains the tag .ta
750 int text_glob::is_ta (void)
752 return( is_tag
&& (strncmp(text_string
, "html-tag:.ta ", strlen("html-tag:.ta ")) == 0) );
756 * is_tab - returns TRUE if glob contains the tag tab
759 int text_glob::is_tab (void)
761 return( is_tag
&& (strncmp(text_string
, "html-tag:tab ", strlen("html-tag:tab ")) == 0) );
765 * is_tab0 - returns TRUE if glob contains the tag tab0
768 int text_glob::is_tab0 (void)
770 return( is_tag
&& (strncmp(text_string
, "html-tag:tab0", strlen("html-tag:tab0")) == 0) );
774 * is_auto_img - returns TRUE if the glob contains an automatically
778 int text_glob::is_auto_img (void)
784 * is_br - returns TRUE if the glob is a tag containing a .br
785 * or an implied .br. Note that we do not include .nf or .fi
786 * as grohtml will place a .br after these commands if they
787 * should break the line.
790 int text_glob::is_br (void)
792 return( is_a_tag() && ((strcmp ("html-tag:.br", text_string
) == 0) ||
793 (strncmp("html-tag:.sp", text_string
, 11) == 0) ||
794 (strcmp ("html-tag:.ce", text_string
) == 0)) );
797 int text_glob::get_arg (void)
799 if (strncmp("html-tag:", text_string
, strlen("html-tag:")) == 0) {
800 const char *p
= text_string
;
802 while ((*p
!= (char)0) && (!isspace(*p
)))
804 while ((*p
!= (char)0) && (isspace(*p
)))
814 * get_tab_args - returns the tab position and alignment of the tab tag
817 int text_glob::get_tab_args (char *align
)
819 if (strncmp("html-tag:", text_string
, strlen("html-tag:")) == 0) {
820 const char *p
= text_string
;
822 // firstly the alignment C|R|L
823 while ((*p
!= (char)0) && (!isspace(*p
)))
825 while ((*p
!= (char)0) && (isspace(*p
)))
829 while ((*p
!= (char)0) && (!isspace(*p
)))
831 while ((*p
!= (char)0) && (isspace(*p
)))
841 * remember_table - saves table, t, in the text_glob.
844 void text_glob::remember_table (html_table
*t
)
852 * get_table - returns the stored table description.
855 html_table
*text_glob::get_table (void)
861 * the class and methods used to construct ordered double linked lists.
862 * In a previous implementation we used templates via #include "ordered-list.h",
863 * but this does assume that all C++ compilers can handle this feature. Pragmatically
864 * it is safer to assume this is not the case.
867 struct element_list
{
872 int minv
, minh
, maxv
, maxh
;
874 element_list (text_glob
*d
,
876 int min_vertical
, int min_horizontal
,
877 int max_vertical
, int max_horizontal
);
882 element_list::element_list ()
883 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
888 * element_list - create a list element assigning the datum and region parameters.
891 element_list::element_list (text_glob
*in
,
893 int min_vertical
, int min_horizontal
,
894 int max_vertical
, int max_horizontal
)
895 : right(0), left(0), datum(in
), lineno(line_number
),
896 minv(min_vertical
), minh(min_horizontal
), maxv(max_vertical
), maxh(max_horizontal
)
900 element_list::~element_list ()
910 int is_less (element_list
*a
, element_list
*b
);
911 void add (text_glob
*in
,
913 int min_vertical
, int min_horizontal
,
914 int max_vertical
, int max_horizontal
);
915 void sub_move_right (void);
916 void move_right (void);
917 void move_left (void);
919 int is_equal_to_tail (void);
920 int is_equal_to_head (void);
921 void start_from_head (void);
922 void start_from_tail (void);
923 void insert (text_glob
*in
);
924 void move_to (text_glob
*in
);
925 text_glob
*move_right_get_data (void);
926 text_glob
*move_left_get_data (void);
927 text_glob
*get_data (void);
935 * list - construct an empty list.
939 : head(NULL
), tail(NULL
), ptr(NULL
)
944 * ~list - destroy a complete list.
949 element_list
*temp
=head
;
957 } while ((head
!= NULL
) && (head
!= tail
));
961 * is_less - returns TRUE if a is left of b if on the same line or
962 * if a is higher up the page than b.
965 int list::is_less (element_list
*a
, element_list
*b
)
967 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
968 if (a
->lineno
< b
->lineno
) {
970 } else if (a
->lineno
> b
->lineno
) {
972 } else if (is_intersection(a
->minv
, a
->maxv
, b
->minv
, b
->maxv
)) {
973 return( a
->minh
< b
->minh
);
975 return( a
->maxv
< b
->maxv
);
980 * add - adds a datum to the list in the order specified by the region position.
983 void list::add (text_glob
*in
, int line_number
, int min_vertical
, int min_horizontal
, int max_vertical
, int max_horizontal
)
985 // create a new list element with datum and position fields initialized
986 element_list
*t
= new element_list(in
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
998 while ((last
!= head
) && (is_less(t
, last
))) {
1002 if (is_less(t
, last
)) {
1004 last
->left
->right
= t
;
1005 t
->left
= last
->left
;
1007 // now check for a new head
1012 // add t beyond last
1013 t
->right
= last
->right
;
1015 last
->right
->left
= t
;
1017 // now check for a new tail
1026 * sub_move_right - removes the element which is currently pointed to by ptr
1027 * from the list and moves ptr to the right.
1030 void list::sub_move_right (void)
1032 element_list
*t
=ptr
->right
;
1048 ptr
->left
->right
= ptr
->right
;
1049 ptr
->right
->left
= ptr
->left
;
1055 * start_from_head - assigns ptr to the head.
1058 void list::start_from_head (void)
1064 * start_from_tail - assigns ptr to the tail.
1067 void list::start_from_tail (void)
1073 * is_empty - returns TRUE if the list has no elements.
1076 int list::is_empty (void)
1078 return( head
== 0 );
1082 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
1085 int list::is_equal_to_tail (void)
1087 return( ptr
== tail
);
1091 * is_equal_to_head - returns TRUE if the ptr equals the head.
1094 int list::is_equal_to_head (void)
1096 return( ptr
== head
);
1100 * move_left - moves the ptr left.
1103 void list::move_left (void)
1109 * move_right - moves the ptr right.
1112 void list::move_right (void)
1118 * get_datum - returns the datum referenced via ptr.
1121 text_glob
* list::get_data (void)
1123 return( ptr
->datum
);
1127 * move_right_get_data - returns the datum referenced via ptr and moves
1131 text_glob
* list::move_right_get_data (void)
1137 return( ptr
->datum
);
1142 * move_left_get_data - returns the datum referenced via ptr and moves
1146 text_glob
* list::move_left_get_data (void)
1152 return( ptr
->datum
);
1157 * insert - inserts data after the current position.
1160 void list::insert (text_glob
*in
)
1163 fatal("list must not be empty if we are inserting data");
1168 element_list
*t
= new element_list(in
, ptr
->lineno
, ptr
->minv
, ptr
->minh
, ptr
->maxv
, ptr
->maxh
);
1171 ptr
->right
->left
= t
;
1172 t
->right
= ptr
->right
;
1179 * move_to - moves the current position to the point where data, in, exists.
1180 * This is an expensive method and should be used sparingly.
1183 void list::move_to (text_glob
*in
)
1186 while (ptr
!= tail
&& ptr
->datum
!= in
)
1191 * page class and methods
1197 void add (style
*s
, const string
&str
,
1199 int min_vertical
, int min_horizontal
,
1200 int max_vertical
, int max_horizontal
);
1201 void add_tag (style
*s
, const string
&str
,
1203 int min_vertical
, int min_horizontal
,
1204 int max_vertical
, int max_horizontal
);
1205 void add_and_encode (style
*s
, const string
&str
,
1207 int min_vertical
, int min_horizontal
,
1208 int max_vertical
, int max_horizontal
);
1209 void add_line (style
*s
,
1211 int x1
, int y1
, int x2
, int y2
,
1213 void insert_tag (const string
&str
);
1214 void dump_page (void); // debugging method
1218 list glyphs
; // position of glyphs and specials on page
1219 char_buffer buffer
; // all characters for this page
1227 * insert_tag - inserts a tag after the current position.
1230 void page::insert_tag (const string
&str
)
1232 if (str
.length() > 0) {
1233 text_glob
*g
=new text_glob();
1234 text_glob
*f
=glyphs
.get_data();
1235 g
->text_glob_tag(&f
->text_style
, buffer
.add_string(str
), str
.length(),
1236 f
->minv
, f
->minh
, f
->maxv
, f
->maxh
);
1242 * add - add html text to the list of glyphs.
1245 void page::add (style
*s
, const string
&str
,
1247 int min_vertical
, int min_horizontal
,
1248 int max_vertical
, int max_horizontal
)
1250 if (str
.length() > 0) {
1251 text_glob
*g
=new text_glob();
1252 g
->text_glob_html(s
, buffer
.add_string(str
), str
.length(),
1253 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1254 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1259 * add_tag - adds a troff tag, for example: .tl .sp .br
1262 void page::add_tag (style
*s
, const string
&str
,
1264 int min_vertical
, int min_horizontal
,
1265 int max_vertical
, int max_horizontal
)
1267 if (str
.length() > 0) {
1270 if (strncmp((str
+'\0').contents(), "html-tag:.auto-image", 20) == 0) {
1271 g
= new text_glob();
1272 g
->text_glob_auto_image(s
, buffer
.add_string(str
), str
.length(),
1273 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1275 g
= new text_glob();
1276 g
->text_glob_tag(s
, buffer
.add_string(str
), str
.length(),
1277 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1279 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1284 * add_line - adds the <line> primitive providing that y1==y2
1287 void page::add_line (style
*s
,
1289 int x1
, int y1
, int x2
, int y2
,
1293 text_glob
*g
= new text_glob();
1294 g
->text_glob_line(s
,
1295 min(y1
, y2
), min(x1
, y2
), max(y1
, y2
), max(x1
, x2
),
1297 glyphs
.add(g
, line_number
, min(y1
, y2
), min(x1
, y2
), max(y1
, y2
), max(x1
, x2
));
1302 * to_unicode - returns a unicode translation of int, ch.
1305 static char *to_unicode (unsigned int ch
)
1307 static char buf
[30];
1309 sprintf(buf
, "&#%u;", ch
);
1314 * add_and_encode - adds a special string to the page, it translates the string
1315 * into html glyphs. The special string will have come from x X html:
1316 * and can contain troff character encodings which appear as
1317 * \(char\). A sequence of \\ represents \.
1318 * So for example we can write:
1319 * "cost = \(Po\)3.00 file = \\foo\\bar"
1320 * which is translated into:
1321 * "cost = £3.00 file = \foo\bar"
1324 void page::add_and_encode (style
*s
, const string
&str
,
1326 int min_vertical
, int min_horizontal
,
1327 int max_vertical
, int max_horizontal
)
1335 while (i
< str
.length()) {
1336 if ((i
+1<str
.length()) && (str
.substring(i
, 2) == string("\\("))) {
1338 i
+= 2; // move over \(
1340 while ((i
+1<str
.length()) && (str
.substring(i
, 2) != string("\\)"))) {
1344 if ((i
+1<str
.length()) && (str
.substring(i
, 2) == string("\\)")))
1349 string troff_charname
= str
.substring(a
, n
-a
);
1350 html_glyph
= get_html_translation(s
->f
, troff_charname
);
1352 html_string
+= html_glyph
;
1354 int index
=s
->f
->name_to_index((troff_charname
+ '\0').contents());
1356 if (s
->f
->contains(index
) && (index
!= 0))
1357 html_string
+= s
->f
->get_code(index
);
1361 html_string
+= str
[i
];
1364 if (html_string
.length() > 0) {
1365 text_glob
*g
=new text_glob();
1366 g
->text_glob_special(s
, buffer
.add_string(html_string
), html_string
.length(),
1367 min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1368 glyphs
.add(g
, line_number
, min_vertical
, min_horizontal
, max_vertical
, max_horizontal
);
1373 * dump_page - dump the page contents for debugging purposes.
1376 void page::dump_page(void)
1378 #if defined(DEBUG_TABLES)
1379 text_glob
*old_pos
= glyphs
.get_data();
1383 printf("\n\ndebugging start\n");
1384 glyphs
.start_from_head();
1386 g
= glyphs
.get_data();
1387 if (g
->is_tab_ts()) {
1389 if (g
->get_table() != NULL
)
1390 g
->get_table()->dump_table();
1392 printf("%s ", g
->text_string
);
1395 glyphs
.move_right();
1396 } while (! glyphs
.is_equal_to_head());
1397 glyphs
.move_to(old_pos
);
1398 printf("\ndebugging end\n\n");
1405 * font classes and methods
1408 class html_font
: public font
{
1409 html_font(const char *);
1413 char *reencoded_name
;
1415 static html_font
*load_html_font(const char *);
1418 html_font
*html_font::load_html_font(const char *s
)
1420 html_font
*f
= new html_font(s
);
1428 html_font::html_font(const char *nm
)
1433 html_font::~html_font()
1438 * a simple class to contain the header to this document
1446 int has_been_written
;
1453 title_desc::title_desc ()
1454 : has_been_written(FALSE
), has_been_found(FALSE
), with_h1(FALSE
)
1458 title_desc::~title_desc ()
1467 int no_of_level_one_headings
; // how many .SH or .NH 1 have we found?
1468 int no_of_headings
; // how many headings have we found?
1469 char_buffer headings
; // all the headings used in the document
1470 list headers
; // list of headers built from .NH and .SH
1471 list header_filename
; // in which file is this header?
1472 int header_level
; // current header level
1473 int written_header
; // have we written the header yet?
1474 string header_buffer
; // current header text
1476 void write_headings (FILE *f
, int force
);
1479 header_desc::header_desc ()
1480 : no_of_level_one_headings(0), no_of_headings(0),
1481 header_level(2), written_header(0)
1485 header_desc::~header_desc ()
1490 * write_headings - emits a list of links for the headings in this document
1493 void header_desc::write_headings (FILE *f
, int force
)
1497 if (auto_links
|| force
) {
1498 if (! headers
.is_empty()) {
1501 headers
.start_from_head();
1502 header_filename
.start_from_head();
1504 g
= headers
.get_data();
1505 fputs("<a href=\"", f
);
1506 if (multiple_files
&& (! header_filename
.is_empty())) {
1507 text_glob
*fn
= header_filename
.get_data();
1508 fputs(fn
->text_string
, f
);
1511 if (simple_anchors
) {
1512 string
buffer(ANCHOR_TEMPLATE
);
1514 buffer
+= as_string(h
);
1516 fprintf(f
, buffer
.contents());
1518 fputs(g
->text_string
, f
);
1521 fputs(g
->text_string
, f
);
1522 fputs("</a><br>\n", f
);
1523 headers
.move_right();
1524 if (multiple_files
&& (! header_filename
.is_empty()))
1525 header_filename
.move_right();
1526 } while (! headers
.is_equal_to_head());
1532 class html_printer
: public printer
{
1536 int space_char_index
;
1538 int no_of_printed_pages
;
1541 int sbuf_start_hpos
;
1547 int last_sbuf_length
;
1548 int overstrike_detected
;
1552 int output_vpos_max
;
1553 int output_draw_point_size
;
1555 int output_line_thickness
;
1556 unsigned char output_space_code
;
1557 char *inside_font_style
;
1562 int supress_sub_sup
;
1564 page
*page_contents
;
1565 html_text
*current_paragraph
;
1566 html_indent
*indent
;
1570 TAG_ALIGNMENT next_tag
;
1578 int vertical_spacing
;
1583 void set_style (const style
&);
1584 void set_space_code (unsigned char c
);
1585 void do_exec (char *, const environment
*);
1586 void do_import (char *, const environment
*);
1587 void do_def (char *, const environment
*);
1588 void do_mdef (char *, const environment
*);
1589 void do_file (char *, const environment
*);
1590 void set_line_thickness (const environment
*);
1591 void terminate_current_font (void);
1592 void flush_font (void);
1593 void add_to_sbuf (int index
, const string
&s
);
1594 void write_title (int in_head
);
1595 int sbuf_continuation (int index
, const char *name
, const environment
*env
, int w
);
1596 void flush_page (void);
1597 void troff_tag (text_glob
*g
);
1598 void flush_globs (void);
1599 void emit_line (text_glob
*g
);
1600 void emit_raw (text_glob
*g
);
1601 void emit_html (text_glob
*g
);
1602 void determine_space (text_glob
*g
);
1603 void start_font (const char *name
);
1604 void end_font (const char *name
);
1605 int is_font_courier (font
*f
);
1606 int is_courier_until_eol (void);
1607 void start_size (int from
, int to
);
1608 void do_font (text_glob
*g
);
1609 void do_center (char *arg
);
1610 void do_break (void);
1612 void do_eol_ce (void);
1613 void do_title (void);
1614 void do_fill (int on
);
1615 void do_heading (char *arg
);
1616 void write_header (void);
1617 void determine_header_level (int level
);
1618 void do_linelength (char *arg
);
1619 void do_pageoffset (char *arg
);
1620 void do_indentation (char *arg
);
1621 void do_tempindent (char *arg
);
1622 void do_indentedparagraph (void);
1623 void do_verticalspacing (char *arg
);
1624 void do_pointsize (char *arg
);
1625 void do_centered_image (void);
1626 void do_left_image (void);
1627 void do_right_image (void);
1628 void do_auto_image (text_glob
*g
, const char *filename
);
1629 void do_links (void);
1630 void do_flush (void);
1631 void do_job_name (char *name
);
1632 void insert_split_file (void);
1633 int is_in_middle (int left
, int right
);
1634 void do_sup_or_sub (text_glob
*g
);
1635 int start_subscript (text_glob
*g
);
1636 int end_subscript (text_glob
*g
);
1637 int start_superscript (text_glob
*g
);
1638 int end_superscript (text_glob
*g
);
1639 void outstanding_eol (int n
);
1640 int is_bold (font
*f
);
1641 font
*make_bold (font
*f
);
1642 int overstrike (int index
, const char *name
, const environment
*env
, int w
);
1643 void do_body (void);
1644 int next_horiz_pos (text_glob
*g
, int nf
);
1645 void lookahead_for_tables (void);
1646 void insert_tab_te (void);
1647 text_glob
*insert_tab_ts (text_glob
*where
);
1648 void insert_tab0_foreach_tab (void);
1649 void insert_tab_0 (text_glob
*where
);
1650 void do_indent (int in
, int pageoff
, int linelen
);
1651 void shutdown_table (void);
1652 void do_tab_ts (text_glob
*g
);
1653 void do_tab_te (void);
1654 void do_col (char *s
);
1655 void do_tab (char *s
);
1656 void do_tab0 (void);
1657 int calc_nf (text_glob
*g
, int nf
);
1658 void calc_po_in (text_glob
*g
, int nf
);
1659 void remove_tabs (void);
1660 void remove_courier_tabs (void);
1661 void update_min_max (colType type_of_col
, int *minimum
, int *maximum
, text_glob
*g
);
1662 void add_table_end (const char *);
1663 void do_file_components (void);
1664 void write_navigation (const string
&top
, const string
&prev
,
1665 const string
&next
, const string
¤t
);
1666 void emit_link (const string
&to
, const char *name
);
1672 void set_char (int i
, font
*f
, const environment
*env
, int w
, const char *name
);
1673 void set_numbered_char(int num
, const environment
*env
, int *widthp
);
1674 void draw (int code
, int *p
, int np
, const environment
*env
);
1675 void begin_page (int);
1676 void end_page (int);
1677 void special (char *arg
, const environment
*env
, char type
);
1678 font
*make_font (const char *);
1679 void end_of_line ();
1682 printer
*make_printer()
1684 return new html_printer
;
1687 static void usage(FILE *stream
);
1689 void html_printer::set_style(const style
&sty
)
1691 const char *fontname
= sty
.f
->get_name();
1692 if (fontname
== NULL
)
1693 fatal("no internalname specified for font");
1696 change_font(fontname
, (font::res
/(72*font::sizescale
))*sty
.point_size
);
1701 * is_bold - returns TRUE if font, f, is bold.
1704 int html_printer::is_bold (font
*f
)
1706 const char *fontname
= f
->get_name();
1707 return (strcmp(fontname
, "B") == 0) || (strcmp(fontname
, "BI") == 0);
1711 * make_bold - if a bold font of, f, exists then return it.
1714 font
*html_printer::make_bold (font
*f
)
1716 const char *fontname
= f
->get_name();
1718 if (strcmp(fontname
, "B") == 0)
1720 if (strcmp(fontname
, "I") == 0)
1721 return font::load_font("BI");
1722 if (strcmp(fontname
, "BI") == 0)
1727 void html_printer::end_of_line()
1734 * emit_line - writes out a horizontal rule.
1737 void html_printer::emit_line (text_glob
*)
1739 // --fixme-- needs to know the length in percentage
1740 html
.put_string("<hr>");
1744 * emit_raw - writes the raw html information directly to the device.
1747 void html_printer::emit_raw (text_glob
*g
)
1750 if (next_tag
== INLINE
) {
1752 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
1754 current_paragraph
->done_para();
1758 current_paragraph
->do_para("align=center");
1761 current_paragraph
->do_para(&html
, "align=left", indentation
, pageoffset
, linelength
);
1764 current_paragraph
->do_para(&html
, "align=right", indentation
, pageoffset
, linelength
);
1767 fatal("unknown enumeration");
1769 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
1770 current_paragraph
->done_para();
1772 supress_sub_sup
= TRUE
;
1773 if (indentation
> 0) {
1775 * restore indentation
1777 int newin
= indentation
;
1779 do_indent(newin
, pageoffset
, linelength
);
1785 * do_center - handle the .ce commands from troff.
1788 void html_printer::do_center (char *arg
)
1791 current_paragraph
->do_break();
1794 current_paragraph
->done_para();
1795 supress_sub_sup
= TRUE
;
1796 current_paragraph
->do_para("align=center");
1800 current_paragraph
->remove_para_align();
1805 * do_centered_image - set a flag such that the next html-tag is
1806 * placed inside a centered paragraph.
1809 void html_printer::do_centered_image (void)
1811 next_tag
= CENTERED
;
1815 * do_right_image - set a flag such that the next html-tag is
1816 * placed inside a right aligned paragraph.
1819 void html_printer::do_right_image (void)
1825 * do_left_image - set a flag such that the next html-tag is
1826 * placed inside a left aligned paragraph.
1829 void html_printer::do_left_image (void)
1835 * exists - returns TRUE if filename exists.
1838 static int exists (const char *filename
)
1840 FILE *fp
= fopen(filename
, "r");
1851 * generate_img_src - returns a html image tag for the filename
1852 * providing that the image exists.
1855 static string
&generate_img_src (const char *filename
)
1857 string
*s
= new string("");
1859 while (filename
&& (filename
[0] == ' ')) {
1862 if (exists(filename
))
1863 *s
+= string("<img src=\"") + filename
+ "\">";
1868 * do_auto_image - tests whether the image, indicated by filename,
1869 * is present, if so then it emits an html image tag.
1870 * An image tag may be passed through from pic, eqn
1871 * but the corresponding image might not be created.
1872 * Consider .EQ delim $$ .EN or an empty .PS .PE.
1875 void html_printer::do_auto_image (text_glob
*g
, const char *filename
)
1877 string buffer
= generate_img_src(filename
);
1879 if (! buffer
.empty()) {
1881 * utilize emit_raw by creating a new text_glob.
1885 h
.text_string
= buffer
.contents();
1886 h
.text_length
= buffer
.length();
1893 * outstanding_eol - call do_eol, n, times.
1896 void html_printer::outstanding_eol (int n
)
1905 * do_title - handle the .tl commands from troff.
1908 void html_printer::do_title (void)
1911 int removed_from_head
;
1914 if (page_number
== 1) {
1915 int found_title_start
= FALSE
;
1916 if (! page_contents
->glyphs
.is_empty()) {
1917 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1919 t
= page_contents
->glyphs
.get_data();
1920 removed_from_head
= FALSE
;
1921 if (t
->is_auto_img()) {
1922 string img
= generate_img_src((char *)(t
->text_string
+ 20));
1924 if (! img
.empty()) {
1925 if (found_title_start
)
1927 found_title_start
= TRUE
;
1928 title
.has_been_found
= TRUE
;
1931 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1932 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1933 (page_contents
->glyphs
.is_equal_to_head()));
1934 } else if (t
->is_eol_ce()) {
1935 /* process the eol associated with .ce
1938 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1939 } else if (t
->is_eol()) {
1940 /* end of title found
1942 title
.has_been_found
= TRUE
;
1943 outstanding_eol(eol_ce
);
1945 } else if (t
->is_a_tag()) {
1946 /* end of title found, but move back so that we read this tag and process it
1948 page_contents
->glyphs
.move_left(); /* move backwards to last word */
1949 title
.has_been_found
= TRUE
;
1950 outstanding_eol(eol_ce
);
1952 } else if (found_title_start
) {
1953 title
.text
+= " " + string(t
->text_string
, t
->text_length
);
1954 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1955 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1956 (page_contents
->glyphs
.is_equal_to_head()));
1958 title
.text
+= string(t
->text_string
, t
->text_length
);
1959 found_title_start
= TRUE
;
1960 title
.has_been_found
= TRUE
;
1961 page_contents
->glyphs
.sub_move_right(); /* move onto next word */
1962 removed_from_head
= ((!page_contents
->glyphs
.is_empty()) &&
1963 (page_contents
->glyphs
.is_equal_to_head()));
1965 } while ((! page_contents
->glyphs
.is_equal_to_head()) || (removed_from_head
));
1967 outstanding_eol(eol_ce
);
1971 void html_printer::write_header (void)
1973 if (! header
.header_buffer
.empty()) {
1974 if (header
.header_level
> 7) {
1975 header
.header_level
= 7;
1978 // firstly we must terminate any font and type faces
1979 current_paragraph
->done_para();
1980 supress_sub_sup
= TRUE
;
1982 if (cutoff_heading
+2 > header
.header_level
) {
1983 // now we save the header so we can issue a list of links
1984 header
.no_of_headings
++;
1987 text_glob
*h
=new text_glob();
1988 h
->text_glob_html(&st
,
1989 header
.headings
.add_string(header
.header_buffer
),
1990 header
.header_buffer
.length(),
1991 header
.no_of_headings
, header
.header_level
,
1992 header
.no_of_headings
, header
.header_level
);
1994 header
.headers
.add(h
,
1995 header
.no_of_headings
,
1996 header
.no_of_headings
, header
.no_of_headings
,
1997 header
.no_of_headings
, header
.no_of_headings
); // and add this header to the header list
1999 // lastly we generate a tag
2001 html
.nl().put_string("<a name=\"");
2002 if (simple_anchors
) {
2003 string
buffer(ANCHOR_TEMPLATE
);
2005 buffer
+= as_string(header
.no_of_headings
);
2007 html
.put_string(buffer
.contents());
2009 html
.put_string(header
.header_buffer
);
2011 html
.put_string("\"></a>").nl();
2014 if (manufacture_headings
) {
2015 // line break before a header
2016 if (!current_paragraph
->emitted_text())
2017 current_paragraph
->do_space();
2018 // user wants manufactured headings which look better than <Hn></Hn>
2019 if (header
.header_level
<4) {
2020 html
.put_string("<b><font size=\"+1\">");
2021 html
.put_string(header
.header_buffer
);
2022 html
.put_string("</font></b>").nl();
2025 html
.put_string("<b>");
2026 html
.put_string(header
.header_buffer
);
2027 html
.put_string("</b>").nl();
2031 // and now we issue the real header
2032 html
.put_string("<h");
2033 html
.put_number(header
.header_level
);
2034 html
.put_string(">");
2035 html
.put_string(header
.header_buffer
);
2036 html
.put_string("</h");
2037 html
.put_number(header
.header_level
);
2038 html
.put_string(">").nl();
2041 /* and now we save the file name in which this header will occur */
2043 style st
; // fake style to enable us to use the list data structure
2045 text_glob
*h
=new text_glob();
2046 h
->text_glob_html(&st
,
2047 header
.headings
.add_string(file_list
.file_name()),
2048 file_list
.file_name().length(),
2049 header
.no_of_headings
, header
.header_level
,
2050 header
.no_of_headings
, header
.header_level
);
2052 header
.header_filename
.add(h
,
2053 header
.no_of_headings
,
2054 header
.no_of_headings
, header
.no_of_headings
,
2055 header
.no_of_headings
, header
.no_of_headings
);
2057 current_paragraph
->do_para(&html
, "", indentation
, pageoffset
, linelength
);
2061 void html_printer::determine_header_level (int level
)
2066 for (i
=0; ((i
<header
.header_buffer
.length())
2067 && ((header
.header_buffer
[i
] == '.')
2068 || is_digit(header
.header_buffer
[i
]))) ; i
++) {
2069 if (header
.header_buffer
[i
] == '.') {
2074 header
.header_level
= level
+1;
2075 if (header
.header_level
== 2) {
2076 header
.no_of_level_one_headings
++;
2077 insert_split_file();
2082 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2085 void html_printer::do_heading (char *arg
)
2089 int level
=atoi(arg
);
2091 header
.header_buffer
.clear();
2092 page_contents
->glyphs
.move_right();
2093 if (! page_contents
->glyphs
.is_equal_to_head()) {
2094 g
= page_contents
->glyphs
.get_data();
2096 if (g
->is_auto_img()) {
2097 string img
=generate_img_src((char *)(g
->text_string
+ 20));
2099 if (! img
.empty()) {
2100 simple_anchors
= TRUE
; // we cannot use full heading anchors with images
2102 header
.header_buffer
+= " ";
2105 header
.header_buffer
+= img
;
2107 } else if (! (g
->is_a_line() || g
->is_a_tag())) {
2109 * we ignore tag commands when constructing a heading
2112 header
.header_buffer
+= " ";
2115 header
.header_buffer
+= string(g
->text_string
, g
->text_length
);
2117 page_contents
->glyphs
.move_right();
2118 g
= page_contents
->glyphs
.get_data();
2119 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
2123 determine_header_level(level
);
2126 // finally set the output to neutral for after the header
2127 g
= page_contents
->glyphs
.get_data();
2128 page_contents
->glyphs
.move_left(); // so that next time we use old g
2132 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
2135 int html_printer::is_courier_until_eol (void)
2137 text_glob
*orig
= page_contents
->glyphs
.get_data();
2141 if (! page_contents
->glyphs
.is_equal_to_tail()) {
2142 page_contents
->glyphs
.move_right();
2144 g
= page_contents
->glyphs
.get_data();
2145 if (! g
->is_a_tag() && (! is_font_courier(g
->text_style
.f
)))
2147 page_contents
->glyphs
.move_right();
2149 (! page_contents
->glyphs
.is_equal_to_head()) &&
2150 (! g
->is_fi()) && (! g
->is_eol()));
2153 * now restore our previous position.
2155 while (page_contents
->glyphs
.get_data() != orig
)
2156 page_contents
->glyphs
.move_left();
2162 * do_linelength - handle the .ll command from troff.
2165 void html_printer::do_linelength (char *arg
)
2167 if (max_linelength
== -1)
2168 max_linelength
= atoi(arg
);
2171 do_indent(indentation
, pageoffset
, atoi(arg
));
2175 * do_pageoffset - handle the .po command from troff.
2178 void html_printer::do_pageoffset (char *arg
)
2181 do_indent(indentation
, atoi(arg
), linelength
);
2185 * do_indentation - handle the .in command from troff.
2188 void html_printer::do_indentation (char *arg
)
2191 do_indent(atoi(arg
), pageoffset
, linelength
);
2195 * do_tempindent - handle the .ti command from troff.
2198 void html_printer::do_tempindent (char *arg
)
2202 prev_indent
= indentation
;
2203 do_indent(atoi(arg
), pageoffset
, linelength
);
2208 * shutdown_table - shuts down the current table.
2211 void html_printer::shutdown_table (void)
2213 if (table
!= NULL
) {
2214 current_paragraph
->done_para();
2215 table
->emit_finish_table();
2216 // dont delete this table as it will be deleted when we destroy the text_glob
2222 * do_indent - remember the indent parameters and if
2223 * indent is > pageoff and indent has changed
2224 * then we start a html table to implement the indentation.
2227 void html_printer::do_indent (int in
, int pageoff
, int linelen
)
2229 if ((indentation
!= -1) &&
2230 (pageoffset
+indentation
!= in
+pageoff
)) {
2232 current_paragraph
->done_para();
2235 pageoffset
= pageoff
;
2236 if (linelen
<= max_linelength
)
2237 linelength
= linelen
;
2239 current_paragraph
->do_para(&html
, "", indentation
, pageoffset
, max_linelength
);
2244 * do_verticalspacing - handle the .vs command from troff.
2247 void html_printer::do_verticalspacing (char *arg
)
2249 vertical_spacing
= atoi(arg
);
2253 * do_pointsize - handle the .ps command from troff.
2256 void html_printer::do_pointsize (char *arg
)
2258 pointsize
= atoi(arg
);
2262 * do_fill - records whether troff has requested that text be filled.
2265 void html_printer::do_fill (int on
)
2267 current_paragraph
->do_break();
2268 output_hpos
= indentation
+pageoffset
;
2269 supress_sub_sup
= TRUE
;
2271 if (fill_on
!= on
) {
2273 current_paragraph
->do_para("");
2275 current_paragraph
->do_pre();
2281 * do_eol - handle the end of line
2284 void html_printer::do_eol (void)
2287 if (current_paragraph
->ever_emitted_text()) {
2288 current_paragraph
->do_newline();
2289 current_paragraph
->do_break();
2292 output_hpos
= indentation
+pageoffset
;
2296 * do_eol_ce - handle end of line specifically for a .ce
2299 void html_printer::do_eol_ce (void)
2301 if (end_center
> 0) {
2303 if (current_paragraph
->emitted_text())
2304 current_paragraph
->do_break();
2307 if (end_center
== 0) {
2308 current_paragraph
->done_para();
2309 supress_sub_sup
= TRUE
;
2315 * do_flush - flushes all output and tags.
2318 void html_printer::do_flush (void)
2320 current_paragraph
->done_para();
2324 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
2327 void html_printer::do_links (void)
2329 current_paragraph
->done_para();
2330 auto_links
= FALSE
; /* from now on only emit under user request */
2331 file_list
.add_new_file(xtmpfile());
2332 file_list
.set_links_required();
2333 html
.set_file(file_list
.get_file());
2337 * insert_split_file -
2340 void html_printer::insert_split_file (void)
2342 if (multiple_files
) {
2343 current_paragraph
->done_para(); // flush paragraph
2344 html
.end_line(); // flush line
2345 html
.set_file(file_list
.get_file()); // flush current file
2346 file_list
.add_new_file(xtmpfile());
2347 string split_file
= job_name
;
2349 split_file
+= string("-");
2350 split_file
+= as_string(header
.no_of_level_one_headings
);
2351 split_file
+= string(".html");
2354 file_list
.set_file_name(split_file
);
2355 html
.set_file(file_list
.get_file());
2360 * do_job_name - assigns the job_name to name.
2363 void html_printer::do_job_name (char *name
)
2365 if (! multiple_files
) {
2366 multiple_files
= TRUE
;
2367 while (name
!= NULL
&& (*name
!= (char)0) && (*name
== ' '))
2374 * do_break - handles the ".br" request and also
2375 * undoes an outstanding ".ti" command.
2378 void html_printer::do_break (void)
2380 current_paragraph
->do_break();
2381 if (end_tempindent
> 0) {
2383 if (end_tempindent
== 0)
2384 do_indent(prev_indent
, pageoffset
, linelength
);
2386 output_hpos
= indentation
+pageoffset
;
2387 supress_sub_sup
= TRUE
;
2391 * do_tab_ts - start a table, which will have already been defined.
2394 void html_printer::do_tab_ts (text_glob
*g
)
2396 html_table
*t
= g
->get_table();
2399 current_paragraph
->done_pre();
2400 current_paragraph
->done_para();
2402 html
.simple_comment("TABS");
2404 t
->set_linelength(max_linelength
);
2405 t
->add_indent(pageoffset
);
2406 t
->emit_table_header(FALSE
);
2413 * do_tab_te - finish a table.
2416 void html_printer::do_tab_te (void)
2419 current_paragraph
->done_para();
2420 table
->emit_finish_table();
2425 if (indentation
> 0) {
2427 * restore indentation
2429 int newin
= indentation
;
2431 do_indent(newin
, pageoffset
, linelength
);
2436 * do_tab - handle the "html-tag:tab" tag
2439 void html_printer::do_tab (char *s
)
2445 int col
= table
->find_column(atoi(s
) + pageoffset
+ indentation
);
2447 current_paragraph
->done_para();
2448 table
->emit_col(col
);
2454 * do_tab0 - handle the "html-tag:tab0" tag
2457 void html_printer::do_tab0 (void)
2460 int col
= table
->find_column(pageoffset
+indentation
);
2462 current_paragraph
->done_para();
2463 table
->emit_col(col
);
2469 * do_col - start column, s.
2472 void html_printer::do_col (char *s
)
2475 current_paragraph
->done_para();
2476 table
->emit_col(atoi(s
));
2481 * troff_tag - processes the troff tag and manipulates the troff state machine.
2484 void html_printer::troff_tag (text_glob
*g
)
2487 * firstly skip over html-tag:
2489 char *t
=(char *)g
->text_string
+9;
2493 } else if (g
->is_eol_ce()) {
2495 } else if (strncmp(t
, ".sp", 3) == 0) {
2496 if (g
->get_arg() > 0)
2497 current_paragraph
->do_space();
2499 current_paragraph
->do_break();
2500 supress_sub_sup
= TRUE
;
2501 } else if (strncmp(t
, ".br", 3) == 0) {
2503 } else if (strcmp(t
, ".centered-image") == 0) {
2504 do_centered_image();
2505 } else if (strcmp(t
, ".right-image") == 0) {
2507 } else if (strcmp(t
, ".left-image") == 0) {
2509 } else if (strncmp(t
, ".auto-image", 11) == 0) {
2510 char *a
= (char *)t
+11;
2511 do_auto_image(g
, a
);
2512 } else if (strncmp(t
, ".ce", 3) == 0) {
2513 char *a
= (char *)t
+3;
2514 supress_sub_sup
= TRUE
;
2516 } else if (strncmp(t
, ".tl", 3) == 0) {
2517 supress_sub_sup
= TRUE
;
2518 title
.with_h1
= TRUE
;
2520 } else if (strncmp(t
, ".html-tl", 8) == 0) {
2521 supress_sub_sup
= TRUE
;
2522 title
.with_h1
= FALSE
;
2524 } else if (strncmp(t
, ".fi", 3) == 0) {
2526 } else if (strncmp(t
, ".nf", 3) == 0) {
2528 } else if ((strncmp(t
, ".SH", 3) == 0) || (strncmp(t
, ".NH", 3) == 0)) {
2529 char *a
= (char *)t
+3;
2531 } else if (strncmp(t
, ".ll", 3) == 0) {
2532 char *a
= (char *)t
+3;
2534 } else if (strncmp(t
, ".po", 3) == 0) {
2535 char *a
= (char *)t
+3;
2537 } else if (strncmp(t
, ".in", 3) == 0) {
2538 char *a
= (char *)t
+3;
2540 } else if (strncmp(t
, ".ti", 3) == 0) {
2541 char *a
= (char *)t
+3;
2543 } else if (strncmp(t
, ".vs", 3) == 0) {
2544 char *a
= (char *)t
+3;
2545 do_verticalspacing(a
);
2546 } else if (strncmp(t
, ".ps", 3) == 0) {
2547 char *a
= (char *)t
+3;
2549 } else if (strcmp(t
, ".links") == 0) {
2551 } else if (strncmp(t
, ".job-name", 9) == 0) {
2552 char *a
= (char *)t
+9;
2554 } else if (strcmp(t
, ".no-auto-rule") == 0) {
2556 } else if (strcmp(t
, ".tab-ts") == 0) {
2558 } else if (strcmp(t
, ".tab-te") == 0) {
2560 } else if (strncmp(t
, ".col ", 5) == 0) {
2561 char *a
= (char *)t
+4;
2563 } else if (strncmp(t
, "tab ", 4) == 0) {
2564 char *a
= (char *)t
+3;
2566 } else if (strncmp(t
, "tab0", 4) == 0) {
2572 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
2575 int html_printer::is_in_middle (int left
, int right
)
2577 return( abs(abs(left
-pageoffset
) - abs(pageoffset
+linelength
-right
)) <= CENTER_TOLERANCE
);
2581 * flush_globs - runs through the text glob list and emits html.
2584 void html_printer::flush_globs (void)
2588 if (! page_contents
->glyphs
.is_empty()) {
2589 page_contents
->glyphs
.start_from_head();
2591 g
= page_contents
->glyphs
.get_data();
2593 if (strcmp(g
->text_string
, "XXXXXXX") == 0)
2596 if (g
->is_a_tag()) {
2598 } else if (g
->is_a_line()) {
2604 * after processing the title (and removing it) the glyph list might be empty
2606 if (! page_contents
->glyphs
.is_empty()) {
2607 page_contents
->glyphs
.move_right();
2609 } while (! page_contents
->glyphs
.is_equal_to_head());
2614 * calc_nf - calculates the _no_ format flag, given the
2618 int html_printer::calc_nf (text_glob
*g
, int nf
)
2630 * calc_po_in - calculates the, in, po, registers
2633 void html_printer::calc_po_in (text_glob
*g
, int nf
)
2636 indentation
= g
->get_arg();
2637 else if (g
->is_po())
2638 pageoffset
= g
->get_arg();
2639 else if (g
->is_ti()) {
2640 prev_indent
= indentation
;
2641 indentation
= g
->get_arg();
2643 } else if (g
->is_br() && ((end_tempindent
> 0) || (nf
&& g
->is_eol()))) {
2645 indentation
= prev_indent
;
2650 * next_horiz_pos - returns the next horiz position.
2651 * -1 is returned if it doesn't exist.
2654 int html_printer::next_horiz_pos (text_glob
*g
, int nf
)
2658 if ((g
!= NULL
) && (g
->is_br() || (nf
&& g
->is_eol())))
2659 if (! page_contents
->glyphs
.is_empty()) {
2660 page_contents
->glyphs
.move_right_get_data();
2662 page_contents
->glyphs
.start_from_head();
2665 page_contents
->glyphs
.move_left();
2672 * insert_tab_ts - inserts a tab-ts before, where.
2675 text_glob
*html_printer::insert_tab_ts (text_glob
*where
)
2677 text_glob
*start_of_table
;
2678 text_glob
*old_pos
= page_contents
->glyphs
.get_data();
2680 page_contents
->glyphs
.move_to(where
);
2681 page_contents
->glyphs
.move_left();
2682 page_contents
->insert_tag(string("html-tag:.tab-ts")); // tab table start
2683 page_contents
->glyphs
.move_right();
2684 start_of_table
= page_contents
->glyphs
.get_data();
2685 page_contents
->glyphs
.move_to(old_pos
);
2686 return start_of_table
;
2690 * insert_tab_te - inserts a tab-te before the current position
2691 * (it skips backwards over .sp/.br)
2694 void html_printer::insert_tab_te (void)
2696 text_glob
*g
= page_contents
->glyphs
.get_data();
2697 page_contents
->dump_page();
2699 while (page_contents
->glyphs
.get_data()->is_a_tag())
2700 page_contents
->glyphs
.move_left();
2702 page_contents
->insert_tag(string("html-tag:.tab-te")); // tab table end
2703 while (g
!= page_contents
->glyphs
.get_data())
2704 page_contents
->glyphs
.move_right();
2705 page_contents
->dump_page();
2709 * insert_tab_0 - inserts a tab0 before, where.
2712 void html_printer::insert_tab_0 (text_glob
*where
)
2714 text_glob
*old_pos
= page_contents
->glyphs
.get_data();
2716 page_contents
->glyphs
.move_to(where
);
2717 page_contents
->glyphs
.move_left();
2718 page_contents
->insert_tag(string("html-tag:tab0")); // tab0 start of line
2719 page_contents
->glyphs
.move_right();
2720 page_contents
->glyphs
.move_to(old_pos
);
2724 * remove_tabs - removes the tabs tags on this line.
2727 void html_printer::remove_tabs (void)
2729 text_glob
*orig
= page_contents
->glyphs
.get_data();
2732 if (! page_contents
->glyphs
.is_equal_to_tail()) {
2734 g
= page_contents
->glyphs
.get_data();
2736 page_contents
->glyphs
.sub_move_right();
2738 orig
= page_contents
->glyphs
.get_data();
2740 page_contents
->glyphs
.move_right();
2741 } while ((! page_contents
->glyphs
.is_equal_to_head()) &&
2745 * now restore our previous position.
2747 while (page_contents
->glyphs
.get_data() != orig
)
2748 page_contents
->glyphs
.move_left();
2752 void html_printer::remove_courier_tabs (void)
2755 int line_start
= TRUE
;
2758 if (! page_contents
->glyphs
.is_empty()) {
2759 page_contents
->glyphs
.start_from_head();
2762 g
= page_contents
->glyphs
.get_data();
2764 nf
= calc_nf(g
, nf
);
2767 if (line_start
&& nf
&& is_courier_until_eol()) {
2769 g
= page_contents
->glyphs
.get_data();
2773 line_start
= g
->is_br() || g
->is_nf() || g
->is_fi() || (nf
&& g
->is_eol());
2774 page_contents
->glyphs
.move_right();
2775 } while (! page_contents
->glyphs
.is_equal_to_head());
2779 void html_printer::insert_tab0_foreach_tab (void)
2781 text_glob
*start_of_line
= NULL
;
2782 text_glob
*g
= NULL
;
2783 int seen_tab
= FALSE
;
2784 int seen_col
= FALSE
;
2787 if (! page_contents
->glyphs
.is_empty()) {
2788 page_contents
->glyphs
.start_from_head();
2789 start_of_line
= page_contents
->glyphs
.get_data();
2791 g
= page_contents
->glyphs
.get_data();
2793 nf
= calc_nf(g
, nf
);
2801 if (g
->is_br() || (nf
&& g
->is_eol())) {
2803 page_contents
->glyphs
.move_right();
2804 g
= page_contents
->glyphs
.get_data();
2805 nf
= calc_nf(g
, nf
);
2806 if (page_contents
->glyphs
.is_equal_to_head()) {
2807 if (seen_tab
&& !seen_col
)
2808 insert_tab_0(start_of_line
);
2811 } while (g
->is_br() || (nf
&& g
->is_eol()) || g
->is_ta());
2812 // printf("\nstart_of_line is: %s\n", g->text_string);
2813 if (seen_tab
&& !seen_col
) {
2814 insert_tab_0(start_of_line
);
2815 page_contents
->glyphs
.move_to(g
);
2822 page_contents
->glyphs
.move_right();
2823 } while (! page_contents
->glyphs
.is_equal_to_head());
2824 if (seen_tab
&& !seen_col
)
2825 insert_tab_0(start_of_line
);
2831 * update_min_max - updates the extent of a column, given the left and right
2832 * extents of a glyph, g.
2835 void html_printer::update_min_max (colType type_of_col
, int *minimum
, int *maximum
, text_glob
*g
)
2837 switch (type_of_col
) {
2854 * add_table_end - moves left one glyph, adds a table end tag and adds a
2858 void html_printer::add_table_end (const char *
2859 #if defined(DEBUG_TABLES)
2864 page_contents
->glyphs
.move_left();
2866 #if defined(DEBUG_TABLES)
2867 page_contents
->insert_tag(string(debug_string
));
2872 * lookahead_for_tables - checks for .col tags and inserts table start/end tags
2875 void html_printer::lookahead_for_tables (void)
2878 text_glob
*start_of_line
= NULL
;
2879 text_glob
*start_of_table
= NULL
;
2880 text_glob
*last
= NULL
;
2881 colType type_of_col
= none
;
2883 int found_col
= FALSE
;
2884 int seen_text
= FALSE
;
2888 html_table
*table
= new html_table(&html
, -1);
2889 const char *tab_defs
= NULL
;
2892 int old_pageoffset
= pageoffset
;
2894 remove_courier_tabs();
2895 page_contents
->dump_page();
2896 insert_tab0_foreach_tab();
2897 page_contents
->dump_page();
2898 if (! page_contents
->glyphs
.is_empty()) {
2899 page_contents
->glyphs
.start_from_head();
2900 g
= page_contents
->glyphs
.get_data();
2902 #if defined(DEBUG_TABLES)
2903 fprintf(stderr
, " [") ;
2904 fprintf(stderr
, g
->text_string
) ;
2905 fprintf(stderr
, "] ") ;
2907 if (strcmp(g
->text_string
, "XXXXXXX") == 0)
2911 nf
= calc_nf(g
, nf
);
2914 if (type_of_col
== tab_tag
&& start_of_table
!= NULL
) {
2915 page_contents
->glyphs
.move_left();
2917 start_of_table
->remember_table(table
);
2918 table
= new html_table(&html
, -1);
2919 page_contents
->insert_tag(string("*** TAB -> COL ***"));
2920 if (tab_defs
!= NULL
)
2921 table
->tab_stops
->init(tab_defs
);
2922 start_of_table
= NULL
;
2925 type_of_col
= col_tag
;
2927 ncol
= g
->get_arg();
2931 } else if (g
->is_tab()) {
2932 type_of_col
= tab_tag
;
2933 colmin
= g
->get_tab_args(&align
);
2934 align
= 'L'; // for now as 'C' and 'R' are broken
2935 ncol
= table
->find_tab_column(colmin
);
2936 colmin
+= pageoffset
+ indentation
;
2937 colmax
= table
->get_tab_pos(ncol
+1);
2939 colmax
+= pageoffset
+ indentation
;
2940 } else if (g
->is_tab0()) {
2941 if (type_of_col
== col_tag
&& start_of_table
!= NULL
) {
2942 page_contents
->glyphs
.move_left();
2944 start_of_table
->remember_table(table
);
2945 table
= new html_table(&html
, -1);
2946 page_contents
->insert_tag(string("*** COL -> TAB ***"));
2947 start_of_table
= NULL
;
2950 if (tab_defs
!= NULL
)
2951 table
->tab_stops
->init(tab_defs
);
2953 type_of_col
= tab0_tag
;
2956 colmax
= table
->get_tab_pos(2) + pageoffset
+ indentation
;
2957 } else if (! g
->is_a_tag())
2958 update_min_max(type_of_col
, &colmin
, &colmax
, g
);
2960 if ((! g
->is_a_tag()) || g
->is_tab())
2963 if ((g
->is_col() || g
->is_tab() || g
->is_tab0())
2964 && (start_of_line
!= NULL
) && (start_of_table
== NULL
)) {
2965 start_of_table
= insert_tab_ts(start_of_line
);
2966 start_of_line
= NULL
;
2968 } else if (g
->is_ce() && (start_of_table
!= NULL
)) {
2969 add_table_end("*** CE ***");
2970 start_of_table
->remember_table(table
);
2971 start_of_table
= NULL
;
2973 } else if (g
->is_ta()) {
2974 tab_defs
= g
->text_string
;
2975 if (!table
->tab_stops
->compatible(tab_defs
)) {
2976 if (start_of_table
!= NULL
) {
2977 add_table_end("*** TABS ***");
2978 start_of_table
->remember_table(table
);
2979 table
= new html_table(&html
, -1);
2980 start_of_table
= NULL
;
2984 table
->tab_stops
->init(tab_defs
);
2988 if (((! g
->is_a_tag()) || g
->is_tab()) && (start_of_table
!= NULL
)) {
2989 // we are in a table and have a glyph
2990 if ((ncol
== 0) || (! table
->add_column(ncol
, colmin
, colmax
, align
))) {
2992 add_table_end("*** NCOL == 0 ***");
2994 add_table_end("*** CROSSED COLS ***");
2996 start_of_table
->remember_table(table
);
2997 table
= new html_table(&html
, -1);
2998 start_of_table
= NULL
;
3005 * move onto next glob, check whether we are starting a new line
3007 g
= page_contents
->glyphs
.move_right_get_data();
3011 page_contents
->glyphs
.start_from_head();
3015 } else if (g
->is_br() || (nf
&& g
->is_eol())) {
3017 g
= page_contents
->glyphs
.move_right_get_data();
3018 nf
= calc_nf(g
, nf
);
3019 } while ((g
!= NULL
) && (g
->is_br() || (nf
&& g
->is_eol())));
3023 left
= next_horiz_pos(g
, nf
);
3028 } while ((g
!= NULL
) && (! page_contents
->glyphs
.is_equal_to_head()));
3030 #if defined(DEBUG_TABLES)
3031 fprintf(stderr
, "finished scanning for tables\n");
3034 page_contents
->glyphs
.start_from_head();
3035 if (start_of_table
!= NULL
) {
3037 while (last
!= page_contents
->glyphs
.get_data())
3038 page_contents
->glyphs
.move_left();
3041 start_of_table
->remember_table(table
);
3043 page_contents
->insert_tag(string("*** LAST ***"));
3049 // and reset the registers
3050 pageoffset
= old_pageoffset
;
3056 void html_printer::flush_page (void)
3058 supress_sub_sup
= TRUE
;
3060 page_contents
->dump_page();
3061 lookahead_for_tables();
3062 page_contents
->dump_page();
3065 current_paragraph
->done_para();
3067 // move onto a new page
3068 delete page_contents
;
3069 #if defined(DEBUG_TABLES)
3070 fprintf(stderr
, "\n\n*** flushed page ***\n\n");
3072 html
.simple_comment("new page called");
3074 page_contents
= new page
;
3078 * determine_space - works out whether we need to write a space.
3079 * If last glyph is ajoining then no space emitted.
3082 void html_printer::determine_space (text_glob
*g
)
3084 if (current_paragraph
->is_in_pre()) {
3086 * .nf has been specified
3088 while (output_hpos
< g
->minh
) {
3089 output_hpos
+= space_width
;
3090 current_paragraph
->emit_space();
3093 if ((output_vpos
!= g
->minv
) || (output_hpos
< g
->minh
)) {
3094 current_paragraph
->emit_space();
3100 * is_font_courier - returns TRUE if the font, f, is courier.
3103 int html_printer::is_font_courier (font
*f
)
3106 const char *fontname
= f
->get_name();
3108 return( (fontname
!= 0) && (fontname
[0] == 'C') );
3114 * end_font - shuts down the font corresponding to fontname.
3117 void html_printer::end_font (const char *fontname
)
3119 if (strcmp(fontname
, "B") == 0) {
3120 current_paragraph
->done_bold();
3121 } else if (strcmp(fontname
, "I") == 0) {
3122 current_paragraph
->done_italic();
3123 } else if (strcmp(fontname
, "BI") == 0) {
3124 current_paragraph
->done_bold();
3125 current_paragraph
->done_italic();
3126 } else if (strcmp(fontname
, "CR") == 0) {
3127 current_paragraph
->done_tt();
3128 } else if (strcmp(fontname
, "CI") == 0) {
3129 current_paragraph
->done_italic();
3130 current_paragraph
->done_tt();
3131 } else if (strcmp(fontname
, "CB") == 0) {
3132 current_paragraph
->done_bold();
3133 current_paragraph
->done_tt();
3134 } else if (strcmp(fontname
, "CBI") == 0) {
3135 current_paragraph
->done_bold();
3136 current_paragraph
->done_italic();
3137 current_paragraph
->done_tt();
3142 * start_font - starts the font corresponding to name.
3145 void html_printer::start_font (const char *fontname
)
3147 if (strcmp(fontname
, "R") == 0) {
3148 current_paragraph
->done_bold();
3149 current_paragraph
->done_italic();
3150 current_paragraph
->done_tt();
3151 } else if (strcmp(fontname
, "B") == 0) {
3152 current_paragraph
->do_bold();
3153 } else if (strcmp(fontname
, "I") == 0) {
3154 current_paragraph
->do_italic();
3155 } else if (strcmp(fontname
, "BI") == 0) {
3156 current_paragraph
->do_bold();
3157 current_paragraph
->do_italic();
3158 } else if (strcmp(fontname
, "CR") == 0) {
3159 if ((! fill_on
) && (is_courier_until_eol())) {
3160 current_paragraph
->do_pre();
3162 current_paragraph
->do_tt();
3163 } else if (strcmp(fontname
, "CI") == 0) {
3164 if ((! fill_on
) && (is_courier_until_eol())) {
3165 current_paragraph
->do_pre();
3167 current_paragraph
->do_tt();
3168 current_paragraph
->do_italic();
3169 } else if (strcmp(fontname
, "CB") == 0) {
3170 if ((! fill_on
) && (is_courier_until_eol())) {
3171 current_paragraph
->do_pre();
3173 current_paragraph
->do_tt();
3174 current_paragraph
->do_bold();
3175 } else if (strcmp(fontname
, "CBI") == 0) {
3176 if ((! fill_on
) && (is_courier_until_eol())) {
3177 current_paragraph
->do_pre();
3179 current_paragraph
->do_tt();
3180 current_paragraph
->do_italic();
3181 current_paragraph
->do_bold();
3186 * start_size - from is old font size, to is the new font size.
3187 * The html increase <big> and <small> decrease alters the
3188 * font size by 20%. We try and map these onto glyph sizes.
3191 void html_printer::start_size (int from
, int to
)
3195 current_paragraph
->do_big();
3196 from
+= SIZE_INCREMENT
;
3198 } else if (from
> to
) {
3200 current_paragraph
->do_small();
3201 from
-= SIZE_INCREMENT
;
3207 * do_font - checks to see whether we need to alter the html font.
3210 void html_printer::do_font (text_glob
*g
)
3213 * check if the output_style.point_size has not been set yet
3214 * this allow users to place .ps at the top of their troff files
3215 * and grohtml can then treat the .ps value as the base font size (3)
3217 if (output_style
.point_size
== -1) {
3218 output_style
.point_size
= pointsize
;
3221 if (g
->text_style
.f
!= output_style
.f
) {
3222 if (output_style
.f
!= 0) {
3223 end_font(output_style
.f
->get_name());
3225 output_style
.f
= g
->text_style
.f
;
3226 if (output_style
.f
!= 0) {
3227 start_font(output_style
.f
->get_name());
3230 if (output_style
.point_size
!= g
->text_style
.point_size
) {
3232 if ((output_style
.point_size
> 0) &&
3233 (g
->text_style
.point_size
> 0)) {
3234 start_size(output_style
.point_size
, g
->text_style
.point_size
);
3236 if (g
->text_style
.point_size
> 0) {
3237 output_style
.point_size
= g
->text_style
.point_size
;
3240 if (output_style
.col
!= g
->text_style
.col
) {
3241 current_paragraph
->done_color();
3242 output_style
.col
= g
->text_style
.col
;
3243 current_paragraph
->do_color(&output_style
.col
);
3248 * start_subscript - returns TRUE if, g, looks like a subscript start.
3251 int html_printer::start_subscript (text_glob
*g
)
3254 int height
= output_style
.point_size
*r
/72;
3256 return( (output_style
.point_size
!= 0) &&
3257 (output_vpos
< g
->minv
) &&
3258 (output_vpos
-height
> g
->maxv
) &&
3259 (output_style
.point_size
> g
->text_style
.point_size
) );
3263 * start_superscript - returns TRUE if, g, looks like a superscript start.
3266 int html_printer::start_superscript (text_glob
*g
)
3269 int height
= output_style
.point_size
*r
/72;
3271 return( (output_style
.point_size
!= 0) &&
3272 (output_vpos
> g
->minv
) &&
3273 (output_vpos
-height
< g
->maxv
) &&
3274 (output_style
.point_size
> g
->text_style
.point_size
) );
3278 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
3281 int html_printer::end_subscript (text_glob
*g
)
3284 int height
= output_style
.point_size
*r
/72;
3286 return( (output_style
.point_size
!= 0) &&
3287 (g
->minv
< output_vpos
) &&
3288 (output_vpos
-height
> g
->maxv
) &&
3289 (output_style
.point_size
< g
->text_style
.point_size
) );
3293 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
3296 int html_printer::end_superscript (text_glob
*g
)
3299 int height
= output_style
.point_size
*r
/72;
3301 return( (output_style
.point_size
!= 0) &&
3302 (g
->minv
> output_vpos
) &&
3303 (output_vpos
-height
< g
->maxv
) &&
3304 (output_style
.point_size
< g
->text_style
.point_size
) );
3308 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
3309 * start/end and it calls the services of html-text to issue the
3313 void html_printer::do_sup_or_sub (text_glob
*g
)
3315 if (! supress_sub_sup
) {
3316 if (start_subscript(g
)) {
3317 current_paragraph
->do_sub();
3318 } else if (start_superscript(g
)) {
3319 current_paragraph
->do_sup();
3320 } else if (end_subscript(g
)) {
3321 current_paragraph
->done_sub();
3322 } else if (end_superscript(g
)) {
3323 current_paragraph
->done_sup();
3329 * emit_html - write out the html text
3332 void html_printer::emit_html (text_glob
*g
)
3336 current_paragraph
->do_emittext(g
->text_string
, g
->text_length
);
3337 output_vpos
= g
->minv
;
3338 output_hpos
= g
->maxh
;
3339 output_vpos_max
= g
->maxv
;
3340 supress_sub_sup
= FALSE
;
3344 * flush_sbuf - flushes the current sbuf into the list of glyphs.
3347 void html_printer::flush_sbuf()
3349 if (sbuf
.length() > 0) {
3350 int r
=font::res
; // resolution of the device
3351 set_style(sbuf_style
);
3352 if (overstrike_detected
&& (! is_bold(sbuf_style
.f
))) {
3353 font
*bold_font
= make_bold(sbuf_style
.f
);
3354 if (bold_font
!= NULL
)
3355 sbuf_style
.f
= bold_font
;
3358 page_contents
->add(&sbuf_style
, sbuf
,
3360 sbuf_vpos
-sbuf_style
.point_size
*r
/72, sbuf_start_hpos
,
3361 sbuf_vpos
, sbuf_end_hpos
);
3363 output_hpos
= sbuf_end_hpos
;
3364 output_vpos
= sbuf_vpos
;
3365 last_sbuf_length
= 0;
3366 sbuf_prev_hpos
= sbuf_end_hpos
;
3367 overstrike_detected
= FALSE
;
3372 void html_printer::set_line_thickness(const environment
*env
)
3374 line_thickness
= env
->size
;
3377 void html_printer::draw(int code
, int *p
, int np
, const environment
*env
)
3384 page_contents
->add_line(&sbuf_style
,
3386 env
->hpos
, env
->vpos
, env
->hpos
+p
[0], env
->vpos
+p
[1], line_thickness
);
3388 error("2 arguments required for line");
3395 line_thickness
= -1;
3397 // troff gratuitously adds an extra 0
3398 if (np
!= 1 && np
!= 2) {
3399 error("0 or 1 argument required for thickness");
3402 line_thickness
= p
[0];
3426 // fill with color env->fill
3427 if (background
!= NULL
)
3429 background
= new color
;
3430 *background
= *env
->fill
;
3434 error("unrecognised drawing command `%1'", char(code
));
3439 html_printer::html_printer()
3440 : html(0, MAX_LINE_LENGTH
),
3441 no_of_printed_pages(0),
3442 last_sbuf_length(0),
3443 overstrike_detected(FALSE
),
3446 output_vpos_max(-1),
3448 inside_font_style(0),
3451 supress_sub_sup(TRUE
),
3452 cutoff_heading(100),
3466 background(default_background
)
3468 file_list
.add_new_file(xtmpfile());
3469 html
.set_file(file_list
.get_file());
3470 if (font::hor
!= 24)
3471 fatal("horizontal resolution must be 24");
3472 if (font::vert
!= 40)
3473 fatal("vertical resolution must be 40");
3475 // should be sorted html..
3476 if (font::res
% (font::sizescale
*72) != 0)
3477 fatal("res must be a multiple of 72*sizescale");
3481 while (r
% 10 == 0) {
3486 html
.set_fixed_point(point
);
3487 space_char_index
= font::name_to_index("space");
3488 space_width
= font::hor
;
3489 paper_length
= font::paperlength
;
3490 linelength
= font::res
*13/2;
3491 if (paper_length
== 0)
3492 paper_length
= 11*font::res
;
3494 page_contents
= new page();
3498 * add_to_sbuf - adds character code or name to the sbuf.
3501 void html_printer::add_to_sbuf (int index
, const string
&s
)
3503 if (sbuf_style
.f
== NULL
)
3506 char *html_glyph
= NULL
;
3507 unsigned int code
= sbuf_style
.f
->get_code(index
);
3510 if (sbuf_style
.f
->contains(index
))
3511 html_glyph
= (char *)sbuf_style
.f
->get_special_device_encoding(index
);
3515 if ((html_glyph
== NULL
) && (code
>= UNICODE_DESC_START
))
3516 html_glyph
= to_unicode(code
);
3518 html_glyph
= get_html_translation(sbuf_style
.f
, s
);
3520 last_sbuf_length
= sbuf
.length();
3521 if (html_glyph
== NULL
)
3522 sbuf
+= ((char)code
);
3527 int html_printer::sbuf_continuation (int index
, const char *name
,
3528 const environment
*env
, int w
)
3531 * lets see whether the glyph is closer to the end of sbuf
3533 if ((sbuf_end_hpos
== env
->hpos
)
3534 || ((sbuf_prev_hpos
< sbuf_end_hpos
)
3535 && (env
->hpos
< sbuf_end_hpos
)
3536 && ((sbuf_end_hpos
-env
->hpos
< env
->hpos
-sbuf_prev_hpos
)))) {
3537 add_to_sbuf(index
, name
);
3538 sbuf_prev_hpos
= sbuf_end_hpos
;
3539 sbuf_end_hpos
+= w
+ sbuf_kern
;
3542 if ((env
->hpos
>= sbuf_end_hpos
) &&
3543 ((sbuf_kern
== 0) || (sbuf_end_hpos
- sbuf_kern
!= env
->hpos
))) {
3545 * lets see whether a space is needed or not
3548 if (env
->hpos
-sbuf_end_hpos
< space_width
) {
3549 add_to_sbuf(index
, name
);
3550 sbuf_prev_hpos
= sbuf_end_hpos
;
3551 sbuf_end_hpos
= env
->hpos
+ w
;
3560 * get_html_translation - given the position of the character and its name
3561 * return the device encoding for such character.
3564 char *get_html_translation (font
*f
, const string
&name
)
3568 if ((f
== 0) || name
.empty())
3571 index
= f
->name_to_index((char *)(name
+ '\0').contents());
3573 error("character `%s' not found", (name
+ '\0').contents());
3576 if (f
->contains(index
))
3577 return (char *)f
->get_special_device_encoding(index
);
3584 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
3585 * a previous glyph in sbuf.
3586 * If TRUE the font is changed to bold and the previous sbuf
3590 int html_printer::overstrike(int index
, const char *name
, const environment
*env
, int w
)
3592 if ((env
->hpos
< sbuf_end_hpos
)
3593 || ((sbuf_kern
!= 0) && (sbuf_end_hpos
- sbuf_kern
< env
->hpos
))) {
3595 * at this point we have detected an overlap
3597 if (overstrike_detected
) {
3598 /* already detected, remove previous glyph and use this glyph */
3599 sbuf
.set_length(last_sbuf_length
);
3600 add_to_sbuf(index
, name
);
3601 sbuf_end_hpos
= env
->hpos
+ w
;
3604 /* first time we have detected an overstrike in the sbuf */
3605 sbuf
.set_length(last_sbuf_length
); /* remove previous glyph */
3606 if (! is_bold(sbuf_style
.f
))
3608 overstrike_detected
= TRUE
;
3609 add_to_sbuf(index
, name
);
3610 sbuf_end_hpos
= env
->hpos
+ w
;
3618 * set_char - adds a character into the sbuf if it is a continuation with the previous
3619 * word otherwise flush the current sbuf and add character anew.
3622 void html_printer::set_char(int i
, font
*f
, const environment
*env
, int w
, const char *name
)
3624 style
sty(f
, env
->size
, env
->height
, env
->slant
, env
->fontno
, *env
->col
);
3625 if (sty
.slant
!= 0) {
3626 if (sty
.slant
> 80 || sty
.slant
< -80) {
3627 error("silly slant `%1' degrees", sty
.slant
);
3631 if (((! sbuf
.empty()) && (sty
== sbuf_style
) && (sbuf_vpos
== env
->vpos
))
3632 && (sbuf_continuation(i
, name
, env
, w
) || overstrike(i
, name
, env
, w
)))
3636 add_to_sbuf(i
, name
);
3637 sbuf_end_hpos
= env
->hpos
+ w
;
3638 sbuf_start_hpos
= env
->hpos
;
3639 sbuf_prev_hpos
= env
->hpos
;
3640 sbuf_vpos
= env
->vpos
;
3646 * set_numbered_char - handle numbered characters.
3647 * Negative values are interpreted as unbreakable spaces;
3648 * the value (taken positive) gives the width.
3651 void html_printer::set_numbered_char(int num
, const environment
*env
,
3657 num
= 160; //
3659 int i
= font::number_to_index(num
);
3660 int fn
= env
->fontno
;
3661 if (fn
< 0 || fn
>= nfonts
) {
3662 error("bad font position `%1'", fn
);
3665 font
*f
= font_table
[fn
];
3667 error("no font mounted at `%1'", fn
);
3670 if (!f
->contains(i
)) {
3671 error("font `%1' does not contain numbered character %2",
3680 w
= f
->get_width(i
, env
->size
);
3683 set_char(i
, f
, env
, w
, 0);
3687 * write_title - writes the title to this document
3690 void html_printer::write_title (int in_head
)
3692 if (title
.has_been_found
) {
3694 html
.put_string("<title>");
3695 html
.put_string(title
.text
);
3696 html
.put_string("</title>").nl().nl();
3698 title
.has_been_written
= TRUE
;
3699 if (title
.with_h1
) {
3700 html
.put_string("<h1 align=center>");
3701 html
.put_string(title
.text
);
3702 html
.put_string("</h1>").nl().nl();
3705 } else if (in_head
) {
3706 // place empty title tags to help conform to `tidy'
3707 html
.put_string("<title></title>").nl();
3712 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
3715 static void write_rule (void)
3718 fputs("<hr>\n", stdout
);
3721 void html_printer::begin_page(int n
)
3724 #if defined(DEBUGGING)
3725 html
.begin_comment("Page: ").put_string(i_to_a(page_number
)).end_comment();;
3727 no_of_printed_pages
++;
3730 output_style
.point_size
= -1;
3731 output_space_code
= 32;
3732 output_draw_point_size
= -1;
3733 output_line_thickness
= -1;
3736 output_vpos_max
= -1;
3737 current_paragraph
= new html_text(&html
);
3738 do_indent(indentation
, pageoffset
, linelength
);
3739 current_paragraph
->do_para("");
3742 void html_printer::end_page(int)
3748 font
*html_printer::make_font(const char *nm
)
3750 return html_font::load_html_font(nm
);
3753 void html_printer::do_body (void)
3755 if (background
== NULL
)
3756 fputs("<body>\n\n", stdout
);
3758 unsigned int r
, g
, b
;
3761 background
->get_rgb(&r
, &g
, &b
);
3762 // we have to scale 0..0xFFFF to 0..0xFF
3763 sprintf(buf
, "%.2X%.2X%.2X", r
/0x101, g
/0x101, b
/0x101);
3765 fputs("<body bgcolor=\"#", stdout
);
3767 fputs("\">\n\n", stdout
);
3772 * emit_link - generates: <a href="to">name</a>
3775 void html_printer::emit_link (const string
&to
, const char *name
)
3777 fputs("<a href=\"", stdout
);
3778 fputs(to
.contents(), stdout
);
3779 fputs("\">", stdout
);
3780 fputs(name
, stdout
);
3781 fputs("</a>", stdout
);
3785 * write_navigation - writes out the links which navigate between
3789 void html_printer::write_navigation (const string
&top
, const string
&prev
,
3790 const string
&next
, const string
¤t
)
3792 int need_bar
= FALSE
;
3794 if (multiple_files
) {
3796 fputs("[ ", stdout
);
3797 if (prev
!= "" && prev
!= top
) {
3798 emit_link(prev
, "prev");
3801 if (next
!= "" && next
!= top
) {
3803 fputs(" | ", stdout
);
3804 emit_link(next
, "next");
3807 if (top
!= "<standard input>" && top
!= "" && top
!= current
) {
3809 fputs(" | ", stdout
);
3810 emit_link(top
, "top");
3811 fputs(" ]\n", stdout
);
3818 * do_file_components - scan the file list copying each temporary file in turn.
3819 * This is used twofold:
3821 * firstly to emit section heading links, between file fragments if required
3822 * and secondly to generate jobname file fragments if required.
3825 void html_printer::do_file_components (void)
3827 int fragment_no
= 1;
3833 file_list
.start_of_list();
3834 top
= string(job_name
);
3835 top
+= string(".html");
3837 next
= file_list
.next_file_name();
3840 while (file_list
.get_file() != 0) {
3841 if (fseek(file_list
.get_file(), 0L, 0) < 0)
3842 fatal("fseek on temporary file failed");
3843 html
.copy_file(file_list
.get_file());
3844 fclose(file_list
.get_file());
3846 file_list
.move_next();
3847 if (file_list
.is_new_output_file()) {
3848 if (fragment_no
> 1)
3849 write_navigation(top
, prev
, next
, current
);
3852 next
= file_list
.next_file_name();
3854 string split_file
= file_list
.file_name();
3857 freopen(split_file
.contents(), "w", stdout
);
3859 write_navigation(top
, prev
, next
, current
);
3861 if (file_list
.are_links_required())
3862 header
.write_headings(stdout
, TRUE
);
3864 if (fragment_no
> 1)
3865 write_navigation(top
, prev
, next
, current
);
3870 html_printer::~html_printer()
3872 #ifdef LONG_FOR_TIME_T
3878 current_paragraph
->flush_text();
3880 html
.set_file(stdout
);
3881 html
.begin_comment("Creator : ")
3882 .put_string("groff ")
3883 .put_string("version ")
3884 .put_string(Version_string
)
3888 html
.begin_comment("CreationDate: ")
3889 .put_string(ctime(&t
), strlen(ctime(&t
))-1)
3892 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout
);
3893 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout
);
3895 fputs("<html>\n", stdout
);
3896 fputs("<head>\n", stdout
);
3897 fputs("<meta name=\"generator\" "
3898 "content=\"groff -Thtml, see www.gnu.org\">\n", stdout
);
3899 fputs("<meta http-equiv=\"Content-Type\" "
3900 "content=\"text/html; charset=US-ASCII\">\n", stdout
);
3901 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout
);
3903 fputs("</head>\n", stdout
);
3907 header
.write_headings(stdout
, FALSE
);
3909 #if defined(DEBUGGING)
3910 html
.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages
)).end_comment();
3915 if (multiple_files
) {
3916 fputs("</body>\n", stdout
);
3917 fputs("</html>\n", stdout
);
3918 do_file_components();
3920 do_file_components();
3921 fputs("</body>\n", stdout
);
3922 fputs("</html>\n", stdout
);
3927 * special - handle all x X requests from troff. For post-html they allow users
3928 * to pass raw html commands, turn auto linked headings off/on and
3929 * also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
3932 void html_printer::special(char *s
, const environment
*env
, char type
)
3938 if (env
->fontno
>= 0) {
3939 style
sty(get_font_from_index(env
->fontno
), env
->size
, env
->height
,
3940 env
->slant
, env
->fontno
, *env
->col
);
3944 if (strncmp(s
, "html:", 5) == 0) {
3945 int r
=font::res
; /* resolution of the device */
3946 font
*f
=sbuf_style
.f
;
3951 f
= font::load_font("TR", &found
);
3955 * need to pass rest of string through to html output during flush
3957 page_contents
->add_and_encode(&sbuf_style
, string(&s
[5]),
3959 env
->vpos
-env
->size
*r
/72, env
->hpos
,
3960 env
->vpos
, env
->hpos
);
3963 * assume that the html command has no width, if it does then hopefully troff
3964 * will have fudged this in a macro by requesting that the formatting move right by
3965 * the appropriate amount.
3967 } else if (strncmp(s
, "index:", 6) == 0) {
3968 cutoff_heading
= atoi(&s
[6]);
3969 } else if (strncmp(s
, "html-tag:", 9) == 0) {
3970 int r
=font::res
; /* resolution of the device */
3972 page_contents
->add_tag(&sbuf_style
, string(s
),
3974 env
->vpos
-env
->size
*r
/72, env
->hpos
,
3975 env
->vpos
, env
->hpos
);
3980 int main(int argc
, char **argv
)
3982 program_name
= argv
[0];
3983 static char stderr_buf
[BUFSIZ
];
3984 setbuf(stderr
, stderr_buf
);
3986 static const struct option long_options
[] = {
3987 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
3988 { "version", no_argument
, 0, 'v' },
3991 while ((c
= getopt_long(argc
, argv
, "a:g:o:i:I:j:D:F:vbdhlrnp", long_options
, NULL
))
3995 printf("GNU post-grohtml (groff) version %s\n", Version_string
);
3999 /* text antialiasing bits - handled by pre-html */
4002 /* graphic antialiasing bits - handled by pre-html */
4005 // set background color to white
4006 default_background
= new color
;
4007 default_background
->set_gray(color::MAX_COLOR_VAL
);
4010 font::command_line_font_dir(optarg
);
4013 multiple_files
= TRUE
;
4023 /* handled by pre-html */
4026 /* do not use the Hn headings of html, but manufacture our own */
4027 manufacture_headings
= TRUE
;
4030 /* handled by pre-html */
4033 /* handled by pre-html */
4036 /* handled by pre-html */
4039 /* handled by pre-html */
4042 /* handled by pre-html */
4045 simple_anchors
= TRUE
;
4047 case CHAR_MAX
+ 1: // --help
4058 if (optind
>= argc
) {
4061 for (int i
= optind
; i
< argc
; i
++)
4067 static void usage(FILE *stream
)
4069 fprintf(stream
, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",