[pango/pango2: 25/118] Redo attributes




commit 0c0a2ddd16e050bae972ee3ea00087c621c2a578
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Jun 4 14:55:28 2022 -0400

    Redo attributes
    
    Do away with PangoAttribute subclassing,
    and turn the PangoAttribute struct into
    a union. Redo custom attribute registration
    to be more useful. Introduce PangoAttrAffects
    and PangoAttrMerge flags that describe what part
    of the pipeline an attribute affects, and how
    it gets merged when ranges overlap.
    
    Split pango-attributes.[hc] into separate files for
    PangoAttribute, PangoAttrList, PangoAttrIterator, and
    the predefined attribute types.

 pango/break.c                       |   48 +-
 pango/ellipsize.c                   |    8 +-
 pango/itemize.c                     |   20 +-
 pango/meson.build                   |    6 +
 pango/pango-attr-iterator-private.h |   39 +
 pango/pango-attr-iterator.c         |  511 ++++++
 pango/pango-attr-iterator.h         |   66 +
 pango/pango-attr-list-private.h     |   36 +
 pango/pango-attr-list.c             | 1301 ++++++++++++++++
 pango/pango-attr-list.h             |  112 ++
 pango/pango-attr-private.h          |   24 +
 pango/pango-attr.c                  |  603 ++++++++
 pango/pango-attr.h                  |  286 ++++
 pango/pango-attributes-private.h    |   55 +-
 pango/pango-attributes.c            | 2904 ++---------------------------------
 pango/pango-attributes.h            |  734 +++------
 pango/pango-glyph-item.c            |    7 +-
 pango/pango-item.c                  |   25 +-
 pango/pango-item.h                  |    3 +-
 pango/pango-line-breaker.c          |   24 +-
 pango/pango-line.c                  |   10 +-
 pango/pango-markup.h                |    2 +-
 pango/pango-renderer.c              |   22 +-
 pango/pango.h                       |    3 +
 pango/serializer.c                  |  247 +--
 pango/shape.c                       |    8 +-
 tests/markups/valid-19.expected     |    2 +-
 tests/markups/valid-4.expected      |    4 +-
 tests/test-itemize.c                |    2 +-
 tests/testattributes.c              |  183 ++-
 tests/testserialize.c               |    8 +-
 31 files changed, 3653 insertions(+), 3650 deletions(-)
---
diff --git a/pango/break.c b/pango/break.c
index 58e3702c0..dbfc88fc7 100644
--- a/pango/break.c
+++ b/pango/break.c
@@ -24,7 +24,9 @@
 #include "pango-break.h"
 #include "pango-script-private.h"
 #include "pango-emoji-private.h"
-#include "pango-attributes-private.h"
+#include "pango-attributes.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-iterator-private.h"
 #include "pango-break-table.h"
 #include "pango-impl-utils.h"
 #include <string.h>
@@ -1786,7 +1788,7 @@ handle_allow_breaks (const char    *text,
   PangoAttrIterator iter;
   gboolean tailored = FALSE;
 
-  _pango_attr_list_get_iterator (attrs, &iter);
+  pango_attr_list_init_iterator (attrs, &iter);
 
   do
     {
@@ -1795,7 +1797,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;
@@ -1822,7 +1824,7 @@ handle_allow_breaks (const char    *text,
     }
   while (pango_attr_iterator_next (&iter));
 
-  _pango_attr_iterator_destroy (&iter);
+  pango_attr_iterator_clear (&iter);
 
   return tailored;
 }
@@ -1839,7 +1841,7 @@ handle_words (const char    *text,
   PangoAttrIterator iter;
   gboolean tailored = FALSE;
 
-  _pango_attr_list_get_iterator (attrs, &iter);
+  pango_attr_list_init_iterator (attrs, &iter);
 
   do
     {
@@ -1928,7 +1930,7 @@ handle_words (const char    *text,
     }
   while (pango_attr_iterator_next (&iter));
 
-  _pango_attr_iterator_destroy (&iter);
+  pango_attr_iterator_clear (&iter);
 
   return tailored;
 }
@@ -1944,7 +1946,7 @@ handle_sentences (const char    *text,
   PangoAttrIterator iter;
   gboolean tailored = FALSE;
 
-  _pango_attr_list_get_iterator (attrs, &iter);
+  pango_attr_list_init_iterator (attrs, &iter);
 
   do
     {
@@ -2016,7 +2018,7 @@ handle_sentences (const char    *text,
     }
   while (pango_attr_iterator_next (&iter));
 
-  _pango_attr_iterator_destroy (&iter);
+  pango_attr_iterator_clear (&iter);
 
   return tailored;
 }
@@ -2032,12 +2034,12 @@ handle_hyphens (const char    *text,
   PangoAttrIterator iter;
   gboolean tailored = FALSE;
 
-  _pango_attr_list_get_iterator (attrs, &iter);
+  pango_attr_list_init_iterator (attrs, &iter);
 
   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;
@@ -2065,7 +2067,7 @@ handle_hyphens (const char    *text,
       }
   } while (pango_attr_iterator_next (&iter));
 
-  _pango_attr_iterator_destroy (&iter);
+  pango_attr_iterator_clear (&iter);
 
   return tailored;
 }
@@ -2085,22 +2087,22 @@ break_attrs (const char   *text,
   GSList *l;
   gboolean tailored = FALSE;
 
-  _pango_attr_list_init (&allow_breaks);
-  _pango_attr_list_init (&words);
-  _pango_attr_list_init (&sentences);
-  _pango_attr_list_init (&hyphens);
+  pango_attr_list_init (&allow_breaks);
+  pango_attr_list_init (&words);
+  pango_attr_list_init (&sentences);
+  pango_attr_list_init (&hyphens);
 
   for (l = attributes; l; l = l->next)
     {
       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));
     }
 
@@ -2116,10 +2118,10 @@ break_attrs (const char   *text,
   tailored |= handle_allow_breaks (text, length, &allow_breaks, offset,
                                    log_attrs, log_attrs_len);
 
-  _pango_attr_list_destroy (&allow_breaks);
-  _pango_attr_list_destroy (&words);
-  _pango_attr_list_destroy (&sentences);
-  _pango_attr_list_destroy (&hyphens);
+  pango_attr_list_destroy (&allow_breaks);
+  pango_attr_list_destroy (&words);
+  pango_attr_list_destroy (&sentences);
+  pango_attr_list_destroy (&hyphens);
 
   return tailored;
 }
diff --git a/pango/ellipsize.c b/pango/ellipsize.c
index 509a515cb..9f68c6e93 100644
--- a/pango/ellipsize.c
+++ b/pango/ellipsize.c
@@ -25,6 +25,8 @@
 #include "pango-glyph-item.h"
 #include "pango-font-private.h"
 #include "pango-attributes-private.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-iterator-private.h"
 #include "pango-impl-utils.h"
 #include "pango-line-private.h"
 
@@ -321,7 +323,7 @@ shape_ellipsis (EllipsizeState *state)
   int len;
   int i;
 
-  _pango_attr_list_init (&attrs);
+  pango_attr_list_init (&attrs);
 
   /* Create/reset state->ellipsis_run
    */
@@ -381,13 +383,13 @@ 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);
     }
 
-  _pango_attr_list_destroy (&attrs);
+  pango_attr_list_destroy (&attrs);
 
   state->ellipsis_run->item = item;
 
diff --git a/pango/itemize.c b/pango/itemize.c
index 619cc6583..f2510537a 100644
--- a/pango/itemize.c
+++ b/pango/itemize.c
@@ -31,7 +31,7 @@
 #include "pango-fontmap-private.h"
 #include "pango-script-private.h"
 #include "pango-emoji-private.h"
-#include "pango-attributes-private.h"
+#include "pango-attr-iterator-private.h"
 #include "pango-item-private.h"
 
 #include <hb-ot.h>
@@ -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)
@@ -1053,7 +1053,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)
             {
@@ -1067,7 +1067,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;
@@ -1128,7 +1128,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;
      }
@@ -1315,8 +1315,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/meson.build b/pango/meson.build
index fefc32412..7d9ef3460 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -3,6 +3,9 @@ pango_sources = [
   'ellipsize.c',
   'glyphstring.c',
   'itemize.c',
+  'pango-attr.c',
+  'pango-attr-list.c',
+  'pango-attr-iterator.c',
   'pango-attributes.c',
   'pango-bidi-type.c',
   'pango-color.c',
@@ -52,6 +55,9 @@ pango_sources = [
 
 pango_headers = [
   'pango.h',
+  'pango-attr.h',
+  'pango-attr-list.h',
+  'pango-attr-iterator.h',
   'pango-attributes.h',
   'pango-break.h',
   'pango-color.h',
diff --git a/pango/pango-attr-iterator-private.h b/pango/pango-attr-iterator-private.h
new file mode 100644
index 000000000..03229fdbc
--- /dev/null
+++ b/pango/pango-attr-iterator-private.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-attr-iterator.h>
+
+
+struct _PangoAttrIterator
+{
+  GPtrArray *attrs; /* From the list */
+  guint n_attrs; /* Copied from the list */
+
+  GPtrArray *attribute_stack;
+
+  guint attr_index;
+  guint start_index;
+  guint end_index;
+};
+
+void     pango_attr_iterator_clear    (PangoAttrIterator *iterator);
+gboolean pango_attr_iterator_advance  (PangoAttrIterator *iterator,
+                                       int                index);
diff --git a/pango/pango-attr-iterator.c b/pango/pango-attr-iterator.c
new file mode 100644
index 000000000..027654253
--- /dev/null
+++ b/pango/pango-attr-iterator.c
@@ -0,0 +1,511 @@
+/* Pango
+ * pango-attr-iterator.c: Attribute iterator
+ *
+ * Copyright (C) 2000-2002 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "pango-attr-iterator-private.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-private.h"
+#include "pango-impl-utils.h"
+
+
+G_DEFINE_BOXED_TYPE (PangoAttrIterator,
+                     pango_attr_iterator,
+                     pango_attr_iterator_copy,
+                     pango_attr_iterator_destroy)
+
+/* {{{ Private API */
+
+void
+pango_attr_list_init_iterator (PangoAttrList     *list,
+                               PangoAttrIterator *iterator)
+{
+  iterator->attribute_stack = NULL;
+  iterator->attrs = list->attributes;
+  iterator->n_attrs = iterator->attrs ? iterator->attrs->len : 0;
+
+  iterator->attr_index = 0;
+  iterator->start_index = 0;
+  iterator->end_index = 0;
+
+  if (!pango_attr_iterator_next (iterator))
+    iterator->end_index = G_MAXUINT;
+}
+
+void
+pango_attr_iterator_clear (PangoAttrIterator *iterator)
+{
+  if (iterator->attribute_stack)
+    g_ptr_array_free (iterator->attribute_stack, TRUE);
+}
+
+gboolean
+pango_attr_iterator_advance (PangoAttrIterator *iterator,
+                             int                index)
+{
+  int start_range, end_range;
+
+  pango_attr_iterator_range (iterator, &start_range, &end_range);
+
+  while (index >= end_range)
+    {
+      if (!pango_attr_iterator_next (iterator))
+        return FALSE;
+      pango_attr_iterator_range (iterator, &start_range, &end_range);
+    }
+
+  if (start_range > index)
+    g_warning ("pango_attr_iterator_advance(): iterator had already "
+               "moved beyond the index");
+
+  return TRUE;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_attr_list_get_iterator:
+ * @list: a `PangoAttrList`
+ *
+ * Create a iterator initialized to the beginning of the list.
+ *
+ * @list must not be modified until this iterator is freed.
+ *
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttrIterator`, which should be freed with
+ *   [method@Pango.AttrIterator.destroy]
+ */
+PangoAttrIterator *
+pango_attr_list_get_iterator (PangoAttrList  *list)
+{
+  PangoAttrIterator *iterator;
+
+  g_return_val_if_fail (list != NULL, NULL);
+
+  iterator = g_slice_new (PangoAttrIterator);
+  pango_attr_list_init_iterator (list, iterator);
+
+  return iterator;
+}
+
+/**
+ * pango_attr_iterator_destroy:
+ * @iterator: a `PangoAttrIterator`
+ *
+ * Destroy a `PangoAttrIterator` and free all associated memory.
+ */
+void
+pango_attr_iterator_destroy (PangoAttrIterator *iterator)
+{
+  g_return_if_fail (iterator != NULL);
+
+  pango_attr_iterator_clear (iterator);
+  g_slice_free (PangoAttrIterator, iterator);
+}
+
+/**
+ * pango_attr_iterator_copy:
+ * @iterator: a `PangoAttrIterator`
+ *
+ * Copy a `PangoAttrIterator`.
+ *
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttrIterator`, which should be freed with
+ *   [method@Pango.AttrIterator.destroy]
+ */
+PangoAttrIterator *
+pango_attr_iterator_copy (PangoAttrIterator *iterator)
+{
+  PangoAttrIterator *copy;
+
+  g_return_val_if_fail (iterator != NULL, NULL);
+
+  copy = g_slice_new (PangoAttrIterator);
+
+  *copy = *iterator;
+
+  if (iterator->attribute_stack)
+    copy->attribute_stack = g_ptr_array_copy (iterator->attribute_stack, NULL, NULL);
+  else
+    copy->attribute_stack = NULL;
+
+  return copy;
+}
+
+/**
+ * pango_attr_iterator_range:
+ * @iterator: a PangoAttrIterator
+ * @start: (out): location to store the start of the range
+ * @end: (out): location to store the end of the range
+ *
+ * Get the range of the current segment.
+ *
+ * Note that the stored return values are signed, not unsigned
+ * like the values in `PangoAttribute`. To deal with this API
+ * oversight, stored return values that wouldn't fit into
+ * a signed integer are clamped to %G_MAXINT.
+ */
+void
+pango_attr_iterator_range (PangoAttrIterator *iterator,
+                           gint              *start,
+                           gint              *end)
+{
+  g_return_if_fail (iterator != NULL);
+
+  if (start)
+    *start = MIN (iterator->start_index, G_MAXINT);
+  if (end)
+    *end = MIN (iterator->end_index, G_MAXINT);
+}
+
+/**
+ * pango_attr_iterator_next:
+ * @iterator: a `PangoAttrIterator`
+ *
+ * Advance the iterator until the next change of style.
+ *
+ * Return value: %FALSE if the iterator is at the end
+ *   of the list, otherwise %TRUE
+ */
+gboolean
+pango_attr_iterator_next (PangoAttrIterator *iterator)
+{
+  int i;
+
+  g_return_val_if_fail (iterator != NULL, FALSE);
+
+  if (iterator->attr_index >= iterator->n_attrs &&
+      (!iterator->attribute_stack || iterator->attribute_stack->len == 0))
+    return FALSE;
+
+  iterator->start_index = iterator->end_index;
+  iterator->end_index = G_MAXUINT;
+
+  if (iterator->attribute_stack)
+    {
+      for (i = iterator->attribute_stack->len - 1; i>= 0; i--)
+        {
+          const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+
+          if (attr->end_index == iterator->start_index)
+            g_ptr_array_remove_index (iterator->attribute_stack, i); /* Can't use index_fast :( */
+          else
+            iterator->end_index = MIN (iterator->end_index, attr->end_index);
+        }
+    }
+
+  while (1)
+    {
+      PangoAttribute *attr;
+
+      if (iterator->attr_index >= iterator->n_attrs)
+        break;
+
+      attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
+
+      if (attr->start_index != iterator->start_index)
+        break;
+
+      if (attr->end_index > iterator->start_index)
+        {
+          if (G_UNLIKELY (!iterator->attribute_stack))
+            iterator->attribute_stack = g_ptr_array_new ();
+
+          g_ptr_array_add (iterator->attribute_stack, attr);
+
+          iterator->end_index = MIN (iterator->end_index, attr->end_index);
+        }
+
+      iterator->attr_index++; /* NEXT! */
+    }
+
+  if (iterator->attr_index < iterator->n_attrs)
+      {
+      PangoAttribute *attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
+
+      iterator->end_index = MIN (iterator->end_index, attr->start_index);
+    }
+
+  return TRUE;
+}
+
+/**
+ * pango_attr_iterator_get:
+ * @iterator: a `PangoAttrIterator`
+ * @type: the type of attribute to find
+ *
+ * Find the current attribute of a particular type
+ * at the iterator location.
+ *
+ * When multiple attributes of the same type overlap,
+ * the attribute whose range starts closest to the
+ * current location is used.
+ *
+ * Return value: (nullable) (transfer none): the current
+ *   attribute of the given type, or %NULL if no attribute
+ *   of that type applies to the current location.
+ */
+PangoAttribute *
+pango_attr_iterator_get (PangoAttrIterator *iterator,
+                         guint              type)
+{
+  int i;
+
+  g_return_val_if_fail (iterator != NULL, NULL);
+
+  if (!iterator->attribute_stack)
+    return NULL;
+
+  for (i = iterator->attribute_stack->len - 1; i>= 0; i--)
+    {
+      PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+
+      if (attr->type == type)
+        return attr;
+    }
+
+  return NULL;
+}
+
+/**
+ * pango_attr_iterator_get_font:
+ * @iterator: a `PangoAttrIterator`
+ * @desc: (out caller-allocates): a `PangoFontDescription` to fill in with the current
+ *   values. The family name in this structure will be set using
+ *   [method@Pango.FontDescription.set_family_static] using
+ *   values from an attribute in the `PangoAttrList` associated
+ *   with the iterator, so if you plan to keep it around, you
+ *   must call:
+ *   `pango_font_description_set_family (desc, pango_font_description_get_family (desc))`.
+ * @language: (out) (optional): location to store language tag
+ *   for item, or %NULL if none is found.
+ * @extra_attrs: (out) (optional) (element-type Pango.Attribute) (transfer full):
+ *   location in which to store a list of non-font attributes
+ *   at the the current position; only the highest priority
+ *   value of each attribute will be added to this list. In
+ *   order to free this value, you must call
+ *   [method@Pango.Attribute.destroy] on each member.
+ *
+ * Get the font and other attributes at the current
+ * iterator position.
+ */
+void
+pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
+                              PangoFontDescription  *desc,
+                              PangoLanguage        **language,
+                              GSList               **extra_attrs)
+{
+  PangoFontMask mask = 0;
+  gboolean have_language = FALSE;
+  gdouble scale = 0;
+  gboolean have_scale = FALSE;
+  int i;
+
+  g_return_if_fail (iterator != NULL);
+  g_return_if_fail (desc != NULL);
+
+  if (language)
+    *language = NULL;
+
+  if (extra_attrs)
+    *extra_attrs = NULL;
+
+  if (!iterator->attribute_stack)
+    return;
+
+  for (i = iterator->attribute_stack->len - 1; i >= 0; i--)
+    {
+      const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+
+      switch ((int) attr->type)
+        {
+        case PANGO_ATTR_FONT_DESC:
+          {
+            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, attr->font_value, FALSE);
+
+            break;
+          }
+        case PANGO_ATTR_FAMILY:
+          if (!(mask & PANGO_FONT_MASK_FAMILY))
+            {
+              mask |= PANGO_FONT_MASK_FAMILY;
+              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, 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, 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, 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, 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, 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, attr->int_value);
+            }
+          break;
+        case PANGO_ATTR_SCALE:
+          if (!have_scale)
+            {
+              have_scale = TRUE;
+              scale = attr->double_value;
+            }
+          break;
+        case PANGO_ATTR_LANGUAGE:
+          if (language)
+            {
+              if (!have_language)
+                {
+                  have_language = TRUE;
+                  *language = attr->lang_value;
+                }
+            }
+          break;
+        default:
+          if (extra_attrs)
+            {
+              gboolean found = FALSE;
+
+              if (PANGO_ATTR_MERGE (attr) == PANGO_ATTR_MERGE_OVERRIDES)
+                {
+                  GSList *tmp_list = *extra_attrs;
+                  while (tmp_list)
+                    {
+                      PangoAttribute *old_attr = tmp_list->data;
+                      if (attr->type == old_attr->type)
+                        {
+                          found = TRUE;
+                          break;
+                        }
+
+                      tmp_list = tmp_list->next;
+                    }
+                }
+
+              if (!found)
+                *extra_attrs = g_slist_prepend (*extra_attrs, pango_attribute_copy (attr));
+            }
+        }
+    }
+
+  if (have_scale)
+    {
+      /* We need to use a local variable to ensure that the compiler won't
+       * implicitly cast it to integer while the result is kept in registers,
+       * leading to a wrong approximation in i386 (with 387 FPU)
+       */
+      volatile double size = scale * pango_font_description_get_size (desc);
+
+      if (pango_font_description_get_size_is_absolute (desc))
+        pango_font_description_set_absolute_size (desc, size);
+      else
+        pango_font_description_set_size (desc, size);
+    }
+}
+
+/**
+ * pango_attr_iterator_get_attrs:
+ * @iterator: a `PangoAttrIterator`
+ *
+ * Gets a list of all attributes at the current position of the
+ * iterator.
+ *
+ * Return value: (element-type Pango.Attribute) (transfer full):
+ *   a list of all attributes for the current range. To free
+ *   this value, call [method@Pango.Attribute.destroy] on each
+ *   value and g_slist_free() on the list.
+ *
+ * Since: 1.2
+ */
+GSList *
+pango_attr_iterator_get_attrs (PangoAttrIterator *iterator)
+{
+  GSList *attrs = NULL;
+  int i;
+
+  if (!iterator->attribute_stack ||
+      iterator->attribute_stack->len == 0)
+    return NULL;
+
+  for (i = iterator->attribute_stack->len - 1; i >= 0; i--)
+    {
+      PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+      GSList *tmp_list2;
+      gboolean found = FALSE;
+
+      if (PANGO_ATTR_MERGE (attr) == PANGO_ATTR_MERGE_OVERRIDES)
+        {
+          for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next)
+            {
+              PangoAttribute *old_attr = tmp_list2->data;
+              if (attr->type == old_attr->type)
+                {
+                  found = TRUE;
+                  break;
+                }
+            }
+        }
+
+      if (!found)
+        attrs = g_slist_prepend (attrs, pango_attribute_copy (attr));
+    }
+
+  return attrs;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-attr-iterator.h b/pango/pango-attr-iterator.h
new file mode 100644
index 000000000..1ff1e7b2e
--- /dev/null
+++ b/pango/pango-attr-iterator.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-attr-list.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+
+/**
+ * PangoAttrIterator:
+ *
+ * A `PangoAttrIterator` is used to iterate through a `PangoAttrList`.
+ *
+ * A new iterator is created with [method@Pango.AttrList.get_iterator].
+ * Once the iterator is created, it can be advanced through the style
+ * changes in the text using [method Pango AttrIterator next]. At each
+ * style change, the range of the current style segment and the attributes
+ * currently in effect can be queried.
+ */
+
+PANGO_AVAILABLE_IN_1_44
+GType                   pango_attr_iterator_get_type    (void) G_GNUC_CONST;
+
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_iterator_range       (PangoAttrIterator     *iterator,
+                                                         int                   *start,
+                                                         int                   *end);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attr_iterator_next        (PangoAttrIterator     *iterator);
+PANGO_AVAILABLE_IN_ALL
+PangoAttrIterator *     pango_attr_iterator_copy        (PangoAttrIterator     *iterator);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_iterator_destroy     (PangoAttrIterator     *iterator);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_iterator_get         (PangoAttrIterator     *iterator,
+                                                         guint                  type);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_iterator_get_font    (PangoAttrIterator     *iterator,
+                                                         PangoFontDescription  *desc,
+                                                         PangoLanguage        **language,
+                                                         GSList               **extra_attrs);
+PANGO_AVAILABLE_IN_1_2
+GSList *                pango_attr_iterator_get_attrs   (PangoAttrIterator     *iterator);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoAttrIterator, pango_attr_iterator_destroy)
+
+G_END_DECLS
diff --git a/pango/pango-attr-list-private.h b/pango/pango-attr-list-private.h
new file mode 100644
index 000000000..5f93b0dfa
--- /dev/null
+++ b/pango/pango-attr-list-private.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 RedHat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-attr-list.h>
+
+
+struct _PangoAttrList
+{
+  guint ref_count;
+  GPtrArray *attributes;
+};
+
+void     pango_attr_list_init           (PangoAttrList       *list);
+void     pango_attr_list_destroy        (PangoAttrList       *list);
+gboolean pango_attr_list_has_attributes (const PangoAttrList *list);
+
+void     pango_attr_list_init_iterator  (PangoAttrList       *list,
+                                         PangoAttrIterator   *iterator);
diff --git a/pango/pango-attr-list.c b/pango/pango-attr-list.c
new file mode 100644
index 000000000..d5e7eeb15
--- /dev/null
+++ b/pango/pango-attr-list.c
@@ -0,0 +1,1301 @@
+/* Pango
+ * pango-attr-list.c: Attribute lists
+ *
+ * Copyright (C) 2000-2002 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "pango-attr-list-private.h"
+#include "pango-attr-private.h"
+#include "pango-impl-utils.h"
+
+
+G_DEFINE_BOXED_TYPE (PangoAttrList, pango_attr_list,
+                     pango_attr_list_copy,
+                     pango_attr_list_unref);
+
+/* {{{ Utilities */
+
+static void
+pango_attr_list_insert_internal (PangoAttrList  *list,
+                                 PangoAttribute *attr,
+                                 gboolean        before)
+{
+  const guint start_index = attr->start_index;
+  PangoAttribute *last_attr;
+
+  if (G_UNLIKELY (!list->attributes))
+    list->attributes = g_ptr_array_new ();
+
+  if (list->attributes->len == 0)
+    {
+      g_ptr_array_add (list->attributes, attr);
+      return;
+    }
+
+  g_assert (list->attributes->len > 0);
+
+  last_attr = g_ptr_array_index (list->attributes, list->attributes->len - 1);
+
+  if (last_attr->start_index < start_index ||
+      (!before && last_attr->start_index == start_index))
+    {
+      g_ptr_array_add (list->attributes, attr);
+    }
+  else
+    {
+      guint i, p;
+
+      for (i = 0, p = list->attributes->len; i < p; i++)
+        {
+          PangoAttribute *cur = g_ptr_array_index (list->attributes, i);
+
+          if (cur->start_index > start_index ||
+              (before && cur->start_index == start_index))
+            {
+              g_ptr_array_insert (list->attributes, i, attr);
+              break;
+            }
+        }
+    }
+}
+
+/* }}} */
+/* {{{ Private API */
+
+void
+pango_attr_list_init (PangoAttrList *list)
+{
+  list->ref_count = 1;
+  list->attributes = NULL;
+}
+
+void
+pango_attr_list_destroy (PangoAttrList *list)
+{
+  if (!list->attributes)
+    return;
+
+  g_ptr_array_free (list->attributes, TRUE);
+}
+
+gboolean
+pango_attr_list_has_attributes (const PangoAttrList *list)
+{
+  return list && list->attributes != NULL && list->attributes->len > 0;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_attr_list_new:
+ *
+ * Create a new empty attribute list with a reference
+ * count of one.
+ *
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttrList`, which should be freed with
+ *   [method@Pango.AttrList.unref]
+ */
+PangoAttrList *
+pango_attr_list_new (void)
+{
+  PangoAttrList *list = g_slice_new (PangoAttrList);
+
+  pango_attr_list_init (list);
+
+  return list;
+}
+
+/**
+ * pango_attr_list_ref:
+ * @list: (nullable): a `PangoAttrList`
+ *
+ * Increase the reference count of the given attribute
+ * list by one.
+ *
+ * Return value: The attribute list passed in
+ *
+ * Since: 1.10
+ */
+PangoAttrList *
+pango_attr_list_ref (PangoAttrList *list)
+{
+  if (list == NULL)
+    return NULL;
+
+  g_atomic_int_inc ((int *) &list->ref_count);
+
+  return list;
+}
+
+/**
+ * pango_attr_list_unref:
+ * @list: (nullable): a `PangoAttrList`
+ *
+ * Decrease the reference count of the given attribute
+ * list by one.
+ *
+ * If the result is zero, free the attribute list
+ * and the attributes it contains.
+ */
+void
+pango_attr_list_unref (PangoAttrList *list)
+{
+  if (list == NULL)
+    return;
+
+  g_return_if_fail (list->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test ((int *) &list->ref_count))
+    {
+      pango_attr_list_destroy (list);
+      g_slice_free (PangoAttrList, list);
+    }
+}
+
+/**
+ * pango_attr_list_copy:
+ * @list: (nullable): a `PangoAttrList`
+ *
+ * Copy @list and return an identical new list.
+ *
+ * Return value: (nullable): the newly allocated
+ *   `PangoAttrList`, with a reference count of one,
+ *   which should be freed with [method@Pango.AttrList.unref].
+ *   Returns %NULL if @list was %NULL.
+ */
+PangoAttrList *
+pango_attr_list_copy (PangoAttrList *list)
+{
+  PangoAttrList *new;
+
+  if (list == NULL)
+    return NULL;
+
+  new = pango_attr_list_new ();
+  if (!list->attributes || list->attributes->len == 0)
+    return new;
+
+  new->attributes = g_ptr_array_copy (list->attributes, (GCopyFunc)pango_attribute_copy, NULL);
+
+  return new;
+}
+
+/**
+ * pango_attr_list_insert:
+ * @list: a `PangoAttrList`
+ * @attr: (transfer full): the attribute to insert
+ *
+ * Insert the given attribute into the `PangoAttrList`.
+ *
+ * It will be inserted after all other attributes with a
+ * matching @start_index.
+ */
+void
+pango_attr_list_insert (PangoAttrList  *list,
+                        PangoAttribute *attr)
+{
+  g_return_if_fail (list != NULL);
+  g_return_if_fail (attr != NULL);
+
+  pango_attr_list_insert_internal (list, attr, FALSE);
+}
+
+/**
+ * pango_attr_list_insert_before:
+ * @list: a `PangoAttrList`
+ * @attr: (transfer full): the attribute to insert
+ *
+ * Insert the given attribute into the `PangoAttrList`.
+ *
+ * It will be inserted before all other attributes with a
+ * matching @start_index.
+ */
+void
+pango_attr_list_insert_before (PangoAttrList  *list,
+                               PangoAttribute *attr)
+{
+  g_return_if_fail (list != NULL);
+  g_return_if_fail (attr != NULL);
+
+  pango_attr_list_insert_internal (list, attr, TRUE);
+}
+
+/**
+ * pango_attr_list_change:
+ * @list: a `PangoAttrList`
+ * @attr: (transfer full): the attribute to insert
+ *
+ * Insert the given attribute into the `PangoAttrList`.
+ *
+ * It will replace any attributes of the same type
+ * on that segment and be merged with any adjoining
+ * attributes that are identical.
+ *
+ * This function is slower than [method@Pango.AttrList.insert]
+ * for creating an attribute list in order (potentially
+ * much slower for large lists). However,
+ * [method@Pango.AttrList.insert] is not suitable for
+ * continually changing a set of attributes since it
+ * never removes or combines existing attributes.
+ */
+void
+pango_attr_list_change (PangoAttrList  *list,
+                        PangoAttribute *attr)
+{
+  guint i, p;
+  guint start_index = attr->start_index;
+  guint end_index = attr->end_index;
+  gboolean inserted;
+
+  g_return_if_fail (list != NULL);
+
+  if (start_index == end_index) /* empty, nothing to do */
+    {
+      pango_attribute_destroy (attr);
+      return;
+    }
+
+  if (!list->attributes || list->attributes->len == 0)
+    {
+      pango_attr_list_insert (list, attr);
+      return;
+    }
+
+  inserted = FALSE;
+  for (i = 0, p = list->attributes->len; i < p; i++)
+    {
+      PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
+
+      if (tmp_attr->start_index > start_index)
+        {
+          g_ptr_array_insert (list->attributes, i, attr);
+          inserted = TRUE;
+          break;
+        }
+
+      if (tmp_attr->type != attr->type)
+        continue;
+
+      if (tmp_attr->end_index < start_index)
+        continue; /* This attr does not overlap with the new one */
+
+      g_assert (tmp_attr->start_index <= start_index);
+      g_assert (tmp_attr->end_index >= start_index);
+
+      if (pango_attribute_equal (tmp_attr, attr))
+        {
+          /* We can merge the new attribute with this attribute
+           */
+          if (tmp_attr->end_index >= end_index)
+            {
+              /* We are totally overlapping the previous attribute.
+               * No action is needed.
+               */
+              pango_attribute_destroy (attr);
+              return;
+            }
+
+          tmp_attr->end_index = end_index;
+          pango_attribute_destroy (attr);
+
+          attr = tmp_attr;
+          inserted = TRUE;
+          break;
+        }
+      else
+        {
+          /* Split, truncate, or remove the old attribute
+           */
+          if (tmp_attr->end_index > end_index)
+            {
+              PangoAttribute *end_attr = pango_attribute_copy (tmp_attr);
+
+              end_attr->start_index = end_index;
+              pango_attr_list_insert (list, end_attr);
+            }
+
+          if (tmp_attr->start_index == start_index)
+            {
+              pango_attribute_destroy (tmp_attr);
+              g_ptr_array_remove_index (list->attributes, i);
+              break;
+            }
+          else
+            {
+              tmp_attr->end_index = start_index;
+            }
+        }
+    }
+
+  if (!inserted)
+    /* we didn't insert attr yet */
+    pango_attr_list_insert (list, attr);
+
+  /* We now have the range inserted into the list one way or the
+   * other. Fix up the remainder
+   */
+  /* Attention: No i = 0 here. */
+  for (i = i + 1, p = list->attributes->len; i < p; i++)
+    {
+      PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
+
+      if (tmp_attr->start_index > end_index)
+        break;
+
+      if (tmp_attr->type != attr->type)
+        continue;
+
+      if (tmp_attr == attr)
+        continue;
+
+      if (tmp_attr->end_index <= attr->end_index ||
+          pango_attribute_equal (tmp_attr, attr))
+        {
+          /* We can merge the new attribute with this attribute. */
+          attr->end_index = MAX (end_index, tmp_attr->end_index);
+          pango_attribute_destroy (tmp_attr);
+          g_ptr_array_remove_index (list->attributes, i);
+          i--;
+          p--;
+          continue;
+        }
+      else
+        {
+          /* Trim the start of this attribute that it begins at the end
+           * of the new attribute. This may involve moving it in the list
+           * to maintain the required non-decreasing order of start indices.
+           */
+          int k, m;
+
+          tmp_attr->start_index = attr->end_index;
+
+          for (k = i + 1, m = list->attributes->len; k < m; k++)
+            {
+              PangoAttribute *tmp_attr2 = g_ptr_array_index (list->attributes, k);
+
+              if (tmp_attr2->start_index >= tmp_attr->start_index)
+                break;
+
+              g_ptr_array_index (list->attributes, k - 1) = tmp_attr2;
+              g_ptr_array_index (list->attributes, k) = tmp_attr;
+            }
+        }
+    }
+}
+
+/**
+ * pango_attr_list_update:
+ * @list: a `PangoAttrList`
+ * @pos: the position of the change
+ * @remove: the number of removed bytes
+ * @add: the number of added bytes
+ *
+ * Update indices of attributes in @list for a change in the
+ * text they refer to.
+ *
+ * The change that this function applies is removing @remove
+ * bytes at position @pos and inserting @add bytes instead.
+ *
+ * Attributes that fall entirely in the (@pos, @pos + @remove)
+ * range are removed.
+ *
+ * Attributes that start or end inside the (@pos, @pos + @remove)
+ * range are shortened to reflect the removal.
+ *
+ * Attributes start and end positions are updated if they are
+ * behind @pos + @remove.
+ *
+ * Since: 1.44
+ */
+void
+pango_attr_list_update (PangoAttrList *list,
+                        int             pos,
+                        int             remove,
+                        int             add)
+{
+  guint i, p;
+
+  g_return_if_fail (pos >= 0);
+  g_return_if_fail (remove >= 0);
+  g_return_if_fail (add >= 0);
+
+  if (list->attributes)
+    for (i = 0, p = list->attributes->len; i < p; i++)
+      {
+        PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
+
+        if (attr->start_index >= pos &&
+          attr->end_index < pos + remove)
+          {
+            pango_attribute_destroy (attr);
+            g_ptr_array_remove_index (list->attributes, i);
+            i--; /* Look at this index again */
+            p--;
+            continue;
+          }
+
+        if (attr->start_index != PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING)
+          {
+            if (attr->start_index >= pos &&
+                attr->start_index < pos + remove)
+              {
+                attr->start_index = pos + add;
+              }
+            else if (attr->start_index >= pos + remove)
+              {
+                attr->start_index += add - remove;
+              }
+          }
+
+        if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END)
+          {
+            if (attr->end_index >= pos &&
+                attr->end_index < pos + remove)
+              {
+                attr->end_index = pos;
+              }
+            else if (attr->end_index >= pos + remove)
+              {
+                if (add > remove &&
+                    G_MAXUINT - attr->end_index < add - remove)
+                  attr->end_index = G_MAXUINT;
+                else
+                  attr->end_index += add - remove;
+              }
+          }
+      }
+}
+
+/**
+ * pango_attr_list_splice:
+ * @list: a `PangoAttrList`
+ * @other: another `PangoAttrList`
+ * @pos: the position in @list at which to insert @other
+ * @len: the length of the spliced segment. (Note that this
+ *   must be specified since the attributes in @other may only
+ *   be present at some subsection of this range)
+ *
+ * This function opens up a hole in @list, fills it
+ * in with attributes from the left, and then merges
+ * @other on top of the hole.
+ *
+ * This operation is equivalent to stretching every attribute
+ * that applies at position @pos in @list by an amount @len,
+ * and then calling [method@Pango.AttrList.change] with a copy
+ * of each attribute in @other in sequence (offset in position
+ * by @pos, and limited in length to @len).
+ *
+ * This operation proves useful for, for instance, inserting
+ * a pre-edit string in the middle of an edit buffer.
+ *
+ * For backwards compatibility, the function behaves differently
+ * when @len is 0. In this case, the attributes from @other are
+ * not imited to @len, and are just overlayed on top of @list.
+ *
+ * This mode is useful for merging two lists of attributes together.
+ */
+void
+pango_attr_list_splice (PangoAttrList *list,
+                        PangoAttrList *other,
+                        gint           pos,
+                        gint           len)
+{
+  guint i, p;
+  guint upos, ulen;
+  guint end;
+
+  g_return_if_fail (list != NULL);
+  g_return_if_fail (other != NULL);
+  g_return_if_fail (pos >= 0);
+  g_return_if_fail (len >= 0);
+
+  upos = (guint)pos;
+  ulen = (guint)len;
+
+/* This definition only works when a and b are unsigned; overflow
+ * isn't defined in the C standard for signed integers
+ */
+#define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? G_MAXUINT : (a) + (b))
+
+  end = CLAMP_ADD (upos, ulen);
+
+  if (list->attributes)
+    for (i = 0, p = list->attributes->len; i < p; i++)
+      {
+        PangoAttribute *attr = g_ptr_array_index (list->attributes, i);;
+
+        if (attr->start_index <= upos)
+          {
+            if (attr->end_index > upos)
+              attr->end_index = CLAMP_ADD (attr->end_index, ulen);
+          }
+        else
+          {
+            /* This could result in a zero length attribute if it
+             * gets squashed up against G_MAXUINT, but deleting such
+             * an element could (in theory) suprise the caller, so
+             * we don't delete it.
+             */
+            attr->start_index = CLAMP_ADD (attr->start_index, ulen);
+            attr->end_index = CLAMP_ADD (attr->end_index, ulen);
+         }
+      }
+
+  if (!other->attributes || other->attributes->len == 0)
+    return;
+
+  for (i = 0, p = other->attributes->len; i < p; i++)
+    {
+      PangoAttribute *attr = pango_attribute_copy (g_ptr_array_index (other->attributes, i));
+      if (ulen > 0)
+        {
+          attr->start_index = MIN (CLAMP_ADD (attr->start_index, upos), end);
+          attr->end_index = MIN (CLAMP_ADD (attr->end_index, upos), end);
+        }
+      else
+        {
+          attr->start_index = CLAMP_ADD (attr->start_index, upos);
+          attr->end_index = CLAMP_ADD (attr->end_index, upos);
+        }
+
+      /* Same as above, the attribute could be squashed to zero-length; here
+       * pango_attr_list_change() will take care of deleting it.
+       */
+      pango_attr_list_change (list, attr);
+    }
+#undef CLAMP_ADD
+}
+
+/**
+ * pango_attr_list_get_attributes:
+ * @list: a `PangoAttrList`
+ *
+ * Gets a list of all attributes in @list.
+ *
+ * Return value: (element-type Pango.Attribute) (transfer full):
+ *   a list of all attributes in @list. To free this value,
+ *   call [method@Pango.Attribute.destroy] on each value and
+ *   g_slist_free() on the list.
+ *
+ * Since: 1.44
+ */
+GSList *
+pango_attr_list_get_attributes (PangoAttrList *list)
+{
+  GSList *result = NULL;
+  guint i, p;
+
+  g_return_val_if_fail (list != NULL, NULL);
+
+  if (!list->attributes || list->attributes->len == 0)
+    return NULL;
+
+  for (i = 0, p = list->attributes->len; i < p; i++)
+    {
+      PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
+
+      result = g_slist_prepend (result, pango_attribute_copy (attr));
+    }
+
+  return g_slist_reverse (result);
+}
+
+/**
+ * pango_attr_list_equal:
+ * @list: a `PangoAttrList`
+ * @other_list: the other `PangoAttrList`
+ *
+ * Checks whether @list and @other_list contain the same
+ * attributes and whether those attributes apply to the
+ * same ranges.
+ *
+ * Beware that this will return wrong values if any list
+ * contains duplicates.
+ *
+ * Return value: %TRUE if the lists are equal, %FALSE if
+ *   they aren't
+ *
+ * Since: 1.46
+ */
+gboolean
+pango_attr_list_equal (PangoAttrList *list,
+                       PangoAttrList *other_list)
+{
+  GPtrArray *attrs, *other_attrs;
+  guint64 skip_bitmask = 0;
+  guint i;
+
+  if (list == other_list)
+    return TRUE;
+
+  if (list == NULL || other_list == NULL)
+    return FALSE;
+
+  if (list->attributes == NULL || other_list->attributes == NULL)
+    return list->attributes == other_list->attributes;
+
+  attrs = list->attributes;
+  other_attrs = other_list->attributes;
+
+  if (attrs->len != other_attrs->len)
+    return FALSE;
+
+  for (i = 0; i < attrs->len; i++)
+    {
+      PangoAttribute *attr = g_ptr_array_index (attrs, i);
+      gboolean attr_equal = FALSE;
+      guint other_attr_index;
+
+      for (other_attr_index = 0; other_attr_index < other_attrs->len; other_attr_index++)
+        {
+          PangoAttribute *other_attr = g_ptr_array_index (other_attrs, other_attr_index);
+          guint64 other_attr_bitmask = other_attr_index < 64 ? 1 << other_attr_index : 0;
+
+          if ((skip_bitmask & other_attr_bitmask) != 0)
+            continue;
+
+          if (attr->start_index == other_attr->start_index &&
+              attr->end_index == other_attr->end_index &&
+              pango_attribute_equal (attr, other_attr))
+            {
+              skip_bitmask |= other_attr_bitmask;
+              attr_equal = TRUE;
+              break;
+            }
+
+        }
+
+      if (!attr_equal)
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * pango_attr_list_filter:
+ * @list: a `PangoAttrList`
+ * @func: (scope call) (closure data): callback function;
+ *   returns %TRUE if an attribute should be filtered out
+ * @data: (closure): Data to be passed to @func
+ *
+ * Given a `PangoAttrList` and callback function, removes
+ * any elements of @list for which @func returns %TRUE and
+ * inserts them into a new list.
+ *
+ * Return value: (transfer full) (nullable): the new
+ *   `PangoAttrList` or %NULL if no attributes of the
+ *   given types were found
+ *
+ * Since: 1.2
+ */
+PangoAttrList *
+pango_attr_list_filter (PangoAttrList       *list,
+                        PangoAttrFilterFunc  func,
+                        gpointer             data)
+
+{
+  PangoAttrList *new = NULL;
+  guint i, p;
+
+  g_return_val_if_fail (list != NULL, NULL);
+
+  if (!list->attributes || list->attributes->len == 0)
+    return NULL;
+
+  for (i = 0, p = list->attributes->len; i < p; i++)
+    {
+      PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
+
+      if ((*func) (tmp_attr, data))
+        {
+          g_ptr_array_remove_index (list->attributes, i);
+          i--; /* Need to look at this index again */
+          p--;
+
+          if (G_UNLIKELY (!new))
+            {
+              new = pango_attr_list_new ();
+              new->attributes = g_ptr_array_new ();
+            }
+
+          g_ptr_array_add (new->attributes, tmp_attr);
+        }
+    }
+
+  return new;
+}
+
+/* }}} */
+/* {{{ Serialization */
+
+/* We serialize attribute lists to strings. The format
+ * is a comma-separated list of the attributes in the order
+ * in which they are in the list, with each attribute having
+ * this format:
+ *
+ * START END NICK VALUE
+ *
+ * Values that can contain a comma, such as font descriptions
+ * are quoted with "".
+ */
+
+static GType
+get_attr_value_type (PangoAttrType type)
+{
+  switch ((int)type)
+    {
+    case PANGO_ATTR_STYLE: return PANGO_TYPE_STYLE;
+    case PANGO_ATTR_WEIGHT: return PANGO_TYPE_WEIGHT;
+    case PANGO_ATTR_VARIANT: return PANGO_TYPE_VARIANT;
+    case PANGO_ATTR_STRETCH: return PANGO_TYPE_STRETCH;
+    case PANGO_ATTR_GRAVITY: return PANGO_TYPE_GRAVITY;
+    case PANGO_ATTR_GRAVITY_HINT: return PANGO_TYPE_GRAVITY_HINT;
+    case PANGO_ATTR_UNDERLINE: return PANGO_TYPE_UNDERLINE;
+    case PANGO_ATTR_OVERLINE: return PANGO_TYPE_OVERLINE;
+    case PANGO_ATTR_BASELINE_SHIFT: return PANGO_TYPE_BASELINE_SHIFT;
+    case PANGO_ATTR_FONT_SCALE: return PANGO_TYPE_FONT_SCALE;
+    case PANGO_ATTR_TEXT_TRANSFORM: return PANGO_TYPE_TEXT_TRANSFORM;
+    default: return G_TYPE_INVALID;
+    }
+}
+
+static void
+append_enum_value (GString *str,
+                   GType    type,
+                   int      value)
+{
+  GEnumClass *enum_class;
+  GEnumValue *enum_value;
+
+  enum_class = g_type_class_ref (type);
+  enum_value = g_enum_get_value (enum_class, value);
+  g_type_class_unref (enum_class);
+
+  if (enum_value)
+    g_string_append_printf (str, " %s", enum_value->value_nick);
+  else
+    g_string_append_printf (str, " %d", value);
+}
+
+static void
+attr_print (GString        *str,
+            PangoAttribute *attr)
+{
+  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))
+    {
+    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 ();
+    }
+}
+
+/**
+ * pango_attr_list_to_string:
+ * @list: a `PangoAttrList`
+ *
+ * Serializes a `PangoAttrList` to a string.
+ *
+ * No guarantees are made about the format of the string,
+ * it may change between Pango versions.
+ *
+ * The intended use of this function is testing and
+ * debugging. The format is not meant as a permanent
+ * storage format.
+ *
+ * Returns: (transfer full): a newly allocated string
+ * Since: 1.50
+ */
+char *
+pango_attr_list_to_string (PangoAttrList *list)
+{
+  GString *s;
+
+  s = g_string_new ("");
+
+  if (list->attributes)
+    for (int i = 0; i < list->attributes->len; i++)
+      {
+        PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
+
+        if (i > 0)
+          g_string_append (s, "\n");
+        attr_print (s, attr);
+      }
+
+  return g_string_free (s, FALSE);
+}
+
+static PangoAttrType
+get_attr_type_by_nick (const char *nick,
+                       int         len)
+{
+  GEnumClass *enum_class;
+
+  enum_class = g_type_class_ref (pango_attr_type_get_type ());
+  for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
+    {
+      if (ev->value_nick && strncmp (ev->value_nick, nick, len) == 0)
+        {
+          g_type_class_unref (enum_class);
+          return (PangoAttrType) ev->value;
+        }
+    }
+
+  g_type_class_unref (enum_class);
+  return PANGO_ATTR_INVALID;
+}
+
+static int
+get_attr_value (PangoAttrType  type,
+                const char    *str,
+                int            len)
+{
+  GEnumClass *enum_class;
+  char *endp;
+  int value;
+
+  enum_class = g_type_class_ref (get_attr_value_type (type));
+  for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
+    {
+      if (ev->value_nick && strncmp (ev->value_nick, str, len) == 0)
+        {
+          g_type_class_unref (enum_class);
+          return ev->value;
+        }
+    }
+  g_type_class_unref (enum_class);
+
+  value = g_ascii_strtoll (str, &endp, 10);
+  if (endp - str == len)
+    return value;
+
+  return -1;
+}
+
+static gboolean
+is_valid_end_char (char c)
+{
+  return c == ',' || c == '\n' || c == '\0';
+}
+
+/**
+ * pango_attr_list_from_string:
+ * @text: a string
+ *
+ * Deserializes a `PangoAttrList` from a string.
+ *
+ * This is the counterpart to [method@Pango.AttrList.to_string].
+ * See that functions for details about the format.
+ *
+ * Returns: (transfer full) (nullable): a new `PangoAttrList`
+ * Since: 1.50
+ */
+PangoAttrList *
+pango_attr_list_from_string (const char *text)
+{
+  PangoAttrList *list;
+  const char *p;
+
+  g_return_val_if_fail (text != NULL, NULL);
+
+  list = pango_attr_list_new ();
+
+  if (*text == '\0')
+    return list;
+
+  list->attributes = g_ptr_array_new ();
+
+  p = text + strspn (text, " \t\n");
+  while (*p)
+    {
+      char *endp;
+      gint64 start_index;
+      gint64 end_index;
+      char *str;
+      PangoAttrType attr_type;
+      PangoAttribute *attr;
+      PangoLanguage *lang;
+      gint64 integer;
+      PangoFontDescription *desc;
+      PangoColor color;
+      double num;
+
+      start_index = g_ascii_strtoll (p, &endp, 10);
+      if (*endp != ' ')
+        goto fail;
+
+      p = endp + strspn (endp, " ");
+      if (!*p)
+        goto fail;
+
+      end_index = g_ascii_strtoll (p, &endp, 10);
+      if (*endp != ' ')
+        goto fail;
+
+      p = endp + strspn (endp, " ");
+
+      endp = (char *)p + strcspn (p, " ");
+      attr_type = get_attr_type_by_nick (p, endp - p);
+
+      p = endp + strspn (endp, " ");
+      if (*p == '\0')
+        goto fail;
+
+#define INT_ATTR(name,type) \
+          integer = g_ascii_strtoll (p, &endp, 10); \
+          if (!is_valid_end_char (*endp)) goto fail; \
+          attr = pango_attr_##name##_new ((type)integer);
+
+#define BOOLEAN_ATTR(name,type) \
+          if (strncmp (p, "true", strlen ("true")) == 0) \
+            { \
+              integer = 1; \
+              endp = (char *)(p + strlen ("true")); \
+            } \
+          else if (strncmp (p, "false", strlen ("false")) == 0) \
+            { \
+              integer = 0; \
+              endp = (char *)(p + strlen ("false")); \
+            } \
+          else \
+            integer = g_ascii_strtoll (p, &endp, 10); \
+          if (!is_valid_end_char (*endp)) goto fail; \
+          attr = pango_attr_##name##_new ((type)integer);
+
+#define ENUM_ATTR(name, type, min, max) \
+          endp = (char *)p + strcspn (p, ",\n"); \
+          integer = get_attr_value (attr_type, p, endp - p); \
+          attr = pango_attr_##name##_new ((type) CLAMP (integer, min, max));
+
+#define FLOAT_ATTR(name) \
+          num = g_ascii_strtod (p, &endp); \
+          if (!is_valid_end_char (*endp)) goto fail; \
+          attr = pango_attr_##name##_new ((float)num);
+
+#define COLOR_ATTR(name) \
+          endp = (char *)p + strcspn (p, ",\n"); \
+          if (!is_valid_end_char (*endp)) goto fail; \
+          str = g_strndup (p, endp - p); \
+          if (!pango_color_parse (&color, str)) \
+            { \
+              g_free (str); \
+              goto fail; \
+            } \
+          attr = pango_attr_##name##_new (color.red, color.green, color.blue); \
+          g_free (str);
+
+      switch (attr_type)
+        {
+        case PANGO_ATTR_INVALID:
+          pango_attr_list_unref (list);
+          return NULL;
+
+        case PANGO_ATTR_LANGUAGE:
+          endp = (char *)p + strcspn (p, ",\n");
+          if (!is_valid_end_char (*endp)) goto fail;
+          str = g_strndup (p, endp - p);
+          lang = pango_language_from_string (str);
+          attr = pango_attr_language_new (lang);
+          g_free (str);
+          break;
+
+        case PANGO_ATTR_FAMILY:
+          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:
+          ENUM_ATTR(style, PangoStyle, PANGO_STYLE_NORMAL, PANGO_STYLE_ITALIC);
+          break;
+
+        case PANGO_ATTR_WEIGHT:
+          ENUM_ATTR(weight, PangoWeight, PANGO_WEIGHT_THIN, PANGO_WEIGHT_ULTRAHEAVY);
+          break;
+
+        case PANGO_ATTR_VARIANT:
+          ENUM_ATTR(variant, PangoVariant, PANGO_VARIANT_NORMAL, PANGO_VARIANT_TITLE_CAPS);
+          break;
+
+        case PANGO_ATTR_STRETCH:
+          ENUM_ATTR(stretch, PangoStretch, PANGO_STRETCH_ULTRA_CONDENSED, PANGO_STRETCH_ULTRA_EXPANDED);
+          break;
+
+        case PANGO_ATTR_SIZE:
+          INT_ATTR(size, int);
+          break;
+
+        case PANGO_ATTR_FONT_DESC:
+          p++;
+          endp = strchr (p, '"');
+          if (!endp) goto fail;
+          str = g_strndup (p, endp - p);
+          desc = pango_font_description_from_string (str);
+          attr = pango_attr_font_desc_new (desc);
+          pango_font_description_free (desc);
+          g_free (str);
+          endp++;
+          if (!is_valid_end_char (*endp)) goto fail;
+          break;
+
+        case PANGO_ATTR_FOREGROUND:
+          COLOR_ATTR(foreground);
+          break;
+
+        case PANGO_ATTR_BACKGROUND:
+          COLOR_ATTR(background);
+          break;
+
+        case PANGO_ATTR_UNDERLINE:
+          ENUM_ATTR(underline, PangoUnderline, PANGO_UNDERLINE_NONE, PANGO_UNDERLINE_ERROR_LINE);
+          break;
+
+        case PANGO_ATTR_STRIKETHROUGH:
+          BOOLEAN_ATTR(strikethrough, gboolean);
+          break;
+
+        case PANGO_ATTR_RISE:
+          INT_ATTR(rise, int);
+          break;
+
+        case PANGO_ATTR_SCALE:
+          FLOAT_ATTR(scale);
+          break;
+
+        case PANGO_ATTR_FALLBACK:
+          BOOLEAN_ATTR(fallback, gboolean);
+          break;
+
+        case PANGO_ATTR_LETTER_SPACING:
+          INT_ATTR(letter_spacing, int);
+          break;
+
+        case PANGO_ATTR_UNDERLINE_COLOR:
+          COLOR_ATTR(underline_color);
+          break;
+
+        case PANGO_ATTR_STRIKETHROUGH_COLOR:
+          COLOR_ATTR(strikethrough_color);
+          break;
+
+        case PANGO_ATTR_ABSOLUTE_SIZE:
+          integer = g_ascii_strtoll (p, &endp, 10);
+          if (!is_valid_end_char (*endp)) goto fail;
+          attr = pango_attr_size_new_absolute (integer);
+          break;
+
+        case PANGO_ATTR_GRAVITY:
+          ENUM_ATTR(gravity, PangoGravity, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_WEST);
+          break;
+
+        case PANGO_ATTR_FONT_FEATURES:
+          p++;
+          endp = strchr (p, '"');
+          if (!endp) goto fail;
+          str = g_strndup (p, endp - p);
+          attr = pango_attr_font_features_new (str);
+          g_free (str);
+          endp++;
+          if (!is_valid_end_char (*endp)) goto fail;
+          break;
+
+        case PANGO_ATTR_GRAVITY_HINT:
+          ENUM_ATTR(gravity_hint, PangoGravityHint, PANGO_GRAVITY_HINT_NATURAL, PANGO_GRAVITY_HINT_LINE);
+          break;
+
+        case PANGO_ATTR_FOREGROUND_ALPHA:
+          INT_ATTR(foreground_alpha, int);
+          break;
+
+        case PANGO_ATTR_BACKGROUND_ALPHA:
+          INT_ATTR(background_alpha, int);
+          break;
+
+        case PANGO_ATTR_ALLOW_BREAKS:
+          BOOLEAN_ATTR(allow_breaks, gboolean);
+          break;
+
+        case PANGO_ATTR_SHOW:
+          INT_ATTR(show, PangoShowFlags);
+          break;
+
+        case PANGO_ATTR_INSERT_HYPHENS:
+          BOOLEAN_ATTR(insert_hyphens, gboolean);
+          break;
+
+        case PANGO_ATTR_OVERLINE:
+          ENUM_ATTR(overline, PangoOverline, PANGO_OVERLINE_NONE, PANGO_OVERLINE_SINGLE);
+          break;
+
+        case PANGO_ATTR_OVERLINE_COLOR:
+          COLOR_ATTR(overline_color);
+          break;
+
+        case PANGO_ATTR_LINE_HEIGHT:
+          FLOAT_ATTR(line_height);
+          break;
+
+        case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
+          integer = g_ascii_strtoll (p, &endp, 10);
+          if (!is_valid_end_char (*endp)) goto fail;
+          attr = pango_attr_line_height_new_absolute (integer);
+          break;
+
+        case PANGO_ATTR_TEXT_TRANSFORM:
+          ENUM_ATTR(text_transform, PangoTextTransform, PANGO_TEXT_TRANSFORM_NONE, 
PANGO_TEXT_TRANSFORM_CAPITALIZE);
+          break;
+
+        case PANGO_ATTR_WORD:
+          integer = g_ascii_strtoll (p, &endp, 10);
+          if (!is_valid_end_char (*endp)) goto fail;
+          attr = pango_attr_word_new ();
+          break;
+
+        case PANGO_ATTR_SENTENCE:
+          integer = g_ascii_strtoll (p, &endp, 10);
+          if (!is_valid_end_char (*endp)) goto fail;
+          attr = pango_attr_sentence_new ();
+          break;
+
+        case PANGO_ATTR_PARAGRAPH:
+          integer = g_ascii_strtoll (p, &endp, 10);
+          if (!is_valid_end_char (*endp)) goto fail;
+          attr = pango_attr_paragraph_new ();
+          break;
+
+        case PANGO_ATTR_BASELINE_SHIFT:
+          ENUM_ATTR(baseline_shift, PangoBaselineShift, 0, G_MAXINT);
+          break;
+
+        case PANGO_ATTR_FONT_SCALE:
+          ENUM_ATTR(font_scale, PangoFontScale, PANGO_FONT_SCALE_NONE, PANGO_FONT_SCALE_SMALL_CAPS);
+          break;
+
+        case PANGO_ATTR_LINE_SPACING:
+          INT_ATTR(line_spacing, int);
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+
+      attr->start_index = (guint)start_index;
+      attr->end_index = (guint)end_index;
+      g_ptr_array_add (list->attributes, attr);
+
+      p = endp;
+      if (*p)
+        {
+          if (*p == ',')
+            p++;
+          p += strspn (p, " \n");
+        }
+    }
+
+  goto success;
+
+fail:
+  pango_attr_list_unref (list);
+  list = NULL;
+
+success:
+  return list;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-attr-list.h b/pango/pango-attr-list.h
new file mode 100644
index 000000000..f08706b2a
--- /dev/null
+++ b/pango/pango-attr-list.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-attributes.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _PangoAttrList     PangoAttrList;
+typedef struct _PangoAttrIterator PangoAttrIterator;
+
+#define PANGO_TYPE_ATTR_LIST pango_attr_list_get_type ()
+
+/**
+ * PangoAttrList:
+ *
+ * A `PangoAttrList` represents a list of attributes that apply to a section
+ * of text.
+ *
+ * The attributes in a `PangoAttrList` are, in general, allowed to overlap in
+ * an arbitrary fashion. However, if the attributes are manipulated only through
+ * [method@Pango.AttrList.change], the overlap between properties will meet
+ * stricter criteria.
+ */
+
+PANGO_AVAILABLE_IN_ALL
+GType                   pango_attr_list_get_type        (void) G_GNUC_CONST;
+
+PANGO_AVAILABLE_IN_ALL
+PangoAttrList *         pango_attr_list_new             (void);
+PANGO_AVAILABLE_IN_1_10
+PangoAttrList *         pango_attr_list_ref             (PangoAttrList         *list);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_list_unref           (PangoAttrList         *list);
+PANGO_AVAILABLE_IN_ALL
+PangoAttrList *         pango_attr_list_copy            (PangoAttrList         *list);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_list_insert          (PangoAttrList         *list,
+                                                         PangoAttribute        *attr);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_list_insert_before   (PangoAttrList         *list,
+                                                         PangoAttribute        *attr);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_list_change          (PangoAttrList         *list,
+                                                         PangoAttribute        *attr);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attr_list_splice          (PangoAttrList         *list,
+                                                         PangoAttrList         *other,
+                                                         int                    pos,
+                                                         int                    len);
+PANGO_AVAILABLE_IN_1_44
+void                    pango_attr_list_update          (PangoAttrList         *list,
+                                                         int                    pos,
+                                                         int                    remove,
+                                                         int                    add);
+
+/**
+ * PangoAttrFilterFunc:
+ * @attribute: a Pango attribute
+ * @user_data: user data passed to the function
+ *
+ * Callback to filter a list of attributes.
+ *
+ * Return value: `TRUE` if the attribute should be selected for
+ *   filtering, `FALSE` otherwise.
+ */
+typedef gboolean (*PangoAttrFilterFunc) (PangoAttribute *attribute,
+                                         gpointer        user_data);
+
+PANGO_AVAILABLE_IN_1_2
+PangoAttrList *         pango_attr_list_filter          (PangoAttrList         *list,
+                                                         PangoAttrFilterFunc    func,
+                                                         gpointer               data);
+
+PANGO_AVAILABLE_IN_1_44
+GSList *                pango_attr_list_get_attributes  (PangoAttrList         *list);
+
+PANGO_AVAILABLE_IN_1_46
+gboolean                pango_attr_list_equal           (PangoAttrList         *list,
+                                                         PangoAttrList         *other_list);
+
+PANGO_AVAILABLE_IN_1_50
+char *                  pango_attr_list_to_string       (PangoAttrList         *list);
+PANGO_AVAILABLE_IN_1_50
+PangoAttrList *         pango_attr_list_from_string     (const char            *text);
+
+PANGO_AVAILABLE_IN_ALL
+PangoAttrIterator *     pango_attr_list_get_iterator    (PangoAttrList         *list);
+
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoAttrList, pango_attr_list_unref)
+
+G_END_DECLS
diff --git a/pango/pango-attr-private.h b/pango/pango-attr-private.h
new file mode 100644
index 000000000..4dea65456
--- /dev/null
+++ b/pango/pango-attr-private.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-attr.h>
+
+char *    pango_attr_value_serialize   (PangoAttribute    *attr);
diff --git a/pango/pango-attr.c b/pango/pango-attr.c
new file mode 100644
index 000000000..0d8d9f551
--- /dev/null
+++ b/pango/pango-attr.c
@@ -0,0 +1,603 @@
+/* Pango
+ * pango-attr.c: Attributed text
+ *
+ * Copyright (C) 2000-2002 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "pango-attr-private.h"
+#include "pango-impl-utils.h"
+
+
+/* {{{ Generic attribute code */
+
+G_LOCK_DEFINE_STATIC (attr_type);
+static GArray *attr_type;
+
+#define MIN_CUSTOM 1000
+
+typedef struct _PangoAttrClass PangoAttrClass;
+
+struct _PangoAttrClass {
+  guint 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;
+}
+
+static gboolean
+is_valid_attr_type (guint type)
+{
+  switch (type)
+    {
+    case PANGO_ATTR_INVALID:
+      return FALSE;
+    case PANGO_ATTR_LANGUAGE:
+    case PANGO_ATTR_FAMILY:
+    case PANGO_ATTR_STYLE:
+    case PANGO_ATTR_WEIGHT:
+    case PANGO_ATTR_VARIANT:
+    case PANGO_ATTR_STRETCH:
+    case PANGO_ATTR_SIZE:
+    case PANGO_ATTR_FONT_DESC:
+    case PANGO_ATTR_FOREGROUND:
+    case PANGO_ATTR_BACKGROUND:
+    case PANGO_ATTR_UNDERLINE:
+    case PANGO_ATTR_STRIKETHROUGH:
+    case PANGO_ATTR_RISE:
+    case PANGO_ATTR_SCALE:
+    case PANGO_ATTR_FALLBACK:
+    case PANGO_ATTR_LETTER_SPACING:
+    case PANGO_ATTR_UNDERLINE_COLOR:
+    case PANGO_ATTR_STRIKETHROUGH_COLOR:
+    case PANGO_ATTR_ABSOLUTE_SIZE:
+    case PANGO_ATTR_GRAVITY:
+    case PANGO_ATTR_GRAVITY_HINT:
+    case PANGO_ATTR_FONT_FEATURES:
+    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_OVERLINE_COLOR:
+    case PANGO_ATTR_LINE_HEIGHT:
+    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 TRUE;
+    default:
+      if (!attr_type)
+        return FALSE;
+      else
+        {
+          gboolean result = FALSE;
+
+          G_LOCK (attr_type);
+
+          for (int i = 0; i < attr_type->len; i++)
+            {
+              PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+              if (class->type == type)
+                {
+                  result = TRUE;
+                  break;
+                }
+            }
+
+          G_UNLOCK (attr_type);
+
+          return result;
+        }
+    }
+}
+
+/**
+ * pango_attr_type_register:
+ * @name: (nullable): an identifier for the type
+ * @value_type: the `PangoAttrValueType` for the attribute
+ * @affects: `PangoAttrAffects` flags for the attribute
+ * @merge: `PangoAttrMerge` flags for the attribute
+ * @copy: (scope forever) (nullable): function to copy the data of an attribute
+ *   of this type
+ * @destroy: (scope forever) (nullable): function to free the data of an attribute
+ *   of this type
+ * @equal: (scope forever) (nullable): function to compare the data of two attributes
+ *   of this type
+ * @serialize: (scope forever) (nullable): function to serialize the data
+ *   of an attribute of this type
+ *
+ * Register a new attribute type.
+ *
+ * The attribute type name can be accessed later
+ * by using [func@Pango.AttrType.get_name].
+ *
+ * The callbacks are only needed if @type is `PANGO_ATTR_VALUE_POINTER`,
+ * Pango knows how to handle other value types and will only use the
+ * callbacks for generic pointer values.
+ *
+ * If @name and @serialize are provided, they will be used
+ * to serialize attributes of this type.
+ *
+ * To create attributes with the new type, use [func@Pango.attr_custom_new].
+ *
+ * Return value: the attribute type ID
+ */
+guint
+pango_attr_type_register (const char                 *name,
+                          PangoAttrValueType          value_type,
+                          PangoAttrAffects            affects,
+                          PangoAttrMerge              merge,
+                          PangoAttrDataCopyFunc       copy,
+                          GDestroyNotify              destroy,
+                          GEqualFunc                  equal,
+                          PangoAttrDataSerializeFunc  serialize)
+{
+  static guint current_id = MIN_CUSTOM; /* MT-safe */
+  PangoAttrClass class;
+
+  G_LOCK (attr_type);
+
+  class.type = value_type | (affects << 8) | (merge << 12) | (current_id << 16);
+  current_id++;
+
+  class.copy = copy;
+  class.destroy = destroy;
+  class.equal = equal;
+  class.serialize = serialize;
+
+  if (name)
+    class.name = 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 class.type;
+}
+
+/**
+ * pango_attr_type_get_name:
+ * @type: an attribute type ID to fetch the name for
+ *
+ * Fetches the attribute type name.
+ *
+ * The attribute type name is the string passed in
+ * when registering the type using
+ * [func@Pango.AttrType.register].
+ *
+ * The returned value is an interned string (see
+ * g_intern_string() for what that means) that should
+ * not be modified or freed.
+ *
+ * Return value: (nullable): the type ID name (which
+ *   may be %NULL), or %NULL if @type is a built-in Pango
+ *   attribute type or invalid.
+ *
+ * Since: 1.22
+ */
+const char *
+pango_attr_type_get_name (guint type)
+{
+  const char *result = NULL;
+
+  if ((type >> 16) < MIN_CUSTOM)
+    return get_attr_type_nick (type);
+
+  G_LOCK (attr_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_copy:
+ * @attr: a `PangoAttribute`
+ *
+ * Make a copy of an attribute.
+ *
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttribute`, which should be freed with
+ *   [method@Pango.Attribute.destroy].
+ */
+PangoAttribute *
+pango_attribute_copy (const PangoAttribute *attr)
+{
+  PangoAttribute *result;
+
+  g_return_val_if_fail (attr != NULL, NULL);
+
+  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);
+
+        if (copy)
+          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;
+}
+
+/**
+ * pango_attribute_destroy:
+ * @attr: a `PangoAttribute`.
+ *
+ * Destroy a `PangoAttribute` and free all associated memory.
+ */
+void
+pango_attribute_destroy (PangoAttribute *attr)
+{
+  g_return_if_fail (attr != NULL);
+
+  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);
+
+        if (destroy)
+          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,
+                     pango_attribute_copy,
+                     pango_attribute_destroy);
+
+/**
+ * pango_attribute_equal:
+ * @attr1: a `PangoAttribute`
+ * @attr2: another `PangoAttribute`
+ *
+ * Compare two attributes for equality.
+ *
+ * This compares only the actual value of the two
+ * attributes and not the ranges that the attributes
+ * apply to.
+ *
+ * Return value: %TRUE if the two attributes have the same value
+ */
+gboolean
+pango_attribute_equal (const PangoAttribute *attr1,
+                       const PangoAttribute *attr2)
+{
+  g_return_val_if_fail (attr1 != NULL, FALSE);
+  g_return_val_if_fail (attr2 != NULL, FALSE);
+
+  if (attr1->type != attr2->type)
+    return FALSE;
+
+  switch (PANGO_ATTR_VALUE_TYPE (attr1))
+    {
+    case PANGO_ATTR_VALUE_STRING:
+      return strcmp (attr1->str_value, attr2->str_value) == 0;
+
+    case PANGO_ATTR_VALUE_INT:
+      return attr1->int_value == attr2->int_value;
+
+    case PANGO_ATTR_VALUE_BOOLEAN:
+      return attr1->boolean_value == attr2->boolean_value;
+
+    case PANGO_ATTR_VALUE_FLOAT:
+      return attr1->double_value == attr2->double_value;
+
+    case PANGO_ATTR_VALUE_COLOR:
+      return memcmp (&attr1->color_value, &attr2->color_value, sizeof (PangoColor)) == 0;
+
+    case PANGO_ATTR_VALUE_LANGUAGE:
+      return attr1->lang_value == attr2->lang_value;
+
+    case PANGO_ATTR_VALUE_FONT_DESC:
+      return pango_font_description_equal (attr1->font_value, attr2->font_value);
+
+    case PANGO_ATTR_VALUE_POINTER:
+      {
+        GEqualFunc equal = 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 == attr1->type)
+              {
+                equal = class->equal;
+                break;
+              }
+          }
+
+        G_UNLOCK (attr_type);
+
+        g_assert (equal != NULL);
+
+        if (equal)
+          return equal (attr1->pointer_value, attr2->pointer_value);
+
+        return attr1->pointer_value == attr2->pointer_value;
+      }
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+/**
+ * pango_attribute_new:
+ * @type: the attribute type
+ *
+ * Creates a new attribute for the given type.
+ *
+ * The type must be one of the `PangoAttrType` values, or
+ * have been registered with [func@Pango.register_attr_type].
+ *
+ * Pango will initialize @start_index and @end_index to an
+ * all-inclusive range of `[0,G_MAXUINT]`.  The caller is
+ * responsible for filling the proper value field with the
+ * desired value.
+ *
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttribute`, which should be freed with
+ *   [method@Pango.Attribute.destroy]
+ */
+PangoAttribute *
+pango_attribute_new (guint type)
+{
+  PangoAttribute *attr;
+
+  g_return_val_if_fail (is_valid_attr_type (type), NULL);
+
+  attr = g_slice_new0 (PangoAttribute);
+  attr->type = type;
+  attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
+  attr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
+
+  return attr;
+}
+
+/* }}} */
+/* {{{ Private API */
+
+char *
+pango_attr_value_serialize (PangoAttribute *attr)
+{
+  PangoAttrDataSerializeFunc serialize = 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)
+        {
+          serialize = class->serialize;
+          break;
+        }
+    }
+
+  G_UNLOCK (attr_type);
+
+  if (serialize)
+    return serialize (attr->pointer_value);
+
+  return g_strdup_printf ("%p", attr->pointer_value);
+}
+
+/* }}} */
+/* {{{ Binding Helpers */
+
+gboolean
+pango_attribute_get_string (PangoAttribute   *attribute,
+                            const char      **value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_STRING)
+    return FALSE;
+
+  *value = attribute->str_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_language (PangoAttribute  *attribute,
+                              PangoLanguage  **value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_LANGUAGE)
+    return FALSE;
+
+  *value = attribute->lang_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_int (PangoAttribute *attribute,
+                         int            *value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_INT)
+    return FALSE;
+
+  *value = attribute->int_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_boolean (PangoAttribute *attribute,
+                             int            *value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_BOOLEAN)
+    return FALSE;
+
+  *value = attribute->boolean_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_float (PangoAttribute *attribute,
+                           double         *value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_FLOAT)
+    return FALSE;
+
+  *value = attribute->double_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_color (PangoAttribute *attribute,
+                           PangoColor     *value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_COLOR)
+    return FALSE;
+
+  *value = attribute->color_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_font_desc (PangoAttribute        *attribute,
+                               PangoFontDescription **value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_FONT_DESC)
+    return FALSE;
+
+  *value = attribute->font_value;
+  return TRUE;
+}
+
+gboolean
+pango_attribute_get_pointer (PangoAttribute *attribute,
+                             gpointer       *value)
+{
+  if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_POINTER)
+    return FALSE;
+
+  *value = attribute->pointer_value;
+  return TRUE;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-attr.h b/pango/pango-attr.h
new file mode 100644
index 000000000..adf87bc7e
--- /dev/null
+++ b/pango/pango-attr.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-font.h>
+#include <pango/pango-color.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _PangoAttribute PangoAttribute;
+
+/**
+ * PangoAttrValueType:
+ * @PANGO_ATTR_VALUE_STRING: a string
+ * @PANGO_ATTR_VALUE_INT: an integer
+ * @PANGO_ATTR_VALUE_BOOLEAN: a boolean
+ * @PANGO_ATTR_VALUE_FLOAT: a floating point number
+ * @PANGO_ATTR_VALUE_COLOR: a `PangoColor`
+ * @PANGO_ATTR_VALUE_LANGUAGE: a `PangoLanguage`
+ * @PANGO_ATTR_VALUE_FONT_DESC: a `PangoFontDescription`
+ * @PANGO_ATTR_VALUE_POINTER: a generic pointer
+ *
+ * The `PangoAttrValueType` enumeration describes the various types
+ * of values that a `PangoAttribute` can contain.
+ *
+ * The `PangoAttrValueType` of a `PangoAttribute` is part of its type,
+ * and can be obtained with `PANGO_ATTR_VALUE_TYPE()`.
+ */
+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;
+
+/**
+ * PangoAttrAffects:
+ * @PANGO_ATTR_AFFECTS_NONE: the attribute does not affect rendering
+ * @PANGO_ATTR_AFFECTS_ITEMIZATION: the attribute affecs itemization
+ * @PANGO_ATTR_AFFECTS_BREAKING: the attribute affects `PangoLogAttr` determination
+ * @PANGO_ATTR_AFFECTS_SHAPING: the attribute affects shaping
+ * @PANGO_ATTR_AFFECTS_RENDERING: the attribute affects rendering
+ *
+ * Flags to indicate what part of Pangos processing pipeline is
+ * affected by an attribute.
+ *
+ * Marking an attribute with `PANGO_ATTR_AFFECTS_ITEMIZATION` ensures
+ * that the attribute values are constant across items.
+ */
+typedef enum
+{
+  PANGO_ATTR_AFFECTS_NONE        = 0,
+  PANGO_ATTR_AFFECTS_ITEMIZATION = 1 << 0,
+  PANGO_ATTR_AFFECTS_BREAKING    = 1 << 1,
+  PANGO_ATTR_AFFECTS_SHAPING     = 1 << 2,
+  PANGO_ATTR_AFFECTS_RENDERING   = 1 << 3
+} PangoAttrAffects;
+
+/**
+ * PangoAttrMerge:
+ * @PANGO_ATTR_MERGE_OVERRIDES: only the attribute with the narrowest range is used
+ * @PANGO_ATTR_MERGE_ACCUMULATES: all attributes with overlapping range are kept
+ *
+ * Options to indicate how overlapping attribute values should be
+ * reconciled to determine the effective attribute value.
+ *
+ * These options influence the @extra_attrs returned by
+ * [method@Pango.AttrIterator.get_font].
+ */
+typedef enum
+{
+  PANGO_ATTR_MERGE_OVERRIDES,
+  PANGO_ATTR_MERGE_ACCUMULATES
+} PangoAttrMerge;
+
+/**
+ * PANGO_ATTR_TYPE_VALUE_TYPE:
+ * @type: an attribute type
+ *
+ * Extracts the `PangoAttrValueType` from an attribute type.
+ */
+#define PANGO_ATTR_TYPE_VALUE_TYPE(type) ((PangoAttrValueType)((type) & 0xff))
+
+/**
+ * PANGO_ATTR_TYPE_AFFECTS:
+ * @type: an attribute type
+ *
+ * Extracts the `PangoAttrAffects` flags from an attribute type.
+ */
+#define PANGO_ATTR_TYPE_AFFECTS(type) ((PangoAttrAffects)(((type) >> 8) & 0xf))
+
+/**
+ * PANGO_ATTR_TYPE_MERGE:
+ * @type: an attribute type
+ *
+ * Extracts the `PangoAttrMerge` flags from an attribute type.
+ */
+#define PANGO_ATTR_TYPE_MERGE(type) ((PangoAttrMerge)(((type) >> 12) & 0xf))
+
+/**
+ * PANGO_ATTR_VALUE_TYPE:
+ * @attr: a `PangoAttribute`
+ *
+ * Obtains the `PangoAttrValueType of a `PangoAttribute`.
+ */
+#define PANGO_ATTR_VALUE_TYPE(attr) PANGO_ATTR_TYPE_VALUE_TYPE ((attr)->type)
+
+/**
+ * PANGO_ATTR_AFFECTS:
+ * @attr: a `PangoAttribute`
+ *
+ * Obtains the `PangoAttrAffects` flags of a `PangoAttribute`.
+ */
+#define PANGO_ATTR_AFFECTS(attr) PANGO_ATTR_TYPE_AFFECTS ((attr)->type)
+
+/**
+ * PANGO_ATTR_MERGE:
+ * @attr: a `PangoAttribute`
+ *
+ * Obtains the `PangoAttrMerge` flags of a `PangoAttribute`.
+ */
+#define PANGO_ATTR_MERGE(attr) PANGO_ATTR_TYPE_MERGE ((attr)->type)
+
+/**
+ * PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING:
+ *
+ * Value for @start_index in `PangoAttribute` that indicates
+ * the beginning of the text.
+ *
+ * Since: 1.24
+ */
+#define PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING ((guint)0)
+
+/**
+ * PANGO_ATTR_INDEX_TO_TEXT_END: (value 4294967295)
+ *
+ * Value for @end_index in `PangoAttribute` that indicates
+ * the end of the text.
+ *
+ * Since: 1.24
+ */
+#define PANGO_ATTR_INDEX_TO_TEXT_END ((guint)(G_MAXUINT + 0))
+
+/**
+ * PangoAttribute:
+ * @type: the type of the attribute
+ * @start_index: the start index of the range (in bytes).
+ * @end_index: end index of the range (in bytes). The character at this index
+ *   is not included in the range.
+ * @str_value: the string value
+ * @int_value: the integer value
+ * @boolean_value: the boolean value
+ * @double_value: the double value
+ * @color_value: the `PangoColor` value
+ * @lang_value: the `PangoLanguage` value
+ * @font_value: the `PangoFontDescription` value
+ * @pointer_value: a custom value
+ *
+ * The `PangoAttribute` structure contains information for a single
+ * attribute.
+ *
+ * The common portion of the attribute holds the range to which
+ * the value in the type-specific part of the attribute applies.
+ * By default, an attribute will have an all-inclusive range of
+ * `[0,G_MAXUINT]`.
+ *
+ * Which of the values is used depends on the value type of the
+ * attribute, which can be obtained with `PANGO_ATTR_VALUE_TYPE()`.
+ */
+struct _PangoAttribute
+{
+  guint 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;
+  };
+};
+
+/**
+ * PangoAttrDataCopyFunc:
+ * @value: value to copy
+ *
+ * Callback to duplicate the value of an attribute.
+ *
+ * Return value: new copy of @value.
+ **/
+typedef gpointer (*PangoAttrDataCopyFunc) (gconstpointer value);
+
+/**
+ * PangoAttrDataSerializeFunc:
+ * @value: value to serialize
+ *
+ * Callback to serialize the value of an attribute.
+ *
+ * Return value: a newly allocated string holding the serialization of @value
+ */
+typedef char * (*PangoAttrDataSerializeFunc) (gconstpointer value);
+
+PANGO_AVAILABLE_IN_ALL
+GType                   pango_attribute_get_type                (void) G_GNUC_CONST;
+
+PANGO_AVAILABLE_IN_ALL
+guint                   pango_attr_type_register                (const char                 *name,
+                                                                 PangoAttrValueType          value_type,
+                                                                 PangoAttrAffects            affects,
+                                                                 PangoAttrMerge              merge,
+                                                                 PangoAttrDataCopyFunc       copy,
+                                                                 GDestroyNotify              destroy,
+                                                                 GEqualFunc                  equal,
+                                                                 PangoAttrDataSerializeFunc  serialize);
+
+PANGO_AVAILABLE_IN_1_22
+const char *            pango_attr_type_get_name                (guint                       type) 
G_GNUC_CONST;
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attribute_copy                    (const PangoAttribute       *attr);
+PANGO_AVAILABLE_IN_ALL
+void                    pango_attribute_destroy                 (PangoAttribute             *attr);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_equal                   (const PangoAttribute       *attr1,
+                                                                 const PangoAttribute       *attr2) 
G_GNUC_PURE;
+
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attribute_new                     (guint                       type);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_string              (PangoAttribute              *attribute,
+                                                                 const char                 **value);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_language            (PangoAttribute              *attribute,
+                                                                 PangoLanguage              **value);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_int                 (PangoAttribute              *attribute,
+                                                                 int                         *value);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_boolean             (PangoAttribute              *attribute,
+                                                                 gboolean                    *value);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_float               (PangoAttribute              *attribute,
+                                                                 double                      *value);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_color               (PangoAttribute              *attribute,
+                                                                 PangoColor                  *value);
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_font_desc           (PangoAttribute              *attribute,
+                                                                 PangoFontDescription       **value);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean                pango_attribute_get_pointer             (PangoAttribute              *attribute,
+                                                                 gpointer                    *value);
+
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoAttribute, pango_attribute_destroy)
+
+G_END_DECLS
diff --git a/pango/pango-attributes-private.h b/pango/pango-attributes-private.h
index 7d3110826..8301b86df 100644
--- a/pango/pango-attributes-private.h
+++ b/pango/pango-attributes-private.h
@@ -1,58 +1,13 @@
-/* Pango
- * pango-attributes-private.h: Internal structures of PangoLayout
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
+#pragma once
 
-#ifndef __PANGO_ATTRIBUTES_PRIVATE_H__
-#define __PANGO_ATTRIBUTES_PRIVATE_H__
-
-struct _PangoAttrIterator
-{
-  GPtrArray *attrs; /* From the list */
-  guint n_attrs; /* Copied from the list */
-
-  GPtrArray *attribute_stack;
-
-  guint attr_index;
-  guint start_index;
-  guint end_index;
-};
-
-struct _PangoAttrList
-{
-  guint ref_count;
-  GPtrArray *attributes;
-};
-
-void     _pango_attr_list_init         (PangoAttrList     *list);
-void     _pango_attr_list_destroy      (PangoAttrList     *list);
-gboolean _pango_attr_list_has_attributes (const PangoAttrList *list);
-
-void     _pango_attr_list_get_iterator (PangoAttrList     *list,
-                                        PangoAttrIterator *iterator);
-
-void     _pango_attr_iterator_destroy  (PangoAttrIterator *iterator);
-gboolean  pango_attr_iterator_advance  (PangoAttrIterator *iterator,
-                                        int                index);
+#include "pango-attributes.h"
 
+G_BEGIN_DECLS
 
 gboolean pango_attribute_affects_itemization    (PangoAttribute *attr,
                                                  gpointer        data);
 gboolean pango_attribute_affects_break_or_shape (PangoAttribute *attr,
                                                  gpointer        data);
 
-#endif
+
+G_END_DECLS
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 2bebcf595..1424bb3f1 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -22,522 +22,129 @@
 #include "config.h"
 #include <string.h>
 
-#include "pango-attributes.h"
 #include "pango-attributes-private.h"
+#include "pango-attr-private.h"
 #include "pango-impl-utils.h"
 
+/* {{{ Attribute value types */
 
-/* {{{ Generic attribute code */
-
-G_LOCK_DEFINE_STATIC (attr_type);
-static GHashTable *name_map = NULL; /* MT-safe */
-
-/**
- * pango_attr_type_register:
- * @name: an identifier for the type
- *
- * Allocate a new attribute type ID.
- *
- * The attribute type name can be accessed later
- * by using [func@Pango.AttrType.get_name].
- *
- * Return value: the new type ID.
- */
-PangoAttrType
-pango_attr_type_register (const gchar *name)
-{
-  static guint current_type = 0x1000000; /* MT-safe */
-  guint type;
-
-  G_LOCK (attr_type);
-
-  type = current_type++;
-
-  if (name)
-    {
-      if (G_UNLIKELY (!name_map))
-        name_map = g_hash_table_new (NULL, NULL);
-
-      g_hash_table_insert (name_map, GUINT_TO_POINTER (type), (gpointer) g_intern_string (name));
-    }
-
-  G_UNLOCK (attr_type);
-
-  return type;
-}
-
-/**
- * pango_attr_type_get_name:
- * @type: an attribute type ID to fetch the name for
- *
- * Fetches the attribute type name.
- *
- * The attribute type name is the string passed in
- * when registering the type using
- * [func@Pango.AttrType.register].
- *
- * The returned value is an interned string (see
- * g_intern_string() for what that means) that should
- * not be modified or freed.
- *
- * Return value: (nullable): the type ID name (which
- *   may be %NULL), or %NULL if @type is a built-in Pango
- *   attribute type or invalid.
- *
- * Since: 1.22
- */
-const char *
-pango_attr_type_get_name (PangoAttrType type)
-{
-  const char *result = NULL;
-
-  G_LOCK (attr_type);
-
-  if (name_map)
-    result = g_hash_table_lookup (name_map, GUINT_TO_POINTER ((guint) type));
-
-  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)
+static inline PangoAttribute *
+pango_attr_init (PangoAttrType type)
 {
-  g_return_if_fail (attr != NULL);
-  g_return_if_fail (klass != NULL);
+  PangoAttribute *attr;
 
-  attr->klass = klass;
+  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;
-}
-
-/**
- * pango_attribute_copy:
- * @attr: a `PangoAttribute`
- *
- * Make a copy of an attribute.
- *
- * Return value: (transfer full): the newly allocated
- *   `PangoAttribute`, which should be freed with
- *   [method@Pango.Attribute.destroy].
- */
-PangoAttribute *
-pango_attribute_copy (const PangoAttribute *attr)
-{
-  PangoAttribute *result;
-
-  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;
-
-  return result;
-}
-
-/**
- * pango_attribute_destroy:
- * @attr: a `PangoAttribute`.
- *
- * Destroy a `PangoAttribute` and free all associated memory.
- */
-void
-pango_attribute_destroy (PangoAttribute *attr)
-{
-  g_return_if_fail (attr != NULL);
-
-  attr->klass->destroy (attr);
-}
-
-G_DEFINE_BOXED_TYPE (PangoAttribute, pango_attribute,
-                     pango_attribute_copy,
-                     pango_attribute_destroy);
-
-/**
- * pango_attribute_equal:
- * @attr1: a `PangoAttribute`
- * @attr2: another `PangoAttribute`
- *
- * Compare two attributes for equality.
- *
- * This compares only the actual value of the two
- * attributes and not the ranges that the attributes
- * apply to.
- *
- * Return value: %TRUE if the two attributes have the same value
- */
-gboolean
-pango_attribute_equal (const PangoAttribute *attr1,
-                       const PangoAttribute *attr2)
-{
-  g_return_val_if_fail (attr1 != NULL, FALSE);
-  g_return_val_if_fail (attr2 != NULL, FALSE);
+  attr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END;
 
-  if (attr1->klass->type != attr2->klass->type)
-    return FALSE;
-
-  return attr1->klass->equal (attr1, attr2);
-}
-
-/* }}} */
-/* {{{ Attribute types */
-/* {{{ String attribute */
-static PangoAttribute *pango_attr_string_new (const PangoAttrClass *klass,
-                                              const char           *str);
-
-static PangoAttribute *
-pango_attr_string_copy (const PangoAttribute *attr)
-{
-  return pango_attr_string_new (attr->klass, ((PangoAttrString *)attr)->value);
-}
-
-static void
-pango_attr_string_destroy (PangoAttribute *attr)
-{
-  PangoAttrString *sattr = (PangoAttrString *)attr;
-
-  g_free (sattr->value);
-  g_slice_free (PangoAttrString, sattr);
-}
-
-static gboolean
-pango_attr_string_equal (const PangoAttribute *attr1,
-                         const PangoAttribute *attr2)
-{
-  return strcmp (((PangoAttrString *)attr1)->value, ((PangoAttrString *)attr2)->value) == 0;
-}
-
-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);
-
-  return (PangoAttribute *)result;
-}
- /* }}} */
-/* {{{ Language attribute */
-static PangoAttribute *
-pango_attr_language_copy (const PangoAttribute *attr)
-{
-  return pango_attr_language_new (((PangoAttrLanguage *)attr)->value);
-}
-
-static void
-pango_attr_language_destroy (PangoAttribute *attr)
-{
-  PangoAttrLanguage *lattr = (PangoAttrLanguage *)attr;
-
-  g_slice_free (PangoAttrLanguage, lattr);
-}
-
-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);
-
-static PangoAttribute *
-pango_attr_color_copy (const PangoAttribute *attr)
-{
-  const PangoAttrColor *color_attr = (PangoAttrColor *)attr;
-
-  return pango_attr_color_new (attr->klass,
-                               color_attr->color.red,
-                               color_attr->color.green,
-                               color_attr->color.blue);
-}
-
-static void
-pango_attr_color_destroy (PangoAttribute *attr)
-{
-  PangoAttrColor *cattr = (PangoAttrColor *)attr;
-
-  g_slice_free (PangoAttrColor, cattr);
-}
-
-static gboolean
-pango_attr_color_equal (const PangoAttribute *attr1,
-                        const PangoAttribute *attr2)
-{
-  const PangoAttrColor *color_attr1 = (const PangoAttrColor *)attr1;
-  const PangoAttrColor *color_attr2 = (const PangoAttrColor *)attr2;
-
-  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);
-}
-
-static PangoAttribute *
-pango_attr_color_new (const PangoAttrClass *klass,
-                      guint16               red,
-                      guint16               green,
-                      guint16               blue)
-{
-  PangoAttrColor *result = g_slice_new (PangoAttrColor);
-  pango_attribute_init (&result->attr, klass);
-  result->color.red = red;
-  result->color.green = green;
-  result->color.blue = blue;
-
-  return (PangoAttribute *)result;
-}
-/* }}}} */
-/* {{{ Integer attribute */
-static PangoAttribute *pango_attr_int_new (const PangoAttrClass *klass,
-                                           int                   value);
-
-static PangoAttribute *
-pango_attr_int_copy (const PangoAttribute *attr)
-{
-  const PangoAttrInt *int_attr = (PangoAttrInt *)attr;
-
-  return pango_attr_int_new (attr->klass, int_attr->value);
+  return attr;
 }
 
-static void
-pango_attr_int_destroy (PangoAttribute *attr)
+static inline PangoAttribute *
+pango_attr_string_new (PangoAttrType  type,
+                       const char    *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_STRING, 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->str_value = g_strdup (value);
 
-  return (int_attr1->value == int_attr2->value);
+  return attr;
 }
 
-static PangoAttribute *
-pango_attr_int_new (const PangoAttrClass *klass,
-                    int                   value)
+static inline PangoAttribute *
+pango_attr_int_new (PangoAttrType type,
+                    int           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_INT, NULL);
 
-static PangoAttribute *
-pango_attr_float_copy (const PangoAttribute *attr)
-{
-  const PangoAttrFloat *float_attr = (PangoAttrFloat *)attr;
+  attr = pango_attr_init (type);
+  attr->int_value = value;
 
-  return pango_attr_float_new (attr->klass, float_attr->value);
+  return attr;
 }
 
-static void
-pango_attr_float_destroy (PangoAttribute *attr)
+static inline PangoAttribute *
+pango_attr_boolean_new (PangoAttrType type,
+                        gboolean      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_BOOLEAN, 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->boolean_value = value;
 
-  return (float_attr1->value == float_attr2->value);
+  return attr;
 }
 
-static PangoAttribute *
-pango_attr_float_new  (const PangoAttrClass *klass,
-                       double                value)
+static inline PangoAttribute *
+pango_attr_float_new (PangoAttrType type,
+                      double        value)
 {
-  PangoAttrFloat *result = g_slice_new (PangoAttrFloat);
-  pango_attribute_init (&result->attr, klass);
-  result->value = value;
+  PangoAttribute *attr;
 
-  return (PangoAttribute *)result;
-}
-/* }}} */
-/* {{{ Size attribute */
-static PangoAttribute *pango_attr_size_new_internal (int      size,
-                                                     gboolean absolute);
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_FLOAT, NULL);
 
-static PangoAttribute *
-pango_attr_size_copy (const PangoAttribute *attr)
-{
-  const PangoAttrSize *size_attr = (PangoAttrSize *)attr;
+  attr = pango_attr_init (type);
+  attr->double_value = value;
 
-  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);
+  return attr;
 }
 
-static void
-pango_attr_size_destroy (PangoAttribute *attr)
+static inline PangoAttribute *
+pango_attr_color_new (PangoAttrType type,
+                      guint16       red,
+                      guint16       green,
+                      guint16       blue)
 {
-  PangoAttrSize *sattr = (PangoAttrSize *)attr;
+  PangoAttribute *attr;
 
-  g_slice_free (PangoAttrSize, sattr);
-}
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_COLOR, NULL);
 
-static gboolean
-pango_attr_size_equal (const PangoAttribute *attr1,
-                       const PangoAttribute *attr2)
-{
-  const PangoAttrSize *size_attr1 = (const PangoAttrSize *)attr1;
-  const PangoAttrSize *size_attr2 = (const PangoAttrSize *)attr2;
+  attr = pango_attr_init (type);
+  attr->color_value.red = red;
+  attr->color_value.green = green;
+  attr->color_value.blue = blue;
 
-  return size_attr1->size == size_attr2->size;
+  return attr;
 }
 
-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;
-}
-/* }}} */
-/* {{{ Font description attribute */
-static PangoAttribute *
-pango_attr_font_desc_copy (const PangoAttribute *attr)
+static inline PangoAttribute *
+pango_attr_lang_new (PangoAttrType  type,
+                     PangoLanguage *value)
 {
-  const PangoAttrFontDesc *desc_attr = (const PangoAttrFontDesc *)attr;
+  PangoAttribute *attr;
 
-  return pango_attr_font_desc_new (desc_attr->desc);
-}
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_LANGUAGE, NULL);
 
-static void
-pango_attr_font_desc_destroy (PangoAttribute *attr)
-{
-  PangoAttrFontDesc *desc_attr = (PangoAttrFontDesc *)attr;
+  attr = pango_attr_init (type);
+  attr->lang_value  = value;
 
-  pango_font_description_free (desc_attr->desc);
-  g_slice_free (PangoAttrFontDesc, desc_attr);
+  return attr;
 }
 
-static gboolean
-pango_attr_font_desc_equal (const PangoAttribute *attr1,
-                            const PangoAttribute *attr2)
+static inline PangoAttribute *
+pango_attr_font_description_new (PangoAttrType               type,
+                                 const PangoFontDescription *value)
 {
-  const PangoAttrFontDesc *desc_attr1 = (const PangoAttrFontDesc *)attr1;
-  const PangoAttrFontDesc *desc_attr2 = (const PangoAttrFontDesc *)attr2;
+  PangoAttribute *attr;
 
-  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);
-}
-/* }}} */
-/* }}} */
-/* {{{ Private API */
+  g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_FONT_DESC, NULL);
 
-gboolean
-pango_attribute_affects_itemization (PangoAttribute *attr,
-                                     gpointer        data)
-{
-  switch ((int)attr->klass->type)
-    {
-    /* These affect font selection */
-    case PANGO_ATTR_LANGUAGE:
-    case PANGO_ATTR_FAMILY:
-    case PANGO_ATTR_STYLE:
-    case PANGO_ATTR_WEIGHT:
-    case PANGO_ATTR_VARIANT:
-    case PANGO_ATTR_STRETCH:
-    case PANGO_ATTR_SIZE:
-    case PANGO_ATTR_FONT_DESC:
-    case PANGO_ATTR_SCALE:
-    case PANGO_ATTR_FALLBACK:
-    case PANGO_ATTR_ABSOLUTE_SIZE:
-    case PANGO_ATTR_GRAVITY:
-    case PANGO_ATTR_GRAVITY_HINT:
-    case PANGO_ATTR_FONT_SCALE:
-    /* These need to be constant across runs */
-    case PANGO_ATTR_LETTER_SPACING:
-    case PANGO_ATTR_RISE:
-    case PANGO_ATTR_BASELINE_SHIFT:
-    case PANGO_ATTR_LINE_HEIGHT:
-    case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-    case PANGO_ATTR_TEXT_TRANSFORM:
-      return TRUE;
-    default:
-      return FALSE;
-    }
-}
+  attr = pango_attr_init (type);
+  attr->font_value  = pango_font_description_copy (value);
 
-gboolean
-pango_attribute_affects_break_or_shape (PangoAttribute *attr,
-                                        gpointer        data)
-{
-  switch ((int)attr->klass->type)
-    {
-    /* Affects breaks */
-    case PANGO_ATTR_ALLOW_BREAKS:
-    case PANGO_ATTR_WORD:
-    case PANGO_ATTR_SENTENCE:
-    case PANGO_ATTR_PARAGRAPH:
-    /* Affects shaping */
-    case PANGO_ATTR_INSERT_HYPHENS:
-    case PANGO_ATTR_FONT_FEATURES:
-    case PANGO_ATTR_SHOW:
-      return TRUE;
-    default:
-      return FALSE;
-    }
+  return attr;
 }
 
 /* }}} */
-/* {{{ Public API */
+/* {{{ Attribute types */
 
 /**
  * pango_attr_family_new:
@@ -552,16 +159,7 @@ pango_attribute_affects_break_or_shape (PangoAttribute *attr,
 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);
 }
 
 /**
@@ -577,20 +175,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);
 }
 
 /**
@@ -610,14 +195,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);
 }
 
 /**
@@ -637,14 +215,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);
 }
 
 /**
@@ -658,11 +229,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
@@ -678,7 +250,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);
 }
 
 /**
@@ -694,14 +266,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);
 }
 
 /**
@@ -717,14 +282,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);
 }
 
 /**
@@ -739,14 +297,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);
 }
 
 /**
@@ -760,16 +311,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);
 }
 
 /**
@@ -788,18 +332,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);
 }
 
 /**
@@ -815,14 +348,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);
 }
 
 /**
@@ -847,14 +373,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);
 }
 
 /**
@@ -870,14 +389,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);
 }
 
 /**
@@ -902,14 +414,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);
 }
 
 /**
@@ -926,14 +431,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);
 }
 
 /**
@@ -961,14 +459,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);
 }
 
 /**
@@ -976,7 +467,6 @@ pango_attr_baseline_shift_new (int rise)
  * @scale: a `PangoFontScale` value, which indicates font size change relative
  *   to the size of the previous run.
  *
- *
  * Create a new font scale attribute.
  *
  * The effect of this attribute is to change the font size of a run,
@@ -991,14 +481,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);
 }
 
 /**
@@ -1017,14 +500,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);
 }
 
 /**
@@ -1048,14 +524,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);
 }
 
 /**
@@ -1074,14 +543,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);
 }
 
 /**
@@ -1099,16 +561,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);
 }
 
 /**
@@ -1126,14 +581,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);
 }
 
 /**
@@ -1153,18 +601,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);
 }
 
 /**
@@ -1182,14 +623,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);
 }
 
 /**
@@ -1207,14 +641,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);
 }
 
 /**
@@ -1235,14 +662,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);
 }
 
 /**
@@ -1264,14 +684,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);
 }
 
 /**
@@ -1290,20 +703,14 @@ 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);
 }
 
 /**
  * pango_attr_word_new:
  *
- * Marks the range of the attribute as a single word.
+ * Create a new attribute that marks its range
+ * as a single word.
  *
  * Note that this may require adjustments to word and
  * sentence classification around the range.
@@ -1317,20 +724,14 @@ 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);
 }
 
 /**
  * pango_attr_sentence_new:
  *
- * Marks the range of the attribute as a single sentence.
+ * Create a new attribute that marks its range
+ * as a single sentence.
  *
  * Note that this may require adjustments to word and
  * sentence classification around the range.
@@ -1344,20 +745,13 @@ 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);
 }
 
 /**
  * pango_attr_paragraph_new:
  *
- * Marks the range of the attribute as a single paragraph.
+ * Create a new attribute that marks its range as a single paragraph.
  *
  * Newlines and similar characters in the range of the attribute
  * will not be treated as paragraph separators.
@@ -1369,14 +763,7 @@ pango_attr_sentence_new (void)
 PangoAttribute *
 pango_attr_paragraph_new (void)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_PARAGRAPH,
-    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);
 }
 
 /**
@@ -1394,14 +781,7 @@ pango_attr_paragraph_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);
 }
 
 /**
@@ -1426,21 +806,15 @@ 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);
 }
 
 /**
  * pango_attr_line_height_new:
  * @factor: the scaling factor to apply to the logical height
  *
- * Modify the height of logical line extents by a factor.
+ * Create a new attribute that modifies the height
+ * of logical line extents by a factor.
  *
  * This affects the values returned by
  * [method Pango Line.get_extents] and
@@ -1449,21 +823,15 @@ 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);
 }
 
 /**
  * pango_attr_line_height_new_absolute:
  * @height: the line height, in %PANGO_SCALE-ths of a point
  *
- * Override the height of logical line extents to be @height.
+ * Create a new attribute that overrides the height
+ * of logical line extents to be @height.
  *
  * This affects the values returned by
  * [method Pango Line.get_extents],
@@ -1472,22 +840,16 @@ 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);
 }
 
 /**
  * pango_attr_line_spacing_new:
  * @spacing: the line spacing, in %PANGO_SCALE-ths of a point
  *
- * Spacing to be added to the leading from font metrics,
- * if not overridden by line spacing attributes.
+ * Create a new attribute that adds space to the
+ * leading from font metrics, if not overridden
+ * by line spacing attributes.
  *
  * This affects the values returned by
  * [method Pango Line.get_extents],
@@ -1496,14 +858,7 @@ pango_attr_line_height_new_absolute (int height)
 PangoAttribute *
 pango_attr_line_spacing_new (int spacing)
 {
-  static const PangoAttrClass klass = {
-    PANGO_ATTR_LINE_SPACING,
-    pango_attr_int_copy,
-    pango_attr_int_destroy,
-    pango_attr_int_equal
-  };
-
-  return pango_attr_int_new (&klass, spacing);
+  return pango_attr_int_new (PANGO_ATTR_LINE_SPACING, spacing);
 }
 
 /**
@@ -1522,2018 +877,27 @@ pango_attr_line_spacing_new (int spacing)
 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);
-}
-/* }}} */
-/* {{{ Binding helpers */
-
-/**
- * pango_attribute_as_int:
- * @attr: A `PangoAttribute` such as weight
- *
- * Returns the attribute cast to `PangoAttrInt`.
- *
- * This is mainly useful for language bindings.
- *
- * Returns: (nullable) (transfer none): The attribute as `PangoAttrInt`,
- *   or %NULL if it's not an integer attribute
- *
- * Since: 1.50
- */
-PangoAttrInt *
-pango_attribute_as_int (PangoAttribute *attr)
-{
-  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_LINE_SPACING:
-    case PANGO_ATTR_TEXT_TRANSFORM:
-    case PANGO_ATTR_WORD:
-    case PANGO_ATTR_SENTENCE:
-    case PANGO_ATTR_PARAGRAPH:
-    case PANGO_ATTR_BASELINE_SHIFT:
-    case PANGO_ATTR_FONT_SCALE:
-      return (PangoAttrInt *)attr;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_SCALE:
-    case PANGO_ATTR_LINE_HEIGHT:
-      return (PangoAttrFloat *)attr;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FAMILY:
-      return (PangoAttrString *)attr;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_SIZE:
-    case PANGO_ATTR_ABSOLUTE_SIZE:
-      return (PangoAttrSize *)attr;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  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;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FONT_DESC:
-      return (PangoAttrFontDesc *)attr;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_FONT_FEATURES:
-      return (PangoAttrFontFeatures *)attr;
-
-    default:
-      return NULL;
-    }
-}
-
-/**
- * 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)
-{
-  switch ((int)attr->klass->type)
-    {
-    case PANGO_ATTR_LANGUAGE:
-      return (PangoAttrLanguage *)attr;
-
-    default:
-      return NULL;
-    }
+  return pango_attr_int_new (PANGO_ATTR_TEXT_TRANSFORM, transform);
 }
 
 /* }}} */
-/* {{{ Attribute List */
-
-G_DEFINE_BOXED_TYPE (PangoAttrList, pango_attr_list,
-                     pango_attr_list_copy,
-                     pango_attr_list_unref);
-
-void
-_pango_attr_list_init (PangoAttrList *list)
-{
-  list->ref_count = 1;
-  list->attributes = NULL;
-}
+/* {{{ Private API */
 
-/**
- * pango_attr_list_new:
- *
- * Create a new empty attribute list with a reference
- * count of one.
- *
- * Return value: (transfer full): the newly allocated
- *   `PangoAttrList`, which should be freed with
- *   [method@Pango.AttrList.unref]
- */
-PangoAttrList *
-pango_attr_list_new (void)
+gboolean
+pango_attribute_affects_itemization (PangoAttribute *attr,
+                                     gpointer        data)
 {
-  PangoAttrList *list = g_slice_new (PangoAttrList);
-
-  _pango_attr_list_init (list);
-
-  return list;
+  return (PANGO_ATTR_AFFECTS (attr) & PANGO_ATTR_AFFECTS_ITEMIZATION) != 0;
 }
 
-/**
- * pango_attr_list_ref:
- * @list: (nullable): a `PangoAttrList`
- *
- * Increase the reference count of the given attribute
- * list by one.
- *
- * Return value: The attribute list passed in
- *
- * Since: 1.10
- */
-PangoAttrList *
-pango_attr_list_ref (PangoAttrList *list)
+gboolean
+pango_attribute_affects_break_or_shape (PangoAttribute *attr,
+                                        gpointer        data)
 {
-  if (list == NULL)
-    return NULL;
-
-  g_atomic_int_inc ((int *) &list->ref_count);
-
-  return list;
+  return (PANGO_ATTR_AFFECTS (attr) & (PANGO_ATTR_AFFECTS_BREAKING |
+                                       PANGO_ATTR_AFFECTS_SHAPING)) != 0;
 }
 
-void
-_pango_attr_list_destroy (PangoAttrList *list)
-{
-  guint i, p;
-
-  if (!list->attributes)
-    return;
-
-  for (i = 0, p = list->attributes->len; i < p; i++)
-    {
-      PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
-
-      attr->klass->destroy (attr);
-    }
-
-  g_ptr_array_free (list->attributes, TRUE);
-}
-
-/**
- * pango_attr_list_unref:
- * @list: (nullable): a `PangoAttrList`
- *
- * Decrease the reference count of the given attribute
- * list by one.
- *
- * If the result is zero, free the attribute list
- * and the attributes it contains.
- */
-void
-pango_attr_list_unref (PangoAttrList *list)
-{
-  if (list == NULL)
-    return;
-
-  g_return_if_fail (list->ref_count > 0);
-
-  if (g_atomic_int_dec_and_test ((int *) &list->ref_count))
-    {
-      _pango_attr_list_destroy (list);
-      g_slice_free (PangoAttrList, list);
-    }
-}
-
-/**
- * pango_attr_list_copy:
- * @list: (nullable): a `PangoAttrList`
- *
- * Copy @list and return an identical new list.
- *
- * Return value: (nullable): the newly allocated
- *   `PangoAttrList`, with a reference count of one,
- *   which should be freed with [method@Pango.AttrList.unref].
- *   Returns %NULL if @list was %NULL.
- */
-PangoAttrList *
-pango_attr_list_copy (PangoAttrList *list)
-{
-  PangoAttrList *new;
-
-  if (list == NULL)
-    return NULL;
-
-  new = pango_attr_list_new ();
-  if (!list->attributes || list->attributes->len == 0)
-    return new;
-
-  new->attributes = g_ptr_array_copy (list->attributes, (GCopyFunc)pango_attribute_copy, NULL);
-
-  return new;
-}
-
-static void
-pango_attr_list_insert_internal (PangoAttrList  *list,
-                                 PangoAttribute *attr,
-                                 gboolean        before)
-{
-  const guint start_index = attr->start_index;
-  PangoAttribute *last_attr;
-
-  if (G_UNLIKELY (!list->attributes))
-    list->attributes = g_ptr_array_new ();
-
-  if (list->attributes->len == 0)
-    {
-      g_ptr_array_add (list->attributes, attr);
-      return;
-    }
-
-  g_assert (list->attributes->len > 0);
-
-  last_attr = g_ptr_array_index (list->attributes, list->attributes->len - 1);
-
-  if (last_attr->start_index < start_index ||
-      (!before && last_attr->start_index == start_index))
-    {
-      g_ptr_array_add (list->attributes, attr);
-    }
-  else
-    {
-      guint i, p;
-
-      for (i = 0, p = list->attributes->len; i < p; i++)
-        {
-          PangoAttribute *cur = g_ptr_array_index (list->attributes, i);
-
-          if (cur->start_index > start_index ||
-              (before && cur->start_index == start_index))
-            {
-              g_ptr_array_insert (list->attributes, i, attr);
-              break;
-            }
-        }
-    }
-}
-
-/**
- * pango_attr_list_insert:
- * @list: a `PangoAttrList`
- * @attr: (transfer full): the attribute to insert
- *
- * Insert the given attribute into the `PangoAttrList`.
- *
- * It will be inserted after all other attributes with a
- * matching @start_index.
- */
-void
-pango_attr_list_insert (PangoAttrList  *list,
-                        PangoAttribute *attr)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (attr != NULL);
-
-  pango_attr_list_insert_internal (list, attr, FALSE);
-}
-
-/**
- * pango_attr_list_insert_before:
- * @list: a `PangoAttrList`
- * @attr: (transfer full): the attribute to insert
- *
- * Insert the given attribute into the `PangoAttrList`.
- *
- * It will be inserted before all other attributes with a
- * matching @start_index.
- */
-void
-pango_attr_list_insert_before (PangoAttrList  *list,
-                               PangoAttribute *attr)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (attr != NULL);
-
-  pango_attr_list_insert_internal (list, attr, TRUE);
-}
-
-/**
- * pango_attr_list_change:
- * @list: a `PangoAttrList`
- * @attr: (transfer full): the attribute to insert
- *
- * Insert the given attribute into the `PangoAttrList`.
- *
- * It will replace any attributes of the same type
- * on that segment and be merged with any adjoining
- * attributes that are identical.
- *
- * This function is slower than [method@Pango.AttrList.insert]
- * for creating an attribute list in order (potentially
- * much slower for large lists). However,
- * [method@Pango.AttrList.insert] is not suitable for
- * continually changing a set of attributes since it
- * never removes or combines existing attributes.
- */
-void
-pango_attr_list_change (PangoAttrList  *list,
-                        PangoAttribute *attr)
-{
-  guint i, p;
-  guint start_index = attr->start_index;
-  guint end_index = attr->end_index;
-  gboolean inserted;
-
-  g_return_if_fail (list != NULL);
-
-  if (start_index == end_index) /* empty, nothing to do */
-    {
-      pango_attribute_destroy (attr);
-      return;
-    }
-
-  if (!list->attributes || list->attributes->len == 0)
-    {
-      pango_attr_list_insert (list, attr);
-      return;
-    }
-
-  inserted = FALSE;
-  for (i = 0, p = list->attributes->len; i < p; i++)
-    {
-      PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
-
-      if (tmp_attr->start_index > start_index)
-        {
-          g_ptr_array_insert (list->attributes, i, attr);
-          inserted = TRUE;
-          break;
-        }
-
-      if (tmp_attr->klass->type != attr->klass->type)
-        continue;
-
-      if (tmp_attr->end_index < start_index)
-        continue; /* This attr does not overlap with the new one */
-
-      g_assert (tmp_attr->start_index <= start_index);
-      g_assert (tmp_attr->end_index >= start_index);
-
-      if (pango_attribute_equal (tmp_attr, attr))
-        {
-          /* We can merge the new attribute with this attribute
-           */
-          if (tmp_attr->end_index >= end_index)
-            {
-              /* We are totally overlapping the previous attribute.
-               * No action is needed.
-               */
-              pango_attribute_destroy (attr);
-              return;
-            }
-
-          tmp_attr->end_index = end_index;
-          pango_attribute_destroy (attr);
-
-          attr = tmp_attr;
-          inserted = TRUE;
-          break;
-        }
-      else
-        {
-          /* Split, truncate, or remove the old attribute
-           */
-          if (tmp_attr->end_index > end_index)
-            {
-              PangoAttribute *end_attr = pango_attribute_copy (tmp_attr);
-
-              end_attr->start_index = end_index;
-              pango_attr_list_insert (list, end_attr);
-            }
-
-          if (tmp_attr->start_index == start_index)
-            {
-              pango_attribute_destroy (tmp_attr);
-              g_ptr_array_remove_index (list->attributes, i);
-              break;
-            }
-          else
-            {
-              tmp_attr->end_index = start_index;
-            }
-        }
-    }
-
-  if (!inserted)
-    /* we didn't insert attr yet */
-    pango_attr_list_insert (list, attr);
-
-  /* We now have the range inserted into the list one way or the
-   * other. Fix up the remainder
-   */
-  /* Attention: No i = 0 here. */
-  for (i = i + 1, p = list->attributes->len; i < p; i++)
-    {
-      PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
-
-      if (tmp_attr->start_index > end_index)
-        break;
-
-      if (tmp_attr->klass->type != attr->klass->type)
-        continue;
-
-      if (tmp_attr == attr)
-        continue;
-
-      if (tmp_attr->end_index <= attr->end_index ||
-          pango_attribute_equal (tmp_attr, attr))
-        {
-          /* We can merge the new attribute with this attribute. */
-          attr->end_index = MAX (end_index, tmp_attr->end_index);
-          pango_attribute_destroy (tmp_attr);
-          g_ptr_array_remove_index (list->attributes, i);
-          i--;
-          p--;
-          continue;
-        }
-      else
-        {
-          /* Trim the start of this attribute that it begins at the end
-           * of the new attribute. This may involve moving it in the list
-           * to maintain the required non-decreasing order of start indices.
-           */
-          int k, m;
-
-          tmp_attr->start_index = attr->end_index;
-
-          for (k = i + 1, m = list->attributes->len; k < m; k++)
-            {
-              PangoAttribute *tmp_attr2 = g_ptr_array_index (list->attributes, k);
-
-              if (tmp_attr2->start_index >= tmp_attr->start_index)
-                break;
-
-              g_ptr_array_index (list->attributes, k - 1) = tmp_attr2;
-              g_ptr_array_index (list->attributes, k) = tmp_attr;
-            }
-        }
-    }
-}
-
-/**
- * pango_attr_list_update:
- * @list: a `PangoAttrList`
- * @pos: the position of the change
- * @remove: the number of removed bytes
- * @add: the number of added bytes
- *
- * Update indices of attributes in @list for a change in the
- * text they refer to.
- *
- * The change that this function applies is removing @remove
- * bytes at position @pos and inserting @add bytes instead.
- *
- * Attributes that fall entirely in the (@pos, @pos + @remove)
- * range are removed.
- *
- * Attributes that start or end inside the (@pos, @pos + @remove)
- * range are shortened to reflect the removal.
- *
- * Attributes start and end positions are updated if they are
- * behind @pos + @remove.
- *
- * Since: 1.44
- */
-void
-pango_attr_list_update (PangoAttrList *list,
-                        int             pos,
-                        int             remove,
-                        int             add)
-{
-  guint i, p;
-
-  g_return_if_fail (pos >= 0);
-  g_return_if_fail (remove >= 0);
-  g_return_if_fail (add >= 0);
-
-  if (list->attributes)
-    for (i = 0, p = list->attributes->len; i < p; i++)
-      {
-        PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
-
-        if (attr->start_index >= pos &&
-          attr->end_index < pos + remove)
-          {
-            pango_attribute_destroy (attr);
-            g_ptr_array_remove_index (list->attributes, i);
-            i--; /* Look at this index again */
-            p--;
-            continue;
-          }
-
-        if (attr->start_index != PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING)
-          {
-            if (attr->start_index >= pos &&
-                attr->start_index < pos + remove)
-              {
-                attr->start_index = pos + add;
-              }
-            else if (attr->start_index >= pos + remove)
-              {
-                attr->start_index += add - remove;
-              }
-          }
-
-        if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END)
-          {
-            if (attr->end_index >= pos &&
-                attr->end_index < pos + remove)
-              {
-                attr->end_index = pos;
-              }
-            else if (attr->end_index >= pos + remove)
-              {
-                if (add > remove &&
-                    G_MAXUINT - attr->end_index < add - remove)
-                  attr->end_index = G_MAXUINT;
-                else
-                  attr->end_index += add - remove;
-              }
-          }
-      }
-}
-
-/**
- * pango_attr_list_splice:
- * @list: a `PangoAttrList`
- * @other: another `PangoAttrList`
- * @pos: the position in @list at which to insert @other
- * @len: the length of the spliced segment. (Note that this
- *   must be specified since the attributes in @other may only
- *   be present at some subsection of this range)
- *
- * This function opens up a hole in @list, fills it
- * in with attributes from the left, and then merges
- * @other on top of the hole.
- *
- * This operation is equivalent to stretching every attribute
- * that applies at position @pos in @list by an amount @len,
- * and then calling [method@Pango.AttrList.change] with a copy
- * of each attribute in @other in sequence (offset in position
- * by @pos, and limited in length to @len).
- *
- * This operation proves useful for, for instance, inserting
- * a pre-edit string in the middle of an edit buffer.
- *
- * For backwards compatibility, the function behaves differently
- * when @len is 0. In this case, the attributes from @other are
- * not imited to @len, and are just overlayed on top of @list.
- *
- * This mode is useful for merging two lists of attributes together.
- */
-void
-pango_attr_list_splice (PangoAttrList *list,
-                        PangoAttrList *other,
-                        gint           pos,
-                        gint           len)
-{
-  guint i, p;
-  guint upos, ulen;
-  guint end;
-
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (other != NULL);
-  g_return_if_fail (pos >= 0);
-  g_return_if_fail (len >= 0);
-
-  upos = (guint)pos;
-  ulen = (guint)len;
-
-/* This definition only works when a and b are unsigned; overflow
- * isn't defined in the C standard for signed integers
- */
-#define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? G_MAXUINT : (a) + (b))
-
-  end = CLAMP_ADD (upos, ulen);
-
-  if (list->attributes)
-    for (i = 0, p = list->attributes->len; i < p; i++)
-      {
-        PangoAttribute *attr = g_ptr_array_index (list->attributes, i);;
-
-        if (attr->start_index <= upos)
-          {
-            if (attr->end_index > upos)
-              attr->end_index = CLAMP_ADD (attr->end_index, ulen);
-          }
-        else
-          {
-            /* This could result in a zero length attribute if it
-             * gets squashed up against G_MAXUINT, but deleting such
-             * an element could (in theory) suprise the caller, so
-             * we don't delete it.
-             */
-            attr->start_index = CLAMP_ADD (attr->start_index, ulen);
-            attr->end_index = CLAMP_ADD (attr->end_index, ulen);
-         }
-      }
-
-  if (!other->attributes || other->attributes->len == 0)
-    return;
-
-  for (i = 0, p = other->attributes->len; i < p; i++)
-    {
-      PangoAttribute *attr = pango_attribute_copy (g_ptr_array_index (other->attributes, i));
-      if (ulen > 0)
-        {
-          attr->start_index = MIN (CLAMP_ADD (attr->start_index, upos), end);
-          attr->end_index = MIN (CLAMP_ADD (attr->end_index, upos), end);
-        }
-      else
-        {
-          attr->start_index = CLAMP_ADD (attr->start_index, upos);
-          attr->end_index = CLAMP_ADD (attr->end_index, upos);
-        }
-
-      /* Same as above, the attribute could be squashed to zero-length; here
-       * pango_attr_list_change() will take care of deleting it.
-       */
-      pango_attr_list_change (list, attr);
-    }
-#undef CLAMP_ADD
-}
-
-/**
- * pango_attr_list_get_attributes:
- * @list: a `PangoAttrList`
- *
- * Gets a list of all attributes in @list.
- *
- * Return value: (element-type Pango.Attribute) (transfer full):
- *   a list of all attributes in @list. To free this value,
- *   call [method@Pango.Attribute.destroy] on each value and
- *   g_slist_free() on the list.
- *
- * Since: 1.44
- */
-GSList *
-pango_attr_list_get_attributes (PangoAttrList *list)
-{
-  GSList *result = NULL;
-  guint i, p;
-
-  g_return_val_if_fail (list != NULL, NULL);
-
-  if (!list->attributes || list->attributes->len == 0)
-    return NULL;
-
-  for (i = 0, p = list->attributes->len; i < p; i++)
-    {
-      PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
-
-      result = g_slist_prepend (result, pango_attribute_copy (attr));
-    }
-
-  return g_slist_reverse (result);
-}
-
-/**
- * pango_attr_list_equal:
- * @list: a `PangoAttrList`
- * @other_list: the other `PangoAttrList`
- *
- * Checks whether @list and @other_list contain the same
- * attributes and whether those attributes apply to the
- * same ranges.
- *
- * Beware that this will return wrong values if any list
- * contains duplicates.
- *
- * Return value: %TRUE if the lists are equal, %FALSE if
- *   they aren't
- *
- * Since: 1.46
- */
-gboolean
-pango_attr_list_equal (PangoAttrList *list,
-                       PangoAttrList *other_list)
-{
-  GPtrArray *attrs, *other_attrs;
-  guint64 skip_bitmask = 0;
-  guint i;
-
-  if (list == other_list)
-    return TRUE;
-
-  if (list == NULL || other_list == NULL)
-    return FALSE;
-
-  if (list->attributes == NULL || other_list->attributes == NULL)
-    return list->attributes == other_list->attributes;
-
-  attrs = list->attributes;
-  other_attrs = other_list->attributes;
-
-  if (attrs->len != other_attrs->len)
-    return FALSE;
-
-  for (i = 0; i < attrs->len; i++)
-    {
-      PangoAttribute *attr = g_ptr_array_index (attrs, i);
-      gboolean attr_equal = FALSE;
-      guint other_attr_index;
-
-      for (other_attr_index = 0; other_attr_index < other_attrs->len; other_attr_index++)
-        {
-          PangoAttribute *other_attr = g_ptr_array_index (other_attrs, other_attr_index);
-          guint64 other_attr_bitmask = other_attr_index < 64 ? 1 << other_attr_index : 0;
-
-          if ((skip_bitmask & other_attr_bitmask) != 0)
-            continue;
-
-          if (attr->start_index == other_attr->start_index &&
-              attr->end_index == other_attr->end_index &&
-              pango_attribute_equal (attr, other_attr))
-            {
-              skip_bitmask |= other_attr_bitmask;
-              attr_equal = TRUE;
-              break;
-            }
-
-        }
-
-      if (!attr_equal)
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-gboolean
-_pango_attr_list_has_attributes (const PangoAttrList *list)
-{
-  return list && list->attributes != NULL && list->attributes->len > 0;
-}
-
-/**
- * pango_attr_list_filter:
- * @list: a `PangoAttrList`
- * @func: (scope call) (closure data): callback function;
- *   returns %TRUE if an attribute should be filtered out
- * @data: (closure): Data to be passed to @func
- *
- * Given a `PangoAttrList` and callback function, removes
- * any elements of @list for which @func returns %TRUE and
- * inserts them into a new list.
- *
- * Return value: (transfer full) (nullable): the new
- *   `PangoAttrList` or %NULL if no attributes of the
- *   given types were found
- *
- * Since: 1.2
- */
-PangoAttrList *
-pango_attr_list_filter (PangoAttrList       *list,
-                        PangoAttrFilterFunc  func,
-                        gpointer             data)
-
-{
-  PangoAttrList *new = NULL;
-  guint i, p;
-
-  g_return_val_if_fail (list != NULL, NULL);
-
-  if (!list->attributes || list->attributes->len == 0)
-    return NULL;
-
-  for (i = 0, p = list->attributes->len; i < p; i++)
-    {
-      PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i);
-
-      if ((*func) (tmp_attr, data))
-        {
-          g_ptr_array_remove_index (list->attributes, i);
-          i--; /* Need to look at this index again */
-          p--;
-
-          if (G_UNLIKELY (!new))
-            {
-              new = pango_attr_list_new ();
-              new->attributes = g_ptr_array_new ();
-            }
-
-          g_ptr_array_add (new->attributes, tmp_attr);
-        }
-    }
-
-  return new;
-}
-
-/* {{{ PangoAttrList serialization */
-
-/* We serialize attribute lists to strings. The format
- * is a comma-separated list of the attributes in the order
- * in which they are in the list, with each attribute having
- * this format:
- *
- * START END NICK VALUE
- *
- * Values that can contain a comma, such as font descriptions
- * are quoted with "".
- */
-
-static const char *
-get_attr_type_nick (PangoAttrType attr_type)
-{
-  GEnumClass *enum_class;
-  GEnumValue *enum_value;
-
-  enum_class = g_type_class_ref (pango_attr_type_get_type ());
-  enum_value = g_enum_get_value (enum_class, attr_type);
-  g_type_class_unref (enum_class);
-
-  if (enum_value)
-    return enum_value->value_nick;
-  else
-    return pango_attr_type_get_name (attr_type);
-}
-
-static GType
-get_attr_value_type (PangoAttrType type)
-{
-  switch ((int)type)
-    {
-    case PANGO_ATTR_STYLE: return PANGO_TYPE_STYLE;
-    case PANGO_ATTR_WEIGHT: return PANGO_TYPE_WEIGHT;
-    case PANGO_ATTR_VARIANT: return PANGO_TYPE_VARIANT;
-    case PANGO_ATTR_STRETCH: return PANGO_TYPE_STRETCH;
-    case PANGO_ATTR_GRAVITY: return PANGO_TYPE_GRAVITY;
-    case PANGO_ATTR_GRAVITY_HINT: return PANGO_TYPE_GRAVITY_HINT;
-    case PANGO_ATTR_UNDERLINE: return PANGO_TYPE_UNDERLINE;
-    case PANGO_ATTR_OVERLINE: return PANGO_TYPE_OVERLINE;
-    case PANGO_ATTR_BASELINE_SHIFT: return PANGO_TYPE_BASELINE_SHIFT;
-    case PANGO_ATTR_FONT_SCALE: return PANGO_TYPE_FONT_SCALE;
-    case PANGO_ATTR_TEXT_TRANSFORM: return PANGO_TYPE_TEXT_TRANSFORM;
-    default: return G_TYPE_INVALID;
-    }
-}
-
-static void
-append_enum_value (GString *str,
-                   GType    type,
-                   int      value)
-{
-  GEnumClass *enum_class;
-  GEnumValue *enum_value;
-
-  enum_class = g_type_class_ref (type);
-  enum_value = g_enum_get_value (enum_class, value);
-  g_type_class_unref (enum_class);
-
-  if (enum_value)
-    g_string_append_printf (str, " %s", enum_value->value_nick);
-  else
-    g_string_append_printf (str, " %d", value);
-}
-
-static void
-attr_print (GString        *str,
-            PangoAttribute *attr)
-{
-  PangoAttrString *string;
-  PangoAttrLanguage *lang;
-  PangoAttrInt *integer;
-  PangoAttrFloat *flt;
-  PangoAttrFontDesc *font;
-  PangoAttrColor *color;
-  PangoAttrSize *size;
-  PangoAttrFontFeatures *features;
-
-  g_string_append_printf (str, "%u %u ", attr->start_index, attr->end_index);
-
-  g_string_append (str, get_attr_type_nick (attr->klass->type));
-
-  if (attr->klass->type == PANGO_ATTR_WEIGHT ||
-      attr->klass->type == PANGO_ATTR_STYLE ||
-      attr->klass->type == PANGO_ATTR_STRETCH ||
-      attr->klass->type == PANGO_ATTR_VARIANT ||
-      attr->klass->type == PANGO_ATTR_GRAVITY ||
-      attr->klass->type == PANGO_ATTR_GRAVITY_HINT ||
-      attr->klass->type == PANGO_ATTR_UNDERLINE ||
-      attr->klass->type == PANGO_ATTR_OVERLINE ||
-      attr->klass->type == PANGO_ATTR_BASELINE_SHIFT ||
-      attr->klass->type == PANGO_ATTR_FONT_SCALE ||
-      attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM)
-    append_enum_value (str, get_attr_value_type (attr->klass->type), ((PangoAttrInt *)attr)->value);
-  else if (attr->klass->type == PANGO_ATTR_STRIKETHROUGH ||
-           attr->klass->type == PANGO_ATTR_ALLOW_BREAKS ||
-           attr->klass->type == PANGO_ATTR_INSERT_HYPHENS ||
-           attr->klass->type == PANGO_ATTR_FALLBACK)
-    g_string_append (str, ((PangoAttrInt *)attr)->value ? " true" : " false");
-  else if ((string = pango_attribute_as_string (attr)) != NULL)
-    g_string_append_printf (str, " %s", string->value);
-  else if ((lang = pango_attribute_as_language (attr)) != NULL)
-    g_string_append_printf (str, " %s", pango_language_to_string (lang->value));
-  else if ((integer = pango_attribute_as_int (attr)) != NULL)
-    g_string_append_printf (str, " %d", integer->value);
-  else if ((flt = pango_attribute_as_float (attr)) != NULL)
-    {
-      char buf[20];
-      g_ascii_formatd (buf, 20, "%f", flt->value);
-      g_string_append_printf (str, " %s", buf);
-    }
-  else if ((font = pango_attribute_as_font_desc (attr)) != NULL)
-    {
-      char *s = pango_font_description_to_string (font->desc);
-      g_string_append_printf (str, " \"%s\"", s);
-      g_free (s);
-    }
-  else if ((color = pango_attribute_as_color (attr)) != NULL)
-    {
-      char *s = pango_color_to_string (&color->color);
-      g_string_append_printf (str, " %s", s);
-      g_free (s);
-    }
-  else if ((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 if (pango_attr_type_get_name (attr->klass->type))
-    {
-      g_string_append (str, "NONE");
-    }
-  else
-    {
-      g_warning ("Unhandled attribute type %s (%d)\n",
-                 get_attr_type_nick (attr->klass->type),
-                 attr->klass->type);
-      g_assert_not_reached ();
-    }
-}
-
-/**
- * pango_attr_list_to_string:
- * @list: a `PangoAttrList`
- *
- * Serializes a `PangoAttrList` to a string.
- *
- * No guarantees are made about the format of the string,
- * it may change between Pango versions.
- *
- * The intended use of this function is testing and
- * debugging. The format is not meant as a permanent
- * storage format.
- *
- * Returns: (transfer full): a newly allocated string
- * Since: 1.50
- */
-char *
-pango_attr_list_to_string (PangoAttrList *list)
-{
-  GString *s;
-
-  s = g_string_new ("");
-
-  if (list->attributes)
-    for (int i = 0; i < list->attributes->len; i++)
-      {
-        PangoAttribute *attr = g_ptr_array_index (list->attributes, i);
-
-        if (i > 0)
-          g_string_append (s, "\n");
-        attr_print (s, attr);
-      }
-
-  return g_string_free (s, FALSE);
-}
-
-static PangoAttrType
-get_attr_type_by_nick (const char *nick,
-                       int         len)
-{
-  GEnumClass *enum_class;
-
-  enum_class = g_type_class_ref (pango_attr_type_get_type ());
-  for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
-    {
-      if (ev->value_nick && strncmp (ev->value_nick, nick, len) == 0)
-        {
-          g_type_class_unref (enum_class);
-          return (PangoAttrType) ev->value;
-        }
-    }
-
-  g_type_class_unref (enum_class);
-  return PANGO_ATTR_INVALID;
-}
-
-static int
-get_attr_value (PangoAttrType  type,
-                const char    *str,
-                int            len)
-{
-  GEnumClass *enum_class;
-  char *endp;
-  int value;
-
-  enum_class = g_type_class_ref (get_attr_value_type (type));
-  for (GEnumValue *ev = enum_class->values; ev->value_name; ev++)
-    {
-      if (ev->value_nick && strncmp (ev->value_nick, str, len) == 0)
-        {
-          g_type_class_unref (enum_class);
-          return ev->value;
-        }
-    }
-  g_type_class_unref (enum_class);
-
-  value = g_ascii_strtoll (str, &endp, 10);
-  if (endp - str == len)
-    return value;
-
-  return -1;
-}
-
-static gboolean
-is_valid_end_char (char c)
-{
-  return c == ',' || c == '\n' || c == '\0';
-}
-
-/**
- * pango_attr_list_from_string:
- * @text: a string
- *
- * Deserializes a `PangoAttrList` from a string.
- *
- * This is the counterpart to [method@Pango.AttrList.to_string].
- * See that functions for details about the format.
- *
- * Returns: (transfer full) (nullable): a new `PangoAttrList`
- * Since: 1.50
- */
-PangoAttrList *
-pango_attr_list_from_string (const char *text)
-{
-  PangoAttrList *list;
-  const char *p;
-
-  g_return_val_if_fail (text != NULL, NULL);
-
-  list = pango_attr_list_new ();
-
-  if (*text == '\0')
-    return list;
-
-  list->attributes = g_ptr_array_new ();
-
-  p = text + strspn (text, " \t\n");
-  while (*p)
-    {
-      char *endp;
-      gint64 start_index;
-      gint64 end_index;
-      char *str;
-      PangoAttrType attr_type;
-      PangoAttribute *attr;
-      PangoLanguage *lang;
-      gint64 integer;
-      PangoFontDescription *desc;
-      PangoColor color;
-      double num;
-
-      start_index = g_ascii_strtoll (p, &endp, 10);
-      if (*endp != ' ')
-        goto fail;
-
-      p = endp + strspn (endp, " ");
-      if (!*p)
-        goto fail;
-
-      end_index = g_ascii_strtoll (p, &endp, 10);
-      if (*endp != ' ')
-        goto fail;
-
-      p = endp + strspn (endp, " ");
-
-      endp = (char *)p + strcspn (p, " ");
-      attr_type = get_attr_type_by_nick (p, endp - p);
-
-      p = endp + strspn (endp, " ");
-      if (*p == '\0')
-        goto fail;
-
-#define INT_ATTR(name,type) \
-          integer = g_ascii_strtoll (p, &endp, 10); \
-          if (!is_valid_end_char (*endp)) goto fail; \
-          attr = pango_attr_##name##_new ((type)integer);
-
-#define BOOLEAN_ATTR(name,type) \
-          if (strncmp (p, "true", strlen ("true")) == 0) \
-            { \
-              integer = 1; \
-              endp = (char *)(p + strlen ("true")); \
-            } \
-          else if (strncmp (p, "false", strlen ("false")) == 0) \
-            { \
-              integer = 0; \
-              endp = (char *)(p + strlen ("false")); \
-            } \
-          else \
-            integer = g_ascii_strtoll (p, &endp, 10); \
-          if (!is_valid_end_char (*endp)) goto fail; \
-          attr = pango_attr_##name##_new ((type)integer);
-
-#define ENUM_ATTR(name, type, min, max) \
-          endp = (char *)p + strcspn (p, ",\n"); \
-          integer = get_attr_value (attr_type, p, endp - p); \
-          attr = pango_attr_##name##_new ((type) CLAMP (integer, min, max));
-
-#define FLOAT_ATTR(name) \
-          num = g_ascii_strtod (p, &endp); \
-          if (!is_valid_end_char (*endp)) goto fail; \
-          attr = pango_attr_##name##_new ((float)num);
-
-#define COLOR_ATTR(name) \
-          endp = (char *)p + strcspn (p, ",\n"); \
-          if (!is_valid_end_char (*endp)) goto fail; \
-          str = g_strndup (p, endp - p); \
-          if (!pango_color_parse (&color, str)) \
-            { \
-              g_free (str); \
-              goto fail; \
-            } \
-          attr = pango_attr_##name##_new (color.red, color.green, color.blue); \
-          g_free (str);
-
-      switch (attr_type)
-        {
-        case PANGO_ATTR_INVALID:
-          pango_attr_list_unref (list);
-          return NULL;
-
-        case PANGO_ATTR_LANGUAGE:
-          endp = (char *)p + strcspn (p, ",\n");
-          if (!is_valid_end_char (*endp)) goto fail;
-          str = g_strndup (p, endp - p);
-          lang = pango_language_from_string (str);
-          attr = pango_attr_language_new (lang);
-          g_free (str);
-          break;
-
-        case PANGO_ATTR_FAMILY:
-          endp = (char *)p + strcspn (p, ",\n");
-          if (!is_valid_end_char (*endp)) goto fail;
-          str = g_strndup (p, endp - p);
-          attr = pango_attr_family_new (str);
-          g_free (str);
-          break;
-
-        case PANGO_ATTR_STYLE:
-          ENUM_ATTR(style, PangoStyle, PANGO_STYLE_NORMAL, PANGO_STYLE_ITALIC);
-          break;
-
-        case PANGO_ATTR_WEIGHT:
-          ENUM_ATTR(weight, PangoWeight, PANGO_WEIGHT_THIN, PANGO_WEIGHT_ULTRAHEAVY);
-          break;
-
-        case PANGO_ATTR_VARIANT:
-          ENUM_ATTR(variant, PangoVariant, PANGO_VARIANT_NORMAL, PANGO_VARIANT_TITLE_CAPS);
-          break;
-
-        case PANGO_ATTR_STRETCH:
-          ENUM_ATTR(stretch, PangoStretch, PANGO_STRETCH_ULTRA_CONDENSED, PANGO_STRETCH_ULTRA_EXPANDED);
-          break;
-
-        case PANGO_ATTR_SIZE:
-          INT_ATTR(size, int);
-          break;
-
-        case PANGO_ATTR_FONT_DESC:
-          p++;
-          endp = strchr (p, '"');
-          if (!endp) goto fail;
-          str = g_strndup (p, endp - p);
-          desc = pango_font_description_from_string (str);
-          attr = pango_attr_font_desc_new (desc);
-          pango_font_description_free (desc);
-          g_free (str);
-          endp++;
-          if (!is_valid_end_char (*endp)) goto fail;
-          break;
-
-        case PANGO_ATTR_FOREGROUND:
-          COLOR_ATTR(foreground);
-          break;
-
-        case PANGO_ATTR_BACKGROUND:
-          COLOR_ATTR(background);
-          break;
-
-        case PANGO_ATTR_UNDERLINE:
-          ENUM_ATTR(underline, PangoUnderline, PANGO_UNDERLINE_NONE, PANGO_UNDERLINE_ERROR_LINE);
-          break;
-
-        case PANGO_ATTR_STRIKETHROUGH:
-          BOOLEAN_ATTR(strikethrough, gboolean);
-          break;
-
-        case PANGO_ATTR_RISE:
-          INT_ATTR(rise, int);
-          break;
-
-        case PANGO_ATTR_SCALE:
-          FLOAT_ATTR(scale);
-          break;
-
-        case PANGO_ATTR_FALLBACK:
-          BOOLEAN_ATTR(fallback, gboolean);
-          break;
-
-        case PANGO_ATTR_LETTER_SPACING:
-          INT_ATTR(letter_spacing, int);
-          break;
-
-        case PANGO_ATTR_UNDERLINE_COLOR:
-          COLOR_ATTR(underline_color);
-          break;
-
-        case PANGO_ATTR_STRIKETHROUGH_COLOR:
-          COLOR_ATTR(strikethrough_color);
-          break;
-
-        case PANGO_ATTR_ABSOLUTE_SIZE:
-          integer = g_ascii_strtoll (p, &endp, 10);
-          if (!is_valid_end_char (*endp)) goto fail;
-          attr = pango_attr_size_new_absolute (integer);
-          break;
-
-        case PANGO_ATTR_GRAVITY:
-          ENUM_ATTR(gravity, PangoGravity, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_WEST);
-          break;
-
-        case PANGO_ATTR_FONT_FEATURES:
-          p++;
-          endp = strchr (p, '"');
-          if (!endp) goto fail;
-          str = g_strndup (p, endp - p);
-          attr = pango_attr_font_features_new (str);
-          g_free (str);
-          endp++;
-          if (!is_valid_end_char (*endp)) goto fail;
-          break;
-
-        case PANGO_ATTR_GRAVITY_HINT:
-          ENUM_ATTR(gravity_hint, PangoGravityHint, PANGO_GRAVITY_HINT_NATURAL, PANGO_GRAVITY_HINT_LINE);
-          break;
-
-        case PANGO_ATTR_FOREGROUND_ALPHA:
-          INT_ATTR(foreground_alpha, int);
-          break;
-
-        case PANGO_ATTR_BACKGROUND_ALPHA:
-          INT_ATTR(background_alpha, int);
-          break;
-
-        case PANGO_ATTR_ALLOW_BREAKS:
-          BOOLEAN_ATTR(allow_breaks, gboolean);
-          break;
-
-        case PANGO_ATTR_SHOW:
-          INT_ATTR(show, PangoShowFlags);
-          break;
-
-        case PANGO_ATTR_INSERT_HYPHENS:
-          BOOLEAN_ATTR(insert_hyphens, gboolean);
-          break;
-
-        case PANGO_ATTR_OVERLINE:
-          ENUM_ATTR(overline, PangoOverline, PANGO_OVERLINE_NONE, PANGO_OVERLINE_SINGLE);
-          break;
-
-        case PANGO_ATTR_OVERLINE_COLOR:
-          COLOR_ATTR(overline_color);
-          break;
-
-        case PANGO_ATTR_LINE_HEIGHT:
-          FLOAT_ATTR(line_height);
-          break;
-
-        case PANGO_ATTR_LINE_SPACING:
-          INT_ATTR(line_spacing, int);
-          break;
-
-        case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-          integer = g_ascii_strtoll (p, &endp, 10);
-          if (!is_valid_end_char (*endp)) goto fail;
-          attr = pango_attr_line_height_new_absolute (integer);
-          break;
-
-        case PANGO_ATTR_TEXT_TRANSFORM:
-          ENUM_ATTR(text_transform, PangoTextTransform, PANGO_TEXT_TRANSFORM_NONE, 
PANGO_TEXT_TRANSFORM_CAPITALIZE);
-          break;
-
-        case PANGO_ATTR_WORD:
-          integer = g_ascii_strtoll (p, &endp, 10);
-          if (!is_valid_end_char (*endp)) goto fail;
-          attr = pango_attr_word_new ();
-          break;
-
-        case PANGO_ATTR_SENTENCE:
-          integer = g_ascii_strtoll (p, &endp, 10);
-          if (!is_valid_end_char (*endp)) goto fail;
-          attr = pango_attr_sentence_new ();
-          break;
-
-        case PANGO_ATTR_PARAGRAPH:
-          integer = g_ascii_strtoll (p, &endp, 10);
-          if (!is_valid_end_char (*endp)) goto fail;
-          attr = pango_attr_paragraph_new ();
-          break;
-
-        case PANGO_ATTR_BASELINE_SHIFT:
-          ENUM_ATTR(baseline_shift, PangoBaselineShift, 0, G_MAXINT);
-          break;
-
-        case PANGO_ATTR_FONT_SCALE:
-          ENUM_ATTR(font_scale, PangoFontScale, PANGO_FONT_SCALE_NONE, PANGO_FONT_SCALE_SMALL_CAPS);
-          break;
-
-        default:
-          g_assert_not_reached ();
-        }
-
-      attr->start_index = (guint)start_index;
-      attr->end_index = (guint)end_index;
-      g_ptr_array_add (list->attributes, attr);
-
-      p = endp;
-      if (*p)
-        {
-          if (*p == ',')
-            p++;
-          p += strspn (p, " \n");
-        }
-    }
-
-  goto success;
-
-fail:
-  pango_attr_list_unref (list);
-  list = NULL;
-
-success:
-  return list;
-}
-
-/* }}} */
-/* {{{ Attribute Iterator */
-
-G_DEFINE_BOXED_TYPE (PangoAttrIterator,
-                     pango_attr_iterator,
-                     pango_attr_iterator_copy,
-                     pango_attr_iterator_destroy)
-
-void
-_pango_attr_list_get_iterator (PangoAttrList     *list,
-                               PangoAttrIterator *iterator)
-{
-  iterator->attribute_stack = NULL;
-  iterator->attrs = list->attributes;
-  iterator->n_attrs = iterator->attrs ? iterator->attrs->len : 0;
-
-  iterator->attr_index = 0;
-  iterator->start_index = 0;
-  iterator->end_index = 0;
-
-  if (!pango_attr_iterator_next (iterator))
-    iterator->end_index = G_MAXUINT;
-}
-
-/**
- * pango_attr_list_get_iterator:
- * @list: a `PangoAttrList`
- *
- * Create a iterator initialized to the beginning of the list.
- *
- * @list must not be modified until this iterator is freed.
- *
- * Return value: (transfer full): the newly allocated
- *   `PangoAttrIterator`, which should be freed with
- *   [method@Pango.AttrIterator.destroy]
- */
-PangoAttrIterator *
-pango_attr_list_get_iterator (PangoAttrList  *list)
-{
-  PangoAttrIterator *iterator;
-
-  g_return_val_if_fail (list != NULL, NULL);
-
-  iterator = g_slice_new (PangoAttrIterator);
-  _pango_attr_list_get_iterator (list, iterator);
-
-  return iterator;
-}
-
-/**
- * pango_attr_iterator_range:
- * @iterator: a PangoAttrIterator
- * @start: (out): location to store the start of the range
- * @end: (out): location to store the end of the range
- *
- * Get the range of the current segment.
- *
- * Note that the stored return values are signed, not unsigned
- * like the values in `PangoAttribute`. To deal with this API
- * oversight, stored return values that wouldn't fit into
- * a signed integer are clamped to %G_MAXINT.
- */
-void
-pango_attr_iterator_range (PangoAttrIterator *iterator,
-                           gint              *start,
-                           gint              *end)
-{
-  g_return_if_fail (iterator != NULL);
-
-  if (start)
-    *start = MIN (iterator->start_index, G_MAXINT);
-  if (end)
-    *end = MIN (iterator->end_index, G_MAXINT);
-}
-
-/**
- * pango_attr_iterator_next:
- * @iterator: a `PangoAttrIterator`
- *
- * Advance the iterator until the next change of style.
- *
- * Return value: %FALSE if the iterator is at the end
- *   of the list, otherwise %TRUE
- */
-gboolean
-pango_attr_iterator_next (PangoAttrIterator *iterator)
-{
-  int i;
-
-  g_return_val_if_fail (iterator != NULL, FALSE);
-
-  if (iterator->attr_index >= iterator->n_attrs &&
-      (!iterator->attribute_stack || iterator->attribute_stack->len == 0))
-    return FALSE;
-
-  iterator->start_index = iterator->end_index;
-  iterator->end_index = G_MAXUINT;
-
-  if (iterator->attribute_stack)
-    {
-      for (i = iterator->attribute_stack->len - 1; i>= 0; i--)
-        {
-          const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
-
-          if (attr->end_index == iterator->start_index)
-            g_ptr_array_remove_index (iterator->attribute_stack, i); /* Can't use index_fast :( */
-          else
-            iterator->end_index = MIN (iterator->end_index, attr->end_index);
-        }
-    }
-
-  while (1)
-    {
-      PangoAttribute *attr;
-
-      if (iterator->attr_index >= iterator->n_attrs)
-        break;
-
-      attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
-
-      if (attr->start_index != iterator->start_index)
-        break;
-
-      if (attr->end_index > iterator->start_index)
-        {
-          if (G_UNLIKELY (!iterator->attribute_stack))
-            iterator->attribute_stack = g_ptr_array_new ();
-
-          g_ptr_array_add (iterator->attribute_stack, attr);
-
-          iterator->end_index = MIN (iterator->end_index, attr->end_index);
-        }
-
-      iterator->attr_index++; /* NEXT! */
-    }
-
-  if (iterator->attr_index < iterator->n_attrs)
-      {
-      PangoAttribute *attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
-
-      iterator->end_index = MIN (iterator->end_index, attr->start_index);
-    }
-
-  return TRUE;
-}
-
-/**
- * pango_attr_iterator_copy:
- * @iterator: a `PangoAttrIterator`
- *
- * Copy a `PangoAttrIterator`.
- *
- * Return value: (transfer full): the newly allocated
- *   `PangoAttrIterator`, which should be freed with
- *   [method@Pango.AttrIterator.destroy]
- */
-PangoAttrIterator *
-pango_attr_iterator_copy (PangoAttrIterator *iterator)
-{
-  PangoAttrIterator *copy;
-
-  g_return_val_if_fail (iterator != NULL, NULL);
-
-  copy = g_slice_new (PangoAttrIterator);
-
-  *copy = *iterator;
-
-  if (iterator->attribute_stack)
-    copy->attribute_stack = g_ptr_array_copy (iterator->attribute_stack, NULL, NULL);
-  else
-    copy->attribute_stack = NULL;
-
-  return copy;
-}
-
-void
-_pango_attr_iterator_destroy (PangoAttrIterator *iterator)
-{
-  if (iterator->attribute_stack)
-    g_ptr_array_free (iterator->attribute_stack, TRUE);
-}
-
-/**
- * pango_attr_iterator_destroy:
- * @iterator: a `PangoAttrIterator`
- *
- * Destroy a `PangoAttrIterator` and free all associated memory.
- */
-void
-pango_attr_iterator_destroy (PangoAttrIterator *iterator)
-{
-  g_return_if_fail (iterator != NULL);
-
-  _pango_attr_iterator_destroy (iterator);
-  g_slice_free (PangoAttrIterator, iterator);
-}
-
-/**
- * pango_attr_iterator_get:
- * @iterator: a `PangoAttrIterator`
- * @type: the type of attribute to find
- *
- * Find the current attribute of a particular type
- * at the iterator location.
- *
- * When multiple attributes of the same type overlap,
- * the attribute whose range starts closest to the
- * current location is used.
- *
- * Return value: (nullable) (transfer none): the current
- *   attribute of the given type, or %NULL if no attribute
- *   of that type applies to the current location.
- */
-PangoAttribute *
-pango_attr_iterator_get (PangoAttrIterator *iterator,
-                         PangoAttrType      type)
-{
-  int i;
-
-  g_return_val_if_fail (iterator != NULL, NULL);
-
-  if (!iterator->attribute_stack)
-    return NULL;
-
-  for (i = iterator->attribute_stack->len - 1; i>= 0; i--)
-    {
-      PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
-
-      if (attr->klass->type == type)
-        return attr;
-    }
-
-  return NULL;
-}
-
-/**
- * pango_attr_iterator_get_font:
- * @iterator: a `PangoAttrIterator`
- * @desc: a `PangoFontDescription` to fill in with the current
- *   values. The family name in this structure will be set using
- *   [method@Pango.FontDescription.set_family_static] using
- *   values from an attribute in the `PangoAttrList` associated
- *   with the iterator, so if you plan to keep it around, you
- *   must call:
- *   `pango_font_description_set_family (desc, pango_font_description_get_family (desc))`.
- * @language: (out) (optional): location to store language tag
- *   for item, or %NULL if none is found.
- * @extra_attrs: (out) (optional) (element-type Pango.Attribute) (transfer full):
- *   location in which to store a list of non-font attributes
- *   at the the current position; only the highest priority
- *   value of each attribute will be added to this list. In
- *   order to free this value, you must call
- *   [method@Pango.Attribute.destroy] on each member.
- *
- * Get the font and other attributes at the current
- * iterator position.
- */
-void
-pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
-                              PangoFontDescription  *desc,
-                              PangoLanguage        **language,
-                              GSList               **extra_attrs)
-{
-  PangoFontMask mask = 0;
-  gboolean have_language = FALSE;
-  gdouble scale = 0;
-  gboolean have_scale = FALSE;
-  int i;
-
-  g_return_if_fail (iterator != NULL);
-  g_return_if_fail (desc != NULL);
-
-  if (language)
-    *language = NULL;
-
-  if (extra_attrs)
-    *extra_attrs = NULL;
-
-  if (!iterator->attribute_stack)
-    return;
-
-  for (i = iterator->attribute_stack->len - 1; i >= 0; i--)
-    {
-      const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
-
-      switch ((int) attr->klass->type)
-        {
-        case PANGO_ATTR_FONT_DESC:
-          {
-            PangoFontMask new_mask = pango_font_description_get_set_fields (((PangoAttrFontDesc 
*)attr)->desc) & ~mask;
-            mask |= new_mask;
-            pango_font_description_unset_fields (desc, new_mask);
-            pango_font_description_merge_static (desc, ((PangoAttrFontDesc *)attr)->desc, FALSE);
-
-            break;
-          }
-        case PANGO_ATTR_FAMILY:
-          if (!(mask & PANGO_FONT_MASK_FAMILY))
-            {
-              mask |= PANGO_FONT_MASK_FAMILY;
-              pango_font_description_set_family (desc, ((PangoAttrString *)attr)->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);
-            }
-          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);
-            }
-          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);
-            }
-          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);
-            }
-          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);
-            }
-          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);
-            }
-          break;
-        case PANGO_ATTR_SCALE:
-          if (!have_scale)
-            {
-              have_scale = TRUE;
-              scale = ((PangoAttrFloat *)attr)->value;
-            }
-          break;
-        case PANGO_ATTR_LANGUAGE:
-          if (language)
-            {
-              if (!have_language)
-                {
-                  have_language = TRUE;
-                  *language = ((PangoAttrLanguage *)attr)->value;
-                }
-            }
-          break;
-        default:
-          if (extra_attrs)
-            {
-              gboolean found = FALSE;
-
-              /* Hack: special-case FONT_FEATURES, BASELINE_SHIFT and FONT_SCALE.
-               * We don't want these to accumulate, not override each other,
-               * 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)
-                {
-                  GSList *tmp_list = *extra_attrs;
-                  while (tmp_list)
-                    {
-                      PangoAttribute *old_attr = tmp_list->data;
-                      if (attr->klass->type == old_attr->klass->type)
-                        {
-                          found = TRUE;
-                          break;
-                        }
-
-                      tmp_list = tmp_list->next;
-                    }
-                }
-
-              if (!found)
-                *extra_attrs = g_slist_prepend (*extra_attrs, pango_attribute_copy (attr));
-            }
-        }
-    }
-
-  if (have_scale)
-    {
-      /* We need to use a local variable to ensure that the compiler won't
-       * implicitly cast it to integer while the result is kept in registers,
-       * leading to a wrong approximation in i386 (with 387 FPU)
-       */
-      volatile double size = scale * pango_font_description_get_size (desc);
-
-      if (pango_font_description_get_size_is_absolute (desc))
-        pango_font_description_set_absolute_size (desc, size);
-      else
-        pango_font_description_set_size (desc, size);
-    }
-}
-
-/**
- * pango_attr_iterator_get_attrs:
- * @iterator: a `PangoAttrIterator`
- *
- * Gets a list of all attributes at the current position of the
- * iterator.
- *
- * Return value: (element-type Pango.Attribute) (transfer full):
- *   a list of all attributes for the current range. To free
- *   this value, call [method@Pango.Attribute.destroy] on each
- *   value and g_slist_free() on the list.
- *
- * Since: 1.2
- */
-GSList *
-pango_attr_iterator_get_attrs (PangoAttrIterator *iterator)
-{
-  GSList *attrs = NULL;
-  int i;
-
-  if (!iterator->attribute_stack ||
-      iterator->attribute_stack->len == 0)
-    return NULL;
-
-  for (i = iterator->attribute_stack->len - 1; i >= 0; i--)
-    {
-      PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
-      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)
-        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)
-              {
-                found = TRUE;
-                break;
-              }
-           }
-
-      if (!found)
-        attrs = g_slist_prepend (attrs, pango_attribute_copy (attr));
-    }
-
-  return attrs;
-}
-
-gboolean
-pango_attr_iterator_advance (PangoAttrIterator *iterator,
-                             int                index)
-{
-  int start_range, end_range;
-
-  pango_attr_iterator_range (iterator, &start_range, &end_range);
-
-  while (index >= end_range)
-    {
-      if (!pango_attr_iterator_next (iterator))
-        return FALSE;
-      pango_attr_iterator_range (iterator, &start_range, &end_range);
-    }
-
-  if (start_range > index)
-    g_warning ("pango_attr_iterator_advance(): iterator had already "
-               "moved beyond the index");
-
-  return TRUE;
-}
 /* }}} */
 
 /* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index 0b8bfc311..908cc194b 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -19,120 +19,137 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __PANGO_ATTRIBUTES_H__
-#define __PANGO_ATTRIBUTES_H__
+#pragma once
 
 #include <pango/pango-font.h>
 #include <pango/pango-color.h>
-#include <glib-object.h>
+#include <pango/pango-attr.h>
 
 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;
-
+#define PANGO_ATTR_TYPE(value, affects, merge) (PANGO_ATTR_VALUE_##value | (PANGO_ATTR_AFFECTS_##affects << 
8) | (PANGO_ATTR_MERGE_##merge << 12) | (__COUNTER__ << 16))
 /**
  * PangoAttrType:
  * @PANGO_ATTR_INVALID: does not happen
- * @PANGO_ATTR_LANGUAGE: language ([struct@Pango.AttrLanguage])
- * @PANGO_ATTR_FAMILY: font family name list ([struct@Pango.AttrString])
- * @PANGO_ATTR_STYLE: font slant style ([struct@Pango.AttrInt])
- * @PANGO_ATTR_WEIGHT: font weight ([struct@Pango.AttrInt])
- * @PANGO_ATTR_VARIANT: font variant (normal or small caps) ([struct@Pango.AttrInt])
- * @PANGO_ATTR_STRETCH: font stretch ([struct@Pango.AttrInt])
- * @PANGO_ATTR_SIZE: font size in points scaled by %PANGO_SCALE ([struct@Pango.AttrInt])
- * @PANGO_ATTR_FONT_DESC: font description ([struct@Pango.AttrFontDesc])
- * @PANGO_ATTR_FOREGROUND: foreground color ([struct@Pango.AttrColor])
- * @PANGO_ATTR_BACKGROUND: background color ([struct@Pango.AttrColor])
- * @PANGO_ATTR_UNDERLINE: whether the text has an underline ([struct@Pango.AttrInt])
- * @PANGO_ATTR_STRIKETHROUGH: whether the text is struck-through ([struct@Pango.AttrInt])
- * @PANGO_ATTR_RISE: baseline displacement ([struct@Pango.AttrInt])
- * @PANGO_ATTR_SCALE: font size scale factor ([struct@Pango.AttrFloat])
- * @PANGO_ATTR_FALLBACK: whether fallback is enabled ([struct@Pango.AttrInt])
- * @PANGO_ATTR_LETTER_SPACING: letter spacing ([struct@PangoAttrInt])
- * @PANGO_ATTR_UNDERLINE_COLOR: underline color ([struct@Pango.AttrColor])
- * @PANGO_ATTR_STRIKETHROUGH_COLOR: strikethrough color ([struct@Pango.AttrColor])
- * @PANGO_ATTR_ABSOLUTE_SIZE: font size in pixels scaled by %PANGO_SCALE ([struct@Pango.AttrInt])
- * @PANGO_ATTR_GRAVITY: base text gravity ([struct@Pango.AttrInt])
- * @PANGO_ATTR_GRAVITY_HINT: gravity hint ([struct@Pango.AttrInt])
- * @PANGO_ATTR_FONT_FEATURES: OpenType font features ([struct@Pango.AttrFontFeatures]). Since 1.38
- * @PANGO_ATTR_FOREGROUND_ALPHA: foreground alpha ([struct@Pango.AttrInt]). Since 1.38
- * @PANGO_ATTR_BACKGROUND_ALPHA: background alpha ([struct@Pango.AttrInt]). Since 1.38
- * @PANGO_ATTR_ALLOW_BREAKS: whether breaks are allowed ([struct@Pango.AttrInt]). Since 1.44
- * @PANGO_ATTR_SHOW: how to render invisible characters ([struct@Pango.AttrInt]). Since 1.44
- * @PANGO_ATTR_INSERT_HYPHENS: whether to insert hyphens at intra-word line breaks ([struct@Pango.AttrInt]). 
Since 1.44
- * @PANGO_ATTR_OVERLINE: whether the text has an overline ([struct@Pango.AttrInt]). Since 1.46
- * @PANGO_ATTR_OVERLINE_COLOR: overline color ([struct@Pango.AttrColor]). Since 1.46
- * @PANGO_ATTR_LINE_HEIGHT: line height factor ([struct@Pango.AttrFloat]). Since: 1.50
- * @PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: line height ([struct@Pango.AttrInt]). Since: 1.50
- * @PANGO_ATTR_WORD: override segmentation to classify the range of the attribute as a single word 
([struct@Pango.AttrInt]). Since 1.50
- * @PANGO_ATTR_SENTENCE: override segmentation to classify the range of the attribute as a single sentence 
([struct@Pango.AttrInt]). Since 1.50
- * @PANGO_ATTR_PARAGRAPH: override segmentation to classify the range of the attribute as a single paragraph 
([struct@Pango.AttrInt]).
- * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement ([struct@Pango.AttrInt]). Since 1.50
- * @PANGO_ATTR_FONT_SCALE: font-relative size change ([struct@Pango.AttrInt]). Since 1.50
- * @PANGO_ATTR_LINE_SPACING: extra space to add to the leading from the font metrics (if
- *   not overridden by line height attribute) ([struct@Pango.AttrInt])
- *
- * The `PangoAttrType` distinguishes between different types of attributes.
+ * @PANGO_ATTR_LANGUAGE: language
+ * @PANGO_ATTR_FAMILY: font family name
+ * @PANGO_ATTR_STYLE: font style
+ * @PANGO_ATTR_WEIGHT: font weight
+ * @PANGO_ATTR_VARIANT: font variant
+ * @PANGO_ATTR_STRETCH: font stretch
+ * @PANGO_ATTR_SIZE: font size in points scaled by `PANGO_SCALE`
+ * @PANGO_ATTR_FONT_DESC: font description
+ * @PANGO_ATTR_FOREGROUND: foreground color
+ * @PANGO_ATTR_BACKGROUND: background color
+ * @PANGO_ATTR_UNDERLINE: whether the text has an underline
+ * @PANGO_ATTR_STRIKETHROUGH: whether the text is struck-through
+ * @PANGO_ATTR_RISE: baseline displacement
+ * @PANGO_ATTR_SCALE: font size scale factor
+ * @PANGO_ATTR_FALLBACK: whether font fallback is enabled
+ * @PANGO_ATTR_LETTER_SPACING: letter spacing in Pango units
+ * @PANGO_ATTR_UNDERLINE_COLOR: underline color
+ * @PANGO_ATTR_STRIKETHROUGH_COLOR: strikethrough color
+ * @PANGO_ATTR_ABSOLUTE_SIZE: font size in pixels scaled by `PANGO_SCALE`
+ * @PANGO_ATTR_GRAVITY: base text gravity
+ * @PANGO_ATTR_GRAVITY_HINT: gravity hint
+ * @PANGO_ATTR_FONT_FEATURES: OpenType font features
+ * @PANGO_ATTR_FOREGROUND_ALPHA: foreground alpha
+ * @PANGO_ATTR_BACKGROUND_ALPHA: background alpha
+ * @PANGO_ATTR_ALLOW_BREAKS: whether line breaks are allowed
+ * @PANGO_ATTR_SHOW: how to render invisible characters
+ * @PANGO_ATTR_INSERT_HYPHENS: whether to insert hyphens at intra-word line breaks
+ * @PANGO_ATTR_OVERLINE: whether the text has an overline
+ * @PANGO_ATTR_OVERLINE_COLOR: overline color
+ * @PANGO_ATTR_LINE_HEIGHT: line height factor
+ * @PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: line height in Pango units
+ * @PANGO_ATTR_WORD: mark the range of the attribute as a single word
+ * @PANGO_ATTR_SENTENCE: mark the range of the attribute as a single sentence
+ * @PANGO_ATTR_PARAGRAPH: mark the range of the attribute as a single paragraph
+ * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement
+ * @PANGO_ATTR_FONT_SCALE: font-relative size change
+ * @PANGO_ATTR_LINE_SPACING: extra space to add to the leading from the
+ *   font metrics (if not overridden by line height attribute)
+ *
+ * The `PangoAttrType` enumeration contains predefined types for attributes.
  *
  * Along with the predefined values, it is possible to allocate additional
  * values for custom attributes using [func@AttrType.register]. The predefined
- * values are given below. The type of structure used to store the attribute is
- * listed in parentheses after the description.
+ * values are given below.
  */
 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_PARAGRAPH,         /* PangoAttrInt */
-  PANGO_ATTR_LINE_SPACING,      /* PangoAttrInt */
+  PANGO_ATTR_INVALID,
+  PANGO_ATTR_LANGUAGE             = PANGO_ATTR_TYPE (LANGUAGE, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_FAMILY               = PANGO_ATTR_TYPE (STRING, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_STYLE                = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_WEIGHT               = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_VARIANT              = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_STRETCH              = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_SIZE                 = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_FONT_DESC            = PANGO_ATTR_TYPE (FONT_DESC, ITEMIZATION, ACCUMULATES),
+  PANGO_ATTR_FOREGROUND           = PANGO_ATTR_TYPE (COLOR, RENDERING, OVERRIDES),
+  PANGO_ATTR_BACKGROUND           = PANGO_ATTR_TYPE (COLOR, RENDERING, OVERRIDES),
+  PANGO_ATTR_UNDERLINE            = PANGO_ATTR_TYPE (INT, RENDERING, OVERRIDES),
+  PANGO_ATTR_STRIKETHROUGH        = PANGO_ATTR_TYPE (BOOLEAN, RENDERING, OVERRIDES),
+  PANGO_ATTR_RISE                 = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_SCALE                = PANGO_ATTR_TYPE (FLOAT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_FALLBACK             = PANGO_ATTR_TYPE (BOOLEAN, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_LETTER_SPACING       = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_UNDERLINE_COLOR      = PANGO_ATTR_TYPE (COLOR, RENDERING, OVERRIDES),
+  PANGO_ATTR_STRIKETHROUGH_COLOR  = PANGO_ATTR_TYPE (COLOR, RENDERING, OVERRIDES),
+  PANGO_ATTR_ABSOLUTE_SIZE        = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_GRAVITY              = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_GRAVITY_HINT         = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_FONT_FEATURES        = PANGO_ATTR_TYPE (STRING, SHAPING, ACCUMULATES),
+  PANGO_ATTR_FOREGROUND_ALPHA     = PANGO_ATTR_TYPE (INT, RENDERING, OVERRIDES),
+  PANGO_ATTR_BACKGROUND_ALPHA     = PANGO_ATTR_TYPE (INT, RENDERING, OVERRIDES),
+  PANGO_ATTR_ALLOW_BREAKS         = PANGO_ATTR_TYPE (BOOLEAN, BREAKING, OVERRIDES),
+  PANGO_ATTR_SHOW                 = PANGO_ATTR_TYPE (INT, SHAPING, OVERRIDES),
+  PANGO_ATTR_INSERT_HYPHENS       = PANGO_ATTR_TYPE (BOOLEAN, SHAPING, OVERRIDES),
+  PANGO_ATTR_OVERLINE             = PANGO_ATTR_TYPE (INT, RENDERING, OVERRIDES),
+  PANGO_ATTR_OVERLINE_COLOR       = PANGO_ATTR_TYPE (COLOR, RENDERING, OVERRIDES),
+  PANGO_ATTR_LINE_HEIGHT          = PANGO_ATTR_TYPE (FLOAT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_ABSOLUTE_LINE_HEIGHT = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_TEXT_TRANSFORM       = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_WORD                 = PANGO_ATTR_TYPE (BOOLEAN, BREAKING, OVERRIDES),
+  PANGO_ATTR_SENTENCE             = PANGO_ATTR_TYPE (BOOLEAN, BREAKING, OVERRIDES),
+  PANGO_ATTR_PARAGRAPH            = PANGO_ATTR_TYPE (BOOLEAN, BREAKING, OVERRIDES),
+  PANGO_ATTR_BASELINE_SHIFT       = PANGO_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES),
+  PANGO_ATTR_FONT_SCALE           = PANGO_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES),
+  PANGO_ATTR_LINE_SPACING         = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
 } PangoAttrType;
 
+#undef PANGO_ATTR_TYPE
+
+
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_language_new                 (PangoLanguage              *language);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_family_new                   (const char                 *family);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_foreground_new               (guint16                     red,
+                                                                 guint16                     green,
+                                                                 guint16                     blue);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_background_new               (guint16                     red,
+                                                                 guint16                     green,
+                                                                 guint16                     blue);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_size_new                     (int                         size);
+PANGO_AVAILABLE_IN_1_8
+PangoAttribute *        pango_attr_size_new_absolute            (int                         size);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_style_new                    (PangoStyle                  style);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_weight_new                   (PangoWeight                 weight);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_variant_new                  (PangoVariant                variant);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_stretch_new                  (PangoStretch                stretch);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_font_desc_new                (const PangoFontDescription *desc);
+
 /**
  * PangoUnderline:
  * @PANGO_UNDERLINE_NONE: no underline should be drawn
@@ -176,61 +193,20 @@ typedef enum {
   PANGO_UNDERLINE_ERROR_LINE
 } PangoUnderline;
 
-
-/**
- * PangoOverline:
- * @PANGO_OVERLINE_NONE: no overline should be drawn
- * @PANGO_OVERLINE_SINGLE: Draw a single line above the ink
- *   extents of the text being underlined.
- *
- * The `PangoOverline` enumeration is used to specify whether text
- * should be overlined, and if so, the type of line.
- *
- * Since: 1.46
- */
-typedef enum {
-  PANGO_OVERLINE_NONE,
-  PANGO_OVERLINE_SINGLE
-} PangoOverline;
-
-/**
- * PangoShowFlags:
- * @PANGO_SHOW_NONE: No special treatment for invisible characters
- * @PANGO_SHOW_SPACES: Render spaces, tabs and newlines visibly
- * @PANGO_SHOW_LINE_BREAKS: Render line breaks visibly
- * @PANGO_SHOW_IGNORABLES: Render default-ignorable Unicode
- *   characters visibly
- *
- * These flags affect how Pango treats characters that are normally
- * not visible in the output.
- *
- * Since: 1.44
- */
-typedef enum {
-  PANGO_SHOW_NONE        = 0,
-  PANGO_SHOW_SPACES      = 1 << 0,
-  PANGO_SHOW_LINE_BREAKS = 1 << 1,
-  PANGO_SHOW_IGNORABLES  = 1 << 2
-} PangoShowFlags;
-
-/**
- * PangoTextTransform:
- * @PANGO_TEXT_TRANSFORM_NONE: Leave text unchanged
- * @PANGO_TEXT_TRANSFORM_LOWERCASE: Display letters and numbers as lowercase
- * @PANGO_TEXT_TRANSFORM_UPPERCASE: Display letters and numbers as uppercase
- * @PANGO_TEXT_TRANSFORM_CAPITALIZE: Display the first character of a word
- *   in titlecase
- *
- * An enumeration that affects how Pango treats characters during shaping.
- *
- * Since: 1.50
- */
-typedef enum {
-  PANGO_TEXT_TRANSFORM_NONE,
-  PANGO_TEXT_TRANSFORM_LOWERCASE,
-  PANGO_TEXT_TRANSFORM_UPPERCASE,
-  PANGO_TEXT_TRANSFORM_CAPITALIZE,
-} PangoTextTransform;
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_underline_new                (PangoUnderline              underline);
+PANGO_AVAILABLE_IN_1_8
+PangoAttribute *        pango_attr_underline_color_new          (guint16                     red,
+                                                                 guint16                     green,
+                                                                 guint16                     blue);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_strikethrough_new            (gboolean                    strikethrough);
+PANGO_AVAILABLE_IN_1_8
+PangoAttribute *        pango_attr_strikethrough_color_new      (guint16                     red,
+                                                                 guint16                     green,
+                                                                 guint16                     blue);
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_rise_new                     (int                         rise);
 
 /**
  * PangoBaselineShift:
@@ -250,6 +226,9 @@ typedef enum {
   PANGO_BASELINE_SHIFT_SUBSCRIPT,
 } PangoBaselineShift;
 
+PANGO_AVAILABLE_IN_1_50
+PangoAttribute *        pango_attr_baseline_shift_new           (int                         shift);
+
 /**
  * PangoFontScale:
  * @PANGO_FONT_SCALE_NONE: Leave the font size unchanged
@@ -269,274 +248,6 @@ typedef enum {
   PANGO_FONT_SCALE_SMALL_CAPS,
 } PangoFontScale;
 
-/**
- * PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING:
- *
- * Value for @start_index in `PangoAttribute` that indicates
- * the beginning of the text.
- *
- * Since: 1.24
- */
-#define PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING ((guint)0)
-
-/**
- * PANGO_ATTR_INDEX_TO_TEXT_END: (value 4294967295)
- *
- * Value for @end_index in `PangoAttribute` that indicates
- * the end of the text.
- *
- * Since: 1.24
- */
-#define PANGO_ATTR_INDEX_TO_TEXT_END ((guint)(G_MAXUINT + 0))
-
-/**
- * PangoAttribute:
- * @klass: the class structure holding information about the type of the attribute
- * @start_index: the start index of the range (in bytes).
- * @end_index: end index of the range (in bytes). The character at this index
- *   is not included in the range.
- *
- * The `PangoAttribute` structure represents the common portions of all
- * attributes.
- *
- * Particular types of attributes include this structure as their initial
- * portion. The common portion of the attribute holds the range to which
- * the value in the type-specific part of the attribute applies and should
- * be initialized using [method Pango Attribute init]. By default, an attribute
- * will have an all-inclusive range of [0,%G_MAXUINT].
- */
-struct _PangoAttribute
-{
-  const PangoAttrClass *klass;
-  guint start_index;
-  guint end_index;
-};
-
-/**
- * PangoAttrFilterFunc:
- * @attribute: a Pango attribute
- * @user_data: user data passed to the function
- *
- * Type of a function filtering a list of attributes.
- *
- * Return value: %TRUE if the attribute should be selected for
- *   filtering, %FALSE otherwise.
- */
-typedef gboolean (*PangoAttrFilterFunc) (PangoAttribute *attribute,
-                                         gpointer        user_data);
-
-/**
- * PangoAttrDataCopyFunc:
- * @user_data: user data to copy
- *
- * Type of a function that can duplicate user data for an attribute.
- *
- * Return value: new copy of @user_data.
- **/
-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;
-};
-
-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);
-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
-void                    pango_attribute_destroy                 (PangoAttribute             *attr);
-PANGO_AVAILABLE_IN_ALL
-gboolean                pango_attribute_equal                   (const PangoAttribute       *attr1,
-                                                                 const PangoAttribute       *attr2) 
G_GNUC_PURE;
-
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_language_new                 (PangoLanguage              *language);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_family_new                   (const char                 *family);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_foreground_new               (guint16                     red,
-                                                                 guint16                     green,
-                                                                 guint16                     blue);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_background_new               (guint16                     red,
-                                                                 guint16                     green,
-                                                                 guint16                     blue);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_size_new                     (int                         size);
-PANGO_AVAILABLE_IN_1_8
-PangoAttribute *        pango_attr_size_new_absolute            (int                         size);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_style_new                    (PangoStyle                  style);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_weight_new                   (PangoWeight                 weight);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_variant_new                  (PangoVariant                variant);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_stretch_new                  (PangoStretch                stretch);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_font_desc_new                (const PangoFontDescription *desc);
-
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_underline_new                (PangoUnderline              underline);
-PANGO_AVAILABLE_IN_1_8
-PangoAttribute *        pango_attr_underline_color_new          (guint16                     red,
-                                                                 guint16                     green,
-                                                                 guint16                     blue);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_strikethrough_new            (gboolean                    strikethrough);
-PANGO_AVAILABLE_IN_1_8
-PangoAttribute *        pango_attr_strikethrough_color_new      (guint16                     red,
-                                                                 guint16                     green,
-                                                                 guint16                     blue);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_rise_new                     (int                         rise);
-PANGO_AVAILABLE_IN_1_50
-PangoAttribute *        pango_attr_baseline_shift_new           (int                         shift);
 PANGO_AVAILABLE_IN_1_50
 PangoAttribute *        pango_attr_font_scale_new               (PangoFontScale              scale);
 PANGO_AVAILABLE_IN_ALL
@@ -557,7 +268,6 @@ PANGO_AVAILABLE_IN_1_38
 PangoAttribute *        pango_attr_background_alpha_new         (guint16                      alpha);
 PANGO_AVAILABLE_IN_1_44
 PangoAttribute *        pango_attr_allow_breaks_new             (gboolean                     allow_breaks);
-
 PANGO_AVAILABLE_IN_1_50
 PangoAttribute *        pango_attr_word_new                     (void);
 PANGO_AVAILABLE_IN_1_50
@@ -567,12 +277,50 @@ PangoAttribute *        pango_attr_paragraph_new                (void);
 
 PANGO_AVAILABLE_IN_1_44
 PangoAttribute *        pango_attr_insert_hyphens_new           (gboolean                     
insert_hyphens);
+
+/**
+ * PangoOverline:
+ * @PANGO_OVERLINE_NONE: no overline should be drawn
+ * @PANGO_OVERLINE_SINGLE: Draw a single line above the ink
+ *   extents of the text being underlined.
+ *
+ * The `PangoOverline` enumeration is used to specify whether text
+ * should be overlined, and if so, the type of line.
+ *
+ * Since: 1.46
+ */
+typedef enum {
+  PANGO_OVERLINE_NONE,
+  PANGO_OVERLINE_SINGLE
+} PangoOverline;
+
 PANGO_AVAILABLE_IN_1_46
 PangoAttribute *        pango_attr_overline_new                 (PangoOverline                overline);
 PANGO_AVAILABLE_IN_1_46
 PangoAttribute *        pango_attr_overline_color_new           (guint16                      red,
                                                                  guint16                      green,
                                                                  guint16                      blue);
+
+/**
+ * PangoShowFlags:
+ * @PANGO_SHOW_NONE: No special treatment for invisible characters
+ * @PANGO_SHOW_SPACES: Render spaces, tabs and newlines visibly
+ * @PANGO_SHOW_LINE_BREAKS: Render line breaks visibly
+ * @PANGO_SHOW_IGNORABLES: Render default-ignorable Unicode
+ *   characters visibly
+ *
+ * These flags affect how Pango treats characters that are normally
+ * not visible in the output.
+ *
+ * Since: 1.44
+ */
+typedef enum {
+  PANGO_SHOW_NONE        = 0,
+  PANGO_SHOW_SPACES      = 1 << 0,
+  PANGO_SHOW_LINE_BREAKS = 1 << 1,
+  PANGO_SHOW_IGNORABLES  = 1 << 2
+} PangoShowFlags;
+
 PANGO_AVAILABLE_IN_1_44
 PangoAttribute *        pango_attr_show_new                     (PangoShowFlags               flags);
 PANGO_AVAILABLE_IN_1_50
@@ -581,140 +329,26 @@ PANGO_AVAILABLE_IN_1_50
 PangoAttribute *        pango_attr_line_height_new_absolute     (int                          height);
 PANGO_AVAILABLE_IN_ALL
 PangoAttribute *        pango_attr_line_spacing_new             (int                          spacing);
-PANGO_AVAILABLE_IN_1_50
-PangoAttribute *        pango_attr_text_transform_new           (PangoTextTransform transform);
 
-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 */
-
-typedef struct _PangoAttrList     PangoAttrList;
-typedef struct _PangoAttrIterator PangoAttrIterator;
-
-#define PANGO_TYPE_ATTR_LIST pango_attr_list_get_type ()
-
-/**
- * PangoAttrIterator:
- *
- * A `PangoAttrIterator` is used to iterate through a `PangoAttrList`.
- *
- * A new iterator is created with [method@Pango.AttrList.get_iterator].
- * Once the iterator is created, it can be advanced through the style
- * changes in the text using [method Pango AttrIterator next]. At each
- * style change, the range of the current style segment and the attributes
- * currently in effect can be queried.
- */
-
-/**
- * PangoAttrList:
- *
- * A `PangoAttrList` represents a list of attributes that apply to a section
- * of text.
- *
- * The attributes in a `PangoAttrList` are, in general, allowed to overlap in
- * an arbitrary fashion. However, if the attributes are manipulated only through
- * [method@Pango.AttrList.change], the overlap between properties will meet
- * stricter criteria.
+/*
+ * PangoTextTransform:
+ * @PANGO_TEXT_TRANSFORM_NONE: Leave text unchanged
+ * @PANGO_TEXT_TRANSFORM_LOWERCASE: Display letters and numbers as lowercase
+ * @PANGO_TEXT_TRANSFORM_UPPERCASE: Display letters and numbers as uppercase
+ * @PANGO_TEXT_TRANSFORM_CAPITALIZE: Display the first character of a word
+ *   in titlecase
  *
- * Since the `PangoAttrList` structure is stored as a linear list, it is not
- * suitable for storing attributes for large amounts of text. In general, you
- * should not use a single `PangoAttrList` for more than one paragraph of text.
- */
-
-PANGO_AVAILABLE_IN_ALL
-GType                   pango_attr_list_get_type        (void) G_GNUC_CONST;
-
-PANGO_AVAILABLE_IN_ALL
-PangoAttrList *         pango_attr_list_new             (void);
-PANGO_AVAILABLE_IN_1_10
-PangoAttrList *         pango_attr_list_ref             (PangoAttrList         *list);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_list_unref           (PangoAttrList         *list);
-PANGO_AVAILABLE_IN_ALL
-PangoAttrList *         pango_attr_list_copy            (PangoAttrList         *list);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_list_insert          (PangoAttrList         *list,
-                                                         PangoAttribute        *attr);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_list_insert_before   (PangoAttrList         *list,
-                                                         PangoAttribute        *attr);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_list_change          (PangoAttrList         *list,
-                                                         PangoAttribute        *attr);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_list_splice          (PangoAttrList         *list,
-                                                         PangoAttrList         *other,
-                                                         int                    pos,
-                                                         int                    len);
-PANGO_AVAILABLE_IN_1_44
-void                    pango_attr_list_update          (PangoAttrList         *list,
-                                                         int                    pos,
-                                                         int                    remove,
-                                                         int                    add);
-
-PANGO_AVAILABLE_IN_1_2
-PangoAttrList *         pango_attr_list_filter          (PangoAttrList         *list,
-                                                         PangoAttrFilterFunc    func,
-                                                         gpointer               data);
-
-PANGO_AVAILABLE_IN_1_44
-GSList *                pango_attr_list_get_attributes  (PangoAttrList         *list);
-
-PANGO_AVAILABLE_IN_1_46
-gboolean                pango_attr_list_equal           (PangoAttrList         *list,
-                                                         PangoAttrList         *other_list);
+ * An enumeration that affects how Pango treats characters during shaping.
+  */
+typedef enum {
+  PANGO_TEXT_TRANSFORM_NONE,
+  PANGO_TEXT_TRANSFORM_LOWERCASE,
+  PANGO_TEXT_TRANSFORM_UPPERCASE,
+  PANGO_TEXT_TRANSFORM_CAPITALIZE,
+} PangoTextTransform;
 
 PANGO_AVAILABLE_IN_1_50
-char *                  pango_attr_list_to_string       (PangoAttrList         *list);
-PANGO_AVAILABLE_IN_1_50
-PangoAttrList *         pango_attr_list_from_string     (const char            *text);
-
-PANGO_AVAILABLE_IN_1_44
-GType                   pango_attr_iterator_get_type    (void) G_GNUC_CONST;
-
-PANGO_AVAILABLE_IN_ALL
-PangoAttrIterator *     pango_attr_list_get_iterator    (PangoAttrList         *list);
-
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_iterator_range       (PangoAttrIterator     *iterator,
-                                                         int                   *start,
-                                                         int                   *end);
-PANGO_AVAILABLE_IN_ALL
-gboolean                pango_attr_iterator_next        (PangoAttrIterator     *iterator);
-PANGO_AVAILABLE_IN_ALL
-PangoAttrIterator *     pango_attr_iterator_copy        (PangoAttrIterator     *iterator);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_iterator_destroy     (PangoAttrIterator     *iterator);
-PANGO_AVAILABLE_IN_ALL
-PangoAttribute *        pango_attr_iterator_get         (PangoAttrIterator     *iterator,
-                                                         PangoAttrType          type);
-PANGO_AVAILABLE_IN_ALL
-void                    pango_attr_iterator_get_font    (PangoAttrIterator     *iterator,
-                                                         PangoFontDescription  *desc,
-                                                         PangoLanguage        **language,
-                                                         GSList               **extra_attrs);
-PANGO_AVAILABLE_IN_1_2
-GSList *                pango_attr_iterator_get_attrs   (PangoAttrIterator     *iterator);
+PangoAttribute *        pango_attr_text_transform_new           (PangoTextTransform transform);
 
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoAttribute, pango_attribute_destroy)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoAttrList, pango_attr_list_unref)
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoAttrIterator, pango_attr_iterator_destroy)
 
 G_END_DECLS
-
-#endif /* __PANGO_ATTRIBUTES_H__ */
diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c
index 7eb1737d8..d128f7f8a 100644
--- a/pango/pango-glyph-item.c
+++ b/pango/pango-glyph-item.c
@@ -24,7 +24,8 @@
 
 #include "pango-glyph-item.h"
 #include "pango-impl-utils.h"
-#include "pango-attributes-private.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-iterator-private.h"
 
 #define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0)
 
@@ -617,7 +618,7 @@ pango_glyph_item_apply_attrs (PangoGlyphItem   *glyph_item,
 
   /* Advance the attr iterator to the start of the item
    */
-  _pango_attr_list_get_iterator (list, &iter);
+  pango_attr_list_init_iterator (list, &iter);
   do
     {
       pango_attr_iterator_range (&iter, &range_start, &range_end);
@@ -713,7 +714,7 @@ pango_glyph_item_apply_attrs (PangoGlyphItem   *glyph_item,
   if (LTR (glyph_item))
     result = g_slist_reverse (result);
 
-  _pango_attr_iterator_destroy (&iter);
+  pango_attr_iterator_clear (&iter);
 
   return result;
 }
diff --git a/pango/pango-item.c b/pango/pango-item.c
index cbf21f42d..50b9e7c88 100644
--- a/pango/pango-item.c
+++ b/pango/pango-item.c
@@ -267,14 +267,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)
             {
@@ -302,7 +301,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'),
@@ -394,10 +393,10 @@ pango_item_get_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;
@@ -422,7 +421,7 @@ pango_item_get_properties (PangoItem      *item,
             }
           break;
         case PANGO_ATTR_OVERLINE:
-          switch (((PangoAttrInt *)attr)->value)
+          switch (attr->int_value)
             {
             case PANGO_OVERLINE_SINGLE:
               properties->oline_single = TRUE;
@@ -434,27 +433,27 @@ pango_item_get_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_LINE_SPACING:
-          properties->line_spacing = ((PangoAttrInt *)attr)->value;
+          properties->line_spacing = 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;
 
         case PANGO_ATTR_PARAGRAPH:
diff --git a/pango/pango-item.h b/pango/pango-item.h
index d04de33d0..92b430299 100644
--- a/pango/pango-item.h
+++ b/pango/pango-item.h
@@ -23,7 +23,8 @@
 #define __PANGO_ITEM_H__
 
 #include <pango/pango-types.h>
-#include <pango/pango-attributes.h>
+#include <pango/pango-attr-list.h>
+#include <pango/pango-attr-list.h>
 
 G_BEGIN_DECLS
 
diff --git a/pango/pango-line-breaker.c b/pango/pango-line-breaker.c
index 1ad180af7..d7a94d5f2 100644
--- a/pango/pango-line-breaker.c
+++ b/pango/pango-line-breaker.c
@@ -6,6 +6,8 @@
 #include "pango-tabs.h"
 #include "pango-impl-utils.h"
 #include "pango-attributes-private.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-iterator-private.h"
 #include "pango-item-private.h"
 #include "pango-utils-internal.h"
 
@@ -158,7 +160,7 @@ apply_attributes_to_items (GList         *items,
   if (!attrs)
     return;
 
-  _pango_attr_list_get_iterator (attrs, &iter);
+  pango_attr_list_init_iterator (attrs, &iter);
 
   for (l = items; l; l = l->next)
     {
@@ -166,7 +168,7 @@ apply_attributes_to_items (GList         *items,
       pango_item_apply_attrs (item, &iter);
     }
 
-  _pango_attr_iterator_destroy (&iter);
+  pango_attr_iterator_clear (&iter);
 }
 
 static PangoLogAttr *
@@ -440,12 +442,12 @@ ensure_tab_width (PangoLineBreaker *self)
         {
           PangoAttrIterator iter;
 
-          _pango_attr_list_get_iterator (attrs, &iter);
+          pango_attr_list_init_iterator (attrs, &iter);
           pango_attr_iterator_get_font (&iter, font_desc, &language, NULL);
-          _pango_attr_iterator_destroy (&iter);
+          pango_attr_iterator_clear (&iter);
         }
 
-      _pango_attr_list_init (&tmp_attrs);
+      pango_attr_list_init (&tmp_attrs);
       attr = pango_attr_font_desc_new (font_desc);
       pango_font_description_free (font_desc);
       pango_attr_list_insert_before (&tmp_attrs, attr);
@@ -465,7 +467,7 @@ ensure_tab_width (PangoLineBreaker *self)
           attrs = NULL;
         }
 
-      _pango_attr_list_destroy (&tmp_attrs);
+      pango_attr_list_destroy (&tmp_attrs);
 
       item = items->data;
       pango_shape ("        ", 8, "        ", 8, &item->analysis, glyphs, shape_flags);
@@ -1847,14 +1849,14 @@ collect_baseline_shift (PangoLineBreaker *self,
     {
       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)
             {
@@ -1865,7 +1867,7 @@ collect_baseline_shift (PangoLineBreaker *self,
               entry->attr = attr;
               self->baseline_shifts = g_list_prepend (self->baseline_shifts, entry);
 
-              value = ((PangoAttrInt *)attr)->value;
+              value = attr->int_value;
 
               if (value > 1024 || value < -1024)
                 {
@@ -1927,7 +1929,7 @@ collect_baseline_shift (PangoLineBreaker *self,
 
                   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;
diff --git a/pango/pango-line.c b/pango/pango-line.c
index 2fdad936f..38c3f35dd 100644
--- a/pango/pango-line.c
+++ b/pango/pango-line.c
@@ -5,6 +5,8 @@
 #include "pango-tabs.h"
 #include "pango-impl-utils.h"
 #include "pango-attributes-private.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-iterator-private.h"
 #include "pango-item-private.h"
 #include "pango-run-private.h"
 
@@ -436,7 +438,7 @@ pango_line_get_empty_extents (PangoLine        *line,
       PangoAttrIterator iter;
       int start, end;
 
-      _pango_attr_list_get_iterator (line->data->attrs, &iter);
+      pango_attr_list_init_iterator (line->data->attrs, &iter);
 
       do
         {
@@ -456,18 +458,18 @@ pango_line_get_empty_extents (PangoLine        *line,
 
               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;
             }
         }
       while (pango_attr_iterator_next (&iter));
 
-      _pango_attr_iterator_destroy (&iter);
+      pango_attr_iterator_clear (&iter);
     }
 
   memset (logical_rect, 0, sizeof (PangoRectangle));
diff --git a/pango/pango-markup.h b/pango/pango-markup.h
index 003ee51ad..b30edb8d9 100644
--- a/pango/pango-markup.h
+++ b/pango/pango-markup.h
@@ -22,7 +22,7 @@
 #ifndef __PANGO_MARKUP_H__
 #define __PANGO_MARKUP_H__
 
-#include <pango/pango-attributes.h>
+#include <pango/pango-attr-list.h>
 
 G_BEGIN_DECLS
 
diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c
index 8cdbec43c..3908378a1 100644
--- a/pango/pango-renderer.c
+++ b/pango/pango-renderer.c
@@ -1415,46 +1415,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/pango.h b/pango/pango.h
index 07169f42c..2364a9a9d 100644
--- a/pango/pango.h
+++ b/pango/pango.h
@@ -22,6 +22,9 @@
 #ifndef __PANGO_H__
 #define __PANGO_H__
 
+#include <pango/pango-attr.h>
+#include <pango/pango-attr-list.h>
+#include <pango/pango-attr-iterator.h>
 #include <pango/pango-attributes.h>
 #include <pango/pango-break.h>
 #include <pango/pango-color.h>
diff --git a/pango/serializer.c b/pango/serializer.c
index 1c7c6ef4f..43897b95b 100644
--- a/pango/serializer.c
+++ b/pango/serializer.c
@@ -27,6 +27,9 @@
 #include <pango/pango-font-private.h>
 #include <pango/pango-line-private.h>
 #include <pango/pango-utils-internal.h>
+#include <pango/pango-hbface.h>
+#include <pango/pango-attributes.h>
+#include <pango/pango-attr-private.h>
 
 #include <hb-ot.h>
 #include "pango/json/gtkjsonparserprivate.h"
@@ -163,48 +166,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",
-  "shape",
-  "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",
-  "paragraph",
-  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 void
 get_script_name (GUnicodeScript  script,
@@ -289,112 +293,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_PARAGRAPH:
-    case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
-    case PANGO_ATTR_LINE_SPACING:
-      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);
@@ -1159,7 +1157,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:
@@ -1173,7 +1171,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 81447d6e3..be73da7c1 100644
--- a/pango/shape.c
+++ b/pango/shape.c
@@ -275,8 +275,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;
@@ -292,8 +292,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 5b84133f4..9de3df07b 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 345e86998..11ecfab17 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 d2b13b213..70f1127e4 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/testattributes.c b/tests/testattributes.c
index 7264f4029..9f9702941 100644
--- a/tests/testattributes.c
+++ b/tests/testattributes.c
@@ -96,64 +96,115 @@ 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 ("my-attribute",
+                                   PANGO_ATTR_VALUE_POINTER,
+                                   PANGO_ATTR_AFFECTS_RENDERING,
+                                   PANGO_ATTR_MERGE_OVERRIDES,
+                                   copy_my_attribute_data,
+                                   destroy_my_attribute_data,
+                                   my_attribute_data_equal,
+                                   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_attribute_new (type);
+  attr->pointer_value = (gpointer)0x42;
+
+  ret = pango_attribute_get_pointer (attr, &value);
+  g_assert_true (ret);
+  g_assert_true (value == (gpointer)0x42);
+
+  attr2 = pango_attribute_new (type);
+  attr2->pointer_value = (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, FLOAT, FONT_FEATURES,
-  } attr_base[] = {
-    INVALID, LANGUAGE, STRING, INT, INT, INT, INT, SIZE, FONT_DESC, COLOR,
-    COLOR, INT, INT, INT, 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_pointer (attr, &pointer_value));
       break;
-    case INVALID:
     default:
       g_assert_not_reached ();
     }
@@ -469,7 +520,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"
@@ -758,7 +809,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;
@@ -895,7 +946,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);
@@ -956,7 +1007,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);
@@ -969,24 +1020,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");
 
@@ -1003,7 +1054,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");
@@ -1011,7 +1062,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");
 
@@ -1038,11 +1089,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);
 }
@@ -1123,28 +1174,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);
@@ -1157,21 +1208,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);
@@ -1193,7 +1244,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");
@@ -1205,7 +1256,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"
@@ -1271,25 +1322,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 d20f5a896..8f62cfc8b 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[] = {
@@ -363,7 +363,7 @@ test_serialize_layout_invalid (void)
       "    }\n"
       "  ]\n"
       "}\n",
-      PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE
+      PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE
     },
     {
       "{\n"
@@ -379,7 +379,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]