From 23cf33a37efb324e633845353e584730e6ef385c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 3 Jan 2022 22:10:29 -0500 Subject: Add a faceid field to font descriptions The faceid will be used in future commits to improve font -> description -> font roundtrip accuracy. Update affected tests. Minimal test included. --- pango/fonts.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++--- pango/pango-font.h | 11 ++++ tests/test-font.c | 76 ++++++++++++++++++---- 3 files changed, 250 insertions(+), 22 deletions(-) diff --git a/pango/fonts.c b/pango/fonts.c index 0f566a79..89607683 100644 --- a/pango/fonts.c +++ b/pango/fonts.c @@ -41,14 +41,16 @@ struct _PangoFontDescription PangoStretch stretch; PangoGravity gravity; + int size; + char *variations; + char *faceid; guint16 mask; guint static_family : 1; guint static_variations : 1; + guint static_faceid: 1; guint size_is_absolute : 1; - - int size; }; G_DEFINE_BOXED_TYPE (PangoFontDescription, pango_font_description, @@ -63,14 +65,16 @@ static const PangoFontDescription pfd_defaults = { PANGO_WEIGHT_NORMAL, /* weight */ PANGO_STRETCH_NORMAL, /* stretch */ PANGO_GRAVITY_SOUTH, /* gravity */ + + 0, /* size */ NULL, /* variations */ + NULL, /* faceid */ 0, /* mask */ 0, /* static_family */ - 0, /* static_variations*/ + 0, /* static_variations */ + 0, /* static_faceid */ 0, /* size_is_absolute */ - - 0, /* size */ }; /** @@ -600,6 +604,101 @@ pango_font_description_get_variations (const PangoFontDescription *desc) return desc->variations; } +/** + * pango_font_description_set_faceid_static: + * @desc: a `PangoFontDescription` + * @faceid: the faceid string + * + * Sets the faceid field of a font description. + * + * This is like [method@Pango.FontDescription.set_faceid], except + * that no copy of @faceid is made. The caller must make sure that + * the string passed in stays around until @desc has been freed + * or the name is set again. This function can be used if + * @faceid is a static string such as a C string literal, + * or if @desc is only needed temporarily. + * + * Since: 1.52 + */ +void +pango_font_description_set_faceid_static (PangoFontDescription *desc, + const char *faceid) +{ + g_return_if_fail (desc != NULL); + + if (desc->faceid == faceid) + return; + + if (desc->faceid && !desc->static_faceid) + g_free (desc->faceid); + + if (faceid) + { + desc->faceid = (char *)faceid; + desc->static_faceid = TRUE; + desc->mask |= PANGO_FONT_MASK_FACEID; + } + else + { + desc->faceid = pfd_defaults.faceid; + desc->static_faceid = pfd_defaults.static_faceid; + desc->mask &= ~PANGO_FONT_MASK_FACEID; + } +} + +/** + * pango_font_description_set_faceid: + * @desc: a `PangoFontDescription`. + * @faceid: (nullable): the faceid string + * + * Sets the faceid field of a font description. + * + * The faceid is mainly for internal use by Pango, to ensure + * that font -> description -> font roundtrips end up with + * the same font they started with, if possible. + * + * Font descriptions originating from [method@Pango.FontFace.describe] + * should ideally include a faceid. Pango takes the faceid + * into account when looking for the best matching face while + * loading a fontset or font. + * + * The format of this string is not guaranteed. + * + * Since: 1.52 + */ +void +pango_font_description_set_faceid (PangoFontDescription *desc, + const char *faceid) +{ + g_return_if_fail (desc != NULL); + + pango_font_description_set_faceid_static (desc, g_strdup (faceid)); + if (faceid) + desc->static_faceid = FALSE; +} + +/** + * pango_font_description_get_faceid: + * @desc: a `PangoFontDescription` + * + * Gets the faceid field of a font description. + * + * See [method@Pango.FontDescription.set_faceid]. + * + * Return value: (nullable): the faceid field for the font + * description, or %NULL if not previously set. This has the same + * life-time as the font description itself and should not be freed. + * + * Since: 1.52 + */ +const char * +pango_font_description_get_faceid (const PangoFontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, NULL); + + return desc->faceid; +} + /** * pango_font_description_get_set_fields: * @desc: a `PangoFontDescription` @@ -667,6 +766,7 @@ pango_font_description_merge (PangoFontDescription *desc, { gboolean family_merged; gboolean variations_merged; + gboolean faceid_merged; g_return_if_fail (desc != NULL); @@ -675,6 +775,7 @@ pango_font_description_merge (PangoFontDescription *desc, family_merged = desc_to_merge->family_name && (replace_existing || !desc->family_name); variations_merged = desc_to_merge->variations && (replace_existing || !desc->variations); + faceid_merged = desc_to_merge->faceid && (replace_existing || !desc->faceid); pango_font_description_merge_static (desc, desc_to_merge, replace_existing); @@ -689,6 +790,12 @@ pango_font_description_merge (PangoFontDescription *desc, desc->variations = g_strdup (desc->variations); desc->static_variations = FALSE; } + + if (faceid_merged) + { + desc->faceid = g_strdup (desc->faceid); + desc->static_faceid = FALSE; + } } /** @@ -741,6 +848,8 @@ pango_font_description_merge_static (PangoFontDescription *desc, desc->gravity = desc_to_merge->gravity; if (new_mask & PANGO_FONT_MASK_VARIATIONS) pango_font_description_set_variations_static (desc, desc_to_merge->variations); + if (new_mask & PANGO_FONT_MASK_FACEID) + pango_font_description_set_faceid_static (desc, desc_to_merge->faceid); desc->mask |= new_mask; } @@ -844,6 +953,9 @@ pango_font_description_copy (const PangoFontDescription *desc) result->variations = g_strdup (result->variations); result->static_variations = FALSE; + result->faceid = g_strdup (result->faceid); + result->static_faceid = FALSE; + return result; } @@ -877,10 +989,12 @@ pango_font_description_copy_static (const PangoFontDescription *desc) if (result->family_name) result->static_family = TRUE; - if (result->variations) result->static_variations = TRUE; + if (result->faceid) + result->static_faceid = TRUE; + return result; } @@ -915,7 +1029,8 @@ pango_font_description_equal (const PangoFontDescription *desc1, desc1->gravity == desc2->gravity && (desc1->family_name == desc2->family_name || (desc1->family_name && desc2->family_name && g_ascii_strcasecmp (desc1->family_name, desc2->family_name) == 0)) && - (g_strcmp0 (desc1->variations, desc2->variations) == 0); + (g_strcmp0 (desc1->variations, desc2->variations) == 0) && + (g_strcmp0 (desc1->faceid, desc2->faceid) == 0); } #define TOLOWER(c) \ @@ -958,6 +1073,8 @@ pango_font_description_hash (const PangoFontDescription *desc) hash = case_insensitive_hash (desc->family_name); if (desc->variations) hash ^= g_str_hash (desc->variations); + if (desc->faceid) + hash ^= g_str_hash (desc->faceid); hash ^= desc->size; hash ^= desc->size_is_absolute ? 0xc33ca55a : 0; hash ^= desc->style << 16; @@ -987,6 +1104,9 @@ pango_font_description_free (PangoFontDescription *desc) if (desc->variations && !desc->static_variations) g_free (desc->variations); + if (desc->faceid && !desc->static_faceid) + g_free (desc->faceid); + g_slice_free (PangoFontDescription, desc); } @@ -1256,6 +1376,40 @@ parse_variations (const char *word, return TRUE; } +static void +faceid_from_variations (PangoFontDescription *desc) +{ + const char *p, *q; + + p = desc->variations; + + if (g_str_has_prefix (p, "faceid=")) + { + p += strlen ("faceid="); + q = strchr (p, ','); + if (q) + { + desc->faceid = g_strndup (p, q - p); + p = q + 1; + } + else + { + desc->faceid = g_strdup (p); + p = NULL; + } + desc->mask |= PANGO_FONT_MASK_FACEID; + } + + if (p != desc->variations) + { + char *variations = g_strdup (p); + g_free (desc->variations); + desc->variations = variations; + if (variations == NULL || *variations == '\0') + desc->mask &= ~PANGO_FONT_MASK_VARIATIONS; + } +} + /** * pango_font_description_from_string: * @str: string representation of a font description. @@ -1333,6 +1487,8 @@ pango_font_description_from_string (const char *str) { desc->mask |= PANGO_FONT_MASK_VARIATIONS; last = p; + + faceid_from_variations (desc); } } @@ -1421,7 +1577,7 @@ append_field (GString *str, const char *what, const FieldMap *map, int n_element return; } - if (G_LIKELY (str->len > 0 || str->str[str->len -1] != ' ')) + if (G_LIKELY (str->len > 0 && str->str[str->len - 1] != ' ')) g_string_append_c (str, ' '); g_string_append_printf (str, "%s=%d", what, val); } @@ -1443,6 +1599,7 @@ char * pango_font_description_to_string (const PangoFontDescription *desc) { GString *result; + gboolean in_variations = FALSE; g_return_val_if_fail (desc != NULL, NULL); @@ -1500,10 +1657,20 @@ pango_font_description_to_string (const PangoFontDescription *desc) g_string_append (result, "px"); } + if (desc->mask & PANGO_FONT_MASK_FACEID) + { + in_variations = TRUE; + g_string_append (result, " @"); + g_string_append_printf (result, "faceid=%s", desc->faceid); + } + if ((desc->variations && desc->mask & PANGO_FONT_MASK_VARIATIONS) && desc->variations[0] != '\0') { - g_string_append (result, " @"); + if (!in_variations) + g_string_append (result, " @"); + else + g_string_append (result, ","); g_string_append (result, desc->variations); } diff --git a/pango/pango-font.h b/pango/pango-font.h index 1ba02b06..f4824c80 100644 --- a/pango/pango-font.h +++ b/pango/pango-font.h @@ -179,6 +179,7 @@ typedef enum { * @PANGO_FONT_MASK_SIZE: the font size is specified. * @PANGO_FONT_MASK_GRAVITY: the font gravity is specified (Since: 1.16.) * @PANGO_FONT_MASK_VARIATIONS: OpenType font variations are specified (Since: 1.42) + * @PANGO_FONT_MASK_FACEID: the face ID is specified (Since: 1.52) * * The bits in a `PangoFontMask` correspond to the set fields in a * `PangoFontDescription`. @@ -192,6 +193,7 @@ typedef enum { PANGO_FONT_MASK_SIZE = 1 << 5, PANGO_FONT_MASK_GRAVITY = 1 << 6, PANGO_FONT_MASK_VARIATIONS = 1 << 7, + PANGO_FONT_MASK_FACEID = 1 << 8, } PangoFontMask; /* CSS scale factors (1.2 factor between each size) */ @@ -316,6 +318,15 @@ void pango_font_description_set_variations (PangoFontDescript PANGO_AVAILABLE_IN_1_42 const char *pango_font_description_get_variations (const PangoFontDescription *desc) G_GNUC_PURE; +PANGO_AVAILABLE_IN_1_52 +void pango_font_description_set_faceid (PangoFontDescription *desc, + const char *faceid); +PANGO_AVAILABLE_IN_ALL +void pango_font_description_set_faceid_static (PangoFontDescription *desc, + const char *faceid); +PANGO_AVAILABLE_IN_1_52 +const char * pango_font_description_get_faceid (const PangoFontDescription *desc) G_GNUC_PURE; + PANGO_AVAILABLE_IN_ALL PangoFontMask pango_font_description_get_set_fields (const PangoFontDescription *desc) G_GNUC_PURE; PANGO_AVAILABLE_IN_ALL diff --git a/tests/test-font.c b/tests/test-font.c index 8dd540f4..8a8230fd 100644 --- a/tests/test-font.c +++ b/tests/test-font.c @@ -89,30 +89,34 @@ test_variations (void) gchar *str; desc1 = pango_font_description_from_string ("Cantarell 14"); - g_assert (desc1 != NULL); - g_assert ((pango_font_description_get_set_fields (desc1) & PANGO_FONT_MASK_VARIATIONS) == 0); - g_assert (pango_font_description_get_variations (desc1) == NULL); + g_assert_nonnull (desc1); + g_assert_cmpint ((pango_font_description_get_set_fields (desc1) & PANGO_FONT_MASK_VARIATIONS), ==, 0); + g_assert_cmpstr (pango_font_description_get_family (desc1), ==, "Cantarell"); + g_assert_cmpint (pango_font_description_get_size (desc1), ==, 14 * PANGO_SCALE); + g_assert_null (pango_font_description_get_variations (desc1)); str = pango_font_description_to_string (desc1); g_assert_cmpstr (str, ==, "Cantarell 14"); g_free (str); desc2 = pango_font_description_from_string ("Cantarell 14 @wght=100,wdth=235"); - g_assert (desc2 != NULL); - g_assert ((pango_font_description_get_set_fields (desc2) & PANGO_FONT_MASK_VARIATIONS) != 0); + g_assert_nonnull (desc2); + g_assert_cmpint ((pango_font_description_get_set_fields (desc2) & PANGO_FONT_MASK_VARIATIONS), ==, PANGO_FONT_MASK_VARIATIONS); + g_assert_cmpstr (pango_font_description_get_family (desc2), ==, "Cantarell"); + g_assert_cmpint (pango_font_description_get_size (desc2), ==, 14 * PANGO_SCALE); g_assert_cmpstr (pango_font_description_get_variations (desc2), ==, "wght=100,wdth=235"); str = pango_font_description_to_string (desc2); g_assert_cmpstr (str, ==, "Cantarell 14 @wght=100,wdth=235"); g_free (str); - g_assert (!pango_font_description_equal (desc1, desc2)); + g_assert_false (pango_font_description_equal (desc1, desc2)); pango_font_description_set_variations (desc1, "wght=100,wdth=235"); - g_assert ((pango_font_description_get_set_fields (desc1) & PANGO_FONT_MASK_VARIATIONS) != 0); + g_assert_cmpint ((pango_font_description_get_set_fields (desc1) & PANGO_FONT_MASK_VARIATIONS), ==, PANGO_FONT_MASK_VARIATIONS); g_assert_cmpstr (pango_font_description_get_variations (desc1), ==, "wght=100,wdth=235"); - g_assert (pango_font_description_equal (desc1, desc2)); + g_assert_true (pango_font_description_equal (desc1, desc2)); pango_font_description_free (desc1); pango_font_description_free (desc2); @@ -280,8 +284,8 @@ test_roundtrip_plain (void) { PangoFontMap *fontmap; PangoContext *context; - PangoFontDescription *desc, *desc2; - PangoFont *font; + PangoFontDescription *desc, *desc2, *desc3; + PangoFont *font, *font2; #ifdef HAVE_CARBON desc = pango_font_description_from_string ("Helvetica 11"); @@ -296,9 +300,15 @@ test_roundtrip_plain (void) font = pango_context_load_font (context, desc); desc2 = pango_font_describe (font); - g_assert_true (pango_font_description_equal (desc2, desc)); + font2 = pango_context_load_font (context, desc2); + desc3 = pango_font_describe (font2); + + g_assert_true (pango_font_description_equal (desc2, desc3)); + //g_assert_true (font == font2); pango_font_description_free (desc2); + g_object_unref (font2); + pango_font_description_free (desc3); g_object_unref (font); pango_font_description_free (desc); g_object_unref (context); @@ -335,6 +345,8 @@ test_roundtrip_small_caps (void) g_assert_true (features[0].tag == HB_TAG ('s', 'm', 'c', 'p')); g_assert_true (features[0].value == 1); g_assert_true (pango_font_description_get_variant (desc2) == PANGO_VARIANT_SMALL_CAPS); + /* We need to unset faceid since desc doesn't have one */ + pango_font_description_unset_fields (desc2, PANGO_FONT_MASK_FACEID); g_assert_true (pango_font_description_equal (desc2, desc)); pango_font_description_free (desc2); @@ -355,6 +367,8 @@ test_roundtrip_small_caps (void) g_assert_true (features[1].tag == HB_TAG ('c', '2', 's', 'c')); g_assert_true (features[1].value == 1); g_assert_true (pango_font_description_get_variant (desc2) == PANGO_VARIANT_ALL_SMALL_CAPS); + + pango_font_description_unset_fields (desc2, PANGO_FONT_MASK_FACEID); g_assert_true (pango_font_description_equal (desc2, desc)); pango_font_description_free (desc2); @@ -373,6 +387,8 @@ test_roundtrip_small_caps (void) g_assert_true (features[0].tag == HB_TAG ('u', 'n', 'i', 'c')); g_assert_true (features[0].value == 1); g_assert_true (pango_font_description_get_variant (desc2) == PANGO_VARIANT_UNICASE); + + pango_font_description_unset_fields (desc2, PANGO_FONT_MASK_FACEID); g_assert_true (pango_font_description_equal (desc2, desc)); pango_font_description_free (desc2); @@ -401,10 +417,11 @@ test_roundtrip_emoji (void) desc2 = pango_font_describe (font); /* We can't expect the family name to match, since we go in with - * a generic family + * a generic family. And we need to unset faceid, since desc doesn't + * have one. */ pango_font_description_unset_fields (desc, PANGO_FONT_MASK_FAMILY); - pango_font_description_unset_fields (desc2, PANGO_FONT_MASK_FAMILY); + pango_font_description_unset_fields (desc2, PANGO_FONT_MASK_FAMILY|PANGO_FONT_MASK_FACEID); g_assert_true (pango_font_description_equal (desc2, desc)); pango_font_description_free (desc2); @@ -600,6 +617,38 @@ test_font_languages (void) g_object_unref (context); } +static void +test_faceid (void) +{ + const char *test = "Cantarell Bold Italic 32 @faceid=Cantarell-Regular:0:-1:0,wght=600"; + PangoFontDescription *desc; + char *s; + + desc = pango_font_description_from_string (test); + g_assert_cmpint (pango_font_description_get_set_fields (desc), ==, PANGO_FONT_MASK_FAMILY| + PANGO_FONT_MASK_STYLE| + PANGO_FONT_MASK_WEIGHT| + PANGO_FONT_MASK_VARIANT| + PANGO_FONT_MASK_STRETCH| + PANGO_FONT_MASK_SIZE| + PANGO_FONT_MASK_FACEID| + PANGO_FONT_MASK_VARIATIONS); + g_assert_cmpstr (pango_font_description_get_family (desc), ==, "Cantarell"); + g_assert_cmpint (pango_font_description_get_size (desc), ==, 32 * PANGO_SCALE); + g_assert_cmpint (pango_font_description_get_style (desc), ==, PANGO_STYLE_ITALIC); + g_assert_cmpint (pango_font_description_get_variant (desc), ==, PANGO_VARIANT_NORMAL); + g_assert_cmpint (pango_font_description_get_weight (desc), ==, PANGO_WEIGHT_BOLD); + g_assert_cmpint (pango_font_description_get_stretch (desc), ==, PANGO_STRETCH_NORMAL); + g_assert_cmpstr (pango_font_description_get_faceid (desc), ==, "Cantarell-Regular:0:-1:0"); + g_assert_cmpstr (pango_font_description_get_variations (desc), ==, "wght=600"); + + s = pango_font_description_to_string (desc); + g_assert_cmpstr (s, ==, test); + g_free (s); + + pango_font_description_free (desc); +} + int main (int argc, char *argv[]) { @@ -617,6 +666,7 @@ main (int argc, char *argv[]) g_test_add_func ("/pango/fontdescription/to-filename", test_to_filename); g_test_add_func ("/pango/fontdescription/set-gravity", test_set_gravity); g_test_add_func ("/pango/fontdescription/match", test_match); + g_test_add_func ("/pango/fontdescription/faceid", test_faceid); g_test_add_func ("/pango/font/extents", test_extents); g_test_add_func ("/pango/font/enumerate", test_enumerate); g_test_add_func ("/pango/font/roundtrip/plain", test_roundtrip_plain); -- cgit v1.2.1