From 1bd5751324866a2bfbb084fcb7d5462d983a878e Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Tue, 14 Jun 2022 22:01:04 +0930 Subject: [PATCH] FT SVG color font test --- configure.ac | 3 +- test/Makefile.am | 3 + test/Makefile.sources | 3 + test/ft-svg-color-font.c | 146 +++++++++++++++++++++++ test/meson.build | 7 ++ test/reference/ft-svg-color-font.ref.png | Bin 0 -> 10973 bytes 6 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 test/ft-svg-color-font.c create mode 100644 test/reference/ft-svg-color-font.ref.png diff --git a/configure.ac b/configure.ac index 0420fba0c..ebca4fd10 100644 --- a/configure.ac +++ b/configure.ac @@ -468,7 +468,8 @@ if test "x$use_ft" = "xyes"; then AC_CHECK_FUNCS(FT_Get_X11_Font_Format FT_GlyphSlot_Embolden FT_GlyphSlot_Oblique FT_Load_Sfnt_Table FT_Library_SetLcdFilter FT_Get_Var_Design_Coordinates FT_Done_MM_Var FT_Palette_Select) - AC_CHECK_TYPES([FT_SVG_Document], [], [], [[#include ]]) + AC_CHECK_TYPES([FT_SVG_Document], [have_ft_svg=yes], [have_ft_svg=no], [[#include ]]) + AM_CONDITIONAL(HAVE_FT_SVG_DOCUMENT, test "x$have_ft_svg=" = "xyes") AC_MSG_CHECKING(for FT_HAS_COLOR) AC_LINK_IFELSE([AC_LANG_PROGRAM([ diff --git a/test/Makefile.am b/test/Makefile.am index a74d71a7c..df2d59f44 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -14,6 +14,9 @@ if CAIRO_HAS_FT_FONT test_sources += $(ft_font_test_sources) if CAIRO_HAS_FC_FONT test_sources += $(fc_font_test_sources) +if HAVE_FT_SVG_DOCUMENT +test_sources += $(fc_svg_font_test_sources) +endif endif endif diff --git a/test/Makefile.sources b/test/Makefile.sources index 330aa9da1..107f1b7d0 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -423,6 +423,9 @@ fc_font_test_sources = \ ft-text-vertical-layout-type3.c \ ft-text-antialias-none.c +fc_svg_font_test_sources = \ + ft-svg-color-font.c + gl_surface_test_sources = \ gl-device-release.c \ gl-oversized-surface.c \ diff --git a/test/ft-svg-color-font.c b/test/ft-svg-color-font.c new file mode 100644 index 000000000..56510feca --- /dev/null +++ b/test/ft-svg-color-font.c @@ -0,0 +1,146 @@ +/* + * Copyright © 2022 Adrian Johnson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Author: Adrian Johnson + */ + +#include "cairo-test.h" +#include + +#define FONT_SIZE 50 +#define MARGIN 5 +#define WIDTH (FONT_SIZE*4 + MARGIN*2) +#define HEIGHT (FONT_SIZE*3 + MARGIN*5) + +/* Check the full name to ensure we got an SVG font. */ +#define FONT_FAMILY "Twitter Color Emoji" +#define FONT_FULLNAME "Twitter Color Emoji SVGinOT" + +static const char spade_utf8[] = { 0xe2, 0x99, 0xa0, 0x00 }; /* U+2660 glyph 87 */ +static const char club_utf8[] = { 0xe2, 0x99, 0xa3, 0x00 }; /* U+2663 glyph 88 */ +static const char heart_utf8[] = { 0xe2, 0x99, 0xa5, 0x00 }; /* U+2665 glyph 89 */ +static const char diamond_utf8[] = { 0xe2, 0x99, 0xa6, 0x00 }; /* U+2666 glyph 90 */ + +static cairo_test_status_t +set_color_emoji_font (cairo_t *cr) +{ + cairo_font_options_t *font_options; + cairo_font_face_t *font_face; + FcPattern *pattern; + FcPattern *resolved; + FcChar8 *font_name; + FcResult result; + + pattern = FcPatternCreate (); + if (pattern == NULL) + return CAIRO_TEST_NO_MEMORY; + + FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) FONT_FAMILY); + FcConfigSubstitute (NULL, pattern, FcMatchPattern); + + font_options = cairo_font_options_create (); + cairo_get_font_options (cr, font_options); + cairo_ft_font_options_substitute (font_options, pattern); + + FcDefaultSubstitute (pattern); + resolved = FcFontMatch (NULL, pattern, &result); + if (resolved == NULL) { + FcPatternDestroy (pattern); + return CAIRO_TEST_NO_MEMORY; + } + + if (FcPatternGetString (resolved, FC_FULLNAME, 0, &font_name) == FcResultMatch) { + if (strcmp((char*)font_name, FONT_FULLNAME) != 0) { + const cairo_test_context_t *ctx = cairo_test_get_context (cr); + cairo_test_log (ctx, "Could not find %s font\n", FONT_FULLNAME); + return CAIRO_TEST_UNTESTED; + } + } else { + return CAIRO_TEST_FAILURE; + } + + font_face = cairo_ft_font_face_create_for_pattern (resolved); + cairo_set_font_face (cr, font_face); + + cairo_font_options_destroy (font_options); + cairo_font_face_destroy (font_face); + FcPatternDestroy (pattern); + FcPatternDestroy (resolved); + + return CAIRO_TEST_SUCCESS; +} + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_font_options_t *font_options; + cairo_test_status_t result; + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + cairo_set_source_rgb (cr, 0, 0, 0); + + result = set_color_emoji_font (cr); + if (result != CAIRO_TEST_SUCCESS) + return result; + + cairo_set_font_size (cr, FONT_SIZE); + + /* Color glyphs */ + cairo_move_to (cr, MARGIN, FONT_SIZE + MARGIN); + cairo_show_text (cr, diamond_utf8); + cairo_show_text (cr, club_utf8); + cairo_show_text (cr, heart_utf8); + cairo_show_text (cr, spade_utf8); + + /* Non-color glyphs */ + font_options = cairo_font_options_create (); + cairo_font_options_set_color_mode (font_options, CAIRO_COLOR_MODE_NO_COLOR); + cairo_set_font_options (cr, font_options); + cairo_move_to (cr, MARGIN, FONT_SIZE*2 + MARGIN*2); + cairo_show_text (cr, diamond_utf8); + cairo_show_text (cr, club_utf8); + cairo_show_text (cr, heart_utf8); + cairo_show_text (cr, spade_utf8); + + /* Color glyph text path */ + cairo_font_options_set_color_mode (font_options, CAIRO_COLOR_MODE_COLOR); + cairo_set_font_options (cr, font_options); + cairo_font_options_destroy (font_options); + cairo_move_to (cr, MARGIN, FONT_SIZE*3 + MARGIN*3); + cairo_text_path (cr, diamond_utf8); + cairo_text_path (cr, club_utf8); + cairo_text_path (cr, heart_utf8); + cairo_text_path (cr, spade_utf8); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (ft_svg_color_font, + "Test color font", + "svgrender", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, draw) diff --git a/test/meson.build b/test/meson.build index 47d590690..7d6d6e11b 100644 --- a/test/meson.build +++ b/test/meson.build @@ -425,6 +425,10 @@ test_ft_font_sources = [ 'ft-text-antialias-none.c', ] +test_ft_svg_font_sources = [ + 'ft-svg-color-font.c', +] + test_gl_sources = [ 'gl-device-release.c', 'gl-oversized-surface.c', @@ -525,6 +529,9 @@ endif if feature_conf.get('CAIRO_HAS_FT_FONT', 0) == 1 and feature_conf.get('CAIRO_HAS_FC_FONT', 0) == 1 test_sources += test_ft_font_sources + if conf.get('HAVE_FT_SVG_DOCUMENT', 0) == 1 + test_sources += test_ft_svg_font_sources + endif endif if feature_conf.get('CAIRO_HAS_QUARTZ_SURFACE', 0) == 1 diff --git a/test/reference/ft-svg-color-font.ref.png b/test/reference/ft-svg-color-font.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..22bafde03913c6ee7b97b767fb55913e06ddd183 GIT binary patch literal 10973 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