[pango/pango2: 1/25] Allow cascading fontmaps
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/pango2: 1/25] Allow cascading fontmaps
- Date: Wed, 22 Jun 2022 11:49:51 +0000 (UTC)
commit 316d643785f3602afae23ccb7ce246379c3c73ed
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Jun 18 13:09:54 2022 -0700
Allow cascading fontmaps
Make it possible to set a fallback fontmap on a fontmap,
that will be used for adding more results to the fontsets
we produce.
Test included.
pango/pango-fontmap-private.h | 1 +
pango/pango-fontmap.c | 103 ++++++++++++++++++++++++++++++--
pango/pango-fontmap.h | 6 ++
tests/test-font.c | 135 ++++++++++++++++++++++++++++++++++++++----
4 files changed, 226 insertions(+), 19 deletions(-)
---
diff --git a/pango/pango-fontmap-private.h b/pango/pango-fontmap-private.h
index 6c0c5e043..b353e2545 100644
--- a/pango/pango-fontmap-private.h
+++ b/pango/pango-fontmap-private.h
@@ -36,6 +36,7 @@ struct _PangoFontMap
GPtrArray *families;
GHashTable *fontsets;
GQueue fontset_cache;
+ PangoFontMap *fallback;
float dpi;
gboolean in_populate;
diff --git a/pango/pango-fontmap.c b/pango/pango-fontmap.c
index fbcd4c86c..f6d6ee861 100644
--- a/pango/pango-fontmap.c
+++ b/pango/pango-fontmap.c
@@ -61,6 +61,8 @@
*
* `PangoFontMap` is the base class for font enumeration.
* It also handles caching and lookup of faces and fonts.
+ * To obtain fonts from a `PangoFontMap`, use [method@Pango.FontMap.load_font]
+ * or [method@Pango.FontMap.load_fontset].
*
* Subclasses populate the fontmap using backend-specific APIs
* to enumerate the available fonts on the sytem, but it is
@@ -68,6 +70,11 @@
* populate it manually using [method@Pango.FontMap.add_file]
* and [method@Pango.FontMap.add_face].
*
+ * Fontmaps can be combined using [method@Pango.FontMap.set_fallback].
+ * This can be useful to add custom fonts to the default fonts
+ * without making them available to every user of the default
+ * fontmap.
+ *
* Note that to be fully functional, a fontmap needs to provide
* generic families for monospace and sans-serif. These can
* be added using [method@Pango.FontMap.add_family] and
@@ -366,6 +373,7 @@ synthesize_bold_and_italic_faces (PangoFontMap *map)
enum {
PROP_RESOLUTION = 1,
+ PROP_FALLBACK,
PROP_ITEM_TYPE,
PROP_N_ITEMS,
N_PROPERTIES
@@ -400,6 +408,7 @@ pango_font_map_finalize (GObject *object)
{
PangoFontMap *self = PANGO_FONT_MAP (object);
+ g_clear_object (&self->fallback);
g_ptr_array_unref (self->added_faces);
g_ptr_array_unref (self->added_families);
g_hash_table_unref (self->families_hash);
@@ -423,6 +432,10 @@ pango_font_map_set_property (GObject *object,
pango_font_map_set_resolution (map, g_value_get_float (value));
break;
+ case PROP_FALLBACK:
+ pango_font_map_set_fallback (map, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -442,6 +455,10 @@ pango_font_map_get_property (GObject *object,
g_value_set_float (value, map->dpi);
break;
+ case PROP_FALLBACK:
+ g_value_set_object (value, map->fallback);
+ break;
+
case PROP_ITEM_TYPE:
g_value_set_gtype (value, PANGO_TYPE_FONT_FAMILY);
break;
@@ -497,6 +514,7 @@ pango_font_map_default_load_fontset (PangoFontMap *self,
PangoFontFamily *family;
PangoFontFace *face;
gboolean has_generic = FALSE;
+ PangoFontset *fallback;
gint64 before G_GNUC_UNUSED;
before = PANGO_TRACE_CURRENT_TIME;
@@ -525,8 +543,13 @@ pango_font_map_default_load_fontset (PangoFontMap *self,
if (self->families->len == 0)
{
- g_warning ("Font map contains no fonts!!!!");
- goto done_no_cache;
+ if (self->fallback)
+ goto add_fallback;
+ else
+ {
+ g_warning ("Font map contains no fonts!!!!");
+ goto done_no_cache;
+ }
}
families = g_strsplit (family_name ? family_name : "", ",", -1);
@@ -558,20 +581,30 @@ pango_font_map_default_load_fontset (PangoFontMap *self,
g_strfreev (families);
+ pango_font_description_free (copy);
+
/* Returning an empty fontset leads to bad outcomes.
*
* We always include a generic family in order
* to produce fontsets with good coverage.
+ *
+ * If we have a fallback fontmap, this is where we bring
+ * it in and just add its results to ours.
*/
if (!has_generic)
{
family = find_family (self, "sans-serif");
if (PANGO_IS_GENERIC_FAMILY (family))
pango_fontset_cached_add_family (fontset, PANGO_GENERIC_FAMILY (family));
+ else if (self->fallback)
+ {
+add_fallback:
+ fallback = pango_font_map_load_fontset (self->fallback, context, description, language);
+ pango_fontset_cached_append (fontset, PANGO_FONTSET_CACHED (fallback));
+ g_object_unref (fallback);
+ }
}
- pango_font_description_free (copy);
-
g_hash_table_add (self->fontsets, fontset);
done:
@@ -585,7 +618,7 @@ done:
}
done_no_cache:
- pango_trace_mark (before, "pango_hb_fontmap_load_fontset", "%s", family_name);
+ pango_trace_mark (before, "pango_fontmap_load_fontset", "%s", family_name);
return g_object_ref (PANGO_FONTSET (fontset));
}
@@ -646,6 +679,16 @@ pango_font_map_class_init (PangoFontMapClass *class)
g_param_spec_float ("resolution", NULL, NULL, 0, G_MAXFLOAT, 96.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ /**
+ * PangoFontMap:fallback: (attributes org.gtk.Property.get=pango_font_map_get_fallback
org.gtk.Property.set=pango_font_map_set_fallback)
+ *
+ * The fallback fontmap is used to look up fonts that
+ * this map does not have itself.
+ */
+ properties[PROP_FALLBACK] =
+ g_param_spec_object ("fallback", NULL, NULL, PANGO_TYPE_FONT_MAP,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
/**
* PangoFontMap:item-type:
*
@@ -762,6 +805,8 @@ pango_font_map_load_font (PangoFontMap *self,
const PangoFontDescription *desc)
{
g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
+ g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (desc != NULL, NULL);
return PANGO_FONT_MAP_GET_CLASS (self)->load_font (self, context, desc);
}
@@ -771,7 +816,8 @@ pango_font_map_load_font (PangoFontMap *self,
* @self: a `PangoFontMap`
* @context: the `PangoContext` the font will be used with
* @desc: a `PangoFontDescription` describing the font to load
- * @language: a `PangoLanguage` the fonts will be used for
+ * @language: (nullable): a `PangoLanguage` the fonts will be used for,
+ * or `NULL` to use the language of @context
*
* Load a set of fonts in the fontmap that can be used to render
* a font matching @desc.
@@ -786,6 +832,8 @@ pango_font_map_load_fontset (PangoFontMap *self,
PangoLanguage *language)
{
g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
+ g_return_val_if_fail (PANGO_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (desc != NULL, NULL);
return PANGO_FONT_MAP_GET_CLASS (self)->load_fontset (self, context, desc, language);
}
@@ -1039,6 +1087,49 @@ pango_font_map_remove_family (PangoFontMap *self,
g_ptr_array_remove_index (self->added_families, position);
}
+/**
+ * pango_font_map_set_fallback:
+ * @self: a `PangoFontMap`
+ * @fallback: (nullable): the `PangoFontMap` to use as fallback
+ *
+ * Sets the fontmap to use as fallback when a font isn't found.
+ *
+ * This can be used to make a custom font available only via a
+ * special fontmap, while still having all the regular fonts
+ * from the fallback fontmap.
+ */
+void
+pango_font_map_set_fallback (PangoFontMap *self,
+ PangoFontMap *fallback)
+{
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+ g_return_if_fail (fallback == NULL || PANGO_IS_FONT_MAP (fallback));
+
+ if (!g_set_object (&self->fallback, fallback))
+ return;
+
+ clear_caches (self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FALLBACK]);
+}
+
+/**
+ * pango_font_map_get_fallback:
+ * @self: a `PangoFontMap`
+ *
+ * Returns the fallback fontmap of @self
+ *
+ * See [method@Pango.FontMap.set_fallback].
+ *
+ * Returns: (nullable) (transfer none): the fallback fontmap
+ */
+PangoFontMap *
+pango_font_map_get_fallback (PangoFontMap *self)
+{
+ g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
+
+ return self->fallback;
+}
+
/**
* pango_font_map_set_resolution:
* @self: a `PangoFontMap`
diff --git a/pango/pango-fontmap.h b/pango/pango-fontmap.h
index 14ff5c30f..de806a8ab 100644
--- a/pango/pango-fontmap.h
+++ b/pango/pango-fontmap.h
@@ -68,6 +68,12 @@ PANGO_AVAILABLE_IN_ALL
void pango_font_map_remove_family (PangoFontMap *self,
PangoFontFamily *family);
+PANGO_AVAILABLE_IN_ALL
+void pango_font_map_set_fallback (PangoFontMap *self,
+ PangoFontMap *fallback);
+PANGO_AVAILABLE_IN_ALL
+PangoFontMap * pango_font_map_get_fallback (PangoFontMap *self);
+
PANGO_AVAILABLE_IN_ALL
float pango_font_map_get_resolution (PangoFontMap *self);
diff --git a/tests/test-font.c b/tests/test-font.c
index c457fac35..f6b5b6867 100644
--- a/tests/test-font.c
+++ b/tests/test-font.c
@@ -1,5 +1,5 @@
/* Pango
- * test-font.c: Test PangoFontDescription
+ * test-font.c: Test fonts and font maps
*
* Copyright (C) 2014 Red Hat, Inc
*
@@ -160,11 +160,7 @@ test_metrics (void)
PangoFontMetrics *metrics;
char *str;
-
- if (strcmp (G_OBJECT_TYPE_NAME (pango_context_get_font_map (context)), "PangoCairoWin32FontMap") == 0)
- desc = pango_font_description_from_string ("Verdana 11");
- else
- desc = pango_font_description_from_string ("Cantarell 11");
+ desc = pango_font_description_from_string ("Cantarell 11");
str = pango_font_description_to_string (desc);
@@ -227,7 +223,7 @@ test_extents (void)
}
static void
-test_enumerate (void)
+test_fontmap_enumerate (void)
{
PangoFontMap *fontmap;
PangoContext *context;
@@ -424,7 +420,7 @@ test_roundtrip_emoji (void)
}
static void
-test_font_models (void)
+test_fontmap_models (void)
{
PangoFontMap *map = pango_font_map_get_default ();
int n_families = 0;
@@ -536,6 +532,118 @@ test_faceid (void)
pango_font_description_free (desc);
}
+static gboolean
+font_info_cb (PangoUserFace *face,
+ int size,
+ hb_font_extents_t *extents,
+ gpointer user_data)
+{
+ extents->ascender = 0.75 * size;
+ extents->descender = - 0.25 * size;
+ extents->line_gap = 0;
+
+ return TRUE;
+}
+
+static gboolean
+glyph_cb (PangoUserFace *face,
+ hb_codepoint_t unicode,
+ hb_codepoint_t *glyph,
+ gpointer user_data)
+{
+ if (unicode == ' ')
+ {
+ *glyph = 0x20;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+glyph_info_cb (PangoUserFace *face,
+ int size,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ hb_position_t *h_advance,
+ hb_position_t *v_advance,
+ gboolean *is_color,
+ gpointer user_data)
+{
+ return FALSE;
+}
+
+static gboolean
+count_fonts (PangoFontset *fontset,
+ PangoFont *font,
+ gpointer user_data)
+{
+ int *count = user_data;
+
+ (*count)++;
+
+ return FALSE;
+}
+
+/* test that fontmap fallback works as expected */
+static void
+test_fontmap_fallback (void)
+{
+ PangoFontMap *map;
+ PangoUserFace *custom;
+ PangoFontDescription *desc;
+ PangoFontset *fontset;
+ PangoFont *font;
+ int count;
+
+ map = pango_font_map_new ();
+
+ desc = pango_font_description_from_string ("CustomFont");
+ custom = pango_user_face_new (font_info_cb,
+ glyph_cb,
+ glyph_info_cb,
+ NULL,
+ NULL,
+ NULL, NULL,
+ "Regular", desc);
+ pango_font_description_free (desc);
+
+ pango_font_map_add_face (map, PANGO_FONT_FACE (custom));
+
+ desc = pango_font_description_from_string ("CustomFont 11");
+ fontset = pango_font_map_load_fontset (map, context, desc, NULL);
+ g_assert_nonnull (fontset);
+
+ font = pango_fontset_get_font (fontset, 0x20);
+ g_assert_nonnull (font);
+ g_assert_true (pango_font_get_face (font) == PANGO_FONT_FACE (custom));
+ g_object_unref (font);
+
+ count = 0;
+ pango_fontset_foreach (fontset, count_fonts, &count);
+ g_assert_true (count == 1);
+
+ pango_font_map_set_fallback (map, pango_font_map_get_default ());
+
+ desc = pango_font_description_from_string ("CustomFont 11");
+ fontset = pango_font_map_load_fontset (map, context, desc, NULL);
+ g_assert_nonnull (fontset);
+
+ font = pango_fontset_get_font (fontset, ' ');
+ g_assert_nonnull (font);
+ g_assert_true (pango_font_get_face (font) == PANGO_FONT_FACE (custom));
+ g_object_unref (font);
+
+ font = pango_fontset_get_font (fontset, 'a');
+ g_assert_nonnull (font);
+
+ count = 0;
+ pango_fontset_foreach (fontset, count_fonts, &count);
+ g_assert_true (count > 1);
+
+ g_object_unref (map);
+}
+
int
main (int argc, char *argv[])
{
@@ -547,21 +655,22 @@ main (int argc, char *argv[])
context = pango_context_new ();
- g_test_add_func ("/pango/font/metrics", test_metrics);
g_test_add_func ("/pango/fontdescription/parse", test_parse);
g_test_add_func ("/pango/fontdescription/roundtrip", test_roundtrip);
g_test_add_func ("/pango/fontdescription/variations", test_variations);
g_test_add_func ("/pango/fontdescription/empty-variations", test_empty_variations);
g_test_add_func ("/pango/fontdescription/set-gravity", test_set_gravity);
g_test_add_func ("/pango/fontdescription/faceid", test_faceid);
+ g_test_add_func ("/pango/font/metrics", test_metrics);
g_test_add_func ("/pango/font/extents", test_extents);
- g_test_add_func ("/pango/font/enumerate", test_enumerate);
+ g_test_add_func ("/pango/font/glyph-extents", test_glyph_extents);
+ g_test_add_func ("/pango/font/font-metrics", test_font_metrics);
g_test_add_func ("/pango/font/roundtrip/plain", test_roundtrip_plain);
g_test_add_func ("/pango/font/roundtrip/small-caps", test_roundtrip_small_caps);
g_test_add_func ("/pango/font/roundtrip/emoji", test_roundtrip_emoji);
- g_test_add_func ("/pango/font/models", test_font_models);
- g_test_add_func ("/pango/font/glyph-extents", test_glyph_extents);
- g_test_add_func ("/pango/font/font-metrics", test_font_metrics);
+ g_test_add_func ("/pango/fontmap/enumerate", test_fontmap_enumerate);
+ g_test_add_func ("/pango/fontmap/models", test_fontmap_models);
+ g_test_add_func ("/pango/fontmap/fallback", test_fontmap_fallback);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]