[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