diff options
author | Kristian Rietveld <kris@gtk.org> | 2010-03-05 21:46:38 +0100 |
---|---|---|
committer | Kristian Rietveld <kris@gtk.org> | 2011-04-14 12:30:03 +0200 |
commit | 87882adf1ccacfb49e8c119b1fcb6d13a0e4731f (patch) | |
tree | e2a7f92ba363d739d75a9a3d36914b8c9948909d /pango/pangocoretext-fontmap.c | |
parent | 08fd5cf33d875c161dd5280b92d1633469943620 (diff) | |
download | pango-87882adf1ccacfb49e8c119b1fcb6d13a0e4731f.tar.gz |
Start implementation of CoreText backend
On Mac OS X systems where CoreText is available (that is, 10.5 or
higher), we will build the CoreText backend instead of the ATSUI
backend. In this case, the ATSUI backend will not be built and
installed. This change is necessary because since Mac OS 10.5
ATSUI has been marked as deprecated.
For older systems, we will have to continue to use the
ATSUI backend.
Diffstat (limited to 'pango/pangocoretext-fontmap.c')
-rw-r--r-- | pango/pangocoretext-fontmap.c | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/pango/pangocoretext-fontmap.c b/pango/pangocoretext-fontmap.c new file mode 100644 index 00000000..b8cf671f --- /dev/null +++ b/pango/pangocoretext-fontmap.c @@ -0,0 +1,952 @@ +/* Pango + * pangocoretext-fontmap.c + * + * Copyright (C) 2000-2003 Red Hat, Inc. + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2010 Kristian Rietveld <kris@gtk.org> + * + * 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. + */ + +#include "config.h" + +#include "pango-fontmap.h" +#include "pangocoretext-private.h" +#include "pango-impl-utils.h" +#include "modules.h" + +#include <Carbon/Carbon.h> + +typedef struct _FontHashKey FontHashKey; + +struct _PangoCoreTextFamily +{ + PangoFontFamily parent_instance; + + char *family_name; + + guint is_monospace : 1; + + PangoFontFace **faces; + gint n_faces; +}; + +#define PANGO_TYPE_CORE_TEXT_FAMILY (pango_core_text_family_get_type ()) +#define PANGO_CORE_TEXT_FAMILY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_CORE_TEXT_FAMILY, PangoCoreTextFamily)) +#define PANGO_IS_CORE_TEXT_FAMILY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_CORE_TEXT_FAMILY)) + +#define PANGO_TYPE_CORE_TEXT_FACE (pango_core_text_face_get_type ()) +#define PANGO_CORE_TEXT_FACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_CORE_TEXT_FACE, PangoCoreTextFace)) +#define PANGO_IS_CORE_TEXT_FACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_CORE_TEXT_FACE)) + +struct _PangoCoreTextFace +{ + PangoFontFace parent_instance; + + PangoCoreTextFamily *family; + + PangoCoverage *coverage; + + char *postscript_name; + char *style_name; + + float weight; + int traits; +}; + +static GType pango_core_text_family_get_type (void); +static GType pango_core_text_face_get_type (void); + +static gpointer pango_core_text_family_parent_class; +static gpointer pango_core_text_face_parent_class; + +static const char * +get_real_family (const char *family_name) +{ + switch (family_name[0]) + { + case 'm': + case 'M': + if (g_ascii_strcasecmp (family_name, "monospace") == 0) + return "Courier"; + break; + case 's': + case 'S': + if (g_ascii_strcasecmp (family_name, "sans") == 0) + return "Helvetica"; + else if (g_ascii_strcasecmp (family_name, "serif") == 0) + return "Times"; + break; + } + + return family_name; +} + +static gchar * +gchar_from_cf_string (CFStringRef str) +{ + CFIndex len; + gchar *buffer; + + /* GetLength returns the number of UTF-16 pairs, so this number + * times 2 should definitely gives us enough space for UTF8. + * We add one for the terminating zero. + */ + len = CFStringGetLength (str) * 2 + 1; + buffer = g_new0 (char, len); + CFStringGetCString (str, buffer, len, kCFStringEncodingUTF8); + + return buffer; +} + +static PangoCoverage * +pango_coverage_from_cf_charset (CFCharacterSetRef charset) +{ + CFIndex i, length; + CFDataRef bitmap; + const UInt8 *ptr; + PangoCoverage *coverage; + + coverage = pango_coverage_new (); + + bitmap = CFCharacterSetCreateBitmapRepresentation (kCFAllocatorDefault, + charset); + + /* We only handle the BMP plane */ + length = MIN (CFDataGetLength (bitmap), 8192); + ptr = CFDataGetBytePtr (bitmap); + + /* FIXME: can and should this be done more efficiently? */ + for (i = 0; i < length; i++) + { + int j; + + for (j = 0; j < 8; j++) + pango_coverage_set (coverage, i * 8 + j, + ((ptr[i] & (1 << j)) == (1 << j)) ? + PANGO_COVERAGE_EXACT : PANGO_COVERAGE_NONE); + } + + CFRelease (bitmap); + + return coverage; +} + +static void +pango_core_text_family_list_faces (PangoFontFamily *family, + PangoFontFace ***faces, + int *n_faces) +{ + PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family); + + if (ctfamily->n_faces < 0) + { + GList *l; + GList *faces = NULL; + const char *real_family = get_real_family (ctfamily->family_name); + CTFontCollectionRef collection; + CFArrayRef ctfaces; + CFArrayRef font_descriptors; + CFDictionaryRef attributes; + CFIndex i, count; + + CFTypeRef keys[] = { + (CFTypeRef) kCTFontFamilyNameAttribute + }; + + CFStringRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, + real_family, + kCFStringEncodingUTF8) + }; + + CTFontDescriptorRef descriptors[1]; + + attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **)keys, + (const void **)values, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + descriptors[0] = CTFontDescriptorCreateWithAttributes (attributes); + font_descriptors = CFArrayCreate (kCFAllocatorDefault, + (const void **)descriptors, + 1, + &kCFTypeArrayCallBacks); + collection = CTFontCollectionCreateWithFontDescriptors (font_descriptors, + NULL); + + ctfaces = CTFontCollectionCreateMatchingFontDescriptors (collection); + + /* FIXME: do we need the code for the synthetic italic faces? */ + count = CFArrayGetCount (ctfaces); + for (i = 0; i < count; i++) + { + int font_traits; + char *buffer; + CFStringRef str; + CFNumberRef number; + CGFloat value; + CFDictionaryRef dict; + CFCharacterSetRef charset; + CTFontDescriptorRef desc = CFArrayGetValueAtIndex (ctfaces, i); + PangoCoreTextFace *face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE, + NULL); + + face->family = ctfamily; + + /* Get font name */ + str = CTFontDescriptorCopyAttribute (desc, kCTFontNameAttribute); + buffer = gchar_from_cf_string (str); + + /* We strdup again to save space. */ + face->postscript_name = g_strdup (buffer); + + CFRelease (str); + g_free (buffer); + + /* Get style name */ + str = CTFontDescriptorCopyAttribute (desc, kCTFontStyleNameAttribute); + buffer = gchar_from_cf_string (str); + + face->style_name = g_strdup (buffer); + + CFRelease (str); + g_free (buffer); + + /* Get font traits, symbolic traits */ + dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); + number = (CFNumberRef)CFDictionaryGetValue (dict, + kCTFontWeightTrait); + if (CFNumberGetValue (number, kCFNumberCGFloatType, &value)) + /* Map value from range [-1.0, 1.0] to range [1, 14] */ + face->weight = (value + 1.0f) * 6.5f + 1; + else + face->weight = PANGO_WEIGHT_NORMAL; + + number = (CFNumberRef)CFDictionaryGetValue (dict, + kCTFontSymbolicTrait); + if (CFNumberGetValue (number, kCFNumberIntType, &font_traits)) + { + face->traits = font_traits; + } + CFRelease (dict); + + /* Get font coverage */ + charset = CTFontDescriptorCopyAttribute (desc, + kCTFontCharacterSetAttribute); + face->coverage = pango_coverage_from_cf_charset (charset); + CFRelease (charset); + + faces = g_list_prepend (faces, face); + } + + CFRelease (font_descriptors); + CFRelease (attributes); + CFRelease (ctfaces); + + ctfamily->n_faces = g_list_length (faces); + ctfamily->faces = g_new (PangoFontFace *, ctfamily->n_faces); + + for (l = faces, i = 0; l; l = l->next, i++) + ctfamily->faces[i] = l->data; + + g_list_free (faces); + } + + if (n_faces) + *n_faces = ctfamily->n_faces; + + if (faces) + *faces = g_memdup (ctfamily->faces, ctfamily->n_faces * sizeof (PangoFontFace *)); +} + +static const char * +pango_core_text_family_get_name (PangoFontFamily *family) +{ + PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family); + + return ctfamily->family_name; +} + +static gboolean +pango_core_text_family_is_monospace (PangoFontFamily *family) +{ + PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family); + + return ctfamily->is_monospace; +} + +static void +pango_core_text_family_finalize (GObject *object) +{ + PangoCoreTextFamily *family = PANGO_CORE_TEXT_FAMILY (object); + int i; + + g_free (family->family_name); + + if (family->n_faces != -1) + { + for (i = 0; i < family->n_faces; i++) + g_object_unref (family->faces[i]); + + g_free (family->faces); + } + + G_OBJECT_CLASS (pango_core_text_family_parent_class)->finalize (object); +} + +static void +pango_core_text_family_class_init (PangoFontFamilyClass *class) +{ + GObjectClass *object_class = (GObjectClass *)class; + int i; + + pango_core_text_family_parent_class = g_type_class_peek_parent (class); + + object_class->finalize = pango_core_text_family_finalize; + + class->list_faces = pango_core_text_family_list_faces; + class->get_name = pango_core_text_family_get_name; + class->is_monospace = pango_core_text_family_is_monospace; + + for (i = 0; _pango_included_core_text_modules[i].list; i++) + pango_module_register (&_pango_included_core_text_modules[i]); +} + +static void +pango_core_text_family_init (PangoCoreTextFamily *family) +{ + family->n_faces = -1; +} + +static GType +pango_core_text_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_core_text_family_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PangoCoreTextFamily), + 0, /* n_preallocs */ + (GInstanceInitFunc) pango_core_text_family_init, + }; + + object_type = g_type_register_static (PANGO_TYPE_FONT_FAMILY, + I_("PangoCoreTextFamily"), + &object_info, 0); + } + + return object_type; +} + +static PangoFontDescription * +pango_core_text_face_describe (PangoFontFace *face) +{ + PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face); + PangoFontDescription *description; + PangoWeight pango_weight; + PangoStyle pango_style; + PangoVariant pango_variant; + int weight; + + description = pango_font_description_new (); + + pango_font_description_set_family (description, ctface->family->family_name); + + weight = ctface->weight; + + switch (weight) + { + case 1: + case 2: + pango_weight = PANGO_WEIGHT_ULTRALIGHT; + break; + + case 3: + case 4: + pango_weight = PANGO_WEIGHT_LIGHT; + break; + + case 5: + case 6: + pango_weight = PANGO_WEIGHT_NORMAL; + break; + + case 7: + case 8: + pango_weight = PANGO_WEIGHT_SEMIBOLD; + break; + + case 9: + case 10: + pango_weight = PANGO_WEIGHT_BOLD; + break; + + case 11: + case 12: + pango_weight = PANGO_WEIGHT_ULTRABOLD; + break; + + case 13: + case 14: + pango_weight = PANGO_WEIGHT_HEAVY; + break; + + default: + g_assert_not_reached (); + }; + + if (ctface->traits & kCTFontItalicTrait) + pango_style = PANGO_STYLE_ITALIC; + else + pango_style = PANGO_STYLE_NORMAL; + + /* FIXME: How can this be figured using CoreText? */ +#if 0 + if (ctface->traits & NSSmallCapsFontMask) + pango_variant = PANGO_VARIANT_SMALL_CAPS; + else +#endif + pango_variant = PANGO_VARIANT_NORMAL; + + pango_font_description_set_weight (description, pango_weight); + pango_font_description_set_style (description, pango_style); + pango_font_description_set_variant (description, pango_variant); + + return description; +} + +static const char * +pango_core_text_face_get_face_name (PangoFontFace *face) +{ + PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face); + + return ctface->style_name; +} + +static void +pango_core_text_face_list_sizes (PangoFontFace *face, + int **sizes, + int *n_sizes) +{ + *n_sizes = 0; + *sizes = NULL; +} + +static void +pango_core_text_face_finalize (GObject *object) +{ + PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (object); + + if (ctface->coverage) + pango_coverage_unref (ctface->coverage); + + g_free (ctface->postscript_name); + g_free (ctface->style_name); + + G_OBJECT_CLASS (pango_core_text_face_parent_class)->finalize (object); +} + +static void +pango_core_text_face_class_init (PangoFontFaceClass *class) +{ + GObjectClass *object_class = (GObjectClass *)class; + + pango_core_text_face_parent_class = g_type_class_peek_parent (class); + + object_class->finalize = pango_core_text_face_finalize; + + class->describe = pango_core_text_face_describe; + class->get_face_name = pango_core_text_face_get_face_name; + class->list_sizes = pango_core_text_face_list_sizes; +} + +GType +pango_core_text_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_core_text_face_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PangoCoreTextFace), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (PANGO_TYPE_FONT_FACE, + I_("PangoCoreTextFace"), + &object_info, 0); + } + + return object_type; +} + +const char * +_pango_core_text_face_get_postscript_name (PangoCoreTextFace *face) +{ + return face->postscript_name; +} + +PangoCoverage * +_pango_core_text_face_get_coverage (PangoCoreTextFace *face, + PangoLanguage *language) +{ + return face->coverage; +} + +static void pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class); +static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap); + +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); + +G_DEFINE_TYPE (PangoCoreTextFontMap, pango_core_text_font_map, PANGO_TYPE_FONT_MAP); + +static void +pango_core_text_font_map_finalize (GObject *object) +{ + PangoCoreTextFontMap *fontmap = PANGO_CORE_TEXT_FONT_MAP (object); + + g_hash_table_destroy (fontmap->font_hash); + g_hash_table_destroy (fontmap->families); + + G_OBJECT_CLASS (pango_core_text_font_map_parent_class)->finalize (object); +} + +struct _FontHashKey { + PangoCoreTextFontMap *fontmap; + PangoMatrix matrix; + PangoFontDescription *desc; + char *postscript_name; + 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 +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 && + pango_font_description_equal (key_a->desc, key_b->desc) && + strcmp (key_a->postscript_name, key_b->postscript_name) == 0) + { + if (key_a->context_key) + return PANGO_CORE_TEXT_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_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap, + key->context_key); + + hash ^= g_str_hash (key->postscript_name); + + return (hash ^ pango_font_description_hash (key->desc)); +} + +static void +font_hash_key_free (FontHashKey *key) +{ + if (key->context_key) + PANGO_CORE_TEXT_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->desc = pango_font_description_copy (old->desc); + key->postscript_name = g_strdup (old->postscript_name); + if (old->context_key) + key->context_key = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap, old->context_key); + else + key->context_key = NULL; + + return key; +} + + +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 (PangoCoreTextFontMap *fcfontmap, + PangoContext *context, + FontHashKey *key) +{ + key->fontmap = fcfontmap; + get_context_matrix (context, &key->matrix); + + if (PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get) + key->context_key = (gpointer)PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get (fcfontmap, context); + else + key->context_key = NULL; +} + +static void +pango_core_text_font_map_add (PangoCoreTextFontMap *ctfontmap, + PangoContext *context, + PangoCoreTextFont *ctfont) +{ + FontHashKey key; + FontHashKey *key_copy; + PangoCoreTextFace *face; + + _pango_core_text_font_set_font_map (ctfont, ctfontmap); + + font_hash_key_for_context (ctfontmap, context, &key); + face = _pango_core_text_font_get_face (ctfont); + key.postscript_name = (char *)_pango_core_text_face_get_postscript_name (face); + key.desc = _pango_core_text_font_get_font_description (ctfont); + + key_copy = font_hash_key_copy (&key); + _pango_core_text_font_set_context_key (ctfont, key_copy->context_key); + g_hash_table_insert (ctfontmap->font_hash, key_copy, g_object_ref (ctfont)); +} + +static PangoCoreTextFont * +pango_core_text_font_map_lookup (PangoCoreTextFontMap *ctfontmap, + PangoContext *context, + PangoFontDescription *desc, + PangoCoreTextFace *face) +{ + FontHashKey key; + + font_hash_key_for_context (ctfontmap, context, &key); + key.postscript_name = (char *)_pango_core_text_face_get_postscript_name (face); + key.desc = desc; + + return g_hash_table_lookup (ctfontmap->font_hash, &key); +} + +static gboolean +find_best_match (PangoCoreTextFamily *font_family, + const PangoFontDescription *description, + PangoFontDescription **best_description, + PangoCoreTextFace **best_face) +{ + PangoFontDescription *new_desc; + int i; + + *best_description = NULL; + *best_face = NULL; + + for (i = 0; i < font_family->n_faces; i++) + { + new_desc = pango_font_face_describe (font_family->faces[i]); + + if (pango_font_description_better_match (description, *best_description, new_desc)) + { + pango_font_description_free (*best_description); + *best_description = new_desc; + *best_face = (PangoCoreTextFace *)font_family->faces[i]; + } + else + pango_font_description_free (new_desc); + } + + if (*best_description) + return TRUE; + + return FALSE; +} + +static PangoFont * +pango_core_text_font_map_load_font (PangoFontMap *fontmap, + PangoContext *context, + const PangoFontDescription *description) +{ + PangoCoreTextFontMap *ctfontmap = (PangoCoreTextFontMap *)fontmap; + PangoCoreTextFamily *font_family; + const gchar *family; + gchar *name; + gint size; + + size = pango_font_description_get_size (description); + if (size < 0) + return NULL; + + family = pango_font_description_get_family (description); + family = family ? family : ""; + name = g_utf8_casefold (family, -1); + font_family = g_hash_table_lookup (ctfontmap->families, name); + g_free (name); + + if (font_family) + { + PangoFontDescription *best_description; + PangoCoreTextFace *best_face; + PangoCoreTextFont *best_font; + + /* Force a listing of the available faces */ + pango_font_family_list_faces ((PangoFontFamily *)font_family, NULL, NULL); + + if (!find_best_match (font_family, description, &best_description, &best_face)) + return NULL; + + pango_font_description_set_size (best_description, size); + + best_font = pango_core_text_font_map_lookup (ctfontmap, + context, + best_description, + best_face); + + if (best_font) + g_object_ref (best_font); + else + { + PangoCoreTextFontMapClass *klass; + + klass = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (ctfontmap); + best_font = klass->create_font (ctfontmap, context, + best_face, best_description); + + if (best_font) + pango_core_text_font_map_add (ctfontmap, context, best_font); + /* FIXME: Handle the else case here. */ + } + + pango_font_description_free (best_description); + + return (PangoFont *)best_font; + } + + return NULL; +} + +static void +list_families_foreach (gpointer key, + gpointer value, + gpointer user_data) +{ + GSList **list = user_data; + + *list = g_slist_prepend (*list, value); +} + +static void +pango_core_text_font_map_list_families (PangoFontMap *fontmap, + PangoFontFamily ***families, + int *n_families) +{ + GSList *family_list = NULL; + GSList *tmp_list; + PangoCoreTextFontMap *ctfontmap = (PangoCoreTextFontMap *)fontmap; + + if (!n_families) + return; + + g_hash_table_foreach (ctfontmap->families, + list_families_foreach, &family_list); + + *n_families = g_slist_length (family_list); + + if (families) + { + int i = 0; + + *families = g_new (PangoFontFamily *, *n_families); + + tmp_list = family_list; + while (tmp_list) + { + (*families)[i] = tmp_list->data; + i++; + tmp_list = tmp_list->next; + } + } + + g_slist_free (family_list); +} + +static void +pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap) +{ + PangoCoreTextFamily *family; + CTFontCollectionRef collection; + CFArrayRef ctfaces; + CFIndex i, count; + + ctfontmap->families = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_object_unref); + + + ctfontmap->font_hash = g_hash_table_new_full ((GHashFunc)font_hash_key_hash, + (GEqualFunc)font_hash_key_equal, + (GDestroyNotify)font_hash_key_free, + NULL); + + collection = CTFontCollectionCreateFromAvailableFonts (0); + ctfaces = CTFontCollectionCreateMatchingFontDescriptors (collection); + count = CFArrayGetCount (ctfaces); + + for (i = 0; i < count; i++) + { + int font_traits; + char *buffer; + char *family_name; + CFStringRef str; + CFNumberRef number; + CFDictionaryRef dict; + CTFontDescriptorRef desc = CFArrayGetValueAtIndex (ctfaces, i); + + str = CTFontDescriptorCopyAttribute (desc, kCTFontFamilyNameAttribute); + buffer = gchar_from_cf_string (str); + + family_name = g_utf8_casefold (buffer, -1); + + CFRelease (str); + g_free (buffer); + + family = g_hash_table_lookup (ctfontmap->families, family_name); + if (!family) + { + family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL); + g_hash_table_insert (ctfontmap->families, g_strdup (family_name), + family); + + family->family_name = family_name; + family_name = NULL; + } + + if (family_name) + g_free (family_name); + + /* We assume that all faces in the family are monospaced or none. */ + dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); + number = (CFNumberRef)CFDictionaryGetValue (dict, + kCTFontSymbolicTrait); + + if (CFNumberGetValue (number, kCFNumberIntType, &font_traits)) + { + if (font_traits & kCTFontMonoSpaceTrait) + family->is_monospace = TRUE; + } + + CFRelease (dict); + } + + /* Insert aliases */ + family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL); + family->family_name = g_strdup ("Sans"); + g_hash_table_insert (ctfontmap->families, + g_utf8_casefold (family->family_name, -1), family); + + family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL); + family->family_name = g_strdup ("Serif"); + g_hash_table_insert (ctfontmap->families, + g_utf8_casefold (family->family_name, -1), family); + + family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL); + family->family_name = g_strdup ("Monospace"); + family->is_monospace = TRUE; + g_hash_table_insert (ctfontmap->families, + g_utf8_casefold (family->family_name, -1), family); +} + +static void +pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (class); + + object_class->finalize = pango_core_text_font_map_finalize; + + fontmap_class->load_font = pango_core_text_font_map_load_font; + fontmap_class->list_families = pango_core_text_font_map_list_families; + fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_CORE_TEXT; +} |