Add option -G to .MPIMG to insert a gap between text and the image.
[s-roff.git] / src / devices / grohtml / post-html.cpp
blob83382f1e2f66dc5a68e233acffb935fe16daabf5
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
5 * but it owes a huge amount of ideas and raw code from
6 * James Clark (jjc@jclark.com) grops/ps.cpp.
7 */
9 /*
10 This file is part of groff.
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 for more details.
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING. If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 #include "driver.h"
27 #include "stringclass.h"
28 #include "cset.h"
29 #include "html.h"
30 #include "html-text.h"
31 #include "html-table.h"
33 #include <time.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
39 #include <stdio.h>
40 #include <fcntl.h>
42 extern "C" const char *Version_string;
44 #if !defined(TRUE)
45 # define TRUE (1==1)
46 #endif
47 #if !defined(FALSE)
48 # define FALSE (1==0)
49 #endif
51 #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
52 #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
53 #define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */
54 #define CENTER_TOLERANCE 2 /* how many pixels off center will we still */
55 #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
56 #define UNICODE_DESC_START 0x80 /* all character entities above this are */
57 /* either encoded by their glyph names or if */
58 /* there is no name then we use &#nnn; */
59 typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
60 typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
62 #undef DEBUG_TABLES
66 * prototypes
69 char *get_html_translation (font *f, const string &name);
70 int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
73 static int auto_links = TRUE; /* by default we enable automatic links at */
74 /* top of the document. */
75 static int auto_rule = TRUE; /* by default we enable an automatic rule */
76 /* at the top and bottom of the document */
77 static int simple_anchors = FALSE; /* default to anchors with heading text */
78 static int manufacture_headings = FALSE; /* default is to use the Hn html headings, */
79 /* rather than manufacture our own. */
80 static color *default_background = NULL; /* has user requested initial bg color? */
81 static string job_name; /* if set then the output is split into */
82 /* multiple files with `job_name'-%d.html */
83 static int multiple_files = FALSE; /* must we the output be divided into */
84 /* multiple html files, one for each */
85 /* heading? */
89 * start with a few favorites
92 void stop () {}
94 static int min (int a, int b)
96 if (a < b)
97 return a;
98 else
99 return b;
102 static int max (int a, int b)
104 if (a > b)
105 return a;
106 else
107 return b;
111 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
114 static int is_intersection (int a1, int a2, int b1, int b2)
116 // easier to prove NOT outside limits
117 return( ! ((a1 > b2) || (a2 < b1)) );
121 * is_digit - returns TRUE if character, ch, is a digit.
124 static int is_digit (char ch)
126 return( (ch >= '0') && (ch <= '9') );
130 * the classes and methods for maintaining a list of files.
133 struct file {
134 FILE *fp;
135 file *next;
136 int new_output_file;
137 int require_links;
138 string output_file_name;
140 file (FILE *f);
144 * file - initialize all fields to NULL
147 file::file (FILE *f)
148 : fp(f), next(0), new_output_file(FALSE),
149 require_links(FALSE), output_file_name("")
153 class files {
154 public:
155 files ();
156 FILE *get_file (void);
157 void start_of_list (void);
158 void move_next (void);
159 void add_new_file (FILE *f);
160 void set_file_name (string name);
161 void set_links_required (void);
162 int are_links_required (void);
163 int is_new_output_file (void);
164 string file_name (void);
165 string next_file_name (void);
166 private:
167 file *head;
168 file *tail;
169 file *ptr;
173 * files - create an empty list of files.
176 files::files ()
177 : head(0), tail(0), ptr(0)
182 * get_file - returns the FILE associated with ptr.
185 FILE *files::get_file (void)
187 if (ptr)
188 return ptr->fp;
189 else
190 return 0;
194 * start_of_list - reset the ptr to the start of the list.
197 void files::start_of_list (void)
199 ptr = head;
203 * move_next - moves the ptr to the next element on the list.
206 void files::move_next (void)
208 if (ptr != 0)
209 ptr = ptr->next;
213 * add_new_file - adds a new file, f, to the list.
216 void files::add_new_file (FILE *f)
218 if (head == 0) {
219 head = new file(f);
220 tail = head;
221 } else {
222 tail->next = new file(f);
223 tail = tail->next;
225 ptr = tail;
229 * set_file_name - sets the final file name to contain the html
230 * data to name.
233 void files::set_file_name (string name)
235 if (ptr != NULL) {
236 ptr->output_file_name = name;
237 ptr->new_output_file = TRUE;
242 * set_links_required - issue links when processing this component
243 * of the file.
246 void files::set_links_required (void)
248 if (ptr != NULL)
249 ptr->require_links = TRUE;
253 * are_links_required - returns TRUE if this section of the file
254 * requires that links should be issued.
257 int files::are_links_required (void)
259 if (ptr != NULL)
260 return ptr->require_links;
261 return FALSE;
265 * is_new_output_file - returns TRUE if this component of the file
266 * is the start of a new output file.
269 int files::is_new_output_file (void)
271 if (ptr != NULL)
272 return ptr->new_output_file;
273 return FALSE;
277 * file_name - returns the name of the file.
280 string files::file_name (void)
282 if (ptr != NULL)
283 return ptr->output_file_name;
284 return string("");
288 * next_file_name - returns the name of the next file.
291 string files::next_file_name (void)
293 if (ptr != NULL && ptr->next != NULL)
294 return ptr->next->output_file_name;
295 return string("");
299 * the class and methods for styles
302 struct style {
303 font *f;
304 int point_size;
305 int font_no;
306 int height;
307 int slant;
308 color col;
309 style ();
310 style (font *, int, int, int, int, color);
311 int operator == (const style &) const;
312 int operator != (const style &) const;
315 style::style()
316 : f(0)
320 style::style(font *p, int sz, int h, int sl, int no, color c)
321 : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
325 int style::operator==(const style &s) const
327 return (f == s.f && point_size == s.point_size
328 && height == s.height && slant == s.slant && col == s.col);
331 int style::operator!=(const style &s) const
333 return !(*this == s);
337 * the class and methods for retaining ascii text
340 struct char_block {
341 enum { SIZE = 256 };
342 char *buffer;
343 int used;
344 char_block *next;
346 char_block();
347 char_block(int length);
348 ~char_block();
351 char_block::char_block()
352 : buffer(NULL), used(0), next(0)
356 char_block::char_block(int length)
357 : used(0), next(0)
359 buffer = (char *)malloc(max(length, char_block::SIZE));
360 if (buffer == NULL)
361 fatal("out of memory error");
364 char_block::~char_block()
366 if (buffer != NULL)
367 free(buffer);
370 class char_buffer {
371 public:
372 char_buffer();
373 ~char_buffer();
374 char *add_string(const char *, unsigned int);
375 char *add_string(const string &);
376 private:
377 char_block *head;
378 char_block *tail;
381 char_buffer::char_buffer()
382 : head(0), tail(0)
386 char_buffer::~char_buffer()
388 while (head != 0) {
389 char_block *temp = head;
390 head = head->next;
391 delete temp;
395 char *char_buffer::add_string (const char *s, unsigned int length)
397 int i=0;
398 unsigned int old_used;
400 if (s == NULL || length == 0)
401 return NULL;
403 if (tail == 0) {
404 tail = new char_block(length+1);
405 head = tail;
406 } else {
407 if (tail->used + length+1 > char_block::SIZE) {
408 tail->next = new char_block(length+1);
409 tail = tail->next;
413 old_used = tail->used;
414 do {
415 tail->buffer[tail->used] = s[i];
416 tail->used++;
417 i++;
418 length--;
419 } while (length>0);
421 // add terminating nul character
423 tail->buffer[tail->used] = '\0';
424 tail->used++;
426 // and return start of new string
428 return( &tail->buffer[old_used] );
431 char *char_buffer::add_string (const string &s)
433 return add_string(s.contents(), s.length());
437 * the classes and methods for maintaining glyph positions.
440 class text_glob {
441 public:
442 void text_glob_html (style *s, char *str, int length,
443 int min_vertical, int min_horizontal,
444 int max_vertical, int max_horizontal);
445 void text_glob_special (style *s, char *str, int length,
446 int min_vertical, int min_horizontal,
447 int max_vertical, int max_horizontal);
448 void text_glob_line (style *s,
449 int min_vertical, int min_horizontal,
450 int max_vertical, int max_horizontal,
451 int thickness);
452 void text_glob_auto_image(style *s, char *str, int length,
453 int min_vertical, int min_horizontal,
454 int max_vertical, int max_horizontal);
455 void text_glob_tag (style *s, char *str, int length,
456 int min_vertical, int min_horizontal,
457 int max_vertical, int max_horizontal);
459 text_glob (void);
460 ~text_glob (void);
461 int is_a_line (void);
462 int is_a_tag (void);
463 int is_eol (void);
464 int is_auto_img (void);
465 int is_br (void);
466 int is_in (void);
467 int is_po (void);
468 int is_ti (void);
469 int is_ce (void);
470 int is_eol_ce (void);
471 int is_col (void);
472 int is_tab (void);
473 int is_tab0 (void);
474 int is_ta (void);
475 int is_tab_ts (void);
476 int is_tab_te (void);
477 int is_nf (void);
478 int is_fi (void);
479 int get_arg (void);
480 int get_tab_args (char *align);
482 void remember_table (html_table *t);
483 html_table *get_table (void);
485 style text_style;
486 const char *text_string;
487 unsigned int text_length;
488 int minv, minh, maxv, maxh;
489 int is_tag; // is this a .br, .sp, .tl etc
490 int is_img_auto; // image created by eqn delim
491 int is_special; // text has come via 'x X html:'
492 int is_line; // is the command a <line>?
493 int thickness; // the thickness of a line
494 html_table *tab; // table description
496 private:
497 text_glob (style *s, const char *str, int length,
498 int min_vertical , int min_horizontal,
499 int max_vertical , int max_horizontal,
500 bool is_troff_command,
501 bool is_auto_image, bool is_special_command,
502 bool is_a_line , int thickness);
505 text_glob::text_glob (style *s, const char *str, int length,
506 int min_vertical, int min_horizontal,
507 int max_vertical, int max_horizontal,
508 bool is_troff_command,
509 bool is_auto_image, bool is_special_command,
510 bool is_a_line, int line_thickness)
511 : text_style(*s), text_string(str), text_length(length),
512 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
513 is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
514 is_line(is_a_line), thickness(line_thickness), tab(NULL)
518 text_glob::text_glob ()
519 : text_string(0), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
520 is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
524 text_glob::~text_glob ()
526 if (tab != NULL)
527 delete tab;
531 * text_glob_html - used to place html text into the glob buffer.
534 void text_glob::text_glob_html (style *s, char *str, int length,
535 int min_vertical , int min_horizontal,
536 int max_vertical , int max_horizontal)
538 text_glob *g = new text_glob(s, str, length,
539 min_vertical, min_horizontal, max_vertical, max_horizontal,
540 FALSE, FALSE, FALSE, FALSE, 0);
541 *this = *g;
542 delete g;
546 * text_glob_html - used to place html specials into the glob buffer.
547 * This text is essentially html commands coming through
548 * from the macro sets, with special designated sequences of
549 * characters translated into html. See add_and_encode.
552 void text_glob::text_glob_special (style *s, char *str, int length,
553 int min_vertical , int min_horizontal,
554 int max_vertical , int max_horizontal)
556 text_glob *g = new text_glob(s, str, length,
557 min_vertical, min_horizontal, max_vertical, max_horizontal,
558 FALSE, FALSE, TRUE, FALSE, 0);
559 *this = *g;
560 delete g;
564 * text_glob_line - record horizontal draw line commands.
567 void text_glob::text_glob_line (style *s,
568 int min_vertical , int min_horizontal,
569 int max_vertical , int max_horizontal,
570 int thickness)
572 text_glob *g = new text_glob(s, "", 0,
573 min_vertical, min_horizontal, max_vertical, max_horizontal,
574 FALSE, FALSE, FALSE, TRUE, thickness);
575 *this = *g;
576 delete g;
580 * text_glob_auto_image - record the presence of a .auto-image tag command.
581 * Used to mark that an image has been created automatically
582 * by a preprocessor and (pre-grohtml/troff) combination.
583 * Under some circumstances images may not be created.
584 * (consider .EQ
585 * delim $$
586 * .EN
587 * .TS
588 * tab(!), center;
589 * l!l.
590 * $1 over x$!recripical of x
591 * .TE
593 * the first auto-image marker is created via .EQ/.EN pair
594 * and no image is created.
595 * The second auto-image marker occurs at $1 over x$
596 * Currently this image will not be created
597 * as the whole of the table is created as an image.
598 * (Once html tables are handled by grohtml this will change.
599 * Shortly this will be the case).
602 void text_glob::text_glob_auto_image(style *s, char *str, int length,
603 int min_vertical, int min_horizontal,
604 int max_vertical, int max_horizontal)
606 text_glob *g = new text_glob(s, str, length,
607 min_vertical, min_horizontal, max_vertical, max_horizontal,
608 TRUE, TRUE, FALSE, FALSE, 0);
609 *this = *g;
610 delete g;
614 * text_glob_tag - records a troff tag.
617 void text_glob::text_glob_tag (style *s, char *str, int length,
618 int min_vertical, int min_horizontal,
619 int max_vertical, int max_horizontal)
621 text_glob *g = new text_glob(s, str, length,
622 min_vertical, min_horizontal, max_vertical, max_horizontal,
623 TRUE, FALSE, FALSE, FALSE, 0);
624 *this = *g;
625 delete g;
629 * is_a_line - returns TRUE if glob should be converted into an <hr>
632 int text_glob::is_a_line (void)
634 return is_line;
638 * is_a_tag - returns TRUE if glob contains a troff directive.
641 int text_glob::is_a_tag (void)
643 return is_tag;
647 * is_eol - returns TRUE if glob contains the tag eol
650 int text_glob::is_eol (void)
652 return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) );
656 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
659 int text_glob::is_eol_ce (void)
661 return( is_tag && (strcmp(text_string, "html-tag:eol.ce") == 0) );
666 * is_nf - returns TRUE if glob contains the tag .nf
669 int text_glob::is_nf (void)
671 return( is_tag && (strcmp(text_string, "html-tag:.nf") == 0) );
675 * is_fi - returns TRUE if glob contains the tag .fi
678 int text_glob::is_fi (void)
680 return( is_tag && (strcmp(text_string, "html-tag:.fi") == 0) );
684 * is_ce - returns TRUE if glob contains the tag .ce
687 int text_glob::is_ce (void)
689 return( is_tag && (strcmp(text_string, "html-tag:.ce") == 0) );
693 * is_in - returns TRUE if glob contains the tag .in
696 int text_glob::is_in (void)
698 return( is_tag && (strncmp(text_string, "html-tag:.in ", strlen("html-tag:.in ")) == 0) );
702 * is_po - returns TRUE if glob contains the tag .po
705 int text_glob::is_po (void)
707 return( is_tag && (strncmp(text_string, "html-tag:.po ", strlen("html-tag:.po ")) == 0) );
711 * is_ti - returns TRUE if glob contains the tag .ti
714 int text_glob::is_ti (void)
716 return( is_tag && (strncmp(text_string, "html-tag:.ti ", strlen("html-tag:.ti ")) == 0) );
720 * is_col - returns TRUE if glob contains the tag .col
723 int text_glob::is_col (void)
725 return( is_tag && (strncmp(text_string, "html-tag:.col", strlen("html-tag:.col")) == 0) );
729 * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
732 int text_glob::is_tab_ts (void)
734 return( is_tag && (strcmp(text_string, "html-tag:.tab-ts") == 0) );
738 * is_tab_te - returns TRUE if glob contains the tag .tab_te
741 int text_glob::is_tab_te (void)
743 return( is_tag && (strcmp(text_string, "html-tag:.tab-te") == 0) );
747 * is_ta - returns TRUE if glob contains the tag .ta
750 int text_glob::is_ta (void)
752 return( is_tag && (strncmp(text_string, "html-tag:.ta ", strlen("html-tag:.ta ")) == 0) );
756 * is_tab - returns TRUE if glob contains the tag tab
759 int text_glob::is_tab (void)
761 return( is_tag && (strncmp(text_string, "html-tag:tab ", strlen("html-tag:tab ")) == 0) );
765 * is_tab0 - returns TRUE if glob contains the tag tab0
768 int text_glob::is_tab0 (void)
770 return( is_tag && (strncmp(text_string, "html-tag:tab0", strlen("html-tag:tab0")) == 0) );
774 * is_auto_img - returns TRUE if the glob contains an automatically
775 * generated image.
778 int text_glob::is_auto_img (void)
780 return is_img_auto;
784 * is_br - returns TRUE if the glob is a tag containing a .br
785 * or an implied .br. Note that we do not include .nf or .fi
786 * as grohtml will place a .br after these commands if they
787 * should break the line.
790 int text_glob::is_br (void)
792 return( is_a_tag() && ((strcmp ("html-tag:.br", text_string) == 0) ||
793 (strncmp("html-tag:.sp", text_string, 11) == 0) ||
794 (strcmp ("html-tag:.ce", text_string) == 0)) );
797 int text_glob::get_arg (void)
799 if (strncmp("html-tag:", text_string, strlen("html-tag:")) == 0) {
800 const char *p = text_string;
802 while ((*p != (char)0) && (!isspace(*p)))
803 p++;
804 while ((*p != (char)0) && (isspace(*p)))
805 p++;
806 if (*p == (char)0)
807 return -1;
808 return atoi(p);
810 return -1;
814 * get_tab_args - returns the tab position and alignment of the tab tag
817 int text_glob::get_tab_args (char *align)
819 if (strncmp("html-tag:", text_string, strlen("html-tag:")) == 0) {
820 const char *p = text_string;
822 // firstly the alignment C|R|L
823 while ((*p != (char)0) && (!isspace(*p)))
824 p++;
825 while ((*p != (char)0) && (isspace(*p)))
826 p++;
827 *align = *p;
828 // now the int value
829 while ((*p != (char)0) && (!isspace(*p)))
830 p++;
831 while ((*p != (char)0) && (isspace(*p)))
832 p++;
833 if (*p == (char)0)
834 return -1;
835 return atoi(p);
837 return -1;
841 * remember_table - saves table, t, in the text_glob.
844 void text_glob::remember_table (html_table *t)
846 if (tab != NULL)
847 delete tab;
848 tab = t;
852 * get_table - returns the stored table description.
855 html_table *text_glob::get_table (void)
857 return tab;
861 * the class and methods used to construct ordered double linked lists.
862 * In a previous implementation we used templates via #include "ordered-list.h",
863 * but this does assume that all C++ compilers can handle this feature. Pragmatically
864 * it is safer to assume this is not the case.
867 struct element_list {
868 element_list *right;
869 element_list *left;
870 text_glob *datum;
871 int lineno;
872 int minv, minh, maxv, maxh;
874 element_list (text_glob *d,
875 int line_number,
876 int min_vertical, int min_horizontal,
877 int max_vertical, int max_horizontal);
878 element_list ();
879 ~element_list ();
882 element_list::element_list ()
883 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
888 * element_list - create a list element assigning the datum and region parameters.
891 element_list::element_list (text_glob *in,
892 int line_number,
893 int min_vertical, int min_horizontal,
894 int max_vertical, int max_horizontal)
895 : right(0), left(0), datum(in), lineno(line_number),
896 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
900 element_list::~element_list ()
902 if (datum != NULL)
903 delete datum;
906 class list {
907 public:
908 list ();
909 ~list ();
910 int is_less (element_list *a, element_list *b);
911 void add (text_glob *in,
912 int line_number,
913 int min_vertical, int min_horizontal,
914 int max_vertical, int max_horizontal);
915 void sub_move_right (void);
916 void move_right (void);
917 void move_left (void);
918 int is_empty (void);
919 int is_equal_to_tail (void);
920 int is_equal_to_head (void);
921 void start_from_head (void);
922 void start_from_tail (void);
923 void insert (text_glob *in);
924 void move_to (text_glob *in);
925 text_glob *move_right_get_data (void);
926 text_glob *move_left_get_data (void);
927 text_glob *get_data (void);
928 private:
929 element_list *head;
930 element_list *tail;
931 element_list *ptr;
935 * list - construct an empty list.
938 list::list ()
939 : head(NULL), tail(NULL), ptr(NULL)
944 * ~list - destroy a complete list.
947 list::~list()
949 element_list *temp=head;
951 do {
952 temp = head;
953 if (temp != NULL) {
954 head = head->right;
955 delete temp;
957 } while ((head != NULL) && (head != tail));
961 * is_less - returns TRUE if a is left of b if on the same line or
962 * if a is higher up the page than b.
965 int list::is_less (element_list *a, element_list *b)
967 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
968 if (a->lineno < b->lineno) {
969 return( TRUE );
970 } else if (a->lineno > b->lineno) {
971 return( FALSE );
972 } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
973 return( a->minh < b->minh );
974 } else {
975 return( a->maxv < b->maxv );
980 * add - adds a datum to the list in the order specified by the region position.
983 void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
985 // create a new list element with datum and position fields initialized
986 element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
987 element_list *last;
989 if (head == 0) {
990 head = t;
991 tail = t;
992 ptr = t;
993 t->left = t;
994 t->right = t;
995 } else {
996 last = tail;
998 while ((last != head) && (is_less(t, last))) {
999 last = last->left;
1002 if (is_less(t, last)) {
1003 t->right = last;
1004 last->left->right = t;
1005 t->left = last->left;
1006 last->left = t;
1007 // now check for a new head
1008 if (last == head) {
1009 head = t;
1011 } else {
1012 // add t beyond last
1013 t->right = last->right;
1014 t->left = last;
1015 last->right->left = t;
1016 last->right = t;
1017 // now check for a new tail
1018 if (last == tail) {
1019 tail = t;
1026 * sub_move_right - removes the element which is currently pointed to by ptr
1027 * from the list and moves ptr to the right.
1030 void list::sub_move_right (void)
1032 element_list *t=ptr->right;
1034 if (head == tail) {
1035 head = 0;
1036 if (tail != 0) {
1037 delete tail;
1039 tail = 0;
1040 ptr = 0;
1041 } else {
1042 if (head == ptr) {
1043 head = head->right;
1045 if (tail == ptr) {
1046 tail = tail->left;
1048 ptr->left->right = ptr->right;
1049 ptr->right->left = ptr->left;
1050 ptr=t;
1055 * start_from_head - assigns ptr to the head.
1058 void list::start_from_head (void)
1060 ptr = head;
1064 * start_from_tail - assigns ptr to the tail.
1067 void list::start_from_tail (void)
1069 ptr = tail;
1073 * is_empty - returns TRUE if the list has no elements.
1076 int list::is_empty (void)
1078 return( head == 0 );
1082 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
1085 int list::is_equal_to_tail (void)
1087 return( ptr == tail );
1091 * is_equal_to_head - returns TRUE if the ptr equals the head.
1094 int list::is_equal_to_head (void)
1096 return( ptr == head );
1100 * move_left - moves the ptr left.
1103 void list::move_left (void)
1105 ptr = ptr->left;
1109 * move_right - moves the ptr right.
1112 void list::move_right (void)
1114 ptr = ptr->right;
1118 * get_datum - returns the datum referenced via ptr.
1121 text_glob* list::get_data (void)
1123 return( ptr->datum );
1127 * move_right_get_data - returns the datum referenced via ptr and moves
1128 * ptr right.
1131 text_glob* list::move_right_get_data (void)
1133 ptr = ptr->right;
1134 if (ptr == head) {
1135 return( 0 );
1136 } else {
1137 return( ptr->datum );
1142 * move_left_get_data - returns the datum referenced via ptr and moves
1143 * ptr right.
1146 text_glob* list::move_left_get_data (void)
1148 ptr = ptr->left;
1149 if (ptr == tail) {
1150 return( 0 );
1151 } else {
1152 return( ptr->datum );
1157 * insert - inserts data after the current position.
1160 void list::insert (text_glob *in)
1162 if (is_empty())
1163 fatal("list must not be empty if we are inserting data");
1164 else {
1165 if (ptr == 0)
1166 ptr = head;
1168 element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1169 if (ptr == tail)
1170 tail = t;
1171 ptr->right->left = t;
1172 t->right = ptr->right;
1173 ptr->right = t;
1174 t->left = ptr;
1179 * move_to - moves the current position to the point where data, in, exists.
1180 * This is an expensive method and should be used sparingly.
1183 void list::move_to (text_glob *in)
1185 ptr = head;
1186 while (ptr != tail && ptr->datum != in)
1187 ptr = ptr->right;
1191 * page class and methods
1194 class page {
1195 public:
1196 page (void);
1197 void add (style *s, const string &str,
1198 int line_number,
1199 int min_vertical, int min_horizontal,
1200 int max_vertical, int max_horizontal);
1201 void add_tag (style *s, const string &str,
1202 int line_number,
1203 int min_vertical, int min_horizontal,
1204 int max_vertical, int max_horizontal);
1205 void add_and_encode (style *s, const string &str,
1206 int line_number,
1207 int min_vertical, int min_horizontal,
1208 int max_vertical, int max_horizontal);
1209 void add_line (style *s,
1210 int line_number,
1211 int x1, int y1, int x2, int y2,
1212 int thickness);
1213 void insert_tag (const string &str);
1214 void dump_page (void); // debugging method
1216 // and the data
1218 list glyphs; // position of glyphs and specials on page
1219 char_buffer buffer; // all characters for this page
1222 page::page()
1227 * insert_tag - inserts a tag after the current position.
1230 void page::insert_tag (const string &str)
1232 if (str.length() > 0) {
1233 text_glob *g=new text_glob();
1234 text_glob *f=glyphs.get_data();
1235 g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1236 f->minv, f->minh, f->maxv, f->maxh);
1237 glyphs.insert(g);
1242 * add - add html text to the list of glyphs.
1245 void page::add (style *s, const string &str,
1246 int line_number,
1247 int min_vertical, int min_horizontal,
1248 int max_vertical, int max_horizontal)
1250 if (str.length() > 0) {
1251 text_glob *g=new text_glob();
1252 g->text_glob_html(s, buffer.add_string(str), str.length(),
1253 min_vertical, min_horizontal, max_vertical, max_horizontal);
1254 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1259 * add_tag - adds a troff tag, for example: .tl .sp .br
1262 void page::add_tag (style *s, const string &str,
1263 int line_number,
1264 int min_vertical, int min_horizontal,
1265 int max_vertical, int max_horizontal)
1267 if (str.length() > 0) {
1268 text_glob *g;
1270 if (strncmp((str+'\0').contents(), "html-tag:.auto-image", 20) == 0) {
1271 g = new text_glob();
1272 g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1273 min_vertical, min_horizontal, max_vertical, max_horizontal);
1274 } else {
1275 g = new text_glob();
1276 g->text_glob_tag(s, buffer.add_string(str), str.length(),
1277 min_vertical, min_horizontal, max_vertical, max_horizontal);
1279 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1284 * add_line - adds the <line> primitive providing that y1==y2
1287 void page::add_line (style *s,
1288 int line_number,
1289 int x1, int y1, int x2, int y2,
1290 int thickness)
1292 if (y1 == y2) {
1293 text_glob *g = new text_glob();
1294 g->text_glob_line(s,
1295 min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2),
1296 thickness);
1297 glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2));
1302 * to_unicode - returns a unicode translation of int, ch.
1305 static char *to_unicode (unsigned int ch)
1307 static char buf[30];
1309 sprintf(buf, "&#%u;", ch);
1310 return buf;
1314 * add_and_encode - adds a special string to the page, it translates the string
1315 * into html glyphs. The special string will have come from x X html:
1316 * and can contain troff character encodings which appear as
1317 * \(char\). A sequence of \\ represents \.
1318 * So for example we can write:
1319 * "cost = \(Po\)3.00 file = \\foo\\bar"
1320 * which is translated into:
1321 * "cost = &pound;3.00 file = \foo\bar"
1324 void page::add_and_encode (style *s, const string &str,
1325 int line_number,
1326 int min_vertical, int min_horizontal,
1327 int max_vertical, int max_horizontal)
1329 string html_string;
1330 char *html_glyph;
1331 int i=0;
1333 if (s->f == NULL)
1334 return;
1335 while (i < str.length()) {
1336 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
1337 // start of escape
1338 i += 2; // move over \(
1339 int a = i;
1340 while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
1341 i++;
1343 int n = i;
1344 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
1345 i++;
1346 else
1347 n = -1;
1348 if (n > 0) {
1349 string troff_charname = str.substring(a, n-a);
1350 html_glyph = get_html_translation(s->f, troff_charname);
1351 if (html_glyph)
1352 html_string += html_glyph;
1353 else {
1354 int index=s->f->name_to_index((troff_charname + '\0').contents());
1356 if (s->f->contains(index) && (index != 0))
1357 html_string += s->f->get_code(index);
1360 } else
1361 html_string += str[i];
1362 i++;
1364 if (html_string.length() > 0) {
1365 text_glob *g=new text_glob();
1366 g->text_glob_special(s, buffer.add_string(html_string), html_string.length(),
1367 min_vertical, min_horizontal, max_vertical, max_horizontal);
1368 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1373 * dump_page - dump the page contents for debugging purposes.
1376 void page::dump_page(void)
1378 #if defined(DEBUG_TABLES)
1379 text_glob *old_pos = glyphs.get_data();
1380 text_glob *g;
1382 printf("\n<!--\n");
1383 printf("\n\ndebugging start\n");
1384 glyphs.start_from_head();
1385 do {
1386 g = glyphs.get_data();
1387 if (g->is_tab_ts()) {
1388 printf("\n\n");
1389 if (g->get_table() != NULL)
1390 g->get_table()->dump_table();
1392 printf("%s ", g->text_string);
1393 if (g->is_tab_te())
1394 printf("\n\n");
1395 glyphs.move_right();
1396 } while (! glyphs.is_equal_to_head());
1397 glyphs.move_to(old_pos);
1398 printf("\ndebugging end\n\n");
1399 printf("\n-->\n");
1400 fflush(stdout);
1401 #endif
1405 * font classes and methods
1408 class html_font : public font {
1409 html_font(const char *);
1410 public:
1411 int encoding_index;
1412 char *encoding;
1413 char *reencoded_name;
1414 ~html_font();
1415 static html_font *load_html_font(const char *);
1418 html_font *html_font::load_html_font(const char *s)
1420 html_font *f = new html_font(s);
1421 if (!f->load()) {
1422 delete f;
1423 return 0;
1425 return f;
1428 html_font::html_font(const char *nm)
1429 : font(nm)
1433 html_font::~html_font()
1438 * a simple class to contain the header to this document
1441 class title_desc {
1442 public:
1443 title_desc ();
1444 ~title_desc ();
1446 int has_been_written;
1447 int has_been_found;
1448 int with_h1;
1449 string text;
1453 title_desc::title_desc ()
1454 : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1458 title_desc::~title_desc ()
1462 class header_desc {
1463 public:
1464 header_desc ();
1465 ~header_desc ();
1467 int no_of_level_one_headings; // how many .SH or .NH 1 have we found?
1468 int no_of_headings; // how many headings have we found?
1469 char_buffer headings; // all the headings used in the document
1470 list headers; // list of headers built from .NH and .SH
1471 list header_filename; // in which file is this header?
1472 int header_level; // current header level
1473 int written_header; // have we written the header yet?
1474 string header_buffer; // current header text
1476 void write_headings (FILE *f, int force);
1479 header_desc::header_desc ()
1480 : no_of_level_one_headings(0), no_of_headings(0),
1481 header_level(2), written_header(0)
1485 header_desc::~header_desc ()
1490 * write_headings - emits a list of links for the headings in this document
1493 void header_desc::write_headings (FILE *f, int force)
1495 text_glob *g;
1497 if (auto_links || force) {
1498 if (! headers.is_empty()) {
1499 int h=1;
1501 headers.start_from_head();
1502 header_filename.start_from_head();
1503 do {
1504 g = headers.get_data();
1505 fputs("<a href=\"", f);
1506 if (multiple_files && (! header_filename.is_empty())) {
1507 text_glob *fn = header_filename.get_data();
1508 fputs(fn->text_string, f);
1510 fputs("#", f);
1511 if (simple_anchors) {
1512 string buffer(ANCHOR_TEMPLATE);
1514 buffer += as_string(h);
1515 buffer += '\0';
1516 fprintf(f, buffer.contents());
1517 } else
1518 fputs(g->text_string, f);
1519 h++;
1520 fputs("\">", f);
1521 fputs(g->text_string, f);
1522 fputs("</a><br>\n", f);
1523 headers.move_right();
1524 if (multiple_files && (! header_filename.is_empty()))
1525 header_filename.move_right();
1526 } while (! headers.is_equal_to_head());
1527 fputs("\n", f);
1532 class html_printer : public printer {
1533 files file_list;
1534 simple_output html;
1535 int res;
1536 int space_char_index;
1537 int space_width;
1538 int no_of_printed_pages;
1539 int paper_length;
1540 string sbuf;
1541 int sbuf_start_hpos;
1542 int sbuf_vpos;
1543 int sbuf_end_hpos;
1544 int sbuf_prev_hpos;
1545 int sbuf_kern;
1546 style sbuf_style;
1547 int last_sbuf_length;
1548 int overstrike_detected;
1549 style output_style;
1550 int output_hpos;
1551 int output_vpos;
1552 int output_vpos_max;
1553 int output_draw_point_size;
1554 int line_thickness;
1555 int output_line_thickness;
1556 unsigned char output_space_code;
1557 char *inside_font_style;
1558 int page_number;
1559 title_desc title;
1560 header_desc header;
1561 int header_indent;
1562 int supress_sub_sup;
1563 int cutoff_heading;
1564 page *page_contents;
1565 html_text *current_paragraph;
1566 html_indent *indent;
1567 html_table *table;
1568 int end_center;
1569 int end_tempindent;
1570 TAG_ALIGNMENT next_tag;
1571 int fill_on;
1572 int max_linelength;
1573 int linelength;
1574 int pageoffset;
1575 int indentation;
1576 int prev_indent;
1577 int pointsize;
1578 int vertical_spacing;
1579 int line_number;
1580 color *background;
1582 void flush_sbuf ();
1583 void set_style (const style &);
1584 void set_space_code (unsigned char c);
1585 void do_exec (char *, const environment *);
1586 void do_import (char *, const environment *);
1587 void do_def (char *, const environment *);
1588 void do_mdef (char *, const environment *);
1589 void do_file (char *, const environment *);
1590 void set_line_thickness (const environment *);
1591 void terminate_current_font (void);
1592 void flush_font (void);
1593 void add_to_sbuf (int index, const string &s);
1594 void write_title (int in_head);
1595 int sbuf_continuation (int index, const char *name, const environment *env, int w);
1596 void flush_page (void);
1597 void troff_tag (text_glob *g);
1598 void flush_globs (void);
1599 void emit_line (text_glob *g);
1600 void emit_raw (text_glob *g);
1601 void emit_html (text_glob *g);
1602 void determine_space (text_glob *g);
1603 void start_font (const char *name);
1604 void end_font (const char *name);
1605 int is_font_courier (font *f);
1606 int is_courier_until_eol (void);
1607 void start_size (int from, int to);
1608 void do_font (text_glob *g);
1609 void do_center (char *arg);
1610 void do_break (void);
1611 void do_eol (void);
1612 void do_eol_ce (void);
1613 void do_title (void);
1614 void do_fill (int on);
1615 void do_heading (char *arg);
1616 void write_header (void);
1617 void determine_header_level (int level);
1618 void do_linelength (char *arg);
1619 void do_pageoffset (char *arg);
1620 void do_indentation (char *arg);
1621 void do_tempindent (char *arg);
1622 void do_indentedparagraph (void);
1623 void do_verticalspacing (char *arg);
1624 void do_pointsize (char *arg);
1625 void do_centered_image (void);
1626 void do_left_image (void);
1627 void do_right_image (void);
1628 void do_auto_image (text_glob *g, const char *filename);
1629 void do_links (void);
1630 void do_flush (void);
1631 void do_job_name (char *name);
1632 void insert_split_file (void);
1633 int is_in_middle (int left, int right);
1634 void do_sup_or_sub (text_glob *g);
1635 int start_subscript (text_glob *g);
1636 int end_subscript (text_glob *g);
1637 int start_superscript (text_glob *g);
1638 int end_superscript (text_glob *g);
1639 void outstanding_eol (int n);
1640 int is_bold (font *f);
1641 font *make_bold (font *f);
1642 int overstrike (int index, const char *name, const environment *env, int w);
1643 void do_body (void);
1644 int next_horiz_pos (text_glob *g, int nf);
1645 void lookahead_for_tables (void);
1646 void insert_tab_te (void);
1647 text_glob *insert_tab_ts (text_glob *where);
1648 void insert_tab0_foreach_tab (void);
1649 void insert_tab_0 (text_glob *where);
1650 void do_indent (int in, int pageoff, int linelen);
1651 void shutdown_table (void);
1652 void do_tab_ts (text_glob *g);
1653 void do_tab_te (void);
1654 void do_col (char *s);
1655 void do_tab (char *s);
1656 void do_tab0 (void);
1657 int calc_nf (text_glob *g, int nf);
1658 void calc_po_in (text_glob *g, int nf);
1659 void remove_tabs (void);
1660 void remove_courier_tabs (void);
1661 void update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g);
1662 void add_table_end (const char *);
1663 void do_file_components (void);
1664 void write_navigation (const string &top, const string &prev,
1665 const string &next, const string &current);
1666 void emit_link (const string &to, const char *name);
1667 // ADD HERE
1669 public:
1670 html_printer ();
1671 ~html_printer ();
1672 void set_char (int i, font *f, const environment *env, int w, const char *name);
1673 void set_numbered_char(int num, const environment *env, int *widthp);
1674 void draw (int code, int *p, int np, const environment *env);
1675 void begin_page (int);
1676 void end_page (int);
1677 void special (char *arg, const environment *env, char type);
1678 font *make_font (const char *);
1679 void end_of_line ();
1682 printer *make_printer()
1684 return new html_printer;
1687 static void usage(FILE *stream);
1689 void html_printer::set_style(const style &sty)
1691 const char *fontname = sty.f->get_name();
1692 if (fontname == NULL)
1693 fatal("no internalname specified for font");
1695 #if 0
1696 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
1697 #endif
1701 * is_bold - returns TRUE if font, f, is bold.
1704 int html_printer::is_bold (font *f)
1706 const char *fontname = f->get_name();
1707 return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
1711 * make_bold - if a bold font of, f, exists then return it.
1714 font *html_printer::make_bold (font *f)
1716 const char *fontname = f->get_name();
1718 if (strcmp(fontname, "B") == 0)
1719 return f;
1720 if (strcmp(fontname, "I") == 0)
1721 return font::load_font("BI");
1722 if (strcmp(fontname, "BI") == 0)
1723 return f;
1724 return NULL;
1727 void html_printer::end_of_line()
1729 flush_sbuf();
1730 line_number++;
1734 * emit_line - writes out a horizontal rule.
1737 void html_printer::emit_line (text_glob *)
1739 // --fixme-- needs to know the length in percentage
1740 html.put_string("<hr>");
1744 * emit_raw - writes the raw html information directly to the device.
1747 void html_printer::emit_raw (text_glob *g)
1749 do_font(g);
1750 if (next_tag == INLINE) {
1751 determine_space(g);
1752 current_paragraph->do_emittext(g->text_string, g->text_length);
1753 } else {
1754 current_paragraph->done_para();
1755 switch (next_tag) {
1757 case CENTERED:
1758 current_paragraph->do_para("align=center");
1759 break;
1760 case LEFT:
1761 current_paragraph->do_para(&html, "align=left", indentation, pageoffset, linelength);
1762 break;
1763 case RIGHT:
1764 current_paragraph->do_para(&html, "align=right", indentation, pageoffset, linelength);
1765 break;
1766 default:
1767 fatal("unknown enumeration");
1769 current_paragraph->do_emittext(g->text_string, g->text_length);
1770 current_paragraph->done_para();
1771 next_tag = INLINE;
1772 supress_sub_sup = TRUE;
1773 if (indentation > 0) {
1775 * restore indentation
1777 int newin = indentation;
1778 indentation = 0;
1779 do_indent(newin, pageoffset, linelength);
1785 * do_center - handle the .ce commands from troff.
1788 void html_printer::do_center (char *arg)
1790 int n = atoi(arg);
1791 current_paragraph->do_break();
1793 if (n > 0) {
1794 current_paragraph->done_para();
1795 supress_sub_sup = TRUE;
1796 current_paragraph->do_para("align=center");
1797 end_center += n;
1798 } else {
1799 end_center = 0;
1800 current_paragraph->remove_para_align();
1805 * do_centered_image - set a flag such that the next html-tag is
1806 * placed inside a centered paragraph.
1809 void html_printer::do_centered_image (void)
1811 next_tag = CENTERED;
1815 * do_right_image - set a flag such that the next html-tag is
1816 * placed inside a right aligned paragraph.
1819 void html_printer::do_right_image (void)
1821 next_tag = RIGHT;
1825 * do_left_image - set a flag such that the next html-tag is
1826 * placed inside a left aligned paragraph.
1829 void html_printer::do_left_image (void)
1831 next_tag = LEFT;
1835 * exists - returns TRUE if filename exists.
1838 static int exists (const char *filename)
1840 FILE *fp = fopen(filename, "r");
1842 if (fp == 0) {
1843 return( FALSE );
1844 } else {
1845 fclose(fp);
1846 return( TRUE );
1851 * generate_img_src - returns a html image tag for the filename
1852 * providing that the image exists.
1855 static string &generate_img_src (const char *filename)
1857 string *s = new string("");
1859 while (filename && (filename[0] == ' ')) {
1860 filename++;
1862 if (exists(filename))
1863 *s += string("<img src=\"") + filename + "\">";
1864 return *s;
1868 * do_auto_image - tests whether the image, indicated by filename,
1869 * is present, if so then it emits an html image tag.
1870 * An image tag may be passed through from pic, eqn
1871 * but the corresponding image might not be created.
1872 * Consider .EQ delim $$ .EN or an empty .PS .PE.
1875 void html_printer::do_auto_image (text_glob *g, const char *filename)
1877 string buffer = generate_img_src(filename);
1879 if (! buffer.empty()) {
1881 * utilize emit_raw by creating a new text_glob.
1883 text_glob h = *g;
1885 h.text_string = buffer.contents();
1886 h.text_length = buffer.length();
1887 emit_raw(&h);
1888 } else
1889 next_tag = INLINE;
1893 * outstanding_eol - call do_eol, n, times.
1896 void html_printer::outstanding_eol (int n)
1898 while (n > 0) {
1899 do_eol();
1900 n--;
1905 * do_title - handle the .tl commands from troff.
1908 void html_printer::do_title (void)
1910 text_glob *t;
1911 int removed_from_head;
1912 int eol_ce = 0;
1914 if (page_number == 1) {
1915 int found_title_start = FALSE;
1916 if (! page_contents->glyphs.is_empty()) {
1917 page_contents->glyphs.sub_move_right(); /* move onto next word */
1918 do {
1919 t = page_contents->glyphs.get_data();
1920 removed_from_head = FALSE;
1921 if (t->is_auto_img()) {
1922 string img = generate_img_src((char *)(t->text_string + 20));
1924 if (! img.empty()) {
1925 if (found_title_start)
1926 title.text += " ";
1927 found_title_start = TRUE;
1928 title.has_been_found = TRUE;
1929 title.text += img;
1931 page_contents->glyphs.sub_move_right(); /* move onto next word */
1932 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1933 (page_contents->glyphs.is_equal_to_head()));
1934 } else if (t->is_eol_ce()) {
1935 /* process the eol associated with .ce
1937 eol_ce++;
1938 page_contents->glyphs.sub_move_right(); /* move onto next word */
1939 } else if (t->is_eol()) {
1940 /* end of title found
1942 title.has_been_found = TRUE;
1943 outstanding_eol(eol_ce);
1944 return;
1945 } else if (t->is_a_tag()) {
1946 /* end of title found, but move back so that we read this tag and process it
1948 page_contents->glyphs.move_left(); /* move backwards to last word */
1949 title.has_been_found = TRUE;
1950 outstanding_eol(eol_ce);
1951 return;
1952 } else if (found_title_start) {
1953 title.text += " " + string(t->text_string, t->text_length);
1954 page_contents->glyphs.sub_move_right(); /* move onto next word */
1955 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1956 (page_contents->glyphs.is_equal_to_head()));
1957 } else {
1958 title.text += string(t->text_string, t->text_length);
1959 found_title_start = TRUE;
1960 title.has_been_found = TRUE;
1961 page_contents->glyphs.sub_move_right(); /* move onto next word */
1962 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1963 (page_contents->glyphs.is_equal_to_head()));
1965 } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
1967 outstanding_eol(eol_ce);
1971 void html_printer::write_header (void)
1973 if (! header.header_buffer.empty()) {
1974 if (header.header_level > 7) {
1975 header.header_level = 7;
1978 // firstly we must terminate any font and type faces
1979 current_paragraph->done_para();
1980 supress_sub_sup = TRUE;
1982 if (cutoff_heading+2 > header.header_level) {
1983 // now we save the header so we can issue a list of links
1984 header.no_of_headings++;
1985 style st;
1987 text_glob *h=new text_glob();
1988 h->text_glob_html(&st,
1989 header.headings.add_string(header.header_buffer),
1990 header.header_buffer.length(),
1991 header.no_of_headings, header.header_level,
1992 header.no_of_headings, header.header_level);
1994 header.headers.add(h,
1995 header.no_of_headings,
1996 header.no_of_headings, header.no_of_headings,
1997 header.no_of_headings, header.no_of_headings); // and add this header to the header list
1999 // lastly we generate a tag
2001 html.nl().put_string("<a name=\"");
2002 if (simple_anchors) {
2003 string buffer(ANCHOR_TEMPLATE);
2005 buffer += as_string(header.no_of_headings);
2006 buffer += '\0';
2007 html.put_string(buffer.contents());
2008 } else {
2009 html.put_string(header.header_buffer);
2011 html.put_string("\"></a>").nl();
2014 if (manufacture_headings) {
2015 // line break before a header
2016 if (!current_paragraph->emitted_text())
2017 current_paragraph->do_space();
2018 // user wants manufactured headings which look better than <Hn></Hn>
2019 if (header.header_level<4) {
2020 html.put_string("<b><font size=\"+1\">");
2021 html.put_string(header.header_buffer);
2022 html.put_string("</font></b>").nl();
2024 else {
2025 html.put_string("<b>");
2026 html.put_string(header.header_buffer);
2027 html.put_string("</b>").nl();
2030 else {
2031 // and now we issue the real header
2032 html.put_string("<h");
2033 html.put_number(header.header_level);
2034 html.put_string(">");
2035 html.put_string(header.header_buffer);
2036 html.put_string("</h");
2037 html.put_number(header.header_level);
2038 html.put_string(">").nl();
2041 /* and now we save the file name in which this header will occur */
2043 style st; // fake style to enable us to use the list data structure
2045 text_glob *h=new text_glob();
2046 h->text_glob_html(&st,
2047 header.headings.add_string(file_list.file_name()),
2048 file_list.file_name().length(),
2049 header.no_of_headings, header.header_level,
2050 header.no_of_headings, header.header_level);
2052 header.header_filename.add(h,
2053 header.no_of_headings,
2054 header.no_of_headings, header.no_of_headings,
2055 header.no_of_headings, header.no_of_headings);
2057 current_paragraph->do_para(&html, "", indentation, pageoffset, linelength);
2061 void html_printer::determine_header_level (int level)
2063 if (level == 0) {
2064 int i;
2066 for (i=0; ((i<header.header_buffer.length())
2067 && ((header.header_buffer[i] == '.')
2068 || is_digit(header.header_buffer[i]))) ; i++) {
2069 if (header.header_buffer[i] == '.') {
2070 level++;
2074 header.header_level = level+1;
2075 if (header.header_level == 2) {
2076 header.no_of_level_one_headings++;
2077 insert_split_file();
2082 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2085 void html_printer::do_heading (char *arg)
2087 text_glob *g;
2088 text_glob *l = 0;
2089 int level=atoi(arg);
2091 header.header_buffer.clear();
2092 page_contents->glyphs.move_right();
2093 if (! page_contents->glyphs.is_equal_to_head()) {
2094 g = page_contents->glyphs.get_data();
2095 do {
2096 if (g->is_auto_img()) {
2097 string img=generate_img_src((char *)(g->text_string + 20));
2099 if (! img.empty()) {
2100 simple_anchors = TRUE; // we cannot use full heading anchors with images
2101 if (l != 0)
2102 header.header_buffer += " ";
2104 l = g;
2105 header.header_buffer += img;
2107 } else if (! (g->is_a_line() || g->is_a_tag())) {
2109 * we ignore tag commands when constructing a heading
2111 if (l != 0)
2112 header.header_buffer += " ";
2113 l = g;
2115 header.header_buffer += string(g->text_string, g->text_length);
2117 page_contents->glyphs.move_right();
2118 g = page_contents->glyphs.get_data();
2119 } while ((! page_contents->glyphs.is_equal_to_head()) &&
2120 (! g->is_br()));
2123 determine_header_level(level);
2124 write_header();
2126 // finally set the output to neutral for after the header
2127 g = page_contents->glyphs.get_data();
2128 page_contents->glyphs.move_left(); // so that next time we use old g
2132 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
2135 int html_printer::is_courier_until_eol (void)
2137 text_glob *orig = page_contents->glyphs.get_data();
2138 int result = TRUE;
2139 text_glob *g;
2141 if (! page_contents->glyphs.is_equal_to_tail()) {
2142 page_contents->glyphs.move_right();
2143 do {
2144 g = page_contents->glyphs.get_data();
2145 if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2146 result = FALSE;
2147 page_contents->glyphs.move_right();
2148 } while (result &&
2149 (! page_contents->glyphs.is_equal_to_head()) &&
2150 (! g->is_fi()) && (! g->is_eol()));
2153 * now restore our previous position.
2155 while (page_contents->glyphs.get_data() != orig)
2156 page_contents->glyphs.move_left();
2158 return result;
2162 * do_linelength - handle the .ll command from troff.
2165 void html_printer::do_linelength (char *arg)
2167 if (max_linelength == -1)
2168 max_linelength = atoi(arg);
2170 if (fill_on)
2171 do_indent(indentation, pageoffset, atoi(arg));
2175 * do_pageoffset - handle the .po command from troff.
2178 void html_printer::do_pageoffset (char *arg)
2180 if (fill_on)
2181 do_indent(indentation, atoi(arg), linelength);
2185 * do_indentation - handle the .in command from troff.
2188 void html_printer::do_indentation (char *arg)
2190 if (fill_on)
2191 do_indent(atoi(arg), pageoffset, linelength);
2195 * do_tempindent - handle the .ti command from troff.
2198 void html_printer::do_tempindent (char *arg)
2200 if (fill_on) {
2201 end_tempindent = 1;
2202 prev_indent = indentation;
2203 do_indent(atoi(arg), pageoffset, linelength);
2208 * shutdown_table - shuts down the current table.
2211 void html_printer::shutdown_table (void)
2213 if (table != NULL) {
2214 current_paragraph->done_para();
2215 table->emit_finish_table();
2216 // dont delete this table as it will be deleted when we destroy the text_glob
2217 table = NULL;
2222 * do_indent - remember the indent parameters and if
2223 * indent is > pageoff and indent has changed
2224 * then we start a html table to implement the indentation.
2227 void html_printer::do_indent (int in, int pageoff, int linelen)
2229 if ((indentation != -1) &&
2230 (pageoffset+indentation != in+pageoff)) {
2232 current_paragraph->done_para();
2234 indentation = in;
2235 pageoffset = pageoff;
2236 if (linelen <= max_linelength)
2237 linelength = linelen;
2239 current_paragraph->do_para(&html, "", indentation, pageoffset, max_linelength);
2244 * do_verticalspacing - handle the .vs command from troff.
2247 void html_printer::do_verticalspacing (char *arg)
2249 vertical_spacing = atoi(arg);
2253 * do_pointsize - handle the .ps command from troff.
2256 void html_printer::do_pointsize (char *arg)
2258 pointsize = atoi(arg);
2262 * do_fill - records whether troff has requested that text be filled.
2265 void html_printer::do_fill (int on)
2267 current_paragraph->do_break();
2268 output_hpos = indentation+pageoffset;
2269 supress_sub_sup = TRUE;
2271 if (fill_on != on) {
2272 if (on)
2273 current_paragraph->do_para("");
2274 else
2275 current_paragraph->do_pre();
2276 fill_on = on;
2281 * do_eol - handle the end of line
2284 void html_printer::do_eol (void)
2286 if (! fill_on) {
2287 if (current_paragraph->ever_emitted_text()) {
2288 current_paragraph->do_newline();
2289 current_paragraph->do_break();
2292 output_hpos = indentation+pageoffset;
2296 * do_eol_ce - handle end of line specifically for a .ce
2299 void html_printer::do_eol_ce (void)
2301 if (end_center > 0) {
2302 if (end_center > 1)
2303 if (current_paragraph->emitted_text())
2304 current_paragraph->do_break();
2306 end_center--;
2307 if (end_center == 0) {
2308 current_paragraph->done_para();
2309 supress_sub_sup = TRUE;
2315 * do_flush - flushes all output and tags.
2318 void html_printer::do_flush (void)
2320 current_paragraph->done_para();
2324 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
2327 void html_printer::do_links (void)
2329 current_paragraph->done_para();
2330 auto_links = FALSE; /* from now on only emit under user request */
2331 file_list.add_new_file(xtmpfile());
2332 file_list.set_links_required();
2333 html.set_file(file_list.get_file());
2337 * insert_split_file -
2340 void html_printer::insert_split_file (void)
2342 if (multiple_files) {
2343 current_paragraph->done_para(); // flush paragraph
2344 html.end_line(); // flush line
2345 html.set_file(file_list.get_file()); // flush current file
2346 file_list.add_new_file(xtmpfile());
2347 string split_file = job_name;
2349 split_file += string("-");
2350 split_file += as_string(header.no_of_level_one_headings);
2351 split_file += string(".html");
2352 split_file += '\0';
2354 file_list.set_file_name(split_file);
2355 html.set_file(file_list.get_file());
2360 * do_job_name - assigns the job_name to name.
2363 void html_printer::do_job_name (char *name)
2365 if (! multiple_files) {
2366 multiple_files = TRUE;
2367 while (name != NULL && (*name != (char)0) && (*name == ' '))
2368 name++;
2369 job_name = name;
2374 * do_break - handles the ".br" request and also
2375 * undoes an outstanding ".ti" command.
2378 void html_printer::do_break (void)
2380 current_paragraph->do_break();
2381 if (end_tempindent > 0) {
2382 end_tempindent--;
2383 if (end_tempindent == 0)
2384 do_indent(prev_indent, pageoffset, linelength);
2386 output_hpos = indentation+pageoffset;
2387 supress_sub_sup = TRUE;
2391 * do_tab_ts - start a table, which will have already been defined.
2394 void html_printer::do_tab_ts (text_glob *g)
2396 html_table *t = g->get_table();
2398 if (t != NULL) {
2399 current_paragraph->done_pre();
2400 current_paragraph->done_para();
2402 html.simple_comment("TABS");
2404 t->set_linelength(max_linelength);
2405 t->add_indent(pageoffset);
2406 t->emit_table_header(FALSE);
2409 table = t;
2413 * do_tab_te - finish a table.
2416 void html_printer::do_tab_te (void)
2418 if (table) {
2419 current_paragraph->done_para();
2420 table->emit_finish_table();
2423 table = NULL;
2425 if (indentation > 0) {
2427 * restore indentation
2429 int newin = indentation;
2430 indentation = 0;
2431 do_indent(newin, pageoffset, linelength);
2436 * do_tab - handle the "html-tag:tab" tag
2439 void html_printer::do_tab (char *s)
2441 if (table) {
2442 while (isspace(*s))
2443 s++;
2444 s++;
2445 int col = table->find_column(atoi(s) + pageoffset + indentation);
2446 if (col > 0) {
2447 current_paragraph->done_para();
2448 table->emit_col(col);
2454 * do_tab0 - handle the "html-tag:tab0" tag
2457 void html_printer::do_tab0 (void)
2459 if (table) {
2460 int col = table->find_column(pageoffset+indentation);
2461 if (col > 0) {
2462 current_paragraph->done_para();
2463 table->emit_col(col);
2469 * do_col - start column, s.
2472 void html_printer::do_col (char *s)
2474 if (table) {
2475 current_paragraph->done_para();
2476 table->emit_col(atoi(s));
2481 * troff_tag - processes the troff tag and manipulates the troff state machine.
2484 void html_printer::troff_tag (text_glob *g)
2487 * firstly skip over html-tag:
2489 char *t=(char *)g->text_string+9;
2491 if (g->is_eol()) {
2492 do_eol();
2493 } else if (g->is_eol_ce()) {
2494 do_eol_ce();
2495 } else if (strncmp(t, ".sp", 3) == 0) {
2496 if (g->get_arg() > 0)
2497 current_paragraph->do_space();
2498 else
2499 current_paragraph->do_break();
2500 supress_sub_sup = TRUE;
2501 } else if (strncmp(t, ".br", 3) == 0) {
2502 do_break();
2503 } else if (strcmp(t, ".centered-image") == 0) {
2504 do_centered_image();
2505 } else if (strcmp(t, ".right-image") == 0) {
2506 do_right_image();
2507 } else if (strcmp(t, ".left-image") == 0) {
2508 do_left_image();
2509 } else if (strncmp(t, ".auto-image", 11) == 0) {
2510 char *a = (char *)t+11;
2511 do_auto_image(g, a);
2512 } else if (strncmp(t, ".ce", 3) == 0) {
2513 char *a = (char *)t+3;
2514 supress_sub_sup = TRUE;
2515 do_center(a);
2516 } else if (strncmp(t, ".tl", 3) == 0) {
2517 supress_sub_sup = TRUE;
2518 title.with_h1 = TRUE;
2519 do_title();
2520 } else if (strncmp(t, ".html-tl", 8) == 0) {
2521 supress_sub_sup = TRUE;
2522 title.with_h1 = FALSE;
2523 do_title();
2524 } else if (strncmp(t, ".fi", 3) == 0) {
2525 do_fill(TRUE);
2526 } else if (strncmp(t, ".nf", 3) == 0) {
2527 do_fill(FALSE);
2528 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
2529 char *a = (char *)t+3;
2530 do_heading(a);
2531 } else if (strncmp(t, ".ll", 3) == 0) {
2532 char *a = (char *)t+3;
2533 do_linelength(a);
2534 } else if (strncmp(t, ".po", 3) == 0) {
2535 char *a = (char *)t+3;
2536 do_pageoffset(a);
2537 } else if (strncmp(t, ".in", 3) == 0) {
2538 char *a = (char *)t+3;
2539 do_indentation(a);
2540 } else if (strncmp(t, ".ti", 3) == 0) {
2541 char *a = (char *)t+3;
2542 do_tempindent(a);
2543 } else if (strncmp(t, ".vs", 3) == 0) {
2544 char *a = (char *)t+3;
2545 do_verticalspacing(a);
2546 } else if (strncmp(t, ".ps", 3) == 0) {
2547 char *a = (char *)t+3;
2548 do_pointsize(a);
2549 } else if (strcmp(t, ".links") == 0) {
2550 do_links();
2551 } else if (strncmp(t, ".job-name", 9) == 0) {
2552 char *a = (char *)t+9;
2553 do_job_name(a);
2554 } else if (strcmp(t, ".no-auto-rule") == 0) {
2555 auto_rule = FALSE;
2556 } else if (strcmp(t, ".tab-ts") == 0) {
2557 do_tab_ts(g);
2558 } else if (strcmp(t, ".tab-te") == 0) {
2559 do_tab_te();
2560 } else if (strncmp(t, ".col ", 5) == 0) {
2561 char *a = (char *)t+4;
2562 do_col(a);
2563 } else if (strncmp(t, "tab ", 4) == 0) {
2564 char *a = (char *)t+3;
2565 do_tab(a);
2566 } else if (strncmp(t, "tab0", 4) == 0) {
2567 do_tab0();
2572 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
2575 int html_printer::is_in_middle (int left, int right)
2577 return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE );
2581 * flush_globs - runs through the text glob list and emits html.
2584 void html_printer::flush_globs (void)
2586 text_glob *g;
2588 if (! page_contents->glyphs.is_empty()) {
2589 page_contents->glyphs.start_from_head();
2590 do {
2591 g = page_contents->glyphs.get_data();
2593 if (strcmp(g->text_string, "XXXXXXX") == 0)
2594 stop();
2596 if (g->is_a_tag()) {
2597 troff_tag(g);
2598 } else if (g->is_a_line()) {
2599 emit_line(g);
2600 } else {
2601 emit_html(g);
2604 * after processing the title (and removing it) the glyph list might be empty
2606 if (! page_contents->glyphs.is_empty()) {
2607 page_contents->glyphs.move_right();
2609 } while (! page_contents->glyphs.is_equal_to_head());
2614 * calc_nf - calculates the _no_ format flag, given the
2615 * text glob, g.
2618 int html_printer::calc_nf (text_glob *g, int nf)
2620 if (g != NULL) {
2621 if (g->is_fi())
2622 return FALSE;
2623 if (g->is_nf())
2624 return TRUE;
2626 return nf;
2630 * calc_po_in - calculates the, in, po, registers
2633 void html_printer::calc_po_in (text_glob *g, int nf)
2635 if (g->is_in())
2636 indentation = g->get_arg();
2637 else if (g->is_po())
2638 pageoffset = g->get_arg();
2639 else if (g->is_ti()) {
2640 prev_indent = indentation;
2641 indentation = g->get_arg();
2642 end_tempindent = 1;
2643 } else if (g->is_br() && ((end_tempindent > 0) || (nf && g->is_eol()))) {
2644 end_tempindent = 0;
2645 indentation = prev_indent;
2650 * next_horiz_pos - returns the next horiz position.
2651 * -1 is returned if it doesn't exist.
2654 int html_printer::next_horiz_pos (text_glob *g, int nf)
2656 int next = -1;
2658 if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
2659 if (! page_contents->glyphs.is_empty()) {
2660 page_contents->glyphs.move_right_get_data();
2661 if (g == NULL)
2662 page_contents->glyphs.start_from_head();
2663 else {
2664 next = g->minh;
2665 page_contents->glyphs.move_left();
2668 return next;
2672 * insert_tab_ts - inserts a tab-ts before, where.
2675 text_glob *html_printer::insert_tab_ts (text_glob *where)
2677 text_glob *start_of_table;
2678 text_glob *old_pos = page_contents->glyphs.get_data();
2680 page_contents->glyphs.move_to(where);
2681 page_contents->glyphs.move_left();
2682 page_contents->insert_tag(string("html-tag:.tab-ts")); // tab table start
2683 page_contents->glyphs.move_right();
2684 start_of_table = page_contents->glyphs.get_data();
2685 page_contents->glyphs.move_to(old_pos);
2686 return start_of_table;
2690 * insert_tab_te - inserts a tab-te before the current position
2691 * (it skips backwards over .sp/.br)
2694 void html_printer::insert_tab_te (void)
2696 text_glob *g = page_contents->glyphs.get_data();
2697 page_contents->dump_page();
2699 while (page_contents->glyphs.get_data()->is_a_tag())
2700 page_contents->glyphs.move_left();
2702 page_contents->insert_tag(string("html-tag:.tab-te")); // tab table end
2703 while (g != page_contents->glyphs.get_data())
2704 page_contents->glyphs.move_right();
2705 page_contents->dump_page();
2709 * insert_tab_0 - inserts a tab0 before, where.
2712 void html_printer::insert_tab_0 (text_glob *where)
2714 text_glob *old_pos = page_contents->glyphs.get_data();
2716 page_contents->glyphs.move_to(where);
2717 page_contents->glyphs.move_left();
2718 page_contents->insert_tag(string("html-tag:tab0")); // tab0 start of line
2719 page_contents->glyphs.move_right();
2720 page_contents->glyphs.move_to(old_pos);
2724 * remove_tabs - removes the tabs tags on this line.
2727 void html_printer::remove_tabs (void)
2729 text_glob *orig = page_contents->glyphs.get_data();
2730 text_glob *g;
2732 if (! page_contents->glyphs.is_equal_to_tail()) {
2733 do {
2734 g = page_contents->glyphs.get_data();
2735 if (g->is_tab()) {
2736 page_contents->glyphs.sub_move_right();
2737 if (g == orig)
2738 orig = page_contents->glyphs.get_data();
2739 } else
2740 page_contents->glyphs.move_right();
2741 } while ((! page_contents->glyphs.is_equal_to_head()) &&
2742 (! g->is_eol()));
2745 * now restore our previous position.
2747 while (page_contents->glyphs.get_data() != orig)
2748 page_contents->glyphs.move_left();
2752 void html_printer::remove_courier_tabs (void)
2754 text_glob *g;
2755 int line_start = TRUE;
2756 int nf = FALSE;
2758 if (! page_contents->glyphs.is_empty()) {
2759 page_contents->glyphs.start_from_head();
2760 line_start = TRUE;
2761 do {
2762 g = page_contents->glyphs.get_data();
2764 nf = calc_nf(g, nf);
2766 if (line_start) {
2767 if (line_start && nf && is_courier_until_eol()) {
2768 remove_tabs();
2769 g = page_contents->glyphs.get_data();
2773 line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
2774 page_contents->glyphs.move_right();
2775 } while (! page_contents->glyphs.is_equal_to_head());
2779 void html_printer::insert_tab0_foreach_tab (void)
2781 text_glob *start_of_line = NULL;
2782 text_glob *g = NULL;
2783 int seen_tab = FALSE;
2784 int seen_col = FALSE;
2785 int nf = FALSE;
2787 if (! page_contents->glyphs.is_empty()) {
2788 page_contents->glyphs.start_from_head();
2789 start_of_line = page_contents->glyphs.get_data();
2790 do {
2791 g = page_contents->glyphs.get_data();
2793 nf = calc_nf(g, nf);
2795 if (g->is_tab())
2796 seen_tab = TRUE;
2798 if (g->is_col())
2799 seen_col = TRUE;
2801 if (g->is_br() || (nf && g->is_eol())) {
2802 do {
2803 page_contents->glyphs.move_right();
2804 g = page_contents->glyphs.get_data();
2805 nf = calc_nf(g, nf);
2806 if (page_contents->glyphs.is_equal_to_head()) {
2807 if (seen_tab && !seen_col)
2808 insert_tab_0(start_of_line);
2809 return;
2811 } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
2812 // printf("\nstart_of_line is: %s\n", g->text_string);
2813 if (seen_tab && !seen_col) {
2814 insert_tab_0(start_of_line);
2815 page_contents->glyphs.move_to(g);
2818 seen_tab = FALSE;
2819 seen_col = FALSE;
2820 start_of_line = g;
2822 page_contents->glyphs.move_right();
2823 } while (! page_contents->glyphs.is_equal_to_head());
2824 if (seen_tab && !seen_col)
2825 insert_tab_0(start_of_line);
2831 * update_min_max - updates the extent of a column, given the left and right
2832 * extents of a glyph, g.
2835 void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
2837 switch (type_of_col) {
2839 case tab_tag:
2840 break;
2841 case tab0_tag:
2842 *minimum = g->minh;
2843 break;
2844 case col_tag:
2845 *minimum = g->minh;
2846 *maximum = g->maxh;
2847 break;
2848 default:
2849 break;
2854 * add_table_end - moves left one glyph, adds a table end tag and adds a
2855 * debugging string.
2858 void html_printer::add_table_end (const char *
2859 #if defined(DEBUG_TABLES)
2860 debug_string
2861 #endif
2864 page_contents->glyphs.move_left();
2865 insert_tab_te();
2866 #if defined(DEBUG_TABLES)
2867 page_contents->insert_tag(string(debug_string));
2868 #endif
2872 * lookahead_for_tables - checks for .col tags and inserts table start/end tags
2875 void html_printer::lookahead_for_tables (void)
2877 text_glob *g;
2878 text_glob *start_of_line = NULL;
2879 text_glob *start_of_table = NULL;
2880 text_glob *last = NULL;
2881 colType type_of_col = none;
2882 int left = 0;
2883 int found_col = FALSE;
2884 int seen_text = FALSE;
2885 int ncol = 0;
2886 int colmin;
2887 int colmax;
2888 html_table *table = new html_table(&html, -1);
2889 const char *tab_defs = NULL;
2890 char align = 'L';
2891 int nf = FALSE;
2892 int old_pageoffset = pageoffset;
2894 remove_courier_tabs();
2895 page_contents->dump_page();
2896 insert_tab0_foreach_tab();
2897 page_contents->dump_page();
2898 if (! page_contents->glyphs.is_empty()) {
2899 page_contents->glyphs.start_from_head();
2900 g = page_contents->glyphs.get_data();
2901 do {
2902 #if defined(DEBUG_TABLES)
2903 fprintf(stderr, " [") ;
2904 fprintf(stderr, g->text_string) ;
2905 fprintf(stderr, "] ") ;
2906 fflush(stderr);
2907 if (strcmp(g->text_string, "XXXXXXX") == 0)
2908 stop();
2909 #endif
2911 nf = calc_nf(g, nf);
2912 calc_po_in(g, nf);
2913 if (g->is_col()) {
2914 if (type_of_col == tab_tag && start_of_table != NULL) {
2915 page_contents->glyphs.move_left();
2916 insert_tab_te();
2917 start_of_table->remember_table(table);
2918 table = new html_table(&html, -1);
2919 page_contents->insert_tag(string("*** TAB -> COL ***"));
2920 if (tab_defs != NULL)
2921 table->tab_stops->init(tab_defs);
2922 start_of_table = NULL;
2923 last = NULL;
2925 type_of_col = col_tag;
2926 found_col = TRUE;
2927 ncol = g->get_arg();
2928 align = 'L';
2929 colmin = 0;
2930 colmax = 0;
2931 } else if (g->is_tab()) {
2932 type_of_col = tab_tag;
2933 colmin = g->get_tab_args(&align);
2934 align = 'L'; // for now as 'C' and 'R' are broken
2935 ncol = table->find_tab_column(colmin);
2936 colmin += pageoffset + indentation;
2937 colmax = table->get_tab_pos(ncol+1);
2938 if (colmax > 0)
2939 colmax += pageoffset + indentation;
2940 } else if (g->is_tab0()) {
2941 if (type_of_col == col_tag && start_of_table != NULL) {
2942 page_contents->glyphs.move_left();
2943 insert_tab_te();
2944 start_of_table->remember_table(table);
2945 table = new html_table(&html, -1);
2946 page_contents->insert_tag(string("*** COL -> TAB ***"));
2947 start_of_table = NULL;
2948 last = NULL;
2950 if (tab_defs != NULL)
2951 table->tab_stops->init(tab_defs);
2953 type_of_col = tab0_tag;
2954 ncol = 1;
2955 colmin = 0;
2956 colmax = table->get_tab_pos(2) + pageoffset + indentation;
2957 } else if (! g->is_a_tag())
2958 update_min_max(type_of_col, &colmin, &colmax, g);
2960 if ((! g->is_a_tag()) || g->is_tab())
2961 seen_text = TRUE;
2963 if ((g->is_col() || g->is_tab() || g->is_tab0())
2964 && (start_of_line != NULL) && (start_of_table == NULL)) {
2965 start_of_table = insert_tab_ts(start_of_line);
2966 start_of_line = NULL;
2967 seen_text = FALSE;
2968 } else if (g->is_ce() && (start_of_table != NULL)) {
2969 add_table_end("*** CE ***");
2970 start_of_table->remember_table(table);
2971 start_of_table = NULL;
2972 last = NULL;
2973 } else if (g->is_ta()) {
2974 tab_defs = g->text_string;
2975 if (!table->tab_stops->compatible(tab_defs)) {
2976 if (start_of_table != NULL) {
2977 add_table_end("*** TABS ***");
2978 start_of_table->remember_table(table);
2979 table = new html_table(&html, -1);
2980 start_of_table = NULL;
2981 type_of_col = none;
2982 last = NULL;
2984 table->tab_stops->init(tab_defs);
2988 if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
2989 // we are in a table and have a glyph
2990 if ((ncol == 0) || (! table->add_column(ncol, colmin, colmax, align))) {
2991 if (ncol == 0)
2992 add_table_end("*** NCOL == 0 ***");
2993 else
2994 add_table_end("*** CROSSED COLS ***");
2996 start_of_table->remember_table(table);
2997 table = new html_table(&html, -1);
2998 start_of_table = NULL;
2999 type_of_col = none;
3000 last = NULL;
3005 * move onto next glob, check whether we are starting a new line
3007 g = page_contents->glyphs.move_right_get_data();
3009 if (g == NULL) {
3010 if (found_col) {
3011 page_contents->glyphs.start_from_head();
3012 last = g;
3013 found_col = FALSE;
3015 } else if (g->is_br() || (nf && g->is_eol())) {
3016 do {
3017 g = page_contents->glyphs.move_right_get_data();
3018 nf = calc_nf(g, nf);
3019 } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
3020 start_of_line = g;
3021 seen_text = FALSE;
3022 ncol = 0;
3023 left = next_horiz_pos(g, nf);
3024 if (found_col)
3025 last = g;
3026 found_col = FALSE;
3028 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3030 #if defined(DEBUG_TABLES)
3031 fprintf(stderr, "finished scanning for tables\n");
3032 #endif
3034 page_contents->glyphs.start_from_head();
3035 if (start_of_table != NULL) {
3036 if (last != NULL)
3037 while (last != page_contents->glyphs.get_data())
3038 page_contents->glyphs.move_left();
3040 insert_tab_te();
3041 start_of_table->remember_table(table);
3042 table = NULL;
3043 page_contents->insert_tag(string("*** LAST ***"));
3046 if (table != NULL)
3047 delete table;
3049 // and reset the registers
3050 pageoffset = old_pageoffset;
3051 indentation = 0;
3052 prev_indent = 0;
3053 end_tempindent = 0;
3056 void html_printer::flush_page (void)
3058 supress_sub_sup = TRUE;
3059 flush_sbuf();
3060 page_contents->dump_page();
3061 lookahead_for_tables();
3062 page_contents->dump_page();
3064 flush_globs();
3065 current_paragraph->done_para();
3067 // move onto a new page
3068 delete page_contents;
3069 #if defined(DEBUG_TABLES)
3070 fprintf(stderr, "\n\n*** flushed page ***\n\n");
3072 html.simple_comment("new page called");
3073 #endif
3074 page_contents = new page;
3078 * determine_space - works out whether we need to write a space.
3079 * If last glyph is ajoining then no space emitted.
3082 void html_printer::determine_space (text_glob *g)
3084 if (current_paragraph->is_in_pre()) {
3086 * .nf has been specified
3088 while (output_hpos < g->minh) {
3089 output_hpos += space_width;
3090 current_paragraph->emit_space();
3092 } else {
3093 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
3094 current_paragraph->emit_space();
3100 * is_font_courier - returns TRUE if the font, f, is courier.
3103 int html_printer::is_font_courier (font *f)
3105 if (f != 0) {
3106 const char *fontname = f->get_name();
3108 return( (fontname != 0) && (fontname[0] == 'C') );
3110 return( FALSE );
3114 * end_font - shuts down the font corresponding to fontname.
3117 void html_printer::end_font (const char *fontname)
3119 if (strcmp(fontname, "B") == 0) {
3120 current_paragraph->done_bold();
3121 } else if (strcmp(fontname, "I") == 0) {
3122 current_paragraph->done_italic();
3123 } else if (strcmp(fontname, "BI") == 0) {
3124 current_paragraph->done_bold();
3125 current_paragraph->done_italic();
3126 } else if (strcmp(fontname, "CR") == 0) {
3127 current_paragraph->done_tt();
3128 } else if (strcmp(fontname, "CI") == 0) {
3129 current_paragraph->done_italic();
3130 current_paragraph->done_tt();
3131 } else if (strcmp(fontname, "CB") == 0) {
3132 current_paragraph->done_bold();
3133 current_paragraph->done_tt();
3134 } else if (strcmp(fontname, "CBI") == 0) {
3135 current_paragraph->done_bold();
3136 current_paragraph->done_italic();
3137 current_paragraph->done_tt();
3142 * start_font - starts the font corresponding to name.
3145 void html_printer::start_font (const char *fontname)
3147 if (strcmp(fontname, "R") == 0) {
3148 current_paragraph->done_bold();
3149 current_paragraph->done_italic();
3150 current_paragraph->done_tt();
3151 } else if (strcmp(fontname, "B") == 0) {
3152 current_paragraph->do_bold();
3153 } else if (strcmp(fontname, "I") == 0) {
3154 current_paragraph->do_italic();
3155 } else if (strcmp(fontname, "BI") == 0) {
3156 current_paragraph->do_bold();
3157 current_paragraph->do_italic();
3158 } else if (strcmp(fontname, "CR") == 0) {
3159 if ((! fill_on) && (is_courier_until_eol())) {
3160 current_paragraph->do_pre();
3162 current_paragraph->do_tt();
3163 } else if (strcmp(fontname, "CI") == 0) {
3164 if ((! fill_on) && (is_courier_until_eol())) {
3165 current_paragraph->do_pre();
3167 current_paragraph->do_tt();
3168 current_paragraph->do_italic();
3169 } else if (strcmp(fontname, "CB") == 0) {
3170 if ((! fill_on) && (is_courier_until_eol())) {
3171 current_paragraph->do_pre();
3173 current_paragraph->do_tt();
3174 current_paragraph->do_bold();
3175 } else if (strcmp(fontname, "CBI") == 0) {
3176 if ((! fill_on) && (is_courier_until_eol())) {
3177 current_paragraph->do_pre();
3179 current_paragraph->do_tt();
3180 current_paragraph->do_italic();
3181 current_paragraph->do_bold();
3186 * start_size - from is old font size, to is the new font size.
3187 * The html increase <big> and <small> decrease alters the
3188 * font size by 20%. We try and map these onto glyph sizes.
3191 void html_printer::start_size (int from, int to)
3193 if (from < to) {
3194 while (from < to) {
3195 current_paragraph->do_big();
3196 from += SIZE_INCREMENT;
3198 } else if (from > to) {
3199 while (from > to) {
3200 current_paragraph->do_small();
3201 from -= SIZE_INCREMENT;
3207 * do_font - checks to see whether we need to alter the html font.
3210 void html_printer::do_font (text_glob *g)
3213 * check if the output_style.point_size has not been set yet
3214 * this allow users to place .ps at the top of their troff files
3215 * and grohtml can then treat the .ps value as the base font size (3)
3217 if (output_style.point_size == -1) {
3218 output_style.point_size = pointsize;
3221 if (g->text_style.f != output_style.f) {
3222 if (output_style.f != 0) {
3223 end_font(output_style.f->get_name());
3225 output_style.f = g->text_style.f;
3226 if (output_style.f != 0) {
3227 start_font(output_style.f->get_name());
3230 if (output_style.point_size != g->text_style.point_size) {
3231 do_sup_or_sub(g);
3232 if ((output_style.point_size > 0) &&
3233 (g->text_style.point_size > 0)) {
3234 start_size(output_style.point_size, g->text_style.point_size);
3236 if (g->text_style.point_size > 0) {
3237 output_style.point_size = g->text_style.point_size;
3240 if (output_style.col != g->text_style.col) {
3241 current_paragraph->done_color();
3242 output_style.col = g->text_style.col;
3243 current_paragraph->do_color(&output_style.col);
3248 * start_subscript - returns TRUE if, g, looks like a subscript start.
3251 int html_printer::start_subscript (text_glob *g)
3253 int r = font::res;
3254 int height = output_style.point_size*r/72;
3256 return( (output_style.point_size != 0) &&
3257 (output_vpos < g->minv) &&
3258 (output_vpos-height > g->maxv) &&
3259 (output_style.point_size > g->text_style.point_size) );
3263 * start_superscript - returns TRUE if, g, looks like a superscript start.
3266 int html_printer::start_superscript (text_glob *g)
3268 int r = font::res;
3269 int height = output_style.point_size*r/72;
3271 return( (output_style.point_size != 0) &&
3272 (output_vpos > g->minv) &&
3273 (output_vpos-height < g->maxv) &&
3274 (output_style.point_size > g->text_style.point_size) );
3278 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
3281 int html_printer::end_subscript (text_glob *g)
3283 int r = font::res;
3284 int height = output_style.point_size*r/72;
3286 return( (output_style.point_size != 0) &&
3287 (g->minv < output_vpos) &&
3288 (output_vpos-height > g->maxv) &&
3289 (output_style.point_size < g->text_style.point_size) );
3293 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
3296 int html_printer::end_superscript (text_glob *g)
3298 int r = font::res;
3299 int height = output_style.point_size*r/72;
3301 return( (output_style.point_size != 0) &&
3302 (g->minv > output_vpos) &&
3303 (output_vpos-height < g->maxv) &&
3304 (output_style.point_size < g->text_style.point_size) );
3308 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
3309 * start/end and it calls the services of html-text to issue the
3310 * appropriate tags.
3313 void html_printer::do_sup_or_sub (text_glob *g)
3315 if (! supress_sub_sup) {
3316 if (start_subscript(g)) {
3317 current_paragraph->do_sub();
3318 } else if (start_superscript(g)) {
3319 current_paragraph->do_sup();
3320 } else if (end_subscript(g)) {
3321 current_paragraph->done_sub();
3322 } else if (end_superscript(g)) {
3323 current_paragraph->done_sup();
3329 * emit_html - write out the html text
3332 void html_printer::emit_html (text_glob *g)
3334 do_font(g);
3335 determine_space(g);
3336 current_paragraph->do_emittext(g->text_string, g->text_length);
3337 output_vpos = g->minv;
3338 output_hpos = g->maxh;
3339 output_vpos_max = g->maxv;
3340 supress_sub_sup = FALSE;
3344 * flush_sbuf - flushes the current sbuf into the list of glyphs.
3347 void html_printer::flush_sbuf()
3349 if (sbuf.length() > 0) {
3350 int r=font::res; // resolution of the device
3351 set_style(sbuf_style);
3352 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
3353 font *bold_font = make_bold(sbuf_style.f);
3354 if (bold_font != NULL)
3355 sbuf_style.f = bold_font;
3358 page_contents->add(&sbuf_style, sbuf,
3359 line_number,
3360 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
3361 sbuf_vpos , sbuf_end_hpos);
3363 output_hpos = sbuf_end_hpos;
3364 output_vpos = sbuf_vpos;
3365 last_sbuf_length = 0;
3366 sbuf_prev_hpos = sbuf_end_hpos;
3367 overstrike_detected = FALSE;
3368 sbuf.clear();
3372 void html_printer::set_line_thickness(const environment *env)
3374 line_thickness = env->size;
3377 void html_printer::draw(int code, int *p, int np, const environment *env)
3379 switch (code) {
3381 case 'l':
3382 # if 0
3383 if (np == 2) {
3384 page_contents->add_line(&sbuf_style,
3385 line_number,
3386 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
3387 } else {
3388 error("2 arguments required for line");
3390 # endif
3391 break;
3392 case 't':
3394 if (np == 0) {
3395 line_thickness = -1;
3396 } else {
3397 // troff gratuitously adds an extra 0
3398 if (np != 1 && np != 2) {
3399 error("0 or 1 argument required for thickness");
3400 break;
3402 line_thickness = p[0];
3404 break;
3407 case 'P':
3408 break;
3409 case 'p':
3410 break;
3411 case 'E':
3412 break;
3413 case 'e':
3414 break;
3415 case 'C':
3416 break;
3417 case 'c':
3418 break;
3419 case 'a':
3420 break;
3421 case '~':
3422 break;
3423 case 'f':
3424 break;
3425 case 'F':
3426 // fill with color env->fill
3427 if (background != NULL)
3428 delete background;
3429 background = new color;
3430 *background = *env->fill;
3431 break;
3433 default:
3434 error("unrecognised drawing command `%1'", char(code));
3435 break;
3439 html_printer::html_printer()
3440 : html(0, MAX_LINE_LENGTH),
3441 no_of_printed_pages(0),
3442 last_sbuf_length(0),
3443 overstrike_detected(FALSE),
3444 output_hpos(-1),
3445 output_vpos(-1),
3446 output_vpos_max(-1),
3447 line_thickness(-1),
3448 inside_font_style(0),
3449 page_number(0),
3450 header_indent(-1),
3451 supress_sub_sup(TRUE),
3452 cutoff_heading(100),
3453 indent(NULL),
3454 table(NULL),
3455 end_center(0),
3456 end_tempindent(0),
3457 next_tag(INLINE),
3458 fill_on(TRUE),
3459 max_linelength(-1),
3460 linelength(0),
3461 pageoffset(0),
3462 indentation(0),
3463 prev_indent(0),
3464 pointsize(0),
3465 line_number(0),
3466 background(default_background)
3468 file_list.add_new_file(xtmpfile());
3469 html.set_file(file_list.get_file());
3470 if (font::hor != 24)
3471 fatal("horizontal resolution must be 24");
3472 if (font::vert != 40)
3473 fatal("vertical resolution must be 40");
3474 #if 0
3475 // should be sorted html..
3476 if (font::res % (font::sizescale*72) != 0)
3477 fatal("res must be a multiple of 72*sizescale");
3478 #endif
3479 int r = font::res;
3480 int point = 0;
3481 while (r % 10 == 0) {
3482 r /= 10;
3483 point++;
3485 res = r;
3486 html.set_fixed_point(point);
3487 space_char_index = font::name_to_index("space");
3488 space_width = font::hor;
3489 paper_length = font::paperlength;
3490 linelength = font::res*13/2;
3491 if (paper_length == 0)
3492 paper_length = 11*font::res;
3494 page_contents = new page();
3498 * add_to_sbuf - adds character code or name to the sbuf.
3501 void html_printer::add_to_sbuf (int index, const string &s)
3503 if (sbuf_style.f == NULL)
3504 return;
3506 char *html_glyph = NULL;
3507 unsigned int code = sbuf_style.f->get_code(index);
3509 if (s.empty()) {
3510 if (sbuf_style.f->contains(index))
3511 html_glyph = (char *)sbuf_style.f->get_special_device_encoding(index);
3512 else
3513 html_glyph = NULL;
3515 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
3516 html_glyph = to_unicode(code);
3517 } else
3518 html_glyph = get_html_translation(sbuf_style.f, s);
3520 last_sbuf_length = sbuf.length();
3521 if (html_glyph == NULL)
3522 sbuf += ((char)code);
3523 else
3524 sbuf += html_glyph;
3527 int html_printer::sbuf_continuation (int index, const char *name,
3528 const environment *env, int w)
3531 * lets see whether the glyph is closer to the end of sbuf
3533 if ((sbuf_end_hpos == env->hpos)
3534 || ((sbuf_prev_hpos < sbuf_end_hpos)
3535 && (env->hpos < sbuf_end_hpos)
3536 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
3537 add_to_sbuf(index, name);
3538 sbuf_prev_hpos = sbuf_end_hpos;
3539 sbuf_end_hpos += w + sbuf_kern;
3540 return TRUE;
3541 } else {
3542 if ((env->hpos >= sbuf_end_hpos) &&
3543 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
3545 * lets see whether a space is needed or not
3548 if (env->hpos-sbuf_end_hpos < space_width) {
3549 add_to_sbuf(index, name);
3550 sbuf_prev_hpos = sbuf_end_hpos;
3551 sbuf_end_hpos = env->hpos + w;
3552 return TRUE;
3556 return FALSE ;
3560 * get_html_translation - given the position of the character and its name
3561 * return the device encoding for such character.
3564 char *get_html_translation (font *f, const string &name)
3566 int index;
3568 if ((f == 0) || name.empty())
3569 return NULL;
3570 else {
3571 index = f->name_to_index((char *)(name + '\0').contents());
3572 if (index == 0) {
3573 error("character `%s' not found", (name + '\0').contents());
3574 return NULL;
3575 } else
3576 if (f->contains(index))
3577 return (char *)f->get_special_device_encoding(index);
3578 else
3579 return NULL;
3584 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
3585 * a previous glyph in sbuf.
3586 * If TRUE the font is changed to bold and the previous sbuf
3587 * is flushed.
3590 int html_printer::overstrike(int index, const char *name, const environment *env, int w)
3592 if ((env->hpos < sbuf_end_hpos)
3593 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
3595 * at this point we have detected an overlap
3597 if (overstrike_detected) {
3598 /* already detected, remove previous glyph and use this glyph */
3599 sbuf.set_length(last_sbuf_length);
3600 add_to_sbuf(index, name);
3601 sbuf_end_hpos = env->hpos + w;
3602 return TRUE;
3603 } else {
3604 /* first time we have detected an overstrike in the sbuf */
3605 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
3606 if (! is_bold(sbuf_style.f))
3607 flush_sbuf();
3608 overstrike_detected = TRUE;
3609 add_to_sbuf(index, name);
3610 sbuf_end_hpos = env->hpos + w;
3611 return TRUE;
3614 return FALSE ;
3618 * set_char - adds a character into the sbuf if it is a continuation with the previous
3619 * word otherwise flush the current sbuf and add character anew.
3622 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
3624 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
3625 if (sty.slant != 0) {
3626 if (sty.slant > 80 || sty.slant < -80) {
3627 error("silly slant `%1' degrees", sty.slant);
3628 sty.slant = 0;
3631 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
3632 && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
3633 return;
3635 flush_sbuf();
3636 add_to_sbuf(i, name);
3637 sbuf_end_hpos = env->hpos + w;
3638 sbuf_start_hpos = env->hpos;
3639 sbuf_prev_hpos = env->hpos;
3640 sbuf_vpos = env->vpos;
3641 sbuf_style = sty;
3642 sbuf_kern = 0;
3646 * set_numbered_char - handle numbered characters.
3647 * Negative values are interpreted as unbreakable spaces;
3648 * the value (taken positive) gives the width.
3651 void html_printer::set_numbered_char(int num, const environment *env,
3652 int *widthp)
3654 int nbsp_width = 0;
3655 if (num < 0) {
3656 nbsp_width = -num;
3657 num = 160; // &nbsp;
3659 int i = font::number_to_index(num);
3660 int fn = env->fontno;
3661 if (fn < 0 || fn >= nfonts) {
3662 error("bad font position `%1'", fn);
3663 return;
3665 font *f = font_table[fn];
3666 if (f == 0) {
3667 error("no font mounted at `%1'", fn);
3668 return;
3670 if (!f->contains(i)) {
3671 error("font `%1' does not contain numbered character %2",
3672 f->get_name(),
3673 num);
3674 return;
3676 int w;
3677 if (nbsp_width)
3678 w = nbsp_width;
3679 else
3680 w = f->get_width(i, env->size);
3681 if (widthp)
3682 *widthp = w;
3683 set_char(i, f, env, w, 0);
3687 * write_title - writes the title to this document
3690 void html_printer::write_title (int in_head)
3692 if (title.has_been_found) {
3693 if (in_head) {
3694 html.put_string("<title>");
3695 html.put_string(title.text);
3696 html.put_string("</title>").nl().nl();
3697 } else {
3698 title.has_been_written = TRUE;
3699 if (title.with_h1) {
3700 html.put_string("<h1 align=center>");
3701 html.put_string(title.text);
3702 html.put_string("</h1>").nl().nl();
3705 } else if (in_head) {
3706 // place empty title tags to help conform to `tidy'
3707 html.put_string("<title></title>").nl();
3712 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
3715 static void write_rule (void)
3717 if (auto_rule)
3718 fputs("<hr>\n", stdout);
3721 void html_printer::begin_page(int n)
3723 page_number = n;
3724 #if defined(DEBUGGING)
3725 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
3726 #endif
3727 no_of_printed_pages++;
3729 output_style.f = 0;
3730 output_style.point_size= -1;
3731 output_space_code = 32;
3732 output_draw_point_size = -1;
3733 output_line_thickness = -1;
3734 output_hpos = -1;
3735 output_vpos = -1;
3736 output_vpos_max = -1;
3737 current_paragraph = new html_text(&html);
3738 do_indent(indentation, pageoffset, linelength);
3739 current_paragraph->do_para("");
3742 void html_printer::end_page(int)
3744 flush_sbuf();
3745 flush_page();
3748 font *html_printer::make_font(const char *nm)
3750 return html_font::load_html_font(nm);
3753 void html_printer::do_body (void)
3755 if (background == NULL)
3756 fputs("<body>\n\n", stdout);
3757 else {
3758 unsigned int r, g, b;
3759 char buf[6+1];
3761 background->get_rgb(&r, &g, &b);
3762 // we have to scale 0..0xFFFF to 0..0xFF
3763 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
3765 fputs("<body bgcolor=\"#", stdout);
3766 fputs(buf, stdout);
3767 fputs("\">\n\n", stdout);
3772 * emit_link - generates: <a href="to">name</a>
3775 void html_printer::emit_link (const string &to, const char *name)
3777 fputs("<a href=\"", stdout);
3778 fputs(to.contents(), stdout);
3779 fputs("\">", stdout);
3780 fputs(name, stdout);
3781 fputs("</a>", stdout);
3785 * write_navigation - writes out the links which navigate between
3786 * file fragments.
3789 void html_printer::write_navigation (const string &top, const string &prev,
3790 const string &next, const string &current)
3792 int need_bar = FALSE;
3794 if (multiple_files) {
3795 write_rule();
3796 fputs("[ ", stdout);
3797 if (prev != "" && prev != top) {
3798 emit_link(prev, "prev");
3799 need_bar = TRUE;
3801 if (next != "" && next != top) {
3802 if (need_bar)
3803 fputs(" | ", stdout);
3804 emit_link(next, "next");
3805 need_bar = TRUE;
3807 if (top != "<standard input>" && top != "" && top != current) {
3808 if (need_bar)
3809 fputs(" | ", stdout);
3810 emit_link(top, "top");
3811 fputs(" ]\n", stdout);
3813 write_rule();
3818 * do_file_components - scan the file list copying each temporary file in turn.
3819 * This is used twofold:
3821 * firstly to emit section heading links, between file fragments if required
3822 * and secondly to generate jobname file fragments if required.
3825 void html_printer::do_file_components (void)
3827 int fragment_no = 1;
3828 string top;
3829 string prev;
3830 string next;
3831 string current;
3833 file_list.start_of_list();
3834 top = string(job_name);
3835 top += string(".html");
3836 top += '\0';
3837 next = file_list.next_file_name();
3838 next += '\0';
3839 current = next;
3840 while (file_list.get_file() != 0) {
3841 if (fseek(file_list.get_file(), 0L, 0) < 0)
3842 fatal("fseek on temporary file failed");
3843 html.copy_file(file_list.get_file());
3844 fclose(file_list.get_file());
3846 file_list.move_next();
3847 if (file_list.is_new_output_file()) {
3848 if (fragment_no > 1)
3849 write_navigation(top, prev, next, current);
3850 prev = current;
3851 current = next;
3852 next = file_list.next_file_name();
3853 next += '\0';
3854 string split_file = file_list.file_name();
3855 split_file += '\0';
3856 fflush(stdout);
3857 freopen(split_file.contents(), "w", stdout);
3858 fragment_no++;
3859 write_navigation(top, prev, next, current);
3861 if (file_list.are_links_required())
3862 header.write_headings(stdout, TRUE);
3864 if (fragment_no > 1)
3865 write_navigation(top, prev, next, current);
3866 else
3867 write_rule();
3870 html_printer::~html_printer()
3872 #ifdef LONG_FOR_TIME_T
3873 long t;
3874 #else
3875 time_t t;
3876 #endif
3878 current_paragraph->flush_text();
3879 html.end_line();
3880 html.set_file(stdout);
3881 html.begin_comment("Creator : ")
3882 .put_string("groff ")
3883 .put_string("version ")
3884 .put_string(Version_string)
3885 .end_comment();
3887 t = time(0);
3888 html.begin_comment("CreationDate: ")
3889 .put_string(ctime(&t), strlen(ctime(&t))-1)
3890 .end_comment();
3892 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
3893 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
3895 fputs("<html>\n", stdout);
3896 fputs("<head>\n", stdout);
3897 fputs("<meta name=\"generator\" "
3898 "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
3899 fputs("<meta http-equiv=\"Content-Type\" "
3900 "content=\"text/html; charset=US-ASCII\">\n", stdout);
3901 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
3902 write_title(TRUE);
3903 fputs("</head>\n", stdout);
3904 do_body();
3906 write_title(FALSE);
3907 header.write_headings(stdout, FALSE);
3908 write_rule();
3909 #if defined(DEBUGGING)
3910 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
3911 #endif
3912 html.end_line();
3913 html.end_line();
3915 if (multiple_files) {
3916 fputs("</body>\n", stdout);
3917 fputs("</html>\n", stdout);
3918 do_file_components();
3919 } else {
3920 do_file_components();
3921 fputs("</body>\n", stdout);
3922 fputs("</html>\n", stdout);
3927 * special - handle all x X requests from troff. For post-html they allow users
3928 * to pass raw html commands, turn auto linked headings off/on and
3929 * also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
3932 void html_printer::special(char *s, const environment *env, char type)
3934 if (type != 'p')
3935 return;
3936 if (s != 0) {
3937 flush_sbuf();
3938 if (env->fontno >= 0) {
3939 style sty(get_font_from_index(env->fontno), env->size, env->height,
3940 env->slant, env->fontno, *env->col);
3941 sbuf_style = sty;
3944 if (strncmp(s, "html:", 5) == 0) {
3945 int r=font::res; /* resolution of the device */
3946 font *f=sbuf_style.f;
3948 if (f == NULL) {
3949 int found=FALSE;
3951 f = font::load_font("TR", &found);
3955 * need to pass rest of string through to html output during flush
3957 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
3958 line_number,
3959 env->vpos-env->size*r/72, env->hpos,
3960 env->vpos , env->hpos);
3963 * assume that the html command has no width, if it does then hopefully troff
3964 * will have fudged this in a macro by requesting that the formatting move right by
3965 * the appropriate amount.
3967 } else if (strncmp(s, "index:", 6) == 0) {
3968 cutoff_heading = atoi(&s[6]);
3969 } else if (strncmp(s, "html-tag:", 9) == 0) {
3970 int r=font::res; /* resolution of the device */
3972 page_contents->add_tag(&sbuf_style, string(s),
3973 line_number,
3974 env->vpos-env->size*r/72, env->hpos,
3975 env->vpos , env->hpos);
3980 int main(int argc, char **argv)
3982 program_name = argv[0];
3983 static char stderr_buf[BUFSIZ];
3984 setbuf(stderr, stderr_buf);
3985 int c;
3986 static const struct option long_options[] = {
3987 { "help", no_argument, 0, CHAR_MAX + 1 },
3988 { "version", no_argument, 0, 'v' },
3989 { NULL, 0, 0, 0 }
3991 while ((c = getopt_long(argc, argv, "a:g:o:i:I:j:D:F:vbdhlrnp", long_options, NULL))
3992 != EOF)
3993 switch(c) {
3994 case 'v':
3995 printf("GNU post-grohtml (groff) version %s\n", Version_string);
3996 exit(0);
3997 break;
3998 case 'a':
3999 /* text antialiasing bits - handled by pre-html */
4000 break;
4001 case 'g':
4002 /* graphic antialiasing bits - handled by pre-html */
4003 break;
4004 case 'b':
4005 // set background color to white
4006 default_background = new color;
4007 default_background->set_gray(color::MAX_COLOR_VAL);
4008 break;
4009 case 'F':
4010 font::command_line_font_dir(optarg);
4011 break;
4012 case 'j':
4013 multiple_files = TRUE;
4014 job_name = optarg;
4015 break;
4016 case 'l':
4017 auto_links = FALSE;
4018 break;
4019 case 'r':
4020 auto_rule = FALSE;
4021 break;
4022 case 'd':
4023 /* handled by pre-html */
4024 break;
4025 case 'h':
4026 /* do not use the Hn headings of html, but manufacture our own */
4027 manufacture_headings = TRUE;
4028 break;
4029 case 'o':
4030 /* handled by pre-html */
4031 break;
4032 case 'p':
4033 /* handled by pre-html */
4034 break;
4035 case 'i':
4036 /* handled by pre-html */
4037 break;
4038 case 'I':
4039 /* handled by pre-html */
4040 break;
4041 case 'D':
4042 /* handled by pre-html */
4043 break;
4044 case 'n':
4045 simple_anchors = TRUE;
4046 break;
4047 case CHAR_MAX + 1: // --help
4048 usage(stdout);
4049 exit(0);
4050 break;
4051 case '?':
4052 usage(stderr);
4053 exit(1);
4054 break;
4055 default:
4056 assert(0);
4058 if (optind >= argc) {
4059 do_file("-");
4060 } else {
4061 for (int i = optind; i < argc; i++)
4062 do_file(argv[i]);
4064 return 0;
4067 static void usage(FILE *stream)
4069 fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
4070 program_name);