diff options
Diffstat (limited to 'src/devices/grohtml/html-text.cpp')
-rw-r--r-- | src/devices/grohtml/html-text.cpp | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/src/devices/grohtml/html-text.cpp b/src/devices/grohtml/html-text.cpp new file mode 100644 index 0000000..b07cbe7 --- /dev/null +++ b/src/devices/grohtml/html-text.cpp @@ -0,0 +1,1056 @@ +// -*- C++ -*- +/* Copyright (C) 2000-2020 Free Software Foundation, Inc. + * + * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cpp + * + * html-text.cpp + * + * 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 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 <http://www.gnu.org/licenses/>. */ + +#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, html_dialect d) : + stackptr(NULL), lastptr(NULL), out(op), dialect(d), + space_emitted(TRUE), current_indentation(-1), + pageoffset(-1), linelength(-1), blank_para(TRUE), + start_space(FALSE) +{ +} + +html_text::~html_text () +{ + flush_text(); +} + + +#if defined(DEBUGGING) +static int debugStack = FALSE; + + +/* + * turnDebug - flip the debugStack boolean and return the new value. + */ + +static int turnDebug (void) +{ + debugStack = 1-debugStack; + return debugStack; +} + +/* + * dump_stack_element - display an element of the html stack, p. + */ + +void html_text::dump_stack_element (tag_definition *p) +{ + fprintf(stderr, " | "); + switch (p->type) { + + case P_TAG: if (p->indent == NULL) { + fprintf(stderr, "<P %s>", (char *)p->arg1); break; + } else { + fprintf(stderr, "<P %s [TABLE]>", (char *)p->arg1); break; + } + case I_TAG: fprintf(stderr, "<I>"); break; + case B_TAG: fprintf(stderr, "<B>"); break; + case SUB_TAG: fprintf(stderr, "<SUB>"); break; + case SUP_TAG: fprintf(stderr, "<SUP>"); break; + case TT_TAG: fprintf(stderr, "<TT>"); break; + case PRE_TAG: if (p->indent == NULL) { + fprintf(stderr, "<PRE>"); break; + } else { + fprintf(stderr, "<PRE [TABLE]>"); break; + } + case SMALL_TAG: fprintf(stderr, "<SMALL>"); break; + case BIG_TAG: fprintf(stderr, "<BIG>"); break; + case BREAK_TAG: fprintf(stderr, "<BREAK>"); break; + case COLOR_TAG: { + if (p->col.is_default()) + fprintf(stderr, "<COLOR (default)>"); + else { + unsigned int r, g, b; + + p->col.get_rgb(&r, &g, &b); + fprintf(stderr, "<COLOR %x %x %x>", r/0x101, g/0x101, b/0x101); + } + break; + } + default: fprintf(stderr, "unknown tag"); + } + if (p->text_emitted) + fprintf(stderr, "[t] "); +} + +/* + * dump_stack - debugging function only. + */ + +void html_text::dump_stack (void) +{ + if (debugStack) { + tag_definition *p = stackptr; + + while (p != NULL) { + dump_stack_element(p); + p = p->next; + } + } + fprintf(stderr, "\n"); + fflush(stderr); +} +#else +void html_text::dump_stack (void) {} +#endif + + +/* + * end_tag - shuts down the tag. + */ + +void html_text::end_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: out->put_string("</i>"); break; + case B_TAG: out->put_string("</b>"); break; + case P_TAG: if (t->indent == NULL) { + out->put_string("</p>"); + } else { + delete t->indent; + t->indent = NULL; + out->put_string("</p>"); + } + out->enable_newlines(FALSE); + blank_para = TRUE; break; + case SUB_TAG: out->put_string("</sub>"); break; + case SUP_TAG: out->put_string("</sup>"); break; + case TT_TAG: out->put_string("</tt>"); break; + case PRE_TAG: out->put_string("</pre>"); out->enable_newlines(TRUE); + blank_para = TRUE; + if (t->indent != NULL) + delete t->indent; + t->indent = NULL; + break; + case SMALL_TAG: if (! is_in_pre ()) + out->put_string("</small>"); + break; + case BIG_TAG: if (! is_in_pre ()) + out->put_string("</big>"); + break; + case COLOR_TAG: if (! is_in_pre ()) + out->put_string("</font>"); + break; + + default: + error("unrecognised tag"); + } +} + +/* + * issue_tag - writes out an html tag with argument. + * space == 0 if no space is requested + * space == 1 if a space is requested + * space == 2 if tag should not have a space style + */ + +void html_text::issue_tag (const char *tagname, const char *arg, + int space) +{ + if ((arg == 0) || (strlen(arg) == 0)) + out->put_string(tagname); + else { + out->put_string(tagname); + out->put_string(" "); + out->put_string(arg); + } + if (space == TRUE) { + out->put_string(" style=\"margin-top: "); + out->put_string(STYLE_VERTICAL_SPACE); + out->put_string("\""); + } +#if 0 + if (space == TRUE || space == FALSE) + out->put_string(" valign=\"top\""); +#endif + out->put_string(">"); +} + +/* + * issue_color_begin - writes out an html color tag. + */ + +void html_text::issue_color_begin (color *c) +{ + char buf[(INT_HEXDIGITS * 3) + 1]; + unsigned int r, g, b; + + out->put_string("<font color=\"#"); + if (c->is_default()) + sprintf(buf, "000000"); + else { + c->get_rgb(&r, &g, &b); + // we have to scale 0..0xFFFF to 0..0xFF + sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101); + } + out->put_string(buf); + out->put_string("\">"); +} + +/* + * start_tag - starts a tag. + */ + +void html_text::start_tag (tag_definition *t) +{ + switch (t->type) { + + case I_TAG: issue_tag("<i", (char *)t->arg1); break; + case B_TAG: issue_tag("<b", (char *)t->arg1); break; + case P_TAG: if (t->indent != NULL) { + out->nl(); +#if defined(DEBUGGING) + out->simple_comment("INDENTATION"); +#endif + out->put_string("\n<p"); + t->indent->begin(start_space); + issue_tag("", (char *)t->arg1); + } else { + out->nl(); + issue_tag("\n<p", (char *)t->arg1, start_space); + } + + out->enable_newlines(TRUE); break; + case SUB_TAG: issue_tag("<sub", (char *)t->arg1); break; + case SUP_TAG: issue_tag("<sup", (char *)t->arg1); break; + case TT_TAG: issue_tag("<tt", (char *)t->arg1); break; + case PRE_TAG: out->enable_newlines(TRUE); + out->nl(); out->put_string("<pre"); + if (t->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("<small", (char *)t->arg1); + break; + case BIG_TAG: if (! is_in_pre ()) + issue_tag("<big", (char *)t->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 + * <pre> 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("<br/>").nl(); + else + out->put_string("<br>").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 <pre> 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 <br> 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 <br> + */ + 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 <small> tag into the html stream. + * However we check for a <big> tag, if present then we terminate it. + * Otherwise a <small> 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); +} |