diff options
author | Matthias Clasen <mclasen@redhat.com> | 2022-06-14 06:59:31 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2022-06-22 13:54:01 -0400 |
commit | 811e3a2b8da20f0fff3fc70f5c73cb1f5b50c6db (patch) | |
tree | d5d1dceba87a4c1f724c2ed0734e0d7198871e25 /pango/pango-fontmap.c | |
parent | 2736a3113518945c5df780f35a57a5395b27ed2f (diff) | |
download | pango-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.c | 1031 |
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: */ |