From d318611dd6f23fcfedd50e9b9e24620b102ba96a Mon Sep 17 00:00:00 2001
From: Daniel Baumann ", (char *)p->arg1); break;
+ } else {
+ fprintf(stderr, " ", (char *)p->arg1); break;
+ }
+ case I_TAG: fprintf(stderr, ""); break;
+ case B_TAG: fprintf(stderr, ""); break;
+ case SUB_TAG: fprintf(stderr, ""); break;
+ case SUP_TAG: fprintf(stderr, ""); break;
+ case TT_TAG: fprintf(stderr, ""); break;
+ case PRE_TAG: if (p->indent == NULL) {
+ fprintf(stderr, "put_string(">")
+ .nl();
+ if (dialect == xhtml)
+ emit_colspan();
+ out->put_string("
");
+}
+
+/*
+ * add_column - adds a column. It returns FALSE if hstart..hend
+ * crosses into a different columns.
+ */
+
+int html_table::add_column (int coln, int hstart, int hend, char align)
+{
+ cols *c = get_column(coln);
+
+ if (c == NULL)
+ return insert_column(coln, hstart, hend, align);
+ else
+ return modify_column(c, hstart, hend, align);
+}
+
+/*
+ * get_column - returns the column, coln.
+ */
+
+cols *html_table::get_column (int coln)
+{
+ cols *c = columns;
+
+ while (c != NULL && coln != c->no)
+ c = c->next;
+
+ if (c != NULL && coln == c->no)
+ return c;
+ else
+ return NULL;
+}
+
+/*
+ * insert_column - inserts a column, coln.
+ * It returns TRUE if it does not bump into
+ * another column.
+ */
+
+int html_table::insert_column (int coln, int hstart, int hend, char align)
+{
+ cols *c = columns;
+ cols *l = columns;
+ cols *n = NULL;
+
+ while (c != NULL && c->no < coln) {
+ l = c;
+ c = c->next;
+ }
+ if (l != NULL && l->no>coln && hend > l->left)
+ return FALSE; // new column bumps into previous one
+
+ l = NULL;
+ c = columns;
+ while (c != NULL && c->no < coln) {
+ l = c;
+ c = c->next;
+ }
+
+ if ((l != NULL) && (hstart < l->right))
+ return FALSE; // new column bumps into previous one
+
+ if ((l != NULL) && (l->next != NULL) &&
+ (l->next->left < hend))
+ return FALSE; // new column bumps into next one
+
+ n = new cols;
+ if (l == NULL) {
+ n->next = columns;
+ columns = n;
+ } else {
+ n->next = l->next;
+ l->next = n;
+ }
+ n->left = hstart;
+ n->right = hend;
+ n->no = coln;
+ n->alignment = align;
+ return TRUE;
+}
+
+/*
+ * modify_column - given a column, c, modify the width to
+ * contain hstart..hend.
+ * It returns TRUE if it does not clash with
+ * the next or previous column.
+ */
+
+int html_table::modify_column (cols *c, int hstart, int hend, char align)
+{
+ cols *l = columns;
+
+ while (l != NULL && l->next != c)
+ l = l->next;
+
+ if ((l != NULL) && (hstart < l->right))
+ return FALSE; // new column bumps into previous one
+
+ if ((c->next != NULL) && (c->next->left < hend))
+ return FALSE; // new column bumps into next one
+
+ if (c->left > hstart)
+ c->left = hstart;
+
+ if (c->right < hend)
+ c->right = hend;
+
+ c->alignment = align;
+
+ return TRUE;
+}
+
+/*
+ * find_tab_column - finds the column number for position, pos.
+ * It searches through the list tab stops.
+ */
+
+int html_table::find_tab_column (int pos)
+{
+ // remember the first column is reserved for untabbed glyphs
+ return tab_stops->find_tab(pos)+1;
+}
+
+/*
+ * find_column - find the column number for position, pos.
+ * It searches through the list of columns.
+ */
+
+int html_table::find_column (int pos)
+{
+ int p=0;
+ cols *c;
+
+ for (c = columns; c != NULL; c = c->next) {
+ if (c->left > pos)
+ return p;
+ p = c->no;
+ }
+ return p;
+}
+
+/*
+ * no_columns - returns the number of table columns (rather than tabs)
+ */
+
+int html_table::no_columns (void)
+{
+ int n=0;
+ cols *c;
+
+ for (c = columns; c != NULL; c = c->next)
+ n++;
+ return n;
+}
+
+/*
+ * is_gap - returns the gap between column, c, and the next column.
+ */
+
+int html_table::is_gap (cols *c)
+{
+ if (c == NULL || c->right <= 0 || c->next == NULL)
+ return 0;
+ else
+ // we compute the difference after converting positions
+ // to avoid rounding errors
+ return (c->next->left*100 + get_effective_linelength()/2)
+ / get_effective_linelength()
+ - (c->right*100 + get_effective_linelength()/2)
+ / get_effective_linelength();
+}
+
+/*
+ * no_gaps - returns the number of table gaps between the columns
+ */
+
+int html_table::no_gaps (void)
+{
+ int n=0;
+ cols *c;
+
+ for (c = columns; c != NULL; c = c->next)
+ if (is_gap(c))
+ n++;
+ return n;
+}
+
+/*
+ * get_tab_pos - returns the, nth, tab position
+ */
+
+int html_table::get_tab_pos (int n)
+{
+ return tab_stops->get_tab_pos(n);
+}
+
+char html_table::get_tab_align (int n)
+{
+ return tab_stops->get_tab_align(n);
+}
+
+
+void html_table::dump_table (void)
+{
+ if (columns != NULL) {
+ cols *c;
+ for (c = columns; c != NULL; c = c->next) {
+ printf("column %d %d..%d %c\n", c->no, c->left, c->right, c->alignment);
+ }
+ } else
+ tab_stops->dump_tabs();
+}
+
+/*
+ * html_indent - creates an indent with indentation, ind, given
+ * a line length of linelength.
+ */
+
+html_indent::html_indent (simple_output *op, int ind, int pageoffset, int linelength)
+{
+ table = new html_table(op, linelength);
+
+ table->add_column(1, ind+pageoffset, linelength, 'L');
+ table->add_indent(pageoffset);
+ in = ind;
+ pg = pageoffset;
+ ll = linelength;
+}
+
+html_indent::~html_indent (void)
+{
+ end();
+ delete table;
+}
+
+void html_indent::begin (int space)
+{
+ if (in + pg == 0) {
+ if (space) {
+ table->out->put_string(" style=\"margin-top: ");
+ table->out->put_string(STYLE_VERTICAL_SPACE);
+ table->out->put_string("\"");
+ }
+ }
+ else {
+ //
+ // we use exactly the same mechanism for calculating
+ // indentation as html_table::emit_col
+ //
+ table->out->put_string(" style=\"margin-left:")
+ .put_number(((in + pg) * 100 + ll/2) / ll -
+ (ll/2)/ll)
+ .put_string("%;");
+
+ if (space) {
+ table->out->put_string(" margin-top: ");
+ table->out->put_string(STYLE_VERTICAL_SPACE);
+ }
+ table->out->put_string("\"");
+ }
+}
+
+void html_indent::end (void)
+{
+}
+
+/*
+ * get_reg - collects the registers as supplied during initialization.
+ */
+
+void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
+{
+ *ind = in;
+ *pageoffset = pg;
+ *linelength = ll;
+}
diff --git a/src/devices/grohtml/html-table.h b/src/devices/grohtml/html-table.h
new file mode 100644
index 0000000..7ed27a9
--- /dev/null
+++ b/src/devices/grohtml/html-table.h
@@ -0,0 +1,133 @@
+// -*- C++ -*-
+/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote html-table.h
+ *
+ * html-table.h
+ *
+ * provides the methods necessary to handle indentation and tab
+ * positions using html tables.
+ */
+
+/*
+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 3 of the License, 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 this program. If not, see put_string(" style=\"margin-top: ");
+ out->put_string(STYLE_VERTICAL_SPACE);
+ out->put_string("\"");
+ }
+ out->put_string(">").nl();
+ }
+}
+
+/*
+ * get_right - returns the right most position of this column.
+ */
+
+int html_table::get_right (cols *c)
+{
+ if (c != NULL && c->right > 0)
+ return c->right;
+ if (c->next != NULL)
+ return c->left;
+ return linelength;
+}
+
+/*
+ * set_space - assigns start_space. Used to determine the
+ * vertical alignment when generating the next table row.
+ */
+
+void html_table::set_space (int space)
+{
+ start_space = space;
+}
+
+/*
+ * emit_colspan - emits a series of colspan entries defining the
+ * table columns.
+ */
+
+void html_table::emit_colspan (void)
+{
+ cols *b = columns;
+ cols *c = columns;
+ int width = 0;
+
+ out->put_string(" ").nl();
+ }
+}
+
+/*
+ * emit_new_row - move to the next row.
+ */
+
+void html_table::emit_new_row (void)
+{
+ finish_row();
+
+ out->put_string(" tag with a corresponding width
+ * if the dialect is html4.
+ */
+
+void html_table::emit_td (int percentage, const char *s)
+{
+ if (percentage) {
+ if (dialect == html4) {
+ out->put_string(" put_string(s);
+ out->nl();
+ }
+ else {
+ out->put_string(" put_string(s);
+ out->nl();
+ }
+ }
+}
+
+/*
+ * emit_col - moves onto column, n.
+ */
+
+void html_table::emit_col (int n)
+{
+ cols *c = columns;
+ cols *b = columns;
+ int width = 0;
+
+ // must be a different row
+ if (last_col != NULL && n <= last_col->no)
+ emit_new_row();
+
+ while (c != NULL && c->no < n)
+ c = c->next;
+
+ // can we find column, n?
+ if (c != NULL && c->no == n) {
+ // shutdown previous column
+ if (last_col != NULL)
+ out->put_string(" ").nl();
+
+ // find previous column
+ if (last_col == NULL)
+ b = columns;
+ else
+ b = last_col;
+
+ // have we a gap?
+ if (last_col != NULL) {
+ emit_td(is_gap(b), ">");
+ b = b->next;
+ }
+
+ // move across to column n
+ while (b != c) {
+ // we compute the difference after converting positions
+ // to avoid rounding errors
+ width = (get_right(b)*100 + get_effective_linelength()/2)
+ / get_effective_linelength()
+ - (b->left*100 + get_effective_linelength()/2)
+ /get_effective_linelength();
+ emit_td(width, ">");
+ // have we a gap?
+ emit_td(is_gap(b), ">");
+ b = b->next;
+ }
+ width = (get_right(b)*100 + get_effective_linelength()/2)
+ / get_effective_linelength()
+ - (b->left*100 + get_effective_linelength()/2)
+ /get_effective_linelength();
+ switch (b->alignment) {
+ case 'C':
+ emit_td(width, " align=center>");
+ break;
+ case 'R':
+ emit_td(width, " align=right>");
+ break;
+ default:
+ emit_td(width);
+ }
+ // remember column, b
+ last_col = b;
+ }
+}
+
+/*
+ * finish_row -
+ */
+
+void html_table::finish_row (void)
+{
+ int n = 0;
+ cols *c;
+
+ if (last_col != NULL) {
+ for (c = last_col->next; c != NULL; c = c->next)
+ n = c->no;
+
+ if (n > 0)
+ emit_col(n);
+#if 1
+ if (last_col != NULL) {
+ out->put_string("");
+ last_col = NULL;
+ }
+#endif
+ out->put_string("put_string(" style=\"margin-top: ");
+ out->put_string(STYLE_VERTICAL_SPACE);
+ out->put_string("\"");
+ }
+ out->put_string(">").nl();
+ start_space = FALSE;
+ last_col = NULL;
+}
+
+void html_table::emit_finish_table (void)
+{
+ finish_row();
+ out->put_string(" "); break;
+ } else {
+ fprintf(stderr, "
"); break;
+ case B_TAG: out->put_string(""); break;
+ case P_TAG: if (t->indent == NULL) {
+ out->put_string(""); break;
+ }
+ case SMALL_TAG: fprintf(stderr, ""); break;
+ case BIG_TAG: fprintf(stderr, ""); break;
+ case BREAK_TAG: fprintf(stderr, "
indent->begin(start_space); + issue_tag("", (char *)t->arg1); + } else { + out->nl(); + issue_tag("\n
arg1, start_space);
+ }
+
+ 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->enable_newlines(TRUE);
+ out->nl(); out->put_string(" ", f);
+ do {
+ g = headers.get_data();
+ fputs("text_string, f);
+ }
+ fputs("#", f);
+ if (simple_anchors) {
+ string buffer(ANCHOR_TEMPLATE);
+
+ buffer += as_string(h);
+ buffer += '\0';
+ fprintf(f, "%s", buffer.contents());
+ } else
+ fputs(g->text_string, f);
+ h++;
+ fputs("\">", f);
+ fputs(g->text_string, f);
+ fputs("", f);
+ if (dialect == xhtml)
+ fputs("indent == NULL)
+ issue_tag("", (char *)t->arg1, start_space);
+ else {
+ t->indent->begin(start_space);
+ issue_tag("", (char *)t->arg1);
+ }
+ out->enable_newlines(FALSE); break;
+ case SMALL_TAG: if (! is_in_pre ())
+ issue_tag("arg1);
+ break;
+ case BIG_TAG: if (! is_in_pre ())
+ issue_tag("arg1);
+ break;
+ case BREAK_TAG: break;
+ case COLOR_TAG: if (! is_in_pre ())
+ issue_color_begin(&t->col);
+ break;
+
+ default:
+ error("unrecognised tag");
+ }
+}
+
+/*
+ * 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;
+ delete 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;
+}
+
+/*
+ * uses_indent - returns TRUE if the current paragraph is using a
+ * html table to effect an indent.
+ */
+
+int html_text::uses_indent (void)
+{
+ tag_definition *p = stackptr;
+
+ while (p != NULL) {
+ if (p->indent != NULL)
+ return TRUE;
+ p = p->next;
+ }
+ return FALSE;
+}
+
+extern void stop();
+
+/*
+ * do_push - places, tag_definition, p, onto the stack
+ */
+
+void html_text::do_push (tag_definition *p)
+{
+ HTML_TAG t = p->type;
+
+#if defined(DEBUGGING)
+ if (t == PRE_TAG)
+ stop();
+ debugStack = TRUE;
+ fprintf(stderr, "\nentering do_push (");
+ dump_stack_element(p);
+ fprintf(stderr, ")\n");
+ dump_stack();
+ fprintf(stderr, ")\n");
+ fflush(stderr);
+#endif
+
+ /*
+ * if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack.
+ */
+
+ if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) {
+ /*
+ * store, p, at the end
+ */
+ lastptr->next = p;
+ lastptr = p;
+ p->next = NULL;
+ } else {
+ p->next = stackptr;
+ if (stackptr == NULL)
+ lastptr = p;
+ stackptr = p;
+ }
+
+#if defined(DEBUGGING)
+ dump_stack();
+ fprintf(stderr, "exiting do_push\n");
+#endif
+}
+
+/*
+ * push_para - adds a new entry onto the html paragraph stack.
+ */
+
+void html_text::push_para (HTML_TAG t, void *arg, html_indent *in)
+{
+ tag_definition *p= new tag_definition;
+
+ p->type = t;
+ p->arg1 = arg;
+ p->text_emitted = FALSE;
+ p->indent = in;
+
+ if (t == PRE_TAG && is_present(PRE_TAG))
+ fatal("cannot have multiple PRE_TAGs");
+
+ do_push(p);
+}
+
+void html_text::push_para (HTML_TAG t)
+{
+ push_para(t, (void *)"", NULL);
+}
+
+void html_text::push_para (color *c)
+{
+ tag_definition *p = new tag_definition;
+
+ p->type = COLOR_TAG;
+ p->arg1 = NULL;
+ p->col = *c;
+ p->text_emitted = FALSE;
+ p->indent = NULL;
+
+ do_push(p);
+}
+
+/*
+ * do_italic - changes to italic
+ */
+
+void html_text::do_italic (void)
+{
+ if (! is_present(I_TAG))
+ push_para(I_TAG);
+}
+
+/*
+ * do_bold - changes to bold.
+ */
+
+void html_text::do_bold (void)
+{
+ if (! is_present(B_TAG))
+ push_para(B_TAG);
+}
+
+/*
+ * do_tt - changes to teletype.
+ */
+
+void html_text::do_tt (void)
+{
+ 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_tt();
+ if (is_present(P_TAG)) {
+ html_indent *i = remove_indent(P_TAG);
+ int space = retrieve_para_space();
+ (void)done_para();
+ if (! is_present(PRE_TAG))
+ push_para(PRE_TAG, NULL, i);
+ start_space = space;
+ } else if (! is_present(PRE_TAG))
+ push_para(PRE_TAG, NULL, NULL);
+ dump_stack();
+}
+
+/*
+ * 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);
+}
+
+/*
+ * do_color - initiates a new color tag.
+ */
+
+void html_text::do_color (color *c)
+{
+ shutdown(COLOR_TAG); // shutdown a previous color tag, if present
+ push_para(c);
+}
+
+/*
+ * done_color - shutdown an outstanding color tag, if it exists.
+ */
+
+void html_text::done_color (void)
+{
+ shutdown(COLOR_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;
+
+ dump_stack();
+ 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 = (char *)stackptr->arg1;
+ }
+ p = stackptr;
+ stackptr = stackptr->next;
+ if (stackptr == NULL)
+ lastptr = NULL;
+ if (p->indent != NULL)
+ delete p->indent;
+ delete p;
+ }
+
+ /*
+ * and restore unaffected tags
+ */
+ while (temp != NULL) {
+ if (temp->type == COLOR_TAG)
+ push_para(&temp->col);
+ else
+ push_para(temp->type, temp->arg1, temp->indent);
+ p = temp;
+ temp = temp->next;
+ delete 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)) {
+ 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 (const char *s, int length)
+{
+ if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
+ do_para("", FALSE);
+
+ if (is_present(BREAK_TAG)) {
+ int text = remove_break();
+ check_emit_text(stackptr);
+ if (text) {
+ if (is_present(PRE_TAG))
+ out->nl();
+ else if (dialect == xhtml)
+ out->put_string("
").nl();
+ else
+ out->put_string("
").nl();
+ }
+ } else
+ check_emit_text(stackptr);
+
+ out->put_string(s, length);
+ space_emitted = FALSE;
+ blank_para = FALSE;
+}
+
+/*
+ * do_para - starts a new paragraph
+ */
+
+void html_text::do_para (const char *arg, html_indent *in, int space)
+{
+ if (! is_present(P_TAG)) {
+ if (is_present(PRE_TAG)) {
+ html_indent *i = remove_indent(PRE_TAG);
+ done_pre();
+ if ((arg == NULL || (strcmp(arg, "") == 0)) &&
+ (i == in || in == NULL))
+ in = i;
+ else
+ delete i;
+ }
+ remove_sub_sup();
+ push_para(P_TAG, (void *)arg, in);
+ start_space = space;
+ }
+}
+
+void html_text::do_para (const char *arg, int space)
+{
+ do_para(arg, NULL, space);
+}
+
+void html_text::do_para (simple_output *op, const char *arg1,
+ int indentation_value, int page_offset,
+ int line_length, int space)
+{
+ html_indent *ind;
+
+ if (indentation_value == 0)
+ ind = NULL;
+ else
+ ind = new html_indent(op, indentation_value, page_offset, line_length);
+ do_para(arg1, ind, space);
+}
+
+/*
+ * done_para - shuts down a paragraph tag.
+ */
+
+char *html_text::done_para (void)
+{
+ char *result;
+ space_emitted = TRUE;
+ result = shutdown(P_TAG);
+ start_space = FALSE;
+ return result;
+}
+
+/*
+ * remove_indent - returns the indent associated with, tag.
+ * The indent associated with tag is set to NULL.
+ */
+
+html_indent *html_text::remove_indent (HTML_TAG tag)
+{
+ tag_definition *p=stackptr;
+
+ while (p != NULL) {
+ if (tag == p->type) {
+ html_indent *i = p->indent;
+ p->indent = NULL;
+ return i;
+ }
+ p = p->next;
+ }
+ return NULL;
+}
+
+/*
+ * remove_para_space - removes the leading space to a paragraph
+ * (effectively this trims off a leading '.sp' tag).
+ */
+
+void html_text::remove_para_space (void)
+{
+ start_space = FALSE;
+}
+
+/*
+ * do_space - issues an end of paragraph
+ */
+
+void html_text::do_space (void)
+{
+ if (is_in_pre()) {
+ do_emittext("", 0);
+ out->force_nl();
+ space_emitted = TRUE;
+ } else {
+ html_indent *i = remove_indent(P_TAG);
+
+ do_para(done_para(), i, TRUE);
+ 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;
+}
+
+/*
+ * ever_emitted_text - returns TRUE if we have ever emitted text in this
+ * paragraph.
+ */
+
+int html_text::ever_emitted_text (void)
+{
+ return !blank_para;
+}
+
+/*
+ * starts_with_space - returns TRUE if we started this paragraph with a .sp
+ */
+
+int html_text::starts_with_space (void)
+{
+ return start_space;
+}
+
+/*
+ * retrieve_para_space - returns TRUE, if the paragraph starts with
+ * a space and text has not yet been emitted.
+ * If TRUE is returned, then the, start_space,
+ * variable is set to FALSE.
+ */
+
+int html_text::retrieve_para_space (void)
+{
+ if (start_space && blank_para) {
+ start_space = FALSE;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/*
+ * emit_space - writes a space providing that text was written beforehand.
+ */
+
+void html_text::emit_space (void)
+{
+ 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;
+
+ 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;
+ } else if (l == 0) {
+ error("stack list pointers are wrong");
+ } else {
+ l->next = p->next;
+ if (l->next == NULL)
+ lastptr = l;
+ }
+ delete 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;
+ }
+ delete p;
+ }
+ /*
+ * now determine whether text was issued before
+ */
+ while (q != 0) {
+ if (q->text_emitted)
+ return TRUE;
+ else
+ q = q->next;
+ }
+ return FALSE;
+}
+
+/*
+ * remove_para_align - removes a paragraph which has a text
+ * argument. If the paragraph has no text
+ * argument then it is left alone.
+ */
+
+void html_text::remove_para_align (void)
+{
+ if (is_present(P_TAG)) {
+ tag_definition *p=stackptr;
+
+ while (p != NULL) {
+ if (p->type == P_TAG && p->arg1 != NULL) {
+ html_indent *i = remove_indent(P_TAG);
+ int space = retrieve_para_space();
+ done_para();
+ do_para("", i, space);
+ return;
+ }
+ p = p->next;
+ }
+ }
+}
+
+/*
+ * get_alignment - returns the alignment for the paragraph.
+ * If no alignment was given then we return "".
+ */
+
+char *html_text::get_alignment (void)
+{
+ if (is_present(P_TAG)) {
+ tag_definition *p=stackptr;
+
+ while (p != NULL) {
+ if (p->type == P_TAG && p->arg1 != NULL)
+ return (char *)p->arg1;
+ p = p->next;
+ }
+ }
+ return (char *)"";
+}
+
+/*
+ * 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/src/devices/grohtml/html-text.h b/src/devices/grohtml/html-text.h
new file mode 100644
index 0000000..ee58601
--- /dev/null
+++ b/src/devices/grohtml/html-text.h
@@ -0,0 +1,138 @@
+// -*- C++ -*-
+/* Copyright (C) 2000-2020 Free Software Foundation, Inc.
+ *
+ * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.h
+ *
+ * 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 3 of the License, 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 this program. If not, see
+ */
+
+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, "devtag:.eol") == 0);
+}
+
+/*
+ * is_eol_ce - returns TRUE if glob contains the tag eol.ce
+ */
+
+int text_glob::is_eol_ce (void)
+{
+ return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
+}
+
+/*
+ * is_tl - returns TRUE if glob contains the tag .tl
+ */
+
+int text_glob::is_tl (void)
+{
+ return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
+}
+
+/*
+ * is_eo_tl - returns TRUE if glob contains the tag eo.tl
+ */
+
+int text_glob::is_eo_tl (void)
+{
+ return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
+}
+
+/*
+ * is_nf - returns TRUE if glob contains the tag .fi 0
+ */
+
+int text_glob::is_nf (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.fi",
+ strlen("devtag:.fi")) == 0) &&
+ (get_arg() == 0);
+}
+
+/*
+ * is_fi - returns TRUE if glob contains the tag .fi 1
+ */
+
+int text_glob::is_fi (void)
+{
+ return (is_tag && (strncmp(text_string, "devtag:.fi",
+ strlen("devtag:.fi")) == 0) &&
+ (get_arg() == 1));
+}
+
+/*
+ * is_eo_h - returns TRUE if glob contains the tag .eo.h
+ */
+
+int text_glob::is_eo_h (void)
+{
+ return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
+}
+
+/*
+ * is_ce - returns TRUE if glob contains the tag .ce
+ */
+
+int text_glob::is_ce (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.ce",
+ strlen("devtag:.ce")) == 0);
+}
+
+/*
+ * is_in - returns TRUE if glob contains the tag .in
+ */
+
+int text_glob::is_in (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.in ",
+ strlen("devtag:.in ")) == 0);
+}
+
+/*
+ * is_po - returns TRUE if glob contains the tag .po
+ */
+
+int text_glob::is_po (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.po ",
+ strlen("devtag:.po ")) == 0);
+}
+
+/*
+ * is_ti - returns TRUE if glob contains the tag .ti
+ */
+
+int text_glob::is_ti (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.ti ",
+ strlen("devtag:.ti ")) == 0);
+}
+
+/*
+ * is_ll - returns TRUE if glob contains the tag .ll
+ */
+
+int text_glob::is_ll (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.ll ",
+ strlen("devtag:.ll ")) == 0);
+}
+
+/*
+ * is_col - returns TRUE if glob contains the tag .col
+ */
+
+int text_glob::is_col (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.col",
+ strlen("devtag:.col")) == 0);
+}
+
+/*
+ * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
+ */
+
+int text_glob::is_tab_ts (void)
+{
+ return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
+}
+
+/*
+ * is_tab_te - returns TRUE if glob contains the tag .tab_te
+ */
+
+int text_glob::is_tab_te (void)
+{
+ return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
+}
+
+/*
+ * is_ta - returns TRUE if glob contains the tag .ta
+ */
+
+int text_glob::is_ta (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:.ta ",
+ strlen("devtag:.ta ")) == 0);
+}
+
+/*
+ * is_tab - returns TRUE if glob contains the tag tab
+ */
+
+int text_glob::is_tab (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:tab ",
+ strlen("devtag:tab ")) == 0);
+}
+
+/*
+ * is_tab0 - returns TRUE if glob contains the tag tab0
+ */
+
+int text_glob::is_tab0 (void)
+{
+ return is_tag && (strncmp(text_string, "devtag:tab0",
+ strlen("devtag:tab0")) == 0);
+}
+
+/*
+ * 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
+ * or an implied .br. Note that we do not include .nf or .fi
+ * as grohtml will place a .br after these commands if they
+ * should break the line.
+ */
+
+int text_glob::is_br (void)
+{
+ return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0) ||
+ (strncmp("devtag:.sp", text_string,
+ strlen("devtag:.sp")) == 0));
+}
+
+int text_glob::get_arg (void)
+{
+ if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
+ const char *p = text_string;
+
+ while ((*p != (char)0) && (!isspace(*p)))
+ p++;
+ while ((*p != (char)0) && (isspace(*p)))
+ p++;
+ if (*p == (char)0)
+ return -1;
+ return atoi(p);
+ }
+ return -1;
+}
+
+/*
+ * get_tab_args - returns the tab position and alignment of the tab tag
+ */
+
+int text_glob::get_tab_args (char *align)
+{
+ if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
+ const char *p = text_string;
+
+ // firstly the alignment C|R|L
+ while ((*p != (char)0) && (!isspace(*p)))
+ p++;
+ while ((*p != (char)0) && (isspace(*p)))
+ p++;
+ *align = *p;
+ // now the int value
+ while ((*p != (char)0) && (!isspace(*p)))
+ p++;
+ while ((*p != (char)0) && (isspace(*p)))
+ p++;
+ if (*p == (char)0)
+ return -1;
+ return atoi(p);
+ }
+ return -1;
+}
+
+/*
+ * remember_table - saves table, t, in the text_glob.
+ */
+
+void text_glob::remember_table (html_table *t)
+{
+ if (tab != 0)
+ delete tab;
+ tab = t;
+}
+
+/*
+ * get_table - returns the stored table description.
+ */
+
+html_table *text_glob::get_table (void)
+{
+ return tab;
+}
+
+/*
+ * 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, minh, maxv, 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::element_list ()
+ : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1),
+ maxv(-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)
+{
+}
+
+element_list::~element_list ()
+{
+ if (datum != 0)
+ delete datum;
+}
+
+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);
+ void insert (text_glob *in);
+ void move_to (text_glob *in);
+ 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 0
+ fprintf(stderr, "[%s %d,%d,%d,%d] ",
+ in->text_string, min_vertical, min_horizontal,
+ max_vertical, max_horizontal);
+ fflush(stderr);
+#endif
+
+ if (0 /* nullptr */ == head) {
+ head = t;
+ tail = t;
+ ptr = 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 0 /* nullptr */ == head;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * insert - inserts data after the current position.
+ */
+
+void list::insert (text_glob *in)
+{
+ if (is_empty())
+ fatal("list must not be empty if we are inserting data");
+ else {
+ if (0 /* nullptr */ == ptr)
+ ptr = head;
+
+ element_list *t = new element_list(in, ptr->lineno,
+ ptr->minv, ptr->minh,
+ ptr->maxv, ptr->maxh);
+ if (ptr == tail)
+ tail = t;
+ ptr->right->left = t;
+ t->right = ptr->right;
+ ptr->right = t;
+ t->left = ptr;
+ }
+}
+
+/*
+ * move_to - moves the current position to the point where data, in,
+ * exists. This is an expensive method and should be used
+ * sparingly.
+ */
+
+void list::move_to (text_glob *in)
+{
+ ptr = head;
+ while (ptr != tail && ptr->datum != in)
+ ptr = ptr->right;
+}
+
+/*
+ * page class and methods
+ */
+
+class page {
+public:
+ page (void);
+ void add (style *s, const string &str,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ void add_tag (style *s, const string &str,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal);
+ void add_and_encode (style *s, const string &str,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal,
+ int is_tag);
+ void add_line (style *s,
+ int line_number,
+ int x1, int y1, int x2, int y2,
+ int thickness);
+ void insert_tag (const string &str);
+ 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()
+{
+}
+
+/*
+ * insert_tag - inserts a tag after the current position.
+ */
+
+void page::insert_tag (const string &str)
+{
+ if (str.length() > 0) {
+ text_glob *g=new text_glob();
+ text_glob *f=glyphs.get_data();
+ g->text_glob_tag(&f->text_style, buffer.add_string(str),
+ str.length(), f->minv, f->minh, f->maxv, f->maxh);
+ glyphs.insert(g);
+ }
+}
+
+/*
+ * add - add html text to the list of glyphs.
+ */
+
+void page::add (style *s, const string &str,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal)
+{
+ if (str.length() > 0) {
+ text_glob *g=new text_glob();
+ g->text_glob_html(s, buffer.add_string(str), str.length(),
+ min_vertical, min_horizontal,
+ max_vertical, max_horizontal);
+ 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, const string &str,
+ int line_number,
+ int min_vertical, int min_horizontal,
+ int max_vertical, int max_horizontal)
+{
+ if (str.length() > 0) {
+ text_glob *g;
+
+ if (strncmp((str+'\0').contents(), "devtag:.auto-image",
+ strlen("devtag:.auto-image")) == 0) {
+ g = new text_glob();
+ g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
+ min_vertical, min_horizontal,
+ max_vertical, max_horizontal);
+ } else {
+ g = new text_glob();
+ g->text_glob_tag(s, buffer.add_string(str), str.length(),
+ min_vertical, min_horizontal,
+ max_vertical, max_horizontal);
+ }
+ glyphs.add(g, line_number, min_vertical, min_horizontal,
+ max_vertical, max_horizontal);
+ }
+}
+
+/*
+ * add_line - adds the
\n", f);
+ else
+ fputs("
\n", f);
+ headers.move_right();
+ if (multiple_files && (! header_filename.is_empty()))
+ header_filename.move_right();
+ } while (! headers.is_equal_to_head());
+ fputs("\n", f);
+ if (dialect == xhtml)
+ fputs("
");
+ else
+ html.put_string("
");
+}
+
+/*
+ * restore_troff_indent - is called when we have temporarily shutdown
+ * indentation (typically done when we have
+ * centered an image).
+ */
+
+void html_printer::restore_troff_indent (void)
+{
+ troff_indent = next_indent;
+ if (troff_indent > 0) {
+ /*
+ * force device indentation
+ */
+ device_indent = 0;
+ do_indent(get_troff_indent(), pageoffset, linelength);
+ }
+}
+
+/*
+ * 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 space = current_paragraph->retrieve_para_space() || seen_space;
+
+ current_paragraph->done_para();
+ shutdown_table();
+ switch (next_tag) {
+
+ case CENTERED:
+ if (dialect == html4)
+ current_paragraph->do_para("align=\"center\"", space);
+ else
+ current_paragraph->do_para("class=\"center\"", space);
+ break;
+ case LEFT:
+ if (dialect == html4)
+ current_paragraph->do_para(&html, "align=\"left\"",
+ get_troff_indent(), pageoffset,
+ linelength, space);
+ else
+ current_paragraph->do_para(&html, "class=\"left\"",
+ get_troff_indent(), pageoffset,
+ linelength, space);
+ break;
+ case RIGHT:
+ if (dialect == html4)
+ current_paragraph->do_para(&html, "align=\"right\"",
+ get_troff_indent(), pageoffset,
+ linelength, space);
+ else
+ current_paragraph->do_para(&html, "class=\"right\"",
+ get_troff_indent(), pageoffset,
+ linelength, space);
+ break;
+ default:
+ fatal("unknown enumeration");
+ }
+ current_paragraph->do_emittext(g->text_string, g->text_length);
+ current_paragraph->done_para();
+ next_tag = INLINE;
+ suppress_sub_sup = TRUE;
+ seen_space = FALSE;
+ restore_troff_indent();
+ }
+}
+
+/*
+ * handle_tag_within_title - handle a limited number of tags within
+ * the context of a table. Those tags which
+ * set values rather than generate spaces
+ * and paragraphs.
+ */
+
+void html_printer::handle_tag_within_title (text_glob *g)
+{
+ if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll()
+ || g->is_fi() || g->is_nf())
+ troff_tag(g);
+}
+
+/*
+ * do_center - handle the .ce commands from troff.
+ */
+
+void html_printer::do_center (char *arg)
+{
+ next_center = atoi(arg);
+ seen_center = TRUE;
+}
+
+/*
+ * do_centered_image - set a flag such that the next devtag 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 devtag 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 devtag 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 string &generate_img_src (const char *filename)
+{
+ string *s = new string("");
+
+ while (filename && (filename[0] == ' ')) {
+ filename++;
+ }
+ if (exists(filename)) {
+ *s += string("";
+ if (dialect == xhtml)
+ *s += "";
+ }
+ return *s;
+}
+
+/*
+ * 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)
+{
+ string buffer = generate_img_src(filename);
+
+ if (! buffer.empty()) {
+ /*
+ * utilize emit_raw by creating a new text_glob.
+ */
+ text_glob h = *g;
+
+ h.text_string = buffer.contents();
+ h.text_length = buffer.length();
+ emit_raw(&h);
+ } else
+ next_tag = INLINE;
+}
+
+/*
+ * outstanding_eol - call do_eol, n, times.
+ */
+
+void html_printer::outstanding_eol (int n)
+{
+ while (n > 0) {
+ do_eol();
+ n--;
+ }
+}
+
+/*
+ * do_title - handle the .tl commands from troff.
+ */
+
+void html_printer::do_title (void)
+{
+ text_glob *t;
+ int removed_from_head;
+
+ 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()) {
+ string img = generate_img_src((char *)(t->text_string + 20));
+
+ if (! img.empty()) {
+ if (found_title_start)
+ title.text += " ";
+ found_title_start = TRUE;
+ title.has_been_found = TRUE;
+ 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_eo_tl()) {
+ // end of title found
+ title.has_been_found = TRUE;
+ return;
+ } else if (t->is_a_tag()) {
+ handle_tag_within_title(t);
+ 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 (found_title_start) {
+ title.text += " " + string(t->text_string, t->text_length);
+ 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 {
+ title.text += string(t->text_string, t->text_length);
+ 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));
+ }
+ }
+}
+
+/*
+ * write_html_anchor - writes out an anchor. The style of the anchor
+ * dependent upon simple_anchor.
+ */
+
+void html_printer::write_html_anchor (text_glob *h)
+{
+ if (dialect == html4) {
+ if (h != 0) {
+ html.put_string("").nl();
+ }
+ }
+}
+
+/*
+ * write_xhtml_anchor - writes out an anchor. The style of the anchor
+ * dependent upon simple_anchor.
+ */
+
+void html_printer::write_xhtml_anchor (text_glob *h)
+{
+ if (dialect == xhtml) {
+ if (h != 0) {
+ html.put_string(" id=\"");
+ if (simple_anchors) {
+ string buffer(ANCHOR_TEMPLATE);
+
+ buffer += as_string(header.no_of_headings);
+ buffer += '\0';
+ html.put_string(buffer.contents());
+ } else
+ html.put_string(header.header_buffer);
+ html.put_string("\"");
+ }
+ }
+}
+
+void html_printer::write_header (void)
+{
+ if (! header.header_buffer.empty()) {
+ text_glob *a = 0;
+ int space = current_paragraph->retrieve_para_space() || seen_space;
+
+ if (header.header_level > 7)
+ header.header_level = 7;
+
+ // firstly we must terminate any font and type faces
+ current_paragraph->done_para();
+ suppress_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;
+
+ a = new text_glob();
+ a->text_glob_html(&st,
+ header.headings
+ .add_string(header.header_buffer),
+ header.header_buffer.length(),
+ header.no_of_headings, header.header_level,
+ header.no_of_headings, header.header_level);
+
+ // and add this header to the header list
+ header.headers.add(a,
+ header.no_of_headings,
+ header.no_of_headings, header.no_of_headings,
+ header.no_of_headings, header.no_of_headings);
+ }
+
+ html.nl().nl();
+
+ if (manufacture_headings) {
+ // line break before a header
+ if (!current_paragraph->emitted_text())
+ current_paragraph->do_space();
+ // user wants manufactured headings which look better than
+ //
", stdout);
+ handle_valid_flag(FALSE);
+ fputs("[ ", stdout);
+ if ((strcmp(prev.contents(), "") != 0)
+ && prev != top
+ && prev != current) {
+ emit_link(prev, "prev");
+ need_bar = TRUE;
+ }
+ if ((strcmp(next.contents(), "") != 0)
+ && next != top
+ && next != current) {
+ if (need_bar)
+ fputs(" | ", stdout);
+ emit_link(next, "next");
+ need_bar = TRUE;
+ }
+ if (top != " | " + "This document was produced using " + "" + "groff-", stdout); + fputs(Version_string, stdout); + fputs(". |
", stdout); + handle_valid_flag(TRUE); + if (groff_sig) { + fputs(" | " + "This document was produced using " + "" + "groff-", stdout); + fputs(Version_string, stdout); + fputs(". |