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