[pango/pango2: 143/195] Add a faceid field to font descriptions




commit da84488747c41b6565b4959b8a7587173ee75b27
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Feb 17 17:24:09 2022 -0600

    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/pango-font-description.c | 182 +++++++++++++++++++++++++++++++++++++++--
 pango/pango-font-description.h |  26 ++++--
 tests/test-font.c              |  72 +++++++++++++---
 3 files changed, 253 insertions(+), 27 deletions(-)
---
diff --git a/pango/pango-font-description.c b/pango/pango-font-description.c
index c18882ef..89de07c9 100644
--- a/pango/pango-font-description.c
+++ b/pango/pango-font-description.c
@@ -36,14 +36,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,
@@ -58,14 +60,15 @@ 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 */
 };
 
 /**
@@ -662,6 +665,7 @@ pango_font_description_merge (PangoFontDescription       *desc,
 {
   gboolean family_merged;
   gboolean variations_merged;
+  gboolean faceid_merged;
 
   g_return_if_fail (desc != NULL);
 
@@ -670,6 +674,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);
 
@@ -684,6 +689,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;
+    }
 }
 
 /**
@@ -736,6 +747,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;
 }
@@ -839,6 +852,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;
 }
 
@@ -872,10 +888,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;
 }
 
@@ -910,7 +928,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) \
@@ -953,6 +972,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;
@@ -982,6 +1003,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);
 }
 
@@ -1229,6 +1253,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.
@@ -1306,6 +1364,8 @@ pango_font_description_from_string (const char *str)
         {
           desc->mask |= PANGO_FONT_MASK_VARIATIONS;
           last = p;
+
+          faceid_from_variations (desc);
         }
     }
 
@@ -1416,6 +1476,7 @@ char *
 pango_font_description_to_string (const PangoFontDescription *desc)
 {
   GString *result;
+  gboolean in_variations = FALSE;
 
   g_return_val_if_fail (desc != NULL, NULL);
 
@@ -1473,10 +1534,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);
     }
 
@@ -1667,3 +1738,98 @@ pango_parse_stretch (const char   *str,
 {
   return FIELD (stretch, PANGO_FONT_MASK_STRETCH);
 }
+
+/**
+ * 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;
+}
diff --git a/pango/pango-font-description.h b/pango/pango-font-description.h
index 31c5b4a4..21989e69 100644
--- a/pango/pango-font-description.h
+++ b/pango/pango-font-description.h
@@ -153,19 +153,21 @@ 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
  *
  * The bits in a `PangoFontMask` correspond to the set fields in a
  * `PangoFontDescription`.
  */
 typedef enum {
-  PANGO_FONT_MASK_FAMILY  = 1 << 0,
-  PANGO_FONT_MASK_STYLE   = 1 << 1,
-  PANGO_FONT_MASK_VARIANT = 1 << 2,
-  PANGO_FONT_MASK_WEIGHT  = 1 << 3,
-  PANGO_FONT_MASK_STRETCH = 1 << 4,
-  PANGO_FONT_MASK_SIZE    = 1 << 5,
-  PANGO_FONT_MASK_GRAVITY = 1 << 6,
+  PANGO_FONT_MASK_FAMILY     = 1 << 0,
+  PANGO_FONT_MASK_STYLE      = 1 << 1,
+  PANGO_FONT_MASK_VARIANT    = 1 << 2,
+  PANGO_FONT_MASK_WEIGHT     = 1 << 3,
+  PANGO_FONT_MASK_STRETCH    = 1 << 4,
+  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) */
@@ -284,6 +286,15 @@ void                    pango_font_description_set_variations    (PangoFontDescr
 PANGO_AVAILABLE_IN_1_42
 const char *            pango_font_description_get_variations    (const PangoFontDescription *desc) 
G_GNUC_PURE;
 
+PANGO_AVAILABLE_IN_ALL
+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_ALL
+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
@@ -304,6 +315,7 @@ gboolean                pango_font_description_better_match      (const PangoFon
                                                                   const PangoFontDescription *old_match,
                                                                   const PangoFontDescription *new_match) 
G_GNUC_PURE;
 
+
 PANGO_AVAILABLE_IN_ALL
 PangoFontDescription *  pango_font_description_from_string       (const char                  *str);
 PANGO_AVAILABLE_IN_ALL
diff --git a/tests/test-font.c b/tests/test-font.c
index 4aa2fc58..0c28f0cb 100644
--- a/tests/test-font.c
+++ b/tests/test-font.c
@@ -90,30 +90,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);
@@ -284,8 +288,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");
@@ -300,9 +304,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);
@@ -339,6 +349,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);
@@ -359,6 +371,7 @@ 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);
@@ -377,6 +390,7 @@ 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);
@@ -406,9 +420,10 @@ test_roundtrip_emoji (void)
 
   /* We can't expect the family name to match, since we go in with
    * 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);
@@ -555,6 +570,38 @@ test_match (void)
   pango_font_description_free (desc2);
 }
 
+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[])
 {
@@ -572,6 +619,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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]