summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/public/cairo-sections.txt3
-rw-r--r--doc/public/tmpl/cairo-status.sgml1
-rw-r--r--doc/public/tmpl/cairo-text.sgml31
-rw-r--r--src/cairo-gstate.c38
-rw-r--r--src/cairo-misc.c2
-rw-r--r--src/cairo-scaled-font.c14
-rw-r--r--src/cairo-surface.c110
-rw-r--r--src/cairo.c109
-rw-r--r--src/cairo.h22
-rw-r--r--src/cairoint.h48
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,