summaryrefslogtreecommitdiff
path: root/pango/pango-fontmap.c
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2022-06-14 06:59:31 -0400
committerMatthias Clasen <mclasen@redhat.com>2022-06-22 13:54:01 -0400
commit811e3a2b8da20f0fff3fc70f5c73cb1f5b50c6db (patch)
treed5d1dceba87a4c1f724c2ed0734e0d7198871e25 /pango/pango-fontmap.c
parent2736a3113518945c5df780f35a57a5395b27ed2f (diff)
downloadpango-811e3a2b8da20f0fff3fc70f5c73cb1f5b50c6db.tar.gz
Rename PangoHbFontMap to PangoFontMap
And rename the backend subclasses too.
Diffstat (limited to 'pango/pango-fontmap.c')
-rw-r--r--pango/pango-fontmap.c1031
1 files changed, 798 insertions, 233 deletions
diff --git a/pango/pango-fontmap.c b/pango/pango-fontmap.c
index 41355608..c2058dfc 100644
--- a/pango/pango-fontmap.c
+++ b/pango/pango-fontmap.c
@@ -1,7 +1,6 @@
/* Pango
- * pango-fontmap.c: Font handling
*
- * Copyright (C) 2000 Red Hat Software
+ * Copyright (C) 2021 Matthias Clasen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -10,7 +9,7 @@
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
@@ -20,51 +19,601 @@
*/
#include "config.h"
+#include <math.h>
#include <gio/gio.h>
#include "pango-fontmap-private.h"
-#include "pango-fontset-simple.h"
-#include "pango-impl-utils.h"
-#include <stdlib.h>
+#include "pango-hbfamily-private.h"
+#include "pango-generic-family-private.h"
+#include "pango-hbface-private.h"
+#include "pango-hbfont-private.h"
+#include "pango-fontset-cached-private.h"
+#include "pango-userface-private.h"
+#include "pango-userfont-private.h"
+#include "pango-fontset.h"
+#include "pango-font-face-private.h"
+#include "pango-trace-private.h"
+#include "pango-context.h"
-static PangoFontset *pango_font_map_real_load_fontset (PangoFontMap *fontmap,
- PangoContext *context,
- const PangoFontDescription *desc,
- PangoLanguage *language);
+#if defined (HAVE_CORE_TEXT) && defined (HAVE_CAIRO_QUARTZ)
+#include "pangocoretext-fontmap.h"
+#endif
+#if defined (HAVE_CAIRO_WIN32)
+#include "pangodwrite-fontmap.h"
+#endif
-static PangoFontFamily *pango_font_map_real_get_family (PangoFontMap *fontmap,
- const char *name);
+#if defined (HAVE_CAIRO_FREETYPE)
+#include "pangofc-fontmap.h"
+#endif
-static void pango_font_map_real_changed (PangoFontMap *fontmap);
+#include <hb-ot.h>
-static void pango_font_map_list_model_init (GListModelInterface *iface);
+
+/**
+ * PangoFontMap:
+ *
+ * `PangoFontMap` is a `PangoFontMap` subclass for use with
+ * `PangoHbFace` and `PangoHbFont`. It handles caching and
+ * lookup of faces and fonts.
+ *
+ * Subclasses populate the fontmap using backend-specific APIs
+ * to enumerate the available fonts on the sytem, but it is
+ * also possible to create an instance of `PangoFontMap` and
+ * populate it manually using [method@Pango.FontMap.add_file]
+ * and [method@Pango.FontMap.add_face].
+ *
+ * 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
+ * [ctor@Pango.GenericFamily.new].
+ */
+
+
+/* The central api is load_fontset, which takes a font description
+ * and language, finds the matching faces, and creates a PangoFontset
+ * for them. To speed this operation up, the font map maintains a
+ * cache of fontsets (in the form of a GQueue) and has a hash table
+ * for looking up existing fontsets.
+ *
+ * The PangoFontsetCached object is the fontset subclass that is used
+ * here, and it contains the necessary data for the cashing and hashing.
+ * PangoFontsetCached also caches the character-to-font mapping that is
+ * used when itemizing text.
+ */
+
+
+/* {{{ GListModel implementation */
+
+static GType
+pango_font_map_get_item_type (GListModel *list)
+{
+ return PANGO_TYPE_FONT_FAMILY;
+}
+
+static guint
+pango_font_map_get_n_items (GListModel *list)
+{
+ PangoFontMap *self = PANGO_FONT_MAP (list);
+
+ return self->families->len;
+}
+
+static gpointer
+pango_font_map_get_item (GListModel *list,
+ guint position)
+{
+ PangoFontMap *self = PANGO_FONT_MAP (list);
+
+ if (position < self->families->len)
+ return g_object_ref (g_ptr_array_index (self->families, position));
+
+ return NULL;
+}
+
+static void
+pango_font_map_list_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = pango_font_map_get_item_type;
+ iface->get_n_items = pango_font_map_get_n_items;
+ iface->get_item = pango_font_map_get_item;
+}
+
+/* }}} */
+/* {{{ Fontset caching */
+
+/* The number of fontsets we keep in the fontset cache */
+#define FONTSET_CACHE_SIZE 256
+
+#define FNV1_32_INIT ((guint32)0x811c9dc5)
+
+static guint
+pango_fontset_cached_hash (const PangoFontsetCached *fontset)
+{
+ guint32 hash = FNV1_32_INIT;
+
+ return (hash ^
+ GPOINTER_TO_UINT (fontset->language) ^
+ pango_font_description_hash (fontset->description));
+}
+
+static gboolean
+pango_fontset_cached_equal (const PangoFontsetCached *a,
+ const PangoFontsetCached *b)
+{
+ return a->language == b->language &&
+ pango_font_description_equal (a->description, b->description);
+}
+
+static void
+pango_fontset_cache (PangoFontsetCached *fontset,
+ PangoFontMap *self)
+{
+ GQueue *cache = &self->fontset_cache;
+ GList *link = &fontset->cache_link;
+
+ if (link->data == fontset)
+ {
+ /* Already in cache, move to head */
+ if (link == cache->head)
+ return;
+
+ g_queue_unlink (cache, link);
+ }
+ else
+ {
+ /* Not in cache yet. Make room... */
+ if (cache->length == FONTSET_CACHE_SIZE)
+ {
+ GList *old = g_queue_pop_tail_link (cache);
+ g_hash_table_remove (self->fontsets, old->data);
+ old->data = NULL;
+ }
+ }
+
+ link->data = fontset;
+ link->prev = NULL;
+ link->next = NULL;
+ g_queue_push_head_link (cache, link);
+}
+
+/* }}} */
+/* {{{ Utilities */
typedef struct {
- guint n_families;
-} PangoFontMapPrivate;
+ PangoFontFamily family;
+ PangoFontMap *map;
+ const char *name;
+} FamilyKey;
+
+static guint
+pango_family_hash (const FamilyKey *key)
+{
+ const char *p;
+ guint32 h = 5381;
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoFontMap, pango_font_map, G_TYPE_OBJECT,
- G_ADD_PRIVATE (PangoFontMap)
- G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pango_font_map_list_model_init))
+ for (p = (const char *)key->name; *p != '\0'; p++)
+ h = (h << 5) + h + g_ascii_tolower (*p);
+
+ return h;
+}
+
+static gboolean
+pango_family_equal (const FamilyKey *a,
+ const FamilyKey *b)
+{
+ return g_ascii_strcasecmp (a->name, b->name) == 0;
+}
+
+static PangoFontFamily *
+find_family (PangoFontMap *self,
+ const char *family_name)
+{
+ FamilyKey lookup;
+ PangoFontFamily *family;
+
+ lookup.name = family_name;
+
+ family = PANGO_FONT_FAMILY (g_hash_table_lookup (self->families_hash, &lookup));
+
+ return family;
+}
static void
-pango_font_map_class_init (PangoFontMapClass *class)
+clear_caches (PangoFontMap *self)
{
- class->load_fontset = pango_font_map_real_load_fontset;
- class->get_family = pango_font_map_real_get_family;
- class->changed = pango_font_map_real_changed;
+ g_hash_table_remove_all (self->fontsets);
+ g_queue_init (&self->fontset_cache);
}
static void
-pango_font_map_init (PangoFontMap *fontmap G_GNUC_UNUSED)
+add_style_variation (PangoHbFamily *family,
+ PangoHbFace *face,
+ PangoStyle style,
+ PangoWeight weight)
+{
+ PangoMatrix italic_matrix = { 1, 0.2, 0, 1, 0, 0 };
+ PangoFontDescription *desc;
+ PangoHbFace *variation;
+
+ desc = pango_font_description_new ();
+ pango_font_description_set_family (desc, pango_font_family_get_name (PANGO_FONT_FAMILY (family)));
+ pango_font_description_set_style (desc, style);
+ pango_font_description_set_weight (desc, weight);
+
+ variation = pango_hb_face_new_synthetic (face,
+ style == PANGO_STYLE_ITALIC ? &italic_matrix : NULL,
+ weight == PANGO_WEIGHT_BOLD,
+ NULL,
+ desc);
+ pango_hb_family_add_face (family, PANGO_FONT_FACE (variation));
+
+ pango_font_description_free (desc);
+}
+
+static void
+synthesize_bold_and_italic_faces (PangoFontMap *map)
+{
+ for (int i = 0; i < map->families->len; i++)
+ {
+ PangoFontFamily *family = g_ptr_array_index (map->families, i);
+ PangoHbFace *regular_face = NULL;
+ int regular_dist = G_MAXINT;
+ int bold_dist = G_MAXINT;
+ gboolean has_italic = FALSE;
+ gboolean has_bold = FALSE;
+ gboolean has_bold_italic = FALSE;
+
+ if (PANGO_IS_GENERIC_FAMILY (family))
+ continue;
+
+ for (int j = 0; j < g_list_model_get_n_items (G_LIST_MODEL (family)); j++)
+ {
+ PangoHbFace *face = g_list_model_get_item (G_LIST_MODEL (family), j);
+ int weight;
+ PangoStyle style;
+ int dist;
+
+ if (!PANGO_IS_HB_FACE (face))
+ continue;
+
+ weight = pango_font_description_get_weight (face->description);
+ style = pango_font_description_get_style (face->description);
+
+ if (style == PANGO_STYLE_NORMAL)
+ {
+ dist = abs (weight - (int)PANGO_WEIGHT_NORMAL);
+
+ if (dist < regular_dist)
+ {
+ regular_dist = dist;
+ if (dist < 150)
+ regular_face = face;
+ }
+
+ dist = abs (weight - (int)PANGO_WEIGHT_BOLD);
+
+ if (dist < bold_dist)
+ {
+ bold_dist = dist;
+ has_bold = dist < 150;
+ }
+ }
+ else
+ {
+ if (weight < PANGO_WEIGHT_SEMIBOLD)
+ has_italic = TRUE;
+ else
+ has_bold_italic = TRUE;
+ }
+
+ g_object_unref (face);
+ }
+
+ if (regular_face)
+ {
+ if (!has_italic)
+ add_style_variation (PANGO_HB_FAMILY (family), regular_face, PANGO_STYLE_ITALIC, PANGO_WEIGHT_NORMAL);
+
+ if (!has_bold)
+ add_style_variation (PANGO_HB_FAMILY (family), regular_face, PANGO_STYLE_NORMAL, PANGO_WEIGHT_BOLD);
+
+ if (!has_bold_italic)
+ add_style_variation (PANGO_HB_FAMILY (family), regular_face, PANGO_STYLE_ITALIC, PANGO_WEIGHT_BOLD);
+ }
+ }
+}
+
+/* }}} */
+/* {{{ PangoFontMap implementation */
+
+G_DEFINE_TYPE_WITH_CODE (PangoFontMap, pango_font_map, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pango_font_map_list_model_init))
+
+
+static void
+pango_font_map_init (PangoFontMap *self)
+{
+ self->added_faces = g_ptr_array_new_with_free_func (g_object_unref);
+ self->added_families = g_ptr_array_new_with_free_func (g_object_unref);
+ self->families = g_ptr_array_new_with_free_func (g_object_unref);
+ self->families_hash = g_hash_table_new_full ((GHashFunc) pango_family_hash,
+ (GEqualFunc) pango_family_equal,
+ NULL,
+ NULL);
+ self->fontsets = g_hash_table_new_full ((GHashFunc) pango_fontset_cached_hash,
+ (GEqualFunc) pango_fontset_cached_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+ g_queue_init (&self->fontset_cache);
+ self->dpi = 96.;
+}
+
+static void
+pango_font_map_finalize (GObject *object)
+{
+ PangoFontMap *self = PANGO_FONT_MAP (object);
+
+ g_ptr_array_unref (self->added_faces);
+ g_ptr_array_unref (self->added_families);
+ g_hash_table_unref (self->families_hash);
+ g_ptr_array_unref (self->families);
+ g_hash_table_unref (self->fontsets);
+
+ G_OBJECT_CLASS (pango_font_map_parent_class)->finalize (object);
+}
+
+/* Load a font from the first matching family */
+static PangoFont *
+pango_font_map_default_load_font (PangoFontMap *self,
+ PangoContext *context,
+ const PangoFontDescription *description)
+{
+ PangoFontsetCached *fontset;
+ PangoLanguage *language;
+ PangoFont *font = NULL;
+
+ if (self->families->len == 0)
+ return NULL;
+
+ if (context)
+ language = pango_context_get_language (context);
+ else
+ language = NULL;
+
+ fontset = (PangoFontsetCached *)pango_font_map_load_fontset (self, context, description, language);
+ if (pango_fontset_cached_size (fontset) > 0)
+ font = pango_fontset_cached_get_first_font (fontset);
+ g_object_unref (fontset);
+
+ return font;
+}
+
+/* Add one font for each family we find */
+static PangoFontset *
+pango_font_map_default_load_fontset (PangoFontMap *self,
+ PangoContext *context,
+ const PangoFontDescription *description,
+ PangoLanguage *language)
+{
+ PangoFontsetCached lookup;
+ PangoFontsetCached *fontset;
+ const PangoMatrix *matrix;
+ const char *family_name;
+ char **families;
+ PangoFontDescription *copy;
+ PangoFontFamily *family;
+ PangoFontFace *face;
+ gboolean has_generic = FALSE;
+ gint64 before G_GNUC_UNUSED;
+
+ before = PANGO_TRACE_CURRENT_TIME;
+
+ if (!language)
+ language = pango_context_get_language (context);
+
+ family_name = pango_font_description_get_family (description);
+
+ lookup.language = language;
+ lookup.description = (PangoFontDescription *)description;
+ fontset = g_hash_table_lookup (self->fontsets, &lookup);
+
+ if (fontset)
+ goto done;
+
+ matrix = pango_context_get_matrix (context);
+
+ fontset = pango_fontset_cached_new (description, language, self->dpi, matrix);
+
+ if (self->families->len == 0)
+ {
+ g_warning ("Font map contains no fonts!!!!");
+ goto done_no_cache;
+ }
+
+ families = g_strsplit (family_name ? family_name : "", ",", -1);
+
+ /* Unset gravity and variant since PangoHbFace does not have these fields */
+ copy = pango_font_description_copy_static (description);
+ pango_font_description_unset_fields (copy, PANGO_FONT_MASK_VARIATIONS |
+ PANGO_FONT_MASK_GRAVITY |
+ PANGO_FONT_MASK_VARIANT);
+
+ for (int i = 0; families[i]; i++)
+ {
+ family = find_family (self, families[i]);
+ if (!family)
+ continue;
+
+ if (PANGO_IS_GENERIC_FAMILY (family))
+ {
+ pango_fontset_cached_add_family (fontset, PANGO_GENERIC_FAMILY (family));
+ has_generic = TRUE;
+ }
+ else
+ {
+ face = pango_hb_family_find_face (PANGO_HB_FAMILY (family), copy, language, 0);
+ if (face)
+ pango_fontset_cached_add_face (fontset, face);
+ }
+ }
+
+ g_strfreev (families);
+
+ /* Returning an empty fontset leads to bad outcomes.
+ *
+ * We always include a generic family in order
+ * to produce fontsets with good coverage.
+ */
+ 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));
+ }
+
+ pango_font_description_free (copy);
+
+ g_hash_table_add (self->fontsets, fontset);
+
+done:
+ pango_fontset_cache (fontset, self);
+
+ if (pango_fontset_cached_size (fontset) == 0)
+ {
+ char *s = pango_font_description_to_string (description);
+ g_warning ("All font fallbacks failed for \"%s\", in %s !!!!", s, pango_language_to_string (language));
+ g_free (s);
+ }
+
+done_no_cache:
+ pango_trace_mark (before, "pango_hb_fontmap_load_fontset", "%s", family_name);
+
+ return g_object_ref (PANGO_FONTSET (fontset));
+}
+
+static PangoFontFamily *
+pango_font_map_default_get_family (PangoFontMap *self,
+ const char *name)
+{
+ return PANGO_FONT_FAMILY (find_family (self, name));
+}
+
+static void
+pango_font_map_default_populate (PangoFontMap *self)
+{
+}
+
+static guint
+pango_font_map_default_get_serial (PangoFontMap *self)
+{
+ return self->serial;
+}
+
+static void
+pango_font_map_default_changed (PangoFontMap *self)
+{
+ self->serial++;
+ if (self->serial == 0)
+ self->serial++;
+}
+
+static void
+pango_font_map_class_init (PangoFontMapClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = pango_font_map_finalize;
+
+ class->load_font = pango_font_map_default_load_font;
+ class->load_fontset = pango_font_map_default_load_fontset;
+ class->get_serial = pango_font_map_default_get_serial;
+ class->changed = pango_font_map_default_changed;
+ class->get_family = pango_font_map_default_get_family;
+ class->populate = pango_font_map_default_populate;
+}
+
+/* }}} */
+/* {{{ Private API */
+
+/*< private >
+ * pango_font_map_repopulate:
+ * @self: a `PangoFontMap`
+ * @add_synthetic: if `TRUE`, missing bold and italic faces will be synthesized
+ *
+ * Clear all cached information and repopulate the fontmap with
+ * families and faces.
+ *
+ * Subclasses should call this when their configuration changes
+ * in a way that requires recreating families and faces.
+ */
+void
+pango_font_map_repopulate (PangoFontMap *self,
+ gboolean add_synthetic)
{
+ int removed, added;
+
+ clear_caches (self);
+
+ removed = self->families->len;
+
+ g_hash_table_remove_all (self->families_hash);
+ g_ptr_array_set_size (self->families, 0);
+
+ self->in_populate = TRUE;
+
+ PANGO_FONT_MAP_GET_CLASS (self)->populate (self);
+
+ if (add_synthetic)
+ synthesize_bold_and_italic_faces (self);
+
+ for (int i = 0; i < self->added_faces->len; i++)
+ {
+ PangoFontFace *face = g_ptr_array_index (self->added_faces, i);
+ pango_font_map_add_face (self, face);
+ }
+
+ for (int i = 0; i < self->added_families->len; i++)
+ {
+ PangoFontFamily *family = PANGO_FONT_FAMILY (g_ptr_array_index (self->added_families, i));
+ pango_font_map_add_family (self, family);
+ }
+
+ self->in_populate = FALSE;
+
+ added = self->families->len;
+
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+ pango_font_map_changed (self);
}
+/*< private >
+ * pango_font_map_changed:
+ * @self: a `PangoFontMap`
+ *
+ * Forces a change in the context, which will cause any `PangoContext`
+ * using this fontmap to change.
+ *
+ * This function is only useful when implementing a new backend
+ * for Pango, something applications won't do. Backends should
+ * call this function if they have attached extra data to the
+ * context and such data is changed.
+ */
+void
+pango_font_map_changed (PangoFontMap *self)
+{
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+
+ PANGO_FONT_MAP_GET_CLASS (self)->changed (self);
+}
+
+/* }}} */
+/* {{{ Public API */
+
/**
* pango_font_map_create_context:
- * @fontmap: a `PangoFontMap`
+ * @self: a `PangoFontMap`
*
* Creates a `PangoContext` connected to @fontmap.
*
@@ -80,21 +629,22 @@ pango_font_map_init (PangoFontMap *fontmap G_GNUC_UNUSED)
* which should be freed with g_object_unref().
*/
PangoContext *
-pango_font_map_create_context (PangoFontMap *fontmap)
+pango_font_map_create_context (PangoFontMap *self)
{
PangoContext *context;
- g_return_val_if_fail (fontmap != NULL, NULL);
+ g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
context = pango_context_new ();
- pango_context_set_font_map (context, fontmap);
+ pango_context_set_font_map (context, self);
return context;
}
+
/**
* pango_font_map_load_font:
- * @fontmap: a `PangoFontMap`
+ * @self: a `PangoFontMap`
* @context: the `PangoContext` the font will be used with
* @desc: a `PangoFontDescription` describing the font to load
*
@@ -104,18 +654,18 @@ pango_font_map_create_context (PangoFontMap *fontmap)
* loaded, or %NULL if no font matched.
*/
PangoFont *
-pango_font_map_load_font (PangoFontMap *fontmap,
- PangoContext *context,
- const PangoFontDescription *desc)
+pango_font_map_load_font (PangoFontMap *self,
+ PangoContext *context,
+ const PangoFontDescription *desc)
{
- g_return_val_if_fail (fontmap != NULL, NULL);
+ g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
- return PANGO_FONT_MAP_GET_CLASS (fontmap)->load_font (fontmap, context, desc);
+ return PANGO_FONT_MAP_GET_CLASS (self)->load_font (self, context, desc);
}
/**
* pango_font_map_load_fontset:
- * @fontmap: a `PangoFontMap`
+ * @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
@@ -127,145 +677,21 @@ pango_font_map_load_font (PangoFontMap *fontmap,
* `PangoFontset` loaded, or %NULL if no font matched.
*/
PangoFontset *
-pango_font_map_load_fontset (PangoFontMap *fontmap,
+pango_font_map_load_fontset (PangoFontMap *self,
PangoContext *context,
const PangoFontDescription *desc,
PangoLanguage *language)
{
- g_return_val_if_fail (fontmap != NULL, NULL);
+ g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
- return PANGO_FONT_MAP_GET_CLASS (fontmap)->load_fontset (fontmap, context, desc, language);
-}
-
-static void
-pango_font_map_fontset_add_fonts (PangoFontMap *fontmap,
- PangoContext *context,
- PangoFontsetSimple *fonts,
- PangoFontDescription *desc,
- const char *family)
-{
- PangoFont *font;
-
- pango_font_description_set_family_static (desc, family);
- font = pango_font_map_load_font (fontmap, context, desc);
- if (font)
- pango_fontset_simple_append (fonts, font);
-}
-
-static PangoFontset *
-pango_font_map_real_load_fontset (PangoFontMap *fontmap,
- PangoContext *context,
- const PangoFontDescription *desc,
- PangoLanguage *language)
-{
- PangoFontDescription *tmp_desc = pango_font_description_copy_static (desc);
- const char *family;
- char **families;
- int i;
- PangoFontsetSimple *fonts;
- static GHashTable *warned_fonts = NULL; /* MT-safe */
- G_LOCK_DEFINE_STATIC (warned_fonts);
-
- family = pango_font_description_get_family (desc);
- families = g_strsplit (family ? family : "", ",", -1);
-
- fonts = pango_fontset_simple_new (language);
-
- for (i = 0; families[i]; i++)
- pango_font_map_fontset_add_fonts (fontmap,
- context,
- fonts,
- tmp_desc,
- families[i]);
-
- g_strfreev (families);
-
- /* The font description was completely unloadable, try with
- * family == "Sans"
- */
- if (pango_fontset_simple_size (fonts) == 0)
- {
- char *ctmp1, *ctmp2;
-
- pango_font_description_set_family_static (tmp_desc,
- pango_font_description_get_family (desc));
-
- ctmp1 = pango_font_description_to_string (desc);
- pango_font_description_set_family_static (tmp_desc, "Sans");
-
- G_LOCK (warned_fonts);
- if (!warned_fonts || !g_hash_table_lookup (warned_fonts, ctmp1))
- {
- if (!warned_fonts)
- warned_fonts = g_hash_table_new (g_str_hash, g_str_equal);
-
- g_hash_table_insert (warned_fonts, g_strdup (ctmp1), GINT_TO_POINTER (1));
-
- ctmp2 = pango_font_description_to_string (tmp_desc);
- g_warning ("couldn't load font \"%s\", falling back to \"%s\", "
- "expect ugly output.", ctmp1, ctmp2);
- g_free (ctmp2);
- }
- G_UNLOCK (warned_fonts);
- g_free (ctmp1);
-
- pango_font_map_fontset_add_fonts (fontmap,
- context,
- fonts,
- tmp_desc,
- "Sans");
- }
-
- /* We couldn't try with Sans and the specified style. Try Sans Normal
- */
- if (pango_fontset_simple_size (fonts) == 0)
- {
- char *ctmp1, *ctmp2;
-
- pango_font_description_set_family_static (tmp_desc, "Sans");
- ctmp1 = pango_font_description_to_string (tmp_desc);
- pango_font_description_set_style (tmp_desc, PANGO_STYLE_NORMAL);
- pango_font_description_set_weight (tmp_desc, PANGO_WEIGHT_NORMAL);
- pango_font_description_set_variant (tmp_desc, PANGO_VARIANT_NORMAL);
- pango_font_description_set_stretch (tmp_desc, PANGO_STRETCH_NORMAL);
-
- G_LOCK (warned_fonts);
- if (!warned_fonts || !g_hash_table_lookup (warned_fonts, ctmp1))
- {
- g_hash_table_insert (warned_fonts, g_strdup (ctmp1), GINT_TO_POINTER (1));
-
- ctmp2 = pango_font_description_to_string (tmp_desc);
-
- g_warning ("couldn't load font \"%s\", falling back to \"%s\", "
- "expect ugly output.", ctmp1, ctmp2);
- g_free (ctmp2);
- }
- G_UNLOCK (warned_fonts);
- g_free (ctmp1);
-
- pango_font_map_fontset_add_fonts (fontmap,
- context,
- fonts,
- tmp_desc,
- "Sans");
- }
-
- pango_font_description_free (tmp_desc);
-
- /* Everything failed, we are screwed, there is no way to continue,
- * but lets just not crash here.
- */
- if (pango_fontset_simple_size (fonts) == 0)
- g_warning ("All font fallbacks failed!!!!");
-
- return PANGO_FONTSET (fonts);
+ return PANGO_FONT_MAP_GET_CLASS (self)->load_fontset (self, context, desc, language);
}
/**
* pango_font_map_get_serial:
- * @fontmap: a `PangoFontMap`
+ * @self: a `PangoFontMap`
*
- * Returns the current serial number of @fontmap.
+ * Returns the current serial number of @self.
*
* The serial number is initialized to an small number larger than zero
* when a new fontmap is created and is increased whenever the fontmap
@@ -281,115 +707,254 @@ pango_font_map_real_load_fontset (PangoFontMap *fontmap,
* Return value: The current serial number of @fontmap.
*/
guint
-pango_font_map_get_serial (PangoFontMap *fontmap)
+pango_font_map_get_serial (PangoFontMap *self)
{
- g_return_val_if_fail (PANGO_IS_FONT_MAP (fontmap), 0);
+ g_return_val_if_fail (PANGO_IS_FONT_MAP (self), 0);
- if (PANGO_FONT_MAP_GET_CLASS (fontmap)->get_serial)
- return PANGO_FONT_MAP_GET_CLASS (fontmap)->get_serial (fontmap);
- else
- return 1;
+ return PANGO_FONT_MAP_GET_CLASS (self)->get_serial (self);
}
-static void
-pango_font_map_real_changed (PangoFontMap *fontmap)
+/**
+ * pango_font_map_get_family:
+ * @self: a `PangoFontMap`
+ * @name: a family name
+ *
+ * Gets a font family by name.
+ *
+ * Returns: (transfer none): the `PangoFontFamily`
+ */
+PangoFontFamily *
+pango_font_map_get_family (PangoFontMap *self,
+ const char *name)
{
- PangoFontMapPrivate *priv = pango_font_map_get_instance_private (fontmap);
- guint removed, added;
+ g_return_val_if_fail (PANGO_IS_FONT_MAP (self), NULL);
- removed = priv->n_families;
- added = g_list_model_get_n_items (G_LIST_MODEL (fontmap));
+ return PANGO_FONT_MAP_GET_CLASS (self)->get_family (self, name);
+}
- g_list_model_items_changed (G_LIST_MODEL (fontmap), 0, removed, added);
+/**
+ * pango_font_map_new:
+ *
+ * Creates a new `PangoFontMap`.
+ *
+ * Returns: A newly created `PangoFontMap
+ */
+PangoFontMap *
+pango_font_map_new (void)
+{
+ return g_object_new (PANGO_TYPE_FONT_MAP, NULL);
}
/**
- * pango_font_map_changed:
- * @fontmap: a `PangoFontMap`
+ * pango_font_map_add_face:
+ * @self: a `PangoFontMap`
+ * @face: (transfer full): a `PangoFontFace`
*
- * Forces a change in the context, which will cause any `PangoContext`
- * using this fontmap to change.
+ * Adds @face to the `PangoFontMap`.
*
- * This function is only useful when implementing a new backend
- * for Pango, something applications won't do. Backends should
- * call this function if they have attached extra data to the
- * context and such data is changed.
+ * This is most useful for creating transformed faces or aliases.
+ * See [ctor@Pango.HbFace.new_synthetic] and [ctor@Pango.HbFace.new_instance].
*/
void
-pango_font_map_changed (PangoFontMap *fontmap)
+pango_font_map_add_face (PangoFontMap *self,
+ PangoFontFace *face)
{
- g_return_if_fail (PANGO_IS_FONT_MAP (fontmap));
+ const char *family_name;
+ PangoHbFamily *family;
+ const PangoFontDescription *description;
- if (PANGO_FONT_MAP_GET_CLASS (fontmap)->changed)
- PANGO_FONT_MAP_GET_CLASS (fontmap)->changed (fontmap);
-}
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+ g_return_if_fail (PANGO_IS_HB_FACE (face) || PANGO_IS_USER_FACE (face));
-static PangoFontFamily *
-pango_font_map_real_get_family (PangoFontMap *fontmap,
- const char *name)
-{
- PangoFontFamily *family;
- int i;
+ description = ((CommonFace *)face)->description;
+
+ if (pango_font_description_get_set_fields (description) &
+ (PANGO_FONT_MASK_VARIANT | PANGO_FONT_MASK_GRAVITY))
+ g_warning ("Font description for PangoFontFace includes things that it shouldn't");
- family = NULL;
+ if (!self->in_populate)
+ g_ptr_array_add (self->added_faces, g_object_ref (face));
- for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (fontmap)); i++)
+ family_name = pango_font_description_get_family (description);
+ family = PANGO_HB_FAMILY (pango_font_map_get_family (self, family_name));
+ if (!family)
{
- PangoFontFamily *fam = g_list_model_get_item (G_LIST_MODEL (fontmap), i);
- g_object_unref (fam);
- if (strcmp (name, pango_font_family_get_name (fam)) == 0)
- {
- family = fam;
- break;
- }
+ family = pango_hb_family_new (family_name);
+ pango_font_map_add_family (self, PANGO_FONT_FAMILY (family));
}
- return family;
+ pango_hb_family_add_face (family, face);
+
+ pango_font_map_changed (self);
+
+ clear_caches (self);
}
/**
- * pango_font_map_get_family:
- * @fontmap: a `PangoFontMap`
- * @name: a family name
+ * pango_font_map_remove_face:
+ * @self: a `PangoFontMap`
+ * @face: a `PangoFontFace` that belongs to @map
*
- * Gets a font family by name.
+ * Removes @face from the `PangoFontMap`.
*
- * Returns: (transfer none): the `PangoFontFamily`
+ * @face must have been added with [method@Pango.FontMap.add_face].
*/
-PangoFontFamily *
-pango_font_map_get_family (PangoFontMap *fontmap,
- const char *name)
+void
+pango_font_map_remove_face (PangoFontMap *self,
+ PangoFontFace *face)
{
- g_return_val_if_fail (PANGO_IS_FONT_MAP (fontmap), NULL);
+ PangoHbFamily *family;
+ unsigned int position;
+
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+ g_return_if_fail (PANGO_IS_HB_FACE (face) || PANGO_IS_USER_FACE (face));
+
+ if (!g_ptr_array_find (self->added_faces, face, &position))
+ return;
+
+ family = PANGO_HB_FAMILY (pango_font_face_get_family (face));
+
+ pango_hb_family_remove_face (family, face);
+
+ if (family->faces->len == 0)
+ pango_font_map_remove_family (self, PANGO_FONT_FAMILY (family));
+
+ pango_font_map_changed (self);
- return PANGO_FONT_MAP_GET_CLASS (fontmap)->get_family (fontmap, name);
+ clear_caches (self);
+
+ g_ptr_array_remove_index (self->added_faces, position);
}
-static GType
-pango_font_map_get_item_type (GListModel *list)
+/**
+ * pango_font_map_add_file:
+ * @self: a `PangoFontMap`
+ * @file: font filename
+ *
+ * Creates a new `PangoHbFace` and adds it.
+ *
+ * If you need to specify an instance id or other
+ * parameters, use [ctor@Pango.HbFace.new_from_file].
+ */
+void
+pango_font_map_add_file (PangoFontMap *self,
+ const char *file)
{
- return PANGO_TYPE_FONT_FAMILY;
+ PangoHbFace *face;
+
+ face = pango_hb_face_new_from_file (file, 0, -1, NULL, NULL);
+ pango_font_map_add_face (self, PANGO_FONT_FACE (face));
}
-static guint
-pango_font_map_get_n_items (GListModel *list)
+/**
+ * pango_font_map_add_family:
+ * @self: a `PangoFontMap`
+ * @family: (transfer full): a `PangoFontFamily`
+ *
+ * Adds @family to @self.
+ *
+ * The fontmap must not contain a family with the
+ * same name as @family yet.
+ *
+ * This is mostly useful for adding generic families
+ * using [ctor@Pango.GenericFamily.new].
+ */
+void
+pango_font_map_add_family (PangoFontMap *self,
+ PangoFontFamily *family)
{
- g_assert_not_reached ();
- return 0;
+ const char *name;
+ int position;
+
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+ g_return_if_fail (PANGO_IS_HB_FAMILY (family) || PANGO_IS_GENERIC_FAMILY (family));
+ g_return_if_fail (((CommonFamily *)family)->map == NULL);
+
+ if (!self->in_populate)
+ g_ptr_array_add (self->added_families, g_object_ref (family));
+
+ name = ((CommonFamily *)family)->name;
+
+ position = 0;
+ while (position < self->families->len)
+ {
+ PangoFontFamily *f = g_ptr_array_index (self->families, position);
+ if (g_ascii_strcasecmp (name, ((CommonFamily *)f)->name) < 0)
+ break;
+ position++;
+ }
+
+ ((CommonFamily *)family)->map = self;
+ g_object_add_weak_pointer (G_OBJECT (self), (gpointer *)&((CommonFamily *)family)->map);
+
+ g_ptr_array_insert (self->families, position, family);
+ g_hash_table_add (self->families_hash, family);
+
+ if (!self->in_populate)
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
+
+ pango_font_map_changed (self);
}
-static gpointer
-pango_font_map_get_item (GListModel *list,
- guint position)
+/**
+ * pango_font_map_remove_family:
+ * @self: a `PangoFontMap`
+ * @family: a `PangoFontFamily` that belongs to @self
+ *
+ * Removes a `PangoHbFamily` from a `PangoFontMap`
+ */
+void
+pango_font_map_remove_family (PangoFontMap *self,
+ PangoFontFamily *family)
{
- g_assert_not_reached ();
- return NULL;
+ unsigned int position;
+
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+ g_return_if_fail (PANGO_IS_HB_FAMILY (family) || PANGO_IS_GENERIC_FAMILY (family));
+ g_return_if_fail (((CommonFamily *)family)->map == self);
+
+ if (!g_ptr_array_find (self->added_families, family, &position))
+ return;
+
+ g_hash_table_remove (self->families_hash, family);
+ g_ptr_array_remove_index (self->families, position);
+
+ g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *)&((CommonFamily *)family)->map);
+ ((CommonFamily *)family)->map = NULL;
+
+ if (!self->in_populate)
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
+
+ pango_font_map_changed (self);
+
+ g_ptr_array_remove_index (self->added_families, position);
}
-static void
-pango_font_map_list_model_init (GListModelInterface *iface)
+/**
+ * pango_font_map_set_resolution:
+ * @self: a `PangoFontMap`
+ * @dpi: the new resolution, in "dots per inch"
+ *
+ * Sets the resolution for the fontmap.
+ *
+ * This is a scale factor between points specified in a
+ * `PangoFontDescription` and Cairo units. The default value
+ * is 96, meaning that a 10 point font will be 13 units high.
+ * (10 * 96. / 72. = 13.3).
+ */
+void
+pango_font_map_set_resolution (PangoFontMap *self,
+ double dpi)
{
- iface->get_item_type = pango_font_map_get_item_type;
- iface->get_n_items = pango_font_map_get_n_items;
- iface->get_item = pango_font_map_get_item;
+ g_return_if_fail (PANGO_IS_FONT_MAP (self));
+ g_return_if_fail (dpi > 0);
+
+ self->dpi = dpi;
+
+ clear_caches (self);
+ pango_font_map_changed (self);
}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */