These patches modify the indentation implementation to use `<p
[s-roff.git] / src / devices / grohtml / html-text.cpp
blob28f051ab2b272b0edabc037e8e2db8446f3e3c15
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
3 * Free Software Foundation, Inc.
5 * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cpp
7 * html-text.cpp
9 * provide a troff like state machine interface which
10 * generates html text.
14 This file is part of groff.
16 groff is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free
18 Software Foundation; either version 2, or (at your option) any later
19 version.
21 groff is distributed in the hope that it will be useful, but WITHOUT ANY
22 WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 for more details.
26 You should have received a copy of the GNU General Public License along
27 with groff; see the file COPYING. If not, write to the Free Software
28 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
30 #include "driver.h"
31 #include "stringclass.h"
32 #include "cset.h"
34 #if !defined(TRUE)
35 # define TRUE (1==1)
36 #endif
37 #if !defined(FALSE)
38 # define FALSE (1==0)
39 #endif
42 #include "html-text.h"
44 #undef DEBUGGING
45 // #define DEBUGGING
47 html_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)
54 html_text::~html_text ()
56 flush_text();
60 #if defined(DEBUGGING)
61 static int debugStack = FALSE;
65 * turnDebug - flip the debugStack boolean and return the new value.
68 static int turnDebug (void)
70 debugStack = 1-debugStack;
71 return debugStack;
75 * dump_stack_element - display an element of the html stack, p.
78 void html_text::dump_stack_element (tag_definition *p)
80 fprintf(stderr, " | ");
81 switch (p->type) {
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;
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;
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;
107 p->col.get_rgb(&r, &g, &b);
108 fprintf(stderr, "<COLOR %x %x %x>", r/0x101, g/0x101, b/0x101);
110 break;
112 default: fprintf(stderr, "unknown tag");
114 if (p->text_emitted)
115 fprintf(stderr, "[t] ");
119 * dump_stack - debugging function only.
122 void html_text::dump_stack (void)
124 if (debugStack) {
125 tag_definition *p = stackptr;
127 while (p != NULL) {
128 dump_stack_element(p);
129 p = p->next;
132 fprintf(stderr, "\n");
133 fflush(stderr);
135 #else
136 void html_text::dump_stack (void) {}
137 #endif
141 * end_tag - shuts down the tag.
144 void html_text::end_tag (tag_definition *t)
146 switch (t->type) {
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>");
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;
172 default:
173 error("unrecognised tag");
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
184 void html_text::issue_tag (const char *tagname, const char *arg,
185 int space)
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);
194 if (space == TRUE) {
195 out->put_string(" style=\"margin-top: ");
196 out->put_string(STYLE_VERTICAL_SPACE);
197 out->put_string("\"");
199 if (space == TRUE || space == FALSE)
200 out->put_string(" valign=\"top\"");
201 out->put_string(">");
205 * issue_color_begin - writes out an html color tag.
208 void html_text::issue_color_begin (color *c)
210 unsigned int r, g, b;
211 char buf[6+1];
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);
221 out->put_string(buf);
222 out->put_string("\">");
226 * start_tag - starts a tag.
229 void html_text::start_tag (tag_definition *t)
231 switch (t->type) {
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);
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);
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;
266 default:
267 error("unrecognised tag");
272 * flush_text - flushes html tags which are outstanding on the html stack.
275 void html_text::flush_text (void)
277 int notext=TRUE;
278 tag_definition *p=stackptr;
280 while (stackptr != 0) {
281 notext = (notext && (! stackptr->text_emitted));
282 if (! notext) {
283 end_tag(stackptr);
285 p = stackptr;
286 stackptr = stackptr->next;
287 free(p);
289 lastptr = NULL;
293 * is_present - returns TRUE if tag is already present on the stack.
296 int html_text::is_present (HTML_TAG t)
298 tag_definition *p=stackptr;
300 while (p != NULL) {
301 if (t == p->type)
302 return TRUE;
303 p = p->next;
305 return FALSE;
309 * uses_indent - returns TRUE if the current paragraph is using a
310 * html table to effect an indent.
313 int html_text::uses_indent (void)
315 tag_definition *p = stackptr;
317 while (p != NULL) {
318 if (p->indent != NULL)
319 return TRUE;
320 p = p->next;
322 return FALSE;
325 extern void stop();
328 * do_push - places, tag_definition, p, onto the stack
331 void html_text::do_push (tag_definition *p)
333 HTML_TAG t = p->type;
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
348 * if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack.
351 if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) {
353 * store, p, at the end
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;
365 #if defined(DEBUGGING)
366 dump_stack();
367 fprintf(stderr, "exiting do_push\n");
368 #endif
372 * push_para - adds a new entry onto the html paragraph stack.
375 void html_text::push_para (HTML_TAG t, void *arg, html_indent *in)
377 tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition));
379 p->type = t;
380 p->arg1 = arg;
381 p->text_emitted = FALSE;
382 p->indent = in;
384 if (t == PRE_TAG && is_present(PRE_TAG))
385 fatal("cannot have multiple PRE_TAGs");
387 do_push(p);
390 void html_text::push_para (HTML_TAG t)
392 push_para(t, (void *)"", NULL);
395 void html_text::push_para (color *c)
397 tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition));
399 p->type = COLOR_TAG;
400 p->arg1 = NULL;
401 p->col = *c;
402 p->text_emitted = FALSE;
403 p->indent = NULL;
405 do_push(p);
409 * do_italic - changes to italic
412 void html_text::do_italic (void)
414 if (! is_present(I_TAG))
415 push_para(I_TAG);
419 * do_bold - changes to bold.
422 void html_text::do_bold (void)
424 if (! is_present(B_TAG))
425 push_para(B_TAG);
429 * do_tt - changes to teletype.
432 void html_text::do_tt (void)
434 if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG)))
435 push_para(TT_TAG);
439 * do_pre - changes to preformated text.
442 void html_text::do_pre (void)
444 done_tt();
445 if (is_present(P_TAG)) {
446 html_indent *i = remove_indent(P_TAG);
447 (void)done_para();
448 if (! is_present(PRE_TAG))
449 push_para(PRE_TAG, NULL, i);
450 } else if (! is_present(PRE_TAG))
451 push_para(PRE_TAG, NULL, NULL);
452 dump_stack();
456 * is_in_pre - returns TRUE if we are currently within a preformatted
457 * <pre> block.
460 int html_text::is_in_pre (void)
462 return is_present(PRE_TAG);
466 * do_color - initiates a new color tag.
469 void html_text::do_color (color *c)
471 shutdown(COLOR_TAG); // shutdown a previous color tag, if present
472 push_para(c);
476 * done_color - shutdown an outstanding color tag, if it exists.
479 void html_text::done_color (void)
481 shutdown(COLOR_TAG);
485 * shutdown - shuts down an html tag.
488 char *html_text::shutdown (HTML_TAG t)
490 char *arg=NULL;
492 if (is_present(t)) {
493 tag_definition *p =stackptr;
494 tag_definition *temp =NULL;
495 int notext =TRUE;
497 dump_stack();
498 while ((stackptr != NULL) && (stackptr->type != t)) {
499 notext = (notext && (! stackptr->text_emitted));
500 if (! notext) {
501 end_tag(stackptr);
505 * pop tag
507 p = stackptr;
508 stackptr = stackptr->next;
509 if (stackptr == NULL)
510 lastptr = NULL;
513 * push tag onto temp stack
515 p->next = temp;
516 temp = p;
520 * and examine stackptr
522 if ((stackptr != NULL) && (stackptr->type == t)) {
523 if (stackptr->text_emitted) {
524 end_tag(stackptr);
526 if (t == P_TAG) {
527 arg = (char *)stackptr->arg1;
529 p = stackptr;
530 stackptr = stackptr->next;
531 if (stackptr == NULL)
532 lastptr = NULL;
533 if (p->indent != NULL)
534 delete p->indent;
535 free(p);
539 * and restore unaffected tags
541 while (temp != NULL) {
542 if (temp->type == COLOR_TAG)
543 push_para(&temp->col);
544 else
545 push_para(temp->type, temp->arg1, temp->indent);
546 p = temp;
547 temp = temp->next;
548 free(p);
551 return arg;
555 * done_bold - shuts downs a bold tag.
558 void html_text::done_bold (void)
560 shutdown(B_TAG);
564 * done_italic - shuts downs an italic tag.
567 void html_text::done_italic (void)
569 shutdown(I_TAG);
573 * done_sup - shuts downs a sup tag.
576 void html_text::done_sup (void)
578 shutdown(SUP_TAG);
582 * done_sub - shuts downs a sub tag.
585 void html_text::done_sub (void)
587 shutdown(SUB_TAG);
591 * done_tt - shuts downs a tt tag.
594 void html_text::done_tt (void)
596 shutdown(TT_TAG);
600 * done_pre - shuts downs a pre tag.
603 void html_text::done_pre (void)
605 shutdown(PRE_TAG);
609 * done_small - shuts downs a small tag.
612 void html_text::done_small (void)
614 shutdown(SMALL_TAG);
618 * done_big - shuts downs a big tag.
621 void html_text::done_big (void)
623 shutdown(BIG_TAG);
627 * check_emit_text - ensures that all previous tags have been emitted (in order)
628 * before the text is written.
631 void html_text::check_emit_text (tag_definition *t)
633 if ((t != NULL) && (! t->text_emitted)) {
634 check_emit_text(t->next);
635 t->text_emitted = TRUE;
636 start_tag(t);
641 * do_emittext - tells the class that text was written during the current tag.
644 void html_text::do_emittext (const char *s, int length)
646 if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
647 do_para("", FALSE);
649 if (is_present(BREAK_TAG)) {
650 int text = remove_break();
651 check_emit_text(stackptr);
652 if (text) {
653 if (is_present(PRE_TAG)) {
654 out->nl();
655 } else
656 out->put_string("<br>").nl();
658 } else
659 check_emit_text(stackptr);
661 out->put_string(s, length);
662 space_emitted = FALSE;
663 blank_para = FALSE;
667 * do_para - starts a new paragraph
670 void html_text::do_para (const char *arg, html_indent *in, int space)
672 if (! is_present(P_TAG)) {
673 if (is_present(PRE_TAG)) {
674 html_indent *i = remove_indent(PRE_TAG);
675 done_pre();
676 if ((arg == NULL || (strcmp(arg, "") == 0)) &&
677 (i == in || in == NULL))
678 in = i;
679 else
680 delete i;
682 remove_sub_sup();
683 push_para(P_TAG, (void *)arg, in);
684 start_space = space;
688 void html_text::do_para (const char *arg, int space)
690 do_para(arg, NULL, space);
693 void html_text::do_para (simple_output *op, const char *arg1,
694 int indentation_value, int page_offset,
695 int line_length, int space)
697 html_indent *ind;
699 if (indentation_value == 0)
700 ind = NULL;
701 else
702 ind = new html_indent(op, indentation_value, page_offset, line_length);
703 do_para(arg1, ind, space);
707 * done_para - shuts down a paragraph tag.
710 char *html_text::done_para (void)
712 space_emitted = TRUE;
713 return shutdown(P_TAG);
717 * remove_indent - returns the indent associated with, tag.
718 * The indent associated with tag is set to NULL.
721 html_indent *html_text::remove_indent (HTML_TAG tag)
723 tag_definition *p=stackptr;
725 while (p != NULL) {
726 if (tag == p->type) {
727 html_indent *i = p->indent;
728 p->indent = NULL;
729 return i;
731 p = p->next;
733 return NULL;
737 * remove_para_space - removes the leading space to a paragraph
738 * (effectively this trims off a leading `.sp' tag).
741 void html_text::remove_para_space (void)
743 start_space = FALSE;
747 * do_space - issues an end of paragraph
750 void html_text::do_space (void)
752 if (is_in_pre()) {
753 do_emittext("", 0);
754 out->force_nl();
755 space_emitted = TRUE;
756 } else {
757 html_indent *i = remove_indent(P_TAG);
759 do_para(done_para(), i, TRUE);
760 space_emitted = TRUE;
765 * do_break - issue a break tag.
768 void html_text::do_break (void)
770 if (! is_present(PRE_TAG))
771 if (emitted_text())
772 if (! is_present(BREAK_TAG))
773 push_para(BREAK_TAG);
775 space_emitted = TRUE;
779 * do_newline - issue a newline providing that we are inside a <pre> tag.
782 void html_text::do_newline (void)
784 if (is_present(PRE_TAG)) {
785 do_emittext("\n", 1);
786 space_emitted = TRUE;
791 * emitted_text - returns FALSE if white space has just been written.
794 int html_text::emitted_text (void)
796 return !space_emitted;
800 * ever_emitted_text - returns TRUE if we have ever emitted text in this
801 * paragraph.
804 int html_text::ever_emitted_text (void)
806 return !blank_para;
810 * starts_with_space - returns TRUE if we started this paragraph with a .sp
813 int html_text::starts_with_space (void)
815 return start_space;
819 * retrieve_para_space - returns TRUE, if the paragraph starts with
820 * a space and text has not yet been emitted.
821 * If TRUE is returned, then the, start_space,
822 * variable is set to FALSE.
825 int html_text::retrieve_para_space (void)
827 if (start_space && blank_para) {
828 start_space = FALSE;
829 return TRUE;
831 else
832 return FALSE;
836 * emit_space - writes a space providing that text was written beforehand.
839 void html_text::emit_space (void)
841 if (is_present(PRE_TAG))
842 do_emittext(" ", 1);
843 else
844 out->space_or_newline();
846 space_emitted = TRUE;
850 * remove_def - removes a definition, t, from the stack.
853 void html_text::remove_def (tag_definition *t)
855 tag_definition *p = stackptr;
856 tag_definition *l = 0;
857 tag_definition *q = 0;
859 while ((p != 0) && (p != t)) {
860 l = p;
861 p = p->next;
863 if ((p != 0) && (p == t)) {
864 if (p == stackptr) {
865 stackptr = stackptr->next;
866 if (stackptr == NULL)
867 lastptr = NULL;
868 q = stackptr;
869 } else if (l == 0) {
870 error("stack list pointers are wrong");
871 } else {
872 l->next = p->next;
873 q = p->next;
874 if (l->next == NULL)
875 lastptr = l;
877 free(p);
882 * remove_tag - removes a tag from the stack.
885 void html_text::remove_tag (HTML_TAG tag)
887 tag_definition *p = stackptr;
889 while ((p != 0) && (p->type != tag)) {
890 p = p->next;
892 if ((p != 0) && (p->type == tag))
893 remove_def(p);
897 * remove_sub_sup - removes a sub or sup tag, should either exist
898 * on the stack.
901 void html_text::remove_sub_sup (void)
903 if (is_present(SUB_TAG)) {
904 remove_tag(SUB_TAG);
906 if (is_present(SUP_TAG)) {
907 remove_tag(SUP_TAG);
909 if (is_present(PRE_TAG)) {
910 remove_tag(PRE_TAG);
915 * remove_break - break tags are not balanced thus remove it once it has been emitted.
916 * It returns TRUE if text was emitted before the <br> was issued.
919 int html_text::remove_break (void)
921 tag_definition *p = stackptr;
922 tag_definition *l = 0;
923 tag_definition *q = 0;
925 while ((p != 0) && (p->type != BREAK_TAG)) {
926 l = p;
927 p = p->next;
929 if ((p != 0) && (p->type == BREAK_TAG)) {
930 if (p == stackptr) {
931 stackptr = stackptr->next;
932 if (stackptr == NULL)
933 lastptr = NULL;
934 q = stackptr;
935 } else if (l == 0)
936 error("stack list pointers are wrong");
937 else {
938 l->next = p->next;
939 q = p->next;
940 if (l->next == NULL)
941 lastptr = l;
943 free(p);
946 * now determine whether text was issued before <br>
948 while (q != 0) {
949 if (q->text_emitted)
950 return TRUE;
951 else
952 q = q->next;
954 return FALSE;
958 * remove_para_align - removes a paragraph which has a text
959 * argument. If the paragraph has no text
960 * argument then it is left alone.
963 void html_text::remove_para_align (void)
965 if (is_present(P_TAG)) {
966 tag_definition *p=stackptr;
968 while (p != NULL) {
969 if (p->type == P_TAG && p->arg1 != NULL) {
970 html_indent *i = remove_indent(P_TAG);
971 done_para();
972 do_para("", i, space_emitted);
973 return;
975 p = p->next;
981 * get_alignment - returns the alignment for the paragraph.
982 * If no alignment was given then we return "".
985 char *html_text::get_alignment (void)
987 if (is_present(P_TAG)) {
988 tag_definition *p=stackptr;
990 while (p != NULL) {
991 if (p->type == P_TAG && p->arg1 != NULL)
992 return (char *)p->arg1;
993 p = p->next;
996 return (char *)"";
1000 * do_small - potentially inserts a <small> tag into the html stream.
1001 * However we check for a <big> tag, if present then we terminate it.
1002 * Otherwise a <small> tag is inserted.
1005 void html_text::do_small (void)
1007 if (is_present(BIG_TAG))
1008 done_big();
1009 else
1010 push_para(SMALL_TAG);
1014 * do_big - is the mirror image of do_small.
1017 void html_text::do_big (void)
1019 if (is_present(SMALL_TAG))
1020 done_small();
1021 else
1022 push_para(BIG_TAG);
1026 * do_sup - save a superscript tag on the stack of tags.
1029 void html_text::do_sup (void)
1031 push_para(SUP_TAG);
1035 * do_sub - save a subscript tag on the stack of tags.
1038 void html_text::do_sub (void)
1040 push_para(SUB_TAG);