* src/devices/grohtml/post-html.cc (html_printer::emit_raw,
[s-roff.git] / src / devices / grohtml / post-html.cc
blob854bd37298e9b916541ceb386b54aa4d900e5268
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc
5 * but it owes a huge amount of ideas and raw code from
6 * James Clark (jjc@jclark.com) grops/ps.cc.
7 */
9 /*
10 This file is part of groff.
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 for more details.
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING. If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 #include "driver.h"
27 #include "stringclass.h"
28 #include "cset.h"
29 #include "html.h"
30 #include "html-text.h"
32 #include <time.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
38 #include <stdio.h>
39 #include <fcntl.h>
41 extern "C" const char *Version_string;
43 #if !defined(TRUE)
44 # define TRUE (1==1)
45 #endif
46 #if !defined(FALSE)
47 # define FALSE (1==0)
48 #endif
50 #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
51 #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
52 #define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */
53 #define CENTER_TOLERANCE 2 /* how many pixels off center will we still */
54 #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
55 #define UNICODE_DESC_START 0x80 /* all character entities above this are */
56 /* either encoded by their glyph names or if */
57 /* there is no name then we use &#nnn; */
58 #define INDENTATION /* #undef INDENTATION to remove .in handling */
60 typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
63 * prototypes
66 char *get_html_translation (font *f, const string &name);
67 int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
70 static int auto_links = TRUE; /* by default we enable automatic links at */
71 /* top of the document. */
72 static int auto_rule = TRUE; /* by default we enable an automatic rule */
73 /* at the top and bottom of the document */
74 static int simple_anchors = FALSE; /* default to anchors with heading text */
75 static int manufacture_headings = FALSE; /* default is to use the Hn html headings, */
76 /* rather than manufacture our own. */
77 static color *default_background = NULL; /* has user requested initial bg color? */
81 * start with a few favorites
84 void stop () {}
86 static int min (int a, int b)
88 if (a < b) {
89 return( a );
90 } else {
91 return( b );
95 static int max (int a, int b)
97 if (a > b) {
98 return( a );
99 } else {
100 return( b );
105 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
108 static int is_intersection (int a1, int a2, int b1, int b2)
110 // again easier to prove NOT outside limits
111 return( ! ((a1 > b2) || (a2 < b1)) );
115 * is_digit - returns TRUE if character, ch, is a digit.
118 static int is_digit (char ch)
120 return( (ch >= '0') && (ch <= '9') );
124 * the classes and methods for maintaining a list of files.
127 struct file {
128 FILE *fp;
129 file *next;
131 file (FILE *f);
135 * file - initialize all fields to NULL
138 file::file (FILE *f)
139 : fp(f), next(0)
143 class files {
144 public:
145 files ();
146 FILE *get_file (void);
147 void start_of_list (void);
148 void move_next (void);
149 void add_new_file (FILE *f);
150 private:
151 file *head;
152 file *tail;
153 file *ptr;
157 * files - create an empty list of files.
160 files::files ()
161 : head(0), tail(0), ptr(0)
166 * get_file - returns the FILE associated with ptr.
169 FILE *files::get_file (void)
171 if (ptr) {
172 return( ptr->fp );
173 } else {
174 return( 0 );
179 * start_of_list - reset the ptr to the start of the list.
182 void files::start_of_list (void)
184 ptr = head;
188 * move_next - moves the ptr to the next element on the list.
191 void files::move_next (void)
193 if (ptr != 0)
194 ptr = ptr->next;
198 * add_new_file - adds a new file, f, to the list.
201 void files::add_new_file (FILE *f)
203 if (head == 0) {
204 head = new file(f);
205 tail = head;
206 } else {
207 tail->next = new file(f);
208 tail = tail->next;
210 ptr = tail;
214 * the class and methods for styles
217 struct style {
218 font *f;
219 int point_size;
220 int font_no;
221 int height;
222 int slant;
223 color col;
224 style ();
225 style (font *, int, int, int, int, color);
226 int operator == (const style &) const;
227 int operator != (const style &) const;
230 style::style()
231 : f(0)
235 style::style(font *p, int sz, int h, int sl, int no, color c)
236 : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
240 int style::operator==(const style &s) const
242 return (f == s.f && point_size == s.point_size
243 && height == s.height && slant == s.slant && col == s.col);
246 int style::operator!=(const style &s) const
248 return !(*this == s);
252 * the class and methods for retaining ascii text
255 struct char_block {
256 enum { SIZE = 256 };
257 char buffer[SIZE];
258 int used;
259 char_block *next;
261 char_block();
264 char_block::char_block()
265 : used(0), next(0)
269 class char_buffer {
270 public:
271 char_buffer();
272 ~char_buffer();
273 char *add_string(const char *, unsigned int);
274 char *add_string(const string &);
275 private:
276 char_block *head;
277 char_block *tail;
280 char_buffer::char_buffer()
281 : head(0), tail(0)
285 char_buffer::~char_buffer()
287 while (head != 0) {
288 char_block *temp = head;
289 head = head->next;
290 delete temp;
294 char *char_buffer::add_string (const char *s, unsigned int length)
296 int i=0;
297 unsigned int old_used;
299 if (s == NULL || length == 0)
300 return NULL;
302 if (tail == 0) {
303 tail = new char_block;
304 head = tail;
305 } else {
306 if (tail->used + length+1 > char_block::SIZE) {
307 tail->next = new char_block;
308 tail = tail->next;
311 // at this point we have a tail which is ready for the string.
312 if (tail->used + length+1 > char_block::SIZE) {
313 fatal("need to increase char_block::SIZE");
316 old_used = tail->used;
317 do {
318 tail->buffer[tail->used] = s[i];
319 tail->used++;
320 i++;
321 length--;
322 } while (length>0);
324 // add terminating nul character
326 tail->buffer[tail->used] = '\0';
327 tail->used++;
329 // and return start of new string
331 return( &tail->buffer[old_used] );
334 char *char_buffer::add_string (const string &s)
336 return add_string(s.contents(), s.length());
340 * the classes and methods for maintaining glyph positions.
343 class text_glob {
344 public:
345 void text_glob_html (style *s, char *str, int length,
346 int min_vertical, int min_horizontal,
347 int max_vertical, int max_horizontal);
348 void text_glob_special (style *s, char *str, int length,
349 int min_vertical, int min_horizontal,
350 int max_vertical, int max_horizontal);
351 void text_glob_line (style *s,
352 int min_vertical, int min_horizontal,
353 int max_vertical, int max_horizontal,
354 int thickness);
355 void text_glob_auto_image(style *s, char *str, int length,
356 int min_vertical, int min_horizontal,
357 int max_vertical, int max_horizontal);
358 void text_glob_tag (style *s, char *str, int length,
359 int min_vertical, int min_horizontal,
360 int max_vertical, int max_horizontal);
362 text_glob (void);
363 ~text_glob (void);
364 int is_a_line (void);
365 int is_a_tag (void);
366 int is_eol (void);
367 int is_auto_img (void);
368 int is_br (void);
369 int is_eol_ce (void);
371 style text_style;
372 const char *text_string;
373 unsigned int text_length;
374 int minv, minh, maxv, maxh;
375 int is_tag; // is this a .br, .sp, .tl etc
376 int is_img_auto; // image created by eqn delim
377 int is_special; // text has come via 'x X html:'
378 int is_line; // is the command a <line>?
379 int thickness; // the thickness of a line
381 private:
382 text_glob (style *s, char *str, int length,
383 int min_vertical , int min_horizontal,
384 int max_vertical , int max_horizontal,
385 bool is_troff_command,
386 bool is_auto_image, bool is_special_command,
387 bool is_a_line , int thickness);
390 text_glob::text_glob (style *s, char *str, int length,
391 int min_vertical, int min_horizontal,
392 int max_vertical, int max_horizontal,
393 bool is_troff_command,
394 bool is_auto_image, bool is_special_command,
395 bool is_a_line, int line_thickness)
396 : text_style(*s), text_string(str), text_length(length),
397 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
398 is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
399 is_line(is_a_line), thickness(line_thickness)
403 text_glob::text_glob ()
404 : text_string(0), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
405 is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0)
409 text_glob::~text_glob ()
414 * text_glob_html - used to place html text into the glob buffer.
417 void text_glob::text_glob_html (style *s, char *str, int length,
418 int min_vertical , int min_horizontal,
419 int max_vertical , int max_horizontal)
421 text_glob *g = new text_glob(s, str, length,
422 min_vertical, min_horizontal, max_vertical, max_horizontal,
423 FALSE, FALSE, FALSE, FALSE, 0);
424 *this = *g;
428 * text_glob_html - used to place html specials into the glob buffer.
429 * This text is essentially html commands coming through
430 * from the macro sets, with special designated sequences of
431 * characters translated into html. See add_and_encode.
434 void text_glob::text_glob_special (style *s, char *str, int length,
435 int min_vertical , int min_horizontal,
436 int max_vertical , int max_horizontal)
438 text_glob *g = new text_glob(s, str, length,
439 min_vertical, min_horizontal, max_vertical, max_horizontal,
440 FALSE, FALSE, TRUE, FALSE, 0);
441 *this = *g;
445 * text_glob_line - record horizontal draw line commands.
448 void text_glob::text_glob_line (style *s,
449 int min_vertical , int min_horizontal,
450 int max_vertical , int max_horizontal,
451 int thickness)
453 text_glob *g = new text_glob(s, "", 0,
454 min_vertical, min_horizontal, max_vertical, max_horizontal,
455 FALSE, FALSE, FALSE, TRUE, thickness);
456 *this = *g;
460 * text_glob_auto_image - record the presence of a .auto-image tag command.
461 * Used to mark that an image has been created automatically
462 * by a preprocessor and (pre-grohtml/troff) combination.
463 * Under some circumstances images may not be created.
464 * (consider .EQ
465 * delim $$
466 * .EN
467 * .TS
468 * tab(!), center;
469 * l!l.
470 * $1 over x$!recripical of x
471 * .TE
473 * the first auto-image marker is created via .EQ/.EN pair
474 * and no image is created.
475 * The second auto-image marker occurs at $1 over x$
476 * Currently this image will not be created
477 * as the whole of the table is created as an image.
478 * (Once html tables are handled by grohtml this will change.
479 * Shortly this will be the case).
482 void text_glob::text_glob_auto_image(style *s, char *str, int length,
483 int min_vertical, int min_horizontal,
484 int max_vertical, int max_horizontal)
486 text_glob *g = new text_glob(s, str, length,
487 min_vertical, min_horizontal, max_vertical, max_horizontal,
488 TRUE, TRUE, FALSE, FALSE, 0);
489 *this = *g;
493 * text_glob_tag - records a troff tag.
496 void text_glob::text_glob_tag (style *s, char *str, int length,
497 int min_vertical, int min_horizontal,
498 int max_vertical, int max_horizontal)
500 text_glob *g = new text_glob(s, str, length,
501 min_vertical, min_horizontal, max_vertical, max_horizontal,
502 TRUE, FALSE, FALSE, FALSE, 0);
503 *this = *g;
507 * is_a_line - returns TRUE if glob should be converted into an <hr>
510 int text_glob::is_a_line (void)
512 return is_line;
516 * is_a_tag - returns TRUE if glob contains a troff directive.
519 int text_glob::is_a_tag (void)
521 return is_tag;
525 * is_eol - returns TRUE if glob contains the tag eol
528 int text_glob::is_eol (void)
530 return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) );
534 * is_eol_ce - returns TRUE if glob contains the tag eol.ce
537 int text_glob::is_eol_ce (void)
539 return( is_tag && (strcmp(text_string, "html-tag:eol.ce") == 0) );
543 * is_auto_img - returns TRUE if the glob contains an automatically
544 * generated image.
547 int text_glob::is_auto_img (void)
549 return is_img_auto;
553 * is_br - returns TRUE if the glob is a tag containing a .br
554 * or an implied .br
557 int text_glob::is_br (void)
559 return( is_a_tag() && ((strcmp ("html-tag:.br", text_string) == 0) ||
560 (strncmp("html-tag:.sp", text_string, 11) == 0) ||
561 (strcmp ("html-tag:.ce", text_string) == 0) ||
562 (strcmp ("html-tag:.nf", text_string) == 0)) );
566 * the class and methods used to construct ordered double linked lists.
567 * In a previous implementation we used templates via #include "ordered-list.h",
568 * but this does assume that all C++ compilers can handle this feature. Pragmatically
569 * it is safer to assume this is not the case.
572 struct element_list {
573 element_list *right;
574 element_list *left;
575 text_glob *datum;
576 int lineno;
577 int minv, minh, maxv, maxh;
579 element_list (text_glob *d,
580 int line_number,
581 int min_vertical, int min_horizontal,
582 int max_vertical, int max_horizontal);
583 element_list ();
586 element_list::element_list ()
587 : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
592 * element_list - create a list element assigning the datum and region parameters.
595 element_list::element_list (text_glob *in,
596 int line_number,
597 int min_vertical, int min_horizontal,
598 int max_vertical, int max_horizontal)
599 : right(0), left(0), datum(in), lineno(line_number),
600 minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
604 class list {
605 public:
606 list ();
607 ~list ();
608 int is_less (element_list *a, element_list *b);
609 void add (text_glob *in,
610 int line_number,
611 int min_vertical, int min_horizontal,
612 int max_vertical, int max_horizontal);
613 void sub_move_right (void);
614 void move_right (void);
615 void move_left (void);
616 int is_empty (void);
617 int is_equal_to_tail (void);
618 int is_equal_to_head (void);
619 void start_from_head (void);
620 void start_from_tail (void);
621 text_glob *move_right_get_data (void);
622 text_glob *move_left_get_data (void);
623 text_glob *get_data (void);
624 private:
625 element_list *head;
626 element_list *tail;
627 element_list *ptr;
631 * list - construct an empty list.
634 list::list ()
635 : head(0), tail(0), ptr(0)
640 * ~list - destroy a complete list.
643 list::~list()
645 element_list *temp=head;
647 do {
648 temp = head;
649 if (temp != 0) {
650 head = head->right;
651 delete temp;
653 } while ((head != 0) && (head != tail));
657 * is_less - returns TRUE if a is left of b if on the same line or
658 * if a is higher up the page than b.
661 int list::is_less (element_list *a, element_list *b)
663 // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
664 if (a->lineno < b->lineno) {
665 return( TRUE );
666 } else if (a->lineno > b->lineno) {
667 return( FALSE );
668 } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
669 return( a->minh < b->minh );
670 } else {
671 return( a->maxv < b->maxv );
676 * add - adds a datum to the list in the order specified by the region position.
679 void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
681 // create a new list element with datum and position fields initialized
682 element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
683 element_list *last;
685 if (head == 0) {
686 head = t;
687 tail = t;
688 t->left = t;
689 t->right = t;
690 } else {
691 last = tail;
693 while ((last != head) && (is_less(t, last))) {
694 last = last->left;
697 if (is_less(t, last)) {
698 t->right = last;
699 last->left->right = t;
700 t->left = last->left;
701 last->left = t;
702 // now check for a new head
703 if (last == head) {
704 head = t;
706 } else {
707 // add t beyond last
708 t->right = last->right;
709 t->left = last;
710 last->right->left = t;
711 last->right = t;
712 // now check for a new tail
713 if (last == tail) {
714 tail = t;
721 * sub_move_right - removes the element which is currently pointed to by ptr
722 * from the list and moves ptr to the right.
725 void list::sub_move_right (void)
727 element_list *t=ptr->right;
729 if (head == tail) {
730 head = 0;
731 if (tail != 0) {
732 delete tail;
734 tail = 0;
735 ptr = 0;
736 } else {
737 if (head == ptr) {
738 head = head->right;
740 if (tail == ptr) {
741 tail = tail->left;
743 ptr->left->right = ptr->right;
744 ptr->right->left = ptr->left;
745 ptr=t;
750 * start_from_head - assigns ptr to the head.
753 void list::start_from_head (void)
755 ptr = head;
759 * start_from_tail - assigns ptr to the tail.
762 void list::start_from_tail (void)
764 ptr = tail;
768 * is_empty - returns TRUE if the list has no elements.
771 int list::is_empty (void)
773 return( head == 0 );
777 * is_equal_to_tail - returns TRUE if the ptr equals the tail.
780 int list::is_equal_to_tail (void)
782 return( ptr == tail );
786 * is_equal_to_head - returns TRUE if the ptr equals the head.
789 int list::is_equal_to_head (void)
791 return( ptr == head );
795 * move_left - moves the ptr left.
798 void list::move_left (void)
800 ptr = ptr->left;
804 * move_right - moves the ptr right.
807 void list::move_right (void)
809 ptr = ptr->right;
813 * get_datum - returns the datum referenced via ptr.
816 text_glob* list::get_data (void)
818 return( ptr->datum );
822 * move_right_get_data - returns the datum referenced via ptr and moves
823 * ptr right.
826 text_glob* list::move_right_get_data (void)
828 ptr = ptr->right;
829 if (ptr == head) {
830 return( 0 );
831 } else {
832 return( ptr->datum );
837 * move_left_get_data - returns the datum referenced via ptr and moves
838 * ptr right.
841 text_glob* list::move_left_get_data (void)
843 ptr = ptr->left;
844 if (ptr == tail) {
845 return( 0 );
846 } else {
847 return( ptr->datum );
852 * page class and methods
855 class page {
856 public:
857 page (void);
858 void add (style *s, const string &str,
859 int line_number,
860 int min_vertical, int min_horizontal,
861 int max_vertical, int max_horizontal);
862 void add_tag (style *s, const string &str,
863 int line_number,
864 int min_vertical, int min_horizontal,
865 int max_vertical, int max_horizontal);
866 void add_and_encode (style *s, const string &str,
867 int line_number,
868 int min_vertical, int min_horizontal,
869 int max_vertical, int max_horizontal);
870 void add_line (style *s,
871 int line_number,
872 int x1, int y1, int x2, int y2,
873 int thickness);
874 void dump_page (void); // debugging method
876 // and the data
878 list glyphs; // position of glyphs and specials on page
879 char_buffer buffer; // all characters for this page
882 page::page()
887 * add - add html text to the list of glyphs.
890 void page::add (style *s, const string &str,
891 int line_number,
892 int min_vertical, int min_horizontal,
893 int max_vertical, int max_horizontal)
895 if (str.length() > 0) {
896 text_glob *g=new text_glob();
897 g->text_glob_html(s, buffer.add_string(str), str.length(),
898 min_vertical, min_horizontal, max_vertical, max_horizontal);
899 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
904 * add_tag - adds a troff tag, for example: .tl .sp .br
907 void page::add_tag (style *s, const string &str,
908 int line_number,
909 int min_vertical, int min_horizontal,
910 int max_vertical, int max_horizontal)
912 if (str.length() > 0) {
913 text_glob *g;
915 if (strncmp((str+'\0').contents(), "html-tag:.auto-image", 20) == 0) {
916 g = new text_glob();
917 g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
918 min_vertical, min_horizontal, max_vertical, max_horizontal);
919 } else {
920 g = new text_glob();
921 g->text_glob_tag(s, buffer.add_string(str), str.length(),
922 min_vertical, min_horizontal, max_vertical, max_horizontal);
924 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
929 * add_line - adds the <line> primitive providing that y1==y2
932 void page::add_line (style *s,
933 int line_number,
934 int x1, int y1, int x2, int y2,
935 int thickness)
937 if (y1 == y2) {
938 text_glob *g = new text_glob();
939 g->text_glob_line(s,
940 min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2),
941 thickness);
942 glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2));
947 * to_unicode - returns a unicode translation of char, ch.
950 static char *to_unicode (unsigned char ch)
952 static char buf[20];
954 sprintf(buf, "&#%u;", (unsigned int)ch);
955 return buf;
959 * add_and_encode - adds a special string to the page, it translates the string
960 * into html glyphs. The special string will have come from x X html:
961 * and can contain troff character encodings which appear as
962 * \(char\). A sequence of \\ represents \.
963 * So for example we can write:
964 * "cost = \(Po\)3.00 file = \\foo\\bar"
965 * which is translated into:
966 * "cost = &pound;3.00 file = \foo\bar"
969 void page::add_and_encode (style *s, const string &str,
970 int line_number,
971 int min_vertical, int min_horizontal,
972 int max_vertical, int max_horizontal)
974 string html_string;
975 char *html_glyph;
976 int i=0;
978 if (s->f == NULL)
979 return;
980 while (i < str.length()) {
981 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
982 // start of escape
983 i += 2; // move over \(
984 int a = i;
985 while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
986 i++;
988 int n = i;
989 if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
990 i++;
991 else
992 n = -1;
993 if (n > 0) {
994 string troff_charname = str.substring(a, n-a);
995 html_glyph = get_html_translation(s->f, troff_charname);
996 if (html_glyph)
997 html_string += html_glyph;
998 else {
999 int index=s->f->name_to_index((troff_charname + '\0').contents());
1001 if (s->f->contains(index) && (index != 0))
1002 html_string += s->f->get_code(index);
1005 } else
1006 html_string += str[i];
1007 i++;
1009 if (html_string.length() > 0) {
1010 text_glob *g=new text_glob();
1011 g->text_glob_special(s, buffer.add_string(html_string), html_string.length(),
1012 min_vertical, min_horizontal, max_vertical, max_horizontal);
1013 glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1018 * dump_page - dump the page contents for debugging purposes.
1021 void page::dump_page(void)
1023 text_glob *g;
1025 printf("\n\ndebugging start\n");
1026 glyphs.start_from_head();
1027 do {
1028 g = glyphs.get_data();
1029 printf("%s ", g->text_string);
1030 glyphs.move_right();
1031 } while (! glyphs.is_equal_to_head());
1032 printf("\ndebugging end\n\n");
1036 * font classes and methods
1039 class html_font : public font {
1040 html_font(const char *);
1041 public:
1042 int encoding_index;
1043 char *encoding;
1044 char *reencoded_name;
1045 ~html_font();
1046 static html_font *load_html_font(const char *);
1049 html_font *html_font::load_html_font(const char *s)
1051 html_font *f = new html_font(s);
1052 if (!f->load()) {
1053 delete f;
1054 return 0;
1056 return f;
1059 html_font::html_font(const char *nm)
1060 : font(nm)
1064 html_font::~html_font()
1069 * a simple class to contain the header to this document
1072 class title_desc {
1073 public:
1074 title_desc ();
1075 ~title_desc ();
1077 int has_been_written;
1078 int has_been_found;
1079 int with_h1;
1080 string text;
1084 title_desc::title_desc ()
1085 : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1089 title_desc::~title_desc ()
1093 class header_desc {
1094 public:
1095 header_desc ();
1096 ~header_desc ();
1098 int no_of_headings; // how many headings have we found?
1099 char_buffer headings; // all the headings used in the document
1100 list headers; // list of headers built from .NH and .SH
1101 int header_level; // current header level
1102 int written_header; // have we written the header yet?
1103 string header_buffer; // current header text
1105 void write_headings (FILE *f, int force);
1108 header_desc::header_desc ()
1109 : no_of_headings(0), header_level(2), written_header(0)
1113 header_desc::~header_desc ()
1118 * write_headings - emits a list of links for the headings in this document
1121 void header_desc::write_headings (FILE *f, int force)
1123 text_glob *g;
1125 if (auto_links || force) {
1126 if (! headers.is_empty()) {
1127 int h=1;
1129 headers.start_from_head();
1130 do {
1131 g = headers.get_data();
1132 fputs("<a href=\"#", f);
1133 if (simple_anchors) {
1134 string buffer(ANCHOR_TEMPLATE);
1136 buffer += as_string(h);
1137 buffer += '\0';
1138 fprintf(f, buffer.contents());
1139 } else
1140 fputs(g->text_string, f);
1141 h++;
1142 fputs("\">", f);
1143 fputs(g->text_string, f);
1144 fputs("</a><br>\n", f);
1145 headers.move_right();
1146 } while (! headers.is_equal_to_head());
1147 fputs("\n", f);
1152 class html_printer : public printer {
1153 files file_list;
1154 simple_output html;
1155 int res;
1156 int space_char_index;
1157 int space_width;
1158 int no_of_printed_pages;
1159 int paper_length;
1160 string sbuf;
1161 int sbuf_start_hpos;
1162 int sbuf_vpos;
1163 int sbuf_end_hpos;
1164 int sbuf_prev_hpos;
1165 int sbuf_kern;
1166 style sbuf_style;
1167 int last_sbuf_length;
1168 int overstrike_detected;
1169 style output_style;
1170 int output_hpos;
1171 int output_vpos;
1172 int output_vpos_max;
1173 int output_draw_point_size;
1174 int line_thickness;
1175 int output_line_thickness;
1176 unsigned char output_space_code;
1177 char *inside_font_style;
1178 int page_number;
1179 title_desc title;
1180 title_desc indent; // use title class to remember $1 of .ip
1181 header_desc header;
1182 int header_indent;
1183 int supress_sub_sup;
1184 int cutoff_heading;
1185 page *page_contents;
1186 html_text *current_paragraph;
1187 int end_center;
1188 int end_tempindent;
1189 TAG_ALIGNMENT next_tag;
1190 int fill_on;
1191 int linelength;
1192 int pageoffset;
1193 int indentation;
1194 int prev_indent;
1195 int pointsize;
1196 int vertical_spacing;
1197 int line_number;
1198 color *background;
1200 void flush_sbuf ();
1201 void set_style (const style &);
1202 void set_space_code (unsigned char c);
1203 void do_exec (char *, const environment *);
1204 void do_import (char *, const environment *);
1205 void do_def (char *, const environment *);
1206 void do_mdef (char *, const environment *);
1207 void do_file (char *, const environment *);
1208 void set_line_thickness (const environment *);
1209 void terminate_current_font (void);
1210 void flush_font (void);
1211 void add_to_sbuf (unsigned char code, const string &s);
1212 void write_title (int in_head);
1213 int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w);
1214 void flush_page (void);
1215 void troff_tag (text_glob *g);
1216 void flush_globs (void);
1217 void emit_line (text_glob *g);
1218 void emit_raw (text_glob *g);
1219 void emit_html (text_glob *g);
1220 void determine_space (text_glob *g);
1221 void start_font (const char *name);
1222 void end_font (const char *name);
1223 int is_font_courier (font *f);
1224 int is_courier_until_eol (void);
1225 void start_size (int from, int to);
1226 void do_font (text_glob *g);
1227 void do_center (char *arg);
1228 void do_break (void);
1229 void do_eol (void);
1230 void do_title (void);
1231 void do_fill (int on);
1232 void do_heading (char *arg);
1233 void write_header (void);
1234 void determine_header_level (int level);
1235 void do_linelength (char *arg);
1236 void do_pageoffset (char *arg);
1237 void do_indentation (char *arg);
1238 void do_tempindent (char *arg);
1239 void do_indentedparagraph (void);
1240 void do_verticalspacing (char *arg);
1241 void do_pointsize (char *arg);
1242 void do_centered_image (void);
1243 void do_left_image (void);
1244 void do_right_image (void);
1245 void do_auto_image (text_glob *g, const char *filename);
1246 void do_links (void);
1247 void do_flush (void);
1248 int is_in_middle (int left, int right);
1249 void do_sup_or_sub (text_glob *g);
1250 int start_subscript (text_glob *g);
1251 int end_subscript (text_glob *g);
1252 int start_superscript (text_glob *g);
1253 int end_superscript (text_glob *g);
1254 void outstanding_eol (int n);
1255 int is_bold (font *f);
1256 font *make_bold (font *f);
1257 int overstrike (unsigned char code, const char *name, const environment *env, int w);
1258 void do_body (void);
1259 // ADD HERE
1261 public:
1262 html_printer ();
1263 ~html_printer ();
1264 void set_char (int i, font *f, const environment *env, int w, const char *name);
1265 void draw (int code, int *p, int np, const environment *env);
1266 void begin_page (int);
1267 void end_page (int);
1268 void special (char *arg, const environment *env, char type);
1269 font *make_font (const char *);
1270 void end_of_line ();
1273 printer *make_printer()
1275 return new html_printer;
1278 static void usage(FILE *stream);
1280 void html_printer::set_style(const style &sty)
1282 const char *fontname = sty.f->get_name();
1283 if (fontname == NULL)
1284 fatal("no internalname specified for font");
1286 #if 0
1287 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
1288 #endif
1292 * is_bold - returns TRUE if font, f, is bold.
1295 int html_printer::is_bold (font *f)
1297 const char *fontname = f->get_name();
1298 return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
1302 * make_bold - if a bold font of, f, exists then return it.
1305 font *html_printer::make_bold (font *f)
1307 const char *fontname = f->get_name();
1309 if (strcmp(fontname, "B") == 0)
1310 return f;
1311 if (strcmp(fontname, "I") == 0)
1312 return font::load_font("BI");
1313 if (strcmp(fontname, "BI") == 0)
1314 return f;
1315 return NULL;
1318 void html_printer::end_of_line()
1320 flush_sbuf();
1321 line_number++;
1325 * emit_line - writes out a horizontal rule.
1328 void html_printer::emit_line (text_glob *g)
1330 // --fixme-- needs to know the length in percentage
1331 html.put_string("<hr>");
1335 * emit_raw - writes the raw html information directly to the device.
1338 void html_printer::emit_raw (text_glob *g)
1340 do_font(g);
1341 if (next_tag == INLINE) {
1342 determine_space(g);
1343 current_paragraph->do_emittext(g->text_string, g->text_length);
1344 } else {
1345 int in_table=current_paragraph->is_in_table();
1347 current_paragraph->done_para();
1348 switch (next_tag) {
1350 case CENTERED:
1351 current_paragraph->do_para("align=center");
1352 break;
1353 case LEFT:
1354 current_paragraph->do_para("align=left");
1355 break;
1356 case RIGHT:
1357 current_paragraph->do_para("align=right");
1358 break;
1359 default:
1360 fatal("unknown enumeration");
1362 current_paragraph->do_emittext(g->text_string, g->text_length);
1363 current_paragraph->done_para();
1364 next_tag = INLINE;
1365 supress_sub_sup = TRUE;
1366 #if defined(INDENTATION)
1367 if (in_table) {
1368 current_paragraph->do_indent(NULL, 0, pageoffset, linelength);
1369 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
1370 indent.text.clear();
1372 #endif
1377 * do_center - handle the .ce commands from troff.
1380 void html_printer::do_center (char *arg)
1382 int n = atoi(arg);
1384 current_paragraph->do_break();
1385 current_paragraph->done_para();
1386 supress_sub_sup = TRUE;
1388 if (n > 0) {
1389 current_paragraph->do_para("align=center");
1390 end_center += n;
1391 } else {
1392 end_center = 0;
1397 * do_centered_image - set a flag such that the next html-tag is
1398 * placed inside a centered paragraph.
1401 void html_printer::do_centered_image (void)
1403 next_tag = CENTERED;
1407 * do_right_image - set a flag such that the next html-tag is
1408 * placed inside a right aligned paragraph.
1411 void html_printer::do_right_image (void)
1413 next_tag = RIGHT;
1417 * do_left_image - set a flag such that the next html-tag is
1418 * placed inside a left aligned paragraph.
1421 void html_printer::do_left_image (void)
1423 next_tag = LEFT;
1427 * exists - returns TRUE if filename exists.
1430 static int exists (const char *filename)
1432 FILE *fp = fopen(filename, "r");
1434 if (fp == 0) {
1435 return( FALSE );
1436 } else {
1437 fclose(fp);
1438 return( TRUE );
1443 * generate_img_src - returns a html image tag for the filename
1444 * providing that the image exists.
1447 static string &generate_img_src (const char *filename)
1449 string *s = new string("");
1451 while (filename && (filename[0] == ' ')) {
1452 filename++;
1454 if (exists(filename))
1455 *s += string("<img src=\"") + filename + "\">";
1456 return *s;
1460 * do_auto_image - tests whether the image, indicated by filename,
1461 * is present, if so then it emits an html image tag.
1462 * An image tag may be passed through from pic, eqn
1463 * but the corresponding image might not be created.
1464 * Consider .EQ delim $$ .EN or an empty .PS .PE.
1467 void html_printer::do_auto_image (text_glob *g, const char *filename)
1469 string buffer = generate_img_src(filename);
1471 if (! buffer.empty()) {
1473 * utilize emit_raw by creating a new text_glob.
1475 text_glob h = *g;
1477 h.text_string = buffer.contents();
1478 h.text_length = buffer.length();
1479 emit_raw(&h);
1480 } else
1481 next_tag = INLINE;
1485 * outstanding_eol - call do_eol, n, times.
1488 void html_printer::outstanding_eol (int n)
1490 while (n > 0) {
1491 do_eol();
1492 n--;
1497 * do_title - handle the .tl commands from troff.
1500 void html_printer::do_title (void)
1502 text_glob *t;
1503 int removed_from_head;
1504 int eol_ce = 0;
1506 if (page_number == 1) {
1507 int found_title_start = FALSE;
1508 if (! page_contents->glyphs.is_empty()) {
1509 page_contents->glyphs.sub_move_right(); /* move onto next word */
1510 do {
1511 t = page_contents->glyphs.get_data();
1512 removed_from_head = FALSE;
1513 if (t->is_auto_img()) {
1514 string img = generate_img_src((char *)(t->text_string + 20));
1516 if (! img.empty()) {
1517 if (found_title_start)
1518 title.text += " ";
1519 found_title_start = TRUE;
1520 title.has_been_found = TRUE;
1521 title.text += img;
1523 page_contents->glyphs.sub_move_right(); /* move onto next word */
1524 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1525 (page_contents->glyphs.is_equal_to_head()));
1526 } else if (t->is_eol_ce()) {
1527 /* process the eol associated with .ce
1529 eol_ce++;
1530 page_contents->glyphs.sub_move_right(); /* move onto next word */
1531 } else if (t->is_eol()) {
1532 /* end of title found
1534 title.has_been_found = TRUE;
1535 outstanding_eol(eol_ce);
1536 return;
1537 } else if (t->is_a_tag()) {
1538 /* end of title found, but move back so that we read this tag and process it
1540 page_contents->glyphs.move_left(); /* move backwards to last word */
1541 title.has_been_found = TRUE;
1542 outstanding_eol(eol_ce);
1543 return;
1544 } else if (found_title_start) {
1545 title.text += " " + string(t->text_string, t->text_length);
1546 page_contents->glyphs.sub_move_right(); /* move onto next word */
1547 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1548 (page_contents->glyphs.is_equal_to_head()));
1549 } else {
1550 title.text += string(t->text_string, t->text_length);
1551 found_title_start = TRUE;
1552 title.has_been_found = TRUE;
1553 page_contents->glyphs.sub_move_right(); /* move onto next word */
1554 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1555 (page_contents->glyphs.is_equal_to_head()));
1557 } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
1559 outstanding_eol(eol_ce);
1563 void html_printer::write_header (void)
1565 if (! header.header_buffer.empty()) {
1566 if (header.header_level > 7) {
1567 header.header_level = 7;
1570 // firstly we must terminate any font and type faces
1571 current_paragraph->done_para();
1572 current_paragraph->done_table();
1573 supress_sub_sup = TRUE;
1575 if (cutoff_heading+2 > header.header_level) {
1576 // now we save the header so we can issue a list of links
1577 header.no_of_headings++;
1578 style st;
1580 text_glob *h=new text_glob();
1581 h->text_glob_html(&st,
1582 header.headings.add_string(header.header_buffer),
1583 header.header_buffer.length(),
1584 header.no_of_headings, header.header_level,
1585 header.no_of_headings, header.header_level);
1587 header.headers.add(h,
1588 header.no_of_headings,
1589 header.no_of_headings, header.no_of_headings,
1590 header.no_of_headings, header.no_of_headings); // and add this header to the header list
1592 // lastly we generate a tag
1594 html.nl().put_string("<a name=\"");
1595 if (simple_anchors) {
1596 string buffer(ANCHOR_TEMPLATE);
1598 buffer += as_string(header.no_of_headings);
1599 buffer += '\0';
1600 html.put_string(buffer.contents());
1601 } else {
1602 html.put_string(header.header_buffer);
1604 html.put_string("\"></a>").nl();
1607 if (manufacture_headings) {
1608 // line break before a header
1609 if (!current_paragraph->emitted_text())
1610 current_paragraph->do_space();
1611 // user wants manufactured headings which look better than <Hn></Hn>
1612 if (header.header_level<4) {
1613 html.put_string("<b><font size=\"+1\">");
1614 html.put_string(header.header_buffer);
1615 html.put_string("</font></b>").nl();
1617 else {
1618 html.put_string("<b>");
1619 html.put_string(header.header_buffer);
1620 html.put_string("</b>").nl();
1623 else {
1624 // and now we issue the real header
1625 html.put_string("<h");
1626 html.put_number(header.header_level);
1627 html.put_string(">");
1628 html.put_string(header.header_buffer);
1629 html.put_string("</h");
1630 html.put_number(header.header_level);
1631 html.put_string(">").nl();
1634 current_paragraph->do_para("");
1638 void html_printer::determine_header_level (int level)
1640 if (level == 0) {
1641 int i;
1643 for (i=0; ((i<header.header_buffer.length())
1644 && ((header.header_buffer[i] == '.')
1645 || is_digit(header.header_buffer[i]))) ; i++) {
1646 if (header.header_buffer[i] == '.') {
1647 level++;
1651 header.header_level = level+1;
1655 * do_heading - handle the .SH and .NH and equivalent commands from troff.
1658 void html_printer::do_heading (char *arg)
1660 text_glob *g;
1661 text_glob *l = 0;
1662 int level=atoi(arg);
1664 header.header_buffer.clear();
1665 page_contents->glyphs.move_right();
1666 if (! page_contents->glyphs.is_equal_to_head()) {
1667 g = page_contents->glyphs.get_data();
1668 do {
1669 if (g->is_auto_img()) {
1670 string img=generate_img_src((char *)(g->text_string + 20));
1672 if (! img.empty()) {
1673 simple_anchors = TRUE; // we cannot use full heading anchors with images
1674 if (l != 0)
1675 header.header_buffer += " ";
1677 l = g;
1678 header.header_buffer += img;
1680 } else if (! (g->is_a_line() || g->is_a_tag())) {
1682 * we ignore tags commands when constructing a heading
1684 if (l != 0)
1685 header.header_buffer += " ";
1686 l = g;
1688 header.header_buffer += string(g->text_string, g->text_length);
1690 page_contents->glyphs.move_right();
1691 g = page_contents->glyphs.get_data();
1692 } while ((! page_contents->glyphs.is_equal_to_head()) &&
1693 (! g->is_br()));
1696 determine_header_level(level);
1697 write_header();
1699 // finally set the output to neutral for after the header
1700 g = page_contents->glyphs.get_data();
1701 page_contents->glyphs.move_left(); // so that next time we use old g
1705 * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
1708 int html_printer::is_courier_until_eol (void)
1710 text_glob *orig = page_contents->glyphs.get_data();
1711 int result = TRUE;
1712 text_glob *g;
1714 if (! page_contents->glyphs.is_equal_to_tail()) {
1715 page_contents->glyphs.move_right();
1716 do {
1717 g = page_contents->glyphs.get_data();
1718 if (! is_font_courier(g->text_style.f)) {
1719 result = FALSE;
1721 page_contents->glyphs.move_right();
1722 } while ((result) &&
1723 (! page_contents->glyphs.is_equal_to_head()) &&
1724 (! g->is_eol()));
1727 * now restore our previous position.
1729 while (page_contents->glyphs.get_data() != orig) {
1730 page_contents->glyphs.move_left();
1733 return( result );
1737 * do_linelength - handle the .ll command from troff.
1740 void html_printer::do_linelength (char *arg)
1742 #if defined(INDENTATION)
1743 if (fill_on) {
1744 linelength = atoi(arg);
1745 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
1746 indent.text.clear();
1748 #endif
1752 * do_pageoffset - handle the .po command from troff.
1755 void html_printer::do_pageoffset (char *arg)
1757 #if defined(INDENTATION)
1758 pageoffset = atoi(arg);
1759 if (fill_on) {
1760 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
1761 indent.text.clear();
1763 #endif
1767 * do_indentation - handle the .in command from troff.
1770 void html_printer::do_indentation (char *arg)
1772 #if defined(INDENTATION)
1773 if (fill_on) {
1774 indentation = atoi(arg);
1775 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
1776 indent.text.clear();
1778 #endif
1782 * do_tempindent - handle the .ti command from troff.
1785 void html_printer::do_tempindent (char *arg)
1787 #if defined(INDENTATION)
1788 if (fill_on) {
1789 end_tempindent = 1;
1790 prev_indent = indentation;
1791 indentation = atoi(arg);
1792 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
1793 indent.text.clear();
1795 #endif
1799 * do_indentedparagraph - handle the .ip tag, this buffers the next line
1800 * and passes this to text-text as the left hand
1801 * column table entry.
1804 void html_printer::do_indentedparagraph (void)
1806 #if defined(INDENTATION)
1807 text_glob *t;
1808 int removed_from_head;
1809 int found_indent_start = FALSE;
1811 indent.has_been_found = FALSE;
1812 indent.text.clear();
1814 if (! page_contents->glyphs.is_empty()) {
1815 page_contents->glyphs.sub_move_right(); /* move onto next word */
1816 do {
1817 t = page_contents->glyphs.get_data();
1818 removed_from_head = FALSE;
1819 if (t->is_auto_img()) {
1820 string img = generate_img_src((char *)(t->text_string + 20));
1822 if (! img.empty()) {
1823 if (found_indent_start)
1824 indent.text += " ";
1826 found_indent_start = TRUE;
1827 indent.text += img;
1829 page_contents->glyphs.sub_move_right(); /* move onto next word */
1830 } else if (t->is_a_tag() && (strncmp(t->text_string, "html-tag:.br", 12) == 0)) {
1831 /* end of indented para found, but move back so that we read this tag and process it
1833 page_contents->glyphs.move_left(); /* move backwards to last word */
1834 indent.has_been_found = TRUE;
1835 return;
1836 } else if (t->is_a_tag()) {
1837 if (strncmp(t->text_string, "html-tag:.ti", 12) == 0) {
1838 char *a = (char *)t->text_string+12;
1839 do_tempindent(a);
1841 page_contents->glyphs.sub_move_right(); /* move onto next word */
1842 } else if (found_indent_start) {
1843 indent.text += " " + string(t->text_string, t->text_length);
1844 page_contents->glyphs.sub_move_right(); /* move onto next word */
1845 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1846 (page_contents->glyphs.is_equal_to_head()));
1847 } else {
1848 indent.text += string(t->text_string, t->text_length);
1849 found_indent_start = TRUE;
1850 indent.has_been_found = TRUE;
1851 page_contents->glyphs.sub_move_right(); /* move onto next word */
1852 removed_from_head = ((!page_contents->glyphs.is_empty()) &&
1853 (page_contents->glyphs.is_equal_to_head()));
1855 } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head));
1857 // page_contents->glyphs.move_left(); /* move backwards to last word */
1858 #endif
1862 * do_verticalspacing - handle the .vs command from troff.
1865 void html_printer::do_verticalspacing (char *arg)
1867 vertical_spacing = atoi(arg);
1871 * do_pointsize - handle the .ps command from troff.
1874 void html_printer::do_pointsize (char *arg)
1876 pointsize = atoi(arg);
1880 * do_fill - records whether troff has requested that text be filled.
1883 void html_printer::do_fill (int on)
1885 current_paragraph->do_break();
1886 output_hpos = indentation+pageoffset;
1887 supress_sub_sup = TRUE;
1889 if (fill_on != on) {
1890 if (on) {
1891 current_paragraph->done_pre();
1892 } else {
1893 current_paragraph->do_pre();
1896 fill_on = on;
1900 * do_eol - handle the end of line
1903 void html_printer::do_eol (void)
1905 if (! fill_on) {
1906 if (current_paragraph->emitted_text()) {
1907 current_paragraph->do_newline();
1908 current_paragraph->do_break();
1911 output_hpos = indentation+pageoffset;
1912 if (end_center > 0) {
1913 if (end_center > 1)
1914 if (current_paragraph->emitted_text())
1915 current_paragraph->do_break();
1917 end_center--;
1918 if (end_center == 0) {
1919 current_paragraph->done_para();
1920 supress_sub_sup = TRUE;
1926 * do_flush - flushes all output and tags.
1929 void html_printer::do_flush (void)
1931 current_paragraph->done_para();
1932 current_paragraph->done_table();
1936 * do_links - moves onto a new temporary file and sets auto_links to FALSE.
1939 void html_printer::do_links (void)
1941 current_paragraph->done_para();
1942 current_paragraph->done_table();
1943 auto_links = FALSE; /* from now on only emit under user request */
1944 file_list.add_new_file(xtmpfile());
1945 html.set_file(file_list.get_file());
1949 * do_break - handles the ".br" request and also
1950 * undoes an outstanding ".ti" command.
1953 void html_printer::do_break (void)
1955 current_paragraph->do_break();
1956 #if defined(INDENTATION)
1957 if (end_tempindent > 0) {
1958 end_tempindent--;
1959 if (end_tempindent == 0) {
1960 indentation = prev_indent;
1961 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
1962 indent.text.clear();
1965 #endif
1966 output_hpos = indentation+pageoffset;
1967 supress_sub_sup = TRUE;
1971 * troff_tag - processes the troff tag and manipulates the troff state machine.
1974 void html_printer::troff_tag (text_glob *g)
1977 * firstly skip over html-tag:
1979 char *t=(char *)g->text_string+9;
1981 if (g->is_eol()) {
1982 do_eol();
1983 } else if (g->is_eol_ce()) {
1984 do_eol();
1985 } else if (strncmp(t, ".sp", 3) == 0) {
1986 current_paragraph->do_space();
1987 supress_sub_sup = TRUE;
1988 } else if (strncmp(t, ".br", 3) == 0) {
1989 do_break();
1990 } else if (strcmp(t, ".centered-image") == 0) {
1991 do_centered_image();
1992 } else if (strcmp(t, ".right-image") == 0) {
1993 do_right_image();
1994 } else if (strcmp(t, ".left-image") == 0) {
1995 do_left_image();
1996 } else if (strncmp(t, ".auto-image", 11) == 0) {
1997 char *a = (char *)t+11;
1998 do_auto_image(g, a);
1999 } else if (strncmp(t, ".ce", 3) == 0) {
2000 char *a = (char *)t+3;
2001 supress_sub_sup = TRUE;
2002 do_center(a);
2003 } else if (strncmp(t, ".tl", 3) == 0) {
2004 supress_sub_sup = TRUE;
2005 title.with_h1 = TRUE;
2006 do_title();
2007 } else if (strncmp(t, ".html-tl", 8) == 0) {
2008 supress_sub_sup = TRUE;
2009 title.with_h1 = FALSE;
2010 do_title();
2011 } else if (strncmp(t, ".fi", 3) == 0) {
2012 do_fill(TRUE);
2013 } else if (strncmp(t, ".nf", 3) == 0) {
2014 do_fill(FALSE);
2015 } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
2016 char *a = (char *)t+3;
2017 do_heading(a);
2018 } else if (strncmp(t, ".ll", 3) == 0) {
2019 char *a = (char *)t+3;
2020 do_linelength(a);
2021 } else if (strncmp(t, ".po", 3) == 0) {
2022 char *a = (char *)t+3;
2023 do_pageoffset(a);
2024 } else if (strncmp(t, ".in", 3) == 0) {
2025 char *a = (char *)t+3;
2026 do_indentation(a);
2027 } else if (strncmp(t, ".ti", 3) == 0) {
2028 char *a = (char *)t+3;
2029 do_tempindent(a);
2030 } else if (strncmp(t, ".vs", 3) == 0) {
2031 char *a = (char *)t+3;
2032 do_verticalspacing(a);
2033 } else if (strncmp(t, ".ip", 3) == 0) {
2034 do_indentedparagraph();
2035 } else if (strcmp(t, ".links") == 0) {
2036 do_links();
2037 } else if (strcmp(t, ".no-auto-rule") == 0) {
2038 auto_rule = FALSE;
2043 * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
2046 int html_printer::is_in_middle (int left, int right)
2048 return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE );
2052 * flush_globs - runs through the text glob list and emits html.
2055 void html_printer::flush_globs (void)
2057 text_glob *g;
2059 if (! page_contents->glyphs.is_empty()) {
2060 page_contents->glyphs.start_from_head();
2061 do {
2062 g = page_contents->glyphs.get_data();
2064 if (strcmp(g->text_string, "XXXXXXX") == 0) {
2065 stop();
2068 if (g->is_a_tag()) {
2069 troff_tag(g);
2070 } else if (g->is_a_line()) {
2071 emit_line(g);
2072 } else {
2073 emit_html(g);
2076 * after processing the title (and removing it) the glyph list might be empty
2078 if (! page_contents->glyphs.is_empty()) {
2079 page_contents->glyphs.move_right();
2081 } while (! page_contents->glyphs.is_equal_to_head());
2085 void html_printer::flush_page (void)
2087 supress_sub_sup = TRUE;
2088 flush_sbuf();
2089 // page_contents->dump_page();
2090 flush_globs();
2091 current_paragraph->done_para();
2092 current_paragraph->done_table();
2094 // move onto a new page
2095 delete page_contents;
2096 page_contents = new page;
2100 * determine_space - works out whether we need to write a space.
2101 * If last glyph is ajoining then no space emitted.
2104 void html_printer::determine_space (text_glob *g)
2106 if (current_paragraph->is_in_pre()) {
2108 * .nf has been specified
2110 while (output_hpos < g->minh) {
2111 output_hpos += space_width;
2112 current_paragraph->emit_space();
2114 } else {
2115 if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
2116 current_paragraph->emit_space();
2122 * is_font_courier - returns TRUE if the font, f, is courier.
2125 int html_printer::is_font_courier (font *f)
2127 if (f != 0) {
2128 const char *fontname = f->get_name();
2130 return( (fontname != 0) && (fontname[0] == 'C') );
2132 return( FALSE );
2136 * end_font - shuts down the font corresponding to fontname.
2139 void html_printer::end_font (const char *fontname)
2141 if (strcmp(fontname, "B") == 0) {
2142 current_paragraph->done_bold();
2143 } else if (strcmp(fontname, "I") == 0) {
2144 current_paragraph->done_italic();
2145 } else if (strcmp(fontname, "BI") == 0) {
2146 current_paragraph->done_bold();
2147 current_paragraph->done_italic();
2148 } else if (strcmp(fontname, "CR") == 0) {
2149 current_paragraph->done_tt();
2154 * start_font - starts the font corresponding to name.
2157 void html_printer::start_font (const char *fontname)
2159 if (strcmp(fontname, "R") == 0) {
2160 current_paragraph->done_bold();
2161 current_paragraph->done_italic();
2162 current_paragraph->done_tt();
2163 } else if (strcmp(fontname, "B") == 0) {
2164 current_paragraph->do_bold();
2165 } else if (strcmp(fontname, "I") == 0) {
2166 current_paragraph->do_italic();
2167 } else if (strcmp(fontname, "BI") == 0) {
2168 current_paragraph->do_bold();
2169 current_paragraph->do_italic();
2170 } else if (strcmp(fontname, "CR") == 0) {
2171 if ((! fill_on) && (is_courier_until_eol())) {
2172 current_paragraph->do_pre();
2174 current_paragraph->do_tt();
2179 * start_size - from is old font size, to is the new font size.
2180 * The html increase <big> and <small> decrease alters the
2181 * font size by 20%. We try and map these onto glyph sizes.
2184 void html_printer::start_size (int from, int to)
2186 if (from < to) {
2187 while (from < to) {
2188 current_paragraph->do_big();
2189 from += SIZE_INCREMENT;
2191 } else if (from > to) {
2192 while (from > to) {
2193 current_paragraph->do_small();
2194 from -= SIZE_INCREMENT;
2200 * do_font - checks to see whether we need to alter the html font.
2203 void html_printer::do_font (text_glob *g)
2206 * check if the output_style.point_size has not been set yet
2207 * this allow users to place .ps at the top of their troff files
2208 * and grohtml can then treat the .ps value as the base font size (3)
2210 if (output_style.point_size == -1) {
2211 output_style.point_size = pointsize;
2214 if (g->text_style.f != output_style.f) {
2215 if (output_style.f != 0) {
2216 end_font(output_style.f->get_name());
2218 output_style.f = g->text_style.f;
2219 if (output_style.f != 0) {
2220 start_font(output_style.f->get_name());
2223 if (output_style.point_size != g->text_style.point_size) {
2224 do_sup_or_sub(g);
2225 if ((output_style.point_size > 0) &&
2226 (g->text_style.point_size > 0)) {
2227 start_size(output_style.point_size, g->text_style.point_size);
2229 if (g->text_style.point_size > 0) {
2230 output_style.point_size = g->text_style.point_size;
2233 if (output_style.col != g->text_style.col) {
2234 current_paragraph->done_color();
2235 output_style.col = g->text_style.col;
2236 current_paragraph->do_color(&output_style.col);
2241 * start_subscript - returns TRUE if, g, looks like a subscript start.
2244 int html_printer::start_subscript (text_glob *g)
2246 int r = font::res;
2247 int height = output_style.point_size*r/72;
2249 return( (output_style.point_size != 0) &&
2250 (output_vpos < g->minv) &&
2251 (output_vpos-height > g->maxv) &&
2252 (output_style.point_size > g->text_style.point_size) );
2256 * start_superscript - returns TRUE if, g, looks like a superscript start.
2259 int html_printer::start_superscript (text_glob *g)
2261 int r = font::res;
2262 int height = output_style.point_size*r/72;
2264 return( (output_style.point_size != 0) &&
2265 (output_vpos > g->minv) &&
2266 (output_vpos-height < g->maxv) &&
2267 (output_style.point_size > g->text_style.point_size) );
2271 * end_subscript - returns TRUE if, g, looks like the end of a subscript.
2274 int html_printer::end_subscript (text_glob *g)
2276 int r = font::res;
2277 int height = output_style.point_size*r/72;
2279 return( (output_style.point_size != 0) &&
2280 (g->minv < output_vpos) &&
2281 (output_vpos-height > g->maxv) &&
2282 (output_style.point_size < g->text_style.point_size) );
2286 * end_superscript - returns TRUE if, g, looks like the end of a superscript.
2289 int html_printer::end_superscript (text_glob *g)
2291 int r = font::res;
2292 int height = output_style.point_size*r/72;
2294 return( (output_style.point_size != 0) &&
2295 (g->minv > output_vpos) &&
2296 (output_vpos-height < g->maxv) &&
2297 (output_style.point_size < g->text_style.point_size) );
2301 * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
2302 * start/end and it calls the services of html-text to issue the
2303 * appropriate tags.
2306 void html_printer::do_sup_or_sub (text_glob *g)
2308 if (! supress_sub_sup) {
2309 if (start_subscript(g)) {
2310 current_paragraph->do_sub();
2311 } else if (start_superscript(g)) {
2312 current_paragraph->do_sup();
2313 } else if (end_subscript(g)) {
2314 current_paragraph->done_sub();
2315 } else if (end_superscript(g)) {
2316 current_paragraph->done_sup();
2322 * emit_html - write out the html text
2325 void html_printer::emit_html (text_glob *g)
2327 do_font(g);
2328 determine_space(g);
2329 current_paragraph->do_emittext(g->text_string, g->text_length);
2330 output_vpos = g->minv;
2331 output_hpos = g->maxh;
2332 output_vpos_max = g->maxv;
2333 supress_sub_sup = FALSE;
2337 * flush_sbuf - flushes the current sbuf into the list of glyphs.
2340 void html_printer::flush_sbuf()
2342 if (sbuf.length() > 0) {
2343 int r=font::res; // resolution of the device
2344 set_style(sbuf_style);
2345 if (overstrike_detected && (! is_bold(sbuf_style.f))) {
2346 font *bold_font = make_bold(sbuf_style.f);
2347 if (bold_font != NULL)
2348 sbuf_style.f = bold_font;
2351 page_contents->add(&sbuf_style, sbuf,
2352 line_number,
2353 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
2354 sbuf_vpos , sbuf_end_hpos);
2356 output_hpos = sbuf_end_hpos;
2357 output_vpos = sbuf_vpos;
2358 last_sbuf_length = 0;
2359 sbuf_prev_hpos = sbuf_end_hpos;
2360 overstrike_detected = FALSE;
2361 sbuf.clear();
2365 void html_printer::set_line_thickness(const environment *env)
2367 line_thickness = env->size;
2370 void html_printer::draw(int code, int *p, int np, const environment *env)
2372 switch (code) {
2374 case 'l':
2375 # if 0
2376 if (np == 2) {
2377 page_contents->add_line(&sbuf_style,
2378 line_number,
2379 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
2380 } else {
2381 error("2 arguments required for line");
2383 # endif
2384 break;
2385 case 't':
2387 if (np == 0) {
2388 line_thickness = -1;
2389 } else {
2390 // troff gratuitously adds an extra 0
2391 if (np != 1 && np != 2) {
2392 error("0 or 1 argument required for thickness");
2393 break;
2395 line_thickness = p[0];
2397 break;
2400 case 'P':
2401 break;
2402 case 'p':
2403 break;
2404 case 'E':
2405 break;
2406 case 'e':
2407 break;
2408 case 'C':
2409 break;
2410 case 'c':
2411 break;
2412 case 'a':
2413 break;
2414 case '~':
2415 break;
2416 case 'f':
2417 break;
2418 case 'F':
2419 // fill with color env->fill
2420 if (background != NULL)
2421 delete background;
2422 background = new color;
2423 *background = *env->fill;
2424 break;
2426 default:
2427 error("unrecognised drawing command `%1'", char(code));
2428 break;
2432 html_printer::html_printer()
2433 : html(0, MAX_LINE_LENGTH),
2434 no_of_printed_pages(0),
2435 last_sbuf_length(0),
2436 overstrike_detected(FALSE),
2437 output_hpos(-1),
2438 output_vpos(-1),
2439 output_vpos_max(-1),
2440 line_thickness(-1),
2441 inside_font_style(0),
2442 page_number(0),
2443 header_indent(-1),
2444 supress_sub_sup(TRUE),
2445 cutoff_heading(100),
2446 end_center(0),
2447 end_tempindent(0),
2448 next_tag(INLINE),
2449 fill_on(TRUE),
2450 linelength(0),
2451 pageoffset(0),
2452 indentation(0),
2453 prev_indent(0),
2454 line_number(0),
2455 background(default_background)
2457 file_list.add_new_file(xtmpfile());
2458 html.set_file(file_list.get_file());
2459 if (font::hor != 24)
2460 fatal("horizontal resolution must be 24");
2461 if (font::vert != 40)
2462 fatal("vertical resolution must be 40");
2463 #if 0
2464 // should be sorted html..
2465 if (font::res % (font::sizescale*72) != 0)
2466 fatal("res must be a multiple of 72*sizescale");
2467 #endif
2468 int r = font::res;
2469 int point = 0;
2470 while (r % 10 == 0) {
2471 r /= 10;
2472 point++;
2474 res = r;
2475 html.set_fixed_point(point);
2476 space_char_index = font::name_to_index("space");
2477 space_width = font::hor;
2478 paper_length = font::paperlength;
2479 linelength = font::res*13/2;
2480 if (paper_length == 0)
2481 paper_length = 11*font::res;
2483 page_contents = new page();
2487 * add_to_sbuf - adds character code or name to the sbuf.
2490 void html_printer::add_to_sbuf (unsigned char code, const string &s)
2492 char *html_glyph;
2494 if (s.empty()) {
2495 html_glyph = get_html_translation(sbuf_style.f, string(code));
2496 if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
2497 html_glyph = to_unicode(code);
2498 } else
2499 if (sbuf_style.f != NULL)
2500 html_glyph = get_html_translation(sbuf_style.f, s);
2502 last_sbuf_length = sbuf.length();
2503 if (html_glyph == NULL)
2504 sbuf += code;
2505 else
2506 sbuf += html_glyph;
2509 int html_printer::sbuf_continuation (unsigned char code, const char *name,
2510 const environment *env, int w)
2513 * lets see whether the glyph is closer to the end of sbuf
2515 if ((sbuf_end_hpos == env->hpos)
2516 || ((sbuf_prev_hpos < sbuf_end_hpos)
2517 && (env->hpos < sbuf_end_hpos)
2518 && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
2519 add_to_sbuf(code, name);
2520 sbuf_prev_hpos = sbuf_end_hpos;
2521 sbuf_end_hpos += w + sbuf_kern;
2522 return TRUE;
2523 } else {
2524 if ((env->hpos >= sbuf_end_hpos) &&
2525 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
2527 * lets see whether a space is needed or not
2530 if (env->hpos-sbuf_end_hpos < space_width) {
2531 add_to_sbuf(code, name);
2532 sbuf_prev_hpos = sbuf_end_hpos;
2533 sbuf_end_hpos = env->hpos + w;
2534 return TRUE;
2538 return FALSE ;
2542 * get_html_translation - given the position of the character and its name
2543 * return the device encoding for such character.
2546 char *get_html_translation (font *f, const string &name)
2548 int index;
2550 if ((f == 0) || name.empty())
2551 return NULL;
2552 else {
2553 index = f->name_to_index((char *)(name + '\0').contents());
2554 if (index == 0) {
2555 error("character `%s' not found", (name + '\0').contents());
2556 return NULL;
2557 } else
2558 if (f->contains(index))
2559 return (char *)f->get_special_device_encoding(index);
2560 else
2561 return NULL;
2566 * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
2567 * a previous glyph in sbuf.
2568 * If TRUE the font is changed to bold and the previous sbuf
2569 * is flushed.
2572 int html_printer::overstrike(unsigned char code, const char *name, const environment *env, int w)
2574 if ((env->hpos < sbuf_end_hpos)
2575 || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
2577 * at this point we have detected an overlap
2579 if (overstrike_detected) {
2580 /* already detected, remove previous glyph and use this glyph */
2581 sbuf.set_length(last_sbuf_length);
2582 add_to_sbuf(code, name);
2583 sbuf_end_hpos = env->hpos + w;
2584 return TRUE;
2585 } else {
2586 /* first time we have detected an overstrike in the sbuf */
2587 sbuf.set_length(last_sbuf_length); /* remove previous glyph */
2588 if (! is_bold(sbuf_style.f))
2589 flush_sbuf();
2590 overstrike_detected = TRUE;
2591 add_to_sbuf(code, name);
2592 sbuf_end_hpos = env->hpos + w;
2593 return TRUE;
2596 return FALSE ;
2600 * set_char - adds a character into the sbuf if it is a continuation with the previous
2601 * word otherwise flush the current sbuf and add character anew.
2604 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
2606 unsigned char code = f->get_code(i);
2608 style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
2609 if (sty.slant != 0) {
2610 if (sty.slant > 80 || sty.slant < -80) {
2611 error("silly slant `%1' degrees", sty.slant);
2612 sty.slant = 0;
2615 if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
2616 && (sbuf_continuation(code, name, env, w) || overstrike(code, name, env, w)))
2617 return;
2619 flush_sbuf();
2620 add_to_sbuf(code, name);
2621 sbuf_end_hpos = env->hpos + w;
2622 sbuf_start_hpos = env->hpos;
2623 sbuf_prev_hpos = env->hpos;
2624 sbuf_vpos = env->vpos;
2625 sbuf_style = sty;
2626 sbuf_kern = 0;
2630 * write_title - writes the title to this document
2633 void html_printer::write_title (int in_head)
2635 if (title.has_been_found) {
2636 if (in_head) {
2637 html.put_string("<title>");
2638 html.put_string(title.text);
2639 html.put_string("</title>").nl().nl();
2640 } else {
2641 title.has_been_written = TRUE;
2642 if (title.with_h1) {
2643 html.put_string("<h1 align=center>");
2644 html.put_string(title.text);
2645 html.put_string("</h1>").nl().nl();
2648 } else if (in_head) {
2649 // place empty title tags to help conform to `tidy'
2650 html.put_string("<title></title>").nl();
2655 * write_rule - emits a html rule tag, if the auto_rule boolean is true.
2658 static void write_rule (void)
2660 if (auto_rule)
2661 fputs("<hr>\n", stdout);
2664 void html_printer::begin_page(int n)
2666 page_number = n;
2667 #if defined(DEBUGGING)
2668 html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
2669 #endif
2670 no_of_printed_pages++;
2672 output_style.f = 0;
2673 output_style.point_size= -1;
2674 output_space_code = 32;
2675 output_draw_point_size = -1;
2676 output_line_thickness = -1;
2677 output_hpos = -1;
2678 output_vpos = -1;
2679 output_vpos_max = -1;
2680 current_paragraph = new html_text(&html);
2681 #if defined(INDENTATION)
2682 current_paragraph->do_indent(page_contents->buffer.add_string(indent.text), indentation, pageoffset, linelength);
2683 indent.text.clear();
2684 #endif
2685 current_paragraph->do_para("");
2688 void html_printer::end_page(int)
2690 flush_sbuf();
2691 flush_page();
2694 font *html_printer::make_font(const char *nm)
2696 return html_font::load_html_font(nm);
2699 void html_printer::do_body (void)
2701 if (background == NULL)
2702 fputs("<body>\n\n", stdout);
2703 else {
2704 unsigned int r, g, b;
2705 char buf[6+1];
2707 background->get_rgb(&r, &g, &b);
2708 // we have to scale 0..0xFFFF to 0..0xFF
2709 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
2711 fputs("<body bgcolor=\"#", stdout);
2712 fputs(buf, stdout);
2713 fputs("\">\n\n", stdout);
2717 html_printer::~html_printer()
2719 current_paragraph->flush_text();
2720 html.end_line();
2721 html.set_file(stdout);
2723 * 'HTML: The definitive guide', O'Reilly, p47. advises against specifying
2724 * the dtd, so for the moment I'll leave this commented out.
2725 * If requested we could always emit it if a command line switch
2726 * was present.
2728 * fputs("<!doctype html public \"-//IETF//DTD HTML 4.0//EN\">\n", stdout);
2730 fputs("<html>\n", stdout);
2731 fputs("<head>\n", stdout);
2732 fputs("<meta name=\"generator\" content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
2733 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
2734 write_title(TRUE);
2735 fputs("</head>\n", stdout);
2736 do_body();
2738 write_title(FALSE);
2739 header.write_headings(stdout, FALSE);
2740 write_rule();
2742 html.begin_comment("Creator : ")
2743 .put_string("groff ")
2744 .put_string("version ")
2745 .put_string(Version_string)
2746 .end_comment();
2749 #ifdef LONG_FOR_TIME_T
2750 long
2751 #else
2752 time_t
2753 #endif
2754 t = time(0);
2755 html.begin_comment("CreationDate: ")
2756 .put_string(ctime(&t), strlen(ctime(&t))-1)
2757 .end_comment();
2759 #if defined(DEBUGGING)
2760 html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
2761 #endif
2762 html.end_line();
2763 html.end_line();
2765 * now run through the file list copying each temporary file in turn and emitting the links.
2767 file_list.start_of_list();
2768 while (file_list.get_file() != 0) {
2769 if (fseek(file_list.get_file(), 0L, 0) < 0)
2770 fatal("fseek on temporary file failed");
2771 html.copy_file(file_list.get_file());
2772 fclose(file_list.get_file());
2773 file_list.move_next();
2774 if (file_list.get_file() != 0)
2775 header.write_headings(stdout, TRUE);
2777 write_rule();
2778 fputs("</body>\n", stdout);
2779 fputs("</html>\n", stdout);
2783 * special - handle all x X requests from troff. For post-html they allow users
2784 * to pass raw html commands, turn auto linked headings off/on and
2785 * also allow troff to emit tags to indicate when a: .br, .sp etc occurs.
2788 void html_printer::special(char *s, const environment *env, char type)
2790 if (type != 'p')
2791 return;
2792 if (s != 0) {
2793 flush_sbuf();
2794 if (env->fontno >= 0) {
2795 style sty(get_font_from_index(env->fontno), env->size, env->height,
2796 env->slant, env->fontno, *env->col);
2797 sbuf_style = sty;
2800 if (strncmp(s, "html:", 5) == 0) {
2801 int r=font::res; /* resolution of the device */
2802 font *f=sbuf_style.f;
2804 if (f == NULL) {
2805 int found=FALSE;
2807 f = font::load_font("TR", &found);
2811 * need to pass rest of string through to html output during flush
2813 page_contents->add_and_encode(&sbuf_style, string(&s[5]),
2814 line_number,
2815 env->vpos-env->size*r/72, env->hpos,
2816 env->vpos , env->hpos);
2819 * assume that the html command has no width, if it does then hopefully troff
2820 * will have fudged this in a macro by requesting that the formatting move right by
2821 * the appropriate amount.
2823 } else if (strncmp(s, "index:", 6) == 0) {
2824 cutoff_heading = atoi(&s[6]);
2825 } else if (strncmp(s, "html-tag:", 9) == 0) {
2826 int r=font::res; /* resolution of the device */
2828 page_contents->add_tag(&sbuf_style, string(s),
2829 line_number,
2830 env->vpos-env->size*r/72, env->hpos,
2831 env->vpos , env->hpos);
2836 int main(int argc, char **argv)
2838 program_name = argv[0];
2839 static char stderr_buf[BUFSIZ];
2840 setbuf(stderr, stderr_buf);
2841 int c;
2842 static const struct option long_options[] = {
2843 { "help", no_argument, 0, CHAR_MAX + 1 },
2844 { "version", no_argument, 0, 'v' },
2845 { NULL, 0, 0, 0 }
2847 while ((c = getopt_long(argc, argv, "a:g:o:i:I:D:F:vbdhlrnp", long_options, NULL))
2848 != EOF)
2849 switch(c) {
2850 case 'v':
2851 printf("GNU post-grohtml (groff) version %s\n", Version_string);
2852 exit(0);
2853 break;
2854 case 'a':
2855 /* text antialiasing bits - handled by pre-html */
2856 break;
2857 case 'g':
2858 /* graphic antialiasing bits - handled by pre-html */
2859 break;
2860 case 'b':
2861 // set background color to white
2862 default_background = new color;
2863 default_background->set_gray(color::MAX_COLOR_VAL);
2864 break;
2865 case 'F':
2866 font::command_line_font_dir(optarg);
2867 break;
2868 case 'l':
2869 auto_links = FALSE;
2870 break;
2871 case 'r':
2872 auto_rule = FALSE;
2873 break;
2874 case 'd':
2875 /* handled by pre-html */
2876 break;
2877 case 'h':
2878 /* do not use the Hn headings of html, but manufacture our own */
2879 manufacture_headings = TRUE;
2880 break;
2881 case 'o':
2882 /* handled by pre-html */
2883 break;
2884 case 'p':
2885 /* handled by pre-html */
2886 break;
2887 case 'i':
2888 /* handled by pre-html */
2889 break;
2890 case 'I':
2891 /* handled by pre-html */
2892 break;
2893 case 'D':
2894 /* handled by pre-html */
2895 break;
2896 case 'n':
2897 simple_anchors = TRUE;
2898 break;
2899 case CHAR_MAX + 1: // --help
2900 usage(stdout);
2901 exit(0);
2902 break;
2903 case '?':
2904 usage(stderr);
2905 exit(1);
2906 break;
2907 default:
2908 assert(0);
2910 if (optind >= argc) {
2911 do_file("-");
2912 } else {
2913 for (int i = optind; i < argc; i++)
2914 do_file(argv[i]);
2916 delete pr;
2917 return 0;
2920 static void usage(FILE *stream)
2922 fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
2923 program_name);