[Swfdec-commits] 29 commits - swfdec/Makefile.am swfdec/swfdec_actor.h swfdec/swfdec_marshal.list swfdec/swfdec_movie.c swfdec/swfdec_player.c swfdec/swfdec_player_internal.h swfdec/swfdec_text_attributes.c swfdec/swfdec_text_attributes.h swfdec/swfdec_text_buffer.c swfdec/swfdec_text_buffer.h swfdec/swfdec_text_field.c swfdec/swfdec_text_field_movie_as.c swfdec/swfdec_text_field_movie.c swfdec/swfdec_text_field_movie.h swfdec/swfdec_text_field_movie_html.c swfdec/swfdec_text_format.c swfdec/swfdec_text_format.h swfdec/swfdec_text_layout.c swfdec/swfdec_text_layout.h test/image test/trace

Benjamin Otte company at kemper.freedesktop.org
Thu May 8 15:19:28 PDT 2008


 swfdec/Makefile.am                             |    6 
 swfdec/swfdec_actor.h                          |    4 
 swfdec/swfdec_marshal.list                     |    1 
 swfdec/swfdec_movie.c                          |   33 
 swfdec/swfdec_player.c                         |  109 -
 swfdec/swfdec_player_internal.h                |    2 
 swfdec/swfdec_text_attributes.c                |  161 ++
 swfdec/swfdec_text_attributes.h                |  115 +
 swfdec/swfdec_text_buffer.c                    |  464 ++++++
 swfdec/swfdec_text_buffer.h                    |  104 +
 swfdec/swfdec_text_field.c                     |   16 
 swfdec/swfdec_text_field_movie.c               | 1681 +++----------------------
 swfdec/swfdec_text_field_movie.h               |   92 -
 swfdec/swfdec_text_field_movie_as.c            |  185 +-
 swfdec/swfdec_text_field_movie_html.c          |  250 +--
 swfdec/swfdec_text_format.c                    |  508 ++-----
 swfdec/swfdec_text_format.h                    |   61 
 swfdec/swfdec_text_layout.c                    |  818 ++++++++++++
 swfdec/swfdec_text_layout.h                    |   91 +
 test/image/Makefile.am                         |   47 
 test/image/text-field-background-5.swf         |binary
 test/image/text-field-background-5.swf.png     |binary
 test/image/text-field-background-6.swf         |binary
 test/image/text-field-background-6.swf.png     |binary
 test/image/text-field-background-7.swf         |binary
 test/image/text-field-background-7.swf.png     |binary
 test/image/text-field-background-8.swf         |binary
 test/image/text-field-background-8.swf.png     |binary
 test/image/text-field-background.as            |   18 
 test/image/text-field-border-5.swf             |binary
 test/image/text-field-border-5.swf.png         |binary
 test/image/text-field-border-6.swf             |binary
 test/image/text-field-border-6.swf.png         |binary
 test/image/text-field-border-7.swf             |binary
 test/image/text-field-border-7.swf.png         |binary
 test/image/text-field-border-8.swf             |binary
 test/image/text-field-border-8.swf.png         |binary
 test/image/text-field-border.as                |   15 
 test/image/text-field-scale-negative-5.swf     |binary
 test/image/text-field-scale-negative-5.swf.png |binary
 test/image/text-field-scale-negative-6.swf     |binary
 test/image/text-field-scale-negative-6.swf.png |binary
 test/image/text-field-scale-negative-7.swf     |binary
 test/image/text-field-scale-negative-7.swf.png |binary
 test/image/text-field-scale-negative-8.swf     |binary
 test/image/text-field-scale-negative-8.swf.png |binary
 test/image/text-field-scale-negative.as        |   22 
 test/image/text-field-scale-parents-5.swf      |binary
 test/image/text-field-scale-parents-5.swf.png  |binary
 test/image/text-field-scale-parents-6.swf      |binary
 test/image/text-field-scale-parents-6.swf.png  |binary
 test/image/text-field-scale-parents-7.swf      |binary
 test/image/text-field-scale-parents-7.swf.png  |binary
 test/image/text-field-scale-parents-8.swf      |binary
 test/image/text-field-scale-parents-8.swf.png  |binary
 test/image/text-field-scale-parents.as         |   14 
 test/image/text-field-wordWrap-5.swf           |binary
 test/image/text-field-wordWrap-5.swf.png       |binary
 test/image/text-field-wordWrap-6.swf           |binary
 test/image/text-field-wordWrap-6.swf.png       |binary
 test/image/text-field-wordWrap-7.swf           |binary
 test/image/text-field-wordWrap-7.swf.png       |binary
 test/image/text-field-wordWrap-8.swf           |binary
 test/image/text-field-wordWrap-8.swf.png       |binary
 test/image/text-field-wordWrap.as              |   24 
 test/trace/Makefile.am                         |    9 
 test/trace/text-field-hscroll-5.swf            |binary
 test/trace/text-field-hscroll-5.swf.trace      |    6 
 test/trace/text-field-hscroll-6.swf            |binary
 test/trace/text-field-hscroll-6.swf.trace      |    6 
 test/trace/text-field-hscroll-7.swf            |binary
 test/trace/text-field-hscroll-7.swf.trace      |    6 
 test/trace/text-field-hscroll-8.swf            |binary
 test/trace/text-field-hscroll-8.swf.trace      |    6 
 test/trace/text-field-hscroll.as               |   29 
 75 files changed, 2729 insertions(+), 2174 deletions(-)

New commits:
commit 49c90e4ceef5ccae2e0976eddc7424a8754597e1
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 23:00:01 2008 +0200

    add a simple hscroll test

diff --git a/test/trace/Makefile.am b/test/trace/Makefile.am
index b00b85b..c27dd2c 100644
--- a/test/trace/Makefile.am
+++ b/test/trace/Makefile.am
@@ -3296,6 +3296,15 @@ EXTRA_DIST = \
 	text-field-get-text-format-7.swf.trace \
 	text-field-get-text-format-8.swf \
 	text-field-get-text-format-8.swf.trace \
+	text-field-hscroll-5.swf \
+	text-field-hscroll-5.swf.trace \
+	text-field-hscroll-6.swf \
+	text-field-hscroll-6.swf.trace \
+	text-field-hscroll-7.swf \
+	text-field-hscroll-7.swf.trace \
+	text-field-hscroll-8.swf \
+	text-field-hscroll-8.swf.trace \
+	text-field-hscroll.as \
 	text-field-html-input.as \
 	text-field-html-input-5.swf \
 	text-field-html-input-5.swf.trace \
diff --git a/test/trace/text-field-hscroll-5.swf b/test/trace/text-field-hscroll-5.swf
new file mode 100644
index 0000000..aea0e49
Binary files /dev/null and b/test/trace/text-field-hscroll-5.swf differ
diff --git a/test/trace/text-field-hscroll-5.swf.trace b/test/trace/text-field-hscroll-5.swf.trace
new file mode 100644
index 0000000..8c4db68
--- /dev/null
+++ b/test/trace/text-field-hscroll-5.swf.trace
@@ -0,0 +1,6 @@
+undefined
+undefined
+undefined
+undefined
+undefined
+undefined
diff --git a/test/trace/text-field-hscroll-6.swf b/test/trace/text-field-hscroll-6.swf
new file mode 100644
index 0000000..99e4519
Binary files /dev/null and b/test/trace/text-field-hscroll-6.swf differ
diff --git a/test/trace/text-field-hscroll-6.swf.trace b/test/trace/text-field-hscroll-6.swf.trace
new file mode 100644
index 0000000..c16cb90
--- /dev/null
+++ b/test/trace/text-field-hscroll-6.swf.trace
@@ -0,0 +1,6 @@
+1
+1
+2
+1
+1
+1
diff --git a/test/trace/text-field-hscroll-7.swf b/test/trace/text-field-hscroll-7.swf
new file mode 100644
index 0000000..069b7f7
Binary files /dev/null and b/test/trace/text-field-hscroll-7.swf differ
diff --git a/test/trace/text-field-hscroll-7.swf.trace b/test/trace/text-field-hscroll-7.swf.trace
new file mode 100644
index 0000000..c16cb90
--- /dev/null
+++ b/test/trace/text-field-hscroll-7.swf.trace
@@ -0,0 +1,6 @@
+1
+1
+2
+1
+1
+1
diff --git a/test/trace/text-field-hscroll-8.swf b/test/trace/text-field-hscroll-8.swf
new file mode 100644
index 0000000..95a75c4
Binary files /dev/null and b/test/trace/text-field-hscroll-8.swf differ
diff --git a/test/trace/text-field-hscroll-8.swf.trace b/test/trace/text-field-hscroll-8.swf.trace
new file mode 100644
index 0000000..c16cb90
--- /dev/null
+++ b/test/trace/text-field-hscroll-8.swf.trace
@@ -0,0 +1,6 @@
+1
+1
+2
+1
+1
+1
diff --git a/test/trace/text-field-hscroll.as b/test/trace/text-field-hscroll.as
new file mode 100644
index 0000000..2d4e26e
--- /dev/null
+++ b/test/trace/text-field-hscroll.as
@@ -0,0 +1,29 @@
+// makeswf -v 7 -s 200x150 -r 1 -o text-field-hscroll.swf text-field-hscroll.as
+
+createTextField ("t", 0, 0, 0, 100, 32);
+var tf = new TextFormat ();
+tf.font = "Bitstream Vera Sans";
+t.setNewTextFormat (tf);
+t.border = true;
+
+t.text = "Hello\nWorld\nHey";
+tf.size = 24;
+t.setTextFormat (0, 5, tf);
+
+trace (t.scroll);
+trace (t.bottomScroll);
+trace (t.maxscroll);
+
+createTextField ("s", 1, 0, 50, 100, 10);
+var tf = new TextFormat ();
+tf.font = "Bitstream Vera Sans";
+s.setNewTextFormat (tf);
+s.border = true;
+
+s.text = "Hello World";
+
+trace (s.scroll);
+trace (s.bottomScroll);
+trace (s.maxscroll);
+
+getURL ("fscommand:quit", "");
commit 5e9205a7fcfde3e3828599558f52bc32c0b1cd56
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 22:54:44 2008 +0200

    add support for vertical scrolling again
    
    I didn't have a signle look at events yet...

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index cfe8783..e7fa1fb 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -132,94 +132,7 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
       text->stage_rect.height - BORDER_TOP - BORDER_BOTTOM);
   cairo_clip (cr);
   swfdec_text_layout_render (text->layout, cr, ctrans,
-      0, text->stage_rect.height - BORDER_TOP + BORDER_BOTTOM, &text->stage_rect);
-}
-
-void
-swfdec_text_field_movie_update_scroll (SwfdecTextFieldMovie *text,
-    gboolean check_limits)
-{
-#if 0
-  SwfdecLayout *layouts;
-  int i, num, y, visible, all, height;
-  double width, width_max;
-
-  g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-
-  layouts = swfdec_text_field_movie_get_layouts (text, &num, NULL, NULL, NULL);
-
-  width = SWFDEC_MOVIE (text)->original_extents.x1 -
-    SWFDEC_MOVIE (text)->original_extents.x0;
-  height = SWFDEC_MOVIE (text)->original_extents.y1 -
-    SWFDEC_MOVIE (text)->original_extents.y0;
-
-  width_max = width;
-  y = 0;
-  all = 0;
-  visible = 0;
-
-  for (i = num - 1; i >= 0; i--)
-  {
-    SwfdecLayout *layout = &layouts[i];
-    PangoLayoutIter *iter_line;
-    PangoRectangle rect;
-
-    if (layouts[i].width > width_max)
-      width_max = layouts[i].width;
-
-    y += layout->height;
-
-    iter_line = pango_layout_get_iter (layout->layout);
-
-    do {
-      pango_layout_iter_get_line_extents (iter_line, NULL, &rect);
-      pango_extents_to_pixels (NULL, &rect);
-
-      if (y - rect.y <= height)
-	visible++;
-
-      all++;
-    } while (pango_layout_iter_next_line (iter_line));
-
-    pango_layout_iter_free (iter_line);
-  }
-
-  swfdec_text_field_movie_free_layouts (layouts);
-  layouts = NULL;
-
-  if (text->scroll_max != all - visible + 1) {
-    text->scroll_max = all - visible + 1;
-    text->scroll_changed = TRUE;
-  }
-  if (text->hscroll_max != SWFDEC_TWIPS_TO_DOUBLE (width_max - width)) {
-    text->hscroll_max = SWFDEC_TWIPS_TO_DOUBLE (width_max - width);
-    text->scroll_changed = TRUE;
-  }
-
-  if (check_limits) {
-    if (text->scroll != CLAMP(text->scroll, 1, text->scroll_max)) {
-      text->scroll = CLAMP(text->scroll, 1, text->scroll_max);
-      text->scroll_changed = TRUE;
-    }
-    if (text->scroll_bottom != text->scroll + (visible > 0 ? visible - 1 : 0))
-    {
-      text->scroll_bottom = text->scroll + (visible > 0 ? visible - 1 : 0);
-      text->scroll_changed = TRUE;
-    }
-    if (text->hscroll != CLAMP(text->hscroll, 0, text->hscroll_max)) {
-      text->hscroll = CLAMP(text->hscroll, 0, text->hscroll_max);
-      text->scroll_changed = TRUE;
-    }
-  } else {
-    if (text->scroll_bottom < text->scroll ||
-	text->scroll_bottom > text->scroll_max + visible - 1) {
-      text->scroll_bottom = text->scroll;
-      text->scroll_changed = TRUE;
-    }
-  }
-#else
-  SWFDEC_STUB ("swfdec_text_field_movie_update_scroll");
-#endif
+      text->scroll, text->stage_rect.height - BORDER_TOP + BORDER_BOTTOM, &text->stage_rect);
 }
 
 gboolean
@@ -368,6 +281,31 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   }
 }
 
+/* NB: can be run with unlocked player */
+void
+swfdec_text_field_movie_update_scroll (SwfdecTextFieldMovie *text)
+{
+  guint scroll_max, lines_visible, rows, height;
+
+  height = text->stage_rect.height - BORDER_TOP - BORDER_BOTTOM;
+  rows = swfdec_text_layout_get_n_rows (text->layout);
+  scroll_max = rows - swfdec_text_layout_get_visible_rows_end (text->layout, height);
+  if (scroll_max != text->scroll_max) {
+    text->scroll_max = scroll_max;
+    text->scroll_changed = TRUE;
+  }
+  if (scroll_max < text->scroll) {
+    text->scroll = scroll_max;
+    text->scroll_changed = TRUE;
+  }
+  lines_visible = swfdec_text_layout_get_visible_rows (text->layout,
+      text->scroll, height);
+  if (lines_visible != text->lines_visible) {
+    text->lines_visible = lines_visible;
+    text->scroll_changed = TRUE;
+  }
+}
+
 /* NB: This signal can happen without a locked player */
 static void
 swfdec_text_field_movie_layout_changed (SwfdecTextLayout *layout,
@@ -387,6 +325,8 @@ swfdec_text_field_movie_layout_changed (SwfdecTextLayout *layout,
     text->layout_height = h;
     //swfdec_text_field_movie_auto_size (text);
   }
+
+  swfdec_text_field_movie_update_scroll (text);
 }
 
 static void
@@ -803,7 +743,6 @@ swfdec_text_field_movie_init (SwfdecTextFieldMovie *text)
   g_signal_connect (text->layout, "changed",
       G_CALLBACK (swfdec_text_field_movie_layout_changed), text);
 
-  text->scroll = 1;
   text->mouse_wheel_enabled = TRUE;
 
   swfdec_text_attributes_reset (&text->default_attributes);
@@ -979,7 +918,6 @@ swfdec_text_field_movie_replace_text (SwfdecTextFieldMovie *text,
   }
 
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
-  swfdec_text_field_movie_update_scroll (text, TRUE);
 }
 
 void
@@ -1020,5 +958,4 @@ swfdec_text_field_movie_set_text (SwfdecTextFieldMovie *text, const char *str,
   }
 
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
-  swfdec_text_field_movie_update_scroll (text, TRUE);
 }
diff --git a/swfdec/swfdec_text_field_movie.h b/swfdec/swfdec_text_field_movie.h
index 82d8ae0..6789432 100644
--- a/swfdec/swfdec_text_field_movie.h
+++ b/swfdec/swfdec_text_field_movie.h
@@ -78,9 +78,10 @@ struct _SwfdecTextFieldMovie {
 
   gboolean		scroll_changed; /* if any of the scroll attributes have changed and we haven't fired the event yet */
   guint			changed;	/* number of onChanged events we have to emit */
-  int			scroll;
-  int			scroll_max;
-  int			scroll_bottom;
+  /* scroll variables */
+  guint			scroll;		/* current scroll offset in lines (0-indexed) */
+  guint			scroll_max;	/* scroll must be smaller than this value */
+  guint			lines_visible;	/* number of lines currently visible */
   int			hscroll;
   int			hscroll_max;
   gboolean		mouse_wheel_enabled;
@@ -103,9 +104,8 @@ GType		swfdec_text_field_movie_get_type		(void);
 void		swfdec_text_field_movie_set_text	(SwfdecTextFieldMovie *	movie,
 							 const char *		str,
 							 gboolean		html);
+void		swfdec_text_field_movie_update_scroll	(SwfdecTextFieldMovie * text);
 gboolean	swfdec_text_field_movie_auto_size	(SwfdecTextFieldMovie *	text);
-void		swfdec_text_field_movie_update_scroll	(SwfdecTextFieldMovie *	text,
-							 gboolean		check_limits);
 const char *	swfdec_text_field_movie_get_text	(SwfdecTextFieldMovie *		text);
 void		swfdec_text_field_movie_set_listen_variable (SwfdecTextFieldMovie *	text,
 							 const char *			value);
diff --git a/swfdec/swfdec_text_field_movie_as.c b/swfdec/swfdec_text_field_movie_as.c
index 05a1d5b..979341f 100644
--- a/swfdec/swfdec_text_field_movie_as.c
+++ b/swfdec/swfdec_text_field_movie_as.c
@@ -583,7 +583,7 @@ swfdec_text_field_movie_get_bottomScroll (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->scroll_bottom);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->scroll + text->lines_visible);
 }
 
 static void
@@ -641,7 +641,7 @@ swfdec_text_field_movie_get_maxscroll (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->scroll_max);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->scroll_max + 1);
 }
 
 static void
@@ -680,7 +680,7 @@ swfdec_text_field_movie_do_get_scroll (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->scroll);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->scroll + 1);
 }
 
 static void
@@ -693,11 +693,11 @@ swfdec_text_field_movie_do_set_scroll (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "i", &value);
 
-  value = CLAMP (value, 1, text->scroll_max);
-  if (value != text->scroll) {
-    text->scroll_bottom += value - text->scroll;
+  value = CLAMP (value - 1, 0, (int) text->scroll_max);
+  if ((guint) value != text->scroll) {
     text->scroll = value;
     text->scroll_changed = TRUE;
+    swfdec_text_field_movie_update_scroll (text);
     swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
   }
 }
@@ -1163,7 +1163,7 @@ swfdec_text_field_movie_setTextFormat (SwfdecAsContext *cx,
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
   swfdec_text_field_movie_auto_size (text);
   // special case: update the max values, not the current values
-  swfdec_text_field_movie_update_scroll (text, FALSE);
+  // swfdec_text_field_movie_update_scroll (text, FALSE);
 }
 
 SWFDEC_AS_NATIVE (104, 101, swfdec_text_field_movie_getTextFormat)
diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
index 7947bd2..8af09d0 100644
--- a/swfdec/swfdec_text_layout.c
+++ b/swfdec/swfdec_text_layout.c
@@ -662,6 +662,100 @@ swfdec_text_layout_find_row (SwfdecTextLayout *layout, guint row)
 }
 
 /**
+ * swfdec_text_layout_get_visible_rows:
+ * @layout: the layout
+ * @row: the first visible row
+ * @height: the height in pixels of visible text
+ *
+ * Queries the number of rows that would be rendered, if rendering were to start
+ * with @row and had to fit into @height pixels.
+ *
+ * Returns: the number of rows that would be rendered
+ **/
+guint
+swfdec_text_layout_get_visible_rows (SwfdecTextLayout *layout, guint row, guint height)
+{
+  GSequenceIter *iter;
+  SwfdecTextBlock *block;
+  PangoRectangle extents;
+  guint count = 0;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), 1);
+  g_return_val_if_fail (row < swfdec_text_layout_get_n_rows (layout), 1);
+
+  swfdec_text_layout_ensure (layout);
+
+  iter = swfdec_text_layout_find_row (layout, row); 
+  block = g_sequence_get (iter);
+  row -= block->row;
+
+  do {
+    block = g_sequence_get (iter);
+    for (;row < (guint) pango_layout_get_line_count (block->layout); row++) {
+      PangoLayoutLine *line = pango_layout_get_line_readonly (block->layout, row);
+      
+      pango_layout_line_get_pixel_extents (line, NULL, &extents);
+      if (extents.height > (int) height)
+	goto out;
+      height -= extents.height;
+      count++;
+    }
+    row = 0;
+    if ((int) height <= pango_layout_get_spacing (block->layout) / PANGO_SCALE)
+      goto out;
+    height -= pango_layout_get_spacing (block->layout) / PANGO_SCALE;
+    iter = g_sequence_iter_next (iter);
+  } while (!g_sequence_iter_is_end (iter));
+
+out:
+  return MAX (count, 1);
+}
+
+/**
+ * swfdec_text_layout_get_visible_rows_end:
+ * @layout: the layout
+ * @height: the height in pixels
+ *
+ * Computes how many rows will be visible if only the last rows would be 
+ * displayed. This is useful for computing maximal scroll offsets.
+ *
+ * Returns: The number of rows visible at the bottom.
+ **/
+guint
+swfdec_text_layout_get_visible_rows_end (SwfdecTextLayout *layout, guint height)
+{
+  GSequenceIter *iter;
+  SwfdecTextBlock *block;
+  PangoRectangle extents;
+  guint row, count = 0;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), 1);
+
+  swfdec_text_layout_ensure (layout);
+  iter = g_sequence_get_end_iter (layout->blocks);
+
+  do {
+    iter = g_sequence_iter_prev (iter);
+    block = g_sequence_get (iter);
+    if ((int) height <= pango_layout_get_spacing (block->layout) / PANGO_SCALE)
+      goto out;
+    height -= pango_layout_get_spacing (block->layout) / PANGO_SCALE;
+    for (row = pango_layout_get_line_count (block->layout); row > 0; row--) {
+      PangoLayoutLine *line = pango_layout_get_line_readonly (block->layout, row - 1);
+      
+      pango_layout_line_get_pixel_extents (line, NULL, &extents);
+      if (extents.height > (int) height) 
+	goto out;
+      height -= extents.height;
+      count++;
+    }
+  } while (!g_sequence_iter_is_begin (iter));
+
+out:
+  return MAX (count, 1);
+}
+
+/**
  * swfdec_text_layout_render:
  * @layout: the layout to render
  * @cr: the Cairo context to render to. The context will be transformed so that
@@ -694,7 +788,7 @@ swfdec_text_layout_render (SwfdecTextLayout *layout, cairo_t *cr,
   iter = swfdec_text_layout_find_row (layout, row); 
   block = g_sequence_get (iter);
   row -= block->row;
-  while (!g_sequence_iter_is_end (iter)) {
+  do {
     block = g_sequence_get (iter);
     pango_cairo_update_layout (cr, block->layout);
     cairo_translate (cr, block->rect.x, 0);
@@ -713,9 +807,12 @@ swfdec_text_layout_render (SwfdecTextLayout *layout, cairo_t *cr,
       height -= extents.height;
       cairo_translate (cr, 0, extents.height + extents.y);
     }
+    if ((int) height <= pango_layout_get_spacing (block->layout) / PANGO_SCALE)
+      return;
+    height -= pango_layout_get_spacing (block->layout) / PANGO_SCALE;
     cairo_translate (cr, -block->rect.x, pango_layout_get_spacing (block->layout) / PANGO_SCALE);
     row = 0;
     iter = g_sequence_iter_next (iter);
-  }
+  } while (!g_sequence_iter_is_end (iter));
 }
 
diff --git a/swfdec/swfdec_text_layout.h b/swfdec/swfdec_text_layout.h
index 0457901..75f969f 100644
--- a/swfdec/swfdec_text_layout.h
+++ b/swfdec/swfdec_text_layout.h
@@ -71,7 +71,13 @@ void			swfdec_text_layout_set_scale		(SwfdecTextLayout *	layout,
 double			swfdec_text_layout_get_scale		(SwfdecTextLayout *	layout);
 guint			swfdec_text_layout_get_width		(SwfdecTextLayout *	layout);
 guint			swfdec_text_layout_get_height		(SwfdecTextLayout *	layout);
+
 guint			swfdec_text_layout_get_n_rows		(SwfdecTextLayout *	layout);
+guint			swfdec_text_layout_get_visible_rows	(SwfdecTextLayout *	layout,
+								 guint			row,
+								 guint			height);
+guint			swfdec_text_layout_get_visible_rows_end	(SwfdecTextLayout *	layout,
+								 guint			height);
 
 void			swfdec_text_layout_render		(SwfdecTextLayout *	layout,
 								 cairo_t *		cr, 
commit dc3ba2caf1e7fd79b0028752e7ae21d380cfc3cf
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 20:56:47 2008 +0200

    keep scale factors around

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 5d36b39..cfe8783 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -359,6 +359,8 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   /* FIXME: floor, ceil or round? */
   text->stage_rect.width = round (x) - text->stage_rect.x;
   text->stage_rect.height = round (y) - text->stage_rect.y;
+  text->xscale = matrix.xx;
+  text->yscale = matrix.yy;
   swfdec_text_layout_set_scale (text->layout, matrix.yy * SWFDEC_TWIPS_SCALE_FACTOR);
   if (text->word_wrap && text->stage_rect.width >= BORDER_LEFT + BORDER_RIGHT) {
     swfdec_text_layout_set_wrap_width (text->layout, text->stage_rect.width - 
@@ -977,7 +979,6 @@ swfdec_text_field_movie_replace_text (SwfdecTextFieldMovie *text,
   }
 
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
-  swfdec_text_field_movie_auto_size (text);
   swfdec_text_field_movie_update_scroll (text, TRUE);
 }
 
@@ -1019,6 +1020,5 @@ swfdec_text_field_movie_set_text (SwfdecTextFieldMovie *text, const char *str,
   }
 
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
-  swfdec_text_field_movie_auto_size (text);
   swfdec_text_field_movie_update_scroll (text, TRUE);
 }
diff --git a/swfdec/swfdec_text_field_movie.h b/swfdec/swfdec_text_field_movie.h
index 4d1bae5..82d8ae0 100644
--- a/swfdec/swfdec_text_field_movie.h
+++ b/swfdec/swfdec_text_field_movie.h
@@ -44,6 +44,8 @@ struct _SwfdecTextFieldMovie {
   SwfdecActor		actor;
 
   SwfdecRect		extents;	/* original extents (copied from graphic) */
+  double		xscale;		/* scale movie => stage in x direction */
+  double		yscale;		/* scale movie => stage in y direction */
   SwfdecRectangle	stage_rect;	/* these extents in stage coordinates */
 
   /* properties copied from textfield */
commit e01583685793c7ff29bc61f95e521c1573f1611e
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 15:57:44 2008 +0200

    notice changes to the layout
    
    Updates textWidth and textHeight automatically when this happens

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 8c60cf3..5d36b39 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -293,6 +293,8 @@ swfdec_text_field_movie_dispose (GObject *object)
     text->text = NULL;
   }
   if (text->layout) {
+    g_signal_handlers_disconnect_matched (text->layout, G_SIGNAL_MATCH_DATA,
+	0, 0, NULL, NULL, text);
     g_object_unref (text->layout);
     text->layout = NULL;
   }
@@ -321,6 +323,7 @@ swfdec_text_field_movie_mark (SwfdecAsObject *object)
   SWFDEC_AS_OBJECT_CLASS (swfdec_text_field_movie_parent_class)->mark (object);
 }
 
+/* NB: This signal can happen without a locked player */
 static void
 swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
 {
@@ -357,12 +360,33 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   text->stage_rect.width = round (x) - text->stage_rect.x;
   text->stage_rect.height = round (y) - text->stage_rect.y;
   swfdec_text_layout_set_scale (text->layout, matrix.yy * SWFDEC_TWIPS_SCALE_FACTOR);
-  if (text->word_wrap) {
+  if (text->word_wrap && text->stage_rect.width >= BORDER_LEFT + BORDER_RIGHT) {
     swfdec_text_layout_set_wrap_width (text->layout, text->stage_rect.width - 
 	BORDER_LEFT - BORDER_RIGHT);
   }
 }
 
+/* NB: This signal can happen without a locked player */
+static void
+swfdec_text_field_movie_layout_changed (SwfdecTextLayout *layout,
+    SwfdecTextFieldMovie *text)
+{
+  double scale;
+  guint w, h;
+
+  swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
+
+  scale = swfdec_text_layout_get_scale (layout);
+  w = swfdec_text_layout_get_width (layout) / scale;
+  h = swfdec_text_layout_get_height (layout) / scale;
+
+  if (w != text->layout_width || h != text->layout_height) {
+    text->layout_width = w;
+    text->layout_height = h;
+    //swfdec_text_field_movie_auto_size (text);
+  }
+}
+
 static void
 swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
 {
@@ -433,6 +457,7 @@ swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
     parent = parent->parent;
   }
   swfdec_text_field_movie_update_area (text);
+  swfdec_text_field_movie_layout_changed (text->layout, text);
 }
 
 static void
@@ -773,6 +798,8 @@ swfdec_text_field_movie_init (SwfdecTextFieldMovie *text)
       G_CALLBACK (swfdec_text_field_movie_text_changed), text);
 
   text->layout = swfdec_text_layout_new (text->text);
+  g_signal_connect (text->layout, "changed",
+      G_CALLBACK (swfdec_text_field_movie_layout_changed), text);
 
   text->scroll = 1;
   text->mouse_wheel_enabled = TRUE;
diff --git a/swfdec/swfdec_text_field_movie.h b/swfdec/swfdec_text_field_movie.h
index 43fcbde..4d1bae5 100644
--- a/swfdec/swfdec_text_field_movie.h
+++ b/swfdec/swfdec_text_field_movie.h
@@ -62,7 +62,6 @@ struct _SwfdecTextFieldMovie {
   gboolean		input_html;	/* whether orginal input was given as HTML */
 
   SwfdecTextLayout *	layout;		/* the layouted text */
-  SwfdecRectangle	layout_area;	/* visible area of the layout */
   guint			layout_width;	/* text width in pixels */
   guint			layout_height;	/* text height in pixels */
 
commit 4bf45d8ce4693d1332639d3abcdbfa67b4a287b4
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 15:56:47 2008 +0200

    setup a default PangoFontDescription for a PangoLayout
    
    This is useful so that we can support empty lines properly.

diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
index 2f0cff8..7947bd2 100644
--- a/swfdec/swfdec_text_layout.c
+++ b/swfdec/swfdec_text_layout.c
@@ -39,10 +39,11 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0, };
 
-static PangoAttribute *
-swfdec_text_attribute_create_bold (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
+static void
+swfdec_text_attribute_apply_bold (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr,
+    PangoFontDescription *desc)
 {
-  return pango_attr_weight_new (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+  pango_font_description_set_weight (desc, attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
 }
 
 static PangoAttribute *
@@ -52,16 +53,19 @@ swfdec_text_attribute_create_color (SwfdecTextLayout *layout, const SwfdecTextAt
       SWFDEC_COLOR_G (attr->color) * 255, SWFDEC_COLOR_B (attr->color) * 255);
 }
 
-static PangoAttribute *
-swfdec_text_attribute_create_font (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
+static void
+swfdec_text_attribute_apply_font (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr,
+    PangoFontDescription *desc)
 {
-  return pango_attr_family_new (attr->font);
+  /* FIXME: resolve _sans, _serif and friends */
+  pango_font_description_set_family (desc, attr->font);
 }
 
-static PangoAttribute *
-swfdec_text_attribute_create_italic (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
+static void
+swfdec_text_attribute_apply_italic (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr,
+    PangoFontDescription *desc)
 {
-  return pango_attr_style_new (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+  pango_font_description_set_style (desc, attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
 }
 
 static PangoAttribute *
@@ -70,12 +74,13 @@ swfdec_text_attribute_create_letter_spacing (SwfdecTextLayout *layout, const Swf
   return pango_attr_letter_spacing_new (attr->letter_spacing * PANGO_SCALE);
 }
 
-static PangoAttribute *
-swfdec_text_attribute_create_size (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
+static void
+swfdec_text_attribute_apply_size (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr,
+    PangoFontDescription *desc)
 {
   guint size = attr->size * layout->scale;
   size = MAX (size, 1);
-  return pango_attr_size_new_absolute (size * PANGO_SCALE);
+  pango_font_description_set_absolute_size (desc, size * PANGO_SCALE);
 }
 
 static PangoAttribute *
@@ -96,29 +101,30 @@ typedef enum {
 struct {
   FormatApplication   application;
   PangoAttribute *    (* create_attribute) (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr);
+  void		      (* apply_attribute) (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr, PangoFontDescription *desc);
 } format_table[] = {
   /* unknown or unhandled properties */
-  [SWFDEC_TEXT_ATTRIBUTE_DISPLAY] = { FORMAT_APPLY_UNKNOWN, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_KERNING] = { FORMAT_APPLY_UNKNOWN, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_DISPLAY] = { FORMAT_APPLY_UNKNOWN, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_KERNING] = { FORMAT_APPLY_UNKNOWN, NULL, NULL },
   /* per-paragraph options */
-  [SWFDEC_TEXT_ATTRIBUTE_BULLET] = { FORMAT_APPLY_BLOCK, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_INDENT] = { FORMAT_APPLY_BLOCK, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_BULLET] = { FORMAT_APPLY_BLOCK, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_INDENT] = { FORMAT_APPLY_BLOCK, NULL, NULL },
   /* per-line options */
-  [SWFDEC_TEXT_ATTRIBUTE_ALIGN] = { FORMAT_APPLY_LINE, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT] = { FORMAT_APPLY_LINE, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_LEADING] = { FORMAT_APPLY_LINE, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN] = { FORMAT_APPLY_LINE, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN] = { FORMAT_APPLY_LINE, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS] = { FORMAT_APPLY_LINE, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_ALIGN] = { FORMAT_APPLY_LINE, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT] = { FORMAT_APPLY_LINE, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_LEADING] = { FORMAT_APPLY_LINE, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN] = { FORMAT_APPLY_LINE, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN] = { FORMAT_APPLY_LINE, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS] = { FORMAT_APPLY_LINE, NULL, NULL },
   /* per-glyph options */
-  [SWFDEC_TEXT_ATTRIBUTE_BOLD] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_bold },
-  [SWFDEC_TEXT_ATTRIBUTE_COLOR] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_color },
-  [SWFDEC_TEXT_ATTRIBUTE_FONT] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_font },
-  [SWFDEC_TEXT_ATTRIBUTE_ITALIC] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_italic },
-  [SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_letter_spacing },
-  [SWFDEC_TEXT_ATTRIBUTE_SIZE] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_size },
-  [SWFDEC_TEXT_ATTRIBUTE_TARGET] = { FORMAT_APPLY_GLYPH, NULL },
-  [SWFDEC_TEXT_ATTRIBUTE_UNDERLINE] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_underline },
+  [SWFDEC_TEXT_ATTRIBUTE_BOLD] = { FORMAT_APPLY_GLYPH, NULL, swfdec_text_attribute_apply_bold },
+  [SWFDEC_TEXT_ATTRIBUTE_COLOR] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_color, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_FONT] = { FORMAT_APPLY_GLYPH, NULL, swfdec_text_attribute_apply_font },
+  [SWFDEC_TEXT_ATTRIBUTE_ITALIC] = { FORMAT_APPLY_GLYPH, NULL, swfdec_text_attribute_apply_italic },
+  [SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_letter_spacing, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_SIZE] = { FORMAT_APPLY_GLYPH, NULL, swfdec_text_attribute_apply_size },
+  [SWFDEC_TEXT_ATTRIBUTE_TARGET] = { FORMAT_APPLY_GLYPH, NULL, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_UNDERLINE] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_underline, NULL },
   [SWFDEC_TEXT_ATTRIBUTE_URL] = { FORMAT_APPLY_GLYPH, NULL }
 };
 
@@ -250,10 +256,24 @@ swfdec_text_layout_apply_line_attributes (SwfdecTextBlock *block,
 }
 
 static void
+swfdec_text_layout_apply_attributes_to_description (SwfdecTextLayout *layout, 
+    const SwfdecTextAttributes *attr, PangoFontDescription *desc)
+{
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS (format_table); i++) {
+    if (format_table[i].apply_attribute) {
+      format_table[i].apply_attribute (layout, attr, desc);
+    }
+  }
+}
+
+static void
 swfdec_text_layout_apply_attributes (SwfdecTextLayout *layout, PangoAttrList *list,
     const SwfdecTextAttributes *attr, guint start, guint end)
 {
   PangoAttribute *attribute;
+  PangoFontDescription *desc;
   guint i;
 
   for (i = 0; i < G_N_ELEMENTS (format_table); i++) {
@@ -264,6 +284,13 @@ swfdec_text_layout_apply_attributes (SwfdecTextLayout *layout, PangoAttrList *li
       pango_attr_list_change (list, attribute);
     }
   }
+  desc = pango_font_description_new ();
+  swfdec_text_layout_apply_attributes_to_description (layout, attr, desc);
+  attribute = pango_attr_font_desc_new (desc);
+  pango_font_description_free (desc);
+  attribute->start_index = start;
+  attribute->end_index = end;
+  pango_attr_list_change (list, attribute);
 }
 
 static const SwfdecTextBlock *
@@ -276,6 +303,7 @@ swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *con
   const char *string;
   const SwfdecTextAttributes *attr, *attr_next;
   gsize new_block, start_next;
+  PangoFontDescription *desc;
   gboolean first = TRUE;
   // TODO: kerning, display
 
@@ -300,6 +328,10 @@ swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *con
     } else {
       block->rect.width = layout->wrap_width;
     }
+    desc = pango_font_description_new ();
+    swfdec_text_layout_apply_attributes_to_description (layout, attr, desc);
+    pango_layout_set_font_description (block->layout, desc);
+    pango_font_description_free (desc);
     g_sequence_append (layout->blocks, block);
 
     if (layout->password) {
commit 8eca2567cfdcf7c444eb9cec3b89b5d0a20af438
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 15:56:06 2008 +0200

    LOG output more stuff from TextField parsing

diff --git a/swfdec/swfdec_text_field.c b/swfdec/swfdec_text_field.c
index 8eaa956..ddffb8d 100644
--- a/swfdec/swfdec_text_field.c
+++ b/swfdec/swfdec_text_field.c
@@ -143,6 +143,16 @@ tag_func_define_edit_text (SwfdecSwfDecoder * s, guint tag)
   if (text->embed_fonts)
     SWFDEC_FIXME ("Using embed fonts in TextField is not supported");
 
+  SWFDEC_LOG ("  word wrap: %u", text->word_wrap);
+  SWFDEC_LOG ("  multiline: %u", text->multiline);
+  SWFDEC_LOG ("  password: %u", text->password);
+  SWFDEC_LOG ("  editable: %u", text->editable);
+  SWFDEC_LOG ("  autosize: %u", text->auto_size);
+  SWFDEC_LOG ("  selectable: %u", text->selectable);
+  SWFDEC_LOG ("  background: %u", text->background);
+  SWFDEC_LOG ("  html: %u", text->html);
+  SWFDEC_LOG ("  embedFonts: %u", text->embed_fonts);
+
   if (has_font) {
     SwfdecCharacter *font;
 
@@ -210,8 +220,10 @@ tag_func_define_edit_text (SwfdecSwfDecoder * s, guint tag)
     text->variable = NULL;
   }
 
-  if (has_text)
+  if (has_text) {
     text->input = swfdec_bits_get_string (b, s->version);
+    SWFDEC_LOG ("  text = %s", text->input);
+  }
 
   return SWFDEC_STATUS_OK;
 }
commit 8f6725cfa0877c19d2a467abc628cd3e40af08df
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 11:28:10 2008 +0200

    emit "matrix-changed" signals when sclaing is updated
    
    This is pretty ugl code now, as the "matrix-changed" signal can be emitted
    with and without the player being locked.
    Maybe we should lock the player?
    The only thing we definitely mustn't do is call script code...

diff --git a/swfdec/swfdec_player.c b/swfdec/swfdec_player.c
index 5b3ea00..1031f08 100644
--- a/swfdec/swfdec_player.c
+++ b/swfdec/swfdec_player.c
@@ -810,6 +810,48 @@ swfdec_player_get_property (GObject *object, guint param_id, GValue *value,
   }
 }
 
+static void
+swfdec_player_emit_signals (SwfdecPlayer *player)
+{
+  SwfdecPlayerPrivate *priv = player->priv;
+  GList *walk;
+
+  /* emit invalidate signal */
+  if (!swfdec_rectangle_is_empty (&priv->invalid_extents)) {
+    g_signal_emit (player, signals[INVALIDATE], 0, &priv->invalid_extents,
+	priv->invalidations->data, priv->invalidations->len);
+    swfdec_rectangle_init_empty (&priv->invalid_extents);
+    g_array_set_size (priv->invalidations, 0);
+  }
+
+  /* emit audio-added for all added audio streams */
+  for (walk = priv->audio; walk; walk = walk->next) {
+    SwfdecAudio *audio = walk->data;
+
+    if (audio->added)
+      continue;
+    g_signal_emit (player, signals[AUDIO_ADDED], 0, audio);
+    audio->added = TRUE;
+  }
+
+  /* emit missing-plugin signal for newly discovered plugins */
+  if (priv->missing_plugins) {
+    GSList *swalk;
+    guint i = 0, n_plugins = g_slist_length (priv->missing_plugins);
+    char **details = g_new (char *, n_plugins + 1);
+
+    for (swalk = priv->missing_plugins; swalk; swalk = swalk->next) {
+      details[i++] = swalk->data;
+    }
+    details[i] = NULL;
+    g_slist_free (priv->missing_plugins);
+    priv->missing_plugins = NULL;
+    SWFDEC_INFO ("emitting missing plugins signal for %u plugins", n_plugins);
+    g_signal_emit (player, signals[MISSING_PLUGINS], 0, details);
+    g_strfreev (details);
+  }
+}
+
 void
 swfdec_player_update_scale (SwfdecPlayer *player)
 {
@@ -883,17 +925,14 @@ swfdec_player_update_scale (SwfdecPlayer *player)
   if (cairo_matrix_invert (&priv->stage_to_global)) {
     g_assert_not_reached ();
   }
-#if 0
-  /* FIXME: make this emit the signal at the right time */
-  priv->invalid.x0 = 0;
-  priv->invalid.y0 = 0;
-  priv->invalid.x1 = priv->stage_width;
-  priv->invalid.y1 = priv->stage_height;
-#endif
   /* FIXME: notify textfields more gentle about the update */
+  /* FIXME: these events can cause invalidations in TextFields */
   for (walk = priv->roots; walk; walk = walk->next) {
     g_signal_emit_by_name (walk->data, "matrix-changed");
   }
+  swfdec_player_invalidate (player, NULL);
+  if (!swfdec_player_is_locked (player))
+    swfdec_player_emit_signals (player);
 }
 
 static void
@@ -1386,48 +1425,6 @@ swfdec_player_do_mouse_release (SwfdecPlayer *player, guint button)
   return TRUE;
 }
 
-static void
-swfdec_player_emit_signals (SwfdecPlayer *player)
-{
-  SwfdecPlayerPrivate *priv = player->priv;
-  GList *walk;
-
-  /* emit invalidate signal */
-  if (!swfdec_rectangle_is_empty (&priv->invalid_extents)) {
-    g_signal_emit (player, signals[INVALIDATE], 0, &priv->invalid_extents,
-	priv->invalidations->data, priv->invalidations->len);
-    swfdec_rectangle_init_empty (&priv->invalid_extents);
-    g_array_set_size (priv->invalidations, 0);
-  }
-
-  /* emit audio-added for all added audio streams */
-  for (walk = priv->audio; walk; walk = walk->next) {
-    SwfdecAudio *audio = walk->data;
-
-    if (audio->added)
-      continue;
-    g_signal_emit (player, signals[AUDIO_ADDED], 0, audio);
-    audio->added = TRUE;
-  }
-
-  /* emit missing-plugin signal for newly discovered plugins */
-  if (priv->missing_plugins) {
-    GSList *swalk;
-    guint i = 0, n_plugins = g_slist_length (priv->missing_plugins);
-    char **details = g_new (char *, n_plugins + 1);
-
-    for (swalk = priv->missing_plugins; swalk; swalk = swalk->next) {
-      details[i++] = swalk->data;
-    }
-    details[i] = NULL;
-    g_slist_free (priv->missing_plugins);
-    priv->missing_plugins = NULL;
-    SWFDEC_INFO ("emitting missing plugins signal for %u plugins", n_plugins);
-    g_signal_emit (player, signals[MISSING_PLUGINS], 0, details);
-    g_strfreev (details);
-  }
-}
-
 static int
 swfdec_player_focus_sort (gconstpointer ca, gconstpointer cb)
 {
@@ -1832,10 +1829,12 @@ void
 swfdec_player_lock_soft (SwfdecPlayer *player)
 {
   g_return_if_fail (SWFDEC_IS_PLAYER (player));
+  g_assert (!swfdec_player_is_locked (player));
   g_assert (swfdec_rectangle_is_empty (&player->priv->invalid_extents));
 
   g_object_freeze_notify (G_OBJECT (player));
   g_timer_start (player->priv->runtime);
+  player->priv->locked = TRUE;
   SWFDEC_DEBUG ("LOCKED");
 }
 
@@ -1843,6 +1842,7 @@ gboolean
 swfdec_player_lock (SwfdecPlayer *player)
 {
   g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
+  g_assert (!swfdec_player_is_locked (player));
   g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[0]) == 0);
   g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[1]) == 0);
   g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[2]) == 0);
@@ -1945,6 +1945,7 @@ void
 swfdec_player_unlock_soft (SwfdecPlayer *player)
 {
   g_return_if_fail (SWFDEC_IS_PLAYER (player));
+  g_assert (swfdec_player_is_locked (player));
 
   SWFDEC_DEBUG ("UNLOCK");
   g_timer_stop (player->priv->runtime);
@@ -1953,6 +1954,7 @@ swfdec_player_unlock_soft (SwfdecPlayer *player)
   swfdec_player_update_focusrect (player);
   g_object_thaw_notify (G_OBJECT (player));
   swfdec_player_emit_signals (player);
+  player->priv->locked = FALSE;
 }
 
 void
@@ -2309,6 +2311,10 @@ swfdec_player_init (SwfdecPlayer *player)
   priv->stage_width = -1;
   priv->stage_height = -1;
   priv->has_focus = TRUE;
+
+  cairo_matrix_init_scale (&priv->stage_to_global, 
+      SWFDEC_TWIPS_SCALE_FACTOR, SWFDEC_TWIPS_SCALE_FACTOR);
+  priv->global_to_stage = priv->stage_to_global;
 }
 
 void
diff --git a/swfdec/swfdec_player_internal.h b/swfdec/swfdec_player_internal.h
index 5e354bf..4fe3dc4 100644
--- a/swfdec/swfdec_player_internal.h
+++ b/swfdec/swfdec_player_internal.h
@@ -62,6 +62,7 @@ struct _SwfdecPlayerPrivate
 {
   SwfdecPlayer *	player;			/* backlink */
 
+  gboolean		locked;			/* guard around swfdec_player_(un)lock */
   /* global properties */
   SwfdecSystem *	system;			/* our system properties */
   gboolean		initialized;		/* if width and height are set already */
@@ -165,6 +166,7 @@ gboolean	swfdec_player_lock		(SwfdecPlayer *		player);
 void		swfdec_player_lock_soft		(SwfdecPlayer *		player);
 void		swfdec_player_unlock		(SwfdecPlayer *		player);
 void		swfdec_player_unlock_soft	(SwfdecPlayer *		player);
+#define swfdec_player_is_locked(player) ((player)->priv->locked)
 void		swfdec_player_perform_actions	(SwfdecPlayer *		player);
 
 #define swfdec_player_root(player, data, mark_func) \
commit 38a8bfa0d9a6bb31842aa43793f97385e2841b86
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 11:23:23 2008 +0200

    remove checks that aren't needed anymore now that we always have at least one block

diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
index 641cdc6..2f0cff8 100644
--- a/swfdec/swfdec_text_layout.c
+++ b/swfdec/swfdec_text_layout.c
@@ -589,9 +589,6 @@ swfdec_text_layout_get_height (SwfdecTextLayout *layout)
   swfdec_text_layout_ensure (layout);
 
   iter = g_sequence_iter_prev (g_sequence_get_end_iter (layout->blocks));
-  if (g_sequence_iter_is_end (iter))
-    return 0;
-
   block = g_sequence_get (iter);
   return block->rect.y + block->rect.height;
 }
@@ -607,9 +604,6 @@ swfdec_text_layout_get_n_rows (SwfdecTextLayout *layout)
   swfdec_text_layout_ensure (layout);
 
   iter = g_sequence_iter_prev (g_sequence_get_end_iter (layout->blocks));
-  if (g_sequence_iter_is_end (iter))
-    return 0;
-
   block = g_sequence_get (iter);
   return block->row + pango_layout_get_line_count (block->layout);
 }
commit b1df96692e074363d025e62e1d3edace00b64c7e
Author: Benjamin Otte <otte at gnome.org>
Date:   Thu May 8 11:20:59 2008 +0200

    rework how an empty layout is handled and implement get_width/height()
    
    Empty layouts used to contain nothing. Now they contain a single block.
    Also implement functions for querying the width and height of a layout.

diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
index fbc5fef..641cdc6 100644
--- a/swfdec/swfdec_text_layout.c
+++ b/swfdec/swfdec_text_layout.c
@@ -280,7 +280,7 @@ swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *con
   // TODO: kerning, display
 
   string = swfdec_text_buffer_get_text (layout->text);
-  while (start != end) {
+  do {
     iter = swfdec_text_buffer_get_iter (layout->text, start);
     attr = swfdec_text_buffer_iter_get_attributes (layout->text, iter);
     new_block = end;
@@ -365,7 +365,7 @@ swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *con
     block->rect.height += pango_layout_get_spacing (block->layout) / PANGO_SCALE;
 
     last = block;
-  }
+  } while (start != end);
 
   return last;
 }
@@ -382,6 +382,7 @@ swfdec_text_layout_create (SwfdecTextLayout *layout)
   context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (map));
 
   p = string = swfdec_text_buffer_get_text (layout->text);
+  //g_print ("=====>\n %s\n<======\n", string);
   for (;;) {
     end = strpbrk (p, "\r\n");
     if (end == NULL) {
@@ -404,9 +405,6 @@ swfdec_text_layout_ensure (SwfdecTextLayout *layout)
   if (!g_sequence_iter_is_end (g_sequence_get_begin_iter (layout->blocks)))
     return;
 
-  if (swfdec_text_buffer_get_length (layout->text) == 0)
-    return;
-
   swfdec_text_layout_create (layout);
 }
 
@@ -433,6 +431,8 @@ swfdec_text_layout_dispose (GObject *object)
 {
   SwfdecTextLayout *layout = SWFDEC_TEXT_LAYOUT (object);
 
+  g_signal_handlers_disconnect_matched (layout->text, 
+	G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, layout);
   g_object_unref (layout->text);
   layout->text = NULL;
   g_sequence_free (layout->blocks);
@@ -470,6 +470,8 @@ swfdec_text_layout_new (SwfdecTextBuffer *buffer)
 
   layout = g_object_new (SWFDEC_TYPE_TEXT_LAYOUT, NULL);
   layout->text = g_object_ref (buffer);
+  g_signal_connect_swapped (buffer, "text-changed",
+      G_CALLBACK (swfdec_text_layout_invalidate), layout);
 
   return layout;
 }
@@ -483,8 +485,8 @@ swfdec_text_layout_set_wrap_width (SwfdecTextLayout *layout, int wrap_width)
   if (layout->wrap_width == wrap_width)
     return;
 
-  swfdec_text_layout_invalidate (layout);
   layout->wrap_width = wrap_width;
+  swfdec_text_layout_invalidate (layout);
 }
 
 int
@@ -511,8 +513,8 @@ swfdec_text_layout_set_password (SwfdecTextLayout *layout, gboolean password)
   if (layout->password == password)
     return;
 
-  swfdec_text_layout_invalidate (layout);
   layout->password = password;
+  swfdec_text_layout_invalidate (layout);
 }
 
 double
@@ -532,8 +534,66 @@ swfdec_text_layout_set_scale (SwfdecTextLayout *layout, double scale)
   if (layout->scale == scale)
     return;
 
-  swfdec_text_layout_invalidate (layout);
   layout->scale = scale;
+  swfdec_text_layout_invalidate (layout);
+}
+
+/**
+ * swfdec_text_layout_get_width:
+ * @layout: the layout
+ *
+ * Computes the width of the layout in pixels. Note that the width can still
+ * exceed the width set with swfdec_text_layout_set_wrap_width() if some
+ * words are too long. Computing the width takes a long time, so it might be
+ * useful to cache the value.
+ *
+ * Returns: The width in pixels
+ **/
+guint
+swfdec_text_layout_get_width (SwfdecTextLayout *layout)
+{
+  GSequenceIter *iter;
+  SwfdecTextBlock *block;
+  guint width = 0;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), 0);
+
+  swfdec_text_layout_ensure (layout);
+
+  for (iter = g_sequence_get_begin_iter (layout->blocks);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter)) {
+    block = g_sequence_get (iter);
+    width = MAX (width, (guint) block->rect.x + block->rect.width);
+  }
+
+  return width;
+}
+
+/**
+ * swfdec_text_layout_get_height:
+ * @layout: the layout
+ *
+ * Computes the height of the layout in pixels. This is a fast operation.
+ *
+ * Returns: The height of the layout in pixels
+ **/
+guint
+swfdec_text_layout_get_height (SwfdecTextLayout *layout)
+{
+  GSequenceIter *iter;
+  SwfdecTextBlock *block;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), 0);
+
+  swfdec_text_layout_ensure (layout);
+
+  iter = g_sequence_iter_prev (g_sequence_get_end_iter (layout->blocks));
+  if (g_sequence_iter_is_end (iter))
+    return 0;
+
+  block = g_sequence_get (iter);
+  return block->rect.y + block->rect.height;
 }
 
 guint
diff --git a/swfdec/swfdec_text_layout.h b/swfdec/swfdec_text_layout.h
index fe75923..0457901 100644
--- a/swfdec/swfdec_text_layout.h
+++ b/swfdec/swfdec_text_layout.h
@@ -69,6 +69,8 @@ gboolean		swfdec_text_layout_get_password		(SwfdecTextLayout *	layout);
 void			swfdec_text_layout_set_scale		(SwfdecTextLayout *	layout,
 								 double			scale);
 double			swfdec_text_layout_get_scale		(SwfdecTextLayout *	layout);
+guint			swfdec_text_layout_get_width		(SwfdecTextLayout *	layout);
+guint			swfdec_text_layout_get_height		(SwfdecTextLayout *	layout);
 guint			swfdec_text_layout_get_n_rows		(SwfdecTextLayout *	layout);
 
 void			swfdec_text_layout_render		(SwfdecTextLayout *	layout,
commit 54a4ae2a24e30e934dd5e13bc559ba1294b5bc44
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 21:46:11 2008 +0200

    add a simple word wrapping test

diff --git a/test/image/Makefile.am b/test/image/Makefile.am
index 1b6f0a6..7455a0b 100644
--- a/test/image/Makefile.am
+++ b/test/image/Makefile.am
@@ -426,6 +426,14 @@ EXTRA_DIST = \
 	text-field-scale-parents-7.swf.png \
 	text-field-scale-parents-8.swf \
 	text-field-scale-parents-8.swf.png \
-	text-field-scale-parents.as
-
+	text-field-scale-parents.as \
+	text-field-wordWrap-5.swf \
+	text-field-wordWrap-5.swf.png \
+	text-field-wordWrap-6.swf \
+	text-field-wordWrap-6.swf.png \
+	text-field-wordWrap-7.swf \
+	text-field-wordWrap-7.swf.png \
+	text-field-wordWrap-8.swf \
+	text-field-wordWrap-8.swf.png \
+	text-field-wordWrap.as
 
diff --git a/test/image/text-field-wordWrap-5.swf b/test/image/text-field-wordWrap-5.swf
new file mode 100644
index 0000000..ed7fe23
Binary files /dev/null and b/test/image/text-field-wordWrap-5.swf differ
diff --git a/test/image/text-field-wordWrap-5.swf.png b/test/image/text-field-wordWrap-5.swf.png
new file mode 100644
index 0000000..a1d8f66
Binary files /dev/null and b/test/image/text-field-wordWrap-5.swf.png differ
diff --git a/test/image/text-field-wordWrap-6.swf b/test/image/text-field-wordWrap-6.swf
new file mode 100644
index 0000000..40308dd
Binary files /dev/null and b/test/image/text-field-wordWrap-6.swf differ
diff --git a/test/image/text-field-wordWrap-6.swf.png b/test/image/text-field-wordWrap-6.swf.png
new file mode 100644
index 0000000..3803380
Binary files /dev/null and b/test/image/text-field-wordWrap-6.swf.png differ
diff --git a/test/image/text-field-wordWrap-7.swf b/test/image/text-field-wordWrap-7.swf
new file mode 100644
index 0000000..db714b8
Binary files /dev/null and b/test/image/text-field-wordWrap-7.swf differ
diff --git a/test/image/text-field-wordWrap-7.swf.png b/test/image/text-field-wordWrap-7.swf.png
new file mode 100644
index 0000000..22033c9
Binary files /dev/null and b/test/image/text-field-wordWrap-7.swf.png differ
diff --git a/test/image/text-field-wordWrap-8.swf b/test/image/text-field-wordWrap-8.swf
new file mode 100644
index 0000000..50b9b44
Binary files /dev/null and b/test/image/text-field-wordWrap-8.swf differ
diff --git a/test/image/text-field-wordWrap-8.swf.png b/test/image/text-field-wordWrap-8.swf.png
new file mode 100644
index 0000000..43b09b6
Binary files /dev/null and b/test/image/text-field-wordWrap-8.swf.png differ
diff --git a/test/image/text-field-wordWrap.as b/test/image/text-field-wordWrap.as
new file mode 100644
index 0000000..7fd4e82
--- /dev/null
+++ b/test/image/text-field-wordWrap.as
@@ -0,0 +1,24 @@
+// makeswf -v 7 -s 200x150 -r 1 -o text-field-wordWrap.swf text-field-wordWrap.as
+
+function create (depth, x, y) {
+  var t = createTextField ("t" + depth, depth, x, y, 50, 40);
+  var t = this["t" + depth];
+  t.border = true;
+  var tf = new TextFormat ();
+  tf.font = "Bitstream Vera Sans";
+  //tf.leading = -5;
+  t.setNewTextFormat (tf);
+  t.text = "Hello,\nfinest World";
+};
+
+create (0, 25, 25);
+create (1, 100.5, 25);
+t1.wordWrap = true;
+create (2, 25, 75);
+t2._y += 0.4;
+t2.multiline = true;
+create (3, 100.5, 75);
+t3._y += 0.5;
+t3.wordWrap = true;
+t3.multiline = true;
+
commit d5199295685e2f10d2bd4d80c6c68b8b14e585f2
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 21:40:16 2008 +0200

    implement word wrap

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 8e2d773..8c60cf3 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -357,6 +357,10 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   text->stage_rect.width = round (x) - text->stage_rect.x;
   text->stage_rect.height = round (y) - text->stage_rect.y;
   swfdec_text_layout_set_scale (text->layout, matrix.yy * SWFDEC_TWIPS_SCALE_FACTOR);
+  if (text->word_wrap) {
+    swfdec_text_layout_set_wrap_width (text->layout, text->stage_rect.width - 
+	BORDER_LEFT - BORDER_RIGHT);
+  }
 }
 
 static void
diff --git a/swfdec/swfdec_text_field_movie_as.c b/swfdec/swfdec_text_field_movie_as.c
index 2988d90..05a1d5b 100644
--- a/swfdec/swfdec_text_field_movie_as.c
+++ b/swfdec/swfdec_text_field_movie_as.c
@@ -829,6 +829,12 @@ swfdec_text_field_movie_set_wordWrap (SwfdecAsContext *cx,
 
   if (text->word_wrap != value) {
     text->word_wrap = value;
+    if (text->word_wrap) {
+      /* FIXME: find a proper way to use BORDER_LEFT and BORDER_RIGHT here */
+      swfdec_text_layout_set_wrap_width (text->layout, text->stage_rect.width - 4);
+    } else {
+      swfdec_text_layout_set_wrap_width (text->layout, -1);
+    }
     swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
     swfdec_text_field_movie_auto_size (text);
     // special case: don't set scrolling
commit 9e92e2bb2f4259e0e05ce8545e862c7e1be8f2e3
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 21:40:01 2008 +0200

    clip the right region when rendering

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 5f0e044..8e2d773 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -126,9 +126,11 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
   if (swfdec_text_buffer_get_length (text->text) == 0)
     return;
   /* render the layout */
-  cairo_rectangle (cr, text->stage_rect.x, text->stage_rect.y, text->stage_rect.width, text->stage_rect.height);
-  cairo_clip (cr);
   cairo_translate (cr, text->stage_rect.x + BORDER_LEFT, text->stage_rect.y + BORDER_TOP);
+  cairo_rectangle (cr, 0, 0, 
+      text->stage_rect.width - BORDER_LEFT - BORDER_RIGHT, 
+      text->stage_rect.height - BORDER_TOP - BORDER_BOTTOM);
+  cairo_clip (cr);
   swfdec_text_layout_render (text->layout, cr, ctrans,
       0, text->stage_rect.height - BORDER_TOP + BORDER_BOTTOM, &text->stage_rect);
 }
commit b95ac67f62d415b7bfce56ebefccc9fd98fc9b5f
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 21:17:07 2008 +0200

    scale the width correctly

diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
index 334cc4d..fbc5fef 100644
--- a/swfdec/swfdec_text_layout.c
+++ b/swfdec/swfdec_text_layout.c
@@ -316,7 +316,7 @@ swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *con
     }
     swfdec_text_layout_apply_line_attributes (block, attr);
     if (layout->wrap_width != -1)
-      pango_layout_set_width (block->layout, block->rect.width);
+      pango_layout_set_width (block->layout, block->rect.width * PANGO_SCALE);
 
     iter = swfdec_text_buffer_get_iter (layout->text, start);
     attr = swfdec_text_buffer_iter_get_attributes (layout->text, iter);
commit a1cb51dd160224793d75841b479dbf1082230b0a
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 21:05:34 2008 +0200

    use scaling
    
    and get rid of all debugging prints

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 9c6c639..5f0e044 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -92,9 +92,6 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
   SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (movie);
   SwfdecColor color;
 
-  g_print ("render %d %d   %d %d\n",
-      text->stage_rect.x, text->stage_rect.y,
-      text->stage_rect.width, text->stage_rect.height);
   /* textfields don't mask */
   if (swfdec_color_transform_is_mask (ctrans) ||
       swfdec_rectangle_is_empty (&text->stage_rect))
@@ -333,7 +330,6 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   swfdec_movie_local_to_global_matrix (movie, &matrix);
   cairo_matrix_multiply (&matrix, &matrix,
       &SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context)->priv->global_to_stage);
-  g_print ("%g %g %g %g\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy);
   if (matrix.xy != 0.0 || matrix.yx != 0.0 ||
       matrix.xx <= 0.0 || matrix.yy <= 0.0) {
     swfdec_rectangle_init_empty (&text->stage_rect);
@@ -343,9 +339,7 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   translate = matrix;
   x = text->extents.x0;
   y = text->extents.y0;
-  g_print ("%g %g\n", x, y);
   cairo_matrix_transform_point (&matrix, &x, &y);
-  g_print ("%g %g\n", x, y);
   cairo_matrix_init_translate (&translate, round (x) - x, round (y) - y);
   cairo_matrix_multiply (&matrix, &matrix, &translate);
   
@@ -360,9 +354,7 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   /* FIXME: floor, ceil or round? */
   text->stage_rect.width = round (x) - text->stage_rect.x;
   text->stage_rect.height = round (y) - text->stage_rect.y;
-  g_print ("%d %d   %d %d\n",
-      text->stage_rect.x, text->stage_rect.y,
-      text->stage_rect.width, text->stage_rect.height);
+  swfdec_text_layout_set_scale (text->layout, matrix.yy * SWFDEC_TWIPS_SCALE_FACTOR);
 }
 
 static void
commit b6a9681de023c6b87496512eb10851d3ae2e3b32
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 21:04:16 2008 +0200

    implement scaling the layout

diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
index 37683fe..334cc4d 100644
--- a/swfdec/swfdec_text_layout.c
+++ b/swfdec/swfdec_text_layout.c
@@ -40,44 +40,46 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0, };
 
 static PangoAttribute *
-swfdec_text_attribute_create_bold (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_bold (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
   return pango_attr_weight_new (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
 }
 
 static PangoAttribute *
-swfdec_text_attribute_create_color (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_color (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
   return pango_attr_foreground_new (SWFDEC_COLOR_R (attr->color) * 255,
       SWFDEC_COLOR_G (attr->color) * 255, SWFDEC_COLOR_B (attr->color) * 255);
 }
 
 static PangoAttribute *
-swfdec_text_attribute_create_font (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_font (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
   return pango_attr_family_new (attr->font);
 }
 
 static PangoAttribute *
-swfdec_text_attribute_create_italic (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_italic (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
   return pango_attr_style_new (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
 }
 
 static PangoAttribute *
-swfdec_text_attribute_create_letter_spacing (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_letter_spacing (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
   return pango_attr_letter_spacing_new (attr->letter_spacing * PANGO_SCALE);
 }
 
 static PangoAttribute *
-swfdec_text_attribute_create_size (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_size (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
-  return pango_attr_size_new_absolute (MAX (attr->size, 1) * PANGO_SCALE);
+  guint size = attr->size * layout->scale;
+  size = MAX (size, 1);
+  return pango_attr_size_new_absolute (size * PANGO_SCALE);
 }
 
 static PangoAttribute *
-swfdec_text_attribute_create_underline (const SwfdecTextAttributes *attr)
+swfdec_text_attribute_create_underline (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr)
 {
   return pango_attr_underline_new (attr->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE);
 }
@@ -93,7 +95,7 @@ typedef enum {
 
 struct {
   FormatApplication   application;
-  PangoAttribute *    (* create_attribute) (const SwfdecTextAttributes *attr);
+  PangoAttribute *    (* create_attribute) (SwfdecTextLayout *layout, const SwfdecTextAttributes *attr);
 } format_table[] = {
   /* unknown or unhandled properties */
   [SWFDEC_TEXT_ATTRIBUTE_DISPLAY] = { FORMAT_APPLY_UNKNOWN, NULL },
@@ -248,15 +250,15 @@ swfdec_text_layout_apply_line_attributes (SwfdecTextBlock *block,
 }
 
 static void
-swfdec_text_layout_apply_attributes (PangoAttrList *list, const SwfdecTextAttributes *attr,
-    guint start, guint end)
+swfdec_text_layout_apply_attributes (SwfdecTextLayout *layout, PangoAttrList *list,
+    const SwfdecTextAttributes *attr, guint start, guint end)
 {
   PangoAttribute *attribute;
   guint i;
 
   for (i = 0; i < G_N_ELEMENTS (format_table); i++) {
     if (format_table[i].create_attribute) {
-      attribute = format_table[i].create_attribute (attr);
+      attribute = format_table[i].create_attribute (layout, attr);
       attribute->start_index = start;
       attribute->end_index = end;
       pango_attr_list_change (list, attribute);
@@ -328,7 +330,7 @@ swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *con
 	swfdec_text_buffer_get_length (layout->text);
       start_next = MIN (start_next, end);
       
-      swfdec_text_layout_apply_attributes (list, attr, start - block->start, start_next - block->start);
+      swfdec_text_layout_apply_attributes (layout, list, attr, start - block->start, start_next - block->start);
 
       if (attr_next && new_block > start_next &&
 	  (swfdec_text_attributes_diff (attr, attr_next) & SWFDEC_TEXT_ATTRIBUTES_MASK_NEW_BLOCK))
@@ -411,6 +413,9 @@ swfdec_text_layout_ensure (SwfdecTextLayout *layout)
 static void
 swfdec_text_layout_invalidate (SwfdecTextLayout *layout)
 {
+  if (g_sequence_iter_is_end (g_sequence_get_begin_iter (layout->blocks)))
+    return;
+
   g_sequence_remove_range (g_sequence_get_begin_iter (layout->blocks),
     g_sequence_get_end_iter (layout->blocks));
   g_signal_emit (layout, signals[CHANGED], 0);
@@ -452,6 +457,7 @@ static void
 swfdec_text_layout_init (SwfdecTextLayout *layout)
 {
   layout->wrap_width = -1;
+  layout->scale = 1.0;
   layout->blocks = g_sequence_new (swfdec_text_block_free);
 }
 
@@ -489,6 +495,14 @@ swfdec_text_layout_get_wrap_width (SwfdecTextLayout *layout)
   return layout->wrap_width;
 }
 
+gboolean
+swfdec_text_layout_get_password (SwfdecTextLayout *layout)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), FALSE);
+
+  return layout->password;
+}
+
 void
 swfdec_text_layout_set_password (SwfdecTextLayout *layout, gboolean password)
 {
@@ -501,6 +515,27 @@ swfdec_text_layout_set_password (SwfdecTextLayout *layout, gboolean password)
   layout->password = password;
 }
 
+double
+swfdec_text_layout_get_scale (SwfdecTextLayout *layout)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), 1.0);
+
+  return layout->scale;
+}
+
+void
+swfdec_text_layout_set_scale (SwfdecTextLayout *layout, double scale)
+{
+  g_return_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout));
+  g_return_if_fail (scale > 0);
+
+  if (layout->scale == scale)
+    return;
+
+  swfdec_text_layout_invalidate (layout);
+  layout->scale = scale;
+}
+
 guint
 swfdec_text_layout_get_n_rows (SwfdecTextLayout *layout)
 {
@@ -519,14 +554,6 @@ swfdec_text_layout_get_n_rows (SwfdecTextLayout *layout)
   return block->row + pango_layout_get_line_count (block->layout);
 }
 
-gboolean
-swfdec_text_layout_get_password (SwfdecTextLayout *layout)
-{
-  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), FALSE);
-
-  return layout->password;
-}
-
 static GSequenceIter *
 swfdec_text_layout_find_row (SwfdecTextLayout *layout, guint row)
 {
diff --git a/swfdec/swfdec_text_layout.h b/swfdec/swfdec_text_layout.h
index cd68999..fe75923 100644
--- a/swfdec/swfdec_text_layout.h
+++ b/swfdec/swfdec_text_layout.h
@@ -41,10 +41,11 @@ struct _SwfdecTextLayout
 {
   GObject		object;
 
-  /* constant data */
   SwfdecTextBuffer *	text;		/* the text we render */
+  /* properties */
   int			wrap_width;	/* maximum width to use */
   gboolean		password;	/* TRUE if the text should be displayed as asterisks */
+  double		scale;		/* scale factor in use */
 
   /* layout data */
   GSequence *		blocks;		/* ordered list of blocks */
@@ -65,6 +66,9 @@ int			swfdec_text_layout_get_wrap_width	(SwfdecTextLayout *	layout);
 void			swfdec_text_layout_set_password		(SwfdecTextLayout *	layout,
 								 gboolean		password);
 gboolean		swfdec_text_layout_get_password		(SwfdecTextLayout *	layout);
+void			swfdec_text_layout_set_scale		(SwfdecTextLayout *	layout,
+								 double			scale);
+double			swfdec_text_layout_get_scale		(SwfdecTextLayout *	layout);
 guint			swfdec_text_layout_get_n_rows		(SwfdecTextLayout *	layout);
 
 void			swfdec_text_layout_render		(SwfdecTextLayout *	layout,
commit 4f6a2d744c9b6ab4a3c7fb07be9180dc3fd97223
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 18:43:41 2008 +0200

    add a test that enusres negaitve scaling causes TextFields to not be scaled

diff --git a/test/image/Makefile.am b/test/image/Makefile.am
index 599bad7..1b6f0a6 100644
--- a/test/image/Makefile.am
+++ b/test/image/Makefile.am
@@ -409,6 +409,15 @@ EXTRA_DIST = \
 	text-field-empty-7.swf.png \
 	text-field-empty-8.swf \
 	text-field-empty-8.swf.png \
+	text-field-scale-negative-5.swf \
+	text-field-scale-negative-5.swf.png \
+	text-field-scale-negative-6.swf \
+	text-field-scale-negative-6.swf.png \
+	text-field-scale-negative-7.swf \
+	text-field-scale-negative-7.swf.png \
+	text-field-scale-negative-8.swf \
+	text-field-scale-negative-8.swf.png \
+	text-field-scale-negative.as \
 	text-field-scale-parents-5.swf \
 	text-field-scale-parents-5.swf.png \
 	text-field-scale-parents-6.swf \
diff --git a/test/image/text-field-scale-negative-5.swf b/test/image/text-field-scale-negative-5.swf
new file mode 100644
index 0000000..0df8aaf
Binary files /dev/null and b/test/image/text-field-scale-negative-5.swf differ
diff --git a/test/image/text-field-scale-negative-5.swf.png b/test/image/text-field-scale-negative-5.swf.png
new file mode 100644
index 0000000..916bba0
Binary files /dev/null and b/test/image/text-field-scale-negative-5.swf.png differ
diff --git a/test/image/text-field-scale-negative-6.swf b/test/image/text-field-scale-negative-6.swf
new file mode 100644
index 0000000..c881d62
Binary files /dev/null and b/test/image/text-field-scale-negative-6.swf differ
diff --git a/test/image/text-field-scale-negative-6.swf.png b/test/image/text-field-scale-negative-6.swf.png
new file mode 100644
index 0000000..b0845e4
Binary files /dev/null and b/test/image/text-field-scale-negative-6.swf.png differ
diff --git a/test/image/text-field-scale-negative-7.swf b/test/image/text-field-scale-negative-7.swf
new file mode 100644
index 0000000..a7a7d0a
Binary files /dev/null and b/test/image/text-field-scale-negative-7.swf differ
diff --git a/test/image/text-field-scale-negative-7.swf.png b/test/image/text-field-scale-negative-7.swf.png
new file mode 100644
index 0000000..fb13c45
Binary files /dev/null and b/test/image/text-field-scale-negative-7.swf.png differ
diff --git a/test/image/text-field-scale-negative-8.swf b/test/image/text-field-scale-negative-8.swf
new file mode 100644
index 0000000..c162cb5
Binary files /dev/null and b/test/image/text-field-scale-negative-8.swf differ
diff --git a/test/image/text-field-scale-negative-8.swf.png b/test/image/text-field-scale-negative-8.swf.png
new file mode 100644
index 0000000..12df204
Binary files /dev/null and b/test/image/text-field-scale-negative-8.swf.png differ
diff --git a/test/image/text-field-scale-negative.as b/test/image/text-field-scale-negative.as
new file mode 100644
index 0000000..08037d4
--- /dev/null
+++ b/test/image/text-field-scale-negative.as
@@ -0,0 +1,22 @@
+// makeswf -v 7 -s 200x150 -r 1 -o text-field-scale-negative.swf text-field-scale-negative.as
+
+function create (depth, x, y) {
+  var t = createTextField ("t" + depth, depth, x, y, 50, 40);
+  var t = this["t" + depth];
+  t.background = true;
+  t.backgroundColor = 0xFF;
+};
+
+create (0, 25, 25);
+create (1, 100.5, 25);
+t1._xscale = -100;
+t1._x += 50;
+create (2, 25, 75.4);
+t2._yscale = -100;
+t2._y += 40;
+create (3, 100.5, 75.5);
+t3._xscale = -100;
+t3._x += 50;
+t3._yscale = -100;
+t3._y += 40;
+
commit b071374a9ae70e3faf7d2b20aa41b22b5d717a00
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 18:29:26 2008 +0200

    add test to check if scaling works

diff --git a/test/image/Makefile.am b/test/image/Makefile.am
index 375d9d7..599bad7 100644
--- a/test/image/Makefile.am
+++ b/test/image/Makefile.am
@@ -408,5 +408,15 @@ EXTRA_DIST = \
 	text-field-empty-7.swf \
 	text-field-empty-7.swf.png \
 	text-field-empty-8.swf \
-	text-field-empty-8.swf.png
+	text-field-empty-8.swf.png \
+	text-field-scale-parents-5.swf \
+	text-field-scale-parents-5.swf.png \
+	text-field-scale-parents-6.swf \
+	text-field-scale-parents-6.swf.png \
+	text-field-scale-parents-7.swf \
+	text-field-scale-parents-7.swf.png \
+	text-field-scale-parents-8.swf \
+	text-field-scale-parents-8.swf.png \
+	text-field-scale-parents.as
+
 
diff --git a/test/image/text-field-scale-parents-5.swf b/test/image/text-field-scale-parents-5.swf
new file mode 100644
index 0000000..92e5382
Binary files /dev/null and b/test/image/text-field-scale-parents-5.swf differ
diff --git a/test/image/text-field-scale-parents-5.swf.png b/test/image/text-field-scale-parents-5.swf.png
new file mode 100644
index 0000000..5f3499f
Binary files /dev/null and b/test/image/text-field-scale-parents-5.swf.png differ
diff --git a/test/image/text-field-scale-parents-6.swf b/test/image/text-field-scale-parents-6.swf
new file mode 100644
index 0000000..a8f5453
Binary files /dev/null and b/test/image/text-field-scale-parents-6.swf differ
diff --git a/test/image/text-field-scale-parents-6.swf.png b/test/image/text-field-scale-parents-6.swf.png
new file mode 100644
index 0000000..e6c7482
Binary files /dev/null and b/test/image/text-field-scale-parents-6.swf.png differ
diff --git a/test/image/text-field-scale-parents-7.swf b/test/image/text-field-scale-parents-7.swf
new file mode 100644
index 0000000..4b2368e
Binary files /dev/null and b/test/image/text-field-scale-parents-7.swf differ
diff --git a/test/image/text-field-scale-parents-7.swf.png b/test/image/text-field-scale-parents-7.swf.png
new file mode 100644
index 0000000..fe8aaff
Binary files /dev/null and b/test/image/text-field-scale-parents-7.swf.png differ
diff --git a/test/image/text-field-scale-parents-8.swf b/test/image/text-field-scale-parents-8.swf
new file mode 100644
index 0000000..3c3bd8b
Binary files /dev/null and b/test/image/text-field-scale-parents-8.swf differ
diff --git a/test/image/text-field-scale-parents-8.swf.png b/test/image/text-field-scale-parents-8.swf.png
new file mode 100644
index 0000000..d2657e0
Binary files /dev/null and b/test/image/text-field-scale-parents-8.swf.png differ
diff --git a/test/image/text-field-scale-parents.as b/test/image/text-field-scale-parents.as
new file mode 100644
index 0000000..c86d3d0
--- /dev/null
+++ b/test/image/text-field-scale-parents.as
@@ -0,0 +1,14 @@
+// makeswf -v 7 -s 200x150 -r 1 -o text-field-scale-parents.swf text-field-scale-parents.as
+
+createEmptyMovieClip ("a", 0);
+a._xscale = -100;
+a.createEmptyMovieClip ("a", 0);
+a.a._xscale = -100;
+a.a.createTextField ("t", 0, 0, 0, 50, 40);
+a.a.t.background = true;
+a.a.t.backgroundColor = 0xFF;
+
+a.createTextField ("t", 1, -100, 0, 50, 40);
+a.t._xscale = -200;
+a.t.background = true;
+a.t.backgroundColor = 0xFF00;
commit bd86e710d3721fb03cb35020e0d171dcf3cca0fa
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 18:27:05 2008 +0200

    fixes to computing the stage_rect correctly

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 356c153..9c6c639 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -333,6 +333,7 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   swfdec_movie_local_to_global_matrix (movie, &matrix);
   cairo_matrix_multiply (&matrix, &matrix,
       &SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context)->priv->global_to_stage);
+  g_print ("%g %g %g %g\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy);
   if (matrix.xy != 0.0 || matrix.yx != 0.0 ||
       matrix.xx <= 0.0 || matrix.yy <= 0.0) {
     swfdec_rectangle_init_empty (&text->stage_rect);
@@ -340,21 +341,21 @@ swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
   }
 
   translate = matrix;
-  x = movie->original_extents.x0;
-  y = movie->original_extents.y0;
+  x = text->extents.x0;
+  y = text->extents.y0;
   g_print ("%g %g\n", x, y);
   cairo_matrix_transform_point (&matrix, &x, &y);
   g_print ("%g %g\n", x, y);
   cairo_matrix_init_translate (&translate, round (x) - x, round (y) - y);
   cairo_matrix_multiply (&matrix, &matrix, &translate);
   
-  x = movie->original_extents.x0;
-  y = movie->original_extents.y0;
+  x = text->extents.x0;
+  y = text->extents.y0;
   cairo_matrix_transform_point (&matrix, &x, &y);
   text->stage_rect.x = x;
   text->stage_rect.y = y;
-  x = movie->original_extents.x1;
-  y = movie->original_extents.y1;
+  x = text->extents.x1;
+  y = text->extents.y1;
   cairo_matrix_transform_point (&matrix, &x, &y);
   /* FIXME: floor, ceil or round? */
   text->stage_rect.width = round (x) - text->stage_rect.x;
@@ -433,6 +434,7 @@ swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
 	G_CALLBACK (swfdec_text_field_movie_update_area), movie);
     parent = parent->parent;
   }
+  swfdec_text_field_movie_update_area (text);
 }
 
 static void
commit 57207ab58d9f06cf00ab7f422a09c880acbef45d
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 18:08:03 2008 +0200

    rework the text field rendering, part 1
    
    Nothing works atm, but I thought I get this in before I lose it.
    The big change is using a SwfdecTextLayout object that renders in native
    coordinates and making SwfdecTextFieldMovie the translation for it.

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index c91612f..4808c72 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -153,6 +153,7 @@ libswfdec_source_files = \
 	swfdec_text_field_movie_as.c \
 	swfdec_text_field_movie_html.c \
 	swfdec_text_format.c \
+	swfdec_text_layout.c \
 	swfdec_text_renderer.c \
 	swfdec_text_snapshot.c \
 	swfdec_transform.c \
@@ -298,6 +299,7 @@ noinst_HEADERS = \
 	swfdec_text_attributes.h \
 	swfdec_text_buffer.h \
 	swfdec_text_format.h \
+	swfdec_text_layout.h \
 	swfdec_types.h \
 	swfdec_utils.h \
 	swfdec_video.h \
diff --git a/swfdec/swfdec_actor.h b/swfdec/swfdec_actor.h
index 5c30bfd..c299769 100644
--- a/swfdec/swfdec_actor.h
+++ b/swfdec/swfdec_actor.h
@@ -43,12 +43,16 @@ struct _SwfdecActor
   SwfdecEventList *	events;			/* events queued on this movie */
 
   SwfdecFlashBool	focusrect;		/* if we should draw a focus rectangle or not */
+  guint			needs_matrix;		/* number of movies contained that need matrix updates */
 };
 
 struct _SwfdecActorClass
 {
   SwfdecMovieClass    	movie_class;
 
+  /* matrix updates go here */
+  void			(* update_matrix)	(SwfdecActor *		actor);
+
   /* iterating */
   void			(* iterate_start)     	(SwfdecActor *		actor);
   gboolean		(* iterate_end)		(SwfdecActor *		actor);
diff --git a/swfdec/swfdec_movie.c b/swfdec/swfdec_movie.c
index 48a27d7..e4d1ed4 100644
--- a/swfdec/swfdec_movie.c
+++ b/swfdec/swfdec_movie.c
@@ -55,6 +55,13 @@ enum {
   PROP_DEPTH
 };
 
+enum {
+  MATRIX_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
 G_DEFINE_ABSTRACT_TYPE (SwfdecMovie, swfdec_movie, SWFDEC_TYPE_AS_OBJECT)
 
 static void
@@ -248,6 +255,8 @@ swfdec_movie_end_update_matrix (SwfdecMovie *movie)
     cairo_matrix_rotate (&movie->matrix, d * G_PI / 180);
   }
   swfdec_matrix_ensure_invertible (&movie->matrix, &movie->inverse_matrix);
+
+  g_signal_emit (movie, signals[MATRIX_CHANGED], 0);
 }
 
 static void
@@ -1288,6 +1297,10 @@ swfdec_movie_class_init (SwfdecMovieClass * movie_class)
   asobject_class->foreach = swfdec_movie_foreach_variable;
   asobject_class->debug = swfdec_movie_get_debug;
 
+  signals[MATRIX_CHANGED] = g_signal_new ("matrix-changed", G_TYPE_FROM_CLASS (movie_class),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+      G_TYPE_NONE, 0);
+
   g_object_class_install_property (object_class, PROP_DEPTH,
       g_param_spec_int ("depth", "depth", "z order inside the parent",
 	  G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
diff --git a/swfdec/swfdec_player.c b/swfdec/swfdec_player.c
index 78f3ed0..5b3ea00 100644
--- a/swfdec/swfdec_player.c
+++ b/swfdec/swfdec_player.c
@@ -816,6 +816,7 @@ swfdec_player_update_scale (SwfdecPlayer *player)
   SwfdecPlayerPrivate *priv = player->priv;
   int width, height;
   double scale_x, scale_y;
+  GList *walk;
 
   if (priv->fullscreen) {
     priv->stage.width = priv->system->screen_width;
@@ -889,6 +890,10 @@ swfdec_player_update_scale (SwfdecPlayer *player)
   priv->invalid.x1 = priv->stage_width;
   priv->invalid.y1 = priv->stage_height;
 #endif
+  /* FIXME: notify textfields more gentle about the update */
+  for (walk = priv->roots; walk; walk = walk->next) {
+    g_signal_emit_by_name (walk->data, "matrix-changed");
+  }
 }
 
 static void
diff --git a/swfdec/swfdec_text_attributes.h b/swfdec/swfdec_text_attributes.h
index 1736f4a..b1417f0 100644
--- a/swfdec/swfdec_text_attributes.h
+++ b/swfdec/swfdec_text_attributes.h
@@ -90,7 +90,7 @@ struct _SwfdecTextAttributes {
   gboolean		kerning;
   int			leading;
   int			left_margin;
-  double		letter_spacing; // number or null
+  double		letter_spacing;
   int			right_margin;
   int			size;
   guint *		tab_stops;
diff --git a/swfdec/swfdec_text_field.c b/swfdec/swfdec_text_field.c
index 9d6285a..8eaa956 100644
--- a/swfdec/swfdec_text_field.c
+++ b/swfdec/swfdec_text_field.c
@@ -50,7 +50,7 @@ swfdec_text_field_create_movie (SwfdecGraphic *graphic, gsize *size)
 
   ret->html = text->html;
   ret->editable = text->editable;
-  ret->password = text->password;
+  swfdec_text_layout_set_password (ret->layout, text->password);
   ret->max_chars = text->max_chars;
   ret->selectable = text->selectable;
   ret->embed_fonts = text->embed_fonts;
diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 62bd5a4..356c153 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -39,8 +39,10 @@
 
 G_DEFINE_TYPE (SwfdecTextFieldMovie, swfdec_text_field_movie, SWFDEC_TYPE_ACTOR)
 
-#define EXTRA_MARGIN 2
-#define BULLET_MARGIN 36
+#define BORDER_TOP 1
+#define BORDER_LEFT 2
+#define BORDER_RIGHT 2
+#define BORDER_BOTTOM 1
 
 /*** VFUNCS ***/
 
@@ -72,737 +74,7 @@ swfdec_text_field_movie_invalidate (SwfdecMovie *movie, const cairo_matrix_t *ma
       SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context), &rect);
 }
 
-static void
-swfdec_text_field_movie_ensure_asterisks (SwfdecTextFieldMovie *text,
-    guint length)
-{
-  g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-
-  if (text->asterisks_length >= length)
-    return;
-
-  if (text->asterisks != NULL)
-    g_free (text->asterisks);
-
-  text->asterisks = g_malloc (length + 1);
-  memset (text->asterisks, '*', length);
-  text->asterisks[length] = 0;
-  text->asterisks_length = length;
-}
-
-static void
-swfdec_text_paragraph_add_attribute (SwfdecParagraph *paragraph,
-    PangoAttribute *attr)
-{
-  paragraph->attrs = g_slist_prepend (paragraph->attrs, attr);
-}
-
-static void
-swfdec_text_paragraph_add_block (SwfdecParagraph *paragraph, int index_,
-    const SwfdecTextAttributes *attr)
-{
-  guint i;
-  SwfdecBlock *block;
-
-  block = g_new0 (SwfdecBlock, 1);
-
-  block->index_ = index_;
-
-  switch (attr->align) {
-    case SWFDEC_TEXT_ALIGN_LEFT:
-      block->align = PANGO_ALIGN_LEFT;
-      block->justify = FALSE;
-      break;
-    case SWFDEC_TEXT_ALIGN_RIGHT:
-      block->align = PANGO_ALIGN_RIGHT;
-      block->justify = FALSE;
-      break;
-    case SWFDEC_TEXT_ALIGN_CENTER:
-      block->align = PANGO_ALIGN_CENTER;
-      block->justify = FALSE;
-      break;
-    case SWFDEC_TEXT_ALIGN_JUSTIFY:
-      block->align = PANGO_ALIGN_LEFT;
-      block->justify = TRUE;
-      break;
-    default:
-      g_assert_not_reached ();
-  }
-  block->leading = attr->leading * 20 * PANGO_SCALE;
-  block->block_indent = attr->block_indent * 20;
-  block->left_margin = attr->left_margin * 20;
-  block->right_margin = attr->right_margin * 20;
-
-  if (attr->n_tab_stops != 0) {
-    block->tab_stops = pango_tab_array_new (attr->n_tab_stops, TRUE);
-    for (i = 0; i < attr->n_tab_stops; i++) {
-      pango_tab_array_set_tab (block->tab_stops, i, PANGO_TAB_LEFT,
-	  attr->tab_stops[i] * 20);
-    }
-  } else {
-    block->tab_stops = NULL;
-  }
-
-  paragraph->blocks = g_slist_prepend (paragraph->blocks, block);
-}
-
-static void
-swfdec_text_field_movie_generate_paragraph (SwfdecTextFieldMovie *text,
-    SwfdecParagraph *paragraph, guint start_index, guint length)
-{
-  guint index_;
-  SwfdecTextBufferIter *iter;
-  PangoAttribute *attr_bold, *attr_color, *attr_font, *attr_italic,
-		 *attr_letter_spacing, *attr_size, *attr_underline;
-  const char *string;
-  const SwfdecTextAttributes *attr, *attr_prev;
-  // TODO: kerning, display
-
-  g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-  g_assert (paragraph != NULL);
-  g_assert (start_index + length <= swfdec_text_buffer_get_length (text->text));
-
-  string = swfdec_text_buffer_get_text (text->text);
-  paragraph->index_ = start_index;
-  paragraph->length = length;
-  if (string[start_index + length - 1] == '\n' ||
-      string[start_index + length - 1] == '\r') {
-    paragraph->newline = TRUE;
-  } else {
-    paragraph->newline = FALSE;
-  }
-
-  paragraph->blocks = NULL;
-  paragraph->attrs = NULL;
-
-  iter = swfdec_text_buffer_get_iter (text->text, start_index);
-  attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
-
-  index_ = start_index;
-
-  // Paragraph formats
-  paragraph->bullet = attr->bullet;
-  paragraph->indent = attr->indent * 20 * PANGO_SCALE;
-
-  // Add new block
-  swfdec_text_paragraph_add_block (paragraph, 0, attr);
-
-  // Open attributes
-  attr_bold = pango_attr_weight_new (
-      (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
-  attr_bold->start_index = 0;
-
-  attr_color = pango_attr_foreground_new (SWFDEC_COLOR_R (attr->color) * 255,
-      SWFDEC_COLOR_G (attr->color) * 255,
-      SWFDEC_COLOR_B (attr->color) * 255);
-  attr_color->start_index = 0;
-
-  // FIXME: embed fonts
-  attr_font = pango_attr_family_new (attr->font);
-  attr_font->start_index = 0;
-
-  attr_italic = pango_attr_style_new (
-      (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
-  attr_italic->start_index = 0;
-
-  attr_letter_spacing = pango_attr_letter_spacing_new (
-      attr->letter_spacing * 20 * PANGO_SCALE);
-  attr_letter_spacing->start_index = 0;
-
-  attr_size =
-    pango_attr_size_new_absolute (MAX (attr->size, 1) * 20 * PANGO_SCALE);
-  attr_size->start_index = 0;
-
-  attr_underline = pango_attr_underline_new (
-      (attr->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
-  attr_underline->start_index = 0;
-
-  for (iter = swfdec_text_buffer_iter_next (text->text, iter);
-      iter != NULL && swfdec_text_buffer_iter_get_start (text->text, iter) < start_index + length;
-      iter = swfdec_text_buffer_iter_next (text->text, iter))
-  {
-    attr_prev = attr;
-    index_ = swfdec_text_buffer_iter_get_start (text->text, iter);
-    attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
-
-    // Add new block if necessary
-    if (attr_prev->align != attr->align ||
-       attr_prev->bullet != attr->bullet ||
-       attr_prev->indent != attr->indent ||
-       attr_prev->leading != attr->leading ||
-       attr_prev->block_indent != attr->block_indent ||
-       attr_prev->left_margin != attr->left_margin)
-    {
-      swfdec_text_paragraph_add_block (paragraph, index_ - start_index, attr);
-    }
-
-    // Change attributes if necessary
-    if (attr_prev->bold != attr->bold) {
-      attr_bold->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_bold);
-
-      attr_bold = pango_attr_weight_new (
-	  (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
-      attr_bold->start_index = index_ - start_index;
-    }
-
-    if (attr_prev->color != attr->color) {
-      attr_color->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_color);
-
-      attr_color = pango_attr_foreground_new (
-	  SWFDEC_COLOR_R (attr->color) * 255,
-	  SWFDEC_COLOR_G (attr->color) * 255,
-	  SWFDEC_COLOR_B (attr->color) * 255);
-      attr_color->start_index = index_ - start_index;
-    }
-
-    if (attr_prev->font != attr->font) {
-      attr_font->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_font);
-
-      // FIXME: embed fonts
-      attr_font = pango_attr_family_new (attr->font);
-      attr_font->start_index = index_ - start_index;
-    }
-
-    if (attr_prev->italic != attr->italic) {
-      attr_italic->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_italic);
-
-      attr_italic = pango_attr_style_new (
-	  (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
-      attr_italic->start_index = index_ - start_index;
-    }
-
-    if (attr_prev->letter_spacing != attr->letter_spacing) {
-      attr_letter_spacing->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_letter_spacing);
-
-      attr_letter_spacing = pango_attr_letter_spacing_new (
-	  attr->letter_spacing * 20 * PANGO_SCALE);
-      attr_letter_spacing->start_index = index_ - start_index;
-    }
-
-    if (attr_prev->size != attr->size) {
-      attr_size->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_size);
-
-      attr_size = pango_attr_size_new_absolute (
-	  MAX (1, attr->size) * 20 * PANGO_SCALE);
-      attr_size->start_index = index_ - start_index;
-    }
-
-    if (attr_prev->underline != attr->underline) {
-      attr_underline->end_index = index_ - start_index;
-      swfdec_text_paragraph_add_attribute (paragraph, attr_underline);
-
-      attr_underline = pango_attr_underline_new (
-	  (attr->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
-      attr_underline->start_index = index_ - start_index;
-    }
-  }
-
-  // Close attributes
-  attr_bold->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_bold);
-  attr_bold = NULL;
-
-  attr_color->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_color);
-  attr_color = NULL;
-
-  attr_font->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_font);
-  attr_font = NULL;
-
-  attr_italic->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_italic);
-  attr_italic = NULL;
-
-  attr_letter_spacing->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_letter_spacing);
-  attr_letter_spacing = NULL;
-
-  attr_size->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_size);
-  attr_size = NULL;
-
-  attr_underline->end_index = length;
-  swfdec_text_paragraph_add_attribute (paragraph, attr_underline);
-  attr_underline = NULL;
-
-  // reverse blocks since we use prepend to add them
-  paragraph->blocks = g_slist_reverse (paragraph->blocks);
-}
-
-static SwfdecParagraph *
-swfdec_text_field_movie_get_paragraphs (SwfdecTextFieldMovie *text, int *num)
-{
-  GArray *paragraphs;
-  SwfdecParagraph paragraph;
-  const char *p, *end, *string;
-  guint max_length;
-
-  g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-
-  paragraphs = g_array_new (TRUE, TRUE, sizeof (SwfdecParagraph));
-
-  max_length = 0;
-  p = string = swfdec_text_buffer_get_text (text->text);
-  while (*p != '\0')
-  {
-    end = strpbrk (p, "\r\n");
-    if (end == NULL) {
-      end = strchr (p, '\0');
-    } else {
-      end++;
-    }
-
-    if ((guint) (end - p) > max_length)
-      max_length = end - p;
-
-    swfdec_text_field_movie_generate_paragraph (text, &paragraph,
-	p - string, end - p);
-    paragraphs = g_array_append_val (paragraphs, paragraph);
-
-    p = end;
-  }
-
-  if (num != NULL)
-    *num = paragraphs->len;
-
-  if (text->password)
-    swfdec_text_field_movie_ensure_asterisks (text, max_length);
-
-  return (SwfdecParagraph *) (void *) g_array_free (paragraphs, FALSE);
-}
-
-static void
-swfdec_text_field_movie_free_paragraphs (SwfdecParagraph *paragraphs)
-{
-  GSList *iter;
-  int i;
-
-  g_return_if_fail (paragraphs != NULL);
-
-  for (i = 0; paragraphs[i].blocks != NULL; i++)
-  {
-    for (iter = paragraphs[i].blocks; iter != NULL; iter = iter->next) {
-      if (((SwfdecBlock *)(iter->data))->tab_stops)
-	pango_tab_array_free (((SwfdecBlock *)(iter->data))->tab_stops);
-      g_free (iter->data);
-    }
-    g_slist_free (paragraphs[i].blocks);
-
-    for (iter = paragraphs[i].attrs; iter != NULL; iter = iter->next) {
-      pango_attribute_destroy ((PangoAttribute *)(iter->data));
-    }
-    g_slist_free (paragraphs[i].attrs);
-  }
-  g_free (paragraphs);
-}
-
-/*
- * Rendering
- */
-static PangoAttrList *
-swfdec_text_field_movie_paragraph_get_attr_list (
-    const SwfdecParagraph *paragraph, guint index_,
-    const SwfdecColorTransform *trans)
-{
-  PangoAttrList *attr_list;
-  GSList *iter;
-
-  attr_list = pango_attr_list_new ();
-
-  for (iter = paragraph->attrs; iter != NULL; iter = iter->next)
-  {
-    PangoAttribute *attr;
-
-    if (((PangoAttribute *)iter->data)->end_index <= index_)
-      continue;
-
-    attr = pango_attribute_copy ((PangoAttribute *)iter->data);
-
-    if (attr->klass->type == PANGO_ATTR_FOREGROUND && trans != NULL &&
-	!swfdec_color_transform_is_identity (trans))
-    {
-      SwfdecColor color;
-      PangoColor color_p;
-
-      color_p = ((PangoAttrColor *)attr)->color;
-
-      color = SWFDEC_COLOR_COMBINE (color_p.red >> 8, color_p.green >> 8,
-	  color_p.blue >> 8, 255);
-      color = swfdec_color_apply_transform (color, trans);
-
-      color_p.red = SWFDEC_COLOR_R (color) << 8;
-      color_p.green = SWFDEC_COLOR_G (color) << 8;
-      color_p.blue = SWFDEC_COLOR_B (color) << 8;
-    }
-
-    attr->start_index =
-      (attr->start_index > index_ ? attr->start_index - index_ : 0);
-    attr->end_index = attr->end_index - index_;
-    pango_attr_list_insert (attr_list, attr);
-  }
-
-  return attr_list;
-}
-
-static void
-swfdec_text_field_movie_attr_list_get_ascent_descent (PangoAttrList *attr_list,
-    guint pos, int *ascent, int *descent)
-{
-  PangoAttrIterator *attr_iter;
-  PangoFontDescription *desc;
-  PangoFontMap *fontmap;
-  PangoFont *font;
-  PangoFontMetrics *metrics;
-  PangoContext *pcontext;
-  int end;
-
-  if (ascent != NULL)
-    *ascent = 0;
-  if (descent != NULL)
-    *descent = 0;
-
-  g_return_if_fail (attr_list != NULL);
-
-  attr_iter = pango_attr_list_get_iterator (attr_list);
-  pango_attr_iterator_range (attr_iter, NULL, &end);
-  while ((guint)end < pos && pango_attr_iterator_next (attr_iter)) {
-    pango_attr_iterator_range (attr_iter, NULL, &end);
-  }
-  desc = pango_font_description_new ();
-  pango_attr_iterator_get_font (attr_iter, desc, NULL, NULL);
-  fontmap = pango_cairo_font_map_get_default ();
-  pcontext =
-    pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
-  font = pango_font_map_load_font (fontmap, pcontext, desc);
-  metrics = pango_font_get_metrics (font, NULL);
-
-  if (ascent != NULL)
-    *ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
-  if (descent != NULL)
-    *descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
-
-  g_object_unref (pcontext);
-  pango_font_metrics_unref (metrics);
-  pango_font_description_free (desc);
-  pango_attr_iterator_destroy (attr_iter);
-}
-
-static SwfdecLayout *
-swfdec_text_field_movie_get_layouts (SwfdecTextFieldMovie *text, int *num,
-    cairo_t *cr, const SwfdecParagraph *paragraphs,
-    const SwfdecColorTransform *trans)
-{
-  GArray *layouts;
-  guint i;
-  SwfdecParagraph *paragraphs_free;
-  gsize cursor_start, cursor_end;
-
-  g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-
-  if (cr == NULL) {
-    cr = cairo_create (swfdec_renderer_get_surface (
-	  SWFDEC_PLAYER (SWFDEC_AS_OBJECT (text)->context)->priv->renderer));
-  } else {
-    cairo_reference (cr);
-  }
-
-  if (paragraphs == NULL) {
-    paragraphs_free = swfdec_text_field_movie_get_paragraphs (text, NULL);
-    paragraphs = paragraphs_free;
-  } else {
-    paragraphs_free = NULL;
-  }
-
-  layouts = g_array_new (TRUE, TRUE, sizeof (SwfdecLayout));
-  swfdec_text_buffer_get_selection (text->text, &cursor_start, &cursor_end);
-
-  for (i = 0; paragraphs[i].blocks != NULL; i++)
-  {
-    GSList *iter;
-    guint skip;
-
-    skip = 0;
-    for (iter = paragraphs[i].blocks; iter != NULL; iter = iter->next)
-    {
-      SwfdecLayout layout;
-      PangoLayout *playout;
-      PangoAttrList *attr_list;
-      SwfdecBlock *block;
-      int width;
-      guint length;
-
-      block = (SwfdecBlock *)iter->data;
-      if (iter->next != NULL) {
-	length =
-	  ((SwfdecBlock *)(iter->next->data))->index_ - block->index_;
-      } else {
-	length = paragraphs[i].length - block->index_;
-	if (paragraphs[i].newline)
-	  length -= 1;
-      }
-
-      if (skip > length) {
-	skip -= length;
-	continue;
-      }
-
-      // create layout
-      playout = layout.layout = pango_cairo_create_layout (cr);
-      pango_layout_set_single_paragraph_mode (playout, TRUE);
-
-      // set rendering position
-      layout.offset_x = block->left_margin + block->block_indent;
-      if (paragraphs[i].bullet)
-	layout.offset_x += SWFDEC_DOUBLE_TO_TWIPS (BULLET_MARGIN);
-
-      width = SWFDEC_MOVIE (text)->original_extents.x1 -
-	SWFDEC_MOVIE (text)->original_extents.x0 - block->right_margin -
-	layout.offset_x;
-
-      if (block->index_ == 0 && paragraphs[i].indent < 0) {
-	// limit negative indent to not go over leftMargin + blockIndent
-	int indent = MAX (paragraphs[i].indent / PANGO_SCALE,
-	    -(block->left_margin + block->block_indent));
-	layout.offset_x += indent;
-	width += -indent;
-      }
-
-      if (text->word_wrap) {
-	pango_layout_set_wrap (playout, PANGO_WRAP_WORD_CHAR);
-	pango_layout_set_width (playout, width * PANGO_SCALE);
-	pango_layout_set_alignment (playout, block->align);
-	pango_layout_set_justify (playout, block->justify);
-      } else {
-	pango_layout_set_width (playout, -1);
-      }
-
-      // set paragraph styles
-      if (block->index_ == 0) {
-	pango_layout_set_indent (playout, paragraphs[i].indent);
-	layout.bullet = paragraphs[i].bullet;
-      } else {
-	pango_layout_set_indent (playout, 0);
-	layout.bullet = FALSE;
-      }
-
-      // set block styles
-      pango_layout_set_spacing (playout, block->leading);
-      if (block->tab_stops != NULL)
-	pango_layout_set_tabs (playout, block->tab_stops);
-
-      // set text attributes
-      attr_list = swfdec_text_field_movie_paragraph_get_attr_list (
-	  &paragraphs[i], block->index_ + skip, trans);
-
-      // add background for selection
-      layout.index_ = paragraphs[i].index_ + block->index_ + skip;
-      layout.index_end = layout.index_ + length;
-      if (cursor_start != cursor_end &&
-	  layout.index_ < cursor_end) {
-	SwfdecColor color;
-	PangoAttribute *attr_fg, *attr_bg;
-
-	color = SWFDEC_COLOR_COMBINE (255, 255, 255, 255);
-	if (trans != NULL)
-	  color = swfdec_color_apply_transform (color, trans);
-	attr_fg = pango_attr_foreground_new (SWFDEC_COLOR_R (color) << 8,
-	    SWFDEC_COLOR_G (color) << 8, SWFDEC_COLOR_B (color) << 8);
-
-	color = SWFDEC_COLOR_COMBINE (0, 0, 0, 255);
-	if (trans != NULL)
-	  color = swfdec_color_apply_transform (color, trans);
-	attr_bg = pango_attr_background_new (SWFDEC_COLOR_R (color) << 8,
-	    SWFDEC_COLOR_G (color) << 8, SWFDEC_COLOR_B (color) << 8);
-
-	if (cursor_start > layout.index_) {
-	  attr_fg->start_index = attr_bg->start_index = cursor_start - layout.index_;
-	} else {
-	  attr_fg->start_index = attr_bg->start_index = 0;
-	}
-	attr_bg->end_index = attr_fg->end_index = cursor_end - layout.index_;
-
-	pango_attr_list_insert (attr_list, attr_fg);
-	pango_attr_list_insert (attr_list, attr_bg);
-      }
-
-      // add shape for newline character at the end
-      if (paragraphs[i].newline) {
-	int ascent, descent;
-	PangoRectangle rect;
-	PangoAttribute *attr;
-
-	swfdec_text_field_movie_attr_list_get_ascent_descent (attr_list,
-	    paragraphs[i].length - block->index_ - skip, &ascent, &descent);
-
-	rect.x = 0;
-	rect.width = 0;
-	rect.y = -ascent * PANGO_SCALE;
-	rect.height = descent * PANGO_SCALE;
-
-	attr = pango_attr_shape_new (&rect, &rect);
-
-	attr->end_index = paragraphs[i].length - block->index_ - skip;
-	attr->start_index = attr->end_index - 1;
-
-	pango_attr_list_insert (attr_list, attr);
-      }
-
-      pango_layout_set_attributes (playout, attr_list);
-
-      if (text->password) {
-	pango_layout_set_text (playout, text->asterisks, paragraphs[i].length -
-	    block->index_ - skip);
-      } else {
-	pango_layout_set_text (playout,
-	    swfdec_text_buffer_get_text (text->text) + paragraphs[i].index_ + block->index_ + skip,
-	    paragraphs[i].length - block->index_ - skip);
-      }
-
-      if (iter->next != NULL && text->word_wrap)
-      {
-	PangoLayoutLine *line;
-	int line_num;
-	guint skip_new;
-
-	pango_layout_index_to_line_x (playout, length - skip, FALSE, &line_num,
-	    NULL);
-	if (line_num < pango_layout_get_line_count (playout) - 1) {
-	  line = pango_layout_get_line_readonly (playout, line_num);
-	  skip_new = line->start_index + line->length - (length - skip);
-	  pango_layout_set_text (playout,
-	      swfdec_text_buffer_get_text (text->text) + paragraphs[i].index_ + block->index_ + skip,
-	      length - skip + skip_new);
-	  skip = skip_new;
-	}
-      }
-      else
-      {
-	if (!text->word_wrap && block->align != PANGO_ALIGN_LEFT) {
-	  int line_width;
-	  pango_layout_get_pixel_size (playout, &line_width, 0);
-	  if (line_width < width) {
-	    if (block->align == PANGO_ALIGN_RIGHT) {
-	      layout.offset_x += width - line_width;
-	    } else if (block->align == PANGO_ALIGN_CENTER) {
-	      layout.offset_x += (width - line_width) / 2;
-	    } else {
-	      g_assert_not_reached ();
-	    }
-	  }
-	}
-
-	skip = 0;
-      }
-
-      pango_layout_get_pixel_size (playout, &layout.width, &layout.height);
-      layout.width += layout.offset_x + block->right_margin;
-
-      pango_attr_list_unref (attr_list);
-
-      // add leading to last line too
-      layout.height += block->leading / PANGO_SCALE;
-
-      layouts = g_array_append_val (layouts, layout);
-
-      if (!text->word_wrap)
-	break;
-    }
-  }
-
-  if (paragraphs_free != NULL) {
-    swfdec_text_field_movie_free_paragraphs (paragraphs_free);
-    paragraphs_free = NULL;
-    paragraphs = NULL;
-  }
-
-  if (num != NULL)
-    *num = layouts->len;
-  cairo_destroy (cr);
-
-  return (SwfdecLayout *) (void *) g_array_free (layouts, FALSE);
-}
-
-static void
-swfdec_text_field_movie_free_layouts (SwfdecLayout *layouts)
-{
-  int i;
-
-  g_return_if_fail (layouts != NULL);
-
-  for (i = 0; layouts[i].layout != NULL; i++) {
-    g_object_unref (layouts[i].layout);
-  }
-
-  g_free (layouts);
-}
-
-static void
-swfdec_text_field_movie_line_position (SwfdecLayout *layouts, int line_num,
-    int *pixels, int *layout_num, int *line_in_layout)
-{
-  int current, i;
-
-  if (pixels != NULL)
-    *pixels = 0;
-
-  if (layout_num != NULL)
-    *layout_num = 0;
-
-  if (line_in_layout != NULL)
-    *line_in_layout = 0;
-
-  g_return_if_fail (layouts != NULL);
-
-  current = 1;
-  for (i = 0; layouts[i].layout != NULL && current < line_num; i++) {
-    current += pango_layout_get_line_count (layouts[i].layout);
-
-    if (pixels != NULL)
-      *pixels += layouts[i].height;
-  }
-
-  if (current > line_num) {
-    PangoLayoutIter *iter_line;
-
-    i--;
-    current -= pango_layout_get_line_count (layouts[i].layout);
-
-    if (pixels != NULL)
-      *pixels -= layouts[i].height;
-
-    iter_line = pango_layout_get_iter (layouts[i].layout);
-
-    while (++current < line_num) {
-      g_assert (!pango_layout_iter_at_last_line (iter_line));
-      pango_layout_iter_next_line (iter_line);
-
-      if (line_in_layout != NULL)
-	(*line_in_layout)++;
-    }
-
-    if (pixels != NULL) {
-      PangoRectangle rect;
-
-      pango_layout_iter_get_line_extents (iter_line, NULL, &rect);
-      pango_extents_to_pixels (NULL, &rect);
-
-      *pixels += rect.y;
-    }
-
-    pango_layout_iter_free (iter_line);
-  }
-
-  if (layout_num != NULL)
-    *layout_num = i;
-}
-
+#if 0
 static gboolean
 swfdec_text_field_movie_has_focus (SwfdecTextFieldMovie *text)
 {
@@ -810,200 +82,65 @@ swfdec_text_field_movie_has_focus (SwfdecTextFieldMovie *text)
 
   return swfdec_player_has_focus (player, SWFDEC_ACTOR (text));
 }
+#endif
 
 static void
 swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
-    const SwfdecColorTransform *trans, const SwfdecRect *inval)
+    const SwfdecColorTransform *ctrans, const SwfdecRect *inval)
 {
-  SwfdecTextFieldMovie *text;
-  SwfdecLayout *layouts;
-  SwfdecRect limit;
+  static const cairo_matrix_t identity = { 1, 0, 0, 1, 0, 0 };
+  SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (movie);
   SwfdecColor color;
-  SwfdecParagraph *paragraphs;
-  int i, y, x, skip;
-  gboolean first;
-  gsize cursor;
-
-  g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (movie));
-  g_return_if_fail (cr != NULL);
-  g_return_if_fail (trans != NULL);
-  g_return_if_fail (inval != NULL);
 
+  g_print ("render %d %d   %d %d\n",
+      text->stage_rect.x, text->stage_rect.y,
+      text->stage_rect.width, text->stage_rect.height);
   /* textfields don't mask */
-  if (swfdec_color_transform_is_mask (trans))
+  if (swfdec_color_transform_is_mask (ctrans) ||
+      swfdec_rectangle_is_empty (&text->stage_rect))
     return;
 
-  text = SWFDEC_TEXT_FIELD_MOVIE (movie);
-
-  paragraphs = swfdec_text_field_movie_get_paragraphs (text, NULL);
-
-  swfdec_rect_intersect (&limit, &movie->original_extents, inval);
+  /* FIXME: need to handle the case where we're not drawing with identity */
+  cairo_set_matrix (cr, &identity);
 
   if (text->background) {
-    cairo_rectangle (cr, limit.x0, limit.y0, limit.x1 - limit.x0,
-	limit.y1 - limit.y0);
-    color = swfdec_color_apply_transform (text->background_color, trans);
-    // always use full alpha
-    swfdec_color_set_source (cr, color | SWFDEC_COLOR_COMBINE (0, 0, 0, 255));
+    cairo_rectangle (cr, text->stage_rect.x, text->stage_rect.y,
+	text->stage_rect.width, text->stage_rect.height);
+    color = swfdec_color_apply_transform (text->background_color, ctrans);
+    swfdec_color_set_source (cr, SWFDEC_COLOR_OPAQUE (color));
     cairo_fill (cr);
   }
-
   if (text->border) {
-    // FIXME: should not be scaled, but always be 1 pixel width
-    // TODO: should clip before to make things faster?
-    cairo_rectangle (cr, movie->original_extents.x0 +
-	SWFDEC_DOUBLE_TO_TWIPS (0.5), movie->original_extents.y0 +
-	SWFDEC_DOUBLE_TO_TWIPS (0.5),
-	movie->original_extents.x1 - movie->original_extents.x0,
-	movie->original_extents.y1 - movie->original_extents.y0);
-    color = swfdec_color_apply_transform (text->border_color, trans);
-    // always use full alpha
-    swfdec_color_set_source (cr, color | SWFDEC_COLOR_COMBINE (0, 0, 0, 255));
-    cairo_set_line_width (cr, SWFDEC_DOUBLE_TO_TWIPS (1));
+    cairo_rectangle (cr, text->stage_rect.x + 0.5, text->stage_rect.y + 0.5,
+	text->stage_rect.width, text->stage_rect.height);
+    color = swfdec_color_apply_transform (text->border_color, ctrans);
+    swfdec_color_set_source (cr, SWFDEC_COLOR_OPAQUE (color));
+    cairo_set_line_width (cr, 1.0);
     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
     cairo_stroke (cr);
   }
 
-  cairo_rectangle (cr, limit.x0, limit.y0, limit.x1 - limit.x0,
-      limit.y1 - limit.y0);
-  cairo_clip (cr);
-
-  layouts = swfdec_text_field_movie_get_layouts (text, NULL, cr,
-      paragraphs, trans);
-
-  first = TRUE;
-  x = movie->original_extents.x0 + SWFDEC_DOUBLE_TO_TWIPS (EXTRA_MARGIN) +
-    MIN (text->hscroll, text->hscroll_max);
-  y = movie->original_extents.y0 + SWFDEC_DOUBLE_TO_TWIPS (EXTRA_MARGIN);
-
-  swfdec_text_field_movie_line_position (layouts,
-      MIN (text->scroll, text->scroll_max), NULL, &i, &skip);
-
-  if (text->editable && swfdec_text_field_movie_has_focus (text) &&
-      !swfdec_text_buffer_has_selection (text->text))
-    cursor = swfdec_text_buffer_get_cursor (text->text);
-  else
-    cursor = G_MAXSIZE;
-
-  for (; layouts[i].layout != NULL && y < limit.y1; i++)
-  {
-    SwfdecLayout *layout = &layouts[i];
-    PangoLayoutIter *iter_line;
-    PangoLayoutLine *line;
-    PangoRectangle rect;
-    int skipped;
-
-    iter_line = pango_layout_get_iter (layout->layout);
-
-    if (layout->bullet && skip == 0) {
-      PangoColor color_p;
-      PangoAttribute *attr;
-      PangoAttrIterator *attr_iter;
-
-      pango_layout_iter_get_line_extents (iter_line, NULL, &rect);
-      pango_extents_to_pixels (NULL, &rect);
-
-      cairo_new_sub_path (cr);
-
-      // get current color
-      attr_iter = pango_attr_list_get_iterator (
-	  pango_layout_get_attributes (layout->layout));
-      attr = pango_attr_iterator_get (attr_iter, PANGO_ATTR_FOREGROUND);
-      color_p = ((PangoAttrColor *)attr)->color;
-      color = SWFDEC_COLOR_COMBINE (color_p.red >> 8, color_p.green >> 8,
-	  color_p.blue >> 8, 255);
-      color = swfdec_color_apply_transform (color, trans);
-      pango_attr_iterator_destroy (attr_iter);
-
-      swfdec_color_set_source (cr, color);
-
-      cairo_arc (cr, x + layout->offset_x +
-	  pango_layout_get_indent (layout->layout) -
-	  SWFDEC_DOUBLE_TO_TWIPS (BULLET_MARGIN) / 2,
-	  y + rect.height / 2, rect.height / 8, 20, 2 * M_PI);
-      cairo_fill (cr);
-    }
-
-    if (skip > 0) {
-      for (skipped = 0; skipped < skip; skipped++) {
-	g_assert (!pango_layout_iter_at_last_line (iter_line));
-	pango_layout_iter_next_line (iter_line);
-      }
-
-      pango_layout_iter_get_line_extents (iter_line, NULL, &rect);
-      pango_extents_to_pixels (NULL, &rect);
-
-      skipped = rect.y;
-      skip = 0;
-    } else {
-      skipped = 0;
-    }
-
-    do {
-      pango_layout_iter_get_line_extents (iter_line, NULL, &rect);
-      pango_extents_to_pixels (NULL, &rect);
-      line = pango_layout_iter_get_line_readonly (iter_line);
-
-      if (!first && y + rect.y + rect.height > movie->original_extents.y1)
-	break;
-
-      first = FALSE;
-
-      if (y + rect.y > limit.y1)
-	break;
-
-      if (y + rect.y + rect.height < limit.y0 ||
-	  x + layout->offset_x + rect.x > limit.x1 ||
-	  x + layout->offset_x + rect.x + rect.width < limit.x0)
-	continue;
-
-      cairo_move_to (cr, x, y);
-      cairo_rel_move_to (cr, layout->offset_x + rect.x,
-	  pango_layout_iter_get_baseline (iter_line) / PANGO_SCALE - skipped);
-
-      pango_cairo_show_layout_line (cr, line);
-      line = NULL;
-    } while (pango_layout_iter_next_line (iter_line));
-
-    if (layouts[i].index_ <= cursor && 
-	(layouts[i].index_end > cursor || (layouts[i].index_end == cursor && cursor == swfdec_text_buffer_get_length (text->text))) &&
-	(line == NULL || layouts[i].index_ + line->start_index >= cursor)) {
-      const SwfdecTextAttributes *attr = swfdec_text_buffer_get_attributes (text->text,
-	  swfdec_text_buffer_get_length (text->text));
-      PangoRectangle cursor_rect;
-
-      pango_layout_get_cursor_pos (layouts[i].layout, 
-	  swfdec_text_buffer_get_cursor (text->text) - layouts[i].index_,
-	  &cursor_rect, NULL);
-
-      cairo_save (cr);
-      /* FIXME: or default attributes? */
-      swfdec_color_set_source (cr, attr->color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
-
-      /* FIXME: what's the proper line width here? */
-      cairo_set_line_width (cr, SWFDEC_DOUBLE_TO_TWIPS (0.5));
-      cairo_move_to (cr, x + layout->offset_x + rect.x, y - skipped);
-      cairo_rel_move_to (cr, (double) cursor_rect.x / PANGO_SCALE, (double) cursor_rect.y / PANGO_SCALE);
-      cairo_rel_line_to (cr, (double) cursor_rect.width / PANGO_SCALE, (double) cursor_rect.height / PANGO_SCALE);
-      cairo_stroke (cr);
-      cairo_restore (cr);
-    }
-
-    y += layout->height - skipped;
-
-    pango_layout_iter_free (iter_line);
-  }
-
-  swfdec_text_field_movie_free_layouts (layouts);
+  /* no more stuff to do if area too small or no text in existance */
+  if (text->stage_rect.width <= BORDER_LEFT + BORDER_RIGHT ||
+      text->stage_rect.height <= BORDER_TOP + BORDER_BOTTOM)
+    return;
 
-  swfdec_text_field_movie_free_paragraphs (paragraphs);
+  /* FIXME: we want to draw a cursor if there's no text, but for now... */
+  if (swfdec_text_buffer_get_length (text->text) == 0)
+    return;
+  /* render the layout */
+  cairo_rectangle (cr, text->stage_rect.x, text->stage_rect.y, text->stage_rect.width, text->stage_rect.height);
+  cairo_clip (cr);
+  cairo_translate (cr, text->stage_rect.x + BORDER_LEFT, text->stage_rect.y + BORDER_TOP);
+  swfdec_text_layout_render (text->layout, cr, ctrans,
+      0, text->stage_rect.height - BORDER_TOP + BORDER_BOTTOM, &text->stage_rect);
 }
 
 void
 swfdec_text_field_movie_update_scroll (SwfdecTextFieldMovie *text,
     gboolean check_limits)
 {
+#if 0
   SwfdecLayout *layouts;
   int i, num, y, visible, all, height;
   double width, width_max;
@@ -1081,42 +218,9 @@ swfdec_text_field_movie_update_scroll (SwfdecTextFieldMovie *text,
       text->scroll_changed = TRUE;
     }
   }
-}
-
-void
-swfdec_text_field_movie_get_text_size (SwfdecTextFieldMovie *text, int *width,
-    int *height)
-{
-  SwfdecLayout *layouts;
-  int i;
-
-  if (width != NULL)
-    *width = 0;
-  if (height != NULL)
-    *height = 0;
-
-  g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-  g_return_if_fail (width != NULL || height != NULL);
-
-  layouts = swfdec_text_field_movie_get_layouts (text, NULL, NULL, NULL, NULL);
-
-  for (i = 0; layouts[i].layout != NULL; i++) {
-    if (!text->word_wrap) {
-      if (width != NULL && layouts[i].width > *width)
-	*width = layouts[i].width;
-    }
-
-    if (height != NULL)
-      *height += layouts[i].height;
-  }
-
-  // align to get integer amount after TWIPS_TO_DOUBLE
-  if (width != NULL && *width % SWFDEC_TWIPS_SCALE_FACTOR != 0)
-    *width += SWFDEC_TWIPS_SCALE_FACTOR - *width % SWFDEC_TWIPS_SCALE_FACTOR;
-  if (height != NULL && *height % SWFDEC_TWIPS_SCALE_FACTOR != 0)
-    *height += SWFDEC_TWIPS_SCALE_FACTOR - *height % SWFDEC_TWIPS_SCALE_FACTOR;
-
-  swfdec_text_field_movie_free_layouts (layouts);
+#else
+  SWFDEC_STUB ("swfdec_text_field_movie_update_scroll");
+#endif
 }
 
 gboolean
@@ -1129,14 +233,8 @@ swfdec_text_field_movie_auto_size (SwfdecTextFieldMovie *text)
   if (text->auto_size == SWFDEC_AUTO_SIZE_NONE)
     return FALSE;
 
-  swfdec_text_field_movie_get_text_size (text, &width, &height);
-  width += SWFDEC_DOUBLE_TO_TWIPS (2 * EXTRA_MARGIN);
-  height += SWFDEC_DOUBLE_TO_TWIPS (2 * EXTRA_MARGIN);
-
-  if ((text->word_wrap ||
-	text->extents.x1 - text->extents.x0 == width) &&
-      text->extents.y1 - text->extents.y0 == height)
-    return FALSE;
+  width = SWFDEC_DOUBLE_TO_TWIPS (text->layout_width + BORDER_LEFT + BORDER_RIGHT);
+  height = SWFDEC_DOUBLE_TO_TWIPS (text->layout_height + BORDER_TOP + BORDER_BOTTOM);
 
   swfdec_movie_invalidate_next (SWFDEC_MOVIE (text));
 
@@ -1181,12 +279,6 @@ swfdec_text_field_movie_dispose (GObject *object)
 
   swfdec_text_attributes_reset (&text->default_attributes);
 
-  if (text->asterisks != NULL) {
-    g_free (text->asterisks);
-    text->asterisks = NULL;
-    text->asterisks_length = 0;
-  }
-
   if (text->style_sheet) {
     if (SWFDEC_IS_STYLESHEET (text->style_sheet)) {
       g_signal_handlers_disconnect_matched (text->style_sheet, 
@@ -1201,6 +293,10 @@ swfdec_text_field_movie_dispose (GObject *object)
     g_object_unref (text->text);
     text->text = NULL;
   }
+  if (text->layout) {
+    g_object_unref (text->layout);
+    text->layout = NULL;
+  }
 
   G_OBJECT_CLASS (swfdec_text_field_movie_parent_class)->dispose (object);
 }
@@ -1227,12 +323,55 @@ swfdec_text_field_movie_mark (SwfdecAsObject *object)
 }
 
 static void
+swfdec_text_field_movie_update_area (SwfdecTextFieldMovie *text)
+{
+  SwfdecMovie *movie = SWFDEC_MOVIE (text);
+  cairo_matrix_t matrix, translate;
+  double x, y;
+
+  /* check if we indeed want to render */
+  swfdec_movie_local_to_global_matrix (movie, &matrix);
+  cairo_matrix_multiply (&matrix, &matrix,
+      &SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context)->priv->global_to_stage);
+  if (matrix.xy != 0.0 || matrix.yx != 0.0 ||
+      matrix.xx <= 0.0 || matrix.yy <= 0.0) {
+    swfdec_rectangle_init_empty (&text->stage_rect);
+    return;
+  }
+
+  translate = matrix;
+  x = movie->original_extents.x0;
+  y = movie->original_extents.y0;
+  g_print ("%g %g\n", x, y);
+  cairo_matrix_transform_point (&matrix, &x, &y);
+  g_print ("%g %g\n", x, y);
+  cairo_matrix_init_translate (&translate, round (x) - x, round (y) - y);
+  cairo_matrix_multiply (&matrix, &matrix, &translate);
+  
+  x = movie->original_extents.x0;
+  y = movie->original_extents.y0;
+  cairo_matrix_transform_point (&matrix, &x, &y);
+  text->stage_rect.x = x;
+  text->stage_rect.y = y;
+  x = movie->original_extents.x1;
+  y = movie->original_extents.y1;
+  cairo_matrix_transform_point (&matrix, &x, &y);
+  /* FIXME: floor, ceil or round? */
+  text->stage_rect.width = round (x) - text->stage_rect.x;
+  text->stage_rect.height = round (y) - text->stage_rect.y;
+  g_print ("%d %d   %d %d\n",
+      text->stage_rect.x, text->stage_rect.y,
+      text->stage_rect.width, text->stage_rect.height);
+}
+
+static void
 swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
 {
   SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (movie);
   SwfdecTextField *text_field = SWFDEC_TEXT_FIELD (movie->graphic);
   SwfdecAsContext *cx;
   SwfdecAsValue val;
+  SwfdecMovie *parent;
   gboolean needs_unuse;
 
   needs_unuse = swfdec_sandbox_try_use (movie->resource->sandbox);
@@ -1287,12 +426,27 @@ swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
 
   if (needs_unuse)
     swfdec_sandbox_unuse (movie->resource->sandbox);
+
+  parent = movie;
+  while (parent) {
+    g_signal_connect_swapped (parent, "matrix-changed", 
+	G_CALLBACK (swfdec_text_field_movie_update_area), movie);
+    parent = parent->parent;
+  }
 }
 
 static void
 swfdec_text_field_movie_finish_movie (SwfdecMovie *movie)
 {
   SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (movie);
+  SwfdecMovie *parent;
+
+  parent = SWFDEC_MOVIE (text);
+  while (parent) {
+    g_signal_handlers_disconnect_matched (parent, 
+	  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, text);
+    parent = parent->parent;
+  }
 
   swfdec_text_field_movie_set_listen_variable (text, NULL);
 }
@@ -1349,50 +503,12 @@ static gboolean
 swfdec_text_field_movie_xy_to_index (SwfdecTextFieldMovie *text, double x,
     double y, guint *index_, gboolean *before)
 {
-  SwfdecLayout *layouts;
-  int i, layout_index, trailing, pixels;
-  double layout_y, layout_x;
-  gboolean direct;
-
-  g_return_val_if_fail (index_ != NULL, FALSE);
-  g_return_val_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text), FALSE);
-
-  *index_ = 0;
-  if (before != NULL)
-    *before = FALSE;
-
-  layouts = swfdec_text_field_movie_get_layouts (text, NULL, NULL, NULL, NULL);
-
-  if (layouts[0].layout == NULL)
-    return FALSE;
-
-  layout_y = y - EXTRA_MARGIN - text->extents.y0;
-  layout_x = x - EXTRA_MARGIN - text->extents.x0;
-
-  // take scrolling into account
-  swfdec_text_field_movie_line_position (layouts,
-      MIN (text->scroll, text->scroll_max), &pixels, NULL, NULL);
-  layout_y += pixels;
-  layout_x += MIN (text->hscroll, text->hscroll_max);
-
-  i = 0;
-  while (layout_y > layouts[i].height && layouts[i + 1].layout != NULL) {
-    layout_y -= layouts[i].height;
-    i++;
-  }
-
-  layout_x -= layouts[i].offset_x;
-
-  direct = pango_layout_xy_to_index (layouts[i].layout, layout_x * PANGO_SCALE,
-      layout_y * PANGO_SCALE, &layout_index, &trailing);
-
-  *index_ = layouts[i].index_ + layout_index;
+  SWFDEC_STUB ("swfdec_text_field_movie_xy_to_index");
+  if (index_)
+    *index_ = 0;
   if (before)
-    *before = (trailing == 0);
-
-  swfdec_text_field_movie_free_layouts (layouts);
-
-  return direct;
+    *before = FALSE;
+  return FALSE;
 }
 
 static SwfdecMouseCursor
@@ -1655,6 +771,9 @@ swfdec_text_field_movie_init (SwfdecTextFieldMovie *text)
       G_CALLBACK (swfdec_text_field_movie_cursor_changed), text);
   g_signal_connect (text->text, "text-changed", 
       G_CALLBACK (swfdec_text_field_movie_text_changed), text);
+
+  text->layout = swfdec_text_layout_new (text->text);
+
   text->scroll = 1;
   text->mouse_wheel_enabled = TRUE;
 
diff --git a/swfdec/swfdec_text_field_movie.h b/swfdec/swfdec_text_field_movie.h
index 80beb63..43fcbde 100644
--- a/swfdec/swfdec_text_field_movie.h
+++ b/swfdec/swfdec_text_field_movie.h
@@ -26,6 +26,7 @@
 #include <swfdec/swfdec_style_sheet.h>
 #include <swfdec/swfdec_text_buffer.h>
 #include <swfdec/swfdec_text_format.h>
+#include <swfdec/swfdec_text_layout.h>
 
 G_BEGIN_DECLS
 
@@ -39,56 +40,15 @@ typedef struct _SwfdecTextFieldMovieClass SwfdecTextFieldMovieClass;
 #define SWFDEC_TEXT_FIELD_MOVIE(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_TEXT_FIELD_MOVIE, SwfdecTextFieldMovie))
 #define SWFDEC_TEXT_FIELD_MOVIE_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_TEXT_FIELD_MOVIE, SwfdecTextFieldMovieClass))
 
-typedef struct {
-  PangoLayout *		layout;		// layout to render
-
-  guint			index_;		// byte offset where this layout's text starts in text->input->str
-  guint			index_end;	// and the offset where it ends
-
-  int			offset_x;	// x offset to apply before rendering
-
-  // dimensions for this layout, including offset_x, might be bigger than the
-  // rendering of PangoLayout needs
-  int			width;
-  int			height;
-
-  // whether the layout starts with bullet point to be rendered separately
-  gboolean		bullet;
-} SwfdecLayout;
-
-typedef struct {
-  guint			index_;
-
-  PangoAlignment	align;
-  gboolean		justify;
-  int			leading;
-  int			block_indent;
-  int			left_margin;
-  int			right_margin;
-  PangoTabArray *	tab_stops;
-} SwfdecBlock;
-
-typedef struct {
-  guint			index_;
-  guint			length;
-  gboolean		newline;	// ends in newline
-
-  gboolean		bullet;
-  int			indent;
-
-  GSList *		blocks;		// SwfdecBlock
-  GSList *		attrs;		// PangoAttribute
-} SwfdecParagraph;
-
 struct _SwfdecTextFieldMovie {
   SwfdecActor		actor;
 
-  SwfdecRect		extents;	/* the extents we were assigned / calculated during autosize */
+  SwfdecRect		extents;	/* original extents (copied from graphic) */
+  SwfdecRectangle	stage_rect;	/* these extents in stage coordinates */
 
   /* properties copied from textfield */
   gboolean		html;
   gboolean		editable;
-  gboolean		password;
   int			max_chars;
   gboolean		selectable;
   gboolean		embed_fonts;
@@ -99,10 +59,13 @@ struct _SwfdecTextFieldMovie {
   gboolean		background;
  
   SwfdecTextBuffer *	text;		/* the text + formatting */
-  char *		asterisks;	/* bunch of asterisks that we display when password mode is enabled */
-  guint			asterisks_length;
   gboolean		input_html;	/* whether orginal input was given as HTML */
 
+  SwfdecTextLayout *	layout;		/* the layouted text */
+  SwfdecRectangle	layout_area;	/* visible area of the layout */
+  guint			layout_width;	/* text width in pixels */
+  guint			layout_height;	/* text height in pixels */
+
   const char *		variable;
 
   SwfdecTextAttributes	default_attributes;
@@ -136,12 +99,9 @@ struct _SwfdecTextFieldMovieClass {
 
 GType		swfdec_text_field_movie_get_type		(void);
 
-void		swfdec_text_field_movie_set_text		(SwfdecTextFieldMovie *	movie,
+void		swfdec_text_field_movie_set_text	(SwfdecTextFieldMovie *	movie,
 							 const char *		str,
 							 gboolean		html);
-void		swfdec_text_field_movie_get_text_size	(SwfdecTextFieldMovie *	text,
-							 int *			width,
-							 int *			height);
 gboolean	swfdec_text_field_movie_auto_size	(SwfdecTextFieldMovie *	text);
 void		swfdec_text_field_movie_update_scroll	(SwfdecTextFieldMovie *	text,
 							 gboolean		check_limits);
@@ -163,5 +123,6 @@ void		swfdec_text_field_movie_html_parse	(SwfdecTextFieldMovie *	text,
 							 const char *		str);
 const char *	swfdec_text_field_movie_get_html_text	(SwfdecTextFieldMovie *		text);
 
+
 G_END_DECLS
 #endif
diff --git a/swfdec/swfdec_text_field_movie_as.c b/swfdec/swfdec_text_field_movie_as.c
index 1f67f79..2988d90 100644
--- a/swfdec/swfdec_text_field_movie_as.c
+++ b/swfdec/swfdec_text_field_movie_as.c
@@ -423,12 +423,10 @@ swfdec_text_field_movie_get_textHeight (SwfdecAsContext *cx,
     SwfdecAsValue *ret)
 {
   SwfdecTextFieldMovie *text;
-  int height;
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  swfdec_text_field_movie_get_text_size (text, NULL, &height);
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, SWFDEC_TWIPS_TO_DOUBLE (height));
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->layout_height);
 }
 
 static void
@@ -437,12 +435,10 @@ swfdec_text_field_movie_get_textWidth (SwfdecAsContext *cx,
     SwfdecAsValue *ret)
 {
   SwfdecTextFieldMovie *text;
-  int width;
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  swfdec_text_field_movie_get_text_size (text, &width, NULL);
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, SWFDEC_TWIPS_TO_DOUBLE (width));
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->layout_width);
 }
 
 /*
@@ -788,7 +784,7 @@ swfdec_text_field_movie_get_password (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_BOOLEAN (ret, text->password);
+  SWFDEC_AS_VALUE_SET_BOOLEAN (ret, swfdec_text_layout_get_password (text->layout));
 }
 
 static void
@@ -803,15 +799,8 @@ swfdec_text_field_movie_set_password (SwfdecAsContext *cx,
 
   swfdec_as_value_to_number (cx, &argv[0]);
 
-  if (text->password != value) {
-    text->password = value;
-    if (!value && text->asterisks != NULL) {
-      g_free (text->asterisks);
-      text->asterisks = NULL;
-      text->asterisks_length = 0;
-    }
-    swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
-  }
+  swfdec_text_layout_set_password (text->layout, value);
+  swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
 }
 
 static void
@@ -1375,12 +1364,12 @@ swfdec_text_field_movie_createTextField (SwfdecAsContext *cx,
       SWFDEC_GRAPHIC (edittext), name);
   g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (movie));
   g_object_unref (edittext);
-  swfdec_movie_initialize (movie);
 
   movie->matrix.x0 = SWFDEC_DOUBLE_TO_TWIPS (x);
   movie->matrix.y0 = SWFDEC_DOUBLE_TO_TWIPS (y);
   movie->modified = TRUE;
 
+  swfdec_movie_initialize (movie);
   swfdec_movie_update (movie);
 
   swfdec_as_object_get_variable (cx->global, SWFDEC_AS_STR_TextField, &val);
diff --git a/swfdec/swfdec_text_layout.c b/swfdec/swfdec_text_layout.c
new file mode 100644
index 0000000..37683fe
--- /dev/null
+++ b/swfdec/swfdec_text_layout.c
@@ -0,0 +1,608 @@
+/* Swfdec
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
+ *                    2007 Pekka Lampila <pekka.lampila at iki.fi>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <string.h>
+
+#include "swfdec_text_layout.h"
+#include "swfdec_debug.h"
+#include "swfdec_decoder.h"
+
+#define BULLET_MARGIN 36
+
+enum {
+  CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static PangoAttribute *
+swfdec_text_attribute_create_bold (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_weight_new (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+}
+
+static PangoAttribute *
+swfdec_text_attribute_create_color (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_foreground_new (SWFDEC_COLOR_R (attr->color) * 255,
+      SWFDEC_COLOR_G (attr->color) * 255, SWFDEC_COLOR_B (attr->color) * 255);
+}
+
+static PangoAttribute *
+swfdec_text_attribute_create_font (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_family_new (attr->font);
+}
+
+static PangoAttribute *
+swfdec_text_attribute_create_italic (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_style_new (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+}
+
+static PangoAttribute *
+swfdec_text_attribute_create_letter_spacing (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_letter_spacing_new (attr->letter_spacing * PANGO_SCALE);
+}
+
+static PangoAttribute *
+swfdec_text_attribute_create_size (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_size_new_absolute (MAX (attr->size, 1) * PANGO_SCALE);
+}
+
+static PangoAttribute *
+swfdec_text_attribute_create_underline (const SwfdecTextAttributes *attr)
+{
+  return pango_attr_underline_new (attr->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE);
+}
+
+/*** THE BIG OPTIONS TABLE ***/
+
+typedef enum {
+  FORMAT_APPLY_UNKNOWN,
+  FORMAT_APPLY_BLOCK,
+  FORMAT_APPLY_LINE,
+  FORMAT_APPLY_GLYPH
+} FormatApplication;
+
+struct {
+  FormatApplication   application;
+  PangoAttribute *    (* create_attribute) (const SwfdecTextAttributes *attr);
+} format_table[] = {
+  /* unknown or unhandled properties */
+  [SWFDEC_TEXT_ATTRIBUTE_DISPLAY] = { FORMAT_APPLY_UNKNOWN, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_KERNING] = { FORMAT_APPLY_UNKNOWN, NULL },
+  /* per-paragraph options */
+  [SWFDEC_TEXT_ATTRIBUTE_BULLET] = { FORMAT_APPLY_BLOCK, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_INDENT] = { FORMAT_APPLY_BLOCK, NULL },
+  /* per-line options */
+  [SWFDEC_TEXT_ATTRIBUTE_ALIGN] = { FORMAT_APPLY_LINE, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT] = { FORMAT_APPLY_LINE, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_LEADING] = { FORMAT_APPLY_LINE, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN] = { FORMAT_APPLY_LINE, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN] = { FORMAT_APPLY_LINE, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS] = { FORMAT_APPLY_LINE, NULL },
+  /* per-glyph options */
+  [SWFDEC_TEXT_ATTRIBUTE_BOLD] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_bold },
+  [SWFDEC_TEXT_ATTRIBUTE_COLOR] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_color },
+  [SWFDEC_TEXT_ATTRIBUTE_FONT] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_font },
+  [SWFDEC_TEXT_ATTRIBUTE_ITALIC] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_italic },
+  [SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_letter_spacing },
+  [SWFDEC_TEXT_ATTRIBUTE_SIZE] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_size },
+  [SWFDEC_TEXT_ATTRIBUTE_TARGET] = { FORMAT_APPLY_GLYPH, NULL },
+  [SWFDEC_TEXT_ATTRIBUTE_UNDERLINE] = { FORMAT_APPLY_GLYPH, swfdec_text_attribute_create_underline },
+  [SWFDEC_TEXT_ATTRIBUTE_URL] = { FORMAT_APPLY_GLYPH, NULL }
+};
+
+#define SWFDEC_TEXT_ATTRIBUTES_MASK_NEW_BLOCK (\
+    (1 << SWFDEC_TEXT_ATTRIBUTE_ALIGN) | \
+    (1 << SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT) | \
+    (1 << SWFDEC_TEXT_ATTRIBUTE_LEADING) | \
+    (1 << SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN) | \
+    (1 << SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN) | \
+    (1 << SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS))
+
+/*** BLOCK ***/
+
+/* A block is one vertical part of the layout that can be represented by exactly 
+ * one PangoLayout. */
+
+typedef struct _SwfdecTextBlock SwfdecTextBlock;
+
+struct _SwfdecTextBlock {
+  PangoLayout *		layout;		/* the layout we render */
+  SwfdecRectangle	rect;		/* the area occupied by this layout */
+  guint			row;		/* index of start row */
+  gsize			start;		/* first character drawn */
+  gsize			end;		/* first character not drawn anymore */
+  gboolean		bullet;		/* TRUE if we need to draw a bullet in front of this layout */
+};
+
+static void
+swfdec_text_block_free (gpointer blockp)
+{
+  SwfdecTextBlock *block = blockp;
+
+  g_object_unref (block->layout);
+
+  g_slice_free (SwfdecTextBlock, block);
+}
+
+static SwfdecTextBlock *
+swfdec_text_block_new (PangoContext *context)
+{
+  SwfdecTextBlock *block;
+
+  block = g_slice_new0 (SwfdecTextBlock);
+  block->layout = pango_layout_new (context);
+  pango_layout_set_wrap (block->layout, PANGO_WRAP_WORD_CHAR);
+
+  return block;
+}
+
+/*** MAINTAINING THE LAYOUT ***/
+
+static void
+swfdec_text_layout_apply_paragraph_attributes (SwfdecTextLayout *layout,
+    SwfdecTextBlock *block, const SwfdecTextAttributes *attr)
+{
+  int indent;
+
+  /* SWFDEC_TEXT_ATTRIBUTE_BULLET */
+  block->bullet = attr->bullet;
+  if (block->bullet) {
+    block->rect.x += BULLET_MARGIN;
+    if (block->rect.width <= BULLET_MARGIN) {
+      SWFDEC_FIXME ("out of horizontal space, what now?");
+      block->rect.width = 0;
+    } else {
+      block->rect.width -= BULLET_MARGIN;
+    }
+  }
+
+  /* SWFDEC_TEXT_ATTRIBUTE_INDENT */
+  indent = attr->indent;
+  indent = MAX (indent, - attr->left_margin - attr->block_indent);
+  pango_layout_set_indent (block->layout, indent * PANGO_SCALE);
+}
+
+static void
+swfdec_text_layout_apply_line_attributes (SwfdecTextBlock *block,
+    const SwfdecTextAttributes *attr)
+{
+  /* SWFDEC_TEXT_ATTRIBUTE_ALIGN */
+  switch (attr->align) {
+    case SWFDEC_TEXT_ALIGN_LEFT:
+      pango_layout_set_alignment (block->layout, PANGO_ALIGN_LEFT);
+      pango_layout_set_justify (block->layout, FALSE);
+      break;
+    case SWFDEC_TEXT_ALIGN_RIGHT:
+      pango_layout_set_alignment (block->layout, PANGO_ALIGN_RIGHT);
+      pango_layout_set_justify (block->layout, FALSE);
+      break;
+    case SWFDEC_TEXT_ALIGN_CENTER:
+      pango_layout_set_alignment (block->layout, PANGO_ALIGN_CENTER);
+      pango_layout_set_justify (block->layout, FALSE);
+      break;
+    case SWFDEC_TEXT_ALIGN_JUSTIFY:
+      pango_layout_set_alignment (block->layout, PANGO_ALIGN_LEFT);
+      pango_layout_set_justify (block->layout, TRUE);
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  /* SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT */
+  /* SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN */
+  /* SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN */
+  block->rect.x += attr->left_margin + attr->block_indent;
+  if (block->rect.width <= attr->left_margin + attr->block_indent + attr->right_margin) {
+    SWFDEC_FIXME ("out of horizontal space, what now?");
+    block->rect.width = 0;
+  } else {
+    block->rect.width -= attr->left_margin + attr->block_indent + attr->right_margin;
+  }
+
+  /* SWFDEC_TEXT_ATTRIBUTE_LEADING */
+  if (attr->leading > 0)
+    pango_layout_set_spacing (block->layout, attr->leading * PANGO_SCALE);
+  else
+    pango_layout_set_spacing (block->layout, (attr->leading - 1) * PANGO_SCALE);
+
+  /* SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS */
+  if (attr->n_tab_stops != 0) {
+    PangoTabArray *tabs = pango_tab_array_new (attr->n_tab_stops, TRUE);
+    guint i;
+    for (i = 0; i < attr->n_tab_stops; i++) {
+      pango_tab_array_set_tab (tabs, i, PANGO_TAB_LEFT, attr->tab_stops[i]);
+    }
+    pango_layout_set_tabs (block->layout, tabs);
+    pango_tab_array_free (tabs);
+  }
+}
+
+static void
+swfdec_text_layout_apply_attributes (PangoAttrList *list, const SwfdecTextAttributes *attr,
+    guint start, guint end)
+{
+  PangoAttribute *attribute;
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS (format_table); i++) {
+    if (format_table[i].create_attribute) {
+      attribute = format_table[i].create_attribute (attr);
+      attribute->start_index = start;
+      attribute->end_index = end;
+      pango_attr_list_change (list, attribute);
+    }
+  }
+}
+
+static const SwfdecTextBlock *
+swfdec_text_layout_create_paragraph (SwfdecTextLayout *layout, PangoContext *context,
+    const SwfdecTextBlock *last, gsize start, gsize end)
+{
+  SwfdecTextBufferIter *iter;
+  SwfdecTextBlock *block;
+  PangoAttrList *list;
+  const char *string;
+  const SwfdecTextAttributes *attr, *attr_next;
+  gsize new_block, start_next;
+  gboolean first = TRUE;
+  // TODO: kerning, display
+
+  string = swfdec_text_buffer_get_text (layout->text);
+  while (start != end) {
+    iter = swfdec_text_buffer_get_iter (layout->text, start);
+    attr = swfdec_text_buffer_iter_get_attributes (layout->text, iter);
+    new_block = end;
+
+    block = swfdec_text_block_new (context);
+    block->start = start;
+    block->rect.x = 0;
+    if (last) {
+      block->rect.y = last->rect.y + last->rect.height;
+      block->row = last->row + pango_layout_get_line_count (last->layout);
+    } else {
+      block->rect.y = 0;
+      block->row = 0;
+    }
+    if (layout->wrap_width == -1) {
+      block->rect.width = G_MAXINT;
+    } else {
+      block->rect.width = layout->wrap_width;
+    }
+    g_sequence_append (layout->blocks, block);
+
+    if (layout->password) {
+      /* requires sane line breaking so we can't just replace the text with * chars.
+       * A good idea might be using a custom font that replaces all chars upon 
+       * rendering, so Pango still thinks it uses proper utf-8 */
+      SWFDEC_FIXME ("implement password handling.");
+    }
+    pango_layout_set_text (block->layout, string + start, end - start);
+
+    if (first) {
+      swfdec_text_layout_apply_paragraph_attributes (layout, block, attr);
+      first = FALSE;
+    }
+    swfdec_text_layout_apply_line_attributes (block, attr);
+    if (layout->wrap_width != -1)
+      pango_layout_set_width (block->layout, block->rect.width);
+
+    iter = swfdec_text_buffer_get_iter (layout->text, start);
+    attr = swfdec_text_buffer_iter_get_attributes (layout->text, iter);
+    list = pango_attr_list_new ();
+
+    while (start != end) {
+      g_assert (attr);
+      iter = swfdec_text_buffer_iter_next (layout->text, iter);
+      attr_next = iter ? swfdec_text_buffer_iter_get_attributes (layout->text, iter) : NULL;
+      start_next = iter ? swfdec_text_buffer_iter_get_start (layout->text, iter) :
+	swfdec_text_buffer_get_length (layout->text);
+      start_next = MIN (start_next, end);
+      
+      swfdec_text_layout_apply_attributes (list, attr, start - block->start, start_next - block->start);
+
+      if (attr_next && new_block > start_next &&
+	  (swfdec_text_attributes_diff (attr, attr_next) & SWFDEC_TEXT_ATTRIBUTES_MASK_NEW_BLOCK))
+	new_block = start_next;
+
+      attr = attr_next;
+      start = start_next;
+    }
+    pango_layout_set_attributes (block->layout, list);
+    pango_attr_list_unref (list);
+    
+    if (new_block < end) {
+      int i;
+
+      for (i = 0; i < pango_layout_get_line_count (block->layout); i++) {
+	PangoLayoutLine *line = pango_layout_get_line_readonly (block->layout, i);
+	if ((gsize) line->start_index + line->length >= new_block) {
+	  new_block = line->start_index + line->length;
+	  /* I hope deleting lines actually works by removing text */
+	  pango_layout_set_text (block->layout, string + start, new_block - start);
+	  g_assert (i + 1 == pango_layout_get_line_count (block->layout));
+	  break;
+	}
+      }
+      /* check that we have found a line */
+      g_assert (i < pango_layout_get_line_count (block->layout));
+    }
+
+    start = new_block;
+    pango_layout_get_pixel_size (block->layout, &block->rect.width, &block->rect.height);
+    /* add leading to last line, too */
+    block->rect.height += pango_layout_get_spacing (block->layout) / PANGO_SCALE;
+
+    last = block;
+  }
+
+  return last;
+}
+
+static void
+swfdec_text_layout_create (SwfdecTextLayout *layout)
+{
+  const char *p, *end, *string;
+  PangoContext *context;
+  PangoFontMap *map;
+  const SwfdecTextBlock *last = NULL;
+
+  map = pango_cairo_font_map_get_default ();
+  context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (map));
+
+  p = string = swfdec_text_buffer_get_text (layout->text);
+  for (;;) {
+    end = strpbrk (p, "\r\n");
+    if (end == NULL) {
+      end = string + swfdec_text_buffer_get_length (layout->text);
+    }
+
+    last = swfdec_text_layout_create_paragraph (layout, context, last, p - string, end - string);
+
+    if (*end == '\0')
+      break;
+    p = end + 1;
+  }
+
+  g_object_unref (context);
+}
+
+static void
+swfdec_text_layout_ensure (SwfdecTextLayout *layout)
+{
+  if (!g_sequence_iter_is_end (g_sequence_get_begin_iter (layout->blocks)))
+    return;
+
+  if (swfdec_text_buffer_get_length (layout->text) == 0)
+    return;
+
+  swfdec_text_layout_create (layout);
+}
+
+static void
+swfdec_text_layout_invalidate (SwfdecTextLayout *layout)
+{
+  g_sequence_remove_range (g_sequence_get_begin_iter (layout->blocks),
+    g_sequence_get_end_iter (layout->blocks));
+  g_signal_emit (layout, signals[CHANGED], 0);
+}
+
+/*** LAYOUT ***/
+
+/* A layout represents the whole text of a TextFieldMovie, this includes the
+ * invisible parts. It also does not care about transformations.
+ * It's the job of the movie to take care of that. */
+G_DEFINE_TYPE (SwfdecTextLayout, swfdec_text_layout, G_TYPE_OBJECT)
+
+static void
+swfdec_text_layout_dispose (GObject *object)
+{
+  SwfdecTextLayout *layout = SWFDEC_TEXT_LAYOUT (object);
+
+  g_object_unref (layout->text);
+  layout->text = NULL;
+  g_sequence_free (layout->blocks);
+  layout->blocks = NULL;
+
+  G_OBJECT_CLASS (swfdec_text_layout_parent_class)->dispose (object);
+}
+
+static void
+swfdec_text_layout_class_init (SwfdecTextLayoutClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = swfdec_text_layout_dispose;
+
+  signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+      G_TYPE_NONE, 0);
+}
+
+static void
+swfdec_text_layout_init (SwfdecTextLayout *layout)
+{
+  layout->wrap_width = -1;
+  layout->blocks = g_sequence_new (swfdec_text_block_free);
+}
+
+SwfdecTextLayout *
+swfdec_text_layout_new (SwfdecTextBuffer *buffer)
+{
+  SwfdecTextLayout *layout;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
+
+  layout = g_object_new (SWFDEC_TYPE_TEXT_LAYOUT, NULL);
+  layout->text = g_object_ref (buffer);
+
+  return layout;
+}
+
+void
+swfdec_text_layout_set_wrap_width (SwfdecTextLayout *layout, int wrap_width)
+{
+  g_return_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout));
+  g_return_if_fail (wrap_width >= -1);
+
+  if (layout->wrap_width == wrap_width)
+    return;
+
+  swfdec_text_layout_invalidate (layout);
+  layout->wrap_width = wrap_width;
+}
+
+int
+swfdec_text_layout_get_wrap_width (SwfdecTextLayout *layout)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), -1);
+
+  return layout->wrap_width;
+}
+
+void
+swfdec_text_layout_set_password (SwfdecTextLayout *layout, gboolean password)
+{
+  g_return_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout));
+
+  if (layout->password == password)
+    return;
+
+  swfdec_text_layout_invalidate (layout);
+  layout->password = password;
+}
+
+guint
+swfdec_text_layout_get_n_rows (SwfdecTextLayout *layout)
+{
+  GSequenceIter *iter;
+  SwfdecTextBlock *block;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), 0);
+
+  swfdec_text_layout_ensure (layout);
+
+  iter = g_sequence_iter_prev (g_sequence_get_end_iter (layout->blocks));
+  if (g_sequence_iter_is_end (iter))
+    return 0;
+
+  block = g_sequence_get (iter);
+  return block->row + pango_layout_get_line_count (block->layout);
+}
+
+gboolean
+swfdec_text_layout_get_password (SwfdecTextLayout *layout)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout), FALSE);
+
+  return layout->password;
+}
+
+static GSequenceIter *
+swfdec_text_layout_find_row (SwfdecTextLayout *layout, guint row)
+{
+  GSequenceIter *begin, *end, *mid;
+  SwfdecTextBlock *cur;
+
+  begin = g_sequence_get_begin_iter (layout->blocks);
+  end = g_sequence_iter_prev (g_sequence_get_end_iter (layout->blocks));
+  while (begin != end) {
+    mid = g_sequence_range_get_midpoint (begin, end); 
+    if (mid == begin)
+      mid = g_sequence_iter_next (mid);
+    cur = g_sequence_get (mid);
+    if (cur->row > row)
+      end = g_sequence_iter_prev (mid);
+    else
+      begin = mid;
+  }
+  return begin;
+}
+
+/**
+ * swfdec_text_layout_render:
+ * @layout: the layout to render
+ * @cr: the Cairo context to render to. The context will be transformed so that
+ *      (0, 0) points to where the @row should be rendered.
+ * @ctrans: The color transform to apply.
+ * @row: index of the first row to render.
+ * @height: The height in pixels of the visible area.
+ * @inval: The invalid area.
+ *
+ * Renders the contents of the layout into the given Cairo context.
+ **/
+void
+swfdec_text_layout_render (SwfdecTextLayout *layout, cairo_t *cr, 
+    const SwfdecColorTransform *ctrans, guint row, guint height,
+    const SwfdecRectangle *inval)
+{
+  GSequenceIter *iter;
+  SwfdecTextBlock *block;
+  PangoRectangle extents;
+  gboolean first_line = TRUE;
+
+  g_return_if_fail (SWFDEC_IS_TEXT_LAYOUT (layout));
+  g_return_if_fail (cr != NULL);
+  g_return_if_fail (ctrans != NULL);
+  g_return_if_fail (row < swfdec_text_layout_get_n_rows (layout));
+  g_return_if_fail (inval != NULL);
+  
+  swfdec_text_layout_ensure (layout);
+
+  iter = swfdec_text_layout_find_row (layout, row); 
+  block = g_sequence_get (iter);
+  row -= block->row;
+  while (!g_sequence_iter_is_end (iter)) {
+    block = g_sequence_get (iter);
+    pango_cairo_update_layout (cr, block->layout);
+    cairo_translate (cr, block->rect.x, 0);
+    if (block->bullet && row == 0) {
+      SWFDEC_FIXME ("render bullet");
+    }
+    for (;row < (guint) pango_layout_get_line_count (block->layout); row++) {
+      PangoLayoutLine *line = pango_layout_get_line_readonly (block->layout, row);
+      
+      pango_layout_line_get_pixel_extents (line, NULL, &extents);
+      if (extents.height > (int) height && !first_line)
+	return;
+      first_line = FALSE;
+      cairo_translate (cr, 0, - extents.y);
+      pango_cairo_show_layout_line (cr, line);
+      height -= extents.height;
+      cairo_translate (cr, 0, extents.height + extents.y);
+    }
+    cairo_translate (cr, -block->rect.x, pango_layout_get_spacing (block->layout) / PANGO_SCALE);
+    row = 0;
+    iter = g_sequence_iter_next (iter);
+  }
+}
+
diff --git a/swfdec/swfdec_text_layout.h b/swfdec/swfdec_text_layout.h
new file mode 100644
index 0000000..cd68999
--- /dev/null
+++ b/swfdec/swfdec_text_layout.h
@@ -0,0 +1,79 @@
+/* Swfdec
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
+ *                    2007 Pekka Lampila <pekka.lampila at iki.fi>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_TEXT_LAYOUT_H_
+#define _SWFDEC_TEXT_LAYOUT_H_
+
+#include <swfdec_rectangle.h>
+#include <swfdec_renderer.h>
+#include <swfdec_text_buffer.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SwfdecTextLayout SwfdecTextLayout;
+typedef struct _SwfdecTextLayoutClass SwfdecTextLayoutClass;
+
+#define SWFDEC_TYPE_TEXT_LAYOUT                    (swfdec_text_layout_get_type())
+#define SWFDEC_IS_TEXT_LAYOUT(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_TEXT_LAYOUT))
+#define SWFDEC_IS_TEXT_LAYOUT_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_TEXT_LAYOUT))
+#define SWFDEC_TEXT_LAYOUT(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_TEXT_LAYOUT, SwfdecTextLayout))
+#define SWFDEC_TEXT_LAYOUT_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_TEXT_LAYOUT, SwfdecTextLayoutClass))
+#define SWFDEC_TEXT_LAYOUT_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_TEXT_LAYOUT, SwfdecTextLayoutClass))
+
+struct _SwfdecTextLayout
+{
+  GObject		object;
+
+  /* constant data */
+  SwfdecTextBuffer *	text;		/* the text we render */
+  int			wrap_width;	/* maximum width to use */
+  gboolean		password;	/* TRUE if the text should be displayed as asterisks */
+
+  /* layout data */
+  GSequence *		blocks;		/* ordered list of blocks */
+};
+
+struct _SwfdecTextLayoutClass
+{
+  GObjectClass		object_class;
+};
+
+GType			swfdec_text_layout_get_type		(void);
+
+SwfdecTextLayout *	swfdec_text_layout_new			(SwfdecTextBuffer *	buffer);
+
+void			swfdec_text_layout_set_wrap_width	(SwfdecTextLayout *	layout,
+								 int			wrap_width);
+int			swfdec_text_layout_get_wrap_width	(SwfdecTextLayout *	layout);
+void			swfdec_text_layout_set_password		(SwfdecTextLayout *	layout,
+								 gboolean		password);
+gboolean		swfdec_text_layout_get_password		(SwfdecTextLayout *	layout);
+guint			swfdec_text_layout_get_n_rows		(SwfdecTextLayout *	layout);
+
+void			swfdec_text_layout_render		(SwfdecTextLayout *	layout,
+								 cairo_t *		cr, 
+								 const SwfdecColorTransform *ctrans,
+								 guint			row,
+								 guint			height,
+								 const SwfdecRectangle *inval);
+
+
+G_END_DECLS
+#endif
commit 9d6fc3d372011377a22eeb318aa2228118e17c21
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 12:24:09 2008 +0200

    add another test

diff --git a/test/image/Makefile.am b/test/image/Makefile.am
index 43084df..375d9d7 100644
--- a/test/image/Makefile.am
+++ b/test/image/Makefile.am
@@ -377,7 +377,16 @@ EXTRA_DIST = \
 	text-field-autosize-7.swf.png \
 	text-field-autosize-8.swf \
 	text-field-autosize-8.swf.png \
-	text-field-border-5.swf
+	text-field-background-5.swf \
+	text-field-background-5.swf.png \
+	text-field-background-6.swf \
+	text-field-background-6.swf.png \
+	text-field-background-7.swf \
+	text-field-background-7.swf.png \
+	text-field-background-8.swf \
+	text-field-background-8.swf.png \
+	text-field-background.as \
+	text-field-border-5.swf \
 	text-field-border-5.swf.png \
 	text-field-border-6.swf \
 	text-field-border-6.swf.png \
diff --git a/test/image/text-field-background-5.swf b/test/image/text-field-background-5.swf
new file mode 100644
index 0000000..6ef2f2c
Binary files /dev/null and b/test/image/text-field-background-5.swf differ
diff --git a/test/image/text-field-background-5.swf.png b/test/image/text-field-background-5.swf.png
new file mode 100644
index 0000000..3506302
Binary files /dev/null and b/test/image/text-field-background-5.swf.png differ
diff --git a/test/image/text-field-background-6.swf b/test/image/text-field-background-6.swf
new file mode 100644
index 0000000..6b2f06a
Binary files /dev/null and b/test/image/text-field-background-6.swf differ
diff --git a/test/image/text-field-background-6.swf.png b/test/image/text-field-background-6.swf.png
new file mode 100644
index 0000000..641cb9e
Binary files /dev/null and b/test/image/text-field-background-6.swf.png differ
diff --git a/test/image/text-field-background-7.swf b/test/image/text-field-background-7.swf
new file mode 100644
index 0000000..3fd128c
Binary files /dev/null and b/test/image/text-field-background-7.swf differ
diff --git a/test/image/text-field-background-7.swf.png b/test/image/text-field-background-7.swf.png
new file mode 100644
index 0000000..0345295
Binary files /dev/null and b/test/image/text-field-background-7.swf.png differ
diff --git a/test/image/text-field-background-8.swf b/test/image/text-field-background-8.swf
new file mode 100644
index 0000000..024a781
Binary files /dev/null and b/test/image/text-field-background-8.swf differ
diff --git a/test/image/text-field-background-8.swf.png b/test/image/text-field-background-8.swf.png
new file mode 100644
index 0000000..2922738
Binary files /dev/null and b/test/image/text-field-background-8.swf.png differ
diff --git a/test/image/text-field-background.as b/test/image/text-field-background.as
new file mode 100644
index 0000000..ca6c520
--- /dev/null
+++ b/test/image/text-field-background.as
@@ -0,0 +1,18 @@
+// makeswf -v 7 -s 200x150 -r 1 -o movie23.swf movie23.as
+
+function create (depth, x, y) {
+  var t = createTextField ("t" + depth, depth, x, y, 50, 25);
+  var t = this["t" + depth];
+  t.background = true;
+  t.backgroundColor = 0xFF0000;
+};
+
+create (0, 25, 25);
+t0.border = true;
+create (1, 100.5, 25);
+create (2, 25, 75);
+t2._y += 0.4;
+create (3, 100.5, 75);
+t3._y += 0.5;
+t3.border = true;
+
commit 07a9f1b43eb99757febe93ea0f17c8b4cb3c8d7f
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed May 7 12:22:56 2008 +0200

    add a test about border handling

diff --git a/test/image/Makefile.am b/test/image/Makefile.am
index df1ee06..43084df 100644
--- a/test/image/Makefile.am
+++ b/test/image/Makefile.am
@@ -377,6 +377,15 @@ EXTRA_DIST = \
 	text-field-autosize-7.swf.png \
 	text-field-autosize-8.swf \
 	text-field-autosize-8.swf.png \
+	text-field-border-5.swf
+	text-field-border-5.swf.png \
+	text-field-border-6.swf \
+	text-field-border-6.swf.png \
+	text-field-border-7.swf \
+	text-field-border-7.swf.png \
+	text-field-border-8.swf \
+	text-field-border-8.swf.png \
+	text-field-border.as \
 	text-field-color-transform.as \
 	text-field-color-transform-6.swf \
 	text-field-color-transform-6.swf.png \
diff --git a/test/image/text-field-border-5.swf b/test/image/text-field-border-5.swf
new file mode 100644
index 0000000..fd2969f
Binary files /dev/null and b/test/image/text-field-border-5.swf differ
diff --git a/test/image/text-field-border-5.swf.png b/test/image/text-field-border-5.swf.png
new file mode 100644
index 0000000..66130d9
Binary files /dev/null and b/test/image/text-field-border-5.swf.png differ
diff --git a/test/image/text-field-border-6.swf b/test/image/text-field-border-6.swf
new file mode 100644
index 0000000..c0d6e2a
Binary files /dev/null and b/test/image/text-field-border-6.swf differ
diff --git a/test/image/text-field-border-6.swf.png b/test/image/text-field-border-6.swf.png
new file mode 100644
index 0000000..63a2a4b
Binary files /dev/null and b/test/image/text-field-border-6.swf.png differ
diff --git a/test/image/text-field-border-7.swf b/test/image/text-field-border-7.swf
new file mode 100644
index 0000000..58cfbc8
Binary files /dev/null and b/test/image/text-field-border-7.swf differ
diff --git a/test/image/text-field-border-7.swf.png b/test/image/text-field-border-7.swf.png
new file mode 100644
index 0000000..d880762
Binary files /dev/null and b/test/image/text-field-border-7.swf.png differ
diff --git a/test/image/text-field-border-8.swf b/test/image/text-field-border-8.swf
new file mode 100644
index 0000000..4149111
Binary files /dev/null and b/test/image/text-field-border-8.swf differ
diff --git a/test/image/text-field-border-8.swf.png b/test/image/text-field-border-8.swf.png
new file mode 100644
index 0000000..5f05547
Binary files /dev/null and b/test/image/text-field-border-8.swf.png differ
diff --git a/test/image/text-field-border.as b/test/image/text-field-border.as
new file mode 100644
index 0000000..c435c1d
--- /dev/null
+++ b/test/image/text-field-border.as
@@ -0,0 +1,15 @@
+// makeswf -v 7 -s 200x150 -r 1 -o text-field-border.swf text-field-border.as
+
+function create (depth, x, y) {
+  var t = createTextField ("t" + depth, depth, x, y, 50, 25);
+  t.border = true;
+  t.borderColor = 0;
+};
+
+create (0, 25, 25);
+create (1, 100.5, 25);
+create (2, 25, 75);
+t2._y += 0.4;
+create (3, 100.5, 75);
+t3._y += 0.5;
+
commit 057773835d9d4ea87a86a555988fdbce5ecf31ff
Author: Benjamin Otte <otte at gnome.org>
Date:   Mon May 5 19:16:59 2008 +0200

    add a text-changed signal
    
    also contains some obvious bugfixes

diff --git a/swfdec/swfdec_text_buffer.c b/swfdec/swfdec_text_buffer.c
index 5efd95f..afd0af7 100644
--- a/swfdec/swfdec_text_buffer.c
+++ b/swfdec/swfdec_text_buffer.c
@@ -55,6 +55,7 @@ swfdec_text_buffer_format_free (SwfdecTextBufferFormat *format)
 /*** OBJECT ***/
 
 enum {
+  TEXT_CHANGED,
   CURSOR_CHANGED,
   LAST_SIGNAL
 };
@@ -84,6 +85,9 @@ swfdec_text_buffer_class_init (SwfdecTextBufferClass *klass)
 
   object_class->dispose = swfdec_text_buffer_dispose;
 
+  signals[TEXT_CHANGED] = g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+      G_TYPE_NONE, 0);
   signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__ULONG_ULONG,
       G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_ULONG);
@@ -156,6 +160,8 @@ swfdec_text_buffer_insert_text (SwfdecTextBuffer *buffer,
   g_return_if_fail (text != NULL);
 
   len = strlen (text);
+  if (len == 0)
+    return;
   g_string_insert_len (buffer->text, pos, text, len);
   iter = g_sequence_get_end_iter (buffer->attributes);
   for (;;) {
@@ -171,7 +177,11 @@ swfdec_text_buffer_insert_text (SwfdecTextBuffer *buffer,
     buffer->cursor_start += len;
   if (buffer->cursor_end >= pos)
     buffer->cursor_end += len;
-  /* FIXME: emit cursor-changed signal? */
+
+  g_signal_emit (buffer, signals[TEXT_CHANGED], 0);
+  g_signal_emit (buffer, signals[CURSOR_CHANGED], 0,
+      (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
+      (gulong) MAX (buffer->cursor_start, buffer->cursor_end));
 }
 
 /* removes duplicates in the range [iter, end) */
@@ -236,7 +246,11 @@ swfdec_text_buffer_delete_text (SwfdecTextBuffer *buffer,
     buffer->cursor_end -= length;
   else if (buffer->cursor_end > pos)
     buffer->cursor_end = pos;
-  /* FIXME: emit cursor-changed signal? */
+
+  g_signal_emit (buffer, signals[TEXT_CHANGED], 0);
+  g_signal_emit (buffer, signals[CURSOR_CHANGED], 0,
+      (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
+      (gulong) MAX (buffer->cursor_start, buffer->cursor_end));
 }
 
 const char *
@@ -352,6 +366,8 @@ swfdec_text_buffer_set_attributes (SwfdecTextBuffer *buffer, gsize start,
   }
   swfdec_text_attributes_copy (&format->attr, attr, mask);
   swfdec_text_buffer_remove_duplicates (start_iter, iter);
+
+  g_signal_emit (buffer, signals[TEXT_CHANGED], 0);
 }
 
 /*** ITERATOR ***/
@@ -441,7 +457,7 @@ swfdec_text_buffer_set_cursor (SwfdecTextBuffer *buffer, gsize start, gsize end)
 
   buffer->cursor_start = start;
   buffer->cursor_end = end;
-  g_signal_emit (buffer, signals[CURSOR_CHANGED],
+  g_signal_emit (buffer, signals[CURSOR_CHANGED], 0,
       (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
       (gulong) MAX (buffer->cursor_start, buffer->cursor_end));
 }
diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 44bbcc3..62bd5a4 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -1529,12 +1529,6 @@ swfdec_text_field_movie_focus_out (SwfdecActor *actor)
 }
 
 static void
-swfdec_text_field_movie_changed (SwfdecTextFieldMovie *text)
-{
-  text->changed++;
-}
-
-static void
 swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint character)
 {
   SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (actor);
@@ -1543,7 +1537,7 @@ swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint char
   gsize start, end;
   const char *string;
 #define BACKWARD(text, _index) ((_index) == 0 ? 0 : (gsize) (g_utf8_prev_char ((text) + (_index)) - (text)))
-#define FORWARD(text, _index) ((text)[0] == 0 ? (_index) : (gsize) (g_utf8_next_char ((text) + (_index)) - (text)))
+#define FORWARD(text, _index) ((text)[_index] == 0 ? (_index) : (gsize) (g_utf8_next_char ((text) + (_index)) - (text)))
 
   if (!text->editable)
     return;
@@ -1574,7 +1568,6 @@ swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint char
       }
       if (start != end) {
 	swfdec_text_buffer_delete_text (text->text, start, end - start);
-	swfdec_text_field_movie_changed (text);
       }
       return;
     case SWFDEC_KEY_DELETE:
@@ -1583,7 +1576,6 @@ swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint char
       }
       if (start != end) {
 	swfdec_text_buffer_delete_text (text->text, start, end - start);
-	swfdec_text_field_movie_changed (text);
       }
       return;
     default:
@@ -1598,7 +1590,6 @@ swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint char
     if (start != end)
       swfdec_text_buffer_delete_text (text->text, start, end - start);
     swfdec_text_buffer_insert_text (text->text, start, insert);
-    swfdec_text_field_movie_changed (text);
   }
 }
 
@@ -1641,6 +1632,14 @@ swfdec_text_field_movie_class_init (SwfdecTextFieldMovieClass * g_class)
 }
 
 static void
+swfdec_text_field_movie_text_changed (SwfdecTextBuffer *buffer, 
+    SwfdecTextFieldMovie *text)
+{
+  swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
+  text->changed++;
+}
+
+static void
 swfdec_text_field_movie_cursor_changed (SwfdecTextBuffer *buffer, 
     gulong start, gulong end, SwfdecTextFieldMovie *text)
 {
@@ -1654,6 +1653,8 @@ swfdec_text_field_movie_init (SwfdecTextFieldMovie *text)
   text->text = swfdec_text_buffer_new ();
   g_signal_connect (text->text, "cursor-changed", 
       G_CALLBACK (swfdec_text_field_movie_cursor_changed), text);
+  g_signal_connect (text->text, "text-changed", 
+      G_CALLBACK (swfdec_text_field_movie_text_changed), text);
   text->scroll = 1;
   text->mouse_wheel_enabled = TRUE;
 
commit 986bbac09c5b280415628f7c6e854647ffa5db35
Author: Benjamin Otte <otte at gnome.org>
Date:   Mon May 5 19:01:01 2008 +0200

    split text handling into its own object - SwfdecTextBuffer

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index 9b743a7..c91612f 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -147,6 +147,7 @@ libswfdec_source_files = \
 	swfdec_tag.c \
 	swfdec_text.c \
 	swfdec_text_attributes.c \
+	swfdec_text_buffer.c \
 	swfdec_text_field.c \
 	swfdec_text_field_movie.c \
 	swfdec_text_field_movie_as.c \
@@ -295,6 +296,7 @@ noinst_HEADERS = \
 	swfdec_tag.h \
 	swfdec_text.h \
 	swfdec_text_attributes.h \
+	swfdec_text_buffer.h \
 	swfdec_text_format.h \
 	swfdec_types.h \
 	swfdec_utils.h \
diff --git a/swfdec/swfdec_marshal.list b/swfdec/swfdec_marshal.list
index 5aeeb44..20eba3c 100644
--- a/swfdec/swfdec_marshal.list
+++ b/swfdec/swfdec_marshal.list
@@ -4,3 +4,4 @@ VOID:BOXED,POINTER,UINT
 VOID:ENUM,STRING,STRING,BOXED
 VOID:STRING,STRING
 VOID:ULONG,UINT
+VOID:ULONG,ULONG
diff --git a/swfdec/swfdec_text_buffer.c b/swfdec/swfdec_text_buffer.c
new file mode 100644
index 0000000..5efd95f
--- /dev/null
+++ b/swfdec/swfdec_text_buffer.c
@@ -0,0 +1,448 @@
+/* Swfdec
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
+ *               2007 Pekka Lampila <pekka.lampila at iki.fi>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "swfdec_text_buffer.h"
+#include "swfdec_debug.h"
+#include "swfdec_marshal.h"
+
+typedef struct _SwfdecTextBufferFormat SwfdecTextBufferFormat;
+
+struct _SwfdecTextBufferFormat {
+  guint			start;
+  SwfdecTextAttributes	attr;
+};
+
+static SwfdecTextBufferFormat *
+swfdec_text_buffer_format_new (void)
+{
+  SwfdecTextBufferFormat *format;
+  
+  format = g_slice_new0 (SwfdecTextBufferFormat);
+  swfdec_text_attributes_reset (&format->attr);
+  return format;
+}
+
+static void
+swfdec_text_buffer_format_free (SwfdecTextBufferFormat *format)
+{
+  swfdec_text_attributes_reset (&format->attr);
+  g_slice_free (SwfdecTextBufferFormat, format);
+}
+
+/*** OBJECT ***/
+
+enum {
+  CURSOR_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (SwfdecTextBuffer, swfdec_text_buffer, G_TYPE_OBJECT)
+
+static void
+swfdec_text_buffer_dispose (GObject *object)
+{
+  SwfdecTextBuffer *buffer = SWFDEC_TEXT_BUFFER (object);
+
+  if (buffer->text != NULL) {
+    g_string_free (buffer->text, TRUE);
+    buffer->text = NULL;
+  }
+  g_sequence_free (buffer->attributes);
+
+  G_OBJECT_CLASS (swfdec_text_buffer_parent_class)->dispose (object);
+}
+
+static void
+swfdec_text_buffer_class_init (SwfdecTextBufferClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = swfdec_text_buffer_dispose;
+
+  signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__ULONG_ULONG,
+      G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_ULONG);
+}
+
+static void
+swfdec_text_buffer_init (SwfdecTextBuffer *text)
+{
+  text->text = g_string_new ("");
+  text->attributes = g_sequence_new ((GDestroyNotify) swfdec_text_buffer_format_free);
+  g_sequence_append (text->attributes, swfdec_text_buffer_format_new ());
+}
+
+SwfdecTextBuffer *
+swfdec_text_buffer_new (void)
+{
+  return g_object_new (SWFDEC_TYPE_TEXT_BUFFER, NULL);
+}
+
+static void
+swfdec_text_buffer_mark_one (gpointer entry, gpointer unused)
+{
+  SwfdecTextBufferFormat *format = entry;
+
+  swfdec_text_attributes_mark (&format->attr);
+}
+
+void
+swfdec_text_buffer_mark (SwfdecTextBuffer *buffer)
+{
+  g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
+
+  g_sequence_foreach (buffer->attributes, swfdec_text_buffer_mark_one, NULL);
+}
+
+static int
+swfdec_text_buffer_format_compare (gconstpointer a, gconstpointer b, gpointer unused)
+{
+  return ((const SwfdecTextBufferFormat *) a)->start - 
+    ((const SwfdecTextBufferFormat *) b)->start;
+}
+
+/* return iter to format relevant for position, that is the format pointed to
+ * by iter will have a lower or equal start index than pos */
+static GSequenceIter *
+swfdec_text_buffer_get_iter_for_pos (SwfdecTextBuffer *buffer, guint pos)
+{
+  SwfdecTextBufferFormat format = { pos, };
+  GSequenceIter *iter;
+
+  iter = g_sequence_search (buffer->attributes, &format,
+      swfdec_text_buffer_format_compare, NULL);
+  if (g_sequence_iter_is_end (iter) ||
+      ((SwfdecTextBufferFormat *) g_sequence_get (iter))->start > pos)
+    iter = g_sequence_iter_prev (iter);
+
+  return iter;
+}
+
+void
+swfdec_text_buffer_insert_text (SwfdecTextBuffer *buffer,
+    gsize pos, const char *text)
+{
+  gsize len;
+  GSequenceIter *iter;
+  SwfdecTextBufferFormat *format;
+
+  g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
+  g_return_if_fail (pos <= buffer->text->len);
+  g_return_if_fail (text != NULL);
+
+  len = strlen (text);
+  g_string_insert_len (buffer->text, pos, text, len);
+  iter = g_sequence_get_end_iter (buffer->attributes);
+  for (;;) {
+    iter = g_sequence_iter_prev (iter);
+    format = g_sequence_get (iter);
+    if (format->start <= pos)
+      break;
+    format->start += len;
+  }
+
+  /* adapt cursor */
+  if (buffer->cursor_start >= pos)
+    buffer->cursor_start += len;
+  if (buffer->cursor_end >= pos)
+    buffer->cursor_end += len;
+  /* FIXME: emit cursor-changed signal? */
+}
+
+/* removes duplicates in the range [iter, end) */
+static void
+swfdec_text_buffer_remove_duplicates (GSequenceIter *iter, GSequenceIter *end)
+{
+  SwfdecTextBufferFormat *format, *next;
+
+  g_assert (iter != end);
+
+  format = g_sequence_get (iter);
+  for (iter = g_sequence_iter_next (iter); iter != end; iter = g_sequence_iter_next (iter)) {
+    next = g_sequence_get (iter);
+    if (swfdec_text_attributes_diff (&format->attr, &next->attr) == 0) {
+      GSequenceIter *prev = g_sequence_iter_prev (iter);
+      g_sequence_remove (iter);
+      iter = prev;
+    } else {
+      format = next;
+    }
+  }
+}
+
+void
+swfdec_text_buffer_delete_text (SwfdecTextBuffer *buffer, 
+    gsize pos, gsize length)
+{
+  GSequenceIter *iter, *prev;
+  SwfdecTextBufferFormat *format;
+  gsize end;
+
+  g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
+  g_return_if_fail (pos + length <= buffer->text->len);
+  g_return_if_fail (length > 0);
+
+  g_string_erase (buffer->text, pos, length);
+  end = pos + length;
+  iter = g_sequence_get_end_iter (buffer->attributes);
+  for (;;) {
+    iter = g_sequence_iter_prev (iter);
+    format = g_sequence_get (iter);
+    if (format->start <= end) {
+      format->start = pos;
+      break;
+    }
+    format->start -= length;
+  }
+  while (!g_sequence_iter_is_begin (iter)) {
+    prev = g_sequence_iter_prev (iter);
+    format = g_sequence_get (prev);
+    if (format->start < pos)
+      break;
+    g_sequence_remove (prev);
+  }
+
+  /* adapt cursor */
+  if (buffer->cursor_start > end)
+    buffer->cursor_start -= length;
+  else if (buffer->cursor_start > pos)
+    buffer->cursor_start = pos;
+  if (buffer->cursor_end > end)
+    buffer->cursor_end -= length;
+  else if (buffer->cursor_end > pos)
+    buffer->cursor_end = pos;
+  /* FIXME: emit cursor-changed signal? */
+}
+
+const char *
+swfdec_text_buffer_get_text (SwfdecTextBuffer *buffer)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
+  
+  return buffer->text->str;
+}
+
+gsize
+swfdec_text_buffer_get_length (SwfdecTextBuffer *buffer)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
+  
+  return buffer->text->len;
+}
+
+const SwfdecTextAttributes *
+swfdec_text_buffer_get_attributes (SwfdecTextBuffer *buffer, gsize pos)
+{
+  GSequenceIter *iter;
+  SwfdecTextBufferFormat *format;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (pos <= buffer->text->len, NULL);
+
+  iter = swfdec_text_buffer_get_iter_for_pos (buffer, pos);
+  format = g_sequence_get (iter);
+  return &format->attr;
+}
+
+guint
+swfdec_text_buffer_get_unique (SwfdecTextBuffer *buffer, 
+    gsize start, gsize length)
+{
+  guint result = SWFDEC_TEXT_ATTRIBUTES_MASK;
+  SwfdecTextBufferFormat *format, *cur;
+  GSequenceIter *iter;
+  gsize end;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
+  g_return_val_if_fail (start + length <= buffer->text->len, 0);
+
+  end = start + length;
+  iter = swfdec_text_buffer_get_iter_for_pos (buffer, start);
+  format = g_sequence_get (iter);
+  for (iter = g_sequence_iter_next (iter); !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+    cur = g_sequence_get (iter);
+    if (cur->start >= end)
+      break;
+    result &= ~swfdec_text_attributes_diff (&format->attr, &cur->attr);
+  }
+
+  return result;
+}
+
+static GSequenceIter *
+swfdec_text_buffer_copy_format (SwfdecTextBuffer *buffer, GSequenceIter *iter, 
+    gsize pos)
+{
+  SwfdecTextBufferFormat *format, *new_format;
+ 
+  format = g_sequence_get (iter);
+  new_format = swfdec_text_buffer_format_new ();
+  new_format->start = pos;
+  swfdec_text_attributes_copy (&new_format->attr, &format->attr, 
+      SWFDEC_TEXT_ATTRIBUTES_MASK);
+  return g_sequence_insert_before (g_sequence_iter_next (iter), new_format);
+}
+
+void
+swfdec_text_buffer_set_attributes (SwfdecTextBuffer *buffer, gsize start,
+    gsize length, const SwfdecTextAttributes *attr, guint mask)
+{
+  SwfdecTextBufferFormat *format;
+  GSequenceIter *start_iter, *iter;
+  gsize end;
+  gboolean need_new_format, last;
+
+  g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
+  g_return_if_fail (start + length <= buffer->text->len);
+  g_return_if_fail (length > 0 || start == buffer->text->len);
+  g_return_if_fail (attr != NULL);
+  g_return_if_fail (mask != 0);
+
+  end = start + length;
+  start_iter = swfdec_text_buffer_get_iter_for_pos (buffer, start);
+  format = g_sequence_get (start_iter);
+  if (format->start < start) {
+    iter = swfdec_text_buffer_copy_format (buffer, start_iter, start);
+  } else {
+    iter = start_iter;
+  }
+  last = FALSE;
+  for (;;) {
+    format = g_sequence_get (iter);
+    iter = g_sequence_iter_next (iter);
+    if (g_sequence_iter_is_end (iter)) {
+      need_new_format = end < buffer->text->len;
+      last = TRUE;
+    } else {
+      SwfdecTextBufferFormat *next = g_sequence_get (iter);
+      need_new_format = next->start > end;
+      last = next->start >= end;
+    }
+    if (last)
+      break;
+    swfdec_text_attributes_copy (&format->attr, attr, mask);
+  }
+  if (need_new_format) {
+    swfdec_text_buffer_copy_format (buffer, g_sequence_iter_prev (iter), end);
+  }
+  swfdec_text_attributes_copy (&format->attr, attr, mask);
+  swfdec_text_buffer_remove_duplicates (start_iter, iter);
+}
+
+/*** ITERATOR ***/
+
+SwfdecTextBufferIter *
+swfdec_text_buffer_get_iter (SwfdecTextBuffer *buffer, gsize pos)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (pos <= buffer->text->len, NULL);
+
+  return swfdec_text_buffer_get_iter_for_pos (buffer, pos);
+}
+
+SwfdecTextBufferIter *
+swfdec_text_buffer_iter_next (SwfdecTextBuffer *buffer, SwfdecTextBufferIter *iter)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (iter != NULL, NULL);
+
+  iter = g_sequence_iter_next (iter);
+  return g_sequence_iter_is_end (iter) ? NULL : iter;
+}
+
+const SwfdecTextAttributes *
+swfdec_text_buffer_iter_get_attributes (SwfdecTextBuffer *buffer, SwfdecTextBufferIter *iter)
+{
+  SwfdecTextBufferFormat *format;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (iter != NULL, NULL);
+
+  format = g_sequence_get (iter);
+  return &format->attr;
+}
+
+gsize	
+swfdec_text_buffer_iter_get_start (SwfdecTextBuffer *buffer, SwfdecTextBufferIter *iter)
+{
+  SwfdecTextBufferFormat *format;
+
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
+  g_return_val_if_fail (iter != NULL, 0);
+
+  format = g_sequence_get (iter);
+  return format->start;
+}
+
+/*** CURSOR API ***/
+
+gsize
+swfdec_text_buffer_get_cursor (SwfdecTextBuffer *buffer)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), 0);
+
+  return buffer->cursor_start;
+}
+
+gboolean
+swfdec_text_buffer_has_selection (SwfdecTextBuffer *buffer)
+{
+  g_return_val_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer), FALSE);
+
+  return buffer->cursor_start != buffer->cursor_end;
+}
+
+void
+swfdec_text_buffer_get_selection (SwfdecTextBuffer *buffer, gsize *start, gsize *end)
+{
+  g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
+
+  if (start)
+    *start = MIN (buffer->cursor_start, buffer->cursor_end);
+  if (end)
+    *end = MAX (buffer->cursor_start, buffer->cursor_end);
+}
+
+void
+swfdec_text_buffer_set_cursor (SwfdecTextBuffer *buffer, gsize start, gsize end)
+{
+  g_return_if_fail (SWFDEC_IS_TEXT_BUFFER (buffer));
+  g_return_if_fail (start <= swfdec_text_buffer_get_length (buffer));
+  g_return_if_fail (end <= swfdec_text_buffer_get_length (buffer));
+
+  if (buffer->cursor_start == start &&
+      buffer->cursor_end == end)
+    return;
+
+  buffer->cursor_start = start;
+  buffer->cursor_end = end;
+  g_signal_emit (buffer, signals[CURSOR_CHANGED],
+      (gulong) MIN (buffer->cursor_start, buffer->cursor_end),
+      (gulong) MAX (buffer->cursor_start, buffer->cursor_end));
+}
+
diff --git a/swfdec/swfdec_text_buffer.h b/swfdec/swfdec_text_buffer.h
new file mode 100644
index 0000000..6dc661b
--- /dev/null
+++ b/swfdec/swfdec_text_buffer.h
@@ -0,0 +1,104 @@
+/* Swfdec
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
+ *               2007 Pekka Lampila <pekka.lampila at iki.fi>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_TEXT_BUFFER_H_
+#define _SWFDEC_TEXT_BUFFER_H_
+
+#include <glib-object.h>
+#include <swfdec/swfdec_text_attributes.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SwfdecTextBuffer SwfdecTextBuffer;
+typedef GSequenceIter SwfdecTextBufferIter;
+typedef struct _SwfdecTextBufferClass SwfdecTextBufferClass;
+
+#define SWFDEC_TYPE_TEXT_BUFFER                    (swfdec_text_buffer_get_type())
+#define SWFDEC_IS_TEXT_BUFFER(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_TEXT_BUFFER))
+#define SWFDEC_IS_TEXT_BUFFER_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_TEXT_BUFFER))
+#define SWFDEC_TEXT_BUFFER(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_TEXT_BUFFER, SwfdecTextBuffer))
+#define SWFDEC_TEXT_BUFFER_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_TEXT_BUFFER, SwfdecTextBufferClass))
+
+struct _SwfdecTextBuffer
+{
+  GObject		object;
+
+  GString *		text;		/* the text in this buffer */
+  GSequence *		attributes;	/* the attributes that apply to this text */
+
+  gsize			cursor_start;	/* byte offset into text for start of cursor */
+  gsize			cursor_end;	/* if some text is selected smaller or bigger */
+};
+
+struct _SwfdecTextBufferClass
+{
+  GObjectClass		object_class;
+};
+
+GType			swfdec_text_buffer_get_type		(void);
+
+SwfdecTextBuffer *	swfdec_text_buffer_new			(void);
+void			swfdec_text_buffer_mark			(SwfdecTextBuffer *	buffer);
+
+void			swfdec_text_buffer_insert_text	  	(SwfdecTextBuffer *	buffer,
+								 gsize			pos,
+							 	const char *		text);
+#define swfdec_text_buffer_append_text(buffer, text) \
+  swfdec_text_buffer_insert_text ((buffer), swfdec_text_buffer_get_length (buffer), (text));
+void			swfdec_text_buffer_delete_text	  	(SwfdecTextBuffer *	buffer,
+								 gsize			pos,
+							 	 gsize			length);
+
+const char *		swfdec_text_buffer_get_text		(SwfdecTextBuffer *	buffer);
+gsize			swfdec_text_buffer_get_length		(SwfdecTextBuffer *	buffer);
+const SwfdecTextAttributes *
+			swfdec_text_buffer_get_attributes	(SwfdecTextBuffer *	buffer,
+							 	 gsize			pos);
+void			swfdec_text_buffer_set_attributes	(SwfdecTextBuffer *	buffer, 
+								 gsize			start,
+								 gsize			length,
+								 const SwfdecTextAttributes *attr,
+								 guint			mask);
+guint			swfdec_text_buffer_get_unique		(SwfdecTextBuffer *	buffer, 
+								 gsize			start,
+								 gsize			length);
+
+SwfdecTextBufferIter *	swfdec_text_buffer_get_iter		(SwfdecTextBuffer *	buffer,
+								 gsize			pos);
+SwfdecTextBufferIter *	swfdec_text_buffer_iter_next		(SwfdecTextBuffer *	buffer,
+								 SwfdecTextBufferIter *	iter);
+const SwfdecTextAttributes *
+			swfdec_text_buffer_iter_get_attributes	(SwfdecTextBuffer *	buffer,
+								 SwfdecTextBufferIter *	iter);
+gsize			swfdec_text_buffer_iter_get_start	(SwfdecTextBuffer *	buffer,
+								 SwfdecTextBufferIter *	iter);
+
+gsize			swfdec_text_buffer_get_cursor		(SwfdecTextBuffer *	buffer);
+gboolean		swfdec_text_buffer_has_selection	(SwfdecTextBuffer *	buffer);
+void			swfdec_text_buffer_get_selection	(SwfdecTextBuffer *	buffer,
+								 gsize *		start,
+								 gsize *		end);
+void			swfdec_text_buffer_set_cursor		(SwfdecTextBuffer *	buffer,
+								 gsize			start,
+								 gsize			end);
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index dfb64fe..44bbcc3 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2006 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
  *               2007 Pekka Lampila <pekka.lampila at iki.fi>
  *
  * This library is free software; you can redistribute it and/or
@@ -42,45 +42,6 @@ G_DEFINE_TYPE (SwfdecTextFieldMovie, swfdec_text_field_movie, SWFDEC_TYPE_ACTOR)
 #define EXTRA_MARGIN 2
 #define BULLET_MARGIN 36
 
-/*** CURSOR API ***/
-
-static gsize
-swfdec_text_field_movie_get_cursor (SwfdecTextFieldMovie *text)
-{
-  return text->cursor_start;
-}
-
-static gboolean
-swfdec_text_field_movie_has_cursor (SwfdecTextFieldMovie *text)
-{
-  return text->cursor_start == text->cursor_end;
-}
-#define swfdec_text_field_movie_has_selection(text) (!swfdec_text_field_movie_has_cursor (text))
-
-static void
-swfdec_text_field_movie_get_selection (SwfdecTextFieldMovie *text, gsize *start, gsize *end)
-{
-  if (start)
-    *start = MIN (text->cursor_start, text->cursor_end);
-  if (end)
-    *end = MAX (text->cursor_start, text->cursor_end);
-}
-
-static void
-swfdec_text_field_movie_set_cursor (SwfdecTextFieldMovie *text, gsize start, gsize end)
-{
-  g_return_if_fail (start <= text->input->len);
-  g_return_if_fail (end <= text->input->len);
-
-  if (text->cursor_start == start &&
-      text->cursor_end == end)
-    return;
-
-  text->cursor_start = start;
-  text->cursor_end = end;
-  swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
-}
-
 /*** VFUNCS ***/
 
 static void
@@ -138,7 +99,7 @@ swfdec_text_paragraph_add_attribute (SwfdecParagraph *paragraph,
 
 static void
 swfdec_text_paragraph_add_block (SwfdecParagraph *paragraph, int index_,
-    SwfdecTextFormat *format)
+    const SwfdecTextAttributes *attr)
 {
   guint i;
   SwfdecBlock *block;
@@ -147,7 +108,7 @@ swfdec_text_paragraph_add_block (SwfdecParagraph *paragraph, int index_,
 
   block->index_ = index_;
 
-  switch (format->attr.align) {
+  switch (attr->align) {
     case SWFDEC_TEXT_ALIGN_LEFT:
       block->align = PANGO_ALIGN_LEFT;
       block->justify = FALSE;
@@ -167,16 +128,16 @@ swfdec_text_paragraph_add_block (SwfdecParagraph *paragraph, int index_,
     default:
       g_assert_not_reached ();
   }
-  block->leading = format->attr.leading * 20 * PANGO_SCALE;
-  block->block_indent = format->attr.block_indent * 20;
-  block->left_margin = format->attr.left_margin * 20;
-  block->right_margin = format->attr.right_margin * 20;
-
-  if (format->attr.n_tab_stops != 0) {
-    block->tab_stops = pango_tab_array_new (format->attr.n_tab_stops, TRUE);
-    for (i = 0; i < format->attr.n_tab_stops; i++) {
+  block->leading = attr->leading * 20 * PANGO_SCALE;
+  block->block_indent = attr->block_indent * 20;
+  block->left_margin = attr->left_margin * 20;
+  block->right_margin = attr->right_margin * 20;
+
+  if (attr->n_tab_stops != 0) {
+    block->tab_stops = pango_tab_array_new (attr->n_tab_stops, TRUE);
+    for (i = 0; i < attr->n_tab_stops; i++) {
       pango_tab_array_set_tab (block->tab_stops, i, PANGO_TAB_LEFT,
-	  format->attr.tab_stops[i] * 20);
+	  attr->tab_stops[i] * 20);
     }
   } else {
     block->tab_stops = NULL;
@@ -189,21 +150,23 @@ static void
 swfdec_text_field_movie_generate_paragraph (SwfdecTextFieldMovie *text,
     SwfdecParagraph *paragraph, guint start_index, guint length)
 {
-  SwfdecTextFormat *format, *format_prev;
   guint index_;
-  GSList *iter;
+  SwfdecTextBufferIter *iter;
   PangoAttribute *attr_bold, *attr_color, *attr_font, *attr_italic,
 		 *attr_letter_spacing, *attr_size, *attr_underline;
+  const char *string;
+  const SwfdecTextAttributes *attr, *attr_prev;
   // TODO: kerning, display
 
   g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
   g_assert (paragraph != NULL);
-  g_assert (start_index + length <= text->input->len);
+  g_assert (start_index + length <= swfdec_text_buffer_get_length (text->text));
 
+  string = swfdec_text_buffer_get_text (text->text);
   paragraph->index_ = start_index;
   paragraph->length = length;
-  if (text->input->str[start_index + length - 1] == '\n' ||
-      text->input->str[start_index + length - 1] == '\r') {
+  if (string[start_index + length - 1] == '\n' ||
+      string[start_index + length - 1] == '\r') {
     paragraph->newline = TRUE;
   } else {
     paragraph->newline = FALSE;
@@ -212,135 +175,130 @@ swfdec_text_field_movie_generate_paragraph (SwfdecTextFieldMovie *text,
   paragraph->blocks = NULL;
   paragraph->attrs = NULL;
 
-  g_assert (text->formats != NULL);
-  for (iter = text->formats; iter->next != NULL &&
-      ((SwfdecFormatIndex *)(iter->next->data))->index_ <= start_index;
-      iter = iter->next);
+  iter = swfdec_text_buffer_get_iter (text->text, start_index);
+  attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
 
   index_ = start_index;
-  format = ((SwfdecFormatIndex *)(iter->data))->format;
 
   // Paragraph formats
-  paragraph->bullet = format->attr.bullet;
-  paragraph->indent = format->attr.indent * 20 * PANGO_SCALE;
+  paragraph->bullet = attr->bullet;
+  paragraph->indent = attr->indent * 20 * PANGO_SCALE;
 
   // Add new block
-  swfdec_text_paragraph_add_block (paragraph, 0, format);
+  swfdec_text_paragraph_add_block (paragraph, 0, attr);
 
   // Open attributes
   attr_bold = pango_attr_weight_new (
-      (format->attr.bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
+      (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
   attr_bold->start_index = 0;
 
-  attr_color = pango_attr_foreground_new (SWFDEC_COLOR_R (format->attr.color) * 255,
-      SWFDEC_COLOR_G (format->attr.color) * 255,
-      SWFDEC_COLOR_B (format->attr.color) * 255);
+  attr_color = pango_attr_foreground_new (SWFDEC_COLOR_R (attr->color) * 255,
+      SWFDEC_COLOR_G (attr->color) * 255,
+      SWFDEC_COLOR_B (attr->color) * 255);
   attr_color->start_index = 0;
 
   // FIXME: embed fonts
-  attr_font = pango_attr_family_new (format->attr.font);
+  attr_font = pango_attr_family_new (attr->font);
   attr_font->start_index = 0;
 
   attr_italic = pango_attr_style_new (
-      (format->attr.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+      (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
   attr_italic->start_index = 0;
 
   attr_letter_spacing = pango_attr_letter_spacing_new (
-      format->attr.letter_spacing * 20 * PANGO_SCALE);
+      attr->letter_spacing * 20 * PANGO_SCALE);
   attr_letter_spacing->start_index = 0;
 
   attr_size =
-    pango_attr_size_new_absolute (MAX (format->attr.size, 1) * 20 * PANGO_SCALE);
+    pango_attr_size_new_absolute (MAX (attr->size, 1) * 20 * PANGO_SCALE);
   attr_size->start_index = 0;
 
   attr_underline = pango_attr_underline_new (
-      (format->attr.underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
+      (attr->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
   attr_underline->start_index = 0;
 
-  for (iter = iter->next;
-      iter != NULL &&
-      ((SwfdecFormatIndex *)(iter->data))->index_ < start_index + length;
-      iter = iter->next)
+  for (iter = swfdec_text_buffer_iter_next (text->text, iter);
+      iter != NULL && swfdec_text_buffer_iter_get_start (text->text, iter) < start_index + length;
+      iter = swfdec_text_buffer_iter_next (text->text, iter))
   {
-    format_prev = format;
-    index_ = ((SwfdecFormatIndex *)(iter->data))->index_;
-    format = ((SwfdecFormatIndex *)(iter->data))->format;
+    attr_prev = attr;
+    index_ = swfdec_text_buffer_iter_get_start (text->text, iter);
+    attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
 
     // Add new block if necessary
-    if (format_prev->attr.align != format->attr.align ||
-       format_prev->attr.bullet != format->attr.bullet ||
-       format_prev->attr.indent != format->attr.indent ||
-       format_prev->attr.leading != format->attr.leading ||
-       format_prev->attr.block_indent != format->attr.block_indent ||
-       format_prev->attr.left_margin != format->attr.left_margin)
+    if (attr_prev->align != attr->align ||
+       attr_prev->bullet != attr->bullet ||
+       attr_prev->indent != attr->indent ||
+       attr_prev->leading != attr->leading ||
+       attr_prev->block_indent != attr->block_indent ||
+       attr_prev->left_margin != attr->left_margin)
     {
-      swfdec_text_paragraph_add_block (paragraph, index_ - start_index,
-	  format);
+      swfdec_text_paragraph_add_block (paragraph, index_ - start_index, attr);
     }
 
     // Change attributes if necessary
-    if (format_prev->attr.bold != format->attr.bold) {
+    if (attr_prev->bold != attr->bold) {
       attr_bold->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_bold);
 
       attr_bold = pango_attr_weight_new (
-	  (format->attr.bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
+	  (attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
       attr_bold->start_index = index_ - start_index;
     }
 
-    if (format_prev->attr.color != format->attr.color) {
+    if (attr_prev->color != attr->color) {
       attr_color->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_color);
 
       attr_color = pango_attr_foreground_new (
-	  SWFDEC_COLOR_R (format->attr.color) * 255,
-	  SWFDEC_COLOR_G (format->attr.color) * 255,
-	  SWFDEC_COLOR_B (format->attr.color) * 255);
+	  SWFDEC_COLOR_R (attr->color) * 255,
+	  SWFDEC_COLOR_G (attr->color) * 255,
+	  SWFDEC_COLOR_B (attr->color) * 255);
       attr_color->start_index = index_ - start_index;
     }
 
-    if (format_prev->attr.font != format->attr.font) {
+    if (attr_prev->font != attr->font) {
       attr_font->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_font);
 
       // FIXME: embed fonts
-      attr_font = pango_attr_family_new (format->attr.font);
+      attr_font = pango_attr_family_new (attr->font);
       attr_font->start_index = index_ - start_index;
     }
 
-    if (format_prev->attr.italic != format->attr.italic) {
+    if (attr_prev->italic != attr->italic) {
       attr_italic->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_italic);
 
       attr_italic = pango_attr_style_new (
-	  (format->attr.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+	  (attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
       attr_italic->start_index = index_ - start_index;
     }
 
-    if (format_prev->attr.letter_spacing != format->attr.letter_spacing) {
+    if (attr_prev->letter_spacing != attr->letter_spacing) {
       attr_letter_spacing->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_letter_spacing);
 
       attr_letter_spacing = pango_attr_letter_spacing_new (
-	  format->attr.letter_spacing * 20 * PANGO_SCALE);
+	  attr->letter_spacing * 20 * PANGO_SCALE);
       attr_letter_spacing->start_index = index_ - start_index;
     }
 
-    if (format_prev->attr.size != format->attr.size) {
+    if (attr_prev->size != attr->size) {
       attr_size->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_size);
 
       attr_size = pango_attr_size_new_absolute (
-	  MAX (1, format->attr.size) * 20 * PANGO_SCALE);
+	  MAX (1, attr->size) * 20 * PANGO_SCALE);
       attr_size->start_index = index_ - start_index;
     }
 
-    if (format_prev->attr.underline != format->attr.underline) {
+    if (attr_prev->underline != attr->underline) {
       attr_underline->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_underline);
 
       attr_underline = pango_attr_underline_new (
-	  (format->attr.underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
+	  (attr->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
       attr_underline->start_index = index_ - start_index;
     }
   }
@@ -383,7 +341,7 @@ swfdec_text_field_movie_get_paragraphs (SwfdecTextFieldMovie *text, int *num)
 {
   GArray *paragraphs;
   SwfdecParagraph paragraph;
-  const char *p, *end;
+  const char *p, *end, *string;
   guint max_length;
 
   g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
@@ -391,7 +349,7 @@ swfdec_text_field_movie_get_paragraphs (SwfdecTextFieldMovie *text, int *num)
   paragraphs = g_array_new (TRUE, TRUE, sizeof (SwfdecParagraph));
 
   max_length = 0;
-  p = text->input->str;
+  p = string = swfdec_text_buffer_get_text (text->text);
   while (*p != '\0')
   {
     end = strpbrk (p, "\r\n");
@@ -405,7 +363,7 @@ swfdec_text_field_movie_get_paragraphs (SwfdecTextFieldMovie *text, int *num)
       max_length = end - p;
 
     swfdec_text_field_movie_generate_paragraph (text, &paragraph,
-	p - text->input->str, end - p);
+	p - string, end - p);
     paragraphs = g_array_append_val (paragraphs, paragraph);
 
     p = end;
@@ -563,7 +521,7 @@ swfdec_text_field_movie_get_layouts (SwfdecTextFieldMovie *text, int *num,
   }
 
   layouts = g_array_new (TRUE, TRUE, sizeof (SwfdecLayout));
-  swfdec_text_field_movie_get_selection (text, &cursor_start, &cursor_end);
+  swfdec_text_buffer_get_selection (text->text, &cursor_start, &cursor_end);
 
   for (i = 0; paragraphs[i].blocks != NULL; i++)
   {
@@ -646,7 +604,7 @@ swfdec_text_field_movie_get_layouts (SwfdecTextFieldMovie *text, int *num,
       // add background for selection
       layout.index_ = paragraphs[i].index_ + block->index_ + skip;
       layout.index_end = layout.index_ + length;
-      if (swfdec_text_field_movie_has_selection (text) &&
+      if (cursor_start != cursor_end &&
 	  layout.index_ < cursor_end) {
 	SwfdecColor color;
 	PangoAttribute *attr_fg, *attr_bg;
@@ -703,7 +661,7 @@ swfdec_text_field_movie_get_layouts (SwfdecTextFieldMovie *text, int *num,
 	    block->index_ - skip);
       } else {
 	pango_layout_set_text (playout,
-	    text->input->str + paragraphs[i].index_ + block->index_ + skip,
+	    swfdec_text_buffer_get_text (text->text) + paragraphs[i].index_ + block->index_ + skip,
 	    paragraphs[i].length - block->index_ - skip);
       }
 
@@ -719,7 +677,7 @@ swfdec_text_field_movie_get_layouts (SwfdecTextFieldMovie *text, int *num,
 	  line = pango_layout_get_line_readonly (playout, line_num);
 	  skip_new = line->start_index + line->length - (length - skip);
 	  pango_layout_set_text (playout,
-	      text->input->str + paragraphs[i].index_ + block->index_ + skip,
+	      swfdec_text_buffer_get_text (text->text) + paragraphs[i].index_ + block->index_ + skip,
 	      length - skip + skip_new);
 	  skip = skip_new;
 	}
@@ -923,8 +881,8 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
       MIN (text->scroll, text->scroll_max), NULL, &i, &skip);
 
   if (text->editable && swfdec_text_field_movie_has_focus (text) &&
-      swfdec_text_field_movie_has_cursor (text))
-    cursor = swfdec_text_field_movie_get_cursor (text);
+      !swfdec_text_buffer_has_selection (text->text))
+    cursor = swfdec_text_buffer_get_cursor (text->text);
   else
     cursor = G_MAXSIZE;
 
@@ -1009,22 +967,21 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
     } while (pango_layout_iter_next_line (iter_line));
 
     if (layouts[i].index_ <= cursor && 
-	(layouts[i].index_end > cursor || (layouts[i].index_end == cursor && cursor == text->input->len)) &&
+	(layouts[i].index_end > cursor || (layouts[i].index_end == cursor && cursor == swfdec_text_buffer_get_length (text->text))) &&
 	(line == NULL || layouts[i].index_ + line->start_index >= cursor)) {
-      SwfdecTextFormat *format = ((SwfdecFormatIndex *) g_slist_last (text->formats))->format;
+      const SwfdecTextAttributes *attr = swfdec_text_buffer_get_attributes (text->text,
+	  swfdec_text_buffer_get_length (text->text));
       PangoRectangle cursor_rect;
 
       pango_layout_get_cursor_pos (layouts[i].layout, 
-	  swfdec_text_field_movie_get_cursor (text) - layouts[i].index_,
+	  swfdec_text_buffer_get_cursor (text->text) - layouts[i].index_,
 	  &cursor_rect, NULL);
 
       cairo_save (cr);
-      if (format && SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_COLOR))
-	swfdec_color_set_source (cr, format->attr.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
-      else
-	swfdec_color_set_source (cr, text->default_attributes.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
+      /* FIXME: or default attributes? */
+      swfdec_color_set_source (cr, attr->color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
 
-      /* FIXME: what's the propwer line width here? */
+      /* FIXME: what's the proper line width here? */
       cairo_set_line_width (cr, SWFDEC_DOUBLE_TO_TWIPS (0.5));
       cairo_move_to (cr, x + layout->offset_x + rect.x, y - skipped);
       cairo_rel_move_to (cr, (double) cursor_rect.x / PANGO_SCALE, (double) cursor_rect.y / PANGO_SCALE);
@@ -1219,7 +1176,6 @@ static void
 swfdec_text_field_movie_dispose (GObject *object)
 {
   SwfdecTextFieldMovie *text;
-  GSList *iter;
 
   text = SWFDEC_TEXT_FIELD_MOVIE (object);
 
@@ -1239,15 +1195,12 @@ swfdec_text_field_movie_dispose (GObject *object)
     text->style_sheet = NULL;
   }
 
-  for (iter = text->formats; iter != NULL; iter = iter->next) {
-    g_free (text->formats->data);
-    text->formats->data = NULL;
+  if (text->text) {
+    g_signal_handlers_disconnect_matched (text->text, G_SIGNAL_MATCH_DATA,
+	0, 0, NULL, NULL, text);
+    g_object_unref (text->text);
+    text->text = NULL;
   }
-  g_slist_free (text->formats);
-  text->formats = NULL;
-
-  g_string_free (text->input, TRUE);
-  text->input = NULL;
 
   G_OBJECT_CLASS (swfdec_text_field_movie_parent_class)->dispose (object);
 }
@@ -1256,16 +1209,13 @@ static void
 swfdec_text_field_movie_mark (SwfdecAsObject *object)
 {
   SwfdecTextFieldMovie *text;
-  GSList *iter;
 
   text = SWFDEC_TEXT_FIELD_MOVIE (object);
 
   if (text->variable != NULL)
     swfdec_as_string_mark (text->variable);
-  for (iter = text->formats; iter != NULL; iter = iter->next) {
-    swfdec_as_object_mark (
-	SWFDEC_AS_OBJECT (((SwfdecFormatIndex *)(iter->data))->format));
-  }
+
+  swfdec_text_buffer_mark (text->text);
   if (text->style_sheet != NULL)
     swfdec_as_object_mark (text->style_sheet);
   if (text->style_sheet_input != NULL)
@@ -1378,41 +1328,20 @@ swfdec_text_field_movie_contains (SwfdecMovie *movie, double x, double y,
   return movie;
 }
 
-static SwfdecTextFormat *
-swfdec_text_field_movie_format_for_index (SwfdecTextFieldMovie *text,
-    guint index_)
-{
-  GSList *iter;
-
-  g_return_val_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text), NULL);
-  g_return_val_if_fail (index_ <= text->input->len, NULL);
-
-  if (text->formats == NULL)
-    return NULL;
-
-  // get current format
-  for (iter = text->formats; iter != NULL && iter->next != NULL &&
-      ((SwfdecFormatIndex *)iter->next->data)->index_ < index_;
-      iter = iter->next);
-
-  return ((SwfdecFormatIndex *)iter->data)->format;
-}
-
 static void
 swfdec_text_field_movie_letter_clicked (SwfdecTextFieldMovie *text,
     guint index_)
 {
-  SwfdecTextFormat *format;
+  const SwfdecTextAttributes *attr;
 
   g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-  g_return_if_fail (index_ <= text->input->len);
+  g_return_if_fail (index_ <= swfdec_text_buffer_get_length (text->text));
 
-  format = swfdec_text_field_movie_format_for_index (text, index_);
+  attr = swfdec_text_buffer_get_attributes (text->text, index_);
 
-  if (format != NULL && format->attr.url != NULL &&
-      format->attr.url != SWFDEC_AS_STR_EMPTY) {
+  if (attr->url != SWFDEC_AS_STR_EMPTY) {
     swfdec_player_launch (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (text)->context),
-	SWFDEC_LOADER_REQUEST_DEFAULT, format->attr.url, format->attr.target, NULL);
+	SWFDEC_LOADER_REQUEST_DEFAULT, attr->url, attr->target, NULL);
   }
 }
 
@@ -1472,18 +1401,17 @@ swfdec_text_field_movie_mouse_cursor (SwfdecActor *actor)
   SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (actor);
   double x, y;
   guint index_;
-  SwfdecTextFormat *format;
+  const SwfdecTextAttributes *attr;
 
   swfdec_movie_get_mouse (SWFDEC_MOVIE (actor), &x, &y);
 
   if (swfdec_text_field_movie_xy_to_index (text, x, y, &index_, NULL)) {
-    format = swfdec_text_field_movie_format_for_index (text, index_);
+    attr = swfdec_text_buffer_get_attributes (text->text, index_);
   } else {
-    format = NULL;
+    attr = NULL;
   }
 
-  if (format != NULL && format->attr.url != NULL &&
-      format->attr.url != SWFDEC_AS_STR_EMPTY) {
+  if (attr != NULL && attr->url != SWFDEC_AS_STR_EMPTY) {
     return SWFDEC_MOUSE_CURSOR_CLICK;
   } else if (text->editable || text->selectable) {
     return SWFDEC_MOUSE_CURSOR_TEXT;
@@ -1519,9 +1447,9 @@ swfdec_text_field_movie_mouse_press (SwfdecActor *actor, guint button)
   direct = swfdec_text_field_movie_xy_to_index (text, x, y, &index_, &before);
 
   text->mouse_pressed = TRUE;
-  if (!before && index_ < text->input->len)
+  if (!before && index_ < swfdec_text_buffer_get_length (text->text))
     index_++;
-  swfdec_text_field_movie_set_cursor (text, index_, index_);
+  swfdec_text_buffer_set_cursor (text->text, index_, index_);
 
   if (direct) {
     text->character_pressed = index_;
@@ -1547,10 +1475,10 @@ swfdec_text_field_movie_mouse_move (SwfdecActor *actor, double x, double y)
 
   direct = swfdec_text_field_movie_xy_to_index (text, x, y, &index_, &before);
 
-  if (!before && index_ < text->input->len)
+  if (!before && index_ < swfdec_text_buffer_get_length (text->text))
     index_++;
 
-  swfdec_text_field_movie_set_cursor (text, swfdec_text_field_movie_get_cursor (text), index_);
+  swfdec_text_buffer_set_cursor (text->text, swfdec_text_buffer_get_cursor (text->text), index_);
 }
 
 static void
@@ -1613,50 +1541,48 @@ swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint char
   char insert[7];
   guint len;
   gsize start, end;
-#define BACKWARD(text, _index) ((_index) == 0 ? 0 : (gsize) (g_utf8_prev_char ((text)->input->str + (_index)) - (text)->input->str))
-#define FORWARD(text, _index) ((_index) == (text)->input->len ? (_index) : (gsize) (g_utf8_next_char ((text)->input->str + (_index)) - (text)->input->str))
+  const char *string;
+#define BACKWARD(text, _index) ((_index) == 0 ? 0 : (gsize) (g_utf8_prev_char ((text) + (_index)) - (text)))
+#define FORWARD(text, _index) ((text)[0] == 0 ? (_index) : (gsize) (g_utf8_next_char ((text) + (_index)) - (text)))
 
   if (!text->editable)
     return;
 
-  swfdec_text_field_movie_get_selection (text, &start, &end);
+  string = swfdec_text_buffer_get_text (text->text);
+  swfdec_text_buffer_get_selection (text->text, &start, &end);
 
   switch (keycode) {
     case SWFDEC_KEY_LEFT:
-      if (swfdec_text_field_movie_has_cursor (text)) {
-	start = BACKWARD (text, start);
-	swfdec_text_field_movie_set_cursor (text, start, start);
+      if (swfdec_text_buffer_has_selection (text->text)) {
+	swfdec_text_buffer_set_cursor (text->text, start, start);
       } else {
-	swfdec_text_field_movie_set_cursor (text, start, start);
+	start = BACKWARD (string, start);
+	swfdec_text_buffer_set_cursor (text->text, start, start);
       }
       return;
     case SWFDEC_KEY_RIGHT:
-      if (swfdec_text_field_movie_has_cursor (text)) {
-	start = FORWARD (text, start);
-	swfdec_text_field_movie_set_cursor (text, start, start);
+      if (swfdec_text_buffer_has_selection (text->text)) {
+	swfdec_text_buffer_set_cursor (text->text, end, end);
       } else {
-	swfdec_text_field_movie_set_cursor (text, end, end);
+	start = FORWARD (string, start);
+	swfdec_text_buffer_set_cursor (text->text, start, start);
       }
       return;
     case SWFDEC_KEY_BACKSPACE:
-      if (swfdec_text_field_movie_has_cursor (text)) {
-	start = BACKWARD (text, start);
+      if (!swfdec_text_buffer_has_selection (text->text)) {
+	start = BACKWARD (string, start);
       }
       if (start != end) {
-	swfdec_sandbox_use (SWFDEC_MOVIE (text)->resource->sandbox);
-	swfdec_text_field_movie_replace_text (text, start, end, "");
-	swfdec_sandbox_unuse (SWFDEC_MOVIE (text)->resource->sandbox);
+	swfdec_text_buffer_delete_text (text->text, start, end - start);
 	swfdec_text_field_movie_changed (text);
       }
       return;
     case SWFDEC_KEY_DELETE:
-      if (swfdec_text_field_movie_has_cursor (text)) {
-	end = FORWARD (text, end);
+      if (!swfdec_text_buffer_has_selection (text->text)) {
+	end = FORWARD (string, end);
       }
       if (start != end) {
-	swfdec_sandbox_use (SWFDEC_MOVIE (text)->resource->sandbox);
-	swfdec_text_field_movie_replace_text (text, start, end, "");
-	swfdec_sandbox_unuse (SWFDEC_MOVIE (text)->resource->sandbox);
+	swfdec_text_buffer_delete_text (text->text, start, end - start);
 	swfdec_text_field_movie_changed (text);
       }
       return;
@@ -1669,9 +1595,9 @@ swfdec_text_field_movie_key_press (SwfdecActor *actor, guint keycode, guint char
   len = g_unichar_to_utf8 (character, insert);
   if (len) {
     insert[len] = 0;
-    swfdec_sandbox_use (SWFDEC_MOVIE (text)->resource->sandbox);
-    swfdec_text_field_movie_replace_text (text, start, end, insert);
-    swfdec_sandbox_unuse (SWFDEC_MOVIE (text)->resource->sandbox);
+    if (start != end)
+      swfdec_text_buffer_delete_text (text->text, start, end - start);
+    swfdec_text_buffer_insert_text (text->text, start, insert);
     swfdec_text_field_movie_changed (text);
   }
 }
@@ -1715,136 +1641,23 @@ swfdec_text_field_movie_class_init (SwfdecTextFieldMovieClass * g_class)
 }
 
 static void
+swfdec_text_field_movie_cursor_changed (SwfdecTextBuffer *buffer, 
+    gulong start, gulong end, SwfdecTextFieldMovie *text)
+{
+  swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
+  /* FIXME: update selection */
+}
+
+static void
 swfdec_text_field_movie_init (SwfdecTextFieldMovie *text)
 {
-  text->input = g_string_new ("");
+  text->text = swfdec_text_buffer_new ();
+  g_signal_connect (text->text, "cursor-changed", 
+      G_CALLBACK (swfdec_text_field_movie_cursor_changed), text);
   text->scroll = 1;
   text->mouse_wheel_enabled = TRUE;
 
   swfdec_text_attributes_reset (&text->default_attributes);
-
-}
-
-void
-swfdec_text_field_movie_set_text_format (SwfdecTextFieldMovie *text,
-    SwfdecTextFormat *format, guint start_index, guint end_index)
-{
-  SwfdecFormatIndex *findex, *findex_new, *findex_prev;
-  guint findex_end_index;
-  GSList *iter, *next;
-
-  g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-  g_return_if_fail (SWFDEC_IS_TEXT_FORMAT (format));
-  g_return_if_fail (start_index < end_index);
-  g_return_if_fail (end_index <= text->input->len);
-
-  g_assert (text->formats != NULL);
-  g_assert (text->formats->data != NULL);
-  g_assert (((SwfdecFormatIndex *)text->formats->data)->index_ == 0);
-
-  findex = NULL;
-  for (iter = text->formats; iter != NULL &&
-      ((SwfdecFormatIndex *)iter->data)->index_ < end_index;
-      iter = next)
-  {
-    findex_prev = findex;
-    next = iter->next;
-    findex = iter->data;
-    if (iter->next != NULL) {
-      findex_end_index =
-	((SwfdecFormatIndex *)iter->next->data)->index_;
-    } else {
-      findex_end_index = text->input->len;
-    }
-
-    if (findex_end_index <= start_index)
-      continue;
-
-    if (swfdec_text_format_equal_or_undefined (findex->format, format))
-      continue;
-
-    if (findex_end_index > end_index) {
-      findex_new = g_new (SwfdecFormatIndex, 1);
-      findex_new->index_ = end_index;
-      findex_new->format = swfdec_text_format_copy (findex->format);
-      if (findex_new->format == NULL) {
-	g_free (findex_new);
-	break;
-      }
-
-      iter = g_slist_insert (iter, findex_new, 1);
-    }
-
-    if (findex->index_ < start_index) {
-      findex_new = g_new (SwfdecFormatIndex, 1);
-      findex_new->index_ = start_index;
-      findex_new->format = swfdec_text_format_copy (findex->format);
-      if (findex_new->format == NULL) {
-	g_free (findex_new);
-	break;
-      }
-      swfdec_text_format_add (findex_new->format, format);
-
-      iter = g_slist_insert (iter, findex_new, 1);
-      findex = findex_new;
-    } else {
-      swfdec_text_format_add (findex->format, format);
-
-      // if current format now equals previous one, remove current
-      if (findex_prev != NULL &&
-	  swfdec_text_format_equal (findex->format, findex_prev->format)) {
-	text->formats = g_slist_remove (text->formats, findex);
-	findex = findex_prev;
-      }
-    }
-
-    // if current format now equals the next one, remove current
-    if (findex_end_index <= end_index && next != NULL &&
-	swfdec_text_format_equal (findex->format,
-	  ((SwfdecFormatIndex *)next->data)->format))
-    {
-      ((SwfdecFormatIndex *)next->data)->index_ = findex->index_;
-      text->formats = g_slist_remove (text->formats, findex);
-      findex = findex_prev;
-    }
-  }
-}
-
-SwfdecTextFormat *
-swfdec_text_field_movie_get_text_format (SwfdecTextFieldMovie *text,
-    guint start_index, guint end_index)
-{
-  SwfdecTextFormat *format;
-  GSList *iter;
-
-  g_assert (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-  g_assert (start_index < end_index);
-  g_assert (end_index <= text->input->len);
-
-  g_assert (text->formats != NULL);
-  g_assert (text->formats->data != NULL);
-  g_assert (((SwfdecFormatIndex *)text->formats->data)->index_ == 0);
-
-  format = NULL;
-  for (iter = text->formats; iter != NULL &&
-      ((SwfdecFormatIndex *)iter->data)->index_ < end_index;
-      iter = iter->next)
-  {
-    if (iter->next != NULL &&
-	((SwfdecFormatIndex *)iter->next->data)->index_ <= start_index)
-      continue;
-
-    if (format == NULL) {
-      swfdec_text_format_init_properties (SWFDEC_AS_OBJECT (text)->context);
-      format =
-	swfdec_text_format_copy (((SwfdecFormatIndex *)iter->data)->format);
-    } else {
-      swfdec_text_format_remove_different (format,
-	  ((SwfdecFormatIndex *)iter->data)->format);
-    }
-  }
-
-  return format;
 }
 
 static void
@@ -1966,7 +1779,7 @@ swfdec_text_field_movie_get_text (SwfdecTextFieldMovie *text)
 {
   char *str, *p;
 
-  str = g_strdup (text->input->str);
+  str = g_strdup (swfdec_text_buffer_get_text (text->text));
 
   // if input was orginally html, remove all \r
   if (text->input_html) {
@@ -1989,13 +1802,8 @@ void
 swfdec_text_field_movie_replace_text (SwfdecTextFieldMovie *text,
     guint start_index, guint end_index, const char *str)
 {
-  SwfdecFormatIndex *findex;
-  GSList *iter;
-  gboolean first;
-  gsize len;
-
   g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
-  g_return_if_fail (end_index <= text->input->len);
+  g_return_if_fail (end_index <= swfdec_text_buffer_get_length (text->text));
   g_return_if_fail (start_index <= end_index);
   g_return_if_fail (str != NULL);
 
@@ -2004,57 +1812,21 @@ swfdec_text_field_movie_replace_text (SwfdecTextFieldMovie *text,
   if (text->style_sheet_input)
     return;
 
-  len = strlen (str);
-  first = TRUE;
-  iter = text->formats; 
-  while (iter) {
-    findex = iter->data;
-    iter = iter->next;
-
-    /* remove formats of deleted text */
-    if (findex->index_ >= start_index &&
-	(end_index == text->input->len ||
-	 (iter != NULL &&
-	  ((SwfdecFormatIndex *) iter->data)->index_ <= end_index)) &&
-	text->formats->next != NULL) {
-      text->formats = g_slist_remove (text->formats, findex);
-      g_free (findex);
-      continue;
-    }
-    /* adapt indexes: remove deleted part, add to-be inserted text */
-    if (findex->index_ > end_index) {
-      findex->index_ = findex->index_ + start_index - end_index + len;
-    } else if (findex->index_ > start_index) {
-      findex->index_ = start_index;
-    }
-  }
+  if (end_index > start_index)
+    swfdec_text_buffer_delete_text (text->text, start_index, end_index - start_index);
 
-  if (end_index == text->input->len && text->input->len > 0) {
+  if (start_index == swfdec_text_buffer_get_length (text->text) &&
+      start_index > 0) {
+    const SwfdecTextAttributes *attr = swfdec_text_buffer_get_attributes (text->text, 0);
     if (SWFDEC_AS_OBJECT (text)->context->version < 8) {
       SWFDEC_FIXME ("replaceText to the end of the TextField might use wrong text format on version 7");
     }
-    findex = g_new0 (SwfdecFormatIndex, 1);
-    findex->index_ = start_index;
-    findex->format = swfdec_text_format_copy (
-	((SwfdecFormatIndex *)text->formats->data)->format);
-    text->formats = g_slist_append (text->formats, findex);
-  }
-
-  text->input = g_string_erase (text->input, start_index,
-      end_index - start_index);
-  text->input = g_string_insert (text->input, start_index, str);
-
-  if (text->cursor_start >= start_index) {
-    if (text->cursor_start <= end_index)
-      text->cursor_start = start_index + len;
-    else
-      text->cursor_start += end_index - start_index + len;
-  }
-  if (text->cursor_end >= start_index) {
-    if (text->cursor_end <= end_index)
-      text->cursor_end = start_index + len;
-    else
-      text->cursor_end += end_index - start_index + len;
+    swfdec_text_buffer_insert_text (text->text, start_index, str);
+    swfdec_text_buffer_set_attributes (text->text, start_index, 
+	swfdec_text_buffer_get_length (text->text) - start_index, attr, 
+	SWFDEC_TEXT_ATTRIBUTES_MASK);
+  } else {
+    swfdec_text_buffer_insert_text (text->text, start_index, str);
   }
 
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
@@ -2066,33 +1838,20 @@ void
 swfdec_text_field_movie_set_text (SwfdecTextFieldMovie *text, const char *str,
     gboolean html)
 {
-  SwfdecFormatIndex *block;
+  gsize length;
 
   g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
   g_return_if_fail (str != NULL);
 
-  // remove old formatting info
-  g_slist_foreach (text->formats, (GFunc) g_free, NULL);
-  g_slist_free (text->formats);
-  text->formats = NULL;
-
   /* Flash 7 resets to default style here */
   if (html && SWFDEC_AS_OBJECT (text)->context->version < 8)
     swfdec_text_attributes_reset (&text->default_attributes);
 
-  block = g_new (SwfdecFormatIndex, 1);
-  block->index_ = 0;
-  block->format = SWFDEC_TEXT_FORMAT (
-      swfdec_text_format_new_no_properties (SWFDEC_AS_OBJECT (text)->context));
-  if (block->format == NULL) {
-    g_free (block);
-    text->input = g_string_truncate (text->input, 0);
-    return;
-  }
-  swfdec_text_attributes_copy (&block->format->attr, &text->default_attributes,
+  length = swfdec_text_buffer_get_length (text->text);
+  if (length)
+    swfdec_text_buffer_delete_text (text->text, 0, length);
+  swfdec_text_buffer_set_attributes (text->text, 0, 0, &text->default_attributes,
       SWFDEC_TEXT_ATTRIBUTES_MASK);
-  block->format->values_set = SWFDEC_TEXT_ATTRIBUTES_MASK;
-  text->formats = g_slist_prepend (text->formats, block);
 
   text->input_html = html;
 
@@ -2108,7 +1867,7 @@ swfdec_text_field_movie_set_text (SwfdecTextFieldMovie *text, const char *str,
     if (html) {
       swfdec_text_field_movie_html_parse (text, str);
     } else {
-      text->input = g_string_assign (text->input, str);
+      swfdec_text_buffer_insert_text (text->text, 0, str);
     }
   }
 
diff --git a/swfdec/swfdec_text_field_movie.h b/swfdec/swfdec_text_field_movie.h
index 4e22dc2..80beb63 100644
--- a/swfdec/swfdec_text_field_movie.h
+++ b/swfdec/swfdec_text_field_movie.h
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2006 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
  *               2007 Pekka Lampila <pekka.lampila at iki.fi>
  *
  * This library is free software; you can redistribute it and/or
@@ -24,6 +24,7 @@
 #include <swfdec/swfdec_actor.h>
 #include <swfdec/swfdec_text_field.h>
 #include <swfdec/swfdec_style_sheet.h>
+#include <swfdec/swfdec_text_buffer.h>
 #include <swfdec/swfdec_text_format.h>
 
 G_BEGIN_DECLS
@@ -79,11 +80,6 @@ typedef struct {
   GSList *		attrs;		// PangoAttribute
 } SwfdecParagraph;
 
-typedef struct {
-  guint			index_;
-  SwfdecTextFormat *	format;
-} SwfdecFormatIndex;
-
 struct _SwfdecTextFieldMovie {
   SwfdecActor		actor;
 
@@ -102,7 +98,7 @@ struct _SwfdecTextFieldMovie {
   gboolean		border;
   gboolean		background;
  
-  GString *		input;
+  SwfdecTextBuffer *	text;		/* the text + formatting */
   char *		asterisks;	/* bunch of asterisks that we display when password mode is enabled */
   guint			asterisks_length;
   gboolean		input_html;	/* whether orginal input was given as HTML */
@@ -110,7 +106,6 @@ struct _SwfdecTextFieldMovie {
   const char *		variable;
 
   SwfdecTextAttributes	default_attributes;
-  GSList *		formats;
 
   gboolean		condense_white;
 
@@ -132,8 +127,6 @@ struct _SwfdecTextFieldMovie {
   SwfdecColor		background_color;
 
   gboolean		mouse_pressed;
-  gsize			cursor_start;		/* index of cursor (aka insertion point) in ->input */
-  gsize			cursor_end;		/* end of cursor, either equal to cursor_start or if text selected smaller or bigger */
   guint			character_pressed;
 };
 
@@ -152,13 +145,6 @@ void		swfdec_text_field_movie_get_text_size	(SwfdecTextFieldMovie *	text,
 gboolean	swfdec_text_field_movie_auto_size	(SwfdecTextFieldMovie *	text);
 void		swfdec_text_field_movie_update_scroll	(SwfdecTextFieldMovie *	text,
 							 gboolean		check_limits);
-void		swfdec_text_field_movie_set_text_format	(SwfdecTextFieldMovie *	text,
-							 SwfdecTextFormat *	format,
-							 guint			start_index,
-							 guint			end_index);
-SwfdecTextFormat *swfdec_text_field_movie_get_text_format (SwfdecTextFieldMovie *	text,
-							 guint			start_index,
-							 guint			end_index);
 const char *	swfdec_text_field_movie_get_text	(SwfdecTextFieldMovie *		text);
 void		swfdec_text_field_movie_set_listen_variable (SwfdecTextFieldMovie *	text,
 							 const char *			value);
diff --git a/swfdec/swfdec_text_field_movie_as.c b/swfdec/swfdec_text_field_movie_as.c
index 5a7f423..1f67f79 100644
--- a/swfdec/swfdec_text_field_movie_as.c
+++ b/swfdec/swfdec_text_field_movie_as.c
@@ -180,7 +180,7 @@ swfdec_text_field_movie_get_length (SwfdecAsContext *cx, SwfdecAsObject *object,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_INT (ret, g_utf8_strlen (text->input->str, -1));
+  SWFDEC_AS_VALUE_SET_INT (ret, g_utf8_strlen (swfdec_text_buffer_get_text (text->text), -1));
 }
 
 /*
@@ -1116,34 +1116,41 @@ swfdec_text_field_movie_setTextFormat (SwfdecAsContext *cx,
 {
   SwfdecTextFieldMovie *text;
   SwfdecTextFormat *format;
-  int val, start_index, end_index;
+  int val;
   guint i;
+  const char *string;
+  gsize start, end, length;
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
   if (argc < 1)
     return;
 
-  i = 0;
-  if (argc <= i + 1) {
-    start_index = 0;
-    end_index = g_utf8_strlen (text->input->str, -1);
+  string = swfdec_text_buffer_get_text (text->text);
+  length = g_utf8_strlen (string, -1);
+  if (argc <= 1) {
+    start = 0;
+    end = length;
+    i = 0;
   } else {
-    start_index = val = swfdec_as_value_to_integer (cx, &argv[i++]);
-    start_index = CLAMP (start_index, 0, g_utf8_strlen (text->input->str, -1));
-    if (argc <= i + 1) {
+    val = swfdec_as_value_to_integer (cx, &argv[0]);
+    start = MAX (val, 0);
+    start = MIN (start, length);
+    if (argc <= 2) {
       if (val < 0) { // fail
-	start_index = end_index = 0;
-      } else{
-	end_index = start_index + 1;
+	return;
+      } else {
+	end = MIN (start + 1, length);
       }
+      i = 1;
     } else {
-      end_index = swfdec_as_value_to_integer (cx, &argv[i++]);
+      val = swfdec_as_value_to_integer (cx, &argv[1]);
+      end = MAX (val, 0);
+      end = CLAMP (end, start, length);
+      i = 2;
     }
-    end_index =
-      CLAMP (end_index, start_index, g_utf8_strlen (text->input->str, -1));
   }
-  if (start_index == end_index)
+  if (start == end)
     return;
 
   if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[i]))
@@ -1152,12 +1159,11 @@ swfdec_text_field_movie_setTextFormat (SwfdecAsContext *cx,
     return;
 
   format = SWFDEC_TEXT_FORMAT (SWFDEC_AS_VALUE_GET_OBJECT (&argv[i]));
+  start = g_utf8_offset_to_pointer (string, start) - string;
+  end = g_utf8_offset_to_pointer (string, end) - string;
 
-  swfdec_text_field_movie_set_text_format (text, format,
-      g_utf8_offset_to_pointer (text->input->str, start_index) -
-      text->input->str,
-      g_utf8_offset_to_pointer (text->input->str, end_index) -
-      text->input->str);
+  swfdec_text_buffer_set_attributes (text->text, start, end - start, 
+      &format->attr, format->values_set);
 
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (text));
   swfdec_text_field_movie_auto_size (text);
@@ -1173,40 +1179,49 @@ swfdec_text_field_movie_getTextFormat (SwfdecAsContext *cx,
 {
   SwfdecTextFieldMovie *text;
   SwfdecTextFormat *format;
-  int val, start_index, end_index;
+  int val;
+  const char *string;
+  guint mask;
+  const SwfdecTextAttributes *attr;
+  gsize start, end, length;
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
+  string = swfdec_text_buffer_get_text (text->text);
+  length = g_utf8_strlen (string, -1);
+
+  format = SWFDEC_TEXT_FORMAT (swfdec_text_format_new (cx));
+  SWFDEC_AS_VALUE_SET_OBJECT (ret, SWFDEC_AS_OBJECT (format));
+
   if (argc == 0) {
-    start_index = 0;
-    end_index = g_utf8_strlen (text->input->str, -1);
+    start = 0;
+    end = length;
   } else {
-    start_index = val = swfdec_as_value_to_integer (cx, &argv[0]);
-    start_index = CLAMP (start_index, 0, g_utf8_strlen (text->input->str, -1));
+    val = swfdec_as_value_to_integer (cx, &argv[0]);
+    start = MAX (val, 0);
+    start = MIN (start, length);
     if (argc == 1) {
       if (val < 0) { // fail
-	start_index = end_index = 0;
-      } else{
-	end_index = start_index + 1;
+	return;
+      } else {
+	end = MIN (start + 1, length);
       }
     } else {
-      end_index = swfdec_as_value_to_integer (cx, &argv[1]);
+      val = swfdec_as_value_to_integer (cx, &argv[1]);
+      end = MAX (val, 0);
+      end = CLAMP (end, start, length);
     }
-    end_index =
-      CLAMP (end_index, start_index, g_utf8_strlen (text->input->str, -1));
   }
 
-  if (start_index == end_index) {
-    format = SWFDEC_TEXT_FORMAT (swfdec_text_format_new (cx));
-  } else {
-    format = swfdec_text_field_movie_get_text_format (text,
-      g_utf8_offset_to_pointer (text->input->str, start_index) -
-      text->input->str,
-      g_utf8_offset_to_pointer (text->input->str, end_index) -
-      text->input->str);
-  }
+  if (start == end)
+    return;
 
-  SWFDEC_AS_VALUE_SET_OBJECT (ret, SWFDEC_AS_OBJECT (format));
+  start = g_utf8_offset_to_pointer (string, start) - string;
+  end = g_utf8_offset_to_pointer (string, end) - string;
+  mask = swfdec_text_buffer_get_unique (text->text, start, end - start);
+  attr = swfdec_text_buffer_get_attributes (text->text, start);
+  swfdec_text_attributes_copy (&format->attr, attr, mask);
+  format->values_set = mask;
 }
 
 SWFDEC_AS_NATIVE (104, 100, swfdec_text_field_movie_replaceSel)
@@ -1226,8 +1241,8 @@ swfdec_text_field_movie_replaceText (SwfdecAsContext *cx,
 {
   SwfdecTextFieldMovie *text;
   int start_index, end_index;
-  const char *str;
-
+  const char *string, *str;
+  gsize start, end;
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "iis", &start_index,
       &end_index, &str);
@@ -1237,14 +1252,14 @@ swfdec_text_field_movie_replaceText (SwfdecAsContext *cx,
   if (end_index < start_index)
     return;
 
-  start_index = MIN (start_index, g_utf8_strlen (text->input->str, -1));
-  end_index = MIN (end_index, g_utf8_strlen (text->input->str, -1));
+  string = swfdec_text_buffer_get_text (text->text);
+  start = end = g_utf8_strlen (string, -1);
+  start = MIN ((gsize) start_index, start);
+  end = MIN ((gsize) end_index, end);
+  start = g_utf8_offset_to_pointer (string, start) - string;
+  end = g_utf8_offset_to_pointer (string, end) - string;
 
-  swfdec_text_field_movie_replace_text (text,
-      g_utf8_offset_to_pointer (text->input->str, start_index) -
-      text->input->str,
-      g_utf8_offset_to_pointer (text->input->str, end_index) -
-      text->input->str, str);
+  swfdec_text_field_movie_replace_text (text, start, end, str);
 }
 
 // static
diff --git a/swfdec/swfdec_text_field_movie_html.c b/swfdec/swfdec_text_field_movie_html.c
index bd8049f..96e7485 100644
--- a/swfdec/swfdec_text_field_movie_html.c
+++ b/swfdec/swfdec_text_field_movie_html.c
@@ -38,15 +38,15 @@ typedef struct {
   int			name_length;
   guint			index;
   guint			end_index;
-  SwfdecTextFormat	*format;
+  SwfdecTextFormat *	format;
 } ParserTag;
 
 typedef struct {
-  SwfdecAsContext	*cx;
+  SwfdecAsContext *	cx;
   gboolean		multiline;
   gboolean		condense_white;
-  SwfdecStyleSheet	*style_sheet;
-  GString *		text;
+  SwfdecStyleSheet *	style_sheet;
+  SwfdecTextBuffer *	text;
   GSList *		tags_open;
   GSList *		tags_closed;
 } ParserData;
@@ -72,7 +72,7 @@ swfdec_text_field_movie_html_parse_close_tag (ParserData *data, ParserTag *tag,
 	ParserTag *n = g_new0 (ParserTag, 1);
 	n->name = f->name;
 	n->name_length = f->name_length;
-	n->index = data->text->len;
+	n->index = swfdec_text_buffer_get_length (data->text);
 	n->end_index = n->index + 1;
 	if (f->format != NULL) {
 	  n->format = swfdec_text_format_copy (f->format);
@@ -83,10 +83,10 @@ swfdec_text_field_movie_html_parse_close_tag (ParserData *data, ParserTag *tag,
 	break;
       }
     }
-    data->text = g_string_append_c (data->text, '\n');
+    swfdec_text_buffer_append_text (data->text, "\n");
   }
 
-  tag->end_index = data->text->len;
+  tag->end_index = swfdec_text_buffer_get_length (data->text);
 
   data->tags_open = g_slist_remove (data->tags_open, tag);
   data->tags_closed = g_slist_prepend (data->tags_closed, tag);
@@ -337,7 +337,7 @@ swfdec_text_field_movie_html_parse_tag (ParserData *data, const char *p)
 	  swfdec_text_field_movie_html_parse_close_tag (data, tag, TRUE);
 	}
 	if (data->multiline)
-	  data->text = g_string_append_c (data->text, '\n');
+	  swfdec_text_buffer_append_text (data->text, "\n");
 	swfdec_text_field_movie_html_parse_close_tag (data, found->data,
 	    TRUE);
       }
@@ -360,19 +360,19 @@ swfdec_text_field_movie_html_parse_tag (ParserData *data, const char *p)
     SwfdecAsValue val;
 
     if (name_length == 3 && !g_strncasecmp (name, "tab", 3))
-      data->text = g_string_append_c (data->text, '\t');
+      swfdec_text_buffer_append_text (data->text, "\t");
 
     if (data->multiline) {
       if (name_length == 2 && !g_strncasecmp (name, "br", 2))
       {
-	data->text = g_string_append_c (data->text, '\n');
+	swfdec_text_buffer_append_text (data->text, "\n");
       }
       else if (name_length == 2 && !g_strncasecmp (name, "li", 1))
       {
-	if (data->text->len > 0 &&
-	    data->text->str[data->text->len - 1] != '\n' &&
-	    data->text->str[data->text->len - 1] != '\r')
-	  data->text = g_string_append_c (data->text, '\n');
+	gsize length = swfdec_text_buffer_get_length (data->text);
+	const char *s = swfdec_text_buffer_get_text (data->text);
+	if (length > 0 && s[length-1] != '\n' && s[length-1] != '\r')
+	  swfdec_text_buffer_append_text (data->text, "\n");
       }
     }
 
@@ -380,7 +380,7 @@ swfdec_text_field_movie_html_parse_tag (ParserData *data, const char *p)
     tag->name = name;
     tag->name_length = name_length;
     tag->format = SWFDEC_TEXT_FORMAT (swfdec_text_format_new (data->cx));
-    tag->index = data->text->len;
+    tag->index = swfdec_text_buffer_get_length (data->text);
 
     data->tags_open = g_slist_prepend (data->tags_open, tag);
 
@@ -445,8 +445,9 @@ swfdec_text_field_movie_html_parse_text (ParserData *data, const char *p)
 
   // condense the space with previous text also, if version >= 8
   if (data->condense_white && data->cx->version >= 8) {
-    if (data->text->len > 0 &&
-	g_ascii_isspace (data->text->str[data->text->len - 1]))
+    gsize length = swfdec_text_buffer_get_length (data->text);
+    const char *s = swfdec_text_buffer_get_text (data->text);
+    if (length > 0 && g_ascii_isspace (s[length - 1]))
       p += strspn (p, " \n\r\t");
   }
 
@@ -462,11 +463,11 @@ swfdec_text_field_movie_html_parse_text (ParserData *data, const char *p)
     }
 
     unescaped = swfdec_xml_unescape_len (data->cx, p, end - p, TRUE);
-    data->text = g_string_append (data->text, unescaped);
+    swfdec_text_buffer_append_text (data->text, unescaped);
     g_free (unescaped);
 
     if (data->condense_white && g_ascii_isspace (*end)) {
-      data->text = g_string_append_c (data->text, ' ');
+      swfdec_text_buffer_append_text (data->text, " ");
       p = end + strspn (end, " \n\r\t");
     } else {
       p = end;
@@ -485,8 +486,6 @@ swfdec_text_field_movie_html_parse (SwfdecTextFieldMovie *text, const char *str)
   g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
   g_return_if_fail (str != NULL);
 
-  text->input = g_string_assign (text->input, "");
-
   data.cx = SWFDEC_AS_OBJECT (text)->context;
   data.multiline = (data.cx->version < 7 || text->multiline);
   data.condense_white = text->condense_white;
@@ -495,7 +494,7 @@ swfdec_text_field_movie_html_parse (SwfdecTextFieldMovie *text, const char *str)
   } else {
     data.style_sheet = NULL;
   }
-  data.text = text->input;
+  data.text = text->text;
   data.tags_open = NULL;
   data.tags_closed = NULL;
 
@@ -527,8 +526,8 @@ swfdec_text_field_movie_html_parse (SwfdecTextFieldMovie *text, const char *str)
     ParserTag *tag = (ParserTag *)data.tags_closed->data;
 
     if (tag->index != tag->end_index && tag->format != NULL) {
-      swfdec_text_field_movie_set_text_format (text, tag->format, tag->index,
-	  tag->end_index);
+      swfdec_text_buffer_set_attributes (text->text, tag->index,
+	  tag->end_index - tag->index, &tag->format->attr, tag->format->values_set);
     }
 
     g_free (tag);
@@ -576,8 +575,9 @@ static GString *
 swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
     GString *string, guint start_index, guint end_index)
 {
-  SwfdecTextFormat *format, *format_prev, *format_font;
-  GSList *iter, *fonts, *iter_font;
+  const SwfdecTextAttributes *attr, *attr_prev, *attr_font;
+  SwfdecTextBufferIter *iter;
+  GSList *fonts, *iter_font;
   guint index_, index_prev;
   gboolean textformat, bullet, font = FALSE;
   char *escaped;
@@ -586,41 +586,37 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
   g_return_val_if_fail (string != NULL, string);
   g_return_val_if_fail (start_index <= end_index, string);
 
-  g_return_val_if_fail (text->formats != NULL, string);
-  for (iter = text->formats; iter->next != NULL &&
-      ((SwfdecFormatIndex *)(iter->next->data))->index_ <= start_index;
-      iter = iter->next);
-
+  iter = swfdec_text_buffer_get_iter (text->text, start_index);
   index_ = start_index;
-  format = ((SwfdecFormatIndex *)(iter->data))->format;
+  attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
 
-  if (format->attr.left_margin != 0 || format->attr.right_margin != 0 ||
-      format->attr.indent != 0 || format->attr.leading != 0 ||
-      format->attr.block_indent != 0 ||
-      format->attr.n_tab_stops > 0)
+  if (attr->left_margin != 0 || attr->right_margin != 0 ||
+      attr->indent != 0 || attr->leading != 0 ||
+      attr->block_indent != 0 ||
+      attr->n_tab_stops > 0)
   {
     string = g_string_append (string, "<TEXTFORMAT");
-    if (format->attr.left_margin) {
+    if (attr->left_margin) {
       g_string_append_printf (string, " LEFTMARGIN=\"%i\"",
-	  format->attr.left_margin);
+	  attr->left_margin);
     }
-    if (format->attr.right_margin) {
+    if (attr->right_margin) {
       g_string_append_printf (string, " RIGHTMARGIN=\"%i\"",
-	  format->attr.right_margin);
+	  attr->right_margin);
     }
-    if (format->attr.indent)
-      g_string_append_printf (string, " INDENT=\"%i\"", format->attr.indent);
-    if (format->attr.leading)
-      g_string_append_printf (string, " LEADING=\"%i\"", format->attr.leading);
-    if (format->attr.block_indent) {
+    if (attr->indent)
+      g_string_append_printf (string, " INDENT=\"%i\"", attr->indent);
+    if (attr->leading)
+      g_string_append_printf (string, " LEADING=\"%i\"", attr->leading);
+    if (attr->block_indent) {
       g_string_append_printf (string, " BLOCKINDENT=\"%i\"",
-	  format->attr.block_indent);
+	  attr->block_indent);
     }
-    if (format->attr.n_tab_stops > 0) {
+    if (attr->n_tab_stops > 0) {
       guint i;
       g_string_append (string, " TABSTOPS=\"\"");
-      for (i = 0; i < format->attr.n_tab_stops; i++) {
-	g_string_append_printf (string, "%d,", format->attr.tab_stops[i]);
+      for (i = 0; i < attr->n_tab_stops; i++) {
+	g_string_append_printf (string, "%d,", attr->tab_stops[i]);
       }
       string->str[string->len - 1] = '\"';
     }
@@ -633,81 +629,81 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
     textformat = FALSE;
   }
 
-  if (format->attr.bullet) {
+  if (attr->bullet) {
     string = g_string_append (string, "<LI>");
     bullet = TRUE;
   } else {
     g_string_append_printf (string, "<P ALIGN=\"%s\">",
-	swfdec_text_field_movie_html_text_align_to_string (format->attr.align));
+	swfdec_text_field_movie_html_text_align_to_string (attr->align));
     bullet = FALSE;
   }
 
-  // note we don't escape format->attr.font, even thought it can have evil chars
+  // note we don't escape attr->font, even thought it can have evil chars
   g_string_append_printf (string, "<FONT FACE=\"%s\" SIZE=\"%i\" COLOR=\"#%06X\" LETTERSPACING=\"%i\" KERNING=\"%i\">",
-      format->attr.font, format->attr.size, format->attr.color, (int)format->attr.letter_spacing,
-      (format->attr.kerning ? 1 : 0));
-  fonts = g_slist_prepend (NULL, format);
+      attr->font, attr->size, attr->color, (int)attr->letter_spacing,
+      (attr->kerning ? 1 : 0));
+  fonts = g_slist_prepend (NULL, (gpointer) attr);
 
-  if (format->attr.url != SWFDEC_AS_STR_EMPTY)
+  if (attr->url != SWFDEC_AS_STR_EMPTY)
     g_string_append_printf (string, "<A HREF=\"%s\" TARGET=\"%s\">",
-	format->attr.url, format->attr.target);
-  if (format->attr.bold)
+	attr->url, attr->target);
+  if (attr->bold)
     string = g_string_append (string, "<B>");
-  if (format->attr.italic)
+  if (attr->italic)
     string = g_string_append (string, "<I>");
-  if (format->attr.underline)
+  if (attr->underline)
     string = g_string_append (string, "<U>");
 
   // special case: use <= instead of < to add some extra markup
-  for (iter = iter->next;
-      iter != NULL && ((SwfdecFormatIndex *)(iter->data))->index_ <= end_index;
-      iter = iter->next)
+  for (iter = swfdec_text_buffer_iter_next (text->text, iter);
+      iter != NULL && swfdec_text_buffer_iter_get_start (text->text, iter) <= end_index;
+      iter = swfdec_text_buffer_iter_next (text->text, iter))
   {
     index_prev = index_;
-    format_prev = format;
-    index_ = ((SwfdecFormatIndex *)(iter->data))->index_;
-    format = ((SwfdecFormatIndex *)(iter->data))->format;
+    attr_prev = attr;
+    index_ = swfdec_text_buffer_iter_get_start (text->text, iter);
+    attr = swfdec_text_buffer_iter_get_attributes (text->text, iter);
 
-    escaped = swfdec_xml_escape_len (text->input->str + index_prev,
+    escaped = swfdec_xml_escape_len (swfdec_text_buffer_get_text (text->text) + index_prev,
 	index_ - index_prev);
     string = g_string_append (string, escaped);
     g_free (escaped);
     escaped = NULL;
 
     // Figure out what tags need to be rewritten
-    if (format->attr.font != format_prev->attr.font ||
-	format->attr.size != format_prev->attr.size ||
-	format->attr.color != format_prev->attr.color ||
-	(int)format->attr.letter_spacing != (int)format_prev->attr.letter_spacing ||
-	format->attr.kerning != format_prev->attr.kerning) {
+    if (attr->font != attr_prev->font ||
+	attr->size != attr_prev->size ||
+	attr->color != attr_prev->color ||
+	(int)attr->letter_spacing != (int)attr_prev->letter_spacing ||
+	attr->kerning != attr_prev->kerning) {
       font = TRUE;
-    } else if (format->attr.url == format_prev->attr.url &&
-	format->attr.target == format_prev->attr.target &&
-	format->attr.bold == format_prev->attr.bold &&
-	format->attr.italic == format_prev->attr.italic &&
-	format->attr.underline == format_prev->attr.underline) {
+    } else if (attr->url == attr_prev->url &&
+	attr->target == attr_prev->target &&
+	attr->bold == attr_prev->bold &&
+	attr->italic == attr_prev->italic &&
+	attr->underline == attr_prev->underline) {
       continue;
     }
 
     // Close tags
     for (iter_font = fonts; iter_font != NULL; iter_font = iter_font->next)
     {
-      format_font = (SwfdecTextFormat *)iter_font->data;
-      if (format->attr.font == format_font->attr.font &&
-	format->attr.size == format_font->attr.size &&
-	format->attr.color == format_font->attr.color &&
-	(int)format->attr.letter_spacing == (int)format_font->attr.letter_spacing &&
-	format->attr.kerning == format_font->attr.kerning) {
+      attr_font = iter_font->data;
+      if (attr->font == attr_font->font &&
+	attr->size == attr_font->size &&
+	attr->color == attr_font->color &&
+	(int)attr->letter_spacing == (int)attr_font->letter_spacing &&
+	attr->kerning == attr_font->kerning) {
 	break;
       }
     }
-    if (format_prev->attr.underline)
+    if (attr_prev->underline)
       string = g_string_append (string, "</U>");
-    if (format_prev->attr.italic)
+    if (attr_prev->italic)
       string = g_string_append (string, "</I>");
-    if (format_prev->attr.bold)
+    if (attr_prev->bold)
       string = g_string_append (string, "</B>");
-    if (format_prev->attr.url != SWFDEC_AS_STR_EMPTY)
+    if (attr_prev->url != SWFDEC_AS_STR_EMPTY)
       string = g_string_append (string, "</A>");
     if (iter_font != NULL) {
       while (fonts != iter_font) {
@@ -717,59 +713,59 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
     }
 
     // Open tags
-    format_font = (SwfdecTextFormat *)fonts->data;
-    if (font && (format->attr.font != format_font->attr.font ||
-	 format->attr.size != format_font->attr.size ||
-	 format->attr.color != format_font->attr.color ||
-	 (int)format->attr.letter_spacing != (int)format_font->attr.letter_spacing ||
-	 format->attr.kerning != format_font->attr.kerning))
+    attr_font = fonts->data;
+    if (font && (attr->font != attr_font->font ||
+	 attr->size != attr_font->size ||
+	 attr->color != attr_font->color ||
+	 (int)attr->letter_spacing != (int)attr_font->letter_spacing ||
+	 attr->kerning != attr_font->kerning))
     {
-      fonts = g_slist_prepend (fonts, format);
+      fonts = g_slist_prepend (fonts, (gpointer) attr);
 
       string = g_string_append (string, "<FONT");
-      // note we don't escape format->attr.font, even thought it can have evil chars
-      if (format->attr.font != format_font->attr.font)
-	g_string_append_printf (string, " FACE=\"%s\"", format->attr.font);
-      if (format->attr.size != format_font->attr.size)
-	g_string_append_printf (string, " SIZE=\"%i\"", format->attr.size);
-      if (format->attr.color != format_font->attr.color)
-	g_string_append_printf (string, " COLOR=\"#%06X\"", format->attr.color);
-      if ((int)format->attr.letter_spacing != (int)format_font->attr.letter_spacing) {
+      // note we don't escape attr->font, even thought it can have evil chars
+      if (attr->font != attr_font->font)
+	g_string_append_printf (string, " FACE=\"%s\"", attr->font);
+      if (attr->size != attr_font->size)
+	g_string_append_printf (string, " SIZE=\"%i\"", attr->size);
+      if (attr->color != attr_font->color)
+	g_string_append_printf (string, " COLOR=\"#%06X\"", attr->color);
+      if ((int)attr->letter_spacing != (int)attr_font->letter_spacing) {
 	g_string_append_printf (string, " LETTERSPACING=\"%i\"",
-	    (int)format->attr.letter_spacing);
+	    (int)attr->letter_spacing);
       }
-      if (format->attr.kerning != format_font->attr.kerning) {
+      if (attr->kerning != attr_font->kerning) {
 	g_string_append_printf (string, " KERNING=\"%i\"",
-	    (format->attr.kerning ? 1 : 0));
+	    (attr->kerning ? 1 : 0));
       }
       string = g_string_append (string, ">");
     }
-    if (format->attr.url != SWFDEC_AS_STR_EMPTY) {
+    if (attr->url != SWFDEC_AS_STR_EMPTY) {
       g_string_append_printf (string, "<A HREF=\"%s\" TARGET=\"%s\">",
-	  format->attr.url, format->attr.target);
+	  attr->url, attr->target);
     }
-    if (format->attr.bold)
+    if (attr->bold)
       string = g_string_append (string, "<B>");
-    if (format->attr.italic)
+    if (attr->italic)
       string = g_string_append (string, "<I>");
-    if (format->attr.underline)
+    if (attr->underline)
       string = g_string_append (string, "<U>");
   }
 
-  escaped = swfdec_xml_escape_len (text->input->str + index_,
+  escaped = swfdec_xml_escape_len (swfdec_text_buffer_get_text (text->text) + index_,
       end_index - index_);
   string = g_string_append (string, escaped);
   g_free (escaped);
 
-  if (format->attr.underline)
+  if (attr->underline)
     string = g_string_append (string, "</U>");
-  if (format->attr.italic)
+  if (attr->italic)
     string = g_string_append (string, "</I>");
-  if (format->attr.bold)
+  if (attr->bold)
     string = g_string_append (string, "</B>");
-  if (format->attr.url != SWFDEC_AS_STR_EMPTY)
+  if (attr->url != SWFDEC_AS_STR_EMPTY)
     string = g_string_append (string, "</A>");
-  for (iter = fonts; iter != NULL; iter = iter->next)
+  for (iter_font = fonts; iter_font != NULL; iter_font = iter_font->next)
     string = g_string_append (string, "</FONT>");
   g_slist_free (fonts);
   if (bullet) {
@@ -786,25 +782,22 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
 const char *
 swfdec_text_field_movie_get_html_text (SwfdecTextFieldMovie *text)
 {
-  const char *p, *end;
+  const char *p, *end, *start;
   GString *string;
 
   g_return_val_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text),
       SWFDEC_AS_STR_EMPTY);
 
-  if (text->input == NULL)
-    return SWFDEC_AS_STR_EMPTY;
-
   string = g_string_new ("");
+  p = start = swfdec_text_buffer_get_text (text->text);
 
-  p = text->input->str;
   while (*p != '\0') {
     end = strpbrk (p, "\r\n");
     if (end == NULL)
       end = strchr (p, '\0');
 
     string = swfdec_text_field_movie_html_text_append_paragraph (text, string,
-	p - text->input->str, end - text->input->str);
+	p - start, end - start);
 
     p = end;
     if (*p != '\0') p++;
commit ce1cbefc3cc3492f9acefc5d0a8876d0a4b1659f
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun May 4 21:29:58 2008 +0200

    change the format_new TextFormat to a default_attributes TextAtrributes

diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 95ebe17..dfb64fe 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -1022,7 +1022,7 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
       if (format && SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_COLOR))
 	swfdec_color_set_source (cr, format->attr.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
       else
-	swfdec_color_set_source (cr, text->format_new->attr.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
+	swfdec_color_set_source (cr, text->default_attributes.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
 
       /* FIXME: what's the propwer line width here? */
       cairo_set_line_width (cr, SWFDEC_DOUBLE_TO_TWIPS (0.5));
@@ -1223,6 +1223,8 @@ swfdec_text_field_movie_dispose (GObject *object)
 
   text = SWFDEC_TEXT_FIELD_MOVIE (object);
 
+  swfdec_text_attributes_reset (&text->default_attributes);
+
   if (text->asterisks != NULL) {
     g_free (text->asterisks);
     text->asterisks = NULL;
@@ -1260,12 +1262,10 @@ swfdec_text_field_movie_mark (SwfdecAsObject *object)
 
   if (text->variable != NULL)
     swfdec_as_string_mark (text->variable);
-  swfdec_as_object_mark (SWFDEC_AS_OBJECT (text->format_new));
   for (iter = text->formats; iter != NULL; iter = iter->next) {
     swfdec_as_object_mark (
 	SWFDEC_AS_OBJECT (((SwfdecFormatIndex *)(iter->data))->format));
   }
-  swfdec_as_object_mark (SWFDEC_AS_OBJECT (text->format_new));
   if (text->style_sheet != NULL)
     swfdec_as_object_mark (text->style_sheet);
   if (text->style_sheet_input != NULL)
@@ -1302,28 +1302,21 @@ swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
   swfdec_as_object_call (SWFDEC_AS_OBJECT (movie), SWFDEC_AS_STR_addListener,
       1, &val, NULL);
 
-  // format
-  text->format_new =
-    SWFDEC_TEXT_FORMAT (swfdec_text_format_new_no_properties (cx));
-  if (!text->format_new)
-    goto out;
-
   text->border_color = SWFDEC_COLOR_COMBINE (0, 0, 0, 0);
   text->background_color = SWFDEC_COLOR_COMBINE (255, 255, 255, 0);
 
-  swfdec_text_format_set_defaults (text->format_new);
   if (text_field) {
-    text->format_new->attr.color = text_field->color;
-    text->format_new->attr.align = text_field->align;
+    text->default_attributes.color = text_field->color;
+    text->default_attributes.align = text_field->align;
     if (text_field->font != NULL)  {
-      text->format_new->attr.font =
+      text->default_attributes.font =
 	swfdec_as_context_get_string (cx, text_field->font);
     }
-    text->format_new->attr.size = text_field->size / 20;
-    text->format_new->attr.left_margin = text_field->left_margin / 20;
-    text->format_new->attr.right_margin = text_field->right_margin / 20;
-    text->format_new->attr.indent = text_field->indent / 20;
-    text->format_new->attr.leading = text_field->leading / 20;
+    text->default_attributes.size = text_field->size / 20;
+    text->default_attributes.left_margin = text_field->left_margin / 20;
+    text->default_attributes.right_margin = text_field->right_margin / 20;
+    text->default_attributes.indent = text_field->indent / 20;
+    text->default_attributes.leading = text_field->leading / 20;
   }
 
   // text
@@ -1342,7 +1335,6 @@ swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
 	swfdec_as_context_get_string (cx, text_field->variable));
   }
 
-out:
   if (needs_unuse)
     swfdec_sandbox_unuse (movie->resource->sandbox);
 }
@@ -1728,6 +1720,9 @@ swfdec_text_field_movie_init (SwfdecTextFieldMovie *text)
   text->input = g_string_new ("");
   text->scroll = 1;
   text->mouse_wheel_enabled = TRUE;
+
+  swfdec_text_attributes_reset (&text->default_attributes);
+
 }
 
 void
@@ -2076,28 +2071,27 @@ swfdec_text_field_movie_set_text (SwfdecTextFieldMovie *text, const char *str,
   g_return_if_fail (SWFDEC_IS_TEXT_FIELD_MOVIE (text));
   g_return_if_fail (str != NULL);
 
-  if (text->format_new == NULL) {
-    text->input = g_string_truncate (text->input, 0);
-    return;
-  }
-
   // remove old formatting info
   g_slist_foreach (text->formats, (GFunc) g_free, NULL);
   g_slist_free (text->formats);
   text->formats = NULL;
 
-  // add the default style
+  /* Flash 7 resets to default style here */
   if (html && SWFDEC_AS_OBJECT (text)->context->version < 8)
-    swfdec_text_format_set_defaults (text->format_new);
+    swfdec_text_attributes_reset (&text->default_attributes);
+
   block = g_new (SwfdecFormatIndex, 1);
   block->index_ = 0;
-  g_assert (SWFDEC_IS_TEXT_FORMAT (text->format_new));
-  block->format = swfdec_text_format_copy (text->format_new);
+  block->format = SWFDEC_TEXT_FORMAT (
+      swfdec_text_format_new_no_properties (SWFDEC_AS_OBJECT (text)->context));
   if (block->format == NULL) {
     g_free (block);
     text->input = g_string_truncate (text->input, 0);
     return;
   }
+  swfdec_text_attributes_copy (&block->format->attr, &text->default_attributes,
+      SWFDEC_TEXT_ATTRIBUTES_MASK);
+  block->format->values_set = SWFDEC_TEXT_ATTRIBUTES_MASK;
   text->formats = g_slist_prepend (text->formats, block);
 
   text->input_html = html;
diff --git a/swfdec/swfdec_text_field_movie.h b/swfdec/swfdec_text_field_movie.h
index 7bd978b..4e22dc2 100644
--- a/swfdec/swfdec_text_field_movie.h
+++ b/swfdec/swfdec_text_field_movie.h
@@ -109,7 +109,7 @@ struct _SwfdecTextFieldMovie {
 
   const char *		variable;
 
-  SwfdecTextFormat *	format_new;
+  SwfdecTextAttributes	default_attributes;
   GSList *		formats;
 
   gboolean		condense_white;
diff --git a/swfdec/swfdec_text_field_movie_as.c b/swfdec/swfdec_text_field_movie_as.c
index 94d543e..5a7f423 100644
--- a/swfdec/swfdec_text_field_movie_as.c
+++ b/swfdec/swfdec_text_field_movie_as.c
@@ -958,7 +958,7 @@ swfdec_text_field_movie_get_textColor (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->format_new->attr.color);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->default_attributes.color);
 }
 
 // This doesn't work the same way as TextFormat's color setting
@@ -972,7 +972,7 @@ swfdec_text_field_movie_set_textColor (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "i", &value);
 
-  text->format_new->attr.color = swfdec_text_field_movie_int_to_color (cx, value);
+  text->default_attributes.color = swfdec_text_field_movie_int_to_color (cx, value);
 }
 
 SWFDEC_AS_NATIVE (104, 300, swfdec_text_field_movie_get_gridFitType)
@@ -1075,13 +1075,19 @@ swfdec_text_field_movie_getNewTextFormat (SwfdecAsContext *cx,
     SwfdecAsValue *ret)
 {
   SwfdecTextFieldMovie *text;
+  SwfdecTextFormat *format;
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  swfdec_text_format_init_properties (cx);
+  format = SWFDEC_TEXT_FORMAT (swfdec_text_format_new (cx));
+  if (format == NULL)
+    return;
+
+  swfdec_text_attributes_copy (&format->attr, &text->default_attributes,
+      SWFDEC_TEXT_ATTRIBUTES_MASK);
+  format->values_set = SWFDEC_TEXT_ATTRIBUTES_MASK;
 
-  SWFDEC_AS_VALUE_SET_OBJECT (ret,
-      SWFDEC_AS_OBJECT (swfdec_text_format_copy (text->format_new)));
+  SWFDEC_AS_VALUE_SET_OBJECT (ret, SWFDEC_AS_OBJECT (format));
 }
 
 SWFDEC_AS_NATIVE (104, 105, swfdec_text_field_movie_setNewTextFormat)
@@ -1091,14 +1097,15 @@ swfdec_text_field_movie_setNewTextFormat (SwfdecAsContext *cx,
     SwfdecAsValue *ret)
 {
   SwfdecTextFieldMovie *text;
-  SwfdecAsObject *obj;
+  SwfdecTextFormat *format;
 
-  SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "o", &obj);
+  SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "o", &format);
 
-  if (!SWFDEC_IS_TEXT_FORMAT (obj))
+  if (!SWFDEC_IS_TEXT_FORMAT (format))
     return;
 
-  swfdec_text_format_add (text->format_new, SWFDEC_TEXT_FORMAT (obj));
+  swfdec_text_attributes_copy (&text->default_attributes, &format->attr,
+      format->values_set);
 }
 
 SWFDEC_AS_NATIVE (104, 102, swfdec_text_field_movie_setTextFormat)
diff --git a/swfdec/swfdec_text_field_movie_html.c b/swfdec/swfdec_text_field_movie_html.c
index 2c834cd..bd8049f 100644
--- a/swfdec/swfdec_text_field_movie_html.c
+++ b/swfdec/swfdec_text_field_movie_html.c
@@ -514,8 +514,10 @@ swfdec_text_field_movie_html_parse (SwfdecTextFieldMovie *text, const char *str)
 
   // close remaining tags
   while (data.tags_open != NULL) {
-    swfdec_text_format_add (text->format_new,
-	((ParserTag *)data.tags_open->data)->format);
+    /* yes, this really appends to the default format */
+    swfdec_text_attributes_copy (&text->default_attributes,
+	&((ParserTag *)data.tags_open->data)->format->attr,
+	((ParserTag *)data.tags_open->data)->format->values_set);
     swfdec_text_field_movie_html_parse_close_tag (&data,
 	(ParserTag *)data.tags_open->data, TRUE);
   }
commit 3f8d634fbbe4f927d7664f3ed706fdc68b48bb41
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun May 4 18:30:55 2008 +0200

    add SWFEC_TEXT_ATTRIBUTES_MASK

diff --git a/swfdec/swfdec_text_attributes.h b/swfdec/swfdec_text_attributes.h
index 6d00d37..1736f4a 100644
--- a/swfdec/swfdec_text_attributes.h
+++ b/swfdec/swfdec_text_attributes.h
@@ -71,6 +71,8 @@ typedef enum {
   SWFDEC_TEXT_ATTRIBUTE_URL,
 } SwfdecTextAttribute;
 
+#define SWFDEC_TEXT_ATTRIBUTES_MASK ((1 << (SWFDEC_TEXT_ATTRIBUTE_URL + 1)) - 1)
+
 #define SWFDEC_TEXT_ATTRIBUTE_SET(flags, attribute) ((flags) |= (1 << (attribute)))
 #define SWFDEC_TEXT_ATTRIBUTE_UNSET(flags, attribute) ((flags) &= ~(1 << (attribute)))
 #define SWFDEC_TEXT_ATTRIBUTE_IS_SET(flags, attribute) (((flags) & (1 << (attribute))) ? TRUE : FALSE)
diff --git a/swfdec/swfdec_text_format.c b/swfdec/swfdec_text_format.c
index f65b23e..b98179b 100644
--- a/swfdec/swfdec_text_format.c
+++ b/swfdec/swfdec_text_format.c
@@ -935,7 +935,7 @@ void
 swfdec_text_format_set_defaults (SwfdecTextFormat *format)
 {
   swfdec_text_attributes_reset (&format->attr);
-  format->values_set = -1;
+  format->values_set = SWFDEC_TEXT_ATTRIBUTES_MASK;
 
   if (SWFDEC_AS_OBJECT (format)->context->version < 8) {
     SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_KERNING);
commit 38259f931f9c17de8484b623b03825e61ba7fd97
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun May 4 14:55:12 2008 +0200

    update loaded movie rendering to the recent image handling changes

diff --git a/swfdec/swfdec_movie.c b/swfdec/swfdec_movie.c
index 8ff6213..48a27d7 100644
--- a/swfdec/swfdec_movie.c
+++ b/swfdec/swfdec_movie.c
@@ -1182,18 +1182,26 @@ swfdec_movie_do_render (SwfdecMovie *movie, cairo_t *cr,
   /* if the movie loaded an image, draw it here now */
   if (movie->image) {
     SwfdecRenderer *renderer = swfdec_renderer_get (cr);
-    cairo_surface_t *surface = swfdec_image_create_surface_transformed (movie->image,
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    
+    if (swfdec_color_transform_is_mask (ctrans))
+      surface = NULL;
+    else
+      surface = swfdec_image_create_surface_transformed (movie->image,
 	renderer, ctrans);
     if (surface) {
       static const cairo_matrix_t matrix = { 1.0 / SWFDEC_TWIPS_SCALE_FACTOR, 0, 0, 1.0 / SWFDEC_TWIPS_SCALE_FACTOR, 0, 0 };
-      cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
+      pattern = cairo_pattern_create_for_surface (surface);
       SWFDEC_LOG ("rendering loaded image");
       cairo_pattern_set_matrix (pattern, &matrix);
-      cairo_set_source (cr, pattern);
-      cairo_paint (cr);
-      cairo_pattern_destroy (pattern);
-      cairo_surface_destroy (surface);
+    } else {
+      pattern = cairo_pattern_create_rgb (1.0, 0.0, 0.0);
     }
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+    cairo_pattern_destroy (pattern);
+    cairo_surface_destroy (surface);
   }
 
   /* draw the children movies */
commit 17ea7d911622113890a73e032f2753e070817500
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun May 4 14:44:09 2008 +0200

    fix list of included headers

diff --git a/swfdec/swfdec_text_attributes.h b/swfdec/swfdec_text_attributes.h
index 6e39926..6d00d37 100644
--- a/swfdec/swfdec_text_attributes.h
+++ b/swfdec/swfdec_text_attributes.h
@@ -21,10 +21,8 @@
 #ifndef _SWFDEC_TEXT_ATTRIBUTES_H_
 #define _SWFDEC_TEXT_ATTRIBUTES_H_
 
-#include <swfdec/swfdec_as_object.h>
-#include <swfdec/swfdec_as_array.h>
-#include <swfdec/swfdec_types.h>
-#include <swfdec/swfdec_script.h>
+#include <glib.h>
+#include <swfdec/swfdec_color.h>
 
 G_BEGIN_DECLS
 
commit 65282460ffeebc2640190a30a2645a72bea884d1
Author: Benjamin Otte <otte at gnome.org>
Date:   Sun May 4 13:29:02 2008 +0200

    convert SwfdecTextFormat to use text attributes

diff --git a/swfdec/swfdec_text_attributes.h b/swfdec/swfdec_text_attributes.h
index 267002d..6e39926 100644
--- a/swfdec/swfdec_text_attributes.h
+++ b/swfdec/swfdec_text_attributes.h
@@ -73,9 +73,9 @@ typedef enum {
   SWFDEC_TEXT_ATTRIBUTE_URL,
 } SwfdecTextAttribute;
 
-#define SWFDEC_TEXT_ATTRIBUTE_SET(flags, attribute) ((flags) |= 1 << (attribute))
+#define SWFDEC_TEXT_ATTRIBUTE_SET(flags, attribute) ((flags) |= (1 << (attribute)))
 #define SWFDEC_TEXT_ATTRIBUTE_UNSET(flags, attribute) ((flags) &= ~(1 << (attribute)))
-#define SWFDEC_TEXT_ATTRIBUTE_IS_SET(flags, attribute) ((flags) & (1 << (attribute)) ? TRUE : FALSE)
+#define SWFDEC_TEXT_ATTRIBUTE_IS_SET(flags, attribute) (((flags) & (1 << (attribute))) ? TRUE : FALSE)
 
 struct _SwfdecTextAttributes {
   SwfdecTextAlign	align;
diff --git a/swfdec/swfdec_text_field_movie.c b/swfdec/swfdec_text_field_movie.c
index 7bf23d5..95ebe17 100644
--- a/swfdec/swfdec_text_field_movie.c
+++ b/swfdec/swfdec_text_field_movie.c
@@ -140,15 +140,14 @@ static void
 swfdec_text_paragraph_add_block (SwfdecParagraph *paragraph, int index_,
     SwfdecTextFormat *format)
 {
-  gint32 length, i;
+  guint i;
   SwfdecBlock *block;
-  SwfdecAsValue val;
 
   block = g_new0 (SwfdecBlock, 1);
 
   block->index_ = index_;
 
-  switch (format->align) {
+  switch (format->attr.align) {
     case SWFDEC_TEXT_ALIGN_LEFT:
       block->align = PANGO_ALIGN_LEFT;
       block->justify = FALSE;
@@ -168,19 +167,16 @@ swfdec_text_paragraph_add_block (SwfdecParagraph *paragraph, int index_,
     default:
       g_assert_not_reached ();
   }
-  block->leading = format->leading * 20 * PANGO_SCALE;
-  block->block_indent = format->block_indent * 20;
-  block->left_margin = format->left_margin * 20;
-  block->right_margin = format->right_margin * 20;
-
-  if (format->tab_stops != NULL) {
-    length = swfdec_as_array_get_length (format->tab_stops);
-    block->tab_stops = pango_tab_array_new (length, TRUE);
-    for (i = 0; i < length; i++) {
-      swfdec_as_array_get_value (format->tab_stops, i, &val);
-      g_assert (SWFDEC_AS_VALUE_IS_NUMBER (&val));
+  block->leading = format->attr.leading * 20 * PANGO_SCALE;
+  block->block_indent = format->attr.block_indent * 20;
+  block->left_margin = format->attr.left_margin * 20;
+  block->right_margin = format->attr.right_margin * 20;
+
+  if (format->attr.n_tab_stops != 0) {
+    block->tab_stops = pango_tab_array_new (format->attr.n_tab_stops, TRUE);
+    for (i = 0; i < format->attr.n_tab_stops; i++) {
       pango_tab_array_set_tab (block->tab_stops, i, PANGO_TAB_LEFT,
-	  SWFDEC_AS_VALUE_GET_NUMBER (&val) * 20);
+	  format->attr.tab_stops[i] * 20);
     }
   } else {
     block->tab_stops = NULL;
@@ -225,40 +221,40 @@ swfdec_text_field_movie_generate_paragraph (SwfdecTextFieldMovie *text,
   format = ((SwfdecFormatIndex *)(iter->data))->format;
 
   // Paragraph formats
-  paragraph->bullet = format->bullet;
-  paragraph->indent = format->indent * 20 * PANGO_SCALE;
+  paragraph->bullet = format->attr.bullet;
+  paragraph->indent = format->attr.indent * 20 * PANGO_SCALE;
 
   // Add new block
   swfdec_text_paragraph_add_block (paragraph, 0, format);
 
   // Open attributes
   attr_bold = pango_attr_weight_new (
-      (format->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
+      (format->attr.bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
   attr_bold->start_index = 0;
 
-  attr_color = pango_attr_foreground_new (SWFDEC_COLOR_R (format->color) * 255,
-      SWFDEC_COLOR_G (format->color) * 255,
-      SWFDEC_COLOR_B (format->color) * 255);
+  attr_color = pango_attr_foreground_new (SWFDEC_COLOR_R (format->attr.color) * 255,
+      SWFDEC_COLOR_G (format->attr.color) * 255,
+      SWFDEC_COLOR_B (format->attr.color) * 255);
   attr_color->start_index = 0;
 
   // FIXME: embed fonts
-  attr_font = pango_attr_family_new (format->font);
+  attr_font = pango_attr_family_new (format->attr.font);
   attr_font->start_index = 0;
 
   attr_italic = pango_attr_style_new (
-      (format->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+      (format->attr.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
   attr_italic->start_index = 0;
 
   attr_letter_spacing = pango_attr_letter_spacing_new (
-      format->letter_spacing * 20 * PANGO_SCALE);
+      format->attr.letter_spacing * 20 * PANGO_SCALE);
   attr_letter_spacing->start_index = 0;
 
   attr_size =
-    pango_attr_size_new_absolute (MAX (format->size, 1) * 20 * PANGO_SCALE);
+    pango_attr_size_new_absolute (MAX (format->attr.size, 1) * 20 * PANGO_SCALE);
   attr_size->start_index = 0;
 
   attr_underline = pango_attr_underline_new (
-      (format->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
+      (format->attr.underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
   attr_underline->start_index = 0;
 
   for (iter = iter->next;
@@ -271,80 +267,80 @@ swfdec_text_field_movie_generate_paragraph (SwfdecTextFieldMovie *text,
     format = ((SwfdecFormatIndex *)(iter->data))->format;
 
     // Add new block if necessary
-    if (format_prev->align != format->align ||
-       format_prev->bullet != format->bullet ||
-       format_prev->indent != format->indent ||
-       format_prev->leading != format->leading ||
-       format_prev->block_indent != format->block_indent ||
-       format_prev->left_margin != format->left_margin)
+    if (format_prev->attr.align != format->attr.align ||
+       format_prev->attr.bullet != format->attr.bullet ||
+       format_prev->attr.indent != format->attr.indent ||
+       format_prev->attr.leading != format->attr.leading ||
+       format_prev->attr.block_indent != format->attr.block_indent ||
+       format_prev->attr.left_margin != format->attr.left_margin)
     {
       swfdec_text_paragraph_add_block (paragraph, index_ - start_index,
 	  format);
     }
 
     // Change attributes if necessary
-    if (format_prev->bold != format->bold) {
+    if (format_prev->attr.bold != format->attr.bold) {
       attr_bold->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_bold);
 
       attr_bold = pango_attr_weight_new (
-	  (format->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
+	  (format->attr.bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
       attr_bold->start_index = index_ - start_index;
     }
 
-    if (format_prev->color != format->color) {
+    if (format_prev->attr.color != format->attr.color) {
       attr_color->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_color);
 
       attr_color = pango_attr_foreground_new (
-	  SWFDEC_COLOR_R (format->color) * 255,
-	  SWFDEC_COLOR_G (format->color) * 255,
-	  SWFDEC_COLOR_B (format->color) * 255);
+	  SWFDEC_COLOR_R (format->attr.color) * 255,
+	  SWFDEC_COLOR_G (format->attr.color) * 255,
+	  SWFDEC_COLOR_B (format->attr.color) * 255);
       attr_color->start_index = index_ - start_index;
     }
 
-    if (format_prev->font != format->font) {
+    if (format_prev->attr.font != format->attr.font) {
       attr_font->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_font);
 
       // FIXME: embed fonts
-      attr_font = pango_attr_family_new (format->font);
+      attr_font = pango_attr_family_new (format->attr.font);
       attr_font->start_index = index_ - start_index;
     }
 
-    if (format_prev->italic != format->italic) {
+    if (format_prev->attr.italic != format->attr.italic) {
       attr_italic->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_italic);
 
       attr_italic = pango_attr_style_new (
-	  (format->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+	  (format->attr.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
       attr_italic->start_index = index_ - start_index;
     }
 
-    if (format_prev->letter_spacing != format->letter_spacing) {
+    if (format_prev->attr.letter_spacing != format->attr.letter_spacing) {
       attr_letter_spacing->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_letter_spacing);
 
       attr_letter_spacing = pango_attr_letter_spacing_new (
-	  format->letter_spacing * 20 * PANGO_SCALE);
+	  format->attr.letter_spacing * 20 * PANGO_SCALE);
       attr_letter_spacing->start_index = index_ - start_index;
     }
 
-    if (format_prev->size != format->size) {
+    if (format_prev->attr.size != format->attr.size) {
       attr_size->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_size);
 
       attr_size = pango_attr_size_new_absolute (
-	  MAX (1, format->size) * 20 * PANGO_SCALE);
+	  MAX (1, format->attr.size) * 20 * PANGO_SCALE);
       attr_size->start_index = index_ - start_index;
     }
 
-    if (format_prev->underline != format->underline) {
+    if (format_prev->attr.underline != format->attr.underline) {
       attr_underline->end_index = index_ - start_index;
       swfdec_text_paragraph_add_attribute (paragraph, attr_underline);
 
       attr_underline = pango_attr_underline_new (
-	  (format->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
+	  (format->attr.underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE));
       attr_underline->start_index = index_ - start_index;
     }
   }
@@ -1023,10 +1019,10 @@ swfdec_text_field_movie_render (SwfdecMovie *movie, cairo_t *cr,
 	  &cursor_rect, NULL);
 
       cairo_save (cr);
-      if (format && format->values_set & (1 << SWFDEC_TEXT_FORMAT_COLOR))
-	swfdec_color_set_source (cr, format->color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
+      if (format && SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_COLOR))
+	swfdec_color_set_source (cr, format->attr.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
       else
-	swfdec_color_set_source (cr, text->format_new->color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
+	swfdec_color_set_source (cr, text->format_new->attr.color | SWFDEC_COLOR_COMBINE (0, 0, 0, 0xFF));
 
       /* FIXME: what's the propwer line width here? */
       cairo_set_line_width (cr, SWFDEC_DOUBLE_TO_TWIPS (0.5));
@@ -1317,17 +1313,17 @@ swfdec_text_field_movie_init_movie (SwfdecMovie *movie)
 
   swfdec_text_format_set_defaults (text->format_new);
   if (text_field) {
-    text->format_new->color = text_field->color;
-    text->format_new->align = text_field->align;
+    text->format_new->attr.color = text_field->color;
+    text->format_new->attr.align = text_field->align;
     if (text_field->font != NULL)  {
-      text->format_new->font =
+      text->format_new->attr.font =
 	swfdec_as_context_get_string (cx, text_field->font);
     }
-    text->format_new->size = text_field->size / 20;
-    text->format_new->left_margin = text_field->left_margin / 20;
-    text->format_new->right_margin = text_field->right_margin / 20;
-    text->format_new->indent = text_field->indent / 20;
-    text->format_new->leading = text_field->leading / 20;
+    text->format_new->attr.size = text_field->size / 20;
+    text->format_new->attr.left_margin = text_field->left_margin / 20;
+    text->format_new->attr.right_margin = text_field->right_margin / 20;
+    text->format_new->attr.indent = text_field->indent / 20;
+    text->format_new->attr.leading = text_field->leading / 20;
   }
 
   // text
@@ -1421,10 +1417,10 @@ swfdec_text_field_movie_letter_clicked (SwfdecTextFieldMovie *text,
 
   format = swfdec_text_field_movie_format_for_index (text, index_);
 
-  if (format != NULL && format->url != NULL &&
-      format->url != SWFDEC_AS_STR_EMPTY) {
+  if (format != NULL && format->attr.url != NULL &&
+      format->attr.url != SWFDEC_AS_STR_EMPTY) {
     swfdec_player_launch (SWFDEC_PLAYER (SWFDEC_AS_OBJECT (text)->context),
-	SWFDEC_LOADER_REQUEST_DEFAULT, format->url, format->target, NULL);
+	SWFDEC_LOADER_REQUEST_DEFAULT, format->attr.url, format->attr.target, NULL);
   }
 }
 
@@ -1494,8 +1490,8 @@ swfdec_text_field_movie_mouse_cursor (SwfdecActor *actor)
     format = NULL;
   }
 
-  if (format != NULL && format->url != NULL &&
-      format->url != SWFDEC_AS_STR_EMPTY) {
+  if (format != NULL && format->attr.url != NULL &&
+      format->attr.url != SWFDEC_AS_STR_EMPTY) {
     return SWFDEC_MOUSE_CURSOR_CLICK;
   } else if (text->editable || text->selectable) {
     return SWFDEC_MOUSE_CURSOR_TEXT;
diff --git a/swfdec/swfdec_text_field_movie_as.c b/swfdec/swfdec_text_field_movie_as.c
index 166e9ad..94d543e 100644
--- a/swfdec/swfdec_text_field_movie_as.c
+++ b/swfdec/swfdec_text_field_movie_as.c
@@ -958,7 +958,7 @@ swfdec_text_field_movie_get_textColor (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "");
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->format_new->color);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, text->format_new->attr.color);
 }
 
 // This doesn't work the same way as TextFormat's color setting
@@ -972,7 +972,7 @@ swfdec_text_field_movie_set_textColor (SwfdecAsContext *cx,
 
   SWFDEC_AS_CHECK (SWFDEC_TYPE_TEXT_FIELD_MOVIE, &text, "i", &value);
 
-  text->format_new->color = swfdec_text_field_movie_int_to_color (cx, value);
+  text->format_new->attr.color = swfdec_text_field_movie_int_to_color (cx, value);
 }
 
 SWFDEC_AS_NATIVE (104, 300, swfdec_text_field_movie_get_gridFitType)
diff --git a/swfdec/swfdec_text_field_movie_html.c b/swfdec/swfdec_text_field_movie_html.c
index 1315f12..2c834cd 100644
--- a/swfdec/swfdec_text_field_movie_html.c
+++ b/swfdec/swfdec_text_field_movie_html.c
@@ -592,34 +592,35 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
   index_ = start_index;
   format = ((SwfdecFormatIndex *)(iter->data))->format;
 
-  if (format->left_margin != 0 || format->right_margin != 0 ||
-      format->indent != 0 || format->leading != 0 ||
-      format->block_indent != 0 ||
-      swfdec_as_array_get_length (format->tab_stops) > 0)
+  if (format->attr.left_margin != 0 || format->attr.right_margin != 0 ||
+      format->attr.indent != 0 || format->attr.leading != 0 ||
+      format->attr.block_indent != 0 ||
+      format->attr.n_tab_stops > 0)
   {
     string = g_string_append (string, "<TEXTFORMAT");
-    if (format->left_margin) {
+    if (format->attr.left_margin) {
       g_string_append_printf (string, " LEFTMARGIN=\"%i\"",
-	  format->left_margin);
+	  format->attr.left_margin);
     }
-    if (format->right_margin) {
+    if (format->attr.right_margin) {
       g_string_append_printf (string, " RIGHTMARGIN=\"%i\"",
-	  format->right_margin);
+	  format->attr.right_margin);
     }
-    if (format->indent)
-      g_string_append_printf (string, " INDENT=\"%i\"", format->indent);
-    if (format->leading)
-      g_string_append_printf (string, " LEADING=\"%i\"", format->leading);
-    if (format->block_indent) {
+    if (format->attr.indent)
+      g_string_append_printf (string, " INDENT=\"%i\"", format->attr.indent);
+    if (format->attr.leading)
+      g_string_append_printf (string, " LEADING=\"%i\"", format->attr.leading);
+    if (format->attr.block_indent) {
       g_string_append_printf (string, " BLOCKINDENT=\"%i\"",
-	  format->block_indent);
+	  format->attr.block_indent);
     }
-    if (swfdec_as_array_get_length (format->tab_stops) > 0) {
-      SwfdecAsValue val;
-      SWFDEC_AS_VALUE_SET_OBJECT (&val, SWFDEC_AS_OBJECT  (format->tab_stops));
-      g_string_append_printf (string, " TABSTOPS=\"%s\"",
-	  swfdec_as_value_to_string (SWFDEC_AS_OBJECT
-	    (format->tab_stops)->context, &val));
+    if (format->attr.n_tab_stops > 0) {
+      guint i;
+      g_string_append (string, " TABSTOPS=\"\"");
+      for (i = 0; i < format->attr.n_tab_stops; i++) {
+	g_string_append_printf (string, "%d,", format->attr.tab_stops[i]);
+      }
+      string->str[string->len - 1] = '\"';
     }
     string = g_string_append (string, ">");
 
@@ -630,29 +631,29 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
     textformat = FALSE;
   }
 
-  if (format->bullet) {
+  if (format->attr.bullet) {
     string = g_string_append (string, "<LI>");
     bullet = TRUE;
   } else {
     g_string_append_printf (string, "<P ALIGN=\"%s\">",
-	swfdec_text_field_movie_html_text_align_to_string (format->align));
+	swfdec_text_field_movie_html_text_align_to_string (format->attr.align));
     bullet = FALSE;
   }
 
-  // note we don't escape format->font, even thought it can have evil chars
+  // note we don't escape format->attr.font, even thought it can have evil chars
   g_string_append_printf (string, "<FONT FACE=\"%s\" SIZE=\"%i\" COLOR=\"#%06X\" LETTERSPACING=\"%i\" KERNING=\"%i\">",
-      format->font, format->size, format->color, (int)format->letter_spacing,
-      (format->kerning ? 1 : 0));
+      format->attr.font, format->attr.size, format->attr.color, (int)format->attr.letter_spacing,
+      (format->attr.kerning ? 1 : 0));
   fonts = g_slist_prepend (NULL, format);
 
-  if (format->url != SWFDEC_AS_STR_EMPTY)
+  if (format->attr.url != SWFDEC_AS_STR_EMPTY)
     g_string_append_printf (string, "<A HREF=\"%s\" TARGET=\"%s\">",
-	format->url, format->target);
-  if (format->bold)
+	format->attr.url, format->attr.target);
+  if (format->attr.bold)
     string = g_string_append (string, "<B>");
-  if (format->italic)
+  if (format->attr.italic)
     string = g_string_append (string, "<I>");
-  if (format->underline)
+  if (format->attr.underline)
     string = g_string_append (string, "<U>");
 
   // special case: use <= instead of < to add some extra markup
@@ -672,17 +673,17 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
     escaped = NULL;
 
     // Figure out what tags need to be rewritten
-    if (format->font != format_prev->font ||
-	format->size != format_prev->size ||
-	format->color != format_prev->color ||
-	(int)format->letter_spacing != (int)format_prev->letter_spacing ||
-	format->kerning != format_prev->kerning) {
+    if (format->attr.font != format_prev->attr.font ||
+	format->attr.size != format_prev->attr.size ||
+	format->attr.color != format_prev->attr.color ||
+	(int)format->attr.letter_spacing != (int)format_prev->attr.letter_spacing ||
+	format->attr.kerning != format_prev->attr.kerning) {
       font = TRUE;
-    } else if (format->url == format_prev->url &&
-	format->target == format_prev->target &&
-	format->bold == format_prev->bold &&
-	format->italic == format_prev->italic &&
-	format->underline == format_prev->underline) {
+    } else if (format->attr.url == format_prev->attr.url &&
+	format->attr.target == format_prev->attr.target &&
+	format->attr.bold == format_prev->attr.bold &&
+	format->attr.italic == format_prev->attr.italic &&
+	format->attr.underline == format_prev->attr.underline) {
       continue;
     }
 
@@ -690,21 +691,21 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
     for (iter_font = fonts; iter_font != NULL; iter_font = iter_font->next)
     {
       format_font = (SwfdecTextFormat *)iter_font->data;
-      if (format->font == format_font->font &&
-	format->size == format_font->size &&
-	format->color == format_font->color &&
-	(int)format->letter_spacing == (int)format_font->letter_spacing &&
-	format->kerning == format_font->kerning) {
+      if (format->attr.font == format_font->attr.font &&
+	format->attr.size == format_font->attr.size &&
+	format->attr.color == format_font->attr.color &&
+	(int)format->attr.letter_spacing == (int)format_font->attr.letter_spacing &&
+	format->attr.kerning == format_font->attr.kerning) {
 	break;
       }
     }
-    if (format_prev->underline)
+    if (format_prev->attr.underline)
       string = g_string_append (string, "</U>");
-    if (format_prev->italic)
+    if (format_prev->attr.italic)
       string = g_string_append (string, "</I>");
-    if (format_prev->bold)
+    if (format_prev->attr.bold)
       string = g_string_append (string, "</B>");
-    if (format_prev->url != SWFDEC_AS_STR_EMPTY)
+    if (format_prev->attr.url != SWFDEC_AS_STR_EMPTY)
       string = g_string_append (string, "</A>");
     if (iter_font != NULL) {
       while (fonts != iter_font) {
@@ -715,41 +716,41 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
 
     // Open tags
     format_font = (SwfdecTextFormat *)fonts->data;
-    if (font && (format->font != format_font->font ||
-	 format->size != format_font->size ||
-	 format->color != format_font->color ||
-	 (int)format->letter_spacing != (int)format_font->letter_spacing ||
-	 format->kerning != format_font->kerning))
+    if (font && (format->attr.font != format_font->attr.font ||
+	 format->attr.size != format_font->attr.size ||
+	 format->attr.color != format_font->attr.color ||
+	 (int)format->attr.letter_spacing != (int)format_font->attr.letter_spacing ||
+	 format->attr.kerning != format_font->attr.kerning))
     {
       fonts = g_slist_prepend (fonts, format);
 
       string = g_string_append (string, "<FONT");
-      // note we don't escape format->font, even thought it can have evil chars
-      if (format->font != format_font->font)
-	g_string_append_printf (string, " FACE=\"%s\"", format->font);
-      if (format->size != format_font->size)
-	g_string_append_printf (string, " SIZE=\"%i\"", format->size);
-      if (format->color != format_font->color)
-	g_string_append_printf (string, " COLOR=\"#%06X\"", format->color);
-      if ((int)format->letter_spacing != (int)format_font->letter_spacing) {
+      // note we don't escape format->attr.font, even thought it can have evil chars
+      if (format->attr.font != format_font->attr.font)
+	g_string_append_printf (string, " FACE=\"%s\"", format->attr.font);
+      if (format->attr.size != format_font->attr.size)
+	g_string_append_printf (string, " SIZE=\"%i\"", format->attr.size);
+      if (format->attr.color != format_font->attr.color)
+	g_string_append_printf (string, " COLOR=\"#%06X\"", format->attr.color);
+      if ((int)format->attr.letter_spacing != (int)format_font->attr.letter_spacing) {
 	g_string_append_printf (string, " LETTERSPACING=\"%i\"",
-	    (int)format->letter_spacing);
+	    (int)format->attr.letter_spacing);
       }
-      if (format->kerning != format_font->kerning) {
+      if (format->attr.kerning != format_font->attr.kerning) {
 	g_string_append_printf (string, " KERNING=\"%i\"",
-	    (format->kerning ? 1 : 0));
+	    (format->attr.kerning ? 1 : 0));
       }
       string = g_string_append (string, ">");
     }
-    if (format->url != SWFDEC_AS_STR_EMPTY) {
+    if (format->attr.url != SWFDEC_AS_STR_EMPTY) {
       g_string_append_printf (string, "<A HREF=\"%s\" TARGET=\"%s\">",
-	  format->url, format->target);
+	  format->attr.url, format->attr.target);
     }
-    if (format->bold)
+    if (format->attr.bold)
       string = g_string_append (string, "<B>");
-    if (format->italic)
+    if (format->attr.italic)
       string = g_string_append (string, "<I>");
-    if (format->underline)
+    if (format->attr.underline)
       string = g_string_append (string, "<U>");
   }
 
@@ -758,13 +759,13 @@ swfdec_text_field_movie_html_text_append_paragraph (SwfdecTextFieldMovie *text,
   string = g_string_append (string, escaped);
   g_free (escaped);
 
-  if (format->underline)
+  if (format->attr.underline)
     string = g_string_append (string, "</U>");
-  if (format->italic)
+  if (format->attr.italic)
     string = g_string_append (string, "</I>");
-  if (format->bold)
+  if (format->attr.bold)
     string = g_string_append (string, "</B>");
-  if (format->url != SWFDEC_AS_STR_EMPTY)
+  if (format->attr.url != SWFDEC_AS_STR_EMPTY)
     string = g_string_append (string, "</A>");
   for (iter = fonts; iter != NULL; iter = iter->next)
     string = g_string_append (string, "</FONT>");
diff --git a/swfdec/swfdec_text_format.c b/swfdec/swfdec_text_format.c
index e3e3d9b..f65b23e 100644
--- a/swfdec/swfdec_text_format.c
+++ b/swfdec/swfdec_text_format.c
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
  *               2007 Pekka Lampila <pekka.lampila at iki.fi>
  *
  * This library is free software; you can redistribute it and/or
@@ -39,25 +39,25 @@
 G_DEFINE_TYPE (SwfdecTextFormat, swfdec_text_format, SWFDEC_TYPE_AS_OBJECT)
 
 static int property_offsets[] = {
-  G_STRUCT_OFFSET (SwfdecTextFormat, align),
-  G_STRUCT_OFFSET (SwfdecTextFormat, block_indent),
-  G_STRUCT_OFFSET (SwfdecTextFormat, bold),
-  G_STRUCT_OFFSET (SwfdecTextFormat, bullet),
-  G_STRUCT_OFFSET (SwfdecTextFormat, color),
-  G_STRUCT_OFFSET (SwfdecTextFormat, display),
-  G_STRUCT_OFFSET (SwfdecTextFormat, font),
-  G_STRUCT_OFFSET (SwfdecTextFormat, indent),
-  G_STRUCT_OFFSET (SwfdecTextFormat, italic),
-  G_STRUCT_OFFSET (SwfdecTextFormat, kerning),
-  G_STRUCT_OFFSET (SwfdecTextFormat, leading),
-  G_STRUCT_OFFSET (SwfdecTextFormat, left_margin),
-  G_STRUCT_OFFSET (SwfdecTextFormat, letter_spacing),
-  G_STRUCT_OFFSET (SwfdecTextFormat, right_margin),
-  G_STRUCT_OFFSET (SwfdecTextFormat, size),
-  G_STRUCT_OFFSET (SwfdecTextFormat, tab_stops),
-  G_STRUCT_OFFSET (SwfdecTextFormat, target),
-  G_STRUCT_OFFSET (SwfdecTextFormat, underline),
-  G_STRUCT_OFFSET (SwfdecTextFormat, url)
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.align),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.block_indent),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.bold),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.bullet),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.color),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.display),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.font),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.indent),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.italic),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.kerning),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.leading),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.left_margin),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.letter_spacing),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.right_margin),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.size),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.tab_stops),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.target),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.underline),
+  G_STRUCT_OFFSET (SwfdecTextFormat, attr.url)
 };
 
 static void
@@ -65,11 +65,7 @@ swfdec_text_format_do_mark (SwfdecAsObject *object)
 {
   SwfdecTextFormat *format = SWFDEC_TEXT_FORMAT (object);
 
-  if (format->tab_stops != NULL)
-    swfdec_as_object_mark (SWFDEC_AS_OBJECT (format->tab_stops));
-  swfdec_as_string_mark (format->font);
-  swfdec_as_string_mark (format->target);
-  swfdec_as_string_mark (format->url);
+  swfdec_text_attributes_mark (&format->attr);
 
   SWFDEC_AS_OBJECT_CLASS (swfdec_text_format_parent_class)->mark (object);
 }
@@ -85,34 +81,12 @@ swfdec_text_format_class_init (SwfdecTextFormatClass *klass)
 static void
 swfdec_text_format_init (SwfdecTextFormat *format)
 {
-  format->font = SWFDEC_AS_STR_Times_New_Roman;
-  format->target = SWFDEC_AS_STR_EMPTY;
-  format->url = SWFDEC_AS_STR_EMPTY;
-}
-
-static gboolean
-swfdec_text_format_is_set (const SwfdecTextFormat *format, SwfdecTextFormatProperty property)
-{
-  return (format->values_set & (1 << property));
-}
-
-static void
-swfdec_text_format_mark_set (SwfdecTextFormat *format,
-    SwfdecTextFormatProperty property)
-{
-  format->values_set |= (1 << property);
-}
-
-static void
-swfdec_text_format_mark_unset (SwfdecTextFormat *format,
-    SwfdecTextFormatProperty property)
-{
-  format->values_set &= ~(1 << property);
+  swfdec_text_attributes_reset (&format->attr);
 }
 
 static void
 swfdec_text_format_get_string (SwfdecAsObject *object,
-    SwfdecTextFormatProperty property, SwfdecAsValue *ret)
+    SwfdecTextAttribute property, SwfdecAsValue *ret)
 {
   SwfdecTextFormat *format;
 
@@ -120,7 +94,7 @@ swfdec_text_format_get_string (SwfdecAsObject *object,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, property)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, property)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
@@ -131,7 +105,7 @@ swfdec_text_format_get_string (SwfdecAsObject *object,
 
 static void
 swfdec_text_format_set_string (SwfdecAsObject *object,
-    SwfdecTextFormatProperty property, guint argc, SwfdecAsValue *argv)
+    SwfdecTextAttribute property, guint argc, SwfdecAsValue *argv)
 {
   SwfdecTextFormat *format;
   const char *s;
@@ -150,17 +124,17 @@ swfdec_text_format_set_string (SwfdecAsObject *object,
   if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv[0]) ||
       SWFDEC_AS_VALUE_IS_NULL (&argv[0])) {
     /* FIXME: reset to defaults here? */
-    swfdec_text_format_mark_unset (format, property);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, property);
   } else {
     G_STRUCT_MEMBER (const char *, format, property_offsets[property]) = s;
-    swfdec_text_format_mark_set (format, property);
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, property);
   }
   /* FIXME: figure out what to do here */
 }
 
 static void
 swfdec_text_format_get_boolean (SwfdecAsObject *object,
-    SwfdecTextFormatProperty property, SwfdecAsValue *ret)
+    SwfdecTextAttribute property, SwfdecAsValue *ret)
 {
   SwfdecTextFormat *format;
 
@@ -168,7 +142,7 @@ swfdec_text_format_get_boolean (SwfdecAsObject *object,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, property)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, property)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
@@ -182,7 +156,7 @@ swfdec_text_format_get_boolean (SwfdecAsObject *object,
 
 static void
 swfdec_text_format_set_boolean (SwfdecAsObject *object,
-    SwfdecTextFormatProperty property, guint argc, SwfdecAsValue *argv)
+    SwfdecTextAttribute property, guint argc, SwfdecAsValue *argv)
 {
   SwfdecTextFormat *format;
 
@@ -199,17 +173,17 @@ swfdec_text_format_set_boolean (SwfdecAsObject *object,
 
   if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv[0]) ||
       SWFDEC_AS_VALUE_IS_NULL (&argv[0])) {
-    swfdec_text_format_mark_unset (format, property);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, property);
   } else {
     G_STRUCT_MEMBER (gboolean, format, property_offsets[property]) =
       swfdec_as_value_to_boolean (object->context, &argv[0]);
-    swfdec_text_format_mark_set (format, property);
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, property);
   }
 }
 
 static void
 swfdec_text_format_get_integer (SwfdecAsObject *object,
-    SwfdecTextFormatProperty property, SwfdecAsValue *ret)
+    SwfdecTextAttribute property, SwfdecAsValue *ret)
 {
   SwfdecTextFormat *format;
 
@@ -217,7 +191,7 @@ swfdec_text_format_get_integer (SwfdecAsObject *object,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, property)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, property)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
@@ -268,7 +242,7 @@ swfdec_text_format_value_to_integer (SwfdecAsContext *cx, SwfdecAsValue *val,
 
 static void
 swfdec_text_format_set_integer (SwfdecAsObject *object,
-    SwfdecTextFormatProperty property, guint argc, SwfdecAsValue *argv,
+    SwfdecTextAttribute property, guint argc, SwfdecAsValue *argv,
     gboolean allow_negative)
 {
   SwfdecTextFormat *format;
@@ -282,12 +256,12 @@ swfdec_text_format_set_integer (SwfdecAsObject *object,
 
   if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv[0]) ||
       SWFDEC_AS_VALUE_IS_NULL (&argv[0])) {
-    swfdec_text_format_mark_unset (format, property);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, property);
   } else {
     G_STRUCT_MEMBER (int, format, property_offsets[property]) =
       swfdec_text_format_value_to_integer (object->context, &argv[0],
 	  allow_negative);
-    swfdec_text_format_mark_set (format, property);
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, property);
   }
 }
 
@@ -301,12 +275,12 @@ swfdec_text_format_do_get_align (SwfdecAsContext *cx, SwfdecAsObject *object,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_ALIGN)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_ALIGN)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
 
-  switch (format->align) {
+  switch (format->attr.align) {
     case SWFDEC_TEXT_ALIGN_LEFT:
       SWFDEC_AS_VALUE_SET_STRING (ret, SWFDEC_AS_STR_left);
       break;
@@ -343,17 +317,17 @@ swfdec_text_format_do_set_align (SwfdecAsContext *cx, SwfdecAsObject *object,
   s = swfdec_as_value_to_string (cx, &argv[0]);
 
   if (!g_ascii_strcasecmp (s, "left")) {
-    format->align = SWFDEC_TEXT_ALIGN_LEFT;
-    swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_ALIGN);
+    format->attr.align = SWFDEC_TEXT_ALIGN_LEFT;
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_ALIGN);
   } else if (!g_ascii_strcasecmp (s, "right")) {
-    format->align = SWFDEC_TEXT_ALIGN_RIGHT;
-    swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_ALIGN);
+    format->attr.align = SWFDEC_TEXT_ALIGN_RIGHT;
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_ALIGN);
   } else if (!g_ascii_strcasecmp (s, "center")) {
-    format->align = SWFDEC_TEXT_ALIGN_CENTER;
-    swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_ALIGN);
+    format->attr.align = SWFDEC_TEXT_ALIGN_CENTER;
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_ALIGN);
   } else if (!g_ascii_strcasecmp (s, "justify")) {
-    format->align = SWFDEC_TEXT_ALIGN_JUSTIFY;
-    swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_ALIGN);
+    format->attr.align = SWFDEC_TEXT_ALIGN_JUSTIFY;
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_ALIGN);
   }
 }
 
@@ -362,7 +336,7 @@ swfdec_text_format_do_get_block_indent (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_integer (object, SWFDEC_TEXT_FORMAT_BLOCK_INDENT, ret);
+  swfdec_text_format_get_integer (object, SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT, ret);
 }
 
 static void
@@ -370,7 +344,7 @@ swfdec_text_format_do_set_block_indent (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_integer (object, SWFDEC_TEXT_FORMAT_BLOCK_INDENT, argc, argv,
+  swfdec_text_format_set_integer (object, SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT, argc, argv,
       cx->version >= 8);
 }
 
@@ -378,28 +352,28 @@ static void
 swfdec_text_format_do_get_bold (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_FORMAT_BOLD, ret);
+  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_ATTRIBUTE_BOLD, ret);
 }
 
 static void
 swfdec_text_format_do_set_bold (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_FORMAT_BOLD, argc, argv);
+  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_ATTRIBUTE_BOLD, argc, argv);
 }
 
 static void
 swfdec_text_format_do_get_bullet (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_FORMAT_BULLET, ret);
+  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_ATTRIBUTE_BULLET, ret);
 }
 
 static void
 swfdec_text_format_do_set_bullet (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_FORMAT_BULLET, argc, argv);
+  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_ATTRIBUTE_BULLET, argc, argv);
 }
 
 static void
@@ -412,12 +386,12 @@ swfdec_text_format_do_get_color (SwfdecAsContext *cx, SwfdecAsObject *object,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_COLOR)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_COLOR)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, format->color);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, format->attr.color);
 }
 
 static void
@@ -435,13 +409,13 @@ swfdec_text_format_do_set_color (SwfdecAsContext *cx, SwfdecAsObject *object,
 
   if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv[0]) ||
       SWFDEC_AS_VALUE_IS_NULL (&argv[0])) {
-    swfdec_text_format_mark_unset (format, SWFDEC_TEXT_FORMAT_COLOR);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_COLOR);
   } else {
-    format->color = (unsigned) swfdec_as_value_to_integer (cx, &argv[0]);
+    format->attr.color = (unsigned) swfdec_as_value_to_integer (cx, &argv[0]);
     swfdec_as_value_to_integer (cx, &argv[0]);
     swfdec_as_value_to_string (cx, &argv[0]);
 
-    swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_COLOR);
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_COLOR);
   }
 }
 
@@ -455,13 +429,13 @@ swfdec_text_format_do_get_display (SwfdecAsContext *cx, SwfdecAsObject *object,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_DISPLAY))
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_DISPLAY))
   {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
 
-  switch (format->display) {
+  switch (format->attr.display) {
     case SWFDEC_TEXT_DISPLAY_NONE:
       SWFDEC_AS_VALUE_SET_STRING (ret, SWFDEC_AS_STR_none);
       break;
@@ -493,42 +467,42 @@ swfdec_text_format_do_set_display (SwfdecAsContext *cx, SwfdecAsObject *object,
   s = swfdec_as_value_to_string (cx, &argv[0]); // oh yes, let's call it twice
 
   if (!g_ascii_strcasecmp (s, "none")) {
-    format->display = SWFDEC_TEXT_DISPLAY_NONE;
+    format->attr.display = SWFDEC_TEXT_DISPLAY_NONE;
   } else if (!g_ascii_strcasecmp (s, "inline")) {
-    format->display = SWFDEC_TEXT_DISPLAY_INLINE;
+    format->attr.display = SWFDEC_TEXT_DISPLAY_INLINE;
   } else {
-    format->display = SWFDEC_TEXT_DISPLAY_BLOCK;
+    format->attr.display = SWFDEC_TEXT_DISPLAY_BLOCK;
   }
 
-  swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_DISPLAY);
+  SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_DISPLAY);
 }
 
 static void
 swfdec_text_format_do_get_font (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_string (object, SWFDEC_TEXT_FORMAT_FONT, ret);
+  swfdec_text_format_get_string (object, SWFDEC_TEXT_ATTRIBUTE_FONT, ret);
 }
 
 static void
 swfdec_text_format_do_set_font (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_string (object, SWFDEC_TEXT_FORMAT_FONT, argc, argv);
+  swfdec_text_format_set_string (object, SWFDEC_TEXT_ATTRIBUTE_FONT, argc, argv);
 }
 
 static void
 swfdec_text_format_do_get_indent (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_integer (object, SWFDEC_TEXT_FORMAT_INDENT, ret);
+  swfdec_text_format_get_integer (object, SWFDEC_TEXT_ATTRIBUTE_INDENT, ret);
 }
 
 static void
 swfdec_text_format_do_set_indent (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_integer (object, SWFDEC_TEXT_FORMAT_INDENT, argc, argv,
+  swfdec_text_format_set_integer (object, SWFDEC_TEXT_ATTRIBUTE_INDENT, argc, argv,
       cx->version >= 8);
 }
 
@@ -536,42 +510,42 @@ static void
 swfdec_text_format_do_get_italic (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_FORMAT_ITALIC, ret);
+  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_ATTRIBUTE_ITALIC, ret);
 }
 
 static void
 swfdec_text_format_do_set_italic (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_FORMAT_ITALIC, argc, argv);
+  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_ATTRIBUTE_ITALIC, argc, argv);
 }
 
 static void
 swfdec_text_format_do_get_kerning (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_FORMAT_KERNING, ret);
+  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_ATTRIBUTE_KERNING, ret);
 }
 
 static void
 swfdec_text_format_do_set_kerning (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_FORMAT_KERNING, argc, argv);
+  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_ATTRIBUTE_KERNING, argc, argv);
 }
 
 static void
 swfdec_text_format_do_get_leading (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_integer (object, SWFDEC_TEXT_FORMAT_LEADING, ret);
+  swfdec_text_format_get_integer (object, SWFDEC_TEXT_ATTRIBUTE_LEADING, ret);
 }
 
 static void
 swfdec_text_format_do_set_leading (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_integer (object, SWFDEC_TEXT_FORMAT_LEADING, argc, argv,
+  swfdec_text_format_set_integer (object, SWFDEC_TEXT_ATTRIBUTE_LEADING, argc, argv,
       cx->version >= 8);
 }
 
@@ -580,7 +554,7 @@ swfdec_text_format_do_get_left_margin (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_integer (object, SWFDEC_TEXT_FORMAT_LEFT_MARGIN, ret);
+  swfdec_text_format_get_integer (object, SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN, ret);
 }
 
 static void
@@ -588,7 +562,7 @@ swfdec_text_format_do_set_left_margin (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_integer (object, SWFDEC_TEXT_FORMAT_LEFT_MARGIN, argc, argv, FALSE);
+  swfdec_text_format_set_integer (object, SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN, argc, argv, FALSE);
 }
 
 static void
@@ -602,12 +576,12 @@ swfdec_text_format_do_get_letter_spacing (SwfdecAsContext *cx,
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_LETTER_SPACING)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
 
-  SWFDEC_AS_VALUE_SET_NUMBER (ret, format->letter_spacing);
+  SWFDEC_AS_VALUE_SET_NUMBER (ret, format->attr.letter_spacing);
 }
 
 static void
@@ -632,14 +606,14 @@ swfdec_text_format_do_set_letter_spacing (SwfdecAsContext *cx,
   if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv[0]) ||
       SWFDEC_AS_VALUE_IS_NULL (&argv[0]))
   {
-    swfdec_text_format_mark_unset (format,
-	SWFDEC_TEXT_FORMAT_LETTER_SPACING);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set,
+	SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING);
   }
   else
   {
-    format->letter_spacing = d;
-    swfdec_text_format_mark_set (format,
-	SWFDEC_TEXT_FORMAT_LETTER_SPACING);
+    format->attr.letter_spacing = d;
+    SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set,
+	SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING);
   }
 }
 
@@ -648,7 +622,7 @@ swfdec_text_format_do_get_right_margin (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_integer (object, SWFDEC_TEXT_FORMAT_RIGHT_MARGIN, ret);
+  swfdec_text_format_get_integer (object, SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN, ret);
 }
 
 static void
@@ -656,7 +630,7 @@ swfdec_text_format_do_set_right_margin (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_integer (object, SWFDEC_TEXT_FORMAT_RIGHT_MARGIN, argc, argv,
+  swfdec_text_format_set_integer (object, SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN, argc, argv,
       FALSE);
 }
 
@@ -664,14 +638,14 @@ static void
 swfdec_text_format_do_get_size (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_integer (object, SWFDEC_TEXT_FORMAT_SIZE, ret);
+  swfdec_text_format_get_integer (object, SWFDEC_TEXT_ATTRIBUTE_SIZE, ret);
 }
 
 static void
 swfdec_text_format_do_set_size (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_integer (object, SWFDEC_TEXT_FORMAT_SIZE, argc, argv, TRUE);
+  swfdec_text_format_set_integer (object, SWFDEC_TEXT_ATTRIBUTE_SIZE, argc, argv, TRUE);
 }
 
 static void
@@ -680,18 +654,25 @@ swfdec_text_format_do_get_tab_stops (SwfdecAsContext *cx,
     SwfdecAsValue *ret)
 {
   SwfdecTextFormat *format;
+  guint i;
+  SwfdecAsValue val;
+  SwfdecAsObject *array;
 
   if (!SWFDEC_IS_TEXT_FORMAT (object))
     return;
   format = SWFDEC_TEXT_FORMAT (object);
 
-  if (!swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_TAB_STOPS)) {
+  if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS)) {
     SWFDEC_AS_VALUE_SET_NULL (ret);
     return;
   }
 
-  g_return_if_fail (SWFDEC_IS_AS_OBJECT (format->tab_stops));
-  SWFDEC_AS_VALUE_SET_OBJECT (ret, SWFDEC_AS_OBJECT (format->tab_stops));
+  array = swfdec_as_array_new (cx);
+  for (i = 0; i < format->attr.n_tab_stops; i++) {
+    SWFDEC_AS_VALUE_SET_INT (&val, format->attr.tab_stops[i]);
+    swfdec_as_array_push (SWFDEC_AS_ARRAY (array), &val);
+  }
+  SWFDEC_AS_VALUE_SET_OBJECT (ret, array);
 }
 
 static void
@@ -715,71 +696,62 @@ swfdec_text_format_do_set_tab_stops (SwfdecAsContext *cx,
   if (SWFDEC_AS_VALUE_IS_UNDEFINED (&argv[0]) ||
       SWFDEC_AS_VALUE_IS_NULL (&argv[0]))
   {
-    format->tab_stops = NULL;
-    swfdec_text_format_mark_unset (format, SWFDEC_TEXT_FORMAT_TAB_STOPS);
+    g_free (format->attr.tab_stops);
+    format->attr.tab_stops = NULL;
+    format->attr.n_tab_stops = 0;
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS);
   }
   else if (SWFDEC_AS_VALUE_IS_OBJECT (&argv[0]) &&
 	SWFDEC_IS_AS_ARRAY (SWFDEC_AS_VALUE_GET_OBJECT (&argv[0])))
   {
     SwfdecAsArray *array;
     SwfdecAsValue val;
-    gint32 len, i;
-    int n;
+    guint i;
+    int len;
 
     array = SWFDEC_AS_ARRAY (SWFDEC_AS_VALUE_GET_OBJECT (&argv[0]));
     len = swfdec_as_array_get_length (array);
 
-    if (!swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_TAB_STOPS)) {
+    if (!SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS)) {
       // special case, if we have null and array is empty, keep it at null
       if (len == 0)
 	return;
-      format->tab_stops = SWFDEC_AS_ARRAY (swfdec_as_array_new (cx));
-      if (!format->tab_stops)
-	return;
-      swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_TAB_STOPS);
+      SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS);
     }
 
-    swfdec_as_array_set_length (format->tab_stops, 0);
-    for (i = 0; i < len; i++) {
+    g_free (format->attr.tab_stops);
+    format->attr.n_tab_stops = MAX (0, len);
+    format->attr.tab_stops = g_new (guint, format->attr.n_tab_stops);
+    for (i = 0; i < format->attr.n_tab_stops; i++) {
       swfdec_as_array_get_value (array, i, &val);
-      n = swfdec_text_format_value_to_integer (cx, &val, TRUE);
-      SWFDEC_AS_VALUE_SET_INT (&val, n);
-      swfdec_as_array_set_value (format->tab_stops, i, &val);
+      format->attr.tab_stops[i] = swfdec_text_format_value_to_integer (cx, &val, TRUE);
     }
   }
   else if (SWFDEC_AS_VALUE_IS_STRING (&argv[0]))
   {
-    gsize i, len;
-    SwfdecAsValue val;
-
-    len = strlen (SWFDEC_AS_VALUE_GET_STRING (&argv[0]));
+    gsize i;
 
     // special case: empty strings mean null
-    if (len == 0) {
-      format->tab_stops = NULL;
-      swfdec_text_format_mark_unset (format,
-	  SWFDEC_TEXT_FORMAT_TAB_STOPS);
+    if (SWFDEC_AS_VALUE_GET_STRING (&argv[0]) == SWFDEC_AS_STR_EMPTY) {
+      g_free (format->attr.tab_stops);
+      format->attr.tab_stops = NULL;
+      format->attr.n_tab_stops = 0;
+      SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS);
     } else {
-      format->tab_stops = SWFDEC_AS_ARRAY (swfdec_as_array_new (cx));
-      if (format->tab_stops != NULL) {
-	swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_TAB_STOPS);
-	if (cx->version >= 8) {
-	  SWFDEC_AS_VALUE_SET_INT (&val, -2147483648);
-	} else {
-	  SWFDEC_AS_VALUE_SET_INT (&val, 0);
-	}
-	for (i = 0; i < len; i++) {
-	  swfdec_as_array_push (format->tab_stops, &val);
-	}
-      } else {
-	swfdec_text_format_mark_unset (format, SWFDEC_TEXT_FORMAT_TAB_STOPS);
+      int n = cx->version >= 8 ? G_MININT : 0;
+      SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS);
+      format->attr.n_tab_stops = strlen (SWFDEC_AS_VALUE_GET_STRING (&argv[0]));
+      format->attr.tab_stops = g_new (guint, format->attr.n_tab_stops);
+      for (i = 0; i < format->attr.n_tab_stops; i++) {
+	format->attr.tab_stops[i] = n;
       }
     }
   }
-  else if (swfdec_text_format_is_set (format, SWFDEC_TEXT_FORMAT_TAB_STOPS))
+  else if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS))
   {
-    swfdec_as_array_set_length (format->tab_stops, 0);
-    swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_TAB_STOPS);
+    format->attr.n_tab_stops = 0;
+    g_free (format->attr.tab_stops);
+    format->attr.tab_stops = NULL;
   }
 }
 
@@ -787,14 +759,14 @@ static void
 swfdec_text_format_do_get_target (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_string (object, SWFDEC_TEXT_FORMAT_TARGET, ret);
+  swfdec_text_format_get_string (object, SWFDEC_TEXT_ATTRIBUTE_TARGET, ret);
 }
 
 static void
 swfdec_text_format_do_set_target (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_string (object, SWFDEC_TEXT_FORMAT_TARGET, argc, argv);
+  swfdec_text_format_set_string (object, SWFDEC_TEXT_ATTRIBUTE_TARGET, argc, argv);
 }
 
 static void
@@ -802,7 +774,7 @@ swfdec_text_format_do_get_underline (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_FORMAT_UNDERLINE, ret);
+  swfdec_text_format_get_boolean (object, SWFDEC_TEXT_ATTRIBUTE_UNDERLINE, ret);
 }
 
 static void
@@ -810,21 +782,21 @@ swfdec_text_format_do_set_underline (SwfdecAsContext *cx,
     SwfdecAsObject *object, guint argc, SwfdecAsValue *argv,
     SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_FORMAT_UNDERLINE, argc, argv);
+  swfdec_text_format_set_boolean (object, SWFDEC_TEXT_ATTRIBUTE_UNDERLINE, argc, argv);
 }
 
 static void
 swfdec_text_format_do_get_url (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_get_string (object, SWFDEC_TEXT_FORMAT_URL, ret);
+  swfdec_text_format_get_string (object, SWFDEC_TEXT_ATTRIBUTE_URL, ret);
 }
 
 static void
 swfdec_text_format_do_set_url (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
 {
-  swfdec_text_format_set_string (object, SWFDEC_TEXT_FORMAT_URL, argc, argv);
+  swfdec_text_format_set_string (object, SWFDEC_TEXT_ATTRIBUTE_URL, argc, argv);
 }
 
 static void
@@ -863,12 +835,12 @@ swfdec_text_format_getTextExtent (SwfdecAsContext *cx, SwfdecAsObject *object,
     pango_shape (text, strlen (text), &analysis, glyph_string);
 
     desc = pango_font_description_new ();
-    pango_font_description_set_family_static (desc, format->font);
-    pango_font_description_set_size (desc, format->size * PANGO_SCALE);
-    if (format->bold){
+    pango_font_description_set_family_static (desc, format->attr.font);
+    pango_font_description_set_size (desc, format->attr.size * PANGO_SCALE);
+    if (format->attr.bold){
       pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
     }
-    if (format->italic)
+    if (format->attr.italic)
       pango_font_description_set_style (desc, PANGO_STYLE_ITALIC);
 
     font = pango_font_map_load_font (fontmap, pcontext, desc);
@@ -881,14 +853,14 @@ swfdec_text_format_getTextExtent (SwfdecAsContext *cx, SwfdecAsObject *object,
     g_object_unref (G_OBJECT (pcontext));
     g_object_unref (G_OBJECT (font));
 
-    width = ink_rect.width / PANGO_SCALE + format->left_margin
-	+ format->right_margin;
-    height = ink_rect.height / PANGO_SCALE + format->leading;
+    width = ink_rect.width / PANGO_SCALE + format->attr.left_margin
+	+ format->attr.right_margin;
+    height = ink_rect.height / PANGO_SCALE + format->attr.leading;
     ascent = PANGO_ASCENT(ink_rect) / PANGO_SCALE;
     descent = PANGO_DESCENT (ink_rect) / PANGO_SCALE;
-    text_field_width = ink_rect.width / PANGO_SCALE + format->left_margin
-	+ format->right_margin + 4;
-    text_field_height = ink_rect.height / PANGO_SCALE + 4 + format->leading;
+    text_field_width = ink_rect.width / PANGO_SCALE + format->attr.left_margin
+	+ format->attr.right_margin + 4;
+    text_field_height = ink_rect.height / PANGO_SCALE + 4 + format->attr.leading;
   }
 
   SWFDEC_AS_VALUE_SET_INT (&val, width);
@@ -918,45 +890,7 @@ swfdec_text_format_add (SwfdecTextFormat *format, const SwfdecTextFormat *from)
   g_return_if_fail (SWFDEC_IS_TEXT_FORMAT (format));
   g_return_if_fail (SWFDEC_IS_TEXT_FORMAT (from));
 
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_ALIGN))
-    format->align = from->align;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_BLOCK_INDENT))
-    format->block_indent = from->block_indent;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_BOLD))
-    format->bold = from->bold;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_BULLET))
-    format->bullet = from->bullet;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_COLOR))
-    format->color = from->color;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_DISPLAY))
-    format->display = from->display;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_FONT))
-    format->font = from->font;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_INDENT))
-    format->indent = from->indent;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_ITALIC))
-    format->italic = from->italic ;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_KERNING))
-    format->kerning = from->kerning;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_LEADING))
-    format->leading = from->leading;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_LEFT_MARGIN))
-    format->left_margin = from->left_margin;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_LETTER_SPACING))
-    format->letter_spacing = from->letter_spacing;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_RIGHT_MARGIN))
-    format->right_margin = from->right_margin;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_SIZE))
-    format->size = from->size;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_TAB_STOPS))
-    format->tab_stops = from->tab_stops;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_TARGET))
-    format->target = from->target;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_UNDERLINE))
-    format->underline = from->underline;
-  if (swfdec_text_format_is_set (from, SWFDEC_TEXT_FORMAT_URL))
-    format->url = from->url;
-
+  swfdec_text_attributes_copy (&format->attr, &from->attr, from->values_set);
   format->values_set |= from->values_set;
 }
 
@@ -964,116 +898,25 @@ void
 swfdec_text_format_remove_different (SwfdecTextFormat *format,
     const SwfdecTextFormat *from)
 {
-  int set;
-
   g_return_if_fail (SWFDEC_IS_TEXT_FORMAT (format));
   g_return_if_fail (SWFDEC_IS_TEXT_FORMAT (from));
 
-  set = format->values_set & from->values_set;
-
-  if (set & (1 << SWFDEC_TEXT_FORMAT_ALIGN) && format->align != from->align)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_ALIGN);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_BLOCK_INDENT) &&
-      format->block_indent != from->block_indent) {
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_BLOCK_INDENT);
-  }
-  if (set & (1 << SWFDEC_TEXT_FORMAT_BOLD) && format->bold != from->bold)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_BOLD);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_BULLET) && format->bullet != from->bullet)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_BULLET);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_COLOR) && format->color != from->color)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_COLOR);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_DISPLAY) && format->display != from->display)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_DISPLAY);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_FONT) && format->font != from->font)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_FONT);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_INDENT) && format->indent != from->indent)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_INDENT);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_ITALIC) && format->italic != from->italic)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_ITALIC);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_KERNING) && format->kerning != from->kerning)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_KERNING);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_LEADING) && format->leading != from->leading)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_LEADING);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_LEFT_MARGIN) &&
-      format->left_margin != from->left_margin) {
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_LEFT_MARGIN);
-  }
-  if (set & (1 << SWFDEC_TEXT_FORMAT_LETTER_SPACING) &&
-      format->letter_spacing != from->letter_spacing) {
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_LETTER_SPACING);
-  }
-  if (set & (1 << SWFDEC_TEXT_FORMAT_RIGHT_MARGIN) &&
-      format->right_margin != from->right_margin) {
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_RIGHT_MARGIN);
-  }
-  if (set & (1 << SWFDEC_TEXT_FORMAT_SIZE) && format->size != from->size)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_SIZE);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_TAB_STOPS) && format->tab_stops != from->tab_stops)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_TAB_STOPS);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_TARGET) && format->target != from->target)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_TARGET);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_UNDERLINE) && format->underline != from->underline)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_UNDERLINE);
-  if (set & (1 << SWFDEC_TEXT_FORMAT_URL) && format->url != from->url)
-    set &= ~(1 << SWFDEC_TEXT_FORMAT_URL);
-
-  format->values_set = set;
+  format->values_set &= ~swfdec_text_attributes_diff (&format->attr, &from->attr);
 }
 
 gboolean
 swfdec_text_format_equal_or_undefined (const SwfdecTextFormat *a,
     const SwfdecTextFormat *b)
 {
-  int set;
-
-  set = a->values_set & b->values_set;
+  int set, diff;
 
   g_return_val_if_fail (SWFDEC_IS_TEXT_FORMAT (a), FALSE);
   g_return_val_if_fail (SWFDEC_IS_TEXT_FORMAT (b), FALSE);
 
-  if (set & (1 << SWFDEC_TEXT_FORMAT_ALIGN) && a->align != b->align)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_BLOCK_INDENT) && a->block_indent != b->block_indent)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_BOLD) && a->bold != b->bold)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_BULLET) && a->bullet != b->bullet)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_COLOR) && a->color != b->color)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_DISPLAY) && a->display != b->display)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_FONT) && a->font != b->font)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_INDENT) && a->indent != b->indent)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_ITALIC) && a->italic != b->italic)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_KERNING) && a->kerning != b->kerning)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_LEADING) && a->leading != b->leading)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_LEFT_MARGIN) && a->left_margin != b->left_margin)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_LETTER_SPACING) &&
-      a->letter_spacing != b->letter_spacing) {
-    return FALSE;
-  }
-  if (set & (1 << SWFDEC_TEXT_FORMAT_RIGHT_MARGIN) && a->right_margin != b->right_margin)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_SIZE) && a->size != b->size)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_TAB_STOPS) && a->tab_stops != b->tab_stops)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_TARGET) && a->target != b->target)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_UNDERLINE) && a->underline != b->underline)
-    return FALSE;
-  if (set & (1 << SWFDEC_TEXT_FORMAT_URL) && a->url != b->url)
-    return FALSE;
+  set = a->values_set & b->values_set;
+  diff = swfdec_text_attributes_diff (&a->attr, &b->attr);
 
-  return TRUE;
+  return (set & diff) == 0;
 }
 
 gboolean
@@ -1085,49 +928,28 @@ swfdec_text_format_equal (const SwfdecTextFormat *a, const SwfdecTextFormat *b)
   if (a->values_set != b->values_set)
     return FALSE;
 
-  return swfdec_text_format_equal_or_undefined (a, b);
+  return (a->values_set & swfdec_text_attributes_diff (&a->attr, &b->attr)) == 0;
 }
 
 void
 swfdec_text_format_set_defaults (SwfdecTextFormat *format)
 {
-  format->align = SWFDEC_TEXT_ALIGN_LEFT;
-  format->block_indent = 0;
-  format->bold = FALSE;
-  format->bullet = FALSE;
-  format->color = 0;
-  format->display = SWFDEC_TEXT_DISPLAY_BLOCK;
-  format->font = SWFDEC_AS_STR_Times_New_Roman;
-  format->indent = 0;
-  format->italic = FALSE;
-  format->kerning = FALSE;
-  format->leading = 0;
-  format->left_margin = 0;
-  format->letter_spacing = 0;
-  format->right_margin = 0;
-  format->size = 12;
-  format->tab_stops =
-    SWFDEC_AS_ARRAY (swfdec_as_array_new (SWFDEC_AS_OBJECT (format)->context));
-  format->target = SWFDEC_AS_STR_EMPTY;
-  format->url = SWFDEC_AS_STR_EMPTY;
-  format->underline = FALSE;
-
-  format->values_set = (1 << SWFDEC_TEXT_FORMAT_TOTAL) - 1;
+  swfdec_text_attributes_reset (&format->attr);
+  format->values_set = -1;
 
   if (SWFDEC_AS_OBJECT (format)->context->version < 8) {
-    swfdec_text_format_mark_unset (format, SWFDEC_TEXT_FORMAT_KERNING);
-    swfdec_text_format_mark_unset (format, SWFDEC_TEXT_FORMAT_LETTER_SPACING);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_KERNING);
+    SWFDEC_TEXT_ATTRIBUTE_UNSET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING);
   }
 }
 
 static void
 swfdec_text_format_clear (SwfdecTextFormat *format)
 {
-  format->tab_stops = NULL;
   format->values_set = 0;
 
-  format->display = SWFDEC_TEXT_DISPLAY_BLOCK;
-  swfdec_text_format_mark_set (format, SWFDEC_TEXT_FORMAT_DISPLAY);
+  format->attr.display = SWFDEC_TEXT_DISPLAY_BLOCK;
+  SWFDEC_TEXT_ATTRIBUTE_SET (format->values_set, SWFDEC_TEXT_ATTRIBUTE_DISPLAY);
 }
 
 void
@@ -1268,25 +1090,7 @@ swfdec_text_format_copy (const SwfdecTextFormat *copy_from)
     return NULL;
   copy_to = SWFDEC_TEXT_FORMAT (object_to);
 
-  copy_to->align = copy_from->align;
-  copy_to->block_indent = copy_from->block_indent;
-  copy_to->bold = copy_from->bold;
-  copy_to->bullet = copy_from->bullet;
-  copy_to->color = copy_from->color;
-  copy_to->display = copy_from->display;
-  copy_to->font = copy_from->font;
-  copy_to->indent = copy_from->indent;
-  copy_to->italic = copy_from->italic ;
-  copy_to->kerning = copy_from->kerning;
-  copy_to->leading = copy_from->leading;
-  copy_to->left_margin = copy_from->left_margin;
-  copy_to->letter_spacing = copy_from->letter_spacing;
-  copy_to->right_margin = copy_from->right_margin;
-  copy_to->size = copy_from->size;
-  copy_to->tab_stops = copy_from->tab_stops;
-  copy_to->target = copy_from->target;
-  copy_to->underline = copy_from->underline;
-  copy_to->url = copy_from->url;
+  swfdec_text_attributes_copy (&copy_to->attr, &copy_from->attr, -1);
   copy_to->values_set = copy_from->values_set;
 
   return copy_to;
diff --git a/swfdec/swfdec_text_format.h b/swfdec/swfdec_text_format.h
index 00a7d46..ed5ef75 100644
--- a/swfdec/swfdec_text_format.h
+++ b/swfdec/swfdec_text_format.h
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
  *               2007 Pekka Lampila <pekka.lampila at iki.fi>
  *
  * This library is free software; you can redistribute it and/or
@@ -23,6 +23,7 @@
 
 #include <swfdec/swfdec_as_object.h>
 #include <swfdec/swfdec_as_array.h>
+#include <swfdec/swfdec_text_attributes.h>
 #include <swfdec/swfdec_types.h>
 #include <swfdec/swfdec_script.h>
 
@@ -38,66 +39,12 @@ typedef struct _SwfdecTextFormatClass SwfdecTextFormatClass;
 #define SWFDEC_TEXT_FORMAT_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_TEXT_FORMAT, SwfdecTextFormatClass))
 #define SWFDEC_TEXT_FORMAT_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_TEXT_FORMAT, SwfdecTextFormatClass))
 
-typedef enum {
-  SWFDEC_TEXT_ALIGN_LEFT,
-  SWFDEC_TEXT_ALIGN_RIGHT,
-  SWFDEC_TEXT_ALIGN_CENTER,
-  SWFDEC_TEXT_ALIGN_JUSTIFY
-} SwfdecTextAlign;
-
-typedef enum {
-  SWFDEC_TEXT_DISPLAY_NONE,
-  SWFDEC_TEXT_DISPLAY_INLINE,
-  SWFDEC_TEXT_DISPLAY_BLOCK,
-} SwfdecTextDisplay;
-
-typedef enum {
-  SWFDEC_TEXT_FORMAT_ALIGN = 0,
-  SWFDEC_TEXT_FORMAT_BLOCK_INDENT,
-  SWFDEC_TEXT_FORMAT_BOLD,
-  SWFDEC_TEXT_FORMAT_BULLET,
-  SWFDEC_TEXT_FORMAT_COLOR,
-  SWFDEC_TEXT_FORMAT_DISPLAY,
-  SWFDEC_TEXT_FORMAT_FONT,
-  SWFDEC_TEXT_FORMAT_INDENT,
-  SWFDEC_TEXT_FORMAT_ITALIC,
-  SWFDEC_TEXT_FORMAT_KERNING,
-  SWFDEC_TEXT_FORMAT_LEADING,
-  SWFDEC_TEXT_FORMAT_LEFT_MARGIN,
-  SWFDEC_TEXT_FORMAT_LETTER_SPACING,
-  SWFDEC_TEXT_FORMAT_RIGHT_MARGIN,
-  SWFDEC_TEXT_FORMAT_SIZE,
-  SWFDEC_TEXT_FORMAT_TAB_STOPS,
-  SWFDEC_TEXT_FORMAT_TARGET,
-  SWFDEC_TEXT_FORMAT_UNDERLINE,
-  SWFDEC_TEXT_FORMAT_URL,
-  SWFDEC_TEXT_FORMAT_TOTAL
-} SwfdecTextFormatProperty;
-
 struct _SwfdecTextFormat {
   SwfdecAsObject	object;
 
-  SwfdecTextAlign	align;
-  int			block_indent;
-  gboolean		bold;
-  gboolean		bullet;
-  SwfdecColor		color;
-  SwfdecTextDisplay	display;
-  const char *		font;
-  int			indent;
-  gboolean		italic;
-  gboolean		kerning;
-  int			leading;
-  int			left_margin;
-  double		letter_spacing; // number or null
-  int			right_margin;
-  int			size;
-  SwfdecAsArray *	tab_stops;
-  const char *		target;
-  gboolean		underline;
-  const char *		url;
+  SwfdecTextAttributes	attr;
 
-  int			values_set;
+  guint			values_set;
 };
 
 struct _SwfdecTextFormatClass {
commit 0e075e1dae2c8731f75daf744e384bb9f6146616
Author: Benjamin Otte <otte at gnome.org>
Date:   Fri May 2 22:28:44 2008 +0200

    add a new way to specify text attributes

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index bc94375..9b743a7 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -146,6 +146,7 @@ libswfdec_source_files = \
 	swfdec_system_security.c \
 	swfdec_tag.c \
 	swfdec_text.c \
+	swfdec_text_attributes.c \
 	swfdec_text_field.c \
 	swfdec_text_field_movie.c \
 	swfdec_text_field_movie_as.c \
@@ -293,6 +294,7 @@ noinst_HEADERS = \
 	swfdec_style_sheet.h \
 	swfdec_tag.h \
 	swfdec_text.h \
+	swfdec_text_attributes.h \
 	swfdec_text_format.h \
 	swfdec_types.h \
 	swfdec_utils.h \
diff --git a/swfdec/swfdec_text_attributes.c b/swfdec/swfdec_text_attributes.c
new file mode 100644
index 0000000..c4739a8
--- /dev/null
+++ b/swfdec/swfdec_text_attributes.c
@@ -0,0 +1,161 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *               2007 Pekka Lampila <pekka.lampila at iki.fi>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "swfdec_text_attributes.h"
+#include "swfdec_as_context.h"
+#include "swfdec_as_strings.h"
+#include "swfdec_debug.h"
+
+void
+swfdec_text_attributes_reset (SwfdecTextAttributes *attr)
+{
+  g_free (attr->tab_stops);
+
+  attr->align = SWFDEC_TEXT_ALIGN_LEFT;
+  attr->block_indent = 0;
+  attr->bold = FALSE;
+  attr->bullet = FALSE;
+  attr->color = 0;
+  attr->display = SWFDEC_TEXT_DISPLAY_BLOCK;
+  attr->font = SWFDEC_AS_STR_Times_New_Roman;
+  attr->indent = 0;
+  attr->italic = FALSE;
+  attr->kerning = FALSE;
+  attr->leading = 0;
+  attr->left_margin = 0;
+  attr->letter_spacing = 0;
+  attr->right_margin = 0;
+  attr->size = 12;
+  attr->tab_stops = NULL;
+  attr->n_tab_stops = 0;
+  attr->target = SWFDEC_AS_STR_EMPTY;
+  attr->url = SWFDEC_AS_STR_EMPTY;
+  attr->underline = FALSE;
+}
+
+void
+swfdec_text_attributes_copy (SwfdecTextAttributes *attr, const SwfdecTextAttributes *from,
+    guint flags)
+{
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_ALIGN))
+    attr->align = from->align;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT))
+    attr->block_indent = from->block_indent;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_BOLD))
+    attr->bold = from->bold;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_BULLET))
+    attr->bullet = from->bullet;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_COLOR))
+    attr->color = from->color;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_DISPLAY))
+    attr->display = from->display;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_FONT))
+    attr->font = from->font;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_INDENT))
+    attr->indent = from->indent;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_ITALIC))
+    attr->italic = from->italic;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_KERNING))
+    attr->kerning = from->kerning;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_LEADING))
+    attr->leading = from->leading;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN))
+    attr->left_margin = from->left_margin;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING))
+    attr->letter_spacing = from->letter_spacing;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN))
+    attr->right_margin = from->right_margin;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_SIZE))
+    attr->size = from->size;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS)) {
+    g_free (attr->tab_stops);
+    attr->tab_stops = g_memdup (from->tab_stops, sizeof (guint) * from->n_tab_stops);
+    attr->n_tab_stops = from->n_tab_stops;
+  }
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_TARGET))
+    attr->target = from->target;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_UNDERLINE))
+    attr->underline = from->underline;
+  if (SWFDEC_TEXT_ATTRIBUTE_IS_SET (flags, SWFDEC_TEXT_ATTRIBUTE_URL))
+    attr->url = from->url;
+}
+
+void
+swfdec_text_attributes_mark (SwfdecTextAttributes *attr)
+{
+  swfdec_as_string_mark (attr->font);
+  swfdec_as_string_mark (attr->target);
+  swfdec_as_string_mark (attr->url);
+}
+
+guint
+swfdec_text_attributes_diff (const SwfdecTextAttributes *a, const SwfdecTextAttributes *b)
+{
+  guint result = 0;
+
+  if (a->align != b->align)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_ALIGN);
+  if (a->block_indent != b->block_indent)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT);
+  if (a->bold != b->bold)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_BOLD);
+  if (a->bullet != b->bullet)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_BULLET);
+  if (a->color != b->color)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_COLOR);
+  if (a->display != b->display)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_DISPLAY);
+  if (a->font != b->font)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_FONT);
+  if (a->indent != b->indent)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_INDENT);
+  if (a->italic != b->italic)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_ITALIC);
+  if (a->kerning != b->kerning)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_KERNING);
+  if (a->leading != b->leading)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_LEADING);
+  if (a->left_margin != b->left_margin)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN);
+  if (a->letter_spacing != b->letter_spacing)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING);
+  if (a->right_margin != b->right_margin)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN);
+  if (a->size != b->size)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_SIZE);
+  if (a->n_tab_stops != b->n_tab_stops)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS);
+  else if (a->n_tab_stops != 0 && 
+      memcmp (a->tab_stops, b->tab_stops, sizeof (guint) & a->n_tab_stops) != 0)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS);
+  if (a->target != b->target)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_TARGET);
+  if (a->underline != b->underline)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_UNDERLINE);
+  if (a->url != b->url)
+    SWFDEC_TEXT_ATTRIBUTE_SET (result, SWFDEC_TEXT_ATTRIBUTE_URL);
+
+  return result;
+}
diff --git a/swfdec/swfdec_text_attributes.h b/swfdec/swfdec_text_attributes.h
new file mode 100644
index 0000000..267002d
--- /dev/null
+++ b/swfdec/swfdec_text_attributes.h
@@ -0,0 +1,115 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *               2007 Pekka Lampila <pekka.lampila at iki.fi>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_TEXT_ATTRIBUTES_H_
+#define _SWFDEC_TEXT_ATTRIBUTES_H_
+
+#include <swfdec/swfdec_as_object.h>
+#include <swfdec/swfdec_as_array.h>
+#include <swfdec/swfdec_types.h>
+#include <swfdec/swfdec_script.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SwfdecTextAttributes SwfdecTextAttributes;
+typedef struct _SwfdecTextAttributesClass SwfdecTextAttributesClass;
+
+#define SWFDEC_TYPE_TEXT_ATTRIBUTES                    (swfdec_text_attributes_get_type())
+#define SWFDEC_IS_TEXT_ATTRIBUTES(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_TEXT_ATTRIBUTES))
+#define SWFDEC_IS_TEXT_ATTRIBUTES_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_TEXT_ATTRIBUTES))
+#define SWFDEC_TEXT_ATTRIBUTES(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_TEXT_ATTRIBUTES, SwfdecTextAttributes))
+#define SWFDEC_TEXT_ATTRIBUTES_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_TEXT_ATTRIBUTES, SwfdecTextAttributesClass))
+#define SWFDEC_TEXT_ATTRIBUTES_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_TEXT_ATTRIBUTES, SwfdecTextAttributesClass))
+
+typedef enum {
+  SWFDEC_TEXT_ALIGN_LEFT,
+  SWFDEC_TEXT_ALIGN_RIGHT,
+  SWFDEC_TEXT_ALIGN_CENTER,
+  SWFDEC_TEXT_ALIGN_JUSTIFY
+} SwfdecTextAlign;
+
+typedef enum {
+  SWFDEC_TEXT_DISPLAY_NONE,
+  SWFDEC_TEXT_DISPLAY_INLINE,
+  SWFDEC_TEXT_DISPLAY_BLOCK,
+} SwfdecTextDisplay;
+
+typedef enum {
+  SWFDEC_TEXT_ATTRIBUTE_ALIGN = 0,
+  SWFDEC_TEXT_ATTRIBUTE_BLOCK_INDENT,
+  SWFDEC_TEXT_ATTRIBUTE_BOLD,
+  SWFDEC_TEXT_ATTRIBUTE_BULLET,
+  SWFDEC_TEXT_ATTRIBUTE_COLOR,
+  SWFDEC_TEXT_ATTRIBUTE_DISPLAY,
+  SWFDEC_TEXT_ATTRIBUTE_FONT,
+  SWFDEC_TEXT_ATTRIBUTE_INDENT,
+  SWFDEC_TEXT_ATTRIBUTE_ITALIC,
+  SWFDEC_TEXT_ATTRIBUTE_KERNING,
+  SWFDEC_TEXT_ATTRIBUTE_LEADING,
+  SWFDEC_TEXT_ATTRIBUTE_LEFT_MARGIN,
+  SWFDEC_TEXT_ATTRIBUTE_LETTER_SPACING,
+  SWFDEC_TEXT_ATTRIBUTE_RIGHT_MARGIN,
+  SWFDEC_TEXT_ATTRIBUTE_SIZE,
+  SWFDEC_TEXT_ATTRIBUTE_TAB_STOPS,
+  SWFDEC_TEXT_ATTRIBUTE_TARGET,
+  SWFDEC_TEXT_ATTRIBUTE_UNDERLINE,
+  SWFDEC_TEXT_ATTRIBUTE_URL,
+} SwfdecTextAttribute;
+
+#define SWFDEC_TEXT_ATTRIBUTE_SET(flags, attribute) ((flags) |= 1 << (attribute))
+#define SWFDEC_TEXT_ATTRIBUTE_UNSET(flags, attribute) ((flags) &= ~(1 << (attribute)))
+#define SWFDEC_TEXT_ATTRIBUTE_IS_SET(flags, attribute) ((flags) & (1 << (attribute)) ? TRUE : FALSE)
+
+struct _SwfdecTextAttributes {
+  SwfdecTextAlign	align;
+  int			block_indent;
+  gboolean		bold;
+  gboolean		bullet;
+  SwfdecColor		color;
+  SwfdecTextDisplay	display;
+  const char *		font;
+  int			indent;
+  gboolean		italic;
+  gboolean		kerning;
+  int			leading;
+  int			left_margin;
+  double		letter_spacing; // number or null
+  int			right_margin;
+  int			size;
+  guint *		tab_stops;
+  gsize			n_tab_stops;
+  const char *		target;
+  gboolean		underline;
+  const char *		url;
+};
+
+
+void			swfdec_text_attributes_reset	(SwfdecTextAttributes *		attr);
+void			swfdec_text_attributes_copy	(SwfdecTextAttributes *		attr,
+							 const SwfdecTextAttributes *	from,
+							 guint				flags);
+void			swfdec_text_attributes_mark	(SwfdecTextAttributes *		attr);
+
+guint			swfdec_text_attributes_diff	(const SwfdecTextAttributes *	a,
+							 const SwfdecTextAttributes *	b);
+
+
+G_END_DECLS
+#endif


More information about the Swfdec-commits mailing list