* src/devices/grohtml/post-html.cpp (html_printer::is_line_start,
[s-roff.git] / src / devices / grohtml / post-html.cpp
blobb66b76248f9da60ed7f62c8b9144301281b700d4
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 = (char *)malloc(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 free(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 free((void *)t->val);
1692 free((void *)t->id);
1693 free(t);
1695 while (yhead != NULL) {
1696 t = yhead;
1697 yhead = yhead->next;
1698 free((void *)t->val);
1699 free((void *)t->id);
1700 free(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 = (assert_pos *)malloc(sizeof(struct 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 free((void *)c);
1744 free((void *)f);
1745 free((void *)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 free((void *)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 free((void *)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, strdup(v));
1837 file_sp = replace_str(file_sp, strdup(f));
1838 line_sp = replace_str(line_sp, strdup(l));
1839 } else if (strcmp(c, "br") == 0) {
1840 check_br_flag = 1;
1841 val_br = replace_str(val_br, strdup(v));
1842 file_br = replace_str(file_br, strdup(f));
1843 line_br = replace_str(line_br, strdup(l));
1844 } else if (strcmp(c, "fi") == 0) {
1845 check_fi_flag = 1;
1846 val_fi = replace_str(val_fi, strdup(v));
1847 file_fi = replace_str(file_fi, strdup(f));
1848 line_fi = replace_str(line_fi, strdup(l));
1849 } else if (strcmp(c, "nf") == 0) {
1850 check_fi_flag = 1;
1851 val_fi = replace_negate_str(val_fi, strdup(v));
1852 file_fi = replace_str(file_fi, strdup(f));
1853 line_fi = replace_str(line_fi, strdup(l));
1854 } else if (strcmp(c, "ce") == 0) {
1855 check_ce_flag = 1;
1856 val_ce = replace_str(val_ce, strdup(v));
1857 file_ce = replace_str(file_ce, strdup(f));
1858 line_ce = replace_str(line_ce, strdup(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 restore_troff_indent();
2233 * handle_tag_within_title - handle a limited number of tags within
2234 * the context of a table. Those tags which
2235 * set values rather than generate spaces
2236 * and paragraphs.
2239 void html_printer::handle_tag_within_title (text_glob *g)
2241 if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll()
2242 || g->is_fi() || g->is_nf())
2243 troff_tag(g);
2247 * do_center - handle the .ce commands from troff.
2250 void html_printer::do_center (char *arg)
2252 next_center = atoi(arg);
2253 seen_center = TRUE;
2257 * do_centered_image - set a flag such that the next devtag is
2258 * placed inside a centered paragraph.
2261 void html_printer::do_centered_image (void)
2263 next_tag = CENTERED;
2267 * do_right_image - set a flag such that the next devtag is
2268 * placed inside a right aligned paragraph.
2271 void html_printer::do_right_image (void)
2273 next_tag = RIGHT;
2277 * do_left_image - set a flag such that the next devtag is
2278 * placed inside a left aligned paragraph.
2281 void html_printer::do_left_image (void)
2283 next_tag = LEFT;
2287 * exists - returns TRUE if filename exists.
2290 static int exists (const char *filename)
2292 FILE *fp = fopen(filename, "r");
2294 if (fp == 0) {
2295 return( FALSE );
2296 } else {
2297 fclose(fp);
2298 return( TRUE );
2303 * generate_img_src - returns a html image tag for the filename
2304 * providing that the image exists.
2307 static string &generate_img_src (const char *filename)
2309 string *s = new string("");
2311 while (filename && (filename[0] == ' ')) {
2312 filename++;
2314 if (exists(filename))
2315 *s += string("<img src=\"") + filename + "\" "
2316 + "alt=\"Image " + filename + "\">";
2317 return *s;
2321 * do_auto_image - tests whether the image, indicated by filename,
2322 * is present, if so then it emits an html image tag.
2323 * An image tag may be passed through from pic, eqn
2324 * but the corresponding image might not be created.
2325 * Consider .EQ delim $$ .EN or an empty .PS .PE.
2328 void html_printer::do_auto_image (text_glob *g, const char *filename)
2330 string buffer = generate_img_src(filename);
2332 if (! buffer.empty()) {
2334 * utilize emit_raw by creating a new text_glob.
2336 text_glob h = *g;
2338 h.text_string = buffer.contents();
2339 h.text_length = buffer.length();
2340 emit_raw(&h);
2341 } else
2342 next_tag = INLINE;
2346 * outstanding_eol - call do_eol, n, times.
2349 void html_printer::outstanding_eol (int n)
2351 while (n > 0) {
2352 do_eol();
2353 n--;
2358 * do_title - handle the .tl commands from troff.
2361 void html_printer::do_title (void)
2363 text_glob *t;
2364 int removed_from_head;
2366 if (page_number == 1) {
2367 int found_title_start = FALSE;
2368 if (! page_contents->glyphs.is_empty()) {
2369 page_contents->glyphs.sub_move_right(); /* move onto next word */
2370 do {
2371 t = page_contents->glyphs.get_data();
2372 removed_from_head = FALSE;
2373 if (t->is_auto_img()) {
2374 string img = generate_img_src((char *)(t->text_string + 20));
2376 if (! img.empty()) {
2377 if (found_title_start)
2378 title.text += " ";
2379 found_title_start = TRUE;
2380 title.has_been_found = TRUE;
2381 title.text += img;
2383 page_contents->glyphs.sub_move_right(); /* move onto next word */
2384 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2385 (page_contents->glyphs.is_equal_to_head()));
2386 } else if (t->is_eo_tl()) {
2387 /* end of title found
2389 title.has_been_found = TRUE;
2390 return;
2391 } else if (t->is_a_tag()) {
2392 handle_tag_within_title(t);
2393 page_contents->glyphs.sub_move_right(); /* move onto next word */
2394 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2395 (page_contents->glyphs.is_equal_to_head()));
2396 } else if (found_title_start) {
2397 title.text += " " + string(t->text_string, t->text_length);
2398 page_contents->glyphs.sub_move_right(); /* move onto next word */
2399 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2400 (page_contents->glyphs.is_equal_to_head()));
2401 } else {
2402 title.text += string(t->text_string, t->text_length);
2403 found_title_start = TRUE;
2404 title.has_been_found = TRUE;
2405 page_contents->glyphs.sub_move_right(); /* move onto next word */
2406 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2407 (page_contents->glyphs.is_equal_to_head()));
2409 } while ((! page_contents->glyphs.is_equal_to_head()) ||
2410 (removed_from_head));
2415 void html_printer::write_header (void)
2417 if (! header.header_buffer.empty()) {
2418 int space = current_paragraph->retrieve_para_space() || seen_space;
2420 if (header.header_level > 7) {
2421 header.header_level = 7;
2424 // firstly we must terminate any font and type faces
2425 current_paragraph->done_para();
2426 supress_sub_sup = TRUE;
2428 if (cutoff_heading+2 > header.header_level) {
2429 // now we save the header so we can issue a list of links
2430 header.no_of_headings++;
2431 style st;
2433 text_glob *h=new text_glob();
2434 h->text_glob_html(&st,
2435 header.headings.add_string(header.header_buffer),
2436 header.header_buffer.length(),
2437 header.no_of_headings, header.header_level,
2438 header.no_of_headings, header.header_level);
2440 header.headers.add(h,
2441 header.no_of_headings,
2442 header.no_of_headings, header.no_of_headings,
2443 header.no_of_headings, header.no_of_headings); // and add this header to the header list
2445 // lastly we generate a tag
2447 html.nl().nl().put_string("<a name=\"");
2448 if (simple_anchors) {
2449 string buffer(ANCHOR_TEMPLATE);
2451 buffer += as_string(header.no_of_headings);
2452 buffer += '\0';
2453 html.put_string(buffer.contents());
2454 } else {
2455 html.put_string(header.header_buffer);
2457 html.put_string("\"></a>").nl();
2460 if (manufacture_headings) {
2461 // line break before a header
2462 if (!current_paragraph->emitted_text())
2463 current_paragraph->do_space();
2464 // user wants manufactured headings which look better than <Hn></Hn>
2465 if (header.header_level<4) {
2466 html.put_string("<b><font size=\"+1\">");
2467 html.put_string(header.header_buffer);
2468 html.put_string("</font></b>").nl();
2470 else {
2471 html.put_string("<b>");
2472 html.put_string(header.header_buffer);
2473 html.put_string("</b>").nl();
2476 else {
2477 // and now we issue the real header
2478 html.put_string("<h");
2479 html.put_number(header.header_level);
2480 html.put_string(">");
2481 html.put_string(header.header_buffer);
2482 html.put_string("</h");
2483 html.put_number(header.header_level);
2484 html.put_string(">").nl();
2487 /* and now we save the file name in which this header will occur */
2489 style st; // fake style to enable us to use the list data structure
2491 text_glob *h=new text_glob();
2492 h->text_glob_html(&st,
2493 header.headings.add_string(file_list.file_name()),
2494 file_list.file_name().length(),
2495 header.no_of_headings, header.header_level,
2496 header.no_of_headings, header.header_level);
2498 header.header_filename.add(h,
2499 header.no_of_headings,
2500 header.no_of_headings, header.no_of_headings,
2501 header.no_of_headings, header.no_of_headings);
2503 current_paragraph->do_para(&html, "", get_troff_indent(), pageoffset, linelength, space);
2507 void html_printer::determine_header_level (int level)
2509 if (level == 0) {
2510 int i;
2512 for (i=0; ((i<header.header_buffer.length())
2513 && ((header.header_buffer[i] == '.')
2514 || is_digit(header.header_buffer[i]))) ; i++) {
2515 if (header.header_buffer[i] == '.') {
2516 level++;
2520 header.header_level = level+1;
2521 if (header.header_level >= 2 && header.header_level <= split_level) {
2522 header.no_of_level_one_headings++;
2523 insert_split_file();
2528 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2531 void html_printer::do_heading (char *arg)
2533 text_glob *g;
2534 int level=atoi(arg);
2535 int horiz;
2537 header.header_buffer.clear();
2538 page_contents->glyphs.move_right();
2539 if (! page_contents->glyphs.is_equal_to_head()) {
2540 g = page_contents->glyphs.get_data();
2541 horiz = g->minh;
2542 do {
2543 if (g->is_auto_img()) {
2544 string img=generate_img_src((char *)(g->text_string + 20));
2546 if (! img.empty()) {
2547 simple_anchors = TRUE; // we cannot use full heading anchors with images
2548 if (horiz < g->minh)
2549 header.header_buffer += " ";
2551 header.header_buffer += img;
2554 else if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll())
2555 troff_tag(g);
2556 else if (g->is_fi())
2557 fill_on = 1;
2558 else if (g->is_nf())
2559 fill_on = 0;
2560 else if (! (g->is_a_line() || g->is_a_tag())) {
2562 * we ignore the other tag commands when constructing a heading
2564 if (horiz < g->minh)
2565 header.header_buffer += " ";
2567 horiz = g->maxh;
2568 header.header_buffer += string(g->text_string, g->text_length);
2570 page_contents->glyphs.move_right();
2571 g = page_contents->glyphs.get_data();
2572 } while ((! page_contents->glyphs.is_equal_to_head()) &&
2573 (! g->is_eo_h()));
2576 determine_header_level(level);
2577 write_header();
2579 // finally set the output to neutral for after the header
2580 g = page_contents->glyphs.get_data();
2581 page_contents->glyphs.move_left(); // so that next time we use old g
2585 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
2588 int html_printer::is_courier_until_eol (void)
2590 text_glob *orig = page_contents->glyphs.get_data();
2591 int result = TRUE;
2592 text_glob *g;
2594 if (! page_contents->glyphs.is_equal_to_tail()) {
2595 page_contents->glyphs.move_right();
2596 do {
2597 g = page_contents->glyphs.get_data();
2598 if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2599 result = FALSE;
2600 page_contents->glyphs.move_right();
2601 } while (result &&
2602 (! page_contents->glyphs.is_equal_to_head()) &&
2603 (! g->is_fi()) && (! g->is_eol()));
2606 * now restore our previous position.
2608 while (page_contents->glyphs.get_data() != orig)
2609 page_contents->glyphs.move_left();
2611 return result;
2615 * do_linelength - handle the .ll command from troff.
2618 void html_printer::do_linelength (char *arg)
2620 if (max_linelength == -1)
2621 max_linelength = atoi(arg);
2623 next_linelength = atoi(arg);
2624 seen_linelength = TRUE;
2628 * do_pageoffset - handle the .po command from troff.
2631 void html_printer::do_pageoffset (char *arg)
2633 next_pageoffset = atoi(arg);
2634 seen_pageoffset = TRUE;
2638 * get_troff_indent - returns the indent value.
2641 int html_printer::get_troff_indent (void)
2643 if (end_tempindent > 0)
2644 return temp_indent;
2645 else
2646 return troff_indent;
2650 * do_indentation - handle the .in command from troff.
2653 void html_printer::do_indentation (char *arg)
2655 next_indent = atoi(arg);
2656 seen_indent = TRUE;
2660 * do_tempindent - handle the .ti command from troff.
2663 void html_printer::do_tempindent (char *arg)
2665 if (fill_on) {
2667 * we set the end_tempindent to 2 as the first .br
2668 * activates the .ti and the second terminates it.
2670 end_tempindent = 2;
2671 temp_indent = atoi(arg);
2676 * shutdown_table - shuts down the current table.
2679 void html_printer::shutdown_table (void)
2681 if (table != NULL) {
2682 current_paragraph->done_para();
2683 table->emit_finish_table();
2684 // dont delete this table as it will be deleted when we destroy the text_glob
2685 table = NULL;
2690 * do_indent - remember the indent parameters and if
2691 * indent is > pageoff and indent has changed
2692 * then we start a html table to implement the indentation.
2695 void html_printer::do_indent (int in, int pageoff, int linelen)
2697 if ((device_indent != -1) &&
2698 (pageoffset+device_indent != in+pageoff)) {
2700 int space = current_paragraph->retrieve_para_space() || seen_space;
2701 current_paragraph->done_para();
2703 device_indent = in;
2704 pageoffset = pageoff;
2705 if (linelen <= max_linelength)
2706 linelength = linelen;
2708 current_paragraph->do_para(&html, "", device_indent,
2709 pageoffset, max_linelength, space);
2714 * do_verticalspacing - handle the .vs command from troff.
2717 void html_printer::do_verticalspacing (char *arg)
2719 vertical_spacing = atoi(arg);
2723 * do_pointsize - handle the .ps command from troff.
2726 void html_printer::do_pointsize (char *arg)
2729 * firstly check to see whether this point size is really associated with a .tl tag
2732 if (! page_contents->glyphs.is_empty()) {
2733 text_glob *g = page_contents->glyphs.get_data();
2734 text_glob *t = page_contents->glyphs.get_data();
2736 while (t->is_a_tag() && (! page_contents->glyphs.is_equal_to_head())) {
2737 if (t->is_tl()) {
2739 * found title therefore ignore this .ps tag
2741 while (t != g) {
2742 page_contents->glyphs.move_left();
2743 t = page_contents->glyphs.get_data();
2745 return;
2747 page_contents->glyphs.move_right();
2748 t = page_contents->glyphs.get_data();
2751 * move back to original position
2753 while (t != g) {
2754 page_contents->glyphs.move_left();
2755 t = page_contents->glyphs.get_data();
2758 * collect legal pointsize
2760 pointsize = atoi(arg);
2765 * do_fill - records whether troff has requested that text be filled.
2768 void html_printer::do_fill (char *arg)
2770 int on = atoi(arg);
2772 output_hpos = get_troff_indent()+pageoffset;
2773 supress_sub_sup = TRUE;
2775 if (fill_on != on) {
2776 if (on)
2777 current_paragraph->do_para("", seen_space);
2778 fill_on = on;
2783 * do_eol - handle the end of line
2786 void html_printer::do_eol (void)
2788 if (! fill_on) {
2789 if (current_paragraph->ever_emitted_text()) {
2790 current_paragraph->do_newline();
2791 current_paragraph->do_break();
2794 output_hpos = get_troff_indent()+pageoffset;
2798 * do_check_center - checks to see whether we have seen a `.ce' tag
2799 * during the previous line.
2802 void html_printer::do_check_center(void)
2804 if (seen_center) {
2805 seen_center = FALSE;
2806 if (next_center > 0) {
2807 if (end_center == 0) {
2808 int space = current_paragraph->retrieve_para_space() || seen_space;
2809 current_paragraph->done_para();
2810 supress_sub_sup = TRUE;
2811 current_paragraph->do_para("align=center", space);
2812 } else
2813 if (strcmp("align=center",
2814 current_paragraph->get_alignment()) != 0) {
2816 * different alignment, so shutdown paragraph and open
2817 * a new one.
2819 int space = current_paragraph->retrieve_para_space() || seen_space;
2820 current_paragraph->done_para();
2821 supress_sub_sup = TRUE;
2822 current_paragraph->do_para("align=center", space);
2823 } else
2825 * same alignment, if we have emitted text then issue a break.
2827 if (current_paragraph->emitted_text())
2828 current_paragraph->do_break();
2829 } else
2831 * next_center == 0
2833 if (end_center > 0) {
2834 seen_space = seen_space || current_paragraph->retrieve_para_space();
2835 current_paragraph->done_para();
2836 supress_sub_sup = TRUE;
2838 end_center = next_center;
2843 * do_eol_ce - handle end of line specifically for a .ce
2846 void html_printer::do_eol_ce (void)
2848 if (end_center > 0) {
2849 if (end_center > 1)
2850 if (current_paragraph->emitted_text())
2851 current_paragraph->do_break();
2853 end_center--;
2854 if (end_center == 0) {
2855 current_paragraph->done_para();
2856 supress_sub_sup = TRUE;
2862 * do_flush - flushes all output and tags.
2865 void html_printer::do_flush (void)
2867 current_paragraph->done_para();
2871 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
2874 void html_printer::do_links (void)
2876 html.end_line(); // flush line
2877 auto_links = FALSE; /* from now on only emit under user request */
2878 file_list.add_new_file(xtmpfile());
2879 file_list.set_links_required();
2880 html.set_file(file_list.get_file());
2884 * insert_split_file -
2887 void html_printer::insert_split_file (void)
2889 if (multiple_files) {
2890 current_paragraph->done_para(); // flush paragraph
2891 html.end_line(); // flush line
2892 html.set_file(file_list.get_file()); // flush current file
2893 file_list.add_new_file(xtmpfile());
2894 string split_file = job_name;
2896 split_file += string("-");
2897 split_file += as_string(header.no_of_level_one_headings);
2898 split_file += string(".html");
2899 split_file += '\0';
2901 file_list.set_file_name(split_file);
2902 html.set_file(file_list.get_file());
2907 * do_job_name - assigns the job_name to name.
2910 void html_printer::do_job_name (char *name)
2912 if (! multiple_files) {
2913 multiple_files = TRUE;
2914 while (name != NULL && (*name != (char)0) && (*name == ' '))
2915 name++;
2916 job_name = name;
2921 * do_head - adds a string to head_info which is to be included into
2922 * the <head> </head> section of the html document.
2925 void html_printer::do_head (char *name)
2927 head_info += string(name);
2928 head_info += '\n';
2932 * do_break - handles the ".br" request and also
2933 * undoes an outstanding ".ti" command
2934 * and calls indent if the indentation
2935 * related registers have changed.
2938 void html_printer::do_break (void)
2940 int seen_temp_indent = FALSE;
2942 current_paragraph->do_break();
2943 if (end_tempindent > 0) {
2944 end_tempindent--;
2945 if (end_tempindent > 0)
2946 seen_temp_indent = TRUE;
2948 if (seen_indent || seen_pageoffset || seen_linelength || seen_temp_indent) {
2949 if (seen_indent && (! seen_temp_indent))
2950 troff_indent = next_indent;
2951 if (! seen_pageoffset)
2952 next_pageoffset = pageoffset;
2953 if (! seen_linelength)
2954 next_linelength = linelength;
2955 do_indent(get_troff_indent(), next_pageoffset, next_linelength);
2957 seen_indent = seen_temp_indent;
2958 seen_linelength = FALSE;
2959 seen_pageoffset = FALSE;
2960 do_check_center();
2961 output_hpos = get_troff_indent()+pageoffset;
2962 supress_sub_sup = TRUE;
2965 void html_printer::do_space (char *arg)
2967 int n = atoi(arg);
2969 seen_space = atoi(arg);
2970 as.check_sp(seen_space);
2971 #if 0
2972 if (n>0 && table)
2973 table->set_space(TRUE);
2974 #endif
2976 while (n>0) {
2977 current_paragraph->do_space();
2978 n--;
2980 supress_sub_sup = TRUE;
2984 * do_tab_ts - start a table, which will have already been defined.
2987 void html_printer::do_tab_ts (text_glob *g)
2989 html_table *t = g->get_table();
2991 if (t != NULL) {
2992 current_column = 0;
2993 current_paragraph->done_pre();
2994 current_paragraph->done_para();
2995 current_paragraph->remove_para_space();
2997 #if defined(DEBUG_TABLES)
2998 html.simple_comment("TABS");
2999 #endif
3001 t->set_linelength(max_linelength);
3002 t->add_indent(pageoffset);
3003 #if 0
3004 t->emit_table_header(seen_space);
3005 #else
3006 t->emit_table_header(FALSE);
3007 row_space = current_paragraph->retrieve_para_space() || seen_space;
3008 seen_space = FALSE;
3009 #endif
3012 table = t;
3016 * do_tab_te - finish a table.
3019 void html_printer::do_tab_te (void)
3021 if (table) {
3022 current_paragraph->done_para();
3023 current_paragraph->remove_para_space();
3024 table->emit_finish_table();
3027 table = NULL;
3028 restore_troff_indent();
3032 * do_tab - handle the "devtag:tab" tag
3035 void html_printer::do_tab (char *s)
3037 if (table) {
3038 while (isspace(*s))
3039 s++;
3040 s++;
3041 int col = table->find_column(atoi(s) + pageoffset + get_troff_indent());
3042 if (col > 0) {
3043 current_paragraph->done_para();
3044 table->emit_col(col);
3050 * do_tab0 - handle the "devtag:tab0" tag
3053 void html_printer::do_tab0 (void)
3055 if (table) {
3056 int col = table->find_column(pageoffset+get_troff_indent());
3057 if (col > 0) {
3058 current_paragraph->done_para();
3059 table->emit_col(col);
3065 * do_col - start column, s.
3068 void html_printer::do_col (char *s)
3070 if (table) {
3071 if (atoi(s) < current_column)
3072 row_space = seen_space;
3074 current_column = atoi(s);
3075 current_paragraph->done_para();
3076 table->emit_col(current_column);
3077 current_paragraph->do_para("", row_space);
3082 * troff_tag - processes the troff tag and manipulates the troff
3083 * state machine.
3086 void html_printer::troff_tag (text_glob *g)
3089 * firstly skip over devtag:
3091 char *t=(char *)g->text_string+strlen("devtag:");
3093 if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
3094 do_end_para(g);
3095 } else if (g->is_eol()) {
3096 do_eol();
3097 } else if (g->is_eol_ce()) {
3098 do_eol_ce();
3099 } else if (strncmp(t, ".sp", 3) == 0) {
3100 char *a = (char *)t+3;
3101 do_space(a);
3102 } else if (strncmp(t, ".br", 3) == 0) {
3103 seen_break = 1;
3104 as.check_br(1);
3105 do_break();
3106 } else if (strcmp(t, ".centered-image") == 0) {
3107 do_centered_image();
3108 } else if (strcmp(t, ".right-image") == 0) {
3109 do_right_image();
3110 } else if (strcmp(t, ".left-image") == 0) {
3111 do_left_image();
3112 } else if (strncmp(t, ".auto-image", 11) == 0) {
3113 char *a = (char *)t+11;
3114 do_auto_image(g, a);
3115 } else if (strncmp(t, ".ce", 3) == 0) {
3116 char *a = (char *)t+3;
3117 supress_sub_sup = TRUE;
3118 do_center(a);
3119 } else if (g->is_tl()) {
3120 supress_sub_sup = TRUE;
3121 title.with_h1 = TRUE;
3122 do_title();
3123 } else if (strncmp(t, ".html-tl", 8) == 0) {
3124 supress_sub_sup = TRUE;
3125 title.with_h1 = FALSE;
3126 do_title();
3127 } else if (strncmp(t, ".fi", 3) == 0) {
3128 char *a = (char *)t+3;
3129 do_fill(a);
3130 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
3131 char *a = (char *)t+3;
3132 do_heading(a);
3133 } else if (strncmp(t, ".ll", 3) == 0) {
3134 char *a = (char *)t+3;
3135 do_linelength(a);
3136 } else if (strncmp(t, ".po", 3) == 0) {
3137 char *a = (char *)t+3;
3138 do_pageoffset(a);
3139 } else if (strncmp(t, ".in", 3) == 0) {
3140 char *a = (char *)t+3;
3141 do_indentation(a);
3142 } else if (strncmp(t, ".ti", 3) == 0) {
3143 char *a = (char *)t+3;
3144 do_tempindent(a);
3145 } else if (strncmp(t, ".vs", 3) == 0) {
3146 char *a = (char *)t+3;
3147 do_verticalspacing(a);
3148 } else if (strncmp(t, ".ps", 3) == 0) {
3149 char *a = (char *)t+3;
3150 do_pointsize(a);
3151 } else if (strcmp(t, ".links") == 0) {
3152 do_links();
3153 } else if (strncmp(t, ".job-name", 9) == 0) {
3154 char *a = (char *)t+9;
3155 do_job_name(a);
3156 } else if (strncmp(t, ".head", 5) == 0) {
3157 char *a = (char *)t+5;
3158 do_head(a);
3159 } else if (strcmp(t, ".no-auto-rule") == 0) {
3160 auto_rule = FALSE;
3161 } else if (strcmp(t, ".tab-ts") == 0) {
3162 do_tab_ts(g);
3163 } else if (strcmp(t, ".tab-te") == 0) {
3164 do_tab_te();
3165 } else if (strncmp(t, ".col ", 5) == 0) {
3166 char *a = (char *)t+4;
3167 do_col(a);
3168 } else if (strncmp(t, "tab ", 4) == 0) {
3169 char *a = (char *)t+3;
3170 do_tab(a);
3171 } else if (strncmp(t, "tab0", 4) == 0) {
3172 do_tab0();
3177 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
3180 int html_printer::is_in_middle (int left, int right)
3182 return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
3183 <= CENTER_TOLERANCE );
3187 * flush_globs - runs through the text glob list and emits html.
3190 void html_printer::flush_globs (void)
3192 text_glob *g;
3194 if (! page_contents->glyphs.is_empty()) {
3195 page_contents->glyphs.start_from_head();
3196 do {
3197 g = page_contents->glyphs.get_data();
3198 #if 0
3199 fprintf(stderr, "[%s:%d:%d:%d:%d]",
3200 g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
3201 fflush(stderr);
3202 #endif
3204 handle_state_assertion(g);
3206 if (strcmp(g->text_string, "XXXXXXX") == 0)
3207 stop();
3209 if (g->is_a_tag())
3210 troff_tag(g);
3211 else if (g->is_a_line())
3212 emit_line(g);
3213 else {
3214 as.check_sp(seen_space);
3215 as.check_br(seen_break);
3216 seen_break = 0;
3217 seen_space = 0;
3218 emit_html(g);
3221 as.check_fi(fill_on);
3222 as.check_ce(end_center);
3224 * after processing the title (and removing it) the glyph list might be empty
3226 if (! page_contents->glyphs.is_empty()) {
3227 page_contents->glyphs.move_right();
3229 } while (! page_contents->glyphs.is_equal_to_head());
3234 * calc_nf - calculates the _no_ format flag, given the
3235 * text glob, g.
3238 int html_printer::calc_nf (text_glob *g, int nf)
3240 if (g != NULL) {
3241 if (g->is_fi()) {
3242 as.check_fi(TRUE);
3243 return FALSE;
3245 if (g->is_nf()) {
3246 as.check_fi(FALSE);
3247 return TRUE;
3250 as.check_fi(! nf);
3251 return nf;
3255 * calc_po_in - calculates the, in, po, registers
3258 void html_printer::calc_po_in (text_glob *g, int nf)
3260 if (g->is_in())
3261 troff_indent = g->get_arg();
3262 else if (g->is_po())
3263 pageoffset = g->get_arg();
3264 else if (g->is_ti()) {
3265 temp_indent = g->get_arg();
3266 end_tempindent = 2;
3267 } else if (g->is_br() || (nf && g->is_eol())) {
3268 if (end_tempindent > 0)
3269 end_tempindent--;
3274 * next_horiz_pos - returns the next horiz position.
3275 * -1 is returned if it doesn't exist.
3278 int html_printer::next_horiz_pos (text_glob *g, int nf)
3280 int next = -1;
3282 if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
3283 if (! page_contents->glyphs.is_empty()) {
3284 page_contents->glyphs.move_right_get_data();
3285 if (g == NULL) {
3286 page_contents->glyphs.start_from_head();
3287 as.reset();
3289 else {
3290 next = g->minh;
3291 page_contents->glyphs.move_left();
3294 return next;
3298 * insert_tab_ts - inserts a tab-ts before, where.
3301 text_glob *html_printer::insert_tab_ts (text_glob *where)
3303 text_glob *start_of_table;
3304 text_glob *old_pos = page_contents->glyphs.get_data();
3306 page_contents->glyphs.move_to(where);
3307 page_contents->glyphs.move_left();
3308 page_contents->insert_tag(string("devtag:.tab-ts")); // tab table start
3309 page_contents->glyphs.move_right();
3310 start_of_table = page_contents->glyphs.get_data();
3311 page_contents->glyphs.move_to(old_pos);
3312 return start_of_table;
3316 * insert_tab_te - inserts a tab-te before the current position
3317 * (it skips backwards over .sp/.br)
3320 void html_printer::insert_tab_te (void)
3322 text_glob *g = page_contents->glyphs.get_data();
3323 page_contents->dump_page();
3325 while (page_contents->glyphs.get_data()->is_a_tag())
3326 page_contents->glyphs.move_left();
3328 page_contents->insert_tag(string("devtag:.tab-te")); // tab table end
3329 while (g != page_contents->glyphs.get_data())
3330 page_contents->glyphs.move_right();
3331 page_contents->dump_page();
3335 * insert_tab_0 - inserts a tab0 before, where.
3338 void html_printer::insert_tab_0 (text_glob *where)
3340 text_glob *old_pos = page_contents->glyphs.get_data();
3342 page_contents->glyphs.move_to(where);
3343 page_contents->glyphs.move_left();
3344 page_contents->insert_tag(string("devtag:tab0")); // tab0 start of line
3345 page_contents->glyphs.move_right();
3346 page_contents->glyphs.move_to(old_pos);
3350 * remove_tabs - removes the tabs tags on this line.
3353 void html_printer::remove_tabs (void)
3355 text_glob *orig = page_contents->glyphs.get_data();
3356 text_glob *g;
3358 if (! page_contents->glyphs.is_equal_to_tail()) {
3359 do {
3360 g = page_contents->glyphs.get_data();
3361 if (g->is_tab()) {
3362 page_contents->glyphs.sub_move_right();
3363 if (g == orig)
3364 orig = page_contents->glyphs.get_data();
3365 } else
3366 page_contents->glyphs.move_right();
3367 } while ((! page_contents->glyphs.is_equal_to_head()) &&
3368 (! g->is_eol()));
3371 * now restore our previous position.
3373 while (page_contents->glyphs.get_data() != orig)
3374 page_contents->glyphs.move_left();
3378 void html_printer::remove_courier_tabs (void)
3380 text_glob *g;
3381 int line_start = TRUE;
3382 int nf = FALSE;
3384 if (! page_contents->glyphs.is_empty()) {
3385 page_contents->glyphs.start_from_head();
3386 as.reset();
3387 line_start = TRUE;
3388 do {
3389 g = page_contents->glyphs.get_data();
3390 handle_state_assertion(g);
3391 nf = calc_nf(g, nf);
3393 if (line_start) {
3394 if (line_start && nf && is_courier_until_eol()) {
3395 remove_tabs();
3396 g = page_contents->glyphs.get_data();
3400 // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
3401 line_start = g->is_br() || (nf && g->is_eol());
3402 page_contents->glyphs.move_right();
3403 } while (! page_contents->glyphs.is_equal_to_head());
3407 void html_printer::insert_tab0_foreach_tab (void)
3409 text_glob *start_of_line = NULL;
3410 text_glob *g = NULL;
3411 int seen_tab = FALSE;
3412 int seen_col = FALSE;
3413 int nf = FALSE;
3415 if (! page_contents->glyphs.is_empty()) {
3416 page_contents->glyphs.start_from_head();
3417 as.reset();
3418 start_of_line = page_contents->glyphs.get_data();
3419 do {
3420 g = page_contents->glyphs.get_data();
3421 handle_state_assertion(g);
3422 nf = calc_nf(g, nf);
3424 if (g->is_tab())
3425 seen_tab = TRUE;
3427 if (g->is_col())
3428 seen_col = TRUE;
3430 if (g->is_br() || (nf && g->is_eol())) {
3431 do {
3432 page_contents->glyphs.move_right();
3433 g = page_contents->glyphs.get_data();
3434 handle_state_assertion(g);
3435 nf = calc_nf(g, nf);
3436 if (page_contents->glyphs.is_equal_to_head()) {
3437 if (seen_tab && !seen_col)
3438 insert_tab_0(start_of_line);
3439 return;
3441 } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
3442 // printf("\nstart_of_line is: %s\n", g->text_string);
3443 if (seen_tab && !seen_col) {
3444 insert_tab_0(start_of_line);
3445 page_contents->glyphs.move_to(g);
3448 seen_tab = FALSE;
3449 seen_col = FALSE;
3450 start_of_line = g;
3452 page_contents->glyphs.move_right();
3453 } while (! page_contents->glyphs.is_equal_to_head());
3454 if (seen_tab && !seen_col)
3455 insert_tab_0(start_of_line);
3461 * update_min_max - updates the extent of a column, given the left and right
3462 * extents of a glyph, g.
3465 void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
3467 switch (type_of_col) {
3469 case tab_tag:
3470 break;
3471 case tab0_tag:
3472 *minimum = g->minh;
3473 break;
3474 case col_tag:
3475 *minimum = g->minh;
3476 *maximum = g->maxh;
3477 break;
3478 default:
3479 break;
3484 * add_table_end - moves left one glyph, adds a table end tag and adds a
3485 * debugging string.
3488 void html_printer::add_table_end (const char *
3489 #if defined(DEBUG_TABLES)
3490 debug_string
3491 #endif
3494 page_contents->glyphs.move_left();
3495 insert_tab_te();
3496 #if defined(DEBUG_TABLES)
3497 page_contents->insert_tag(string(debug_string));
3498 #endif
3502 * lookahead_for_tables - checks for .col tags and inserts table
3503 * start/end tags
3506 void html_printer::lookahead_for_tables (void)
3508 text_glob *g;
3509 text_glob *start_of_line = NULL;
3510 text_glob *start_of_table = NULL;
3511 text_glob *last = NULL;
3512 colType type_of_col = none;
3513 int left = 0;
3514 int found_col = FALSE;
3515 int seen_text = FALSE;
3516 int ncol = 0;
3517 int colmin = 0; // pacify compiler
3518 int colmax = 0; // pacify compiler
3519 html_table *tbl = new html_table(&html, -1);
3520 const char *tab_defs = NULL;
3521 char align = 'L';
3522 int nf = FALSE;
3523 int old_pageoffset = pageoffset;
3525 remove_courier_tabs();
3526 page_contents->dump_page();
3527 insert_tab0_foreach_tab();
3528 page_contents->dump_page();
3529 if (! page_contents->glyphs.is_empty()) {
3530 page_contents->glyphs.start_from_head();
3531 as.reset();
3532 g = page_contents->glyphs.get_data();
3533 if (g->is_br()) {
3534 g = page_contents->glyphs.move_right_get_data();
3535 handle_state_assertion(g);
3536 if (page_contents->glyphs.is_equal_to_head()) {
3537 if (tbl != NULL) {
3538 delete tbl;
3539 tbl = NULL;
3541 return;
3544 start_of_line = g;
3545 seen_text = FALSE;
3546 ncol = 0;
3547 left = next_horiz_pos(g, nf);
3548 if (found_col)
3549 last = g;
3550 found_col = FALSE;
3553 do {
3554 #if defined(DEBUG_TABLES)
3555 fprintf(stderr, " [") ;
3556 fprintf(stderr, g->text_string) ;
3557 fprintf(stderr, "] ") ;
3558 fflush(stderr);
3559 if (strcmp(g->text_string, "XXXXXXX") == 0)
3560 stop();
3561 #endif
3563 nf = calc_nf(g, nf);
3564 calc_po_in(g, nf);
3565 if (g->is_col()) {
3566 if (type_of_col == tab_tag && start_of_table != NULL) {
3567 page_contents->glyphs.move_left();
3568 insert_tab_te();
3569 start_of_table->remember_table(tbl);
3570 tbl = new html_table(&html, -1);
3571 page_contents->insert_tag(string("*** TAB -> COL ***"));
3572 if (tab_defs != NULL)
3573 tbl->tab_stops->init(tab_defs);
3574 start_of_table = NULL;
3575 last = NULL;
3577 type_of_col = col_tag;
3578 found_col = TRUE;
3579 ncol = g->get_arg();
3580 align = 'L';
3581 colmin = 0;
3582 colmax = 0;
3583 } else if (g->is_tab()) {
3584 type_of_col = tab_tag;
3585 colmin = g->get_tab_args(&align);
3586 align = 'L'; // for now as 'C' and 'R' are broken
3587 ncol = tbl->find_tab_column(colmin);
3588 colmin += pageoffset + get_troff_indent();
3589 colmax = tbl->get_tab_pos(ncol+1);
3590 if (colmax > 0)
3591 colmax += pageoffset + get_troff_indent();
3592 } else if (g->is_tab0()) {
3593 if (type_of_col == col_tag && start_of_table != NULL) {
3594 page_contents->glyphs.move_left();
3595 insert_tab_te();
3596 start_of_table->remember_table(tbl);
3597 tbl = new html_table(&html, -1);
3598 page_contents->insert_tag(string("*** COL -> TAB ***"));
3599 start_of_table = NULL;
3600 last = NULL;
3602 if (tab_defs != NULL)
3603 tbl->tab_stops->init(tab_defs);
3605 type_of_col = tab0_tag;
3606 ncol = 1;
3607 colmin = 0;
3608 colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
3609 } else if (! g->is_a_tag())
3610 update_min_max(type_of_col, &colmin, &colmax, g);
3612 if ((! g->is_a_tag()) || g->is_tab())
3613 seen_text = TRUE;
3615 if ((g->is_col() || g->is_tab() || g->is_tab0())
3616 && (start_of_line != NULL) && (start_of_table == NULL)) {
3617 start_of_table = insert_tab_ts(start_of_line);
3618 start_of_line = NULL;
3619 seen_text = FALSE;
3620 } else if (g->is_ce() && (start_of_table != NULL)) {
3621 add_table_end("*** CE ***");
3622 start_of_table->remember_table(tbl);
3623 start_of_table = NULL;
3624 last = NULL;
3625 } else if (g->is_ta()) {
3626 tab_defs = g->text_string;
3628 if (type_of_col == col_tag)
3629 tbl->tab_stops->check_init(tab_defs);
3631 if (!tbl->tab_stops->compatible(tab_defs)) {
3632 if (start_of_table != NULL) {
3633 add_table_end("*** TABS ***");
3634 start_of_table->remember_table(tbl);
3635 tbl = new html_table(&html, -1);
3636 start_of_table = NULL;
3637 type_of_col = none;
3638 last = NULL;
3640 tbl->tab_stops->init(tab_defs);
3644 if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
3645 // we are in a table and have a glyph
3646 if ((ncol == 0) || (! tbl->add_column(ncol, colmin, colmax, align))) {
3647 if (ncol == 0)
3648 add_table_end("*** NCOL == 0 ***");
3649 else
3650 add_table_end("*** CROSSED COLS ***");
3652 start_of_table->remember_table(tbl);
3653 tbl = new html_table(&html, -1);
3654 start_of_table = NULL;
3655 type_of_col = none;
3656 last = NULL;
3661 * move onto next glob, check whether we are starting a new line
3663 g = page_contents->glyphs.move_right_get_data();
3664 handle_state_assertion(g);
3666 if (g == NULL) {
3667 if (found_col) {
3668 page_contents->glyphs.start_from_head();
3669 as.reset();
3670 last = g;
3671 found_col = FALSE;
3673 } else if (g->is_br() || (nf && g->is_eol())) {
3674 do {
3675 g = page_contents->glyphs.move_right_get_data();
3676 handle_state_assertion(g);
3677 nf = calc_nf(g, nf);
3678 } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
3679 start_of_line = g;
3680 seen_text = FALSE;
3681 ncol = 0;
3682 left = next_horiz_pos(g, nf);
3683 if (found_col)
3684 last = g;
3685 found_col = FALSE;
3687 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3689 #if defined(DEBUG_TABLES)
3690 fprintf(stderr, "finished scanning for tables\n");
3691 #endif
3693 page_contents->glyphs.start_from_head();
3694 if (start_of_table != NULL) {
3695 if (last != NULL)
3696 while (last != page_contents->glyphs.get_data())
3697 page_contents->glyphs.move_left();
3699 insert_tab_te();
3700 start_of_table->remember_table(tbl);
3701 tbl = NULL;
3702 page_contents->insert_tag(string("*** LAST ***"));
3705 if (tbl != NULL) {
3706 delete tbl;
3707 tbl = NULL;
3710 // and reset the registers
3711 pageoffset = old_pageoffset;
3712 troff_indent = 0;
3713 temp_indent = 0;
3714 end_tempindent = 0;
3717 void html_printer::flush_page (void)
3719 supress_sub_sup = TRUE;
3720 flush_sbuf();
3721 page_contents->dump_page();
3722 lookahead_for_tables();
3723 page_contents->dump_page();
3725 flush_globs();
3726 current_paragraph->done_para();
3728 // move onto a new page
3729 delete page_contents;
3730 #if defined(DEBUG_TABLES)
3731 fprintf(stderr, "\n\n*** flushed page ***\n\n");
3733 html.simple_comment("new page called");
3734 #endif
3735 page_contents = new page;
3739 * determine_space - works out whether we need to write a space.
3740 * If last glyph is ajoining then no space emitted.
3743 void html_printer::determine_space (text_glob *g)
3745 if (current_paragraph->is_in_pre()) {
3747 * .nf has been specified
3749 while (output_hpos < g->minh) {
3750 output_hpos += space_width;
3751 current_paragraph->emit_space();
3753 } else {
3754 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
3755 current_paragraph->emit_space();
3761 * is_line_start - returns TRUE if we are at the start of a line.
3764 int html_printer::is_line_start (int nf)
3766 int line_start = FALSE;
3767 int result = TRUE;
3768 text_glob *orig = page_contents->glyphs.get_data();
3769 text_glob *g;
3771 if (! page_contents->glyphs.is_equal_to_head()) {
3772 do {
3773 page_contents->glyphs.move_left();
3774 g = page_contents->glyphs.get_data();
3775 result = g->is_a_tag();
3776 if (g->is_fi())
3777 nf = FALSE;
3778 else if (g->is_nf())
3779 nf = TRUE;
3780 line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
3781 } while ((!line_start) && (result));
3783 * now restore our previous position.
3785 while (page_contents->glyphs.get_data() != orig)
3786 page_contents->glyphs.move_right();
3788 return result;
3792 * is_font_courier - returns TRUE if the font, f, is courier.
3795 int html_printer::is_font_courier (font *f)
3797 if (f != 0) {
3798 const char *fontname = f->get_name();
3800 return( (fontname != 0) && (fontname[0] == 'C') );
3802 return FALSE;
3806 * end_font - shuts down the font corresponding to fontname.
3809 void html_printer::end_font (const char *fontname)
3811 if (strcmp(fontname, "B") == 0) {
3812 current_paragraph->done_bold();
3813 } else if (strcmp(fontname, "I") == 0) {
3814 current_paragraph->done_italic();
3815 } else if (strcmp(fontname, "BI") == 0) {
3816 current_paragraph->done_bold();
3817 current_paragraph->done_italic();
3818 } else if (strcmp(fontname, "CR") == 0) {
3819 current_paragraph->done_tt();
3820 } else if (strcmp(fontname, "CI") == 0) {
3821 current_paragraph->done_italic();
3822 current_paragraph->done_tt();
3823 } else if (strcmp(fontname, "CB") == 0) {
3824 current_paragraph->done_bold();
3825 current_paragraph->done_tt();
3826 } else if (strcmp(fontname, "CBI") == 0) {
3827 current_paragraph->done_bold();
3828 current_paragraph->done_italic();
3829 current_paragraph->done_tt();
3834 * start_font - starts the font corresponding to name.
3837 void html_printer::start_font (const char *fontname)
3839 if (strcmp(fontname, "R") == 0) {
3840 current_paragraph->done_bold();
3841 current_paragraph->done_italic();
3842 current_paragraph->done_tt();
3843 } else if (strcmp(fontname, "B") == 0) {
3844 current_paragraph->do_bold();
3845 } else if (strcmp(fontname, "I") == 0) {
3846 current_paragraph->do_italic();
3847 } else if (strcmp(fontname, "BI") == 0) {
3848 current_paragraph->do_bold();
3849 current_paragraph->do_italic();
3850 } else if (strcmp(fontname, "CR") == 0) {
3851 if ((! fill_on) && (is_courier_until_eol()) &&
3852 is_line_start(! fill_on)) {
3853 current_paragraph->do_pre();
3855 current_paragraph->do_tt();
3856 } else if (strcmp(fontname, "CI") == 0) {
3857 if ((! fill_on) && (is_courier_until_eol()) &&
3858 is_line_start(! fill_on)) {
3859 current_paragraph->do_pre();
3861 current_paragraph->do_tt();
3862 current_paragraph->do_italic();
3863 } else if (strcmp(fontname, "CB") == 0) {
3864 if ((! fill_on) && (is_courier_until_eol()) &&
3865 is_line_start(! fill_on)) {
3866 current_paragraph->do_pre();
3868 current_paragraph->do_tt();
3869 current_paragraph->do_bold();
3870 } else if (strcmp(fontname, "CBI") == 0) {
3871 if ((! fill_on) && (is_courier_until_eol()) &&
3872 is_line_start(! fill_on)) {
3873 current_paragraph->do_pre();
3875 current_paragraph->do_tt();
3876 current_paragraph->do_italic();
3877 current_paragraph->do_bold();
3882 * start_size - from is old font size, to is the new font size.
3883 * The html increase <big> and <small> decrease alters the
3884 * font size by 20%. We try and map these onto glyph sizes.
3887 void html_printer::start_size (int from, int to)
3889 if (from < to) {
3890 while (from < to) {
3891 current_paragraph->do_big();
3892 from += SIZE_INCREMENT;
3894 } else if (from > to) {
3895 while (from > to) {
3896 current_paragraph->do_small();
3897 from -= SIZE_INCREMENT;
3903 * do_font - checks to see whether we need to alter the html font.
3906 void html_printer::do_font (text_glob *g)
3909 * check if the output_style.point_size has not been set yet
3910 * this allow users to place .ps at the top of their troff files
3911 * and grohtml can then treat the .ps value as the base font size (3)
3913 if (output_style.point_size == -1) {
3914 output_style.point_size = pointsize;
3917 if (g->text_style.f != output_style.f) {
3918 if (output_style.f != 0) {
3919 end_font(output_style.f->get_name());
3921 output_style.f = g->text_style.f;
3922 if (output_style.f != 0) {
3923 start_font(output_style.f->get_name());
3926 if (output_style.point_size != g->text_style.point_size) {
3927 do_sup_or_sub(g);
3928 if ((output_style.point_size > 0) &&
3929 (g->text_style.point_size > 0)) {
3930 start_size(output_style.point_size, g->text_style.point_size);
3932 if (g->text_style.point_size > 0) {
3933 output_style.point_size = g->text_style.point_size;
3936 if (output_style.col != g->text_style.col) {
3937 current_paragraph->done_color();
3938 output_style.col = g->text_style.col;
3939 current_paragraph->do_color(&output_style.col);
3944 * start_subscript - returns TRUE if, g, looks like a subscript start.
3947 int html_printer::start_subscript (text_glob *g)
3949 int r = font::res;
3950 int height = output_style.point_size*r/72;
3952 return( (output_style.point_size != 0) &&
3953 (output_vpos < g->minv) &&
3954 (output_vpos-height > g->maxv) &&
3955 (output_style.point_size > g->text_style.point_size) );
3959 * start_superscript - returns TRUE if, g, looks like a superscript start.
3962 int html_printer::start_superscript (text_glob *g)
3964 int r = font::res;
3965 int height = output_style.point_size*r/72;
3967 return( (output_style.point_size != 0) &&
3968 (output_vpos > g->minv) &&
3969 (output_vpos-height < g->maxv) &&
3970 (output_style.point_size > g->text_style.point_size) );
3974 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
3977 int html_printer::end_subscript (text_glob *g)
3979 int r = font::res;
3980 int height = output_style.point_size*r/72;
3982 return( (output_style.point_size != 0) &&
3983 (g->minv < output_vpos) &&
3984 (output_vpos-height > g->maxv) &&
3985 (output_style.point_size < g->text_style.point_size) );
3989 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
3992 int html_printer::end_superscript (text_glob *g)
3994 int r = font::res;
3995 int height = output_style.point_size*r/72;
3997 return( (output_style.point_size != 0) &&
3998 (g->minv > output_vpos) &&
3999 (output_vpos-height < g->maxv) &&
4000 (output_style.point_size < g->text_style.point_size) );
4004 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4005 * start/end and it calls the services of html-text to issue the
4006 * appropriate tags.
4009 void html_printer::do_sup_or_sub (text_glob *g)
4011 if (! supress_sub_sup) {
4012 if (start_subscript(g)) {
4013 current_paragraph->do_sub();
4014 } else if (start_superscript(g)) {
4015 current_paragraph->do_sup();
4016 } else if (end_subscript(g)) {
4017 current_paragraph->done_sub();
4018 } else if (end_superscript(g)) {
4019 current_paragraph->done_sup();
4025 * do_end_para - writes out the html text after shutting down the
4026 * current paragraph.
4029 void html_printer::do_end_para (text_glob *g)
4031 do_font(g);
4032 current_paragraph->done_para();
4033 current_paragraph->remove_para_space();
4034 html.put_string(g->text_string+9);
4035 output_vpos = g->minv;
4036 output_hpos = g->maxh;
4037 output_vpos_max = g->maxv;
4038 supress_sub_sup = FALSE;
4042 * emit_html - write out the html text
4045 void html_printer::emit_html (text_glob *g)
4047 do_font(g);
4048 determine_space(g);
4049 current_paragraph->do_emittext(g->text_string, g->text_length);
4050 output_vpos = g->minv;
4051 output_hpos = g->maxh;
4052 output_vpos_max = g->maxv;
4053 supress_sub_sup = FALSE;
4057 * flush_sbuf - flushes the current sbuf into the list of glyphs.
4060 void html_printer::flush_sbuf()
4062 if (sbuf.length() > 0) {
4063 int r=font::res; // resolution of the device
4064 set_style(sbuf_style);
4066 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
4067 font *bold_font = make_bold(sbuf_style.f);
4068 if (bold_font != NULL)
4069 sbuf_style.f = bold_font;
4072 page_contents->add(&sbuf_style, sbuf,
4073 line_number,
4074 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4075 sbuf_vpos , sbuf_end_hpos);
4077 output_hpos = sbuf_end_hpos;
4078 output_vpos = sbuf_vpos;
4079 last_sbuf_length = 0;
4080 sbuf_prev_hpos = sbuf_end_hpos;
4081 overstrike_detected = FALSE;
4082 sbuf.clear();
4086 void html_printer::set_line_thickness(const environment *env)
4088 line_thickness = env->size;
4091 void html_printer::draw(int code, int *p, int np, const environment *env)
4093 switch (code) {
4095 case 'l':
4096 # if 0
4097 if (np == 2) {
4098 page_contents->add_line(&sbuf_style,
4099 line_number,
4100 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
4101 } else {
4102 error("2 arguments required for line");
4104 # endif
4105 break;
4106 case 't':
4108 if (np == 0) {
4109 line_thickness = -1;
4110 } else {
4111 // troff gratuitously adds an extra 0
4112 if (np != 1 && np != 2) {
4113 error("0 or 1 argument required for thickness");
4114 break;
4116 line_thickness = p[0];
4118 break;
4121 case 'P':
4122 break;
4123 case 'p':
4124 break;
4125 case 'E':
4126 break;
4127 case 'e':
4128 break;
4129 case 'C':
4130 break;
4131 case 'c':
4132 break;
4133 case 'a':
4134 break;
4135 case '~':
4136 break;
4137 case 'f':
4138 break;
4139 case 'F':
4140 // fill with color env->fill
4141 if (background != NULL)
4142 delete background;
4143 background = new color;
4144 *background = *env->fill;
4145 break;
4147 default:
4148 error("unrecognised drawing command `%1'", char(code));
4149 break;
4153 html_printer::html_printer()
4154 : html(0, MAX_LINE_LENGTH),
4155 no_of_printed_pages(0),
4156 last_sbuf_length(0),
4157 overstrike_detected(FALSE),
4158 output_hpos(-1),
4159 output_vpos(-1),
4160 output_vpos_max(-1),
4161 line_thickness(-1),
4162 inside_font_style(0),
4163 page_number(0),
4164 header_indent(-1),
4165 supress_sub_sup(TRUE),
4166 cutoff_heading(100),
4167 indent(NULL),
4168 table(NULL),
4169 end_center(0),
4170 end_tempindent(0),
4171 next_tag(INLINE),
4172 fill_on(TRUE),
4173 max_linelength(-1),
4174 linelength(0),
4175 pageoffset(0),
4176 troff_indent(0),
4177 device_indent(0),
4178 temp_indent(0),
4179 pointsize(base_point_size),
4180 line_number(0),
4181 background(default_background),
4182 seen_indent(FALSE),
4183 next_indent(0),
4184 seen_pageoffset(FALSE),
4185 next_pageoffset(0),
4186 seen_linelength(FALSE),
4187 next_linelength(0),
4188 seen_center(FALSE),
4189 next_center(0),
4190 seen_space(0),
4191 seen_break(0),
4192 current_column(0),
4193 row_space(FALSE)
4195 file_list.add_new_file(xtmpfile());
4196 html.set_file(file_list.get_file());
4197 if (font::hor != 24)
4198 fatal("horizontal resolution must be 24");
4199 if (font::vert != 40)
4200 fatal("vertical resolution must be 40");
4201 #if 0
4202 // should be sorted html..
4203 if (font::res % (font::sizescale*72) != 0)
4204 fatal("res must be a multiple of 72*sizescale");
4205 #endif
4206 int r = font::res;
4207 int point = 0;
4208 while (r % 10 == 0) {
4209 r /= 10;
4210 point++;
4212 res = r;
4213 html.set_fixed_point(point);
4214 space_char_index = font::name_to_index("space");
4215 space_width = font::hor;
4216 paper_length = font::paperlength;
4217 linelength = font::res*13/2;
4218 if (paper_length == 0)
4219 paper_length = 11*font::res;
4221 page_contents = new page();
4225 * add_to_sbuf - adds character code or name to the sbuf.
4228 void html_printer::add_to_sbuf (int idx, const string &s)
4230 if (sbuf_style.f == NULL)
4231 return;
4233 char *html_glyph = NULL;
4234 unsigned int code = sbuf_style.f->get_code(idx);
4236 if (s.empty()) {
4237 if (sbuf_style.f->contains(idx))
4238 html_glyph = (char *)sbuf_style.f->get_special_device_encoding(idx);
4239 else
4240 html_glyph = NULL;
4242 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
4243 html_glyph = to_unicode(code);
4244 } else
4245 html_glyph = get_html_translation(sbuf_style.f, s);
4247 last_sbuf_length = sbuf.length();
4248 if (html_glyph == NULL)
4249 sbuf += ((char)code);
4250 else
4251 sbuf += html_glyph;
4254 int html_printer::sbuf_continuation (int idx, const char *name,
4255 const environment *env, int w)
4258 * lets see whether the glyph is closer to the end of sbuf
4260 if ((sbuf_end_hpos == env->hpos)
4261 || ((sbuf_prev_hpos < sbuf_end_hpos)
4262 && (env->hpos < sbuf_end_hpos)
4263 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
4264 add_to_sbuf(idx, name);
4265 sbuf_prev_hpos = sbuf_end_hpos;
4266 sbuf_end_hpos += w + sbuf_kern;
4267 return TRUE;
4268 } else {
4269 if ((env->hpos >= sbuf_end_hpos) &&
4270 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
4272 * lets see whether a space is needed or not
4275 if (env->hpos-sbuf_end_hpos < space_width) {
4276 add_to_sbuf(idx, name);
4277 sbuf_prev_hpos = sbuf_end_hpos;
4278 sbuf_end_hpos = env->hpos + w;
4279 return TRUE;
4283 return FALSE ;
4287 * get_html_translation - given the position of the character and its name
4288 * return the device encoding for such character.
4291 char *get_html_translation (font *f, const string &name)
4293 int idx;
4295 if ((f == 0) || name.empty())
4296 return NULL;
4297 else {
4298 idx = f->name_to_index((char *)(name + '\0').contents());
4299 if (idx == 0) {
4300 error("character `%s' not found", (name + '\0').contents());
4301 return NULL;
4302 } else
4303 if (f->contains(idx))
4304 return (char *)f->get_special_device_encoding(idx);
4305 else
4306 return NULL;
4311 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
4312 * a previous glyph in sbuf.
4313 * If TRUE the font is changed to bold and the previous sbuf
4314 * is flushed.
4317 int html_printer::overstrike(int idx, const char *name, const environment *env, int w)
4319 if ((env->hpos < sbuf_end_hpos)
4320 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
4322 * at this point we have detected an overlap
4324 if (overstrike_detected) {
4325 /* already detected, remove previous glyph and use this glyph */
4326 sbuf.set_length(last_sbuf_length);
4327 add_to_sbuf(idx, name);
4328 sbuf_end_hpos = env->hpos + w;
4329 return TRUE;
4330 } else {
4331 /* first time we have detected an overstrike in the sbuf */
4332 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
4333 if (! is_bold(sbuf_style.f))
4334 flush_sbuf();
4335 overstrike_detected = TRUE;
4336 add_to_sbuf(idx, name);
4337 sbuf_end_hpos = env->hpos + w;
4338 return TRUE;
4341 return FALSE ;
4345 * set_char - adds a character into the sbuf if it is a continuation
4346 * with the previous word otherwise flush the current sbuf
4347 * and add character anew.
4350 void html_printer::set_char(int i, font *f, const environment *env,
4351 int w, const char *name)
4353 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
4354 if (sty.slant != 0) {
4355 if (sty.slant > 80 || sty.slant < -80) {
4356 error("silly slant `%1' degrees", sty.slant);
4357 sty.slant = 0;
4360 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
4361 && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
4362 return;
4364 flush_sbuf();
4365 if (sbuf_style.f == NULL)
4366 sbuf_style = sty;
4367 add_to_sbuf(i, name);
4368 sbuf_end_hpos = env->hpos + w;
4369 sbuf_start_hpos = env->hpos;
4370 sbuf_prev_hpos = env->hpos;
4371 sbuf_vpos = env->vpos;
4372 sbuf_style = sty;
4373 sbuf_kern = 0;
4377 * set_numbered_char - handle numbered characters.
4378 * Negative values are interpreted as unbreakable spaces;
4379 * the value (taken positive) gives the width.
4382 void html_printer::set_numbered_char(int num, const environment *env,
4383 int *widthp)
4385 int nbsp_width = 0;
4386 if (num < 0) {
4387 nbsp_width = -num;
4388 num = 160; // &nbsp;
4390 int i = font::number_to_index(num);
4391 int fn = env->fontno;
4392 if (fn < 0 || fn >= nfonts) {
4393 error("bad font position `%1'", fn);
4394 return;
4396 font *f = font_table[fn];
4397 if (f == 0) {
4398 error("no font mounted at `%1'", fn);
4399 return;
4401 if (!f->contains(i)) {
4402 error("font `%1' does not contain numbered character %2",
4403 f->get_name(),
4404 num);
4405 return;
4407 int w;
4408 if (nbsp_width)
4409 w = nbsp_width;
4410 else
4411 w = f->get_width(i, env->size);
4412 w = round_width(w);
4413 if (widthp)
4414 *widthp = w;
4415 set_char(i, f, env, w, 0);
4418 int html_printer::set_char_and_width(const char *nm, const environment *env,
4419 int *widthp, font **f)
4421 int i = font::name_to_index(nm);
4422 int fn = env->fontno;
4423 if (fn < 0 || fn >= nfonts) {
4424 error("bad font position `%1'", fn);
4425 return -1;
4427 *f = font_table[fn];
4428 if (*f == 0) {
4429 error("no font mounted at `%1'", fn);
4430 return -1;
4432 if (!(*f)->contains(i)) {
4433 if (nm[0] != '\0' && nm[1] == '\0')
4434 error("font `%1' does not contain ascii character `%2'",
4435 (*f)->get_name(),
4436 nm[0]);
4437 else
4438 error("font `%1' does not contain special character `%2'",
4439 (*f)->get_name(),
4440 nm);
4441 return -1;
4443 int w = (*f)->get_width(i, env->size);
4444 w = round_width(w);
4445 if (widthp)
4446 *widthp = w;
4447 return i;
4451 * write_title - writes the title to this document
4454 void html_printer::write_title (int in_head)
4456 if (title.has_been_found) {
4457 if (in_head) {
4458 html.put_string("<title>");
4459 html.put_string(title.text);
4460 html.put_string("</title>").nl().nl();
4461 } else {
4462 title.has_been_written = TRUE;
4463 if (title.with_h1) {
4464 html.put_string("<h1 align=center>");
4465 html.put_string(title.text);
4466 html.put_string("</h1>").nl().nl();
4469 } else if (in_head) {
4470 // place empty title tags to help conform to `tidy'
4471 html.put_string("<title></title>").nl();
4476 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
4479 static void write_rule (void)
4481 if (auto_rule)
4482 fputs("<hr>\n", stdout);
4485 void html_printer::begin_page(int n)
4487 page_number = n;
4488 #if defined(DEBUGGING)
4489 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
4490 #endif
4491 no_of_printed_pages++;
4493 output_style.f = 0;
4494 output_style.point_size= -1;
4495 output_space_code = 32;
4496 output_draw_point_size = -1;
4497 output_line_thickness = -1;
4498 output_hpos = -1;
4499 output_vpos = -1;
4500 output_vpos_max = -1;
4501 current_paragraph = new html_text(&html);
4502 do_indent(get_troff_indent(), pageoffset, linelength);
4503 current_paragraph->do_para("", FALSE);
4506 void html_printer::end_page(int)
4508 flush_sbuf();
4509 flush_page();
4512 font *html_printer::make_font(const char *nm)
4514 return html_font::load_html_font(nm);
4517 void html_printer::do_body (void)
4519 if (background == NULL)
4520 fputs("<body>\n\n", stdout);
4521 else {
4522 unsigned int r, g, b;
4523 char buf[6+1];
4525 background->get_rgb(&r, &g, &b);
4526 // we have to scale 0..0xFFFF to 0..0xFF
4527 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
4529 fputs("<body bgcolor=\"#", stdout);
4530 fputs(buf, stdout);
4531 fputs("\">\n\n", stdout);
4536 * emit_link - generates: <a href="to">name</a>
4539 void html_printer::emit_link (const string &to, const char *name)
4541 fputs("<a href=\"", stdout);
4542 fputs(to.contents(), stdout);
4543 fputs("\">", stdout);
4544 fputs(name, stdout);
4545 fputs("</a>", stdout);
4549 * write_navigation - writes out the links which navigate between
4550 * file fragments.
4553 void html_printer::write_navigation (const string &top, const string &prev,
4554 const string &next, const string &current)
4556 int need_bar = FALSE;
4558 if (multiple_files) {
4559 write_rule();
4560 fputs("[ ", stdout);
4561 if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
4562 emit_link(prev, "prev");
4563 need_bar = TRUE;
4565 if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
4566 if (need_bar)
4567 fputs(" | ", stdout);
4568 emit_link(next, "next");
4569 need_bar = TRUE;
4571 if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
4572 if (need_bar)
4573 fputs(" | ", stdout);
4574 emit_link(top, "top");
4576 fputs(" ]\n", stdout);
4577 write_rule();
4582 * do_file_components - scan the file list copying each temporary
4583 * file in turn. This is used twofold:
4585 * firstly to emit section heading links,
4586 * between file fragments if required and
4587 * secondly to generate jobname file fragments
4588 * if required.
4591 void html_printer::do_file_components (void)
4593 int fragment_no = 1;
4594 string top;
4595 string prev;
4596 string next;
4597 string current;
4599 file_list.start_of_list();
4600 top = string(job_name);
4601 top += string(".html");
4602 top += '\0';
4603 next = file_list.next_file_name();
4604 next += '\0';
4605 current = next;
4606 while (file_list.get_file() != 0) {
4607 if (fseek(file_list.get_file(), 0L, 0) < 0)
4608 fatal("fseek on temporary file failed");
4609 html.copy_file(file_list.get_file());
4610 fclose(file_list.get_file());
4612 file_list.move_next();
4613 if (file_list.is_new_output_file()) {
4614 if (fragment_no > 1)
4615 write_navigation(top, prev, next, current);
4616 prev = current;
4617 current = next;
4618 next = file_list.next_file_name();
4619 next += '\0';
4620 string split_file = file_list.file_name();
4621 split_file += '\0';
4622 fflush(stdout);
4623 freopen(split_file.contents(), "w", stdout);
4624 fragment_no++;
4625 writeHeadMetaStyle();
4626 write_navigation(top, prev, next, current);
4628 if (file_list.are_links_required())
4629 header.write_headings(stdout, TRUE);
4631 if (fragment_no > 1)
4632 write_navigation(top, prev, next, current);
4633 else
4634 write_rule();
4638 * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
4639 * related information.
4642 void html_printer::writeHeadMetaStyle (void)
4644 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
4645 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
4647 fputs("<html>\n", stdout);
4648 fputs("<head>\n", stdout);
4649 fputs("<meta name=\"generator\" "
4650 "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
4651 fputs("<meta http-equiv=\"Content-Type\" "
4652 "content=\"text/html; charset=US-ASCII\">\n", stdout);
4653 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
4655 fputs("<style type=\"text/css\">\n", stdout);
4656 fputs(" p { margin-top: 0; margin-bottom: 0; }\n", stdout);
4657 fputs(" pre { margin-top: 0; margin-bottom: 0; }\n", stdout);
4658 fputs(" table { margin-top: 0; margin-bottom: 0; }\n", stdout);
4659 fputs("</style>\n", stdout);
4662 html_printer::~html_printer()
4664 #ifdef LONG_FOR_TIME_T
4665 long t;
4666 #else
4667 time_t t;
4668 #endif
4670 current_paragraph->flush_text();
4671 html.end_line();
4672 html.set_file(stdout);
4673 html.begin_comment("Creator : ")
4674 .put_string("groff ")
4675 .put_string("version ")
4676 .put_string(Version_string)
4677 .end_comment();
4679 t = time(0);
4680 html.begin_comment("CreationDate: ")
4681 .put_string(ctime(&t), strlen(ctime(&t))-1)
4682 .end_comment();
4684 writeHeadMetaStyle();
4686 write_title(TRUE);
4687 head_info += '\0';
4688 fputs(head_info.contents(), stdout);
4689 fputs("</head>\n", stdout);
4690 do_body();
4692 write_title(FALSE);
4693 header.write_headings(stdout, FALSE);
4694 write_rule();
4695 #if defined(DEBUGGING)
4696 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
4697 #endif
4698 html.end_line();
4699 html.end_line();
4701 if (multiple_files) {
4702 fputs("</body>\n", stdout);
4703 fputs("</html>\n", stdout);
4704 do_file_components();
4705 } else {
4706 do_file_components();
4707 fputs("</body>\n", stdout);
4708 fputs("</html>\n", stdout);
4713 * get_str - returns a dupicate of string, s. The duplicate
4714 * string is terminated at the next ',' or ']'.
4717 static char *get_str (const char *s, char **n)
4719 int i=0;
4720 char *v;
4722 while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
4723 i++;
4724 if (i>0) {
4725 v = (char *)malloc(i+1);
4726 memcpy(v, s, i+1);
4727 v[i] = (char)0;
4728 if (s[i] == ',')
4729 (*n) = (char *)&s[i+1];
4730 else
4731 (*n) = (char *)&s[i];
4732 return v;
4734 if (s[i] == ',')
4735 (*n) = (char *)&s[1];
4736 else
4737 (*n) = (char *)s;
4738 return NULL;
4742 * make_val - creates a string from if s is NULL.
4745 char *make_val (char *s, int v, char *id, char *f, char *l)
4747 if (s == NULL) {
4748 char buf[30];
4750 sprintf(buf, "%d", v);
4751 return strdup(buf);
4753 else {
4755 * check that value, s, is the same as, v.
4757 char *t = s;
4759 while (*t == '=')
4760 t++;
4761 if (atoi(t) != v) {
4762 if (f == NULL)
4763 f = (char *)"stdin";
4764 if (l == NULL)
4765 l = (char *)"<none>";
4766 fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %d and was given %s\n",
4767 f, l, id, v, s);
4769 return s;
4774 * handle_assertion - handles the assertions created via .www:ASSERT
4775 * in www.tmac. See www.tmac for examples.
4776 * This method should be called as we are
4777 * parsing the ditroff input. It checks the x, y
4778 * position assertions. It does _not_ check the
4779 * troff state assertions as these are unknown at this
4780 * point.
4783 void html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
4785 char *n;
4786 char *cmd = get_str(s, &n);
4787 char *id = get_str(n, &n);
4788 char *val = get_str(n, &n);
4789 char *file= get_str(n, &n);
4790 char *line= get_str(n, &n);
4792 if (strcmp(cmd, "assertion:[x") == 0)
4793 as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
4794 else if (strcmp(cmd, "assertion:[y") == 0)
4795 as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
4796 else
4797 if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
4798 page_contents->add_tag(&sbuf_style, string(s),
4799 line_number, minv, minh, maxv, maxh);
4803 * build_state_assertion - builds the troff state assertions.
4806 void html_printer::handle_state_assertion (text_glob *g)
4808 if (g != NULL && g->is_a_tag() &&
4809 (strncmp(g->text_string, "assertion:[", 11) == 0)) {
4810 char *n = (char *)&g->text_string[11];
4811 char *cmd = get_str(n, &n);
4812 char *val = get_str(n, &n);
4813 (void)get_str(n, &n); // unused
4814 char *file= get_str(n, &n);
4815 char *line= get_str(n, &n);
4817 as.build(cmd, val, file, line);
4822 * special - handle all x X requests from troff. For post-html they
4823 * allow users to pass raw html commands, turn auto linked
4824 * headings off/on etc.
4827 void html_printer::special(char *s, const environment *env, char type)
4829 if (type != 'p')
4830 return;
4831 if (s != 0) {
4832 flush_sbuf();
4833 if (env->fontno >= 0) {
4834 style sty(get_font_from_index(env->fontno), env->size, env->height,
4835 env->slant, env->fontno, *env->col);
4836 sbuf_style = sty;
4839 if (strncmp(s, "html:", 5) == 0) {
4840 int r=font::res; /* resolution of the device */
4841 font *f=sbuf_style.f;
4843 if (f == NULL) {
4844 int found=FALSE;
4846 f = font::load_font("TR", &found);
4850 * need to pass rest of string through to html output during flush
4852 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
4853 line_number,
4854 env->vpos-env->size*r/72, env->hpos,
4855 env->vpos , env->hpos,
4856 FALSE);
4859 * assume that the html command has no width, if it does then
4860 * hopefully troff will have fudged this in a macro by
4861 * requesting that the formatting move right by the appropriate
4862 * amount.
4864 } else if (strncmp(s, "html</p>:", 9) == 0) {
4865 int r=font::res; /* resolution of the device */
4866 font *f=sbuf_style.f;
4868 if (f == NULL) {
4869 int found=FALSE;
4871 f = font::load_font("TR", &found);
4875 * need to pass all of string through to html output during flush
4877 page_contents->add_and_encode(&sbuf_style, string(s),
4878 line_number,
4879 env->vpos-env->size*r/72, env->hpos,
4880 env->vpos , env->hpos,
4881 TRUE);
4884 * assume that the html command has no width, if it does then
4885 * hopefully troff will have fudged this in a macro by
4886 * requesting that the formatting move right by the appropriate
4887 * amount.
4889 } else if (strncmp(s, "index:", 6) == 0) {
4890 cutoff_heading = atoi(&s[6]);
4891 } else if (strncmp(s, "assertion:[", 11) == 0) {
4892 int r=font::res; /* resolution of the device */
4894 handle_assertion(env->vpos-env->size*r/72, env->hpos,
4895 env->vpos, env->hpos, s);
4901 * devtag - handles device troff tags sent from the `troff'.
4902 * These include the troff state machine tags:
4903 * .br, .sp, .in, .tl, .ll etc
4905 * (see man 5 grohtml_tags).
4908 void html_printer::devtag (char *s, const environment *env, char type)
4910 if (type != 'p')
4911 return;
4913 if (s != 0) {
4914 flush_sbuf();
4915 if (env->fontno >= 0) {
4916 style sty(get_font_from_index(env->fontno), env->size, env->height,
4917 env->slant, env->fontno, *env->col);
4918 sbuf_style = sty;
4921 if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
4922 int r=font::res; /* resolution of the device */
4924 page_contents->add_tag(&sbuf_style, string(s),
4925 line_number,
4926 env->vpos-env->size*r/72, env->hpos,
4927 env->vpos , env->hpos);
4934 * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
4937 int html_printer::round_width(int x)
4939 int r = font::hor;
4940 int n;
4942 // don't depend on the rounding direction for division of negative integers
4943 if (r == 1)
4944 n = x;
4945 else
4946 n = (x < 0
4947 ? -((-x + r/2 - 1)/r)
4948 : (x + r/2 - 1)/r);
4949 return n * r;
4952 int main(int argc, char **argv)
4954 program_name = argv[0];
4955 static char stderr_buf[BUFSIZ];
4956 setbuf(stderr, stderr_buf);
4957 int c;
4958 static const struct option long_options[] = {
4959 { "help", no_argument, 0, CHAR_MAX + 1 },
4960 { "version", no_argument, 0, 'v' },
4961 { NULL, 0, 0, 0 }
4963 while ((c = getopt_long(argc, argv, "a:bdD:F:g:hi:I:j:lno:prs:S:v",
4964 long_options, NULL))
4965 != EOF)
4966 switch(c) {
4967 case 'a':
4968 /* text antialiasing bits - handled by pre-html */
4969 break;
4970 case 'b':
4971 // set background color to white
4972 default_background = new color;
4973 default_background->set_gray(color::MAX_COLOR_VAL);
4974 break;
4975 case 'd':
4976 /* handled by pre-html */
4977 break;
4978 case 'D':
4979 /* handled by pre-html */
4980 break;
4981 case 'F':
4982 font::command_line_font_dir(optarg);
4983 break;
4984 case 'g':
4985 /* graphic antialiasing bits - handled by pre-html */
4986 break;
4987 case 'h':
4988 /* do not use the Hn headings of html, but manufacture our own */
4989 manufacture_headings = TRUE;
4990 break;
4991 case 'i':
4992 /* handled by pre-html */
4993 break;
4994 case 'I':
4995 /* handled by pre-html */
4996 break;
4997 case 'j':
4998 multiple_files = TRUE;
4999 job_name = optarg;
5000 break;
5001 case 'l':
5002 auto_links = FALSE;
5003 break;
5004 case 'n':
5005 simple_anchors = TRUE;
5006 break;
5007 case 'o':
5008 /* handled by pre-html */
5009 break;
5010 case 'p':
5011 /* handled by pre-html */
5012 break;
5013 case 'r':
5014 auto_rule = FALSE;
5015 break;
5016 case 's':
5017 base_point_size = atoi(optarg);
5018 break;
5019 case 'S':
5020 split_level = atoi(optarg) + 1;
5021 break;
5022 case 'v':
5023 printf("GNU post-grohtml (groff) version %s\n", Version_string);
5024 exit(0);
5025 break;
5026 case CHAR_MAX + 1: // --help
5027 usage(stdout);
5028 exit(0);
5029 break;
5030 case '?':
5031 usage(stderr);
5032 exit(1);
5033 break;
5034 default:
5035 assert(0);
5037 if (optind >= argc) {
5038 do_file("-");
5039 } else {
5040 for (int i = optind; i < argc; i++)
5041 do_file(argv[i]);
5043 return 0;
5046 static void usage(FILE *stream)
5048 fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
5049 program_name);