[pango/simple-fontmap: 14/31] Add a faceid field to font descriptions




commit 1eb14e17a6eb2700675dec79501e709587d07ab9
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jan 3 22:10:29 2022 -0500

    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 1a9b7da9..b5eed7e2 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 38cd7582..78fbe3d8 100644
--- a/pango/pango-font.h
+++ b/pango/pango-font.h
@@ -182,6 +182,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`.
@@ -195,6 +196,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) */
@@ -319,6 +321,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);


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