From 095a1fd7860bd1f6d4116766ade0ec9a382bb957 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 5 Dec 2008 21:14:45 +0000 Subject: [PATCH] [gstate] Remove culled glyphs from clusters. 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 | 221 +++++++++++++++++++++++++++++++++++---------- src/cairo.c | 9 +- 2 files changed, 182 insertions(+), 48 deletions(-) diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 09161aa48..e76d72a76 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) +_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; + 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++) { + 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 75674f98a..308459382 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,