Adapt src/dev-html (src/devices/grohtml)
[s-roff.git] / src / dev-html / post-html.cpp
blobfdeed2114dc3a970a3732ee7b06ebdc8694fe673
1 /*@
2 * Copyright (c) 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 2000 - 2007
5 * Free Software Foundation, Inc.
7 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
8 * but it owes a huge amount of ideas and raw code from
9 * James Clark (jjc@jclark.com) grops/ps.cpp.
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "config.h"
28 #include "html-config.h"
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <time.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
39 #include "cset.h"
40 #include "driver.h"
41 #include "stringclass.h"
43 #include "html.h"
44 #include "html-text.h"
45 #include "html-table.h"
47 typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
48 typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
50 const char *get_html_translation (font *f, const string &name);
51 static const char *get_html_entity(unsigned int code);
52 int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
54 static int auto_links = true; /* by default we enable automatic links at */
55 /* top of the document. */
56 static int auto_rule = true; /* by default we enable an automatic rule */
57 /* at the top and bottom of the document */
58 static int simple_anchors = false; /* default to anchors with heading text */
59 static int manufacture_headings = false; /* default is to use the Hn html headings, */
60 /* rather than manufacture our own. */
61 static color *default_background = NULL; /* has user requested initial bg color? */
62 static string job_name; /* if set then the output is split into */
63 /* multiple files with `job_name'-%d.html */
64 static int multiple_files = false; /* must we the output be divided into */
65 /* multiple html files, one for each */
66 /* heading? */
67 static int base_point_size = 0; /* which troff font size maps onto html */
68 /* size 3? */
69 static int split_level = 2; /* what heading level to split at? */
70 static string head_info; /* user supplied information to be placed */
71 /* into <head> </head> */
72 static int valid_flag = false; /* has user requested a valid flag at the */
73 /* end of each page? */
74 static int roff_sig = false; /* "This document was produced using" */
75 html_dialect dialect = html4; /* which html dialect should grohtml output */
78 * start with a few favorites
81 void stop () {}
83 static int min (int a, int b)
85 if (a < b)
86 return a;
87 else
88 return b;
91 static int max (int a, int b)
93 if (a > b)
94 return a;
95 else
96 return b;
100 * is_intersection - returns true if range a1..a2 intersects with b1..b2
103 static int is_intersection (int a1, int a2, int b1, int b2)
105 // easier to prove NOT outside limits
106 return ! ((a1 > b2) || (a2 < b1));
110 * is_digit - returns true if character, ch, is a digit.
113 static int is_digit (char ch)
115 return (ch >= '0') && (ch <= '9');
119 * the classes and methods for maintaining a list of files.
122 struct file {
123 FILE *fp;
124 file *next;
125 int new_output_file;
126 int require_links;
127 string output_file_name;
129 file (FILE *f);
133 * file - initialize all fields to NULL
136 file::file (FILE *f)
137 : fp(f), next(NULL), new_output_file(false),
138 require_links(false), output_file_name("")
142 class files
144 file *head;
145 file *tail;
146 file *ptr;
148 public:
149 files ();
150 FILE *get_file (void);
151 void start_of_list (void);
152 void move_next (void);
153 void add_new_file (FILE *f);
154 void set_file_name (string name);
155 void set_links_required (void);
156 int are_links_required (void);
157 int is_new_output_file (void);
158 string file_name (void);
159 string next_file_name (void);
163 * files - create an empty list of files.
166 files::files ()
167 : head(NULL), tail(NULL), ptr(NULL)
172 * get_file - returns the FILE associated with ptr.
175 FILE *files::get_file (void)
177 if (ptr)
178 return ptr->fp;
179 else
180 return NULL;
184 * start_of_list - reset the ptr to the start of the list.
187 void files::start_of_list (void)
189 ptr = head;
193 * move_next - moves the ptr to the next element on the list.
196 void files::move_next (void)
198 if (ptr != NULL)
199 ptr = ptr->next;
203 * add_new_file - adds a new file, f, to the list.
206 void files::add_new_file (FILE *f)
208 if (head == NULL) {
209 head = new file(f);
210 tail = head;
211 } else {
212 tail->next = new file(f);
213 tail = tail->next;
215 ptr = tail;
219 * set_file_name - sets the final file name to contain the html
220 * data to name.
223 void files::set_file_name (string name)
225 if (ptr != NULL) {
226 ptr->output_file_name = name;
227 ptr->new_output_file = true;
232 * set_links_required - issue links when processing this component
233 * of the file.
236 void files::set_links_required (void)
238 if (ptr != NULL)
239 ptr->require_links = true;
243 * are_links_required - returns true if this section of the file
244 * requires that links should be issued.
247 int files::are_links_required (void)
249 if (ptr != NULL)
250 return ptr->require_links;
251 return false;
255 * is_new_output_file - returns true if this component of the file
256 * is the start of a new output file.
259 int files::is_new_output_file (void)
261 if (ptr != NULL)
262 return ptr->new_output_file;
263 return false;
267 * file_name - returns the name of the file.
270 string files::file_name (void)
272 if (ptr != NULL)
273 return ptr->output_file_name;
274 return string("");
278 * next_file_name - returns the name of the next file.
281 string files::next_file_name (void)
283 if (ptr != NULL && ptr->next != NULL)
284 return ptr->next->output_file_name;
285 return string("");
289 * the class and methods for styles
292 class style
294 public:
295 font *f;
296 int point_size;
297 int font_no;
298 int height;
299 int slant;
300 color col;
301 style ();
302 style (font *, int, int, int, int, color);
303 int operator == (const style &) const;
304 int operator != (const style &) const;
307 style::style()
308 : f(NULL)
312 style::style(font *p, int sz, int h, int sl, int no, color c)
313 : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
317 int style::operator==(const style &s) const
319 return (f == s.f && point_size == s.point_size
320 && height == s.height && slant == s.slant && col == s.col);
323 int style::operator!=(const style &s) const
325 return !(*this == s);
329 * the class and methods for retaining ascii text
332 class char_block
334 public:
335 enum { SIZE = 256 };
336 char *buffer;
337 int used;
338 char_block *next;
340 char_block();
341 char_block(int length);
342 ~char_block();
345 char_block::char_block()
346 : buffer(NULL), used(0), next(NULL)
350 char_block::char_block(int length)
351 : used(0), next(NULL)
353 buffer = new char[max(length, char_block::SIZE)];
354 if (buffer == NULL)
355 fatal("out of memory error");
358 char_block::~char_block()
360 if (buffer != NULL)
361 a_delete buffer;
364 class char_buffer {
365 public:
366 char_buffer();
367 ~char_buffer();
368 char *add_string(const char *, unsigned int);
369 char *add_string(const string &);
370 private:
371 char_block *head;
372 char_block *tail;
375 char_buffer::char_buffer()
376 : head(NULL), tail(NULL)
380 char_buffer::~char_buffer()
382 while (head != NULL) {
383 char_block *temp = head;
384 head = head->next;
385 delete temp;
389 char *char_buffer::add_string (const char *s, unsigned int length)
391 int i=0;
392 unsigned int old_used;
394 if (s == NULL || length == 0)
395 return NULL;
397 if (tail == NULL) {
398 tail = new char_block(length+1);
399 head = tail;
400 } else {
401 if (tail->used + length+1 > char_block::SIZE) {
402 tail->next = new char_block(length+1);
403 tail = tail->next;
407 old_used = tail->used;
408 do {
409 tail->buffer[tail->used] = s[i];
410 tail->used++;
411 i++;
412 length--;
413 } while (length>0);
415 // add terminating nul character
417 tail->buffer[tail->used] = '\0';
418 tail->used++;
420 // and return start of new string
422 return &tail->buffer[old_used];
425 char *char_buffer::add_string (const string &s)
427 return add_string(s.contents(), s.length());
431 * the classes and methods for maintaining glyph positions.
434 class text_glob
436 text_glob (style *s, const char *str, int length,
437 int min_vertical , int min_horizontal,
438 int max_vertical , int max_horizontal,
439 bool is_troff_command,
440 bool is_auto_image, bool is_special_command,
441 bool is_a_line , int thickness);
443 public:
444 void text_glob_html (style *s, char *str, int length,
445 int min_vertical, int min_horizontal,
446 int max_vertical, int max_horizontal);
447 void text_glob_special (style *s, char *str, int length,
448 int min_vertical, int min_horizontal,
449 int max_vertical, int max_horizontal);
450 void text_glob_line (style *s,
451 int min_vertical, int min_horizontal,
452 int max_vertical, int max_horizontal,
453 int thickness);
454 void text_glob_auto_image(style *s, char *str, int length,
455 int min_vertical, int min_horizontal,
456 int max_vertical, int max_horizontal);
457 void text_glob_tag (style *s, char *str, int length,
458 int min_vertical, int min_horizontal,
459 int max_vertical, int max_horizontal);
461 text_glob (void);
462 ~text_glob (void);
463 int is_a_line (void);
464 int is_a_tag (void);
465 int is_eol (void);
466 int is_auto_img (void);
467 int is_br (void);
468 int is_in (void);
469 int is_po (void);
470 int is_ti (void);
471 int is_ll (void);
472 int is_ce (void);
473 int is_tl (void);
474 int is_eo_tl (void);
475 int is_eol_ce (void);
476 int is_col (void);
477 int is_tab (void);
478 int is_tab0 (void);
479 int is_ta (void);
480 int is_tab_ts (void);
481 int is_tab_te (void);
482 int is_nf (void);
483 int is_fi (void);
484 int is_eo_h (void);
485 int get_arg (void);
486 int get_tab_args (char *align);
488 void remember_table (html_table *t);
489 html_table *get_table (void);
491 style text_style;
492 const char *text_string;
493 unsigned int text_length;
494 int minv, minh, maxv, maxh;
495 int is_tag; // is this a .br, .sp, .tl etc
496 int is_img_auto; // image created by eqn delim
497 int is_special; // text has come via 'x X html:'
498 int is_line; // is the command a <line>?
499 int thickness; // the thickness of a line
500 html_table *tab; // table description
503 text_glob::text_glob (style *s, const char *str, int length,
504 int min_vertical, int min_horizontal,
505 int max_vertical, int max_horizontal,
506 bool is_troff_command,
507 bool is_auto_image, bool is_special_command,
508 bool is_a_line_flag, int line_thickness)
509 : text_style(*s), text_string(str), text_length(length),
510 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
511 is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
512 is_line(is_a_line_flag), thickness(line_thickness), tab(NULL)
516 text_glob::text_glob ()
517 : text_string(NULL), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
518 is_tag(false), is_special(false), is_line(false), thickness(0), tab(NULL)
522 text_glob::~text_glob ()
524 if (tab != NULL)
525 delete tab;
529 * text_glob_html - used to place html text into the glob buffer.
532 void text_glob::text_glob_html (style *s, char *str, int length,
533 int min_vertical , int min_horizontal,
534 int max_vertical , int max_horizontal)
536 text_glob *g = new text_glob(s, str, length,
537 min_vertical, min_horizontal, max_vertical, max_horizontal,
538 false, false, false, false, 0);
539 *this = *g;
540 delete g;
544 * text_glob_html - used to place html specials into the glob buffer.
545 * This text is essentially html commands coming through
546 * from the macro sets, with special designated sequences of
547 * characters translated into html. See add_and_encode.
550 void text_glob::text_glob_special (style *s, char *str, int length,
551 int min_vertical , int min_horizontal,
552 int max_vertical , int max_horizontal)
554 text_glob *g = new text_glob(s, str, length,
555 min_vertical, min_horizontal, max_vertical, max_horizontal,
556 false, false, true, false, 0);
557 *this = *g;
558 delete g;
562 * text_glob_line - record horizontal draw line commands.
565 void text_glob::text_glob_line (style *s,
566 int min_vertical , int min_horizontal,
567 int max_vertical , int max_horizontal,
568 int thickness_value)
570 text_glob *g = new text_glob(s, "", 0,
571 min_vertical, min_horizontal, max_vertical, max_horizontal,
572 false, false, false, true, thickness_value);
573 *this = *g;
574 delete g;
578 * text_glob_auto_image - record the presence of a .auto-image tag command.
579 * Used to mark that an image has been created automatically
580 * by a preprocessor and (pre-grohtml/troff) combination.
581 * Under some circumstances images may not be created.
582 * (consider .EQ
583 * delim $$
584 * .EN
585 * .TS
586 * tab(!), center;
587 * l!l.
588 * $1 over x$!recripical of x
589 * .TE
591 * the first auto-image marker is created via .EQ/.EN pair
592 * and no image is created.
593 * The second auto-image marker occurs at $1 over x$
594 * Currently this image will not be created
595 * as the whole of the table is created as an image.
596 * (Once html tables are handled by grohtml this will change.
597 * Shortly this will be the case).
600 void text_glob::text_glob_auto_image(style *s, char *str, int length,
601 int min_vertical, int min_horizontal,
602 int max_vertical, int max_horizontal)
604 text_glob *g = new text_glob(s, str, length,
605 min_vertical, min_horizontal, max_vertical, max_horizontal,
606 true, true, false, false, 0);
607 *this = *g;
608 delete g;
612 * text_glob_tag - records a troff tag.
615 void text_glob::text_glob_tag (style *s, char *str, int length,
616 int min_vertical, int min_horizontal,
617 int max_vertical, int max_horizontal)
619 text_glob *g = new text_glob(s, str, length,
620 min_vertical, min_horizontal, max_vertical, max_horizontal,
621 true, false, false, false, 0);
622 *this = *g;
623 delete g;
627 * is_a_line - returns true if glob should be converted into an <hr>
630 int text_glob::is_a_line (void)
632 return is_line;
636 * is_a_tag - returns true if glob contains a troff directive.
639 int text_glob::is_a_tag (void)
641 return is_tag;
645 * is_eol - returns true if glob contains the tag eol
648 int text_glob::is_eol (void)
650 return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
654 * is_eol_ce - returns true if glob contains the tag eol.ce
657 int text_glob::is_eol_ce (void)
659 return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
663 * is_tl - returns true if glob contains the tag .tl
666 int text_glob::is_tl (void)
668 return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
672 * is_eo_tl - returns true if glob contains the tag eo.tl
675 int text_glob::is_eo_tl (void)
677 return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
681 * is_nf - returns true if glob contains the tag .fi 0
684 int text_glob::is_nf (void)
686 return is_tag && (strncmp(text_string, "devtag:.fi",
687 strlen("devtag:.fi")) == 0) &&
688 (get_arg() == 0);
692 * is_fi - returns true if glob contains the tag .fi 1
695 int text_glob::is_fi (void)
697 return( is_tag && (strncmp(text_string, "devtag:.fi",
698 strlen("devtag:.fi")) == 0) &&
699 (get_arg() == 1) );
703 * is_eo_h - returns true if glob contains the tag .eo.h
706 int text_glob::is_eo_h (void)
708 return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
712 * is_ce - returns true if glob contains the tag .ce
715 int text_glob::is_ce (void)
717 return is_tag && (strncmp(text_string, "devtag:.ce",
718 strlen("devtag:.ce")) == 0);
722 * is_in - returns true if glob contains the tag .in
725 int text_glob::is_in (void)
727 return is_tag && (strncmp(text_string, "devtag:.in ",
728 strlen("devtag:.in ")) == 0);
732 * is_po - returns true if glob contains the tag .po
735 int text_glob::is_po (void)
737 return is_tag && (strncmp(text_string, "devtag:.po ",
738 strlen("devtag:.po ")) == 0);
742 * is_ti - returns true if glob contains the tag .ti
745 int text_glob::is_ti (void)
747 return is_tag && (strncmp(text_string, "devtag:.ti ",
748 strlen("devtag:.ti ")) == 0);
752 * is_ll - returns true if glob contains the tag .ll
755 int text_glob::is_ll (void)
757 return is_tag && (strncmp(text_string, "devtag:.ll ",
758 strlen("devtag:.ll ")) == 0);
762 * is_col - returns true if glob contains the tag .col
765 int text_glob::is_col (void)
767 return is_tag && (strncmp(text_string, "devtag:.col",
768 strlen("devtag:.col")) == 0);
772 * is_tab_ts - returns true if glob contains the tag .tab_ts
775 int text_glob::is_tab_ts (void)
777 return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
781 * is_tab_te - returns true if glob contains the tag .tab_te
784 int text_glob::is_tab_te (void)
786 return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
790 * is_ta - returns true if glob contains the tag .ta
793 int text_glob::is_ta (void)
795 return is_tag && (strncmp(text_string, "devtag:.ta ",
796 strlen("devtag:.ta ")) == 0);
800 * is_tab - returns true if glob contains the tag tab
803 int text_glob::is_tab (void)
805 return is_tag && (strncmp(text_string, "devtag:tab ",
806 strlen("devtag:tab ")) == 0);
810 * is_tab0 - returns true if glob contains the tag tab0
813 int text_glob::is_tab0 (void)
815 return is_tag && (strncmp(text_string, "devtag:tab0",
816 strlen("devtag:tab0")) == 0);
820 * is_auto_img - returns true if the glob contains an automatically
821 * generated image.
824 int text_glob::is_auto_img (void)
826 return is_img_auto;
830 * is_br - returns true if the glob is a tag containing a .br
831 * or an implied .br. Note that we do not include .nf or .fi
832 * as grohtml will place a .br after these commands if they
833 * should break the line.
836 int text_glob::is_br (void)
838 return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0) ||
839 (strncmp("devtag:.sp", text_string,
840 strlen("devtag:.sp")) == 0));
843 int text_glob::get_arg (void)
845 if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
846 const char *p = text_string;
848 while ((*p != (char)0) && (!isspace(*p)))
849 p++;
850 while ((*p != (char)0) && (isspace(*p)))
851 p++;
852 if (*p == (char)0)
853 return -1;
854 return atoi(p);
856 return -1;
860 * get_tab_args - returns the tab position and alignment of the tab tag
863 int text_glob::get_tab_args (char *align)
865 if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
866 const char *p = text_string;
868 // firstly the alignment C|R|L
869 while ((*p != (char)0) && (!isspace(*p)))
870 p++;
871 while ((*p != (char)0) && (isspace(*p)))
872 p++;
873 *align = *p;
874 // now the int value
875 while ((*p != (char)0) && (!isspace(*p)))
876 p++;
877 while ((*p != (char)0) && (isspace(*p)))
878 p++;
879 if (*p == (char)0)
880 return -1;
881 return atoi(p);
883 return -1;
887 * remember_table - saves table, t, in the text_glob.
890 void text_glob::remember_table (html_table *t)
892 if (tab != NULL)
893 delete tab;
894 tab = t;
898 * get_table - returns the stored table description.
901 html_table *text_glob::get_table (void)
903 return tab;
907 * the class and methods used to construct ordered double linked
908 * lists. In a previous implementation we used templates via
909 * #include "ordered-list.h", but this does assume that all C++
910 * compilers can handle this feature. Pragmatically it is safer to
911 * assume this is not the case.
914 class element_list
916 public:
917 element_list *right;
918 element_list *left;
919 text_glob *datum;
920 int lineno;
921 int minv, minh, maxv, maxh;
923 element_list (text_glob *d,
924 int line_number,
925 int min_vertical, int min_horizontal,
926 int max_vertical, int max_horizontal);
927 element_list ();
928 ~element_list ();
931 element_list::element_list ()
932 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
937 * element_list - create a list element assigning the datum and region parameters.
940 element_list::element_list (text_glob *in,
941 int line_number,
942 int min_vertical, int min_horizontal,
943 int max_vertical, int max_horizontal)
944 : right(0), left(0), datum(in), lineno(line_number),
945 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
949 element_list::~element_list ()
951 if (datum != NULL)
952 delete datum;
955 class list
957 element_list *head;
958 element_list *tail;
959 element_list *ptr;
961 public:
962 list ();
963 ~list ();
964 int is_less (element_list *a, element_list *b);
965 void add (text_glob *in,
966 int line_number,
967 int min_vertical, int min_horizontal,
968 int max_vertical, int max_horizontal);
969 void sub_move_right (void);
970 void move_right (void);
971 void move_left (void);
972 int is_empty (void);
973 int is_equal_to_tail (void);
974 int is_equal_to_head (void);
975 void start_from_head (void);
976 void start_from_tail (void);
977 void insert (text_glob *in);
978 void move_to (text_glob *in);
979 text_glob *move_right_get_data (void);
980 text_glob *move_left_get_data (void);
981 text_glob *get_data (void);
985 * list - construct an empty list.
988 list::list ()
989 : head(NULL), tail(NULL), ptr(NULL)
994 * ~list - destroy a complete list.
997 list::~list()
999 element_list *temp=head;
1001 do {
1002 temp = head;
1003 if (temp != NULL) {
1004 head = head->right;
1005 delete temp;
1007 } while ((head != NULL) && (head != tail));
1011 * is_less - returns true if a is left of b if on the same line or
1012 * if a is higher up the page than b.
1015 int list::is_less (element_list *a, element_list *b)
1017 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1018 if (a->lineno < b->lineno) {
1019 return( true );
1020 } else if (a->lineno > b->lineno) {
1021 return( false );
1022 } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
1023 return( a->minh < b->minh );
1024 } else {
1025 return( a->maxv < b->maxv );
1030 * add - adds a datum to the list in the order specified by the
1031 * region position.
1034 void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
1036 // create a new list element with datum and position fields initialized
1037 element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1038 element_list *last;
1040 #if 0
1041 fprintf(stderr, "[%s %d,%d,%d,%d] ",
1042 in->text_string, min_vertical, min_horizontal, max_vertical, max_horizontal);
1043 fflush(stderr);
1044 #endif
1046 if (head == NULL) {
1047 head = t;
1048 tail = t;
1049 ptr = t;
1050 t->left = t;
1051 t->right = t;
1052 } else {
1053 last = tail;
1055 while ((last != head) && (is_less(t, last)))
1056 last = last->left;
1058 if (is_less(t, last)) {
1059 t->right = last;
1060 last->left->right = t;
1061 t->left = last->left;
1062 last->left = t;
1063 // now check for a new head
1064 if (last == head)
1065 head = t;
1066 } else {
1067 // add t beyond last
1068 t->right = last->right;
1069 t->left = last;
1070 last->right->left = t;
1071 last->right = t;
1072 // now check for a new tail
1073 if (last == tail)
1074 tail = t;
1080 * sub_move_right - removes the element which is currently pointed to by ptr
1081 * from the list and moves ptr to the right.
1084 void list::sub_move_right (void)
1086 element_list *t=ptr->right;
1088 if (head == tail) {
1089 head = NULL;
1090 if (tail != NULL)
1091 delete tail;
1093 tail = NULL;
1094 ptr = NULL;
1095 } else {
1096 if (head == ptr)
1097 head = head->right;
1098 if (tail == ptr)
1099 tail = tail->left;
1100 ptr->left->right = ptr->right;
1101 ptr->right->left = ptr->left;
1102 ptr = t;
1107 * start_from_head - assigns ptr to the head.
1110 void list::start_from_head (void)
1112 ptr = head;
1116 * start_from_tail - assigns ptr to the tail.
1119 void list::start_from_tail (void)
1121 ptr = tail;
1125 * is_empty - returns true if the list has no elements.
1128 int list::is_empty (void)
1130 return head == NULL;
1134 * is_equal_to_tail - returns true if the ptr equals the tail.
1137 int list::is_equal_to_tail (void)
1139 return ptr == tail;
1143 * is_equal_to_head - returns true if the ptr equals the head.
1146 int list::is_equal_to_head (void)
1148 return ptr == head;
1152 * move_left - moves the ptr left.
1155 void list::move_left (void)
1157 ptr = ptr->left;
1161 * move_right - moves the ptr right.
1164 void list::move_right (void)
1166 ptr = ptr->right;
1170 * get_datum - returns the datum referenced via ptr.
1173 text_glob* list::get_data (void)
1175 return ptr->datum;
1179 * move_right_get_data - returns the datum referenced via ptr and moves
1180 * ptr right.
1183 text_glob* list::move_right_get_data (void)
1185 ptr = ptr->right;
1186 if (ptr == head)
1187 return NULL;
1188 else
1189 return ptr->datum;
1193 * move_left_get_data - returns the datum referenced via ptr and moves
1194 * ptr right.
1197 text_glob* list::move_left_get_data (void)
1199 ptr = ptr->left;
1200 if (ptr == tail)
1201 return NULL;
1202 else
1203 return ptr->datum;
1207 * insert - inserts data after the current position.
1210 void list::insert (text_glob *in)
1212 if (is_empty())
1213 fatal("list must not be empty if we are inserting data");
1214 else {
1215 if (ptr == NULL)
1216 ptr = head;
1218 element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1219 if (ptr == tail)
1220 tail = t;
1221 ptr->right->left = t;
1222 t->right = ptr->right;
1223 ptr->right = t;
1224 t->left = ptr;
1229 * move_to - moves the current position to the point where data, in, exists.
1230 * This is an expensive method and should be used sparingly.
1233 void list::move_to (text_glob *in)
1235 ptr = head;
1236 while (ptr != tail && ptr->datum != in)
1237 ptr = ptr->right;
1241 * page class and methods
1244 class page
1246 public:
1247 page (void);
1248 void add (style *s, const string &str,
1249 int line_number,
1250 int min_vertical, int min_horizontal,
1251 int max_vertical, int max_horizontal);
1252 void add_tag (style *s, const string &str,
1253 int line_number,
1254 int min_vertical, int min_horizontal,
1255 int max_vertical, int max_horizontal);
1256 void add_and_encode (style *s, const string &str,
1257 int line_number,
1258 int min_vertical, int min_horizontal,
1259 int max_vertical, int max_horizontal,
1260 int is_tag);
1261 void add_line (style *s,
1262 int line_number,
1263 int x1, int y1, int x2, int y2,
1264 int thickness);
1265 void insert_tag (const string &str);
1266 void dump_page (void); // debugging method
1268 // and the data
1270 list glyphs; // position of glyphs and specials on page
1271 char_buffer buffer; // all characters for this page
1274 page::page()
1279 * insert_tag - inserts a tag after the current position.
1282 void page::insert_tag (const string &str)
1284 if (str.length() > 0) {
1285 text_glob *g=new text_glob();
1286 text_glob *f=glyphs.get_data();
1287 g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1288 f->minv, f->minh, f->maxv, f->maxh);
1289 glyphs.insert(g);
1294 * add - add html text to the list of glyphs.
1297 void page::add (style *s, const string &str,
1298 int line_number,
1299 int min_vertical, int min_horizontal,
1300 int max_vertical, int max_horizontal)
1302 if (str.length() > 0) {
1303 text_glob *g=new text_glob();
1304 g->text_glob_html(s, buffer.add_string(str), str.length(),
1305 min_vertical, min_horizontal, max_vertical, max_horizontal);
1306 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1311 * add_tag - adds a troff tag, for example: .tl .sp .br
1314 void page::add_tag (style *s, const string &str,
1315 int line_number,
1316 int min_vertical, int min_horizontal,
1317 int max_vertical, int max_horizontal)
1319 if (str.length() > 0) {
1320 text_glob *g;
1322 if (strncmp((str+'\0').contents(), "devtag:.auto-image",
1323 strlen("devtag:.auto-image")) == 0) {
1324 g = new text_glob();
1325 g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1326 min_vertical, min_horizontal, max_vertical, max_horizontal);
1327 } else {
1328 g = new text_glob();
1329 g->text_glob_tag(s, buffer.add_string(str), str.length(),
1330 min_vertical, min_horizontal, max_vertical, max_horizontal);
1332 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1337 * add_line - adds the <line> primitive providing that y1==y2
1340 void page::add_line (style *s,
1341 int line_number,
1342 int x_1, int y_1, int x_2, int y_2,
1343 int thickness)
1345 if (y_1 == y_2) {
1346 text_glob *g = new text_glob();
1347 g->text_glob_line(s,
1348 min(y_1, y_2), min(x_1, x_2),
1349 max(y_1, y_2), max(x_1, x_2),
1350 thickness);
1351 glyphs.add(g, line_number,
1352 min(y_1, y_2), min(x_1, x_2),
1353 max(y_1, y_2), max(x_1, x_2));
1358 * to_unicode - returns a unicode translation of int, ch.
1361 static char *to_unicode (unsigned int ch)
1363 static char buf[30];
1365 sprintf(buf, "&#%u;", ch);
1366 return buf;
1370 * add_and_encode - adds a special string to the page, it translates the string
1371 * into html glyphs. The special string will have come from x X html:
1372 * and can contain troff character encodings which appear as
1373 * \[char]. A sequence of \\ represents \.
1374 * So for example we can write:
1375 * "cost = \[Po]3.00 file = \\foo\\bar"
1376 * which is translated into:
1377 * "cost = &pound;3.00 file = \foo\bar"
1380 void page::add_and_encode (style *s, const string &str,
1381 int line_number,
1382 int min_vertical, int min_horizontal,
1383 int max_vertical, int max_horizontal,
1384 int is_tag)
1386 string html_string;
1387 const char *html_glyph;
1388 int i = 0;
1389 const int len = str.length();
1391 if (s->f == NULL)
1392 return;
1393 while (i < len) {
1394 if ((i + 1 < len) && (str.substring(i, 2) == string("\\["))) {
1395 // start of escape
1396 i += 2; // move over \[
1397 int a = i;
1398 while ((i < len) && (str[i] != ']'))
1399 i++;
1400 if (i > 0) {
1401 string troff_charname = str.substring(a, i - a);
1402 html_glyph = get_html_translation(s->f, troff_charname);
1403 if (html_glyph)
1404 html_string += html_glyph;
1405 else {
1406 glyph *g = name_to_glyph((troff_charname + '\0').contents());
1407 if (s->f->contains(g))
1408 html_string += s->f->get_code(g);
1412 else
1413 html_string += str[i];
1414 i++;
1416 if (html_string.length() > 0) {
1417 text_glob *g=new text_glob();
1418 if (is_tag)
1419 g->text_glob_tag(s, buffer.add_string(html_string),
1420 html_string.length(),
1421 min_vertical, min_horizontal,
1422 max_vertical, max_horizontal);
1423 else
1424 g->text_glob_special(s, buffer.add_string(html_string),
1425 html_string.length(),
1426 min_vertical, min_horizontal,
1427 max_vertical, max_horizontal);
1428 glyphs.add(g, line_number, min_vertical,
1429 min_horizontal, max_vertical, max_horizontal);
1434 * dump_page - dump the page contents for debugging purposes.
1437 void page::dump_page(void)
1439 #if defined(DEBUG_TABLES)
1440 text_glob *old_pos = glyphs.get_data();
1441 text_glob *g;
1443 printf("\n<!--\n");
1444 printf("\n\ndebugging start\n");
1445 glyphs.start_from_head();
1446 do {
1447 g = glyphs.get_data();
1448 if (g->is_tab_ts()) {
1449 printf("\n\n");
1450 if (g->get_table() != NULL)
1451 g->get_table()->dump_table();
1453 printf("%s ", g->text_string);
1454 if (g->is_tab_te())
1455 printf("\n\n");
1456 glyphs.move_right();
1457 } while (! glyphs.is_equal_to_head());
1458 glyphs.move_to(old_pos);
1459 printf("\ndebugging end\n\n");
1460 printf("\n-->\n");
1461 fflush(stdout);
1462 #endif
1466 * font classes and methods
1469 class html_font
1470 : public font
1472 html_font(const char *);
1474 public:
1475 int encoding_index;
1476 char *encoding;
1477 char *reencoded_name;
1478 ~html_font();
1479 static html_font *load_html_font(const char *);
1482 html_font *html_font::load_html_font(const char *s)
1484 html_font *f = new html_font(s);
1485 if (!f->load()) {
1486 delete f;
1487 return 0;
1489 return f;
1492 html_font::html_font(const char *nm)
1493 : font(nm)
1497 html_font::~html_font()
1502 * a simple class to contain the header to this document
1505 class title_desc
1507 public:
1508 title_desc ();
1509 ~title_desc ();
1511 int has_been_written;
1512 int has_been_found;
1513 int with_h1;
1514 string text;
1518 title_desc::title_desc ()
1519 : has_been_written(false), has_been_found(false), with_h1(false)
1523 title_desc::~title_desc ()
1527 class header_desc
1529 public:
1530 header_desc ();
1531 ~header_desc ();
1533 int no_of_level_one_headings; // how many .SH or .NH 1 have we found?
1534 int no_of_headings; // how many headings have we found?
1535 char_buffer headings; // all the headings used in the document
1536 list headers; // list of headers built from .NH and .SH
1537 list header_filename; // in which file is this header?
1538 int header_level; // current header level
1539 int written_header; // have we written the header yet?
1540 string header_buffer; // current header text
1542 void write_headings (FILE *f, int force);
1545 header_desc::header_desc ()
1546 : no_of_level_one_headings(0), no_of_headings(0),
1547 header_level(2), written_header(0)
1551 header_desc::~header_desc ()
1556 * write_headings - emits a list of links for the headings in this document
1559 void header_desc::write_headings (FILE *f, int force)
1561 text_glob *g;
1563 if (auto_links || force) {
1564 if (! headers.is_empty()) {
1565 int h=1;
1567 headers.start_from_head();
1568 header_filename.start_from_head();
1569 if (dialect == xhtml)
1570 fputs("<p>", f);
1571 do {
1572 g = headers.get_data();
1573 fputs("<a href=\"", f);
1574 if (multiple_files && (! header_filename.is_empty())) {
1575 text_glob *fn = header_filename.get_data();
1576 fputs(fn->text_string, f);
1578 fputs("#", f);
1579 if (simple_anchors) {
1580 string buffer(ANCHOR_TEMPLATE);
1582 buffer += as_string(h);
1583 buffer += '\0';
1584 fprintf(f, "%s", buffer.contents());
1585 } else
1586 fputs(g->text_string, f);
1587 h++;
1588 fputs("\">", f);
1589 fputs(g->text_string, f);
1590 fputs("</a>", f);
1591 if (dialect == xhtml)
1592 fputs("<br/>\n", f);
1593 else
1594 fputs("<br>\n", f);
1595 headers.move_right();
1596 if (multiple_files && (! header_filename.is_empty()))
1597 header_filename.move_right();
1598 } while (! headers.is_equal_to_head());
1599 fputs("\n", f);
1600 if (dialect == xhtml)
1601 fputs("</p>\n", f);
1606 struct assert_pos {
1607 assert_pos *next;
1608 const char *val;
1609 const char *id;
1612 class assert_state
1614 int check_br_flag;
1615 int check_ce_flag;
1616 int check_fi_flag;
1617 int check_sp_flag;
1618 const char *val_br;
1619 const char *val_ce;
1620 const char *val_fi;
1621 const char *val_sp;
1622 const char *file_br;
1623 const char *file_ce;
1624 const char *file_fi;
1625 const char *file_sp;
1626 const char *line_br;
1627 const char *line_ce;
1628 const char *line_fi;
1629 const char *line_sp;
1631 assert_pos *xhead;
1632 assert_pos *yhead;
1634 void add (assert_pos **h,
1635 const char *c, const char *i, const char *v,
1636 const char *f, const char *l);
1637 void compare(assert_pos *t,
1638 const char *v, const char *f, const char *l);
1639 void close (const char *c);
1640 void set (const char *c, const char *v,
1641 const char *f, const char *l);
1642 void check_value (const char *s, int v, const char *name,
1643 const char *f, const char *l, int *flag);
1644 int check_value_error (int c, int v, const char *s,
1645 const char *name,
1646 const char *f, const char *l, int flag);
1648 public:
1649 assert_state ();
1650 ~assert_state ();
1652 void addx (const char *c, const char *i, const char *v,
1653 const char *f, const char *l);
1654 void addy (const char *c, const char *i, const char *v,
1655 const char *f, const char *l);
1656 void build(const char *c, const char *v,
1657 const char *f, const char *l);
1658 void check_br (int br);
1659 void check_ce (int ce);
1660 void check_fi (int fi);
1661 void check_sp (int sp);
1662 void reset (void);
1665 assert_state::assert_state ()
1667 reset();
1668 val_br = NULL;
1669 val_ce = NULL;
1670 val_fi = NULL;
1671 val_sp = NULL;
1672 file_br = NULL;
1673 file_ce = NULL;
1674 file_fi = NULL;
1675 file_sp = NULL;
1676 line_br = NULL;
1677 line_ce = NULL;
1678 line_fi = NULL;
1679 line_sp = NULL;
1680 xhead = NULL;
1681 yhead = NULL;
1684 assert_state::~assert_state ()
1686 assert_pos *t;
1688 while (xhead != NULL) {
1689 t = xhead;
1690 xhead = xhead->next;
1691 a_delete (char *)t->val;
1692 a_delete (char *)t->id;
1693 delete t;
1695 while (yhead != NULL) {
1696 t = yhead;
1697 yhead = yhead->next;
1698 a_delete (char *)t->val;
1699 a_delete (char *)t->id;
1700 delete t;
1704 void assert_state::reset (void)
1706 check_br_flag = 0;
1707 check_ce_flag = 0;
1708 check_fi_flag = 0;
1709 check_sp_flag = 0;
1712 void assert_state::add (assert_pos **h,
1713 const char *c, const char *i, const char *v,
1714 const char *f, const char *l)
1716 assert_pos *t = *h;
1718 while (t != NULL) {
1719 if (strcmp(t->id, i) == 0)
1720 break;
1721 t = t->next;
1723 if (t != NULL && v != NULL && (v[0] != '='))
1724 compare(t, v, f, l);
1725 else {
1726 if (t == NULL) {
1727 t = new assert_pos;
1728 t->next = *h;
1729 (*h) = t;
1731 if (v == NULL || v[0] != '=') {
1732 if (f == NULL)
1733 f = "stdin";
1734 if (l == NULL)
1735 l = "<none>";
1736 if (v == NULL)
1737 v = "no value at all";
1738 fprintf(stderr, "%s:%s:error in assert format of id=%s expecting value to be prefixed with an `=' got %s\n",
1739 f, l, i, v);
1741 t->id = i;
1742 t->val = v;
1743 a_delete (char *)c;
1744 a_delete (char *)f;
1745 a_delete (char *)l;
1749 void assert_state::addx (const char *c, const char *i, const char *v,
1750 const char *f, const char *l)
1752 add(&xhead, c, i, v, f, l);
1755 void assert_state::addy (const char *c, const char *i, const char *v,
1756 const char *f, const char *l)
1758 add(&yhead, c, i, v, f, l);
1761 void assert_state::compare(assert_pos *t,
1762 const char *v, const char *f, const char *l)
1764 const char *s=t->val;
1766 while ((*v) == '=')
1767 v++;
1768 while ((*s) == '=')
1769 s++;
1771 if (strcmp(v, s) != 0) {
1772 if (f == NULL)
1773 f = "stdin";
1774 if (l == NULL)
1775 l = "<none>";
1776 fprintf(stderr,
1777 "%s:%s: " L_D_HTML " assertion failed at id%s: should %s, got %s\n",
1778 f, l, t->id, s, v);
1782 void assert_state::close (const char *c)
1784 if (strcmp(c, "sp") == 0)
1785 check_sp_flag = 0;
1786 else if (strcmp(c, "br") == 0)
1787 check_br_flag = 0;
1788 else if (strcmp(c, "fi") == 0)
1789 check_fi_flag = 0;
1790 else if (strcmp(c, "nf") == 0)
1791 check_fi_flag = 0;
1792 else if (strcmp(c, "ce") == 0)
1793 check_ce_flag = 0;
1794 else
1795 fprintf(stderr, "internal error: unrecognised tag in " L_D_HTML " (%s)\n",
1799 const char *replace_negate_str (const char *before, char *after)
1801 if (before != NULL)
1802 a_delete (char *)before;
1804 if (strlen(after) > 0) {
1805 int d = atoi(after);
1807 if (d < 0 || d > 1) {
1808 fprintf(stderr, "expecting nf/fi value to be 0 or 1 not %d\n", d);
1809 d = 0;
1811 if (d == 0)
1812 after[0] = '1';
1813 else
1814 after[0] = '0';
1815 after[1] = (char)0;
1817 return after;
1820 const char *replace_str (const char *before, const char *after)
1822 if (before != NULL)
1823 a_delete (char *)before;
1824 return after;
1827 void assert_state::set (const char *c, const char *v,
1828 const char *f, const char *l)
1830 if (l == NULL)
1831 l = "<none>";
1832 if (f == NULL)
1833 f = "stdin";
1835 // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1836 if (strcmp(c, "sp") == 0) {
1837 check_sp_flag = 1;
1838 val_sp = replace_str(val_sp, strsave(v));
1839 file_sp = replace_str(file_sp, strsave(f));
1840 line_sp = replace_str(line_sp, strsave(l));
1841 } else if (strcmp(c, "br") == 0) {
1842 check_br_flag = 1;
1843 val_br = replace_str(val_br, strsave(v));
1844 file_br = replace_str(file_br, strsave(f));
1845 line_br = replace_str(line_br, strsave(l));
1846 } else if (strcmp(c, "fi") == 0) {
1847 check_fi_flag = 1;
1848 val_fi = replace_str(val_fi, strsave(v));
1849 file_fi = replace_str(file_fi, strsave(f));
1850 line_fi = replace_str(line_fi, strsave(l));
1851 } else if (strcmp(c, "nf") == 0) {
1852 check_fi_flag = 1;
1853 val_fi = replace_negate_str(val_fi, strsave(v));
1854 file_fi = replace_str(file_fi, strsave(f));
1855 line_fi = replace_str(line_fi, strsave(l));
1856 } else if (strcmp(c, "ce") == 0) {
1857 check_ce_flag = 1;
1858 val_ce = replace_str(val_ce, strsave(v));
1859 file_ce = replace_str(file_ce, strsave(f));
1860 line_ce = replace_str(line_ce, strsave(l));
1865 * build - builds the troff state assertion.
1866 * see tmac/www.tmac for cmd examples.
1869 void assert_state::build (const char *c, const char *v,
1870 const char *f, const char *l)
1872 if (c[0] == '{')
1873 set(&c[1], v, f, l);
1874 if (c[0] == '}')
1875 close(&c[1]);
1878 int assert_state::check_value_error (int c, int v, const char *s,
1879 const char *name,
1880 const char *f, const char *l, int flag)
1882 if (! c) {
1883 if (f == NULL)
1884 f = "stdin";
1885 if (l == NULL)
1886 l = "<none>";
1887 fprintf(stderr,
1888 "%s:%s: " L_D_HTML " assertion failed, <%s>: should %s, got %d\n",
1889 f, l, name, s, v);
1890 return 0;
1892 return flag;
1895 void assert_state::check_value (const char *s, int v, const char *name,
1896 const char *f, const char *l, int *flag)
1898 if (strncmp(s, "<=", 2) == 0)
1899 *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
1900 else if (strncmp(s, ">=", 2) == 0)
1901 *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
1902 else if (strncmp(s, "==", 2) == 0)
1903 *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
1904 else if (strncmp(s, "!=", 2) == 0)
1905 *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
1906 else if (strncmp(s, "<", 1) == 0)
1907 *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
1908 else if (strncmp(s, ">", 1) == 0)
1909 *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
1910 else if (strncmp(s, "=", 1) == 0)
1911 *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
1912 else
1913 *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
1916 void assert_state::check_sp (int sp)
1918 if (check_sp_flag)
1919 check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
1922 void assert_state::check_fi (int fi)
1924 if (check_fi_flag)
1925 check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
1928 void assert_state::check_br (int br)
1930 if (check_br_flag)
1931 check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
1934 void assert_state::check_ce (int ce)
1936 if (check_ce_flag)
1937 check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
1940 class html_printer
1941 : public printer
1943 files file_list;
1944 simple_output html;
1945 int res;
1946 glyph *space_glyph;
1947 int space_width;
1948 int no_of_printed_pages;
1949 int paper_length;
1950 string sbuf;
1951 int sbuf_start_hpos;
1952 int sbuf_vpos;
1953 int sbuf_end_hpos;
1954 int sbuf_prev_hpos;
1955 int sbuf_kern;
1956 style sbuf_style;
1957 int last_sbuf_length;
1958 int overstrike_detected;
1959 style output_style;
1960 int output_hpos;
1961 int output_vpos;
1962 int output_vpos_max;
1963 int output_draw_point_size;
1964 int line_thickness;
1965 int output_line_thickness;
1966 unsigned char output_space_code;
1967 char *inside_font_style;
1968 int page_number;
1969 title_desc title;
1970 header_desc header;
1971 int header_indent;
1972 int supress_sub_sup;
1973 int cutoff_heading;
1974 page *page_contents;
1975 html_text *current_paragraph;
1976 html_indent *indent;
1977 html_table *table;
1978 int end_center;
1979 int end_tempindent;
1980 TAG_ALIGNMENT next_tag;
1981 int fill_on;
1982 int max_linelength;
1983 int linelength;
1984 int pageoffset;
1985 int troff_indent;
1986 int device_indent;
1987 int temp_indent;
1988 int pointsize;
1989 int vertical_spacing;
1990 int line_number;
1991 color *background;
1992 int seen_indent;
1993 int next_indent;
1994 int seen_pageoffset;
1995 int next_pageoffset;
1996 int seen_linelength;
1997 int next_linelength;
1998 int seen_center;
1999 int next_center;
2000 int seen_space;
2001 int seen_break;
2002 int current_column;
2003 int row_space;
2004 assert_state as;
2006 void flush_sbuf ();
2007 void set_style (const style &);
2008 void set_space_code (unsigned char c);
2009 void do_exec (char *, const environment *);
2010 void do_import (char *, const environment *);
2011 void do_def (char *, const environment *);
2012 void do_mdef (char *, const environment *);
2013 void do_file (char *, const environment *);
2014 void set_line_thickness (const environment *);
2015 void terminate_current_font (void);
2016 void flush_font (void);
2017 void add_to_sbuf (glyph *g, const string &s);
2018 void write_title (int in_head);
2019 int sbuf_continuation (glyph *g, const char *name, const environment *env, int w);
2020 void flush_page (void);
2021 void troff_tag (text_glob *g);
2022 void flush_globs (void);
2023 void emit_line (text_glob *g);
2024 void emit_raw (text_glob *g);
2025 void emit_html (text_glob *g);
2026 void determine_space (text_glob *g);
2027 void start_font (const char *name);
2028 void end_font (const char *name);
2029 int is_font_courier (font *f);
2030 int is_line_start (int nf);
2031 int is_courier_until_eol (void);
2032 void start_size (int from, int to);
2033 void do_font (text_glob *g);
2034 void do_center (char *arg);
2035 void do_check_center (void);
2036 void do_break (void);
2037 void do_space (char *arg);
2038 void do_eol (void);
2039 void do_eol_ce (void);
2040 void do_title (void);
2041 void do_fill (char *arg);
2042 void do_heading (char *arg);
2043 void write_header (void);
2044 void determine_header_level (int level);
2045 void do_linelength (char *arg);
2046 void do_pageoffset (char *arg);
2047 void do_indentation (char *arg);
2048 void do_tempindent (char *arg);
2049 void do_indentedparagraph (void);
2050 void do_verticalspacing (char *arg);
2051 void do_pointsize (char *arg);
2052 void do_centered_image (void);
2053 void do_left_image (void);
2054 void do_right_image (void);
2055 void do_auto_image (text_glob *g, const char *filename);
2056 void do_links (void);
2057 void do_flush (void);
2058 void do_job_name (char *name);
2059 void do_head (char *name);
2060 void insert_split_file (void);
2061 int is_in_middle (int left, int right);
2062 void do_sup_or_sub (text_glob *g);
2063 int start_subscript (text_glob *g);
2064 int end_subscript (text_glob *g);
2065 int start_superscript (text_glob *g);
2066 int end_superscript (text_glob *g);
2067 void outstanding_eol (int n);
2068 int is_bold (font *f);
2069 font *make_bold (font *f);
2070 int overstrike (glyph *g, const char *name, const environment *env, int w);
2071 void do_body (void);
2072 int next_horiz_pos (text_glob *g, int nf);
2073 void lookahead_for_tables (void);
2074 void insert_tab_te (void);
2075 text_glob *insert_tab_ts (text_glob *where);
2076 void insert_tab0_foreach_tab (void);
2077 void insert_tab_0 (text_glob *where);
2078 void do_indent (int in, int pageoff, int linelen);
2079 void shutdown_table (void);
2080 void do_tab_ts (text_glob *g);
2081 void do_tab_te (void);
2082 void do_col (char *s);
2083 void do_tab (char *s);
2084 void do_tab0 (void);
2085 int calc_nf (text_glob *g, int nf);
2086 void calc_po_in (text_glob *g, int nf);
2087 void remove_tabs (void);
2088 void remove_courier_tabs (void);
2089 void update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g);
2090 void add_table_end (const char *);
2091 void do_file_components (void);
2092 void write_navigation (const string &top, const string &prev,
2093 const string &next, const string &current);
2094 void emit_link (const string &to, const char *name);
2095 int get_troff_indent (void);
2096 void restore_troff_indent (void);
2097 void handle_assertion (int minv, int minh, int maxv, int maxh, const char *s);
2098 void handle_state_assertion (text_glob *g);
2099 void do_end_para (text_glob *g);
2100 int round_width (int x);
2101 void handle_tag_within_title (text_glob *g);
2102 void writeHeadMetaStyle (void);
2103 void handle_valid_flag (int needs_para);
2104 void do_math (text_glob *g);
2105 void write_html_anchor (text_glob *h);
2106 void write_xhtml_anchor (text_glob *h);
2107 // ADD HERE
2109 public:
2110 html_printer ();
2111 ~html_printer ();
2112 void set_char (glyph *g, font *f, const environment *env, int w, const char *name);
2113 void set_numbered_char(int num, const environment *env, int *widthp);
2114 glyph *set_char_and_width(const char *nm, const environment *env,
2115 int *widthp, font **f);
2116 void draw (int code, int *p, int np, const environment *env);
2117 void begin_page (int);
2118 void end_page (int);
2119 void special (char *arg, const environment *env, char type);
2120 void devtag (char *arg, const environment *env, char type);
2121 font *make_font (const char *);
2122 void end_of_line ();
2125 printer *make_printer()
2127 return new html_printer;
2130 static void usage(FILE *stream);
2132 void html_printer::set_style(const style &sty)
2134 const char *fontname = sty.f->get_name();
2135 if (fontname == NULL)
2136 fatal("no internalname specified for font");
2138 #if 0
2139 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
2140 #endif
2144 * is_bold - returns true if font, f, is bold.
2147 int html_printer::is_bold (font *f)
2149 const char *fontname = f->get_name();
2150 return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
2154 * make_bold - if a bold font of, f, exists then return it.
2157 font *html_printer::make_bold (font *f)
2159 const char *fontname = f->get_name();
2161 if (strcmp(fontname, "B") == 0)
2162 return f;
2163 if (strcmp(fontname, "I") == 0)
2164 return font::load_font("BI");
2165 if (strcmp(fontname, "BI") == 0)
2166 return f;
2167 return NULL;
2170 void html_printer::end_of_line()
2172 flush_sbuf();
2173 line_number++;
2177 * emit_line - writes out a horizontal rule.
2180 void html_printer::emit_line (text_glob *)
2182 // --fixme-- needs to know the length in percentage
2183 if (dialect == xhtml)
2184 html.put_string("<hr/>");
2185 else
2186 html.put_string("<hr>");
2190 * restore_troff_indent - is called when we have temporarily shutdown
2191 * indentation (typically done when we have
2192 * centered an image).
2195 void html_printer::restore_troff_indent (void)
2197 troff_indent = next_indent;
2198 if (troff_indent > 0) {
2200 * force device indentation
2202 device_indent = 0;
2203 do_indent(get_troff_indent(), pageoffset, linelength);
2208 * emit_raw - writes the raw html information directly to the device.
2211 void html_printer::emit_raw (text_glob *g)
2213 do_font(g);
2214 if (next_tag == INLINE) {
2215 determine_space(g);
2216 current_paragraph->do_emittext(g->text_string, g->text_length);
2217 } else {
2218 int space = current_paragraph->retrieve_para_space() || seen_space;
2220 current_paragraph->done_para();
2221 shutdown_table();
2222 switch (next_tag) {
2224 case CENTERED:
2225 if (dialect == html4)
2226 current_paragraph->do_para("align=\"center\"", space);
2227 else
2228 current_paragraph->do_para("class=\"center\"", space);
2229 break;
2230 case LEFT:
2231 if (dialect == html4)
2232 current_paragraph->do_para(&html, "align=\"left\"", get_troff_indent(), pageoffset, linelength, space);
2233 else
2234 current_paragraph->do_para(&html, "class=\"left\"", get_troff_indent(), pageoffset, linelength, space);
2235 break;
2236 case RIGHT:
2237 if (dialect == html4)
2238 current_paragraph->do_para(&html, "align=\"right\"", get_troff_indent(), pageoffset, linelength, space);
2239 else
2240 current_paragraph->do_para(&html, "class=\"right\"", get_troff_indent(), pageoffset, linelength, space);
2241 break;
2242 default:
2243 fatal("unknown enumeration");
2245 current_paragraph->do_emittext(g->text_string, g->text_length);
2246 current_paragraph->done_para();
2247 next_tag = INLINE;
2248 supress_sub_sup = true;
2249 seen_space = false;
2250 restore_troff_indent();
2255 * handle_tag_within_title - handle a limited number of tags within
2256 * the context of a table. Those tags which
2257 * set values rather than generate spaces
2258 * and paragraphs.
2261 void html_printer::handle_tag_within_title (text_glob *g)
2263 if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll()
2264 || g->is_fi() || g->is_nf())
2265 troff_tag(g);
2269 * do_center - handle the .ce commands from troff.
2272 void html_printer::do_center (char *arg)
2274 next_center = atoi(arg);
2275 seen_center = true;
2279 * do_centered_image - set a flag such that the next devtag is
2280 * placed inside a centered paragraph.
2283 void html_printer::do_centered_image (void)
2285 next_tag = CENTERED;
2289 * do_right_image - set a flag such that the next devtag is
2290 * placed inside a right aligned paragraph.
2293 void html_printer::do_right_image (void)
2295 next_tag = RIGHT;
2299 * do_left_image - set a flag such that the next devtag is
2300 * placed inside a left aligned paragraph.
2303 void html_printer::do_left_image (void)
2305 next_tag = LEFT;
2309 * exists - returns true if filename exists.
2312 static int exists (const char *filename)
2314 FILE *fp = fopen(filename, "r");
2316 if (fp == 0) {
2317 return( false );
2318 } else {
2319 fclose(fp);
2320 return( true );
2325 * generate_img_src - returns a html image tag for the filename
2326 * providing that the image exists.
2329 static string &generate_img_src (const char *filename)
2331 string *s = new string("");
2333 while (filename && (filename[0] == ' ')) {
2334 filename++;
2336 if (exists(filename)) {
2337 *s += string("<img src=\"") + filename + "\" "
2338 + "alt=\"Image " + filename + "\">";
2339 if (dialect == xhtml)
2340 *s += "</img>";
2342 return *s;
2346 * do_auto_image - tests whether the image, indicated by filename,
2347 * is present, if so then it emits an html image tag.
2348 * An image tag may be passed through from pic, eqn
2349 * but the corresponding image might not be created.
2350 * Consider .EQ delim $$ .EN or an empty .PS .PE.
2353 void html_printer::do_auto_image (text_glob *g, const char *filename)
2355 string buffer = generate_img_src(filename);
2357 if (! buffer.empty()) {
2359 * utilize emit_raw by creating a new text_glob.
2361 text_glob h = *g;
2363 h.text_string = buffer.contents();
2364 h.text_length = buffer.length();
2365 emit_raw(&h);
2366 } else
2367 next_tag = INLINE;
2371 * outstanding_eol - call do_eol, n, times.
2374 void html_printer::outstanding_eol (int n)
2376 while (n > 0) {
2377 do_eol();
2378 n--;
2383 * do_title - handle the .tl commands from troff.
2386 void html_printer::do_title (void)
2388 text_glob *t;
2389 int removed_from_head;
2391 if (page_number == 1) {
2392 int found_title_start = false;
2393 if (! page_contents->glyphs.is_empty()) {
2394 page_contents->glyphs.sub_move_right(); /* move onto next word */
2395 do {
2396 t = page_contents->glyphs.get_data();
2397 removed_from_head = false;
2398 if (t->is_auto_img()) {
2399 string img = generate_img_src((char *)(t->text_string + 20));
2401 if (! img.empty()) {
2402 if (found_title_start)
2403 title.text += " ";
2404 found_title_start = true;
2405 title.has_been_found = true;
2406 title.text += img;
2408 page_contents->glyphs.sub_move_right(); /* move onto next word */
2409 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2410 (page_contents->glyphs.is_equal_to_head()));
2411 } else if (t->is_eo_tl()) {
2412 /* end of title found
2414 title.has_been_found = true;
2415 return;
2416 } else if (t->is_a_tag()) {
2417 handle_tag_within_title(t);
2418 page_contents->glyphs.sub_move_right(); /* move onto next word */
2419 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2420 (page_contents->glyphs.is_equal_to_head()));
2421 } else if (found_title_start) {
2422 title.text += " " + string(t->text_string, t->text_length);
2423 page_contents->glyphs.sub_move_right(); /* move onto next word */
2424 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2425 (page_contents->glyphs.is_equal_to_head()));
2426 } else {
2427 title.text += string(t->text_string, t->text_length);
2428 found_title_start = true;
2429 title.has_been_found = true;
2430 page_contents->glyphs.sub_move_right(); /* move onto next word */
2431 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2432 (page_contents->glyphs.is_equal_to_head()));
2434 } while ((! page_contents->glyphs.is_equal_to_head()) ||
2435 (removed_from_head));
2441 * write_html_anchor - writes out an anchor. The style of the anchor
2442 * dependent upon simple_anchor.
2445 void html_printer::write_html_anchor (text_glob *h)
2447 if (dialect == html4) {
2448 if (h != NULL) {
2449 html.put_string("<a name=\"");
2450 if (simple_anchors) {
2451 string buffer(ANCHOR_TEMPLATE);
2453 buffer += as_string(header.no_of_headings);
2454 buffer += '\0';
2455 html.put_string(buffer.contents());
2456 } else
2457 html.put_string(header.header_buffer);
2458 html.put_string("\"></a>").nl();
2464 * write_xhtml_anchor - writes out an anchor. The style of the anchor
2465 * dependent upon simple_anchor.
2468 void html_printer::write_xhtml_anchor (text_glob *h)
2470 if (dialect == xhtml) {
2471 if (h != NULL) {
2472 html.put_string(" id=\"");
2473 if (simple_anchors) {
2474 string buffer(ANCHOR_TEMPLATE);
2476 buffer += as_string(header.no_of_headings);
2477 buffer += '\0';
2478 html.put_string(buffer.contents());
2479 } else
2480 html.put_string(header.header_buffer);
2481 html.put_string("\"");
2486 void html_printer::write_header (void)
2488 if (! header.header_buffer.empty()) {
2489 text_glob *a = NULL;
2490 int space = current_paragraph->retrieve_para_space() || seen_space;
2492 if (header.header_level > 7)
2493 header.header_level = 7;
2495 // firstly we must terminate any font and type faces
2496 current_paragraph->done_para();
2497 supress_sub_sup = true;
2499 if (cutoff_heading+2 > header.header_level) {
2500 // now we save the header so we can issue a list of links
2501 header.no_of_headings++;
2502 style st;
2504 a = new text_glob();
2505 a->text_glob_html(&st,
2506 header.headings.add_string(header.header_buffer),
2507 header.header_buffer.length(),
2508 header.no_of_headings, header.header_level,
2509 header.no_of_headings, header.header_level);
2511 // and add this header to the header list
2512 header.headers.add(a,
2513 header.no_of_headings,
2514 header.no_of_headings, header.no_of_headings,
2515 header.no_of_headings, header.no_of_headings);
2518 html.nl().nl();
2520 if (manufacture_headings) {
2521 // line break before a header
2522 if (!current_paragraph->emitted_text())
2523 current_paragraph->do_space();
2524 // user wants manufactured headings which look better than <Hn></Hn>
2525 if (header.header_level<4) {
2526 html.put_string("<b><font size=\"+1\">");
2527 html.put_string(header.header_buffer);
2528 html.put_string("</font>").nl();
2529 write_html_anchor(a);
2530 html.put_string("</b>").nl();
2532 else {
2533 html.put_string("<b>");
2534 html.put_string(header.header_buffer).nl();
2535 write_html_anchor(a);
2536 html.put_string("</b>").nl();
2539 else {
2540 // and now we issue the real header
2541 html.put_string("<h");
2542 html.put_number(header.header_level);
2543 write_xhtml_anchor(a);
2544 html.put_string(">");
2545 html.put_string(header.header_buffer).nl();
2546 write_html_anchor(a);
2547 html.put_string("</h");
2548 html.put_number(header.header_level);
2549 html.put_string(">").nl();
2552 /* and now we save the file name in which this header will occur */
2554 style st; // fake style to enable us to use the list data structure
2556 text_glob *h=new text_glob();
2557 h->text_glob_html(&st,
2558 header.headings.add_string(file_list.file_name()),
2559 file_list.file_name().length(),
2560 header.no_of_headings, header.header_level,
2561 header.no_of_headings, header.header_level);
2563 header.header_filename.add(h,
2564 header.no_of_headings,
2565 header.no_of_headings, header.no_of_headings,
2566 header.no_of_headings, header.no_of_headings);
2568 current_paragraph->do_para(&html, "", get_troff_indent(), pageoffset, linelength, space);
2572 void html_printer::determine_header_level (int level)
2574 if (level == 0) {
2575 int i;
2577 for (i=0; ((i<header.header_buffer.length())
2578 && ((header.header_buffer[i] == '.')
2579 || is_digit(header.header_buffer[i]))) ; i++) {
2580 if (header.header_buffer[i] == '.') {
2581 level++;
2585 header.header_level = level+1;
2586 if (header.header_level >= 2 && header.header_level <= split_level) {
2587 header.no_of_level_one_headings++;
2588 insert_split_file();
2593 * do_heading - handle the .SH and .NH and equivalent commands from troff.
2596 void html_printer::do_heading (char *arg)
2598 text_glob *g;
2599 int level=atoi(arg);
2600 int horiz;
2602 header.header_buffer.clear();
2603 page_contents->glyphs.move_right();
2604 if (! page_contents->glyphs.is_equal_to_head()) {
2605 g = page_contents->glyphs.get_data();
2606 horiz = g->minh;
2607 do {
2608 if (g->is_auto_img()) {
2609 string img=generate_img_src((char *)(g->text_string + 20));
2611 if (! img.empty()) {
2612 simple_anchors = true; // we cannot use full heading anchors with images
2613 if (horiz < g->minh)
2614 header.header_buffer += " ";
2616 header.header_buffer += img;
2619 else if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll())
2620 troff_tag(g);
2621 else if (g->is_fi())
2622 fill_on = 1;
2623 else if (g->is_nf())
2624 fill_on = 0;
2625 else if (! (g->is_a_line() || g->is_a_tag())) {
2627 * we ignore the other tag commands when constructing a heading
2629 if (horiz < g->minh)
2630 header.header_buffer += " ";
2632 horiz = g->maxh;
2633 header.header_buffer += string(g->text_string, g->text_length);
2635 page_contents->glyphs.move_right();
2636 g = page_contents->glyphs.get_data();
2637 } while ((! page_contents->glyphs.is_equal_to_head()) &&
2638 (! g->is_eo_h()));
2641 determine_header_level(level);
2642 write_header();
2645 * finally set the output font to uninitialized, thus forcing
2646 * the new paragraph to start a new font block.
2649 output_style.f = NULL;
2650 g = page_contents->glyphs.get_data();
2651 page_contents->glyphs.move_left(); // so that next time we use old g
2655 * is_courier_until_eol - returns true if we can see a whole line which is courier
2658 int html_printer::is_courier_until_eol (void)
2660 text_glob *orig = page_contents->glyphs.get_data();
2661 int result = true;
2662 text_glob *g;
2664 if (! page_contents->glyphs.is_equal_to_tail()) {
2665 page_contents->glyphs.move_right();
2666 do {
2667 g = page_contents->glyphs.get_data();
2668 if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2669 result = false;
2670 page_contents->glyphs.move_right();
2671 } while (result &&
2672 (! page_contents->glyphs.is_equal_to_head()) &&
2673 (! g->is_fi()) && (! g->is_eol()));
2676 * now restore our previous position.
2678 while (page_contents->glyphs.get_data() != orig)
2679 page_contents->glyphs.move_left();
2681 return result;
2685 * do_linelength - handle the .ll command from troff.
2688 void html_printer::do_linelength (char *arg)
2690 if (max_linelength == -1)
2691 max_linelength = atoi(arg);
2693 next_linelength = atoi(arg);
2694 seen_linelength = true;
2698 * do_pageoffset - handle the .po command from troff.
2701 void html_printer::do_pageoffset (char *arg)
2703 next_pageoffset = atoi(arg);
2704 seen_pageoffset = true;
2708 * get_troff_indent - returns the indent value.
2711 int html_printer::get_troff_indent (void)
2713 if (end_tempindent > 0)
2714 return temp_indent;
2715 else
2716 return troff_indent;
2720 * do_indentation - handle the .in command from troff.
2723 void html_printer::do_indentation (char *arg)
2725 next_indent = atoi(arg);
2726 seen_indent = true;
2730 * do_tempindent - handle the .ti command from troff.
2733 void html_printer::do_tempindent (char *arg)
2735 if (fill_on) {
2737 * we set the end_tempindent to 2 as the first .br
2738 * activates the .ti and the second terminates it.
2740 end_tempindent = 2;
2741 temp_indent = atoi(arg);
2746 * shutdown_table - shuts down the current table.
2749 void html_printer::shutdown_table (void)
2751 if (table != NULL) {
2752 current_paragraph->done_para();
2753 table->emit_finish_table();
2754 // dont delete this table as it will be deleted when we destroy the text_glob
2755 table = NULL;
2760 * do_indent - remember the indent parameters and if
2761 * indent is > pageoff and indent has changed
2762 * then we start a html table to implement the indentation.
2765 void html_printer::do_indent (int in, int pageoff, int linelen)
2767 if ((device_indent != -1) &&
2768 (pageoffset+device_indent != in+pageoff)) {
2770 int space = current_paragraph->retrieve_para_space() || seen_space;
2771 current_paragraph->done_para();
2773 device_indent = in;
2774 pageoffset = pageoff;
2775 if (linelen <= max_linelength)
2776 linelength = linelen;
2778 current_paragraph->do_para(&html, "", device_indent,
2779 pageoffset, max_linelength, space);
2784 * do_verticalspacing - handle the .vs command from troff.
2787 void html_printer::do_verticalspacing (char *arg)
2789 vertical_spacing = atoi(arg);
2793 * do_pointsize - handle the .ps command from troff.
2796 void html_printer::do_pointsize (char *arg)
2799 * firstly check to see whether this point size is really associated with a .tl tag
2802 if (! page_contents->glyphs.is_empty()) {
2803 text_glob *g = page_contents->glyphs.get_data();
2804 text_glob *t = page_contents->glyphs.get_data();
2806 while (t->is_a_tag() && (! page_contents->glyphs.is_equal_to_head())) {
2807 if (t->is_tl()) {
2809 * found title therefore ignore this .ps tag
2811 while (t != g) {
2812 page_contents->glyphs.move_left();
2813 t = page_contents->glyphs.get_data();
2815 return;
2817 page_contents->glyphs.move_right();
2818 t = page_contents->glyphs.get_data();
2821 * move back to original position
2823 while (t != g) {
2824 page_contents->glyphs.move_left();
2825 t = page_contents->glyphs.get_data();
2828 * collect valid pointsize
2830 pointsize = atoi(arg);
2835 * do_fill - records whether troff has requested that text be filled.
2838 void html_printer::do_fill (char *arg)
2840 int on = atoi(arg);
2842 output_hpos = get_troff_indent()+pageoffset;
2843 supress_sub_sup = true;
2845 if (fill_on != on) {
2846 if (on)
2847 current_paragraph->do_para("", seen_space);
2848 fill_on = on;
2853 * do_eol - handle the end of line
2856 void html_printer::do_eol (void)
2858 if (! fill_on) {
2859 if (current_paragraph->ever_emitted_text()) {
2860 current_paragraph->do_newline();
2861 current_paragraph->do_break();
2864 output_hpos = get_troff_indent()+pageoffset;
2868 * do_check_center - checks to see whether we have seen a `.ce' tag
2869 * during the previous line.
2872 void html_printer::do_check_center(void)
2874 if (seen_center) {
2875 seen_center = false;
2876 if (next_center > 0) {
2877 if (end_center == 0) {
2878 int space = current_paragraph->retrieve_para_space() || seen_space;
2879 current_paragraph->done_para();
2880 supress_sub_sup = true;
2881 if (dialect == html4)
2882 current_paragraph->do_para("align=\"center\"", space);
2883 else
2884 current_paragraph->do_para("class=\"center\"", space);
2885 } else
2886 if ((strcmp("align=\"center\"",
2887 current_paragraph->get_alignment()) != 0) &&
2888 (strcmp("class=\"center\"",
2889 current_paragraph->get_alignment()) != 0)) {
2891 * different alignment, so shutdown paragraph and open
2892 * a new one.
2894 int space = current_paragraph->retrieve_para_space() || seen_space;
2895 current_paragraph->done_para();
2896 supress_sub_sup = true;
2897 if (dialect == html4)
2898 current_paragraph->do_para("align=\"center\"", space);
2899 else
2900 current_paragraph->do_para("class=\"center\"", space);
2901 } else
2903 * same alignment, if we have emitted text then issue a break.
2905 if (current_paragraph->emitted_text())
2906 current_paragraph->do_break();
2907 } else
2909 * next_center == 0
2911 if (end_center > 0) {
2912 seen_space = seen_space || current_paragraph->retrieve_para_space();
2913 current_paragraph->done_para();
2914 supress_sub_sup = true;
2915 current_paragraph->do_para("", seen_space);
2917 end_center = next_center;
2922 * do_eol_ce - handle end of line specifically for a .ce
2925 void html_printer::do_eol_ce (void)
2927 if (end_center > 0) {
2928 if (end_center > 1)
2929 if (current_paragraph->emitted_text())
2930 current_paragraph->do_break();
2932 end_center--;
2933 if (end_center == 0) {
2934 current_paragraph->done_para();
2935 supress_sub_sup = true;
2941 * do_flush - flushes all output and tags.
2944 void html_printer::do_flush (void)
2946 current_paragraph->done_para();
2950 * do_links - moves onto a new temporary file and sets auto_links to false.
2953 void html_printer::do_links (void)
2955 html.end_line(); // flush line
2956 auto_links = false; /* from now on only emit under user request */
2957 file_list.add_new_file(xtmpfile());
2958 file_list.set_links_required();
2959 html.set_file(file_list.get_file());
2963 * insert_split_file -
2966 void html_printer::insert_split_file (void)
2968 if (multiple_files) {
2969 current_paragraph->done_para(); // flush paragraph
2970 html.end_line(); // flush line
2971 html.set_file(file_list.get_file()); // flush current file
2972 file_list.add_new_file(xtmpfile());
2973 string split_file = job_name;
2975 split_file += string("-");
2976 split_file += as_string(header.no_of_level_one_headings);
2977 if (dialect == xhtml)
2978 split_file += string(".xhtml");
2979 else
2980 split_file += string(".html");
2981 split_file += '\0';
2983 file_list.set_file_name(split_file);
2984 html.set_file(file_list.get_file());
2989 * do_job_name - assigns the job_name to name.
2992 void html_printer::do_job_name (char *name)
2994 if (! multiple_files) {
2995 multiple_files = true;
2996 while (name != NULL && (*name != (char)0) && (*name == ' '))
2997 name++;
2998 job_name = name;
3003 * do_head - adds a string to head_info which is to be included into
3004 * the <head> </head> section of the html document.
3007 void html_printer::do_head (char *name)
3009 head_info += string(name);
3010 head_info += '\n';
3014 * do_break - handles the ".br" request and also
3015 * undoes an outstanding ".ti" command
3016 * and calls indent if the indentation
3017 * related registers have changed.
3020 void html_printer::do_break (void)
3022 int seen_temp_indent = false;
3024 current_paragraph->do_break();
3025 if (end_tempindent > 0) {
3026 end_tempindent--;
3027 if (end_tempindent > 0)
3028 seen_temp_indent = true;
3030 if (seen_indent || seen_pageoffset || seen_linelength || seen_temp_indent) {
3031 if (seen_indent && (! seen_temp_indent))
3032 troff_indent = next_indent;
3033 if (! seen_pageoffset)
3034 next_pageoffset = pageoffset;
3035 if (! seen_linelength)
3036 next_linelength = linelength;
3037 do_indent(get_troff_indent(), next_pageoffset, next_linelength);
3039 seen_indent = seen_temp_indent;
3040 seen_linelength = false;
3041 seen_pageoffset = false;
3042 do_check_center();
3043 output_hpos = get_troff_indent()+pageoffset;
3044 supress_sub_sup = true;
3047 void html_printer::do_space (char *arg)
3049 int n = atoi(arg);
3051 seen_space = atoi(arg);
3052 as.check_sp(seen_space);
3053 #if 0
3054 if (n>0 && table)
3055 table->set_space(true);
3056 #endif
3058 while (n>0) {
3059 current_paragraph->do_space();
3060 n--;
3062 supress_sub_sup = true;
3066 * do_tab_ts - start a table, which will have already been defined.
3069 void html_printer::do_tab_ts (text_glob *g)
3071 html_table *t = g->get_table();
3073 if (t != NULL) {
3074 current_column = 0;
3075 current_paragraph->done_pre();
3076 current_paragraph->done_para();
3077 current_paragraph->remove_para_space();
3079 #if defined(DEBUG_TABLES)
3080 html.simple_comment("TABS");
3081 #endif
3083 t->set_linelength(max_linelength);
3084 t->add_indent(pageoffset);
3085 #if 0
3086 t->emit_table_header(seen_space);
3087 #else
3088 t->emit_table_header(false);
3089 row_space = current_paragraph->retrieve_para_space() || seen_space;
3090 seen_space = false;
3091 #endif
3094 table = t;
3098 * do_tab_te - finish a table.
3101 void html_printer::do_tab_te (void)
3103 if (table) {
3104 current_paragraph->done_para();
3105 current_paragraph->remove_para_space();
3106 table->emit_finish_table();
3109 table = NULL;
3110 restore_troff_indent();
3114 * do_tab - handle the "devtag:tab" tag
3117 void html_printer::do_tab (char *s)
3119 if (table) {
3120 while (isspace(*s))
3121 s++;
3122 s++;
3123 int col = table->find_column(atoi(s) + pageoffset + get_troff_indent());
3124 if (col > 0) {
3125 current_paragraph->done_para();
3126 table->emit_col(col);
3132 * do_tab0 - handle the "devtag:tab0" tag
3135 void html_printer::do_tab0 (void)
3137 if (table) {
3138 int col = table->find_column(pageoffset+get_troff_indent());
3139 if (col > 0) {
3140 current_paragraph->done_para();
3141 table->emit_col(col);
3147 * do_col - start column, s.
3150 void html_printer::do_col (char *s)
3152 if (table) {
3153 if (atoi(s) < current_column)
3154 row_space = seen_space;
3156 current_column = atoi(s);
3157 current_paragraph->done_para();
3158 table->emit_col(current_column);
3159 current_paragraph->do_para("", row_space);
3164 * troff_tag - processes the troff tag and manipulates the troff
3165 * state machine.
3168 void html_printer::troff_tag (text_glob *g)
3171 * firstly skip over devtag:
3173 char *t=(char *)g->text_string+strlen("devtag:");
3174 if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
3175 do_end_para(g);
3176 } else if (strncmp(g->text_string, "html<?p>:", strlen("html<?p>:")) == 0) {
3177 if (current_paragraph->emitted_text())
3178 html.put_string(g->text_string+9);
3179 else
3180 do_end_para(g);
3181 } else if (strncmp(g->text_string, "math<?p>:", strlen("math<?p>:")) == 0) {
3182 do_math(g);
3183 } else if (g->is_eol()) {
3184 do_eol();
3185 } else if (g->is_eol_ce()) {
3186 do_eol_ce();
3187 } else if (strncmp(t, ".sp", 3) == 0) {
3188 char *a = (char *)t+3;
3189 do_space(a);
3190 } else if (strncmp(t, ".br", 3) == 0) {
3191 seen_break = 1;
3192 as.check_br(1);
3193 do_break();
3194 } else if (strcmp(t, ".centered-image") == 0) {
3195 do_centered_image();
3196 } else if (strcmp(t, ".right-image") == 0) {
3197 do_right_image();
3198 } else if (strcmp(t, ".left-image") == 0) {
3199 do_left_image();
3200 } else if (strncmp(t, ".auto-image", 11) == 0) {
3201 char *a = (char *)t+11;
3202 do_auto_image(g, a);
3203 } else if (strncmp(t, ".ce", 3) == 0) {
3204 char *a = (char *)t+3;
3205 supress_sub_sup = true;
3206 do_center(a);
3207 } else if (g->is_tl()) {
3208 supress_sub_sup = true;
3209 title.with_h1 = true;
3210 do_title();
3211 } else if (strncmp(t, ".html-tl", 8) == 0) {
3212 supress_sub_sup = true;
3213 title.with_h1 = false;
3214 do_title();
3215 } else if (strncmp(t, ".fi", 3) == 0) {
3216 char *a = (char *)t+3;
3217 do_fill(a);
3218 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
3219 char *a = (char *)t+3;
3220 do_heading(a);
3221 } else if (strncmp(t, ".ll", 3) == 0) {
3222 char *a = (char *)t+3;
3223 do_linelength(a);
3224 } else if (strncmp(t, ".po", 3) == 0) {
3225 char *a = (char *)t+3;
3226 do_pageoffset(a);
3227 } else if (strncmp(t, ".in", 3) == 0) {
3228 char *a = (char *)t+3;
3229 do_indentation(a);
3230 } else if (strncmp(t, ".ti", 3) == 0) {
3231 char *a = (char *)t+3;
3232 do_tempindent(a);
3233 } else if (strncmp(t, ".vs", 3) == 0) {
3234 char *a = (char *)t+3;
3235 do_verticalspacing(a);
3236 } else if (strncmp(t, ".ps", 3) == 0) {
3237 char *a = (char *)t+3;
3238 do_pointsize(a);
3239 } else if (strcmp(t, ".links") == 0) {
3240 do_links();
3241 } else if (strncmp(t, ".job-name", 9) == 0) {
3242 char *a = (char *)t+9;
3243 do_job_name(a);
3244 } else if (strncmp(t, ".head", 5) == 0) {
3245 char *a = (char *)t+5;
3246 do_head(a);
3247 } else if (strcmp(t, ".no-auto-rule") == 0) {
3248 auto_rule = false;
3249 } else if (strcmp(t, ".tab-ts") == 0) {
3250 do_tab_ts(g);
3251 } else if (strcmp(t, ".tab-te") == 0) {
3252 do_tab_te();
3253 } else if (strncmp(t, ".col ", 5) == 0) {
3254 char *a = (char *)t+4;
3255 do_col(a);
3256 } else if (strncmp(t, "tab ", 4) == 0) {
3257 char *a = (char *)t+3;
3258 do_tab(a);
3259 } else if (strncmp(t, "tab0", 4) == 0) {
3260 do_tab0();
3265 * do_math - prints out the equation
3268 void html_printer::do_math (text_glob *g)
3270 do_font(g);
3271 if (current_paragraph->emitted_text())
3272 html.put_string(g->text_string+9);
3273 else
3274 do_end_para(g);
3278 * is_in_middle - returns true if the positions left..right are in the center of the page.
3281 int html_printer::is_in_middle (int left, int right)
3283 return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
3284 <= CENTER_TOLERANCE );
3288 * flush_globs - runs through the text glob list and emits html.
3291 void html_printer::flush_globs (void)
3293 text_glob *g;
3295 if (! page_contents->glyphs.is_empty()) {
3296 page_contents->glyphs.start_from_head();
3297 do {
3298 g = page_contents->glyphs.get_data();
3299 #if 0
3300 fprintf(stderr, "[%s:%d:%d:%d:%d]",
3301 g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
3302 fflush(stderr);
3303 #endif
3305 handle_state_assertion(g);
3307 if (strcmp(g->text_string, "XXXXXXX") == 0)
3308 stop();
3310 if (g->is_a_tag())
3311 troff_tag(g);
3312 else if (g->is_a_line())
3313 emit_line(g);
3314 else {
3315 as.check_sp(seen_space);
3316 as.check_br(seen_break);
3317 seen_break = 0;
3318 seen_space = 0;
3319 emit_html(g);
3322 as.check_fi(fill_on);
3323 as.check_ce(end_center);
3325 * after processing the title (and removing it) the glyph list might be empty
3327 if (! page_contents->glyphs.is_empty()) {
3328 page_contents->glyphs.move_right();
3330 } while (! page_contents->glyphs.is_equal_to_head());
3335 * calc_nf - calculates the _no_ format flag, given the
3336 * text glob, g.
3339 int html_printer::calc_nf (text_glob *g, int nf)
3341 if (g != NULL) {
3342 if (g->is_fi()) {
3343 as.check_fi(true);
3344 return false;
3346 if (g->is_nf()) {
3347 as.check_fi(false);
3348 return true;
3351 as.check_fi(! nf);
3352 return nf;
3356 * calc_po_in - calculates the, in, po, registers
3359 void html_printer::calc_po_in (text_glob *g, int nf)
3361 if (g->is_in())
3362 troff_indent = g->get_arg();
3363 else if (g->is_po())
3364 pageoffset = g->get_arg();
3365 else if (g->is_ti()) {
3366 temp_indent = g->get_arg();
3367 end_tempindent = 2;
3368 } else if (g->is_br() || (nf && g->is_eol())) {
3369 if (end_tempindent > 0)
3370 end_tempindent--;
3375 * next_horiz_pos - returns the next horiz position.
3376 * -1 is returned if it doesn't exist.
3379 int html_printer::next_horiz_pos (text_glob *g, int nf)
3381 int next = -1;
3383 if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
3384 if (! page_contents->glyphs.is_empty()) {
3385 page_contents->glyphs.move_right_get_data();
3386 if (g == NULL) {
3387 page_contents->glyphs.start_from_head();
3388 as.reset();
3390 else {
3391 next = g->minh;
3392 page_contents->glyphs.move_left();
3395 return next;
3399 * insert_tab_ts - inserts a tab-ts before, where.
3402 text_glob *html_printer::insert_tab_ts (text_glob *where)
3404 text_glob *start_of_table;
3405 text_glob *old_pos = page_contents->glyphs.get_data();
3407 page_contents->glyphs.move_to(where);
3408 page_contents->glyphs.move_left();
3409 page_contents->insert_tag(string("devtag:.tab-ts")); // tab table start
3410 page_contents->glyphs.move_right();
3411 start_of_table = page_contents->glyphs.get_data();
3412 page_contents->glyphs.move_to(old_pos);
3413 return start_of_table;
3417 * insert_tab_te - inserts a tab-te before the current position
3418 * (it skips backwards over .sp/.br)
3421 void html_printer::insert_tab_te (void)
3423 text_glob *g = page_contents->glyphs.get_data();
3424 page_contents->dump_page();
3426 while (page_contents->glyphs.get_data()->is_a_tag())
3427 page_contents->glyphs.move_left();
3429 page_contents->insert_tag(string("devtag:.tab-te")); // tab table end
3430 while (g != page_contents->glyphs.get_data())
3431 page_contents->glyphs.move_right();
3432 page_contents->dump_page();
3436 * insert_tab_0 - inserts a tab0 before, where.
3439 void html_printer::insert_tab_0 (text_glob *where)
3441 text_glob *old_pos = page_contents->glyphs.get_data();
3443 page_contents->glyphs.move_to(where);
3444 page_contents->glyphs.move_left();
3445 page_contents->insert_tag(string("devtag:tab0")); // tab0 start of line
3446 page_contents->glyphs.move_right();
3447 page_contents->glyphs.move_to(old_pos);
3451 * remove_tabs - removes the tabs tags on this line.
3454 void html_printer::remove_tabs (void)
3456 text_glob *orig = page_contents->glyphs.get_data();
3457 text_glob *g;
3459 if (! page_contents->glyphs.is_equal_to_tail()) {
3460 do {
3461 g = page_contents->glyphs.get_data();
3462 if (g->is_tab()) {
3463 page_contents->glyphs.sub_move_right();
3464 if (g == orig)
3465 orig = page_contents->glyphs.get_data();
3466 } else
3467 page_contents->glyphs.move_right();
3468 } while ((! page_contents->glyphs.is_equal_to_head()) &&
3469 (! g->is_eol()));
3472 * now restore our previous position.
3474 while (page_contents->glyphs.get_data() != orig)
3475 page_contents->glyphs.move_left();
3479 void html_printer::remove_courier_tabs (void)
3481 text_glob *g;
3482 int line_start = true;
3483 int nf = false;
3485 if (! page_contents->glyphs.is_empty()) {
3486 page_contents->glyphs.start_from_head();
3487 as.reset();
3488 line_start = true;
3489 do {
3490 g = page_contents->glyphs.get_data();
3491 handle_state_assertion(g);
3492 nf = calc_nf(g, nf);
3494 if (line_start) {
3495 if (line_start && nf && is_courier_until_eol()) {
3496 remove_tabs();
3497 g = page_contents->glyphs.get_data();
3501 // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
3502 line_start = g->is_br() || (nf && g->is_eol());
3503 page_contents->glyphs.move_right();
3504 } while (! page_contents->glyphs.is_equal_to_head());
3508 void html_printer::insert_tab0_foreach_tab (void)
3510 text_glob *start_of_line = NULL;
3511 text_glob *g = NULL;
3512 int seen_tab = false;
3513 int seen_col = false;
3514 int nf = false;
3516 if (! page_contents->glyphs.is_empty()) {
3517 page_contents->glyphs.start_from_head();
3518 as.reset();
3519 start_of_line = page_contents->glyphs.get_data();
3520 do {
3521 g = page_contents->glyphs.get_data();
3522 handle_state_assertion(g);
3523 nf = calc_nf(g, nf);
3525 if (g->is_tab())
3526 seen_tab = true;
3528 if (g->is_col())
3529 seen_col = true;
3531 if (g->is_br() || (nf && g->is_eol())) {
3532 do {
3533 page_contents->glyphs.move_right();
3534 g = page_contents->glyphs.get_data();
3535 handle_state_assertion(g);
3536 nf = calc_nf(g, nf);
3537 if (page_contents->glyphs.is_equal_to_head()) {
3538 if (seen_tab && !seen_col)
3539 insert_tab_0(start_of_line);
3540 return;
3542 } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
3543 // printf("\nstart_of_line is: %s\n", g->text_string);
3544 if (seen_tab && !seen_col) {
3545 insert_tab_0(start_of_line);
3546 page_contents->glyphs.move_to(g);
3549 seen_tab = false;
3550 seen_col = false;
3551 start_of_line = g;
3553 page_contents->glyphs.move_right();
3554 } while (! page_contents->glyphs.is_equal_to_head());
3555 if (seen_tab && !seen_col)
3556 insert_tab_0(start_of_line);
3562 * update_min_max - updates the extent of a column, given the left and right
3563 * extents of a glyph, g.
3566 void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
3568 switch (type_of_col) {
3570 case tab_tag:
3571 break;
3572 case tab0_tag:
3573 *minimum = g->minh;
3574 break;
3575 case col_tag:
3576 *minimum = g->minh;
3577 *maximum = g->maxh;
3578 break;
3579 default:
3580 break;
3585 * add_table_end - moves left one glyph, adds a table end tag and adds a
3586 * debugging string.
3589 void html_printer::add_table_end (const char *
3590 #if defined(DEBUG_TABLES)
3591 debug_string
3592 #endif
3595 page_contents->glyphs.move_left();
3596 insert_tab_te();
3597 #if defined(DEBUG_TABLES)
3598 page_contents->insert_tag(string(debug_string));
3599 #endif
3603 * lookahead_for_tables - checks for .col tags and inserts table
3604 * start/end tags
3607 void html_printer::lookahead_for_tables (void)
3609 text_glob *g;
3610 text_glob *start_of_line = NULL;
3611 text_glob *start_of_table = NULL;
3612 text_glob *last = NULL;
3613 colType type_of_col = none;
3614 int left = 0;
3615 int found_col = false;
3616 int seen_text = false;
3617 int ncol = 0;
3618 int colmin = 0; // pacify compiler
3619 int colmax = 0; // pacify compiler
3620 html_table *tbl = new html_table(&html, -1);
3621 const char *tab_defs = NULL;
3622 char align = 'L';
3623 int nf = false;
3624 int old_pageoffset = pageoffset;
3626 remove_courier_tabs();
3627 page_contents->dump_page();
3628 insert_tab0_foreach_tab();
3629 page_contents->dump_page();
3630 if (! page_contents->glyphs.is_empty()) {
3631 page_contents->glyphs.start_from_head();
3632 as.reset();
3633 g = page_contents->glyphs.get_data();
3634 if (g->is_br()) {
3635 g = page_contents->glyphs.move_right_get_data();
3636 handle_state_assertion(g);
3637 if (page_contents->glyphs.is_equal_to_head()) {
3638 if (tbl != NULL) {
3639 delete tbl;
3640 tbl = NULL;
3642 return;
3645 start_of_line = g;
3646 seen_text = false;
3647 ncol = 0;
3648 left = next_horiz_pos(g, nf);
3649 if (found_col)
3650 last = g;
3651 found_col = false;
3654 do {
3655 #if defined(DEBUG_TABLES)
3656 fprintf(stderr, " [") ;
3657 fprintf(stderr, g->text_string) ;
3658 fprintf(stderr, "] ") ;
3659 fflush(stderr);
3660 if (strcmp(g->text_string, "XXXXXXX") == 0)
3661 stop();
3662 #endif
3664 nf = calc_nf(g, nf);
3665 calc_po_in(g, nf);
3666 if (g->is_col()) {
3667 if (type_of_col == tab_tag && start_of_table != NULL) {
3668 page_contents->glyphs.move_left();
3669 insert_tab_te();
3670 start_of_table->remember_table(tbl);
3671 tbl = new html_table(&html, -1);
3672 page_contents->insert_tag(string("*** TAB -> COL ***"));
3673 if (tab_defs != NULL)
3674 tbl->tab_stops->init(tab_defs);
3675 start_of_table = NULL;
3676 last = NULL;
3678 type_of_col = col_tag;
3679 found_col = true;
3680 ncol = g->get_arg();
3681 align = 'L';
3682 colmin = 0;
3683 colmax = 0;
3684 } else if (g->is_tab()) {
3685 type_of_col = tab_tag;
3686 colmin = g->get_tab_args(&align);
3687 align = 'L'; // for now as 'C' and 'R' are broken
3688 ncol = tbl->find_tab_column(colmin);
3689 colmin += pageoffset + get_troff_indent();
3690 colmax = tbl->get_tab_pos(ncol+1);
3691 if (colmax > 0)
3692 colmax += pageoffset + get_troff_indent();
3693 } else if (g->is_tab0()) {
3694 if (type_of_col == col_tag && start_of_table != NULL) {
3695 page_contents->glyphs.move_left();
3696 insert_tab_te();
3697 start_of_table->remember_table(tbl);
3698 tbl = new html_table(&html, -1);
3699 page_contents->insert_tag(string("*** COL -> TAB ***"));
3700 start_of_table = NULL;
3701 last = NULL;
3703 if (tab_defs != NULL)
3704 tbl->tab_stops->init(tab_defs);
3706 type_of_col = tab0_tag;
3707 ncol = 1;
3708 colmin = 0;
3709 colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
3710 } else if (! g->is_a_tag())
3711 update_min_max(type_of_col, &colmin, &colmax, g);
3713 if ((! g->is_a_tag()) || g->is_tab())
3714 seen_text = true;
3716 if ((g->is_col() || g->is_tab() || g->is_tab0())
3717 && (start_of_line != NULL) && (start_of_table == NULL)) {
3718 start_of_table = insert_tab_ts(start_of_line);
3719 start_of_line = NULL;
3720 seen_text = false;
3721 } else if (g->is_ce() && (start_of_table != NULL)) {
3722 add_table_end("*** CE ***");
3723 start_of_table->remember_table(tbl);
3724 tbl = new html_table(&html, -1);
3725 start_of_table = NULL;
3726 last = NULL;
3727 } else if (g->is_ta()) {
3728 tab_defs = g->text_string;
3730 if (type_of_col == col_tag)
3731 tbl->tab_stops->check_init(tab_defs);
3733 if (!tbl->tab_stops->compatible(tab_defs)) {
3734 if (start_of_table != NULL) {
3735 add_table_end("*** TABS ***");
3736 start_of_table->remember_table(tbl);
3737 tbl = new html_table(&html, -1);
3738 start_of_table = NULL;
3739 type_of_col = none;
3740 last = NULL;
3742 tbl->tab_stops->init(tab_defs);
3746 if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
3747 // we are in a table and have a glyph
3748 if ((ncol == 0) || (! tbl->add_column(ncol, colmin, colmax, align))) {
3749 if (ncol == 0)
3750 add_table_end("*** NCOL == 0 ***");
3751 else
3752 add_table_end("*** CROSSED COLS ***");
3754 start_of_table->remember_table(tbl);
3755 tbl = new html_table(&html, -1);
3756 start_of_table = NULL;
3757 type_of_col = none;
3758 last = NULL;
3763 * move onto next glob, check whether we are starting a new line
3765 g = page_contents->glyphs.move_right_get_data();
3766 handle_state_assertion(g);
3768 if (g == NULL) {
3769 if (found_col) {
3770 page_contents->glyphs.start_from_head();
3771 as.reset();
3772 last = g;
3773 found_col = false;
3775 } else if (g->is_br() || (nf && g->is_eol())) {
3776 do {
3777 g = page_contents->glyphs.move_right_get_data();
3778 handle_state_assertion(g);
3779 nf = calc_nf(g, nf);
3780 } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
3781 start_of_line = g;
3782 seen_text = false;
3783 ncol = 0;
3784 left = next_horiz_pos(g, nf);
3785 if (found_col)
3786 last = g;
3787 found_col = false;
3789 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3791 #if defined(DEBUG_TABLES)
3792 fprintf(stderr, "finished scanning for tables\n");
3793 #endif
3795 page_contents->glyphs.start_from_head();
3796 if (start_of_table != NULL) {
3797 if (last != NULL)
3798 while (last != page_contents->glyphs.get_data())
3799 page_contents->glyphs.move_left();
3801 insert_tab_te();
3802 start_of_table->remember_table(tbl);
3803 tbl = NULL;
3804 page_contents->insert_tag(string("*** LAST ***"));
3807 if (tbl != NULL) {
3808 delete tbl;
3809 tbl = NULL;
3812 // and reset the registers
3813 pageoffset = old_pageoffset;
3814 troff_indent = 0;
3815 temp_indent = 0;
3816 end_tempindent = 0;
3819 void html_printer::flush_page (void)
3821 supress_sub_sup = true;
3822 flush_sbuf();
3823 page_contents->dump_page();
3824 lookahead_for_tables();
3825 page_contents->dump_page();
3827 flush_globs();
3828 current_paragraph->done_para();
3829 current_paragraph->flush_text();
3831 // move onto a new page
3832 delete page_contents;
3833 #if defined(DEBUG_TABLES)
3834 fprintf(stderr, "\n\n*** flushed page ***\n\n");
3836 html.simple_comment("new page called");
3837 #endif
3838 page_contents = new page;
3842 * determine_space - works out whether we need to write a space.
3843 * If last glyph is ajoining then no space emitted.
3846 void html_printer::determine_space (text_glob *g)
3848 if (current_paragraph->is_in_pre()) {
3850 * .nf has been specified
3852 while (output_hpos < g->minh) {
3853 output_hpos += space_width;
3854 current_paragraph->emit_space();
3856 } else {
3857 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
3858 current_paragraph->emit_space();
3864 * is_line_start - returns true if we are at the start of a line.
3867 int html_printer::is_line_start (int nf)
3869 int line_start = false;
3870 int result = true;
3871 text_glob *orig = page_contents->glyphs.get_data();
3872 text_glob *g;
3874 if (! page_contents->glyphs.is_equal_to_head()) {
3875 do {
3876 page_contents->glyphs.move_left();
3877 g = page_contents->glyphs.get_data();
3878 result = g->is_a_tag();
3879 if (g->is_fi())
3880 nf = false;
3881 else if (g->is_nf())
3882 nf = true;
3883 line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
3884 } while ((!line_start) && (result));
3886 * now restore our previous position.
3888 while (page_contents->glyphs.get_data() != orig)
3889 page_contents->glyphs.move_right();
3891 return result;
3895 * is_font_courier - returns true if the font, f, is courier.
3898 int html_printer::is_font_courier (font *f)
3900 if (f != 0) {
3901 const char *fontname = f->get_name();
3903 return( (fontname != 0) && (fontname[0] == 'C') );
3905 return false;
3909 * end_font - shuts down the font corresponding to fontname.
3912 void html_printer::end_font (const char *fontname)
3914 if (strcmp(fontname, "B") == 0) {
3915 current_paragraph->done_bold();
3916 } else if (strcmp(fontname, "I") == 0) {
3917 current_paragraph->done_italic();
3918 } else if (strcmp(fontname, "BI") == 0) {
3919 current_paragraph->done_bold();
3920 current_paragraph->done_italic();
3921 } else if (strcmp(fontname, "CR") == 0) {
3922 current_paragraph->done_tt();
3923 } else if (strcmp(fontname, "CI") == 0) {
3924 current_paragraph->done_italic();
3925 current_paragraph->done_tt();
3926 } else if (strcmp(fontname, "CB") == 0) {
3927 current_paragraph->done_bold();
3928 current_paragraph->done_tt();
3929 } else if (strcmp(fontname, "CBI") == 0) {
3930 current_paragraph->done_bold();
3931 current_paragraph->done_italic();
3932 current_paragraph->done_tt();
3937 * start_font - starts the font corresponding to name.
3940 void html_printer::start_font (const char *fontname)
3942 if (strcmp(fontname, "R") == 0) {
3943 current_paragraph->done_bold();
3944 current_paragraph->done_italic();
3945 current_paragraph->done_tt();
3946 } else if (strcmp(fontname, "B") == 0) {
3947 current_paragraph->do_bold();
3948 } else if (strcmp(fontname, "I") == 0) {
3949 current_paragraph->do_italic();
3950 } else if (strcmp(fontname, "BI") == 0) {
3951 current_paragraph->do_bold();
3952 current_paragraph->do_italic();
3953 } else if (strcmp(fontname, "CR") == 0) {
3954 if ((! fill_on) && (is_courier_until_eol()) &&
3955 is_line_start(! fill_on)) {
3956 current_paragraph->do_pre();
3958 current_paragraph->do_tt();
3959 } else if (strcmp(fontname, "CI") == 0) {
3960 if ((! fill_on) && (is_courier_until_eol()) &&
3961 is_line_start(! fill_on)) {
3962 current_paragraph->do_pre();
3964 current_paragraph->do_tt();
3965 current_paragraph->do_italic();
3966 } else if (strcmp(fontname, "CB") == 0) {
3967 if ((! fill_on) && (is_courier_until_eol()) &&
3968 is_line_start(! fill_on)) {
3969 current_paragraph->do_pre();
3971 current_paragraph->do_tt();
3972 current_paragraph->do_bold();
3973 } else if (strcmp(fontname, "CBI") == 0) {
3974 if ((! fill_on) && (is_courier_until_eol()) &&
3975 is_line_start(! fill_on)) {
3976 current_paragraph->do_pre();
3978 current_paragraph->do_tt();
3979 current_paragraph->do_italic();
3980 current_paragraph->do_bold();
3985 * start_size - from is old font size, to is the new font size.
3986 * The html increase <big> and <small> decrease alters the
3987 * font size by 20%. We try and map these onto glyph sizes.
3990 void html_printer::start_size (int from, int to)
3992 if (from < to) {
3993 while (from < to) {
3994 current_paragraph->do_big();
3995 from += SIZE_INCREMENT;
3997 } else if (from > to) {
3998 while (from > to) {
3999 current_paragraph->do_small();
4000 from -= SIZE_INCREMENT;
4006 * do_font - checks to see whether we need to alter the html font.
4009 void html_printer::do_font (text_glob *g)
4012 * check if the output_style.point_size has not been set yet
4013 * this allow users to place .ps at the top of their troff files
4014 * and grohtml can then treat the .ps value as the base font size (3)
4016 if (output_style.point_size == -1) {
4017 output_style.point_size = pointsize;
4020 if (g->text_style.f != output_style.f) {
4021 if (output_style.f != 0) {
4022 end_font(output_style.f->get_name());
4024 output_style.f = g->text_style.f;
4025 if (output_style.f != 0) {
4026 start_font(output_style.f->get_name());
4029 if (output_style.point_size != g->text_style.point_size) {
4030 do_sup_or_sub(g);
4031 if ((output_style.point_size > 0) &&
4032 (g->text_style.point_size > 0)) {
4033 start_size(output_style.point_size, g->text_style.point_size);
4035 if (g->text_style.point_size > 0) {
4036 output_style.point_size = g->text_style.point_size;
4039 if (output_style.col != g->text_style.col) {
4040 current_paragraph->done_color();
4041 output_style.col = g->text_style.col;
4042 current_paragraph->do_color(&output_style.col);
4047 * start_subscript - returns true if, g, looks like a subscript start.
4050 int html_printer::start_subscript (text_glob *g)
4052 int r = font::res;
4053 int height = output_style.point_size*r/72;
4055 return( (output_style.point_size != 0) &&
4056 (output_vpos < g->minv) &&
4057 (output_vpos-height > g->maxv) &&
4058 (output_style.point_size > g->text_style.point_size) );
4062 * start_superscript - returns true if, g, looks like a superscript start.
4065 int html_printer::start_superscript (text_glob *g)
4067 int r = font::res;
4068 int height = output_style.point_size*r/72;
4070 return( (output_style.point_size != 0) &&
4071 (output_vpos > g->minv) &&
4072 (output_vpos-height < g->maxv) &&
4073 (output_style.point_size > g->text_style.point_size) );
4077 * end_subscript - returns true if, g, looks like the end of a subscript.
4080 int html_printer::end_subscript (text_glob *g)
4082 int r = font::res;
4083 int height = output_style.point_size*r/72;
4085 return( (output_style.point_size != 0) &&
4086 (g->minv < output_vpos) &&
4087 (output_vpos-height > g->maxv) &&
4088 (output_style.point_size < g->text_style.point_size) );
4092 * end_superscript - returns true if, g, looks like the end of a superscript.
4095 int html_printer::end_superscript (text_glob *g)
4097 int r = font::res;
4098 int height = output_style.point_size*r/72;
4100 return( (output_style.point_size != 0) &&
4101 (g->minv > output_vpos) &&
4102 (output_vpos-height < g->maxv) &&
4103 (output_style.point_size < g->text_style.point_size) );
4107 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4108 * start/end and it calls the services of html-text to issue the
4109 * appropriate tags.
4112 void html_printer::do_sup_or_sub (text_glob *g)
4114 if (! supress_sub_sup) {
4115 if (start_subscript(g)) {
4116 current_paragraph->do_sub();
4117 } else if (start_superscript(g)) {
4118 current_paragraph->do_sup();
4119 } else if (end_subscript(g)) {
4120 current_paragraph->done_sub();
4121 } else if (end_superscript(g)) {
4122 current_paragraph->done_sup();
4128 * do_end_para - writes out the html text after shutting down the
4129 * current paragraph.
4132 void html_printer::do_end_para (text_glob *g)
4134 do_font(g);
4135 current_paragraph->done_para();
4136 current_paragraph->remove_para_space();
4137 html.put_string(g->text_string+9);
4138 output_vpos = g->minv;
4139 output_hpos = g->maxh;
4140 output_vpos_max = g->maxv;
4141 supress_sub_sup = false;
4145 * emit_html - write out the html text
4148 void html_printer::emit_html (text_glob *g)
4150 do_font(g);
4151 determine_space(g);
4152 current_paragraph->do_emittext(g->text_string, g->text_length);
4153 output_vpos = g->minv;
4154 output_hpos = g->maxh;
4155 output_vpos_max = g->maxv;
4156 supress_sub_sup = false;
4160 * flush_sbuf - flushes the current sbuf into the list of glyphs.
4163 void html_printer::flush_sbuf()
4165 if (sbuf.length() > 0) {
4166 int r=font::res; // resolution of the device
4167 set_style(sbuf_style);
4169 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
4170 font *bold_font = make_bold(sbuf_style.f);
4171 if (bold_font != NULL)
4172 sbuf_style.f = bold_font;
4175 page_contents->add(&sbuf_style, sbuf,
4176 line_number,
4177 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4178 sbuf_vpos , sbuf_end_hpos);
4180 output_hpos = sbuf_end_hpos;
4181 output_vpos = sbuf_vpos;
4182 last_sbuf_length = 0;
4183 sbuf_prev_hpos = sbuf_end_hpos;
4184 overstrike_detected = false;
4185 sbuf.clear();
4189 void html_printer::set_line_thickness(const environment *env)
4191 line_thickness = env->size;
4194 void html_printer::draw(int code, int *p, int np, const environment *env)
4196 switch (code) {
4198 case 'l':
4199 # if 0
4200 if (np == 2) {
4201 page_contents->add_line(&sbuf_style,
4202 line_number,
4203 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
4204 } else {
4205 error("2 arguments required for line");
4207 # endif
4208 break;
4209 case 't':
4211 if (np == 0) {
4212 line_thickness = -1;
4213 } else {
4214 // troff gratuitously adds an extra 0
4215 if (np != 1 && np != 2) {
4216 error("0 or 1 argument required for thickness");
4217 break;
4219 line_thickness = p[0];
4221 break;
4224 case 'P':
4225 break;
4226 case 'p':
4227 break;
4228 case 'E':
4229 break;
4230 case 'e':
4231 break;
4232 case 'C':
4233 break;
4234 case 'c':
4235 break;
4236 case 'a':
4237 break;
4238 case '~':
4239 break;
4240 case 'f':
4241 break;
4242 case 'F':
4243 // fill with color env->fill
4244 if (background != NULL)
4245 delete background;
4246 background = new color;
4247 *background = *env->fill;
4248 break;
4250 default:
4251 error("unrecognised drawing command `%1'", char(code));
4252 break;
4256 html_printer::html_printer()
4257 : html(0, MAX_LINE_LENGTH),
4258 no_of_printed_pages(0),
4259 last_sbuf_length(0),
4260 overstrike_detected(false),
4261 output_hpos(-1),
4262 output_vpos(-1),
4263 output_vpos_max(-1),
4264 line_thickness(-1),
4265 inside_font_style(0),
4266 page_number(0),
4267 header_indent(-1),
4268 supress_sub_sup(true),
4269 cutoff_heading(100),
4270 indent(NULL),
4271 table(NULL),
4272 end_center(0),
4273 end_tempindent(0),
4274 next_tag(INLINE),
4275 fill_on(true),
4276 max_linelength(-1),
4277 linelength(0),
4278 pageoffset(0),
4279 troff_indent(0),
4280 device_indent(0),
4281 temp_indent(0),
4282 pointsize(base_point_size),
4283 line_number(0),
4284 background(default_background),
4285 seen_indent(false),
4286 next_indent(0),
4287 seen_pageoffset(false),
4288 next_pageoffset(0),
4289 seen_linelength(false),
4290 next_linelength(0),
4291 seen_center(false),
4292 next_center(0),
4293 seen_space(0),
4294 seen_break(0),
4295 current_column(0),
4296 row_space(false)
4298 file_list.add_new_file(xtmpfile());
4299 html.set_file(file_list.get_file());
4300 if (font::hor != 24)
4301 fatal("horizontal resolution must be 24");
4302 if (font::vert != 40)
4303 fatal("vertical resolution must be 40");
4304 #if 0
4305 // should be sorted html..
4306 if (font::res % (font::sizescale*72) != 0)
4307 fatal("res must be a multiple of 72*sizescale");
4308 #endif
4309 int r = font::res;
4310 int point = 0;
4311 while (r % 10 == 0) {
4312 r /= 10;
4313 point++;
4315 res = r;
4316 html.set_fixed_point(point);
4317 space_glyph = name_to_glyph("space");
4318 space_width = font::hor;
4319 paper_length = font::paperlength;
4320 linelength = font::res*13/2;
4321 if (paper_length == 0)
4322 paper_length = 11*font::res;
4324 page_contents = new page();
4328 * add_to_sbuf - adds character code or name to the sbuf.
4331 void html_printer::add_to_sbuf (glyph *g, const string &s)
4333 if (sbuf_style.f == NULL)
4334 return;
4336 const char *html_glyph = NULL;
4337 unsigned int code = sbuf_style.f->get_code(g);
4339 if (s.empty()) {
4340 if (sbuf_style.f->contains(g))
4341 html_glyph = get_html_entity(sbuf_style.f->get_code(g));
4342 else
4343 html_glyph = NULL;
4345 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
4346 html_glyph = to_unicode(code);
4347 } else
4348 html_glyph = get_html_translation(sbuf_style.f, s);
4350 last_sbuf_length = sbuf.length();
4351 if (html_glyph == NULL)
4352 sbuf += ((char)code);
4353 else
4354 sbuf += html_glyph;
4357 int html_printer::sbuf_continuation (glyph *g, const char *name,
4358 const environment *env, int w)
4361 * lets see whether the glyph is closer to the end of sbuf
4363 if ((sbuf_end_hpos == env->hpos)
4364 || ((sbuf_prev_hpos < sbuf_end_hpos)
4365 && (env->hpos < sbuf_end_hpos)
4366 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
4367 add_to_sbuf(g, name);
4368 sbuf_prev_hpos = sbuf_end_hpos;
4369 sbuf_end_hpos += w + sbuf_kern;
4370 return true;
4371 } else {
4372 if ((env->hpos >= sbuf_end_hpos) &&
4373 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
4375 * lets see whether a space is needed or not
4378 if (env->hpos-sbuf_end_hpos < space_width) {
4379 add_to_sbuf(g, name);
4380 sbuf_prev_hpos = sbuf_end_hpos;
4381 sbuf_end_hpos = env->hpos + w;
4382 return true;
4386 return false ;
4390 * get_html_translation - given the position of the character and its name
4391 * return the device encoding for such character.
4394 const char *get_html_translation (font *f, const string &name)
4396 if ((f == 0) || name.empty())
4397 return NULL;
4398 else {
4399 glyph *g = name_to_glyph((char *)(name + '\0').contents());
4400 if (f->contains(g))
4401 return get_html_entity(f->get_code(g));
4402 else
4403 return NULL;
4408 * get_html_entity - given a Unicode character's code point, return a
4409 * HTML entity that represents the character, if the
4410 * character cannot represent itself in all contexts.
4411 * The return value, if non-NULL, is allocated in a static buffer and is
4412 * only valid until the next call of this function.
4414 static const char *get_html_entity (unsigned int code)
4416 if (code < UNICODE_DESC_START) {
4417 switch (code) {
4418 case 0x0022: return "&quot;";
4419 case 0x0026: return "&amp;";
4420 case 0x003C: return "&lt;";
4421 case 0x003E: return "&gt;";
4422 default: return NULL;
4424 } else {
4425 switch (code) {
4426 case 0x00A0: return "&nbsp;";
4427 case 0x00A1: return "&iexcl;";
4428 case 0x00A2: return "&cent;";
4429 case 0x00A3: return "&pound;";
4430 case 0x00A4: return "&curren;";
4431 case 0x00A5: return "&yen;";
4432 case 0x00A6: return "&brvbar;";
4433 case 0x00A7: return "&sect;";
4434 case 0x00A8: return "&uml;";
4435 case 0x00A9: return "&copy;";
4436 case 0x00AA: return "&ordf;";
4437 case 0x00AB: return "&laquo;";
4438 case 0x00AC: return "&not;";
4439 case 0x00AE: return "&reg;";
4440 case 0x00AF: return "&macr;";
4441 case 0x00B0: return "&deg;";
4442 case 0x00B1: return "&plusmn;";
4443 case 0x00B2: return "&sup2;";
4444 case 0x00B3: return "&sup3;";
4445 case 0x00B4: return "&acute;";
4446 case 0x00B5: return "&micro;";
4447 case 0x00B6: return "&para;";
4448 case 0x00B7: return "&middot;";
4449 case 0x00B8: return "&cedil;";
4450 case 0x00B9: return "&sup1;";
4451 case 0x00BA: return "&ordm;";
4452 case 0x00BB: return "&raquo;";
4453 case 0x00BC: return "&frac14;";
4454 case 0x00BD: return "&frac12;";
4455 case 0x00BE: return "&frac34;";
4456 case 0x00BF: return "&iquest;";
4457 case 0x00C0: return "&Agrave;";
4458 case 0x00C1: return "&Aacute;";
4459 case 0x00C2: return "&Acirc;";
4460 case 0x00C3: return "&Atilde;";
4461 case 0x00C4: return "&Auml;";
4462 case 0x00C5: return "&Aring;";
4463 case 0x00C6: return "&AElig;";
4464 case 0x00C7: return "&Ccedil;";
4465 case 0x00C8: return "&Egrave;";
4466 case 0x00C9: return "&Eacute;";
4467 case 0x00CA: return "&Ecirc;";
4468 case 0x00CB: return "&Euml;";
4469 case 0x00CC: return "&Igrave;";
4470 case 0x00CD: return "&Iacute;";
4471 case 0x00CE: return "&Icirc;";
4472 case 0x00CF: return "&Iuml;";
4473 case 0x00D0: return "&ETH;";
4474 case 0x00D1: return "&Ntilde;";
4475 case 0x00D2: return "&Ograve;";
4476 case 0x00D3: return "&Oacute;";
4477 case 0x00D4: return "&Ocirc;";
4478 case 0x00D5: return "&Otilde;";
4479 case 0x00D6: return "&Ouml;";
4480 case 0x00D7: return "&times;";
4481 case 0x00D8: return "&Oslash;";
4482 case 0x00D9: return "&Ugrave;";
4483 case 0x00DA: return "&Uacute;";
4484 case 0x00DB: return "&Ucirc;";
4485 case 0x00DC: return "&Uuml;";
4486 case 0x00DD: return "&Yacute;";
4487 case 0x00DE: return "&THORN;";
4488 case 0x00DF: return "&szlig;";
4489 case 0x00E0: return "&agrave;";
4490 case 0x00E1: return "&aacute;";
4491 case 0x00E2: return "&acirc;";
4492 case 0x00E3: return "&atilde;";
4493 case 0x00E4: return "&auml;";
4494 case 0x00E5: return "&aring;";
4495 case 0x00E6: return "&aelig;";
4496 case 0x00E7: return "&ccedil;";
4497 case 0x00E8: return "&egrave;";
4498 case 0x00E9: return "&eacute;";
4499 case 0x00EA: return "&ecirc;";
4500 case 0x00EB: return "&euml;";
4501 case 0x00EC: return "&igrave;";
4502 case 0x00ED: return "&iacute;";
4503 case 0x00EE: return "&icirc;";
4504 case 0x00EF: return "&iuml;";
4505 case 0x00F0: return "&eth;";
4506 case 0x00F1: return "&ntilde;";
4507 case 0x00F2: return "&ograve;";
4508 case 0x00F3: return "&oacute;";
4509 case 0x00F4: return "&ocirc;";
4510 case 0x00F5: return "&otilde;";
4511 case 0x00F6: return "&ouml;";
4512 case 0x00F7: return "&divide;";
4513 case 0x00F8: return "&oslash;";
4514 case 0x00F9: return "&ugrave;";
4515 case 0x00FA: return "&uacute;";
4516 case 0x00FB: return "&ucirc;";
4517 case 0x00FC: return "&uuml;";
4518 case 0x00FD: return "&yacute;";
4519 case 0x00FE: return "&thorn;";
4520 case 0x00FF: return "&yuml;";
4521 case 0x0152: return "&OElig;";
4522 case 0x0153: return "&oelig;";
4523 case 0x0160: return "&Scaron;";
4524 case 0x0161: return "&scaron;";
4525 case 0x0178: return "&Yuml;";
4526 case 0x0192: return "&fnof;";
4527 case 0x0391: return "&Alpha;";
4528 case 0x0392: return "&Beta;";
4529 case 0x0393: return "&Gamma;";
4530 case 0x0394: return "&Delta;";
4531 case 0x0395: return "&Epsilon;";
4532 case 0x0396: return "&Zeta;";
4533 case 0x0397: return "&Eta;";
4534 case 0x0398: return "&Theta;";
4535 case 0x0399: return "&Iota;";
4536 case 0x039A: return "&Kappa;";
4537 case 0x039B: return "&Lambda;";
4538 case 0x039C: return "&Mu;";
4539 case 0x039D: return "&Nu;";
4540 case 0x039E: return "&Xi;";
4541 case 0x039F: return "&Omicron;";
4542 case 0x03A0: return "&Pi;";
4543 case 0x03A1: return "&Rho;";
4544 case 0x03A3: return "&Sigma;";
4545 case 0x03A4: return "&Tau;";
4546 case 0x03A5: return "&Upsilon;";
4547 case 0x03A6: return "&Phi;";
4548 case 0x03A7: return "&Chi;";
4549 case 0x03A8: return "&Psi;";
4550 case 0x03A9: return "&Omega;";
4551 case 0x03B1: return "&alpha;";
4552 case 0x03B2: return "&beta;";
4553 case 0x03B3: return "&gamma;";
4554 case 0x03B4: return "&delta;";
4555 case 0x03B5: return "&epsilon;";
4556 case 0x03B6: return "&zeta;";
4557 case 0x03B7: return "&eta;";
4558 case 0x03B8: return "&theta;";
4559 case 0x03B9: return "&iota;";
4560 case 0x03BA: return "&kappa;";
4561 case 0x03BB: return "&lambda;";
4562 case 0x03BC: return "&mu;";
4563 case 0x03BD: return "&nu;";
4564 case 0x03BE: return "&xi;";
4565 case 0x03BF: return "&omicron;";
4566 case 0x03C0: return "&pi;";
4567 case 0x03C1: return "&rho;";
4568 case 0x03C2: return "&sigmaf;";
4569 case 0x03C3: return "&sigma;";
4570 case 0x03C4: return "&tau;";
4571 case 0x03C5: return "&upsilon;";
4572 case 0x03C6: return "&phi;";
4573 case 0x03C7: return "&chi;";
4574 case 0x03C8: return "&psi;";
4575 case 0x03C9: return "&omega;";
4576 case 0x03D1: return "&thetasym;";
4577 case 0x03D6: return "&piv;";
4578 case 0x2013: return "&ndash;";
4579 case 0x2014: return "&mdash;";
4580 case 0x2018: return "&lsquo;";
4581 case 0x2019: return "&rsquo;";
4582 case 0x201A: return "&sbquo;";
4583 case 0x201C: return "&ldquo;";
4584 case 0x201D: return "&rdquo;";
4585 case 0x201E: return "&bdquo;";
4586 case 0x2020: return "&dagger;";
4587 case 0x2021: return "&Dagger;";
4588 case 0x2022: return "&bull;";
4589 case 0x2030: return "&permil;";
4590 case 0x2032: return "&prime;";
4591 case 0x2033: return "&Prime;";
4592 case 0x2039: return "&lsaquo;";
4593 case 0x203A: return "&rsaquo;";
4594 case 0x203E: return "&oline;";
4595 case 0x2044: return "&frasl;";
4596 case 0x20AC: return "&euro;";
4597 case 0x2111: return "&image;";
4598 case 0x2118: return "&weierp;";
4599 case 0x211C: return "&real;";
4600 case 0x2122: return "&trade;";
4601 case 0x2135: return "&alefsym;";
4602 case 0x2190: return "&larr;";
4603 case 0x2191: return "&uarr;";
4604 case 0x2192: return "&rarr;";
4605 case 0x2193: return "&darr;";
4606 case 0x2194: return "&harr;";
4607 case 0x21D0: return "&lArr;";
4608 case 0x21D1: return "&uArr;";
4609 case 0x21D2: return "&rArr;";
4610 case 0x21D3: return "&dArr;";
4611 case 0x21D4: return "&hArr;";
4612 case 0x2200: return "&forall;";
4613 case 0x2202: return "&part;";
4614 case 0x2203: return "&exist;";
4615 case 0x2205: return "&empty;";
4616 case 0x2207: return "&nabla;";
4617 case 0x2208: return "&isin;";
4618 case 0x2209: return "&notin;";
4619 case 0x220B: return "&ni;";
4620 case 0x220F: return "&prod;";
4621 case 0x2211: return "&sum;";
4622 case 0x2212: return "&minus;";
4623 case 0x2217: return "&lowast;";
4624 case 0x221A: return "&radic;";
4625 case 0x221D: return "&prop;";
4626 case 0x221E: return "&infin;";
4627 case 0x2220: return "&ang;";
4628 case 0x2227: return "&and;";
4629 case 0x2228: return "&or;";
4630 case 0x2229: return "&cap;";
4631 case 0x222A: return "&cup;";
4632 case 0x222B: return "&int;";
4633 case 0x2234: return "&there4;";
4634 case 0x223C: return "&sim;";
4635 case 0x2245: return "&cong;";
4636 case 0x2248: return "&asymp;";
4637 case 0x2260: return "&ne;";
4638 case 0x2261: return "&equiv;";
4639 case 0x2264: return "&le;";
4640 case 0x2265: return "&ge;";
4641 case 0x2282: return "&sub;";
4642 case 0x2283: return "&sup;";
4643 case 0x2284: return "&nsub;";
4644 case 0x2286: return "&sube;";
4645 case 0x2287: return "&supe;";
4646 case 0x2295: return "&oplus;";
4647 case 0x2297: return "&otimes;";
4648 case 0x22A5: return "&perp;";
4649 case 0x22C5: return "&sdot;";
4650 case 0x2308: return "&lceil;";
4651 case 0x2309: return "&rceil;";
4652 case 0x230A: return "&lfloor;";
4653 case 0x230B: return "&rfloor;";
4654 case 0x2329: return "&lang;";
4655 case 0x232A: return "&rang;";
4656 case 0x25CA: return "&loz;";
4657 case 0x2660: return "&spades;";
4658 case 0x2663: return "&clubs;";
4659 case 0x2665: return "&hearts;";
4660 case 0x2666: return "&diams;";
4661 default: return to_unicode(code);
4667 * overstrike - returns true if the glyph (i, name) is going to overstrike
4668 * a previous glyph in sbuf.
4669 * If true the font is changed to bold and the previous sbuf
4670 * is flushed.
4673 int html_printer::overstrike(glyph *g, const char *name, const environment *env, int w)
4675 if ((env->hpos < sbuf_end_hpos)
4676 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
4678 * at this point we have detected an overlap
4680 if (overstrike_detected) {
4681 /* already detected, remove previous glyph and use this glyph */
4682 sbuf.set_length(last_sbuf_length);
4683 add_to_sbuf(g, name);
4684 sbuf_end_hpos = env->hpos + w;
4685 return true;
4686 } else {
4687 /* first time we have detected an overstrike in the sbuf */
4688 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
4689 if (! is_bold(sbuf_style.f))
4690 flush_sbuf();
4691 overstrike_detected = true;
4692 add_to_sbuf(g, name);
4693 sbuf_end_hpos = env->hpos + w;
4694 return true;
4697 return false ;
4701 * set_char - adds a character into the sbuf if it is a continuation
4702 * with the previous word otherwise flush the current sbuf
4703 * and add character anew.
4706 void html_printer::set_char(glyph *g, font *f, const environment *env,
4707 int w, const char *name)
4709 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
4710 if (sty.slant != 0) {
4711 if (sty.slant > 80 || sty.slant < -80) {
4712 error("silly slant `%1' degrees", sty.slant);
4713 sty.slant = 0;
4716 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
4717 && (sbuf_continuation(g, name, env, w)
4718 || overstrike(g, name, env, w)))
4719 return;
4721 flush_sbuf();
4722 if (sbuf_style.f == NULL)
4723 sbuf_style = sty;
4724 add_to_sbuf(g, name);
4725 sbuf_end_hpos = env->hpos + w;
4726 sbuf_start_hpos = env->hpos;
4727 sbuf_prev_hpos = env->hpos;
4728 sbuf_vpos = env->vpos;
4729 sbuf_style = sty;
4730 sbuf_kern = 0;
4734 * set_numbered_char - handle numbered characters.
4735 * Negative values are interpreted as unbreakable spaces;
4736 * the value (taken positive) gives the width.
4739 void html_printer::set_numbered_char(int num, const environment *env,
4740 int *widthp)
4742 int nbsp_width = 0;
4743 if (num < 0) {
4744 nbsp_width = -num;
4745 num = 160; // &nbsp;
4747 glyph *g = number_to_glyph(num);
4748 int fn = env->fontno;
4749 if (fn < 0 || fn >= nfonts) {
4750 error("bad font position `%1'", fn);
4751 return;
4753 font *f = font_table[fn];
4754 if (f == 0) {
4755 error("no font mounted at `%1'", fn);
4756 return;
4758 if (!f->contains(g)) {
4759 error("font `%1' does not contain numbered character %2",
4760 f->get_name(),
4761 num);
4762 return;
4764 int w;
4765 if (nbsp_width)
4766 w = nbsp_width;
4767 else
4768 w = f->get_width(g, env->size);
4769 w = round_width(w);
4770 if (widthp)
4771 *widthp = w;
4772 set_char(g, f, env, w, 0);
4775 glyph *html_printer::set_char_and_width(const char *nm, const environment *env,
4776 int *widthp, font **f)
4778 glyph *g = name_to_glyph(nm);
4779 int fn = env->fontno;
4780 if (fn < 0 || fn >= nfonts) {
4781 error("bad font position `%1'", fn);
4782 return UNDEFINED_GLYPH;
4784 *f = font_table[fn];
4785 if (*f == 0) {
4786 error("no font mounted at `%1'", fn);
4787 return UNDEFINED_GLYPH;
4789 if (!(*f)->contains(g)) {
4790 if (nm[0] != '\0' && nm[1] == '\0')
4791 error("font `%1' does not contain ascii character `%2'",
4792 (*f)->get_name(),
4793 nm[0]);
4794 else
4795 error("font `%1' does not contain special character `%2'",
4796 (*f)->get_name(),
4797 nm);
4798 return UNDEFINED_GLYPH;
4800 int w = (*f)->get_width(g, env->size);
4801 w = round_width(w);
4802 if (widthp)
4803 *widthp = w;
4804 return g;
4808 * write_title - writes the title to this document
4811 void html_printer::write_title (int in_head)
4813 if (title.has_been_found) {
4814 if (in_head) {
4815 html.put_string("<title>");
4816 html.put_string(title.text);
4817 html.put_string("</title>").nl().nl();
4818 } else {
4819 title.has_been_written = true;
4820 if (title.with_h1) {
4821 if (dialect == xhtml)
4822 html.put_string("<h1>");
4823 else
4824 html.put_string("<h1 align=\"center\">");
4825 html.put_string(title.text);
4826 html.put_string("</h1>").nl().nl();
4829 } else if (in_head) {
4830 // place empty title tags to help conform to `tidy'
4831 html.put_string("<title></title>").nl();
4836 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
4839 static void write_rule (void)
4841 if (auto_rule) {
4842 if (dialect == xhtml)
4843 fputs("<hr/>\n", stdout);
4844 else
4845 fputs("<hr>\n", stdout);
4849 void html_printer::begin_page(int n)
4851 page_number = n;
4852 #if defined(DEBUGGING)
4853 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
4854 #endif
4855 no_of_printed_pages++;
4857 output_style.f = 0;
4858 output_style.point_size= -1;
4859 output_space_code = 32;
4860 output_draw_point_size = -1;
4861 output_line_thickness = -1;
4862 output_hpos = -1;
4863 output_vpos = -1;
4864 output_vpos_max = -1;
4865 current_paragraph = new html_text(&html, dialect);
4866 do_indent(get_troff_indent(), pageoffset, linelength);
4867 current_paragraph->do_para("", false);
4870 void html_printer::end_page(int)
4872 flush_sbuf();
4873 flush_page();
4876 font *html_printer::make_font(const char *nm)
4878 return html_font::load_html_font(nm);
4881 void html_printer::do_body (void)
4883 if (background == NULL)
4884 fputs("<body>\n\n", stdout);
4885 else {
4886 unsigned int r, g, b;
4887 char buf[6+1];
4889 background->get_rgb(&r, &g, &b);
4890 // we have to scale 0..0xFFFF to 0..0xFF
4891 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
4893 fputs("<body bgcolor=\"#", stdout);
4894 fputs(buf, stdout);
4895 fputs("\">\n\n", stdout);
4900 * emit_link - generates: <a href="to">name</a>
4903 void html_printer::emit_link (const string &to, const char *name)
4905 fputs("<a href=\"", stdout);
4906 fputs(to.contents(), stdout);
4907 fputs("\">", stdout);
4908 fputs(name, stdout);
4909 fputs("</a>", stdout);
4913 * write_navigation - writes out the links which navigate between
4914 * file fragments.
4917 void html_printer::write_navigation (const string &top, const string &prev,
4918 const string &next, const string &current)
4920 int need_bar = false;
4922 if (multiple_files) {
4923 current_paragraph->done_para();
4924 write_rule();
4925 if (roff_sig)
4926 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
4927 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
4928 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
4929 "<tr><td class=\"left\">", stdout);
4930 handle_valid_flag(false);
4931 fputs("[ ", stdout);
4932 if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
4933 emit_link(prev, "prev");
4934 need_bar = true;
4936 if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
4937 if (need_bar)
4938 fputs(" | ", stdout);
4939 emit_link(next, "next");
4940 need_bar = true;
4942 if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
4943 if (need_bar)
4944 fputs(" | ", stdout);
4945 emit_link(top, "top");
4947 fputs(" ]\n", stdout);
4949 if (roff_sig)
4950 fputs("</td><td class=\"right\"><small>"
4951 "This document was produced using "
4952 "<a href=\"" ROFF_URL "\">" T_ROFF " v" VERSION
4953 "</a>.</small></td></tr></table>\n",
4954 stdout);
4955 write_rule();
4960 * do_file_components - scan the file list copying each temporary
4961 * file in turn. This is used twofold:
4963 * firstly to emit section heading links,
4964 * between file fragments if required and
4965 * secondly to generate jobname file fragments
4966 * if required.
4969 void html_printer::do_file_components (void)
4971 int fragment_no = 1;
4972 string top;
4973 string prev;
4974 string next;
4975 string current;
4977 file_list.start_of_list();
4978 top = string(job_name);
4979 if (dialect == xhtml)
4980 top += string(".xhtml");
4981 else
4982 top += string(".html");
4983 top += '\0';
4984 next = file_list.next_file_name();
4985 next += '\0';
4986 current = next;
4987 while (file_list.get_file() != 0) {
4988 if (fseek(file_list.get_file(), 0L, 0) < 0)
4989 fatal("fseek on temporary file failed");
4990 html.copy_file(file_list.get_file());
4991 fclose(file_list.get_file());
4993 file_list.move_next();
4994 if (file_list.is_new_output_file()) {
4995 #ifdef LONG_FOR_TIME_T
4996 long t;
4997 #else
4998 time_t t;
4999 #endif
5001 if (fragment_no > 1)
5002 write_navigation(top, prev, next, current);
5003 prev = current;
5004 current = next;
5005 next = file_list.next_file_name();
5006 next += '\0';
5007 string split_file = file_list.file_name();
5008 split_file += '\0';
5009 fflush(stdout);
5010 freopen(split_file.contents(), "w", stdout);
5011 fragment_no++;
5012 if (dialect == xhtml)
5013 writeHeadMetaStyle();
5015 html.begin_comment("Creator : " T_ROFF " version " VERSION).end_comment();
5017 t = time(0);
5018 html.begin_comment("CreationDate: ")
5019 .put_string(ctime(&t), strlen(ctime(&t))-1)
5020 .end_comment();
5022 if (dialect == html4)
5023 writeHeadMetaStyle();
5025 html.put_string("<title>");
5026 html.put_string(split_file.contents());
5027 html.put_string("</title>").nl().nl();
5029 fputs(head_info.contents(), stdout);
5030 fputs("</head>\n", stdout);
5031 write_navigation(top, prev, next, current);
5033 if (file_list.are_links_required())
5034 header.write_headings(stdout, true);
5036 if (fragment_no > 1)
5037 write_navigation(top, prev, next, current);
5038 else {
5039 current_paragraph->done_para();
5040 write_rule();
5041 if (valid_flag) {
5042 if (roff_sig)
5043 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
5044 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
5045 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
5046 "<tr><td class=\"left\">", stdout);
5047 handle_valid_flag(true);
5048 if (roff_sig)
5049 fputs("</td><td class=\"right\"><small>"
5050 "This document was produced using "
5051 "<a href=\"" ROFF_URL "\">" T_ROFF " v" VERSION
5052 "</a>.</small></td></tr></table>\n",
5053 stdout);
5054 write_rule();
5060 * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
5061 * related information.
5064 void html_printer::writeHeadMetaStyle (void)
5066 if (dialect == html4) {
5067 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
5068 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
5069 fputs("<html>\n", stdout);
5070 fputs("<head>\n", stdout);
5071 fputs("<meta name=\"generator\" "
5072 "content=\"" L_ROFF " -Thtml, see " ROFF_URL "\">\n", stdout);
5073 fputs("<meta http-equiv=\"Content-Type\" "
5074 "content=\"text/html; charset=US-ASCII\">\n", stdout);
5075 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
5076 fputs("<style type=\"text/css\">\n", stdout);
5078 else {
5079 fputs("<?xml version=\"1.0\" encoding=\"us-ascii\"?>\n", stdout);
5080 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\"\n", stdout);
5081 fputs(" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\"\n", stdout);
5082 fputs(" [<!ENTITY mathml \"http://www.w3.org/1998/Math/MathML\">]>\n", stdout);
5084 fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n",
5085 stdout);
5086 fputs("<head>\n", stdout);
5087 fputs("<meta name=\"generator\" "
5088 "content=\"" L_ROFF " -Txhtml, see " ROFF_URL "\"/>\n", stdout);
5089 fputs("<meta http-equiv=\"Content-Type\" "
5090 "content=\"text/html; charset=US-ASCII\"/>\n", stdout);
5091 fputs("<meta name=\"Content-Style\" content=\"text/css\"/>\n", stdout);
5092 fputs("<style type=\"text/css\">\n", stdout);
5093 fputs(" .center { text-align: center }\n", stdout);
5094 fputs(" .right { text-align: right }\n", stdout);
5096 fputs(" p { margin-top: 0; margin-bottom: 0; "
5097 "vertical-align: top }\n", stdout);
5098 fputs(" pre { margin-top: 0; margin-bottom: 0; "
5099 "vertical-align: top }\n", stdout);
5100 fputs(" table { margin-top: 0; margin-bottom: 0; "
5101 "vertical-align: top }\n", stdout);
5102 fputs(" h1 { text-align: center }\n", stdout);
5103 fputs("</style>\n", stdout);
5106 html_printer::~html_printer()
5108 #ifdef LONG_FOR_TIME_T
5109 long t;
5110 #else
5111 time_t t;
5112 #endif
5114 if (current_paragraph)
5115 current_paragraph->flush_text();
5116 html.end_line();
5117 html.set_file(stdout);
5119 if (dialect == xhtml)
5120 writeHeadMetaStyle();
5122 html.begin_comment("Creator : " T_ROFF " version " VERSION).end_comment();
5124 t = time(0);
5125 html.begin_comment("CreationDate: ")
5126 .put_string(ctime(&t), strlen(ctime(&t))-1)
5127 .end_comment();
5129 if (dialect == html4)
5130 writeHeadMetaStyle();
5132 write_title(true);
5133 head_info += '\0';
5134 fputs(head_info.contents(), stdout);
5135 fputs("</head>\n", stdout);
5136 do_body();
5138 write_title(false);
5139 header.write_headings(stdout, false);
5140 write_rule();
5141 #if defined(DEBUGGING)
5142 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
5143 #endif
5144 html.end_line();
5145 html.end_line();
5147 if (multiple_files) {
5148 fputs("</body>\n", stdout);
5149 fputs("</html>\n", stdout);
5150 do_file_components();
5151 } else {
5152 do_file_components();
5153 fputs("</body>\n", stdout);
5154 fputs("</html>\n", stdout);
5159 * get_str - returns a dupicate of string, s. The duplicate
5160 * string is terminated at the next ',' or ']'.
5163 static char *get_str (const char *s, char **n)
5165 int i=0;
5166 char *v;
5168 while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
5169 i++;
5170 if (i>0) {
5171 v = new char[i+1];
5172 memcpy(v, s, i+1);
5173 v[i] = (char)0;
5174 if (s[i] == ',')
5175 (*n) = (char *)&s[i+1];
5176 else
5177 (*n) = (char *)&s[i];
5178 return v;
5180 if (s[i] == ',')
5181 (*n) = (char *)&s[1];
5182 else
5183 (*n) = (char *)s;
5184 return NULL;
5188 * make_val - creates a string from if s is NULL.
5191 char *make_val (char *s, int v, char *id, char *f, char *l)
5193 if (s == NULL) {
5194 char buf[30];
5196 sprintf(buf, "%d", v);
5197 return strsave(buf);
5199 else {
5201 * check that value, s, is the same as, v.
5203 char *t = s;
5205 while (*t == '=')
5206 t++;
5207 if (atoi(t) != v) {
5208 if (f == NULL)
5209 f = (char *)"stdin";
5210 if (l == NULL)
5211 l = (char *)"<none>";
5212 fprintf(stderr,
5213 "%s:%s: " L_D_HTML " assertion failed at id%s; should: %d; got: %s\n",
5214 f, l, id, v, s);
5216 return s;
5221 * handle_assertion - handles the assertions created via .www:ASSERT
5222 * in www.tmac. See www.tmac for examples.
5223 * This method should be called as we are
5224 * parsing the ditroff input. It checks the x, y
5225 * position assertions. It does _not_ check the
5226 * troff state assertions as these are unknown at this
5227 * point.
5230 void html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
5232 char *n;
5233 char *cmd = get_str(s, &n);
5234 char *id = get_str(n, &n);
5235 char *val = get_str(n, &n);
5236 char *file= get_str(n, &n);
5237 char *line= get_str(n, &n);
5239 if (strcmp(cmd, "assertion:[x") == 0)
5240 as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
5241 else if (strcmp(cmd, "assertion:[y") == 0)
5242 as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
5243 else
5244 if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
5245 page_contents->add_tag(&sbuf_style, string(s),
5246 line_number, minv, minh, maxv, maxh);
5250 * build_state_assertion - builds the troff state assertions.
5253 void html_printer::handle_state_assertion (text_glob *g)
5255 if (g != NULL && g->is_a_tag() &&
5256 (strncmp(g->text_string, "assertion:[", 11) == 0)) {
5257 char *n = (char *)&g->text_string[11];
5258 char *cmd = get_str(n, &n);
5259 char *val = get_str(n, &n);
5260 (void)get_str(n, &n); // unused
5261 char *file= get_str(n, &n);
5262 char *line= get_str(n, &n);
5264 as.build(cmd, val, file, line);
5269 * special - handle all x X requests from troff. For post-html they
5270 * allow users to pass raw html commands, turn auto linked
5271 * headings off/on etc.
5274 void html_printer::special(char *s, const environment *env, char type)
5276 if (type != 'p')
5277 return;
5278 if (s != 0) {
5279 flush_sbuf();
5280 if (env->fontno >= 0) {
5281 style sty(get_font_from_index(env->fontno), env->size, env->height,
5282 env->slant, env->fontno, *env->col);
5283 sbuf_style = sty;
5286 if (strncmp(s, "html:", 5) == 0) {
5287 int r=font::res; /* resolution of the device */
5288 font *f=sbuf_style.f;
5290 if (f == NULL) {
5291 int found=false;
5293 f = font::load_font("TR", &found);
5297 * need to pass rest of string through to html output during flush
5299 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
5300 line_number,
5301 env->vpos-env->size*r/72, env->hpos,
5302 env->vpos , env->hpos,
5303 false);
5306 * assume that the html command has no width, if it does then
5307 * hopefully troff will have fudged this in a macro by
5308 * requesting that the formatting move right by the appropriate
5309 * amount.
5311 } else if ((strncmp(s, "html</p>:", 9) == 0) ||
5312 (strncmp(s, "html<?p>:", 9) == 0) ||
5313 (strncmp(s, "math<?p>:", 9) == 0)) {
5314 int r=font::res; /* resolution of the device */
5315 font *f=sbuf_style.f;
5316 string t;
5318 if (f == NULL) {
5319 int found=false;
5321 f = font::load_font("TR", &found);
5324 if (strncmp(s, "math<?p>:", 9) == 0) {
5325 if (strncmp((char *)&s[9], "<math>", 6) == 0) {
5326 s[9] = '\0';
5327 t = s;
5328 t += "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
5329 t += (char *)&s[15];
5330 t += '\0';
5331 s = (char *)&t[0];
5336 * need to pass all of string through to html output during flush
5338 page_contents->add_and_encode(&sbuf_style, string(s),
5339 line_number,
5340 env->vpos-env->size*r/72, env->hpos,
5341 env->vpos , env->hpos,
5342 true);
5345 * assume that the html command has no width, if it does then
5346 * hopefully troff will have fudged this in a macro by
5347 * requesting that the formatting move right by the appropriate
5348 * amount.
5351 } else if (strncmp(s, "index:", 6) == 0) {
5352 cutoff_heading = atoi(&s[6]);
5353 } else if (strncmp(s, "assertion:[", 11) == 0) {
5354 int r=font::res; /* resolution of the device */
5356 handle_assertion(env->vpos-env->size*r/72, env->hpos,
5357 env->vpos, env->hpos, s);
5363 * devtag - handles device troff tags sent from the `troff'.
5364 * These include the troff state machine tags:
5365 * .br, .sp, .in, .tl, .ll etc
5367 * (see man 5 grohtml_tags).
5370 void html_printer::devtag (char *s, const environment *env, char type)
5372 if (type != 'p')
5373 return;
5375 if (s != 0) {
5376 flush_sbuf();
5377 if (env->fontno >= 0) {
5378 style sty(get_font_from_index(env->fontno), env->size, env->height,
5379 env->slant, env->fontno, *env->col);
5380 sbuf_style = sty;
5383 if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
5384 int r=font::res; /* resolution of the device */
5386 page_contents->add_tag(&sbuf_style, string(s),
5387 line_number,
5388 env->vpos-env->size*r/72, env->hpos,
5389 env->vpos , env->hpos);
5396 * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
5399 int html_printer::round_width(int x)
5401 int r = font::hor;
5402 int n;
5404 // don't depend on the rounding direction for division of negative integers
5405 if (r == 1)
5406 n = x;
5407 else
5408 n = (x < 0
5409 ? -((-x + r/2 - 1)/r)
5410 : (x + r/2 - 1)/r);
5411 return n * r;
5415 * handle_valid_flag - emits a valid xhtml 1.1 or html-4.01 button, provided -V
5416 * was supplied on the command line.
5419 void html_printer::handle_valid_flag (int needs_para)
5421 if (valid_flag) {
5422 if (needs_para)
5423 fputs("<p>", stdout);
5424 if (dialect == xhtml)
5425 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5426 "src=\"http://www.w3.org/Icons/valid-xhtml11-blue\" "
5427 "alt=\"Valid XHTML 1.1 Transitional\" height=\"31\" width=\"88\" /></a>\n",
5428 stdout);
5429 else
5430 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5431 "src=\"http://www.w3.org/Icons/valid-html401-blue\" "
5432 "alt=\"Valid HTML 4.01 Transitional\" height=\"31\" width=\"88\"></a>\n",
5433 stdout);
5434 if (needs_para)
5435 fputs("</p>", stdout);
5439 int main(int argc, char **argv)
5441 program_name = argv[0];
5442 static char stderr_buf[BUFSIZ];
5443 setbuf(stderr, stderr_buf);
5444 int c;
5445 static const struct option long_options[] = {
5446 { "help", no_argument, 0, CHAR_MAX + 1 },
5447 { "version", no_argument, 0, 'v' },
5448 { NULL, 0, 0, 0 }
5450 while ((c = getopt_long(argc, argv, "a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
5451 long_options, NULL))
5452 != EOF)
5453 switch(c) {
5454 case 'a':
5455 /* text antialiasing bits - handled by pre-html */
5456 break;
5457 case 'b':
5458 // set background color to white
5459 default_background = new color;
5460 default_background->set_gray(color::MAX_COLOR_VAL);
5461 break;
5462 case 'd':
5463 /* handled by pre-html */
5464 break;
5465 case 'D':
5466 /* handled by pre-html */
5467 break;
5468 case 'e':
5469 /* handled by pre-html */
5470 break;
5471 case 'F':
5472 font::command_line_font_dir(optarg);
5473 break;
5474 case 'g':
5475 /* graphic antialiasing bits - handled by pre-html */
5476 break;
5477 case 'h':
5478 /* do not use the Hn headings of html, but manufacture our own */
5479 manufacture_headings = true;
5480 break;
5481 case 'i':
5482 /* handled by pre-html */
5483 break;
5484 case 'I':
5485 /* handled by pre-html */
5486 break;
5487 case 'j':
5488 multiple_files = true;
5489 job_name = optarg;
5490 break;
5491 case 'l':
5492 auto_links = false;
5493 break;
5494 case 'n':
5495 simple_anchors = true;
5496 break;
5497 case 'o':
5498 /* handled by pre-html */
5499 break;
5500 case 'p':
5501 /* handled by pre-html */
5502 break;
5503 case 'r':
5504 auto_rule = false;
5505 break;
5506 case 's':
5507 base_point_size = atoi(optarg);
5508 break;
5509 case 'S':
5510 split_level = atoi(optarg) + 1;
5511 break;
5512 case 'v':
5513 puts(L_D_HTML " (" T_ROFF ") v" VERSION);
5514 exit(0);
5515 break;
5516 case 'V':
5517 valid_flag = true;
5518 break;
5519 case 'x':
5520 if (strcmp(optarg, "x") == 0) {
5521 dialect = xhtml;
5522 simple_anchors = true;
5523 } else if (strcmp(optarg, "4") == 0)
5524 dialect = html4;
5525 else
5526 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
5527 break;
5528 case 'y':
5529 roff_sig = true;
5530 break;
5531 case CHAR_MAX + 1: // --help
5532 usage(stdout);
5533 exit(0);
5534 break;
5535 case '?':
5536 usage(stderr);
5537 exit(1);
5538 break;
5539 default:
5540 assert(0);
5542 if (optind >= argc) {
5543 do_file("-");
5544 } else {
5545 for (int i = optind; i < argc; i++)
5546 do_file(argv[i]);
5548 return 0;
5551 static void usage(FILE *stream)
5553 fprintf(stream,
5554 "Synopsis: %s [-vbelnhVy] [-D dir] "
5555 "[-I image_stem] [-F dir] [-x x] [files ...]\n",
5556 program_name);
5559 // s-it2-mode