Sync-to-go: src/dev-html..
[s-roff.git] / src / dev-html / post-html.cpp
blobd7c2f63f83eb26c0be7fbcc6fa9f0a5607d12ddc
1 /*@
2 * Copyright (c) 2014 - 2015 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 * This 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 * This 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 ncol = 0;
3617 int colmin = 0; // pacify compiler
3618 int colmax = 0; // pacify compiler
3619 html_table *tbl = new html_table(&html, -1);
3620 const char *tab_defs = NULL;
3621 char align = 'L';
3622 int nf = false;
3623 int old_pageoffset = pageoffset;
3625 remove_courier_tabs();
3626 page_contents->dump_page();
3627 insert_tab0_foreach_tab();
3628 page_contents->dump_page();
3629 if (! page_contents->glyphs.is_empty()) {
3630 page_contents->glyphs.start_from_head();
3631 as.reset();
3632 g = page_contents->glyphs.get_data();
3633 if (g->is_br()) {
3634 g = page_contents->glyphs.move_right_get_data();
3635 handle_state_assertion(g);
3636 if (page_contents->glyphs.is_equal_to_head()) {
3637 if (tbl != NULL) {
3638 delete tbl;
3639 tbl = NULL;
3641 return;
3644 start_of_line = g;
3645 ncol = 0;
3646 left = next_horiz_pos(g, nf);
3647 if (found_col)
3648 last = g;
3649 found_col = false;
3652 do {
3653 #if defined(DEBUG_TABLES)
3654 fprintf(stderr, " [") ;
3655 fprintf(stderr, g->text_string) ;
3656 fprintf(stderr, "] ") ;
3657 fflush(stderr);
3658 if (strcmp(g->text_string, "XXXXXXX") == 0)
3659 stop();
3660 #endif
3662 nf = calc_nf(g, nf);
3663 calc_po_in(g, nf);
3664 if (g->is_col()) {
3665 if (type_of_col == tab_tag && start_of_table != NULL) {
3666 page_contents->glyphs.move_left();
3667 insert_tab_te();
3668 start_of_table->remember_table(tbl);
3669 tbl = new html_table(&html, -1);
3670 page_contents->insert_tag(string("*** TAB -> COL ***"));
3671 if (tab_defs != NULL)
3672 tbl->tab_stops->init(tab_defs);
3673 start_of_table = NULL;
3674 last = NULL;
3676 type_of_col = col_tag;
3677 found_col = true;
3678 ncol = g->get_arg();
3679 align = 'L';
3680 colmin = 0;
3681 colmax = 0;
3682 } else if (g->is_tab()) {
3683 type_of_col = tab_tag;
3684 colmin = g->get_tab_args(&align);
3685 align = 'L'; // for now as 'C' and 'R' are broken
3686 ncol = tbl->find_tab_column(colmin);
3687 colmin += pageoffset + get_troff_indent();
3688 colmax = tbl->get_tab_pos(ncol+1);
3689 if (colmax > 0)
3690 colmax += pageoffset + get_troff_indent();
3691 } else if (g->is_tab0()) {
3692 if (type_of_col == col_tag && start_of_table != NULL) {
3693 page_contents->glyphs.move_left();
3694 insert_tab_te();
3695 start_of_table->remember_table(tbl);
3696 tbl = new html_table(&html, -1);
3697 page_contents->insert_tag(string("*** COL -> TAB ***"));
3698 start_of_table = NULL;
3699 last = NULL;
3701 if (tab_defs != NULL)
3702 tbl->tab_stops->init(tab_defs);
3704 type_of_col = tab0_tag;
3705 ncol = 1;
3706 colmin = 0;
3707 colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
3708 } else if (! g->is_a_tag())
3709 update_min_max(type_of_col, &colmin, &colmax, g);
3711 if ((g->is_col() || g->is_tab() || g->is_tab0())
3712 && (start_of_line != NULL) && (start_of_table == NULL)) {
3713 start_of_table = insert_tab_ts(start_of_line);
3714 start_of_line = NULL;
3715 } else if (g->is_ce() && (start_of_table != NULL)) {
3716 add_table_end("*** CE ***");
3717 start_of_table->remember_table(tbl);
3718 tbl = new html_table(&html, -1);
3719 start_of_table = NULL;
3720 last = NULL;
3721 } else if (g->is_ta()) {
3722 tab_defs = g->text_string;
3724 if (type_of_col == col_tag)
3725 tbl->tab_stops->check_init(tab_defs);
3727 if (!tbl->tab_stops->compatible(tab_defs)) {
3728 if (start_of_table != NULL) {
3729 add_table_end("*** TABS ***");
3730 start_of_table->remember_table(tbl);
3731 tbl = new html_table(&html, -1);
3732 start_of_table = NULL;
3733 type_of_col = none;
3734 last = NULL;
3736 tbl->tab_stops->init(tab_defs);
3740 if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
3741 // we are in a table and have a glyph
3742 if ((ncol == 0) || (! tbl->add_column(ncol, colmin, colmax, align))) {
3743 if (ncol == 0)
3744 add_table_end("*** NCOL == 0 ***");
3745 else
3746 add_table_end("*** CROSSED COLS ***");
3748 start_of_table->remember_table(tbl);
3749 tbl = new html_table(&html, -1);
3750 start_of_table = NULL;
3751 type_of_col = none;
3752 last = NULL;
3757 * move onto next glob, check whether we are starting a new line
3759 g = page_contents->glyphs.move_right_get_data();
3760 handle_state_assertion(g);
3762 if (g == NULL) {
3763 if (found_col) {
3764 page_contents->glyphs.start_from_head();
3765 as.reset();
3766 last = g;
3767 found_col = false;
3769 } else if (g->is_br() || (nf && g->is_eol())) {
3770 do {
3771 g = page_contents->glyphs.move_right_get_data();
3772 handle_state_assertion(g);
3773 nf = calc_nf(g, nf);
3774 } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
3775 start_of_line = g;
3776 ncol = 0;
3777 left = next_horiz_pos(g, nf);
3778 if (found_col)
3779 last = g;
3780 found_col = false;
3782 } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3784 #if defined(DEBUG_TABLES)
3785 fprintf(stderr, "finished scanning for tables\n");
3786 #endif
3788 page_contents->glyphs.start_from_head();
3789 if (start_of_table != NULL) {
3790 if (last != NULL)
3791 while (last != page_contents->glyphs.get_data())
3792 page_contents->glyphs.move_left();
3794 insert_tab_te();
3795 start_of_table->remember_table(tbl);
3796 tbl = NULL;
3797 page_contents->insert_tag(string("*** LAST ***"));
3800 if (tbl != NULL) {
3801 delete tbl;
3802 tbl = NULL;
3805 // and reset the registers
3806 pageoffset = old_pageoffset;
3807 troff_indent = 0;
3808 temp_indent = 0;
3809 end_tempindent = 0;
3812 void html_printer::flush_page (void)
3814 supress_sub_sup = true;
3815 flush_sbuf();
3816 page_contents->dump_page();
3817 lookahead_for_tables();
3818 page_contents->dump_page();
3820 flush_globs();
3821 current_paragraph->done_para();
3822 current_paragraph->flush_text();
3824 // move onto a new page
3825 delete page_contents;
3826 #if defined(DEBUG_TABLES)
3827 fprintf(stderr, "\n\n*** flushed page ***\n\n");
3829 html.simple_comment("new page called");
3830 #endif
3831 page_contents = new page;
3835 * determine_space - works out whether we need to write a space.
3836 * If last glyph is ajoining then no space emitted.
3839 void html_printer::determine_space (text_glob *g)
3841 if (current_paragraph->is_in_pre()) {
3843 * .nf has been specified
3845 while (output_hpos < g->minh) {
3846 output_hpos += space_width;
3847 current_paragraph->emit_space();
3849 } else {
3850 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
3851 current_paragraph->emit_space();
3857 * is_line_start - returns true if we are at the start of a line.
3860 int html_printer::is_line_start (int nf)
3862 int line_start = false;
3863 int result = true;
3864 text_glob *orig = page_contents->glyphs.get_data();
3865 text_glob *g;
3867 if (! page_contents->glyphs.is_equal_to_head()) {
3868 do {
3869 page_contents->glyphs.move_left();
3870 g = page_contents->glyphs.get_data();
3871 result = g->is_a_tag();
3872 if (g->is_fi())
3873 nf = false;
3874 else if (g->is_nf())
3875 nf = true;
3876 line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
3877 } while ((!line_start) && (result));
3879 * now restore our previous position.
3881 while (page_contents->glyphs.get_data() != orig)
3882 page_contents->glyphs.move_right();
3884 return result;
3888 * is_font_courier - returns true if the font, f, is courier.
3891 int html_printer::is_font_courier (font *f)
3893 if (f != 0) {
3894 const char *fontname = f->get_name();
3896 return( (fontname != 0) && (fontname[0] == 'C') );
3898 return false;
3902 * end_font - shuts down the font corresponding to fontname.
3905 void html_printer::end_font (const char *fontname)
3907 if (strcmp(fontname, "B") == 0) {
3908 current_paragraph->done_bold();
3909 } else if (strcmp(fontname, "I") == 0) {
3910 current_paragraph->done_italic();
3911 } else if (strcmp(fontname, "BI") == 0) {
3912 current_paragraph->done_bold();
3913 current_paragraph->done_italic();
3914 } else if (strcmp(fontname, "CR") == 0) {
3915 current_paragraph->done_tt();
3916 } else if (strcmp(fontname, "CI") == 0) {
3917 current_paragraph->done_italic();
3918 current_paragraph->done_tt();
3919 } else if (strcmp(fontname, "CB") == 0) {
3920 current_paragraph->done_bold();
3921 current_paragraph->done_tt();
3922 } else if (strcmp(fontname, "CBI") == 0) {
3923 current_paragraph->done_bold();
3924 current_paragraph->done_italic();
3925 current_paragraph->done_tt();
3930 * start_font - starts the font corresponding to name.
3933 void html_printer::start_font (const char *fontname)
3935 if (strcmp(fontname, "R") == 0) {
3936 current_paragraph->done_bold();
3937 current_paragraph->done_italic();
3938 current_paragraph->done_tt();
3939 } else if (strcmp(fontname, "B") == 0) {
3940 current_paragraph->do_bold();
3941 } else if (strcmp(fontname, "I") == 0) {
3942 current_paragraph->do_italic();
3943 } else if (strcmp(fontname, "BI") == 0) {
3944 current_paragraph->do_bold();
3945 current_paragraph->do_italic();
3946 } else if (strcmp(fontname, "CR") == 0) {
3947 if ((! fill_on) && (is_courier_until_eol()) &&
3948 is_line_start(! fill_on)) {
3949 current_paragraph->do_pre();
3951 current_paragraph->do_tt();
3952 } else if (strcmp(fontname, "CI") == 0) {
3953 if ((! fill_on) && (is_courier_until_eol()) &&
3954 is_line_start(! fill_on)) {
3955 current_paragraph->do_pre();
3957 current_paragraph->do_tt();
3958 current_paragraph->do_italic();
3959 } else if (strcmp(fontname, "CB") == 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_bold();
3966 } else if (strcmp(fontname, "CBI") == 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_italic();
3973 current_paragraph->do_bold();
3978 * start_size - from is old font size, to is the new font size.
3979 * The html increase <big> and <small> decrease alters the
3980 * font size by 20%. We try and map these onto glyph sizes.
3983 void html_printer::start_size (int from, int to)
3985 if (from < to) {
3986 while (from < to) {
3987 current_paragraph->do_big();
3988 from += SIZE_INCREMENT;
3990 } else if (from > to) {
3991 while (from > to) {
3992 current_paragraph->do_small();
3993 from -= SIZE_INCREMENT;
3999 * do_font - checks to see whether we need to alter the html font.
4002 void html_printer::do_font (text_glob *g)
4005 * check if the output_style.point_size has not been set yet
4006 * this allow users to place .ps at the top of their troff files
4007 * and grohtml can then treat the .ps value as the base font size (3)
4009 if (output_style.point_size == -1) {
4010 output_style.point_size = pointsize;
4013 if (g->text_style.f != output_style.f) {
4014 if (output_style.f != 0) {
4015 end_font(output_style.f->get_name());
4017 output_style.f = g->text_style.f;
4018 if (output_style.f != 0) {
4019 start_font(output_style.f->get_name());
4022 if (output_style.point_size != g->text_style.point_size) {
4023 do_sup_or_sub(g);
4024 if ((output_style.point_size > 0) &&
4025 (g->text_style.point_size > 0)) {
4026 start_size(output_style.point_size, g->text_style.point_size);
4028 if (g->text_style.point_size > 0) {
4029 output_style.point_size = g->text_style.point_size;
4032 if (output_style.col != g->text_style.col) {
4033 current_paragraph->done_color();
4034 output_style.col = g->text_style.col;
4035 current_paragraph->do_color(&output_style.col);
4040 * start_subscript - returns true if, g, looks like a subscript start.
4043 int html_printer::start_subscript (text_glob *g)
4045 int r = font::res;
4046 int height = output_style.point_size*r/72;
4048 return( (output_style.point_size != 0) &&
4049 (output_vpos < g->minv) &&
4050 (output_vpos-height > g->maxv) &&
4051 (output_style.point_size > g->text_style.point_size) );
4055 * start_superscript - returns true if, g, looks like a superscript start.
4058 int html_printer::start_superscript (text_glob *g)
4060 int r = font::res;
4061 int height = output_style.point_size*r/72;
4063 return( (output_style.point_size != 0) &&
4064 (output_vpos > g->minv) &&
4065 (output_vpos-height < g->maxv) &&
4066 (output_style.point_size > g->text_style.point_size) );
4070 * end_subscript - returns true if, g, looks like the end of a subscript.
4073 int html_printer::end_subscript (text_glob *g)
4075 int r = font::res;
4076 int height = output_style.point_size*r/72;
4078 return( (output_style.point_size != 0) &&
4079 (g->minv < output_vpos) &&
4080 (output_vpos-height > g->maxv) &&
4081 (output_style.point_size < g->text_style.point_size) );
4085 * end_superscript - returns true if, g, looks like the end of a superscript.
4088 int html_printer::end_superscript (text_glob *g)
4090 int r = font::res;
4091 int height = output_style.point_size*r/72;
4093 return( (output_style.point_size != 0) &&
4094 (g->minv > output_vpos) &&
4095 (output_vpos-height < g->maxv) &&
4096 (output_style.point_size < g->text_style.point_size) );
4100 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4101 * start/end and it calls the services of html-text to issue the
4102 * appropriate tags.
4105 void html_printer::do_sup_or_sub (text_glob *g)
4107 if (! supress_sub_sup) {
4108 if (start_subscript(g)) {
4109 current_paragraph->do_sub();
4110 } else if (start_superscript(g)) {
4111 current_paragraph->do_sup();
4112 } else if (end_subscript(g)) {
4113 current_paragraph->done_sub();
4114 } else if (end_superscript(g)) {
4115 current_paragraph->done_sup();
4121 * do_end_para - writes out the html text after shutting down the
4122 * current paragraph.
4125 void html_printer::do_end_para (text_glob *g)
4127 do_font(g);
4128 current_paragraph->done_para();
4129 current_paragraph->remove_para_space();
4130 html.put_string(g->text_string+9);
4131 output_vpos = g->minv;
4132 output_hpos = g->maxh;
4133 output_vpos_max = g->maxv;
4134 supress_sub_sup = false;
4138 * emit_html - write out the html text
4141 void html_printer::emit_html (text_glob *g)
4143 do_font(g);
4144 determine_space(g);
4145 current_paragraph->do_emittext(g->text_string, g->text_length);
4146 output_vpos = g->minv;
4147 output_hpos = g->maxh;
4148 output_vpos_max = g->maxv;
4149 supress_sub_sup = false;
4153 * flush_sbuf - flushes the current sbuf into the list of glyphs.
4156 void html_printer::flush_sbuf()
4158 if (sbuf.length() > 0) {
4159 int r=font::res; // resolution of the device
4160 set_style(sbuf_style);
4162 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
4163 font *bold_font = make_bold(sbuf_style.f);
4164 if (bold_font != NULL)
4165 sbuf_style.f = bold_font;
4168 page_contents->add(&sbuf_style, sbuf,
4169 line_number,
4170 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4171 sbuf_vpos , sbuf_end_hpos);
4173 output_hpos = sbuf_end_hpos;
4174 output_vpos = sbuf_vpos;
4175 last_sbuf_length = 0;
4176 sbuf_prev_hpos = sbuf_end_hpos;
4177 overstrike_detected = false;
4178 sbuf.clear();
4182 void html_printer::set_line_thickness(const environment *env)
4184 line_thickness = env->size;
4187 void html_printer::draw(int code, int *p, int np, const environment *env)
4189 switch (code) {
4191 case 'l':
4192 # if 0
4193 if (np == 2) {
4194 page_contents->add_line(&sbuf_style,
4195 line_number,
4196 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
4197 } else {
4198 error("2 arguments required for line");
4200 # endif
4201 break;
4202 case 't':
4204 if (np == 0) {
4205 line_thickness = -1;
4206 } else {
4207 // troff gratuitously adds an extra 0
4208 if (np != 1 && np != 2) {
4209 error("0 or 1 argument required for thickness");
4210 break;
4212 line_thickness = p[0];
4214 break;
4217 case 'P':
4218 break;
4219 case 'p':
4220 break;
4221 case 'E':
4222 break;
4223 case 'e':
4224 break;
4225 case 'C':
4226 break;
4227 case 'c':
4228 break;
4229 case 'a':
4230 break;
4231 case '~':
4232 break;
4233 case 'f':
4234 break;
4235 case 'F':
4236 // fill with color env->fill
4237 if (background != NULL)
4238 delete background;
4239 background = new color;
4240 *background = *env->fill;
4241 break;
4243 default:
4244 error("unrecognised drawing command `%1'", char(code));
4245 break;
4249 html_printer::html_printer()
4250 : html(0, MAX_LINE_LENGTH),
4251 no_of_printed_pages(0),
4252 last_sbuf_length(0),
4253 overstrike_detected(false),
4254 output_hpos(-1),
4255 output_vpos(-1),
4256 output_vpos_max(-1),
4257 line_thickness(-1),
4258 inside_font_style(0),
4259 page_number(0),
4260 header_indent(-1),
4261 supress_sub_sup(true),
4262 cutoff_heading(100),
4263 indent(NULL),
4264 table(NULL),
4265 end_center(0),
4266 end_tempindent(0),
4267 next_tag(INLINE),
4268 fill_on(true),
4269 max_linelength(-1),
4270 linelength(0),
4271 pageoffset(0),
4272 troff_indent(0),
4273 device_indent(0),
4274 temp_indent(0),
4275 pointsize(base_point_size),
4276 line_number(0),
4277 background(default_background),
4278 seen_indent(false),
4279 next_indent(0),
4280 seen_pageoffset(false),
4281 next_pageoffset(0),
4282 seen_linelength(false),
4283 next_linelength(0),
4284 seen_center(false),
4285 next_center(0),
4286 seen_space(0),
4287 seen_break(0),
4288 current_column(0),
4289 row_space(false)
4291 file_list.add_new_file(xtmpfile());
4292 html.set_file(file_list.get_file());
4293 if (font::hor != 24)
4294 fatal("horizontal resolution must be 24");
4295 if (font::vert != 40)
4296 fatal("vertical resolution must be 40");
4297 #if 0
4298 // should be sorted html..
4299 if (font::res % (font::sizescale*72) != 0)
4300 fatal("res must be a multiple of 72*sizescale");
4301 #endif
4302 int r = font::res;
4303 int point = 0;
4304 while (r % 10 == 0) {
4305 r /= 10;
4306 point++;
4308 res = r;
4309 html.set_fixed_point(point);
4310 space_glyph = name_to_glyph("space");
4311 space_width = font::hor;
4312 paper_length = font::paperlength;
4313 linelength = font::res*13/2;
4314 if (paper_length == 0)
4315 paper_length = 11*font::res;
4317 page_contents = new page();
4321 * add_to_sbuf - adds character code or name to the sbuf.
4324 void html_printer::add_to_sbuf (glyph *g, const string &s)
4326 if (sbuf_style.f == NULL)
4327 return;
4329 const char *html_glyph = NULL;
4330 unsigned int code = sbuf_style.f->get_code(g);
4332 if (s.empty()) {
4333 if (sbuf_style.f->contains(g))
4334 html_glyph = get_html_entity(sbuf_style.f->get_code(g));
4335 else
4336 html_glyph = NULL;
4338 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
4339 html_glyph = to_unicode(code);
4340 } else
4341 html_glyph = get_html_translation(sbuf_style.f, s);
4343 last_sbuf_length = sbuf.length();
4344 if (html_glyph == NULL)
4345 sbuf += ((char)code);
4346 else
4347 sbuf += html_glyph;
4350 int html_printer::sbuf_continuation (glyph *g, const char *name,
4351 const environment *env, int w)
4354 * lets see whether the glyph is closer to the end of sbuf
4356 if ((sbuf_end_hpos == env->hpos)
4357 || ((sbuf_prev_hpos < sbuf_end_hpos)
4358 && (env->hpos < sbuf_end_hpos)
4359 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
4360 add_to_sbuf(g, name);
4361 sbuf_prev_hpos = sbuf_end_hpos;
4362 sbuf_end_hpos += w + sbuf_kern;
4363 return true;
4364 } else {
4365 if ((env->hpos >= sbuf_end_hpos) &&
4366 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
4368 * lets see whether a space is needed or not
4371 if (env->hpos-sbuf_end_hpos < space_width) {
4372 add_to_sbuf(g, name);
4373 sbuf_prev_hpos = sbuf_end_hpos;
4374 sbuf_end_hpos = env->hpos + w;
4375 return true;
4379 return false ;
4383 * get_html_translation - given the position of the character and its name
4384 * return the device encoding for such character.
4387 const char *get_html_translation (font *f, const string &name)
4389 if ((f == 0) || name.empty())
4390 return NULL;
4391 else {
4392 glyph *g = name_to_glyph((char *)(name + '\0').contents());
4393 if (f->contains(g))
4394 return get_html_entity(f->get_code(g));
4395 else
4396 return NULL;
4401 * get_html_entity - given a Unicode character's code point, return a
4402 * HTML entity that represents the character, if the
4403 * character cannot represent itself in all contexts.
4404 * The return value, if non-NULL, is allocated in a static buffer and is
4405 * only valid until the next call of this function.
4407 static const char *get_html_entity (unsigned int code)
4409 if (code < UNICODE_DESC_START) {
4410 switch (code) {
4411 case 0x0022: return "&quot;";
4412 case 0x0026: return "&amp;";
4413 case 0x003C: return "&lt;";
4414 case 0x003E: return "&gt;";
4415 default: return NULL;
4417 } else {
4418 switch (code) {
4419 case 0x00A0: return "&nbsp;";
4420 case 0x00A1: return "&iexcl;";
4421 case 0x00A2: return "&cent;";
4422 case 0x00A3: return "&pound;";
4423 case 0x00A4: return "&curren;";
4424 case 0x00A5: return "&yen;";
4425 case 0x00A6: return "&brvbar;";
4426 case 0x00A7: return "&sect;";
4427 case 0x00A8: return "&uml;";
4428 case 0x00A9: return "&copy;";
4429 case 0x00AA: return "&ordf;";
4430 case 0x00AB: return "&laquo;";
4431 case 0x00AC: return "&not;";
4432 case 0x00AE: return "&reg;";
4433 case 0x00AF: return "&macr;";
4434 case 0x00B0: return "&deg;";
4435 case 0x00B1: return "&plusmn;";
4436 case 0x00B2: return "&sup2;";
4437 case 0x00B3: return "&sup3;";
4438 case 0x00B4: return "&acute;";
4439 case 0x00B5: return "&micro;";
4440 case 0x00B6: return "&para;";
4441 case 0x00B7: return "&middot;";
4442 case 0x00B8: return "&cedil;";
4443 case 0x00B9: return "&sup1;";
4444 case 0x00BA: return "&ordm;";
4445 case 0x00BB: return "&raquo;";
4446 case 0x00BC: return "&frac14;";
4447 case 0x00BD: return "&frac12;";
4448 case 0x00BE: return "&frac34;";
4449 case 0x00BF: return "&iquest;";
4450 case 0x00C0: return "&Agrave;";
4451 case 0x00C1: return "&Aacute;";
4452 case 0x00C2: return "&Acirc;";
4453 case 0x00C3: return "&Atilde;";
4454 case 0x00C4: return "&Auml;";
4455 case 0x00C5: return "&Aring;";
4456 case 0x00C6: return "&AElig;";
4457 case 0x00C7: return "&Ccedil;";
4458 case 0x00C8: return "&Egrave;";
4459 case 0x00C9: return "&Eacute;";
4460 case 0x00CA: return "&Ecirc;";
4461 case 0x00CB: return "&Euml;";
4462 case 0x00CC: return "&Igrave;";
4463 case 0x00CD: return "&Iacute;";
4464 case 0x00CE: return "&Icirc;";
4465 case 0x00CF: return "&Iuml;";
4466 case 0x00D0: return "&ETH;";
4467 case 0x00D1: return "&Ntilde;";
4468 case 0x00D2: return "&Ograve;";
4469 case 0x00D3: return "&Oacute;";
4470 case 0x00D4: return "&Ocirc;";
4471 case 0x00D5: return "&Otilde;";
4472 case 0x00D6: return "&Ouml;";
4473 case 0x00D7: return "&times;";
4474 case 0x00D8: return "&Oslash;";
4475 case 0x00D9: return "&Ugrave;";
4476 case 0x00DA: return "&Uacute;";
4477 case 0x00DB: return "&Ucirc;";
4478 case 0x00DC: return "&Uuml;";
4479 case 0x00DD: return "&Yacute;";
4480 case 0x00DE: return "&THORN;";
4481 case 0x00DF: return "&szlig;";
4482 case 0x00E0: return "&agrave;";
4483 case 0x00E1: return "&aacute;";
4484 case 0x00E2: return "&acirc;";
4485 case 0x00E3: return "&atilde;";
4486 case 0x00E4: return "&auml;";
4487 case 0x00E5: return "&aring;";
4488 case 0x00E6: return "&aelig;";
4489 case 0x00E7: return "&ccedil;";
4490 case 0x00E8: return "&egrave;";
4491 case 0x00E9: return "&eacute;";
4492 case 0x00EA: return "&ecirc;";
4493 case 0x00EB: return "&euml;";
4494 case 0x00EC: return "&igrave;";
4495 case 0x00ED: return "&iacute;";
4496 case 0x00EE: return "&icirc;";
4497 case 0x00EF: return "&iuml;";
4498 case 0x00F0: return "&eth;";
4499 case 0x00F1: return "&ntilde;";
4500 case 0x00F2: return "&ograve;";
4501 case 0x00F3: return "&oacute;";
4502 case 0x00F4: return "&ocirc;";
4503 case 0x00F5: return "&otilde;";
4504 case 0x00F6: return "&ouml;";
4505 case 0x00F7: return "&divide;";
4506 case 0x00F8: return "&oslash;";
4507 case 0x00F9: return "&ugrave;";
4508 case 0x00FA: return "&uacute;";
4509 case 0x00FB: return "&ucirc;";
4510 case 0x00FC: return "&uuml;";
4511 case 0x00FD: return "&yacute;";
4512 case 0x00FE: return "&thorn;";
4513 case 0x00FF: return "&yuml;";
4514 case 0x0152: return "&OElig;";
4515 case 0x0153: return "&oelig;";
4516 case 0x0160: return "&Scaron;";
4517 case 0x0161: return "&scaron;";
4518 case 0x0178: return "&Yuml;";
4519 case 0x0192: return "&fnof;";
4520 case 0x0391: return "&Alpha;";
4521 case 0x0392: return "&Beta;";
4522 case 0x0393: return "&Gamma;";
4523 case 0x0394: return "&Delta;";
4524 case 0x0395: return "&Epsilon;";
4525 case 0x0396: return "&Zeta;";
4526 case 0x0397: return "&Eta;";
4527 case 0x0398: return "&Theta;";
4528 case 0x0399: return "&Iota;";
4529 case 0x039A: return "&Kappa;";
4530 case 0x039B: return "&Lambda;";
4531 case 0x039C: return "&Mu;";
4532 case 0x039D: return "&Nu;";
4533 case 0x039E: return "&Xi;";
4534 case 0x039F: return "&Omicron;";
4535 case 0x03A0: return "&Pi;";
4536 case 0x03A1: return "&Rho;";
4537 case 0x03A3: return "&Sigma;";
4538 case 0x03A4: return "&Tau;";
4539 case 0x03A5: return "&Upsilon;";
4540 case 0x03A6: return "&Phi;";
4541 case 0x03A7: return "&Chi;";
4542 case 0x03A8: return "&Psi;";
4543 case 0x03A9: return "&Omega;";
4544 case 0x03B1: return "&alpha;";
4545 case 0x03B2: return "&beta;";
4546 case 0x03B3: return "&gamma;";
4547 case 0x03B4: return "&delta;";
4548 case 0x03B5: return "&epsilon;";
4549 case 0x03B6: return "&zeta;";
4550 case 0x03B7: return "&eta;";
4551 case 0x03B8: return "&theta;";
4552 case 0x03B9: return "&iota;";
4553 case 0x03BA: return "&kappa;";
4554 case 0x03BB: return "&lambda;";
4555 case 0x03BC: return "&mu;";
4556 case 0x03BD: return "&nu;";
4557 case 0x03BE: return "&xi;";
4558 case 0x03BF: return "&omicron;";
4559 case 0x03C0: return "&pi;";
4560 case 0x03C1: return "&rho;";
4561 case 0x03C2: return "&sigmaf;";
4562 case 0x03C3: return "&sigma;";
4563 case 0x03C4: return "&tau;";
4564 case 0x03C5: return "&upsilon;";
4565 case 0x03C6: return "&phi;";
4566 case 0x03C7: return "&chi;";
4567 case 0x03C8: return "&psi;";
4568 case 0x03C9: return "&omega;";
4569 case 0x03D1: return "&thetasym;";
4570 case 0x03D6: return "&piv;";
4571 case 0x2013: return "&ndash;";
4572 case 0x2014: return "&mdash;";
4573 case 0x2018: return "&lsquo;";
4574 case 0x2019: return "&rsquo;";
4575 case 0x201A: return "&sbquo;";
4576 case 0x201C: return "&ldquo;";
4577 case 0x201D: return "&rdquo;";
4578 case 0x201E: return "&bdquo;";
4579 case 0x2020: return "&dagger;";
4580 case 0x2021: return "&Dagger;";
4581 case 0x2022: return "&bull;";
4582 case 0x2030: return "&permil;";
4583 case 0x2032: return "&prime;";
4584 case 0x2033: return "&Prime;";
4585 case 0x2039: return "&lsaquo;";
4586 case 0x203A: return "&rsaquo;";
4587 case 0x203E: return "&oline;";
4588 case 0x2044: return "&frasl;";
4589 case 0x20AC: return "&euro;";
4590 case 0x2111: return "&image;";
4591 case 0x2118: return "&weierp;";
4592 case 0x211C: return "&real;";
4593 case 0x2122: return "&trade;";
4594 case 0x2135: return "&alefsym;";
4595 case 0x2190: return "&larr;";
4596 case 0x2191: return "&uarr;";
4597 case 0x2192: return "&rarr;";
4598 case 0x2193: return "&darr;";
4599 case 0x2194: return "&harr;";
4600 case 0x21D0: return "&lArr;";
4601 case 0x21D1: return "&uArr;";
4602 case 0x21D2: return "&rArr;";
4603 case 0x21D3: return "&dArr;";
4604 case 0x21D4: return "&hArr;";
4605 case 0x2200: return "&forall;";
4606 case 0x2202: return "&part;";
4607 case 0x2203: return "&exist;";
4608 case 0x2205: return "&empty;";
4609 case 0x2207: return "&nabla;";
4610 case 0x2208: return "&isin;";
4611 case 0x2209: return "&notin;";
4612 case 0x220B: return "&ni;";
4613 case 0x220F: return "&prod;";
4614 case 0x2211: return "&sum;";
4615 case 0x2212: return "&minus;";
4616 case 0x2217: return "&lowast;";
4617 case 0x221A: return "&radic;";
4618 case 0x221D: return "&prop;";
4619 case 0x221E: return "&infin;";
4620 case 0x2220: return "&ang;";
4621 case 0x2227: return "&and;";
4622 case 0x2228: return "&or;";
4623 case 0x2229: return "&cap;";
4624 case 0x222A: return "&cup;";
4625 case 0x222B: return "&int;";
4626 case 0x2234: return "&there4;";
4627 case 0x223C: return "&sim;";
4628 case 0x2245: return "&cong;";
4629 case 0x2248: return "&asymp;";
4630 case 0x2260: return "&ne;";
4631 case 0x2261: return "&equiv;";
4632 case 0x2264: return "&le;";
4633 case 0x2265: return "&ge;";
4634 case 0x2282: return "&sub;";
4635 case 0x2283: return "&sup;";
4636 case 0x2284: return "&nsub;";
4637 case 0x2286: return "&sube;";
4638 case 0x2287: return "&supe;";
4639 case 0x2295: return "&oplus;";
4640 case 0x2297: return "&otimes;";
4641 case 0x22A5: return "&perp;";
4642 case 0x22C5: return "&sdot;";
4643 case 0x2308: return "&lceil;";
4644 case 0x2309: return "&rceil;";
4645 case 0x230A: return "&lfloor;";
4646 case 0x230B: return "&rfloor;";
4647 case 0x2329: return "&lang;";
4648 case 0x232A: return "&rang;";
4649 case 0x25CA: return "&loz;";
4650 case 0x2660: return "&spades;";
4651 case 0x2663: return "&clubs;";
4652 case 0x2665: return "&hearts;";
4653 case 0x2666: return "&diams;";
4654 case 0x27E8: return "&lang;";
4655 case 0x27E9: return "&rang;";
4656 default: return to_unicode(code);
4662 * overstrike - returns true if the glyph (i, name) is going to overstrike
4663 * a previous glyph in sbuf.
4664 * If true the font is changed to bold and the previous sbuf
4665 * is flushed.
4668 int html_printer::overstrike(glyph *g, const char *name, const environment *env, int w)
4670 if ((env->hpos < sbuf_end_hpos)
4671 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
4673 * at this point we have detected an overlap
4675 if (overstrike_detected) {
4676 /* already detected, remove previous glyph and use this glyph */
4677 sbuf.set_length(last_sbuf_length);
4678 add_to_sbuf(g, name);
4679 sbuf_end_hpos = env->hpos + w;
4680 return true;
4681 } else {
4682 /* first time we have detected an overstrike in the sbuf */
4683 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
4684 if (! is_bold(sbuf_style.f))
4685 flush_sbuf();
4686 overstrike_detected = true;
4687 add_to_sbuf(g, name);
4688 sbuf_end_hpos = env->hpos + w;
4689 return true;
4692 return false ;
4696 * set_char - adds a character into the sbuf if it is a continuation
4697 * with the previous word otherwise flush the current sbuf
4698 * and add character anew.
4701 void html_printer::set_char(glyph *g, font *f, const environment *env,
4702 int w, const char *name)
4704 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
4705 if (sty.slant != 0) {
4706 if (sty.slant > 80 || sty.slant < -80) {
4707 error("silly slant `%1' degrees", sty.slant);
4708 sty.slant = 0;
4711 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
4712 && (sbuf_continuation(g, name, env, w)
4713 || overstrike(g, name, env, w)))
4714 return;
4716 flush_sbuf();
4717 if (sbuf_style.f == NULL)
4718 sbuf_style = sty;
4719 add_to_sbuf(g, name);
4720 sbuf_end_hpos = env->hpos + w;
4721 sbuf_start_hpos = env->hpos;
4722 sbuf_prev_hpos = env->hpos;
4723 sbuf_vpos = env->vpos;
4724 sbuf_style = sty;
4725 sbuf_kern = 0;
4729 * set_numbered_char - handle numbered characters.
4730 * Negative values are interpreted as unbreakable spaces;
4731 * the value (taken positive) gives the width.
4734 void html_printer::set_numbered_char(int num, const environment *env,
4735 int *widthp)
4737 int nbsp_width = 0;
4738 if (num < 0) {
4739 nbsp_width = -num;
4740 num = 160; // &nbsp;
4742 glyph *g = number_to_glyph(num);
4743 int fn = env->fontno;
4744 if (fn < 0 || fn >= nfonts) {
4745 error("bad font position `%1'", fn);
4746 return;
4748 font *f = font_table[fn];
4749 if (f == 0) {
4750 error("no font mounted at `%1'", fn);
4751 return;
4753 if (!f->contains(g)) {
4754 error("font `%1' does not contain numbered character %2",
4755 f->get_name(),
4756 num);
4757 return;
4759 int w;
4760 if (nbsp_width)
4761 w = nbsp_width;
4762 else
4763 w = f->get_width(g, env->size);
4764 w = round_width(w);
4765 if (widthp)
4766 *widthp = w;
4767 set_char(g, f, env, w, 0);
4770 glyph *html_printer::set_char_and_width(const char *nm, const environment *env,
4771 int *widthp, font **f)
4773 glyph *g = name_to_glyph(nm);
4774 int fn = env->fontno;
4775 if (fn < 0 || fn >= nfonts) {
4776 error("bad font position `%1'", fn);
4777 return UNDEFINED_GLYPH;
4779 *f = font_table[fn];
4780 if (*f == 0) {
4781 error("no font mounted at `%1'", fn);
4782 return UNDEFINED_GLYPH;
4784 if (!(*f)->contains(g)) {
4785 if (nm[0] != '\0' && nm[1] == '\0')
4786 error("font `%1' does not contain ascii character `%2'",
4787 (*f)->get_name(),
4788 nm[0]);
4789 else
4790 error("font `%1' does not contain special character `%2'",
4791 (*f)->get_name(),
4792 nm);
4793 return UNDEFINED_GLYPH;
4795 int w = (*f)->get_width(g, env->size);
4796 w = round_width(w);
4797 if (widthp)
4798 *widthp = w;
4799 return g;
4803 * write_title - writes the title to this document
4806 void html_printer::write_title (int in_head)
4808 if (title.has_been_found) {
4809 if (in_head) {
4810 html.put_string("<title>");
4811 html.put_string(title.text);
4812 html.put_string("</title>").nl().nl();
4813 } else {
4814 title.has_been_written = true;
4815 if (title.with_h1) {
4816 if (dialect == xhtml)
4817 html.put_string("<h1>");
4818 else
4819 html.put_string("<h1 align=\"center\">");
4820 html.put_string(title.text);
4821 html.put_string("</h1>").nl().nl();
4824 } else if (in_head) {
4825 // place empty title tags to help conform to `tidy'
4826 html.put_string("<title></title>").nl();
4831 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
4834 static void write_rule (void)
4836 if (auto_rule) {
4837 if (dialect == xhtml)
4838 fputs("<hr/>\n", stdout);
4839 else
4840 fputs("<hr>\n", stdout);
4844 void html_printer::begin_page(int n)
4846 page_number = n;
4847 #if defined(DEBUGGING)
4848 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
4849 #endif
4850 no_of_printed_pages++;
4852 output_style.f = 0;
4853 output_style.point_size= -1;
4854 output_space_code = 32;
4855 output_draw_point_size = -1;
4856 output_line_thickness = -1;
4857 output_hpos = -1;
4858 output_vpos = -1;
4859 output_vpos_max = -1;
4860 current_paragraph = new html_text(&html, dialect);
4861 do_indent(get_troff_indent(), pageoffset, linelength);
4862 current_paragraph->do_para("", false);
4865 void html_printer::end_page(int)
4867 flush_sbuf();
4868 flush_page();
4871 font *html_printer::make_font(const char *nm)
4873 return html_font::load_html_font(nm);
4876 void html_printer::do_body (void)
4878 if (background == NULL)
4879 fputs("<body>\n\n", stdout);
4880 else {
4881 unsigned int r, g, b;
4882 char buf[6+1];
4884 background->get_rgb(&r, &g, &b);
4885 // we have to scale 0..0xFFFF to 0..0xFF
4886 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
4888 fputs("<body bgcolor=\"#", stdout);
4889 fputs(buf, stdout);
4890 fputs("\">\n\n", stdout);
4895 * emit_link - generates: <a href="to">name</a>
4898 void html_printer::emit_link (const string &to, const char *name)
4900 fputs("<a href=\"", stdout);
4901 fputs(to.contents(), stdout);
4902 fputs("\">", stdout);
4903 fputs(name, stdout);
4904 fputs("</a>", stdout);
4908 * write_navigation - writes out the links which navigate between
4909 * file fragments.
4912 void html_printer::write_navigation (const string &top, const string &prev,
4913 const string &next, const string &current)
4915 int need_bar = false;
4917 if (multiple_files) {
4918 current_paragraph->done_para();
4919 write_rule();
4920 if (roff_sig)
4921 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
4922 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
4923 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
4924 "<tr><td class=\"left\">", stdout);
4925 handle_valid_flag(false);
4926 fputs("[ ", stdout);
4927 if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
4928 emit_link(prev, "prev");
4929 need_bar = true;
4931 if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
4932 if (need_bar)
4933 fputs(" | ", stdout);
4934 emit_link(next, "next");
4935 need_bar = true;
4937 if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
4938 if (need_bar)
4939 fputs(" | ", stdout);
4940 emit_link(top, "top");
4942 fputs(" ]\n", stdout);
4944 if (roff_sig)
4945 fputs("</td><td class=\"right\"><small>"
4946 "This document was produced using "
4947 "<a href=\"" ROFF_URL "\">" T_ROFF " v" VERSION
4948 "</a>.</small></td></tr></table>\n",
4949 stdout);
4950 write_rule();
4955 * do_file_components - scan the file list copying each temporary
4956 * file in turn. This is used twofold:
4958 * firstly to emit section heading links,
4959 * between file fragments if required and
4960 * secondly to generate jobname file fragments
4961 * if required.
4964 void html_printer::do_file_components (void)
4966 int fragment_no = 1;
4967 string top;
4968 string prev;
4969 string next;
4970 string current;
4972 file_list.start_of_list();
4973 top = string(job_name);
4974 if (dialect == xhtml)
4975 top += string(".xhtml");
4976 else
4977 top += string(".html");
4978 top += '\0';
4979 next = file_list.next_file_name();
4980 next += '\0';
4981 current = next;
4982 while (file_list.get_file() != 0) {
4983 if (fseek(file_list.get_file(), 0L, 0) < 0)
4984 fatal("fseek on temporary file failed");
4985 html.copy_file(file_list.get_file());
4986 fclose(file_list.get_file());
4988 file_list.move_next();
4989 if (file_list.is_new_output_file()) {
4990 #ifdef LONG_FOR_TIME_T
4991 long t;
4992 #else
4993 time_t t;
4994 #endif
4996 if (fragment_no > 1)
4997 write_navigation(top, prev, next, current);
4998 prev = current;
4999 current = next;
5000 next = file_list.next_file_name();
5001 next += '\0';
5002 string split_file = file_list.file_name();
5003 split_file += '\0';
5004 fflush(stdout);
5005 freopen(split_file.contents(), "w", stdout);
5006 fragment_no++;
5007 if (dialect == xhtml)
5008 writeHeadMetaStyle();
5010 html.begin_comment("Creator : " T_ROFF " version " VERSION).end_comment();
5012 t = time(0);
5013 html.begin_comment("CreationDate: ")
5014 .put_string(ctime(&t), strlen(ctime(&t))-1)
5015 .end_comment();
5017 if (dialect == html4)
5018 writeHeadMetaStyle();
5020 html.put_string("<title>");
5021 html.put_string(split_file.contents());
5022 html.put_string("</title>").nl().nl();
5024 fputs(head_info.contents(), stdout);
5025 fputs("</head>\n", stdout);
5026 write_navigation(top, prev, next, current);
5028 if (file_list.are_links_required())
5029 header.write_headings(stdout, true);
5031 if (fragment_no > 1)
5032 write_navigation(top, prev, next, current);
5033 else {
5034 current_paragraph->done_para();
5035 write_rule();
5036 if (valid_flag) {
5037 if (roff_sig)
5038 fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
5039 "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
5040 "<colgroup><col class=\"left\"></col><col class=\"right\"></col></colgroup>\n"
5041 "<tr><td class=\"left\">", stdout);
5042 handle_valid_flag(true);
5043 if (roff_sig)
5044 fputs("</td><td class=\"right\"><small>"
5045 "This document was produced using "
5046 "<a href=\"" ROFF_URL "\">" T_ROFF " v" VERSION
5047 "</a>.</small></td></tr></table>\n",
5048 stdout);
5049 write_rule();
5055 * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
5056 * related information.
5059 void html_printer::writeHeadMetaStyle (void)
5061 if (dialect == html4) {
5062 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
5063 fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
5064 fputs("<html>\n", stdout);
5065 fputs("<head>\n", stdout);
5066 fputs("<meta name=\"generator\" "
5067 "content=\"" L_ROFF " -Thtml, see " ROFF_URL "\">\n", stdout);
5068 fputs("<meta http-equiv=\"Content-Type\" "
5069 "content=\"text/html; charset=US-ASCII\">\n", stdout);
5070 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
5071 fputs("<style type=\"text/css\">\n", stdout);
5073 else {
5074 fputs("<?xml version=\"1.0\" encoding=\"us-ascii\"?>\n", stdout);
5075 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\"\n", stdout);
5076 fputs(" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\"\n", stdout);
5077 fputs(" [<!ENTITY mathml \"http://www.w3.org/1998/Math/MathML\">]>\n", stdout);
5079 fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n",
5080 stdout);
5081 fputs("<head>\n", stdout);
5082 fputs("<meta name=\"generator\" "
5083 "content=\"" L_ROFF " -Txhtml, see " ROFF_URL "\"/>\n", stdout);
5084 fputs("<meta http-equiv=\"Content-Type\" "
5085 "content=\"text/html; charset=US-ASCII\"/>\n", stdout);
5086 fputs("<meta name=\"Content-Style\" content=\"text/css\"/>\n", stdout);
5087 fputs("<style type=\"text/css\">\n", stdout);
5088 fputs(" .center { text-align: center }\n", stdout);
5089 fputs(" .right { text-align: right }\n", stdout);
5091 fputs(" p { margin-top: 0; margin-bottom: 0; "
5092 "vertical-align: top }\n", stdout);
5093 fputs(" pre { margin-top: 0; margin-bottom: 0; "
5094 "vertical-align: top }\n", stdout);
5095 fputs(" table { margin-top: 0; margin-bottom: 0; "
5096 "vertical-align: top }\n", stdout);
5097 fputs(" h1 { text-align: center }\n", stdout);
5098 fputs("</style>\n", stdout);
5101 html_printer::~html_printer()
5103 #ifdef LONG_FOR_TIME_T
5104 long t;
5105 #else
5106 time_t t;
5107 #endif
5109 if (current_paragraph)
5110 current_paragraph->flush_text();
5111 html.end_line();
5112 html.set_file(stdout);
5114 if (dialect == xhtml)
5115 writeHeadMetaStyle();
5117 html.begin_comment("Creator : " T_ROFF " version " VERSION).end_comment();
5119 t = time(0);
5120 html.begin_comment("CreationDate: ")
5121 .put_string(ctime(&t), strlen(ctime(&t))-1)
5122 .end_comment();
5124 if (dialect == html4)
5125 writeHeadMetaStyle();
5127 write_title(true);
5128 head_info += '\0';
5129 fputs(head_info.contents(), stdout);
5130 fputs("</head>\n", stdout);
5131 do_body();
5133 write_title(false);
5134 header.write_headings(stdout, false);
5135 write_rule();
5136 #if defined(DEBUGGING)
5137 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
5138 #endif
5139 html.end_line();
5140 html.end_line();
5142 if (multiple_files) {
5143 fputs("</body>\n", stdout);
5144 fputs("</html>\n", stdout);
5145 do_file_components();
5146 } else {
5147 do_file_components();
5148 fputs("</body>\n", stdout);
5149 fputs("</html>\n", stdout);
5154 * get_str - returns a dupicate of string, s. The duplicate
5155 * string is terminated at the next ',' or ']'.
5158 static char *get_str (const char *s, char **n)
5160 int i=0;
5161 char *v;
5163 while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
5164 i++;
5165 if (i>0) {
5166 v = new char[i+1];
5167 memcpy(v, s, i+1);
5168 v[i] = (char)0;
5169 if (s[i] == ',')
5170 (*n) = (char *)&s[i+1];
5171 else
5172 (*n) = (char *)&s[i];
5173 return v;
5175 if (s[i] == ',')
5176 (*n) = (char *)&s[1];
5177 else
5178 (*n) = (char *)s;
5179 return NULL;
5183 * make_val - creates a string from if s is NULL.
5186 char *make_val (char *s, int v, char *id, char *f, char *l)
5188 if (s == NULL) {
5189 char buf[30];
5191 sprintf(buf, "%d", v);
5192 return strsave(buf);
5194 else {
5196 * check that value, s, is the same as, v.
5198 char *t = s;
5200 while (*t == '=')
5201 t++;
5202 if (atoi(t) != v) {
5203 if (f == NULL)
5204 f = (char *)"stdin";
5205 if (l == NULL)
5206 l = (char *)"<none>";
5207 fprintf(stderr,
5208 "%s:%s: " L_D_HTML " assertion failed at id%s; should: %d; got: %s\n",
5209 f, l, id, v, s);
5211 return s;
5216 * handle_assertion - handles the assertions created via .www:ASSERT
5217 * in www.tmac. See www.tmac for examples.
5218 * This method should be called as we are
5219 * parsing the ditroff input. It checks the x, y
5220 * position assertions. It does _not_ check the
5221 * troff state assertions as these are unknown at this
5222 * point.
5225 void html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
5227 char *n;
5228 char *cmd = get_str(s, &n);
5229 char *id = get_str(n, &n);
5230 char *val = get_str(n, &n);
5231 char *file= get_str(n, &n);
5232 char *line= get_str(n, &n);
5234 if (strcmp(cmd, "assertion:[x") == 0)
5235 as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
5236 else if (strcmp(cmd, "assertion:[y") == 0)
5237 as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
5238 else
5239 if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
5240 page_contents->add_tag(&sbuf_style, string(s),
5241 line_number, minv, minh, maxv, maxh);
5245 * build_state_assertion - builds the troff state assertions.
5248 void html_printer::handle_state_assertion (text_glob *g)
5250 if (g != NULL && g->is_a_tag() &&
5251 (strncmp(g->text_string, "assertion:[", 11) == 0)) {
5252 char *n = (char *)&g->text_string[11];
5253 char *cmd = get_str(n, &n);
5254 char *val = get_str(n, &n);
5255 (void)get_str(n, &n); // unused
5256 char *file= get_str(n, &n);
5257 char *line= get_str(n, &n);
5259 as.build(cmd, val, file, line);
5264 * special - handle all x X requests from troff. For post-html they
5265 * allow users to pass raw html commands, turn auto linked
5266 * headings off/on etc.
5269 void html_printer::special(char *s, const environment *env, char type)
5271 if (type != 'p')
5272 return;
5273 if (s != 0) {
5274 flush_sbuf();
5275 if (env->fontno >= 0) {
5276 style sty(get_font_from_index(env->fontno), env->size, env->height,
5277 env->slant, env->fontno, *env->col);
5278 sbuf_style = sty;
5281 if (strncmp(s, "html:", 5) == 0) {
5282 int r=font::res; /* resolution of the device */
5283 font *f=sbuf_style.f;
5285 if (f == NULL) {
5286 int found=false;
5288 f = font::load_font("TR", &found);
5292 * need to pass rest of string through to html output during flush
5294 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
5295 line_number,
5296 env->vpos-env->size*r/72, env->hpos,
5297 env->vpos , env->hpos,
5298 false);
5301 * assume that the html command has no width, if it does then
5302 * hopefully troff will have fudged this in a macro by
5303 * requesting that the formatting move right by the appropriate
5304 * amount.
5306 } else if ((strncmp(s, "html</p>:", 9) == 0) ||
5307 (strncmp(s, "html<?p>:", 9) == 0) ||
5308 (strncmp(s, "math<?p>:", 9) == 0)) {
5309 int r=font::res; /* resolution of the device */
5310 font *f=sbuf_style.f;
5311 string t;
5313 if (f == NULL) {
5314 int found=false;
5316 f = font::load_font("TR", &found);
5319 if (strncmp(s, "math<?p>:", 9) == 0) {
5320 if (strncmp((char *)&s[9], "<math>", 6) == 0) {
5321 s[9] = '\0';
5322 t = s;
5323 t += "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
5324 t += (char *)&s[15];
5325 t += '\0';
5326 s = (char *)&t[0];
5331 * need to pass all of string through to html output during flush
5333 page_contents->add_and_encode(&sbuf_style, string(s),
5334 line_number,
5335 env->vpos-env->size*r/72, env->hpos,
5336 env->vpos , env->hpos,
5337 true);
5340 * assume that the html command has no width, if it does then
5341 * hopefully troff will have fudged this in a macro by
5342 * requesting that the formatting move right by the appropriate
5343 * amount.
5346 } else if (strncmp(s, "index:", 6) == 0) {
5347 cutoff_heading = atoi(&s[6]);
5348 } else if (strncmp(s, "assertion:[", 11) == 0) {
5349 int r=font::res; /* resolution of the device */
5351 handle_assertion(env->vpos-env->size*r/72, env->hpos,
5352 env->vpos, env->hpos, s);
5358 * devtag - handles device troff tags sent from the `troff'.
5359 * These include the troff state machine tags:
5360 * .br, .sp, .in, .tl, .ll etc
5362 * (see man 5 grohtml_tags).
5365 void html_printer::devtag (char *s, const environment *env, char type)
5367 if (type != 'p')
5368 return;
5370 if (s != 0) {
5371 flush_sbuf();
5372 if (env->fontno >= 0) {
5373 style sty(get_font_from_index(env->fontno), env->size, env->height,
5374 env->slant, env->fontno, *env->col);
5375 sbuf_style = sty;
5378 if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
5379 int r=font::res; /* resolution of the device */
5381 page_contents->add_tag(&sbuf_style, string(s),
5382 line_number,
5383 env->vpos-env->size*r/72, env->hpos,
5384 env->vpos , env->hpos);
5391 * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
5394 int html_printer::round_width(int x)
5396 int r = font::hor;
5397 int n;
5399 // don't depend on the rounding direction for division of negative integers
5400 if (r == 1)
5401 n = x;
5402 else
5403 n = (x < 0
5404 ? -((-x + r/2 - 1)/r)
5405 : (x + r/2 - 1)/r);
5406 return n * r;
5410 * handle_valid_flag - emits a valid xhtml 1.1 or html-4.01 button, provided -V
5411 * was supplied on the command line.
5414 void html_printer::handle_valid_flag (int needs_para)
5416 if (valid_flag) {
5417 if (needs_para)
5418 fputs("<p>", stdout);
5419 if (dialect == xhtml)
5420 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5421 "src=\"http://www.w3.org/Icons/valid-xhtml11-blue\" "
5422 "alt=\"Valid XHTML 1.1 Transitional\" height=\"31\" width=\"88\" /></a>\n",
5423 stdout);
5424 else
5425 fputs("<a href=\"http://validator.w3.org/check?uri=referer\"><img "
5426 "src=\"http://www.w3.org/Icons/valid-html401-blue\" "
5427 "alt=\"Valid HTML 4.01 Transitional\" height=\"31\" width=\"88\"></a>\n",
5428 stdout);
5429 if (needs_para)
5430 fputs("</p>", stdout);
5434 int main(int argc, char **argv)
5436 program_name = argv[0];
5437 static char stderr_buf[BUFSIZ];
5438 setbuf(stderr, stderr_buf);
5439 int c;
5440 static const struct option long_options[] = {
5441 { "help", no_argument, 0, CHAR_MAX + 1 },
5442 { "version", no_argument, 0, 'v' },
5443 { NULL, 0, 0, 0 }
5445 while ((c = getopt_long(argc, argv, "a:bdD:eF:g:hi:I:j:lno:prs:S:vVx:y",
5446 long_options, NULL))
5447 != EOF)
5448 switch(c) {
5449 case 'a':
5450 /* text antialiasing bits - handled by pre-html */
5451 break;
5452 case 'b':
5453 // set background color to white
5454 default_background = new color;
5455 default_background->set_gray(color::MAX_COLOR_VAL);
5456 break;
5457 case 'd':
5458 /* handled by pre-html */
5459 break;
5460 case 'D':
5461 /* handled by pre-html */
5462 break;
5463 case 'e':
5464 /* handled by pre-html */
5465 break;
5466 case 'F':
5467 font::command_line_font_dir(optarg);
5468 break;
5469 case 'g':
5470 /* graphic antialiasing bits - handled by pre-html */
5471 break;
5472 case 'h':
5473 /* do not use the Hn headings of html, but manufacture our own */
5474 manufacture_headings = true;
5475 break;
5476 case 'i':
5477 /* handled by pre-html */
5478 break;
5479 case 'I':
5480 /* handled by pre-html */
5481 break;
5482 case 'j':
5483 multiple_files = true;
5484 job_name = optarg;
5485 break;
5486 case 'l':
5487 auto_links = false;
5488 break;
5489 case 'n':
5490 simple_anchors = true;
5491 break;
5492 case 'o':
5493 /* handled by pre-html */
5494 break;
5495 case 'p':
5496 /* handled by pre-html */
5497 break;
5498 case 'r':
5499 auto_rule = false;
5500 break;
5501 case 's':
5502 base_point_size = atoi(optarg);
5503 break;
5504 case 'S':
5505 split_level = atoi(optarg) + 1;
5506 break;
5507 case 'v':
5508 puts(L_D_HTML " (" T_ROFF ") v" VERSION);
5509 exit(0);
5510 break;
5511 case 'V':
5512 valid_flag = true;
5513 break;
5514 case 'x':
5515 if (strcmp(optarg, "x") == 0) {
5516 dialect = xhtml;
5517 simple_anchors = true;
5518 } else if (strcmp(optarg, "4") == 0)
5519 dialect = html4;
5520 else
5521 printf("unsupported html dialect %s (defaulting to html4)\n", optarg);
5522 break;
5523 case 'y':
5524 roff_sig = true;
5525 break;
5526 case CHAR_MAX + 1: // --help
5527 usage(stdout);
5528 exit(0);
5529 break;
5530 case '?':
5531 usage(stderr);
5532 exit(1);
5533 break;
5534 default:
5535 assert(0);
5537 if (optind >= argc) {
5538 do_file("-");
5539 } else {
5540 for (int i = optind; i < argc; i++)
5541 do_file(argv[i]);
5543 return 0;
5546 static void usage(FILE *stream)
5548 fprintf(stream,
5549 "Synopsis: %s [-vbelnhVy] [-D dir] "
5550 "[-I image_stem] [-F dir] [-x x] [files ...]\n",
5551 program_name);
5554 // s-it2-mode