summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pango/pango-attributes.c543
-rw-r--r--pango/pango-attributes.h5
-rw-r--r--tests/meson.build1
-rw-r--r--tests/testserialize.c86
4 files changed, 635 insertions, 0 deletions
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index c929a3d5..13468963 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -2524,6 +2524,549 @@ pango_attr_list_filter (PangoAttrList *list,
return new;
}
+/* {{{ PangoAttrList serialization */
+
+/* We serialize attribute lists to strings. The format
+ * is a comma-separated list of the attributes in the order
+ * in which they are in the list, with each attribute having
+ * this format:
+ *
+ * START END NICK VALUE
+ *
+ * Values that can contain a comma, such as font descriptions
+ * are quoted with "".
+ */
+
+static const char *
+get_attr_type_nick (PangoAttrType attr_type)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (pango_attr_type_get_type ());
+ enum_value = g_enum_get_value (enum_class, attr_type);
+ g_type_class_unref (enum_class);
+
+ return enum_value->value_nick;
+}
+
+static GType
+get_attr_value_type (PangoAttrType type)
+{
+ switch ((int)type)
+ {
+ case PANGO_ATTR_STYLE: return PANGO_TYPE_STYLE;
+ case PANGO_ATTR_WEIGHT: return PANGO_TYPE_WEIGHT;
+ case PANGO_ATTR_VARIANT: return PANGO_TYPE_VARIANT;
+ case PANGO_ATTR_STRETCH: return PANGO_TYPE_STRETCH;
+ case PANGO_ATTR_GRAVITY: return PANGO_TYPE_GRAVITY;
+ case PANGO_ATTR_GRAVITY_HINT: return PANGO_TYPE_GRAVITY_HINT;
+ case PANGO_ATTR_UNDERLINE: return PANGO_TYPE_UNDERLINE;
+ case PANGO_ATTR_OVERLINE: return PANGO_TYPE_OVERLINE;
+ case PANGO_ATTR_BASELINE_SHIFT: return PANGO_TYPE_BASELINE_SHIFT;
+ case PANGO_ATTR_FONT_SCALE: return PANGO_TYPE_FONT_SCALE;
+ case PANGO_ATTR_TEXT_TRANSFORM: return PANGO_TYPE_TEXT_TRANSFORM;
+ default: return G_TYPE_INVALID;
+ }
+}
+
+static void
+append_enum_value (GString *str,
+ GType type,
+ int value)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (type);
+ enum_value = g_enum_get_value (enum_class, value);
+ g_type_class_unref (enum_class);
+
+ if (enum_value)
+ g_string_append_printf (str, " %s", enum_value->value_nick);
+ else
+ g_string_append_printf (str, " %d", value);
+}
+
+static void
+attr_print (GString *str,
+ PangoAttribute *attr)
+{
+ PangoAttrString *string;
+ PangoAttrLanguage *lang;
+ PangoAttrInt *integer;
+ PangoAttrFloat *flt;
+ PangoAttrFontDesc *font;
+ PangoAttrColor *color;
+ PangoAttrShape *shape;
+ PangoAttrSize *size;
+ PangoAttrFontFeatures *features;
+
+ g_string_append_printf (str, "%u %u ", attr->start_index, attr->end_index);
+
+ g_string_append (str, get_attr_type_nick (attr->klass->type));
+
+ if (attr->klass->type == PANGO_ATTR_WEIGHT ||
+ attr->klass->type == PANGO_ATTR_STYLE ||
+ attr->klass->type == PANGO_ATTR_STRETCH ||
+ attr->klass->type == PANGO_ATTR_VARIANT ||
+ attr->klass->type == PANGO_ATTR_GRAVITY ||
+ attr->klass->type == PANGO_ATTR_GRAVITY_HINT ||
+ attr->klass->type == PANGO_ATTR_UNDERLINE ||
+ attr->klass->type == PANGO_ATTR_OVERLINE ||
+ attr->klass->type == PANGO_ATTR_BASELINE_SHIFT ||
+ attr->klass->type == PANGO_ATTR_FONT_SCALE ||
+ attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
+ append_enum_value (str, get_attr_value_type (attr->klass->type), ((PangoAttrInt *)attr)->value);
+ else if (attr->klass->type == PANGO_ATTR_STRIKETHROUGH ||
+ attr->klass->type == PANGO_ATTR_ALLOW_BREAKS ||
+ attr->klass->type == PANGO_ATTR_INSERT_HYPHENS ||
+ attr->klass->type == PANGO_ATTR_FALLBACK)
+ g_string_append (str, ((PangoAttrInt *)attr)->value ? " true" : " false");
+ else if ((string = pango_attribute_as_string (attr)) != NULL)
+ g_string_append_printf (str, " %s", string->value);
+ else if ((lang = pango_attribute_as_language (attr)) != NULL)
+ g_string_append_printf (str, " %s", pango_language_to_string (lang->value));
+ else if ((integer = pango_attribute_as_int (attr)) != NULL)
+ g_string_append_printf (str, " %d", integer->value);
+ else if ((flt = pango_attribute_as_float (attr)) != NULL)
+ {
+ char buf[20];
+ g_ascii_formatd (buf, 20, " %f", flt->value);
+ g_string_append_printf (str, " %s", buf);
+ }
+ else if ((font = pango_attribute_as_font_desc (attr)) != NULL)
+ {
+ char *s = pango_font_description_to_string (font->desc);
+ g_string_append_printf (str, " \"%s\"", s);
+ g_free (s);
+ }
+ else if ((color = pango_attribute_as_color (attr)) != NULL)
+ {
+ char *s = pango_color_to_string (&color->color);
+ g_string_append_printf (str, " %s", s);
+ g_free (s);
+ }
+ else if ((shape = pango_attribute_as_shape (attr)) != NULL)
+ g_string_append (str, "shape"); /* FIXME */
+ else if ((size = pango_attribute_as_size (attr)) != NULL)
+ g_string_append_printf (str, " %d", size->size);
+ else if ((features = pango_attribute_as_font_features (attr)) != NULL)
+ g_string_append_printf (str, " \"%s\"", features->features);
+ else
+ g_assert_not_reached ();
+}
+
+/**
+ * pango_attr_list_to_string:
+ * @list: a `PangoAttrList`
+ *
+ * Serializes a `PangoAttrList` to a string.
+ *
+ * No guarantees are made about the format of the string,
+ * it may change between Pango versions.
+ *
+ * The intended use of this function is testing and
+ * debugging. The format is not meant as a permanent
+ * storage format.
+ *
+ * Returns: (transfer full): a newly allocated string
+ * Since: 1.50
+ */
+char *
+pango_attr_list_to_string (PangoAttrList *list)
+{
+ GString *s;
+
+ s = g_string_new ("");
+
+ if (list->attributes)
+ for (int i = 0; i < list->attributes->len; i++)
+ {
+ PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
+
+ if (i > 0)
+ g_string_append (s, "\n");
+ attr_print (s, attr);
+ }
+
+ return g_string_free (s, FALSE);
+}
+
+static PangoAttrType
+get_attr_type_by_nick (const char *nick,
+ int len)
+{
+ GEnumClass *enum_class;
+
+ enum_class = g_type_class_ref (pango_attr_type_get_type ());
+ for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
+ {
+ if (ev->value_nick && strncmp (ev->value_nick, nick, len) == 0)
+ {
+ g_type_class_unref (enum_class);
+ return (PangoAttrType) ev->value;
+ }
+ }
+
+ g_type_class_unref (enum_class);
+ return PANGO_ATTR_INVALID;
+}
+
+static int
+get_attr_value (PangoAttrType type,
+ const char *str,
+ int len)
+{
+ GEnumClass *enum_class;
+ char *endp;
+ int value;
+
+ enum_class = g_type_class_ref (get_attr_value_type (type));
+ for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
+ {
+ if (ev->value_nick && strncmp (ev->value_nick, str, len) == 0)
+ {
+ g_type_class_unref (enum_class);
+ return ev->value;
+ }
+ }
+ g_type_class_unref (enum_class);
+
+ value = g_ascii_strtoll (str, &endp, 10);
+ if (endp - str == len)
+ return value;
+
+ return -1;
+}
+
+static gboolean
+is_valid_end_char (char c)
+{
+ return c == ',' || c == '\n' || c == '\0';
+}
+
+/**
+ * pango_attr_list_from_string:
+ * @text: a string
+ *
+ * Deserializes a `PangoAttrList` from a string.
+ *
+ * This is the counterpart to [func@Pango.AttrList.to_string].
+ * See that functions for details about the format.
+ *
+ * Returns: (transfer full) (nullable): a new `PangoAttrList`
+ * Since: 1.50
+ */
+PangoAttrList *
+pango_attr_list_from_string (const char *text)
+{
+ PangoAttrList *list;
+ const char *p;
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ list = pango_attr_list_new ();
+
+ if (*text == '\0')
+ return list;
+
+ list->attributes = g_ptr_array_new ();
+
+ p = text + strspn (text, " \t\n");
+ while (*p)
+ {
+ char *endp;
+ gint64 start_index;
+ gint64 end_index;
+ char *str;
+ PangoAttrType attr_type;
+ PangoAttribute *attr;
+ PangoLanguage *lang;
+ gint64 integer;
+ PangoFontDescription *desc;
+ PangoColor color;
+ double num;
+
+ start_index = g_ascii_strtoll (p, &endp, 10);
+ if (*endp != ' ')
+ goto fail;
+
+ p = endp + strspn (endp, " ");
+ if (!*p)
+ goto fail;
+
+ end_index = g_ascii_strtoll (p, &endp, 10);
+ if (*endp != ' ')
+ goto fail;
+
+ p = endp + strspn (endp, " ");
+
+ endp = (char *)strpbrk (p, " ");
+ attr_type = get_attr_type_by_nick (p, endp - p);
+
+ p = endp + strspn (endp, " ");
+ if (*p == '\0')
+ goto fail;
+
+#define INT_ATTR(name,type) \
+ integer = g_ascii_strtoll (p, &endp, 10); \
+ if (!is_valid_end_char (*endp)) goto fail; \
+ attr = pango_attr_##name##_new ((type)integer);
+
+#define BOOLEAN_ATTR(name,type) \
+ if (strncmp (p, "true", strlen ("true")) == 0) \
+ { \
+ integer = 1; \
+ endp = (char *)(p + strlen ("true")); \
+ } \
+ else if (strncmp (p, "false", strlen ("false")) == 0) \
+ { \
+ integer = 0; \
+ endp = (char *)(p + strlen ("false")); \
+ } \
+ else \
+ integer = g_ascii_strtoll (p, &endp, 10); \
+ if (!is_valid_end_char (*endp)) goto fail; \
+ attr = pango_attr_##name##_new ((type)integer);
+
+#define ENUM_ATTR(name, type, min, max) \
+ endp = (char *)p + strcspn (p, ",\n"); \
+ integer = get_attr_value (attr_type, p, endp - p); \
+ attr = pango_attr_##name##_new ((type) CLAMP (integer, min, max));
+
+#define FLOAT_ATTR(name) \
+ num = g_ascii_strtod (p, &endp); \
+ if (!is_valid_end_char (*endp)) goto fail; \
+ attr = pango_attr_##name##_new ((float)num);
+
+#define COLOR_ATTR(name) \
+ endp = (char *)p + strcspn (p, ",\n"); \
+ if (!is_valid_end_char (*endp)) goto fail; \
+ str = g_strndup (p, endp - p); \
+ if (!pango_color_parse (&color, str)) \
+ { \
+ g_free (str); \
+ goto fail; \
+ } \
+ attr = pango_attr_##name##_new (color.red, color.green, color.blue); \
+ g_free (str);
+
+ switch (attr_type)
+ {
+ case PANGO_ATTR_INVALID:
+ pango_attr_list_unref (list);
+ return NULL;
+
+ case PANGO_ATTR_LANGUAGE:
+ endp = (char *)p + strcspn (p, ",\n");
+ if (!is_valid_end_char (*endp)) goto fail;
+ str = g_strndup (p, endp - p);
+ lang = pango_language_from_string (str);
+ attr = pango_attr_language_new (lang);
+ g_free (str);
+ break;
+
+ case PANGO_ATTR_FAMILY:
+ endp = (char *)p + strcspn (p, ",\n");
+ if (!is_valid_end_char (*endp)) goto fail;
+ str = g_strndup (p, endp - p);
+ attr = pango_attr_family_new (str);
+ g_free (str);
+ break;
+
+ case PANGO_ATTR_STYLE:
+ ENUM_ATTR(style, PangoStyle, PANGO_STYLE_NORMAL, PANGO_STYLE_ITALIC);
+ break;
+
+ case PANGO_ATTR_WEIGHT:
+ ENUM_ATTR(weight, PangoWeight, PANGO_WEIGHT_THIN, PANGO_WEIGHT_ULTRAHEAVY);
+ break;
+
+ case PANGO_ATTR_VARIANT:
+ ENUM_ATTR(variant, PangoVariant, PANGO_VARIANT_NORMAL, PANGO_VARIANT_TITLE_CAPS);
+ break;
+
+ case PANGO_ATTR_STRETCH:
+ ENUM_ATTR(stretch, PangoStretch, PANGO_STRETCH_ULTRA_CONDENSED, PANGO_STRETCH_ULTRA_EXPANDED);
+ break;
+
+ case PANGO_ATTR_SIZE:
+ INT_ATTR(size, int);
+ break;
+
+ case PANGO_ATTR_FONT_DESC:
+ p++;
+ endp = strchr (p, '"');
+ if (!endp) goto fail;
+ str = g_strndup (p, endp - p);
+ desc = pango_font_description_from_string (str);
+ attr = pango_attr_font_desc_new (desc);
+ pango_font_description_free (desc);
+ g_free (str);
+ endp++;
+ if (!is_valid_end_char (*endp)) goto fail;
+ break;
+
+ case PANGO_ATTR_FOREGROUND:
+ COLOR_ATTR(foreground);
+ break;
+
+ case PANGO_ATTR_BACKGROUND:
+ COLOR_ATTR(background);
+ break;
+
+ case PANGO_ATTR_UNDERLINE:
+ ENUM_ATTR(underline, PangoUnderline, PANGO_UNDERLINE_NONE, PANGO_UNDERLINE_ERROR_LINE);
+ break;
+
+ case PANGO_ATTR_STRIKETHROUGH:
+ BOOLEAN_ATTR(strikethrough, gboolean);
+ break;
+
+ case PANGO_ATTR_RISE:
+ INT_ATTR(rise, int);
+ break;
+
+ case PANGO_ATTR_SHAPE:
+ endp = (char *)strpbrk (p, ",\n");
+ p = endp + strspn (endp, " ");
+ continue; /* FIXME */
+
+ case PANGO_ATTR_SCALE:
+ FLOAT_ATTR(scale);
+ break;
+
+ case PANGO_ATTR_FALLBACK:
+ BOOLEAN_ATTR(fallback, gboolean);
+ break;
+
+ case PANGO_ATTR_LETTER_SPACING:
+ INT_ATTR(letter_spacing, int);
+ break;
+
+ case PANGO_ATTR_UNDERLINE_COLOR:
+ COLOR_ATTR(underline_color);
+ break;
+
+ case PANGO_ATTR_STRIKETHROUGH_COLOR:
+ COLOR_ATTR(strikethrough_color);
+ break;
+
+ case PANGO_ATTR_ABSOLUTE_SIZE:
+ integer = g_ascii_strtoll (p, &endp, 10);
+ if (!is_valid_end_char (*endp)) goto fail;
+ attr = pango_attr_size_new_absolute (integer);
+ break;
+
+ case PANGO_ATTR_GRAVITY:
+ ENUM_ATTR(gravity, PangoGravity, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_WEST);
+ break;
+
+ case PANGO_ATTR_FONT_FEATURES:
+ p++;
+ endp = strchr (p, '"');
+ if (!endp) goto fail;
+ str = g_strndup (p, endp - p);
+ attr = pango_attr_font_features_new (str);
+ g_free (str);
+ endp++;
+ if (!is_valid_end_char (*endp)) goto fail;
+ break;
+
+ case PANGO_ATTR_GRAVITY_HINT:
+ ENUM_ATTR(gravity_hint, PangoGravityHint, PANGO_GRAVITY_HINT_NATURAL, PANGO_GRAVITY_HINT_LINE);
+ break;
+
+ case PANGO_ATTR_FOREGROUND_ALPHA:
+ INT_ATTR(foreground_alpha, int);
+ break;
+
+ case PANGO_ATTR_BACKGROUND_ALPHA:
+ INT_ATTR(background_alpha, int);
+ break;
+
+ case PANGO_ATTR_ALLOW_BREAKS:
+ BOOLEAN_ATTR(allow_breaks, gboolean);
+ break;
+
+ case PANGO_ATTR_SHOW:
+ INT_ATTR(show, PangoShowFlags);
+ break;
+
+ case PANGO_ATTR_INSERT_HYPHENS:
+ BOOLEAN_ATTR(insert_hyphens, gboolean);
+ break;
+
+ case PANGO_ATTR_OVERLINE:
+ ENUM_ATTR(overline, PangoOverline, PANGO_OVERLINE_NONE, PANGO_OVERLINE_SINGLE);
+ break;
+
+ case PANGO_ATTR_OVERLINE_COLOR:
+ COLOR_ATTR(overline_color);
+ break;
+
+ case PANGO_ATTR_LINE_HEIGHT:
+ FLOAT_ATTR(line_height);
+ break;
+
+ case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
+ integer = g_ascii_strtoll (p, &endp, 10);
+ if (!is_valid_end_char (*endp)) goto fail;
+ attr = pango_attr_line_height_new_absolute (integer);
+ break;
+
+ case PANGO_ATTR_TEXT_TRANSFORM:
+ ENUM_ATTR(text_transform, PangoTextTransform, PANGO_TEXT_TRANSFORM_NONE, PANGO_TEXT_TRANSFORM_CAPITALIZE);
+ break;
+
+ case PANGO_ATTR_WORD:
+ integer = g_ascii_strtoll (p, &endp, 10);
+ if (!is_valid_end_char (*endp)) goto fail;
+ attr = pango_attr_word_new ();
+ break;
+
+ case PANGO_ATTR_SENTENCE:
+ integer = g_ascii_strtoll (p, &endp, 10);
+ if (!is_valid_end_char (*endp)) goto fail;
+ attr = pango_attr_sentence_new ();
+ break;
+
+ case PANGO_ATTR_BASELINE_SHIFT:
+ ENUM_ATTR(baseline_shift, PangoBaselineShift, 0, G_MAXINT);
+ break;
+
+ case PANGO_ATTR_FONT_SCALE:
+ ENUM_ATTR(font_scale, PangoFontScale, PANGO_FONT_SCALE_NONE, PANGO_FONT_SCALE_SMALL_CAPS);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ attr->start_index = (guint)start_index;
+ attr->end_index = (guint)end_index;
+ g_ptr_array_add (list->attributes, attr);
+
+ p = endp;
+ if (*p)
+ {
+ if (*p == ',')
+ p++;
+ p += strspn (p, " \n");
+ }
+ }
+
+ goto success;
+
+fail:
+ pango_attr_list_unref (list);
+ list = NULL;
+
+success:
+ return list;
+}
+
/* }}} */
/* {{{ Attribute Iterator */
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 018417d5..5ea6bd9e 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -707,6 +707,11 @@ PANGO_AVAILABLE_IN_1_46
gboolean pango_attr_list_equal (PangoAttrList *list,
PangoAttrList *other_list);
+PANGO_AVAILABLE_IN_1_50
+char * pango_attr_list_to_string (PangoAttrList *list);
+PANGO_AVAILABLE_IN_1_50
+PangoAttrList * pango_attr_list_from_string (const char *text);
+
PANGO_AVAILABLE_IN_1_44
GType pango_attr_iterator_get_type (void) G_GNUC_CONST;
diff --git a/tests/meson.build b/tests/meson.build
index b5eda3e7..00741a14 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -53,6 +53,7 @@ if cairo_dep.found()
[ 'cxx-test', [ 'cxx-test.cpp' ], [ libpangocairo_dep, gobject_dep, harfbuzz_dep ] ],
[ 'test-harfbuzz', [ 'test-harfbuzz.c' ], [ libpangocairo_dep, gobject_dep, harfbuzz_dep ] ],
[ 'test-break', [ 'test-break.c', 'test-common.c', 'validate-log-attrs.c' ], [libpangocairo_dep, glib_dep, harfbuzz_dep ] ],
+ [ 'testserialize', [ 'testserialize.c' ], [ libpangocairo_dep ] ],
]
if host_system != 'darwin'
diff --git a/tests/testserialize.c b/tests/testserialize.c
new file mode 100644
index 00000000..cb5b27fd
--- /dev/null
+++ b/tests/testserialize.c
@@ -0,0 +1,86 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <pango/pangocairo.h>
+
+static void
+test_serialize_attr_list (void)
+{
+ const char *valid[] = {
+ "5 16 style italic",
+ "0 10 foreground red, 5 15 weight bold, 0 200 font-desc \"Sans Small-Caps 10\"",
+ "0 10 foreground red\n5 15 weight bold\n0 200 font-desc \"Sans Small-Caps 10\"",
+ " 0 10 fallback false,\n 5 15 weight semilight\n\n \n \n",
+ "0 100 font-desc \"Cantarell, Sans, Italic Ultra-Light 64\", 10 11 weight 100",
+ "0 -1 size 10",
+ "0 1 weight 700, 2 4 weight book",
+ "0 200 rise 100\n5 15 family Times\n10 11 size 10240\n11 100 fallback 0\n30 60 stretch 2\n",
+ ""
+ };
+ const char *roundtripped[] = {
+ "5 16 style italic",
+ "0 10 foreground #ffff00000000\n5 15 weight bold\n0 200 font-desc \"Sans Small-Caps 10\"",
+ "0 10 foreground #ffff00000000\n5 15 weight bold\n0 200 font-desc \"Sans Small-Caps 10\"",
+ "0 10 fallback false\n5 15 weight semilight",
+ "0 100 font-desc \"Cantarell,Sans Ultra-Light Italic 64\"\n10 11 weight thin",
+ "0 4294967295 size 10",
+ "0 1 weight bold\n2 4 weight book",
+ "0 200 rise 100\n5 15 family Times\n10 11 size 10240\n11 100 fallback false\n30 60 stretch condensed",
+ ""
+ };
+ const char *invalid[] = {
+ "not an attr list",
+ "0 -1 FOREGROUND xyz",
+ ",,bla.wewq",
+ };
+
+ for (int i = 0; i < G_N_ELEMENTS (valid); i++)
+ {
+ PangoAttrList *attrs;
+ char *str;
+
+ attrs = pango_attr_list_from_string (valid[i]);
+ g_assert_nonnull (attrs);
+ str = pango_attr_list_to_string (attrs);
+ g_assert_cmpstr (str, ==, roundtripped[i]);
+ g_free (str);
+ pango_attr_list_unref (attrs);
+ }
+
+ for (int i = 0; i < G_N_ELEMENTS (invalid); i++)
+ {
+ PangoAttrList *attrs;
+
+ attrs = pango_attr_list_from_string (invalid[i]);
+ g_assert_null (attrs);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/serialize/attr-list", test_serialize_attr_list);
+
+ return g_test_run ();
+}