From 0e0a0e6429c7113acf15c4c94bd5fe94c45f9e99 Mon Sep 17 00:00:00 2001 From: ru Date: Tue, 17 Apr 2001 12:12:05 +0000 Subject: Virgin import of FSF groff v1.17 --- contrib/groff/src/devices/grohtml/Makefile.sub | 16 + contrib/groff/src/devices/grohtml/grohtml.man | 137 ++ contrib/groff/src/devices/grohtml/html-chars.h | 27 + contrib/groff/src/devices/grohtml/html-text.cc | 829 +++++++ contrib/groff/src/devices/grohtml/html-text.h | 109 + contrib/groff/src/devices/grohtml/html.h | 96 + contrib/groff/src/devices/grohtml/output.cc | 335 +++ contrib/groff/src/devices/grohtml/post-html.cc | 2933 ++++++++++++++++++++++++ 8 files changed, 4482 insertions(+) create mode 100644 contrib/groff/src/devices/grohtml/Makefile.sub create mode 100644 contrib/groff/src/devices/grohtml/grohtml.man create mode 100644 contrib/groff/src/devices/grohtml/html-chars.h create mode 100644 contrib/groff/src/devices/grohtml/html-text.cc create mode 100644 contrib/groff/src/devices/grohtml/html-text.h create mode 100644 contrib/groff/src/devices/grohtml/html.h create mode 100644 contrib/groff/src/devices/grohtml/output.cc create mode 100644 contrib/groff/src/devices/grohtml/post-html.cc (limited to 'contrib/groff/src/devices/grohtml') diff --git a/contrib/groff/src/devices/grohtml/Makefile.sub b/contrib/groff/src/devices/grohtml/Makefile.sub new file mode 100644 index 0000000..2c3a55a --- /dev/null +++ b/contrib/groff/src/devices/grohtml/Makefile.sub @@ -0,0 +1,16 @@ +PROG=post-grohtml +MAN1=grohtml.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=\ + post-html.o \ + html-text.o \ + output.o +CCSRCS=\ + $(srcdir)/post-html.cc \ + $(srcdir)/html-text.cc \ + $(srcdir)/output.cc +HDRS=\ + $(srcdir)/html.h \ + $(srcdir)/html-chars.h \ + $(srcdir)/html-text.h diff --git a/contrib/groff/src/devices/grohtml/grohtml.man b/contrib/groff/src/devices/grohtml/grohtml.man new file mode 100644 index 0000000..8796d8f --- /dev/null +++ b/contrib/groff/src/devices/grohtml/grohtml.man @@ -0,0 +1,137 @@ +.ig \"-*- nroff -*- +Copyright (C) 1999-2000, 2001 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GROHTML @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grohtml \- html driver for groff +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fBgrohtml 'u +.ti \niu +.B grohtml +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-v?lrn +.OP \-F dir +.OP \-i resolution +.OP \-o image vertical offset +.RI "[\ " files\|.\|.\|. "\ ]" +.br +.ad \na +.SH DESCRIPTION +.B grohtml +translates the output of GNU +.B troff +to html. +Users should always invoke +.B grohtml +via the groff command with a +.B \-Thtml +option. +If no files are given, +.B grohtml +will read the standard input. +A filename of +.B \- +will also cause +.B grohtml +to read the standard input. +Html output is written to the standard output. +When +.B grohtml +is run by +.B groff +options can be passed to +.B grohtml +using +.BR groff 's +.B \-P +option. +.SH OPTIONS +.TP +.B \-v +Displays the version. +.TP +.B \-? +Emits a usage synopsis. +.TP +.B -l +Turns off the production of automatic section links at the top of the document. +.TP +.B -r +Turns off the automatic header and footer line (html rule). +.TP +.B -n +Generate simple heading anchors whenever a section/number heading is found. +Without the option the anchor value is the textual heading. This can cause problems +when a heading contains a `?' on some brousers (netscape). +This flag is automatically turned on if a heading contains an image. +.TP +.BI \-F dir +Prepend directory +.IB dir /dev name +to the search path for font and device description files; +.I name +is the name of the device, usually +.BR html . +.TP +.BI \-i resolution +select the resolution for all images. +By default this is 80 pixels per inch. +Example: -i100 indicates 100 pixels per inch. +.TP +.B \-v +Print the version number. +.TP +.B \-? +Display usage. +.SH USAGE +There are styles called +.BR R , +.BR I , +.BR B , +and +.B BI +mounted at font positions 1 to 4. +.SH DEPENDENCIES +.B grohtml +is dependent upon the png utilities and gs. +Images are generated whenever a table, picture, equation or line is +encountered. +.SH BUGS +.B Grohtml +has been completely redesigned and rewritten. +It is still alpha code. +.SH "SEE ALSO" +.BR afmtodit (@MAN1EXT@), +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR psbb (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) diff --git a/contrib/groff/src/devices/grohtml/html-chars.h b/contrib/groff/src/devices/grohtml/html-chars.h new file mode 100644 index 0000000..f58f8dc --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html-chars.h @@ -0,0 +1,27 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc + * but it owes a huge amount of ideas and raw code from + * James Clark (jjc@jclark.com) grops/ps.cc. + * + * html-chars.h + * + * provides a diacritical character combination table for html + */ + + + +struct diacritical_desc { + char *mark; + char *second_troff_char; + char translation; +}; + + +static struct diacritical_desc diacritical_table[] = { + { "ad" , "aeiouyAEIOU" , ':' , }, /* */ + { "ac" , "cC" , ',' , }, /* cedilla */ + { "aa" , "aeiouyAEIOU" , '\'' , }, /* acute */ + { NULL , NULL , (char)0, }, +}; diff --git a/contrib/groff/src/devices/grohtml/html-text.cc b/contrib/groff/src/devices/grohtml/html-text.cc new file mode 100644 index 0000000..0b63aa0 --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html-text.cc @@ -0,0 +1,829 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc + * + * html-text.cc + * + * provide a troff like state machine interface which + * generates html text. + */ + +/* +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + + +#include "html-text.h" + + +html_text::html_text (simple_output *op) : + stackptr(NULL), lastptr(NULL), out(op), space_emitted(TRUE), + current_indentation(-1), pageoffset(-1), linelength(-1) +{ +} + +html_text::~html_text () +{ + flush_text(); +} + +/* + * end_tag - shuts down the tag. + */ + +void html_text::end_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: out->put_string(""); break; + case B_TAG: out->put_string(""); break; + case P_TAG: out->put_string("

").nl().enable_newlines(FALSE); break; + case SUB_TAG: out->put_string(""); break; + case SUP_TAG: out->put_string(""); break; + case TT_TAG: out->put_string(""); break; + case PRE_TAG: out->put_string(""); + if (! is_present(TABLE_TAG)) { + out->nl(); + out->enable_newlines(TRUE); + } + break; + case SMALL_TAG: out->put_string(""); break; + case BIG_TAG: out->put_string(""); break; + case TABLE_TAG: issue_table_end(); break; + + default: + error("unrecognised tag"); + } +} + +/* + * issue_tag - writes out an html tag with argument. + */ + +void html_text::issue_tag (char *tagname, char *arg) +{ + if ((arg == 0) || (strlen(arg) == 0)) { + out->put_string(tagname); + out->put_string(">"); + } else { + out->put_string(tagname); + out->put_string(" "); + out->put_string(arg); + out->put_string(">"); + } +} + +/* + * start_tag - starts a tag. + */ + +void html_text::start_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: issue_tag("arg1); break; + case B_TAG: issue_tag("arg1); break; + case P_TAG: issue_tag("\narg1); + out->enable_newlines(TRUE); break; + case SUB_TAG: issue_tag("arg1); break; + case SUP_TAG: issue_tag("arg1); break; + case TT_TAG: issue_tag("arg1); break; + case PRE_TAG: out->nl(); issue_tag("arg1); + out->enable_newlines(FALSE); break; + case SMALL_TAG: issue_tag("arg1); break; + case BIG_TAG: issue_tag("arg1); break; + case TABLE_TAG: issue_table_begin(t); break; + case BREAK_TAG: break; + + default: + error("unrecognised tag"); + } +} + +int html_text::table_is_void (tag_definition *t) +{ + if (linelength > 0) { + return( current_indentation*100/linelength <= 0 ); + } else { + return( FALSE ); + } +} + +void html_text::issue_table_begin (tag_definition *t) +{ + if (linelength > 0) { + int width=current_indentation*100/linelength; + + if (width > 0) { + out->put_string("").nl(); + out->put_string("").nl(); + if ((t->arg1 == 0) || (strcmp(t->arg1, "") == 0)) + out->put_string(""); + else { + out->put_string(""); + t->arg1[0] = (char)0; + } + out->put_string("
").nl(); + out->put_string(t->arg1).put_string("").nl(); + } + } +} + +void html_text::issue_table_end (void) +{ + out->put_string("
").nl(); + out->enable_newlines(TRUE); +} + +/* + * flush_text - flushes html tags which are outstanding on the html stack. + */ + +void html_text::flush_text (void) +{ + int notext=TRUE; + tag_definition *p=stackptr; + + while (stackptr != 0) { + notext = (notext && (! stackptr->text_emitted)); + if (! notext) { + end_tag(stackptr); + } + p = stackptr; + stackptr = stackptr->next; + free(p); + } + lastptr = NULL; +} + +/* + * is_present - returns TRUE if tag is already present on the stack. + */ + +int html_text::is_present (HTML_TAG t) +{ + tag_definition *p=stackptr; + + while (p != NULL) { + if (t == p->type) { + return( TRUE ); + } + p = p->next; + } + return( FALSE ); +} + +/* + * push_para - adds a new entry onto the html paragraph stack. + */ + +void html_text::push_para (HTML_TAG t, char *arg) +{ + tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition)); + + p->type = t; + p->arg1 = arg; + p->text_emitted = FALSE; + + /* + * if t is a P_TAG or TABLE_TAG or PRE_TAG make sure it goes on the end of the stack. + * But we insist that a TABLE_TAG is always after a PRE_TAG + * and that a P_TAG is always after a TABLE_TAG + */ + + if (((t == P_TAG) || (t == PRE_TAG) || (t == TABLE_TAG)) && + (lastptr != NULL)) { + if (((lastptr->type == TABLE_TAG) && (t == PRE_TAG)) || + ((lastptr->type == P_TAG) && (t == TABLE_TAG))) { + /* + * insert p before the lastptr + */ + if (stackptr == lastptr) { + /* + * only on element of the stack + */ + p->next = stackptr; + stackptr = p; + } else { + /* + * more than one element is on the stack + */ + tag_definition *q = stackptr; + + while (q->next != lastptr) { + q = q->next; + } + q->next = p; + p->next = lastptr; + } + } else { + /* + * store, p, at the end + */ + lastptr->next = p; + lastptr = p; + p->next = NULL; + } + } else { + p->next = stackptr; + if (stackptr == NULL) + lastptr = p; + stackptr = p; + } +} + +/* + * do_indent - remember the indent parameters and if + * indent is > pageoff and indent has changed + * then we start a html table to implement the indentation. + */ + +void html_text::do_indent (char *arg, int indent, int pageoff, int linelen) +{ + if ((current_indentation != -1) && + (pageoffset+current_indentation != indent+pageoff)) { + /* + * actual indentation of text has changed, we need to put + * a table tag onto the stack. + */ + do_table(arg); + } + current_indentation = indent; + pageoffset = pageoff; + linelength = linelen; +} + +void html_text::do_table (char *arg) +{ + int in_pre = is_in_pre(); + // char *para_type = done_para(); + done_pre(); + shutdown(TABLE_TAG); // shutdown a previous table, if present + remove_break(); + if (in_pre) { + do_pre(); + } + // do_para(para_type); + push_para(TABLE_TAG, arg); +} + +/* + * done_table - terminates a possibly existing table. + */ + +void html_text::done_table (void) +{ + shutdown(TABLE_TAG); + space_emitted = TRUE; +} + +/* + * do_italic - changes to italic + */ + +void html_text::do_italic (void) +{ + done_bold(); + done_tt(); + if (! is_present(I_TAG)) { + push_para(I_TAG, ""); + } +} + +/* + * do_bold - changes to bold. + */ + +void html_text::do_bold (void) +{ + done_italic(); + done_tt(); + if (! is_present(B_TAG)) { + push_para(B_TAG, ""); + } +} + +/* + * do_tt - changes to teletype. + */ + +void html_text::do_tt (void) +{ + done_bold(); + done_italic(); + if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG))) { + push_para(TT_TAG, ""); + } +} + +/* + * do_pre - changes to preformated text. + */ + +void html_text::do_pre (void) +{ + done_bold(); + done_italic(); + done_tt(); + char *type = done_para(); + if (! is_present(PRE_TAG)) { + push_para(PRE_TAG, ""); + } +} + +/* + * is_in_pre - returns TRUE if we are currently within a preformatted + *
 block.
+ */
+
+int html_text::is_in_pre (void)
+{
+  return( is_present(PRE_TAG) );
+}
+
+/*
+ *  is_in_table - returns TRUE if we are currently within a table.
+ */
+
+int html_text::is_in_table (void)
+{
+  return( is_present(TABLE_TAG) );
+}
+
+/*
+ *  shutdown - shuts down an html tag.
+ */
+
+char *html_text::shutdown (HTML_TAG t)
+{
+  char *arg=NULL;
+
+  if (is_present(t)) {
+    tag_definition *p    =stackptr;
+    tag_definition *temp =NULL;
+    int notext           =TRUE;
+    
+    while ((stackptr != NULL) && (stackptr->type != t)) {
+      notext = (notext && (! stackptr->text_emitted));
+      if (! notext) {
+	end_tag(stackptr);
+      }
+
+      /*
+       *  pop tag
+       */
+      p        = stackptr;
+      stackptr = stackptr->next;
+      if (stackptr == NULL)
+	lastptr = NULL;
+    
+      /*
+       *  push tag onto temp stack
+       */
+      p->next  = temp;
+      temp     = p;
+    }
+
+    /*
+     *  and examine stackptr
+     */
+    if ((stackptr != NULL) && (stackptr->type == t)) {
+      if (stackptr->text_emitted) {
+	end_tag(stackptr);
+      }
+      if (t == P_TAG) {
+	arg = stackptr->arg1;
+      }
+      p        = stackptr;
+      stackptr = stackptr->next;
+      if (stackptr == NULL)
+	lastptr = NULL;
+      free(p);
+    }
+
+    /*
+     *  and restore unaffected tags
+     */
+    while (temp != NULL) {
+      push_para(temp->type, temp->arg1);
+      p    = temp;
+      temp = temp->next;
+      free(p);
+    }
+  }
+  return( arg );
+}
+
+/*
+ *  done_bold - shuts downs a bold tag.
+ */
+
+void html_text::done_bold (void)
+{
+  shutdown(B_TAG);
+}
+
+/*
+ *  done_italic - shuts downs an italic tag.
+ */
+
+void html_text::done_italic (void)
+{
+  shutdown(I_TAG);
+}
+
+/*
+ *  done_sup - shuts downs a sup tag.
+ */
+
+void html_text::done_sup (void)
+{
+  shutdown(SUP_TAG);
+}
+
+/*
+ *  done_sub - shuts downs a sub tag.
+ */
+
+void html_text::done_sub (void)
+{
+  shutdown(SUB_TAG);
+}
+
+/*
+ *  done_tt - shuts downs a tt tag.
+ */
+
+void html_text::done_tt (void)
+{
+  shutdown(TT_TAG);
+}
+
+/*
+ *  done_pre - shuts downs a pre tag.
+ */
+
+void html_text::done_pre (void)
+{
+  shutdown(PRE_TAG);
+}
+
+/*
+ *  done_small - shuts downs a small tag.
+ */
+
+void html_text::done_small (void)
+{
+  shutdown(SMALL_TAG);
+}
+
+/*
+ *  done_big - shuts downs a big tag.
+ */
+
+void html_text::done_big (void)
+{
+  shutdown(BIG_TAG);
+}
+
+/*
+ *  check_emit_text - ensures that all previous tags have been emitted (in order)
+ *                    before the text is written.
+ */
+
+void html_text::check_emit_text (tag_definition *t)
+{
+  if ((t != NULL) && (! t->text_emitted)) {
+    /*
+     *  we peep and see whether there is a 

before the + * in which case we skip the

+ */ + if (t->type == TABLE_TAG) { + if (table_is_void(t)) { + tag_definition *n = t->next; + remove_def(t); + check_emit_text(n); + } else { + /* + * a table which will be emitted, is there a

succeeding it? + */ + if ((t->next != NULL) && + (t->next->type == P_TAG) && + ((t->next->arg1 == 0) || strcmp(t->next->arg1, "") == 0)) { + /* + * yes skip the

+ */ + check_emit_text(t->next->next); + } else { + check_emit_text(t->next); + } + t->text_emitted = TRUE; + start_tag(t); + } + } else { + check_emit_text(t->next); + t->text_emitted = TRUE; + start_tag(t); + } + } +} + +/* + * do_emittext - tells the class that text was written during the current tag. + */ + +void html_text::do_emittext (char *s, int length) +{ + if ((! is_present(P_TAG)) && (! is_present(PRE_TAG))) + do_para(""); + + if (is_present(BREAK_TAG)) { + int text = remove_break(); + check_emit_text(stackptr); + if (text) { + if (is_present(PRE_TAG)) { + out->nl(); + } else { + out->put_string("
").nl(); + } + } + } else { + check_emit_text(stackptr); + } + out->put_string(s, length); + space_emitted = FALSE; +} + +/* + * do_para- starts a new paragraph + */ + +void html_text::do_para (char *arg) +{ + done_pre(); + if (! is_present(P_TAG)) { + remove_sub_sup(); + if ((arg != 0) && (strcmp(arg, "") != 0)) { + remove_tag(TABLE_TAG); + } + push_para(P_TAG, arg); + space_emitted = TRUE; + } +} + +/* + * done_para - shuts down a paragraph tag. + */ + +char *html_text::done_para (void) +{ + space_emitted = TRUE; + return( shutdown(P_TAG) ); +} + +/* + * do_space - issues an end of paragraph + */ + +void html_text::do_space (void) +{ + if (is_in_pre()) { + do_emittext("", 0); + } else { + do_para(done_para()); + } + space_emitted = TRUE; +} + +/* + * do_break - issue a break tag. + */ + +void html_text::do_break (void) +{ + if (! is_present(PRE_TAG)) { + if (emitted_text()) { + if (! is_present(BREAK_TAG)) { + push_para(BREAK_TAG, ""); + } + } + } + space_emitted = TRUE; +} + +/* + * do_newline - issue a newline providing that we are inside a

 tag.
+ */
+
+void html_text::do_newline (void)
+{
+  if (is_present(PRE_TAG)) {
+    do_emittext("\n", 1);
+    space_emitted = TRUE;
+  }
+}
+
+/*
+ *  emitted_text - returns FALSE if white space has just been written.
+ */
+
+int html_text::emitted_text (void)
+{
+  return( ! space_emitted);
+}
+
+/*
+ *  emit_space - writes a space providing that text was written beforehand.
+ */
+
+int html_text::emit_space (void)
+{
+  if (space_emitted) {
+    if (is_present(PRE_TAG)) {
+      do_emittext(" ", 1);
+    }
+  } else {
+    out->space_or_newline();
+    space_emitted = TRUE;
+  }
+}
+
+/*
+ *  remove_def - removes a definition, t, from the stack.
+ */
+
+void html_text::remove_def (tag_definition *t)
+{
+  tag_definition *p    = stackptr;
+  tag_definition *l    = 0;
+  tag_definition *q    = 0;
+    
+  while ((p != 0) && (p != t)) {
+    l = p;
+    p = p->next;
+  }
+  if ((p != 0) && (p == t)) {
+    if (p == stackptr) {
+      stackptr = stackptr->next;
+      if (stackptr == NULL)
+	lastptr = NULL;
+      q = stackptr;
+    } else if (l == 0) {
+      error("stack list pointers are wrong");
+    } else {
+      l->next = p->next;
+      q = p->next;
+      if (l->next == NULL)
+	lastptr = l;
+    }
+    free(p);
+  }
+}
+
+/*
+ *  remove_tag - removes a tag from the stack.
+ */
+
+void html_text::remove_tag (HTML_TAG tag)
+{
+  tag_definition *p = stackptr;
+    
+  while ((p != 0) && (p->type != tag)) {
+    p = p->next;
+  }
+  if ((p != 0) && (p->type == tag))
+    remove_def(p);
+}
+
+/*
+ *  remove_sub_sup - removes a sub or sup tag, should either exist on the stack.
+ */
+
+void html_text::remove_sub_sup (void)
+{
+  if (is_present(SUB_TAG)) {
+    remove_tag(SUB_TAG);
+  }
+  if (is_present(SUP_TAG)) {
+    remove_tag(SUP_TAG);
+  }
+  if (is_present(PRE_TAG)) {
+    remove_tag(PRE_TAG);
+  }
+}
+
+/*
+ *  remove_break - break tags are not balanced thus remove it once it has been emitted.
+ *                 It returns TRUE if text was emitted before the 
was issued. + */ + +int html_text::remove_break (void) +{ + tag_definition *p = stackptr; + tag_definition *l = 0; + tag_definition *q = 0; + + while ((p != 0) && (p->type != BREAK_TAG)) { + l = p; + p = p->next; + } + if ((p != 0) && (p->type == BREAK_TAG)) { + if (p == stackptr) { + stackptr = stackptr->next; + if (stackptr == NULL) + lastptr = NULL; + q = stackptr; + } else if (l == 0) { + error("stack list pointers are wrong"); + } else { + l->next = p->next; + q = p->next; + if (l->next == NULL) + lastptr = l; + } + free(p); + } + /* + * now determine whether text was issued before
+ */ + while (q != 0) { + if (q->text_emitted) { + return( TRUE ); + } else { + q = q->next; + } + } + return( FALSE ); +} + +/* + * do_small - potentially inserts a tag into the html stream. + * However we check for a tag, if present then we terminate it. + * Otherwise a tag is inserted. + */ + +void html_text::do_small (void) +{ + if (is_present(BIG_TAG)) { + done_big(); + } else { + push_para(SMALL_TAG, ""); + } +} + +/* + * do_big - is the mirror image of do_small. + */ + +void html_text::do_big (void) +{ + if (is_present(SMALL_TAG)) { + done_small(); + } else { + push_para(BIG_TAG, ""); + } +} + +/* + * do_sup - save a superscript tag on the stack of tags. + */ + +void html_text::do_sup (void) +{ + push_para(SUP_TAG, ""); +} + +/* + * do_sub - save a subscript tag on the stack of tags. + */ + +void html_text::do_sub (void) +{ + push_para(SUB_TAG, ""); +} + diff --git a/contrib/groff/src/devices/grohtml/html-text.h b/contrib/groff/src/devices/grohtml/html-text.h new file mode 100644 index 0000000..c8ab2ac --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html-text.h @@ -0,0 +1,109 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cc + * + * html-text.h + * + * provides a state machine interface which generates html text. + */ + +/* +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "html.h" + +/* + * html tags + */ + +typedef enum {I_TAG, B_TAG, P_TAG, SUB_TAG, SUP_TAG, TT_TAG, + PRE_TAG, SMALL_TAG, BIG_TAG, BREAK_TAG, TABLE_TAG} HTML_TAG; + +typedef struct tag_definition { + HTML_TAG type; + char *arg1; + int text_emitted; + tag_definition *next; +} tag_definition ; + +/* + * the state of the current paragraph. + * It allows post-html.cc to request font changes, paragraph start/end + * and emits balanced tags with a small amount of peephole optimization. + */ + +class html_text { +public: + html_text (simple_output *op); + ~html_text (void); + void flush_text (void); + void do_emittext (char *s, int length); + void do_italic (void); + void do_bold (void); + void do_roman (void); + void do_tt (void); + void do_pre (void); + void do_small (void); + void do_big (void); + void do_para (char *arg1); + void do_sup (void); + void do_sub (void); + void do_space (void); + void do_break (void); + void do_newline (void); + void do_table (char *arg); + void done_bold (void); + void done_italic (void); + char *done_para (void); + void done_sup (void); + void done_sub (void); + void done_tt (void); + void done_pre (void); + void done_small (void); + void done_big (void); + void do_indent (char *arg, int indent, int pageoff, int linelen); + int emitted_text (void); + int emit_space (void); + int is_in_pre (void); + void remove_tag (HTML_TAG tag); + void remove_sub_sup (void); + void done_table (void); + int is_in_table (void); + +private: + tag_definition *stackptr; /* the current paragraph state */ + tag_definition *lastptr; /* the end of the stack */ + simple_output *out; + int space_emitted; + int current_indentation; /* current .in value */ + int pageoffset; /* .po value */ + int linelength; /* current line length */ + + int is_present (HTML_TAG t); + void end_tag (tag_definition *t); + void start_tag (tag_definition *t); + void push_para (HTML_TAG t, char *arg); + char *shutdown (HTML_TAG t); + void check_emit_text (tag_definition *t); + int remove_break (void); + void issue_tag (char *tagname, char *arg); + void issue_table_begin (tag_definition *t); + void issue_table_end (void); + int table_is_void (tag_definition *t); + void remove_def (tag_definition *t); +}; diff --git a/contrib/groff/src/devices/grohtml/html.h b/contrib/groff/src/devices/grohtml/html.h new file mode 100644 index 0000000..69b6e35 --- /dev/null +++ b/contrib/groff/src/devices/grohtml/html.h @@ -0,0 +1,96 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if !defined(HTML_H) +# define HTML_H +# undef DEBUGGING +// # define DEBUGGING + +/* + * class and structure needed to buffer words + */ + +struct word { + char *s; + word *next; + + word (const char *w, int n); + ~word (); +}; + +class word_list { +public: + word_list (); + int flush (FILE *f); + void add_word (const char *s, int n); + int get_length (void); + +private: + int length; + word *head; + word *tail; +}; + +class simple_output { +public: + simple_output(FILE *, int max_line_length); + simple_output &put_string(const char *, int); + simple_output &put_string(const char *s); + simple_output &put_troffps_char (const char *s); + simple_output &put_translated_string(const char *s); + simple_output &put_number(int); + simple_output &put_float(double); + simple_output &put_symbol(const char *); + simple_output &put_literal_symbol(const char *); + simple_output &set_fixed_point(int); + simple_output &simple_comment(const char *); + simple_output &begin_comment(const char *); + simple_output &comment_arg(const char *); + simple_output &end_comment(); + simple_output &set_file(FILE *); + simple_output &include_file(FILE *); + simple_output ©_file(FILE *); + simple_output &end_line(); + simple_output &put_raw_char(char); + simple_output &special(const char *); + simple_output &enable_newlines(int); + simple_output &check_newline(int n); + simple_output &nl(void); + simple_output &space_or_newline (void); + simple_output &begin_tag (void); + FILE *get_file(); +private: + FILE *fp; + int max_line_length; // not including newline + int col; + int fixed_point; + int newlines; // can we issue newlines automatically? + word_list last_word; + + void flush_last_word (void); + int check_space (const char *s, int n); +}; + +inline FILE *simple_output::get_file() +{ + return fp; +} + +#endif diff --git a/contrib/groff/src/devices/grohtml/output.cc b/contrib/groff/src/devices/grohtml/output.cc new file mode 100644 index 0000000..4c72bba --- /dev/null +++ b/contrib/groff/src/devices/grohtml/output.cc @@ -0,0 +1,335 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote output.cc + * but it owes a huge amount of ideas and raw code from + * James Clark (jjc@jclark.com) grops/ps.cc. + * + * output.cc + * + * provide the simple low level output routines needed by html.cc + */ + +/* +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#include +#include "html.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +/* + * word - initialise a word and set next to NULL + */ + +word::word (const char *w, int n) + : next(0) +{ + s = (char *)malloc(n+1); + strncpy(s, w, n); + s[n] = (char)0; +} + +/* + * destroy word and the string copy. + */ + +word::~word () +{ + free(s); +} + +/* + * word_list - create an empty word list. + */ + +word_list::word_list () + : head(0), tail(0), length(0) +{ +} + +/* + * flush - flush a word list to a FILE, f, and return the + * length of the buffered string. + */ + +int word_list::flush (FILE *f) +{ + word *t; + int len=length; + + while (head != 0) { + t = head; + head = head->next; + fputs(t->s, f); + delete t; + } + head = 0; + tail = 0; + length = 0; +#if defined(DEBUGGING) + fflush(f); // just for testing +#endif + return( len ); +} + +/* + * add_word - adds a word to the outstanding word list. + */ + +void word_list::add_word (const char *s, int n) +{ + if (head == 0) { + head = new word(s, n); + tail = head; + } else { + tail->next = new word(s, n); + tail = tail->next; + } + length += n; +} + +/* + * get_length - returns the number of characters buffered + */ + +int word_list::get_length (void) +{ + return( length ); +} + +/* + * the classes and methods for simple_output manipulation + */ + +simple_output::simple_output(FILE *f, int n) +: fp(f), max_line_length(n), col(0), fixed_point(0), newlines(0) +{ +} + +simple_output &simple_output::set_file(FILE *f) +{ + if (fp) + fflush(fp); + fp = f; + return *this; +} + +simple_output &simple_output::copy_file(FILE *infp) +{ + int c; + while ((c = getc(infp)) != EOF) + putc(c, fp); + return *this; +} + +simple_output &simple_output::end_line() +{ + flush_last_word(); + if (col != 0) { + putc('\n', fp); + col = 0; + } + return *this; +} + +simple_output &simple_output::special(const char *s) +{ + return *this; +} + +simple_output &simple_output::simple_comment(const char *s) +{ + flush_last_word(); + if (col != 0) + putc('\n', fp); + fputs("\n", fp); + col = 0; + return *this; +} + +simple_output &simple_output::begin_comment(const char *s) +{ + flush_last_word(); + if (col != 0) + putc('\n', fp); + col = 0; + put_string("").nl(); + return *this; +} + +/* + * check_newline - checks to see whether we are able to issue + * a newline and that one is needed. + */ + +simple_output &simple_output::check_newline(int n) +{ + if ((col + n + last_word.get_length() + 1 > max_line_length) && (newlines)) { + fputc('\n', fp); + col = last_word.flush(fp); + } +} + +/* + * space_or_newline - will emit a newline or a space later on + * depending upon the current column. + */ + +simple_output &simple_output::space_or_newline (void) +{ +#if defined(DEBUGGING) + fflush(fp); // just for testing +#endif + if ((col + last_word.get_length() + 1 > max_line_length) && (newlines)) { + fputc('\n', fp); + if (last_word.get_length() > 0) { + col = last_word.flush(fp); + } else { + col = 0; + } + } else { + if (last_word.get_length() != 0) { + if (col > 0) { + fputc(' ', fp); + col++; + } + col += last_word.flush(fp); + } + } +} + +/* + * nl - writes a newline providing that we + * are not in the first column. + */ + +simple_output &simple_output::nl (void) +{ + space_or_newline(); + col += last_word.flush(fp); + if (col != 0) { + fputc('\n', fp); + col = 0; + } + return *this ; +} + +simple_output &simple_output::set_fixed_point(int n) +{ + assert(n >= 0 && n <= 10); + fixed_point = n; + return *this; +} + +simple_output &simple_output::put_raw_char(char c) +{ + col += last_word.flush(fp); + putc(c, fp); + col++; + return *this; +} + +simple_output &simple_output::put_string(const char *s, int n) +{ + last_word.add_word(s, n); + return *this; +} + +simple_output &simple_output::put_string(const char *s) +{ + last_word.add_word(s, strlen(s)); + return *this; +} + +simple_output &simple_output::put_number(int n) +{ + char buf[1 + INT_DIGITS + 1]; + sprintf(buf, "%d", n); + put_string(buf); + return *this; +} + +simple_output &simple_output::put_float(double d) +{ + char buf[128]; + + sprintf(buf, "%.4f", d); + put_string(buf); + return *this; +} + +simple_output &simple_output::enable_newlines (int auto_newlines) +{ + check_newline(0); + newlines = auto_newlines; + check_newline(0); +} + +/* + * flush_last_word - flushes the last word and adjusts the + * col position. It will insert a newline + * before the last word if allowed and if + * necessary. + */ + +void simple_output::flush_last_word (void) +{ + int len=last_word.get_length(); + + if (len > 0) { + if (newlines) { + if (col + len + 1 > max_line_length) { + fputs("\n", fp); + col = 0; + } else { + fputs(" ", fp); + col++; + } + len += last_word.flush(fp); + } else { + fputs(" ", fp); + col++; + col += last_word.flush(fp); + } + } +} diff --git a/contrib/groff/src/devices/grohtml/post-html.cc b/contrib/groff/src/devices/grohtml/post-html.cc new file mode 100644 index 0000000..0237bfc --- /dev/null +++ b/contrib/groff/src/devices/grohtml/post-html.cc @@ -0,0 +1,2933 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cc + * but it owes a huge amount of ideas and raw code from + * James Clark (jjc@jclark.com) grops/ps.cc. + */ + +/* +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" +#include "html.h" +#include "html-chars.h" +#include "html-text.h" + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +#define MAX_STRING_LENGTH 4096 +#define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */ +#define SIZE_INCREMENT 2 /* font size increment = +2 */ +#define BASE_POINT_SIZE 10 /* 10 points is the base size ie html size 3 */ +#define CENTER_TOLERANCE 2 /* how many pixels off center will we still */ +#define ANCHOR_TEMPLATE "heading%d" /* if simple anchor is set we use this */ +#define UNICODE_DESC_START 0x80 /* all character entities above this are */ + /* either encoded by their glyph names or if */ + /* there is no name then we use &#nnn; */ +#define INDENTATION /* #undef INDENTATION to remove .in handling */ + +typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT; + +/* + * prototypes + */ + +void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single); +char *get_html_translation (font *f, const char *name); + + +static int auto_links = TRUE; /* by default we enable automatic links at */ + /* top of the document. */ +static int auto_rule = TRUE; /* by default we enable an automatic rule */ + /* at the top and bottom of the document */ +static int simple_anchors = FALSE; /* default to anchors with heading text */ + + +/* + * start with a few favorites + */ + +void stop () {} + +static int min (int a, int b) +{ + if (a < b) { + return( a ); + } else { + return( b ); + } +} + +static int max (int a, int b) +{ + if (a > b) { + return( a ); + } else { + return( b ); + } +} + +/* + * is_subsection - returns TRUE if a1..a2 is within b1..b2 + */ + +static int is_subsection (int a1, int a2, int b1, int b2) +{ + // easier to see whether this is not the case + return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) ); +} + +/* + * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2 + */ + +static int is_intersection (int a1, int a2, int b1, int b2) +{ + // again easier to prove NOT outside limits + return( ! ((a1 > b2) || (a2 < b1)) ); +} + +/* + * is_digit - returns TRUE if character, ch, is a digit. + */ + +static int is_digit (char ch) +{ + return( (ch >= '0') && (ch <= '9') ); +} + +/* + * the classes and methods for maintaining a list of files. + */ + +struct file { + FILE *fp; + file *next; + + file (FILE *f); +}; + +/* + * file - initialize all fields to NULL + */ + +file::file (FILE *f) + : fp(f), next(0) +{ +} + +class files { +public: + files (); + FILE *get_file (void); + void start_of_list (void); + void move_next (void); + void add_new_file (FILE *f); +private: + file *head; + file *tail; + file *ptr; +}; + +/* + * files - create an empty list of files. + */ + +files::files () + : head(0), tail(0), ptr(0) +{ +} + +/* + * get_file - returns the FILE associated with ptr. + */ + +FILE *files::get_file (void) +{ + if (ptr) { + return( ptr->fp ); + } else { + return( 0 ); + } +} + +/* + * start_of_list - reset the ptr to the start of the list. + */ + +void files::start_of_list (void) +{ + ptr = head; +} + +/* + * move_next - moves the ptr to the next element on the list. + */ + +void files::move_next (void) +{ + if (ptr != 0) + ptr = ptr->next; +} + +/* + * add_new_file - adds a new file, f, to the list. + */ + +void files::add_new_file (FILE *f) +{ + if (head == 0) { + head = new file(f); + tail = head; + } else { + tail->next = new file(f); + tail = tail->next; + } + ptr = tail; +} + +/* + * the class and methods for styles + */ + +struct style { + font *f; + int point_size; + int font_no; + int height; + int slant; + style (); + style (font *, int, int, int, int); + int operator == (const style &) const; + int operator != (const style &) const; +}; + +style::style() + : f(0) +{ +} + +style::style(font *p, int sz, int h, int sl, int no) + : f(p), point_size(sz), font_no(no), height(h), slant(sl) +{ +} + +int style::operator==(const style &s) const +{ + return (f == s.f && point_size == s.point_size + && height == s.height && slant == s.slant); +} + +int style::operator!=(const style &s) const +{ + return !(*this == s); +} + +/* + * the class and methods for retaining ascii text + */ + +struct char_block { + enum { SIZE = 256 }; + char buffer[SIZE]; + int used; + char_block *next; + + char_block(); +}; + +char_block::char_block() +: used(0), next(0) +{ +} + +class char_buffer { +public: + char_buffer(); + ~char_buffer(); + char *add_string(char *, unsigned int); +private: + char_block *head; + char_block *tail; +}; + +char_buffer::char_buffer() +: head(0), tail(0) +{ +} + +char_buffer::~char_buffer() +{ + while (head != 0) { + char_block *temp = head; + head = head->next; + delete temp; + } +} + +char *char_buffer::add_string (char *s, unsigned int length) +{ + int i=0; + unsigned int old_used; + + if (tail == 0) { + tail = new char_block; + head = tail; + } else { + if (tail->used + length+1 > char_block::SIZE) { + tail->next = new char_block; + tail = tail->next; + } + } + // at this point we have a tail which is ready for the string. + if (tail->used + length+1 > char_block::SIZE) { + fatal("need to increase char_block::SIZE"); + } + + old_used = tail->used; + do { + tail->buffer[tail->used] = s[i]; + tail->used++; + i++; + length--; + } while (length>0); + + // add terminating nul character + + tail->buffer[tail->used] = '\0'; + tail->used++; + + // and return start of new string + + return( &tail->buffer[old_used] ); +} + +/* + * the classes and methods for maintaining glyph positions. + */ + +class text_glob { +public: + text_glob (style *s, char *string, unsigned int length, + int min_vertical , int min_horizontal, + int max_vertical , int max_horizontal, + int is_html , int is_troff_command, + int is_auto_image, + int is_a_line , int thickness); + text_glob (void); + ~text_glob (void); + int is_a_line (void); + int is_a_tag (void); + int is_raw (void); + int is_eol (void); + int is_auto_img (void); + int is_br (void); + + style text_style; + char *text_string; + unsigned int text_length; + int minv, maxv, minh, maxh; + int is_raw_command; // should the text be sent directly to the device? + int is_tag; // is this a .br, .sp, .tl etc + int is_line; // is the command a ? + int is_img_auto; // image created by eqn delim + int thickness; // the thickness of a line +}; + +text_glob::text_glob (style *s, char *string, unsigned int length, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal, + int is_html, int is_troff_command, + int is_auto_image, + int is_a_line, int line_thickness) + : text_style(*s), text_string(string), text_length(length), + minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal), + is_raw_command(is_html), is_tag(is_troff_command), is_img_auto(is_auto_image), + is_line(is_a_line), thickness(line_thickness) +{ +} + +text_glob::text_glob () + : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1), + is_raw_command(FALSE), is_tag(FALSE), is_line(FALSE), thickness(0) +{ +} + +text_glob::~text_glob () +{ +} + +/* + * is_a_line - returns TRUE if glob should be converted into an
+ */ + +int text_glob::is_a_line (void) +{ + return( is_line ); +} + +/* + * is_a_tag - returns TRUE if glob contains a troff directive. + */ + +int text_glob::is_a_tag (void) +{ + return( is_tag ); +} + +/* + * is_eol - returns TRUE if glob contains the tag eol + */ + +int text_glob::is_eol (void) +{ + return( is_tag && (strcmp(text_string, "html-tag:eol") == 0) ); +} + +/* + * is_raw - returns TRUE if glob contains raw html. + */ + +int text_glob::is_raw (void) +{ + return( is_raw_command ); +} + +/* + * is_auto_img - returns TRUE if the glob contains an automatically + * generated image. + */ + +int text_glob::is_auto_img (void) +{ + return( is_img_auto ); +} + +/* + * is_br - returns TRUE if the glob is a tag containing a .br + */ + +int text_glob::is_br (void) +{ + return( is_a_tag() && (strcmp("html-tag:.br", text_string) == 0) ); +} + +/* + * the class and methods used to construct ordered double linked lists. + * In a previous implementation we used templates via #include "ordered-list.h", + * but this does assume that all C++ compilers can handle this feature. Pragmatically + * it is safer to assume this is not the case. + */ + +struct element_list { + element_list *right; + element_list *left; + text_glob *datum; + int lineno; + int minv, maxv, minh, maxh; + + element_list (text_glob *d, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + element_list (); +}; + +element_list::element_list () + : right(0), left(0), datum(0), lineno(0), minv(-1), maxv(-1), minh(-1), maxh(-1) +{ +} + +/* + * element_list - create a list element assigning the datum and region parameters. + */ + +element_list::element_list (text_glob *in, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) + : right(0), left(0), datum(in), lineno(line_number), + minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal) +{ +} + +class list { +public: + list (); + ~list (); + int is_less (element_list *a, element_list *b); + void add (text_glob *in, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void sub_move_right (void); + void move_right (void); + void move_left (void); + int is_empty (void); + int is_equal_to_tail (void); + int is_equal_to_head (void); + void start_from_head (void); + void start_from_tail (void); + text_glob *move_right_get_data (void); + text_glob *move_left_get_data (void); + text_glob *get_data (void); +private: + element_list *head; + element_list *tail; + element_list *ptr; +}; + +/* + * list - construct an empty list. + */ + +list::list () + : head(0), tail(0), ptr(0) +{ +} + +/* + * ~list - destroy a complete list. + */ + +list::~list() +{ + element_list *temp=head; + + do { + temp = head; + if (temp != 0) { + head = head->right; + delete temp; + } + } while ((head != 0) && (head != tail)); +} + +/* + * is_less - returns TRUE if a is left of b if on the same line or + * if a is higher up the page than b. + */ + +int list::is_less (element_list *a, element_list *b) +{ + // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) { + if (a->lineno < b->lineno) { + return( TRUE ); + } else if (a->lineno > b->lineno) { + return( FALSE ); + } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) { + return( a->minh < b->minh ); + } else { + return( a->maxv < b->maxv ); + } +} + +/* + * add - adds a datum to the list in the order specified by the region position. + */ + +void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal) +{ + // create a new list element with datum and position fields initialized + element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + element_list *last; + + if (head == 0) { + head = t; + tail = t; + t->left = t; + t->right = t; + } else { + last = tail; + + while ((last != head) && (is_less(t, last))) { + last = last->left; + } + + if (is_less(t, last)) { + t->right = last; + last->left->right = t; + t->left = last->left; + last->left = t; + // now check for a new head + if (last == head) { + head = t; + } + } else { + // add t beyond last + t->right = last->right; + t->left = last; + last->right->left = t; + last->right = t; + // now check for a new tail + if (last == tail) { + tail = t; + } + } + } +} + +/* + * sub_move_right - removes the element which is currently pointed to by ptr + * from the list and moves ptr to the right. + */ + +void list::sub_move_right (void) +{ + element_list *t=ptr->right; + + if (head == tail) { + head = 0; + if (tail != 0) { + delete tail; + } + tail = 0; + ptr = 0; + } else { + if (head == ptr) { + head = head->right; + } + if (tail == ptr) { + tail = tail->left; + } + ptr->left->right = ptr->right; + ptr->right->left = ptr->left; + ptr=t; + } +} + +/* + * start_from_head - assigns ptr to the head. + */ + +void list::start_from_head (void) +{ + ptr = head; +} + +/* + * start_from_tail - assigns ptr to the tail. + */ + +void list::start_from_tail (void) +{ + ptr = tail; +} + +/* + * is_empty - returns TRUE if the list has no elements. + */ + +int list::is_empty (void) +{ + return( head == 0 ); +} + +/* + * is_equal_to_tail - returns TRUE if the ptr equals the tail. + */ + +int list::is_equal_to_tail (void) +{ + return( ptr == tail ); +} + +/* + * is_equal_to_head - returns TRUE if the ptr equals the head. + */ + +int list::is_equal_to_head (void) +{ + return( ptr == head ); +} + +/* + * move_left - moves the ptr left. + */ + +void list::move_left (void) +{ + ptr = ptr->left; +} + +/* + * move_right - moves the ptr right. + */ + +void list::move_right (void) +{ + ptr = ptr->right; +} + +/* + * get_datum - returns the datum referenced via ptr. + */ + +text_glob* list::get_data (void) +{ + return( ptr->datum ); +} + +/* + * move_right_get_data - returns the datum referenced via ptr and moves + * ptr right. + */ + +text_glob* list::move_right_get_data (void) +{ + ptr = ptr->right; + if (ptr == head) { + return( 0 ); + } else { + return( ptr->datum ); + } +} + +/* + * move_left_get_data - returns the datum referenced via ptr and moves + * ptr right. + */ + +text_glob* list::move_left_get_data (void) +{ + ptr = ptr->left; + if (ptr == tail) { + return( 0 ); + } else { + return( ptr->datum ); + } +} + +/* + * page class and methods + */ + +class page { +public: + page (void); + void add (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_html (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_tag (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal); + void add_line (style *s, + int line_number, + int x1, int y1, int x2, int y2, + int thickness); + void dump_page (void); // debugging method + + // and the data + + list glyphs; // position of glyphs and specials on page + char_buffer buffer; // all characters for this page +}; + +page::page() +{ +} + +void page::add (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + FALSE, FALSE, FALSE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_html - add a raw html command, for example mailto, line, background, image etc. + */ + +void page::add_html (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + TRUE, FALSE, FALSE, FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_tag - adds a troff tag, for example: .tl .sp .br + */ + +void page::add_tag (style *s, char *string, unsigned int length, + int line_number, + int min_vertical, int min_horizontal, + int max_vertical, int max_horizontal) +{ + if (length > 0) { + text_glob *g=new text_glob(s, buffer.add_string(string, length), length, + min_vertical, min_horizontal, max_vertical, max_horizontal, + FALSE, TRUE, + (strncmp(string, "html-tag:.auto-image", 20) == 0), + FALSE, 0); + glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal); + } +} + +/* + * add_line - adds the primitive providing that y1==y2 + */ + +void page::add_line (style *s, + int line_number, + int x1, int y1, int x2, int y2, + int thickness) +{ + if (y1 == y2) { + text_glob *g = new text_glob(s, "", 0, + min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2), + FALSE, TRUE, FALSE, FALSE, thickness); + glyphs.add(g, line_number, min(y1, y2), min(x1, y2), max(y1, y2), max(x1, x2)); + } +} + +/* + * dump_page - dump the page contents for debugging purposes. + */ + +void page::dump_page(void) +{ + text_glob *g; + + printf("\n\ndebugging start\n"); + glyphs.start_from_head(); + do { + g = glyphs.get_data(); + printf("%s ", g->text_string); + glyphs.move_right(); + } while (! glyphs.is_equal_to_head()); + printf("\ndebugging end\n\n"); +} + +/* + * font classes and methods + */ + +class html_font : public font { + html_font(const char *); +public: + int encoding_index; + char *encoding; + char *reencoded_name; + ~html_font(); + static html_font *load_html_font(const char *); +}; + +html_font *html_font::load_html_font(const char *s) +{ + html_font *f = new html_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +html_font::html_font(const char *nm) +: font(nm) +{ +} + +html_font::~html_font() +{ +} + +/* + * a simple class to contain the header to this document + */ + +class title_desc { +public: + title_desc (); + ~title_desc (); + + int has_been_written; + int has_been_found; + char text[MAX_STRING_LENGTH]; +}; + + +title_desc::title_desc () + : has_been_written(FALSE), has_been_found(FALSE) +{ +} + +title_desc::~title_desc () +{ +} + +class header_desc { +public: + header_desc (); + ~header_desc (); + + int no_of_headings; // how many headings have we found? + char_buffer headings; // all the headings used in the document + list headers; // list of headers built from .NH and .SH + int header_level; // current header level + int written_header; // have we written the header yet? + char header_buffer[MAX_STRING_LENGTH]; // current header text + + void write_headings (FILE *f, int force); +}; + +header_desc::header_desc () + : no_of_headings(0), header_level(2), written_header(0) +{ +} + +header_desc::~header_desc () +{ +} + +/* + * write_headings - emits a list of links for the headings in this document + */ + +void header_desc::write_headings (FILE *f, int force) +{ + text_glob *g; + + if (auto_links || force) { + if (! headers.is_empty()) { + int h=1; + + headers.start_from_head(); + do { + g = headers.get_data(); + fputs("text_string, f); + h++; + fputs("\">", f); + fputs(g->text_string, f); + fputs("
\n", f); + headers.move_right(); + } while (! headers.is_equal_to_head()); + fputs("\n", f); + } + } +} + +class html_printer : public printer { + files file_list; + simple_output html; + int res; + int space_char_index; + int no_of_printed_pages; + int paper_length; + enum { SBUF_SIZE = 8192 }; + char sbuf[SBUF_SIZE]; + int sbuf_len; + int sbuf_start_hpos; + int sbuf_vpos; + int sbuf_end_hpos; + int sbuf_kern; + style sbuf_style; + style output_style; + int output_hpos; + int output_vpos; + int output_vpos_max; + int output_draw_point_size; + int line_thickness; + int output_line_thickness; + unsigned char output_space_code; + string defs; + char *inside_font_style; + int page_number; + title_desc title; + title_desc indent; // use title class to remember $1 of .ip + header_desc header; + int header_indent; + int supress_sub_sup; + int cutoff_heading; + page *page_contents; + html_text *current_paragraph; + int end_center; + int end_tempindent; + TAG_ALIGNMENT next_tag; + int fill_on; + int linelength; + int pageoffset; + int indentation; + int prev_indent; + int pointsize; + int vertical_spacing; + int line_number; + + void flush_sbuf (); + void set_style (const style &); + void set_space_code (unsigned char c); + void do_exec (char *, const environment *); + void do_import (char *, const environment *); + void do_def (char *, const environment *); + void do_mdef (char *, const environment *); + void do_file (char *, const environment *); + void set_line_thickness (const environment *); + void terminate_current_font (void); + void flush_font (void); + void add_char_to_sbuf (unsigned char code); + void add_to_sbuf (unsigned char code, const char *name); + void write_title (int in_head); + void determine_diacritical_mark (const char *name, const environment *env); + int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w); + char *remove_last_char_from_sbuf (); + int seen_backwards_escape (char *s, int l); + void flush_page (void); + void troff_tag (text_glob *g); + void flush_globs (void); + void emit_line (text_glob *g); + void emit_raw (text_glob *g); + void translate_to_html (text_glob *g); + void determine_space (text_glob *g); + void start_font (const char *name); + void end_font (const char *name); + int is_font_courier (font *f); + int is_courier_until_eol (void); + void start_size (int from, int to); + void do_font (text_glob *g); + void do_center (char *arg); + void do_break (void); + void do_eol (void); + void do_title (void); + void do_fill (int on); + void do_heading (char *arg); + void write_header (void); + void determine_header_level (int level); + void do_linelength (char *arg); + void do_pageoffset (char *arg); + void do_indentation (char *arg); + void do_tempindent (char *arg); + void do_indentedparagraph (void); + void do_verticalspacing (char *arg); + void do_pointsize (char *arg); + void do_centered_image (void); + void do_left_image (void); + void do_right_image (void); + void do_auto_image (text_glob *g, const char *filename); + void do_links (void); + void do_flush (void); + int is_in_middle (int left, int right); + void do_sup_or_sub (text_glob *g); + int start_subscript (text_glob *g); + int end_subscript (text_glob *g); + int start_superscript (text_glob *g); + int end_superscript (text_glob *g); + + // ADD HERE + +public: + html_printer (); + ~html_printer (); + void set_char (int i, font *f, const environment *env, int w, const char *name); + void draw (int code, int *p, int np, const environment *env); + void begin_page (int); + void end_page (int); + void special (char *arg, const environment *env, char type); + font *make_font (const char *); + void end_of_line (); +}; + +printer *make_printer() +{ + return new html_printer; +} + +static void usage(FILE *stream); + +void html_printer::set_style(const style &sty) +{ + const char *fontname = sty.f->get_name(); + if (fontname == 0) + fatal("no internalname specified for font"); + +#if 0 + change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size); +#endif +} + +void html_printer::end_of_line() +{ + flush_sbuf(); + line_number++; +} + +/* + * emit_line - writes out a horizontal rule. + */ + +void html_printer::emit_line (text_glob *g) +{ + // --fixme-- needs to know the length in percentage + html.put_string("
"); +} + +/* + * emit_raw - writes the raw html information directly to the device. + */ + +void html_printer::emit_raw (text_glob *g) +{ + do_font(g); + if (next_tag == INLINE) { + determine_space(g); + current_paragraph->do_emittext(g->text_string, g->text_length); + } else { + int in_table=current_paragraph->is_in_table(); + + current_paragraph->done_para(); + switch (next_tag) { + + case CENTERED: + current_paragraph->do_para("align=center"); + break; + case LEFT: + current_paragraph->do_para("align=left"); + break; + case RIGHT: + current_paragraph->do_para("align=right"); + break; + default: + fatal("unknown enumeration"); + } + current_paragraph->do_emittext(g->text_string, g->text_length); + current_paragraph->done_para(); + next_tag = INLINE; + supress_sub_sup = TRUE; +#if defined(INDENTATION) + if (in_table) { + stop(); + current_paragraph->do_indent(NULL, 0, pageoffset, linelength); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif + } +} + +/* + * do_center - handle the .ce commands from troff. + */ + +void html_printer::do_center (char *arg) +{ + int n = atoi(arg); + + current_paragraph->do_break(); + current_paragraph->done_para(); + supress_sub_sup = TRUE; + + if (n > 0) { + current_paragraph->do_para("align=center"); + end_center += n; + } else { + end_center = 0; + } +} + +/* + * do_centered_image - set a flag such that the next html-tag is + * placed inside a centered paragraph. + */ + +void html_printer::do_centered_image (void) +{ + next_tag = CENTERED; +} + +/* + * do_right_image - set a flag such that the next html-tag is + * placed inside a right aligned paragraph. + */ + +void html_printer::do_right_image (void) +{ + next_tag = RIGHT; +} + +/* + * do_left_image - set a flag such that the next html-tag is + * placed inside a left aligned paragraph. + */ + +void html_printer::do_left_image (void) +{ + next_tag = LEFT; +} + +/* + * exists - returns TRUE if filename exists. + */ + +static int exists (const char *filename) +{ + FILE *fp = fopen(filename, "r"); + + if (fp == 0) { + return( FALSE ); + } else { + fclose(fp); + return( TRUE ); + } +} + +/* + * generate_img_src - returns a html image tag for the filename + * providing that the image exists. + */ + +static char *generate_img_src (const char *filename) +{ + static char buffer[MAX_STRING_LENGTH]; + + while (filename && (filename[0] == ' ')) { + filename++; + } + if (exists(filename)) { + strcpy(buffer, "", 3); + } + return( (char *)&buffer ); + } else { + return( 0 ); + } +} + +/* + * do_auto_image - tests whether the image, indicated by filename, + * is present, if so then it emits an html image tag. + * An image tag may be passed through from pic, eqn + * but the corresponding image might not be created. + * Consider .EQ delim $$ .EN or an empty .PS .PE. + */ + +void html_printer::do_auto_image (text_glob *g, const char *filename) +{ + char *buffer = generate_img_src(filename); + + if (buffer) { + /* + * utilize emit_raw by creating a new text_glob. + */ + text_glob h = *g; + + h.text_string = buffer; + h.text_length = strlen(buffer); + emit_raw(&h); + } else { + next_tag = INLINE; + } +} + +/* + * do_title - handle the .tl commands from troff. + */ + +void html_printer::do_title (void) +{ + text_glob *t; + int removed_from_head; + char buf[MAX_STRING_LENGTH]; + + if (page_number == 1) { + int found_title_start = FALSE; + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + do { + t = page_contents->glyphs.get_data(); + removed_from_head = FALSE; + if (t->is_auto_img()) { + char *img=generate_img_src((char *)(t->text_string + 20)); + + if (img) { + if (found_title_start) { + strcat(title.text, " "); + } + found_title_start = TRUE; + title.has_been_found = TRUE; + strcat(title.text, img); + } + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else if (t->is_raw_command) { + /* skip raw commands + */ + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_eol()) { + /* end of title found + */ + title.has_been_found = TRUE; + return; + } else if (t->is_a_tag()) { + /* end of title found, but move back so that we read this tag and process it + */ + page_contents->glyphs.move_left(); /* move backwards to last word */ + title.has_been_found = TRUE; + return; + } else if (found_title_start) { + strcat(title.text, " "); + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcat(title.text, buf); + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else { + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcpy((char *)title.text, buf); + found_title_start = TRUE; + title.has_been_found = TRUE; + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } + } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head)); + } + // page_contents->glyphs.move_left(); /* move backwards to last word */ + } +} + +void html_printer::write_header (void) +{ + if (strlen(header.header_buffer) > 0) { + if (header.header_level > 7) { + header.header_level = 7; + } + + // firstly we must terminate any font and type faces + current_paragraph->done_para(); + current_paragraph->done_table(); + supress_sub_sup = TRUE; + + if (cutoff_heading+2 > header.header_level) { + // now we save the header so we can issue a list of links + header.no_of_headings++; + style st; + + text_glob *h=new text_glob(&st, + header.headings.add_string(header.header_buffer, strlen(header.header_buffer)), + strlen(header.header_buffer), + header.no_of_headings, header.header_level, + header.no_of_headings, header.header_level, + FALSE, FALSE, FALSE, FALSE, FALSE); + header.headers.add(h, + header.no_of_headings, + header.no_of_headings, header.no_of_headings, + header.no_of_headings, header.no_of_headings); // and add this header to the header list + + // lastly we generate a tag + + html.nl().put_string("").nl(); + } + + // and now we issue the real header + html.put_string(""); + html.put_string(header.header_buffer); + html.put_string("").nl(); + + current_paragraph->do_para(""); + } +} + +void html_printer::determine_header_level (int level) +{ + if (level == 0) { + int i; + int l=strlen(header.header_buffer); + + for (i=0; ((iglyphs.move_right(); + if (! page_contents->glyphs.is_equal_to_head()) { + g = page_contents->glyphs.get_data(); + do { + if (g->is_auto_img()) { + char *img=generate_img_src((char *)(g->text_string + 20)); + + if (img) { + simple_anchors = TRUE; // we cannot use full heading anchors with images + if (l != 0) { + strcat(header.header_buffer, " "); + } + l = g; + strcat(header.header_buffer, img); + } + } else if (! (g->is_a_line() || g->is_a_tag() || g->is_raw())) { + /* + * we ignore raw commands when constructing a heading + */ + if (l != 0) { + strcat(header.header_buffer, " "); + } + l = g; + str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE); + strcat(header.header_buffer, (char *)buf); + } + page_contents->glyphs.move_right(); + g = page_contents->glyphs.get_data(); + } while ((! page_contents->glyphs.is_equal_to_head()) && + (! g->is_br())); + } + + determine_header_level(level); + write_header(); + + // finally set the output to neutral for after the header + g = page_contents->glyphs.get_data(); + page_contents->glyphs.move_left(); // so that next time we use old g +} + +/* + * is_courier_until_eol - returns TRUE if we can see a whole line which is courier + */ + +int html_printer::is_courier_until_eol (void) +{ + text_glob *orig = page_contents->glyphs.get_data(); + int result = TRUE; + text_glob *g; + + if (! page_contents->glyphs.is_equal_to_tail()) { + page_contents->glyphs.move_right(); + do { + g = page_contents->glyphs.get_data(); + if (! is_font_courier(g->text_style.f)) { + result = FALSE; + } + page_contents->glyphs.move_right(); + } while ((result) && + (! page_contents->glyphs.is_equal_to_head()) && + (! g->is_eol())); + + /* + * now restore our previous position. + */ + while (page_contents->glyphs.get_data() != orig) { + page_contents->glyphs.move_left(); + } + } + return( result ); +} + +/* + * do_linelength - handle the .ll command from troff. + */ + +void html_printer::do_linelength (char *arg) +{ +#if defined(INDENTATION) + if (fill_on) { + linelength = atoi(arg); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_pageoffset - handle the .po command from troff. + */ + +void html_printer::do_pageoffset (char *arg) +{ +#if defined(INDENTATION) + pageoffset = atoi(arg); + if (fill_on) { + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_indentation - handle the .in command from troff. + */ + +void html_printer::do_indentation (char *arg) +{ +#if defined(INDENTATION) + if (fill_on) { + indentation = atoi(arg); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_tempindent - handle the .ti command from troff. + */ + +void html_printer::do_tempindent (char *arg) +{ +#if defined(INDENTATION) + if (fill_on) { + end_tempindent = 1; + prev_indent = indentation; + indentation = atoi(arg); + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } +#endif +} + +/* + * do_indentedparagraph - handle the .ip tag, this buffers the next line + * and passes this to text-text as the left hand + * column table entry. + */ + +void html_printer::do_indentedparagraph (void) +{ +#if defined(INDENTATION) + text_glob *t; + int removed_from_head; + char buf[MAX_STRING_LENGTH]; + int found_indent_start = FALSE; + + indent.has_been_found = FALSE; + indent.text[0] = (char)0; + + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + do { + t = page_contents->glyphs.get_data(); + removed_from_head = FALSE; + if (t->is_auto_img()) { + char *img=generate_img_src((char *)(t->text_string + 20)); + + if (img) { + if (found_indent_start) { + strcat(indent.text, " "); + } + found_indent_start = TRUE; + strcat(indent.text, img); + } + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_raw_command) { + /* skip raw commands + */ + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (t->is_a_tag() && (strncmp(t->text_string, "html-tag:.br", 12) == 0)) { + /* end of indented para found, but move back so that we read this tag and process it + */ + page_contents->glyphs.move_left(); /* move backwards to last word */ + indent.has_been_found = TRUE; + return; + } else if (t->is_a_tag()) { + page_contents->glyphs.sub_move_right(); /* move onto next word */ + } else if (found_indent_start) { + strcat(indent.text, " "); + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcat(indent.text, buf); + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } else { + str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE); + strcpy((char *)indent.text, buf); + found_indent_start = TRUE; + indent.has_been_found = TRUE; + page_contents->glyphs.sub_move_right(); /* move onto next word */ + removed_from_head = ((!page_contents->glyphs.is_empty()) && + (page_contents->glyphs.is_equal_to_head())); + } + } while ((! page_contents->glyphs.is_equal_to_head()) || (removed_from_head)); + } + // page_contents->glyphs.move_left(); /* move backwards to last word */ +#endif +} + +/* + * do_verticalspacing - handle the .vs command from troff. + */ + +void html_printer::do_verticalspacing (char *arg) +{ + vertical_spacing = atoi(arg); +} + +/* + * do_pointsize - handle the .ps command from troff. + */ + +void html_printer::do_pointsize (char *arg) +{ + pointsize = atoi(arg); +} + +/* + * do_fill - records whether troff has requested that text be filled. + */ + +void html_printer::do_fill (int on) +{ + current_paragraph->do_break(); + output_hpos = indentation+pageoffset; + supress_sub_sup = TRUE; + + if (fill_on != on) { + if (on) { + current_paragraph->done_pre(); + } else { + current_paragraph->do_pre(); + } + } + fill_on = on; +} + +/* + * do_eol - handle the end of line + */ + +void html_printer::do_eol (void) +{ + if (! fill_on) { + current_paragraph->do_newline(); + current_paragraph->do_break(); + } + output_hpos = indentation+pageoffset; + if (end_center > 0) { + if (end_center > 1) { + current_paragraph->do_break(); + } + end_center--; + if (end_center == 0) { + current_paragraph->done_para(); + supress_sub_sup = TRUE; + } + } +} + +/* + * do_flush - flushes all output and tags. + */ + +void html_printer::do_flush (void) +{ + current_paragraph->done_para(); + current_paragraph->done_table(); +} + +/* + * do_links - moves onto a new temporary file and sets auto_links to FALSE. + */ + +void html_printer::do_links (void) +{ + current_paragraph->done_para(); + current_paragraph->done_table(); + auto_links = FALSE; /* from now on only emit under user request */ +#if !defined(DEBUGGING) + file_list.add_new_file(xtmpfile()); + html.set_file(file_list.get_file()); +#endif +} + +/* + * do_break - handles the ".br" request and also + * undoes an outstanding ".ti" command. + */ + +void html_printer::do_break (void) +{ + current_paragraph->do_break(); +#if defined(INDENTATION) + if (end_tempindent > 0) { + end_tempindent--; + if (end_tempindent == 0) { + indentation = prev_indent; + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); + } + } +#endif + output_hpos = indentation+pageoffset; + supress_sub_sup = TRUE; +} + +/* + * troff_tag - processes the troff tag and manipulates the troff state machine. + */ + +void html_printer::troff_tag (text_glob *g) +{ + /* + * firstly skip over html-tag: + */ + char *t=(char *)g->text_string+9; + + if (g->is_eol()) { + do_eol(); + } else if (strncmp(t, ".sp", 3) == 0) { + current_paragraph->do_space(); + supress_sub_sup = TRUE; + } else if (strncmp(t, ".br", 3) == 0) { + do_break(); + } else if (strcmp(t, ".centered-image") == 0) { + do_centered_image(); + } else if (strcmp(t, ".right-image") == 0) { + do_right_image(); + } else if (strcmp(t, ".left-image") == 0) { + do_left_image(); + } else if (strncmp(t, ".auto-image", 11) == 0) { + char *a = (char *)t+11; + do_auto_image(g, a); + } else if (strncmp(t, ".ce", 3) == 0) { + char *a = (char *)t+3; + supress_sub_sup = TRUE; + do_center(a); + } else if (strncmp(t, ".tl", 3) == 0) { + supress_sub_sup = TRUE; + do_title(); + } else if (strncmp(t, ".fi", 3) == 0) { + do_fill(TRUE); + } else if (strncmp(t, ".nf", 3) == 0) { + do_fill(FALSE); + } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) { + char *a = (char *)t+3; + do_heading(a); + } else if (strncmp(t, ".ll", 3) == 0) { + char *a = (char *)t+3; + do_linelength(a); + } else if (strncmp(t, ".po", 3) == 0) { + char *a = (char *)t+3; + do_pageoffset(a); + } else if (strncmp(t, ".in", 3) == 0) { + char *a = (char *)t+3; + do_indentation(a); + } else if (strncmp(t, ".ti", 3) == 0) { + char *a = (char *)t+3; + do_tempindent(a); + } else if (strncmp(t, ".vs", 3) == 0) { + char *a = (char *)t+3; + do_verticalspacing(a); + } else if (strncmp(t, ".ip", 3) == 0) { + do_indentedparagraph(); + } else if (strcmp(t, ".links") == 0) { + do_links(); + } +} + +/* + * is_in_middle - returns TRUE if the positions left..right are in the center of the page. + */ + +int html_printer::is_in_middle (int left, int right) +{ + return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right)) <= CENTER_TOLERANCE ); +} + +/* + * flush_globs - runs through the text glob list and emits html. + */ + +void html_printer::flush_globs (void) +{ + text_glob *g; + + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.start_from_head(); + do { + g = page_contents->glyphs.get_data(); + + if (strcmp(g->text_string, "XXXXXXX") == 0) { + stop(); + } + + if (g->is_raw()) { + emit_raw(g); + } else if (g->is_a_tag()) { + troff_tag(g); + } else if (g->is_a_line()) { + emit_line(g); + } else { + translate_to_html(g); + } + /* + * after processing the title (and removing it) the glyph list might be empty + */ + if (! page_contents->glyphs.is_empty()) { + page_contents->glyphs.move_right(); + } + } while (! page_contents->glyphs.is_equal_to_head()); + } +} + +void html_printer::flush_page (void) +{ + supress_sub_sup = TRUE; + flush_sbuf(); + // page_contents->dump_page(); + flush_globs(); + current_paragraph->done_para(); + current_paragraph->done_table(); + + // move onto a new page + delete page_contents; + page_contents = new page; +} + +/* + * determine_space - works out whether we need to write a space. + * If last glyth is ajoining then no space emitted. + */ + +void html_printer::determine_space (text_glob *g) +{ + if (current_paragraph->is_in_pre()) { + int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); + /* + * .nf has been specified + */ + while (output_hpos < g->minh) { + output_hpos += space_width; + current_paragraph->emit_space(); + } + } else { + if ((output_vpos != g->minv) || (output_hpos < g->minh)) { + current_paragraph->emit_space(); + } + } +} + +/* + * is_font_courier - returns TRUE if the font, f, is courier. + */ + +int html_printer::is_font_courier (font *f) +{ + if (f != 0) { + const char *fontname = f->get_name(); + + return( (fontname != 0) && (fontname[0] == 'C') ); + } + return( FALSE ); +} + +/* + * end_font - shuts down the font corresponding to fontname. + */ + +void html_printer::end_font (const char *fontname) +{ + if (strcmp(fontname, "B") == 0) { + current_paragraph->done_bold(); + } else if (strcmp(fontname, "I") == 0) { + current_paragraph->done_italic(); + } else if (strcmp(fontname, "BI") == 0) { + current_paragraph->done_bold(); + current_paragraph->done_italic(); + } else if (strcmp(fontname, "CR") == 0) { + current_paragraph->done_tt(); + current_paragraph->done_pre(); + } +} + +/* + * start_font - starts the font corresponding to name. + */ + +void html_printer::start_font (const char *fontname) +{ + if (strcmp(fontname, "R") == 0) { + current_paragraph->done_bold(); + current_paragraph->done_italic(); + current_paragraph->done_tt(); + } else if (strcmp(fontname, "B") == 0) { + current_paragraph->do_bold(); + } else if (strcmp(fontname, "I") == 0) { + current_paragraph->do_italic(); + } else if (strcmp(fontname, "BI") == 0) { + current_paragraph->do_bold(); + current_paragraph->do_italic(); + } else if (strcmp(fontname, "CR") == 0) { + if ((! fill_on) && (is_courier_until_eol())) { + current_paragraph->do_pre(); + } + current_paragraph->do_tt(); + } +} + +/* + * start_size - from is old font size, to is the new font size. + * The html increase and decrease alters the + * font size by 20%. We try and map these onto glyph sizes. + */ + +void html_printer::start_size (int from, int to) +{ + if (from < to) { + while (from < to) { + current_paragraph->do_big(); + from += SIZE_INCREMENT; + } + } else if (from > to) { + while (from > to) { + current_paragraph->do_small(); + from -= SIZE_INCREMENT; + } + } +} + +/* + * do_font - checks to see whether we need to alter the html font. + */ + +void html_printer::do_font (text_glob *g) +{ + /* + * check if the output_style.point_size has not been set yet + * this allow users to place .ps at the top of their troff files + * and grohtml can then treat the .ps value as the base font size (3) + */ + if (output_style.point_size == -1) { + output_style.point_size = pointsize; + } + + if (g->text_style.f != output_style.f) { + if (output_style.f != 0) { + end_font(output_style.f->get_name()); + } + output_style.f = g->text_style.f; + if (output_style.f != 0) { + start_font(output_style.f->get_name()); + } + } + if (output_style.point_size != g->text_style.point_size) { + do_sup_or_sub(g); + if ((output_style.point_size > 0) && + (g->text_style.point_size > 0)) { + start_size(output_style.point_size, g->text_style.point_size); + } + if (g->text_style.point_size > 0) { + output_style.point_size = g->text_style.point_size; + } + } +} + +/* + * start_subscript - returns TRUE if, g, looks like a subscript start. + */ + +int html_printer::start_subscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (output_vpos < g->minv) && + (output_vpos-height > g->maxv) && + (output_style.point_size > g->text_style.point_size) ); +} + +/* + * start_superscript - returns TRUE if, g, looks like a superscript start. + */ + +int html_printer::start_superscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (output_vpos > g->minv) && + (output_vpos-height < g->maxv) && + (output_style.point_size > g->text_style.point_size) ); +} + +/* + * end_subscript - returns TRUE if, g, looks like the end of a subscript. + */ + +int html_printer::end_subscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (g->minv < output_vpos) && + (output_vpos-height > g->maxv) && + (output_style.point_size < g->text_style.point_size) ); +} + +/* + * end_superscript - returns TRUE if, g, looks like the end of a superscript. + */ + +int html_printer::end_superscript (text_glob *g) +{ + int r = font::res; + int height = output_style.point_size*r/72; + + return( (output_style.point_size != 0) && + (g->minv > output_vpos) && + (output_vpos-height < g->maxv) && + (output_style.point_size < g->text_style.point_size) ); +} + +/* + * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript + * start/end and it calls the services of html-text to issue the + * appropriate tags. + */ + +void html_printer::do_sup_or_sub (text_glob *g) +{ + if (! supress_sub_sup) { + if (start_subscript(g)) { + current_paragraph->do_sub(); + } else if (start_superscript(g)) { + current_paragraph->do_sup(); + } else if (end_subscript(g)) { + current_paragraph->done_sub(); + } else if (end_superscript(g)) { + current_paragraph->done_sup(); + } + } +} + +/* + * translate_to_html - translates a textual string into html text + */ + +void html_printer::translate_to_html (text_glob *g) +{ + char buf[MAX_STRING_LENGTH]; + + do_font(g); + determine_space(g); + str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, + g->text_string, g->text_length, TRUE); + current_paragraph->do_emittext(buf, strlen(buf)); + output_vpos = g->minv; + output_hpos = g->maxh; + output_vpos_max = g->maxv; + supress_sub_sup = FALSE; +} + +/* + * flush_sbuf - flushes the current sbuf into the list of glyphs. + */ + +void html_printer::flush_sbuf() +{ + if (sbuf_len > 0) { + int r=font::res; // resolution of the device + set_style(sbuf_style); + + page_contents->add(&sbuf_style, sbuf, sbuf_len, + line_number, + sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos, + sbuf_vpos , sbuf_end_hpos); + + output_hpos = sbuf_end_hpos; + output_vpos = sbuf_vpos; + sbuf_len = 0; + } +} + +void html_printer::set_line_thickness(const environment *env) +{ + line_thickness = env->size; +} + +void html_printer::draw(int code, int *p, int np, const environment *env) +{ + switch (code) { + + case 'l': + if (np == 2) { + page_contents->add_line(&sbuf_style, + line_number, + env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness); + } else { + error("2 arguments required for line"); + } + break; + case 't': + { + if (np == 0) { + line_thickness = -1; + } else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + + case 'P': + // fall through + case 'p': + { +#if 0 + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + // firstly lets add our current position to polygon + int oh=env->hpos; + int ov=env->vpos; + int i=0; + + while (iadd_polygon(code, np, p, env->hpos, env->vpos, env->size, fill); +#endif + } + break; + case 'E': + // fall through + case 'e': +#if 0 + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + page_contents->add_line(code, + env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2, + env->size, fill); +#endif + break; + case 'C': + // fill circle + + case 'c': + { +#if 0 + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + page_contents->add_line(code, + env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2, + env->size, fill); +#endif + } + break; + case 'a': + { +#if 0 + if (np == 4) { + double c[2]; + + if (adjust_arc_center(p, c)) { + page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill); + } else { + // a straignt line + page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill); + } + } else { + error("4 arguments required for arc"); + } +#endif + } + break; + case '~': + { +#if 0 + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + // firstly lets add our current position to spline + int oh=env->hpos; + int ov=env->vpos; + int i=0; + + while (iadd_spline('~', env->hpos, env->vpos, np, p, env->size, fill); +#endif + } + break; + case 'f': + { +#if 0 + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + fill = p[0]; + if (fill < 0 || fill > FILL_MAX) { + // This means fill with the current color. + fill = FILL_MAX + 1; + } +#endif + break; + } + + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } +} + +html_printer::html_printer() +: html(0, MAX_LINE_LENGTH), + no_of_printed_pages(0), + sbuf_len(0), + output_hpos(-1), + output_vpos(-1), + output_vpos_max(-1), + line_thickness(-1), + inside_font_style(0), + page_number(0), + header_indent(-1), + supress_sub_sup(TRUE), + cutoff_heading(100), + end_center(0), + end_tempindent(0), + next_tag(INLINE), + fill_on(TRUE), + pageoffset(0), + indentation(0), + prev_indent(0), + linelength(0), + line_number(0) +{ +#if defined(DEBUGGING) + file_list.add_new_file(stdout); +#else + file_list.add_new_file(xtmpfile()); +#endif + html.set_file(file_list.get_file()); + if (font::hor != 24) + fatal("horizontal resolution must be 24"); + if (font::vert != 40) + fatal("vertical resolution must be 40"); +#if 0 + // should be sorted html.. + if (font::res % (font::sizescale*72) != 0) + fatal("res must be a multiple of 72*sizescale"); +#endif + int r = font::res; + int point = 0; + while (r % 10 == 0) { + r /= 10; + point++; + } + res = r; + html.set_fixed_point(point); + space_char_index = font::name_to_index("space"); + paper_length = font::paperlength; + linelength = font::res*13/2; + if (paper_length == 0) + paper_length = 11*font::res; + + page_contents = new page(); +} + +/* + * add_char_to_sbuf - adds a single character to the sbuf. + */ + +void html_printer::add_char_to_sbuf (unsigned char code) +{ + if (sbuf_len < SBUF_SIZE) { + sbuf[sbuf_len] = code; + sbuf_len++; + } else { + fatal("need to increase SBUF_SIZE"); + } +} + +/* + * add_to_sbuf - adds character code or name to the sbuf. + */ + +void html_printer::add_to_sbuf (unsigned char code, const char *name) +{ + if (name == 0) { + add_char_to_sbuf(code); + } else { + if (sbuf_style.f != NULL) { + char *html_glyph = get_html_translation(sbuf_style.f, name); + + if (html_glyph == NULL) { + add_char_to_sbuf(code); + } else { + int l = strlen(html_glyph); + int i; + + // Escape the name, so that "&" doesn't get expanded to "&" + // later during translate_to_html. + add_char_to_sbuf('\\'); add_char_to_sbuf('('); + + for (i=0; ihpos) { + add_to_sbuf(code, name); + sbuf_end_hpos += w + sbuf_kern; + return( TRUE ); + } else { + if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) && + ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) { + /* + * lets see whether a space is needed or not + */ + int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size); + + if (env->hpos-sbuf_end_hpos < space_width/2) { + add_to_sbuf(code, name); + sbuf_end_hpos = env->hpos + w; + return( TRUE ); + } + } + } + return( FALSE ); +} + +/* + * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s + */ + +int html_printer::seen_backwards_escape (char *s, int l) +{ + /* + * this is tricky so it is broken into components for clarity + * (we let the compiler put in all back into a complex expression) + */ + if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) { + /* + * ok seen '\(' but we must now check for '\\(' + */ + if ((l>1) && (sbuf[l-2] == '\\')) { + /* + * escaped the escape + */ + return( FALSE ); + } else { + return( TRUE ); + } + } else { + return( FALSE ); + } +} + +/* + * reverse - return reversed string. + */ + +char *reverse (char *s) +{ + int i=0; + int j=strlen(s)-1; + char t; + + while (i0) { + l--; + if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) { + /* + * found terminating escape + */ + int i=0; + + l -= 2; + while ((l>0) && (! seen_backwards_escape(sbuf, l))) { + if (sbuf[l] == '\\') { + if (sbuf[l-1] == '\\') { + last[i] = sbuf[l]; + i++; + l--; + } + } else { + last[i] = sbuf[l]; + i++; + } + l--; + } + last[i] = (char)0; + sbuf_len = l; + if (seen_backwards_escape(sbuf, l)) { + sbuf_len--; + } + return( reverse(last) ); + } else { + if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) { + l -= 2; + sbuf_len = l; + return( "\\" ); + } else { + sbuf_len--; + last[0] = sbuf[sbuf_len]; + last[1] = (char)0; + return( last ); + } + } + } else { + return( NULL ); + } +} + +/* + * get_html_translation - given the position of the character and its name + * return the device encoding for such character. + */ + +char *get_html_translation (font *f, const char *name) +{ + int index; + + if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) { + return( NULL ); + } else { + index = f->name_to_index((char *)name); + if (index == 0) { + error("character `%s' not found", name); + return( NULL ); + } else { + if (f->contains(index)) { + return( (char *)f->get_special_device_encoding(index) ); + } else { + return( NULL ); + } + } + } +} + +/* + * to_unicode - returns a unicode translation of char, ch. + */ + +static char *to_unicode (unsigned char ch) +{ + static char buf[20]; + + sprintf(buf, "&#%u;", (unsigned int)ch); + return( buf ); +} + +/* + * char_translate_to_html - convert a single non escaped character + * into the appropriate html character. + */ + +int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single) +{ + if (and_single) { + int t, l; + char *translation; + char name[2]; + + name[0] = ch; + name[1] = (char)0; + translation = get_html_translation(f, name); + if ((translation == NULL) && (ch >= UNICODE_DESC_START)) { + translation = to_unicode(ch); + } + if (translation) { + l = strlen(translation); + t = max(0, min(l, buflen-b)); + strncpy(&buf[b], translation, t); + b += t; + } else { + if (b & etc. + */ + +void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single) +{ + char *translation; + int e; + char escaped_char[MAX_STRING_LENGTH]; + int l; + int i=0; + int b=0; + int t=0; + +#if 0 + if (strcmp(str, "``@,;:\\\\()[]''") == 0) { + stop(); + } +#endif + while (str[i] != (char)0) { + if ((str[i]=='\\') && (i+1 0) { + translation = get_html_translation(f, escaped_char); + if (translation) { + l = strlen(translation); + t = max(0, min(l, buflen-b)); + strncpy(&buf[b], translation, t); + b += t; + } else { + int index=f->name_to_index(escaped_char); + + if (f->contains(index) && (index != 0)) { + buf[b] = f->get_code(index); + b++; + } + } + } + } else { + b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); + i++; + } + } else { + b = char_translate_to_html(f, buf, buflen, str[i], b, and_single); + i++; + } + } + buf[min(b, buflen)] = (char)0; +} + +/* + * set_char - adds a character into the sbuf if it is a continuation with the previous + * word otherwise flush the current sbuf and add character anew. + */ + +void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name) +{ + unsigned char code = f->get_code(i); + +#if 0 + if (code == ' ') { + stop(); + } +#endif + style sty(f, env->size, env->height, env->slant, env->fontno); + if (sty.slant != 0) { + if (sty.slant > 80 || sty.slant < -80) { + error("silly slant `%1' degrees", sty.slant); + sty.slant = 0; + } + } + if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) && + (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) { + return; + } else { + flush_sbuf(); + sbuf_len = 0; + add_to_sbuf(code, name); + sbuf_end_hpos = env->hpos + w; + sbuf_start_hpos = env->hpos; + sbuf_vpos = env->vpos; + sbuf_style = sty; + sbuf_kern = 0; + } +} + +/* + * write_title - writes the title to this document + */ + +void html_printer::write_title (int in_head) +{ + if (title.has_been_found) { + if (in_head) { + html.put_string(""); + html.put_string(title.text); + html.put_string("").nl().nl(); + } else { + title.has_been_written = TRUE; + html.put_string("

"); + html.put_string(title.text); + html.put_string("

").nl().nl(); + } + } else if (in_head) { + // place empty title tags to help conform to `tidy' + html.put_string("").nl(); + } +} + +/* + * write_rule - emits a html rule tag, if the auto_rule boolean is true. + */ + +static void write_rule (void) +{ + if (auto_rule) + fputs("
\n", stdout); +} + +void html_printer::begin_page(int n) +{ + page_number = n; +#if defined(DEBUGGING) + html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();; +#endif + no_of_printed_pages++; + + output_style.f = 0; + output_style.point_size= -1; + output_space_code = 32; + output_draw_point_size = -1; + output_line_thickness = -1; + output_hpos = -1; + output_vpos = -1; + output_vpos_max = -1; + current_paragraph = new html_text(&html); +#if defined(INDENTATION) + current_paragraph->do_indent(indent.text, indentation, pageoffset, linelength); +#endif + current_paragraph->do_para(""); +} + +void html_printer::end_page(int) +{ + flush_sbuf(); + flush_page(); +} + +font *html_printer::make_font(const char *nm) +{ + return html_font::load_html_font(nm); +} + +html_printer::~html_printer() +{ + current_paragraph->flush_text(); + html.end_line(); + html.set_file(stdout); + /* + * 'HTML: The definitive guide', O'Reilly, p47. advises against specifying + * the dtd, so for the moment I'll leave this commented out. + * If requested we could always emit it if a command line switch + * was present. + * + * fputs("\n", stdout); + */ + fputs("\n", stdout); + fputs("\n", stdout); + fputs("\n", stdout); + fputs("\n", stdout); + write_title(TRUE); + fputs("\n", stdout); + fputs("\n\n", stdout); + write_title(FALSE); + header.write_headings(stdout, FALSE); + write_rule(); + { + extern const char *Version_string; + html.begin_comment("Creator : ") + .put_string("groff ") + .put_string("version ") + .put_string(Version_string) + .end_comment(); + } + { +#ifdef LONG_FOR_TIME_T + long +#else + time_t +#endif + t = time(0); + html.begin_comment("CreationDate: ") + .put_string(ctime(&t), strlen(ctime(&t))-1) + .end_comment(); + } +#if defined(DEBUGGING) + html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment(); +#endif + html.end_line(); + html.end_line(); + /* + * now run through the file list copying each temporary file in turn and emitting the links. + */ + file_list.start_of_list(); + while (file_list.get_file() != 0) { + if (fseek(file_list.get_file(), 0L, 0) < 0) + fatal("fseek on temporary file failed"); + html.copy_file(file_list.get_file()); + fclose(file_list.get_file()); + file_list.move_next(); + if (file_list.get_file() != 0) + header.write_headings(stdout, TRUE); + } + write_rule(); + fputs("\n", stdout); + fputs("\n", stdout); +} + +/* + * special - handle all x X requests from troff. For post-html they allow users + * to pass raw html commands, turn auto linked headings off/on and + * also allow troff to emit tags to indicate when a: .br, .sp etc occurs. + */ + +void html_printer::special(char *s, const environment *env, char type) +{ + if (type != 'p') + return; + if (s != 0) { + flush_sbuf(); + if (env->fontno >= 0) { + style sty(get_font_from_index(env->fontno), env->size, env->height, env->slant, env->fontno); + sbuf_style = sty; + } + + if (strncmp(s, "html:", 5) == 0) { + int r=font::res; /* resolution of the device */ + char buf[MAX_STRING_LENGTH]; + font *f=sbuf_style.f; + + if (f == NULL) { + int found=FALSE; + + f = font::load_font("TR", &found); + } + str_translate_to_html(f, buf, MAX_STRING_LENGTH, + &s[5], strlen(s)-5, FALSE); + + /* + * need to pass rest of string through to html output during flush + */ + page_contents->add_html(&sbuf_style, buf, strlen(buf), + line_number, + env->vpos-env->size*r/72, env->hpos, + env->vpos , env->hpos); + + /* + * assume that the html command has no width, if it does then hopefully troff + * will have fudged this in a macro by requesting that the formatting move right by + * the appropriate width. + */ + } else if (strncmp(s, "index:", 6) == 0) { + cutoff_heading = atoi(&s[6]); + } else if (strncmp(s, "html-tag:", 9) == 0) { + int r=font::res; /* resolution of the device */ + + page_contents->add_tag(&sbuf_style, s, strlen(s), + line_number, + env->vpos-env->size*r/72, env->hpos, + env->vpos , env->hpos); + } + } +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "o:i:F:vd?lrn", long_options, NULL)) + != EOF) + switch(c) { + case 'v': + { + extern const char *Version_string; + printf("GNU post-grohtml (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'F': + font::command_line_font_dir(optarg); + break; + case 'l': + auto_links = FALSE; + break; + case 'r': + auto_rule = FALSE; + break; + case 'o': + /* handled by pre-html */ + break; + case 'i': + /* handled by pre-html */ + break; + case 'n': + simple_anchors = TRUE; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + if (optind >= argc) { + do_file("-"); + } else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [-vld?n] [-F dir] [files ...]\n", + program_name); +} -- cgit v1.1