[PATCH] [gstate] Remove culled glyphs from clusters.

Chris Wilson chris at chris-wilson.co.uk
Fri Dec 5 13:14:45 PST 2008


Sascha Steinbiss reported a bug where the PDF backend was reading beyond
the end of the glyph array:
http://lists.cairographics.org/archives/cairo/2008-December/015976.html.

It transpires that in the early glyph culling in the gstate we were
not updating the clusters to skip culled glyphs.
---
 src/cairo-gstate.c |  223 +++++++++++++++++++++++++++++++++++++++++-----------
 src/cairo.c        |    9 ++-
 2 files changed, 183 insertions(+), 49 deletions(-)

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 09161aa..01e79df 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -64,8 +64,12 @@ static cairo_status_t
 _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
                                            const cairo_glyph_t *glyphs,
                                            int                  num_glyphs,
+					   const cairo_text_cluster_t	*clusters,
+					   int			 num_clusters,
+					   cairo_text_cluster_flags_t cluster_flags,
                                            cairo_glyph_t       *transformed_glyphs,
-					   int		       *num_transformed_glyphs);
+					   int			*num_transformed_glyphs,
+					   cairo_text_cluster_t *transformed_clusters);
 
 cairo_status_t
 _cairo_gstate_init (cairo_gstate_t  *gstate,
@@ -1594,11 +1598,13 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 				int			    num_clusters,
 				cairo_text_cluster_flags_t  cluster_flags)
 {
-    cairo_status_t status;
     cairo_pattern_union_t source_pattern_stack;
     cairo_pattern_t *source_pattern;
-    cairo_glyph_t *transformed_glyphs;
     cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+    cairo_glyph_t *transformed_glyphs;
+    cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
+    cairo_text_cluster_t *transformed_clusters;
+    cairo_status_t status;
 
     if (gstate->source->status)
 	return gstate->source->status;
@@ -1611,18 +1617,37 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
     if (unlikely (status))
 	return status;
 
-    if (num_glyphs <= ARRAY_LENGTH (stack_transformed_glyphs)) {
-	transformed_glyphs = stack_transformed_glyphs;
-    } else {
+    transformed_glyphs = stack_transformed_glyphs;
+    transformed_clusters = stack_transformed_clusters;
+
+    if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) {
 	transformed_glyphs = cairo_glyph_allocate (num_glyphs);
-	if (unlikely (transformed_glyphs == NULL))
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	if (unlikely (transformed_glyphs == NULL)) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto CLEANUP_GLYPHS;
+	}
+    }
+
+    /* Just in case */
+    if (!clusters)
+	num_clusters = 0;
+
+    if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
+	transformed_clusters = cairo_text_cluster_allocate (num_clusters);
+	if (unlikely (transformed_clusters == NULL)) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto CLEANUP_GLYPHS;
+	}
     }
 
     status = _cairo_gstate_transform_glyphs_to_backend (gstate,
 							glyphs, num_glyphs,
+							clusters,
+							num_clusters,
+							cluster_flags,
 							transformed_glyphs,
-							&num_glyphs);
+							&num_glyphs,
+							transformed_clusters);
 
     if (status || num_glyphs == 0)
 	goto CLEANUP_GLYPHS;
@@ -1632,10 +1657,6 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
     if (unlikely (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
@@ -1653,7 +1674,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 						  source_pattern,
 						  utf8, utf8_len,
 						  transformed_glyphs, num_glyphs,
-						  clusters, num_clusters,
+						  transformed_clusters, num_clusters,
 						  cluster_flags,
 						  gstate->scaled_font, NULL);
     } else {
@@ -1683,6 +1704,8 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 CLEANUP_GLYPHS:
     if (transformed_glyphs != stack_transformed_glyphs)
       cairo_glyph_free (transformed_glyphs);
+    if (transformed_clusters != stack_transformed_clusters)
+      cairo_text_cluster_free (transformed_clusters);
 
     return status;
 }
@@ -1711,8 +1734,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
 
     status = _cairo_gstate_transform_glyphs_to_backend (gstate,
 							glyphs, num_glyphs,
+							NULL, 0, 0,
 							transformed_glyphs,
-							NULL);
+							NULL, NULL);
     if (unlikely (status))
 	goto CLEANUP_GLYPHS;
 
@@ -1761,13 +1785,17 @@ _cairo_gstate_get_antialias (cairo_gstate_t *gstate)
  * cull/drop glyphs that will not be visible.
  **/
 static cairo_status_t
-_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
-                                           const cairo_glyph_t *glyphs,
-                                           int                  num_glyphs,
-                                           cairo_glyph_t       *transformed_glyphs,
-					   int		       *num_transformed_glyphs)
-{
-    int i, j;
+_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t	*gstate,
+                                           const cairo_glyph_t	*glyphs,
+                                           int			 num_glyphs,
+					   const cairo_text_cluster_t	*clusters,
+					   int			 num_clusters,
+					   cairo_text_cluster_flags_t cluster_flags,
+                                           cairo_glyph_t	*transformed_glyphs,
+					   int			*num_transformed_glyphs,
+					   cairo_text_cluster_t *transformed_clusters)
+{
+    int i, j, k;
     cairo_matrix_t *ctm = &gstate->ctm;
     cairo_matrix_t *font_matrix = &gstate->font_matrix;
     cairo_matrix_t *device_transform = &gstate->target->device_transform;
@@ -1813,22 +1841,52 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
 
 #define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2)
 
+    j = 0;
     if (_cairo_matrix_is_identity (ctm) &&
         _cairo_matrix_is_identity (device_transform) &&
 	font_matrix->x0 == 0 && font_matrix->y0 == 0)
     {
-	if (!drop)
-	    memcpy (transformed_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
-	else {
-	    for (j = 0, i = 0; i < num_glyphs; i++)
-	    {
+	if (! drop) {
+	    memcpy (transformed_glyphs, glyphs,
+		    num_glyphs * sizeof (cairo_glyph_t));
+	} else if (num_clusters == 0) {
+	    for (i = 0; i < num_glyphs; i++) {
 		transformed_glyphs[j].index = glyphs[i].index;
 		transformed_glyphs[j].x = glyphs[i].x;
 		transformed_glyphs[j].y = glyphs[i].y;
 		if (KEEP_GLYPH (transformed_glyphs[j]))
 		    j++;
 	    }
-	    *num_transformed_glyphs = j;
+	} else {
+	    const cairo_glyph_t *cur_glyph;
+
+	    if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+		cur_glyph = glyphs + num_glyphs - 1;
+	    else
+		cur_glyph = glyphs;
+
+	    for (i = 0; i < num_clusters; i++) {
+		cairo_bool_t cluster_visible = FALSE;
+
+		for (k = 0; k < clusters[i].num_glyphs; k++) {
+		    transformed_glyphs[j+k].index = cur_glyph->index;
+		    transformed_glyphs[j+k].x = cur_glyph->x;
+		    transformed_glyphs[j+k].y = cur_glyph->y;
+		    if (KEEP_GLYPH (transformed_glyphs[j+k]))
+			cluster_visible = TRUE;
+
+		    if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+			cur_glyph--;
+		    else
+			cur_glyph++;
+		}
+
+		transformed_clusters[i] = clusters[i];
+		if (cluster_visible)
+		    j += k;
+		else
+		    transformed_clusters[i].num_glyphs = 0;
+	    }
 	}
     }
     else if (_cairo_matrix_is_translation (ctm) &&
@@ -1837,15 +1895,45 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
         double tx = font_matrix->x0 + ctm->x0 + device_transform->x0;
         double ty = font_matrix->y0 + ctm->y0 + device_transform->y0;
 
-        for (j = 0, i = 0; i < num_glyphs; i++)
-        {
-            transformed_glyphs[j].index = glyphs[i].index;
-            transformed_glyphs[j].x = glyphs[i].x + tx;
-            transformed_glyphs[j].y = glyphs[i].y + ty;
-	    if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
-		j++;
-        }
-	*num_transformed_glyphs = j;
+	if (! drop || num_clusters == 0) {
+	    for (i = 0; i < num_glyphs; i++, j++) {
+		transformed_glyphs[j].index = glyphs[i].index;
+		transformed_glyphs[j].x = glyphs[i].x + tx;
+		transformed_glyphs[j].y = glyphs[i].y + ty;
+		if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
+		    j++;
+	    }
+	} else {
+	    const cairo_glyph_t *cur_glyph;
+
+	    if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+		cur_glyph = glyphs + num_glyphs - 1;
+	    else
+		cur_glyph = glyphs;
+
+	    for (i = 0; i < num_clusters; i++) {
+		cairo_bool_t cluster_visible = FALSE;
+
+		for (k = 0; k < clusters[i].num_glyphs; k++) {
+		    transformed_glyphs[j+k].index = cur_glyph->index;
+		    transformed_glyphs[j+k].x = cur_glyph->x + tx;
+		    transformed_glyphs[j+k].y = cur_glyph->y + ty;
+		    if (KEEP_GLYPH (transformed_glyphs[j+k]))
+			cluster_visible = TRUE;
+
+		    if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+			cur_glyph--;
+		    else
+			cur_glyph++;
+		}
+
+		transformed_clusters[i] = clusters[i];
+		if (cluster_visible)
+		    j += k;
+		else
+		    transformed_clusters[i].num_glyphs = 0;
+	    }
+	}
     }
     else
     {
@@ -1859,16 +1947,57 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
         cairo_matrix_multiply (&aggregate_transform,
                                &aggregate_transform, device_transform);
 
-        for (j = 0, i = 0; i < num_glyphs; i++)
-        {
-            transformed_glyphs[j] = glyphs[i];
-            cairo_matrix_transform_point (&aggregate_transform,
-                                          &transformed_glyphs[j].x,
-                                          &transformed_glyphs[j].y);
-	    if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
-		j++;
-        }
-	*num_transformed_glyphs = j;
+	if (! drop || num_clusters == 0) {
+	    for (i = 0; i < num_glyphs; i++) {
+		transformed_glyphs[j] = glyphs[i];
+		cairo_matrix_transform_point (&aggregate_transform,
+					      &transformed_glyphs[j].x,
+					      &transformed_glyphs[j].y);
+		if (! drop || KEEP_GLYPH (transformed_glyphs[j]))
+		    j++;
+	    }
+	} else {
+	    const cairo_glyph_t *cur_glyph;
+
+	    if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+		cur_glyph = glyphs + num_glyphs - 1;
+	    else
+		cur_glyph = glyphs;
+
+	    for (i = 0; i < num_clusters; i++) {
+		cairo_bool_t cluster_visible = FALSE;
+		for (k = 0; k < clusters[i].num_glyphs; k++) {
+		    transformed_glyphs[j+k] = *cur_glyph;
+		    cairo_matrix_transform_point (&aggregate_transform,
+						  &transformed_glyphs[j+k].x,
+						  &transformed_glyphs[j+k].y);
+		    if (KEEP_GLYPH (transformed_glyphs[j+k]))
+			cluster_visible = TRUE;
+
+		    if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+			cur_glyph--;
+		    else
+			cur_glyph++;
+		}
+
+		transformed_clusters[i] = clusters[i];
+		if (cluster_visible)
+		    j += k;
+		else
+		    transformed_clusters[i].num_glyphs = 0;
+	    }
+	}
+    }
+    *num_transformed_glyphs = j;
+
+    if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) {
+	for (i = 0; i < --j; i++) {
+	    cairo_glyph_t tmp;
+
+	    tmp = transformed_glyphs[i];
+	    transformed_glyphs[i] = transformed_glyphs[j];
+	    transformed_glyphs[j] = tmp;
+	}
     }
 
     return CAIRO_STATUS_SUCCESS;
diff --git a/src/cairo.c b/src/cairo.c
index 75674f9..3084593 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -3104,8 +3104,13 @@ cairo_show_text (cairo_t *cr, const char *utf8)
     glyphs = stack_glyphs;
     num_glyphs = ARRAY_LENGTH (stack_glyphs);
 
-    clusters = stack_clusters;
-    num_clusters = ARRAY_LENGTH (stack_clusters);
+    if (has_show_text_glyphs) {
+	clusters = stack_clusters;
+	num_clusters = ARRAY_LENGTH (stack_clusters);
+    } else {
+	clusters = NULL;
+	num_clusters = 0;
+    }
 
     status = _cairo_gstate_text_to_glyphs (cr->gstate,
 					   x, y,
-- 
1.6.0.4


--=-y/PVcXeSdqHysKFYZfhP--



More information about the cairo mailing list