OpenType font variations and cairo
Matthias Clasen <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <CAFwd_vAQqDmW+v3JJ0c3PSrLXyqyYosdUt26VTLoAuKLzwE2XQ@mail.gmail.com> |
Over the last few days, Behdad and I worked together on supporting OpenType font variations in the Linux text rendering stack. This requires small additions to pango, fontconfig, and ... cairo. The cairo patch is simple and ready (i'm still debugging some issues on the pango side). It adds a new member to the font options struct, a string which contains the font variation settings. -- cairo mailing list [email protected] https://lists.cairographics.org/mailman/listinfo/cairo
0001-Add-support-for-OpenType-font-variations.patch
(text/x-patch, 10.3 KB)
From ac6223e62e4b6387b2df84a8cac3ae24802d9b65 Mon Sep 17 00:00:00 2001 From: Matthias Clasen <[email protected]> Date: Tue, 12 Sep 2017 22:19:07 -0400 Subject: [PATCH] Add support for OpenType font variations Add a font option for OpenType font variations, specified as a string in the form AXIS1=VALUE,AXIS2=VALUE... We parse these variation settings when loading freetype fonts, and pass them on to freetype. --- src/cairo-font-options.c | 89 +++++++++++++++++++++++++++++++++++++++++++++-- src/cairo-ft-font.c | 64 +++++++++++++++++++++++++++++++++- src/cairo-types-private.h | 1 + src/cairo.h | 7 ++++ 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c index ad28745e6..269a03e67 100644 --- a/src/cairo-font-options.c +++ b/src/cairo-font-options.c @@ -55,7 +55,8 @@ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_LCD_FILTER_DEFAULT, CAIRO_HINT_STYLE_DEFAULT, CAIRO_HINT_METRICS_DEFAULT, - CAIRO_ROUND_GLYPH_POS_DEFAULT + CAIRO_ROUND_GLYPH_POS_DEFAULT, + NULL }; /** @@ -73,6 +74,7 @@ _cairo_font_options_init_default (cairo_font_options_t *options) options->hint_style = CAIRO_HINT_STYLE_DEFAULT; options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT; options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT; + options->variations = NULL; } void @@ -85,6 +87,7 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, options->hint_style = other->hint_style; options->hint_metrics = other->hint_metrics; options->round_glyph_positions = other->round_glyph_positions; + options->variations = other->variations ? strdup (other->variations) : NULL; } /** @@ -166,6 +169,7 @@ cairo_font_options_destroy (cairo_font_options_t *options) if (cairo_font_options_status (options)) return; + free (options->variations); free (options); } @@ -226,6 +230,24 @@ cairo_font_options_merge (cairo_font_options_t *options, options->hint_metrics = other->hint_metrics; if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) options->round_glyph_positions = other->round_glyph_positions; + + if (other->variations) { + if (options->variations) { + char *p; + + /* 'merge' variations by concatenating - later entries win */ + p = malloc (strlen (other->variations) + strlen (options->variations) + 2); + p[0] = 0; + strcat (p, options->variations); + strcat (p, ","); + strcat (p, other->variations); + free (options->variations); + options->variations = p; + } + else { + options->variations = strdup (other->variations); + } + } } slim_hidden_def (cairo_font_options_merge); @@ -259,10 +281,25 @@ cairo_font_options_equal (const cairo_font_options_t *options, options->lcd_filter == other->lcd_filter && options->hint_style == other->hint_style && options->hint_metrics == other->hint_metrics && - options->round_glyph_positions == other->round_glyph_positions); + options->round_glyph_positions == other->round_glyph_positions && + ((options->variations == NULL && other->variations == NULL) || + (options->variations != NULL && other->variations != NULL && + strcmp (options->variations, other->variations) == 0))); } slim_hidden_def (cairo_font_options_equal); +static unsigned long +_intern_string_hash (const char *str, int len) +{ + const signed char *p = (const signed char *) str; + unsigned int h = *p; + + for (p += 1; --len; p++) + h = (h << 5) - h + *p; + + return h; +} + /** * cairo_font_options_hash: * @options: a #cairo_font_options_t @@ -280,14 +317,19 @@ slim_hidden_def (cairo_font_options_equal); unsigned long cairo_font_options_hash (const cairo_font_options_t *options) { + unsigned long hash = 0; + if (cairo_font_options_status ((cairo_font_options_t *) options)) options = &_cairo_font_options_nil; /* force default values */ + if (options->variations) + hash = _intern_string_hash (options->variations, strlen (options->variations)); + return ((options->antialias) | (options->subpixel_order << 4) | (options->lcd_filter << 8) | (options->hint_style << 12) | - (options->hint_metrics << 16)); + (options->hint_metrics << 16)) ^ hash; } slim_hidden_def (cairo_font_options_hash); @@ -533,3 +575,44 @@ cairo_font_options_get_hint_metrics (const cairo_font_options_t *options) return options->hint_metrics; } + +/** + * cairo_font_options_set_variations: + * @options: a #cairo_font_options_t + * @variations: the new font variations, or %NULL + * + * Sets the OpenType font variations for the font options object. + * Font variations are specified as a string of the form + * AXIS1=VALUE1,AXIS2=VALUE2,... with each AXIS being a 4 character + * tag naming a font variation axis such as wdth or wght, and each + * VALUE being a floating point number. + * + * Since: 1.16 + **/ +void +cairo_font_options_set_variations (cairo_font_options_t *options, + const char *variations) +{ + free (options->variations); + options->variations = variations ? strdup (variations) : NULL; +} + +/** + * cairo_font_options_get_variations: + * @options: a #cairo_font_options_t + * + * Gets the OpenType font variations for the font options object. + * See cairo_font_options_set_variations() for details about the + * string format. + * + * Return value: the font variations for the font options object. The + * returned string belongs to the @options and must not be modified. + * It is valid until the @options struct is modified. + * + * Since: 1.16 + **/ +const char * +cairo_font_options_get_variations (cairo_font_options_t *options) +{ + return options->variations; +} diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 3b3087578..50442d600 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -1714,6 +1714,7 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) #ifdef FC_HINT_STYLE int hintstyle; #endif + char *variations; _cairo_font_options_init_default (&ft_options.base); ft_options.load_flags = FT_LOAD_DEFAULT; @@ -1855,6 +1856,13 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) if (embolden) ft_options.synth_flags |= CAIRO_FT_SYNTHESIZE_BOLD; +#ifndef FC_FONT_VARIATIONS +#define FC_FONT_VARIATIONS "fontvariations" +#endif + if (FcPatternGetString (pattern, FC_FONT_VARIATIONS, 0, (FcChar8 **) &variations) == FcResultMatch) { + ft_options.base.variations = strdup (variations); + } + *ret = ft_options; } #endif @@ -3616,6 +3624,57 @@ cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face) return ft->ft_options.synth_flags; } +static void +cairo_ft_apply_variations (FT_Face face, + const char *variations) +{ + FT_MM_Var *ft_mm_var; + FT_Error ret; + + if (!variations || !variations[0]) + return; + + ret = FT_Get_MM_Var (face, &ft_mm_var); + if (ret == 0) { + FT_Fixed *coords; + unsigned int i; + char *p; + + coords = malloc (sizeof (FT_Fixed) * ft_mm_var->num_axis); + + for (i = 0; i < ft_mm_var->num_axis; i++) + coords[i] = ft_mm_var->axis[i].def; + + p = variations; + while (p && *p) { + char *end, *end2; + FT_ULong tag; + float value; + + end = strchr (p, ','); + if (end && (end - p < 6) || p[4] != '=') + goto skip; + + tag = (((uint8_t)p[0])<<24)|(((uint8_t)p[1])<<16)|(((uint8_t)p[2])<<8)|((uint8_t)p[3]); + value = strtod (p + 5, &end2); + if (end2 < end) + goto skip; + + for (i = 0; i < ft_mm_var->num_axis; i++) { + if (ft_mm_var->axis[i].tag == tag) { + coords[i] = (FT_Fixed)(value*65536); + break; + } + } +skip: + p = end ? end + 1 : NULL; + } + + FT_Set_Var_Design_Coordinates (face, ft_mm_var->num_axis, coords); + free (coords); + } +} + /** * cairo_ft_scaled_font_lock_face: * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an @@ -3624,7 +3683,8 @@ cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face) * cairo_ft_font_face_create_for_ft_face()). * * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType - * backend font and scales it appropriately for the font. You must + * backend font and scales it appropriately for the font and applies OpenType + * font variations if applicable. You must * release the face with cairo_ft_scaled_font_unlock_face() * when you are done using it. Since the #FT_Face object can be * shared between multiple #cairo_scaled_font_t objects, you must not @@ -3677,6 +3737,8 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) return NULL; } + cairo_ft_apply_variations (face, scaled_font->base.options.variations); + /* Note: We deliberately release the unscaled font's mutex here, * so that we are not holding a lock across two separate calls to * cairo function, (which would give the application some diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 3d15d968e..da373030c 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -194,6 +194,7 @@ struct _cairo_font_options { cairo_hint_style_t hint_style; cairo_hint_metrics_t hint_metrics; cairo_round_glyph_positions_t round_glyph_positions; + char *variations; }; struct _cairo_glyph_text_info { diff --git a/src/cairo.h b/src/cairo.h index 32fc88b17..f8ab1d306 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1430,6 +1430,13 @@ cairo_font_options_set_hint_metrics (cairo_font_options_t *options, cairo_public cairo_hint_metrics_t cairo_font_options_get_hint_metrics (const cairo_font_options_t *options); +cairo_public const char * +cairo_font_options_get_variations (cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_variations (cairo_font_options_t *options, + const char *variations); + /* This interface is for dealing with text as text, not caring about the font object inside the the cairo_t. */ -- 2.14.1