Sync-to-go: update copyright for 2015
[s-roff.git] / src / dev-html / html-text.cpp
blobcd17ea820f234872749c85aecd227c992b0251f3
1 /*@ Provide a troff like state machine interface which generates html text.
3 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
5 * Copyright (C) 2000 - 2005, 2007
6 * Free Software Foundation, Inc.
8 * Gaius Mulley (gaius@glam.ac.uk) wrote html-text.cpp
9 */
11 * This is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2, or (at your option) any later
14 * version.
16 * This is distributed in the hope that it will be useful, but WITHOUT ANY
17 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with groff; see the file COPYING. If not, write to the Free Software
23 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "config.h"
27 #include "html-config.h"
29 #include "cset.h"
30 #include "driver.h"
31 #include "stringclass.h"
33 #include "html-text.h"
35 html_text::html_text(simple_output *op, html_dialect d)
36 : stackptr(NULL), lastptr(NULL), out(op), dialect(d),
37 space_emitted(true), current_indentation(-1),
38 pageoffset(-1), linelength(-1), blank_para(true),
39 start_space(false)
43 html_text::~html_text()
45 flush_text();
48 #ifdef DEBUGGING
49 static int debugStack = false;
52 * turnDebug - flip the debugStack boolean and return the new value.
55 static int turnDebug (void)
57 debugStack = 1-debugStack;
58 return debugStack;
62 * dump_stack_element - display an element of the html stack, p.
65 void html_text::dump_stack_element (tag_definition *p)
67 fprintf(stderr, " | ");
68 switch (p->type) {
70 case P_TAG: if (p->indent == NULL) {
71 fprintf(stderr, "<P %s>", (char *)p->arg1); break;
72 } else {
73 fprintf(stderr, "<P %s [TABLE]>", (char *)p->arg1); break;
75 case I_TAG: fprintf(stderr, "<I>"); break;
76 case B_TAG: fprintf(stderr, "<B>"); break;
77 case SUB_TAG: fprintf(stderr, "<SUB>"); break;
78 case SUP_TAG: fprintf(stderr, "<SUP>"); break;
79 case TT_TAG: fprintf(stderr, "<TT>"); break;
80 case PRE_TAG: if (p->indent == NULL) {
81 fprintf(stderr, "<PRE>"); break;
82 } else {
83 fprintf(stderr, "<PRE [TABLE]>"); break;
85 case SMALL_TAG: fprintf(stderr, "<SMALL>"); break;
86 case BIG_TAG: fprintf(stderr, "<BIG>"); break;
87 case BREAK_TAG: fprintf(stderr, "<BREAK>"); break;
88 case COLOR_TAG: {
89 if (p->col.is_default())
90 fprintf(stderr, "<COLOR (default)>");
91 else {
92 unsigned int r, g, b;
94 p->col.get_rgb(&r, &g, &b);
95 fprintf(stderr, "<COLOR %x %x %x>", r/0x101, g/0x101, b/0x101);
97 break;
99 default: fprintf(stderr, "unknown tag");
101 if (p->text_emitted)
102 fprintf(stderr, "[t] ");
106 * dump_stack - debugging function only.
109 void html_text::dump_stack (void)
111 if (debugStack) {
112 tag_definition *p = stackptr;
114 while (p != NULL) {
115 dump_stack_element(p);
116 p = p->next;
119 fprintf(stderr, "\n");
120 fflush(stderr);
123 #else
124 void html_text::dump_stack (void) {}
125 #endif // DEBUGGING
128 * end_tag - shuts down the tag.
131 void html_text::end_tag (tag_definition *t)
133 switch (t->type) {
135 case I_TAG: out->put_string("</i>"); break;
136 case B_TAG: out->put_string("</b>"); break;
137 case P_TAG: if (t->indent == NULL) {
138 out->put_string("</p>");
139 } else {
140 delete t->indent;
141 t->indent = NULL;
142 out->put_string("</p>");
144 out->enable_newlines(false);
145 blank_para = true; break;
146 case SUB_TAG: out->put_string("</sub>"); break;
147 case SUP_TAG: out->put_string("</sup>"); break;
148 case TT_TAG: out->put_string("</tt>"); break;
149 case PRE_TAG: out->put_string("</pre>"); out->enable_newlines(true);
150 blank_para = true;
151 if (t->indent != NULL)
152 delete t->indent;
153 t->indent = NULL;
154 break;
155 case SMALL_TAG: if (! is_in_pre ())
156 out->put_string("</small>");
157 break;
158 case BIG_TAG: if (! is_in_pre ())
159 out->put_string("</big>");
160 break;
161 case COLOR_TAG: if (! is_in_pre ())
162 out->put_string("</font>");
163 break;
165 default:
166 error("unrecognised tag");
171 * issue_tag - writes out an html tag with argument.
172 * space == 0 if no space is requested
173 * space == 1 if a space is requested
174 * space == 2 if tag should not have a space style
177 void html_text::issue_tag (const char *tagname, const char *arg,
178 int space)
180 if ((arg == 0) || (strlen(arg) == 0))
181 out->put_string(tagname);
182 else {
183 out->put_string(tagname);
184 out->put_string(" ");
185 out->put_string(arg);
187 if (space == true) {
188 out->put_string(" style=\"margin-top: ");
189 out->put_string(STYLE_VERTICAL_SPACE);
190 out->put_string("\"");
192 #if 0
193 if (space == true || space == false)
194 out->put_string(" valign=\"top\"");
195 #endif
196 out->put_string(">");
200 * issue_color_begin - writes out an html color tag.
203 void html_text::issue_color_begin (color *c)
205 unsigned int r, g, b;
206 char buf[6+1];
208 out->put_string("<font color=\"#");
209 if (c->is_default())
210 sprintf(buf, "000000");
211 else {
212 c->get_rgb(&r, &g, &b);
213 // we have to scale 0..0xFFFF to 0..0xFF
214 sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
216 out->put_string(buf);
217 out->put_string("\">");
221 * start_tag - starts a tag.
224 void html_text::start_tag (tag_definition *t)
226 switch (t->type) {
228 case I_TAG: issue_tag("<i", (char *)t->arg1); break;
229 case B_TAG: issue_tag("<b", (char *)t->arg1); break;
230 case P_TAG: if (t->indent != NULL) {
231 out->nl();
232 #if defined(DEBUGGING)
233 out->simple_comment("INDENTATION");
234 #endif
235 out->put_string("\n<p");
236 t->indent->begin(start_space);
237 issue_tag("", (char *)t->arg1);
238 } else {
239 out->nl();
240 issue_tag("\n<p", (char *)t->arg1, start_space);
243 out->enable_newlines(true); break;
244 case SUB_TAG: issue_tag("<sub", (char *)t->arg1); break;
245 case SUP_TAG: issue_tag("<sup", (char *)t->arg1); break;
246 case TT_TAG: issue_tag("<tt", (char *)t->arg1); break;
247 case PRE_TAG: out->enable_newlines(true);
248 out->nl(); out->put_string("<pre");
249 if (t->indent == NULL)
250 issue_tag("", (char *)t->arg1, start_space);
251 else {
252 t->indent->begin(start_space);
253 issue_tag("", (char *)t->arg1);
255 out->enable_newlines(false); break;
256 case SMALL_TAG: if (! is_in_pre ())
257 issue_tag("<small", (char *)t->arg1);
258 break;
259 case BIG_TAG: if (! is_in_pre ())
260 issue_tag("<big", (char *)t->arg1);
261 break;
262 case BREAK_TAG: break;
263 case COLOR_TAG: if (! is_in_pre ())
264 issue_color_begin(&t->col);
265 break;
267 default:
268 error("unrecognised tag");
273 * flush_text - flushes html tags which are outstanding on the html stack.
276 void html_text::flush_text (void)
278 int notext=true;
279 tag_definition *p=stackptr;
281 while (stackptr != 0) {
282 notext = (notext && (! stackptr->text_emitted));
283 if (! notext) {
284 end_tag(stackptr);
286 p = stackptr;
287 stackptr = stackptr->next;
288 delete p;
290 lastptr = NULL;
294 * is_present - returns true if tag is already present on the stack.
297 int html_text::is_present (HTML_TAG t)
299 tag_definition *p=stackptr;
301 while (p != NULL) {
302 if (t == p->type)
303 return true;
304 p = p->next;
306 return false;
310 * uses_indent - returns true if the current paragraph is using a
311 * html table to effect an indent.
314 int html_text::uses_indent (void)
316 tag_definition *p = stackptr;
318 while (p != NULL) {
319 if (p->indent != NULL)
320 return true;
321 p = p->next;
323 return false;
326 extern void stop();
329 * do_push - places, tag_definition, p, onto the stack
332 void html_text::do_push (tag_definition *p)
334 HTML_TAG t = p->type;
336 #if defined(DEBUGGING)
337 if (t == PRE_TAG)
338 stop();
339 debugStack = true;
340 fprintf(stderr, "\nentering do_push (");
341 dump_stack_element(p);
342 fprintf(stderr, ")\n");
343 dump_stack();
344 fprintf(stderr, ")\n");
345 fflush(stderr);
346 #endif
349 * if t is a P_TAG or PRE_TAG make sure it goes on the end of the stack.
352 if (((t == P_TAG) || (t == PRE_TAG)) && (lastptr != NULL)) {
354 * store, p, at the end
356 lastptr->next = p;
357 lastptr = p;
358 p->next = NULL;
359 } else {
360 p->next = stackptr;
361 if (stackptr == NULL)
362 lastptr = p;
363 stackptr = p;
366 #if defined(DEBUGGING)
367 dump_stack();
368 fprintf(stderr, "exiting do_push\n");
369 #endif
373 * push_para - adds a new entry onto the html paragraph stack.
376 void html_text::push_para (HTML_TAG t, void *arg, html_indent *in)
378 tag_definition *p= new tag_definition;
380 p->type = t;
381 p->arg1 = arg;
382 p->text_emitted = false;
383 p->indent = in;
385 if (t == PRE_TAG && is_present(PRE_TAG))
386 fatal("cannot have multiple PRE_TAGs");
388 do_push(p);
391 void html_text::push_para (HTML_TAG t)
393 push_para(t, (void *)"", NULL);
396 void html_text::push_para (color *c)
398 tag_definition *p = new tag_definition;
400 p->type = COLOR_TAG;
401 p->arg1 = NULL;
402 p->col = *c;
403 p->text_emitted = false;
404 p->indent = NULL;
406 do_push(p);
410 * do_italic - changes to italic
413 void html_text::do_italic (void)
415 if (! is_present(I_TAG))
416 push_para(I_TAG);
420 * do_bold - changes to bold.
423 void html_text::do_bold (void)
425 if (! is_present(B_TAG))
426 push_para(B_TAG);
430 * do_tt - changes to teletype.
433 void html_text::do_tt (void)
435 if ((! is_present(TT_TAG)) && (! is_present(PRE_TAG)))
436 push_para(TT_TAG);
440 * do_pre - changes to preformated text.
443 void html_text::do_pre (void)
445 done_tt();
446 if (is_present(P_TAG)) {
447 html_indent *i = remove_indent(P_TAG);
448 int space = retrieve_para_space();
449 (void)done_para();
450 if (! is_present(PRE_TAG))
451 push_para(PRE_TAG, NULL, i);
452 start_space = space;
453 } else if (! is_present(PRE_TAG))
454 push_para(PRE_TAG, NULL, NULL);
455 dump_stack();
459 * is_in_pre - returns true if we are currently within a preformatted
460 * <pre> block.
463 int html_text::is_in_pre (void)
465 return is_present(PRE_TAG);
469 * do_color - initiates a new color tag.
472 void html_text::do_color (color *c)
474 shutdown(COLOR_TAG); // shutdown a previous color tag, if present
475 push_para(c);
479 * done_color - shutdown an outstanding color tag, if it exists.
482 void html_text::done_color (void)
484 shutdown(COLOR_TAG);
488 * shutdown - shuts down an html tag.
491 char *html_text::shutdown (HTML_TAG t)
493 char *arg=NULL;
495 if (is_present(t)) {
496 tag_definition *p =stackptr;
497 tag_definition *temp =NULL;
498 int notext =true;
500 dump_stack();
501 while ((stackptr != NULL) && (stackptr->type != t)) {
502 notext = (notext && (! stackptr->text_emitted));
503 if (! notext) {
504 end_tag(stackptr);
508 * pop tag
510 p = stackptr;
511 stackptr = stackptr->next;
512 if (stackptr == NULL)
513 lastptr = NULL;
516 * push tag onto temp stack
518 p->next = temp;
519 temp = p;
523 * and examine stackptr
525 if ((stackptr != NULL) && (stackptr->type == t)) {
526 if (stackptr->text_emitted) {
527 end_tag(stackptr);
529 if (t == P_TAG) {
530 arg = (char *)stackptr->arg1;
532 p = stackptr;
533 stackptr = stackptr->next;
534 if (stackptr == NULL)
535 lastptr = NULL;
536 if (p->indent != NULL)
537 delete p->indent;
538 delete p;
542 * and restore unaffected tags
544 while (temp != NULL) {
545 if (temp->type == COLOR_TAG)
546 push_para(&temp->col);
547 else
548 push_para(temp->type, temp->arg1, temp->indent);
549 p = temp;
550 temp = temp->next;
551 delete p;
554 return arg;
558 * done_bold - shuts downs a bold tag.
561 void html_text::done_bold (void)
563 shutdown(B_TAG);
567 * done_italic - shuts downs an italic tag.
570 void html_text::done_italic (void)
572 shutdown(I_TAG);
576 * done_sup - shuts downs a sup tag.
579 void html_text::done_sup (void)
581 shutdown(SUP_TAG);
585 * done_sub - shuts downs a sub tag.
588 void html_text::done_sub (void)
590 shutdown(SUB_TAG);
594 * done_tt - shuts downs a tt tag.
597 void html_text::done_tt (void)
599 shutdown(TT_TAG);
603 * done_pre - shuts downs a pre tag.
606 void html_text::done_pre (void)
608 shutdown(PRE_TAG);
612 * done_small - shuts downs a small tag.
615 void html_text::done_small (void)
617 shutdown(SMALL_TAG);
621 * done_big - shuts downs a big tag.
624 void html_text::done_big (void)
626 shutdown(BIG_TAG);
630 * check_emit_text - ensures that all previous tags have been emitted (in order)
631 * before the text is written.
634 void html_text::check_emit_text (tag_definition *t)
636 if ((t != NULL) && (! t->text_emitted)) {
637 check_emit_text(t->next);
638 t->text_emitted = true;
639 start_tag(t);
644 * do_emittext - tells the class that text was written during the current tag.
647 void html_text::do_emittext (const char *s, int length)
649 if ((! is_present(P_TAG)) && (! is_present(PRE_TAG)))
650 do_para("", false);
652 if (is_present(BREAK_TAG)) {
653 int text = remove_break();
654 check_emit_text(stackptr);
655 if (text) {
656 if (is_present(PRE_TAG))
657 out->nl();
658 else if (dialect == xhtml)
659 out->put_string("<br/>").nl();
660 else
661 out->put_string("<br>").nl();
663 } else
664 check_emit_text(stackptr);
666 out->put_string(s, length);
667 space_emitted = false;
668 blank_para = false;
672 * do_para - starts a new paragraph
675 void html_text::do_para (const char *arg, html_indent *in, int space)
677 if (! is_present(P_TAG)) {
678 if (is_present(PRE_TAG)) {
679 html_indent *i = remove_indent(PRE_TAG);
680 done_pre();
681 if ((arg == NULL || (strcmp(arg, "") == 0)) &&
682 (i == in || in == NULL))
683 in = i;
684 else
685 delete i;
687 remove_sub_sup();
688 push_para(P_TAG, (void *)arg, in);
689 start_space = space;
693 void html_text::do_para (const char *arg, int space)
695 do_para(arg, NULL, space);
698 void html_text::do_para (simple_output *op, const char *arg1,
699 int indentation_value, int page_offset,
700 int line_length, int space)
702 html_indent *ind;
704 if (indentation_value == 0)
705 ind = NULL;
706 else
707 ind = new html_indent(op, indentation_value, page_offset, line_length);
708 do_para(arg1, ind, space);
712 * done_para - shuts down a paragraph tag.
715 char *html_text::done_para (void)
717 char *result;
718 space_emitted = true;
719 result = shutdown(P_TAG);
720 start_space = false;
721 return result;
725 * remove_indent - returns the indent associated with, tag.
726 * The indent associated with tag is set to NULL.
729 html_indent *html_text::remove_indent (HTML_TAG tag)
731 tag_definition *p=stackptr;
733 while (p != NULL) {
734 if (tag == p->type) {
735 html_indent *i = p->indent;
736 p->indent = NULL;
737 return i;
739 p = p->next;
741 return NULL;
745 * remove_para_space - removes the leading space to a paragraph
746 * (effectively this trims off a leading `.sp' tag).
749 void html_text::remove_para_space (void)
751 start_space = false;
755 * do_space - issues an end of paragraph
758 void html_text::do_space (void)
760 if (is_in_pre()) {
761 do_emittext("", 0);
762 out->force_nl();
763 space_emitted = true;
764 } else {
765 html_indent *i = remove_indent(P_TAG);
767 do_para(done_para(), i, true);
768 space_emitted = true;
773 * do_break - issue a break tag.
776 void html_text::do_break (void)
778 if (! is_present(PRE_TAG))
779 if (emitted_text())
780 if (! is_present(BREAK_TAG))
781 push_para(BREAK_TAG);
783 space_emitted = true;
787 * do_newline - issue a newline providing that we are inside a <pre> tag.
790 void html_text::do_newline (void)
792 if (is_present(PRE_TAG)) {
793 do_emittext("\n", 1);
794 space_emitted = true;
799 * emitted_text - returns false if white space has just been written.
802 int html_text::emitted_text (void)
804 return !space_emitted;
808 * ever_emitted_text - returns true if we have ever emitted text in this
809 * paragraph.
812 int html_text::ever_emitted_text (void)
814 return !blank_para;
818 * starts_with_space - returns true if we started this paragraph with a .sp
821 int html_text::starts_with_space (void)
823 return start_space;
827 * retrieve_para_space - returns true, if the paragraph starts with
828 * a space and text has not yet been emitted.
829 * If true is returned, then the, start_space,
830 * variable is set to false.
833 int html_text::retrieve_para_space (void)
835 if (start_space && blank_para) {
836 start_space = false;
837 return true;
839 else
840 return false;
844 * emit_space - writes a space providing that text was written beforehand.
847 void html_text::emit_space (void)
849 if (is_present(PRE_TAG))
850 do_emittext(" ", 1);
851 else
852 out->space_or_newline();
854 space_emitted = true;
858 * remove_def - removes a definition, t, from the stack.
861 void html_text::remove_def (tag_definition *t)
863 tag_definition *p = stackptr;
864 tag_definition *l = 0;
865 tag_definition *q = 0;
867 while ((p != 0) && (p != t)) {
868 l = p;
869 p = p->next;
871 if ((p != 0) && (p == t)) {
872 if (p == stackptr) {
873 stackptr = stackptr->next;
874 if (stackptr == NULL)
875 lastptr = NULL;
876 q = stackptr;
877 } else if (l == 0) {
878 error("stack list pointers are wrong");
879 } else {
880 l->next = p->next;
881 q = p->next;
882 if (l->next == NULL)
883 lastptr = l;
885 delete p;
890 * remove_tag - removes a tag from the stack.
893 void html_text::remove_tag (HTML_TAG tag)
895 tag_definition *p = stackptr;
897 while ((p != 0) && (p->type != tag)) {
898 p = p->next;
900 if ((p != 0) && (p->type == tag))
901 remove_def(p);
905 * remove_sub_sup - removes a sub or sup tag, should either exist
906 * on the stack.
909 void html_text::remove_sub_sup (void)
911 if (is_present(SUB_TAG)) {
912 remove_tag(SUB_TAG);
914 if (is_present(SUP_TAG)) {
915 remove_tag(SUP_TAG);
917 if (is_present(PRE_TAG)) {
918 remove_tag(PRE_TAG);
923 * remove_break - break tags are not balanced thus remove it once it has been emitted.
924 * It returns true if text was emitted before the <br> was issued.
927 int html_text::remove_break (void)
929 tag_definition *p = stackptr;
930 tag_definition *l = 0;
931 tag_definition *q = 0;
933 while ((p != 0) && (p->type != BREAK_TAG)) {
934 l = p;
935 p = p->next;
937 if ((p != 0) && (p->type == BREAK_TAG)) {
938 if (p == stackptr) {
939 stackptr = stackptr->next;
940 if (stackptr == NULL)
941 lastptr = NULL;
942 q = stackptr;
943 } else if (l == 0)
944 error("stack list pointers are wrong");
945 else {
946 l->next = p->next;
947 q = p->next;
948 if (l->next == NULL)
949 lastptr = l;
951 delete p;
954 * now determine whether text was issued before <br>
956 while (q != 0) {
957 if (q->text_emitted)
958 return true;
959 else
960 q = q->next;
962 return false;
966 * remove_para_align - removes a paragraph which has a text
967 * argument. If the paragraph has no text
968 * argument then it is left alone.
971 void html_text::remove_para_align (void)
973 if (is_present(P_TAG)) {
974 tag_definition *p=stackptr;
976 while (p != NULL) {
977 if (p->type == P_TAG && p->arg1 != NULL) {
978 html_indent *i = remove_indent(P_TAG);
979 int space = retrieve_para_space();
980 done_para();
981 do_para("", i, space);
982 return;
984 p = p->next;
990 * get_alignment - returns the alignment for the paragraph.
991 * If no alignment was given then we return "".
994 char *html_text::get_alignment (void)
996 if (is_present(P_TAG)) {
997 tag_definition *p=stackptr;
999 while (p != NULL) {
1000 if (p->type == P_TAG && p->arg1 != NULL)
1001 return (char *)p->arg1;
1002 p = p->next;
1005 return (char *)"";
1009 * do_small - potentially inserts a <small> tag into the html stream.
1010 * However we check for a <big> tag, if present then we terminate it.
1011 * Otherwise a <small> tag is inserted.
1014 void html_text::do_small (void)
1016 if (is_present(BIG_TAG))
1017 done_big();
1018 else
1019 push_para(SMALL_TAG);
1023 * do_big - is the mirror image of do_small.
1026 void html_text::do_big (void)
1028 if (is_present(SMALL_TAG))
1029 done_small();
1030 else
1031 push_para(BIG_TAG);
1035 * do_sup - save a superscript tag on the stack of tags.
1038 void html_text::do_sup (void)
1040 push_para(SUP_TAG);
1044 * do_sub - save a subscript tag on the stack of tags.
1047 void html_text::do_sub (void)
1049 push_para(SUB_TAG);
1052 // s-it2-mode