Home | History | Annotate | Line # | Download | only in grohtml
      1 /*	$NetBSD: html-text.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $	*/
      2 
      3 // -*- C++ -*-
      4 /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
      5  * Free Software Foundation, Inc.
      6  *
      7  *  Gaius Mulley (gaius (at) glam.ac.uk) wrote html-text.cpp
      8  *
      9  *  html-text.cpp
     10  *
     11  *  provide a troff like state machine interface which
     12  *  generates html text.
     13  */
     14 
     15 /*
     16 This file is part of groff.
     17 
     18 groff is free software; you can redistribute it and/or modify it under
     19 the terms of the GNU General Public License as published by the Free
     20 Software Foundation; either version 2, or (at your option) any later
     21 version.
     22 
     23 groff is distributed in the hope that it will be useful, but WITHOUT ANY
     24 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     25 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     26 for more details.
     27 
     28 You should have received a copy of the GNU General Public License along
     29 with groff; see the file COPYING.  If not, write to the Free Software
     30 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
     31 
     32 #include "driver.h"
     33 #include "stringclass.h"
     34 #include "cset.h"
     35 
     36 #if !defined(TRUE)
     37 #   define TRUE  (1==1)
     38 #endif
     39 #if !defined(FALSE)
     40 #   define FALSE (1==0)
     41 #endif
     42 
     43 
     44 #include "html-text.h"
     45 
     46 #undef DEBUGGING
     47 // #define DEBUGGING
     48 
     49 html_text::html_text (simple_output *op) :
     50   stackptr(NULL), lastptr(NULL), out(op), space_emitted(TRUE),
     51   current_indentation(-1), pageoffset(-1), linelength(-1),
     52   blank_para(TRUE), start_space(FALSE)
     53 {
     54 }
     55 
     56 html_text::~html_text ()
     57 {
     58   flush_text();
     59 }
     60 
     61 
     62 #if defined(DEBUGGING)
     63 static int debugStack = FALSE;
     64 
     65 
     66 /*
     67  *  turnDebug - flip the debugStack boolean and return the new value.
     68  */
     69 
     70 static int turnDebug (void)
     71 {
     72   debugStack = 1-debugStack;
     73   return debugStack;
     74 }
     75 
     76 /*
     77  *  dump_stack_element - display an element of the html stack, p.
     78  */
     79 
     80 void html_text::dump_stack_element (tag_definition *p)
     81 {
     82   fprintf(stderr, " | ");
     83   switch (p->type) {
     84 
     85   case P_TAG:      if (p->indent == NULL) {
     86                       fprintf(stderr, "<P %s>", (char *)p->arg1); break;
     87                    } else {
     88                       fprintf(stderr, "<P %s [TABLE]>", (char *)p->arg1); break;
     89 		   }
     90   case I_TAG:      fprintf(stderr, "<I>"); break;
     91   case B_TAG:      fprintf(stderr, "<B>"); break;
     92   case SUB_TAG:    fprintf(stderr, "<SUB>"); break;
     93   case SUP_TAG:    fprintf(stderr, "<SUP>"); break;
     94   case TT_TAG:     fprintf(stderr, "<TT>"); break;
     95   case PRE_TAG:    if (p->indent == NULL) {
     96                       fprintf(stderr, "<PRE>"); break;
     97                    } else {
     98                       fprintf(stderr, "<PRE [TABLE]>"); break;
     99 		   }
    100   case SMALL_TAG:  fprintf(stderr, "<SMALL>"); break;
    101   case BIG_TAG:    fprintf(stderr, "<BIG>"); break;
    102   case BREAK_TAG:  fprintf(stderr, "<BREAK>"); break;
    103   case COLOR_TAG:  {
    104     if (p->col.is_default())
    105       fprintf(stderr, "<COLOR (default)>");
    106     else {
    107       unsigned int r, g, b;
    108 
    109       p->col.get_rgb(&r, &g, &b);
    110       fprintf(stderr, "<COLOR %x %x %x>", r/0x101, g/0x101, b/0x101);
    111     }
    112     break;
    113   }
    114   default: fprintf(stderr, "unknown tag");
    115   }
    116   if (p->text_emitted)
    117     fprintf(stderr, "[t] ");
    118 }
    119 
    120 /*
    121  *  dump_stack - debugging function only.
    122  */
    123 
    124 void html_text::dump_stack (void)
    125 {
    126   if (debugStack) {
    127     tag_definition *p = stackptr;
    128 
    129     while (p != NULL) {
    130       dump_stack_element(p);
    131       p = p->next;
    132     }
    133   }
    134   fprintf(stderr, "\n");
    135   fflush(stderr);
    136 }
    137 #else
    138 void html_text::dump_stack (void) {}
    139 #endif
    140 
    141 
    142 /*
    143  *  end_tag - shuts down the tag.
    144  */
    145 
    146 void html_text::end_tag (tag_definition *t)
    147 {
    148   switch (t->type) {
    149 
    150   case I_TAG:      out->put_string("</i>"); break;
    151   case B_TAG:      out->put_string("</b>"); break;
    152   case P_TAG:      if (t->indent == NULL) {
    153                      out->put_string("</p>");
    154                    } else {
    155 		     delete t->indent;
    156 		     t->indent = NULL;
    157                      out->put_string("</p>");
    158 		   }
    159 		   out->enable_newlines(FALSE);
    160                    blank_para = TRUE; break;
    161   case SUB_TAG:    out->put_string("</sub>"); break;
    162   case SUP_TAG:    out->put_string("</sup>"); break;
    163   case TT_TAG:     out->put_string("</tt>"); break;
    164   case PRE_TAG:    out->put_string("</pre>"); out->enable_newlines(TRUE);
    165                    blank_para = TRUE;
    166                    if (t->indent != NULL)
    167 		     delete t->indent;
    168 		   t->indent = NULL;
    169                    break;
    170   case SMALL_TAG:  out->put_string("</small>"); break;
    171   case BIG_TAG:    out->put_string("</big>"); break;
    172   case COLOR_TAG:  out->put_string("</font>"); break;
    173 
    174   default:
    175     error("unrecognised tag");
    176   }
    177 }
    178 
    179 /*
    180  *  issue_tag - writes out an html tag with argument.
    181  *              space == 0 if no space is requested
    182  *              space == 1 if a space is requested
    183  *              space == 2 if tag should not have a space style
    184  */
    185 
    186 void html_text::issue_tag (const char *tagname, const char *arg,
    187 			   int space)
    188 {
    189   if ((arg == 0) || (strlen(arg) == 0))
    190     out->put_string(tagname);
    191   else {
    192     out->put_string(tagname);
    193     out->put_string(" ");
    194     out->put_string(arg);
    195   }
    196   if (space == TRUE) {
    197     out->put_string(" style=\"margin-top: ");
    198     out->put_string(STYLE_VERTICAL_SPACE);
    199     out->put_string("\"");
    200   }
    201   if (space == TRUE || space == FALSE)
    202     out->put_string(" valign=\"top\"");
    203   out->put_string(">");
    204 }
    205 
    206 /*
    207  *  issue_color_begin - writes out an html color tag.
    208  */
    209 
    210 void html_text::issue_color_begin (color *c)
    211 {
    212   unsigned int r, g, b;
    213   char buf[6+1];
    214 
    215   out->put_string("<font color=\"#");
    216   if (c->is_default())
    217     sprintf(buf, "000000");
    218   else {
    219     c->get_rgb(&r, &g, &b);
    220     // we have to scale 0..0xFFFF to 0..0xFF
    221     sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
    222   }
    223   out->put_string(buf);
    224   out->put_string("\">");
    225 }
    226 
    227 /*
    228  *  start_tag - starts a tag.
    229  */
    230 
    231 void html_text::start_tag (tag_definition *t)
    232 {
    233   switch (t->type) {
    234 
    235   case I_TAG:      issue_tag("<i", (char *)t->arg1); break;
    236   case B_TAG:      issue_tag("<b", (char *)t->arg1); break;
    237   case P_TAG:      if (t->indent != NULL) {
    238                      out->nl();
    239 #if defined(DEBUGGING)
    240 		     out->simple_comment("INDENTATION");
    241 #endif
    242 		     out->put_string("\n<p");
    243 		     t->indent->begin(start_space);
    244                      issue_tag("", (char *)t->arg1);
    245                    } else {
    246                      out->nl();
    247                      issue_tag("\n<p", (char *)t->arg1, start_space);
    248 		   }
    249 
    250                    out->enable_newlines(TRUE); break;
    251   case SUB_TAG:    issue_tag("<sub", (char *)t->arg1); break;
    252   case SUP_TAG:    issue_tag("<sup", (char *)t->arg1); break;
    253   case TT_TAG:     issue_tag("<tt", (char *)t->arg1); break;
    254   case PRE_TAG:    out->enable_newlines(TRUE);
    255                    out->nl(); out->put_string("<pre");
    256 		   if (t->indent == NULL)
    257 		     issue_tag("", (char *)t->arg1, start_space);
    258 		   else {
    259 		     t->indent->begin(start_space);
    260 		     issue_tag("", (char *)t->arg1);
    261 		   }
    262                    out->enable_newlines(FALSE); break;
    263   case SMALL_TAG:  issue_tag("<small", (char *)t->arg1); break;
    264   case BIG_TAG:    issue_tag("<big", (char *)t->arg1); break;
    265   case BREAK_TAG:  break;
    266   case COLOR_TAG:  issue_color_begin(&t->col); break;
    267 
    268   default:
    269     error("unrecognised tag");
    270   }
    271 }
    272 
    273 /*
    274  *  flush_text - flushes html tags which are outstanding on the html stack.
    275  */
    276 
    277 void html_text::flush_text (void)
    278 {
    279   int notext=TRUE;
    280   tag_definition *p=stackptr;
    281 
    282   while (stackptr != 0) {
    283     notext = (notext && (! stackptr->text_emitted));
    284     if (! notext) {
    285       end_tag(stackptr);
    286     }
    287     p = stackptr;
    288     stackptr = stackptr->next;
    289     delete p;
    290   }
    291   lastptr = NULL;
    292 }
    293 
    294 /*
    295  *  is_present - returns TRUE if tag is already present on the stack.
    296  */
    297 
    298 int html_text::is_present (HTML_TAG t)
    299 {
    300   tag_definition *p=stackptr;
    301 
    302   while (p != NULL) {
    303     if (t == p->type)
    304       return TRUE;
    305     p = p->next;
    306   }
    307   return FALSE;
    308 }
    309 
    310 /*
    311  *  uses_indent - returns TRUE if the current paragraph is using a
    312  *                html table to effect an indent.
    313  */
    314 
    315 int html_text::uses_indent (void)
    316 {
    317   tag_definition *p = stackptr;
    318 
    319   while (p != NULL) {
    320     if (p->indent != NULL)
    321       return TRUE;
    322     p = p->next;
    323   }
    324   return FALSE;
    325 }
    326 
    327 extern void stop();
    328 
    329 /*
    330  *  do_push - places, tag_definition, p, onto the stack
    331  */
    332 
    333 void html_text::do_push (tag_definition *p)
    334 {
    335   HTML_TAG t = p->type;
    336 
    337 #if defined(DEBUGGING)
    338   if (t == PRE_TAG)
    339     stop();
    340   debugStack = TRUE;
    341   fprintf(stderr, "\nentering do_push (");
    342   dump_stack_element(p);
    343   fprintf(stderr, ")\n");
    344   dump_stack();
    345   fprintf(stderr, ")\n");
    346   fflush(stderr);
    347 #endif
    348 
    349   /*
    350    *  if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack.
    351    */
    352 
    353   if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) {
    354     /*
    355      *  store, p, at the end
    356      */
    357     lastptr->next = p;
    358     lastptr       = p;
    359     p->next       = NULL;
    360   } else {
    361     p->next       = stackptr;
    362     if (stackptr == NULL)
    363       lastptr = p;
    364     stackptr      = p;
    365   }
    366 
    367 #if defined(DEBUGGING)
    368   dump_stack();
    369   fprintf(stderr, "exiting do_push\n");
    370 #endif
    371 }
    372 
    373 /*
    374  *  push_para - adds a new entry onto the html paragraph stack.
    375  */
    376 
    377 void html_text::push_para (HTML_TAG t, void *arg, html_indent *in)
    378 {
    379   tag_definition *p= new tag_definition;
    380 
    381   p->type         = t;
    382   p->arg1         = arg;
    383   p->text_emitted = FALSE;
    384   p->indent       = in;
    385 
    386   if (t == PRE_TAG && is_present(PRE_TAG))
    387     fatal("cannot have multiple PRE_TAGs");
    388 
    389   do_push(p);
    390 }
    391 
    392 void html_text::push_para (HTML_TAG t)
    393 {
    394   push_para(t, (void *)"", NULL);
    395 }
    396 
    397 void html_text::push_para (color *c)
    398 {
    399   tag_definition *p = new tag_definition;
    400 
    401   p->type         = COLOR_TAG;
    402   p->arg1         = NULL;
    403   p->col          = *c;
    404   p->text_emitted = FALSE;
    405   p->indent       = NULL;
    406 
    407   do_push(p);
    408 }
    409 
    410 /*
    411  *  do_italic - changes to italic
    412  */
    413 
    414 void html_text::do_italic (void)
    415 {
    416   if (! is_present(I_TAG))
    417     push_para(I_TAG);
    418 }
    419 
    420 /*
    421  *  do_bold - changes to bold.
    422  */
    423 
    424 void html_text::do_bold (void)
    425 {
    426   if (! is_present(B_TAG))
    427     push_para(B_TAG);
    428 }
    429 
    430 /*
    431  *  do_tt - changes to teletype.
    432  */
    433 
    434 void html_text::do_tt (void)
    435 {
    436   if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG)))
    437     push_para(TT_TAG);
    438 }
    439 
    440 /*
    441  *  do_pre - changes to preformated text.
    442  */
    443 
    444 void html_text::do_pre (void)
    445 {
    446   done_tt();
    447   if (is_present(P_TAG)) {
    448     html_indent *i = remove_indent(P_TAG);
    449     int space = retrieve_para_space();
    450     (void)done_para();
    451     if (! is_present(PRE_TAG))
    452       push_para(PRE_TAG, NULL, i);
    453     start_space = space;
    454   } else if (! is_present(PRE_TAG))
    455     push_para(PRE_TAG, NULL, NULL);
    456   dump_stack();
    457 }
    458 
    459 /*
    460  *  is_in_pre - returns TRUE if we are currently within a preformatted
    461  *              <pre> block.
    462  */
    463 
    464 int html_text::is_in_pre (void)
    465 {
    466   return is_present(PRE_TAG);
    467 }
    468 
    469 /*
    470  *  do_color - initiates a new color tag.
    471  */
    472 
    473 void html_text::do_color (color *c)
    474 {
    475   shutdown(COLOR_TAG);   // shutdown a previous color tag, if present
    476   push_para(c);
    477 }
    478 
    479 /*
    480  *  done_color - shutdown an outstanding color tag, if it exists.
    481  */
    482 
    483 void html_text::done_color (void)
    484 {
    485   shutdown(COLOR_TAG);
    486 }
    487 
    488 /*
    489  *  shutdown - shuts down an html tag.
    490  */
    491 
    492 char *html_text::shutdown (HTML_TAG t)
    493 {
    494   char *arg=NULL;
    495 
    496   if (is_present(t)) {
    497     tag_definition *p    =stackptr;
    498     tag_definition *temp =NULL;
    499     int notext           =TRUE;
    500 
    501     dump_stack();
    502     while ((stackptr != NULL) && (stackptr->type != t)) {
    503       notext = (notext && (! stackptr->text_emitted));
    504       if (! notext) {
    505 	end_tag(stackptr);
    506       }
    507 
    508       /*
    509        *  pop tag
    510        */
    511       p        = stackptr;
    512       stackptr = stackptr->next;
    513       if (stackptr == NULL)
    514 	lastptr = NULL;
    515 
    516       /*
    517        *  push tag onto temp stack
    518        */
    519       p->next = temp;
    520       temp    = p;
    521     }
    522 
    523     /*
    524      *  and examine stackptr
    525      */
    526     if ((stackptr != NULL) && (stackptr->type == t)) {
    527       if (stackptr->text_emitted) {
    528 	end_tag(stackptr);
    529       }
    530       if (t == P_TAG) {
    531 	arg = (char *)stackptr->arg1;
    532       }
    533       p        = stackptr;
    534       stackptr = stackptr->next;
    535       if (stackptr == NULL)
    536 	lastptr = NULL;
    537       if (p->indent != NULL)
    538 	delete p->indent;
    539       delete p;
    540     }
    541 
    542     /*
    543      *  and restore unaffected tags
    544      */
    545     while (temp != NULL) {
    546       if (temp->type == COLOR_TAG)
    547 	push_para(&temp->col);
    548       else
    549 	push_para(temp->type, temp->arg1, temp->indent);
    550       p    = temp;
    551       temp = temp->next;
    552       delete p;
    553     }
    554   }
    555   return arg;
    556 }
    557 
    558 /*
    559  *  done_bold - shuts downs a bold tag.
    560  */
    561 
    562 void html_text::done_bold (void)
    563 {
    564   shutdown(B_TAG);
    565 }
    566 
    567 /*
    568  *  done_italic - shuts downs an italic tag.
    569  */
    570 
    571 void html_text::done_italic (void)
    572 {
    573   shutdown(I_TAG);
    574 }
    575 
    576 /*
    577  *  done_sup - shuts downs a sup tag.
    578  */
    579 
    580 void html_text::done_sup (void)
    581 {
    582   shutdown(SUP_TAG);
    583 }
    584 
    585 /*
    586  *  done_sub - shuts downs a sub tag.
    587  */
    588 
    589 void html_text::done_sub (void)
    590 {
    591   shutdown(SUB_TAG);
    592 }
    593 
    594 /*
    595  *  done_tt - shuts downs a tt tag.
    596  */
    597 
    598 void html_text::done_tt (void)
    599 {
    600   shutdown(TT_TAG);
    601 }
    602 
    603 /*
    604  *  done_pre - shuts downs a pre tag.
    605  */
    606 
    607 void html_text::done_pre (void)
    608 {
    609   shutdown(PRE_TAG);
    610 }
    611 
    612 /*
    613  *  done_small - shuts downs a small tag.
    614  */
    615 
    616 void html_text::done_small (void)
    617 {
    618   shutdown(SMALL_TAG);
    619 }
    620 
    621 /*
    622  *  done_big - shuts downs a big tag.
    623  */
    624 
    625 void html_text::done_big (void)
    626 {
    627   shutdown(BIG_TAG);
    628 }
    629 
    630 /*
    631  *  check_emit_text - ensures that all previous tags have been emitted (in order)
    632  *                    before the text is written.
    633  */
    634 
    635 void html_text::check_emit_text (tag_definition *t)
    636 {
    637   if ((t != NULL) && (! t->text_emitted)) {
    638     check_emit_text(t->next);
    639     t->text_emitted = TRUE;
    640     start_tag(t);
    641   }
    642 }
    643 
    644 /*
    645  *  do_emittext - tells the class that text was written during the current tag.
    646  */
    647 
    648 void html_text::do_emittext (const char *s, int length)
    649 {
    650   if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
    651     do_para("", FALSE);
    652 
    653   if (is_present(BREAK_TAG)) {
    654     int text = remove_break();
    655     check_emit_text(stackptr);
    656     if (text) {
    657       if (is_present(PRE_TAG)) {
    658 	out->nl();
    659       } else
    660 	out->put_string("<br>").nl();
    661     }
    662   } else
    663     check_emit_text(stackptr);
    664 
    665   out->put_string(s, length);
    666   space_emitted = FALSE;
    667   blank_para = FALSE;
    668 }
    669 
    670 /*
    671  *  do_para - starts a new paragraph
    672  */
    673 
    674 void html_text::do_para (const char *arg, html_indent *in, int space)
    675 {
    676   if (! is_present(P_TAG)) {
    677     if (is_present(PRE_TAG)) {
    678       html_indent *i = remove_indent(PRE_TAG);
    679       done_pre();
    680       if ((arg == NULL || (strcmp(arg, "") == 0)) &&
    681 	  (i == in || in == NULL))
    682 	in = i;
    683       else
    684 	delete i;
    685     }
    686     remove_sub_sup();
    687     push_para(P_TAG, (void *)arg, in);
    688     start_space = space;
    689   }
    690 }
    691 
    692 void html_text::do_para (const char *arg, int space)
    693 {
    694   do_para(arg, NULL, space);
    695 }
    696 
    697 void html_text::do_para (simple_output *op, const char *arg1,
    698 			 int indentation_value, int page_offset,
    699 			 int line_length, int space)
    700 {
    701   html_indent *ind;
    702 
    703   if (indentation_value == 0)
    704     ind = NULL;
    705   else
    706     ind = new html_indent(op, indentation_value, page_offset, line_length);
    707   do_para(arg1, ind, space);
    708 }
    709 
    710 /*
    711  *  done_para - shuts down a paragraph tag.
    712  */
    713 
    714 char *html_text::done_para (void)
    715 {
    716   char *result;
    717   space_emitted = TRUE;
    718   result = shutdown(P_TAG);
    719   start_space = FALSE;
    720   return result;
    721 }
    722 
    723 /*
    724  *  remove_indent - returns the indent associated with, tag.
    725  *                  The indent associated with tag is set to NULL.
    726  */
    727 
    728 html_indent *html_text::remove_indent (HTML_TAG tag)
    729 {
    730   tag_definition *p=stackptr;
    731 
    732   while (p != NULL) {
    733     if (tag == p->type) {
    734       html_indent *i = p->indent;
    735       p->indent = NULL;
    736       return i;
    737     }
    738     p = p->next;
    739   }
    740   return NULL;
    741 }
    742 
    743 /*
    744  *  remove_para_space - removes the leading space to a paragraph
    745  *                      (effectively this trims off a leading `.sp' tag).
    746  */
    747 
    748 void html_text::remove_para_space (void)
    749 {
    750   start_space = FALSE;
    751 }
    752 
    753 /*
    754  *  do_space - issues an end of paragraph
    755  */
    756 
    757 void html_text::do_space (void)
    758 {
    759   if (is_in_pre()) {
    760     do_emittext("", 0);
    761     out->force_nl();
    762     space_emitted = TRUE;
    763   } else {
    764     html_indent *i = remove_indent(P_TAG);
    765 
    766     do_para(done_para(), i, TRUE);
    767     space_emitted = TRUE;
    768   }
    769 }
    770 
    771 /*
    772  *  do_break - issue a break tag.
    773  */
    774 
    775 void html_text::do_break (void)
    776 {
    777   if (! is_present(PRE_TAG))
    778     if (emitted_text())
    779       if (! is_present(BREAK_TAG))
    780 	push_para(BREAK_TAG);
    781 
    782   space_emitted = TRUE;
    783 }
    784 
    785 /*
    786  *  do_newline - issue a newline providing that we are inside a <pre> tag.
    787  */
    788 
    789 void html_text::do_newline (void)
    790 {
    791   if (is_present(PRE_TAG)) {
    792     do_emittext("\n", 1);
    793     space_emitted = TRUE;
    794   }
    795 }
    796 
    797 /*
    798  *  emitted_text - returns FALSE if white space has just been written.
    799  */
    800 
    801 int html_text::emitted_text (void)
    802 {
    803   return !space_emitted;
    804 }
    805 
    806 /*
    807  *  ever_emitted_text - returns TRUE if we have ever emitted text in this
    808  *                      paragraph.
    809  */
    810 
    811 int html_text::ever_emitted_text (void)
    812 {
    813   return !blank_para;
    814 }
    815 
    816 /*
    817  *  starts_with_space - returns TRUE if we started this paragraph with a .sp
    818  */
    819 
    820 int html_text::starts_with_space (void)
    821 {
    822   return start_space;
    823 }
    824 
    825 /*
    826  *  retrieve_para_space - returns TRUE, if the paragraph starts with
    827  *                        a space and text has not yet been emitted.
    828  *                        If TRUE is returned, then the, start_space,
    829  *                        variable is set to FALSE.
    830  */
    831 
    832 int html_text::retrieve_para_space (void)
    833 {
    834   if (start_space && blank_para) {
    835     start_space = FALSE;
    836     return TRUE;
    837   }
    838   else
    839     return FALSE;
    840 }
    841 
    842 /*
    843  *  emit_space - writes a space providing that text was written beforehand.
    844  */
    845 
    846 void html_text::emit_space (void)
    847 {
    848   if (is_present(PRE_TAG))
    849     do_emittext(" ", 1);
    850   else
    851     out->space_or_newline();
    852 
    853   space_emitted = TRUE;
    854 }
    855 
    856 /*
    857  *  remove_def - removes a definition, t, from the stack.
    858  */
    859 
    860 void html_text::remove_def (tag_definition *t)
    861 {
    862   tag_definition *p    = stackptr;
    863   tag_definition *l    = 0;
    864   tag_definition *q    = 0;
    865 
    866   while ((p != 0) && (p != t)) {
    867     l = p;
    868     p = p->next;
    869   }
    870   if ((p != 0) && (p == t)) {
    871     if (p == stackptr) {
    872       stackptr = stackptr->next;
    873       if (stackptr == NULL)
    874 	lastptr = NULL;
    875       q = stackptr;
    876     } else if (l == 0) {
    877       error("stack list pointers are wrong");
    878     } else {
    879       l->next = p->next;
    880       q = p->next;
    881       if (l->next == NULL)
    882 	lastptr = l;
    883     }
    884     delete p;
    885   }
    886 }
    887 
    888 /*
    889  *  remove_tag - removes a tag from the stack.
    890  */
    891 
    892 void html_text::remove_tag (HTML_TAG tag)
    893 {
    894   tag_definition *p = stackptr;
    895 
    896   while ((p != 0) && (p->type != tag)) {
    897     p = p->next;
    898   }
    899   if ((p != 0) && (p->type == tag))
    900     remove_def(p);
    901 }
    902 
    903 /*
    904  *  remove_sub_sup - removes a sub or sup tag, should either exist
    905  *                   on the stack.
    906  */
    907 
    908 void html_text::remove_sub_sup (void)
    909 {
    910   if (is_present(SUB_TAG)) {
    911     remove_tag(SUB_TAG);
    912   }
    913   if (is_present(SUP_TAG)) {
    914     remove_tag(SUP_TAG);
    915   }
    916   if (is_present(PRE_TAG)) {
    917     remove_tag(PRE_TAG);
    918   }
    919 }
    920 
    921 /*
    922  *  remove_break - break tags are not balanced thus remove it once it has been emitted.
    923  *                 It returns TRUE if text was emitted before the <br> was issued.
    924  */
    925 
    926 int html_text::remove_break (void)
    927 {
    928   tag_definition *p    = stackptr;
    929   tag_definition *l    = 0;
    930   tag_definition *q    = 0;
    931 
    932   while ((p != 0) && (p->type != BREAK_TAG)) {
    933     l = p;
    934     p = p->next;
    935   }
    936   if ((p != 0) && (p->type == BREAK_TAG)) {
    937     if (p == stackptr) {
    938       stackptr = stackptr->next;
    939       if (stackptr == NULL)
    940 	lastptr = NULL;
    941       q = stackptr;
    942     } else if (l == 0)
    943       error("stack list pointers are wrong");
    944     else {
    945       l->next = p->next;
    946       q = p->next;
    947       if (l->next == NULL)
    948 	lastptr = l;
    949     }
    950     delete p;
    951   }
    952   /*
    953    *  now determine whether text was issued before <br>
    954    */
    955   while (q != 0) {
    956     if (q->text_emitted)
    957       return TRUE;
    958     else
    959       q = q->next;
    960   }
    961   return FALSE;
    962 }
    963 
    964 /*
    965  *  remove_para_align - removes a paragraph which has a text
    966  *                      argument. If the paragraph has no text
    967  *                      argument then it is left alone.
    968  */
    969 
    970 void html_text::remove_para_align (void)
    971 {
    972   if (is_present(P_TAG)) {
    973     tag_definition *p=stackptr;
    974 
    975     while (p != NULL) {
    976       if (p->type == P_TAG && p->arg1 != NULL) {
    977 	html_indent *i = remove_indent(P_TAG);
    978 	int          space = retrieve_para_space();
    979 	done_para();
    980 	do_para("", i, space);
    981 	return;
    982       }
    983       p = p->next;
    984     }
    985   }
    986 }
    987 
    988 /*
    989  *  get_alignment - returns the alignment for the paragraph.
    990  *                  If no alignment was given then we return "".
    991  */
    992 
    993 char *html_text::get_alignment (void)
    994 {
    995   if (is_present(P_TAG)) {
    996     tag_definition *p=stackptr;
    997 
    998     while (p != NULL) {
    999       if (p->type == P_TAG && p->arg1 != NULL)
   1000 	return (char *)p->arg1;
   1001       p = p->next;
   1002     }
   1003   }
   1004   return (char *)"";
   1005 }
   1006 
   1007 /*
   1008  *  do_small - potentially inserts a <small> tag into the html stream.
   1009  *             However we check for a <big> tag, if present then we terminate it.
   1010  *             Otherwise a <small> tag is inserted.
   1011  */
   1012 
   1013 void html_text::do_small (void)
   1014 {
   1015   if (is_present(BIG_TAG))
   1016     done_big();
   1017   else
   1018     push_para(SMALL_TAG);
   1019 }
   1020 
   1021 /*
   1022  *  do_big - is the mirror image of do_small.
   1023  */
   1024 
   1025 void html_text::do_big (void)
   1026 {
   1027   if (is_present(SMALL_TAG))
   1028     done_small();
   1029   else
   1030     push_para(BIG_TAG);
   1031 }
   1032 
   1033 /*
   1034  *  do_sup - save a superscript tag on the stack of tags.
   1035  */
   1036 
   1037 void html_text::do_sup (void)
   1038 {
   1039   push_para(SUP_TAG);
   1040 }
   1041 
   1042 /*
   1043  *  do_sub - save a subscript tag on the stack of tags.
   1044  */
   1045 
   1046 void html_text::do_sub (void)
   1047 {
   1048   push_para(SUB_TAG);
   1049 }
   1050