Add cairo_show_text_glyphs API

New public API:

	cairo_text_cluster_t
	cairo_has_show_text_glyphs()
	cairo_show_text_glyphs()

Add accompanying gstate and surface functions, and surface backend methods.
No backends implement them just yet.
This commit is contained in:
Behdad Esfahbod 2008-06-26 16:15:12 -04:00
parent dff0dd0c63
commit 047566fd52
10 changed files with 336 additions and 42 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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