[pango/redo-attrs: 12/13] Redo attributes




commit 5bb11a70a0258a7801038714a1c8ad511cee640f
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Jan 30 04:46:33 2022 +0100

    Redo attributes
    
    Do away with PangoAttribute subclassing,
    and turn the PangoAttribute struct into
    a union. Redo custom attribute registration
    to be more useful.

 pango/break.c                    |   12 +-
 pango/ellipsize.c                |    2 +-
 pango/itemize.c                  |   18 +-
 pango/pango-attributes-private.h |    2 +
 pango/pango-attributes.c         | 1386 +++++++++++++++-----------------------
 pango/pango-attributes.h         |  308 +++------
 pango/pango-item.c               |    7 +-
 pango/pango-layout.c             |   34 +-
 pango/pango-renderer.c           |   22 +-
 pango/serializer.c               |  241 +++----
 pango/shape.c                    |    8 +-
 tests/markups/valid-19.expected  |    2 +-
 tests/markups/valid-4.expected   |    4 +-
 tests/test-itemize.c             |    2 +-
 tests/test-shape.c               |    4 +-
 tests/testattributes.c           |  180 +++--
 tests/testserialize.c            |    8 +-
 17 files changed, 937 insertions(+), 1303 deletions(-)
---
diff --git a/pango/break.c b/pango/break.c
index 05ec97c9..b31eca13 100644
--- a/pango/break.c
+++ b/pango/break.c
@@ -1801,7 +1801,7 @@ handle_allow_breaks (const char    *text,
       if (!attr)
         continue;
 
-      if (!((PangoAttrInt*)attr)->value)
+      if (!attr->int_value)
         {
           int start, end;
           int start_pos, end_pos;
@@ -2043,7 +2043,7 @@ handle_hyphens (const char    *text,
   do {
     const PangoAttribute *attr = pango_attr_iterator_get (&iter, PANGO_ATTR_INSERT_HYPHENS);
 
-    if (attr && ((PangoAttrInt*)attr)->value == 0)
+    if (attr && attr->int_value == 0)
       {
         int start, end;
         int start_pos, end_pos;
@@ -2100,13 +2100,13 @@ break_attrs (const char   *text,
     {
       PangoAttribute *attr = l->data;
 
-      if (attr->klass->type == PANGO_ATTR_ALLOW_BREAKS)
+      if (attr->type == PANGO_ATTR_ALLOW_BREAKS)
         pango_attr_list_insert (&allow_breaks, pango_attribute_copy (attr));
-      else if (attr->klass->type == PANGO_ATTR_WORD)
+      else if (attr->type == PANGO_ATTR_WORD)
         pango_attr_list_insert (&words, pango_attribute_copy (attr));
-      else if (attr->klass->type == PANGO_ATTR_SENTENCE)
+      else if (attr->type == PANGO_ATTR_SENTENCE)
         pango_attr_list_insert (&sentences, pango_attribute_copy (attr));
-      else if (attr->klass->type == PANGO_ATTR_INSERT_HYPHENS)
+      else if (attr->type == PANGO_ATTR_INSERT_HYPHENS)
         pango_attr_list_insert (&hyphens, pango_attribute_copy (attr));
     }
 
diff --git a/pango/ellipsize.c b/pango/ellipsize.c
index d7b7fee0..13edd5a0 100644
--- a/pango/ellipsize.c
+++ b/pango/ellipsize.c
@@ -364,7 +364,7 @@ shape_ellipsis (EllipsizeState *state)
 
       /* Modify the fallback iter while it is inside the PangoAttrList; Don't try this at home
        */
-      ((PangoAttrInt *)fallback)->value = TRUE;
+      fallback->int_value = TRUE;
 
       ellipsis_text = "...";
       item = itemize_text (state, ellipsis_text, &attrs);
diff --git a/pango/itemize.c b/pango/itemize.c
index 935a3250..dab6eb7e 100644
--- a/pango/itemize.c
+++ b/pango/itemize.c
@@ -352,7 +352,7 @@ find_attribute (GSList        *attr_list,
   GSList *node;
 
   for (node = attr_list; node; node = node->next)
-    if (((PangoAttribute *) node->data)->klass->type == type)
+    if (((PangoAttribute *) node->data)->type == type)
       return (PangoAttribute *) node->data;
 
   return NULL;
@@ -394,13 +394,13 @@ update_attr_iterator (ItemizeState *state)
     state->lang = state->context->language;
 
   attr = find_attribute (state->extra_attrs, PANGO_ATTR_FALLBACK);
-  state->enable_fallback = (attr == NULL || ((PangoAttrInt *)attr)->value);
+  state->enable_fallback = (attr == NULL || attr->int_value);
 
   attr = find_attribute (state->extra_attrs, PANGO_ATTR_GRAVITY);
-  state->gravity = attr == NULL ? PANGO_GRAVITY_AUTO : ((PangoAttrInt *)attr)->value;
+  state->gravity = attr == NULL ? PANGO_GRAVITY_AUTO : attr->int_value;
 
   attr = find_attribute (state->extra_attrs, PANGO_ATTR_GRAVITY_HINT);
-  state->gravity_hint = attr == NULL ? state->context->gravity_hint : (PangoGravityHint)((PangoAttrInt 
*)attr)->value;
+  state->gravity_hint = attr == NULL ? state->context->gravity_hint : (PangoGravityHint)attr->int_value;
 
   state->changed |= FONT_CHANGED;
   if (state->lang != old_lang)
@@ -1046,7 +1046,7 @@ collect_font_scale (PangoContext  *context,
     {
       PangoAttribute *attr = l->data;
 
-      if (attr->klass->type == PANGO_ATTR_FONT_SCALE)
+      if (attr->type == PANGO_ATTR_FONT_SCALE)
         {
           if (attr->start_index == item->offset)
             {
@@ -1060,7 +1060,7 @@ collect_font_scale (PangoContext  *context,
               entry->attr = attr;
               *stack = g_list_prepend (*stack, entry);
 
-              switch (((PangoAttrInt *)attr)->value)
+              switch (attr->int_value)
                 {
                 case PANGO_FONT_SCALE_NONE:
                   break;
@@ -1121,7 +1121,7 @@ collect_font_scale (PangoContext  *context,
      {
        ScaleItem *entry = l->data;
        *scale *= entry->scale;
-       if (((PangoAttrInt *)entry->attr)->value != PANGO_FONT_SCALE_SMALL_CAPS)
+       if (entry->attr->int_value != PANGO_FONT_SCALE_SMALL_CAPS)
          *is_small_caps = FALSE;
        retval = TRUE;
      }
@@ -1302,8 +1302,8 @@ find_text_transform (const PangoAnalysis *analysis)
     {
       PangoAttribute *attr = l->data;
 
-      if (attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
-        transform = (PangoTextTransform) ((PangoAttrInt*)attr)->value;
+      if (attr->type == PANGO_ATTR_TEXT_TRANSFORM)
+        transform = (PangoTextTransform) attr->int_value;
     }
 
   return transform;
diff --git a/pango/pango-attributes-private.h b/pango/pango-attributes-private.h
index 4c427695..b4c5b671 100644
--- a/pango/pango-attributes-private.h
+++ b/pango/pango-attributes-private.h
@@ -38,6 +38,7 @@ struct _PangoAttrList
   GPtrArray *attributes;
 };
 
+char *    pango_attr_value_serialize   (PangoAttribute    *attr);
 void     _pango_attr_list_init         (PangoAttrList     *list);
 void     _pango_attr_list_destroy      (PangoAttrList     *list);
 gboolean _pango_attr_list_has_attributes (const PangoAttrList *list);
@@ -51,3 +52,4 @@ gboolean  pango_attr_iterator_advance  (PangoAttrIterator *iterator,
 
 
 #endif
+
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index b3b28f08..73fd5799 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -30,40 +30,87 @@
 /* {{{ Generic attribute code */
 
 G_LOCK_DEFINE_STATIC (attr_type);
-static GHashTable *name_map = NULL; /* MT-safe */
+static GArray *attr_type;
+
+#define MIN_CUSTOM 1000
+
+typedef struct _PangoAttrClass PangoAttrClass;
+
+struct _PangoAttrClass {
+  PangoAttrType type;
+  const char *name;
+  PangoAttrDataCopyFunc copy;
+  GDestroyNotify destroy;
+  GEqualFunc equal;
+  PangoAttrDataSerializeFunc serialize;
+};
+
+static const char *
+get_attr_type_nick (PangoAttrType 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, type);
+  g_type_class_unref (enum_class);
+
+  return enum_value->value_nick;
+}
 
 /**
  * pango_attr_type_register:
- * @name: an identifier for the type
+ * @copy: function to copy the data of an attribute
+ *   of this type
+ * @destroy: function to free the data of an attribute
+ *   of this type
+ * @equal: function to compare the data of two attributes
+ *   of this type
+ * @name: (nullable): an identifier for the type
+ * @serialize: (nullable): function to serialize the data
+ *   of an attribute of this type
  *
  * Allocate a new attribute type ID.
  *
  * The attribute type name can be accessed later
  * by using [func@Pango.AttrType.get_name].
  *
+ * If @name and to @serialize are provided, they will be used
+ * to serialize attributes of this type.
+ *
  * Return value: the new type ID.
  */
 PangoAttrType
-pango_attr_type_register (const gchar *name)
+pango_attr_type_register (PangoAttrDataCopyFunc       copy,
+                          GDestroyNotify              destroy,
+                          GEqualFunc                  equal,
+                          const char                 *name,
+                          PangoAttrDataSerializeFunc  serialize)
 {
-  static guint current_type = 0x1000000; /* MT-safe */
-  guint type;
+  static guint current_id = MIN_CUSTOM; /* MT-safe */
+  PangoAttrClass class;
 
   G_LOCK (attr_type);
 
-  type = current_type++;
+  class.type = PANGO_ATTR_VALUE_POINTER | (current_id << 8);
+  current_id++;
+
+  class.copy = copy;
+  class.destroy = destroy;
+  class.equal = equal;
+  class.serialize = serialize;
 
   if (name)
-    {
-      if (G_UNLIKELY (!name_map))
-        name_map = g_hash_table_new (NULL, NULL);
+    class.name = g_intern_string (name);
 
-      g_hash_table_insert (name_map, GUINT_TO_POINTER (type), (gpointer) g_intern_string (name));
-    }
+  if (attr_type == NULL)
+    attr_type = g_array_new (FALSE, FALSE, sizeof (PangoAttrClass));
+
+  g_array_append_val (attr_type, class);
 
   G_UNLOCK (attr_type);
 
-  return type;
+  return class.type;
 }
 
 /**
@@ -91,40 +138,26 @@ pango_attr_type_get_name (PangoAttrType type)
 {
   const char *result = NULL;
 
+  if ((type >> 8) < MIN_CUSTOM)
+    return get_attr_type_nick (type);
+
   G_LOCK (attr_type);
 
-  if (name_map)
-    result = g_hash_table_lookup (name_map, GUINT_TO_POINTER ((guint) type));
+  for (int i = 0; i < attr_type->len; i++)
+    {
+      PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+      if (class->type == type)
+        {
+          result = class->name;
+          break;
+        }
+    }
 
   G_UNLOCK (attr_type);
 
   return result;
 }
 
-/**
- * pango_attribute_init:
- * @attr: a `PangoAttribute`
- * @klass: a `PangoAttrClass`
- *
- * Initializes @attr's klass to @klass, it's start_index to
- * %PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING and end_index to
- * %PANGO_ATTR_INDEX_TO_TEXT_END such that the attribute applies
- * to the entire text by default.
- *
- * Since: 1.20
- */
-void
-pango_attribute_init (PangoAttribute       *attr,
-                      const PangoAttrClass *klass)
-{
-  g_return_if_fail (attr != NULL);
-  g_return_if_fail (klass != NULL);
-
-  attr->klass = klass;
-  attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
-  attr->end_index   = PANGO_ATTR_INDEX_TO_TEXT_END;
-}
-
 /**
  * pango_attribute_copy:
  * @attr: a `PangoAttribute`
@@ -142,9 +175,51 @@ pango_attribute_copy (const PangoAttribute *attr)
 
   g_return_val_if_fail (attr != NULL, NULL);
 
-  result = attr->klass->copy (attr);
-  result->start_index = attr->start_index;
-  result->end_index = attr->end_index;
+  result = g_slice_dup (PangoAttribute, attr);
+
+  switch (PANGO_ATTR_VALUE_TYPE (attr))
+    {
+    case PANGO_ATTR_VALUE_STRING:
+      result->str_value = g_strdup (attr->str_value);
+      break;
+
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      result->font_value = pango_font_description_copy (attr->font_value);
+      break;
+
+    case PANGO_ATTR_VALUE_POINTER:
+      {
+        PangoAttrDataCopyFunc copy = NULL;
+
+        G_LOCK (attr_type);
+
+        g_assert (attr_type != NULL);
+
+        for (int i = 0; i < attr_type->len; i++)
+          {
+            PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+            if (class->type == attr->type)
+              {
+                copy = class->copy;
+                break;
+              }
+          }
+
+        G_UNLOCK (attr_type);
+
+        g_assert (copy != NULL);
+
+        result->pointer_value = copy (attr->pointer_value);
+      }
+      break;
+
+    case PANGO_ATTR_VALUE_INT:
+    case PANGO_ATTR_VALUE_BOOLEAN:
+    case PANGO_ATTR_VALUE_FLOAT:
+    case PANGO_ATTR_VALUE_COLOR:
+    case PANGO_ATTR_VALUE_LANGUAGE:
+    default: ;
+    }
 
   return result;
 }
@@ -160,7 +235,51 @@ pango_attribute_destroy (PangoAttribute *attr)
 {
   g_return_if_fail (attr != NULL);
 
-  attr->klass->destroy (attr);
+  switch (PANGO_ATTR_VALUE_TYPE (attr))
+    {
+    case PANGO_ATTR_VALUE_STRING:
+      g_free (attr->str_value);
+      break;
+
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      pango_font_description_free (attr->font_value);
+      break;
+
+    case PANGO_ATTR_VALUE_POINTER:
+      {
+        GDestroyNotify destroy = NULL;
+
+        G_LOCK (attr_type);
+
+        g_assert (attr_type != NULL);
+
+        for (int i = 0; i < attr_type->len; i++)
+          {
+            PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+            if (class->type == attr->type)
+              {
+                destroy = class->destroy;
+                break;
+              }
+          }
+
+        G_UNLOCK (attr_type);
+
+        g_assert (destroy != NULL);
+
+        destroy (attr->pointer_value);
+      }
+      break;
+
+    case PANGO_ATTR_VALUE_INT:
+    case PANGO_ATTR_VALUE_BOOLEAN:
+    case PANGO_ATTR_VALUE_FLOAT:
+    case PANGO_ATTR_VALUE_COLOR:
+    case PANGO_ATTR_VALUE_LANGUAGE:
+    default: ;
+    }
+
+  g_slice_free (PangoAttribute, attr);
 }
 
 G_DEFINE_BOXED_TYPE (PangoAttribute, pango_attribute,
@@ -187,297 +306,209 @@ pango_attribute_equal (const PangoAttribute *attr1,
   g_return_val_if_fail (attr1 != NULL, FALSE);
   g_return_val_if_fail (attr2 != NULL, FALSE);
 
-  if (attr1->klass->type != attr2->klass->type)
+  if (attr1->type != attr2->type)
     return FALSE;
 
-  return attr1->klass->equal (attr1, attr2);
-}
+  switch (PANGO_ATTR_VALUE_TYPE (attr1))
+    {
+    case PANGO_ATTR_VALUE_STRING:
+      return strcmp (attr1->str_value, attr2->str_value) == 0;
 
-/* }}} */
-/* {{{ Attribute types */
-/* {{{ String attribute */
-static PangoAttribute *pango_attr_string_new (const PangoAttrClass *klass,
-                                              const char           *str);
+    case PANGO_ATTR_VALUE_INT:
+      return attr1->int_value == attr2->int_value;
 
-static PangoAttribute *
-pango_attr_string_copy (const PangoAttribute *attr)
-{
-  return pango_attr_string_new (attr->klass, ((PangoAttrString *)attr)->value);
-}
+    case PANGO_ATTR_VALUE_BOOLEAN:
+      return attr1->boolean_value == attr2->boolean_value;
 
-static void
-pango_attr_string_destroy (PangoAttribute *attr)
-{
-  PangoAttrString *sattr = (PangoAttrString *)attr;
+    case PANGO_ATTR_VALUE_FLOAT:
+      return attr1->double_value == attr2->double_value;
 
-  g_free (sattr->value);
-  g_slice_free (PangoAttrString, sattr);
-}
+    case PANGO_ATTR_VALUE_COLOR:
+      return memcmp (&attr1->color_value, &attr2->color_value, sizeof (PangoColor)) == 0;
 
-static gboolean
-pango_attr_string_equal (const PangoAttribute *attr1,
-                         const PangoAttribute *attr2)
-{
-  return strcmp (((PangoAttrString *)attr1)->value, ((PangoAttrString *)attr2)->value) == 0;
-}
+    case PANGO_ATTR_VALUE_LANGUAGE:
+      return attr1->lang_value == attr2->lang_value;
 
-static PangoAttribute *
-pango_attr_string_new (const PangoAttrClass *klass,
-                       const char           *str)
-{
-  PangoAttrString *result = g_slice_new (PangoAttrString);
-  pango_attribute_init (&result->attr, klass);
-  result->value = g_strdup (str);
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      return pango_font_description_equal (attr1->font_value, attr2->font_value);
 
-  return (PangoAttribute *)result;
-}
- /* }}} */
-/* {{{ Language attribute */
-static PangoAttribute *
-pango_attr_language_copy (const PangoAttribute *attr)
-{
-  return pango_attr_language_new (((PangoAttrLanguage *)attr)->value);
-}
+    case PANGO_ATTR_VALUE_POINTER:
+      {
+        GEqualFunc equal = NULL;
 
-static void
-pango_attr_language_destroy (PangoAttribute *attr)
-{
-  PangoAttrLanguage *lattr = (PangoAttrLanguage *)attr;
+        G_LOCK (attr_type);
 
-  g_slice_free (PangoAttrLanguage, lattr);
-}
+        g_assert (attr_type != NULL);
 
-static gboolean
-pango_attr_language_equal (const PangoAttribute *attr1,
-                           const PangoAttribute *attr2)
-{
-  return ((PangoAttrLanguage *)attr1)->value == ((PangoAttrLanguage *)attr2)->value;
-}
-/* }}}} */
-/* {{{ Color attribute */
-static PangoAttribute *pango_attr_color_new (const PangoAttrClass *klass,
-                                             guint16               red,
-                                             guint16               green,
-                                             guint16               blue);
+        for (int i = 0; i < attr_type->len; i++)
+          {
+            PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+            if (class->type == attr1->type)
+              {
+                equal = class->equal;
+                break;
+              }
+          }
 
-static PangoAttribute *
-pango_attr_color_copy (const PangoAttribute *attr)
-{
-  const PangoAttrColor *color_attr = (PangoAttrColor *)attr;
+        G_UNLOCK (attr_type);
 
-  return pango_attr_color_new (attr->klass,
-                               color_attr->color.red,
-                               color_attr->color.green,
-                               color_attr->color.blue);
-}
+        g_assert (equal != NULL);
 
-static void
-pango_attr_color_destroy (PangoAttribute *attr)
-{
-  PangoAttrColor *cattr = (PangoAttrColor *)attr;
+        return equal (attr1->pointer_value, attr2->pointer_value);
+      }
 
-  g_slice_free (PangoAttrColor, cattr);
+    default:
+      g_assert_not_reached ();
+    }
 }
 
-static gboolean
-pango_attr_color_equal (const PangoAttribute *attr1,
-                        const PangoAttribute *attr2)
+/* {{{ Builtin Attribute value types */
+
+static inline PangoAttribute *
+pango_attr_init (PangoAttrType type)
 {
-  const PangoAttrColor *color_attr1 = (const PangoAttrColor *)attr1;
-  const PangoAttrColor *color_attr2 = (const PangoAttrColor *)attr2;
+  PangoAttribute *attr;
+
+  attr = g_slice_new (PangoAttribute);
+  attr->type = type;
+  attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
+  attr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
 
-  return (color_attr1->color.red == color_attr2->color.red &&
-          color_attr1->color.blue == color_attr2->color.blue &&
-          color_attr1->color.green == color_attr2->color.green);
+  return attr;
 }
 
 static PangoAttribute *
-pango_attr_color_new (const PangoAttrClass *klass,
-                      guint16               red,
-                      guint16               green,
-                      guint16               blue)
+pango_attr_string_new (PangoAttrType  type,
+                       const char    *value)
 {
-  PangoAttrColor *result = g_slice_new (PangoAttrColor);
-  pango_attribute_init (&result->attr, klass);
-  result->color.red = red;
-  result->color.green = green;
-  result->color.blue = blue;
+  PangoAttribute *attr;
 
-  return (PangoAttribute *)result;
-}
-/* }}}} */
-/* {{{ Integer attribute */
-static PangoAttribute *pango_attr_int_new (const PangoAttrClass *klass,
-                                           int                   value);
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_STRING, NULL);
 
-static PangoAttribute *
-pango_attr_int_copy (const PangoAttribute *attr)
-{
-  const PangoAttrInt *int_attr = (PangoAttrInt *)attr;
+  attr = pango_attr_init (type);
+  attr->str_value = g_strdup (value);
 
-  return pango_attr_int_new (attr->klass, int_attr->value);
+  return attr;
 }
 
-static void
-pango_attr_int_destroy (PangoAttribute *attr)
+static PangoAttribute *
+pango_attr_int_new (PangoAttrType type,
+                    int           value)
 {
-  PangoAttrInt *iattr = (PangoAttrInt *)attr;
+  PangoAttribute *attr;
 
-  g_slice_free (PangoAttrInt, iattr);
-}
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_INT, NULL);
 
-static gboolean
-pango_attr_int_equal (const PangoAttribute *attr1,
-                      const PangoAttribute *attr2)
-{
-  const PangoAttrInt *int_attr1 = (const PangoAttrInt *)attr1;
-  const PangoAttrInt *int_attr2 = (const PangoAttrInt *)attr2;
+  attr = pango_attr_init (type);
+  attr->int_value = value;
 
-  return (int_attr1->value == int_attr2->value);
+  return attr;
 }
 
 static PangoAttribute *
-pango_attr_int_new (const PangoAttrClass *klass,
-                    int                   value)
+pango_attr_boolean_new (PangoAttrType type,
+                        gboolean      value)
 {
-  PangoAttrInt *result = g_slice_new (PangoAttrInt);
-  pango_attribute_init (&result->attr, klass);
-  result->value = value;
+  PangoAttribute *attr;
 
-  return (PangoAttribute *)result;
-}
-/* }}} */
-/* {{{ Float attribute */
-static PangoAttribute *pango_attr_float_new (const PangoAttrClass *klass,
-                                             double                value);
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_BOOLEAN, NULL);
 
-static PangoAttribute *
-pango_attr_float_copy (const PangoAttribute *attr)
-{
-  const PangoAttrFloat *float_attr = (PangoAttrFloat *)attr;
+  attr = pango_attr_init (type);
+  attr->boolean_value = value;
 
-  return pango_attr_float_new (attr->klass, float_attr->value);
+  return attr;
 }
 
-static void
-pango_attr_float_destroy (PangoAttribute *attr)
+static PangoAttribute *
+pango_attr_float_new (PangoAttrType type,
+                      double        value)
 {
-  PangoAttrFloat *fattr = (PangoAttrFloat *)attr;
+  PangoAttribute *attr;
 
-  g_slice_free (PangoAttrFloat, fattr);
-}
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_FLOAT, NULL);
 
-static gboolean
-pango_attr_float_equal (const PangoAttribute *attr1,
-                        const PangoAttribute *attr2)
-{
-  const PangoAttrFloat *float_attr1 = (const PangoAttrFloat *)attr1;
-  const PangoAttrFloat *float_attr2 = (const PangoAttrFloat *)attr2;
+  attr = pango_attr_init (type);
+  attr->double_value = value;
 
-  return (float_attr1->value == float_attr2->value);
+  return attr;
 }
 
 static PangoAttribute *
-pango_attr_float_new  (const PangoAttrClass *klass,
-                       double                value)
+pango_attr_color_new (PangoAttrType type,
+                      guint16       red,
+                      guint16       green,
+                      guint16       blue)
 {
-  PangoAttrFloat *result = g_slice_new (PangoAttrFloat);
-  pango_attribute_init (&result->attr, klass);
-  result->value = value;
+  PangoAttribute *attr;
+
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_COLOR, NULL);
 
-  return (PangoAttribute *)result;
+  attr = pango_attr_init (type);
+  attr->color_value.red = red;
+  attr->color_value.green = green;
+  attr->color_value.blue = blue;
+
+  return attr;
 }
-/* }}} */
-/* {{{ Size attribute */
-static PangoAttribute *pango_attr_size_new_internal (int      size,
-                                                     gboolean absolute);
 
 static PangoAttribute *
-pango_attr_size_copy (const PangoAttribute *attr)
+pango_attr_lang_new (PangoAttrType  type,
+                     PangoLanguage *value)
 {
-  const PangoAttrSize *size_attr = (PangoAttrSize *)attr;
+  PangoAttribute *attr;
 
-  if (attr->klass->type == PANGO_ATTR_ABSOLUTE_SIZE)
-    return pango_attr_size_new_absolute (size_attr->size);
-  else
-    return pango_attr_size_new (size_attr->size);
-}
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_LANGUAGE, NULL);
 
-static void
-pango_attr_size_destroy (PangoAttribute *attr)
-{
-  PangoAttrSize *sattr = (PangoAttrSize *)attr;
+  attr = pango_attr_init (type);
+  attr->lang_value  = value;
 
-  g_slice_free (PangoAttrSize, sattr);
+  return attr;
 }
 
-static gboolean
-pango_attr_size_equal (const PangoAttribute *attr1,
-                       const PangoAttribute *attr2)
+static PangoAttribute *
+pango_attr_font_description_new (PangoAttrType               type,
+                                 const PangoFontDescription *value)
 {
-  const PangoAttrSize *size_attr1 = (const PangoAttrSize *)attr1;
-  const PangoAttrSize *size_attr2 = (const PangoAttrSize *)attr2;
+  PangoAttribute *attr;
 
-  return size_attr1->size == size_attr2->size;
-}
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_FONT_DESC, NULL);
 
-static PangoAttribute *
-pango_attr_size_new_internal (int size,
-                              gboolean absolute)
-{
-  PangoAttrSize *result;
-
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_SIZE,
-    pango_attr_size_copy,
-    pango_attr_size_destroy,
-    pango_attr_size_equal
-  };
-  static const PangoAttrClass absolute_klass = {
-    PANGO_ATTR_ABSOLUTE_SIZE,
-    pango_attr_size_copy,
-    pango_attr_size_destroy,
-    pango_attr_size_equal
-  };
-
-  result = g_slice_new (PangoAttrSize);
-  pango_attribute_init (&result->attr, absolute ? &absolute_klass : &klass);
-  result->size = size;
-  result->absolute = absolute;
-
-  return (PangoAttribute *)result;
+  attr = pango_attr_init (type);
+  attr->font_value  = pango_font_description_copy (value);
+
+  return attr;
 }
 /* }}} */
-/* {{{ Font description attribute */
-static PangoAttribute *
-pango_attr_font_desc_copy (const PangoAttribute *attr)
+/* }}} */
+/* {{{ Private API */
+
+char *
+pango_attr_value_serialize (PangoAttribute *attr)
 {
-  const PangoAttrFontDesc *desc_attr = (const PangoAttrFontDesc *)attr;
+  PangoAttrDataSerializeFunc serialize = NULL;
 
-  return pango_attr_font_desc_new (desc_attr->desc);
-}
+  G_LOCK (attr_type);
 
-static void
-pango_attr_font_desc_destroy (PangoAttribute *attr)
-{
-  PangoAttrFontDesc *desc_attr = (PangoAttrFontDesc *)attr;
+  g_assert (attr_type != NULL);
 
-  pango_font_description_free (desc_attr->desc);
-  g_slice_free (PangoAttrFontDesc, desc_attr);
-}
+  for (int i = 0; i < attr_type->len; i++)
+    {
+      PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+      if (class->type == attr->type)
+        {
+          serialize = class->serialize;
+          break;
+        }
+    }
 
-static gboolean
-pango_attr_font_desc_equal (const PangoAttribute *attr1,
-                            const PangoAttribute *attr2)
-{
-  const PangoAttrFontDesc *desc_attr1 = (const PangoAttrFontDesc *)attr1;
-  const PangoAttrFontDesc *desc_attr2 = (const PangoAttrFontDesc *)attr2;
+  G_UNLOCK (attr_type);
 
-  return pango_font_description_get_set_fields (desc_attr1->desc) ==
-         pango_font_description_get_set_fields (desc_attr2->desc) &&
-         pango_font_description_equal (desc_attr1->desc, desc_attr2->desc);
+  if (serialize)
+    return serialize (attr->pointer_value);
+
+  return NULL;
 }
-/* }}} */
+
 /* }}} */
 /* {{{ Public API */
 
@@ -494,16 +525,7 @@ pango_attr_font_desc_equal (const PangoAttribute *attr1,
 PangoAttribute *
 pango_attr_family_new (const char *family)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FAMILY,
-    pango_attr_string_copy,
-    pango_attr_string_destroy,
-    pango_attr_string_equal
-  };
-
-  g_return_val_if_fail (family != NULL, NULL);
-
-  return pango_attr_string_new (&klass, family);
+  return pango_attr_string_new (PANGO_ATTR_FAMILY, family);
 }
 
 /**
@@ -519,20 +541,7 @@ pango_attr_family_new (const char *family)
 PangoAttribute *
 pango_attr_language_new (PangoLanguage *language)
 {
-  PangoAttrLanguage *result;
-
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_LANGUAGE,
-    pango_attr_language_copy,
-    pango_attr_language_destroy,
-    pango_attr_language_equal
-  };
-
-  result = g_slice_new (PangoAttrLanguage);
-  pango_attribute_init (&result->attr, &klass);
-  result->value = language;
-
-  return (PangoAttribute *)result;
+  return pango_attr_lang_new (PANGO_ATTR_LANGUAGE, language);
 }
 
 /**
@@ -552,14 +561,7 @@ pango_attr_foreground_new (guint16 red,
                            guint16 green,
                            guint16 blue)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FOREGROUND,
-    pango_attr_color_copy,
-    pango_attr_color_destroy,
-    pango_attr_color_equal
-  };
-
-  return pango_attr_color_new (&klass, red, green, blue);
+  return pango_attr_color_new (PANGO_ATTR_FOREGROUND, red, green, blue);
 }
 
 /**
@@ -579,14 +581,7 @@ pango_attr_background_new (guint16 red,
                            guint16 green,
                            guint16 blue)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_BACKGROUND,
-    pango_attr_color_copy,
-    pango_attr_color_destroy,
-    pango_attr_color_equal
-  };
-
-  return pango_attr_color_new (&klass, red, green, blue);
+  return pango_attr_color_new (PANGO_ATTR_BACKGROUND, red, green, blue);
 }
 
 /**
@@ -600,11 +595,12 @@ pango_attr_background_new (guint16 red,
  *   [method@Pango.Attribute.destroy]
  */
 PangoAttribute *
-pango_attr_size_new (int size)
+pango_attr_size_new (int value)
 {
-  return pango_attr_size_new_internal (size, FALSE);
+  return pango_attr_int_new (PANGO_ATTR_SIZE, value);
 }
 
+
 /**
  * pango_attr_size_new_absolute:
  * @size: the font size, in %PANGO_SCALE-ths of a device unit
@@ -620,7 +616,7 @@ pango_attr_size_new (int size)
 PangoAttribute *
 pango_attr_size_new_absolute (int size)
 {
-  return pango_attr_size_new_internal (size, TRUE);
+  return pango_attr_int_new (PANGO_ATTR_ABSOLUTE_SIZE, size);
 }
 
 /**
@@ -636,14 +632,7 @@ pango_attr_size_new_absolute (int size)
 PangoAttribute *
 pango_attr_style_new (PangoStyle style)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_STYLE,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)style);
+  return pango_attr_int_new (PANGO_ATTR_STYLE, (int)style);
 }
 
 /**
@@ -659,14 +648,7 @@ pango_attr_style_new (PangoStyle style)
 PangoAttribute *
 pango_attr_weight_new (PangoWeight weight)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_WEIGHT,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)weight);
+  return pango_attr_int_new (PANGO_ATTR_WEIGHT, (int)weight);
 }
 
 /**
@@ -681,14 +663,7 @@ pango_attr_weight_new (PangoWeight weight)
 PangoAttribute *
 pango_attr_variant_new (PangoVariant variant)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_VARIANT,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)variant);
+  return pango_attr_int_new (PANGO_ATTR_VARIANT, (int)variant);
 }
 
 /**
@@ -702,16 +677,9 @@ pango_attr_variant_new (PangoVariant variant)
  *   [method@Pango.Attribute.destroy]
  */
 PangoAttribute *
-pango_attr_stretch_new (PangoStretch  stretch)
+pango_attr_stretch_new (PangoStretch stretch)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_STRETCH,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)stretch);
+  return pango_attr_int_new (PANGO_ATTR_STRETCH, (int)stretch);
 }
 
 /**
@@ -730,18 +698,7 @@ pango_attr_stretch_new (PangoStretch  stretch)
 PangoAttribute *
 pango_attr_font_desc_new (const PangoFontDescription *desc)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FONT_DESC,
-    pango_attr_font_desc_copy,
-    pango_attr_font_desc_destroy,
-    pango_attr_font_desc_equal
-  };
-
-  PangoAttrFontDesc *result = g_slice_new (PangoAttrFontDesc);
-  pango_attribute_init (&result->attr, &klass);
-  result->desc = pango_font_description_copy (desc);
-
-  return (PangoAttribute *)result;
+  return pango_attr_font_description_new (PANGO_ATTR_FONT_DESC, desc);
 }
 
 /**
@@ -757,14 +714,7 @@ pango_attr_font_desc_new (const PangoFontDescription *desc)
 PangoAttribute *
 pango_attr_underline_new (PangoUnderline underline)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_UNDERLINE,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)underline);
+  return pango_attr_int_new (PANGO_ATTR_UNDERLINE, (int)underline);
 }
 
 /**
@@ -789,14 +739,7 @@ pango_attr_underline_color_new (guint16 red,
                                 guint16 green,
                                 guint16 blue)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_UNDERLINE_COLOR,
-    pango_attr_color_copy,
-    pango_attr_color_destroy,
-    pango_attr_color_equal
-  };
-
-  return pango_attr_color_new (&klass, red, green, blue);
+  return pango_attr_color_new (PANGO_ATTR_UNDERLINE_COLOR, red, green, blue);
 }
 
 /**
@@ -812,14 +755,7 @@ pango_attr_underline_color_new (guint16 red,
 PangoAttribute *
 pango_attr_strikethrough_new (gboolean strikethrough)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_STRIKETHROUGH,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)strikethrough);
+  return pango_attr_boolean_new (PANGO_ATTR_STRIKETHROUGH, (int)strikethrough);
 }
 
 /**
@@ -844,14 +780,7 @@ pango_attr_strikethrough_color_new (guint16 red,
                                     guint16 green,
                                     guint16 blue)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_STRIKETHROUGH_COLOR,
-    pango_attr_color_copy,
-    pango_attr_color_destroy,
-    pango_attr_color_equal
-  };
-
-  return pango_attr_color_new (&klass, red, green, blue);
+  return pango_attr_color_new (PANGO_ATTR_STRIKETHROUGH_COLOR, red, green, blue);
 }
 
 /**
@@ -868,14 +797,7 @@ pango_attr_strikethrough_color_new (guint16 red,
 PangoAttribute *
 pango_attr_rise_new (int rise)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_RISE,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)rise);
+  return pango_attr_int_new (PANGO_ATTR_RISE, (int)rise);
 }
 
 /**
@@ -903,14 +825,7 @@ pango_attr_rise_new (int rise)
 PangoAttribute *
 pango_attr_baseline_shift_new (int rise)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_BASELINE_SHIFT,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)rise);
+  return pango_attr_int_new (PANGO_ATTR_BASELINE_SHIFT, (int)rise);
 }
 
 /**
@@ -933,14 +848,7 @@ pango_attr_baseline_shift_new (int rise)
 PangoAttribute *
 pango_attr_font_scale_new (PangoFontScale scale)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FONT_SCALE,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)scale);
+  return pango_attr_int_new (PANGO_ATTR_FONT_SCALE, (int)scale);
 }
 
 /**
@@ -959,14 +867,7 @@ pango_attr_font_scale_new (PangoFontScale scale)
 PangoAttribute*
 pango_attr_scale_new (double scale_factor)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_SCALE,
-    pango_attr_float_copy,
-    pango_attr_float_destroy,
-    pango_attr_float_equal
-  };
-
-  return pango_attr_float_new (&klass, scale_factor);
+  return pango_attr_float_new (PANGO_ATTR_SCALE, scale_factor);
 }
 
 /**
@@ -990,14 +891,7 @@ pango_attr_scale_new (double scale_factor)
 PangoAttribute *
 pango_attr_fallback_new (gboolean enable_fallback)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FALLBACK,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal,
-  };
-
-  return pango_attr_int_new (&klass, (int)enable_fallback);
+  return pango_attr_boolean_new (PANGO_ATTR_FALLBACK, enable_fallback);
 }
 
 /**
@@ -1016,14 +910,7 @@ pango_attr_fallback_new (gboolean enable_fallback)
 PangoAttribute *
 pango_attr_letter_spacing_new (int letter_spacing)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_LETTER_SPACING,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, letter_spacing);
+  return pango_attr_int_new (PANGO_ATTR_LETTER_SPACING, letter_spacing);
 }
 
 /**
@@ -1041,16 +928,9 @@ pango_attr_letter_spacing_new (int letter_spacing)
 PangoAttribute *
 pango_attr_gravity_new (PangoGravity gravity)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_GRAVITY,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
   g_return_val_if_fail (gravity != PANGO_GRAVITY_AUTO, NULL);
 
-  return pango_attr_int_new (&klass, (int)gravity);
+  return pango_attr_int_new (PANGO_ATTR_GRAVITY, (int)gravity);
 }
 
 /**
@@ -1068,14 +948,7 @@ pango_attr_gravity_new (PangoGravity gravity)
 PangoAttribute *
 pango_attr_gravity_hint_new (PangoGravityHint hint)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_GRAVITY_HINT,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)hint);
+  return pango_attr_int_new (PANGO_ATTR_GRAVITY_HINT, (int)hint);
 }
 
 /**
@@ -1095,18 +968,11 @@ pango_attr_gravity_hint_new (PangoGravityHint hint)
  * Since: 1.38
  */
 PangoAttribute *
-pango_attr_font_features_new (const gchar *features)
+pango_attr_font_features_new (const char *features)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FONT_FEATURES,
-    pango_attr_string_copy,
-    pango_attr_string_destroy,
-    pango_attr_string_equal
-  };
-
   g_return_val_if_fail (features != NULL, NULL);
 
-  return pango_attr_string_new (&klass, features);
+  return pango_attr_string_new (PANGO_ATTR_FONT_FEATURES, features);
 }
 
 /**
@@ -1124,14 +990,7 @@ pango_attr_font_features_new (const gchar *features)
 PangoAttribute *
 pango_attr_foreground_alpha_new (guint16 alpha)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_FOREGROUND_ALPHA,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)alpha);
+  return pango_attr_int_new (PANGO_ATTR_FOREGROUND_ALPHA, (int)alpha);
 }
 
 /**
@@ -1149,14 +1008,7 @@ pango_attr_foreground_alpha_new (guint16 alpha)
 PangoAttribute *
 pango_attr_background_alpha_new (guint16 alpha)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_BACKGROUND_ALPHA,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)alpha);
+  return pango_attr_int_new (PANGO_ATTR_BACKGROUND_ALPHA, (int)alpha);
 }
 
 /**
@@ -1177,14 +1029,7 @@ pango_attr_background_alpha_new (guint16 alpha)
 PangoAttribute *
 pango_attr_allow_breaks_new (gboolean allow_breaks)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_ALLOW_BREAKS,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal,
-  };
-
-  return pango_attr_int_new (&klass, (int)allow_breaks);
+  return pango_attr_boolean_new (PANGO_ATTR_ALLOW_BREAKS, allow_breaks);
 }
 
 /**
@@ -1206,14 +1051,7 @@ pango_attr_allow_breaks_new (gboolean allow_breaks)
 PangoAttribute *
 pango_attr_insert_hyphens_new (gboolean insert_hyphens)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_INSERT_HYPHENS,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal,
-  };
-
-  return pango_attr_int_new (&klass, (int)insert_hyphens);
+  return pango_attr_boolean_new (PANGO_ATTR_INSERT_HYPHENS, insert_hyphens);
 }
 
 /**
@@ -1232,14 +1070,7 @@ pango_attr_insert_hyphens_new (gboolean insert_hyphens)
 PangoAttribute *
 pango_attr_show_new (PangoShowFlags flags)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_SHOW,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal,
-  };
-
-  return pango_attr_int_new (&klass, (int)flags);
+  return pango_attr_int_new (PANGO_ATTR_SHOW, (int)flags);
 }
 
 /**
@@ -1259,14 +1090,7 @@ pango_attr_show_new (PangoShowFlags flags)
 PangoAttribute *
 pango_attr_word_new (void)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_WORD,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal,
-  };
-
-  return pango_attr_int_new (&klass, 1);
+  return pango_attr_boolean_new (PANGO_ATTR_WORD, TRUE);
 }
 
 /**
@@ -1286,14 +1110,7 @@ pango_attr_word_new (void)
 PangoAttribute *
 pango_attr_sentence_new (void)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_SENTENCE,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal,
-  };
-
-  return pango_attr_int_new (&klass, 1);
+  return pango_attr_boolean_new (PANGO_ATTR_SENTENCE, TRUE);
 }
 
 /**
@@ -1311,14 +1128,7 @@ pango_attr_sentence_new (void)
 PangoAttribute *
 pango_attr_overline_new (PangoOverline overline)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_OVERLINE,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, (int)overline);
+  return pango_attr_int_new (PANGO_ATTR_OVERLINE, (int)overline);
 }
 
 /**
@@ -1343,14 +1153,7 @@ pango_attr_overline_color_new (guint16 red,
                                guint16 green,
                                guint16 blue)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_OVERLINE_COLOR,
-    pango_attr_color_copy,
-    pango_attr_color_destroy,
-    pango_attr_color_equal
-  };
-
-  return pango_attr_color_new (&klass, red, green, blue);
+  return pango_attr_color_new (PANGO_ATTR_OVERLINE_COLOR, red, green, blue);
 }
 
 /**
@@ -1370,14 +1173,7 @@ pango_attr_overline_color_new (guint16 red,
 PangoAttribute *
 pango_attr_line_height_new (double factor)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_LINE_HEIGHT,
-    pango_attr_float_copy,
-    pango_attr_float_destroy,
-    pango_attr_float_equal
-  };
-
-  return pango_attr_float_new (&klass, factor);
+  return pango_attr_float_new (PANGO_ATTR_LINE_HEIGHT, factor);
 }
 
 /**
@@ -1396,14 +1192,7 @@ pango_attr_line_height_new (double factor)
 PangoAttribute *
 pango_attr_line_height_new_absolute (int height)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_ABSOLUTE_LINE_HEIGHT,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, height);
+  return pango_attr_int_new (PANGO_ATTR_ABSOLUTE_LINE_HEIGHT, height);
 }
 
 /**
@@ -1422,252 +1211,146 @@ pango_attr_line_height_new_absolute (int height)
 PangoAttribute *
 pango_attr_text_transform_new (PangoTextTransform transform)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_TEXT_TRANSFORM,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, transform);
+  return pango_attr_int_new (PANGO_ATTR_TEXT_TRANSFORM, transform);
 }
-/* }}} */
-/* {{{ Binding helpers */
 
 /**
- * pango_attribute_as_int:
- * @attr: A `PangoAttribute` such as weight
- *
- * Returns the attribute cast to `PangoAttrInt`.
+ * pango_attr_custom_new:
+ * @type: the attribute type
+ * @user_data: data for the attribute
  *
- * This is mainly useful for language bindings.
+ * Creates a new attribute for the given type.
  *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrInt`,
- *   or %NULL if it's not an integer attribute
+ * The type must have been registered with [func@Pango.register_attr_type]
+ * before. @user_data will be copied with the copy function that
+ * was given when the type was registered.
  *
- * Since: 1.50
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttribute`, which should be freed with
+ *   [method@Pango.Attribute.destroy]
  */
-PangoAttrInt *
-pango_attribute_as_int (PangoAttribute *attr)
+PangoAttribute *
+pango_attr_custom_new (PangoAttrType type,
+                       gpointer      user_data)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_STYLE:
-    case PANGO_ATTR_WEIGHT:
-    case PANGO_ATTR_VARIANT:
-    case PANGO_ATTR_STRETCH:
-    case PANGO_ATTR_UNDERLINE:
-    case PANGO_ATTR_STRIKETHROUGH:
-    case PANGO_ATTR_RISE:
-    case PANGO_ATTR_FALLBACK:
-    case PANGO_ATTR_LETTER_SPACING:
-    case PANGO_ATTR_GRAVITY:
-    case PANGO_ATTR_GRAVITY_HINT:
-    case PANGO_ATTR_FOREGROUND_ALPHA:
-    case PANGO_ATTR_BACKGROUND_ALPHA:
-    case PANGO_ATTR_ALLOW_BREAKS:
-    case PANGO_ATTR_SHOW:
-    case PANGO_ATTR_INSERT_HYPHENS:
-    case PANGO_ATTR_OVERLINE:
-    case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-    case PANGO_ATTR_TEXT_TRANSFORM:
-    case PANGO_ATTR_WORD:
-    case PANGO_ATTR_SENTENCE:
-    case PANGO_ATTR_BASELINE_SHIFT:
-    case PANGO_ATTR_FONT_SCALE:
-      return (PangoAttrInt *)attr;
+  PangoAttrClass *class = NULL;
+  PangoAttribute *attr;
 
-    default:
-      return NULL;
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_POINTER, NULL);
+
+  G_LOCK (attr_type);
+
+  g_assert (attr_type != NULL);
+
+  for (int i = 0; i < attr_type->len; i++)
+    {
+      PangoAttrClass *c = &g_array_index (attr_type, PangoAttrClass, i);
+      if (c->type == type)
+        {
+          class = c;
+          break;
+        }
     }
+
+  g_assert (class != NULL);
+
+  G_UNLOCK (attr_type);
+
+  attr = pango_attr_init (type);
+  attr->pointer_value = class->copy (user_data);
+
+  return attr;
 }
 
-/**
- * pango_attribute_as_float:
- * @attr: A `PangoAttribute` such as scale
- *
- * Returns the attribute cast to `PangoAttrFloat`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrFloat`,
- *   or %NULL if it's not a floating point attribute
- *
- * Since: 1.50
- */
-PangoAttrFloat *
-pango_attribute_as_float (PangoAttribute *attr)
+/* }}} */
+/* {{{ Binding Helpers */
+
+gboolean
+pango_attribute_get_string (PangoAttribute   *attribute,
+                            const char      **value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_SCALE:
-    case PANGO_ATTR_LINE_HEIGHT:
-      return (PangoAttrFloat *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_STRING)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->str_value;
+  return TRUE;
 }
 
-/**
- * pango_attribute_as_string:
- * @attr: A `PangoAttribute` such as family
- *
- * Returns the attribute cast to `PangoAttrString`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrString`,
- *   or %NULL if it's not a string attribute
- *
- * Since: 1.50
- */
-PangoAttrString *
-pango_attribute_as_string (PangoAttribute *attr)
+gboolean
+pango_attribute_get_language (PangoAttribute  *attribute,
+                              PangoLanguage  **value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FAMILY:
-      return (PangoAttrString *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_LANGUAGE)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->lang_value;
+  return TRUE;
 }
 
-/**
- * pango_attribute_as_size:
- * @attr: A `PangoAttribute` representing a size
- *
- * Returns the attribute cast to `PangoAttrSize`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrSize`,
- *   or NULL if it's not a size attribute
- *
- * Since: 1.50
- */
-PangoAttrSize *
-pango_attribute_as_size (PangoAttribute *attr)
+gboolean
+pango_attribute_get_int (PangoAttribute *attribute,
+                         int            *value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_SIZE:
-    case PANGO_ATTR_ABSOLUTE_SIZE:
-      return (PangoAttrSize *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_INT)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->int_value;
+  return TRUE;
 }
 
-/**
- * pango_attribute_as_color:
- * @attr: A `PangoAttribute` such as foreground
- *
- * Returns the attribute cast to `PangoAttrColor`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrColor`,
- *   or %NULL if it's not a color attribute
- *
- * Since: 1.50
- */
-PangoAttrColor *
-pango_attribute_as_color (PangoAttribute *attr)
+gboolean
+pango_attribute_get_boolean (PangoAttribute *attribute,
+                             int            *value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FOREGROUND:
-    case PANGO_ATTR_BACKGROUND:
-    case PANGO_ATTR_UNDERLINE_COLOR:
-    case PANGO_ATTR_STRIKETHROUGH_COLOR:
-    case PANGO_ATTR_OVERLINE_COLOR:
-      return (PangoAttrColor *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_BOOLEAN)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->boolean_value;
+  return TRUE;
 }
 
-/**
- * pango_attribute_as_font_desc:
- * @attr: A `PangoAttribute` representing a font description
- *
- * Returns the attribute cast to `PangoAttrFontDesc`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrFontDesc`,
- *   or %NULL if it's not a font description attribute
- *
- * Since: 1.50
- */
-PangoAttrFontDesc *
-pango_attribute_as_font_desc (PangoAttribute *attr)
+gboolean
+pango_attribute_get_float (PangoAttribute *attribute,
+                           double         *value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FONT_DESC:
-      return (PangoAttrFontDesc *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_FLOAT)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->double_value;
+  return TRUE;
 }
 
-/**
- * pango_attribute_as_font_features:
- * @attr: A `PangoAttribute` representing font features
- *
- * Returns the attribute cast to `PangoAttrFontFeatures`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrFontFeatures`,
- *   or %NULL if it's not a font features attribute
- *
- * Since: 1.50
- */
-PangoAttrFontFeatures *
-pango_attribute_as_font_features (PangoAttribute *attr)
+gboolean
+pango_attribute_get_color (PangoAttribute *attribute,
+                           PangoColor     *value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FONT_FEATURES:
-      return (PangoAttrFontFeatures *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_COLOR)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->color_value;
+  return TRUE;
 }
 
-/**
- * pango_attribute_as_language:
- * @attr: A `PangoAttribute` representing a language
- *
- * Returns the attribute cast to `PangoAttrLanguage`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrLanguage`,
- *   or %NULL if it's not a language attribute
- *
- * Since: 1.50
- */
-PangoAttrLanguage *
-pango_attribute_as_language (PangoAttribute *attr)
+gboolean
+pango_attribute_get_font_desc (PangoAttribute        *attribute,
+                               PangoFontDescription **value)
 {
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_LANGUAGE:
-      return (PangoAttrLanguage *)attr;
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_FONT_DESC)
+    return FALSE;
 
-    default:
-      return NULL;
-    }
+  *value = attribute->font_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_custom (PangoAttribute *attribute,
+                            gpointer       *value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_POINTER)
+    return FALSE;
+
+  *value = attribute->pointer_value;
+  return TRUE;
 }
 
 /* }}} */
@@ -1729,17 +1412,18 @@ pango_attr_list_ref (PangoAttrList *list)
 void
 _pango_attr_list_destroy (PangoAttrList *list)
 {
-  guint i, p;
+//  guint i, p;
 
   if (!list->attributes)
     return;
 
+#if 0
   for (i = 0, p = list->attributes->len; i < p; i++)
     {
       PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
-
-      attr->klass->destroy (attr);
+      //attr->klass->destroy (attr);
     }
+#endif
 
   g_ptr_array_free (list->attributes, TRUE);
 }
@@ -1934,7 +1618,7 @@ pango_attr_list_change (PangoAttrList  *list,
           break;
         }
 
-      if (tmp_attr->klass->type != attr->klass->type)
+      if (tmp_attr->type != attr->type)
         continue;
 
       if (tmp_attr->end_index < start_index)
@@ -2003,7 +1687,7 @@ pango_attr_list_change (PangoAttrList  *list,
       if (tmp_attr->start_index > end_index)
         break;
 
-      if (tmp_attr->klass->type != attr->klass->type)
+      if (tmp_attr->type != attr->type)
         continue;
 
       if (tmp_attr->end_index <= attr->end_index ||
@@ -2400,19 +2084,6 @@ pango_attr_list_filter (PangoAttrList       *list,
  * 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)
 {
@@ -2455,66 +2126,80 @@ static void
 attr_print (GString        *str,
             PangoAttribute *attr)
 {
-  PangoAttrString *string;
-  PangoAttrLanguage *lang;
-  PangoAttrInt *integer;
-  PangoAttrFloat *flt;
-  PangoAttrFontDesc *font;
-  PangoAttrColor *color;
-  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)
+  const char *name;
+
+  name = pango_attr_type_get_name (attr->type);
+  if (!name)
+    return;
+
+  g_string_append_printf (str, "%u %u %s", attr->start_index, attr->end_index, name);
+
+  switch (PANGO_ATTR_VALUE_TYPE (attr))
     {
-      char *s = pango_color_to_string (&color->color);
-      g_string_append_printf (str, " %s", s);
-      g_free (s);
+    case PANGO_ATTR_VALUE_INT:
+      if (attr->type == PANGO_ATTR_WEIGHT ||
+          attr->type == PANGO_ATTR_STYLE ||
+          attr->type == PANGO_ATTR_STRETCH ||
+          attr->type == PANGO_ATTR_VARIANT ||
+          attr->type == PANGO_ATTR_GRAVITY ||
+          attr->type == PANGO_ATTR_GRAVITY_HINT ||
+          attr->type == PANGO_ATTR_UNDERLINE ||
+          attr->type == PANGO_ATTR_OVERLINE ||
+          attr->type == PANGO_ATTR_BASELINE_SHIFT ||
+          attr->type == PANGO_ATTR_FONT_SCALE ||
+          attr->type == PANGO_ATTR_TEXT_TRANSFORM)
+        append_enum_value (str, get_attr_value_type (attr->type), attr->int_value);
+      else
+        g_string_append_printf (str, " %d", attr->int_value);
+      break;
+
+    case PANGO_ATTR_VALUE_BOOLEAN:
+      g_string_append (str, attr->int_value ? " true" : " false");
+      break;
+
+    case PANGO_ATTR_VALUE_STRING:
+      g_string_append_printf (str, " \"%s\"", attr->str_value);
+      break;
+
+    case PANGO_ATTR_VALUE_LANGUAGE:
+      g_string_append_printf (str, " %s", pango_language_to_string (attr->lang_value));
+      break;
+
+    case PANGO_ATTR_VALUE_FLOAT:
+      {
+        char buf[20];
+        g_ascii_formatd (buf, 20, "%f", attr->double_value);
+        g_string_append_printf (str, " %s", buf);
+      }
+      break;
+
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      {
+        char *s = pango_font_description_to_string (attr->font_value);
+        g_string_append_printf (str, " \"%s\"", s);
+        g_free (s);
+      }
+      break;
+
+    case PANGO_ATTR_VALUE_COLOR:
+      {
+        char *s = pango_color_to_string (&attr->color_value);
+        g_string_append_printf (str, " %s", s);
+        g_free (s);
+      }
+      break;
+
+    case PANGO_ATTR_VALUE_POINTER:
+      {
+        char *s = pango_attr_value_serialize (attr);
+        g_string_append_printf (str, " %s", s);
+        g_free (s);
+      }
+      break;
+
+    default:
+      g_assert_not_reached ();
     }
-  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 ();
 }
 
 /**
@@ -2728,11 +2413,14 @@ pango_attr_list_from_string (const char *text)
           break;
 
         case PANGO_ATTR_FAMILY:
-          endp = (char *)p + strcspn (p, ",\n");
-          if (!is_valid_end_char (*endp)) goto fail;
+          p++;
+          endp = strchr (p, '"');
+          if (!endp) goto fail;
           str = g_strndup (p, endp - p);
           attr = pango_attr_family_new (str);
           g_free (str);
+          endp++;
+          if (!is_valid_end_char (*endp)) goto fail;
           break;
 
         case PANGO_ATTR_STYLE:
@@ -3150,7 +2838,7 @@ pango_attr_iterator_get (PangoAttrIterator *iterator,
     {
       PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
 
-      if (attr->klass->type == type)
+      if (attr->type == type)
         return attr;
     }
 
@@ -3207,14 +2895,14 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
     {
       const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
 
-      switch ((int) attr->klass->type)
+      switch ((int) attr->type)
         {
         case PANGO_ATTR_FONT_DESC:
           {
-            PangoFontMask new_mask = pango_font_description_get_set_fields (((PangoAttrFontDesc 
*)attr)->desc) & ~mask;
+            PangoFontMask new_mask = pango_font_description_get_set_fields (attr->font_value) & ~mask;
             mask |= new_mask;
             pango_font_description_unset_fields (desc, new_mask);
-            pango_font_description_merge_static (desc, ((PangoAttrFontDesc *)attr)->desc, FALSE);
+            pango_font_description_merge_static (desc, attr->font_value, FALSE);
 
             break;
           }
@@ -3222,56 +2910,56 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
           if (!(mask & PANGO_FONT_MASK_FAMILY))
             {
               mask |= PANGO_FONT_MASK_FAMILY;
-              pango_font_description_set_family (desc, ((PangoAttrString *)attr)->value);
+              pango_font_description_set_family (desc, attr->str_value);
             }
           break;
         case PANGO_ATTR_STYLE:
           if (!(mask & PANGO_FONT_MASK_STYLE))
             {
               mask |= PANGO_FONT_MASK_STYLE;
-              pango_font_description_set_style (desc, ((PangoAttrInt *)attr)->value);
+              pango_font_description_set_style (desc, attr->int_value);
             }
           break;
         case PANGO_ATTR_VARIANT:
           if (!(mask & PANGO_FONT_MASK_VARIANT))
             {
               mask |= PANGO_FONT_MASK_VARIANT;
-              pango_font_description_set_variant (desc, ((PangoAttrInt *)attr)->value);
+              pango_font_description_set_variant (desc, attr->int_value);
             }
           break;
         case PANGO_ATTR_WEIGHT:
           if (!(mask & PANGO_FONT_MASK_WEIGHT))
             {
               mask |= PANGO_FONT_MASK_WEIGHT;
-              pango_font_description_set_weight (desc, ((PangoAttrInt *)attr)->value);
+              pango_font_description_set_weight (desc, attr->int_value);
             }
           break;
         case PANGO_ATTR_STRETCH:
           if (!(mask & PANGO_FONT_MASK_STRETCH))
             {
               mask |= PANGO_FONT_MASK_STRETCH;
-              pango_font_description_set_stretch (desc, ((PangoAttrInt *)attr)->value);
+              pango_font_description_set_stretch (desc, attr->int_value);
             }
           break;
         case PANGO_ATTR_SIZE:
           if (!(mask & PANGO_FONT_MASK_SIZE))
             {
               mask |= PANGO_FONT_MASK_SIZE;
-              pango_font_description_set_size (desc, ((PangoAttrSize *)attr)->size);
+              pango_font_description_set_size (desc, attr->int_value);
             }
           break;
         case PANGO_ATTR_ABSOLUTE_SIZE:
           if (!(mask & PANGO_FONT_MASK_SIZE))
             {
               mask |= PANGO_FONT_MASK_SIZE;
-              pango_font_description_set_absolute_size (desc, ((PangoAttrSize *)attr)->size);
+              pango_font_description_set_absolute_size (desc, attr->int_value);
             }
           break;
         case PANGO_ATTR_SCALE:
           if (!have_scale)
             {
               have_scale = TRUE;
-              scale = ((PangoAttrFloat *)attr)->value;
+              scale = attr->double_value;
             }
           break;
         case PANGO_ATTR_LANGUAGE:
@@ -3280,7 +2968,7 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
               if (!have_language)
                 {
                   have_language = TRUE;
-                  *language = ((PangoAttrLanguage *)attr)->value;
+                  *language = attr->lang_value;
                 }
             }
           break;
@@ -3294,15 +2982,15 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
                * so we never merge them.
                * This needs to be handled more systematically.
                */
-              if (attr->klass->type != PANGO_ATTR_FONT_FEATURES &&
-                  attr->klass->type != PANGO_ATTR_BASELINE_SHIFT &&
-                  attr->klass->type != PANGO_ATTR_FONT_SCALE)
+              if (attr->type != PANGO_ATTR_FONT_FEATURES &&
+                  attr->type != PANGO_ATTR_BASELINE_SHIFT &&
+                  attr->type != PANGO_ATTR_FONT_SCALE)
                 {
                   GSList *tmp_list = *extra_attrs;
                   while (tmp_list)
                     {
                       PangoAttribute *old_attr = tmp_list->data;
-                      if (attr->klass->type == old_attr->klass->type)
+                      if (attr->type == old_attr->type)
                         {
                           found = TRUE;
                           break;
@@ -3363,13 +3051,13 @@ pango_attr_iterator_get_attrs (PangoAttrIterator *iterator)
       GSList *tmp_list2;
       gboolean found = FALSE;
 
-      if (attr->klass->type != PANGO_ATTR_FONT_DESC &&
-          attr->klass->type != PANGO_ATTR_BASELINE_SHIFT &&
-          attr->klass->type != PANGO_ATTR_FONT_SCALE)
+      if (attr->type != PANGO_ATTR_FONT_DESC &&
+          attr->type != PANGO_ATTR_BASELINE_SHIFT &&
+          attr->type != PANGO_ATTR_FONT_SCALE)
         for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next)
           {
             PangoAttribute *old_attr = tmp_list2->data;
-            if (attr->klass->type == old_attr->klass->type)
+            if (attr->type == old_attr->type)
               {
                 found = TRUE;
                 break;
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index ae2e7bca..d168e83c 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -32,14 +32,21 @@ G_BEGIN_DECLS
 typedef struct _PangoAttribute        PangoAttribute;
 typedef struct _PangoAttrClass        PangoAttrClass;
 
-typedef struct _PangoAttrString       PangoAttrString;
-typedef struct _PangoAttrLanguage     PangoAttrLanguage;
-typedef struct _PangoAttrInt          PangoAttrInt;
-typedef struct _PangoAttrSize         PangoAttrSize;
-typedef struct _PangoAttrFloat        PangoAttrFloat;
-typedef struct _PangoAttrColor        PangoAttrColor;
-typedef struct _PangoAttrFontDesc     PangoAttrFontDesc;
-typedef struct _PangoAttrFontFeatures PangoAttrFontFeatures;
+typedef enum
+{
+  PANGO_ATTR_VALUE_STRING,
+  PANGO_ATTR_VALUE_INT,
+  PANGO_ATTR_VALUE_BOOLEAN,
+  PANGO_ATTR_VALUE_FLOAT,
+  PANGO_ATTR_VALUE_COLOR,
+  PANGO_ATTR_VALUE_LANGUAGE,
+  PANGO_ATTR_VALUE_FONT_DESC,
+  PANGO_ATTR_VALUE_POINTER
+} PangoAttrValueType;
+
+#define PANGO_ATTR_TYPE(value) (PANGO_ATTR_VALUE_##value | (__COUNTER__ << 8))
+#define PANGO_ATTR_TYPE_VALUE_TYPE(type) ((PangoAttrValueType)((type) & 0xff))
+#define PANGO_ATTR_VALUE_TYPE(attr) PANGO_ATTR_TYPE_VALUE_TYPE ((attr)->type)
 
 /**
  * PangoAttrType:
@@ -89,45 +96,47 @@ typedef struct _PangoAttrFontFeatures PangoAttrFontFeatures;
  */
 typedef enum
 {
-  PANGO_ATTR_INVALID,           /* 0 is an invalid attribute type */
-  PANGO_ATTR_LANGUAGE,          /* PangoAttrLanguage */
-  PANGO_ATTR_FAMILY,            /* PangoAttrString */
-  PANGO_ATTR_STYLE,             /* PangoAttrInt */
-  PANGO_ATTR_WEIGHT,            /* PangoAttrInt */
-  PANGO_ATTR_VARIANT,           /* PangoAttrInt */
-  PANGO_ATTR_STRETCH,           /* PangoAttrInt */
-  PANGO_ATTR_SIZE,              /* PangoAttrSize */
-  PANGO_ATTR_FONT_DESC,         /* PangoAttrFontDesc */
-  PANGO_ATTR_FOREGROUND,        /* PangoAttrColor */
-  PANGO_ATTR_BACKGROUND,        /* PangoAttrColor */
-  PANGO_ATTR_UNDERLINE,         /* PangoAttrInt */
-  PANGO_ATTR_STRIKETHROUGH,     /* PangoAttrInt */
-  PANGO_ATTR_RISE,              /* PangoAttrInt */
-  PANGO_ATTR_SCALE,             /* PangoAttrFloat */
-  PANGO_ATTR_FALLBACK,          /* PangoAttrInt */
-  PANGO_ATTR_LETTER_SPACING,    /* PangoAttrInt */
-  PANGO_ATTR_UNDERLINE_COLOR,   /* PangoAttrColor */
-  PANGO_ATTR_STRIKETHROUGH_COLOR,/* PangoAttrColor */
-  PANGO_ATTR_ABSOLUTE_SIZE,     /* PangoAttrSize */
-  PANGO_ATTR_GRAVITY,           /* PangoAttrInt */
-  PANGO_ATTR_GRAVITY_HINT,      /* PangoAttrInt */
-  PANGO_ATTR_FONT_FEATURES,     /* PangoAttrFontFeatures */
-  PANGO_ATTR_FOREGROUND_ALPHA,  /* PangoAttrInt */
-  PANGO_ATTR_BACKGROUND_ALPHA,  /* PangoAttrInt */
-  PANGO_ATTR_ALLOW_BREAKS,      /* PangoAttrInt */
-  PANGO_ATTR_SHOW,              /* PangoAttrInt */
-  PANGO_ATTR_INSERT_HYPHENS,    /* PangoAttrInt */
-  PANGO_ATTR_OVERLINE,          /* PangoAttrInt */
-  PANGO_ATTR_OVERLINE_COLOR,    /* PangoAttrColor */
-  PANGO_ATTR_LINE_HEIGHT,       /* PangoAttrFloat */
-  PANGO_ATTR_ABSOLUTE_LINE_HEIGHT, /* PangoAttrInt */
-  PANGO_ATTR_TEXT_TRANSFORM,    /* PangoAttrInt */
-  PANGO_ATTR_WORD,              /* PangoAttrInt */
-  PANGO_ATTR_SENTENCE,          /* PangoAttrInt */
-  PANGO_ATTR_BASELINE_SHIFT,    /* PangoAttrSize */
-  PANGO_ATTR_FONT_SCALE,        /* PangoAttrInt */
+  PANGO_ATTR_INVALID,
+  PANGO_ATTR_LANGUAGE             = PANGO_ATTR_TYPE (LANGUAGE),
+  PANGO_ATTR_FAMILY               = PANGO_ATTR_TYPE (STRING),
+  PANGO_ATTR_STYLE                = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_WEIGHT               = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_VARIANT              = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_STRETCH              = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_SIZE                 = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_FONT_DESC            = PANGO_ATTR_TYPE (FONT_DESC),
+  PANGO_ATTR_FOREGROUND           = PANGO_ATTR_TYPE (COLOR),
+  PANGO_ATTR_BACKGROUND           = PANGO_ATTR_TYPE (COLOR),
+  PANGO_ATTR_UNDERLINE            = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_STRIKETHROUGH        = PANGO_ATTR_TYPE (BOOLEAN),
+  PANGO_ATTR_RISE                 = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_SCALE                = PANGO_ATTR_TYPE (FLOAT),
+  PANGO_ATTR_FALLBACK             = PANGO_ATTR_TYPE (BOOLEAN),
+  PANGO_ATTR_LETTER_SPACING       = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_UNDERLINE_COLOR      = PANGO_ATTR_TYPE (COLOR),
+  PANGO_ATTR_STRIKETHROUGH_COLOR  = PANGO_ATTR_TYPE (COLOR),
+  PANGO_ATTR_ABSOLUTE_SIZE        = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_GRAVITY              = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_GRAVITY_HINT         = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_FONT_FEATURES        = PANGO_ATTR_TYPE (STRING),
+  PANGO_ATTR_FOREGROUND_ALPHA     = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_BACKGROUND_ALPHA     = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_ALLOW_BREAKS         = PANGO_ATTR_TYPE (BOOLEAN),
+  PANGO_ATTR_SHOW                 = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_INSERT_HYPHENS       = PANGO_ATTR_TYPE (BOOLEAN),
+  PANGO_ATTR_OVERLINE             = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_OVERLINE_COLOR       = PANGO_ATTR_TYPE (COLOR),
+  PANGO_ATTR_LINE_HEIGHT          = PANGO_ATTR_TYPE (FLOAT),
+  PANGO_ATTR_ABSOLUTE_LINE_HEIGHT = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_TEXT_TRANSFORM       = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_WORD                 = PANGO_ATTR_TYPE (BOOLEAN),
+  PANGO_ATTR_SENTENCE             = PANGO_ATTR_TYPE (BOOLEAN),
+  PANGO_ATTR_BASELINE_SHIFT       = PANGO_ATTR_TYPE (INT),
+  PANGO_ATTR_FONT_SCALE           = PANGO_ATTR_TYPE (INT),
 } PangoAttrType;
 
+#undef PANGO_ATTR_TYPE
+
 /**
  * PangoUnderline:
  * @PANGO_UNDERLINE_NONE: no underline should be drawn
@@ -302,11 +311,22 @@ typedef enum {
  */
 struct _PangoAttribute
 {
-  const PangoAttrClass *klass;
+  PangoAttrType type;
   guint start_index;
   guint end_index;
+  union {
+    char *str_value;
+    int  int_value;
+    gboolean boolean_value;
+    double double_value;
+    PangoColor color_value;
+    PangoLanguage *lang_value;
+    PangoFontDescription *font_value;
+    gpointer pointer_value;
+  };
 };
 
+
 /**
  * PangoAttrFilterFunc:
  * @attribute: a Pango attribute
@@ -330,157 +350,19 @@ typedef gboolean (*PangoAttrFilterFunc) (PangoAttribute *attribute,
  **/
 typedef gpointer (*PangoAttrDataCopyFunc) (gconstpointer user_data);
 
-/**
- * PangoAttrClass:
- * @type: the type ID for this attribute
- * @copy: function to duplicate an attribute of this type
- *   (see [method Pango Attribute copy])
- * @destroy: function to free an attribute of this type
- *   (see [method@Pango.Attribute.destroy])
- * @equal: function to check two attributes of this type for equality
- *   (see [method@Pango.Attribute.equal])
- *
- * The `PangoAttrClass` structure stores the type and operations for
- * a particular type of attribute.
- *
- * The functions in this structure should not be called directly. Instead,
- * one should use the wrapper functions provided for `PangoAttribute`.
- */
-struct _PangoAttrClass
-{
-  /*< public >*/
-  PangoAttrType type;
-  PangoAttribute * (*copy) (const PangoAttribute *attr);
-  void             (*destroy) (PangoAttribute *attr);
-  gboolean         (*equal) (const PangoAttribute *attr1, const PangoAttribute *attr2);
-};
-
-/**
- * PangoAttrString:
- * @attr: the common portion of the attribute
- * @value: the string which is the value of the attribute
- *
- * The `PangoAttrString` structure is used to represent attributes with
- * a string value.
- */
-struct _PangoAttrString
-{
-  PangoAttribute attr;
-  char *value;
-};
-/**
- * PangoAttrLanguage:
- * @attr: the common portion of the attribute
- * @value: the `PangoLanguage` which is the value of the attribute
- *
- * The `PangoAttrLanguage` structure is used to represent attributes that
- * are languages.
- */
-struct _PangoAttrLanguage
-{
-  PangoAttribute attr;
-  PangoLanguage *value;
-};
-/**
- * PangoAttrInt:
- * @attr: the common portion of the attribute
- * @value: the value of the attribute
- *
- * The `PangoAttrInt` structure is used to represent attributes with
- * an integer or enumeration value.
- */
-struct _PangoAttrInt
-{
-  PangoAttribute attr;
-  int value;
-};
-/**
- * PangoAttrFloat:
- * @attr: the common portion of the attribute
- * @value: the value of the attribute
- *
- * The `PangoAttrFloat` structure is used to represent attributes with
- * a float or double value.
- */
-struct _PangoAttrFloat
-{
-  PangoAttribute attr;
-  double value;
-};
-/**
- * PangoAttrColor:
- * @attr: the common portion of the attribute
- * @color: the `PangoColor` which is the value of the attribute
- *
- * The `PangoAttrColor` structure is used to represent attributes that
- * are colors.
- */
-struct _PangoAttrColor
-{
-  PangoAttribute attr;
-  PangoColor color;
-};
-
-/**
- * PangoAttrSize:
- * @attr: the common portion of the attribute
- * @size: size of font, in units of 1/%PANGO_SCALE of a point (for
- *   %PANGO_ATTR_SIZE) or of a device unit (for %PANGO_ATTR_ABSOLUTE_SIZE)
- * @absolute: whether the font size is in device units or points.
- *   This field is only present for compatibility with Pango-1.8.0
- *   (%PANGO_ATTR_ABSOLUTE_SIZE was added in 1.8.1); and always will
- *   be %FALSE for %PANGO_ATTR_SIZE and %TRUE for %PANGO_ATTR_ABSOLUTE_SIZE.
- *
- * The `PangoAttrSize` structure is used to represent attributes which
- * set font size.
- */
-struct _PangoAttrSize
-{
-  PangoAttribute attr;
-  int size;
-  guint absolute : 1;
-};
-
-/**
- * PangoAttrFontDesc:
- * @attr: the common portion of the attribute
- * @desc: the font description which is the value of this attribute
- *
- * The `PangoAttrFontDesc` structure is used to store an attribute that
- * sets all aspects of the font description at once.
- */
-struct _PangoAttrFontDesc
-{
-  PangoAttribute attr;
-  PangoFontDescription *desc;
-};
-
-/**
- * PangoAttrFontFeatures:
- * @attr: the common portion of the attribute
- * @features: the features, as a string in CSS syntax
- *
- * The `PangoAttrFontFeatures` structure is used to represent OpenType
- * font features as an attribute.
- *
- * Since: 1.38
- */
-struct _PangoAttrFontFeatures
-{
-  PangoAttribute attr;
-  gchar *features;
-};
+typedef char * (*PangoAttrDataSerializeFunc) (gconstpointer user_data);
 
 PANGO_AVAILABLE_IN_ALL
 GType                   pango_attribute_get_type                (void) G_GNUC_CONST;
 
 PANGO_AVAILABLE_IN_ALL
-PangoAttrType           pango_attr_type_register                (const char                 *name);
+PangoAttrType           pango_attr_type_register                (PangoAttrDataCopyFunc       copy,
+                                                                 GDestroyNotify              destroy,
+                                                                 GEqualFunc                  equal,
+                                                                 const char                 *name,
+                                                                 PangoAttrDataSerializeFunc  serialize);
 PANGO_AVAILABLE_IN_1_22
 const char *            pango_attr_type_get_name                (PangoAttrType               type) 
G_GNUC_CONST;
-PANGO_AVAILABLE_IN_1_20
-void                    pango_attribute_init                    (PangoAttribute             *attr,
-                                                                 const PangoAttrClass       *klass);
 PANGO_AVAILABLE_IN_ALL
 PangoAttribute *        pango_attribute_copy                    (const PangoAttribute       *attr);
 PANGO_AVAILABLE_IN_ALL
@@ -583,23 +465,37 @@ PANGO_AVAILABLE_IN_1_50
 PangoAttribute *        pango_attr_line_height_new_absolute     (int                          height);
 PANGO_AVAILABLE_IN_1_50
 PangoAttribute *        pango_attr_text_transform_new           (PangoTextTransform transform);
+PANGO_AVAILABLE_IN_1_52
+PangoAttribute *        pango_attr_custom_new                   (PangoAttrType                type,
+                                                                 gpointer                     user_data);
+
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_string              (PangoAttribute              *attribute,
+                                                                 const char                 **value);
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_language            (PangoAttribute              *attribute,
+                                                                 PangoLanguage              **value);
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_int                 (PangoAttribute              *attribute,
+                                                                 int                         *value);
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_boolean             (PangoAttribute              *attribute,
+                                                                 gboolean                    *value);
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_float               (PangoAttribute              *attribute,
+                                                                 double                      *value);
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_color               (PangoAttribute              *attribute,
+                                                                 PangoColor                  *value);
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_font_desc           (PangoAttribute              *attribute,
+                                                                 PangoFontDescription       **value);
+
+PANGO_AVAILABLE_IN_1_52
+gboolean                pango_attribute_get_custom              (PangoAttribute              *attribute,
+                                                                 gpointer                    *value);
+
 
-PANGO_AVAILABLE_IN_1_50
-PangoAttrString       * pango_attribute_as_string               (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrLanguage     * pango_attribute_as_language             (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrInt          * pango_attribute_as_int                  (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrSize         * pango_attribute_as_size                 (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrFloat        * pango_attribute_as_float                (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrColor        * pango_attribute_as_color                (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrFontDesc     * pango_attribute_as_font_desc            (PangoAttribute              *attr);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrFontFeatures * pango_attribute_as_font_features        (PangoAttribute              *attr);
 
 /* Attribute lists */
 
diff --git a/pango/pango-item.c b/pango/pango-item.c
index 944bfe2d..ce809066 100644
--- a/pango/pango-item.c
+++ b/pango/pango-item.c
@@ -276,14 +276,13 @@ pango_analysis_collect_features (const PangoAnalysis *analysis,
   for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
     {
       PangoAttribute *attr = l->data;
-      if (attr->klass->type == PANGO_ATTR_FONT_FEATURES)
+      if (attr->type == PANGO_ATTR_FONT_FEATURES)
         {
-          PangoAttrFontFeatures *fattr = (PangoAttrFontFeatures *) attr;
           const gchar *feat;
           const gchar *end;
           int len;
 
-          feat = fattr->features;
+          feat = attr->str_value;
 
           while (feat != NULL && *num_features < length)
             {
@@ -311,7 +310,7 @@ pango_analysis_collect_features (const PangoAnalysis *analysis,
   for (l = analysis->extra_attrs; l && *num_features < length; l = l->next)
     {
       PangoAttribute *attr = l->data;
-      if (attr->klass->type == PANGO_ATTR_LETTER_SPACING)
+      if (attr->type == PANGO_ATTR_LETTER_SPACING)
         {
           hb_tag_t tags[] = {
             HB_TAG('l','i','g','a'),
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index 7d5ca3cd..94b59427 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -4647,7 +4647,7 @@ static gboolean
 affects_itemization (PangoAttribute *attr,
                      gpointer        data)
 {
-  switch ((int)attr->klass->type)
+  switch ((int)attr->type)
     {
     /* These affect font selection */
     case PANGO_ATTR_LANGUAGE:
@@ -4681,7 +4681,7 @@ static gboolean
 affects_break_or_shape (PangoAttribute *attr,
                         gpointer        data)
 {
-  switch ((int)attr->klass->type)
+  switch ((int)attr->type)
     {
     /* Affects breaks */
     case PANGO_ATTR_ALLOW_BREAKS:
@@ -5515,11 +5515,11 @@ pango_layout_get_empty_extents_and_height_at_index (PangoLayout    *layout,
 
                   attr = pango_attr_iterator_get (&iter, PANGO_ATTR_LINE_HEIGHT);
                   if (attr)
-                    line_height_factor = ((PangoAttrFloat *)attr)->value;
+                    line_height_factor = attr->double_value;
 
                   attr = pango_attr_iterator_get (&iter, PANGO_ATTR_ABSOLUTE_LINE_HEIGHT);
                   if (attr)
-                    absolute_line_height = ((PangoAttrInt *)attr)->value;
+                    absolute_line_height = attr->int_value;
 
                   break;
                 }
@@ -6572,14 +6572,14 @@ collect_baseline_shift (ParaBreakState *state,
     {
       PangoAttribute *attr = l->data;
 
-      if (attr->klass->type == PANGO_ATTR_RISE)
+      if (attr->type == PANGO_ATTR_RISE)
         {
-          int value = ((PangoAttrInt *)attr)->value;
+          int value = attr->int_value;
 
           *start_y_offset += value;
           *end_y_offset -= value;
         }
-      else if (attr->klass->type == PANGO_ATTR_BASELINE_SHIFT)
+      else if (attr->type == PANGO_ATTR_BASELINE_SHIFT)
         {
           if (attr->start_index == item->offset)
             {
@@ -6590,7 +6590,7 @@ collect_baseline_shift (ParaBreakState *state,
               entry->attr = attr;
               state->baseline_shifts = g_list_prepend (state->baseline_shifts, entry);
 
-              value = ((PangoAttrInt *)attr)->value;
+              value = attr->int_value;
 
               if (value > 1024 || value < -1024)
                 {
@@ -6652,7 +6652,7 @@ collect_baseline_shift (ParaBreakState *state,
 
                   if (attr->start_index == entry->attr->start_index &&
                       attr->end_index == entry->attr->end_index &&
-                      ((PangoAttrInt *)attr)->value == ((PangoAttrInt *)entry->attr)->value)
+                      attr->int_value == entry->attr->int_value)
                     {
                       *end_x_offset -= entry->x_offset;
                       *end_y_offset -= entry->y_offset;
@@ -6779,10 +6779,10 @@ pango_layout_get_item_properties (PangoItem      *item,
     {
       PangoAttribute *attr = tmp_list->data;
 
-      switch ((int) attr->klass->type)
+      switch ((int) attr->type)
         {
         case PANGO_ATTR_UNDERLINE:
-          switch (((PangoAttrInt *)attr)->value)
+          switch (attr->int_value)
             {
             case PANGO_UNDERLINE_NONE:
               break;
@@ -6808,7 +6808,7 @@ pango_layout_get_item_properties (PangoItem      *item,
           break;
 
         case PANGO_ATTR_OVERLINE:
-          switch (((PangoAttrInt *)attr)->value)
+          switch (attr->int_value)
             {
             case PANGO_OVERLINE_SINGLE:
               properties->oline_single = TRUE;
@@ -6820,23 +6820,23 @@ pango_layout_get_item_properties (PangoItem      *item,
           break;
 
         case PANGO_ATTR_STRIKETHROUGH:
-          properties->strikethrough = ((PangoAttrInt *)attr)->value;
+          properties->strikethrough = attr->int_value;
           break;
 
         case PANGO_ATTR_LETTER_SPACING:
-          properties->letter_spacing = ((PangoAttrInt *)attr)->value;
+          properties->letter_spacing = attr->int_value;
           break;
 
         case PANGO_ATTR_LINE_HEIGHT:
-          properties->line_height = ((PangoAttrFloat *)attr)->value;
+          properties->line_height = attr->double_value;
           break;
 
         case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-          properties->absolute_line_height = ((PangoAttrInt *)attr)->value;
+          properties->absolute_line_height = attr->int_value;
           break;
 
         case PANGO_ATTR_SHOW:
-          properties->showing_space = (((PangoAttrInt *)attr)->value & PANGO_SHOW_SPACES) != 0;
+          properties->showing_space = (attr->int_value & PANGO_SHOW_SPACES) != 0;
           break;
 
         default:
diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c
index 4e65f489..da052874 100644
--- a/pango/pango-renderer.c
+++ b/pango/pango-renderer.c
@@ -1411,46 +1411,46 @@ pango_renderer_default_prepare_run (PangoRenderer  *renderer,
     {
       PangoAttribute *attr = l->data;
 
-      switch ((int) attr->klass->type)
+      switch ((int) attr->type)
         {
         case PANGO_ATTR_UNDERLINE:
-          renderer->underline = ((PangoAttrInt *)attr)->value;
+          renderer->underline = attr->int_value;
           break;
 
         case PANGO_ATTR_OVERLINE:
-          renderer->priv->overline = ((PangoAttrInt *)attr)->value;
+          renderer->priv->overline = attr->int_value;
           break;
 
         case PANGO_ATTR_STRIKETHROUGH:
-          renderer->strikethrough = ((PangoAttrInt *)attr)->value;
+          renderer->strikethrough = attr->int_value;
           break;
 
         case PANGO_ATTR_FOREGROUND:
-          fg_color = &((PangoAttrColor *)attr)->color;
+          fg_color = &attr->color_value;
           break;
 
         case PANGO_ATTR_BACKGROUND:
-          bg_color = &((PangoAttrColor *)attr)->color;
+          bg_color = &attr->color_value;
           break;
 
         case PANGO_ATTR_UNDERLINE_COLOR:
-          underline_color = &((PangoAttrColor *)attr)->color;
+          underline_color = &attr->color_value;
           break;
 
         case PANGO_ATTR_OVERLINE_COLOR:
-          overline_color = &((PangoAttrColor *)attr)->color;
+          overline_color = &attr->color_value;
           break;
 
         case PANGO_ATTR_STRIKETHROUGH_COLOR:
-          strikethrough_color = &((PangoAttrColor *)attr)->color;
+          strikethrough_color = &attr->color_value;
           break;
 
         case PANGO_ATTR_FOREGROUND_ALPHA:
-          fg_alpha = ((PangoAttrInt *)attr)->value;
+          fg_alpha = attr->int_value;
           break;
 
         case PANGO_ATTR_BACKGROUND_ALPHA:
-          bg_alpha = ((PangoAttrInt *)attr)->value;
+          bg_alpha = attr->int_value;
           break;
 
         default:
diff --git a/pango/serializer.c b/pango/serializer.c
index ddd9de4c..78bd59a3 100644
--- a/pango/serializer.c
+++ b/pango/serializer.c
@@ -27,6 +27,7 @@
 #include <pango/pango-enum-types.h>
 #include <pango/pango-font-private.h>
 #include <pango/pango-hbface.h>
+#include <pango/pango-attributes-private.h>
 
 #include <hb-ot.h>
 #include "pango/json/gtkjsonparserprivate.h"
@@ -163,46 +164,49 @@ get_weight_name (int weight)
   return NULL;
 }
 
-static const char *attr_type_names[] = {
-  "invalid",
-  "language",
-  "family",
-  "style",
-  "weight",
-  "variant",
-  "stretch",
-  "size",
-  "font-desc",
-  "foreground",
-  "background",
-  "underline",
-  "strikethrough",
-  "rise",
-  "scale",
-  "fallback",
-  "letter-spacing",
-  "underline-color",
-  "strikethrough-color",
-  "absolute-size",
-  "gravity",
-  "gravity-hint",
-  "font-features",
-  "foreground-alpha",
-  "background-alpha",
-  "allow-breaks",
-  "show",
-  "insert-hyphens",
-  "overline",
-  "overline-color",
-  "line-height",
-  "absolute-line-height",
-  "text-transform",
-  "word",
-  "sentence",
-  "baseline-shift",
-  "font-scale",
-  NULL
-};
+static PangoAttrType
+get_attr_type (const char *nick)
+{
+  GEnumClass *enum_class;
+  GEnumValue *enum_value = NULL;
+
+  enum_class = g_type_class_ref (PANGO_TYPE_ATTR_TYPE);
+  for (int i = 0; i < enum_class->n_values; i++)
+    {
+      enum_value = &enum_class->values[i];
+      if (strcmp (enum_value->value_nick, nick) == 0)
+        break;
+      enum_value = NULL;
+    }
+  g_type_class_unref (enum_class);
+
+  if (enum_value)
+    return enum_value->value;
+
+  return 0;
+}
+
+static const char *
+get_attr_type_name (PangoAttrType type)
+{
+  GEnumClass *enum_class;
+  GEnumValue *enum_value = NULL;
+
+  enum_class = g_type_class_ref (PANGO_TYPE_ATTR_TYPE);
+  for (int i = 0; i < enum_class->n_values; i++)
+    {
+      enum_value = &enum_class->values[i];
+      if (enum_value->value == type)
+        break;
+      enum_value = NULL;
+    }
+  g_type_class_unref (enum_class);
+
+  if (enum_value)
+    return enum_value->value_nick;
+
+  return NULL;
+}
 
 static const char *
 get_script_name (PangoScript script)
@@ -293,110 +297,106 @@ add_attribute (GtkJsonPrinter *printer,
     gtk_json_printer_add_integer (printer, "start", (int)attr->start_index);
   if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END)
     gtk_json_printer_add_integer (printer, "end", (int)attr->end_index);
-  gtk_json_printer_add_string (printer, "type", attr_type_names[attr->klass->type]);
+  gtk_json_printer_add_string (printer, "type", get_attr_type_name (attr->type));
 
-  switch (attr->klass->type)
+  switch (PANGO_ATTR_VALUE_TYPE (attr))
     {
-    default:
-    case PANGO_ATTR_INVALID:
-      g_assert_not_reached ();
-    case PANGO_ATTR_LANGUAGE:
-      gtk_json_printer_add_string (printer, "value", pango_language_to_string 
(((PangoAttrLanguage*)attr)->value));
-      break;
-    case PANGO_ATTR_FAMILY:
-    case PANGO_ATTR_FONT_FEATURES:
-      gtk_json_printer_add_string (printer, "value", ((PangoAttrString*)attr)->value);
-      break;
-    case PANGO_ATTR_STYLE:
-      gtk_json_printer_add_string (printer, "value", style_names[((PangoAttrInt*)attr)->value]);
+    case PANGO_ATTR_VALUE_STRING:
+      gtk_json_printer_add_string (printer, "value", attr->str_value);
       break;
 
-    case PANGO_ATTR_VARIANT:
-      gtk_json_printer_add_string (printer, "value", variant_names[((PangoAttrInt*)attr)->value]);
-      break;
+    case PANGO_ATTR_VALUE_INT:
+      switch ((int)attr->type)
+        {
+        case PANGO_ATTR_STYLE:
+          gtk_json_printer_add_string (printer, "value", style_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_STRETCH:
-      gtk_json_printer_add_string (printer, "value", stretch_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_VARIANT:
+          gtk_json_printer_add_string (printer, "value", variant_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_UNDERLINE:
-      gtk_json_printer_add_string (printer, "value", underline_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_STRETCH:
+          gtk_json_printer_add_string (printer, "value", stretch_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_OVERLINE:
-      gtk_json_printer_add_string (printer, "value", overline_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_UNDERLINE:
+          gtk_json_printer_add_string (printer, "value", underline_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_GRAVITY:
-      gtk_json_printer_add_string (printer, "value", gravity_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_OVERLINE:
+          gtk_json_printer_add_string (printer, "value", overline_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_GRAVITY_HINT:
-      gtk_json_printer_add_string (printer, "value", gravity_hint_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_GRAVITY:
+          gtk_json_printer_add_string (printer, "value", gravity_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_TEXT_TRANSFORM:
-      gtk_json_printer_add_string (printer, "value", text_transform_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_GRAVITY_HINT:
+          gtk_json_printer_add_string (printer, "value", gravity_hint_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_FONT_SCALE:
-      gtk_json_printer_add_string (printer, "value", font_scale_names[((PangoAttrInt*)attr)->value]);
-      break;
+        case PANGO_ATTR_TEXT_TRANSFORM:
+          gtk_json_printer_add_string (printer, "value", text_transform_names[attr->int_value]);
+          break;
 
-    case PANGO_ATTR_WEIGHT:
-      {
-        const char *name = get_weight_name (((PangoAttrInt*)attr)->value);
-        if (name)
-          gtk_json_printer_add_string (printer, "value", name);
-        else
-          gtk_json_printer_add_integer (printer, "value", ((PangoAttrInt*)attr)->value);
-      }
-      break;
+        case PANGO_ATTR_FONT_SCALE:
+          gtk_json_printer_add_string (printer, "value", font_scale_names[attr->int_value]);
+          break;
+
+        case PANGO_ATTR_WEIGHT:
+          {
+            const char *name = get_weight_name (attr->int_value);
+            if (name)
+              gtk_json_printer_add_string (printer, "value", name);
+            else
+              gtk_json_printer_add_integer (printer, "value", attr->int_value);
+          }
+          break;
+
+        case PANGO_ATTR_BASELINE_SHIFT:
+          gtk_json_printer_add_string (printer, "value", baseline_shift_names[attr->int_value]);
+          break;
+
+        default:
+          gtk_json_printer_add_integer (printer, "value", attr->int_value);
+          break;
+        }
 
-    case PANGO_ATTR_BASELINE_SHIFT:
-      gtk_json_printer_add_string (printer, "value", baseline_shift_names[((PangoAttrInt*)attr)->value]);
       break;
 
+    case PANGO_ATTR_VALUE_BOOLEAN:
+      gtk_json_printer_add_boolean (printer, "value", attr->boolean_value);
+      break;
 
-    case PANGO_ATTR_SIZE:
-    case PANGO_ATTR_RISE:
-    case PANGO_ATTR_LETTER_SPACING:
-    case PANGO_ATTR_ABSOLUTE_SIZE:
-    case PANGO_ATTR_FOREGROUND_ALPHA:
-    case PANGO_ATTR_BACKGROUND_ALPHA:
-    case PANGO_ATTR_SHOW:
-    case PANGO_ATTR_WORD:
-    case PANGO_ATTR_SENTENCE:
-    case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-      gtk_json_printer_add_integer (printer, "value", ((PangoAttrInt*)attr)->value);
+    case PANGO_ATTR_VALUE_LANGUAGE:
+      gtk_json_printer_add_string (printer, "value", pango_language_to_string (attr->lang_value));
       break;
 
-    case PANGO_ATTR_FONT_DESC:
-      str = font_description_to_string (((PangoAttrFontDesc*)attr)->desc);
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      str = font_description_to_string (attr->font_value);
       gtk_json_printer_add_string (printer, "value", str);
       g_free (str);
       break;
 
-    case PANGO_ATTR_FOREGROUND:
-    case PANGO_ATTR_BACKGROUND:
-    case PANGO_ATTR_UNDERLINE_COLOR:
-    case PANGO_ATTR_OVERLINE_COLOR:
-    case PANGO_ATTR_STRIKETHROUGH_COLOR:
-      str = pango_color_to_string (&((PangoAttrColor*)attr)->color);
+    case PANGO_ATTR_VALUE_COLOR:
+      str = pango_color_to_string (&attr->color_value);
       gtk_json_printer_add_string (printer, "value", str);
       g_free (str);
       break;
 
-    case PANGO_ATTR_STRIKETHROUGH:
-    case PANGO_ATTR_FALLBACK:
-    case PANGO_ATTR_ALLOW_BREAKS:
-    case PANGO_ATTR_INSERT_HYPHENS:
-      gtk_json_printer_add_boolean (printer, "value", ((PangoAttrInt*)attr)->value != 0);
+    case PANGO_ATTR_VALUE_FLOAT:
+      gtk_json_printer_add_number (printer, "value", attr->double_value);
       break;
 
-    case PANGO_ATTR_SCALE:
-    case PANGO_ATTR_LINE_HEIGHT:
-      gtk_json_printer_add_number (printer, "value", ((PangoAttrFloat*)attr)->value);
+    case PANGO_ATTR_VALUE_POINTER:
+      str = pango_attr_value_serialize (attr);
+      gtk_json_printer_add_string (printer, "value", str);
+      g_free (str);
+      break;
+
+    default:
+      g_assert_not_reached ();
     }
 
   gtk_json_printer_end (printer);
@@ -1158,7 +1158,7 @@ json_to_attribute (GtkJsonParser *parser)
           break;
 
         case ATTR_TYPE:
-          type = parser_select_string (parser, attr_type_names);
+          type = get_attr_type (gtk_json_parser_get_string (parser));
           break;
 
         case ATTR_VALUE:
@@ -1172,7 +1172,12 @@ json_to_attribute (GtkJsonParser *parser)
   while (gtk_json_parser_next (parser));
 
   if (!attr && !gtk_json_parser_get_error (parser))
-    gtk_json_parser_schema_error (parser, "Attribute missing \"value\"");
+    {
+      if (type == PANGO_ATTR_INVALID)
+        gtk_json_parser_schema_error (parser, "Invalid attribute \"type\"");
+      else
+        gtk_json_parser_schema_error (parser, "Attribute missing \"value\"");
+    }
 
   gtk_json_parser_end (parser);
 
diff --git a/pango/shape.c b/pango/shape.c
index 68d11eee..2f2472e7 100644
--- a/pango/shape.c
+++ b/pango/shape.c
@@ -276,8 +276,8 @@ find_show_flags (const PangoAnalysis *analysis)
     {
       PangoAttribute *attr = l->data;
 
-      if (attr->klass->type == PANGO_ATTR_SHOW)
-        flags |= ((PangoAttrInt*)attr)->value;
+      if (attr->type == PANGO_ATTR_SHOW)
+        flags |= attr->int_value;
     }
 
   return flags;
@@ -293,8 +293,8 @@ find_text_transform (const PangoAnalysis *analysis)
     {
       PangoAttribute *attr = l->data;
 
-      if (attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
-        transform = (PangoTextTransform) ((PangoAttrInt*)attr)->value;
+      if (attr->type == PANGO_ATTR_TEXT_TRANSFORM)
+        transform = (PangoTextTransform) attr->int_value;
     }
 
   return transform;
diff --git a/tests/markups/valid-19.expected b/tests/markups/valid-19.expected
index 5b84133f..9de3df07 100644
--- a/tests/markups/valid-19.expected
+++ b/tests/markups/valid-19.expected
@@ -4,7 +4,7 @@ test
 ---
 
 range 0 4
-0 4 family Times
+0 4 family "Times"
 0 4 weight bold
 0 4 foreground #ffff00000000
 0 4 letter-spacing 1024
diff --git a/tests/markups/valid-4.expected b/tests/markups/valid-4.expected
index 345e8699..11ecfab1 100644
--- a/tests/markups/valid-4.expected
+++ b/tests/markups/valid-4.expected
@@ -31,9 +31,9 @@ range 38 41
 38 41 baseline-shift superscript
 range 41 42
 range 42 45
-42 54 family Monospace
+42 54 family "Monospace"
 range 45 54
-42 54 family Monospace
+42 54 family "Monospace"
 45 54 underline single
 range 54 2147483647
 
diff --git a/tests/test-itemize.c b/tests/test-itemize.c
index 267c1b62..fc87a959 100644
--- a/tests/test-itemize.c
+++ b/tests/test-itemize.c
@@ -57,7 +57,7 @@ static gboolean
 affects_itemization (PangoAttribute *attr,
                      gpointer        data)
 {
-  switch ((int)attr->klass->type)
+  switch ((int)attr->type)
     {
     /* These affect font selection */
     case PANGO_ATTR_LANGUAGE:
diff --git a/tests/test-shape.c b/tests/test-shape.c
index bbfd91db..a3d4e8b7 100644
--- a/tests/test-shape.c
+++ b/tests/test-shape.c
@@ -63,7 +63,7 @@ static gboolean
 affects_itemization (PangoAttribute *attr,
                      gpointer        data)
 {
-  switch ((int)attr->klass->type)
+  switch ((int)attr->type)
     {
     /* These affect font selection */
     case PANGO_ATTR_LANGUAGE:
@@ -94,7 +94,7 @@ static gboolean
 affects_break_or_shape (PangoAttribute *attr,
                         gpointer        data)
 {
-  switch ((int)attr->klass->type)
+  switch ((int)attr->type)
     {
     /* Affects breaks */
     case PANGO_ATTR_ALLOW_BREAKS:
diff --git a/tests/testattributes.c b/tests/testattributes.c
index 772578de..be98cdd0 100644
--- a/tests/testattributes.c
+++ b/tests/testattributes.c
@@ -36,7 +36,6 @@ static void
 test_attributes_basic (void)
 {
   PangoFontDescription *desc;
-  PangoRectangle rect = { 0, 0, 10, 10 };
 
   test_copy (pango_attr_language_new (pango_language_from_string ("ja-JP")));
   test_copy (pango_attr_family_new ("Times"));
@@ -97,64 +96,110 @@ test_attributes_equal (void)
   pango_attribute_destroy (attr3);
 }
 
+static gpointer
+copy_my_attribute_data (gconstpointer data)
+{
+  return (gpointer)data;
+}
+
+static void
+destroy_my_attribute_data (gpointer data)
+{
+}
+
+static gboolean
+my_attribute_data_equal (gconstpointer data1,
+                         gconstpointer data2)
+{
+  return data1 == data2;
+}
+
+static char *
+my_attribute_data_serialize (gconstpointer data)
+{
+  return g_strdup_printf ("%p", data);
+}
+
 static void
 test_attributes_register (void)
 {
   PangoAttrType type;
-  GEnumClass *class;
+  PangoAttribute *attr;
+  PangoAttribute *attr2;
+  gpointer value = NULL;
+  gboolean ret;
+  PangoAttrList *list;
+  char *str;
+
+  type = pango_attr_type_register (copy_my_attribute_data,
+                                   destroy_my_attribute_data,
+                                   my_attribute_data_equal,
+                                   "my-attribute",
+                                   my_attribute_data_serialize);
 
-  type = pango_attr_type_register ("my-attribute");
   g_assert_cmpstr (pango_attr_type_get_name (type), ==, "my-attribute");
 
-  class = g_type_class_ref (PANGO_TYPE_ATTR_TYPE);
-  for (int i = 0; i < class->n_values; i++)
-    {
-      g_assert_cmpint (type, !=, class->values[i].value);
-      g_assert_null (pango_attr_type_get_name (class->values[i].value));
-    }
+  attr = pango_attr_custom_new (type, (gpointer)0x42);
+
+  ret = pango_attribute_get_custom (attr, &value);
+  g_assert_true (ret);
+  g_assert_true (value == (gpointer)0x42);
+
+  attr2 = pango_attr_custom_new (type, (gpointer)0x43);
 
-  g_type_class_unref (class);
+  ret = pango_attribute_equal (attr, attr2);
+  g_assert_false (ret);
+
+  list = pango_attr_list_new ();
+  pango_attr_list_insert (list, attr2);
+
+  str = pango_attr_list_to_string (list);
+  g_assert_cmpstr (str, ==, "0 4294967295 my-attribute 0x43");
+  g_free (str);
+
+  pango_attr_list_unref (list);
+
+  pango_attribute_destroy (attr);
 }
 
 static void
 test_binding (PangoAttribute *attr)
 {
-  enum {
-    INVALID, INT, LANGUAGE, STRING, SIZE, FONT_DESC, COLOR, SHAPE, FLOAT, FONT_FEATURES,
-  } attr_base[] = {
-    INVALID, LANGUAGE, STRING, INT, INT, INT, INT, SIZE, FONT_DESC, COLOR,
-    COLOR, INT, INT, INT, SHAPE, FLOAT, INT, INT, COLOR, COLOR, SIZE,
-    INT, INT, FONT_FEATURES, INT, INT, INT, INT, INT, INT, COLOR, FLOAT,
-    INT, INT, INT, INT, INT, INT
-  };
-
-  switch (attr_base[attr->klass->type])
+  int int_value;
+  gboolean boolean_value;
+  PangoLanguage *lang_value;
+  const char *string;
+  PangoFontDescription *font_desc;
+  PangoColor color;
+  double double_value;
+  gpointer pointer_value;
+
+  switch (PANGO_ATTR_VALUE_TYPE (attr))
     {
-    case INT:
-      g_assert_nonnull (pango_attribute_as_int (attr));
+    case PANGO_ATTR_VALUE_INT:
+      g_assert_true (pango_attribute_get_int (attr, &int_value));
       break;
-    case LANGUAGE:
-      g_assert_nonnull (pango_attribute_as_language (attr));
+    case PANGO_ATTR_VALUE_BOOLEAN:
+      g_assert_true (pango_attribute_get_boolean (attr, &boolean_value));
       break;
-    case STRING:
-      g_assert_nonnull (pango_attribute_as_string (attr));
+    case PANGO_ATTR_VALUE_LANGUAGE:
+      g_assert_true (pango_attribute_get_language (attr, &lang_value));
       break;
-    case SIZE:
-      g_assert_nonnull (pango_attribute_as_size (attr));
+    case PANGO_ATTR_VALUE_STRING:
+      g_assert_true (pango_attribute_get_string (attr, &string));
       break;
-    case FONT_DESC:
-      g_assert_nonnull (pango_attribute_as_font_desc (attr));
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      g_assert_true (pango_attribute_get_font_desc (attr, &font_desc));
       break;
-    case COLOR:
-      g_assert_nonnull (pango_attribute_as_color (attr));
+    case PANGO_ATTR_VALUE_COLOR:
+      g_assert_true (pango_attribute_get_color (attr, &color));
       break;
-    case FLOAT:
-      g_assert_nonnull (pango_attribute_as_float (attr));
+    case PANGO_ATTR_VALUE_FLOAT:
+      g_assert_true (pango_attribute_get_float (attr, &double_value));
       break;
-    case FONT_FEATURES:
-      g_assert_nonnull (pango_attribute_as_font_features (attr));
+    case PANGO_ATTR_VALUE_POINTER:
+      g_assert_true (pango_attribute_get_custom (attr, &pointer_value));
       break;
-    case INVALID:
     default:
       g_assert_not_reached ();
     }
@@ -166,7 +211,6 @@ static void
 test_binding_helpers (void)
 {
   PangoFontDescription *desc;
-  PangoRectangle rect = { 0, 0, 10, 10 };
 
   test_binding (pango_attr_language_new (pango_language_from_string ("ja-JP")));
   test_binding (pango_attr_family_new ("Times"));
@@ -451,7 +495,7 @@ test_list_change5 (void)
   attr = attribute_from_string ("5 15 style italic");
   g_assert (attr->start_index == 5);
   g_assert (attr->end_index == 15);
-  g_assert (((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
+  g_assert (attr->int_value == PANGO_STYLE_ITALIC);
   pango_attr_list_change (list, attr);
 
   assert_attr_list (list, "0 3 weight ultrabold\n"
@@ -740,7 +784,7 @@ never_true (PangoAttribute *attribute, gpointer user_data)
 static gboolean
 just_weight (PangoAttribute *attribute, gpointer user_data)
 {
-  if (attribute->klass->type == PANGO_ATTR_WEIGHT)
+  if (attribute->type == PANGO_ATTR_WEIGHT)
     return TRUE;
   else
     return FALSE;
@@ -877,7 +921,7 @@ test_iter_get_font (void)
   list = pango_attr_list_new ();
   attr = pango_attr_size_new (10 * PANGO_SCALE);
   pango_attr_list_insert (list, attr);
-  attr = attribute_from_string ("0 -1 family Times");
+  attr = attribute_from_string ("0 -1 family \"Times\"");
   pango_attr_list_insert (list, attr);
   attr = attribute_from_string ("10 30 stretch condensed");
   pango_attr_list_insert (list, attr);
@@ -938,7 +982,7 @@ test_iter_get_attrs (void)
   list = pango_attr_list_new ();
   attr = pango_attr_size_new (10 * PANGO_SCALE);
   pango_attr_list_insert (list, attr);
-  attr = attribute_from_string ("0 -1 family Times");
+  attr = attribute_from_string ("0 -1 family \"Times\"");
   pango_attr_list_insert (list, attr);
   attr = attribute_from_string ("10 30 stretch condensed");
   pango_attr_list_insert (list, attr);
@@ -951,24 +995,24 @@ test_iter_get_attrs (void)
 
   iter = pango_attr_list_get_iterator (list);
   assert_attr_iterator (iter, "0 -1 size 10240\n"
-                              "0 -1 family Times\n");
+                              "0 -1 family \"Times\"\n");
 
   pango_attr_iterator_next (iter);
   assert_attr_iterator (iter, "0 -1 size 10240\n"
-                              "0 -1 family Times\n"
+                              "0 -1 family \"Times\"\n"
                               "10 30 stretch 2\n"
                               "10 20 language ja-jp\n");
 
   pango_attr_iterator_next (iter);
   assert_attr_iterator (iter, "0 -1 size 10240\n"
-                              "0 -1 family Times\n"
+                              "0 -1 family \"Times\"\n"
                               "10 30 stretch 2\n"
                               "20 -1 rise 100\n"
                               "20 -1 fallback 0\n");
 
   pango_attr_iterator_next (iter);
   assert_attr_iterator (iter, "0 -1 size 10240\n"
-                              "0 -1 family Times\n"
+                              "0 -1 family \"Times\"\n"
                               "20 -1 rise 100\n"
                               "20 -1 fallback 0\n");
 
@@ -985,7 +1029,7 @@ test_list_update (void)
   PangoAttrList *list;
 
   list = pango_attr_list_from_string ("0 200 rise 100\n"
-                                      "5 15 family Times\n"
+                                      "5 15 family \"Times\"\n"
                                       "10 11 size 10240\n"
                                       "11 100 fallback 0\n"
                                       "30 60 stretch 2\n");
@@ -993,7 +1037,7 @@ test_list_update (void)
   pango_attr_list_update (list, 8, 10, 20);
 
   assert_attr_list (list, "0 210 rise 100\n"
-                          "5 8 family Times\n"
+                          "5 8 family \"Times\"\n"
                           "28 110 fallback false\n"
                           "40 70 stretch condensed\n");
 
@@ -1020,11 +1064,11 @@ test_list_update3 (void)
 {
   PangoAttrList *list;
 
-  list = pango_attr_list_from_string ("5 4294967285 family Times\n");
+  list = pango_attr_list_from_string ("5 4294967285 family \"Times\"\n");
 
   pango_attr_list_update (list, 8, 10, 30);
 
-  assert_attr_list (list, "5 -1 family Times\n");
+  assert_attr_list (list, "5 -1 family \"Times\"\n");
 
   pango_attr_list_unref (list);
 }
@@ -1105,28 +1149,28 @@ test_insert (void)
   PangoAttribute *attr;
 
   list = pango_attr_list_from_string ("0 200 rise 100\n"
-                                      "5 15 family Times\n"
+                                      "5 15 family \"Times\"\n"
                                       "10 11 size 10240\n"
                                       "11 100 fallback 0\n"
                                       "30 60 stretch 2\n");
 
-  attr = attribute_from_string ("10 25 family Times");
+  attr = attribute_from_string ("10 25 family \"Times\"");
   pango_attr_list_change (list, attr);
 
   assert_attr_list (list, "0 200 rise 100\n"
-                          "5 25 family Times\n"
+                          "5 25 family \"Times\"\n"
                           "10 11 size 10240\n"
                           "11 100 fallback false\n"
                           "30 60 stretch condensed\n");
 
-  attr = attribute_from_string ("11 25 family Futura");
+  attr = attribute_from_string ("11 25 family \"Futura\"");
   pango_attr_list_insert (list, attr);
 
   assert_attr_list (list, "0 200 rise 100\n"
-                          "5 25 family Times\n"
+                          "5 25 family \"Times\"\n"
                           "10 11 size 10240\n"
                           "11 100 fallback false\n"
-                          "11 25 family Futura\n"
+                          "11 25 family \"Futura\"\n"
                           "30 60 stretch condensed\n");
 
   pango_attr_list_unref (list);
@@ -1139,21 +1183,21 @@ test_insert2 (void)
   PangoAttribute *attr;
 
   list = pango_attr_list_from_string ("0 200 rise 100\n"
-                                      "5 15 family Times\n"
+                                      "5 15 family \"Times\"\n"
                                       "10 11 size 10240\n"
                                       "11 100 fallback 0\n"
-                                      "20 30 family Times\n"
-                                      "30 40 family Futura\n"
+                                      "20 30 family \"Times\"\n"
+                                      "30 40 family \"Futura\"\n"
                                       "30 60 stretch 2\n");
 
-  attr = attribute_from_string ("10 35 family Times");
+  attr = attribute_from_string ("10 35 family \"Times\"");
   pango_attr_list_change (list, attr);
 
   assert_attr_list (list, "0 200 rise 100\n"
-                          "5 35 family Times\n"
+                          "5 35 family \"Times\"\n"
                           "10 11 size 10240\n"
                           "11 100 fallback false\n"
-                          "35 40 family Futura\n"
+                          "35 40 family \"Futura\"\n"
                           "30 60 stretch condensed\n");
 
   pango_attr_list_unref (list);
@@ -1175,7 +1219,7 @@ test_merge (void)
   PangoAttrList *list2;
 
   list = pango_attr_list_from_string ("0 200 rise 100\n"
-                                      "5 15 family Times\n"
+                                      "5 15 family \"Times\"\n"
                                       "10 11 size 10240\n"
                                       "11 100 fallback 0\n"
                                       "30 60 stretch 2\n");
@@ -1187,7 +1231,7 @@ test_merge (void)
   pango_attr_list_filter (list2, attr_list_merge_filter, list);
 
   assert_attr_list (list, "0 200 rise 100\n"
-                          "5 15 family Times\n"
+                          "5 15 family \"Times\"\n"
                           "10 13 size 10240\n"
                           "11 100 fallback false\n"
                           "13 15 size 11264\n"
@@ -1253,25 +1297,25 @@ print_tags_for_attributes (PangoAttrIterator *iter,
   if (attr)
     g_string_append_printf (s, "%d  %d rise %d\n",
                             attr->start_index, attr->end_index,
-                            ((PangoAttrInt*)attr)->value);
+                            attr->int_value);
 
   attr = pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
   if (attr)
     g_string_append_printf (s, "%d  %d size %d\n",
                             attr->start_index, attr->end_index,
-                            ((PangoAttrInt*)attr)->value);
+                            attr->int_value);
 
   attr = pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
   if (attr)
     g_string_append_printf (s, "%d  %d scale %f\n",
                             attr->start_index, attr->end_index,
-                            ((PangoAttrFloat*)attr)->value);
+                            attr->double_value);
 
   attr = pango_attr_iterator_get (iter, PANGO_ATTR_ALLOW_BREAKS);
   if (attr)
     g_string_append_printf (s, "%d  %d allow_breaks %d\n",
                             attr->start_index, attr->end_index,
-                            ((PangoAttrInt*)attr)->value);
+                            attr->int_value);
 }
 
 static void
diff --git a/tests/testserialize.c b/tests/testserialize.c
index e480da31..f8a7bca3 100644
--- a/tests/testserialize.c
+++ b/tests/testserialize.c
@@ -36,7 +36,7 @@ test_serialize_attr_list (void)
     "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",
+    "0 200 rise 100\n5 15 family \"Times\"\n10 11 size 10240\n11 100 fallback 0\n30 60 stretch 2\n",
     ""
   };
   const char *roundtripped[] = {
@@ -47,7 +47,7 @@ test_serialize_attr_list (void)
     "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",
+    "0 200 rise 100\n5 15 family \"Times\"\n10 11 size 10240\n11 100 fallback false\n30 60 stretch 
condensed",
     ""
   };
   const char *invalid[] = {
@@ -366,7 +366,7 @@ test_serialize_layout_invalid (void)
       "    }\n"
       "  ]\n"
       "}\n",
-      PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE
+      PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE
     },
     {
       "{\n"
@@ -382,7 +382,7 @@ test_serialize_layout_invalid (void)
       "{\n"
       "  \"attributes\" : [\n"
       "    {\n"
-      "      \"type\" : \"alignment\",\n"
+      "      \"type\" : \"background\",\n"
       "      \"value\" : \"nonsense\"\n"
       "    }\n"
       "  ]\n"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]