[cairo-commit] 18 commits - doc/public src/cairo.c src/cairo-font-face.c src/cairo-gstate.c src/cairo.h src/cairoint.h src/cairo-misc.c src/cairo-pdf-operators.c src/cairo-pdf-surface.c src/cairo-scaled-font.c src/cairo-scaled-font-subsets.c src/cairo-surface.c src/cairo-truetype-subset.c src/cairo-unicode.c src/cairo-user-font.c src/cairo-win32-font.c test/.gitignore test/Makefile.am test/toy-font-face.c test/user-font.c test/user-font-proxy.c test/user-font-proxy-pdf-ref.png test/user-font-proxy-ps-ref.png

Behdad Esfahbod behdad at kemper.freedesktop.org
Fri Aug 8 00:08:04 PDT 2008


 doc/public/Headers.mk                  |    1 
 doc/public/Makefile.am                 |    1 
 doc/public/cairo-sections.txt          |    9 
 doc/public/tmpl/cairo-scaled-font.sgml |   18 +
 doc/public/tmpl/cairo-status.sgml      |    2 
 doc/public/tmpl/cairo-text.sgml        |   72 ++++++
 doc/public/tmpl/cairo-user-fonts.sgml  |    5 
 src/cairo-font-face.c                  |  196 ++++++++++++++++-
 src/cairo-gstate.c                     |   44 ++--
 src/cairo-misc.c                       |  182 ++++++++++++++++
 src/cairo-pdf-operators.c              |    6 
 src/cairo-pdf-surface.c                |    2 
 src/cairo-scaled-font-subsets.c        |   30 +-
 src/cairo-scaled-font.c                |  363 +++++++++++++++++++++++++++++----
 src/cairo-surface.c                    |    2 
 src/cairo-truetype-subset.c            |   11 -
 src/cairo-unicode.c                    |   42 +++
 src/cairo-user-font.c                  |   82 ++++---
 src/cairo-win32-font.c                 |    2 
 src/cairo.c                            |  205 +++++++++++-------
 src/cairo.h                            |  138 +++++++++++-
 src/cairoint.h                         |   71 ++++--
 test/.gitignore                        |    1 
 test/Makefile.am                       |    3 
 test/toy-font-face.c                   |  129 +++++++++++
 test/user-font-proxy-pdf-ref.png       |binary
 test/user-font-proxy-ps-ref.png        |binary
 test/user-font-proxy.c                 |  104 +++++----
 test/user-font.c                       |   13 -
 29 files changed, 1453 insertions(+), 281 deletions(-)

New commits:
commit eabe572981e1e415171dbfde81c3bf94297355b4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 00:56:36 2008 -0400

    Document all new API
    
    Also validate clusters generated by font backends.

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index 4b9cdc5..0f9a4c6 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -292,6 +292,7 @@ cairo_scaled_font_extents
 cairo_text_extents_t
 cairo_scaled_font_text_extents
 cairo_scaled_font_glyph_extents
+cairo_scaled_font_text_to_glyphs
 cairo_scaled_font_get_font_face
 cairo_scaled_font_get_font_options
 cairo_scaled_font_get_font_matrix
@@ -412,6 +413,10 @@ cairo_toy_font_face_create
 cairo_toy_font_face_get_family
 cairo_toy_font_face_get_slant
 cairo_toy_font_face_get_weight
+cairo_glyph_allocate
+cairo_glyph_free
+cairo_text_cluster_allocate
+cairo_text_cluster_free
 </SECTION>
 
 <SECTION>
diff --git a/doc/public/tmpl/cairo-scaled-font.sgml b/doc/public/tmpl/cairo-scaled-font.sgml
index 97922ef..1abf0f3 100644
--- a/doc/public/tmpl/cairo-scaled-font.sgml
+++ b/doc/public/tmpl/cairo-scaled-font.sgml
@@ -119,6 +119,24 @@ size and transformation and a certain set of font options.
 @extents: 
 
 
+<!-- ##### FUNCTION cairo_scaled_font_text_to_glyphs ##### -->
+<para>
+
+</para>
+
+ at scaled_font: 
+ at x: 
+ at y: 
+ at utf8: 
+ at utf8_len: 
+ at glyphs: 
+ at num_glyphs: 
+ at clusters: 
+ at num_clusters: 
+ at backward: 
+ at Returns: 
+
+
 <!-- ##### FUNCTION cairo_scaled_font_get_font_face ##### -->
 <para>
 
diff --git a/doc/public/tmpl/cairo-text.sgml b/doc/public/tmpl/cairo-text.sgml
index 3e5c6bb..54fb9bd 100644
--- a/doc/public/tmpl/cairo-text.sgml
+++ b/doc/public/tmpl/cairo-text.sgml
@@ -282,3 +282,37 @@ Cairo has two sets of text rendering capabilities:
 @Returns:
 
 
+<!-- ##### FUNCTION cairo_glyph_allocate ##### -->
+<para>
+
+</para>
+
+ at num_glyphs: 
+ at Returns: 
+
+
+<!-- ##### FUNCTION cairo_glyph_free ##### -->
+<para>
+
+</para>
+
+ at glyphs: 
+
+
+<!-- ##### FUNCTION cairo_text_cluster_allocate ##### -->
+<para>
+
+</para>
+
+ at num_clusters: 
+ at Returns: 
+
+
+<!-- ##### FUNCTION cairo_text_cluster_free ##### -->
+<para>
+
+</para>
+
+ at clusters: 
+
+
diff --git a/doc/public/tmpl/cairo-user-fonts.sgml b/doc/public/tmpl/cairo-user-fonts.sgml
index 1a9322a..b64bf25 100644
--- a/doc/public/tmpl/cairo-user-fonts.sgml
+++ b/doc/public/tmpl/cairo-user-fonts.sgml
@@ -23,6 +23,7 @@ User Fonts
 </para>
 
 @scaled_font: 
+ at cr: 
 @extents: 
 @Returns: 
 
@@ -46,8 +47,12 @@ User Fonts
 
 @scaled_font: 
 @utf8: 
+ at utf8_len: 
 @glyphs: 
 @num_glyphs: 
+ at clusters: 
+ at num_clusters: 
+ at backward: 
 @Returns: 
 
 
diff --git a/src/cairo-misc.c b/src/cairo-misc.c
index 1e74c67..9a0ede0 100644
--- a/src/cairo-misc.c
+++ b/src/cairo-misc.c
@@ -153,6 +153,7 @@ cairo_glyph_allocate (int num_glyphs)
 
     return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
 }
+slim_hidden_def (cairo_glyph_allocate);
 
 /**
  * cairo_glyph_free:
@@ -173,6 +174,7 @@ cairo_glyph_free (cairo_glyph_t *glyphs)
     if (glyphs)
 	free (glyphs);
 }
+slim_hidden_def (cairo_glyph_free);
 
 /**
  * cairo_text_cluster_allocate:
@@ -202,10 +204,11 @@ cairo_text_cluster_allocate (int num_clusters)
 
     return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
 }
+slim_hidden_def (cairo_text_cluster_allocate);
 
 /**
  * cairo_text_cluster_free:
- * @text_clusters: array of text clusters to free, or %NULL
+ * @clusters: array of text clusters to free, or %NULL
  *
  * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate().
  * This function is only useful to free text cluster array returned
@@ -222,11 +225,82 @@ cairo_text_cluster_free (cairo_text_cluster_t *clusters)
     if (clusters)
 	free (clusters);
 }
+slim_hidden_def (cairo_text_cluster_free);
 
 
 /* Private stuff */
 
 /**
+ * _cairo_validate_text_clusters:
+ * @utf8: UTF-8 text
+ * @utf8_len: length of @utf8 in bytes
+ * @glyphs: array of glyphs
+ * @num_glyphs: number of glyphs
+ * @clusters: array of cluster mapping information
+ * @num_clusters: number of clusters in the mapping
+ * @backward: whether the text to glyphs mapping goes backward
+ *
+ * Check that clusters cover the entire glyphs and utf8 arrays,
+ * and that cluster boundaries are UTF-8 boundaries.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS upon success, or
+ *               %CAIRO_STATUS_INVALID_CLUSTERS on error.
+ *               The error is either invalid UTF-8 input,
+ *               or bad cluster mapping.
+ */
+cairo_status_t
+_cairo_validate_text_clusters (const char		   *utf8,
+			       int			    utf8_len,
+			       const cairo_glyph_t	   *glyphs,
+			       int			    num_glyphs,
+			       const cairo_text_cluster_t  *clusters,
+			       int			    num_clusters,
+			       cairo_bool_t		    backward)
+{
+    cairo_status_t status;
+    unsigned int n_bytes  = 0;
+    unsigned int n_glyphs = 0;
+    int i;
+
+    for (i = 0; i < num_clusters; i++) {
+	int cluster_bytes  = clusters[i].num_bytes;
+	int cluster_glyphs = clusters[i].num_glyphs;
+
+	if (cluster_bytes < 0 || cluster_glyphs < 0)
+	    goto BAD;
+
+	/* A cluster should cover at least one character or glyph.
+	 * I can't see any use for a 0,0 cluster.
+	 * I can't see an immediate use for a zero-text cluster
+	 * right now either, but they don't harm.
+	 * Zero-glyph clusters on the other hand are useful for
+	 * things like U+200C ZERO WIDTH NON-JOINER */
+	if (cluster_bytes == 0 && cluster_glyphs == 0)
+	    goto BAD;
+
+	/* Since n_bytes and n_glyphs are unsigned, but the rest of
+	 * values involved are signed, we can detect overflow easily */
+	if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs)
+	    goto BAD;
+
+	/* Make sure we've got valid UTF-8 for the cluster */
+	status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL);
+	if (status)
+	    return CAIRO_STATUS_INVALID_CLUSTERS;
+
+	n_bytes  += cluster_bytes ;
+	n_glyphs += cluster_glyphs;
+    }
+
+    if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) {
+      BAD:
+	return CAIRO_STATUS_INVALID_CLUSTERS;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
  * _cairo_operator_bounded_by_mask:
  * @op: a #cairo_operator_t
  *
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index fa54231..1429780 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1083,7 +1083,7 @@ slim_hidden_def (cairo_scaled_font_extents);
 /**
  * cairo_scaled_font_text_extents:
  * @scaled_font: a #cairo_scaled_font_t
- * @utf8: a string of text, encoded in UTF-8
+ * @utf8: a NUL-terminated string of text, encoded in UTF-8
  * @extents: a #cairo_text_extents_t which to store the retrieved extents.
  *
  * Gets the extents for a string of text. The extents describe a
@@ -1268,6 +1268,143 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t   *scaled_font,
 }
 slim_hidden_def (cairo_scaled_font_glyph_extents);
 
+/**
+ * cairo_scaled_font_text_to_glyphs:
+ * @x: X position to place first glyph
+ * @y: Y position to place first glyph
+ * @scaled_font: a #cairo_scaled_font_t
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
+ * @glyphs: pointer to array of glyphs to fill
+ * @num_glyphs: pointer to number of glyphs
+ * @clusters: pointer to array of cluster mapping information to fill, or %NULL
+ * @num_clusters: pointer to number of clusters, or %NULL
+ * @backward: pointer to whether the text to glyphs mapping goes backward, or
+ *            %NULL
+ *
+ * Converts UTF-8 text to an array of glyphs, optionally with cluster
+ * mapping, that can be used to render later using @scaled_font.
+ *
+ * If @glyphs initially points to a non-%NULL value, that array is used
+ * as a glyph buffer, and @num_glyphs should point to the number of glyph
+ * entries available there.  If the provided glyph array is too short for
+ * the conversion, a new glyph array is allocated using cairo_glyph_allocate()
+ * and placed in @glyphs.  Upon return, @num_glyphs always contains the
+ * number of generated glyphs.  If the value @glyphs points at has changed
+ * after the call, the user is responsible for freeing the allocated glyph
+ * array using cairo_glyph_free().
+ *
+ * If @clusters is not %NULL, @num_clusters and @backward should not be %NULL,
+ * and cluster mapping will be computed.
+ * The semantics of how cluster array allocation works is similar to the glyph
+ * array.  That is,
+ * if @clusters initially points to a non-%NULL value, that array is used
+ * as a cluster buffer, and @num_clusters should point to the number of cluster
+ * entries available there.  If the provided cluster array is too short for
+ * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate()
+ * and placed in @clusters.  Upon return, @num_clusters always contains the
+ * number of generated clusters.  If the value @clusters points at has changed
+ * after the call, the user is responsible for freeing the allocated cluster
+ * array using cairo_text_cluster_free().
+ *
+ * In the simplest case, @glyphs and @clusters can point to %NULL initially
+ * and a suitable array will be allocated.  In code:
+ * <informalexample><programlisting>
+ * cairo_status_t status;
+ *
+ * cairo_glyph_t *glyphs = NULL;
+ * int num_glyphs;
+ * cairo_text_cluster_t *clusters = NULL;
+ * int num_clusters;
+ * cairo_bool_t backward;
+ *
+ * status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ *                                            x, y,
+ *                                            utf8, utf8_len,
+ *                                            &amp;glyphs, &amp;num_glyphs,
+ *                                            &amp;clusters, &amp;num_clusters,
+ *                                            &amp;backward);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ *     cairo_show_text_glyphs (cr,
+ *                             utf8, utf8_len,
+ *                             *glyphs, *num_glyphs,
+ *                             *clusters, *num_clusters,
+ *                             *backward);
+ *
+ *     cairo_glyph_free (*glyphs);
+ *     cairo_text_cluster_free (*clusters);
+ * }
+ * </programlisting></informalexample>
+ *
+ * If no cluster mapping is needed:
+ * <informalexample><programlisting>
+ * cairo_status_t status;
+ *
+ * cairo_glyph_t *glyphs = NULL;
+ * int num_glyphs;
+ *
+ * status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ *                                            x, y,
+ *                                            utf8, utf8_len,
+ *                                            &amp;glyphs, &amp;num_glyphs,
+ *                                            NULL, NULL,
+ *                                            NULL);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ *     cairo_show_glyphs (cr, *glyphs, *num_glyphs);
+ *     cairo_glyph_free (*glyphs);
+ * }
+ * </programlisting></informalexample>
+ *
+ * If stack-based glyph and cluster arrays are to be used for small
+ * arrays:
+ * <informalexample><programlisting>
+ * cairo_status_t status;
+ *
+ * cairo_glyph_t stack_glyphs[40];
+ * cairo_glyph_t *glyphs = stack_glyphs;
+ * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]);
+ * cairo_text_cluster_t stack_clusters[40];
+ * cairo_text_cluster_t *clusters = stack_clusters;
+ * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]);
+ * cairo_bool_t backward;
+ *
+ * status = cairo_scaled_font_text_to_glyphs (scaled_font,
+ *                                            x, y,
+ *                                            utf8, utf8_len,
+ *                                            &amp;glyphs, &amp;num_glyphs,
+ *                                            &amp;clusters, &amp;num_clusters,
+ *                                            &amp;backward);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ *     cairo_show_text_glyphs (cr,
+ *                             utf8, utf8_len,
+ *                             *glyphs, *num_glyphs,
+ *                             *clusters, *num_clusters,
+ *                             *backward);
+ *
+ *     if (glyphs != stack_glyphs)
+ *         cairo_glyph_free (*glyphs);
+ *     if (clusters != stack_clusters)
+ *         cairo_text_cluster_free (*clusters);
+ * }
+ * </programlisting></informalexample>
+ *
+ * For details of how (@clusters, @num_clusters, and @backward map input
+ * UTF-8 text to the output glyphs see cairo_show_text_glyphs().
+ *
+ * The output values can be readily passed to cairo_show_text_glyphs()
+ * cairo_show_glyphs(), or related functions, assuming that the exact
+ * same @scaled_font is used for the operation.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status
+ * if the input values are wrong or if conversion failed.  If the input
+ * values are correct but the conversion failed, the error status is also
+ * set on @scaled_font.
+ *
+ * Since: 1.8
+ **/
 cairo_status_t
 cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 				  double		 x,
@@ -1300,6 +1437,10 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 	goto BAIL;
     }
 
+    /* Special case for NULL and -1 */
+    if (utf8 == NULL && utf8_len == -1)
+	utf8_len = 0;
+
     /* No NULLs for non-NULLs! */
     if ((utf8_len && utf8         == NULL) ||
 	(clusters && num_clusters == NULL) ||
@@ -1364,8 +1505,44 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 						       clusters, num_clusters,
 						       backward);
 
-        if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+        if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+
+	    if (status == CAIRO_STATUS_SUCCESS) {
+
+	        /* The checks here are crude; we only should do them in
+		 * user-font backend, but they don't hurt here.  This stuff
+		 * can be hard to get right. */
+
+	        if (*num_glyphs < 0) {
+		    status = CAIRO_STATUS_NEGATIVE_COUNT;
+		    goto DONE;
+		}
+		if (num_glyphs && *glyphs == NULL) {
+		    status = CAIRO_STATUS_NULL_POINTER;
+		    goto DONE;
+		}
+
+		if (clusters) {
+
+		    if (*num_clusters < 0) {
+			status = CAIRO_STATUS_NEGATIVE_COUNT;
+			goto DONE;
+		    }
+		    if (num_clusters && *clusters == NULL) {
+			status = CAIRO_STATUS_NULL_POINTER;
+			goto DONE;
+		    }
+
+		    /* Dont trust the backend, validate clusters! */
+		    status = _cairo_validate_text_clusters (utf8, utf8_len,
+							    *glyphs, *num_glyphs,
+							    *clusters, *num_clusters,
+							    *backward);
+		}
+	    }
+
             goto DONE;
+	}
     }
 
     if (*num_glyphs < num_chars) {
@@ -1450,6 +1627,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 
     return status;
 }
+slim_hidden_def (cairo_scaled_font_text_to_glyphs);
 
 /*
  * Compute a device-space bounding box for the glyphs.
diff --git a/src/cairo.c b/src/cairo.c
index e42fd70..82cd80b 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -2971,7 +2971,7 @@ cairo_get_scaled_font (cairo_t *cr)
 /**
  * cairo_text_extents:
  * @cr: a #cairo_t
- * @utf8: a string of text encoded in UTF-8, or %NULL
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
  * @extents: a #cairo_text_extents_t object into which the results
  * will be stored
  *
@@ -3088,7 +3088,7 @@ cairo_glyph_extents (cairo_t                *cr,
 /**
  * cairo_show_text:
  * @cr: a cairo context
- * @utf8: a string of text encoded in UTF-8, or %NULL
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
  *
  * A drawing operator that generates the shape from a string of UTF-8
  * characters, rendered according to the current font_face, font_size
@@ -3179,8 +3179,8 @@ cairo_show_text (cairo_t *cr, const char *utf8)
  * @num_glyphs: number of glyphs to show
  *
  * A drawing operator that generates the shape from an array of glyphs,
- * rendered according to the current font_face, font_size
- * (font_matrix), and font_options.
+ * rendered according to the current font face, font size
+ * (font matrix), and font options.
  **/
 void
 cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
@@ -3212,12 +3212,66 @@ cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
 	_cairo_set_error (cr, status);
 }
 
+/**
+ * cairo_has_show_text_glyphs:
+ * @cr: a cairo context
+ *
+ * Returns whether the target surface of a cairo context supports
+ * sophisticated cairo_show_text_glyphs() operations.  That is,
+ * whether it actually uses the provided text and cluster data
+ * to a cairo_show_text_glyphs() call.
+ *
+ * Note: Even if this function returns %FALSE, a
+ * cairo_show_text_glyphs() operation will still succeed.  It just will
+ * act like a cairo_show_glyphs() operation.  Users can use this
+ * function to avoid computing UTF-8 text and cluster mapping if the
+ * target surface does not use it.
+ *
+ * Return value: %TRUE if the target surface of @cr supports
+ *               cairo_show_text_glyphs(), %FALSE otherwise
+ *
+ * Since: 1.8
+ **/
 cairo_bool_t
 cairo_has_show_text_glyphs (cairo_t			   *cr)
 {
     return _cairo_gstate_has_show_text_glyphs (cr->gstate);
 }
+slim_hidden_def (cairo_has_show_text_glyphs);
 
+/**
+ * cairo_show_text_glyphs:
+ * @cr: a cairo context
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated
+ * @glyphs: array of glyphs to show
+ * @num_glyphs: number of glyphs to show
+ * @clusters: array of cluster mapping information
+ * @num_clusters: number of clusters in the mapping
+ * @backward: whether the text to glyphs mapping goes backward
+ *
+ * This operation has rendering effects similar to cairo_show_glyphs()
+ * but, if the target surface supports it, uses the provided text and
+ * cluster mapping to embed the text for the glyphs shown in the output.
+ * The cairo_has_show_text_glyphs() function can be used to query that.
+ * If the target does not support it, this function acts like
+ * cairo_show_glyphs().
+ *
+ * The mapping between @utf8 and @glyphs is provided by an array of
+ * <firstterm>clusters</firstterm>.  Each cluster covers a number of
+ * text bytes and glyphs, and neighboring clusters cover neighboring
+ * areas of @utf8 and @glyphs.  The clusters should collectively cover @utf8
+ * and @glyphs in entirety.
+ *
+ * The first cluster always covers bytes from the beginning of @utf8.
+ * If @backward is %FALSE, the first cluster also covers the beginning
+ * of @glyphs, otherwise it covers the end of the @glyphs array and
+ * following clusters move backward.
+ *
+ * See #cairo_text_cluster_t for constraints on valid clusters.
+ *
+ * Since: 1.8
+ **/
 void
 cairo_show_text_glyphs (cairo_t			   *cr,
 			const char		   *utf8,
@@ -3235,6 +3289,10 @@ cairo_show_text_glyphs (cairo_t			   *cr,
 
     /* A slew of sanity checks */
 
+    /* Special case for NULL and -1 */
+    if (utf8 == NULL && utf8_len == -1)
+	utf8_len = 0;
+
     /* No NULLs for non-zeros */
     if ((num_glyphs   && glyphs   == NULL) ||
 	(utf8_len     && utf8     == NULL) ||
@@ -3255,48 +3313,22 @@ cairo_show_text_glyphs (cairo_t			   *cr,
 
     /* Make sure clusters cover the entire glyphs and utf8 arrays,
      * and that cluster boundaries are UTF-8 boundaries. */
-    {
-	unsigned int n_bytes  = 0;
-	unsigned int n_glyphs = 0;
-	int i;
-
-	for (i = 0; i < num_clusters; i++) {
-	    int cluster_bytes  = clusters[i].num_bytes;
-	    int cluster_glyphs = clusters[i].num_glyphs;
-
-	    if (cluster_bytes < 0 || cluster_glyphs < 0)
-	        goto BAD;
-
-	    /* A cluster should cover at least one character or glyph.
-	     * I can't see any use for a 0,0 cluster.
-	     * I can't see an immediate use for a zero-text cluster
-	     * right now either, but they don't harm.
-	     * Zero-glyph clusters on the other hand are useful for
-	     * things like U+200C ZERO WIDTH NON-JOINER */
-	    if (cluster_bytes == 0 && cluster_glyphs == 0)
-	        goto BAD;
-
-	    /* Since n_bytes and n_glyphs are unsigned, but the rest of
-	     * values involved are signed, we can detect overflow easily */
-	    if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs)
-	        goto BAD;
-
-	    /* Make sure we've got valid UTF-8 for the cluster */
-	    status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL);
-	    if (status) {
-		_cairo_set_error (cr, status);
-		return;
-	    }
-
-	    n_bytes  += cluster_bytes ;
-	    n_glyphs += cluster_glyphs;
-	}
-
-	if (n_bytes != (unsigned int)utf8_len || n_glyphs != (unsigned int)num_glyphs) {
-	  BAD:
-	    _cairo_set_error (cr, CAIRO_STATUS_INVALID_CLUSTERS);
-	    return;
-	}
+    status = _cairo_validate_text_clusters (utf8, utf8_len,
+					    glyphs, num_glyphs,
+					    clusters, num_clusters,
+					    backward);
+    if (status == CAIRO_STATUS_INVALID_CLUSTERS) {
+	/* Either got invalid UTF-8 text, or cluster mapping is bad.
+	 * Differentiate those. */
+
+	cairo_status_t status2;
+
+	status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL);
+	if (status2)
+	    status = status2;
+
+	_cairo_set_error (cr, status);
+	return;
     }
 
     if (num_glyphs == 0 && utf8_len == 0)
@@ -3314,7 +3346,7 @@ cairo_show_text_glyphs (cairo_t			   *cr,
 /**
  * cairo_text_path:
  * @cr: a cairo context
- * @utf8: a string of text encoded in UTF-8, or %NULL
+ * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL
  *
  * Adds closed paths for text to the current path.  The generated
  * path if filled, achieves an effect similar to that of
@@ -3522,11 +3554,11 @@ cairo_has_current_point (cairo_t *cr)
  * cairo_move_to(), cairo_line_to(), cairo_curve_to(),
  * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(),
  * cairo_arc(), cairo_arc_negative(), cairo_rectangle(),
- * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path()
+ * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path().
  *
- * Some functions use and alter the current point but do not otherwise
- * change current path:
- * cairo_show_text(), cairo_show_glyphs().
+ * Some functions use and alter the current point but do not
+ * otherwise change current path:
+ * cairo_show_text().
  *
  * Some functions unset the current path and as a result, current point:
  * cairo_fill(), cairo_stroke().
@@ -3688,6 +3720,7 @@ cairo_get_target (cairo_t *cr)
 
     return _cairo_gstate_get_original_target (cr->gstate);
 }
+slim_hidden_def (cairo_get_target);
 
 /**
  * cairo_get_group_target:
diff --git a/src/cairo.h b/src/cairo.h
index ee933ab..8b50c04 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -209,7 +209,7 @@ typedef struct _cairo_user_data_key {
  * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8)
  * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8)
  * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8)
- * @CAIRO_STATUS_INVALID_CLUSTERS: input value for an input #cairo_font_weight_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8)
  *
  * #cairo_status_t is used to indicate errors that can occur when
  * using Cairo. In some cases it is returned directly by functions.
@@ -836,6 +836,23 @@ cairo_glyph_allocate (int num_glyphs);
 cairo_public void
 cairo_glyph_free (cairo_glyph_t *glyphs);
 
+/**
+ * cairo_text_cluster_t:
+ * @num_bytes: the number of bytes of UTF-8 text covered by cluster
+ * @num_glyphs: the number of glyphs covered by cluster
+ *
+ * The #cairo_text_cluster_t structure holds information about a single
+ * <firstterm>text cluster</firstterm>.  A text cluster is a minimal
+ * mapping of some glyphs corresponding to some UTF-8 text.
+ *
+ * For a cluster to be valid, both @num_bytes and @num_glyphs should
+ * be non-negative, and at least one should be non-zero.
+ *
+ * See cairo_show_text_glyphs() for how clusters are used in advanced
+ * text operations.
+ *
+ * Since: 1.8
+ **/
 typedef struct {
     int        num_bytes;
     int        num_glyphs;
@@ -1417,8 +1434,6 @@ cairo_user_font_face_create (void);
  * Returns: %CAIRO_STATUS_SUCCESS upon success, or
  * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
  *
- * Returns: the status code of the operation
- *
  * Since: 1.8
  **/
 typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t  *scaled_font,
@@ -1476,16 +1491,19 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal
 /**
  * cairo_user_scaled_font_text_to_glyphs_func_t:
  * @scaled_font: the scaled-font being created
- * @utf8: input string of text, encoded in UTF-8
- * @glyphs: output array of glyphs, in font space
- * @num_glyphs: number of output glyphs
+ * @utf8: a string of text encoded in UTF-8
+ * @utf8_len: length of @utf8 in bytes
+ * @glyphs: pointer to array of glyphs to fill, in font space
+ * @num_glyphs: pointer to number of glyphs
+ * @clusters: pointer to array of cluster mapping information to fill, or %NULL
+ * @num_clusters: pointer to number of clusters
+ * @backward: pointer to whether the text to glyphs mapping goes backward
  *
- * XXXXXXXXXXXXX
  * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which
  * is called to convert input text to an array of glyphs.  This is used by the
  * cairo_show_text() operation.
  *
- * Using this callback the user font has full control on glyphs and their
+ * Using this callback the user-font has full control on glyphs and their
  * positions.  That means, it allows for features like ligatures and kerning,
  * as well as complex <firstterm>shaping</firstterm> required for scripts like
  * Arabic and Indic.
@@ -1496,12 +1514,39 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal
  * origin.  Cairo will free the glyph array when done with it, no matter what
  * the return value of the callback is.
  *
+ * If @glyphs initially points to a non-%NULL value, that array can be used
+ * as a glyph buffer, and @num_glyphs points to the number of glyph
+ * entries available there.  If the provided glyph array is too short for
+ * the conversion (or for convenience), a new glyph array may be allocated
+ * using cairo_glyph_allocate() and placed in @glyphs.  Upon return,
+ * @num_glyphs should contain the number of generated glyphs.
+ * If the value @glyphs points at has changed after the call, cairo will
+ * free the allocated glyph array using cairo_glyph_free().
+ *
+ * If @clusters is not %NULL, @num_clusters and @backward are also non-%NULL,
+ * and cluster mapping should be computed.
+ * The semantics of how cluster array allocation works is similar to the glyph
+ * array.  That is,
+ * if @clusters initially points to a non-%NULL value, that array may be used
+ * as a cluster buffer, and @num_clusters points to the number of cluster
+ * entries available there.  If the provided cluster array is too short for
+ * the conversion (or for convenience), a new cluster array may be allocated
+ * using cairo_text_cluster_allocate() and placed in @clusters.  Upon return,
+ * @num_clusters should contain the number of generated clusters.
+ * If the value @clusters points at has changed after the call, cairo will
+ * free the allocated cluster array using cairo_text_cluster_free().
+ *
  * The callback is optional.  If not set, or if @num_glyphs is negative upon
- * the callback returning (which by default is), the unicode_to_glyph callback
+ * the callback returning, the unicode_to_glyph callback
  * is tried.  See #cairo_user_scaled_font_unicode_to_glyph_func_t.
  *
- * Note: The signature and details of this callback is expected to change
- * before cairo 1.8.0 is released.
+ * Note: While cairo does not impose any limitation on glyph indices,
+ * some applications may assume that a glyph index fits in a 16-bit
+ * unsigned integer.  As such, it is advised that user-fonts keep their
+ * glyphs in the 0 to 65535 range.  Furthermore, some applications may
+ * assume that glyph 0 is a special glyph-not-found glyph.  User-fonts
+ * are advised to use glyph 0 for such purposes and do not use that
+ * glyph value for other purposes.
  *
  * Returns: %CAIRO_STATUS_SUCCESS upon success, or
  * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
@@ -1540,6 +1585,14 @@ typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_sc
  * set or fails to return glyphs.  If this callback is not set, an identity
  * mapping from Unicode code-points to glyph indices is assumed.
  *
+ * Note: While cairo does not impose any limitation on glyph indices,
+ * some applications may assume that a glyph index fits in a 16-bit
+ * unsigned integer.  As such, it is advised that user-fonts keep their
+ * glyphs in the 0 to 65535 range.  Furthermore, some applications may
+ * assume that glyph 0 is a special glyph-not-found glyph.  User-fonts
+ * are advised to use glyph 0 for such purposes and do not use that
+ * glyph value for other purposes.
+ *
  * Returns: %CAIRO_STATUS_SUCCESS upon success, or
  * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error.
  *
diff --git a/src/cairoint.h b/src/cairoint.h
index 2c37035..e333555 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1349,6 +1349,16 @@ _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices);
 cairo_private unsigned char *
 _cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out);
 
+/* cairo-misc.c */
+cairo_private cairo_status_t
+_cairo_validate_text_clusters (const char		   *utf8,
+			       int			    utf8_len,
+			       const cairo_glyph_t	   *glyphs,
+			       int			    num_glyphs,
+			       const cairo_text_cluster_t  *clusters,
+			       int			    num_clusters,
+			       cairo_bool_t		    backward);
+
 /* cairo-path-fixed.c */
 cairo_private void
 _cairo_path_fixed_init (cairo_path_fixed_t *path);
@@ -2370,7 +2380,11 @@ slim_hidden_proto (cairo_font_options_set_subpixel_order);
 slim_hidden_proto (cairo_font_options_status);
 slim_hidden_proto (cairo_get_current_point);
 slim_hidden_proto (cairo_get_matrix);
+slim_hidden_proto (cairo_get_target);
 slim_hidden_proto (cairo_get_tolerance);
+slim_hidden_proto (cairo_has_show_text_glyphs);
+slim_hidden_proto (cairo_glyph_allocate);
+slim_hidden_proto (cairo_glyph_free);
 slim_hidden_proto (cairo_image_surface_create);
 slim_hidden_proto (cairo_image_surface_create_for_data);
 slim_hidden_proto (cairo_image_surface_get_data);
@@ -2419,6 +2433,7 @@ slim_hidden_proto (cairo_scaled_font_get_ctm);
 slim_hidden_proto (cairo_scaled_font_get_font_face);
 slim_hidden_proto (cairo_scaled_font_get_font_matrix);
 slim_hidden_proto (cairo_scaled_font_get_font_options);
+slim_hidden_proto (cairo_scaled_font_text_to_glyphs);
 slim_hidden_proto (cairo_scaled_font_glyph_extents);
 slim_hidden_proto_no_warn (cairo_scaled_font_reference);
 slim_hidden_proto (cairo_scaled_font_status);
@@ -2446,6 +2461,8 @@ slim_hidden_proto (cairo_surface_set_fallback_resolution);
 slim_hidden_proto (cairo_surface_copy_page);
 slim_hidden_proto (cairo_surface_show_page);
 slim_hidden_proto (cairo_surface_status);
+slim_hidden_proto (cairo_text_cluster_allocate);
+slim_hidden_proto (cairo_text_cluster_free);
 slim_hidden_proto (cairo_toy_font_face_create);
 slim_hidden_proto (cairo_version_string);
 
commit 08e8a42ea149deea760b94b62d9690393ce7cfcd
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 02:22:26 2008 -0400

    [test/user-font] Fix comments

diff --git a/test/user-font.c b/test/user-font.c
index ef81978..b0b5fce 100644
--- a/test/user-font.c
+++ b/test/user-font.c
@@ -239,13 +239,13 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_line_width (cr, 2);
     cairo_stroke (cr);
 
-    /* text in gray */
+    /* text in black */
     cairo_set_source_rgb (cr, 0, 0, 0);
     cairo_move_to (cr, BORDER, BORDER + font_extents.ascent);
     cairo_show_text (cr, text);
 
 
-    /* filled version of text in light blue */
+    /* filled version of text in blue */
     cairo_set_source_rgb (cr, 0, 0, 1);
     cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
     cairo_text_path (cr, text);
commit 38c5f0d49b2ce1a6146cbea5ec3376a52cac8e68
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 02:18:25 2008 -0400

    [scaled-font-subsets] Fix UTF-8 mapping
    
    Prevously all show_text_glyphs() clusters were using ActualText.  This
    fixes that.
    
    I have a feeling that the following scenario is broken still though:
    
      - show_text_glyphs maps glyph 1 to some utf8 text different from
        what index_to_ucs4 will give for glyph 1.  This will assign the
        utf8 text to glyph 1's ToUnicode.
    
      - show_glyphs shows glyph 1.  Since cluster has no utf8 text, we
        won't use ActualText and fall back to ToUnicode.  But the ToUnicode
        value assigned to glyph 1 is non-standard now.  We should use
        ActualText.
    
    I have not verified this hypothesis though.

diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 103e117..2740d63 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -460,9 +460,10 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t	*sub_font,
 	if (sub_font_glyph == NULL)
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-	_cairo_sub_font_glyph_lookup_unicode (sub_font_glyph,
-					      sub_font->scaled_font,
-					      scaled_font_glyph_index);
+	if (utf8_len < 0)
+	    _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph,
+						  sub_font->scaled_font,
+						  scaled_font_glyph_index);
 
 	status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
 	if (status) {
@@ -491,7 +492,7 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t	*sub_font,
     subset_glyph->is_composite = sub_font->is_composite;
     subset_glyph->x_advance = sub_font_glyph->x_advance;
     subset_glyph->y_advance = sub_font_glyph->y_advance;
-    subset_glyph->utf8_is_mapped = FALSE;
+    subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len);
 
     return CAIRO_STATUS_SUCCESS;
 }
commit bc4d363e9238da0908ce01d0c08c3f344bd9d34f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 01:27:41 2008 -0400

    [pdf-operators] Add note about clusters with no glyphs

diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index 4f39318..d3eddc8 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -1287,6 +1287,8 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t      *pdf_operators,
     _cairo_pdf_operators_flush_glyphs (pdf_operators);
     status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len);
     cur_glyph = glyphs;
+    /* XXX
+     * If no glyphs, we should put *something* here for the text to be selectable. */
     for (i = 0; i < num_glyphs; i++) {
 	status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
 						       scaled_font,
commit 1bc404e3460a32ba94b420725ecff25bda7a0c35
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 01:27:12 2008 -0400

    Make utf8 handling in font subsets more consistent

diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index d51363a..4f39318 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -1266,7 +1266,7 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t      *pdf_operators,
 						       scaled_font,
 						       glyphs->index,
 						       utf8,
-						       utf8_len < 0 ? 0 : utf8_len,
+						       utf8_len,
 						       &subset_glyph);
 	if (status)
 	    return status;
@@ -1291,7 +1291,7 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t      *pdf_operators,
 	status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
 						       scaled_font,
 						       cur_glyph->index,
-						       NULL, 0,
+						       NULL, -1,
 						       &subset_glyph);
 	if (status)
 	    return status;
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 66200b2..103e117 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -280,7 +280,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t	*parent,
     sub_font->next = NULL;
 
     /* Reserve first glyph in subset for the .notdef glyph */
-    status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, 0, &subset_glyph);
+    status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph);
     if (status) {
 	_cairo_hash_table_destroy (sub_font->sub_font_glyphs);
 	free (sub_font);
@@ -355,6 +355,9 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
 				      const char 	     *utf8,
 				      int 		      utf8_len)
 {
+    if (utf8_len < 0)
+	return FALSE;
+
     if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
 	utf8_len--;
 
@@ -436,7 +439,7 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t	*sub_font,
 	    sub_font->num_glyphs_in_current_subset = 0;
 
 	    /* Reserve first glyph in subset for the .notdef glyph */
-	    status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, 0, &tmp_subset_glyph);
+	    status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph);
 	    if (status)
 		return status;
 	}
commit 849159ddd15b21b4feee05aebe82b3acfd94ba38
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 00:54:54 2008 -0400

    [truetype] Fix gcc warning about possibly-infinite-loops

diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c
index 53d7232..892ce41 100644
--- a/src/cairo-truetype-subset.c
+++ b/src/cairo-truetype-subset.c
@@ -1245,12 +1245,13 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font,
 	    uint16_t g_id_be = cpu_to_be16 (index);
 	    int j;
 
-	    for (j = 0; j < range_size; j++) {
-		if (glyph_ids[j] == g_id_be) {
-		    *ucs4 = be16_to_cpu (start_code[i]) + j;
-		    goto found;
+	    if (range_size > 0)
+		for (j = 0; j < range_size; j++) {
+		    if (glyph_ids[j] == g_id_be) {
+			*ucs4 = be16_to_cpu (start_code[i]) + j;
+			goto found;
+		    }
 		}
-	    }
 	}
     }
 
commit 178789c37a2a6edaba14a453817b3f9338eab1bd
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Aug 8 00:52:47 2008 -0400

    [cairo-scaled-font-subsets] Make utf8 handling more robust

diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 7e0114e..66200b2 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -355,7 +355,8 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
 				      const char 	     *utf8,
 				      int 		      utf8_len)
 {
-    int add_zero_byte = 0;
+    if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
+	utf8_len--;
 
     if (utf8 != NULL && utf8_len != 0) {
 	if (sub_font_glyph->utf8 != NULL) {
@@ -372,12 +373,9 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
 	    }
 	} else {
 	    /* No existing mapping. Use the requested mapping */
-	    if (utf8[utf8_len - 1] != 0)
-		add_zero_byte = 1;
-	    sub_font_glyph->utf8 = malloc (utf8_len + add_zero_byte);
+	    sub_font_glyph->utf8 = malloc (utf8_len + 1);
 	    memcpy (sub_font_glyph->utf8, utf8, utf8_len);
-	    if (add_zero_byte)
-		sub_font_glyph->utf8[utf8_len] = 0;
+	    sub_font_glyph->utf8[utf8_len] = 0;
 	    sub_font_glyph->utf8_len = utf8_len;
 	    return TRUE;
 	}
commit 597bfa922ae28ae4e7aa0ee72b856b5868799d4e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 22:48:38 2008 -0400

    [test/user-font] Document glyph-not-found situation

diff --git a/test/user-font.c b/test/user-font.c
index 406bd3a..ef81978 100644
--- a/test/user-font.c
+++ b/test/user-font.c
@@ -97,8 +97,8 @@ test_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
 	    return CAIRO_STATUS_SUCCESS;
 	}
 
-    /* Fall through and default to undefined glyph. */
-    return CAIRO_STATUS_INVALID_INDEX;
+    /* Not found.  Default to glyph 0 */
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
@@ -114,6 +114,8 @@ test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
     div_t d;
     double x, y;
 
+    /* FIXME: We simply crash on out-of-bound glyph indices */
+
     metrics->x_advance = glyphs[glyph].width / 4.0;
 
     cairo_set_line_width (cr, 0.1);
@@ -160,7 +162,7 @@ get_user_font_face (void)
 	 *     13 14 15
 	 */
 	static const test_scaled_font_glyph_t glyphs [] = {
-	    { '\0', 0, { END_GLYPH } }, /* Poppler has a bug assuming glyph 0 is .notdef */
+	    { '\0', 1, { END_GLYPH } }, /* Poppler has a bug assuming glyph 0 is .notdef */
 	    { ' ',  1, { END_GLYPH } },
 	    { '-',  2, { 7, 8, STROKE, END_GLYPH } },
 	    { '.',  1, { 10, 10, STROKE, END_GLYPH } },
commit d9408041aa220c8a61e520de25bce9671ba4d0a9
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 22:06:15 2008 -0400

    Add cairo_scaled_font_text_to_glyphs()
    
    And update user-font text_to_glyphs() method to match.
    
    Currently disable the win32-font text_to_glyphs(), until that one
    is updated.  Or better yet, remove it and implement ucs4_to_index().
    It's the toy font API afterall.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 3160d07..bb05446 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1445,12 +1445,16 @@ _cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
 }
 
 cairo_status_t
-_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
-			      const char     *utf8,
-			      double	      x,
-			      double	      y,
-			      cairo_glyph_t **glyphs,
-			      int	     *num_glyphs)
+_cairo_gstate_text_to_glyphs (cairo_gstate_t	    *gstate,
+			      double		     x,
+			      double		     y,
+			      const char	    *utf8,
+			      int		     utf8_len,
+			      cairo_glyph_t	   **glyphs,
+			      int		    *num_glyphs,
+			      cairo_text_cluster_t **clusters,
+			      int		    *num_clusters,
+			      cairo_bool_t	    *backward)
 {
     cairo_status_t status;
 
@@ -1458,8 +1462,11 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
     if (status)
 	return status;
 
-    return _cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
-					      utf8, glyphs, num_glyphs);
+    return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
+					     utf8, utf8_len,
+					     glyphs, num_glyphs,
+					     clusters, num_clusters,
+					     backward);
 }
 
 cairo_status_t
@@ -1536,7 +1543,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
     if (num_glyphs <= ARRAY_LENGTH (stack_transformed_glyphs)) {
 	transformed_glyphs = stack_transformed_glyphs;
     } else {
-	transformed_glyphs = _cairo_malloc_ab (num_glyphs, sizeof(cairo_glyph_t));
+	transformed_glyphs = cairo_glyph_allocate (num_glyphs);
 	if (transformed_glyphs == NULL)
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
@@ -1551,6 +1558,10 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
     if (status)
 	goto CLEANUP_GLYPHS;
 
+    /* Just in case */
+    if (!clusters)
+	num_clusters = 0;
+
     /* For really huge font sizes, we can just do path;fill instead of
      * show_glyphs, as show_glyphs would put excess pressure on the cache,
      * and moreover, not all components below us correctly handle huge font
@@ -1598,7 +1609,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 
 CLEANUP_GLYPHS:
     if (transformed_glyphs != stack_transformed_glyphs)
-      free (transformed_glyphs);
+      cairo_glyph_free (transformed_glyphs);
 
     return status;
 }
@@ -1620,7 +1631,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
     if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs))
       transformed_glyphs = stack_transformed_glyphs;
     else
-      transformed_glyphs = _cairo_malloc_ab (num_glyphs, sizeof(cairo_glyph_t));
+      transformed_glyphs = cairo_glyph_allocate (num_glyphs);
     if (transformed_glyphs == NULL)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
@@ -1634,7 +1645,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
     CAIRO_MUTEX_UNLOCK (gstate->scaled_font->mutex);
 
     if (transformed_glyphs != stack_transformed_glyphs)
-      free (transformed_glyphs);
+      cairo_glyph_free (transformed_glyphs);
 
     return status;
 }
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 3fe3332..fa54231 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1109,7 +1109,7 @@ cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
 				cairo_text_extents_t  *extents)
 {
     cairo_status_t status;
-    cairo_glyph_t *glyphs;
+    cairo_glyph_t *glyphs = NULL;
     int num_glyphs;
 
     if (scaled_font->status)
@@ -1118,7 +1118,11 @@ cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
     if (utf8 == NULL)
 	goto ZERO_EXTENTS;
 
-    status = _cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0., utf8, &glyphs, &num_glyphs);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
+					       utf8, -1,
+					       &glyphs, &num_glyphs,
+					       NULL, NULL,
+					       NULL);
     if (status)
 	goto ZERO_EXTENTS;
 
@@ -1265,70 +1269,148 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t   *scaled_font,
 slim_hidden_def (cairo_scaled_font_glyph_extents);
 
 cairo_status_t
-_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
-				   double		x,
-				   double		y,
-				   const char          *utf8,
-				   cairo_glyph_t      **glyphs,
-				   int 		       *num_glyphs)
+cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
+				  double		 x,
+				  double		 y,
+				  const char	        *utf8,
+				  int		         utf8_len,
+				  cairo_glyph_t	       **glyphs,
+				  int		        *num_glyphs,
+				  cairo_text_cluster_t **clusters,
+				  int		        *num_clusters,
+				  cairo_bool_t	        *backward)
 {
     int i;
-    uint32_t *ucs4 = NULL;
+    int num_chars = 0;
+    const char *p;
     cairo_status_t status;
-    cairo_scaled_glyph_t *scaled_glyph;
-
-    *num_glyphs = 0;
-    *glyphs = NULL;
+    cairo_glyph_t *orig_glyphs;
+    cairo_text_cluster_t *orig_clusters;
 
     status = scaled_font->status;
     if (status)
 	return status;
 
-    if (utf8[0] == '\0')
-	return CAIRO_STATUS_SUCCESS;
+    /* A slew of sanity checks */
+
+    /* glyphs and num_glyphs can't be NULL */
+    if (glyphs     == NULL ||
+	num_glyphs == NULL) {
+	status = CAIRO_STATUS_NULL_POINTER;
+	goto BAIL;
+    }
+
+    /* No NULLs for non-NULLs! */
+    if ((utf8_len && utf8         == NULL) ||
+	(clusters && num_clusters == NULL) ||
+	(clusters && backward     == NULL)) {
+	status = CAIRO_STATUS_NULL_POINTER;
+	goto BAIL;
+    }
+
+    /* A -1 for utf8_len means NUL-terminated */
+    if (utf8_len == -1)
+	utf8_len = strlen (utf8);
+
+    /* A NULL *glyphs means no prealloced glyphs array */
+    if (glyphs && *glyphs == NULL)
+	*num_glyphs = 0;
+
+    /* A NULL *clusters means no prealloced clusters array */
+    if (clusters && *clusters == NULL)
+	*num_clusters = 0;
+
+    if (!clusters && num_clusters) {
+	num_clusters = NULL;
+    }
+
+    if (backward) {
+	*backward = FALSE;
+    }
+
+    if (!clusters && backward) {
+	backward = NULL;
+    }
+
+    /* Apart from that, no negatives */
+    if (utf8_len < 0 ||
+	*num_glyphs < 0 ||
+	(num_clusters && *num_clusters < 0)) {
+	status = CAIRO_STATUS_NEGATIVE_COUNT;
+	goto BAIL;
+    }
+
+    if (utf8_len == 0) {
+	status = CAIRO_STATUS_SUCCESS;
+	goto BAIL;
+    }
+
+    /* validate input so backend does not have to */
+    status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
+    if (status)
+	goto BAIL;
 
     CAIRO_MUTEX_LOCK (scaled_font->mutex);
     _cairo_scaled_font_freeze_cache (scaled_font);
 
-    if (scaled_font->backend->text_to_glyphs) {
+    orig_glyphs = *glyphs;
+    orig_clusters = clusters ? *clusters : NULL;
 
-	/* validate input so backend does not have to */
-	status = _cairo_utf8_to_ucs4 (utf8, -1, NULL, NULL);
-	if (status)
-	    goto DONE;
+    if (scaled_font->backend->text_to_glyphs) {
 
-	status = scaled_font->backend->text_to_glyphs (scaled_font,
-						       x, y, utf8,
-						       glyphs, num_glyphs);
+	status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
+						       utf8, utf8_len,
+						       glyphs, num_glyphs,
+						       clusters, num_clusters,
+						       backward);
 
         if (status != CAIRO_INT_STATUS_UNSUPPORTED)
             goto DONE;
     }
 
-    status = _cairo_utf8_to_ucs4 (utf8, -1, &ucs4, num_glyphs);
-    if (status)
-	goto DONE;
-
-    *glyphs = (cairo_glyph_t *) _cairo_malloc_ab ((*num_glyphs), sizeof (cairo_glyph_t));
+    if (*num_glyphs < num_chars) {
+	*glyphs = cairo_glyph_allocate (num_chars);
+	if (*glyphs == NULL) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto DONE;
+	}
+    }
+    *num_glyphs = num_chars;
 
-    if (*glyphs == NULL) {
-	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	goto DONE;
+    if (clusters) {
+	if (*num_clusters < num_chars) {
+	    *clusters = cairo_text_cluster_allocate (num_chars);
+	    if (*clusters == NULL) {
+		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		goto DONE;
+	    }
+	}
+	*num_clusters = num_chars;
     }
 
-    for (i = 0; i < *num_glyphs; i++) {
-        (*glyphs)[i].index = (*scaled_font->backend->
-			      ucs4_to_index) (scaled_font, ucs4[i]);
+    p = utf8;
+    for (i = 0; i < num_chars; i++) {
+	int num_bytes;
+	uint32_t unicode;
+	cairo_scaled_glyph_t *scaled_glyph;
+
+	num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
+	p += num_bytes;
+
+        (*glyphs)[i].index = (*scaled_font->backend->ucs4_to_index) (scaled_font, unicode);
 	(*glyphs)[i].x = x;
 	(*glyphs)[i].y = y;
 
+	if (clusters) {
+	    (*clusters)[i].num_bytes  = num_bytes;
+	    (*clusters)[i].num_glyphs = 1;
+	}
+
 	status = _cairo_scaled_glyph_lookup (scaled_font,
 					     (*glyphs)[i].index,
 					     CAIRO_SCALED_GLYPH_INFO_METRICS,
 					     &scaled_glyph);
 	if (status) {
-	    free (*glyphs);
-	    *glyphs = NULL;
 	    goto DONE;
 	}
 
@@ -1336,14 +1418,37 @@ _cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
         y += scaled_glyph->metrics.y_advance;
     }
 
- DONE:
+ DONE: /* error that should be logged on scaled_font happened */
     _cairo_scaled_font_thaw_cache (scaled_font);
     CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
 
-    if (ucs4)
-	free (ucs4);
+    if (status) {
+	*num_glyphs = 0;
+	if (*glyphs != orig_glyphs) {
+	    cairo_glyph_free (*glyphs);
+	    *glyphs = orig_glyphs;
+	}
+
+	if (clusters) {
+	    *num_clusters = 0;
+	    if (*clusters != orig_clusters) {
+		cairo_text_cluster_free (*clusters);
+		*clusters = orig_clusters;
+	    }
+	}
+    }
 
     return _cairo_scaled_font_set_error (scaled_font, status);
+
+ BAIL: /* error with input arguments */
+
+    if (num_glyphs)
+	*num_glyphs = 0;
+
+    if (num_clusters)
+	*num_clusters = 0;
+
+    return status;
 }
 
 /*
diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index bcc3cfa..1949269 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -265,12 +265,16 @@ _cairo_user_ucs4_to_index (void	    *abstract_font,
 }
 
 static cairo_int_status_t
-_cairo_user_text_to_glyphs (void           *abstract_font,
-			    double          x,
-			    double          y,
-			    const char     *utf8,
-			    cairo_glyph_t **glyphs,
-			    int	           *num_glyphs)
+_cairo_user_text_to_glyphs (void		 *abstract_font,
+			    double		  x,
+			    double		  y,
+			    const char		 *utf8,
+			    int			  utf8_len,
+			    cairo_glyph_t	**glyphs,
+			    int			  *num_glyphs,
+			    cairo_text_cluster_t **clusters,
+			    int			  *num_clusters,
+			    cairo_bool_t	  *backward)
 {
     cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -280,25 +284,21 @@ _cairo_user_text_to_glyphs (void           *abstract_font,
 
     if (face->scaled_font_methods.text_to_glyphs) {
 	int i;
+	int orig_num_glyphs = *num_glyphs;
 
-	*glyphs = NULL;
-	*num_glyphs = -1;
-
-	/* XXX currently user allocs glyphs array but cairo frees it */
 	status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base,
-							   utf8, glyphs, num_glyphs);
+							   utf8, utf8_len,
+							   glyphs, num_glyphs,
+							   clusters, num_clusters,
+							   backward);
 
-	if (status != CAIRO_STATUS_SUCCESS) {
-	    status = _cairo_scaled_font_set_error (&scaled_font->base, status);
-	    if (*glyphs) {
-		free (*glyphs);
-		*glyphs = NULL;
-	    }
+	if (status != CAIRO_STATUS_SUCCESS)
 	    return status;
-	}
 
-	if (*num_glyphs < 0)
+	if (*num_glyphs < 0) {
+	    *num_glyphs = orig_num_glyphs;
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
 
 	/* Convert from font space to user space and add x,y */
 	for (i = 0; i < *num_glyphs; i++) {
diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c
index 5c68f07..ef2d2f0 100644
--- a/src/cairo-win32-font.c
+++ b/src/cairo-win32-font.c
@@ -1780,7 +1780,7 @@ const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = {
     _cairo_win32_scaled_font_create_toy,
     _cairo_win32_scaled_font_fini,
     _cairo_win32_scaled_font_glyph_init,
-    _cairo_win32_scaled_font_text_to_glyphs,
+    /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */
     NULL,			/* ucs4_to_index */
     _cairo_win32_scaled_font_show_glyphs,
     _cairo_win32_scaled_font_load_truetype_table,
diff --git a/src/cairo.c b/src/cairo.c
index e96a3ee..e42fd70 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -3013,16 +3013,18 @@ cairo_text_extents (cairo_t              *cr,
 
     cairo_get_current_point (cr, &x, &y);
 
-    status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
+    status = _cairo_gstate_text_to_glyphs (cr->gstate,
 					   x, y,
-					   &glyphs, &num_glyphs);
+					   utf8, strlen (utf8),
+					   &glyphs, &num_glyphs,
+					   NULL, NULL,
+					   NULL);
 
     if (status == CAIRO_STATUS_SUCCESS)
 	status = _cairo_gstate_glyph_extents (cr->gstate,
 		                              glyphs, num_glyphs,
 					      extents);
-    if (glyphs)
-	free (glyphs);
+    cairo_glyph_free (glyphs);
 
     if (status)
 	_cairo_set_error (cr, status);
@@ -3116,7 +3118,9 @@ cairo_show_text (cairo_t *cr, const char *utf8)
     cairo_text_extents_t extents;
     cairo_status_t status;
     cairo_glyph_t *glyphs = NULL, *last_glyph;
-    int num_glyphs;
+    cairo_text_cluster_t *clusters = NULL;
+    int utf8_len, num_glyphs, num_clusters;
+    cairo_bool_t backward;
     double x, y;
 
     if (cr->status)
@@ -3127,9 +3131,14 @@ cairo_show_text (cairo_t *cr, const char *utf8)
 
     cairo_get_current_point (cr, &x, &y);
 
-    status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
+    utf8_len = strlen (utf8);
+
+    status = _cairo_gstate_text_to_glyphs (cr->gstate,
 					   x, y,
-					   &glyphs, &num_glyphs);
+					   utf8, utf8_len,
+					   &glyphs, &num_glyphs,
+					   cairo_has_show_text_glyphs (cr) ? &clusters : NULL, &num_clusters,
+					   &backward);
     if (status)
 	goto BAIL;
 
@@ -3137,10 +3146,10 @@ cairo_show_text (cairo_t *cr, const char *utf8)
 	return;
 
     status = _cairo_gstate_show_text_glyphs (cr->gstate,
-					     NULL, 0,
+					     utf8, utf8_len,
 					     glyphs, num_glyphs,
-					     NULL, 0,
-					     FALSE);
+					     clusters, num_clusters,
+					     backward);
     if (status)
 	goto BAIL;
 
@@ -3156,8 +3165,8 @@ cairo_show_text (cairo_t *cr, const char *utf8)
     cairo_move_to (cr, x, y);
 
  BAIL:
-    if (glyphs)
-	free (glyphs);
+    cairo_glyph_free (glyphs);
+    cairo_text_cluster_free (clusters);
 
     if (status)
 	_cairo_set_error (cr, status);
@@ -3226,6 +3235,14 @@ cairo_show_text_glyphs (cairo_t			   *cr,
 
     /* A slew of sanity checks */
 
+    /* No NULLs for non-zeros */
+    if ((num_glyphs   && glyphs   == NULL) ||
+	(utf8_len     && utf8     == NULL) ||
+	(num_clusters && clusters == NULL)) {
+	_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+	return;
+    }
+
     /* A -1 for utf8_len means NUL-terminated */
     if (utf8_len == -1)
 	utf8_len = strlen (utf8);
@@ -3236,14 +3253,6 @@ cairo_show_text_glyphs (cairo_t			   *cr,
 	return;
     }
 
-    /* And no NULLs for non-zeros */
-    if ((num_glyphs   && glyphs   == NULL) ||
-	(utf8_len     && utf8     == NULL) ||
-	(num_clusters && clusters == NULL)) {
-	_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
-	return;
-    }
-
     /* Make sure clusters cover the entire glyphs and utf8 arrays,
      * and that cluster boundaries are UTF-8 boundaries. */
     {
@@ -3343,9 +3352,12 @@ cairo_text_path  (cairo_t *cr, const char *utf8)
 
     cairo_get_current_point (cr, &x, &y);
 
-    status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
+    status = _cairo_gstate_text_to_glyphs (cr->gstate,
 					   x, y,
-					   &glyphs, &num_glyphs);
+					   utf8, strlen (utf8),
+					   &glyphs, &num_glyphs,
+					   NULL, NULL,
+					   NULL);
 
     if (status)
 	goto BAIL;
@@ -3373,8 +3385,7 @@ cairo_text_path  (cairo_t *cr, const char *utf8)
     cairo_move_to (cr, x, y);
 
  BAIL:
-    if (glyphs)
-	free (glyphs);
+    cairo_glyph_free (glyphs);
 
     if (status)
 	_cairo_set_error (cr, status);
diff --git a/src/cairo.h b/src/cairo.h
index 02d346a..ee933ab 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1330,6 +1330,18 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t   *scaled_font,
 				 int                   num_glyphs,
 				 cairo_text_extents_t  *extents);
 
+cairo_public cairo_status_t
+cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
+				  double		 x,
+				  double		 y,
+				  const char	        *utf8,
+				  int		         utf8_len,
+				  cairo_glyph_t	       **glyphs,
+				  int		        *num_glyphs,
+				  cairo_text_cluster_t **clusters,
+				  int		        *num_clusters,
+				  cairo_bool_t	        *backward);
+
 cairo_public cairo_font_face_t *
 cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font);
 
@@ -1468,6 +1480,7 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal
  * @glyphs: output array of glyphs, in font space
  * @num_glyphs: number of output glyphs
  *
+ * XXXXXXXXXXXXX
  * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which
  * is called to convert input text to an array of glyphs.  This is used by the
  * cairo_show_text() operation.
@@ -1496,9 +1509,13 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal
  * Since: 1.8
  **/
 typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t   *scaled_font,
-									const char            *utf8,
-									cairo_glyph_t        **glyphs,
-									int                   *num_glyphs);
+									const char	      *utf8,
+									int		       utf8_len,
+									cairo_glyph_t	     **glyphs,
+									int		      *num_glyphs,
+									cairo_text_cluster_t **clusters,
+									int		      *num_clusters,
+									cairo_bool_t	      *backward);
 
 /**
  * cairo_user_scaled_font_unicode_to_glyph_func_t:
diff --git a/src/cairoint.h b/src/cairoint.h
index 8e27eb1..2c37035 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -408,12 +408,16 @@ struct _cairo_scaled_font_backend {
      * then just converting characters one by one.
      */
     cairo_warn cairo_int_status_t
-    (*text_to_glyphs) (void                *scaled_font,
-		       double		    x,
-		       double		    y,
-		       const char          *utf8,
-		       cairo_glyph_t      **glyphs,
-		       int 		   *num_glyphs);
+    (*text_to_glyphs) (void                  *scaled_font,
+		       double		      x,
+		       double		      y,
+		       const char	     *utf8,
+		       int		      utf8_len,
+		       cairo_glyph_t	    **glyphs,
+		       int		     *num_glyphs,
+		       cairo_text_cluster_t **clusters,
+		       int		     *num_clusters,
+		       cairo_bool_t	     *backward);
 
     unsigned long
     (*ucs4_to_index)		(void			     *scaled_font,
@@ -1219,12 +1223,16 @@ _cairo_gstate_set_font_face (cairo_gstate_t    *gstate,
 			     cairo_font_face_t *font_face);
 
 cairo_private cairo_status_t
-_cairo_gstate_text_to_glyphs (cairo_gstate_t *font,
-			      const char     *utf8,
-			      double	      x,
-			      double	      y,
-			      cairo_glyph_t **glyphs,
-			      int	     *num_glyphs);
+_cairo_gstate_text_to_glyphs (cairo_gstate_t	    *gstate,
+			      double		     x,
+			      double		     y,
+			      const char	    *utf8,
+			      int		     utf8_len,
+			      cairo_glyph_t	   **glyphs,
+			      int		    *num_glyphs,
+			      cairo_text_cluster_t **clusters,
+			      int		    *num_clusters,
+			      cairo_bool_t	    *backward);
 
 cairo_private cairo_status_t
 _cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
@@ -1529,14 +1537,6 @@ _cairo_scaled_font_font_extents (cairo_scaled_font_t  *scaled_font,
 				 cairo_font_extents_t *extents);
 
 cairo_private cairo_status_t
-_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t	*scaled_font,
-				   double		x,
-				   double		y,
-				   const char           *utf8,
-				   cairo_glyph_t       **glyphs,
-				   int 		        *num_glyphs);
-
-cairo_private cairo_status_t
 _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
 					 const cairo_glyph_t	 *glyphs,
 					 int                      num_glyphs,
diff --git a/test/user-font-proxy.c b/test/user-font-proxy.c
index 8684729..770b0c4 100644
--- a/test/user-font-proxy.c
+++ b/test/user-font-proxy.c
@@ -51,7 +51,7 @@ cairo_test_t test = {
     draw
 };
 
-static cairo_user_data_key_t fallback_font_face_key;
+static cairo_user_data_key_t fallback_font_key;
 
 static cairo_status_t
 test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
@@ -60,7 +60,12 @@ test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
 {
     cairo_set_font_face (cr,
 			 cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
-							&fallback_font_face_key));
+							&fallback_font_key));
+
+    cairo_scaled_font_set_user_data (scaled_font,
+				     &fallback_font_key,
+				     cairo_scaled_font_reference (cairo_get_scaled_font (cr)),
+				     (cairo_destroy_func_t) cairo_scaled_font_destroy);
 
     cairo_font_extents (cr, extents);
 
@@ -73,21 +78,44 @@ test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
 			       cairo_t              *cr,
 			       cairo_text_extents_t *extents)
 {
-    char text[2] = "\0";
+    cairo_glyph_t cairo_glyph;
 
-    /* XXX only works for ASCII.  need ucs4_to_utf8 :( */
-    text[0] = glyph;
+    cairo_glyph.index = glyph;
+    cairo_glyph.x = 0;
+    cairo_glyph.y = 0;
 
     cairo_set_font_face (cr,
 			 cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
-							&fallback_font_face_key));
+							&fallback_font_key));
 
-    cairo_show_text (cr, text);
-    cairo_text_extents (cr, text, extents);
+    cairo_show_glyphs (cr, &cairo_glyph, 1);
+    cairo_glyph_extents (cr, &cairo_glyph, 1, extents);
 
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+test_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
+				 const char	       *utf8,
+				 int		        utf8_len,
+				 cairo_glyph_t	      **glyphs,
+				 int		       *num_glyphs,
+				 cairo_text_cluster_t **clusters,
+				 int		       *num_clusters,
+				 cairo_bool_t	       *backward)
+{
+  cairo_scaled_font_t *fallback_scaled_font;
+
+  fallback_scaled_font = cairo_scaled_font_get_user_data (scaled_font,
+							  &fallback_font_key);
+
+  return cairo_scaled_font_text_to_glyphs (fallback_scaled_font, 0, 0,
+					   utf8, utf8_len,
+					   glyphs, num_glyphs,
+					   clusters, num_clusters,
+					   backward);
+}
+
 static cairo_font_face_t *user_font_face = NULL;
 
 static cairo_font_face_t *
@@ -99,6 +127,7 @@ get_user_font_face (void)
 	user_font_face = cairo_user_font_face_create ();
 	cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
 	cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
+	cairo_user_font_face_set_text_to_glyphs_func   (user_font_face, test_scaled_font_text_to_glyphs);
 
 	/* This also happens to be default font face on cairo_t, so does
 	 * not make much sense here.  For demonstration only.
@@ -108,9 +137,9 @@ get_user_font_face (void)
 							 CAIRO_FONT_WEIGHT_NORMAL);
 
 	cairo_font_face_set_user_data (user_font_face,
-				       &fallback_font_face_key,
+				       &fallback_font_key,
 				       fallback_font_face,
-				       cairo_font_face_destroy);
+				       (cairo_destroy_func_t) cairo_font_face_destroy);
     }
 
     return user_font_face;
commit b8fc845094e07ad2520a2c10f27c532bd3273720
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 21:46:36 2008 -0400

    [cairo-scaled-font-subsets] Fix bug with utf8 handling

diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 94d1cea..7e0114e 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -372,7 +372,7 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
 	    }
 	} else {
 	    /* No existing mapping. Use the requested mapping */
-	    if (sub_font_glyph->utf8[utf8_len - 1] != 0)
+	    if (utf8[utf8_len - 1] != 0)
 		add_zero_byte = 1;
 	    sub_font_glyph->utf8 = malloc (utf8_len + add_zero_byte);
 	    memcpy (sub_font_glyph->utf8, utf8, utf8_len);
commit 6b3f6dc77abbc48d741b92cd62f93da68f00b9a2
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 20:45:54 2008 -0400

    [unicode] Add _cairo_utf8_get_char_validated()

diff --git a/src/cairo-unicode.c b/src/cairo-unicode.c
index 973ef57..39d37b8 100644
--- a/src/cairo-unicode.c
+++ b/src/cairo-unicode.c
@@ -196,6 +196,40 @@ _utf8_get_char_extended (const unsigned char *p,
 }
 
 /**
+ * _cairo_utf8_get_char_validated:
+ * @p: a UTF-8 string
+ * @unicode: location to store one Unicode character
+ *
+ * Decodes the first character of a valid UTF-8 string, and returns
+ * the number of bytes consumed.
+ *
+ * Note that the string should be valid.  Do not use this without
+ * validating the string first.
+ *
+ * Returns: the number of bytes forming the character returned.
+ **/
+int
+_cairo_utf8_get_char_validated (const char *p,
+				uint32_t   *unicode)
+{
+    int i, mask = 0, len;
+    uint32_t result;
+    unsigned char c = (unsigned char) *p;
+
+    UTF8_COMPUTE (c, mask, len);
+    if (len == -1) {
+	if (unicode)
+	    *unicode = (uint32_t)-1;
+	return 1;
+    }
+    UTF8_GET (result, p, i, mask, len);
+
+    if (unicode)
+	*unicode = result;
+    return len;
+}
+
+/**
  * _cairo_utf8_to_utf32:
  * @str: an UTF-8 string
  * @len: length of @str in bytes, or -1 if it is nul-terminated.
@@ -266,7 +300,7 @@ _cairo_utf8_to_ucs4 (const char *str,
  * _cairo_ucs4_to_utf8:
  * @unicode: a UCS-4 character
  * @utf8: buffer to write utf8 string into. Must have at least 4 bytes
- * space available.
+ * space available. Or %NULL.
  *
  * Return value: Number of bytes in the utf8 string or 0 if an invalid
  * unicode character
@@ -279,7 +313,8 @@ _cairo_ucs4_to_utf8 (uint32_t  unicode,
     char *p;
 
     if (unicode < 0x80) {
-	*utf8 = unicode;
+	if (utf8)
+	    *utf8 = unicode;
 	return 1;
     } else if (unicode < 0x800) {
 	bytes = 2;
@@ -291,6 +326,9 @@ _cairo_ucs4_to_utf8 (uint32_t  unicode,
 	return 0;
     }
 
+    if (!utf8)
+	return bytes;
+
     p = utf8 + bytes;
     while (p > utf8) {
 	*--p = 0x80 | (unicode & 0x3f);
diff --git a/src/cairoint.h b/src/cairoint.h
index 486a164..8e27eb1 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2316,6 +2316,10 @@ _cairo_gstate_get_antialias (cairo_gstate_t *gstate);
 
 /* cairo-unicode.c */
 
+cairo_private int
+_cairo_utf8_get_char_validated (const char *p,
+				uint32_t   *unicode);
+
 cairo_private cairo_status_t
 _cairo_utf8_to_ucs4 (const char *str,
 		     int	 len,
commit 8c514a40b80e11cc904db3ce9ee353839321044e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 20:44:45 2008 -0400

    Add cairo_glyph/text_cluster_allocate/free
    
    These are needed by the upcoming cairo_scaled_font_text_to_glyphs()

diff --git a/src/cairo-misc.c b/src/cairo-misc.c
index 3432e50..1e74c67 100644
--- a/src/cairo-misc.c
+++ b/src/cairo-misc.c
@@ -41,6 +41,8 @@
 COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < CAIRO_INT_STATUS_UNSUPPORTED);
 COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127);
 
+/* Public stuff */
+
 /**
  * cairo_status_to_string:
  * @status: a cairo status
@@ -122,6 +124,108 @@ cairo_status_to_string (cairo_status_t status)
     return "<unknown error status>";
 }
 
+
+/**
+ * cairo_glyph_allocate:
+ * @num_glyphs: number of glyphs to allocate
+ *
+ * Allocates an array of #cairo_glyph_t's.
+ * This function is only useful in implementations of
+ * #cairo_user_scaled_font_text_to_glyphs_func_t where the user
+ * needs to allocate an array of glyphs that cairo will free.
+ * For all other uses, user can use their own allocation method
+ * for glyphs.
+ *
+ * This function returns %NULL if @num_glyphs is not positive,
+ * or if out of memory.  That means, the %NULL return value
+ * signals out-of-memory only if @num_glyphs was positive.
+ *
+ * Returns: the newly allocated array of glyphs that should be
+ *          freed using cairo_glyph_free()
+ *
+ * Since: 1.8
+ */
+cairo_glyph_t *
+cairo_glyph_allocate (int num_glyphs)
+{
+    if (num_glyphs <= 0)
+	return NULL;
+
+    return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+}
+
+/**
+ * cairo_glyph_free:
+ * @glyphs: array of glyphs to free, or %NULL
+ *
+ * Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate().
+ * This function is only useful to free glyph array returned
+ * by cairo_scaled_font_text_to_glyphs() where cairo returns
+ * an array of glyphs that the user will free.
+ * For all other uses, user can use their own allocation method
+ * for glyphs.
+ *
+ * Since: 1.8
+ */
+void
+cairo_glyph_free (cairo_glyph_t *glyphs)
+{
+    if (glyphs)
+	free (glyphs);
+}
+
+/**
+ * cairo_text_cluster_allocate:
+ * @num_clusters: number of text_clusters to allocate
+ *
+ * Allocates an array of #cairo_text_cluster_t's.
+ * This function is only useful in implementations of
+ * #cairo_user_scaled_font_text_to_glyphs_func_t where the user
+ * needs to allocate an array of text clusters that cairo will free.
+ * For all other uses, user can use their own allocation method
+ * for text clusters.
+ *
+ * This function returns %NULL if @num_clusters is not positive,
+ * or if out of memory.  That means, the %NULL return value
+ * signals out-of-memory only if @num_clusters was positive.
+ *
+ * Returns: the newly allocated array of text clusters that should be
+ *          freed using cairo_text_cluster_free()
+ *
+ * Since: 1.8
+ */
+cairo_text_cluster_t *
+cairo_text_cluster_allocate (int num_clusters)
+{
+    if (num_clusters <= 0)
+	return NULL;
+
+    return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
+}
+
+/**
+ * cairo_text_cluster_free:
+ * @text_clusters: array of text clusters to free, or %NULL
+ *
+ * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate().
+ * This function is only useful to free text cluster array returned
+ * by cairo_scaled_font_text_to_glyphs() where cairo returns
+ * an array of text clusters that the user will free.
+ * For all other uses, user can use their own allocation method
+ * for text clusters.
+ *
+ * Since: 1.8
+ */
+void
+cairo_text_cluster_free (cairo_text_cluster_t *clusters)
+{
+    if (clusters)
+	free (clusters);
+}
+
+
+/* Private stuff */
+
 /**
  * _cairo_operator_bounded_by_mask:
  * @op: a #cairo_operator_t
diff --git a/src/cairo.h b/src/cairo.h
index e26c257..02d346a 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -830,11 +830,23 @@ typedef struct {
     double               y;
 } cairo_glyph_t;
 
+cairo_public cairo_glyph_t *
+cairo_glyph_allocate (int num_glyphs);
+
+cairo_public void
+cairo_glyph_free (cairo_glyph_t *glyphs);
+
 typedef struct {
     int        num_bytes;
     int        num_glyphs;
 } cairo_text_cluster_t;
 
+cairo_public cairo_text_cluster_t *
+cairo_text_cluster_allocate (int num_clusters);
+
+cairo_public void
+cairo_text_cluster_free (cairo_text_cluster_t *clusters);
+
 /**
  * cairo_text_extents_t:
  * @x_bearing: the horizontal distance from the origin to the
commit b01ad0835d25fbee91d037e4484ba652075ffb39
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 15:44:11 2008 -0400

    [user-font] Add a cairo_t argument to cairo_user_scaled_font_init_func_t
    
    The init func does not actually need to draw anything, but having a cairo_t
    similar to that passed to render_glyph is handy for computing font extents.
    This is because cairo makes doing some things really hard (if not impossible)
    without a cairo_t.
    
    The user-font-proxy test case is a great example of how the added cairo_t
    makes life much easier.

diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index cb15ca4..bcc3cfa 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -74,6 +74,28 @@ typedef struct _cairo_user_scaled_font {
 
 /* #cairo_user_scaled_font_t */
 
+static cairo_t *
+_cairo_user_scaled_font_create_meta_context (cairo_user_scaled_font_t *scaled_font)
+{
+    cairo_content_t content;
+    cairo_surface_t *meta_surface;
+    cairo_t *cr;
+
+    content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ?
+						     CAIRO_CONTENT_COLOR_ALPHA :
+						     CAIRO_CONTENT_ALPHA;
+
+    meta_surface = _cairo_meta_surface_create (content, -1, -1);
+    cr = cairo_create (meta_surface);
+    cairo_surface_destroy (meta_surface);
+
+    cairo_set_matrix (cr, &scaled_font->base.scale);
+    cairo_set_font_size (cr, 1.0);
+    cairo_set_font_options (cr, &scaled_font->base.options);
+
+    return cr;
+}
+
 static const cairo_scaled_font_backend_t cairo_user_scaled_font_backend;
 
 static cairo_int_status_t
@@ -89,17 +111,9 @@ _cairo_user_scaled_glyph_init (void			 *abstract_font,
 	cairo_user_font_face_t *face =
 	    (cairo_user_font_face_t *) scaled_font->base.font_face;
 	cairo_text_extents_t extents = scaled_font->default_glyph_extents;
-	cairo_content_t content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ?
-									 CAIRO_CONTENT_COLOR_ALPHA :
-									 CAIRO_CONTENT_ALPHA;
 	cairo_t *cr;
 
-	meta_surface = _cairo_meta_surface_create (content, -1, -1);
-	cr = cairo_create (meta_surface);
-
-	cairo_set_matrix (cr, &scaled_font->base.scale);
-	cairo_set_font_size (cr, 1.0);
-	cairo_set_font_options (cr, &scaled_font->base.options);
+	cr = _cairo_user_scaled_font_create_meta_context (scaled_font);
 
 	if (face->scaled_font_methods.render_glyph)
 	    status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font,
@@ -111,6 +125,8 @@ _cairo_user_scaled_glyph_init (void			 *abstract_font,
 	if (status == CAIRO_STATUS_SUCCESS)
 	    status = cairo_status (cr);
 
+	meta_surface = cairo_surface_reference (cairo_get_target (cr));
+
 	cairo_destroy (cr);
 
 	if (status) {
@@ -377,6 +393,8 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
 
     if (status == CAIRO_STATUS_SUCCESS && font_face->scaled_font_methods.init != NULL) {
 
+	cairo_t *cr;
+
 	/* Lock the scaled_font mutex such that user doesn't accidentally try
          * to use it just yet. */
 	CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex);
@@ -384,9 +402,17 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
 	/* Give away fontmap lock such that user-font can use other fonts */
 	_cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base);
 
+	cr = _cairo_user_scaled_font_create_meta_context (user_scaled_font);
+
 	status = font_face->scaled_font_methods.init (&user_scaled_font->base,
+						      cr,
 						      &font_extents);
 
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = cairo_status (cr);
+
+	cairo_destroy (cr);
+
 	_cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base);
 
 	CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex);
diff --git a/src/cairo.h b/src/cairo.h
index 1be31ab..e26c257 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1365,11 +1365,18 @@ cairo_user_font_face_create (void);
 /**
  * cairo_user_scaled_font_init_func_t:
  * @scaled_font: the scaled-font being created
+ * @cr: a cairo context, in font space
  * @extents: font extents to fill in, in font space
  *
  * #cairo_user_scaled_font_init_func_t is the type of function which is
  * called when a scaled-font needs to be created for a user font-face.
  *
+ * The cairo context @cr is not used by the caller, but is prepared in font
+ * space, similar to what the cairo contexts passed to the render_glyph
+ * method will look like.  The callback can use this context for extents
+ * computation for example.  After the callback is called, @cr is checked
+ * for any error status.
+ *
  * The @extents argument is where the user font sets the font extents for
  * @scaled_font.  It is in font space, which means that for most cases its
  * ascent and descent members should add to 1.0.  @extents is preset to
@@ -1391,6 +1398,7 @@ cairo_user_font_face_create (void);
  * Since: 1.8
  **/
 typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t  *scaled_font,
+							      cairo_t              *cr,
 							      cairo_font_extents_t *extents);
 
 /**
diff --git a/test/user-font-proxy.c b/test/user-font-proxy.c
index c7b29ab..8684729 100644
--- a/test/user-font-proxy.c
+++ b/test/user-font-proxy.c
@@ -55,40 +55,16 @@ static cairo_user_data_key_t fallback_font_face_key;
 
 static cairo_status_t
 test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
+		       cairo_t              *cr,
 		       cairo_font_extents_t *extents)
 {
-  cairo_font_face_t *font_face;
-  cairo_matrix_t font_matrix, ctm;
-  cairo_font_options_t *font_options;
-  cairo_scaled_font_t *fallback_scaled_font;
-
-  font_face = cairo_toy_font_face_create ("",
-					  CAIRO_FONT_SLANT_NORMAL,
-					  CAIRO_FONT_WEIGHT_NORMAL);
-
-  cairo_matrix_init_identity (&font_matrix);
-  cairo_scaled_font_get_scale_matrix (scaled_font, &ctm);
-
-  font_options = cairo_font_options_create ();
-  cairo_scaled_font_get_font_options (scaled_font, font_options);
-
-  fallback_scaled_font = cairo_scaled_font_create (font_face,
-						   &font_matrix,
-						   &ctm,
-						   font_options);
-
-  cairo_font_options_destroy (font_options);
-
-  cairo_scaled_font_extents (fallback_scaled_font, extents);
-
-  cairo_scaled_font_destroy (fallback_scaled_font);
+    cairo_set_font_face (cr,
+			 cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+							&fallback_font_face_key));
 
-  cairo_scaled_font_set_user_data (scaled_font,
-				   &fallback_font_face_key,
-				   font_face,
-				   cairo_font_face_destroy);
+    cairo_font_extents (cr, extents);
 
-  return CAIRO_STATUS_SUCCESS;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
@@ -103,23 +79,38 @@ test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
     text[0] = glyph;
 
     cairo_set_font_face (cr,
-			 cairo_scaled_font_get_user_data (scaled_font,
-							  &fallback_font_face_key));
+			 cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+							&fallback_font_face_key));
+
     cairo_show_text (cr, text);
     cairo_text_extents (cr, text, extents);
 
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_font_face_t *user_font_face = NULL;
+
 static cairo_font_face_t *
 get_user_font_face (void)
 {
-    static cairo_font_face_t *user_font_face = NULL;
-
     if (!user_font_face) {
+	cairo_font_face_t *fallback_font_face;
+
 	user_font_face = cairo_user_font_face_create ();
 	cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
 	cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
+
+	/* This also happens to be default font face on cairo_t, so does
+	 * not make much sense here.  For demonstration only.
+	 */
+	fallback_font_face = cairo_toy_font_face_create ("",
+							 CAIRO_FONT_SLANT_NORMAL,
+							 CAIRO_FONT_WEIGHT_NORMAL);
+
+	cairo_font_face_set_user_data (user_font_face,
+				       &fallback_font_face_key,
+				       fallback_font_face,
+				       cairo_font_face_destroy);
     }
 
     return user_font_face;
@@ -181,6 +172,11 @@ draw (cairo_t *cr, int width, int height)
     cairo_text_path (cr, text);
     cairo_fill (cr);
 
+    if (user_font_face) {
+        cairo_font_face_destroy (user_font_face);
+	user_font_face = NULL;
+    }
+
     return CAIRO_TEST_SUCCESS;
 }
 
diff --git a/test/user-font.c b/test/user-font.c
index c414719..406bd3a 100644
--- a/test/user-font.c
+++ b/test/user-font.c
@@ -74,6 +74,7 @@ static cairo_user_data_key_t test_font_face_glyphs_key;
 
 static cairo_status_t
 test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
+		       cairo_t              *cr,
 		       cairo_font_extents_t *metrics)
 {
   metrics->ascent  = .75;
commit d6ae23478ae6bde0714a50b2ed77e788f17cc03d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 15:43:21 2008 -0400

    Check for NULL pointer in cairo_set_scaled_font

diff --git a/src/cairo.c b/src/cairo.c
index 4151eed..e96a3ee 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -2905,6 +2905,11 @@ cairo_set_scaled_font (cairo_t                   *cr,
     if (cr->status)
 	return;
 
+    if (scaled_font == NULL) {
+	status = CAIRO_STATUS_NULL_POINTER;
+	goto BAIL;
+    }
+
     status = scaled_font->status;
     if (status)
         goto BAIL;
commit b67d34e960fa07cfafb13e3a5b5b4a63cfb024d3
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Aug 7 15:42:58 2008 -0400

    [gstate] Check for NULL font_face in _cairo_gstate_set_font_face

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index b23d0b6..3160d07 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1466,7 +1466,10 @@ cairo_status_t
 _cairo_gstate_set_font_face (cairo_gstate_t    *gstate,
 			     cairo_font_face_t *font_face)
 {
-    if (font_face && font_face->status)
+    if (font_face == NULL)
+	return CAIRO_STATUS_NULL_POINTER;
+
+    if (font_face->status)
 	return font_face->status;
 
     if (font_face != gstate->font_face) {
commit 7e57892983bbc639fe4a402a427b255e4d4ab746
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Aug 6 21:37:36 2008 -0400

    Add toy font constructor and getters
    
    New public API:
    
    	cairo_toy_font_face_create()
    	cairo_toy_font_face_get_family()
    	cairo_toy_font_face_get_slant()
    	cairo_toy_font_face_get_weight()

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index 3954c45..4b9cdc5 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -408,6 +408,10 @@ cairo_show_text_glyphs
 cairo_font_extents
 cairo_text_extents
 cairo_glyph_extents
+cairo_toy_font_face_create
+cairo_toy_font_face_get_family
+cairo_toy_font_face_get_slant
+cairo_toy_font_face_get_weight
 </SECTION>
 
 <SECTION>
diff --git a/doc/public/tmpl/cairo-status.sgml b/doc/public/tmpl/cairo-status.sgml
index 37b5898..7d65bd5 100644
--- a/doc/public/tmpl/cairo-status.sgml
+++ b/doc/public/tmpl/cairo-status.sgml
@@ -68,6 +68,8 @@ code is required before or after each individual cairo function call.
 @CAIRO_STATUS_USER_FONT_ERROR: 
 @CAIRO_STATUS_NEGATIVE_COUNT: 
 @CAIRO_STATUS_INVALID_CLUSTERS: 
+ at CAIRO_STATUS_INVALID_SLANT:
+ at CAIRO_STATUS_INVALID_WEIGHT:
 
 <!-- ##### FUNCTION cairo_status_to_string ##### -->
 <para>
diff --git a/doc/public/tmpl/cairo-text.sgml b/doc/public/tmpl/cairo-text.sgml
index 06e365c..3e5c6bb 100644
--- a/doc/public/tmpl/cairo-text.sgml
+++ b/doc/public/tmpl/cairo-text.sgml
@@ -244,3 +244,41 @@ Cairo has two sets of text rendering capabilities:
 @extents: 
 
 
+<!-- ##### FUNCTION cairo_toy_font_face_create ##### -->
+<para>
+
+</para>
+
+ at family:
+ at slant:
+ at weight:
+ at Returns:
+
+
+<!-- ##### FUNCTION cairo_toy_font_face_get_family ##### -->
+<para>
+
+</para>
+
+ at font_face:
+ at Returns:
+
+
+<!-- ##### FUNCTION cairo_toy_font_face_get_slant ##### -->
+<para>
+
+</para>
+
+ at font_face:
+ at Returns:
+
+
+<!-- ##### FUNCTION cairo_toy_font_face_get_weight ##### -->
+<para>
+
+</para>
+
+ at font_face:
+ at Returns:
+
+
diff --git a/src/cairo-font-face.c b/src/cairo-font-face.c
index 1d67398..4f822a2 100644
--- a/src/cairo-font-face.c
+++ b/src/cairo-font-face.c
@@ -41,19 +41,78 @@
 #define _BSD_SOURCE /* for strdup() */
 #include "cairoint.h"
 
-/* Forward declare so we can use it as an arbitrary backend for
- * _cairo_font_face_nil.
- */
 static const cairo_font_face_backend_t _cairo_toy_font_face_backend;
 
 /* #cairo_font_face_t */
 
-const cairo_font_face_t _cairo_font_face_nil = {
-    { 0 },			/* hash_entry */
-    CAIRO_STATUS_NO_MEMORY,	/* status */
+const cairo_toy_font_face_t _cairo_font_face_nil = {
+    {
+    { 0 },				/* hash_entry */
+    CAIRO_STATUS_NO_MEMORY,		/* status */
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
+    { 0, 0, 0, NULL },			/* user_data */
+    &_cairo_toy_font_face_backend
+    },
+    CAIRO_FONT_FAMILY_DEFAULT,		/* family */
+    TRUE,				/* owns_family */
+    CAIRO_FONT_SLANT_DEFAULT,		/* slant */
+    CAIRO_FONT_WEIGHT_DEFAULT		/* weight */
+};
+
+static const cairo_toy_font_face_t _cairo_font_face_null_pointer = {
+    {
+    { 0 },				/* hash_entry */
+    CAIRO_STATUS_NULL_POINTER,		/* status */
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
+    { 0, 0, 0, NULL },			/* user_data */
+    &_cairo_toy_font_face_backend
+    },
+    CAIRO_FONT_FAMILY_DEFAULT,		/* family */
+    TRUE,				/* owns_family */
+    CAIRO_FONT_SLANT_DEFAULT,		/* slant */
+    CAIRO_FONT_WEIGHT_DEFAULT		/* weight */
+};
+
+static const cairo_toy_font_face_t _cairo_font_face_invalid_string = {
+    {
+    { 0 },				/* hash_entry */
+    CAIRO_STATUS_INVALID_STRING,	/* status */
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
+    { 0, 0, 0, NULL },			/* user_data */
+    &_cairo_toy_font_face_backend
+    },
+    CAIRO_FONT_FAMILY_DEFAULT,		/* family */
+    TRUE,				/* owns_family */
+    CAIRO_FONT_SLANT_DEFAULT,		/* slant */
+    CAIRO_FONT_WEIGHT_DEFAULT		/* weight */
+};
+
+static const cairo_toy_font_face_t _cairo_font_face_invalid_slant = {
+    {
+    { 0 },				/* hash_entry */
+    CAIRO_STATUS_INVALID_SLANT,		/* status */
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
+    { 0, 0, 0, NULL },			/* user_data */
+    &_cairo_toy_font_face_backend
+    },
+    CAIRO_FONT_FAMILY_DEFAULT,		/* family */
+    TRUE,				/* owns_family */
+    CAIRO_FONT_SLANT_DEFAULT,		/* slant */
+    CAIRO_FONT_WEIGHT_DEFAULT		/* weight */
+};
+
+static const cairo_toy_font_face_t _cairo_font_face_invalid_weight = {
+    {
+    { 0 },				/* hash_entry */
+    CAIRO_STATUS_INVALID_WEIGHT,	/* status */
     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
-    { 0, 0, 0, NULL },		/* user_data */
+    { 0, 0, 0, NULL },			/* user_data */
     &_cairo_toy_font_face_backend
+    },
+    CAIRO_FONT_FAMILY_DEFAULT,		/* family */
+    TRUE,				/* owns_family */
+    CAIRO_FONT_SLANT_DEFAULT,		/* slant */
+    CAIRO_FONT_WEIGHT_DEFAULT		/* weight */
 };
 
 cairo_status_t
@@ -371,7 +430,7 @@ _cairo_toy_font_face_keys_equal (const void *key_a,
 }
 
 /**
- * _cairo_toy_font_face_create:
+ * cairo_toy_font_face_create:
  * @family: a font family name, encoded in UTF-8
  * @slant: the slant for the font
  * @weight: the weight for the font
@@ -380,18 +439,57 @@ _cairo_toy_font_face_keys_equal (const void *key_a,
  * These font faces are used in implementation of the the #cairo_t "toy"
  * font API.
  *
- * Return value: a newly created #cairo_font_face_t, destroy with
- *  cairo_font_face_destroy()
+ * If @family is the zero-length string "", the platform-specific default
+ * family is assumed.  The default family then can be queried using
+ * cairo_toy_font_face_get_family().
+ *
+ * The cairo_select_font_face() function uses this to create font faces.
+ * See that function for limitations of toy font faces.
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ *  cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.8
  **/
 cairo_font_face_t *
-_cairo_toy_font_face_create (const char          *family,
-			     cairo_font_slant_t   slant,
-			     cairo_font_weight_t  weight)
+cairo_toy_font_face_create (const char          *family,
+			    cairo_font_slant_t   slant,
+			    cairo_font_weight_t  weight)
 {
     cairo_status_t status;
     cairo_toy_font_face_t key, *font_face;
     cairo_hash_table_t *hash_table;
 
+    if (family == NULL)
+	return (cairo_font_face_t*) &_cairo_font_face_null_pointer;
+
+    /* Make sure we've got valid UTF-8 for the family */
+    status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL);
+    if (status == CAIRO_STATUS_INVALID_STRING)
+	return (cairo_font_face_t*) &_cairo_font_face_invalid_string;
+    else if (status)
+	return (cairo_font_face_t*) &_cairo_font_face_nil;
+
+    switch (slant) {
+	case CAIRO_FONT_SLANT_NORMAL:
+	case CAIRO_FONT_SLANT_ITALIC:
+	case CAIRO_FONT_SLANT_OBLIQUE:
+	    break;
+	default:
+	    return (cairo_font_face_t*) &_cairo_font_face_invalid_slant;
+    }
+
+    switch (weight) {
+	case CAIRO_FONT_WEIGHT_NORMAL:
+	case CAIRO_FONT_WEIGHT_BOLD:
+	    break;
+	default:
+	    return (cairo_font_face_t*) &_cairo_font_face_invalid_weight;
+    }
+
+    if (*family == '\0')
+	family = CAIRO_FONT_FAMILY_DEFAULT;
+
     hash_table = _cairo_toy_font_face_hash_table_lock ();
     if (hash_table == NULL)
 	goto UNWIND;
@@ -444,6 +542,7 @@ _cairo_toy_font_face_create (const char          *family,
  UNWIND:
     return (cairo_font_face_t*) &_cairo_font_face_nil;
 }
+slim_hidden_def (cairo_toy_font_face_create);
 
 static void
 _cairo_toy_font_face_destroy (void *abstract_face)
@@ -493,6 +592,77 @@ _cairo_toy_font_face_scaled_font_create (void                *abstract_font_face
 							    scaled_font));
 }
 
+static cairo_bool_t
+_cairo_font_face_is_toy (cairo_font_face_t *font_face)
+{
+    return font_face->backend == &_cairo_toy_font_face_backend;
+}
+
+/**
+ * cairo_toy_font_face_get_family:
+ * @font_face: A toy font face
+ *
+ * Gets the familly name of a toy font.
+ *
+ * Return value: The family name.  This string is owned by the font face
+ * and remains valid as long as the font face is alive (referenced).
+ *
+ * Since: 1.8
+ **/
+const char *
+cairo_toy_font_face_get_family (cairo_font_face_t *font_face)
+{
+    cairo_toy_font_face_t *toy_font_face = (cairo_toy_font_face_t *) font_face;
+    if (! _cairo_font_face_is_toy (font_face)) {
+	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+	    return CAIRO_FONT_FAMILY_DEFAULT;
+    }
+    assert (toy_font_face->owns_family);
+    return toy_font_face->family;
+}
+
+/**
+ * cairo_toy_font_face_get_slant:
+ * @font_face: A toy font face
+ *
+ * Gets the slant a toy font.
+ *
+ * Return value: The slant value
+ *
+ * Since: 1.8
+ **/
+cairo_font_slant_t
+cairo_toy_font_face_get_slant (cairo_font_face_t *font_face)
+{
+    cairo_toy_font_face_t *toy_font_face = (cairo_toy_font_face_t *) font_face;
+    if (! _cairo_font_face_is_toy (font_face)) {
+	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+	    return CAIRO_FONT_SLANT_DEFAULT;
+    }
+    return toy_font_face->slant;
+}
+
+/**
+ * cairo_toy_font_face_get_weight:
+ * @font_face: A toy font face
+ *
+ * Gets the weight a toy font.
+ *
+ * Return value: The weight value
+ *
+ * Since: 1.8
+ **/
+cairo_font_weight_t
+cairo_toy_font_face_get_weight (cairo_font_face_t *font_face)
+{
+    cairo_toy_font_face_t *toy_font_face = (cairo_toy_font_face_t *) font_face;
+    if (! _cairo_font_face_is_toy (font_face)) {
+	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+	    return CAIRO_FONT_WEIGHT_DEFAULT;
+    }
+    return toy_font_face->weight;
+}
+
 static const cairo_font_face_backend_t _cairo_toy_font_face_backend = {
     CAIRO_FONT_TYPE_TOY,
     _cairo_toy_font_face_destroy,
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index b9d59d3..b23d0b6 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1215,7 +1215,7 @@ _cairo_gstate_select_font_face (cairo_gstate_t       *gstate,
     cairo_font_face_t *font_face;
     cairo_status_t status;
 
-    font_face = _cairo_toy_font_face_create (family, slant, weight);
+    font_face = cairo_toy_font_face_create (family, slant, weight);
     if (font_face->status)
 	return font_face->status;
 
@@ -1389,7 +1389,7 @@ _cairo_gstate_ensure_font_face (cairo_gstate_t *gstate)
 	return gstate->font_face->status;
 
 
-    font_face = _cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT,
+    font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT,
 					     CAIRO_FONT_SLANT_DEFAULT,
 					     CAIRO_FONT_WEIGHT_DEFAULT);
     if (font_face->status)
diff --git a/src/cairo-misc.c b/src/cairo-misc.c
index 71338bf..3432e50 100644
--- a/src/cairo-misc.c
+++ b/src/cairo-misc.c
@@ -113,6 +113,10 @@ cairo_status_to_string (cairo_status_t status)
 	return "negative number used where it is not allowed";
     case CAIRO_STATUS_INVALID_CLUSTERS:
 	return "input clusters do not represent the accompanying text and glyph arrays";
+    case CAIRO_STATUS_INVALID_SLANT:
+	return "invalid value for an input #cairo_font_slant_t";
+    case CAIRO_STATUS_INVALID_WEIGHT:
+	return "input value for an input #cairo_font_weight_t";
     }
 
     return "<unknown error status>";
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index ca703aa..3eca6fc 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -2632,6 +2632,8 @@ _cairo_surface_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_USER_FONT_ERROR:
     case CAIRO_STATUS_NEGATIVE_COUNT:
     case CAIRO_STATUS_INVALID_CLUSTERS:
+    case CAIRO_STATUS_INVALID_SLANT:
+    case CAIRO_STATUS_INVALID_WEIGHT:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_surface_t *) &_cairo_surface_nil;
diff --git a/src/cairo.c b/src/cairo.c
index 46e587b..4151eed 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -2641,8 +2641,12 @@ cairo_copy_clip_rectangle_list (cairo_t *cr)
  *
  * If text is drawn without a call to cairo_select_font_face(), (nor
  * cairo_set_font_face() nor cairo_set_scaled_font()), the default
- * family is "sans", slant is %CAIRO_FONT_SLANT_NORMAL, and weight is
+ * family is platform-specific, but is essentially "sans-serif".
+ * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is
  * %CAIRO_FONT_WEIGHT_NORMAL.
+ *
+ * This function is equivalent to a call to cairo_toy_font_face_create()
+ * followed by cairo_set_font_face().
  **/
 void
 cairo_select_font_face (cairo_t              *cr,
diff --git a/src/cairo.h b/src/cairo.h
index 0829591..1be31ab 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -208,6 +208,8 @@ typedef struct _cairo_user_data_key {
  * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8)
  * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8)
  * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8)
+ * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_CLUSTERS: input value for an input #cairo_font_weight_t (Since 1.8)
  *
  * #cairo_status_t is used to indicate errors that can occur when
  * using Cairo. In some cases it is returned directly by functions.
@@ -247,7 +249,9 @@ typedef enum _cairo_status {
     CAIRO_STATUS_USER_FONT_IMMUTABLE,
     CAIRO_STATUS_USER_FONT_ERROR,
     CAIRO_STATUS_NEGATIVE_COUNT,
-    CAIRO_STATUS_INVALID_CLUSTERS
+    CAIRO_STATUS_INVALID_CLUSTERS,
+    CAIRO_STATUS_INVALID_SLANT,
+    CAIRO_STATUS_INVALID_WEIGHT
     /* after adding a new error: update CAIRO_STATUS_LAST_STATUS in cairoint.h */
 } cairo_status_t;
 
@@ -1333,6 +1337,24 @@ cairo_public void
 cairo_scaled_font_get_font_options (cairo_scaled_font_t		*scaled_font,
 				    cairo_font_options_t	*options);
 
+
+/* Toy fonts */
+
+cairo_public cairo_font_face_t *
+cairo_toy_font_face_create (const char           *family,
+			    cairo_font_slant_t    slant,
+			    cairo_font_weight_t   weight);
+
+cairo_public const char *
+cairo_toy_font_face_get_family (cairo_font_face_t *font_face);
+
+cairo_public cairo_font_slant_t
+cairo_toy_font_face_get_slant (cairo_font_face_t *font_face);
+
+cairo_public cairo_font_weight_t
+cairo_toy_font_face_get_weight (cairo_font_face_t *font_face);
+
+
 /* User fonts */
 
 cairo_public cairo_font_face_t *
diff --git a/src/cairoint.h b/src/cairoint.h
index 0f21776..486a164 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -110,7 +110,7 @@ _cairo_win32_tmpfile (void);
  * a bit of a pain, but it should be easy to always catch as long as
  * one adds a new test case to test a trigger of the new status value.
  */
-#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_INVALID_CLUSTERS
+#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_INVALID_SLANT
 
 
 /* Size in bytes of buffer to use off the stack per functions.
@@ -1304,7 +1304,7 @@ _cairo_color_equal (const cairo_color_t *color_a,
 
 /* cairo-font-face.c */
 
-extern const cairo_private cairo_font_face_t _cairo_font_face_nil;
+extern const cairo_private cairo_toy_font_face_t _cairo_font_face_nil;
 
 cairo_private void
 _cairo_font_face_init (cairo_font_face_t               *font_face,
@@ -1314,11 +1314,6 @@ cairo_private cairo_status_t
 _cairo_font_face_set_error (cairo_font_face_t *font_face,
 	                    cairo_status_t     status);
 
-cairo_private cairo_font_face_t *
-_cairo_toy_font_face_create (const char           *family,
-			     cairo_font_slant_t    slant,
-			     cairo_font_weight_t   weight);
-
 cairo_private void
 _cairo_unscaled_font_init (cairo_unscaled_font_t               *font,
 			   const cairo_unscaled_font_backend_t *backend);
@@ -2447,6 +2442,7 @@ slim_hidden_proto (cairo_surface_set_fallback_resolution);
 slim_hidden_proto (cairo_surface_copy_page);
 slim_hidden_proto (cairo_surface_show_page);
 slim_hidden_proto (cairo_surface_status);
+slim_hidden_proto (cairo_toy_font_face_create);
 slim_hidden_proto (cairo_version_string);
 
 #if CAIRO_HAS_PNG_FUNCTIONS
diff --git a/test/.gitignore b/test/.gitignore
index 4a72553..1edaff3 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -197,6 +197,7 @@ text-pattern
 text-rotate
 text-transform
 text-zero-len
+toy-font-face
 transforms
 translate-show-surface
 trap-clip
diff --git a/test/Makefile.am b/test/Makefile.am
index f9ab75d..5af7c08 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -158,6 +158,7 @@ text-pattern$(EXEEXT)					\
 text-rotate$(EXEEXT)					\
 text-transform$(EXEEXT)					\
 text-zero-len$(EXEEXT)					\
+toy-font-face$(EXEEXT)					\
 transforms$(EXEEXT)					\
 translate-show-surface$(EXEEXT)				\
 trap-clip$(EXEEXT)					\
@@ -715,6 +716,7 @@ REFERENCE_IMAGES = \
 	user-font-pdf-ref.png	\
 	user-font-svg-ref.png	\
 	user-font-proxy-ref.png	\
+	user-font-proxy-pdf-ref.png	\
 	user-font-proxy-ps-ref.png	\
 	user-font-proxy-svg-ref.png	\
 	unbounded-operator-quartz-ref.png	\
@@ -764,6 +766,7 @@ png				\
 ps-features			\
 svg-clip			\
 svg-surface			\
+toy-font-face			\
 user-data
 
 # A hook that summarises the failures
diff --git a/test/toy-font-face.c b/test/toy-font-face.c
new file mode 100644
index 0000000..262f7ce
--- /dev/null
+++ b/test/toy-font-face.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2005,2008 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ *         Behdad Esfahbod <behdad at behdad.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cairo.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+int
+main (void)
+{
+    cairo_t *cr;
+    cairo_surface_t *surface;
+    cairo_font_face_t *font_face;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 0, 0);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    font_face = cairo_font_face_reference (cairo_get_font_face (cr));
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (cairo_toy_font_face_get_family (font_face) != NULL);
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_SUCCESS);
+    cairo_font_face_destroy (font_face);
+
+    cairo_select_font_face (cr,
+			    "bizarre",
+			    CAIRO_FONT_SLANT_OBLIQUE,
+			    CAIRO_FONT_WEIGHT_BOLD);
+    font_face = cairo_font_face_reference (cairo_get_font_face (cr));
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), "bizarre"));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_OBLIQUE);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_SUCCESS);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("bozarre",
+					    CAIRO_FONT_SLANT_OBLIQUE,
+					    CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), "bozarre"));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_OBLIQUE);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_SUCCESS);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create (NULL,
+					    CAIRO_FONT_SLANT_OBLIQUE,
+					    CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), ""));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_NULL_POINTER);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("\xff",
+					    CAIRO_FONT_SLANT_OBLIQUE,
+					    CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), ""));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_INVALID_STRING);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("sans",
+					    -1,
+					    CAIRO_FONT_WEIGHT_BOLD);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), ""));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_INVALID_SLANT);
+    cairo_font_face_destroy (font_face);
+
+    font_face = cairo_toy_font_face_create ("sans",
+					    CAIRO_FONT_SLANT_OBLIQUE,
+					    -1);
+    assert (cairo_font_face_get_type (font_face) == CAIRO_FONT_TYPE_TOY);
+    assert (0 == (strcmp) (cairo_toy_font_face_get_family (font_face), ""));
+    assert (cairo_toy_font_face_get_slant (font_face) == CAIRO_FONT_SLANT_NORMAL);
+    assert (cairo_toy_font_face_get_weight (font_face) == CAIRO_FONT_WEIGHT_NORMAL);
+    assert (cairo_font_face_status(font_face) == CAIRO_STATUS_INVALID_WEIGHT);
+    cairo_font_face_destroy (font_face);
+
+    cairo_destroy (cr);
+
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+
+    return 0;
+}
diff --git a/test/user-font-proxy-pdf-ref.png b/test/user-font-proxy-pdf-ref.png
new file mode 100644
index 0000000..0eda0f4
Binary files /dev/null and b/test/user-font-proxy-pdf-ref.png differ
diff --git a/test/user-font-proxy-ps-ref.png b/test/user-font-proxy-ps-ref.png
index 78bee6c..4305411 100644
Binary files a/test/user-font-proxy-ps-ref.png and b/test/user-font-proxy-ps-ref.png differ
diff --git a/test/user-font-proxy.c b/test/user-font-proxy.c
index f54139c..c7b29ab 100644
--- a/test/user-font-proxy.c
+++ b/test/user-font-proxy.c
@@ -51,42 +51,43 @@ cairo_test_t test = {
     draw
 };
 
-static cairo_user_data_key_t fallback_scaled_font_key;
+static cairo_user_data_key_t fallback_font_face_key;
 
 static cairo_status_t
 test_scaled_font_init (cairo_scaled_font_t  *scaled_font,
 		       cairo_font_extents_t *extents)
 {
-  cairo_t *cr;
-  cairo_surface_t *surface;
-  cairo_matrix_t ctm;
+  cairo_font_face_t *font_face;
+  cairo_matrix_t font_matrix, ctm;
   cairo_font_options_t *font_options;
   cairo_scaled_font_t *fallback_scaled_font;
 
-  /* painful way to get default font face used by toy api */
-  surface = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0);
-  cr = cairo_create (surface);
-  cairo_surface_destroy (surface);
+  font_face = cairo_toy_font_face_create ("",
+					  CAIRO_FONT_SLANT_NORMAL,
+					  CAIRO_FONT_WEIGHT_NORMAL);
 
-  cairo_set_font_size (cr, 1.);
+  cairo_matrix_init_identity (&font_matrix);
   cairo_scaled_font_get_scale_matrix (scaled_font, &ctm);
-  cairo_set_matrix (cr, &ctm);
 
   font_options = cairo_font_options_create ();
   cairo_scaled_font_get_font_options (scaled_font, font_options);
-  cairo_set_font_options (cr, font_options);
-  cairo_font_options_destroy (font_options);
 
-  fallback_scaled_font = cairo_scaled_font_reference (cairo_get_scaled_font (cr)),
-  cairo_scaled_font_set_user_data (scaled_font,
-				   &fallback_scaled_font_key,
-				   fallback_scaled_font,
-				   cairo_scaled_font_destroy);
+  fallback_scaled_font = cairo_scaled_font_create (font_face,
+						   &font_matrix,
+						   &ctm,
+						   font_options);
 
-  cairo_destroy (cr);
+  cairo_font_options_destroy (font_options);
 
   cairo_scaled_font_extents (fallback_scaled_font, extents);
 
+  cairo_scaled_font_destroy (fallback_scaled_font);
+
+  cairo_scaled_font_set_user_data (scaled_font,
+				   &fallback_font_face_key,
+				   font_face,
+				   cairo_font_face_destroy);
+
   return CAIRO_STATUS_SUCCESS;
 }
 
@@ -101,9 +102,9 @@ test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
     /* XXX only works for ASCII.  need ucs4_to_utf8 :( */
     text[0] = glyph;
 
-    cairo_set_scaled_font (cr,
-			   cairo_scaled_font_get_user_data (scaled_font,
-							    &fallback_scaled_font_key));
+    cairo_set_font_face (cr,
+			 cairo_scaled_font_get_user_data (scaled_font,
+							  &fallback_font_face_key));
     cairo_show_text (cr, text);
     cairo_text_extents (cr, text, extents);
 
commit bca9a21e98c870cdb4695c9155517377757beaea
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Aug 6 20:40:23 2008 -0400

    [docs] Update private header list

diff --git a/doc/public/Headers.mk b/doc/public/Headers.mk
index d04e1a9..7b9edff 100644
--- a/doc/public/Headers.mk
+++ b/doc/public/Headers.mk
@@ -48,4 +48,5 @@ PRIVATE_TEST_HFILES = \
 	cairo-xlib-private.h \
 	cairo-xlib-surface-private.h \
 	cairo-xlib-xrender-private.h \
+	cairo-features-win32.h \
 	cairoint.h
diff --git a/doc/public/Makefile.am b/doc/public/Makefile.am
index 0a570ad..952ded3 100644
--- a/doc/public/Makefile.am
+++ b/doc/public/Makefile.am
@@ -39,6 +39,7 @@ Headers.mk:
 		-name '*-test.h' | \
 	sed 's at .*/@	@; s@$$@ \\@' | \
 	LANG=C sort; \
+	echo '	cairo-features-win32.h \'; \
 	echo '	cairoint.h' ) > $@.tmp
 	mv $@.tmp $@
 
commit 4bb7388b65ea56287f877f8241caa7a189c0c519
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Aug 6 20:38:29 2008 -0400

    Remove unnecessary checks for CAIRO_STATUS_INVALID_STRING
    
    We check that all input text is valid UTF-8.  Not sure why the code
    was being lenient to invalid strings.

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index f58bd7a..22c5dc9 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -2868,7 +2868,7 @@ _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t	*surface,
 
     if (utf8 && *utf8) {
 	status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
-	if (status && status != CAIRO_STATUS_INVALID_STRING)
+	if (status)
 	    return status;
     }
 
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 6cfccac..94d1cea 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -944,8 +944,8 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset
 	utf16_len = 0;
 	if (utf8 && *utf8) {
 	    status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
-	    if (status && status != CAIRO_STATUS_INVALID_STRING)
-		return status; // FIXME
+	    if (status)
+		return status; /* FIXME */
 	}
 
 	if (utf16_len == 1) {


More information about the cairo-commit mailing list