Add cairo_scaled_font_text_to_glyphs()

And update user-font text_to_glyphs() method to match.

Currently disable the win32-font text_to_glyphs(), until that one
is updated.  Or better yet, remove it and implement ucs4_to_index().
It's the toy font API afterall.
This commit is contained in:
Behdad Esfahbod 2008-08-07 22:06:15 -04:00
parent b8fc845094
commit d9408041aa
8 changed files with 301 additions and 128 deletions

View file

@ -1445,12 +1445,16 @@ _cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
}
cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
const char *utf8,
double x,
double y,
cairo_glyph_t **glyphs,
int *num_glyphs)
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward)
{
cairo_status_t status;
@ -1458,8 +1462,11 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
if (status)
return status;
return _cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
utf8, glyphs, num_glyphs);
return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
backward);
}
cairo_status_t
@ -1536,7 +1543,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
if (num_glyphs <= ARRAY_LENGTH (stack_transformed_glyphs)) {
transformed_glyphs = stack_transformed_glyphs;
} else {
transformed_glyphs = _cairo_malloc_ab (num_glyphs, sizeof(cairo_glyph_t));
transformed_glyphs = cairo_glyph_allocate (num_glyphs);
if (transformed_glyphs == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
@ -1551,6 +1558,10 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
if (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
@ -1598,7 +1609,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
CLEANUP_GLYPHS:
if (transformed_glyphs != stack_transformed_glyphs)
free (transformed_glyphs);
cairo_glyph_free (transformed_glyphs);
return status;
}
@ -1620,7 +1631,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate,
if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs))
transformed_glyphs = stack_transformed_glyphs;
else
transformed_glyphs = _cairo_malloc_ab (num_glyphs, sizeof(cairo_glyph_t));
transformed_glyphs = cairo_glyph_allocate (num_glyphs);
if (transformed_glyphs == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@ -1634,7 +1645,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate,
CAIRO_MUTEX_UNLOCK (gstate->scaled_font->mutex);
if (transformed_glyphs != stack_transformed_glyphs)
free (transformed_glyphs);
cairo_glyph_free (transformed_glyphs);
return status;
}

View file

@ -1109,7 +1109,7 @@ cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font,
cairo_text_extents_t *extents)
{
cairo_status_t status;
cairo_glyph_t *glyphs;
cairo_glyph_t *glyphs = NULL;
int num_glyphs;
if (scaled_font->status)
@ -1118,7 +1118,11 @@ cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font,
if (utf8 == NULL)
goto ZERO_EXTENTS;
status = _cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0., utf8, &glyphs, &num_glyphs);
status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
utf8, -1,
&glyphs, &num_glyphs,
NULL, NULL,
NULL);
if (status)
goto ZERO_EXTENTS;
@ -1265,70 +1269,148 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
slim_hidden_def (cairo_scaled_font_glyph_extents);
cairo_status_t
_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
double x,
double y,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs)
cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward)
{
int i;
uint32_t *ucs4 = NULL;
int num_chars = 0;
const char *p;
cairo_status_t status;
cairo_scaled_glyph_t *scaled_glyph;
*num_glyphs = 0;
*glyphs = NULL;
cairo_glyph_t *orig_glyphs;
cairo_text_cluster_t *orig_clusters;
status = scaled_font->status;
if (status)
return status;
if (utf8[0] == '\0')
return CAIRO_STATUS_SUCCESS;
/* A slew of sanity checks */
/* glyphs and num_glyphs can't be NULL */
if (glyphs == NULL ||
num_glyphs == NULL) {
status = CAIRO_STATUS_NULL_POINTER;
goto BAIL;
}
/* No NULLs for non-NULLs! */
if ((utf8_len && utf8 == NULL) ||
(clusters && num_clusters == NULL) ||
(clusters && backward == NULL)) {
status = CAIRO_STATUS_NULL_POINTER;
goto BAIL;
}
/* A -1 for utf8_len means NUL-terminated */
if (utf8_len == -1)
utf8_len = strlen (utf8);
/* A NULL *glyphs means no prealloced glyphs array */
if (glyphs && *glyphs == NULL)
*num_glyphs = 0;
/* A NULL *clusters means no prealloced clusters array */
if (clusters && *clusters == NULL)
*num_clusters = 0;
if (!clusters && num_clusters) {
num_clusters = NULL;
}
if (backward) {
*backward = FALSE;
}
if (!clusters && backward) {
backward = NULL;
}
/* Apart from that, no negatives */
if (utf8_len < 0 ||
*num_glyphs < 0 ||
(num_clusters && *num_clusters < 0)) {
status = CAIRO_STATUS_NEGATIVE_COUNT;
goto BAIL;
}
if (utf8_len == 0) {
status = CAIRO_STATUS_SUCCESS;
goto BAIL;
}
/* validate input so backend does not have to */
status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars);
if (status)
goto BAIL;
CAIRO_MUTEX_LOCK (scaled_font->mutex);
_cairo_scaled_font_freeze_cache (scaled_font);
orig_glyphs = *glyphs;
orig_clusters = clusters ? *clusters : NULL;
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);
status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
backward);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto DONE;
}
status = _cairo_utf8_to_ucs4 (utf8, -1, &ucs4, num_glyphs);
if (status)
goto DONE;
if (*num_glyphs < num_chars) {
*glyphs = cairo_glyph_allocate (num_chars);
if (*glyphs == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
}
}
*num_glyphs = num_chars;
*glyphs = (cairo_glyph_t *) _cairo_malloc_ab ((*num_glyphs), sizeof (cairo_glyph_t));
if (*glyphs == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
if (clusters) {
if (*num_clusters < num_chars) {
*clusters = cairo_text_cluster_allocate (num_chars);
if (*clusters == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
}
}
*num_clusters = num_chars;
}
for (i = 0; i < *num_glyphs; i++) {
(*glyphs)[i].index = (*scaled_font->backend->
ucs4_to_index) (scaled_font, ucs4[i]);
p = utf8;
for (i = 0; i < num_chars; i++) {
int num_bytes;
uint32_t unicode;
cairo_scaled_glyph_t *scaled_glyph;
num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
p += num_bytes;
(*glyphs)[i].index = (*scaled_font->backend->ucs4_to_index) (scaled_font, unicode);
(*glyphs)[i].x = x;
(*glyphs)[i].y = y;
if (clusters) {
(*clusters)[i].num_bytes = num_bytes;
(*clusters)[i].num_glyphs = 1;
}
status = _cairo_scaled_glyph_lookup (scaled_font,
(*glyphs)[i].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (status) {
free (*glyphs);
*glyphs = NULL;
goto DONE;
}
@ -1336,14 +1418,37 @@ _cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
y += scaled_glyph->metrics.y_advance;
}
DONE:
DONE: /* error that should be logged on scaled_font happened */
_cairo_scaled_font_thaw_cache (scaled_font);
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
if (ucs4)
free (ucs4);
if (status) {
*num_glyphs = 0;
if (*glyphs != orig_glyphs) {
cairo_glyph_free (*glyphs);
*glyphs = orig_glyphs;
}
if (clusters) {
*num_clusters = 0;
if (*clusters != orig_clusters) {
cairo_text_cluster_free (*clusters);
*clusters = orig_clusters;
}
}
}
return _cairo_scaled_font_set_error (scaled_font, status);
BAIL: /* error with input arguments */
if (num_glyphs)
*num_glyphs = 0;
if (num_clusters)
*num_clusters = 0;
return status;
}
/*

View file

@ -265,12 +265,16 @@ _cairo_user_ucs4_to_index (void *abstract_font,
}
static cairo_int_status_t
_cairo_user_text_to_glyphs (void *abstract_font,
double x,
double y,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs)
_cairo_user_text_to_glyphs (void *abstract_font,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward)
{
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
@ -280,25 +284,21 @@ _cairo_user_text_to_glyphs (void *abstract_font,
if (face->scaled_font_methods.text_to_glyphs) {
int i;
int orig_num_glyphs = *num_glyphs;
*glyphs = NULL;
*num_glyphs = -1;
/* XXX currently user allocs glyphs array but cairo frees it */
status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base,
utf8, glyphs, num_glyphs);
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
backward);
if (status != CAIRO_STATUS_SUCCESS) {
status = _cairo_scaled_font_set_error (&scaled_font->base, status);
if (*glyphs) {
free (*glyphs);
*glyphs = NULL;
}
if (status != CAIRO_STATUS_SUCCESS)
return status;
}
if (*num_glyphs < 0)
if (*num_glyphs < 0) {
*num_glyphs = orig_num_glyphs;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* Convert from font space to user space and add x,y */
for (i = 0; i < *num_glyphs; i++) {

View file

@ -1780,7 +1780,7 @@ const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = {
_cairo_win32_scaled_font_create_toy,
_cairo_win32_scaled_font_fini,
_cairo_win32_scaled_font_glyph_init,
_cairo_win32_scaled_font_text_to_glyphs,
/* _cairo_win32_scaled_font_text_to_glyphs, FIXME */
NULL, /* ucs4_to_index */
_cairo_win32_scaled_font_show_glyphs,
_cairo_win32_scaled_font_load_truetype_table,

View file

@ -3013,16 +3013,18 @@ cairo_text_extents (cairo_t *cr,
cairo_get_current_point (cr, &x, &y);
status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
status = _cairo_gstate_text_to_glyphs (cr->gstate,
x, y,
&glyphs, &num_glyphs);
utf8, strlen (utf8),
&glyphs, &num_glyphs,
NULL, NULL,
NULL);
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_gstate_glyph_extents (cr->gstate,
glyphs, num_glyphs,
extents);
if (glyphs)
free (glyphs);
cairo_glyph_free (glyphs);
if (status)
_cairo_set_error (cr, status);
@ -3116,7 +3118,9 @@ cairo_show_text (cairo_t *cr, const char *utf8)
cairo_text_extents_t extents;
cairo_status_t status;
cairo_glyph_t *glyphs = NULL, *last_glyph;
int num_glyphs;
cairo_text_cluster_t *clusters = NULL;
int utf8_len, num_glyphs, num_clusters;
cairo_bool_t backward;
double x, y;
if (cr->status)
@ -3127,9 +3131,14 @@ 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,
utf8_len = strlen (utf8);
status = _cairo_gstate_text_to_glyphs (cr->gstate,
x, y,
&glyphs, &num_glyphs);
utf8, utf8_len,
&glyphs, &num_glyphs,
cairo_has_show_text_glyphs (cr) ? &clusters : NULL, &num_clusters,
&backward);
if (status)
goto BAIL;
@ -3137,10 +3146,10 @@ cairo_show_text (cairo_t *cr, const char *utf8)
return;
status = _cairo_gstate_show_text_glyphs (cr->gstate,
NULL, 0,
utf8, utf8_len,
glyphs, num_glyphs,
NULL, 0,
FALSE);
clusters, num_clusters,
backward);
if (status)
goto BAIL;
@ -3156,8 +3165,8 @@ cairo_show_text (cairo_t *cr, const char *utf8)
cairo_move_to (cr, x, y);
BAIL:
if (glyphs)
free (glyphs);
cairo_glyph_free (glyphs);
cairo_text_cluster_free (clusters);
if (status)
_cairo_set_error (cr, status);
@ -3226,6 +3235,14 @@ cairo_show_text_glyphs (cairo_t *cr,
/* A slew of sanity checks */
/* 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;
}
/* A -1 for utf8_len means NUL-terminated */
if (utf8_len == -1)
utf8_len = strlen (utf8);
@ -3236,14 +3253,6 @@ cairo_show_text_glyphs (cairo_t *cr,
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. */
{
@ -3343,9 +3352,12 @@ cairo_text_path (cairo_t *cr, const char *utf8)
cairo_get_current_point (cr, &x, &y);
status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
status = _cairo_gstate_text_to_glyphs (cr->gstate,
x, y,
&glyphs, &num_glyphs);
utf8, strlen (utf8),
&glyphs, &num_glyphs,
NULL, NULL,
NULL);
if (status)
goto BAIL;
@ -3373,8 +3385,7 @@ cairo_text_path (cairo_t *cr, const char *utf8)
cairo_move_to (cr, x, y);
BAIL:
if (glyphs)
free (glyphs);
cairo_glyph_free (glyphs);
if (status)
_cairo_set_error (cr, status);

View file

@ -1330,6 +1330,18 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
int num_glyphs,
cairo_text_extents_t *extents);
cairo_public cairo_status_t
cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward);
cairo_public cairo_font_face_t *
cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font);
@ -1468,6 +1480,7 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal
* @glyphs: output array of glyphs, in font space
* @num_glyphs: number of output glyphs
*
* XXXXXXXXXXXXX
* #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which
* is called to convert input text to an array of glyphs. This is used by the
* cairo_show_text() operation.
@ -1496,9 +1509,13 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal
* Since: 1.8
**/
typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs);
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward);
/**
* cairo_user_scaled_font_unicode_to_glyph_func_t:

View file

@ -408,12 +408,16 @@ struct _cairo_scaled_font_backend {
* then just converting characters one by one.
*/
cairo_warn cairo_int_status_t
(*text_to_glyphs) (void *scaled_font,
double x,
double y,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs);
(*text_to_glyphs) (void *scaled_font,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward);
unsigned long
(*ucs4_to_index) (void *scaled_font,
@ -1219,12 +1223,16 @@ _cairo_gstate_set_font_face (cairo_gstate_t *gstate,
cairo_font_face_t *font_face);
cairo_private cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *font,
const char *utf8,
double x,
double y,
cairo_glyph_t **glyphs,
int *num_glyphs);
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward);
cairo_private cairo_status_t
_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
@ -1528,14 +1536,6 @@ cairo_private cairo_status_t
_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *extents);
cairo_private cairo_status_t
_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
double x,
double y,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyphs,

View file

@ -51,7 +51,7 @@ cairo_test_t test = {
draw
};
static cairo_user_data_key_t fallback_font_face_key;
static cairo_user_data_key_t fallback_font_key;
static cairo_status_t
test_scaled_font_init (cairo_scaled_font_t *scaled_font,
@ -60,7 +60,12 @@ test_scaled_font_init (cairo_scaled_font_t *scaled_font,
{
cairo_set_font_face (cr,
cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
&fallback_font_face_key));
&fallback_font_key));
cairo_scaled_font_set_user_data (scaled_font,
&fallback_font_key,
cairo_scaled_font_reference (cairo_get_scaled_font (cr)),
(cairo_destroy_func_t) cairo_scaled_font_destroy);
cairo_font_extents (cr, extents);
@ -73,21 +78,44 @@ test_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font,
cairo_t *cr,
cairo_text_extents_t *extents)
{
char text[2] = "\0";
cairo_glyph_t cairo_glyph;
/* XXX only works for ASCII. need ucs4_to_utf8 :( */
text[0] = glyph;
cairo_glyph.index = glyph;
cairo_glyph.x = 0;
cairo_glyph.y = 0;
cairo_set_font_face (cr,
cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
&fallback_font_face_key));
&fallback_font_key));
cairo_show_text (cr, text);
cairo_text_extents (cr, text, extents);
cairo_show_glyphs (cr, &cairo_glyph, 1);
cairo_glyph_extents (cr, &cairo_glyph, 1, extents);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
test_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_bool_t *backward)
{
cairo_scaled_font_t *fallback_scaled_font;
fallback_scaled_font = cairo_scaled_font_get_user_data (scaled_font,
&fallback_font_key);
return cairo_scaled_font_text_to_glyphs (fallback_scaled_font, 0, 0,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
backward);
}
static cairo_font_face_t *user_font_face = NULL;
static cairo_font_face_t *
@ -99,6 +127,7 @@ get_user_font_face (void)
user_font_face = cairo_user_font_face_create ();
cairo_user_font_face_set_init_func (user_font_face, test_scaled_font_init);
cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph);
cairo_user_font_face_set_text_to_glyphs_func (user_font_face, test_scaled_font_text_to_glyphs);
/* This also happens to be default font face on cairo_t, so does
* not make much sense here. For demonstration only.
@ -108,9 +137,9 @@ get_user_font_face (void)
CAIRO_FONT_WEIGHT_NORMAL);
cairo_font_face_set_user_data (user_font_face,
&fallback_font_face_key,
&fallback_font_key,
fallback_font_face,
cairo_font_face_destroy);
(cairo_destroy_func_t) cairo_font_face_destroy);
}
return user_font_face;