Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / devices / grohtml / post-html.cpp
blob1283e06139a780a1dc44f0fb0a5f5a30853fe821
1 // -*- C++ -*-
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.
8 */
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
16 version.
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
21 for more details.
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. */
27 #include "driver.h"
28 #include "stringclass.h"
29 #include "cset.h"
30 #include "html.h"
31 #include "html-text.h"
32 #include "html-table.h"
34 #include <time.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
40 #include <stdio.h>
41 #include <fcntl.h>
42 #include <string.h>
44 extern "C" const char *Version_string;
46 #if !defined(TRUE)
47 # define TRUE (1==1)
48 #endif
49 #if !defined(FALSE)
50 # define FALSE (1==0)
51 #endif
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;
63 #undef DEBUG_TABLES
64 // #define DEBUG_TABLES
67 * prototypes
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 */
86 /* heading? */
87 static int base_point_size = 0; /* which troff font size maps onto html */
88 /* size 3? */
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
98 void stop () {}
100 static int min (int a, int b)
102 if (a < b)
103 return a;
104 else
105 return b;
108 static int max (int a, int b)
110 if (a > b)
111 return a;
112 else
113 return 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.
139 struct file {
140 FILE *fp;
141 file *next;
142 int new_output_file;
143 int require_links;
144 string output_file_name;
146 file (FILE *f);
150 * file - initialize all fields to NULL
153 file::file (FILE *f)
154 : fp(f), next(NULL), new_output_file(FALSE),
155 require_links(FALSE), output_file_name("")
159 class files {
160 public:
161 files ();
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);
172 private:
173 file *head;
174 file *tail;
175 file *ptr;
179 * files - create an empty list of files.
182 files::files ()
183 : head(NULL), tail(NULL), ptr(NULL)
188 * get_file - returns the FILE associated with ptr.
191 FILE *files::get_file (void)
193 if (ptr)
194 return ptr->fp;
195 else
196 return NULL;
200 * start_of_list - reset the ptr to the start of the list.
203 void files::start_of_list (void)
205 ptr = head;
209 * move_next - moves the ptr to the next element on the list.
212 void files::move_next (void)
214 if (ptr != NULL)
215 ptr = ptr->next;
219 * add_new_file - adds a new file, f, to the list.
222 void files::add_new_file (FILE *f)
224 if (head == NULL) {
225 head = new file(f);
226 tail = head;
227 } else {
228 tail->next = new file(f);
229 tail = tail->next;
231 ptr = tail;
235 * set_file_name - sets the final file name to contain the html
236 * data to name.
239 void files::set_file_name (string name)
241 if (ptr != NULL) {
242 ptr->output_file_name = name;
243 ptr->new_output_file = TRUE;
248 * set_links_required - issue links when processing this component
249 * of the file.
252 void files::set_links_required (void)
254 if (ptr != NULL)
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)
265 if (ptr != NULL)
266 return ptr->require_links;
267 return FALSE;
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)
277 if (ptr != NULL)
278 return ptr->new_output_file;
279 return FALSE;
283 * file_name - returns the name of the file.
286 string files::file_name (void)
288 if (ptr != NULL)
289 return ptr->output_file_name;
290 return string("");
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;
301 return string("");
305 * the class and methods for styles
308 struct style {
309 font *f;
310 int point_size;
311 int font_no;
312 int height;
313 int slant;
314 color col;
315 style ();
316 style (font *, int, int, int, int, color);
317 int operator == (const style &) const;
318 int operator != (const style &) const;
321 style::style()
322 : f(NULL)
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
346 struct char_block {
347 enum { SIZE = 256 };
348 char *buffer;
349 int used;
350 char_block *next;
352 char_block();
353 char_block(int length);
354 ~char_block();
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)];
366 if (buffer == NULL)
367 fatal("out of memory error");
370 char_block::~char_block()
372 if (buffer != NULL)
373 a_delete buffer;
376 class char_buffer {
377 public:
378 char_buffer();
379 ~char_buffer();
380 char *add_string(const char *, unsigned int);
381 char *add_string(const string &);
382 private:
383 char_block *head;
384 char_block *tail;
387 char_buffer::char_buffer()
388 : head(NULL), tail(NULL)
392 char_buffer::~char_buffer()
394 while (head != NULL) {
395 char_block *temp = head;
396 head = head->next;
397 delete temp;
401 char *char_buffer::add_string (const char *s, unsigned int length)
403 int i=0;
404 unsigned int old_used;
406 if (s == NULL || length == 0)
407 return NULL;
409 if (tail == NULL) {
410 tail = new char_block(length+1);
411 head = tail;
412 } else {
413 if (tail->used + length+1 > char_block::SIZE) {
414 tail->next = new char_block(length+1);
415 tail = tail->next;
419 old_used = tail->used;
420 do {
421 tail->buffer[tail->used] = s[i];
422 tail->used++;
423 i++;
424 length--;
425 } while (length>0);
427 // add terminating nul character
429 tail->buffer[tail->used] = '\0';
430 tail->used++;
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.
446 class text_glob {
447 public:
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,
457 int thickness);
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);
465 text_glob (void);
466 ~text_glob (void);
467 int is_a_line (void);
468 int is_a_tag (void);
469 int is_eol (void);
470 int is_auto_img (void);
471 int is_br (void);
472 int is_in (void);
473 int is_po (void);
474 int is_ti (void);
475 int is_ll (void);
476 int is_ce (void);
477 int is_tl (void);
478 int is_eo_tl (void);
479 int is_eol_ce (void);
480 int is_col (void);
481 int is_tab (void);
482 int is_tab0 (void);
483 int is_ta (void);
484 int is_tab_ts (void);
485 int is_tab_te (void);
486 int is_nf (void);
487 int is_fi (void);
488 int is_eo_h (void);
489 int get_arg (void);
490 int get_tab_args (char *align);
492 void remember_table (html_table *t);
493 html_table *get_table (void);
495 style text_style;
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
506 private:
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 ()
536 if (tab != NULL)
537 delete tab;
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);
551 *this = *g;
552 delete g;
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);
569 *this = *g;
570 delete g;
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,
580 int thickness_value)
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);
585 *this = *g;
586 delete g;
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.
594 * (consider .EQ
595 * delim $$
596 * .EN
597 * .TS
598 * tab(!), center;
599 * l!l.
600 * $1 over x$!recripical of x
601 * .TE
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);
619 *this = *g;
620 delete g;
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);
634 *this = *g;
635 delete g;
639 * is_a_line - returns TRUE if glob should be converted into an <hr>
642 int text_glob::is_a_line (void)
644 return is_line;
648 * is_a_tag - returns TRUE if glob contains a troff directive.
651 int text_glob::is_a_tag (void)
653 return is_tag;
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) &&
700 (get_arg() == 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) &&
711 (get_arg() == 1) );
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
833 * generated image.
836 int text_glob::is_auto_img (void)
838 return is_img_auto;
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)))
861 p++;
862 while ((*p != (char)0) && (isspace(*p)))
863 p++;
864 if (*p == (char)0)
865 return -1;
866 return atoi(p);
868 return -1;
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)))
882 p++;
883 while ((*p != (char)0) && (isspace(*p)))
884 p++;
885 *align = *p;
886 // now the int value
887 while ((*p != (char)0) && (!isspace(*p)))
888 p++;
889 while ((*p != (char)0) && (isspace(*p)))
890 p++;
891 if (*p == (char)0)
892 return -1;
893 return atoi(p);
895 return -1;
899 * remember_table - saves table, t, in the text_glob.
902 void text_glob::remember_table (html_table *t)
904 if (tab != NULL)
905 delete tab;
906 tab = t;
910 * get_table - returns the stored table description.
913 html_table *text_glob::get_table (void)
915 return tab;
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 {
927 element_list *right;
928 element_list *left;
929 text_glob *datum;
930 int lineno;
931 int minv, minh, maxv, maxh;
933 element_list (text_glob *d,
934 int line_number,
935 int min_vertical, int min_horizontal,
936 int max_vertical, int max_horizontal);
937 element_list ();
938 ~element_list ();
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,
951 int line_number,
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 ()
961 if (datum != NULL)
962 delete datum;
965 class list {
966 public:
967 list ();
968 ~list ();
969 int is_less (element_list *a, element_list *b);
970 void add (text_glob *in,
971 int line_number,
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);
977 int is_empty (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);
987 private:
988 element_list *head;
989 element_list *tail;
990 element_list *ptr;
994 * list - construct an empty list.
997 list::list ()
998 : head(NULL), tail(NULL), ptr(NULL)
1003 * ~list - destroy a complete list.
1006 list::~list()
1008 element_list *temp=head;
1010 do {
1011 temp = head;
1012 if (temp != NULL) {
1013 head = head->right;
1014 delete temp;
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) {
1028 return( TRUE );
1029 } else if (a->lineno > b->lineno) {
1030 return( FALSE );
1031 } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
1032 return( a->minh < b->minh );
1033 } else {
1034 return( a->maxv < b->maxv );
1039 * add - adds a datum to the list in the order specified by the
1040 * region position.
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);
1047 element_list *last;
1049 #if 0
1050 fprintf(stderr, "[%s %d,%d,%d,%d] ",
1051 in->text_string, min_vertical, min_horizontal, max_vertical, max_horizontal);
1052 fflush(stderr);
1053 #endif
1055 if (head == NULL) {
1056 head = t;
1057 tail = t;
1058 ptr = t;
1059 t->left = t;
1060 t->right = t;
1061 } else {
1062 last = tail;
1064 while ((last != head) && (is_less(t, last)))
1065 last = last->left;
1067 if (is_less(t, last)) {
1068 t->right = last;
1069 last->left->right = t;
1070 t->left = last->left;
1071 last->left = t;
1072 // now check for a new head
1073 if (last == head)
1074 head = t;
1075 } else {
1076 // add t beyond last
1077 t->right = last->right;
1078 t->left = last;
1079 last->right->left = t;
1080 last->right = t;
1081 // now check for a new tail
1082 if (last == tail)
1083 tail = t;
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;
1097 if (head == tail) {
1098 head = NULL;
1099 if (tail != NULL)
1100 delete tail;
1102 tail = NULL;
1103 ptr = NULL;
1104 } else {
1105 if (head == ptr)
1106 head = head->right;
1107 if (tail == ptr)
1108 tail = tail->left;
1109 ptr->left->right = ptr->right;
1110 ptr->right->left = ptr->left;
1111 ptr = t;
1116 * start_from_head - assigns ptr to the head.
1119 void list::start_from_head (void)
1121 ptr = head;
1125 * start_from_tail - assigns ptr to the tail.
1128 void list::start_from_tail (void)
1130 ptr = tail;
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)
1148 return ptr == tail;
1152 * is_equal_to_head - returns TRUE if the ptr equals the head.
1155 int list::is_equal_to_head (void)
1157 return ptr == head;
1161 * move_left - moves the ptr left.
1164 void list::move_left (void)
1166 ptr = ptr->left;
1170 * move_right - moves the ptr right.
1173 void list::move_right (void)
1175 ptr = ptr->right;
1179 * get_datum - returns the datum referenced via ptr.
1182 text_glob* list::get_data (void)
1184 return ptr->datum;
1188 * move_right_get_data - returns the datum referenced via ptr and moves
1189 * ptr right.
1192 text_glob* list::move_right_get_data (void)
1194 ptr = ptr->right;
1195 if (ptr == head)
1196 return NULL;
1197 else
1198 return ptr->datum;
1202 * move_left_get_data - returns the datum referenced via ptr and moves
1203 * ptr right.
1206 text_glob* list::move_left_get_data (void)
1208 ptr = ptr->left;
1209 if (ptr == tail)
1210 return NULL;
1211 else
1212 return ptr->datum;
1216 * insert - inserts data after the current position.
1219 void list::insert (text_glob *in)
1221 if (is_empty())
1222 fatal("list must not be empty if we are inserting data");
1223 else {
1224 if (ptr == NULL)
1225 ptr = head;
1227 element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1228 if (ptr == tail)
1229 tail = t;
1230 ptr->right->left = t;
1231 t->right = ptr->right;
1232 ptr->right = t;
1233 t->left = ptr;
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)
1244 ptr = head;
1245 while (ptr != tail && ptr->datum != in)
1246 ptr = ptr->right;
1250 * page class and methods
1253 class page {
1254 public:
1255 page (void);
1256 void add (style *s, const string &str,
1257 int line_number,
1258 int min_vertical, int min_horizontal,
1259 int max_vertical, int max_horizontal);
1260 void add_tag (style *s, const string &str,
1261 int line_number,
1262 int min_vertical, int min_horizontal,
1263 int max_vertical, int max_horizontal);
1264 void add_and_encode (style *s, const string &str,
1265 int line_number,
1266 int min_vertical, int min_horizontal,
1267 int max_vertical, int max_horizontal,
1268 int is_tag);
1269 void add_line (style *s,
1270 int line_number,
1271 int x1, int y1, int x2, int y2,
1272 int thickness);
1273 void insert_tag (const string &str);
1274 void dump_page (void); // debugging method
1276 // and the data
1278 list glyphs; // position of glyphs and specials on page
1279 char_buffer buffer; // all characters for this page
1282 page::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);
1297 glyphs.insert(g);
1302 * add - add html text to the list of glyphs.
1305 void page::add (style *s, const string &str,
1306 int line_number,
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,
1323 int line_number,
1324 int min_vertical, int min_horizontal,
1325 int max_vertical, int max_horizontal)
1327 if (str.length() > 0) {
1328 text_glob *g;
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);
1335 } else {
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,
1349 int line_number,
1350 int x_1, int y_1, int x_2, int y_2,
1351 int thickness)
1353 if (y_1 == 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),
1358 thickness);
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);
1374 return buf;
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 = &pound;3.00 file = \foo\bar"
1388 void page::add_and_encode (style *s, const string &str,
1389 int line_number,
1390 int min_vertical, int min_horizontal,
1391 int max_vertical, int max_horizontal,
1392 int is_tag)
1394 string html_string;
1395 char *html_glyph;
1396 int i=0;
1398 if (s->f == NULL)
1399 return;
1400 while (i < str.length()) {
1401 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
1402 // start of escape
1403 i += 2; // move over \(
1404 int a = i;
1405 while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
1406 i++;
1408 int n = i;
1409 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
1410 i++;
1411 else
1412 n = -1;
1413 if (n > 0) {
1414 string troff_charname = str.substring(a, n-a);
1415 html_glyph = get_html_translation(s->f, troff_charname);
1416 if (html_glyph)
1417 html_string += html_glyph;
1418 else {
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);
1425 } else
1426 html_string += str[i];
1427 i++;
1429 if (html_string.length() > 0) {
1430 text_glob *g=new text_glob();
1431 if (is_tag)
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);
1436 else
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();
1454 text_glob *g;
1456 printf("\n<!--\n");
1457 printf("\n\ndebugging start\n");
1458 glyphs.start_from_head();
1459 do {
1460 g = glyphs.get_data();
1461 if (g->is_tab_ts()) {
1462 printf("\n\n");
1463 if (g->get_table() != NULL)
1464 g->get_table()->dump_table();
1466 printf("%s ", g->text_string);
1467 if (g->is_tab_te())
1468 printf("\n\n");
1469 glyphs.move_right();
1470 } while (! glyphs.is_equal_to_head());
1471 glyphs.move_to(old_pos);
1472 printf("\ndebugging end\n\n");
1473 printf("\n-->\n");
1474 fflush(stdout);
1475 #endif
1479 * font classes and methods
1482 class html_font : public font {
1483 html_font(const char *);
1484 public:
1485 int encoding_index;
1486 char *encoding;
1487 char *reencoded_name;
1488 ~html_font();
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);
1495 if (!f->load()) {
1496 delete f;
1497 return 0;
1499 return f;
1502 html_font::html_font(const char *nm)
1503 : font(nm)
1507 html_font::~html_font()
1512 * a simple class to contain the header to this document
1515 class title_desc {
1516 public:
1517 title_desc ();
1518 ~title_desc ();
1520 int has_been_written;
1521 int has_been_found;
1522 int with_h1;
1523 string text;
1527 title_desc::title_desc ()
1528 : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1532 title_desc::~title_desc ()
1536 class header_desc {
1537 public:
1538 header_desc ();
1539 ~header_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)
1569 text_glob *g;
1571 if (auto_links || force) {
1572 if (! headers.is_empty()) {
1573 int h=1;
1575 headers.start_from_head();
1576 header_filename.start_from_head();
1577 do {
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);
1584 fputs("#", f);
1585 if (simple_anchors) {
1586 string buffer(ANCHOR_TEMPLATE);
1588 buffer += as_string(h);
1589 buffer += '\0';
1590 fprintf(f, buffer.contents());
1591 } else
1592 fputs(g->text_string, f);
1593 h++;
1594 fputs("\">", 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());
1601 fputs("\n", f);
1606 struct assert_pos {
1607 assert_pos *next;
1608 const char *val;
1609 const char *id;
1612 class assert_state {
1613 public:
1614 assert_state ();
1615 ~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);
1627 void reset (void);
1629 private:
1630 int check_br_flag;
1631 int check_ce_flag;
1632 int check_fi_flag;
1633 int check_sp_flag;
1634 const char *val_br;
1635 const char *val_ce;
1636 const char *val_fi;
1637 const char *val_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;
1647 assert_pos *xhead;
1648 assert_pos *yhead;
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,
1661 const char *name,
1662 const char *f, const char *l, int flag);
1665 assert_state::assert_state ()
1667 reset();
1668 val_br = NULL;
1669 val_ce = NULL;
1670 val_fi = NULL;
1671 val_sp = NULL;
1672 file_br = NULL;
1673 file_ce = NULL;
1674 file_fi = NULL;
1675 file_sp = NULL;
1676 line_br = NULL;
1677 line_ce = NULL;
1678 line_fi = NULL;
1679 line_sp = NULL;
1680 xhead = NULL;
1681 yhead = NULL;
1684 assert_state::~assert_state ()
1686 assert_pos *t;
1688 while (xhead != NULL) {
1689 t = xhead;
1690 xhead = xhead->next;
1691 a_delete (char *)t->val;
1692 a_delete (char *)t->id;
1693 delete t;
1695 while (yhead != NULL) {
1696 t = yhead;
1697 yhead = yhead->next;
1698 a_delete (char *)t->val;
1699 a_delete (char *)t->id;
1700 delete t;
1704 void assert_state::reset (void)
1706 check_br_flag = 0;
1707 check_ce_flag = 0;
1708 check_fi_flag = 0;
1709 check_sp_flag = 0;
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)
1716 assert_pos *t = *h;
1718 while (t != NULL) {
1719 if (strcmp(t->id, i) == 0)
1720 break;
1721 t = t->next;
1723 if (t != NULL && v != NULL && (v[0] != '='))
1724 compare(t, v, f, l);
1725 else {
1726 if (t == NULL) {
1727 t = new assert_pos;
1728 t->next = *h;
1729 (*h) = t;
1731 if (v == NULL || v[0] != '=') {
1732 if (f == NULL)
1733 f = "stdin";
1734 if (l == NULL)
1735 l = "<none>";
1736 if (v == NULL)
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",
1739 f, l, i, v);
1741 t->id = i;
1742 t->val = v;
1743 a_delete (char *)c;
1744 a_delete (char *)f;
1745 a_delete (char *)l;
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;
1766 while ((*v) == '=')
1767 v++;
1768 while ((*s) == '=')
1769 s++;
1771 if (strcmp(v, s) != 0) {
1772 if (f == NULL)
1773 f = "stdin";
1774 if (l == NULL)
1775 l = "<none>";
1776 fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %s and was given %s\n",
1777 f, l, t->id, s, v);
1781 void assert_state::close (const char *c)
1783 if (strcmp(c, "sp") == 0)
1784 check_sp_flag = 0;
1785 else if (strcmp(c, "br") == 0)
1786 check_br_flag = 0;
1787 else if (strcmp(c, "fi") == 0)
1788 check_fi_flag = 0;
1789 else if (strcmp(c, "nf") == 0)
1790 check_fi_flag = 0;
1791 else if (strcmp(c, "ce") == 0)
1792 check_ce_flag = 0;
1793 else
1794 fprintf(stderr, "internal error: unrecognised tag in grohtml (%s)\n", c);
1797 const char *replace_negate_str (const char *before, char *after)
1799 if (before != NULL)
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);
1807 d = 0;
1809 if (d == 0)
1810 after[0] = '1';
1811 else
1812 after[0] = '0';
1813 after[1] = (char)0;
1815 return after;
1818 const char *replace_str (const char *before, const char *after)
1820 if (before != NULL)
1821 a_delete (char *)before;
1822 return after;
1825 void assert_state::set (const char *c, const char *v,
1826 const char *f, const char *l)
1828 if (l == NULL)
1829 l = "<none>";
1830 if (f == NULL)
1831 f = "stdin";
1833 // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1834 if (strcmp(c, "sp") == 0) {
1835 check_sp_flag = 1;
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) {
1840 check_br_flag = 1;
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) {
1845 check_fi_flag = 1;
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) {
1850 check_fi_flag = 1;
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) {
1855 check_ce_flag = 1;
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)
1870 if (c[0] == '{')
1871 set(&c[1], v, f, l);
1872 if (c[0] == '}')
1873 close(&c[1]);
1876 int assert_state::check_value_error (int c, int v, const char *s,
1877 const char *name,
1878 const char *f, const char *l, int flag)
1880 if (! c) {
1881 if (f == NULL)
1882 f = "stdin";
1883 if (l == NULL)
1884 l = "<none>";
1885 fprintf(stderr, "%s:%s:grohtml (troff state) assertion failed, expected %s to be %s but found it to contain %d\n",
1886 f, l, name, s, v);
1887 return 0;
1889 return flag;
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);
1909 else
1910 *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
1913 void assert_state::check_sp (int sp)
1915 if (check_sp_flag)
1916 check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
1919 void assert_state::check_fi (int fi)
1921 if (check_fi_flag)
1922 check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
1925 void assert_state::check_br (int br)
1927 if (check_br_flag)
1928 check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
1931 void assert_state::check_ce (int ce)
1933 if (check_ce_flag)
1934 check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
1937 class html_printer : public printer {
1938 files file_list;
1939 simple_output html;
1940 int res;
1941 int space_char_index;
1942 int space_width;
1943 int no_of_printed_pages;
1944 int paper_length;
1945 string sbuf;
1946 int sbuf_start_hpos;
1947 int sbuf_vpos;
1948 int sbuf_end_hpos;
1949 int sbuf_prev_hpos;
1950 int sbuf_kern;
1951 style sbuf_style;
1952 int last_sbuf_length;
1953 int overstrike_detected;
1954 style output_style;
1955 int output_hpos;
1956 int output_vpos;
1957 int output_vpos_max;
1958 int output_draw_point_size;
1959 int line_thickness;
1960 int output_line_thickness;
1961 unsigned char output_space_code;
1962 char *inside_font_style;
1963 int page_number;
1964 title_desc title;
1965 header_desc header;
1966 int header_indent;
1967 int supress_sub_sup;
1968 int cutoff_heading;
1969 page *page_contents;
1970 html_text *current_paragraph;
1971 html_indent *indent;
1972 html_table *table;
1973 int end_center;
1974 int end_tempindent;
1975 TAG_ALIGNMENT next_tag;
1976 int fill_on;
1977 int max_linelength;
1978 int linelength;
1979 int pageoffset;
1980 int troff_indent;
1981 int device_indent;
1982 int temp_indent;
1983 int pointsize;
1984 int vertical_spacing;
1985 int line_number;
1986 color *background;
1987 int seen_indent;
1988 int next_indent;
1989 int seen_pageoffset;
1990 int next_pageoffset;
1991 int seen_linelength;
1992 int next_linelength;
1993 int seen_center;
1994 int next_center;
1995 int seen_space;
1996 int seen_break;
1997 int current_column;
1998 int row_space;
1999 assert_state as;
2001 void flush_sbuf ();
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);
2033 void do_eol (void);
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 &current);
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);
2098 // ADD HERE
2100 public:
2101 html_printer ();
2102 ~html_printer ();
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");
2129 #if 0
2130 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
2131 #endif
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)
2153 return f;
2154 if (strcmp(fontname, "I") == 0)
2155 return font::load_font("BI");
2156 if (strcmp(fontname, "BI") == 0)
2157 return f;
2158 return NULL;
2161 void html_printer::end_of_line()
2163 flush_sbuf();
2164 line_number++;
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
2190 device_indent = 0;
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)
2201 do_font(g);
2202 if (next_tag == INLINE) {
2203 determine_space(g);
2204 current_paragraph->do_emittext(g->text_string, g->text_length);
2205 } else {
2206 int space = current_paragraph->retrieve_para_space() || seen_space;
2208 current_paragraph->done_para();
2209 shutdown_table();
2210 switch (next_tag) {
2212 case CENTERED:
2213 current_paragraph->do_para("align=center", space);
2214 break;
2215 case LEFT:
2216 current_paragraph->do_para(&html, "align=left", get_troff_indent(), pageoffset, linelength, space);
2217 break;
2218 case RIGHT:
2219 current_paragraph->do_para(&html, "align=right", get_troff_indent(), pageoffset, linelength, space);
2220 break;
2221 default:
2222 fatal("unknown enumeration");
2224 current_paragraph->do_emittext(g->text_string, g->text_length);
2225 current_paragraph->done_para();
2226 next_tag = INLINE;
2227 supress_sub_sup = TRUE;
2228 seen_space = FALSE;
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
2237 * and paragraphs.
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())
2244 troff_tag(g);
2248 * do_center - handle the .ce commands from troff.
2251 void html_printer::do_center (char *arg)
2253 next_center = atoi(arg);
2254 seen_center = TRUE;
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)
2274 next_tag = RIGHT;
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)
2284 next_tag = LEFT;
2288 * exists - returns TRUE if filename exists.
2291 static int exists (const char *filename)
2293 FILE *fp = fopen(filename, "r");
2295 if (fp == 0) {
2296 return( FALSE );
2297 } else {
2298 fclose(fp);
2299 return( TRUE );
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] == ' ')) {
2313 filename++;
2315 if (exists(filename))
2316 *s += string("<img src=\"") + filename + "\" "
2317 + "alt=\"Image " + filename + "\">";
2318 return *s;
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.
2337 text_glob h = *g;
2339 h.text_string = buffer.contents();
2340 h.text_length = buffer.length();
2341 emit_raw(&h);
2342 } else
2343 next_tag = INLINE;
2347 * outstanding_eol - call do_eol, n, times.
2350 void html_printer::outstanding_eol (int n)
2352 while (n > 0) {
2353 do_eol();
2354 n--;
2359 * do_title - handle the .tl commands from troff.
2362 void html_printer::do_title (void)
2364 text_glob *t;
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 */
2371 do {
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)
2379 title.text += " ";
2380 found_title_start = TRUE;
2381 title.has_been_found = TRUE;
2382 title.text += img;
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;
2391 return;
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()));
2402 } else {
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++;
2432 style st;
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);
2453 buffer += '\0';
2454 html.put_string(buffer.contents());
2455 } else {
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();
2471 else {
2472 html.put_string("<b>");
2473 html.put_string(header.header_buffer);
2474 html.put_string("</b>").nl();
2477 else {
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)
2510 if (level == 0) {
2511 int i;
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] == '.') {
2517 level++;
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)
2534 text_glob *g;
2535 int level=atoi(arg);
2536 int horiz;
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();
2542 horiz = g->minh;
2543 do {
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())
2556 troff_tag(g);
2557 else if (g->is_fi())
2558 fill_on = 1;
2559 else if (g->is_nf())
2560 fill_on = 0;
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 += " ";
2568 horiz = g->maxh;
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()) &&
2574 (! g->is_eo_h()));
2577 determine_header_level(level);
2578 write_header();
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();
2592 int result = TRUE;
2593 text_glob *g;
2595 if (! page_contents->glyphs.is_equal_to_tail()) {
2596 page_contents->glyphs.move_right();
2597 do {
2598 g = page_contents->glyphs.get_data();
2599 if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2600 result = FALSE;
2601 page_contents->glyphs.move_right();
2602 } while (result &&
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();
2612 return result;
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)
2645 return temp_indent;
2646 else
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);
2657 seen_indent = TRUE;
2661 * do_tempindent - handle the .ti command from troff.
2664 void html_printer::do_tempindent (char *arg)
2666 if (fill_on) {
2668 * we set the end_tempindent to 2 as the first .br
2669 * activates the .ti and the second terminates it.
2671 end_tempindent = 2;
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
2686 table = NULL;
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();
2704 device_indent = in;
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())) {
2738 if (t->is_tl()) {
2740 * found title therefore ignore this .ps tag
2742 while (t != g) {
2743 page_contents->glyphs.move_left();
2744 t = page_contents->glyphs.get_data();
2746 return;
2748 page_contents->glyphs.move_right();
2749 t = page_contents->glyphs.get_data();
2752 * move back to original position
2754 while (t != g) {
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)
2771 int on = atoi(arg);
2773 output_hpos = get_troff_indent()+pageoffset;
2774 supress_sub_sup = TRUE;
2776 if (fill_on != on) {
2777 if (on)
2778 current_paragraph->do_para("", seen_space);
2779 fill_on = on;
2784 * do_eol - handle the end of line
2787 void html_printer::do_eol (void)
2789 if (! fill_on) {
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)
2805 if (seen_center) {
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);
2813 } else
2814 if (strcmp("align=center",
2815 current_paragraph->get_alignment()) != 0) {
2817 * different alignment, so shutdown paragraph and open
2818 * a new one.
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);
2824 } else
2826 * same alignment, if we have emitted text then issue a break.
2828 if (current_paragraph->emitted_text())
2829 current_paragraph->do_break();
2830 } else
2832 * next_center == 0
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) {
2851 if (end_center > 1)
2852 if (current_paragraph->emitted_text())
2853 current_paragraph->do_break();
2855 end_center--;
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");
2901 split_file += '\0';
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 == ' '))
2917 name++;
2918 job_name = 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);
2930 head_info += '\n';
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) {
2946 end_tempindent--;
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;
2962 do_check_center();
2963 output_hpos = get_troff_indent()+pageoffset;
2964 supress_sub_sup = TRUE;
2967 void html_printer::do_space (char *arg)
2969 int n = atoi(arg);
2971 seen_space = atoi(arg);
2972 as.check_sp(seen_space);
2973 #if 0
2974 if (n>0 && table)
2975 table->set_space(TRUE);
2976 #endif
2978 while (n>0) {
2979 current_paragraph->do_space();
2980 n--;
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();
2993 if (t != NULL) {
2994 current_column = 0;
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");
3001 #endif
3003 t->set_linelength(max_linelength);
3004 t->add_indent(pageoffset);
3005 #if 0
3006 t->emit_table_header(seen_space);
3007 #else
3008 t->emit_table_header(FALSE);
3009 row_space = current_paragraph->retrieve_para_space() || seen_space;
3010 seen_space = FALSE;
3011 #endif
3014 table = t;
3018 * do_tab_te - finish a table.
3021 void html_printer::do_tab_te (void)
3023 if (table) {
3024 current_paragraph->done_para();
3025 current_paragraph->remove_para_space();
3026 table->emit_finish_table();
3029 table = NULL;
3030 restore_troff_indent();
3034 * do_tab - handle the "devtag:tab" tag
3037 void html_printer::do_tab (char *s)
3039 if (table) {
3040 while (isspace(*s))
3041 s++;
3042 s++;
3043 int col = table->find_column(atoi(s) + pageoffset + get_troff_indent());
3044 if (col > 0) {
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)
3057 if (table) {
3058 int col = table->find_column(pageoffset+get_troff_indent());
3059 if (col > 0) {
3060 current_paragraph->done_para();
3061 table->emit_col(col);
3067 * do_col - start column, s.
3070 void html_printer::do_col (char *s)
3072 if (table) {
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
3085 * state machine.
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) {
3096 do_end_para(g);
3097 } else if (g->is_eol()) {
3098 do_eol();
3099 } else if (g->is_eol_ce()) {
3100 do_eol_ce();
3101 } else if (strncmp(t, ".sp", 3) == 0) {
3102 char *a = (char *)t+3;
3103 do_space(a);
3104 } else if (strncmp(t, ".br", 3) == 0) {
3105 seen_break = 1;
3106 as.check_br(1);
3107 do_break();
3108 } else if (strcmp(t, ".centered-image") == 0) {
3109 do_centered_image();
3110 } else if (strcmp(t, ".right-image") == 0) {
3111 do_right_image();
3112 } else if (strcmp(t, ".left-image") == 0) {
3113 do_left_image();
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;
3120 do_center(a);
3121 } else if (g->is_tl()) {
3122 supress_sub_sup = TRUE;
3123 title.with_h1 = TRUE;
3124 do_title();
3125 } else if (strncmp(t, ".html-tl", 8) == 0) {
3126 supress_sub_sup = TRUE;
3127 title.with_h1 = FALSE;
3128 do_title();
3129 } else if (strncmp(t, ".fi", 3) == 0) {
3130 char *a = (char *)t+3;
3131 do_fill(a);
3132 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
3133 char *a = (char *)t+3;
3134 do_heading(a);
3135 } else if (strncmp(t, ".ll", 3) == 0) {
3136 char *a = (char *)t+3;
3137 do_linelength(a);
3138 } else if (strncmp(t, ".po", 3) == 0) {
3139 char *a = (char *)t+3;
3140 do_pageoffset(a);
3141 } else if (strncmp(t, ".in", 3) == 0) {
3142 char *a = (char *)t+3;
3143 do_indentation(a);
3144 } else if (strncmp(t, ".ti", 3) == 0) {
3145 char *a = (char *)t+3;
3146 do_tempindent(a);
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;
3152 do_pointsize(a);
3153 } else if (strcmp(t, ".links") == 0) {
3154 do_links();
3155 } else if (strncmp(t, ".job-name", 9) == 0) {
3156 char *a = (char *)t+9;
3157 do_job_name(a);
3158 } else if (strncmp(t, ".head", 5) == 0) {
3159 char *a = (char *)t+5;
3160 do_head(a);
3161 } else if (strcmp(t, ".no-auto-rule") == 0) {
3162 auto_rule = FALSE;
3163 } else if (strcmp(t, ".tab-ts") == 0) {
3164 do_tab_ts(g);
3165 } else if (strcmp(t, ".tab-te") == 0) {
3166 do_tab_te();
3167 } else if (strncmp(t, ".col ", 5) == 0) {
3168 char *a = (char *)t+4;
3169 do_col(a);
3170 } else if (strncmp(t, "tab ", 4) == 0) {
3171 char *a = (char *)t+3;
3172 do_tab(a);
3173 } else if (strncmp(t, "tab0", 4) == 0) {
3174 do_tab0();
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)
3194 text_glob *g;
3196 if (! page_contents->glyphs.is_empty()) {
3197 page_contents->glyphs.start_from_head();
3198 do {
3199 g = page_contents->glyphs.get_data();
3200 #if 0
3201 fprintf(stderr, "[%s:%d:%d:%d:%d]",
3202 g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
3203 fflush(stderr);
3204 #endif
3206 handle_state_assertion(g);
3208 if (strcmp(g->text_string, "XXXXXXX") == 0)
3209 stop();
3211 if (g->is_a_tag())
3212 troff_tag(g);
3213 else if (g->is_a_line())
3214 emit_line(g);
3215 else {
3216 as.check_sp(seen_space);
3217 as.check_br(seen_break);
3218 seen_break = 0;
3219 seen_space = 0;
3220 emit_html(g);
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
3237 * text glob, g.
3240 int html_printer::calc_nf (text_glob *g, int nf)
3242 if (g != NULL) {
3243 if (g->is_fi()) {
3244 as.check_fi(TRUE);
3245 return FALSE;
3247 if (g->is_nf()) {
3248 as.check_fi(FALSE);
3249 return TRUE;
3252 as.check_fi(! nf);
3253 return nf;
3257 * calc_po_in - calculates the, in, po, registers
3260 void html_printer::calc_po_in (text_glob *g, int nf)
3262 if (g->is_in())
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();
3268 end_tempindent = 2;
3269 } else if (g->is_br() || (nf && g->is_eol())) {
3270 if (end_tempindent > 0)
3271 end_tempindent--;
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)
3282 int next = -1;
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();
3287 if (g == NULL) {
3288 page_contents->glyphs.start_from_head();
3289 as.reset();
3291 else {
3292 next = g->minh;
3293 page_contents->glyphs.move_left();
3296 return next;
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();
3358 text_glob *g;
3360 if (! page_contents->glyphs.is_equal_to_tail()) {
3361 do {
3362 g = page_contents->glyphs.get_data();
3363 if (g->is_tab()) {
3364 page_contents->glyphs.sub_move_right();
3365 if (g == orig)
3366 orig = page_contents->glyphs.get_data();
3367 } else
3368 page_contents->glyphs.move_right();
3369 } while ((! page_contents->glyphs.is_equal_to_head()) &&
3370 (! g->is_eol()));
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)
3382 text_glob *g;
3383 int line_start = TRUE;
3384 int nf = FALSE;
3386 if (! page_contents->glyphs.is_empty()) {
3387 page_contents->glyphs.start_from_head();
3388 as.reset();
3389 line_start = TRUE;
3390 do {
3391 g = page_contents->glyphs.get_data();
3392 handle_state_assertion(g);
3393 nf = calc_nf(g, nf);
3395 if (line_start) {
3396 if (line_start && nf && is_courier_until_eol()) {
3397 remove_tabs();
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;
3415 int nf = FALSE;
3417 if (! page_contents->glyphs.is_empty()) {
3418 page_contents->glyphs.start_from_head();
3419 as.reset();
3420 start_of_line = page_contents->glyphs.get_data();
3421 do {
3422 g = page_contents->glyphs.get_data();
3423 handle_state_assertion(g);
3424 nf = calc_nf(g, nf);
3426 if (g->is_tab())
3427 seen_tab = TRUE;
3429 if (g->is_col())
3430 seen_col = TRUE;
3432 if (g->is_br() || (nf && g->is_eol())) {
3433 do {
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);
3441 return;
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);
3450 seen_tab = FALSE;
3451 seen_col = FALSE;
3452 start_of_line = 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) {
3471 case tab_tag:
3472 break;
3473 case tab0_tag:
3474 *minimum = g->minh;
3475 break;
3476 case col_tag:
3477 *minimum = g->minh;
3478 *maximum = g->maxh;
3479 break;
3480 default:
3481 break;
3486 * add_table_end - moves left one glyph, adds a table end tag and adds a
3487 * debugging string.
3490 void html_printer::add_table_end (const char *
3491 #if defined(DEBUG_TABLES)
3492 debug_string
3493 #endif
3496 page_contents->glyphs.move_left();
3497 insert_tab_te();
3498 #if defined(DEBUG_TABLES)
3499 page_contents->insert_tag(string(debug_string));
3500 #endif
3504 * lookahead_for_tables - checks for .col tags and inserts table
3505 * start/end tags
3508 void html_printer::lookahead_for_tables (void)
3510 text_glob *g;
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;
3515 int left = 0;
3516 int found_col = FALSE;
3517 int seen_text = FALSE;
3518 int ncol = 0;
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;
3523 char align = 'L';
3524 int nf = FALSE;
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();
3533 as.reset();
3534 g = page_contents->glyphs.get_data();
3535 if (g->is_br()) {
3536 g = page_contents->glyphs.move_right_get_data();
3537 handle_state_assertion(g);
3538 if (page_contents->glyphs.is_equal_to_head()) {
3539 if (tbl != NULL) {
3540 delete tbl;
3541 tbl = NULL;
3543 return;
3546 start_of_line = g;
3547 seen_text = FALSE;
3548 ncol = 0;
3549 left = next_horiz_pos(g, nf);
3550 if (found_col)
3551 last = g;
3552 found_col = FALSE;
3555 do {
3556 #if defined(DEBUG_TABLES)
3557 fprintf(stderr, " [") ;
3558 fprintf(stderr, g->text_string) ;
3559 fprintf(stderr, "] ") ;
3560 fflush(stderr);
3561 if (strcmp(g->text_string, "XXXXXXX") == 0)
3562 stop();
3563 #endif
3565 nf = calc_nf(g, nf);
3566 calc_po_in(g, nf);
3567 if (g->is_col()) {
3568 if (type_of_col == tab_tag && start_of_table != NULL) {
3569 page_contents->glyphs.move_left();
3570 insert_tab_te();
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;
3577 last = NULL;
3579 type_of_col = col_tag;
3580 found_col = TRUE;
3581 ncol = g->get_arg();
3582 align = 'L';
3583 colmin = 0;
3584 colmax = 0;
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);
3592 if (colmax > 0)
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();
3597 insert_tab_te();
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;
3602 last = NULL;
3604 if (tab_defs != NULL)
3605 tbl->tab_stops->init(tab_defs);
3607 type_of_col = tab0_tag;
3608 ncol = 1;
3609 colmin = 0;
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())
3615 seen_text = TRUE;
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;
3621 seen_text = FALSE;
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;
3627 last = 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;
3640 type_of_col = none;
3641 last = 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))) {
3650 if (ncol == 0)
3651 add_table_end("*** NCOL == 0 ***");
3652 else
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;
3658 type_of_col = none;
3659 last = 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);
3669 if (g == NULL) {
3670 if (found_col) {
3671 page_contents->glyphs.start_from_head();
3672 as.reset();
3673 last = g;
3674 found_col = FALSE;
3676 } else if (g->is_br() || (nf && g->is_eol())) {
3677 do {
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())));
3682 start_of_line = g;
3683 seen_text = FALSE;
3684 ncol = 0;
3685 left = next_horiz_pos(g, nf);
3686 if (found_col)
3687 last = g;
3688 found_col = FALSE;
3690 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3692 #if defined(DEBUG_TABLES)
3693 fprintf(stderr, "finished scanning for tables\n");
3694 #endif
3696 page_contents->glyphs.start_from_head();
3697 if (start_of_table != NULL) {
3698 if (last != NULL)
3699 while (last != page_contents->glyphs.get_data())
3700 page_contents->glyphs.move_left();
3702 insert_tab_te();
3703 start_of_table->remember_table(tbl);
3704 tbl = NULL;
3705 page_contents->insert_tag(string("*** LAST ***"));
3708 if (tbl != NULL) {
3709 delete tbl;
3710 tbl = NULL;
3713 // and reset the registers
3714 pageoffset = old_pageoffset;
3715 troff_indent = 0;
3716 temp_indent = 0;
3717 end_tempindent = 0;
3720 void html_printer::flush_page (void)
3722 supress_sub_sup = TRUE;
3723 flush_sbuf();
3724 page_contents->dump_page();
3725 lookahead_for_tables();
3726 page_contents->dump_page();
3728 flush_globs();
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");
3737 #endif
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();
3756 } else {
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;
3770 int result = TRUE;
3771 text_glob *orig = page_contents->glyphs.get_data();
3772 text_glob *g;
3774 if (! page_contents->glyphs.is_equal_to_head()) {
3775 do {
3776 page_contents->glyphs.move_left();
3777 g = page_contents->glyphs.get_data();
3778 result = g->is_a_tag();
3779 if (g->is_fi())
3780 nf = FALSE;
3781 else if (g->is_nf())
3782 nf = TRUE;
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();
3791 return result;
3795 * is_font_courier - returns TRUE if the font, f, is courier.
3798 int html_printer::is_font_courier (font *f)
3800 if (f != 0) {
3801 const char *fontname = f->get_name();
3803 return( (fontname != 0) && (fontname[0] == 'C') );
3805 return FALSE;
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)
3892 if (from < to) {
3893 while (from < to) {
3894 current_paragraph->do_big();
3895 from += SIZE_INCREMENT;
3897 } else if (from > to) {
3898 while (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) {
3930 do_sup_or_sub(g);
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)
3952 int r = font::res;
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)
3967 int r = font::res;
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)
3982 int r = font::res;
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)
3997 int r = font::res;
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
4009 * appropriate tags.
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)
4034 do_font(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)
4050 do_font(g);
4051 determine_space(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,
4076 line_number,
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;
4085 sbuf.clear();
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)
4096 switch (code) {
4098 case 'l':
4099 # if 0
4100 if (np == 2) {
4101 page_contents->add_line(&sbuf_style,
4102 line_number,
4103 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
4104 } else {
4105 error("2 arguments required for line");
4107 # endif
4108 break;
4109 case 't':
4111 if (np == 0) {
4112 line_thickness = -1;
4113 } else {
4114 // troff gratuitously adds an extra 0
4115 if (np != 1 && np != 2) {
4116 error("0 or 1 argument required for thickness");
4117 break;
4119 line_thickness = p[0];
4121 break;
4124 case 'P':
4125 break;
4126 case 'p':
4127 break;
4128 case 'E':
4129 break;
4130 case 'e':
4131 break;
4132 case 'C':
4133 break;
4134 case 'c':
4135 break;
4136 case 'a':
4137 break;
4138 case '~':
4139 break;
4140 case 'f':
4141 break;
4142 case 'F':
4143 // fill with color env->fill
4144 if (background != NULL)
4145 delete background;
4146 background = new color;
4147 *background = *env->fill;
4148 break;
4150 default:
4151 error("unrecognised drawing command `%1'", char(code));
4152 break;
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),
4161 output_hpos(-1),
4162 output_vpos(-1),
4163 output_vpos_max(-1),
4164 line_thickness(-1),
4165 inside_font_style(0),
4166 page_number(0),
4167 header_indent(-1),
4168 supress_sub_sup(TRUE),
4169 cutoff_heading(100),
4170 indent(NULL),
4171 table(NULL),
4172 end_center(0),
4173 end_tempindent(0),
4174 next_tag(INLINE),
4175 fill_on(TRUE),
4176 max_linelength(-1),
4177 linelength(0),
4178 pageoffset(0),
4179 troff_indent(0),
4180 device_indent(0),
4181 temp_indent(0),
4182 pointsize(base_point_size),
4183 line_number(0),
4184 background(default_background),
4185 seen_indent(FALSE),
4186 next_indent(0),
4187 seen_pageoffset(FALSE),
4188 next_pageoffset(0),
4189 seen_linelength(FALSE),
4190 next_linelength(0),
4191 seen_center(FALSE),
4192 next_center(0),
4193 seen_space(0),
4194 seen_break(0),
4195 current_column(0),
4196 row_space(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");
4204 #if 0
4205 // should be sorted html..
4206 if (font::res % (font::sizescale*72) != 0)
4207 fatal("res must be a multiple of 72*sizescale");
4208 #endif
4209 int r = font::res;
4210 int point = 0;
4211 while (r % 10 == 0) {
4212 r /= 10;
4213 point++;
4215 res = r;
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)
4234 return;
4236 char *html_glyph = NULL;
4237 unsigned int code = sbuf_style.f->get_code(idx);
4239 if (s.empty()) {
4240 if (sbuf_style.f->contains(idx))
4241 html_glyph = (char *)sbuf_style.f->get_special_device_encoding(idx);
4242 else
4243 html_glyph = NULL;
4245 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
4246 html_glyph = to_unicode(code);
4247 } else
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);
4253 else
4254 sbuf += html_glyph;
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;
4270 return TRUE;
4271 } else {
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;
4282 return TRUE;
4286 return FALSE ;
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)
4296 int idx;
4298 if ((f == 0) || name.empty())
4299 return NULL;
4300 else {
4301 idx = f->name_to_index((char *)(name + '\0').contents());
4302 if (idx == 0) {
4303 error("character `%s' not found", (name + '\0').contents());
4304 return NULL;
4305 } else
4306 if (f->contains(idx))
4307 return (char *)f->get_special_device_encoding(idx);
4308 else
4309 return NULL;
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
4317 * is flushed.
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;
4332 return TRUE;
4333 } else {
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))
4337 flush_sbuf();
4338 overstrike_detected = TRUE;
4339 add_to_sbuf(idx, name);
4340 sbuf_end_hpos = env->hpos + w;
4341 return TRUE;
4344 return FALSE ;
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);
4360 sty.slant = 0;
4363 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
4364 && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
4365 return;
4367 flush_sbuf();
4368 if (sbuf_style.f == NULL)
4369 sbuf_style = sty;
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;
4375 sbuf_style = sty;
4376 sbuf_kern = 0;
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,
4386 int *widthp)
4388 int nbsp_width = 0;
4389 if (num < 0) {
4390 nbsp_width = -num;
4391 num = 160; // &nbsp;
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);
4397 return;
4399 font *f = font_table[fn];
4400 if (f == 0) {
4401 error("no font mounted at `%1'", fn);
4402 return;
4404 if (!f->contains(i)) {
4405 error("font `%1' does not contain numbered character %2",
4406 f->get_name(),
4407 num);
4408 return;
4410 int w;
4411 if (nbsp_width)
4412 w = nbsp_width;
4413 else
4414 w = f->get_width(i, env->size);
4415 w = round_width(w);
4416 if (widthp)
4417 *widthp = w;
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);
4428 return -1;
4430 *f = font_table[fn];
4431 if (*f == 0) {
4432 error("no font mounted at `%1'", fn);
4433 return -1;
4435 if (!(*f)->contains(i)) {
4436 if (nm[0] != '\0' && nm[1] == '\0')
4437 error("font `%1' does not contain ascii character `%2'",
4438 (*f)->get_name(),
4439 nm[0]);
4440 else
4441 error("font `%1' does not contain special character `%2'",
4442 (*f)->get_name(),
4443 nm);
4444 return -1;
4446 int w = (*f)->get_width(i, env->size);
4447 w = round_width(w);
4448 if (widthp)
4449 *widthp = w;
4450 return i;
4454 * write_title - writes the title to this document
4457 void html_printer::write_title (int in_head)
4459 if (title.has_been_found) {
4460 if (in_head) {
4461 html.put_string("<title>");
4462 html.put_string(title.text);
4463 html.put_string("</title>").nl().nl();
4464 } else {
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)
4484 if (auto_rule)
4485 fputs("<hr>\n", stdout);
4488 void html_printer::begin_page(int n)
4490 page_number = n;
4491 #if defined(DEBUGGING)
4492 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
4493 #endif
4494 no_of_printed_pages++;
4496 output_style.f = 0;
4497 output_style.point_size= -1;
4498 output_space_code = 32;
4499 output_draw_point_size = -1;
4500 output_line_thickness = -1;
4501 output_hpos = -1;
4502 output_vpos = -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)
4511 flush_sbuf();
4512 flush_page();
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);
4524 else {
4525 unsigned int r, g, b;
4526 char buf[6+1];
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);
4533 fputs(buf, 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
4553 * file fragments.
4556 void html_printer::write_navigation (const string &top, const string &prev,
4557 const string &next, const string &current)
4559 int need_bar = FALSE;
4561 if (multiple_files) {
4562 write_rule();
4563 fputs("[ ", stdout);
4564 if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
4565 emit_link(prev, "prev");
4566 need_bar = TRUE;
4568 if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
4569 if (need_bar)
4570 fputs(" | ", stdout);
4571 emit_link(next, "next");
4572 need_bar = TRUE;
4574 if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
4575 if (need_bar)
4576 fputs(" | ", stdout);
4577 emit_link(top, "top");
4579 fputs(" ]\n", stdout);
4580 write_rule();
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
4591 * if required.
4594 void html_printer::do_file_components (void)
4596 int fragment_no = 1;
4597 string top;
4598 string prev;
4599 string next;
4600 string current;
4602 file_list.start_of_list();
4603 top = string(job_name);
4604 top += string(".html");
4605 top += '\0';
4606 next = file_list.next_file_name();
4607 next += '\0';
4608 current = next;
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);
4619 prev = current;
4620 current = next;
4621 next = file_list.next_file_name();
4622 next += '\0';
4623 string split_file = file_list.file_name();
4624 split_file += '\0';
4625 fflush(stdout);
4626 freopen(split_file.contents(), "w", stdout);
4627 fragment_no++;
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);
4636 else
4637 write_rule();
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
4668 long t;
4669 #else
4670 time_t t;
4671 #endif
4673 current_paragraph->flush_text();
4674 html.end_line();
4675 html.set_file(stdout);
4676 html.begin_comment("Creator : ")
4677 .put_string("groff ")
4678 .put_string("version ")
4679 .put_string(Version_string)
4680 .end_comment();
4682 t = time(0);
4683 html.begin_comment("CreationDate: ")
4684 .put_string(ctime(&t), strlen(ctime(&t))-1)
4685 .end_comment();
4687 writeHeadMetaStyle();
4689 write_title(TRUE);
4690 head_info += '\0';
4691 fputs(head_info.contents(), stdout);
4692 fputs("</head>\n", stdout);
4693 do_body();
4695 write_title(FALSE);
4696 header.write_headings(stdout, FALSE);
4697 write_rule();
4698 #if defined(DEBUGGING)
4699 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
4700 #endif
4701 html.end_line();
4702 html.end_line();
4704 if (multiple_files) {
4705 fputs("</body>\n", stdout);
4706 fputs("</html>\n", stdout);
4707 do_file_components();
4708 } else {
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)
4722 int i=0;
4723 char *v;
4725 while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
4726 i++;
4727 if (i>0) {
4728 v = new char[i+1];
4729 memcpy(v, s, i+1);
4730 v[i] = (char)0;
4731 if (s[i] == ',')
4732 (*n) = (char *)&s[i+1];
4733 else
4734 (*n) = (char *)&s[i];
4735 return v;
4737 if (s[i] == ',')
4738 (*n) = (char *)&s[1];
4739 else
4740 (*n) = (char *)s;
4741 return NULL;
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)
4750 if (s == NULL) {
4751 char buf[30];
4753 sprintf(buf, "%d", v);
4754 return strsave(buf);
4756 else {
4758 * check that value, s, is the same as, v.
4760 char *t = s;
4762 while (*t == '=')
4763 t++;
4764 if (atoi(t) != v) {
4765 if (f == NULL)
4766 f = (char *)"stdin";
4767 if (l == NULL)
4768 l = (char *)"<none>";
4769 fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %d and was given %s\n",
4770 f, l, id, v, s);
4772 return s;
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
4783 * point.
4786 void html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
4788 char *n;
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);
4799 else
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)
4832 if (type != 'p')
4833 return;
4834 if (s != 0) {
4835 flush_sbuf();
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);
4839 sbuf_style = sty;
4842 if (strncmp(s, "html:", 5) == 0) {
4843 int r=font::res; /* resolution of the device */
4844 font *f=sbuf_style.f;
4846 if (f == NULL) {
4847 int found=FALSE;
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]),
4856 line_number,
4857 env->vpos-env->size*r/72, env->hpos,
4858 env->vpos , env->hpos,
4859 FALSE);
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
4865 * amount.
4867 } else if (strncmp(s, "html</p>:", 9) == 0) {
4868 int r=font::res; /* resolution of the device */
4869 font *f=sbuf_style.f;
4871 if (f == NULL) {
4872 int found=FALSE;
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),
4881 line_number,
4882 env->vpos-env->size*r/72, env->hpos,
4883 env->vpos , env->hpos,
4884 TRUE);
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
4890 * amount.
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)
4913 if (type != 'p')
4914 return;
4916 if (s != 0) {
4917 flush_sbuf();
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);
4921 sbuf_style = sty;
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),
4928 line_number,
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)
4942 int r = font::hor;
4943 int n;
4945 // don't depend on the rounding direction for division of negative integers
4946 if (r == 1)
4947 n = x;
4948 else
4949 n = (x < 0
4950 ? -((-x + r/2 - 1)/r)
4951 : (x + r/2 - 1)/r);
4952 return n * r;
4955 int main(int argc, char **argv)
4957 program_name = argv[0];
4958 static char stderr_buf[BUFSIZ];
4959 setbuf(stderr, stderr_buf);
4960 int c;
4961 static const struct option long_options[] = {
4962 { "help", no_argument, 0, CHAR_MAX + 1 },
4963 { "version", no_argument, 0, 'v' },
4964 { NULL, 0, 0, 0 }
4966 while ((c = getopt_long(argc, argv, "a:bdD:F:g:hi:I:j:lno:prs:S:v",
4967 long_options, NULL))
4968 != EOF)
4969 switch(c) {
4970 case 'a':
4971 /* text antialiasing bits - handled by pre-html */
4972 break;
4973 case 'b':
4974 // set background color to white
4975 default_background = new color;
4976 default_background->set_gray(color::MAX_COLOR_VAL);
4977 break;
4978 case 'd':
4979 /* handled by pre-html */
4980 break;
4981 case 'D':
4982 /* handled by pre-html */
4983 break;
4984 case 'F':
4985 font::command_line_font_dir(optarg);
4986 break;
4987 case 'g':
4988 /* graphic antialiasing bits - handled by pre-html */
4989 break;
4990 case 'h':
4991 /* do not use the Hn headings of html, but manufacture our own */
4992 manufacture_headings = TRUE;
4993 break;
4994 case 'i':
4995 /* handled by pre-html */
4996 break;
4997 case 'I':
4998 /* handled by pre-html */
4999 break;
5000 case 'j':
5001 multiple_files = TRUE;
5002 job_name = optarg;
5003 break;
5004 case 'l':
5005 auto_links = FALSE;
5006 break;
5007 case 'n':
5008 simple_anchors = TRUE;
5009 break;
5010 case 'o':
5011 /* handled by pre-html */
5012 break;
5013 case 'p':
5014 /* handled by pre-html */
5015 break;
5016 case 'r':
5017 auto_rule = FALSE;
5018 break;
5019 case 's':
5020 base_point_size = atoi(optarg);
5021 break;
5022 case 'S':
5023 split_level = atoi(optarg) + 1;
5024 break;
5025 case 'v':
5026 printf("GNU post-grohtml (groff) version %s\n", Version_string);
5027 exit(0);
5028 break;
5029 case CHAR_MAX + 1: // --help
5030 usage(stdout);
5031 exit(0);
5032 break;
5033 case '?':
5034 usage(stderr);
5035 exit(1);
5036 break;
5037 default:
5038 assert(0);
5040 if (optind >= argc) {
5041 do_file("-");
5042 } else {
5043 for (int i = optind; i < argc; i++)
5044 do_file(argv[i]);
5046 return 0;
5049 static void usage(FILE *stream)
5051 fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
5052 program_name);