From 62cc53f9b806eedbc361e807fb5f523bf953c52a Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Sat, 14 Aug 2021 06:06:19 +0000 Subject: [PATCH] Add color user-font support Adds new API cairo_user_font_face_set_render_color_glyph_func() to set a color glyph renderer. --- src/cairo-scaled-font-private.h | 2 + src/cairo-surface.c | 10 +- src/cairo-user-font.c | 216 +++++++++++---- src/cairo.h | 31 ++- src/cairoint.h | 1 + test/Makefile.sources | 1 + test/meson.build | 1 + .../reference/user-font-color.image16.ref.png | Bin 0 -> 3496 bytes test/reference/user-font-color.pdf.ref.png | Bin 0 -> 4156 bytes test/reference/user-font-color.ps.ref.png | Bin 0 -> 3924 bytes test/reference/user-font-color.quartz.ref.png | Bin 0 -> 4061 bytes .../user-font-color.recording.xfail.png | Bin 0 -> 3040 bytes test/reference/user-font-color.ref.png | Bin 0 -> 4155 bytes .../user-font-color.script.xfail.png | Bin 0 -> 1964 bytes test/reference/user-font-color.svg.ref.png | Bin 0 -> 4152 bytes .../user-font-color.svg.rgb24.xfail.png | Bin 0 -> 3040 bytes test/user-font-color.c | 251 ++++++++++++++++++ 17 files changed, 460 insertions(+), 53 deletions(-) create mode 100644 test/reference/user-font-color.image16.ref.png create mode 100644 test/reference/user-font-color.pdf.ref.png create mode 100644 test/reference/user-font-color.ps.ref.png create mode 100644 test/reference/user-font-color.quartz.ref.png create mode 100644 test/reference/user-font-color.recording.xfail.png create mode 100644 test/reference/user-font-color.ref.png create mode 100644 test/reference/user-font-color.script.xfail.png create mode 100644 test/reference/user-font-color.svg.ref.png create mode 100644 test/reference/user-font-color.svg.rgb24.xfail.png create mode 100644 test/user-font-color.c diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h index 317d21197..4bacb1a01 100644 --- a/src/cairo-scaled-font-private.h +++ b/src/cairo-scaled-font-private.h @@ -146,6 +146,8 @@ struct _cairo_scaled_glyph { const void *dev_private_key; void *dev_private; cairo_list_t dev_privates; + + cairo_bool_t has_color; }; struct _cairo_scaled_glyph_private { diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 45296fccd..3029e2cf0 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -2614,8 +2614,16 @@ ensure_scaled_glyph (cairo_scaled_font_t *scaled_font, if (*scaled_glyph == NULL || _cairo_scaled_glyph_index (*scaled_glyph) != glyph->index) { status = _cairo_scaled_glyph_lookup (scaled_font, glyph->index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, scaled_glyph); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* If the color surface not available, ensure scaled_glyph is not NULL. */ + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph->index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + scaled_glyph); + } + if (unlikely (status)) status = _cairo_scaled_font_set_error (scaled_font, status); diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index a26fb57ee..9b8872db5 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2006, 2008 Red Hat, Inc @@ -44,7 +45,7 @@ * SECTION:cairo-user-fonts * @Title:User Fonts * @Short_Description: Font support with font data provided by the user - * + * * The user-font feature allows the cairo user to provide drawings for glyphs * in a font. This is most useful in implementing fonts in non-standard * formats, like SVG fonts and Flash fonts, but can also be used by games and @@ -64,6 +65,7 @@ typedef struct _cairo_user_scaled_font_methods { cairo_user_scaled_font_init_func_t init; + cairo_user_scaled_font_render_glyph_func_t render_color_glyph; cairo_user_scaled_font_render_glyph_func_t render_glyph; cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph; cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs; @@ -75,7 +77,7 @@ typedef struct _cairo_user_font_face { /* Set to true after first scaled font is created. At that point, * the scaled_font_methods cannot change anymore. */ cairo_bool_t immutable; - + cairo_bool_t has_color; cairo_user_scaled_font_methods_t scaled_font_methods; } cairo_user_font_face_t; @@ -98,13 +100,18 @@ typedef struct _cairo_user_scaled_font { /* #cairo_user_scaled_font_t */ static cairo_surface_t * -_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font) +_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font, + cairo_bool_t color) { cairo_content_t content; - content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? - CAIRO_CONTENT_COLOR_ALPHA : - CAIRO_CONTENT_ALPHA; + if (color) { + content = CAIRO_CONTENT_COLOR_ALPHA; + } else { + content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? + CAIRO_CONTENT_COLOR_ALPHA : + CAIRO_CONTENT_ALPHA; + } return cairo_recording_surface_create (content, NULL); } @@ -112,7 +119,8 @@ _cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t static cairo_t * _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font, - cairo_surface_t *recording_surface) + cairo_surface_t *recording_surface, + cairo_bool_t color) { cairo_t *cr; @@ -127,7 +135,8 @@ _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t cairo_set_font_size (cr, 1.0); cairo_set_font_options (cr, &scaled_font->base.options); - cairo_set_source_rgb (cr, 1., 1., 1.); + if (!color) + cairo_set_source_rgb (cr, 1., 1., 1.); return cr; } @@ -147,33 +156,57 @@ _cairo_user_scaled_glyph_init (void *abstract_font, cairo_text_extents_t extents = scaled_font->default_glyph_extents; cairo_t *cr; - if (!face->scaled_font_methods.render_glyph) + recording_surface = NULL; + if (!face->scaled_font_methods.render_color_glyph && !face->scaled_font_methods.render_glyph) return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font); - /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */ - if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { - cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface); - status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, - _cairo_scaled_glyph_index(scaled_glyph), - cr, &extents); - if (status == CAIRO_INT_STATUS_SUCCESS) - status = cairo_status (cr); + if (_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE); + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface); + } else { + status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - cairo_destroy (cr); + if (face->scaled_font_methods.render_color_glyph) { + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE); - if (unlikely (status)) { - cairo_surface_destroy (recording_surface); - return status; + cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, TRUE); + status = face->scaled_font_methods.render_color_glyph ((cairo_scaled_font_t *)scaled_font, + _cairo_scaled_glyph_index(scaled_glyph), + cr, &extents); + if (status == CAIRO_STATUS_SUCCESS) { + status = cairo_status (cr); + scaled_glyph->has_color = TRUE; + } } + + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED && + face->scaled_font_methods.render_glyph) { + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE); + + cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, FALSE); + status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, + _cairo_scaled_glyph_index(scaled_glyph), + cr, &extents); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = cairo_status (cr); + + cairo_destroy (cr); + } + + if (status != CAIRO_STATUS_SUCCESS) { + if (recording_surface) + cairo_surface_destroy (recording_surface); + return status; + } + + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface); } - _cairo_scaled_glyph_set_recording_surface (scaled_glyph, - &scaled_font->base, - recording_surface); - - /* set metrics */ if (extents.width == 0.) { @@ -210,7 +243,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, &extents); } - if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + if (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) { cairo_surface_t *surface; cairo_format_t format; int width, height; @@ -226,16 +259,26 @@ _cairo_user_scaled_glyph_init (void *abstract_font, height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); - switch (scaled_font->base.options.antialias) { - default: - case CAIRO_ANTIALIAS_DEFAULT: - case CAIRO_ANTIALIAS_FAST: - case CAIRO_ANTIALIAS_GOOD: - case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break; - case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; - case CAIRO_ANTIALIAS_BEST: - case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break; - } + if (scaled_glyph->has_color) { + format = CAIRO_FORMAT_ARGB32; + } else { + switch (scaled_font->base.options.antialias) { + default: + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_GRAY: + format = CAIRO_FORMAT_A8; + break; + case CAIRO_ANTIALIAS_NONE: + format = CAIRO_FORMAT_A1; + break; + case CAIRO_ANTIALIAS_BEST: + case CAIRO_ANTIALIAS_SUBPIXEL: + format = CAIRO_FORMAT_ARGB32; + break; + } + } surface = cairo_image_surface_create (format, width, height); cairo_surface_set_device_offset (surface, @@ -248,9 +291,17 @@ _cairo_user_scaled_glyph_init (void *abstract_font, return status; } - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - (cairo_image_surface_t *) surface); + if (!scaled_glyph->has_color && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) { + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) surface); + } + + if (scaled_glyph->has_color && (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) { + _cairo_scaled_glyph_set_color_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *)surface); + } } if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { @@ -303,6 +354,16 @@ not_implemented: return glyph; } +static cairo_bool_t +_cairo_user_has_color_glyphs (void *abstract_font) +{ + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + + return face->has_color; +} + static cairo_int_status_t _cairo_user_text_to_glyphs (void *abstract_font, double x, @@ -381,9 +442,12 @@ static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { _cairo_user_scaled_glyph_init, _cairo_user_text_to_glyphs, _cairo_user_ucs4_to_index, - NULL, /* show_glyphs */ NULL, /* load_truetype_table */ - NULL /* index_to_ucs4 */ + NULL, /* index_to_ucs4 */ + NULL, /* is_synthetic */ + NULL, /* index_to_glyph_name */ + NULL, /* load_type1_data */ + _cairo_user_has_color_glyphs, }; /* #cairo_user_font_face_t */ @@ -462,8 +526,8 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ cairo_surface_t *recording_surface; cairo_t *cr; - recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font); - cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface); + recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE); + cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface, FALSE); cairo_surface_destroy (recording_surface); status = font_face->scaled_font_methods.init (&user_scaled_font->base, @@ -553,6 +617,7 @@ cairo_user_font_face_create (void) _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend); font_face->immutable = FALSE; + font_face->has_color = FALSE; memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods)); return &font_face->base; @@ -600,6 +665,57 @@ cairo_user_font_face_set_init_func (cairo_font_face_t *font_fac } slim_hidden_def(cairo_user_font_face_set_init_func); +/** + * cairo_user_font_face_set_render_color_glyph_func: + * @font_face: A user font face + * @render_glyph_func: The render_glyph callback, or %NULL + * + * Sets the color glyph rendering function of a user-font. + * See #cairo_user_scaled_font_render_glyph_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * The render_glyph callback is the only mandatory callback of a + * user-font. At least one of + * cairo_user_font_face_set_render_color_glyph_func() or + * cairo_user_font_face_set_render_glyph_func() must be called to set + * a render callback. If both callbacks are set, the color glyph + * render callback is invoked first. If the color glyph render + * callback returns %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the + * non-color version of the callback is invoked. + * + * If the callback is %NULL and a glyph is tried to be rendered using + * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur. + * + * Since: 1.18 + **/ +void +cairo_user_font_face_set_render_color_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.render_color_glyph = render_glyph_func; + user_font_face->has_color = render_glyph_func ? TRUE : FALSE; +} +slim_hidden_def(cairo_user_font_face_set_render_color_glyph_func); + /** * cairo_user_font_face_set_render_glyph_func: * @font_face: A user font face @@ -613,7 +729,15 @@ slim_hidden_def(cairo_user_font_face_set_init_func); * error will occur. A user font-face is immutable as soon as a scaled-font * is created from it. * - * The render_glyph callback is the only mandatory callback of a user-font. + * The render_glyph callback is the only mandatory callback of a + * user-font. At least one of + * cairo_user_font_face_set_render_color_glyph_func() or + * cairo_user_font_face_set_render_glyph_func() must be called to set + * a render callback. If both callbacks are set, the color glyph + * render callback is invoked first. If the color glyph render + * callback returns %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the + * non-color version of the callback is invoked. + * * If the callback is %NULL and a glyph is tried to be rendered using * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur. * diff --git a/src/cairo.h b/src/cairo.h index 96427b425..8533d44ba 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1749,10 +1749,16 @@ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_ * font space. That is, the matrix set on @cr is the scale matrix of @scaled_font, * The @extents argument is where the user font sets the font extents for * @scaled_font. However, if user prefers to draw in user space, they can - * achieve that by changing the matrix on @cr. All cairo rendering operations - * to @cr are permitted, however, the result is undefined if any source other - * than the default source on @cr is used. That means, glyph bitmaps should - * be rendered using cairo_mask() instead of cairo_paint(). + * achieve that by changing the matrix on @cr. + * + * All cairo rendering operations to @cr are permitted. However, when + * this callback set with + * cairo_user_font_face_set_render_glyph_func(), the result is + * undefined if any source other than the default source on @cr is + * used. That means, glyph bitmaps should be rendered using + * cairo_mask() instead of cairo_paint(). When this callback set with + * cairo_user_font_face_set_render_color_glyph_func(), setting the + * source is a valid operation. * * Other non-default settings on @cr include a font size of 1.0 (given that * it is set up to be in font space), and font options corresponding to @@ -1772,8 +1778,17 @@ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_ * extents, it must be ink extents, and include the extents of all drawing * done to @cr in the callback. * - * Returns: %CAIRO_STATUS_SUCCESS upon success, or - * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * Where both color and non-color callbacks has been set using + * cairo_user_font_face_set_render_color_glyph_func(), and + * cairo_user_font_face_set_render_glyph_func(), the color glyph + * callback may return %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if the + * glyph is not a color glyph. This is the only case in which the + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED may be returned from a + * render callback. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. * * Since: 1.8 **/ @@ -1908,6 +1923,10 @@ cairo_public void cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, cairo_user_scaled_font_render_glyph_func_t render_glyph_func); +cairo_public void +cairo_user_font_face_set_render_color_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func); + cairo_public void cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func); diff --git a/src/cairoint.h b/src/cairoint.h index 64ed2aa4d..23b1d9c25 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -2069,6 +2069,7 @@ slim_hidden_proto (cairo_translate); slim_hidden_proto (cairo_transform); slim_hidden_proto (cairo_user_font_face_create); slim_hidden_proto (cairo_user_font_face_set_init_func); +slim_hidden_proto (cairo_user_font_face_set_render_color_glyph_func); slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); slim_hidden_proto (cairo_device_to_user); diff --git a/test/Makefile.sources b/test/Makefile.sources index b887b054d..007cb6945 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -386,6 +386,7 @@ test_sources = \ unclosed-strokes.c \ user-data.c \ user-font.c \ + user-font-color.c \ user-font-mask.c \ user-font-proxy.c \ user-font-rescale.c \ diff --git a/test/meson.build b/test/meson.build index 9d84d048d..c79357c81 100644 --- a/test/meson.build +++ b/test/meson.build @@ -386,6 +386,7 @@ test_sources = [ 'unclosed-strokes.c', 'user-data.c', 'user-font.c', + 'user-font-color.c', 'user-font-mask.c', 'user-font-proxy.c', 'user-font-rescale.c', diff --git a/test/reference/user-font-color.image16.ref.png b/test/reference/user-font-color.image16.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..8b29f0b38f45b49f1aa586cdd6f565c99f7961df GIT binary patch literal 3496 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 literal 0 HcmV?d00001 diff --git a/test/reference/user-font-color.pdf.ref.png b/test/reference/user-font-color.pdf.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..849614f0d5764c04842162597729e012a1416434 GIT binary patch literal 4156 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 A(f|Me literal 0 HcmV?d00001 diff --git a/test/reference/user-font-color.recording.xfail.png b/test/reference/user-font-color.recording.xfail.png new file mode 100644 index 0000000000000000000000000000000000000000..37b8aaa1e3079cd29a9afc515bf9b0d8cc0d7d90 GIT binary patch literal 3040 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 EADWgIjsO4v literal 0 HcmV?d00001 diff --git a/test/reference/user-font-color.script.xfail.png b/test/reference/user-font-color.script.xfail.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1f7ddc67287e6b3e4993bd03e0bea1bb751e69 GIT binary patch literal 1964 zcmah~e>Bq#7ylwDDy4{4Q$xkfNc7B)R218?N}EY%D0BUzt_Js}VDrxbPk4&Z3hoIfvfVJ;j-dR%!09wIFx8oOZrGk+&Sq8E_ zePVVrSl@G3&hcu}j@IO@($^W5W~&H8oRwY^Y==L$%PaZ1F8I~)?WQSD&Yt-vrShVg zw33?j2z1;Jq=A?V_SAVtL7CWt)roGE9c)|4z@qhX9eZAK^(w!_g&oaThTy&lRPc&{ zR6ZBYkg(SsFDK;WSE=a$9yvnZ_0md506=cV0(EXUwI;oB%`Q~NP5`>?M3l#AANod;PdttaM{O$#mQ-JcIyww;9^mJ z&)N2{EBwrr8SmUwlX!ab<9%IsfFfg1forZrwSF`X#e&opjO$IN$@J0~c?J{uhA4MTWW<3v=66D&mgl zmLKxk(kM56=}!@<|F)^)&VRdaDLS^0J+F~&mBBj(>o=jNpSv6Lg5i|fPxce|0w$TF zuX=Skux=egWCjm4@O zZQwv9yDtUOZe!#Um(UdykL|uWxISB{JmI#{9h;ZnglaRE8wC^#O9I}4l14gH+NQ05 zh=0G23cdT@j}$=K1i!TCy`K&-;FAkkEGT_-AAVb8e;LyM>D)%hbDa1yEp`e5OU5eB z^>)^_YG?S?b*CoAgyAD6+PGv+QC#!f`pP8H*oy~_i4d5=bfVz`cBls$70ifzMmmG* zv=q#`$oyx@1PQHnQEpEYg`YzEa+mHei^h9cWH?;e<*9qf=aUHKQ!G=Q9vU(i%-K@+ z!Gz^>NF?hiDC-fnkvF?UX$HFx=xJ-KP0k2}ob$~cA*E)c6r&YaH_-ymH$UV=|LSoE zLeLCqx5UGcy9x<@ZpK?nrjl;z8Z#1wlqwCaEv3#Wtqet4!!e#R#yoSKxreIf&vjtO+=xaetijF7P1==}H;&TO4 zUbuHtvE0ULPee3WTzV9*7$~tVXpI_iaCGbl({U$C&K8MKRO?Y2{z8DAvt0nJU7pFq z&%g_+;<^4@bW#-PNgyp|@|$>~z&`3DPN`T5>`2fbdHDDs8kc0Q&v)+k9;+RHJQL3G z?(sHZ4F!-JMT18d`cLPSA+{?%XKx(QFQJXkCuc`@06}O{YYQT`N7K4%wKN{q|{{Cm9zq* zLVlH?ph{LXI{B0X{!AfllQkZkZ9cj{3lO1amZUbM^V|;T)Ru4e_UfDG(G^7B73*xO zfzAE(FH7JxPYyfZu0Q$KAoekjc@WRVG`JWP2V{K)&Adv8QMN*9zx^vCk@R|uH7SuX z zfAh%mNYqC|5D41t{?@xvlpWif`tS@f;%$0FMXjc8{BCzz5*EEd~zV(BY9@vWd{bYX?{@MLKuuITbf0 z|D{cN!Yd5n9b)yMd*zKZqWI_FWuz-_4Z8?0hGR&E;2PCbElnD)s75tt!+VpeI^25Z zfw;}S#X%VA;0$t~Ormc6y|jE)X$U&yFonoV%-Ct2TyT?oD>hzm_fbgwiYwkQLLTHK z(;B5}QpEnbLL;WP#CakmSBN06#Lj)P)2z)P49k`=`^>3k(gDU62Tss(jaR1~C=q^9 z^^^Ci6Po!+z0(JtC_+1nc@=$%P>-h7=OHpJ0~EGNt7*TsbO?E9VORY`4q##4ucQOz z*Uj+BThpKVANhrRA0M;VF_oFw^ZWn1_| literal 0 HcmV?d00001 diff --git a/test/reference/user-font-color.svg.ref.png b/test/reference/user-font-color.svg.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..9e9bf7d58d756fb6003430cf406aaab3748e023a GIT binary patch literal 4152 zcmaJ_cRX8f`;Hn#?N-&QQKe#~Mp0Dk5uIv?wDycm zZ3%)JB`GnJH~oHozdzsiKA+Ed&bXg*p69vGb>G)@;_jR1F*EWo0ssJJ1AT3C0Dvl$ za%@R|fwBiGSraJ@oeM-y8$kZM<+qfk0068z2HF~yf!RAbA-N04t^a8Ws??`v z&!o~~VsweDmS}hd;uv+wsd&%WY)i*{%MNalW;Ijpe>I@Q&G}NPTmAe(xTL?P!SjnY zLNrDX=olENbs}r2W5H4Va%J*H*}IOXxEL zGCoIUJ$p)WAS&=N4*(oRM;|4^40YTU+}s633F^z{qu6Nr4DUnfqug%ZlA+P~#*xYu zC1}gge@P?h3(Xo_P~SQVugNANG3pz`7bQ4D|L9*=$pN2aO;h zdc^zx_B;qyf#-29ky_(I9Ievjb{Rx67XYBTTzOvQW-2df?MRyY9Q}&cNp?X+pq>SHP(_r{o8;3HmvPIrO?B)vS(39~|z5 zUnCX4(19`96m`RLNkH-@pT!2~X}B1Yv}PQ|tcd^Z8q60G*jf z((Zxnqu3#(b#1Bb!X!f?@wH&DQVh(BZRgwO6=Ab)?m{-S$HGVjo$Z5{G-TgJ^bYiT9Ma#G z2Ti`Y_2hneaZRyqY!thMcGq{yfkf{22@f)Yg!FOjY(WP&b5lkFa1xOKwscMf6mF78 zY&6_P;8k~=?7uh6XVMIRNE%k+;DX%xl*_As{mtOE@&zDWHNHCm0b!w=V?WeMRkejH z66lb2%p4=5;hT7YT&1UpHl<+IqKb-&s;d3fz7!iIbZ2+>x=O&d!;1wgYwN?_8}E+P z=BOKQQYEMNqvpAAg*ws(JIRGQEoDJD%7nHy@_E+bp{eV=kRJYUQs?DK^`jq>L@`ZO zObc;wuF_cx0vQ-7okBW>FNG4lW@|QoTof=0?Mb?&k!*9FPy~|UrkYxpwA#aKL%qCC z3voPZ!7flJyU>?v!WJ6s1ITYV4>TS!O_T9K5zu)J>Ri~|R~K1}j>-%4w=YvuQ^$mLMFoYS92La{HU35#ZKy8GK@rG@l@q28XZK+|%qlU?#2=j# z)gA0kY#JL~3*Utk7d(uO+s?4@!RU}*zkr|1(Pli)!tZw8OsMMb?_V28Pk;X&-htUz zSs%`GYzg*vaBwg+9scG%fX-Gf+zRG7RYoxM;`85;U@>CwAPK}jI-HN#UCi(@uB35y zR^w{>X~K#sY;sCkIW(9TAPGI2%aluRRGq%>8d*bd>g8C}ESXO2zeo&&d5dgNYT;{xq^jp7&*Mgb^ zk&b^#eE9tN9BP&$-YLfgc$`)DXo%+~vb(!G5U+XG?HXk4+Y}V7hsP=perQ)MwkD4X z3+21yiA!deZv~Ychoatuy_sov>N?n?mx$t!bM0o*j7`ON#$Ddr9q7F&s30TvI@mnp zH>8%n>K2t|g!!HcR838>^{4>z!6)%yb3ki-y;khv20)LV8sNQnmSVH6Iq!FjU)!0h z7f=iS(+JozB7$Gb`CSU2LbYUGc>28EQau zlCmFeFk890va&Ki-)Z8LS@}@BOlz;Qsi&geU8-a*Rtq0SLi7W6FRIb#ks=zQ`7jeM zX?bzKw>%2YeTZ3u6c;0wyS~eg4ms5OiBk4;xBM1Di3-9vldD&Yi7F%~}0%?V(xe}h>8cv3i4WRGN9fYj7uD1jyQIG)F02DHR2wB9y+XF0{M=Dy`>hmm z_=OQ`oUi_T<33~at4>itJcl#Rz!}PzbfIXXXd=3@2p}G3WLd=zB}RXYT3ke*o$YJ! z@~NHh)~|z69rDJU58J}j6ct}QexB6%zIT2mZj)S+*Qx_`WW}Oz5;`a>;gnBHCbn!1LFH4)C!z$;xvpZDS>y`0{K& zd}30q>w$DJMhU5FZX6WU$Y@<*R~tmYZRABD9h&R@nJlMRm1~dr8+*%=%m^l4_t={j z8DiRY)Qkl42sS~uC|tByw1K~QdvP#xfVB5|8u?K9QPz^rT;22G+;B-rlpcL`%elq) zHeCcBpS93T#^8paGWVD|rH1dFKJoWI*k9>sZfZ)CafDh~S=rgmj*kEw) z$fN?Dcn(u4tus-j%yiCgdhx)PZx&K5j^)L=iOC-ebUv{;dDv?f+kH1S_I2@q#)ujT z8?`gnz#$K1DSh(1nJ2eP?iC#E zMe&^{5Z@t_9}O4Z3=a#mxpa}@xrjL`#L>6BURk=jZ<}1PodTvgP5RRWz;d71797N| z?+$vl+MAs5Za7zMZuC@j54^2XETDq>%|B`}e6+tp++7Gc+QU%Nkxbhe;`SZq_Gn5l zq#jg)nXj=5+P)M)>)x~Dg)!^qkD%^4JTj)+ys}sgrGIa=3Y+M8vSSnbGE*? zE7}dYz~l&sqBmc|bC{A;Gjm6qw_wNWugT9HQhS(ZGIG zL=I=r=m)wkKXGzvUESK850z8c)BSES)mael{>5>52_VoU=PoUPRsGD@kpw@|1tX)#CI6b@s?<0rve3J(gYQTpy^nN^lH-6n9?GIaYIAYou&&dSy{DJ2EVBA6I> zFll2|uHZf2K;ZyauUsi=*1HiE(F?K55`!N8iCg9|cx}pLVn8oz~ zlAeZrpEUDiu5M>7-H|RLJ3BijCPv(%dT`N?mbkIehZd|T?C9-S>s zJF2(1zNXEmLRmmbBSi+{Dhdc1y+pnsT&71+YF0_h`Qe-&mW;W!&nFdE%kWFVdRrpc zVSD<^oWS}JR`vS27q%+cE4Stojj$vz(lFjI{*At%l;}-PV5O^Q@i}Llzj(&|NH_Bh z&0pp}_r(s%emb>!4Q8}GY{buO_P_}n9ZxPIN|Sn5uX8o zN-C#=?;e+-qOlhPbAu`fC(N(ml@IdD!9lJfpdCW+kmk+*_(hHmWI+q=s^q@?W(&wR2D>E zjWbl4*l?YyVW!q6(Iqg_j%oPp*gO-!-QC@1cZT}oNRoAZlaa4;oyrAs0=9(02dpsW zk@A^4GUQ8#>PwA&OI`5*P|K%JyB_M}X?YQ4-B z#1PVyHrI}-0HrW?u%x!X+&M$CUjK6Z#{$rxrXD_RVY(1w5iR@SU~9VVbhjCB-_mko zWCTQKGf)~A(HsBay#CuJ*N3{c6LBMD30XUN82KX>?4{$<(PIjOcy48do&p5Zt763R zvJ-4HA65HrO;LEv1+cfbx0aR`3dwtmu!2;#9Z zui9TO?fL3EL$7J`n8D3ppfwqCu=I_CSy!8{e9g~pY7B6ypRo5!_&uhK00uLdaG~uV zaJ+CsW+>(VK4DNXh#X8>iM#w-*>bR_+?TGBQUNG{V=i^~a;{j);l@yiMSBq?Cl&v& zOqM_dP+kH%QX1ZR^@fqW$hV)3@h;?<>A1d65iRjV4hjz)b1O*ylnvoA`ZIfM)6}L; zqd|8$XVAp(J0L{N;yMzFQWS`C zT@&P^7nO%|Y! z5)>E8Jf~=2jWL#8Xaq&=BId&=3b%m{Df7aKBAWhR{{C-u`>$GFB~$6{Ja*sYmD-{F P3IG`Bm}r01bc*^9qsR-{ literal 0 HcmV?d00001 diff --git a/test/reference/user-font-color.svg.rgb24.xfail.png b/test/reference/user-font-color.svg.rgb24.xfail.png new file mode 100644 index 0000000000000000000000000000000000000000..37b8aaa1e3079cd29a9afc515bf9b0d8cc0d7d90 GIT binary patch literal 3040 zcmaJ@XH=8R7X3si5@`Yg0SVFtLy#^Yh$bL{G-(pLfb=F{=q(B&a3zr{{Q}Z^=n@DT zqzDR9q=N`3RYSRoGp8FaFnp&Y|0<@G+rRZLAWN~HO4&2Csw3JK!tbGw8#@vAQI^Si*ovl;3 zo=h=g`|x_lUG$zp%d7j!2Wf6`LkI>BVw_PoK~mQcs#xAEkPtdFS}842>XCP*6=!Al z(w0;0P&?2JAAb5h?-hfLiutXUYK%uOg3*eVOitk)@p0lTWGX*f^Gz=n{wH#-SDNUK zMN#Evi7-GnC3Q=p#C6hywTLyqn#|sBkA>hi95d)Ap5+1)*THk9-M`;+b7ZFdCzI?lfdVB=f)@EW;XG@KE`X^2M0?-Y# zkb;hW$Q;c}{MIiEM~P^v5{ZTfm4>@4#oJ9zgk5jhqYTDqYN?OxTh=sTz7PE>HBnz# zYr9%u1zcghb`~)iC<5#6z9p41-+T4N_x?7u!>t^zwi@c^lGF0UR{M94$Ew2jhjgm+ z+l3-%1rhTjMPTT<^97~pj`Y?g*s&y`(>}g%i_c$1M=xQayeeu60a3AfSwW9iN6{O^ zlbt>UQH0tKT!;d%JtlaMr$0sqbxn$dR6ZTt?NizEV^`jdzv)*mtf9f0Lp^+dyYu+C zo8{s}$xie7WH5i|cJ<3Ai>;^MzX7*<8kU?}iY^|B6N*PF_mK&1!B&lOJJfG|RT~>K zpT-(}Fqp}em72UfF4L-Z1a$-OMSR=(5mYjuUagjOv=5g_?hMJiAIY$KgF!4|pv=h^ zmFd5l4dtKFM8>cXZ7)0j8p&sMSQSM#l=t~hhMc_m zF1im$YwM*Xggw`Fl(z98thpL8;BZ5bsz=KMe9GGk&)83>zxw`=Og4NVq2aeI`9M`r zIe7PZ{VwebFEHQJ`9nPYIvO{(n#LKixpGd{8n$%wIa7_BiWWJq>|_TIB>u_Pv;l51@^ zmL*4XS(Wl0ZhO(Cg4DN%bJ4`fh26<;8`);nP01_Vr+(n@TSKVyURyQRC_X#C4nA^9 z1-o+Set1LELL5O}@vrS+Mf}ET+ei@K>G^jYr3asmv(|O3akE!na_SWQInOhAA-UJQ z#P8>4rkStiw@Z~OyCVX$Ks`e@eU?|*p>rx zeeXq)Ky&W6Q;MWV7%lC)%5#hoiPDKex_5Zd1qJi2?g@ie)>X({JzZsGtIX)NnWMKK zKT>{DEOLS=A7(vf!>Wubj4rTm(AsMB^wcInN|QR4chX|g{W_uyH_!qfCFE$9?JhlP0CIyel@cU5Iyd-T=^8GH~oFu>c=vf(|gAUIL) z^7?8!Ym>EFN4XrIwU|_e8)X;&o0-jfS_aY@UuVl`u{mY_aQ!qsl3%Qurnf%Y{q0wa zZ=lr&uMyP~2vCH_r<)lV_-9>(HJG`B99LIL6*2E-6E!-A0B-A8lln}+e_U3Eg6B8r z2*bt)=e#zFC(Dm#(m$l272U-V_lMeqPQ6db#B#Oz7RfGsSd?AzaC&m95?Y3`3$Yh6 zhbWMQ)lBT^v<5SIRD=8!&|&L=A5_jo^n=JtDg)^$hFXZ){%8j`;u+UpCMQe6T74!C zvM=&p47GE9KT7KS!I>)|Hvi73NG7eZhx1{IM{5Sux-q#A?D9l;i;}6swYr8d+YcFT z%E}p!xq)<$iv`UJWzwq1rt&1=1B$morc--s)h00~R1?`)X|dD4|57TA=>=EdSwx|$ zb#Kv3X9zdCy~{$=|50kfF!ejgg2z9*k)H|fUlKPqqTW(%0Cw=B(p+}?+nKOlx-_Qh zXHA@L-28rY@XR?7Jz1HZT?mIi3IPDFb}$$8dLX$sjo8D-Q|++SM-w~ly!^+G;p%Jz zaY9||L2seXvhOBvKMB7j6M##`NW&T}KtCKo?wR_eQt*jd*kR;F6&k^q6$G!sRK4%F z%yp;$nj>pZeeSgJbg6-42*2lBMlPdHoPn^=f zd&n>kM7S9L$TIVqQ1>>~%`G)VDZI{SfN;>`?mv;&VhOY~u92~`6Do(I$xZH4*Z$%h zsINaPe!X00^emP?Xm46WCV^=Z~6W8~qz6v%217c$Hj-+7%j(Mqzfl~OB zMA9_B$8tOtyytn{J0cd0x!$rj^A2Un0-ZbbJ#Re$3nw2oO|VehhIl9=Bl(DK1g99; z)HwKVNOCp|I??u!g^eJwvru%`Y_5e3r~|#v0RNF52nbG7P(ng#e zP_6vFvce1f@m%fc^$~<;iwi0^n38x#0R1~o*#l`CD}FtmeqQ}*F0C{N{V#{EGf9u+ zs;0w#Egs`#t-N+SS1WH}PQCSIxBeSFj6(hQ6i6A57Zv>i27F7*B{=@Uw@f=&&IYq< zZ&enra3$Rd2B<9MJ5f4|l( zDMyKhno=Hfrjr+xrUi6Eqv7g47kLKPrgE!M~)1K z_B}7@7;nN|U2NghIP;_w2M#JOUeJRm?1Iuv+SD8rACjWRg`|d{$RdLx9?6qBs4j9p`9B-O(A2i6n@v-A# + * Behdad Esfahbod + */ + +/* Test that user-fonts can handle color and non-color glyphs in the + * same font. + */ + +#include "cairo-test.h" + +#include +#include + + +#define BORDER 10 +#define TEXT_SIZE 64 +#define WIDTH (TEXT_SIZE * 6 + 2*BORDER) +#define HEIGHT (TEXT_SIZE + 2*BORDER) + +#define TEXT "abcdef" + + +static cairo_status_t +test_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_t *cr, + cairo_font_extents_t *metrics) +{ + metrics->ascent = .75; + metrics->descent = .25; + return CAIRO_STATUS_SUCCESS; +} + +static void +render_glyph_solid (cairo_t *cr, double width, double height, cairo_bool_t color) +{ + if (color) + cairo_set_source_rgba (cr, 0, 1, 1, 0.5); + cairo_rectangle (cr, 0, 0, width/2, height/2); + cairo_fill (cr); + + if (color) + cairo_set_source_rgba (cr, 1, 0, 1, 0.5); + cairo_rectangle (cr, width/4, height/4, width/2, height/2); + cairo_fill (cr); + + if (color) + cairo_set_source_rgba (cr, 1, 1, 0, 0.5); + cairo_rectangle (cr, width/2, height/2, width/2, height/2); + cairo_fill (cr); +} + +static void +render_glyph_linear (cairo_t *cr, double width, double height, cairo_bool_t color) +{ + cairo_pattern_t *pat; + + pat = cairo_pattern_create_linear (0.0, 0.0, width, height); + cairo_pattern_add_color_stop_rgba (pat, 0, 1, 0, 0, 1); + cairo_pattern_add_color_stop_rgba (pat, 0.5, 0, 1, 0, color ? 0.5 : 1); + cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 1, 1); + cairo_set_source (cr, pat); + + cairo_rectangle (cr, 0, 0, width, height); + cairo_fill (cr); +} + +static void +render_glyph_text (cairo_t *cr, double width, double height, cairo_bool_t color) +{ + cairo_select_font_face (cr, CAIRO_TEST_FONT_FAMILY " Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 0.5); + + if (color) + cairo_set_source_rgb (cr, 0.5, 0.5, 0); + cairo_move_to (cr, width*0.1, height/2); + cairo_show_text (cr, "a"); + + if (color) + cairo_set_source_rgb (cr, 0, 0.5, 0.5); + cairo_move_to (cr, width*0.4, height*0.9); + cairo_show_text (cr, "z"); +} + +static cairo_status_t +test_scaled_font_render_color_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + double width = 0.5; + double height = 0.8; + + metrics->x_advance = 0.75; + cairo_translate (cr, 0.125, -0.6); + switch (glyph) { + case 'a': + render_glyph_solid (cr, width, height, TRUE); + break; + case 'b': + render_glyph_linear (cr, width, height, TRUE); + break; + case 'c': + render_glyph_text (cr, width, height, TRUE); + break; + case 'd': + render_glyph_solid (cr, width, height, TRUE); + status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + break; + case 'e': + render_glyph_linear (cr, width, height, TRUE); + status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + break; + case 'f': + render_glyph_solid (cr, width, height, TRUE); + status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + break; + } + + return status; +} + +static cairo_status_t +test_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics) +{ + double width = 0.5; + double height = 0.8; + metrics->x_advance = 0.75; + cairo_translate (cr, 0.125, -0.6); + switch (glyph) { + case 'd': + render_glyph_solid (cr, width, height, FALSE); + break; + case 'e': + render_glyph_linear (cr, width, height, FALSE); + break; + case 'f': + render_glyph_text (cr, width, height, FALSE); + break; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_user_font_face_create (cairo_font_face_t **out) +{ + + cairo_font_face_t *user_font_face; + + 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_color_glyph_func (user_font_face, test_scaled_font_render_color_glyph); + cairo_user_font_face_set_render_glyph_func (user_font_face, test_scaled_font_render_glyph); + + *out = user_font_face; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_font_face_t *font_face; + const char text[] = TEXT; + cairo_font_extents_t font_extents; + cairo_text_extents_t extents; + cairo_status_t status; + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + status = _user_font_face_create (&font_face); + if (status) { + return cairo_test_status_from_status (cairo_test_get_context (cr), + status); + } + + cairo_set_font_face (cr, font_face); + cairo_font_face_destroy (font_face); + + cairo_set_font_size (cr, TEXT_SIZE); + + cairo_font_extents (cr, &font_extents); + cairo_text_extents (cr, text, &extents); + + /* logical boundaries in red */ + cairo_move_to (cr, 0, BORDER); + cairo_rel_line_to (cr, WIDTH, 0); + cairo_move_to (cr, 0, BORDER + font_extents.ascent); + cairo_rel_line_to (cr, WIDTH, 0); + cairo_move_to (cr, 0, BORDER + font_extents.ascent + font_extents.descent); + cairo_rel_line_to (cr, WIDTH, 0); + cairo_move_to (cr, BORDER, 0); + cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE); + cairo_move_to (cr, BORDER + extents.x_advance, 0); + cairo_rel_line_to (cr, 0, 2*BORDER + TEXT_SIZE); + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_set_line_width (cr, 2); + cairo_stroke (cr); + + /* ink boundaries in green */ + cairo_rectangle (cr, + BORDER + extents.x_bearing, BORDER + font_extents.ascent + extents.y_bearing, + extents.width, extents.height); + cairo_set_source_rgb (cr, 0, 1, 0); + cairo_set_line_width (cr, 2); + cairo_stroke (cr); + + /* text in color */ + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_move_to (cr, BORDER, BORDER + font_extents.ascent); + cairo_show_text (cr, text); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (user_font_color, + "Tests user font color feature", + "font, user-font", /* keywords */ + "cairo >= 1.17.4", /* requirements */ + WIDTH, HEIGHT, + NULL, draw)