diff options
-rw-r--r-- | doc/public/cairo-sections.txt | 3 | ||||
-rw-r--r-- | doc/public/tmpl/cairo-status.sgml | 1 | ||||
-rw-r--r-- | doc/public/tmpl/cairo-text.sgml | 31 | ||||
-rw-r--r-- | src/cairo-gstate.c | 38 | ||||
-rw-r--r-- | src/cairo-misc.c | 2 | ||||
-rw-r--r-- | src/cairo-scaled-font.c | 14 | ||||
-rw-r--r-- | src/cairo-surface.c | 110 | ||||
-rw-r--r-- | src/cairo.c | 109 | ||||
-rw-r--r-- | src/cairo.h | 22 | ||||
-rw-r--r-- | src/cairoint.h | 48 |
10 files changed, 336 insertions, 42 deletions
diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index c9586386..7e608827 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -387,6 +387,7 @@ cairo_path_extents cairo_glyph_t cairo_font_slant_t cairo_font_weight_t +cairo_text_cluster_t cairo_select_font_face cairo_set_font_size cairo_set_font_matrix @@ -399,6 +400,8 @@ cairo_set_scaled_font cairo_get_scaled_font cairo_show_text cairo_show_glyphs +cairo_has_show_text_glyphs +cairo_show_text_glyphs cairo_font_extents cairo_text_extents cairo_glyph_extents diff --git a/doc/public/tmpl/cairo-status.sgml b/doc/public/tmpl/cairo-status.sgml index f88befee..37b5898c 100644 --- a/doc/public/tmpl/cairo-status.sgml +++ b/doc/public/tmpl/cairo-status.sgml @@ -67,6 +67,7 @@ code is required before or after each individual cairo function call. @CAIRO_STATUS_USER_FONT_IMMUTABLE: @CAIRO_STATUS_USER_FONT_ERROR: @CAIRO_STATUS_NEGATIVE_COUNT: +@CAIRO_STATUS_INVALID_CLUSTERS: <!-- ##### FUNCTION cairo_status_to_string ##### --> <para> diff --git a/doc/public/tmpl/cairo-text.sgml b/doc/public/tmpl/cairo-text.sgml index 8de82add..925d29b9 100644 --- a/doc/public/tmpl/cairo-text.sgml +++ b/doc/public/tmpl/cairo-text.sgml @@ -70,6 +70,14 @@ Cairo has two sets of text rendering capabilities: @CAIRO_FONT_WEIGHT_NORMAL: @CAIRO_FONT_WEIGHT_BOLD: +<!-- ##### STRUCT cairo_text_cluster_t ##### --> +<para> + +</para> + +@num_bytes: +@num_glyphs: + <!-- ##### FUNCTION cairo_select_font_face ##### --> <para> @@ -181,6 +189,29 @@ Cairo has two sets of text rendering capabilities: @num_glyphs: +<!-- ##### FUNCTION cairo_has_show_text_glyphs ##### --> +<para> + +</para> + +@cr: + + +<!-- ##### FUNCTION cairo_show_text_glyphs ##### --> +<para> + +</para> + +@cr: +@utf8: +@utf8_len: +@glyphs: +@num_glyphs: +@clusters: +@num_clusters: +@backward: + + <!-- ##### FUNCTION cairo_font_extents ##### --> <para> diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 2579de79..0eb6e7d2 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1497,10 +1497,21 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, return cairo_scaled_font_status (gstate->scaled_font); } +cairo_bool_t +_cairo_gstate_has_show_text_glyphs (cairo_gstate_t *gstate) +{ + return _cairo_surface_has_show_text_glyphs (gstate->target); +} + cairo_status_t -_cairo_gstate_show_glyphs (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, + 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; cairo_pattern_union_t source_pattern; @@ -1543,14 +1554,19 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, * rasterizer is something like ten times slower than freetype's for huge * sizes. So, no win just yet. For now, do it for insanely-huge sizes, * just to make sure we don't make anyone unhappy. When we get a really - * fast rasterizer in cairo, we may want to readjust this. */ - if (_cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240) { - status = _cairo_surface_show_glyphs (gstate->target, - gstate->op, - &source_pattern.base, - transformed_glyphs, - num_glyphs, - gstate->scaled_font); + * fast rasterizer in cairo, we may want to readjust this. + * + * Needless to say, do this only if show_text_glyphs is not available. */ + if (_cairo_gstate_has_show_text_glyphs (gstate) || + _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240) { + status = _cairo_surface_show_text_glyphs (gstate->target, + gstate->op, + &source_pattern.base, + utf8, utf8_len, + transformed_glyphs, num_glyphs, + clusters, num_clusters, + backward, + gstate->scaled_font); } else { cairo_path_fixed_t path; diff --git a/src/cairo-misc.c b/src/cairo-misc.c index 30686a01..71338bff 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -111,6 +111,8 @@ cairo_status_to_string (cairo_status_t status) return "error occurred in a user-font callback function"; case CAIRO_STATUS_NEGATIVE_COUNT: 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"; } return "<unknown error status>"; diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index 31ff7cf6..f244984c 100644 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -1277,20 +1277,26 @@ _cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, cairo_status_t status; cairo_scaled_glyph_t *scaled_glyph; + *num_glyphs = 0; + *glyphs = NULL; + status = scaled_font->status; if (status) return status; - if (utf8[0] == '\0') { - *num_glyphs = 0; - *glyphs = NULL; + if (utf8[0] == '\0') return CAIRO_STATUS_SUCCESS; - } CAIRO_MUTEX_LOCK (scaled_font->mutex); _cairo_scaled_font_freeze_cache (scaled_font); if (scaled_font->backend->text_to_glyphs) { + + /* validate input so backend does not have to */ + status = _cairo_utf8_to_ucs4 (utf8, -1, NULL, NULL); + if (status) + goto DONE; + status = scaled_font->backend->text_to_glyphs (scaled_font, x, y, utf8, glyphs, num_glyphs); diff --git a/src/cairo-surface.c b/src/cairo-surface.c index c186dd99..6e6f7fc9 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -2135,13 +2135,41 @@ _cairo_surface_get_extents (cairo_surface_t *surface, return status; } +cairo_status_t +_cairo_surface_show_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font) +{ + return _cairo_surface_show_text_glyphs (surface, + op, + source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font); +} + +cairo_bool_t +_cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) +{ + if (surface->backend->has_show_text_glyphs) + return surface->backend->has_show_text_glyphs (surface); + else + return surface->backend->show_text_glyphs != NULL; +} + /* Note: the backends may modify the contents of the glyph array as long as * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to * avoid copying the array again and again, and edit it in-place. * Backends are in fact free to use the array as a generic buffer as they * see fit. * - * When they do return UNSUPPORTED, they may adjust remaining_glyphs to notify + * For show_glyphs backend method, and NOT for show_text_glyphs method, + * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify * that they have successfully rendered some of the glyphs (from the beginning * of the array), but not all. If they don't touch remaining_glyphs, it * defaults to all glyphs. @@ -2150,12 +2178,17 @@ _cairo_surface_get_extents (cairo_surface_t *surface, * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale. */ cairo_status_t -_cairo_surface_show_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font) +_cairo_surface_show_text_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_bool_t backward, + cairo_scaled_font_t *scaled_font) { cairo_status_t status; cairo_scaled_font_t *dev_scaled_font = scaled_font; @@ -2166,7 +2199,7 @@ _cairo_surface_show_glyphs (cairo_surface_t *surface, if (surface->status) return surface->status; - if (!num_glyphs) + if (!num_glyphs && !utf8_len) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_copy_pattern_for_destination (source, @@ -2200,16 +2233,56 @@ _cairo_surface_show_glyphs (cairo_surface_t *surface, status = CAIRO_INT_STATUS_UNSUPPORTED; - if (surface->backend->show_glyphs) { - int remaining_glyphs = num_glyphs; - status = surface->backend->show_glyphs (surface, op, dev_source, - glyphs, num_glyphs, - dev_scaled_font, - &remaining_glyphs); - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; + if (clusters) { + /* A real show_text_glyphs call. Try show_text_glyphs backend + * method first */ + if (surface->backend->show_text_glyphs) { + status = surface->backend->show_text_glyphs (surface, op, dev_source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + backward, + dev_scaled_font); + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->backend->show_glyphs) { + int remaining_glyphs = num_glyphs; + status = surface->backend->show_glyphs (surface, op, dev_source, + glyphs, num_glyphs, + dev_scaled_font, + &remaining_glyphs); + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (remaining_glyphs == 0) + status = CAIRO_STATUS_SUCCESS; + } + } else { + /* A mere show_glyphs call. Try show_glyphs backend method first */ + if (surface->backend->show_glyphs) { + int remaining_glyphs = num_glyphs; + status = surface->backend->show_glyphs (surface, op, dev_source, + glyphs, num_glyphs, + dev_scaled_font, + &remaining_glyphs); + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (remaining_glyphs == 0) + status = CAIRO_STATUS_SUCCESS; + } else if (surface->backend->show_text_glyphs) { + /* Intentionally only try show_text_glyphs method for show_glyphs + * calls if backend does not have show_glyphs. If backend has + * both methods implemented, we don't fallback from show_glyphs to + * show_text_glyphs, and hence the backend an assume in its + * show_text_glyphs call that clusters is not NULL (which also + * implies that UTF-8 is not NULL, unless the text is + * zero-length). + */ + status = surface->backend->show_text_glyphs (surface, op, dev_source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + backward, + dev_scaled_font); + } } if (status == CAIRO_INT_STATUS_UNSUPPORTED) @@ -2581,6 +2654,7 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_USER_FONT_IMMUTABLE: case CAIRO_STATUS_USER_FONT_ERROR: case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: 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 afc076db..fe68f0a8 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -3109,15 +3109,19 @@ 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, - x, y, - &glyphs, &num_glyphs); + x, y, + &glyphs, &num_glyphs); if (status) goto BAIL; if (num_glyphs == 0) return; - status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, num_glyphs); + status = _cairo_gstate_show_text_glyphs (cr->gstate, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE); if (status) goto BAIL; @@ -3171,7 +3175,104 @@ cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) return; } - status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, num_glyphs); + status = _cairo_gstate_show_text_glyphs (cr->gstate, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE); + if (status) + _cairo_set_error (cr, status); +} + +cairo_bool_t +cairo_has_show_text_glyphs (cairo_t *cr) +{ + return _cairo_gstate_has_show_text_glyphs (cr->gstate); +} + +void +cairo_show_text_glyphs (cairo_t *cr, + 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; + + if (cr->status) + return; + + /* A slew of sanity checks */ + + /* A -1 for utf8_len means NUL-terminated */ + if (utf8_len == -1) + utf8_len = strlen (utf8); + + /* Apart from that, no negatives */ + if (num_glyphs < 0 || utf8_len < 0 || num_clusters < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + 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. */ + { + unsigned int n_bytes = 0; + unsigned int n_glyphs = 0; + int i; + + for (i = 0; i < num_clusters; i++) { + unsigned int cluster_bytes = clusters[i].num_bytes; + unsigned int cluster_glyphs = clusters[i].num_glyphs; + + /* A cluster should cover at least one byte or glyph. */ + if (cluster_bytes == 0 && cluster_glyphs == 0) + goto BAD; + + /* Since utf8_len and num_glyphs are signed, but the rest of + * values involved here unsigned, we can avoid overflow easily */ + if (cluster_bytes > (unsigned int)utf8_len || cluster_glyphs > (unsigned int)num_glyphs) + goto BAD; + 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; + } + } + + if (num_glyphs == 0 && utf8_len == 0) + return; + + status = _cairo_gstate_show_text_glyphs (cr->gstate, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + !!backward); if (status) _cairo_set_error (cr, status); } diff --git a/src/cairo.h b/src/cairo.h index 3954159e..e8c6daf5 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -207,6 +207,7 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8) * @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_t is used to indicate errors that can occur when * using Cairo. In some cases it is returned directly by functions. @@ -245,7 +246,8 @@ typedef enum _cairo_status { CAIRO_STATUS_FONT_TYPE_MISMATCH, CAIRO_STATUS_USER_FONT_IMMUTABLE, CAIRO_STATUS_USER_FONT_ERROR, - CAIRO_STATUS_NEGATIVE_COUNT + CAIRO_STATUS_NEGATIVE_COUNT, + CAIRO_STATUS_INVALID_CLUSTERS, /* after adding a new error: update CAIRO_STATUS_LAST_STATUS in cairoint.h */ } cairo_status_t; @@ -824,6 +826,11 @@ typedef struct { double y; } cairo_glyph_t; +typedef struct { + unsigned int num_bytes; + unsigned int num_glyphs; +} cairo_text_cluster_t; + /** * cairo_text_extents_t: * @x_bearing: the horizontal distance from the origin to the @@ -1124,6 +1131,19 @@ cairo_show_text (cairo_t *cr, const char *utf8); cairo_public void cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); +cairo_public cairo_bool_t +cairo_has_show_text_glyphs (cairo_t *cr); + +cairo_public void +cairo_show_text_glyphs (cairo_t *cr, + 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_public void cairo_text_path (cairo_t *cr, const char *utf8); diff --git a/src/cairoint.h b/src/cairoint.h index 9e5c38ca..50f5681f 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_NEGATIVE_COUNT +#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_INVALID_CLUSTERS /* Size in bytes of buffer to use off the stack per functions. @@ -751,6 +751,22 @@ struct _cairo_surface_backend { (*create_solid_pattern_surface) (void *surface, cairo_solid_pattern_t *solid_pattern); + + cairo_bool_t + (*has_show_text_glyphs) (void *surface); + + cairo_warn cairo_int_status_t + (*show_text_glyphs) (void *surface, + cairo_operator_t op, + cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_bool_t backward, + cairo_scaled_font_t *scaled_font); }; #include "cairo-surface-private.h" @@ -1214,10 +1230,18 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, int num_glyphs, cairo_text_extents_t *extents); +cairo_private cairo_bool_t +_cairo_gstate_has_show_text_glyphs (cairo_gstate_t *gstate); + cairo_private cairo_status_t -_cairo_gstate_show_glyphs (cairo_gstate_t *gstate, - const cairo_glyph_t *glyphs, - int num_glyphs); +_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, + 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_private cairo_status_t _cairo_gstate_glyph_path (cairo_gstate_t *gstate, @@ -1729,6 +1753,22 @@ _cairo_surface_show_glyphs (cairo_surface_t *surface, int num_glyphs, cairo_scaled_font_t *scaled_font); +cairo_private cairo_bool_t +_cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_surface_show_text_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_bool_t backward, + cairo_scaled_font_t *scaled_font); + cairo_private cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_t *pattern, |