summaryrefslogtreecommitdiff
path: root/trunk/pango/pangofc-fontmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pango/pangofc-fontmap.c')
-rw-r--r--trunk/pango/pangofc-fontmap.c2055
1 files changed, 2055 insertions, 0 deletions
diff --git a/trunk/pango/pangofc-fontmap.c b/trunk/pango/pangofc-fontmap.c
new file mode 100644
index 00000000..267dc483
--- /dev/null
+++ b/trunk/pango/pangofc-fontmap.c
@@ -0,0 +1,2055 @@
+/* Pango
+ * pangofc-fontmap.c: Base fontmap type for fontconfig-based backends
+ *
+ * Copyright (C) 2000-2003 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Size of fontset cache */
+#define FONTSET_CACHE_SIZE 64
+
+#include <config.h>
+#include <math.h>
+
+#include "pango-context.h"
+#include "pangofc-fontmap.h"
+#include "pangofc-private.h"
+#include "pango-impl-utils.h"
+#include "modules.h"
+#include "pango-enum-types.h"
+
+typedef struct _PangoFcCoverageKey PangoFcCoverageKey;
+typedef struct _PangoFcFace PangoFcFace;
+typedef struct _PangoFcFamily PangoFcFamily;
+typedef struct _PangoFcPatternSet PangoFcPatternSet;
+typedef struct _PangoFcFindFuncInfo PangoFcFindFuncInfo;
+typedef struct _FontsetHashKey FontsetHashKey;
+typedef struct _FontHashKey FontHashKey;
+
+#define PANGO_FC_TYPE_FAMILY (pango_fc_family_get_type ())
+#define PANGO_FC_FAMILY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_FC_TYPE_FAMILY, PangoFcFamily))
+#define PANGO_FC_IS_FAMILY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_FC_TYPE_FAMILY))
+
+#define PANGO_FC_TYPE_FACE (pango_fc_face_get_type ())
+#define PANGO_FC_FACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_FC_TYPE_FACE, PangoFcFace))
+#define PANGO_FC_IS_FACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_FC_TYPE_FACE))
+
+struct _PangoFcFontMapPrivate
+{
+ GHashTable *fontset_hash; /* Maps PangoFontDescription -> PangoFcPatternSet */
+
+ /* pattern_hash is used to make sure we only store one copy of
+ * each identical pattern. (Speeds up lookup).
+ */
+ GHashTable *pattern_hash;
+ GHashTable *coverage_hash; /* Maps font file name/id -> PangoCoverage */
+
+ GHashTable *font_hash; /* Maps FcPattern -> PangoFcFont */
+
+ GQueue *fontset_cache; /* Recently used fontsets */
+
+ /* List of all families availible */
+ PangoFcFamily **families;
+ int n_families; /* -1 == uninitialized */
+
+ double dpi;
+
+ /* Decoders */
+ GSList *findfuncs;
+
+ guint closed : 1;
+};
+
+struct _PangoFcCoverageKey
+{
+ char *filename;
+ int id; /* needed to handle TTC files with multiple faces */
+};
+
+struct _PangoFcFace
+{
+ PangoFontFace parent_instance;
+
+ PangoFcFamily *family;
+ char *style;
+
+ guint fake : 1;
+};
+
+struct _PangoFcFamily
+{
+ PangoFontFamily parent_instance;
+
+ PangoFcFontMap *fontmap;
+ char *family_name;
+
+ PangoFcFace **faces;
+ int n_faces; /* -1 == uninitialized */
+
+ int spacing; /* FC_SPACING */
+};
+
+struct _PangoFcPatternSet
+{
+ int n_patterns;
+ FcPattern **patterns;
+ PangoFontset *fontset;
+ GList *cache_link;
+
+ FontsetHashKey *key;
+};
+
+struct _PangoFcFindFuncInfo
+{
+ PangoFcDecoderFindFunc findfunc;
+ gpointer user_data;
+ GDestroyNotify dnotify;
+ gpointer ddata;
+};
+
+static GType pango_fc_family_get_type (void);
+static GType pango_fc_face_get_type (void);
+
+static void pango_fc_font_map_finalize (GObject *object);
+static PangoFont * pango_fc_font_map_load_font (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *description);
+static PangoFontset *pango_fc_font_map_load_fontset (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *desc,
+ PangoLanguage *language);
+static void pango_fc_font_map_list_families (PangoFontMap *fontmap,
+ PangoFontFamily ***families,
+ int *n_families);
+
+static void pango_fc_font_map_cache_fontset (PangoFcFontMap *fcfontmap,
+ PangoFcPatternSet *patterns);
+
+static void pango_fc_pattern_set_free (PangoFcPatternSet *patterns);
+
+static guint pango_fc_pattern_hash (FcPattern *pattern);
+static gboolean pango_fc_pattern_equal (FcPattern *pattern1,
+ FcPattern *pattern2);
+static guint pango_fc_coverage_key_hash (PangoFcCoverageKey *key);
+static gboolean pango_fc_coverage_key_equal (PangoFcCoverageKey *key1,
+ PangoFcCoverageKey *key2);
+
+static guint font_hash_key_hash (const FontHashKey *key);
+static gboolean font_hash_key_equal (const FontHashKey *key_a,
+ const FontHashKey *key_b);
+static void font_hash_key_free (FontHashKey *key);
+
+static guint fontset_hash_key_hash (const FontsetHashKey *key);
+static gboolean fontset_hash_key_equal (const FontsetHashKey *key_a,
+ const FontsetHashKey *key_b);
+static void fontset_hash_key_free (FontsetHashKey *key);
+
+G_DEFINE_TYPE (PangoFcFontMap, pango_fc_font_map, PANGO_TYPE_FONT_MAP)
+
+static void
+pango_fc_font_map_init (PangoFcFontMap *fcfontmap)
+{
+ static gboolean registered_modules = FALSE;
+ PangoFcFontMapPrivate *priv;
+
+ priv = fcfontmap->priv = G_TYPE_INSTANCE_GET_PRIVATE (fcfontmap,
+ PANGO_TYPE_FC_FONT_MAP,
+ PangoFcFontMapPrivate);
+
+ if (!registered_modules)
+ {
+ int i;
+
+ registered_modules = TRUE;
+
+ for (i = 0; _pango_included_fc_modules[i].list; i++)
+ pango_module_register (&_pango_included_fc_modules[i]);
+ }
+
+ priv->n_families = -1;
+
+ priv->font_hash = g_hash_table_new_full ((GHashFunc)font_hash_key_hash,
+ (GEqualFunc)font_hash_key_equal,
+ (GDestroyNotify)font_hash_key_free,
+ NULL);
+ priv->fontset_hash = g_hash_table_new_full ((GHashFunc)fontset_hash_key_hash,
+ (GEqualFunc)fontset_hash_key_equal,
+ (GDestroyNotify)fontset_hash_key_free,
+ (GDestroyNotify)pango_fc_pattern_set_free);
+
+ priv->coverage_hash = g_hash_table_new_full ((GHashFunc)pango_fc_coverage_key_hash,
+ (GEqualFunc)pango_fc_coverage_key_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)pango_coverage_unref);
+ priv->fontset_cache = g_queue_new ();
+ priv->dpi = -1;
+}
+
+static void
+pango_fc_font_map_class_init (PangoFcFontMapClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (class);
+
+ object_class->finalize = pango_fc_font_map_finalize;
+ fontmap_class->load_font = pango_fc_font_map_load_font;
+ fontmap_class->load_fontset = pango_fc_font_map_load_fontset;
+ fontmap_class->list_families = pango_fc_font_map_list_families;
+ fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_FC;
+
+ g_type_class_add_private (object_class, sizeof (PangoFcFontMapPrivate));
+}
+
+static gpointer
+get_gravity_class (void)
+{
+ static GEnumClass *class = NULL;
+
+ if (G_UNLIKELY (!class))
+ class = g_type_class_ref (PANGO_TYPE_GRAVITY);
+
+ return class;
+}
+
+static guint
+pango_fc_pattern_hash (FcPattern *pattern)
+{
+#if 1
+ return FcPatternHash (pattern);
+#else
+ /* Hashing only part of the pattern can improve speed a bit.
+ */
+ char *str;
+ int i;
+ double d;
+ guint hash = 0;
+
+ FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &str);
+ if (str)
+ hash = g_str_hash (str);
+
+ if (FcPatternGetInteger (pattern, FC_INDEX, 0, &i) == FcResultMatch)
+ hash ^= i;
+
+ if (FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &d) == FcResultMatch)
+ hash ^= (guint) (d*1000.0);
+
+ return hash;
+#endif
+}
+
+static gboolean
+pango_fc_pattern_equal (FcPattern *pattern1,
+ FcPattern *pattern2)
+{
+ if (pattern1 == pattern2)
+ return TRUE;
+ else
+ return FcPatternEqual (pattern1, pattern2);
+}
+
+static guint
+pango_fc_coverage_key_hash (PangoFcCoverageKey *key)
+{
+ return g_str_hash (key->filename) ^ key->id;
+}
+
+static gboolean
+pango_fc_coverage_key_equal (PangoFcCoverageKey *key1,
+ PangoFcCoverageKey *key2)
+{
+ return key1->id == key2->id && strcmp (key1->filename, key2->filename) == 0;
+}
+
+struct _FontsetHashKey {
+ PangoFcFontMap *fontmap;
+ PangoMatrix matrix;
+ PangoLanguage *language;
+ PangoFontDescription *desc;
+ int size; /* scaled via the current DPI */
+ gpointer context_key;
+};
+
+struct _FontHashKey {
+ PangoFcFontMap *fontmap;
+ PangoMatrix matrix;
+ FcPattern *pattern;
+ gpointer context_key;
+};
+
+/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
+ *
+ * Not necessarily better than a lot of other hashes, but should be OK, and
+ * well tested with binary data.
+ */
+
+#define FNV_32_PRIME ((guint32)0x01000193)
+#define FNV1_32_INIT ((guint32)0x811c9dc5)
+
+static guint32
+hash_bytes_fnv (unsigned char *buffer,
+ int len,
+ guint32 hval)
+{
+ while (len--)
+ {
+ hval *= FNV_32_PRIME;
+ hval ^= *buffer++;
+ }
+
+ return hval;
+}
+
+static gboolean
+fontset_hash_key_equal (const FontsetHashKey *key_a,
+ const FontsetHashKey *key_b)
+{
+ if (key_a->size == key_b->size &&
+ pango_font_description_equal (key_a->desc, key_b->desc) &&
+ key_a->language == key_b->language &&
+ key_a->matrix.xx == key_b->matrix.xx &&
+ key_a->matrix.xy == key_b->matrix.xy &&
+ key_a->matrix.yx == key_b->matrix.yx &&
+ key_a->matrix.yy == key_b->matrix.yy)
+ {
+ if (key_a->context_key)
+ return PANGO_FC_FONT_MAP_GET_CLASS (key_a->fontmap)->context_key_equal (key_a->fontmap,
+ key_a->context_key,
+ key_b->context_key);
+ else
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static guint
+fontset_hash_key_hash (const FontsetHashKey *key)
+{
+ guint32 hash = FNV1_32_INIT;
+
+ /* We do a bytewise hash on the context matrix */
+ hash = hash_bytes_fnv ((unsigned char *)(&key->matrix),
+ sizeof(double) * 4,
+ hash);
+
+ if (key->context_key)
+ hash ^= PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap,
+ key->context_key);
+
+ /* 1237 is just an abitrary prime */
+ return (hash ^
+ GPOINTER_TO_UINT (key->language) ^
+ (key->size * 1237) ^
+ pango_font_description_hash (key->desc));
+}
+
+static void
+fontset_hash_key_free (FontsetHashKey *key)
+{
+ pango_font_description_free (key->desc);
+
+ if (key->context_key)
+ PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap,
+ key->context_key);
+
+ g_slice_free (FontsetHashKey, key);
+}
+
+static FontsetHashKey *
+fontset_hash_key_copy (FontsetHashKey *old)
+{
+ FontsetHashKey *key = g_slice_new (FontsetHashKey);
+
+ key->fontmap = old->fontmap;
+ key->matrix = old->matrix;
+ key->language = old->language;
+ key->desc = pango_font_description_copy (old->desc);
+ key->size = old->size;
+ if (old->context_key)
+ key->context_key = PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap,
+ old->context_key);
+ else
+ key->context_key = NULL;
+
+ return key;
+}
+
+static gboolean
+font_hash_key_equal (const FontHashKey *key_a,
+ const FontHashKey *key_b)
+{
+ if (key_a->matrix.xx == key_b->matrix.xx &&
+ key_a->matrix.xy == key_b->matrix.xy &&
+ key_a->matrix.yx == key_b->matrix.yx &&
+ key_a->matrix.yy == key_b->matrix.yy &&
+ key_a->pattern == key_b->pattern)
+ {
+ if (key_a->context_key)
+ return PANGO_FC_FONT_MAP_GET_CLASS (key_a->fontmap)->context_key_equal (key_a->fontmap,
+ key_a->context_key,
+ key_b->context_key);
+ else
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static guint
+font_hash_key_hash (const FontHashKey *key)
+{
+ guint32 hash = FNV1_32_INIT;
+
+ /* We do a bytewise hash on the context matrix */
+ hash = hash_bytes_fnv ((unsigned char *)(&key->matrix),
+ sizeof(double) * 4,
+ hash);
+
+ if (key->context_key)
+ hash ^= PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap,
+ key->context_key);
+
+ return (hash ^ GPOINTER_TO_UINT (key->pattern));
+}
+
+static void
+font_hash_key_free (FontHashKey *key)
+{
+ if (key->context_key)
+ PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap,
+ key->context_key);
+
+ g_slice_free (FontHashKey, key);
+}
+
+static FontHashKey *
+font_hash_key_copy (FontHashKey *old)
+{
+ FontHashKey *key = g_slice_new (FontHashKey);
+
+ key->fontmap = old->fontmap;
+ key->matrix = old->matrix;
+ key->pattern = old->pattern;
+ if (old->context_key)
+ key->context_key = PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap,
+ old->context_key);
+ else
+ key->context_key = NULL;
+
+ return key;
+}
+
+/**
+ * pango_fc_font_map_add_decoder_find_func:
+ * @fcfontmap: The #PangoFcFontMap to add this method to.
+ * @findfunc: The #PangoFcDecoderFindFunc callback function
+ * @user_data: User data.
+ * @dnotify: A #GDestroyNotify callback that will be called when the
+ * fontmap is finalized and the decoder is released.
+ *
+ * This function saves a callback method in the #PangoFcFontMap that
+ * will be called whenever new fonts are created. If the
+ * function returns a #PangoFcDecoder, that decoder will be used to
+ * determine both coverage via a #FcCharSet and a one-to-one mapping of
+ * characters to glyphs. This will allow applications to have
+ * application-specific encodings for various fonts.
+ *
+ * Since: 1.6.
+ **/
+void
+pango_fc_font_map_add_decoder_find_func (PangoFcFontMap *fcfontmap,
+ PangoFcDecoderFindFunc findfunc,
+ gpointer user_data,
+ GDestroyNotify dnotify)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ PangoFcFindFuncInfo *info;
+
+ info = g_slice_new (PangoFcFindFuncInfo);
+
+ info->findfunc = findfunc;
+ info->user_data = user_data;
+ info->dnotify = dnotify;
+
+ priv->findfuncs = g_slist_append (priv->findfuncs, info);
+}
+
+static void
+pango_fc_font_map_finalize (GObject *object)
+{
+ PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (object);
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+
+ pango_fc_font_map_cache_clear (fcfontmap);
+ g_queue_free (priv->fontset_cache);
+ g_hash_table_destroy (priv->coverage_hash);
+
+ if (priv->fontset_hash)
+ g_hash_table_destroy (priv->fontset_hash);
+
+ if (priv->font_hash)
+ g_hash_table_destroy (priv->font_hash);
+
+ if (priv->pattern_hash)
+ g_hash_table_destroy (priv->pattern_hash);
+
+ while (priv->findfuncs)
+ {
+ PangoFcFindFuncInfo *info;
+ info = priv->findfuncs->data;
+ if (info->dnotify)
+ info->dnotify (info->user_data);
+
+ g_slice_free (PangoFcFindFuncInfo, info);
+ priv->findfuncs = g_slist_delete_link (priv->findfuncs, priv->findfuncs);
+ }
+
+ G_OBJECT_CLASS (pango_fc_font_map_parent_class)->finalize (object);
+}
+
+static void
+get_context_matrix (PangoContext *context,
+ PangoMatrix *matrix)
+{
+ const PangoMatrix *set_matrix;
+ static const PangoMatrix identity = PANGO_MATRIX_INIT;
+
+ if (context)
+ set_matrix = pango_context_get_matrix (context);
+ else
+ set_matrix = NULL;
+
+ if (set_matrix)
+ *matrix = *set_matrix;
+ else
+ *matrix = identity;
+}
+
+static void
+font_hash_key_for_context (PangoFcFontMap *fcfontmap,
+ PangoContext *context,
+ FontHashKey *key)
+{
+ key->fontmap = fcfontmap;
+ get_context_matrix (context, &key->matrix);
+
+ if (PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get)
+ key->context_key = (gpointer)PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get (fcfontmap, context);
+ else
+ key->context_key = NULL;
+}
+
+/* Add a mapping from xfont->font_pattern to xfont */
+static void
+pango_fc_font_map_add (PangoFcFontMap *fcfontmap,
+ PangoContext *context,
+ PangoFcFont *fcfont)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FontHashKey key;
+ FontHashKey *key_copy;
+
+ g_assert (fcfont->fontmap == NULL);
+
+ fcfont->fontmap = g_object_ref (fcfontmap);
+
+ font_hash_key_for_context (fcfontmap, context, &key);
+ key.pattern = fcfont->font_pattern;
+
+ key_copy = font_hash_key_copy (&key);
+ _pango_fc_font_set_context_key (fcfont, key_copy->context_key);
+ fcfont->matrix = key.matrix;
+
+ g_hash_table_insert (priv->font_hash, key_copy, fcfont);
+}
+
+static PangoFcFont *
+pango_fc_font_map_lookup (PangoFcFontMap *fcfontmap,
+ PangoContext *context,
+ FcPattern *pattern)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FontHashKey key;
+
+ font_hash_key_for_context (fcfontmap, context, &key);
+ key.pattern = pattern;
+
+ return g_hash_table_lookup (priv->font_hash, &key);
+}
+
+/* Remove mapping from xfont->font_pattern to xfont */
+void
+_pango_fc_font_map_remove (PangoFcFontMap *fcfontmap,
+ PangoFcFont *fcfont)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FontHashKey key;
+
+ key.fontmap = fcfontmap;
+ key.matrix = fcfont->matrix;
+ key.pattern = fcfont->font_pattern;
+ key.context_key = _pango_fc_font_get_context_key (fcfont);
+
+ g_hash_table_remove (priv->font_hash, &key);
+ fcfont->fontmap = NULL;
+ _pango_fc_font_set_context_key (fcfont, NULL);
+ g_object_unref (fcfontmap);
+}
+
+static PangoFcFamily *
+create_family (PangoFcFontMap *fcfontmap,
+ const char *family_name,
+ int spacing)
+{
+ PangoFcFamily *family = g_object_new (PANGO_FC_TYPE_FAMILY, NULL);
+ family->fontmap = fcfontmap;
+ family->family_name = g_strdup (family_name);
+ family->spacing = spacing;
+
+ return family;
+}
+
+static gboolean
+is_alias_family (const char *family_name)
+{
+ switch (family_name[0])
+ {
+ case 'm':
+ case 'M':
+ return (g_ascii_strcasecmp (family_name, "monospace") == 0);
+ case 's':
+ case 'S':
+ return (g_ascii_strcasecmp (family_name, "sans") == 0 ||
+ g_ascii_strcasecmp (family_name, "serif") == 0);
+ }
+
+ return FALSE;
+}
+
+static void
+pango_fc_font_map_list_families (PangoFontMap *fontmap,
+ PangoFontFamily ***families,
+ int *n_families)
+{
+ PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (fontmap);
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FcFontSet *fontset;
+ int i;
+ int count;
+
+ if (priv->closed)
+ {
+ if (families)
+ *families = NULL;
+ if (n_families)
+ *n_families = 0;
+
+ return;
+ }
+
+ if (priv->n_families < 0)
+ {
+ FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_SPACING, NULL);
+ FcPattern *pat = FcPatternCreate ();
+ /* use hash table to avoid duplicate listings if different faces in
+ * the same family have different spacing values */
+ GHashTable *temp_family_hash;
+
+ fontset = FcFontList (NULL, pat, os);
+
+ FcPatternDestroy (pat);
+ FcObjectSetDestroy (os);
+
+ priv->families = g_new (PangoFcFamily *, fontset->nfont + 3); /* 3 standard aliases */
+ temp_family_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ count = 0;
+ for (i = 0; i < fontset->nfont; i++)
+ {
+ FcChar8 *s;
+ FcResult res;
+ int spacing;
+
+ res = FcPatternGetString (fontset->fonts[i], FC_FAMILY, 0, (FcChar8 **) &s);
+ g_assert (res == FcResultMatch);
+
+ res = FcPatternGetInteger (fontset->fonts[i], FC_SPACING, 0, &spacing);
+ g_assert (res == FcResultMatch || res == FcResultNoMatch);
+ if (res == FcResultNoMatch)
+ spacing = FC_PROPORTIONAL;
+
+ if (!is_alias_family (s) && !g_hash_table_lookup (temp_family_hash, s))
+ {
+ PangoFcFamily *temp_family = create_family (fcfontmap, (gchar *)s, spacing);
+ g_hash_table_insert (temp_family_hash, g_strdup (s), s);
+ priv->families[count++] = temp_family;
+ }
+ }
+
+ FcFontSetDestroy (fontset);
+ g_hash_table_destroy (temp_family_hash);
+
+ priv->families[count++] = create_family (fcfontmap, "Sans", FC_PROPORTIONAL);
+ priv->families[count++] = create_family (fcfontmap, "Serif", FC_PROPORTIONAL);
+ priv->families[count++] = create_family (fcfontmap, "Monospace", FC_MONO);
+
+ priv->n_families = count;
+ }
+
+ if (n_families)
+ *n_families = priv->n_families;
+
+ if (families)
+ *families = g_memdup (priv->families, priv->n_families * sizeof (PangoFontFamily *));
+}
+
+static int
+pango_fc_convert_weight_to_fc (PangoWeight pango_weight)
+{
+#ifdef FC_WEIGHT_ULTRABOLD
+ /* fontconfig 2.1 only had light/medium/demibold/bold/black */
+ if (pango_weight < (PANGO_WEIGHT_ULTRALIGHT + PANGO_WEIGHT_LIGHT) / 2)
+ return FC_WEIGHT_ULTRALIGHT;
+ else if (pango_weight < (PANGO_WEIGHT_LIGHT + PANGO_WEIGHT_NORMAL) / 2)
+ return FC_WEIGHT_LIGHT;
+ else if (pango_weight < (PANGO_WEIGHT_NORMAL + 500 /* PANGO_WEIGHT_MEDIUM */) / 2)
+ return FC_WEIGHT_NORMAL;
+ else if (pango_weight < (500 /* PANGO_WEIGHT_MEDIUM */ + PANGO_WEIGHT_SEMIBOLD) / 2)
+ return FC_WEIGHT_MEDIUM;
+ else if (pango_weight < (PANGO_WEIGHT_SEMIBOLD + PANGO_WEIGHT_BOLD) / 2)
+ return FC_WEIGHT_DEMIBOLD;
+ else if (pango_weight < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD) / 2)
+ return FC_WEIGHT_BOLD;
+ else if (pango_weight < (PANGO_WEIGHT_ULTRABOLD + PANGO_WEIGHT_HEAVY) / 2)
+ return FC_WEIGHT_ULTRABOLD;
+ else
+ return FC_WEIGHT_BLACK;
+#else /* fontconfig < 2.2 */
+ if (pango_weight < (PANGO_WEIGHT_LIGHT + PANGO_WEIGHT_NORMAL) / 2)
+ return FC_WEIGHT_LIGHT;
+ else if (pango_weight < (500 /* PANGO_WEIGHT_MEDIUM */ + PANGO_WEIGHT_SEMIBOLD) / 2)
+ return FC_WEIGHT_MEDIUM;
+ else if (pango_weight < (PANGO_WEIGHT_SEMIBOLD + PANGO_WEIGHT_BOLD) / 2)
+ return FC_WEIGHT_DEMIBOLD;
+ else if (pango_weight < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD) / 2)
+ return FC_WEIGHT_BOLD;
+ else
+ return FC_WEIGHT_BLACK;
+#endif
+}
+
+static int
+pango_fc_convert_slant_to_fc (PangoStyle pango_style)
+{
+ switch (pango_style)
+ {
+ case PANGO_STYLE_NORMAL:
+ return FC_SLANT_ROMAN;
+ case PANGO_STYLE_ITALIC:
+ return FC_SLANT_ITALIC;
+ case PANGO_STYLE_OBLIQUE:
+ return FC_SLANT_OBLIQUE;
+ default:
+ return FC_SLANT_ROMAN;
+ }
+}
+
+#ifdef FC_WIDTH
+static int
+pango_fc_convert_width_to_fc (PangoStretch pango_stretch)
+{
+ switch (pango_stretch)
+ {
+ case PANGO_STRETCH_NORMAL:
+ return FC_WIDTH_NORMAL;
+ case PANGO_STRETCH_ULTRA_CONDENSED:
+ return FC_WIDTH_ULTRACONDENSED;
+ case PANGO_STRETCH_EXTRA_CONDENSED:
+ return FC_WIDTH_EXTRACONDENSED;
+ case PANGO_STRETCH_CONDENSED:
+ return FC_WIDTH_CONDENSED;
+ case PANGO_STRETCH_SEMI_CONDENSED:
+ return FC_WIDTH_SEMICONDENSED;
+ case PANGO_STRETCH_SEMI_EXPANDED:
+ return FC_WIDTH_SEMIEXPANDED;
+ case PANGO_STRETCH_EXPANDED:
+ return FC_WIDTH_EXPANDED;
+ case PANGO_STRETCH_EXTRA_EXPANDED:
+ return FC_WIDTH_EXTRAEXPANDED;
+ case PANGO_STRETCH_ULTRA_EXPANDED:
+ return FC_WIDTH_ULTRAEXPANDED;
+ default:
+ return FC_WIDTH_NORMAL;
+ }
+}
+#endif
+
+static FcPattern *
+pango_fc_make_pattern (const PangoFontDescription *description,
+ PangoLanguage *language,
+ double pixel_size,
+ double dpi)
+{
+ FcPattern *pattern;
+ int slant;
+ int weight;
+ PangoGravity gravity;
+ FcBool vertical;
+ char **families;
+ int i;
+#ifdef FC_WIDTH
+ int width;
+#endif
+
+ slant = pango_fc_convert_slant_to_fc (pango_font_description_get_style (description));
+ weight = pango_fc_convert_weight_to_fc (pango_font_description_get_weight (description));
+#ifdef FC_WIDTH
+ width = pango_fc_convert_width_to_fc (pango_font_description_get_stretch (description));
+#endif
+
+ gravity = pango_font_description_get_gravity (description);
+ vertical = PANGO_GRAVITY_IS_VERTICAL (gravity) ? FcTrue : FcFalse;
+
+ /* The reason for passing in FC_SIZE as well as FC_PIXEL_SIZE is
+ * to work around a bug in libgnomeprint where it doesn't look
+ * for FC_PIXEL_SIZE. See http://bugzilla.gnome.org/show_bug.cgi?id=169020
+ *
+ * Putting FC_SIZE in here slightly reduces the efficiency
+ * of caching of patterns and fonts when working with multiple different
+ * dpi values.
+ */
+ pattern = FcPatternBuild (NULL,
+ FC_WEIGHT, FcTypeInteger, weight,
+ FC_SLANT, FcTypeInteger, slant,
+#ifdef FC_WIDTH
+ FC_WIDTH, FcTypeInteger, width,
+#endif
+#ifdef FC_VERTICAL_LAYOUT
+ FC_VERTICAL_LAYOUT, FcTypeBool, vertical,
+#endif
+ FC_SIZE, FcTypeDouble, pixel_size * (72. / dpi),
+ FC_PIXEL_SIZE, FcTypeDouble, pixel_size,
+ NULL);
+
+ families = g_strsplit (pango_font_description_get_family (description), ",", -1);
+
+ for (i = 0; families[i]; i++)
+ FcPatternAddString (pattern, FC_FAMILY, families[i]);
+
+ g_strfreev (families);
+
+ if (language)
+ FcPatternAddString (pattern, FC_LANG, (FcChar8 *) pango_language_to_string (language));
+
+ if (gravity != PANGO_GRAVITY_SOUTH)
+ {
+ GEnumValue *value = g_enum_get_value (get_gravity_class (), gravity);
+ FcPatternAddString (pattern, PANGO_FC_GRAVITY, value->value_nick);
+ }
+
+ return pattern;
+}
+
+static PangoFont *
+pango_fc_font_map_new_font (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *description,
+ FcPattern *match)
+{
+ PangoFcFontMapClass *class;
+ PangoFcFontMap *fcfontmap = (PangoFcFontMap *)fontmap;
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FcPattern *pattern;
+ PangoFcFont *fcfont;
+ GSList *l;
+
+ if (priv->closed)
+ return NULL;
+
+ fcfont = pango_fc_font_map_lookup (fcfontmap, context, match);
+ if (fcfont)
+ return g_object_ref (fcfont);
+
+ class = PANGO_FC_FONT_MAP_GET_CLASS (fontmap);
+
+ if (class->create_font)
+ {
+ fcfont = PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->create_font (fcfontmap,
+ context,
+ description,
+ match);
+ }
+ else
+ {
+ const PangoMatrix *pango_matrix;
+
+ if (context)
+ pango_matrix = pango_context_get_matrix (context);
+ else
+ pango_matrix = NULL;
+
+ if (pango_matrix)
+ {
+ FcMatrix fc_matrix;
+
+ /* Fontconfig has the Y axis pointing up, Pango, down.
+ */
+ fc_matrix.xx = pango_matrix->xx;
+ fc_matrix.xy = - pango_matrix->xy;
+ fc_matrix.yx = - pango_matrix->yx;
+ fc_matrix.yy = pango_matrix->yy;
+
+ pattern = FcPatternDuplicate (match);
+ FcPatternAddMatrix (pattern, FC_MATRIX, &fc_matrix);
+ }
+ else
+ pattern = match;
+
+ fcfont = PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->new_font (fcfontmap, pattern);
+
+ if (pango_matrix)
+ FcPatternDestroy (pattern);
+ }
+
+ if (!fcfont)
+ return NULL;
+
+ pango_fc_font_map_add (fcfontmap, context, fcfont);
+
+ /*
+ * Give any custom decoders a crack at this font now that it's been
+ * created.
+ */
+ for (l = priv->findfuncs; l && l->data; l = l->next)
+ {
+ PangoFcFindFuncInfo *info = l->data;
+ PangoFcDecoder *decoder;
+
+ decoder = info->findfunc (match, info->user_data);
+ if (decoder)
+ {
+ _pango_fc_font_set_decoder (fcfont, decoder);
+ break;
+ }
+ }
+
+ return (PangoFont *)fcfont;
+}
+
+static FcPattern *
+uniquify_pattern (PangoFcFontMap *fcfontmap,
+ FcPattern *pattern)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FcPattern *old_pattern;
+
+ if (!priv->pattern_hash)
+ priv->pattern_hash =
+ g_hash_table_new_full ((GHashFunc)pango_fc_pattern_hash,
+ (GEqualFunc)pango_fc_pattern_equal,
+ (GDestroyNotify)FcPatternDestroy, NULL);
+
+ old_pattern = g_hash_table_lookup (priv->pattern_hash, pattern);
+ if (old_pattern)
+ {
+ FcPatternDestroy (pattern);
+ FcPatternReference (old_pattern);
+ return old_pattern;
+ }
+ else
+ {
+ FcPatternReference (pattern);
+ g_hash_table_insert (priv->pattern_hash, pattern, pattern);
+ return pattern;
+ }
+}
+
+static void
+pango_fc_default_substitute (PangoFcFontMap *fontmap,
+ PangoContext *context,
+ FcPattern *pattern)
+{
+ if (PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->context_substitute)
+ PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->context_substitute (fontmap, context, pattern);
+ else if (PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->default_substitute)
+ PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->default_substitute (fontmap, pattern);
+}
+
+static gdouble
+pango_fc_font_map_get_resolution (PangoFcFontMap *fcfontmap,
+ PangoContext *context)
+{
+ if (PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->get_resolution)
+ return PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->get_resolution (fcfontmap, context);
+
+ if (fcfontmap->priv->dpi < 0)
+ {
+ FcResult result = FcResultNoMatch;
+ FcPattern *tmp = FcPatternBuild (NULL,
+ FC_FAMILY, FcTypeString, "Sans",
+ FC_SIZE, FcTypeDouble, 10.,
+ NULL);
+ if (tmp)
+ {
+ pango_fc_default_substitute (fcfontmap, NULL, tmp);
+ result = FcPatternGetDouble (tmp, FC_DPI, 0, &fcfontmap->priv->dpi);
+ FcPatternDestroy (tmp);
+ }
+
+ if (result != FcResultMatch)
+ {
+ g_warning ("Error getting DPI from fontconfig, using 72.0");
+ fcfontmap->priv->dpi = 72.0;
+ }
+ }
+
+ return fcfontmap->priv->dpi;
+}
+
+static int
+get_unscaled_size (PangoFcFontMap *fcfontmap,
+ PangoContext *context,
+ const PangoFontDescription *desc)
+{
+ int size = pango_font_description_get_size (desc);
+
+ if (pango_font_description_get_size_is_absolute (desc))
+ return size;
+ else
+ {
+ double dpi = pango_fc_font_map_get_resolution (fcfontmap, context);
+
+ return (int)(0.5 + size * dpi / 72.);
+ }
+}
+
+static PangoFcPatternSet *
+pango_fc_font_map_get_patterns (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *desc,
+ PangoLanguage *language)
+{
+ PangoFcFontMap *fcfontmap = (PangoFcFontMap *)fontmap;
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ FcPattern *pattern, *font_pattern;
+ FcResult res;
+ int f;
+ PangoFcPatternSet *patterns;
+ FcFontSet *font_patterns;
+ FontsetHashKey key;
+
+ if (!language && context)
+ language = pango_context_get_language (context);
+
+ key.fontmap = fcfontmap;
+ get_context_matrix (context, &key.matrix);
+ key.language = language;
+ key.desc = pango_font_description_copy_static (desc);
+ pango_font_description_unset_fields (key.desc, PANGO_FONT_MASK_SIZE);
+ key.size = get_unscaled_size (fcfontmap, context, desc);
+
+ if (PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get)
+ key.context_key = (gpointer)PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get (fcfontmap, context);
+ else
+ key.context_key = NULL;
+
+ patterns = g_hash_table_lookup (priv->fontset_hash, &key);
+
+ if (patterns == NULL)
+ {
+ double scale_factor = pango_matrix_get_font_scale_factor (&key.matrix);
+ double scaled_size = key.size * scale_factor / PANGO_SCALE;
+ pattern = pango_fc_make_pattern (desc, language,
+ scaled_size,
+ pango_fc_font_map_get_resolution (fcfontmap, context));
+
+ pango_fc_default_substitute (fcfontmap, context, pattern);
+
+ font_patterns = FcFontSort (NULL, pattern, FcTrue, NULL, &res);
+
+ if (!font_patterns)
+ {
+ g_printerr ("No fonts found; this probably means that the fontconfig\n"
+ "library is not correctly configured. You may need to\n"
+ "edit the fonts.conf configuration file. More information\n"
+ "about fontconfig can be found in the fontconfig(3) manual\n"
+ "page and on http://fontconfig.org\n");
+
+ /* There is no point in proceeding; we'll just get a segfault later
+ * on, and a bunch more possibly confusing error messages in between.
+ */
+
+ /* return NULL; */
+ exit (1);
+ }
+
+ patterns = g_slice_new (PangoFcPatternSet);
+ patterns->patterns = g_new (FcPattern *, font_patterns->nfont);
+ patterns->n_patterns = 0;
+ patterns->fontset = NULL;
+ patterns->cache_link = NULL;
+
+ for (f = 0; f < font_patterns->nfont; f++)
+ {
+ font_pattern = FcFontRenderPrepare (NULL, pattern,
+ font_patterns->fonts[f]);
+
+ if (font_pattern)
+ {
+#ifdef FC_PATTERN
+ /* The FC_PATTERN element, which points back to our the original
+ * pattern defeats our hash tables.
+ */
+ FcPatternDel (font_pattern, FC_PATTERN);
+#endif /* FC_PATTERN */
+
+ patterns->patterns[patterns->n_patterns++] = uniquify_pattern (fcfontmap, font_pattern);
+ }
+ }
+
+ FcPatternDestroy (pattern);
+
+ FcFontSetSortDestroy (font_patterns);
+
+ patterns->key = fontset_hash_key_copy (&key);
+ g_hash_table_insert (priv->fontset_hash,
+ patterns->key,
+ patterns);
+ }
+
+ if ((!patterns->cache_link ||
+ patterns->cache_link != priv->fontset_cache->head))
+ pango_fc_font_map_cache_fontset (fcfontmap, patterns);
+
+ pango_font_description_free (key.desc);
+
+ return patterns;
+}
+
+static gboolean
+get_first_font (PangoFontset *fontset,
+ PangoFont *font,
+ gpointer data)
+{
+ *(PangoFont **)data = font;
+
+ return TRUE;
+}
+
+static PangoFont *
+pango_fc_font_map_load_font (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *description)
+{
+ PangoLanguage *language;
+ PangoFontset *fontset;
+ PangoFont *font = NULL;
+
+ if (context)
+ language = pango_context_get_language (context);
+ else
+ language = NULL;
+
+ fontset = pango_font_map_load_fontset (fontmap, context, description, language);
+
+ if (fontset)
+ {
+ pango_fontset_foreach (fontset, get_first_font, &font);
+
+ if (font)
+ g_object_ref (font);
+
+ g_object_unref (fontset);
+ }
+
+ return font;
+}
+
+static void
+pango_fc_pattern_set_free (PangoFcPatternSet *patterns)
+{
+ int i;
+
+ if (patterns->fontset)
+ g_object_unref (patterns->fontset);
+
+ for (i = 0; i < patterns->n_patterns; i++)
+ FcPatternDestroy (patterns->patterns[i]);
+
+ g_free (patterns->patterns);
+ g_slice_free (PangoFcPatternSet, patterns);
+}
+
+static void
+pango_fc_font_map_cache_fontset (PangoFcFontMap *fcfontmap,
+ PangoFcPatternSet *patterns)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ GQueue *cache = priv->fontset_cache;
+
+ if (patterns->cache_link)
+ {
+ /* Already in cache, move to head
+ */
+ if (patterns->cache_link == cache->tail)
+ cache->tail = patterns->cache_link->prev;
+
+ cache->head = g_list_remove_link (cache->head, patterns->cache_link);
+ cache->length--;
+ }
+ else
+ {
+ /* Add to cache initially
+ */
+ if (cache->length == FONTSET_CACHE_SIZE)
+ {
+ PangoFcPatternSet *tmp_patterns = g_queue_pop_tail (cache);
+ tmp_patterns->cache_link = NULL;
+ g_hash_table_remove (priv->fontset_hash, tmp_patterns->key);
+ }
+
+ patterns->cache_link = g_list_prepend (NULL, patterns);
+ }
+
+ g_queue_push_head_link (cache, patterns->cache_link);
+}
+
+static PangoFontset *
+pango_fc_font_map_load_fontset (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *desc,
+ PangoLanguage *language)
+{
+ PangoFcPatternSet *patterns = pango_fc_font_map_get_patterns (fontmap, context, desc, language);
+ PangoFontset *result;
+ int i;
+
+ if (!patterns)
+ return NULL;
+
+ if (!patterns->fontset)
+ {
+ PangoFontsetSimple *simple;
+ simple = pango_fontset_simple_new (language);
+ result = PANGO_FONTSET (simple);
+
+ for (i = 0; i < patterns->n_patterns; i++)
+ {
+ PangoFont *font;
+
+ font = pango_fc_font_map_new_font (fontmap, context, desc,
+ patterns->patterns[i]);
+ if (font)
+ pango_fontset_simple_append (simple, font);
+ }
+
+ patterns->fontset = PANGO_FONTSET (simple);
+ }
+
+ result = g_object_ref (patterns->fontset);
+
+ return result;
+}
+
+static void
+uncache_patterns (PangoFcPatternSet *patterns,
+ PangoFcFontMap *fcfontmap)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+
+ g_hash_table_remove (priv->fontset_hash, patterns->key);
+}
+
+static void
+pango_fc_font_map_clear_fontset_cache (PangoFcFontMap *fcfontmap)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ GQueue *cache = priv->fontset_cache;
+
+ g_list_foreach (cache->head, (GFunc)uncache_patterns, fcfontmap);
+ g_list_free (cache->head);
+ cache->head = NULL;
+ cache->tail = NULL;
+ cache->length = 0;
+}
+
+/**
+ * pango_fc_font_map_cache_clear:
+ * @fcfontmap: a #PangoFcFontmap
+ *
+ * Clear all cached information and fontsets for this font map;
+ * this should be called whenever there is a change in the
+ * output of the default_substitute() virtual function.
+ *
+ * This function is intended to be used only by backend implementations
+ * deriving from #PangoFcFontmap.
+ *
+ * Since: 1.4
+ **/
+void
+pango_fc_font_map_cache_clear (PangoFcFontMap *fcfontmap)
+{
+ fcfontmap->priv->dpi = -1;
+
+ pango_fc_font_map_clear_fontset_cache (fcfontmap);
+}
+
+static void
+pango_fc_font_map_set_coverage (PangoFcFontMap *fcfontmap,
+ PangoFcCoverageKey *key,
+ PangoCoverage *coverage)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ PangoFcCoverageKey *key_dup;
+
+ key_dup = g_malloc (sizeof (PangoFcCoverageKey) + strlen (key->filename) + 1);
+ key_dup->id = key->id;
+ key_dup->filename = (char *) (key_dup + 1);
+ strcpy (key_dup->filename, key->filename);
+
+ g_hash_table_insert (priv->coverage_hash,
+ key_dup, pango_coverage_ref (coverage));
+}
+
+PangoCoverage *
+_pango_fc_font_map_get_coverage (PangoFcFontMap *fcfontmap,
+ PangoFcFont *fcfont)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+ PangoFcCoverageKey key;
+ PangoCoverage *coverage;
+ FcCharSet *charset;
+
+ /*
+ * Assume that coverage information is identified by
+ * a filename/index pair; there shouldn't be any reason
+ * this isn't true, but it's not specified anywhere
+ */
+ if (FcPatternGetString (fcfont->font_pattern, FC_FILE, 0, (FcChar8 **) &key.filename) != FcResultMatch)
+ return NULL;
+
+ if (FcPatternGetInteger (fcfont->font_pattern, FC_INDEX, 0, &key.id) != FcResultMatch)
+ return NULL;
+
+ coverage = g_hash_table_lookup (priv->coverage_hash, &key);
+ if (coverage)
+ return pango_coverage_ref (coverage);
+
+ /*
+ * Pull the coverage out of the pattern, this
+ * doesn't require loading the font
+ */
+ if (FcPatternGetCharSet (fcfont->font_pattern, FC_CHARSET, 0, &charset) != FcResultMatch)
+ return NULL;
+
+ coverage = _pango_fc_font_map_fc_to_coverage (charset);
+
+ pango_fc_font_map_set_coverage (fcfontmap, &key, coverage);
+
+ return coverage;
+}
+
+/**
+ * _pango_fc_font_map_fc_to_coverage:
+ * @charset: #FcCharSet to convert to a #PangoCoverage object.
+ *
+ * Convert the given #FcCharSet into a new #PangoCoverage object. The
+ * caller is responsible for freeing the newly created object.
+ *
+ * Since: 1.6
+ **/
+
+PangoCoverage *
+_pango_fc_font_map_fc_to_coverage (FcCharSet *charset)
+{
+ PangoCoverage *coverage;
+ FcChar32 ucs4, pos;
+ FcChar32 map[FC_CHARSET_MAP_SIZE];
+ int i;
+
+ /*
+ * Convert an Fc CharSet into a pango coverage structure. Sure
+ * would be nice to just use the Fc structure in place...
+ */
+ coverage = pango_coverage_new ();
+ for (ucs4 = FcCharSetFirstPage (charset, map, &pos);
+ ucs4 != FC_CHARSET_DONE;
+ ucs4 = FcCharSetNextPage (charset, map, &pos))
+ {
+ for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
+ {
+ FcChar32 bits = map[i];
+ FcChar32 base = ucs4 + i * 32;
+ int b = 0;
+ bits = map[i];
+ while (bits)
+ {
+ if (bits & 1)
+ pango_coverage_set (coverage, base + b, PANGO_COVERAGE_EXACT);
+
+ bits >>= 1;
+ b++;
+ }
+ }
+ }
+
+ /* Awful hack so Hangul Tone marks get rendered with the same
+ * font and in the same run as other Hangul characters. If a font
+ * covers the first composed Hangul glyph, then it is declared to cover
+ * the Hangul tone marks. This hack probably needs to be formalized
+ * by choosing fonts for scripts rather than individual code points.
+ */
+ if (pango_coverage_get (coverage, 0xac00) == PANGO_COVERAGE_EXACT)
+ {
+ pango_coverage_set (coverage, 0x302e, PANGO_COVERAGE_EXACT);
+ pango_coverage_set (coverage, 0x302f, PANGO_COVERAGE_EXACT);
+ }
+
+ return coverage;
+}
+
+/**
+ * pango_fc_font_map_create_context:
+ * @fcfontmap: a #PangoFcFontMap
+ *
+ * Creates a new context for this fontmap. This function is intended
+ * only for backend implementations deriving from #PangoFcFontmap;
+ * it is possible that a backend will store additional information
+ * needed for correct operation on the #PangoContext after calling
+ * this function.
+ *
+ * Return value: a new #PangoContext
+ *
+ * Since: 1.4
+ **/
+PangoContext *
+pango_fc_font_map_create_context (PangoFcFontMap *fcfontmap)
+{
+ PangoContext *context = pango_context_new ();
+ pango_context_set_font_map (context, PANGO_FONT_MAP (fcfontmap));
+
+ return context;
+}
+
+static void
+cleanup_font (gpointer key,
+ PangoFcFont *fcfont)
+{
+ _pango_fc_font_shutdown (fcfont);
+
+ g_object_unref (fcfont->fontmap);
+ fcfont->fontmap = NULL;
+}
+
+/**
+ * pango_fc_font_map_shutdown:
+ * @fcfontmap: a #PangoFcFontmap
+ *
+ * Clears all cached information for the fontmap and marks
+ * all fonts open for the fontmap as dead. (See the shutdown()
+ * virtual function of #PangoFcFont.) This function might be used
+ * by a backend when the underlying windowing system for the font
+ * map exits. This function is only intended to be called from
+ * only for backend implementations deriving from #PangoFcFontmap.
+ *
+ * Since: 1.4
+ **/
+void
+pango_fc_font_map_shutdown (PangoFcFontMap *fcfontmap)
+{
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+
+ pango_fc_font_map_cache_clear (fcfontmap);
+ g_hash_table_destroy (priv->fontset_hash);
+ priv->fontset_hash = NULL;
+
+ g_hash_table_foreach (priv->font_hash, (GHFunc)cleanup_font, NULL);
+ g_hash_table_destroy (priv->font_hash);
+ priv->font_hash = NULL;
+ priv->closed = TRUE;
+}
+
+static PangoWeight
+pango_fc_convert_weight_to_pango (int fc_weight)
+{
+#ifdef FC_WEIGHT_ULTRABOLD
+ /* fontconfig 2.1 only had light/medium/demibold/bold/black */
+ if (fc_weight < (FC_WEIGHT_ULTRALIGHT + FC_WEIGHT_LIGHT) / 2)
+ return PANGO_WEIGHT_ULTRALIGHT;
+ else if (fc_weight < (FC_WEIGHT_LIGHT + FC_WEIGHT_REGULAR) / 2)
+ return PANGO_WEIGHT_LIGHT;
+ else if (fc_weight < (FC_WEIGHT_REGULAR + FC_WEIGHT_DEMIBOLD) / 2)
+ return PANGO_WEIGHT_NORMAL;
+ /* We group the 500/MEDIUM weight with normal to reduce confusion
+ *
+ * else if (fc_weight < (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
+ * return 500;
+ */
+ else if (fc_weight < (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
+ return PANGO_WEIGHT_SEMIBOLD;
+ else if (fc_weight < (FC_WEIGHT_BOLD + FC_WEIGHT_ULTRABOLD) / 2)
+ return PANGO_WEIGHT_BOLD;
+ else if (fc_weight < (FC_WEIGHT_ULTRABOLD + FC_WEIGHT_BLACK) / 2)
+ return PANGO_WEIGHT_ULTRABOLD;
+ else
+ return PANGO_WEIGHT_HEAVY;
+#else /* fontconfig < 2.2 */
+ if (fc_weight < (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2)
+ return PANGO_WEIGHT_LIGHT;
+ else if (fc_weight < (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
+ return PANGO_WEIGHT_NORMAL;
+ else if (fc_weight < (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
+ return PANGO_WEIGHT_SEMIBOLD;
+ else if (fc_weight < (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
+ return PANGO_WEIGHT_BOLD;
+ else
+ return PANGO_WEIGHT_ULTRABOLD;
+#endif
+}
+
+static PangoStyle
+pango_fc_convert_slant_to_pango (int fc_style)
+{
+ switch (fc_style)
+ {
+ case FC_SLANT_ROMAN:
+ return PANGO_STYLE_NORMAL;
+ case FC_SLANT_ITALIC:
+ return PANGO_STYLE_ITALIC;
+ case FC_SLANT_OBLIQUE:
+ return PANGO_STYLE_OBLIQUE;
+ default:
+ return PANGO_STYLE_NORMAL;
+ }
+}
+
+#ifdef FC_WIDTH
+static PangoStretch
+pango_fc_convert_width_to_pango (int fc_stretch)
+{
+ switch (fc_stretch)
+ {
+ case FC_WIDTH_NORMAL:
+ return PANGO_STRETCH_NORMAL;
+ case FC_WIDTH_ULTRACONDENSED:
+ return PANGO_STRETCH_ULTRA_CONDENSED;
+ case FC_WIDTH_EXTRACONDENSED:
+ return PANGO_STRETCH_EXTRA_CONDENSED;
+ case FC_WIDTH_CONDENSED:
+ return PANGO_STRETCH_CONDENSED;
+ case FC_WIDTH_SEMICONDENSED:
+ return PANGO_STRETCH_SEMI_CONDENSED;
+ case FC_WIDTH_SEMIEXPANDED:
+ return PANGO_STRETCH_SEMI_EXPANDED;
+ case FC_WIDTH_EXPANDED:
+ return PANGO_STRETCH_EXPANDED;
+ case FC_WIDTH_EXTRAEXPANDED:
+ return PANGO_STRETCH_EXTRA_EXPANDED;
+ case FC_WIDTH_ULTRAEXPANDED:
+ return PANGO_STRETCH_ULTRA_EXPANDED;
+ default:
+ return PANGO_STRETCH_NORMAL;
+ }
+}
+#endif
+
+/**
+ * pango_fc_font_description_from_pattern:
+ * @pattern: a #FcPattern
+ * @include_size: if %TRUE, the pattern will include the size from
+ * the @pattern; otherwise the resulting pattern will be unsized.
+ * (only %FC_SIZE is examined, not %FC_PIXEL_SIZE)
+ *
+ * Creates a #PangoFontDescription that matches the specified
+ * Fontconfig pattern as closely as possible. Many possible Fontconfig
+ * pattern values, such as %FC_RASTERIZER or %FC_DPI, don't make sense in
+ * the context of #PangoFontDescription, so will be ignored.
+ *
+ * Return value: a new #PangoFontDescription. Free with
+ * pango_font_description_free().
+ *
+ * Since: 1.4
+ **/
+PangoFontDescription *
+pango_fc_font_description_from_pattern (FcPattern *pattern, gboolean include_size)
+{
+ PangoFontDescription *desc;
+ PangoStyle style;
+ PangoWeight weight;
+ PangoStretch stretch;
+ double size;
+ PangoGravity gravity;
+
+ FcChar8 *s;
+ int i;
+ FcResult res;
+
+ desc = pango_font_description_new ();
+
+ res = FcPatternGetString (pattern, FC_FAMILY, 0, (FcChar8 **) &s);
+ g_assert (res == FcResultMatch);
+
+ pango_font_description_set_family (desc, (gchar *)s);
+
+ if (FcPatternGetInteger (pattern, FC_SLANT, 0, &i) == FcResultMatch)
+ style = pango_fc_convert_slant_to_pango (i);
+ else
+ style = PANGO_STYLE_NORMAL;
+
+ pango_font_description_set_style (desc, style);
+
+ if (FcPatternGetInteger (pattern, FC_WEIGHT, 0, &i) == FcResultMatch)
+ weight = pango_fc_convert_weight_to_pango (i);
+ else
+ weight = PANGO_WEIGHT_NORMAL;
+
+ pango_font_description_set_weight (desc, weight);
+
+#ifdef FC_WIDTH
+ if (FcPatternGetInteger (pattern, FC_WIDTH, 0, &i) == FcResultMatch)
+ stretch = pango_fc_convert_width_to_pango (i);
+ else
+#endif
+ stretch = PANGO_STRETCH_NORMAL;
+
+ pango_font_description_set_stretch (desc, stretch);
+
+ pango_font_description_set_variant (desc, PANGO_VARIANT_NORMAL);
+
+ if (include_size && FcPatternGetDouble (pattern, FC_SIZE, 0, &size) == FcResultMatch)
+ pango_font_description_set_size (desc, size * PANGO_SCALE);
+
+ /* gravity is a bit different. we don't want to set it if it was not set on
+ * the pattern */
+ if (FcPatternGetString (pattern, PANGO_FC_GRAVITY, 0, (FcChar8 **)&s) == FcResultMatch)
+ {
+ GEnumValue *value = g_enum_get_value_by_nick (get_gravity_class (), s);
+ gravity = value->value;
+
+ pango_font_description_set_gravity (desc, gravity);
+ }
+
+ return desc;
+}
+
+/*
+ * PangoFcFace
+ */
+
+static PangoFontDescription *
+make_alias_description (PangoFcFamily *fcfamily,
+ gboolean bold,
+ gboolean italic)
+{
+ PangoFontDescription *desc = pango_font_description_new ();
+
+ pango_font_description_set_family (desc, fcfamily->family_name);
+ pango_font_description_set_style (desc, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+ pango_font_description_set_weight (desc, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+
+ return desc;
+}
+
+static PangoFontDescription *
+pango_fc_face_describe (PangoFontFace *face)
+{
+ PangoFcFace *fcface = PANGO_FC_FACE (face);
+ PangoFcFamily *fcfamily = fcface->family;
+ PangoFontDescription *desc = NULL;
+ FcResult res;
+ FcPattern *match_pattern;
+ FcPattern *result_pattern;
+
+ if (fcface->fake)
+ {
+ if (strcmp (fcface->style, "Regular") == 0)
+ return make_alias_description (fcfamily, FALSE, FALSE);
+ else if (strcmp (fcface->style, "Bold") == 0)
+ return make_alias_description (fcfamily, TRUE, FALSE);
+ else if (strcmp (fcface->style, "Italic") == 0)
+ return make_alias_description (fcfamily, FALSE, TRUE);
+ else /* Bold Italic */
+ return make_alias_description (fcfamily, TRUE, TRUE);
+ }
+
+ match_pattern = FcPatternBuild (NULL,
+ FC_FAMILY, FcTypeString, fcfamily->family_name,
+ FC_STYLE, FcTypeString, fcface->style,
+ NULL);
+
+ g_assert (match_pattern);
+
+ FcConfigSubstitute (NULL, match_pattern, FcMatchPattern);
+ FcDefaultSubstitute (match_pattern);
+
+ result_pattern = FcFontMatch (NULL, match_pattern, &res);
+ if (result_pattern)
+ {
+ desc = pango_fc_font_description_from_pattern (result_pattern, FALSE);
+ FcPatternDestroy (result_pattern);
+ }
+
+ FcPatternDestroy (match_pattern);
+
+ return desc;
+}
+
+static const char *
+pango_fc_face_get_face_name (PangoFontFace *face)
+{
+ PangoFcFace *fcface = PANGO_FC_FACE (face);
+
+ return fcface->style;
+}
+
+static int
+compare_ints (gconstpointer ap,
+ gconstpointer bp)
+{
+ int a = *(int *)ap;
+ int b = *(int *)bp;
+
+ if (a == b)
+ return 0;
+ else if (a > b)
+ return 1;
+ else
+ return -1;
+}
+
+static void
+pango_fc_face_list_sizes (PangoFontFace *face,
+ int **sizes,
+ int *n_sizes)
+{
+ PangoFcFace *fcface = PANGO_FC_FACE (face);
+ FcPattern *pattern;
+ FcFontSet *fontset;
+ FcObjectSet *objectset;
+
+ pattern = FcPatternCreate ();
+ FcPatternAddString (pattern, FC_FAMILY, fcface->family->family_name);
+ FcPatternAddString (pattern, FC_STYLE, fcface->style);
+
+ objectset = FcObjectSetCreate ();
+ FcObjectSetAdd (objectset, FC_PIXEL_SIZE);
+
+ fontset = FcFontList (NULL, pattern, objectset);
+
+ if (fontset)
+ {
+ GArray *size_array;
+ double size, dpi = -1.0;
+ int i, size_i;
+
+ size_array = g_array_new (FALSE, FALSE, sizeof (int));
+
+ for (i = 0; i < fontset->nfont; i++)
+ {
+ if (FcPatternGetDouble (fontset->fonts[i], FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+ {
+ if (dpi < 0)
+ dpi = pango_fc_font_map_get_resolution (fcface->family->fontmap, NULL);
+
+ size_i = (int) (PANGO_SCALE * size * 72.0 / dpi);
+ g_array_append_val (size_array, size_i);
+ }
+ }
+
+ g_array_sort (size_array, compare_ints);
+
+ if (size_array->len == 0)
+ {
+ *n_sizes = 0;
+ if (sizes)
+ *sizes = NULL;
+ g_array_free (size_array, TRUE);
+ }
+ else
+ {
+ *n_sizes = size_array->len;
+ if (sizes)
+ {
+ *sizes = (int *) size_array->data;
+ g_array_free (size_array, FALSE);
+ }
+ else
+ g_array_free (size_array, TRUE);
+ }
+
+ FcFontSetDestroy (fontset);
+ }
+ else
+ {
+ *n_sizes = 0;
+ if (sizes)
+ *sizes = NULL;
+ }
+
+ FcPatternDestroy (pattern);
+ FcObjectSetDestroy (objectset);
+}
+
+static gboolean
+pango_fc_face_is_synthesized (PangoFontFace *face)
+{
+ PangoFcFace *fcface = PANGO_FC_FACE (face);
+
+ return fcface->fake;
+}
+
+static void
+pango_fc_face_class_init (PangoFontFaceClass *class)
+{
+ class->describe = pango_fc_face_describe;
+ class->get_face_name = pango_fc_face_get_face_name;
+ class->list_sizes = pango_fc_face_list_sizes;
+ class->is_synthesized = pango_fc_face_is_synthesized;
+}
+
+static GType
+pango_fc_face_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (G_UNLIKELY (!object_type))
+ {
+ const GTypeInfo object_info =
+ {
+ sizeof (PangoFontFaceClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) pango_fc_face_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoFcFace),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ object_type = g_type_register_static (PANGO_TYPE_FONT_FACE,
+ I_("PangoFcFace"),
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+/*
+ * PangoFcFamily
+ */
+static PangoFcFace *
+create_face (PangoFcFamily *fcfamily,
+ const char *style,
+ gboolean fake)
+{
+ PangoFcFace *face = g_object_new (PANGO_FC_TYPE_FACE, NULL);
+ face->style = g_strdup (style);
+ face->family = fcfamily;
+ face->fake = fake;
+
+ return face;
+}
+
+static void
+pango_fc_family_list_faces (PangoFontFamily *family,
+ PangoFontFace ***faces,
+ int *n_faces)
+{
+ PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
+ PangoFcFontMap *fcfontmap = fcfamily->fontmap;
+ PangoFcFontMapPrivate *priv = fcfontmap->priv;
+
+ if (fcfamily->n_faces < 0)
+ {
+ FcFontSet *fontset;
+ int i;
+
+ if (is_alias_family (fcfamily->family_name) || priv->closed)
+ {
+ fcfamily->n_faces = 4;
+ fcfamily->faces = g_new (PangoFcFace *, fcfamily->n_faces);
+
+ i = 0;
+ fcfamily->faces[i++] = create_face (fcfamily, "Regular", TRUE);
+ fcfamily->faces[i++] = create_face (fcfamily, "Bold", TRUE);
+ fcfamily->faces[i++] = create_face (fcfamily, "Italic", TRUE);
+ fcfamily->faces[i++] = create_face (fcfamily, "Bold Italic", TRUE);
+ }
+ else
+ {
+ FcObjectSet *os = FcObjectSetBuild (FC_STYLE, FC_WEIGHT, FC_SLANT, NULL);
+ FcPattern *pat = FcPatternBuild (NULL,
+ FC_FAMILY, FcTypeString, fcfamily->family_name,
+ NULL);
+
+ enum {
+ REGULAR,
+ ITALIC,
+ BOLD,
+ BOLD_ITALIC
+ };
+ /* Regular, Italic, Bold, Bold Italic */
+ gboolean has_face [4] = { FALSE, FALSE, FALSE, FALSE };
+ PangoFcFace **faces;
+ gint num = 0;
+
+ fontset = FcFontList (NULL, pat, os);
+
+ FcPatternDestroy (pat);
+ FcObjectSetDestroy (os);
+
+ /* at most we have 3 additional artifical faces */
+ faces = g_new (PangoFcFace *, fontset->nfont + 3);
+
+ for (i = 0; i < fontset->nfont; i++)
+ {
+ FcChar8 *style, *font_style = NULL;
+ int weight, slant;
+
+ if (FcPatternGetInteger(fontset->fonts[i], FC_WEIGHT, 0, &weight) != FcResultMatch)
+ weight = FC_WEIGHT_MEDIUM;
+
+ if (FcPatternGetInteger(fontset->fonts[i], FC_SLANT, 0, &slant) != FcResultMatch)
+ slant = FC_SLANT_ROMAN;
+
+ if (FcPatternGetString (fontset->fonts[i], FC_STYLE, 0, &font_style) != FcResultMatch)
+ font_style = NULL;
+
+ if (weight <= FC_WEIGHT_MEDIUM)
+ {
+ if (slant == FC_SLANT_ROMAN)
+ {
+ has_face[REGULAR] = TRUE;
+ style = "Regular";
+ }
+ else
+ {
+ has_face[ITALIC] = TRUE;
+ style = "Italic";
+ }
+ }
+ else
+ {
+ if (slant == FC_SLANT_ROMAN)
+ {
+ has_face[BOLD] = TRUE;
+ style = "Bold";
+ }
+ else
+ {
+ has_face[BOLD_ITALIC] = TRUE;
+ style = "Bold Italic";
+ }
+ }
+
+ if (!font_style)
+ font_style = style;
+ faces[num++] = create_face (fcfamily, font_style, FALSE);
+ }
+
+ if (has_face[REGULAR])
+ {
+ if (!has_face[ITALIC])
+ faces[num++] = create_face (fcfamily, "Italic", TRUE);
+ if (!has_face[BOLD])
+ faces[num++] = create_face (fcfamily, "Bold", TRUE);
+
+ }
+ if ((has_face[REGULAR] || has_face[ITALIC] || has_face[BOLD]) && !has_face[BOLD_ITALIC])
+ faces[num++] = create_face (fcfamily, "Bold Italic", TRUE);
+
+ faces = g_renew (PangoFcFace *, faces, num);
+
+ fcfamily->n_faces = num;
+ fcfamily->faces = faces;
+
+ FcFontSetDestroy (fontset);
+ }
+ }
+
+ if (n_faces)
+ *n_faces = fcfamily->n_faces;
+
+ if (faces)
+ *faces = g_memdup (fcfamily->faces, fcfamily->n_faces * sizeof (PangoFontFace *));
+}
+
+static const char *
+pango_fc_family_get_name (PangoFontFamily *family)
+{
+ PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
+
+ return fcfamily->family_name;
+}
+
+static gboolean
+pango_fc_family_is_monospace (PangoFontFamily *family)
+{
+ PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
+
+ return fcfamily->spacing == FC_MONO ||
+#ifdef FC_DUAL
+ fcfamily->spacing == FC_DUAL ||
+#endif
+ fcfamily->spacing == FC_CHARCELL;
+}
+
+static void
+pango_fc_family_class_init (PangoFontFamilyClass *class)
+{
+ class->list_faces = pango_fc_family_list_faces;
+ class->get_name = pango_fc_family_get_name;
+ class->is_monospace = pango_fc_family_is_monospace;
+}
+
+static void
+pango_fc_family_init (PangoFcFamily *fcfamily)
+{
+ fcfamily->n_faces = -1;
+}
+
+static GType
+pango_fc_family_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (G_UNLIKELY (!object_type))
+ {
+ const GTypeInfo object_info =
+ {
+ sizeof (PangoFontFamilyClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) pango_fc_family_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoFcFamily),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) pango_fc_family_init,
+ NULL /* value_table */
+ };
+
+ object_type = g_type_register_static (PANGO_TYPE_FONT_FAMILY,
+ I_("PangoFcFamily"),
+ &object_info, 0);
+ }
+
+ return object_type;
+}