[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.
This commit is contained in:
Chris Wilson 2008-12-05 21:14:45 +00:00
parent 834f1d7b70
commit 095a1fd786
2 changed files with 182 additions and 48 deletions

View file

@ -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;

View file

@ -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,