diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 5e5e585..e4f1c3e 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -55,6 +55,7 @@ static PangoAttribute *pango_attr_size_new_internal (int                   size,
 
 
 static GHashTable *name_map = NULL;
+static GHashTable *type_map = NULL;
 
 /**
  * pango_attr_type_register:
@@ -68,18 +69,110 @@ static GHashTable *name_map = NULL;
 PangoAttrType
 pango_attr_type_register (const gchar *name)
 {
-  static guint current_type = 0x1000000;
-  guint type = current_type++;
+  return pango_attr_type_register_full (name, PANGO_ATTR_INVALID);
+}
+
+/**
+ * pango_attr_type_register_full:
+ * @name: an identifier for the new type
+ * @parent_type: The base type
+ *
+ * Allocates a new attribute type ID that is based on an existing attribute.
+ * Attribute extensions may be up to four extensions 'deep', e.g.: Attribute
+ * A1 is not derived from another attribute; it may be derived into A2,
+ * which is then extended into A3, which is then extended into A4.  Any
+ * attempt to extend A4 will fail.  However, A1-A3 may be extended further.
+ *
+ *
+ * The attribute type name may be accessed later by using pango_attr_type_get_name().
+ **/
+PangoAttrType
+pango_attr_type_register_full (const gchar   *name,
+			       PangoAttrType  parent_type)
+{
+  guint new_subtype = 0;
+  PangoAttrType new_type = 0;
+  gpointer new_ptr;
+
+  /* This type cannot be extended any further. */
+  g_return_val_if_fail ((parent_type & 0xff000000) == 0, PANGO_ATTR_INVALID);
+  g_return_val_if_fail (name != NULL, PANGO_ATTR_INVALID);
+
+  if (G_UNLIKELY (!name_map)) {
+    name_map = g_hash_table_new (NULL, NULL);
+    type_map = g_hash_table_new (NULL, NULL);
+  }
 
-  if (name)
+  new_type = parent_type << 8;
+  new_ptr = g_hash_table_lookup (type_map, GUINT_TO_POINTER (new_type));
+
+  if (new_ptr == NULL)
     {
-      if (G_UNLIKELY (!name_map))
-	name_map = g_hash_table_new (NULL, NULL);
+      /* parent_type doesn't have any derived types yet, so start at 1. */
+      new_subtype = 1;
+      g_hash_table_insert (type_map, GUINT_TO_POINTER (new_type), GUINT_TO_POINTER (new_subtype));
+    } else {
+      /* There are already derived types.  Increment and store the latest one. */
+      new_subtype = GPOINTER_TO_UINT (new_ptr);
+      g_hash_table_replace (type_map, GUINT_TO_POINTER (new_type), GUINT_TO_POINTER (++new_subtype));
+    }
+
+  new_type += new_subtype;
+
+  g_hash_table_insert (name_map, GUINT_TO_POINTER (new_type), (gpointer)g_intern_string (name));
+
+  return new_type;
+}
+
+/**
+ * pango_attr_type_get_parent_type:
+ * @attr_type: an attribute type to find the base type of
+ *
+ * Retrieves the base type of a given attribute.  If @attr_type is a
+ * type that was registered using pango_attr_register_full() then this
+ * will return the base type that was used.  If @attr_type is not extended
+ * from another type, this will return PANGO_ATTR_INVALID.
+ **/
+PangoAttrType
+pango_attr_type_get_parent_type (PangoAttrType attr_type)
+{
+  return attr_type >> 8;
+}
+
+gboolean
+pango_attr_type_is_a (const PangoAttrType attr,
+		      const PangoAttrType possible_ancestor)
+{
+  PangoAttrType test_attr = attr;
+  if (test_attr == possible_ancestor)
+    return TRUE;
 
-      g_hash_table_insert (name_map, GUINT_TO_POINTER (type), (gpointer) g_intern_string (name));
+  do {
+    test_attr = test_attr >> 8;
+    if (test_attr == possible_ancestor)
+      return TRUE;
+  } while (test_attr & 0x0000ff00);
+
+  return FALSE;
+}
+
+static gboolean
+check_extended_type (guint test, guint mask, PangoAttrType attr, PangoAttrType ancestor)
+{
+  if (test)
+    {
+      if (((attr & mask) >> 8) == ancestor)
+	return TRUE;
     }
 
-  return type;
+  return FALSE;
+}
+
+gboolean
+pango_attribute_is_a (const PangoAttribute *attr,
+		      const PangoAttrType   ancestor_type)
+{
+  return pango_attr_type_is_a (attr->klass->type, ancestor_type);
 }
 
 /**
@@ -475,7 +568,7 @@ pango_attr_size_copy (const PangoAttribute *attr)
 {
   const PangoAttrSize *size_attr = (PangoAttrSize *)attr;
 
-  if (attr->klass->type == PANGO_ATTR_ABSOLUTE_SIZE)
+  if (pango_attr_type_is_a (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);
@@ -1771,7 +1864,7 @@ pango_attr_iterator_get (PangoAttrIterator *iterator,
     {
       PangoAttribute *attr = tmp_list->data;
 
-      if (attr->klass->type == type)
+      if (pango_attr_type_is_a (attr->klass->type, type))
 	return attr;
 
       tmp_list = tmp_list->next;
@@ -1828,74 +1921,88 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
       PangoAttribute *attr = tmp_list1->data;
       tmp_list1 = tmp_list1->next;
 
-      switch (attr->klass->type)
+      if (pango_attr_type_is_a (attr->klass->type, 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);
+	}
+
+      if (pango_attr_type_is_a (attr->klass->type, PANGO_ATTR_FAMILY))
 	{
-	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 (pango_attr_type_is_a (attr->klass->type, 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 (pango_attr_type_is_a (attr->klass->type, 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 (pango_attr_type_is_a (attr->klass->type, 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 (pango_attr_type_is_a (attr->klass->type, 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 (pango_attr_type_is_a (attr->klass->type, 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 (pango_attr_type_is_a (attr->klass->type, 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 (pango_attr_type_is_a (attr->klass->type, PANGO_ATTR_SCALE))
+	{
 	  if (!have_scale)
 	    {
 	      have_scale = TRUE;
 	      scale = ((PangoAttrFloat *)attr)->value;
 	    }
-	  break;
-	case PANGO_ATTR_LANGUAGE:
+	}
+
+      if (pango_attr_type_is_a (attr->klass->type, PANGO_ATTR_LANGUAGE))
+	{
 	  if (language)
 	    {
 	      if (!have_language)
@@ -1904,28 +2011,27 @@ pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
 		  *language = ((PangoAttrLanguage *)attr)->value;
 		}
 	    }
-	  break;
-	default:
-	  if (extra_attrs)
-	    {
-	      gboolean found = FALSE;
+	}
 
-	      tmp_list2 = *extra_attrs;
-	      while (tmp_list2)
-		{
-		  PangoAttribute *old_attr = tmp_list2->data;
-		  if (attr->klass->type == old_attr->klass->type)
-		    {
-		      found = TRUE;
-		      break;
-		    }
+      if (extra_attrs)
+	{
+	  gboolean found = FALSE;
 
-		  tmp_list2 = tmp_list2->next;
+	  tmp_list2 = *extra_attrs;
+	  while (tmp_list2)
+	    {
+	      PangoAttribute *old_attr = tmp_list2->data;
+	      if (attr->klass->type == old_attr->klass->type)
+		{
+		  found = TRUE;
+		  break;
 		}
 
-	      if (!found)
-		*extra_attrs = g_slist_prepend (*extra_attrs, pango_attribute_copy (attr));
+	      tmp_list2 = tmp_list2->next;
 	    }
+
+	  if (!found)
+	    *extra_attrs = g_slist_prepend (*extra_attrs, pango_attribute_copy (attr));
 	}
     }
 
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index d4ce840..af82475 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -176,8 +176,13 @@ struct _PangoAttrFontDesc
   PangoFontDescription *desc;
 };
 
-PangoAttrType         pango_attr_type_register (const gchar        *name);
-G_CONST_RETURN char * pango_attr_type_get_name (PangoAttrType       type) G_GNUC_CONST;
+PangoAttrType         pango_attr_type_register        (const gchar   *name);
+PangoAttrType         pango_attr_type_register_full   (const char    *name,
+						       PangoAttrType  similar_type);
+G_CONST_RETURN char * pango_attr_type_get_name        (PangoAttrType  type) G_GNUC_CONST;
+PangoAttrType         pango_attr_type_get_parent_type (PangoAttrType  attr_type);
+gboolean              pango_attr_type_is_a            (const PangoAttrType attr,
+						       const PangoAttrType possible_ancestor);
 
 void             pango_attribute_init        (PangoAttribute       *attr,
 					      const PangoAttrClass *klass);
@@ -185,6 +190,8 @@ PangoAttribute * pango_attribute_copy        (const PangoAttribute *attr);
 void             pango_attribute_destroy     (PangoAttribute       *attr);
 gboolean         pango_attribute_equal       (const PangoAttribute *attr1,
 					      const PangoAttribute *attr2) G_GNUC_PURE;
+gboolean         pango_attribute_is_a        (const PangoAttribute *attr,
+					      const PangoAttrType   ancestor_type);
 
 PangoAttribute *pango_attr_language_new      (PangoLanguage              *language);
 PangoAttribute *pango_attr_family_new        (const char                 *family);
diff --git a/pango/pangocairo-context.c b/pango/pangocairo-context.c
index 455f5e4..2135a81 100644
--- a/pango/pangocairo-context.c
+++ b/pango/pangocairo-context.c
@@ -27,7 +27,15 @@
 
 #include <string.h>
 
-typedef struct _PangoCairoContextInfo PangoCairoContextInfo;
+typedef struct _PangoCairoContextInfo       PangoCairoContextInfo;
+typedef struct _PangoCairoShapeRendererInfo PangoCairoShapeRendererInfo;
+
+struct _PangoCairoShapeRendererInfo
+{
+  PangoCairoShapeRendererFunc shape_renderer_func;
+  gpointer                    shape_renderer_data;
+  GDestroyNotify              shape_renderer_notify;
+};
 
 struct _PangoCairoContextInfo
 {
@@ -37,25 +45,35 @@ struct _PangoCairoContextInfo
   cairo_font_options_t *surface_options;
   cairo_font_options_t *merged_options;
 
-  PangoCairoShapeRendererFunc shape_renderer_func;
-  gpointer                    shape_renderer_data;
-  GDestroyNotify              shape_renderer_notify;
+  GHashTable *shape_renderers;
 };
 
 static void
+free_shape_renderer_info (gpointer key,
+			  gpointer value,
+			  gpointer data)
+{
+  PangoCairoShapeRendererInfo *info = value;
+
+  if (info->shape_renderer_notify)
+    info->shape_renderer_notify (info->shape_renderer_data);
+
+  g_slice_free (PangoCairoShapeRendererInfo, info);
+}
+
+static void
 free_context_info (PangoCairoContextInfo *info)
 {
+  if (info->shape_renderers)
+    g_hash_table_foreach (info->shape_renderers,
+			  free_shape_renderer_info, NULL);
+
   if (info->set_options)
     cairo_font_options_destroy (info->set_options);
   if (info->surface_options)
     cairo_font_options_destroy (info->surface_options);
   if (info->merged_options)
     cairo_font_options_destroy (info->merged_options);
-
-  if (info->shape_renderer_notify)
-    info->shape_renderer_notify (info->shape_renderer_data);
-
-  g_slice_free (PangoCairoContextInfo, info);
 }
 
 static PangoCairoContextInfo *
@@ -325,18 +343,46 @@ pango_cairo_context_set_shape_renderer (PangoContext                *context,
 					gpointer                     data,
 					GDestroyNotify               dnotify)
 {
+  pango_cairo_context_set_shape_renderer_for_attr_type (context,
+							PANGO_ATTR_SHAPE,
+							func,
+							data,
+							dnotify);
+}
+
+void
+pango_cairo_context_set_shape_renderer_for_attr_type (PangoContext                *context,
+						      PangoAttrType                attr_type,
+						      PangoCairoShapeRendererFunc  func,
+						      gpointer                     data,
+						      GDestroyNotify               dnotify)
+{
   PangoCairoContextInfo *info;
+  PangoCairoShapeRendererInfo *renderer_info;
 
   g_return_if_fail (PANGO_IS_CONTEXT (context));
+  g_return_if_fail (pango_attr_type_is_a (attr_type, PANGO_ATTR_SHAPE));
 
-  info  = get_context_info (context, TRUE);
+  info = get_context_info (context, TRUE);
 
-  if (info->shape_renderer_notify)
-    info->shape_renderer_notify (info->shape_renderer_data);
+  if (!info->shape_renderers)
+    info->shape_renderers = g_hash_table_new (g_int_hash, g_int_equal);
 
-  info->shape_renderer_func   = func;
-  info->shape_renderer_data   = data;
-  info->shape_renderer_notify = dnotify;
+  renderer_info = g_hash_table_lookup (info->shape_renderers, GUINT_TO_POINTER (attr_type));
+  if (!renderer_info)
+    {
+      renderer_info = g_slice_new0 (PangoCairoShapeRendererInfo);
+      g_hash_table_insert (info->shape_renderers,
+			   GUINT_TO_POINTER (attr_type),
+			   renderer_info);
+    }
+
+  if (renderer_info->shape_renderer_notify)
+    renderer_info->shape_renderer_notify (renderer_info->shape_renderer_data);
+
+  renderer_info->shape_renderer_func   = func;
+  renderer_info->shape_renderer_data   = data;
+  renderer_info->shape_renderer_notify = dnotify;
 }
 
 /**
@@ -358,27 +404,42 @@ pango_cairo_context_set_shape_renderer (PangoContext                *context,
  * Since: 1.18
  */
 PangoCairoShapeRendererFunc
-pango_cairo_context_get_shape_renderer (PangoContext                *context,
-					gpointer                    *data)
+pango_cairo_context_get_shape_renderer (PangoContext *context,
+					gpointer     *data)
+{
+  return pango_cairo_context_get_shape_renderer_for_attr_type (context,
+							       PANGO_ATTR_SHAPE,
+							       data);
+}
+
+PangoCairoShapeRendererFunc
+pango_cairo_context_get_shape_renderer_for_attr_type (PangoContext   *context,
+						      PangoAttrType   attr_type,
+						      gpointer       *data)
 {
   PangoCairoContextInfo *info;
+  PangoCairoShapeRendererInfo *renderer_info;
 
   g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
+  g_return_val_if_fail (pango_attr_type_is_a (attr_type, PANGO_ATTR_SHAPE), NULL);
 
   info = get_context_info (context, FALSE);
 
   if (info)
     {
-      if (data)
-        *data = info->shape_renderer_data;
-      return info->shape_renderer_func;
-    }
-  else
-    {
-      if (data)
-        *data = NULL;
-      return NULL;
+      renderer_info = g_hash_table_lookup (info->shape_renderers,
+					   GUINT_TO_POINTER (attr_type));
+      if (renderer_info)
+	{
+	  if (data)
+	    *data = renderer_info->shape_renderer_data;
+	  return renderer_info->shape_renderer_func;
+	}
     }
+
+  if (data)
+    *data = NULL;
+  return NULL;
 }
 
 /**
diff --git a/pango/pangocairo.h b/pango/pangocairo.h
index f6fc261..c5c724f 100644
--- a/pango/pangocairo.h
+++ b/pango/pangocairo.h
@@ -102,8 +102,16 @@ void                        pango_cairo_context_set_shape_renderer (PangoContext
 								    PangoCairoShapeRendererFunc  func,
 								    gpointer                     data,
 								    GDestroyNotify               dnotify);
-PangoCairoShapeRendererFunc pango_cairo_context_get_shape_renderer (PangoContext                *context,
-								    gpointer                    *data);
+void          pango_cairo_context_set_shape_renderer_for_attr_type (PangoContext                *context,
+								    PangoAttrType                attr_type,
+								    PangoCairoShapeRendererFunc  func,
+								    gpointer                     data,
+								    GDestroyNotify               dnotify);
+PangoCairoShapeRendererFunc pango_cairo_context_get_shape_renderer               (PangoContext   *context,
+										  gpointer       *data);
+PangoCairoShapeRendererFunc pango_cairo_context_get_shape_renderer_for_attr_type (PangoContext   *context,
+										  PangoAttrType   attr_type,
+										  gpointer       *data);
 
 /* Convenience
  */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5eafedd..b3967af 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -41,9 +41,9 @@ TESTS_ENVIRONMENT =                    \
    srcdir=$(srcdir)                    \
    PANGO_RC_FILE=./pangorc
 
-noinst_PROGRAMS = gen-all-unicode dump-boundaries 
+noinst_PROGRAMS = gen-all-unicode dump-boundaries testattributes
 
-check_PROGRAMS = testboundaries testboundaries_ucd testcolor testscript
+check_PROGRAMS = testboundaries testboundaries_ucd testcolor testscript 
 
 if HAVE_CAIRO
 check_PROGRAMS += testiter
@@ -55,6 +55,8 @@ endif
 gen_all_unicode_LDADD = $(GLIB_LIBS)
 testboundaries_LDADD = ../pango/libpango-$(PANGO_API_VERSION).la
 testboundaries_ucd_LDADD = ../pango/libpango-$(PANGO_API_VERSION).la
+testattributes_SOURCES = testattributes.c
+testattributes_LDADD = ../pango/libpango-$(PANGO_API_VERSION).la
 testcolor_LDADD = ../pango/libpango-$(PANGO_API_VERSION).la
 testiter_LDADD = ../pango/libpango-$(PANGO_API_VERSION).la ../pango/libpangocairo-$(PANGO_API_VERSION).la 
 testscript_LDADD = ../pango/libpango-$(PANGO_API_VERSION).la
diff --git a/tests/testattributes.c b/tests/testattributes.c
new file mode 100644
index 0000000..842b6b2
--- /dev/null
+++ b/tests/testattributes.c
@@ -0,0 +1,52 @@
+/* Pango
+ * testattributes.c: Test program for Pango extended attributes
+ *
+ * Copyright (C) 2008 Cody Russell
+ *
+ * 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 <glib.h>
+#include <pango/pango.h>
+
+
+static void
+test_derived ()
+{
+  PangoAttrType german = pango_attr_type_register_full ("German",
+							PANGO_ATTR_LANGUAGE);
+  PangoAttrType swiss_german = pango_attr_type_register_full ("SwissGerman",
+							      german);
+  PangoAttrType french = pango_attr_type_register_full ("French",
+							PANGO_ATTR_LANGUAGE);
+
+  g_assert (pango_attr_type_is_a (german, german));
+  g_assert (pango_attr_type_is_a (german, PANGO_ATTR_LANGUAGE));
+  g_assert (pango_attr_type_is_a (swiss_german, PANGO_ATTR_LANGUAGE));
+  g_assert (pango_attr_type_is_a (swiss_german, german));
+  g_assert (!pango_attr_type_is_a (french, german));
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/attributes/test-derived", test_derived);
+
+  return g_test_run ();
+}
