* src/libs/libgroff/glyphuni.cpp (glyph_to_unicode_list),
[s-roff.git] / src / devices / grohtml / html-text.cpp
blob0a56a4ec638c7a2fc2bbc9da174cee07e187871a
1 // -*- C++ -*-
2 /* Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cpp
6 * html-text.cpp
8 * provide a troff like state machine interface which
9 * generates html text.
13 This file is part of groff.
15 groff is free software; you can redistribute it and/or modify it under
16 the terms of the GNU General Public License as published by the Free
17 Software Foundation; either version 2, or (at your option) any later
18 version.
20 groff is distributed in the hope that it will be useful, but WITHOUT ANY
21 WARRANTY; without even the implied warranty of MERCHANTABILITY or
22 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
25 You should have received a copy of the GNU General Public License along
26 with groff; see the file COPYING. If not, write to the Free Software
27 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
29 #include "driver.h"
30 #include "stringclass.h"
31 #include "cset.h"
33 #if !defined(TRUE)
34 # define TRUE (1==1)
35 #endif
36 #if !defined(FALSE)
37 # define FALSE (1==0)
38 #endif
41 #include "html-text.h"
43 // #define DEBUGGING
45 html_text::html_text (simple_output *op) :
46 stackptr(NULL), lastptr(NULL), out(op), space_emitted(TRUE),
47 current_indentation(-1), pageoffset(-1), linelength(-1),
48 blank_para(TRUE), start_space(FALSE)
52 html_text::~html_text ()
54 flush_text();
58 #if defined(DEBUGGING)
59 static int debugStack = FALSE;
63 * turnDebug - flip the debugStack boolean and return the new value.
66 static int turnDebug (void)
68 debugStack = 1-debugStack;
69 return debugStack;
73 * dump_stack_element - display an element of the html stack, p.
76 void html_text::dump_stack_element (tag_definition *p)
78 fprintf(stderr, " | ");
79 switch (p->type) {
81 case P_TAG: if (p->indent == NULL) {
82 fprintf(stderr, "<P %s>", (char *)p->arg1); break;
83 } else {
84 fprintf(stderr, "<P %s [TABLE]>", (char *)p->arg1); break;
86 case I_TAG: fprintf(stderr, "<I>"); break;
87 case B_TAG: fprintf(stderr, "<B>"); break;
88 case SUB_TAG: fprintf(stderr, "<SUB>"); break;
89 case SUP_TAG: fprintf(stderr, "<SUP>"); break;
90 case TT_TAG: fprintf(stderr, "<TT>"); break;
91 case PRE_TAG: if (p->indent == NULL) {
92 fprintf(stderr, "<PRE>"); break;
93 } else {
94 fprintf(stderr, "<PRE [TABLE]>"); break;
96 case SMALL_TAG: fprintf(stderr, "<SMALL>"); break;
97 case BIG_TAG: fprintf(stderr, "<BIG>"); break;
98 case BREAK_TAG: fprintf(stderr, "<BREAK>"); break;
99 case COLOR_TAG: {
100 if (p->col.is_default())
101 fprintf(stderr, "<COLOR (default)>");
102 else {
103 unsigned int r, g, b;
105 p->col.get_rgb(&r, &g, &b);
106 fprintf(stderr, "<COLOR %x %x %x>", r/0x101, g/0x101, b/0x101);
108 break;
110 default: fprintf(stderr, "unknown tag");
112 if (p->text_emitted)
113 fprintf(stderr, "[t] ");
117 * dump_stack - debugging function only.
120 void html_text::dump_stack (void)
122 if (debugStack) {
123 tag_definition *p = stackptr;
125 while (p != NULL) {
126 dump_stack_element(p);
127 p = p->next;
130 fprintf(stderr, "\n");
131 fflush(stderr);
133 #else
134 void html_text::dump_stack (void) {}
135 #endif
139 * end_tag - shuts down the tag.
142 void html_text::end_tag (tag_definition *t)
144 switch (t->type) {
146 case I_TAG: out->put_string("</i>"); break;
147 case B_TAG: out->put_string("</b>"); break;
148 case P_TAG: out->put_string("</p>");
149 if (t->indent != NULL) {
150 delete t->indent;
151 t->indent = NULL;
153 out->nl(); out->enable_newlines(FALSE);
154 blank_para = TRUE; break;
155 case SUB_TAG: out->put_string("</sub>"); break;
156 case SUP_TAG: out->put_string("</sup>"); break;
157 case TT_TAG: out->put_string("</tt>"); break;
158 case PRE_TAG: out->put_string("</pre>"); out->nl(); out->enable_newlines(TRUE);
159 blank_para = TRUE; break;
160 case SMALL_TAG: out->put_string("</small>"); break;
161 case BIG_TAG: out->put_string("</big>"); break;
162 case COLOR_TAG: out->put_string("</font>"); break;
164 default:
165 error("unrecognised tag");
170 * issue_tag - writes out an html tag with argument.
173 void html_text::issue_tag (const char *tagname, const char *arg)
175 if ((arg == 0) || (strlen(arg) == 0)) {
176 out->put_string(tagname);
177 out->put_string(">");
178 } else {
179 out->put_string(tagname);
180 out->put_string(" ");
181 out->put_string(arg);
182 out->put_string(">");
187 * issue_color_begin - writes out an html color tag.
190 void html_text::issue_color_begin (color *c)
192 unsigned int r, g, b;
193 char buf[6+1];
195 out->put_string("<font color=\"#");
196 if (c->is_default())
197 sprintf(buf, "000000");
198 else {
199 c->get_rgb(&r, &g, &b);
200 // we have to scale 0..0xFFFF to 0..0xFF
201 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
203 out->put_string(buf);
204 out->put_string("\">");
208 * start_tag - starts a tag.
211 void html_text::start_tag (tag_definition *t)
213 switch (t->type) {
215 case I_TAG: issue_tag("<i", (char *)t->arg1); break;
216 case B_TAG: issue_tag("<b", (char *)t->arg1); break;
217 case P_TAG: if (t->indent == NULL) {
218 out->nl();
219 issue_tag("\n<p", (char *)t->arg1);
220 } else {
221 out->nl();
222 out->simple_comment("INDENTATION");
223 t->indent->begin(FALSE);
224 start_space = FALSE;
225 issue_tag("<p", (char *)t->arg1);
228 out->enable_newlines(TRUE); break;
229 case SUB_TAG: issue_tag("<sub", (char *)t->arg1); break;
230 case SUP_TAG: issue_tag("<sup", (char *)t->arg1); break;
231 case TT_TAG: issue_tag("<tt", (char *)t->arg1); break;
232 case PRE_TAG: if (t->indent != NULL) {
233 out->nl();
234 out->simple_comment("INDENTATION");
235 t->indent->begin(FALSE);
236 start_space = FALSE;
238 out->enable_newlines(TRUE);
239 out->nl(); issue_tag("<pre", (char *)t->arg1);
240 out->enable_newlines(FALSE); break;
241 case SMALL_TAG: issue_tag("<small", (char *)t->arg1); break;
242 case BIG_TAG: issue_tag("<big", (char *)t->arg1); break;
243 case BREAK_TAG: break;
244 case COLOR_TAG: issue_color_begin(&t->col); break;
246 default:
247 error("unrecognised tag");
252 * flush_text - flushes html tags which are outstanding on the html stack.
255 void html_text::flush_text (void)
257 int notext=TRUE;
258 tag_definition *p=stackptr;
260 while (stackptr != 0) {
261 notext = (notext && (! stackptr->text_emitted));
262 if (! notext) {
263 end_tag(stackptr);
265 p = stackptr;
266 stackptr = stackptr->next;
267 free(p);
269 lastptr = NULL;
273 * is_present - returns TRUE if tag is already present on the stack.
276 int html_text::is_present (HTML_TAG t)
278 tag_definition *p=stackptr;
280 while (p != NULL) {
281 if (t == p->type)
282 return TRUE;
283 p = p->next;
285 return FALSE;
288 extern void stop();
291 * do_push - places, tag_definition, p, onto the stack
294 void html_text::do_push (tag_definition *p)
296 HTML_TAG t = p->type;
298 #if defined(DEBUGGING)
299 if (t == PRE_TAG)
300 stop();
301 debugStack = TRUE;
302 fprintf(stderr, "\nentering do_push (");
303 dump_stack_element(p);
304 fprintf(stderr, ")\n");
305 dump_stack();
306 fprintf(stderr, ")\n");
307 fflush(stderr);
308 #endif
311 * if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack.
314 if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) {
316 * store, p, at the end
318 lastptr->next = p;
319 lastptr = p;
320 p->next = NULL;
321 } else {
322 p->next = stackptr;
323 if (stackptr == NULL)
324 lastptr = p;
325 stackptr = p;
328 #if defined(DEBUGGING)
329 dump_stack();
330 fprintf(stderr, "exiting do_push\n");
331 #endif
335 * push_para - adds a new entry onto the html paragraph stack.
338 void html_text::push_para (HTML_TAG t, void *arg, html_indent *in)
340 tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition));
342 p->type = t;
343 p->arg1 = arg;
344 p->text_emitted = FALSE;
345 p->indent = in;
347 if (t == PRE_TAG && is_present(PRE_TAG))
348 fatal("cannot have multiple PRE_TAGs");
350 do_push(p);
353 void html_text::push_para (HTML_TAG t)
355 push_para(t, (void *)"", NULL);
358 void html_text::push_para (color *c)
360 tag_definition *p=(tag_definition *)malloc(sizeof(tag_definition));
362 p->type = COLOR_TAG;
363 p->arg1 = NULL;
364 p->col = *c;
365 p->text_emitted = FALSE;
366 p->indent = NULL;
368 do_push(p);
372 * do_italic - changes to italic
375 void html_text::do_italic (void)
377 if (! is_present(I_TAG))
378 push_para(I_TAG);
382 * do_bold - changes to bold.
385 void html_text::do_bold (void)
387 if (! is_present(B_TAG))
388 push_para(B_TAG);
392 * do_tt - changes to teletype.
395 void html_text::do_tt (void)
397 if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG)))
398 push_para(TT_TAG);
402 * do_pre - changes to preformated text.
405 void html_text::do_pre (void)
407 done_tt();
408 if (is_present(P_TAG)) {
409 html_indent *i = remove_indent(P_TAG);
410 (void)done_para();
411 if (! is_present(PRE_TAG))
412 push_para(PRE_TAG, NULL, i);
413 } else if (! is_present(PRE_TAG))
414 push_para(PRE_TAG, NULL, NULL);
415 dump_stack();
419 * is_in_pre - returns TRUE if we are currently within a preformatted
420 * <pre> block.
423 int html_text::is_in_pre (void)
425 return is_present(PRE_TAG);
429 * do_color - initiates a new color tag.
432 void html_text::do_color (color *c)
434 shutdown(COLOR_TAG); // shutdown a previous color tag, if present
435 push_para(c);
439 * done_color - shutdown an outstanding color tag, if it exists.
442 void html_text::done_color (void)
444 shutdown(COLOR_TAG);
448 * shutdown - shuts down an html tag.
451 char *html_text::shutdown (HTML_TAG t)
453 char *arg=NULL;
455 if (is_present(t)) {
456 tag_definition *p =stackptr;
457 tag_definition *temp =NULL;
458 int notext =TRUE;
460 dump_stack();
461 while ((stackptr != NULL) && (stackptr->type != t)) {
462 notext = (notext && (! stackptr->text_emitted));
463 if (! notext) {
464 end_tag(stackptr);
468 * pop tag
470 p = stackptr;
471 stackptr = stackptr->next;
472 if (stackptr == NULL)
473 lastptr = NULL;
476 * push tag onto temp stack
478 p->next = temp;
479 temp = p;
483 * and examine stackptr
485 if ((stackptr != NULL) && (stackptr->type == t)) {
486 if (stackptr->text_emitted) {
487 end_tag(stackptr);
489 if (t == P_TAG) {
490 arg = (char *)stackptr->arg1;
492 p = stackptr;
493 stackptr = stackptr->next;
494 if (stackptr == NULL)
495 lastptr = NULL;
496 if (p->indent != NULL)
497 delete p->indent;
498 free(p);
502 * and restore unaffected tags
504 while (temp != NULL) {
505 if (temp->type == COLOR_TAG)
506 push_para(&temp->col);
507 else
508 push_para(temp->type, temp->arg1, temp->indent);
509 p = temp;
510 temp = temp->next;
511 free(p);
514 return arg;
518 * done_bold - shuts downs a bold tag.
521 void html_text::done_bold (void)
523 shutdown(B_TAG);
527 * done_italic - shuts downs an italic tag.
530 void html_text::done_italic (void)
532 shutdown(I_TAG);
536 * done_sup - shuts downs a sup tag.
539 void html_text::done_sup (void)
541 shutdown(SUP_TAG);
545 * done_sub - shuts downs a sub tag.
548 void html_text::done_sub (void)
550 shutdown(SUB_TAG);
554 * done_tt - shuts downs a tt tag.
557 void html_text::done_tt (void)
559 shutdown(TT_TAG);
563 * done_pre - shuts downs a pre tag.
566 void html_text::done_pre (void)
568 shutdown(PRE_TAG);
572 * done_small - shuts downs a small tag.
575 void html_text::done_small (void)
577 shutdown(SMALL_TAG);
581 * done_big - shuts downs a big tag.
584 void html_text::done_big (void)
586 shutdown(BIG_TAG);
590 * check_emit_text - ensures that all previous tags have been emitted (in order)
591 * before the text is written.
594 void html_text::check_emit_text (tag_definition *t)
596 if ((t != NULL) && (! t->text_emitted)) {
597 check_emit_text(t->next);
598 t->text_emitted = TRUE;
599 start_tag(t);
604 * do_emittext - tells the class that text was written during the current tag.
607 void html_text::do_emittext (const char *s, int length)
609 if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
610 do_para("");
612 if (is_present(BREAK_TAG)) {
613 int text = remove_break();
614 check_emit_text(stackptr);
615 if (text) {
616 if (is_present(PRE_TAG)) {
617 out->nl();
618 } else {
619 out->put_string("<br>").nl();
622 } else {
623 check_emit_text(stackptr);
625 out->put_string(s, length);
626 space_emitted = FALSE;
627 blank_para = FALSE;
631 * do_para - starts a new paragraph
634 void html_text::do_para (const char *arg, html_indent *in)
636 if (! is_present(P_TAG)) {
637 if (is_present(PRE_TAG)) {
638 html_indent *i = remove_indent(PRE_TAG);
639 done_pre();
640 if (i == in || in == NULL)
641 in = i;
642 else
643 delete i;
645 remove_sub_sup();
646 push_para(P_TAG, (void *)arg, in);
647 space_emitted = TRUE;
651 void html_text::do_para (const char *arg)
653 do_para(arg, NULL);
656 void html_text::do_para (simple_output *op, const char *arg1,
657 int indentation_value, int page_offset,
658 int line_length)
660 html_indent *ind;
662 if (indentation_value == 0)
663 ind = NULL;
664 else
665 ind = new html_indent(op, indentation_value, page_offset, line_length);
666 do_para(arg1, ind);
670 * done_para - shuts down a paragraph tag.
673 char *html_text::done_para (void)
675 space_emitted = TRUE;
676 return shutdown(P_TAG);
680 * remove_indent - returns the indent associated with, tag.
681 * The indent associated with tag is set to NULL.
684 html_indent *html_text::remove_indent (HTML_TAG tag)
686 tag_definition *p=stackptr;
688 while (p != NULL) {
689 if (tag == p->type) {
690 html_indent *i = p->indent;
691 p->indent = NULL;
692 return i;
694 p = p->next;
696 return NULL;
700 * do_space - issues an end of paragraph
703 void html_text::do_space (void)
705 if (is_in_pre()) {
706 if (blank_para)
707 start_space = TRUE;
708 else {
709 do_emittext("", 0);
710 out->nl();
711 space_emitted = TRUE;
713 } else {
714 html_indent *i = remove_indent(P_TAG);
716 do_para(done_para(), i);
717 space_emitted = TRUE;
718 start_space = TRUE;
723 * do_break - issue a break tag.
726 void html_text::do_break (void)
728 if (! is_present(PRE_TAG)) {
729 if (emitted_text()) {
730 if (! is_present(BREAK_TAG)) {
731 push_para(BREAK_TAG);
735 space_emitted = TRUE;
739 * do_newline - issue a newline providing that we are inside a <pre> tag.
742 void html_text::do_newline (void)
744 if (is_present(PRE_TAG)) {
745 do_emittext("\n", 1);
746 space_emitted = TRUE;
751 * emitted_text - returns FALSE if white space has just been written.
754 int html_text::emitted_text (void)
756 return !space_emitted;
760 * ever_emitted_text - returns TRUE if we have ever emitted text in this paragraph.
763 int html_text::ever_emitted_text (void)
765 return !blank_para;
769 * starts_with_space - returns TRUE if we have start this paragraph with a .sp
772 int html_text::starts_with_space (void)
774 return start_space;
778 * emit_space - writes a space providing that text was written beforehand.
781 void html_text::emit_space (void)
783 if (space_emitted) {
784 if (is_present(PRE_TAG)) {
785 do_emittext(" ", 1);
787 } else {
788 out->space_or_newline();
789 space_emitted = TRUE;
794 * remove_def - removes a definition, t, from the stack.
797 void html_text::remove_def (tag_definition *t)
799 tag_definition *p = stackptr;
800 tag_definition *l = 0;
801 tag_definition *q = 0;
803 while ((p != 0) && (p != t)) {
804 l = p;
805 p = p->next;
807 if ((p != 0) && (p == t)) {
808 if (p == stackptr) {
809 stackptr = stackptr->next;
810 if (stackptr == NULL)
811 lastptr = NULL;
812 q = stackptr;
813 } else if (l == 0) {
814 error("stack list pointers are wrong");
815 } else {
816 l->next = p->next;
817 q = p->next;
818 if (l->next == NULL)
819 lastptr = l;
821 free(p);
826 * remove_tag - removes a tag from the stack.
829 void html_text::remove_tag (HTML_TAG tag)
831 tag_definition *p = stackptr;
833 while ((p != 0) && (p->type != tag)) {
834 p = p->next;
836 if ((p != 0) && (p->type == tag))
837 remove_def(p);
841 * remove_sub_sup - removes a sub or sup tag, should either exist on the stack.
844 void html_text::remove_sub_sup (void)
846 if (is_present(SUB_TAG)) {
847 remove_tag(SUB_TAG);
849 if (is_present(SUP_TAG)) {
850 remove_tag(SUP_TAG);
852 if (is_present(PRE_TAG)) {
853 remove_tag(PRE_TAG);
858 * remove_break - break tags are not balanced thus remove it once it has been emitted.
859 * It returns TRUE if text was emitted before the <br> was issued.
862 int html_text::remove_break (void)
864 tag_definition *p = stackptr;
865 tag_definition *l = 0;
866 tag_definition *q = 0;
868 while ((p != 0) && (p->type != BREAK_TAG)) {
869 l = p;
870 p = p->next;
872 if ((p != 0) && (p->type == BREAK_TAG)) {
873 if (p == stackptr) {
874 stackptr = stackptr->next;
875 if (stackptr == NULL)
876 lastptr = NULL;
877 q = stackptr;
878 } else if (l == 0)
879 error("stack list pointers are wrong");
880 else {
881 l->next = p->next;
882 q = p->next;
883 if (l->next == NULL)
884 lastptr = l;
886 free(p);
889 * now determine whether text was issued before <br>
891 while (q != 0) {
892 if (q->text_emitted)
893 return TRUE;
894 else
895 q = q->next;
897 return FALSE;
901 * remove_para_align - removes a paragraph which has a text
902 * argument. If the paragraph has no text
903 * argument then it is left alone.
906 void html_text::remove_para_align (void)
908 if (is_present(P_TAG)) {
909 tag_definition *p=stackptr;
911 while (p != NULL) {
912 if (p->type == P_TAG && p->arg1 != NULL) {
913 html_indent *i = remove_indent(P_TAG);
914 done_para();
915 do_para("", i);
916 return;
918 p = p->next;
924 * do_small - potentially inserts a <small> tag into the html stream.
925 * However we check for a <big> tag, if present then we terminate it.
926 * Otherwise a <small> tag is inserted.
929 void html_text::do_small (void)
931 if (is_present(BIG_TAG))
932 done_big();
933 else
934 push_para(SMALL_TAG);
938 * do_big - is the mirror image of do_small.
941 void html_text::do_big (void)
943 if (is_present(SMALL_TAG))
944 done_small();
945 else
946 push_para(BIG_TAG);
950 * do_sup - save a superscript tag on the stack of tags.
953 void html_text::do_sup (void)
955 push_para(SUP_TAG);
959 * do_sub - save a subscript tag on the stack of tags.
962 void html_text::do_sub (void)
964 push_para(SUB_TAG);