Fix few bugs in SwfdecGtkSocket that made it unusable
[swfdec.git] / swfdec / swfdec_text_field_movie_html.c
blobbcc606df4435ea2f7810753f5c1793bfd9181300
1 /* Swfdec
2 * Copyright (C) 2007 Pekka Lampila <pekka.lampila@iki.fi>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <stdlib.h>
25 #include <string.h>
27 #include "swfdec_text_field_movie.h"
28 #include "swfdec_as_strings.h"
29 #include "swfdec_style_sheet.h"
30 #include "swfdec_xml.h"
31 #include "swfdec_debug.h"
34 * Parsing
36 typedef struct {
37 const char * name;
38 int name_length;
39 guint index;
40 guint end_index;
41 SwfdecTextFormat * format;
42 } ParserTag;
44 typedef struct {
45 SwfdecAsContext * cx;
46 gboolean multiline;
47 gboolean condense_white;
48 SwfdecStyleSheet * style_sheet;
49 SwfdecTextBuffer * text;
50 GSList * tags_open;
51 GSList * tags_closed;
52 } ParserData;
54 static void
55 swfdec_text_field_movie_html_parse_close_tag (ParserData *data, ParserTag *tag,
56 gboolean end)
58 g_return_if_fail (data != NULL);
59 g_return_if_fail (tag != NULL);
61 if (data->multiline && !end &&
62 ((tag->name_length == 1 && !g_strncasecmp (tag->name, "p", 1)) ||
63 (tag->name_length == 2 && !g_strncasecmp (tag->name, "li", 2))))
65 GSList *iter;
67 for (iter = data->tags_closed; iter != NULL; iter = iter->next) {
68 ParserTag *f = iter->data;
69 if (f->end_index < tag->index)
70 break;
71 if (f->name_length == 4 && !g_strncasecmp (f->name, "font", 4)) {
72 ParserTag *n = g_new0 (ParserTag, 1);
73 n->name = f->name;
74 n->name_length = f->name_length;
75 n->index = swfdec_text_buffer_get_length (data->text);
76 n->end_index = n->index + 1;
77 if (f->format != NULL) {
78 n->format = swfdec_text_format_copy (f->format);
79 } else {
80 n->format = NULL;
82 data->tags_closed = g_slist_prepend (data->tags_closed, n);
83 break;
86 swfdec_text_buffer_append_text (data->text, "\n");
89 tag->end_index = swfdec_text_buffer_get_length (data->text);
91 data->tags_open = g_slist_remove (data->tags_open, tag);
92 data->tags_closed = g_slist_prepend (data->tags_closed, tag);
95 static const char *
96 swfdec_text_field_movie_html_parse_comment (ParserData *data, const char *p)
98 const char *end;
100 g_return_val_if_fail (data != NULL, NULL);
101 g_return_val_if_fail (p != NULL, NULL);
102 g_return_val_if_fail (strncmp (p, "<!--", strlen ("<!--")) == 0, NULL);
104 end = strstr (p + strlen ("<!--"), "-->");
105 if (end != NULL)
106 end += strlen("-->");
108 // return NULL if no end found
109 return end;
112 static void
113 swfdec_text_field_movie_html_tag_set_attribute (ParserData *data,
114 ParserTag *tag, const char *name, int name_length, const char *value,
115 int value_length)
117 SwfdecAsValue val;
118 SwfdecAsObject *object;
120 g_return_if_fail (data != NULL);
121 g_return_if_fail (tag != NULL);
122 g_return_if_fail (name != NULL);
123 g_return_if_fail (name_length >= 0);
124 g_return_if_fail (value != NULL);
125 g_return_if_fail (value_length >= 0);
127 if (!tag->format)
128 return;
130 object = SWFDEC_AS_OBJECT (tag->format);
131 SWFDEC_AS_VALUE_SET_STRING (&val, swfdec_as_context_give_string (
132 swfdec_gc_object_get_context (object), g_strndup (value, value_length)));
134 if (tag->name_length == 10 && !g_strncasecmp (tag->name, "textformat", 10))
136 if (name_length == 10 && !g_strncasecmp (name, "leftmargin", 10))
138 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_leftMargin, &val);
140 else if (name_length == 11 && !g_strncasecmp (name, "rightmargin", 11))
142 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_rightMargin, &val);
144 else if (name_length == 6 && !g_strncasecmp (name, "indent", 6))
146 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_indent, &val);
148 else if (name_length == 11 && !g_strncasecmp (name, "blockindent", 11))
150 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_blockIndent, &val);
152 else if (name_length == 8 && !g_strncasecmp (name, "tabstops", 8))
154 // FIXME
155 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_tabStops, &val);
158 else if (tag->name_length == 1 && !g_strncasecmp (tag->name, "p", 1))
160 if (name_length == 5 && !g_strncasecmp (name, "align", 5))
162 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_align, &val);
165 else if (tag->name_length == 4 && !g_strncasecmp (tag->name, "font", 4))
167 if (name_length == 4 && !g_strncasecmp (name, "face", 4))
169 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_font, &val);
171 else if (name_length == 4 && !g_strncasecmp (name, "size", 4))
173 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_size, &val);
175 else if (name_length == 5 && !g_strncasecmp (name, "color", 5))
177 SwfdecAsValue val_number;
179 if (value_length != 7 || *value != '#') {
180 SWFDEC_AS_VALUE_SET_NUMBER (&val_number, 0);
181 } else {
182 int number;
183 char *tail;
185 number = g_ascii_strtoll (value + 1, &tail, 16);
186 if (tail != value + 7)
187 number = 0;
188 SWFDEC_AS_VALUE_SET_NUMBER (&val_number, number);
191 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_color, &val_number);
193 else if (name_length == 13 && !g_strncasecmp (name, "letterspacing", 13))
195 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_letterSpacing,
196 &val);
198 // special case: Don't parse kerning
200 else if (tag->name_length == 1 && !g_strncasecmp (tag->name, "a", 1))
202 if (name_length == 4 && !g_strncasecmp (name, "href", 4))
204 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_url, &val);
206 else if (name_length == 6 && !g_strncasecmp (name, "target", 6))
208 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_target, &val);
212 if (data->style_sheet &&
213 ((tag->name_length == 2 && !g_strncasecmp (tag->name, "li", 2)) ||
214 (tag->name_length == 4 && !g_strncasecmp (tag->name, "span", 4)) ||
215 (tag->name_length == 1 && !g_strncasecmp (tag->name, "p", 1))))
217 if (name_length == 5 && !g_strncasecmp (name, "class", 5)) {
218 SwfdecTextFormat *format = swfdec_style_sheet_get_class_format (
219 data->style_sheet, swfdec_as_context_give_string (data->cx,
220 g_strndup (value, value_length)));
221 if (format != NULL)
222 swfdec_text_format_add (tag->format, format);
227 static const char *
228 swfdec_text_field_movie_html_parse_attribute (ParserData *data, ParserTag *tag,
229 const char *p)
231 const char *end, *name, *value;
232 int name_length, value_length;
234 g_return_val_if_fail (data != NULL, NULL);
235 g_return_val_if_fail (tag != NULL, NULL);
236 g_return_val_if_fail ((*p != '>' && *p != '\0'), NULL);
238 end = p + strcspn (p, "=> \r\n\t");
239 if (end - p <= 0)
240 return NULL; // Correct?
242 name = p;
243 name_length = end - p;
245 p = end + strspn (end, " \r\n\t");
246 if (*p != '=')
247 return NULL; // FIXME: Correct?
248 p = p + 1;
249 p = p + strspn (p, " \r\n\t");
251 if (*p != '"' && *p != '\'')
252 return NULL; // FIXME: Correct?
254 end = p + 1;
255 do {
256 end = strchr (end, *p);
257 } while (end != NULL && *(end - 1) == '\\');
259 if (end == NULL)
260 return NULL; // FIXME: Correct?
262 value = p + 1;
263 value_length = end - (p + 1);
265 if (tag != NULL) {
266 swfdec_text_field_movie_html_tag_set_attribute (data, tag, name,
267 name_length, value, value_length);
270 g_return_val_if_fail (end + 1 > p, NULL);
272 return end + 1;
275 static const char *
276 swfdec_text_field_movie_html_parse_tag (ParserData *data, const char *p)
278 ParserTag *tag;
279 const char *name, *end;
280 int name_length;
281 gboolean close;
283 g_return_val_if_fail (data != NULL, NULL);
284 g_return_val_if_fail (p != NULL, NULL);
285 g_return_val_if_fail (*p == '<', NULL);
287 p++;
289 // closing tag or opening tag?
290 if (*p == '/') {
291 close = TRUE;
292 p++;
293 } else {
294 close = FALSE;
297 // find the end of the name
298 end = p + strcspn (p, "> \r\n\t");
300 if (*end == '\0')
301 return NULL;
303 // don't count trailing / as part of the name if it's followed by >
304 // we still act like it's a normal opening tag even if it has /
305 if (*end == '>' && *(end - 1) == '/')
306 end = end - 1;
308 if (end == p) // empty name
309 return NULL;
311 name = p;
312 name_length = end - p;
314 if (close)
316 if (name_length == 1 && !g_strncasecmp (name, "p", 1)) {
317 GSList *iter, *found;
319 found = NULL;
320 iter = data->tags_open;
321 while (iter != NULL) {
322 tag = iter->data;
323 if (tag->name_length == 1 && !g_strncasecmp (tag->name, "p", 1))
324 found = iter;
325 iter = iter->next;
328 if (found != NULL) {
329 iter = data->tags_open;
330 while (iter != found) {
331 tag = iter->data;
332 iter = iter->next;
333 if ((tag->name_length == 2 && !g_strncasecmp (tag->name, "li", 2)) ||
334 (tag->name_length == 10 &&
335 !g_strncasecmp (tag->name, "textformat", 10)))
336 continue;
337 swfdec_text_field_movie_html_parse_close_tag (data, tag, TRUE);
339 if (data->multiline)
340 swfdec_text_buffer_append_text (data->text, "\n");
341 swfdec_text_field_movie_html_parse_close_tag (data, found->data,
342 TRUE);
344 } else {
345 if (data->tags_open != NULL) {
346 tag = data->tags_open->data;
347 if (name_length == tag->name_length &&
348 !g_strncasecmp (name, tag->name, name_length))
349 swfdec_text_field_movie_html_parse_close_tag (data, tag, FALSE);
353 end = strchr (end, '>');
354 if (end != NULL)
355 end += 1;
357 else
359 SwfdecAsObject *object;
360 SwfdecAsValue val;
362 if (name_length == 3 && !g_strncasecmp (name, "tab", 3))
363 swfdec_text_buffer_append_text (data->text, "\t");
365 if (data->multiline) {
366 if (name_length == 2 && !g_strncasecmp (name, "br", 2))
368 swfdec_text_buffer_append_text (data->text, "\n");
370 else if (name_length == 2 && !g_strncasecmp (name, "li", 1))
372 gsize length = swfdec_text_buffer_get_length (data->text);
373 const char *s = swfdec_text_buffer_get_text (data->text);
374 if (length > 0 && s[length-1] != '\n' && s[length-1] != '\r')
375 swfdec_text_buffer_append_text (data->text, "\n");
379 tag = g_new0 (ParserTag, 1);
380 tag->name = name;
381 tag->name_length = name_length;
382 tag->format = SWFDEC_TEXT_FORMAT (swfdec_text_format_new (data->cx));
383 tag->index = swfdec_text_buffer_get_length (data->text);
385 data->tags_open = g_slist_prepend (data->tags_open, tag);
387 // set format based on tag
388 if (tag->format != NULL) {
389 object = SWFDEC_AS_OBJECT (tag->format);
390 SWFDEC_AS_VALUE_SET_BOOLEAN (&val, TRUE);
392 if (tag->name_length == 2 && !g_strncasecmp (tag->name, "li", 2)) {
393 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_bullet, &val);
394 } else if (tag->name_length == 1 && !g_strncasecmp (tag->name, "b", 1)) {
395 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_bold, &val);
396 } else if (tag->name_length == 1 && !g_strncasecmp (tag->name, "i", 1)) {
397 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_italic, &val);
398 } else if (tag->name_length == 1 && !g_strncasecmp (tag->name, "u", 1)) {
399 swfdec_as_object_set_variable (object, SWFDEC_AS_STR_underline, &val);
401 else if (tag->name_length == 3 && !g_strncasecmp (tag->name, "img", 3))
403 SWFDEC_FIXME ("IMG tag support for TextField's HTML input missing");
407 if (data->style_sheet &&
408 ((tag->name_length == 2 && !g_strncasecmp (tag->name, "li", 2)) ||
409 (tag->name_length == 1 && !g_strncasecmp (tag->name, "p", 1)))) {
410 SwfdecTextFormat *format = swfdec_style_sheet_get_tag_format (
411 data->style_sheet, swfdec_as_context_give_string (data->cx,
412 g_strndup (tag->name, tag->name_length)));
413 if (format != NULL)
414 swfdec_text_format_add (tag->format, format);
417 // parse attributes
418 end = end + strspn (end, " \r\n\t");
419 while (*end != '\0' && *end != '>' && (*end != '/' || *(end + 1) != '>')) {
420 end = swfdec_text_field_movie_html_parse_attribute (data, tag, end);
421 if (end == NULL)
422 break;
423 end = end + strspn (end, " \r\n\t");
425 if (end != NULL) {
426 if (*end == '/')
427 end += 1;
428 if (*end == '>')
429 end += 1;
433 return end;
436 static const char *
437 swfdec_text_field_movie_html_parse_text (ParserData *data, const char *p)
439 const char *end;
440 char *unescaped;
442 g_return_val_if_fail (data != NULL, NULL);
443 g_return_val_if_fail (p != NULL, NULL);
444 g_return_val_if_fail (*p != '\0' && *p != '<', NULL);
446 // condense the whitespace with previous text
447 // also skip any whitespace that would be preceding first actual text
448 if (data->condense_white && data->cx->version >= 8) {
449 gsize length = swfdec_text_buffer_get_length (data->text);
450 const char *s = swfdec_text_buffer_get_text (data->text);
451 if (length == 0 || g_ascii_isspace (s[length - 1]))
452 p += strspn (p, " \n\r\t");
455 // if it's only whitespace, don't add it
456 if (data->cx->version < 8) {
457 end = p + strspn (p, " \n\r\t");
458 if (*end == '\0' || *end == '<')
459 return end;
462 // get the text
463 // if condense_white: all whitespace blocks are converted to a single space
464 while (*p != '\0' && *p != '<') {
465 if (data->condense_white) {
466 end = p + strcspn (p, "< \n\r\t");
467 } else {
468 end = strchr (p, '<');
469 if (end == NULL)
470 end = strchr (p, '\0');
473 unescaped = swfdec_xml_unescape_len (data->cx, p, end - p, TRUE);
474 swfdec_text_buffer_append_text (data->text, unescaped);
475 g_free (unescaped);
477 if (data->condense_white && g_ascii_isspace (*end)) {
478 swfdec_text_buffer_append_text (data->text, " ");
479 p = end + strspn (end, " \n\r\t");
480 } else {
481 p = end;
485 return p;
488 void
489 swfdec_text_field_movie_html_parse (SwfdecTextFieldMovie *text, const char *str)
491 ParserData data;
492 const char *p;
494 g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
495 g_return_if_fail (str != NULL);
497 data.cx = swfdec_gc_object_get_context (text);
498 data.multiline = (data.cx->version < 7 || text->multiline);
499 data.condense_white = text->condense_white;
500 if (text->style_sheet != NULL && SWFDEC_IS_STYLESHEET (text->style_sheet)) {
501 data.style_sheet = SWFDEC_STYLESHEET (text->style_sheet);
502 } else {
503 data.style_sheet = NULL;
505 data.text = text->text;
506 data.tags_open = NULL;
507 data.tags_closed = NULL;
509 p = str;
510 while (p != NULL && *p != '\0') {
511 if (*p == '<') {
512 if (strncmp (p + 1, "!--", strlen ("!--")) == 0) {
513 p = swfdec_text_field_movie_html_parse_comment (&data, p);
514 } else {
515 p = swfdec_text_field_movie_html_parse_tag (&data, p);
517 } else {
518 p = swfdec_text_field_movie_html_parse_text (&data, p);
522 // close remaining tags
523 while (data.tags_open != NULL) {
524 /* yes, this really appends to the default format */
525 swfdec_text_buffer_set_default_attributes (text->text,
526 &((ParserTag *)data.tags_open->data)->format->attr,
527 ((ParserTag *)data.tags_open->data)->format->values_set);
528 swfdec_text_field_movie_html_parse_close_tag (&data,
529 (ParserTag *)data.tags_open->data, TRUE);
532 // add parsed styles
533 while (data.tags_closed != NULL) {
534 ParserTag *tag = (ParserTag *)data.tags_closed->data;
536 if (tag->index != tag->end_index && tag->format != NULL) {
537 swfdec_text_buffer_set_attributes (text->text, tag->index,
538 tag->end_index - tag->index, &tag->format->attr, tag->format->values_set);
541 g_free (tag);
542 data.tags_closed = g_slist_remove (data.tags_closed, tag);
547 * Generating
549 static const char *
550 swfdec_text_field_movie_html_text_align_to_string (SwfdecTextAlign align)
552 switch (align) {
553 case SWFDEC_TEXT_ALIGN_LEFT:
554 return "LEFT";
555 case SWFDEC_TEXT_ALIGN_RIGHT:
556 return "RIGHT";
557 case SWFDEC_TEXT_ALIGN_CENTER:
558 return "CENTER";
559 case SWFDEC_TEXT_ALIGN_JUSTIFY:
560 return "JUSTIFY";
561 default:
562 g_assert_not_reached ();
563 return "";
568 * Order of tags:
569 * TEXTFORMAT / P or LI / FONT / A / B / I / U
571 * Order of attributes:
572 * TEXTFORMAT:
573 * LEFTMARGIN / RIGHTMARGIN / INDENT / LEADING / BLOCKINDENT / TABSTOPS
574 * P: ALIGN
575 * LI: none
576 * FONT: FACE / SIZE / COLOR / LETTERSPACING / KERNING
577 * A: HREF / TARGET
578 * B: none
579 * I: none
580 * U: none
582 static GString *
583 swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
584 GString *string, guint start_index, guint end_index)
586 const SwfdecTextAttributes *attr, *attr_prev, *attr_font;
587 SwfdecTextBufferIter *iter;
588 GSList *fonts, *iter_font;
589 guint index_, index_prev;
590 gboolean textformat, bullet, font = FALSE;
591 char *escaped;
593 g_return_val_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text), string);
594 g_return_val_if_fail (string != NULL, string);
595 g_return_val_if_fail (start_index <= end_index, string);
597 iter = swfdec_text_buffer_get_iter (text->text, start_index);
598 index_ = start_index;
599 attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
601 if (attr->left_margin != 0 || attr->right_margin != 0 ||
602 attr->indent != 0 || attr->leading != 0 ||
603 attr->block_indent != 0 ||
604 attr->n_tab_stops > 0)
606 string = g_string_append (string, "<TEXTFORMAT");
607 if (attr->left_margin) {
608 g_string_append_printf (string, " LEFTMARGIN=\"%i\"",
609 attr->left_margin);
611 if (attr->right_margin) {
612 g_string_append_printf (string, " RIGHTMARGIN=\"%i\"",
613 attr->right_margin);
615 if (attr->indent)
616 g_string_append_printf (string, " INDENT=\"%i\"", attr->indent);
617 if (attr->leading)
618 g_string_append_printf (string, " LEADING=\"%i\"", attr->leading);
619 if (attr->block_indent) {
620 g_string_append_printf (string, " BLOCKINDENT=\"%i\"",
621 attr->block_indent);
623 if (attr->n_tab_stops > 0) {
624 guint i;
625 g_string_append (string, " TABSTOPS=\"\"");
626 for (i = 0; i < attr->n_tab_stops; i++) {
627 g_string_append_printf (string, "%d,", attr->tab_stops[i]);
629 string->str[string->len - 1] = '\"';
631 string = g_string_append (string, ">");
633 textformat = TRUE;
635 else
637 textformat = FALSE;
640 if (attr->bullet) {
641 string = g_string_append (string, "<LI>");
642 bullet = TRUE;
643 } else {
644 g_string_append_printf (string, "<P ALIGN=\"%s\">",
645 swfdec_text_field_movie_html_text_align_to_string (attr->align));
646 bullet = FALSE;
649 // note we don't escape attr->font, even thought it can have evil chars
650 g_string_append_printf (string, "<FONT FACE=\"%s\" SIZE=\"%i\" COLOR=\"#%06X\" LETTERSPACING=\"%i\" KERNING=\"%i\">",
651 attr->font, attr->size, attr->color, (int)attr->letter_spacing,
652 (attr->kerning ? 1 : 0));
653 fonts = g_slist_prepend (NULL, (gpointer) attr);
655 if (attr->url != SWFDEC_AS_STR_EMPTY)
656 g_string_append_printf (string, "<A HREF=\"%s\" TARGET=\"%s\">",
657 attr->url, attr->target);
658 if (attr->bold)
659 string = g_string_append (string, "<B>");
660 if (attr->italic)
661 string = g_string_append (string, "<I>");
662 if (attr->underline)
663 string = g_string_append (string, "<U>");
665 // special case: use <= instead of < to add some extra markup
666 for (iter = swfdec_text_buffer_iter_next (text->text, iter);
667 iter != NULL && swfdec_text_buffer_iter_get_start (text->text, iter) <= end_index;
668 iter = swfdec_text_buffer_iter_next (text->text, iter))
670 index_prev = index_;
671 attr_prev = attr;
672 index_ = swfdec_text_buffer_iter_get_start (text->text, iter);
673 attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
675 escaped = swfdec_xml_escape_len (swfdec_text_buffer_get_text (text->text) + index_prev,
676 index_ - index_prev);
677 string = g_string_append (string, escaped);
678 g_free (escaped);
679 escaped = NULL;
681 // Figure out what tags need to be rewritten
682 if (attr->font != attr_prev->font ||
683 attr->size != attr_prev->size ||
684 attr->color != attr_prev->color ||
685 (int)attr->letter_spacing != (int)attr_prev->letter_spacing ||
686 attr->kerning != attr_prev->kerning) {
687 font = TRUE;
688 } else if (attr->url == attr_prev->url &&
689 attr->target == attr_prev->target &&
690 attr->bold == attr_prev->bold &&
691 attr->italic == attr_prev->italic &&
692 attr->underline == attr_prev->underline) {
693 continue;
696 // Close tags
697 for (iter_font = fonts; iter_font != NULL; iter_font = iter_font->next)
699 attr_font = iter_font->data;
700 if (attr->font == attr_font->font &&
701 attr->size == attr_font->size &&
702 attr->color == attr_font->color &&
703 (int)attr->letter_spacing == (int)attr_font->letter_spacing &&
704 attr->kerning == attr_font->kerning) {
705 break;
708 if (attr_prev->underline)
709 string = g_string_append (string, "</U>");
710 if (attr_prev->italic)
711 string = g_string_append (string, "</I>");
712 if (attr_prev->bold)
713 string = g_string_append (string, "</B>");
714 if (attr_prev->url != SWFDEC_AS_STR_EMPTY)
715 string = g_string_append (string, "</A>");
716 if (iter_font != NULL) {
717 while (fonts != iter_font) {
718 string = g_string_append (string, "</FONT>");
719 fonts = g_slist_remove (fonts, fonts->data);
723 // Open tags
724 attr_font = fonts->data;
725 if (font && (attr->font != attr_font->font ||
726 attr->size != attr_font->size ||
727 attr->color != attr_font->color ||
728 (int)attr->letter_spacing != (int)attr_font->letter_spacing ||
729 attr->kerning != attr_font->kerning))
731 fonts = g_slist_prepend (fonts, (gpointer) attr);
733 string = g_string_append (string, "<FONT");
734 // note we don't escape attr->font, even thought it can have evil chars
735 if (attr->font != attr_font->font)
736 g_string_append_printf (string, " FACE=\"%s\"", attr->font);
737 if (attr->size != attr_font->size)
738 g_string_append_printf (string, " SIZE=\"%i\"", attr->size);
739 if (attr->color != attr_font->color)
740 g_string_append_printf (string, " COLOR=\"#%06X\"", attr->color);
741 if ((int)attr->letter_spacing != (int)attr_font->letter_spacing) {
742 g_string_append_printf (string, " LETTERSPACING=\"%i\"",
743 (int)attr->letter_spacing);
745 if (attr->kerning != attr_font->kerning) {
746 g_string_append_printf (string, " KERNING=\"%i\"",
747 (attr->kerning ? 1 : 0));
749 string = g_string_append (string, ">");
751 if (attr->url != SWFDEC_AS_STR_EMPTY) {
752 g_string_append_printf (string, "<A HREF=\"%s\" TARGET=\"%s\">",
753 attr->url, attr->target);
755 if (attr->bold)
756 string = g_string_append (string, "<B>");
757 if (attr->italic)
758 string = g_string_append (string, "<I>");
759 if (attr->underline)
760 string = g_string_append (string, "<U>");
763 escaped = swfdec_xml_escape_len (swfdec_text_buffer_get_text (text->text) + index_,
764 end_index - index_);
765 string = g_string_append (string, escaped);
766 g_free (escaped);
768 if (attr->underline)
769 string = g_string_append (string, "</U>");
770 if (attr->italic)
771 string = g_string_append (string, "</I>");
772 if (attr->bold)
773 string = g_string_append (string, "</B>");
774 if (attr->url != SWFDEC_AS_STR_EMPTY)
775 string = g_string_append (string, "</A>");
776 for (iter_font = fonts; iter_font != NULL; iter_font = iter_font->next)
777 string = g_string_append (string, "</FONT>");
778 g_slist_free (fonts);
779 if (bullet) {
780 string = g_string_append (string, "</LI>");
781 } else {
782 string = g_string_append (string, "</P>");
784 if (textformat)
785 string = g_string_append (string, "</TEXTFORMAT>");
787 return string;
790 const char *
791 swfdec_text_field_movie_get_html_text (SwfdecTextFieldMovie *text)
793 const char *p, *end, *start;
794 GString *string;
796 g_return_val_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text),
797 SWFDEC_AS_STR_EMPTY);
799 string = g_string_new ("");
800 p = start = swfdec_text_buffer_get_text (text->text);
802 while (*p != '\0') {
803 end = strpbrk (p, "\r\n");
804 if (end == NULL)
805 end = strchr (p, '\0');
807 string = swfdec_text_field_movie_html_text_append_paragraph (text, string,
808 p - start, end - start);
810 p = end;
811 if (*p != '\0') p++;
814 return swfdec_as_context_give_string (swfdec_gc_object_get_context (text),
815 g_string_free (string, FALSE));