/* Pango * pangocoretext-fontmap.c * * Copyright (C) 2000-2003 Red Hat, Inc. * Copyright (C) 2005-2007 Imendio AB * Copyright (C) 2010 Kristian Rietveld * * 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 typedef struct _FontHashKey FontHashKey; typedef struct _PangoCoreTextFontset PangoCoreTextFontset; #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_CORE_TEXT_FAMILY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_CORE_TEXT_FAMILY, PangoCoreTextFamilyClass)) #define PANGO_IS_CORE_TEXT_FAMILY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_CORE_TEXT_FAMILY)) #define PANGO_CORE_TEXT_FAMILY_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), PANGO_CORE_TEXT_FAMILY, PangoCoreTextFamilyClass)) #define PANGO_TYPE_CORE_TEXT_FONTSET (pango_core_text_fontset_get_type ()) #define PANGO_CORE_TEXT_FONTSET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_CORE_TEXT_FONTSET, PangoCoreTextFontset)) #define PANGO_IS_CORE_TEXT_FONTSET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_CORE_TEXT_FONTSET)) struct _PangoCoreTextFamily { PangoFontFamily parent_instance; char *family_name; guint is_monospace : 1; PangoFontFace **faces; gint n_faces; }; struct _PangoCoreTextFamilyClass { PangoFontFamilyClass parent_class; }; typedef struct _PangoCoreTextFamilyClass PangoCoreTextFamilyClass; #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)) #define PANGO_CORE_TEXT_FACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_CORE_TEXT_FACE, PangoCoreTextFaceClass)) #define PANGO_IS_CORE_TEXT_FACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_CORE_TEXT_FACE)) #define PANGO_CORE_TEXT_FACE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), PANGO_CORE_TEXT_FACE, PangoCoreTextFaceClass)) struct _PangoCoreTextFace { PangoFontFace parent_instance; PangoCoreTextFamily *family; CTFontDescriptorRef ctfontdescriptor; char *style_name; PangoWeight weight; CTFontSymbolicTraits traits; guint synthetic_italic : 1; }; struct _PangoCoreTextFaceClass { PangoFontFaceClass parent_class; }; typedef struct _PangoCoreTextFaceClass PangoCoreTextFaceClass; static GType pango_core_text_family_get_type (void); static GType pango_core_text_face_get_type (void); static GType pango_core_text_fontset_get_type (void); static PangoCoreTextFontset *pango_core_text_fontset_new (PangoCoreTextFontsetKey *key, const PangoFontDescription *description); static PangoCoreTextFontsetKey *pango_core_text_fontset_get_key (PangoCoreTextFontset *fontset); /* * Helper functions to translate CoreText data to Pango */ typedef struct { float ct_weight; PangoWeight pango_weight; } PangoCTWeight; #define ct_weight_min -0.7f #define ct_weight_max 0.8f /* This map is based on empirical data from analyzing a large collection of * fonts and comparing the opentype value with the value that OSX returns. * see: https://bugzilla.gnome.org/show_bug.cgi?id=766148 */ static const PangoCTWeight ct_weight_map[] = { { ct_weight_min, PANGO_WEIGHT_THIN }, { -0.5, PANGO_WEIGHT_ULTRALIGHT }, { -0.23, PANGO_WEIGHT_LIGHT }, { -0.115, PANGO_WEIGHT_SEMILIGHT }, { 0.00, PANGO_WEIGHT_NORMAL }, { 0.2, PANGO_WEIGHT_MEDIUM }, { 0.3, PANGO_WEIGHT_SEMIBOLD }, { 0.4, PANGO_WEIGHT_BOLD }, { 0.6, PANGO_WEIGHT_ULTRABOLD }, { ct_weight_max, PANGO_WEIGHT_HEAVY } }; static const char * get_real_family (const char *family_name) { switch (family_name[0]) { case 'c': case 'C': if (g_ascii_strcasecmp (family_name, "cursive") == 0) return "Apple Chancery"; break; case 'f': case 'F': if (g_ascii_strcasecmp (family_name, "fantasy") == 0) return "Papyrus"; break; 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"; else if (g_ascii_strcasecmp (family_name, "system-ui") == 0) return ".AppleSystemUIFont"; 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 char * ct_font_descriptor_get_family_name (CTFontDescriptorRef desc, gboolean may_fail) { CFStringRef cf_str; char *buffer; cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontFamilyNameAttribute); if (!cf_str) { int i; /* No font family name is set, try to retrieve font name and deduce * the family name from that instead. */ cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontNameAttribute); if (!cf_str) { if (may_fail) return NULL; /* This font is likely broken, return a default family name ... */ return g_strdup ("Sans"); } buffer = gchar_from_cf_string (cf_str); CFRelease (cf_str); for (i = 0; i < strlen (buffer); i++) if (buffer[i] == '-') break; if (i < strlen (buffer)) { char *ret; ret = g_strndup (buffer, i); g_free (buffer); return ret; } else return buffer; } /* else */ buffer = gchar_from_cf_string (cf_str); CFRelease (cf_str); return buffer; } static char * ct_font_descriptor_get_style_name (CTFontDescriptorRef desc) { CFStringRef cf_str; char *buffer; cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontStyleNameAttribute); if (!cf_str) return NULL; buffer = gchar_from_cf_string (cf_str); CFRelease (cf_str); return buffer; } static CTFontSymbolicTraits ct_font_descriptor_get_traits (CTFontDescriptorRef desc) { CFDictionaryRef dict; CFNumberRef cf_number; SInt64 traits; /* This is interesting, the value stored is a CTFontSymbolicTraits which * is defined as uint32_t. CFNumber does not have an obvious type which * deals with unsigned values. Upon inspection with CFNumberGetType, * it turns out this value is stored as SInt64, so we use that to * obtain the value from the CFNumber. */ dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); cf_number = (CFNumberRef)CFDictionaryGetValue (dict, kCTFontSymbolicTrait); if (!CFNumberGetValue (cf_number, kCFNumberSInt64Type, &traits)) traits = 0; CFRelease (dict); return (CTFontSymbolicTraits)traits; } static CTFontDescriptorRef cf_font_descriptor_copy_with_traits (CTFontDescriptorRef desc, const CTFontSymbolicTraits traits) { CFMutableDictionaryRef dict, traits_dict; CFDictionaryRef tmp; CTFontDescriptorRef new_desc; SInt64 tmp_traits; tmp = CTFontDescriptorCopyAttributes (desc); dict = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, tmp); CFRelease (tmp); tmp = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); traits_dict = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, tmp); CFRelease (tmp); tmp_traits = traits; CFDictionarySetValue (traits_dict, (CFTypeRef) kCTFontSymbolicTrait, CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &tmp_traits)); CFDictionarySetValue (dict, (CFTypeRef)kCTFontTraitsAttribute, traits_dict); new_desc = CTFontDescriptorCreateCopyWithAttributes (desc, dict); CFRelease (dict); return new_desc; } static int lerp(float x, float x1, float x2, int y1, int y2) { float dx = x2 - x1; int dy = y2 - y1; return y1 + (dy*(x-x1) + dx/2) / dx; } static PangoWeight ct_font_descriptor_get_weight (CTFontDescriptorRef desc) { CFDictionaryRef dict; CFNumberRef cf_number; CGFloat value; PangoWeight weight = PANGO_WEIGHT_NORMAL; dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); cf_number = (CFNumberRef)CFDictionaryGetValue (dict, kCTFontWeightTrait); if (cf_number != NULL && CFNumberGetValue (cf_number, kCFNumberCGFloatType, &value)) { if (value < ct_weight_min || value > ct_weight_max) { /* This is really an error */ weight = PANGO_WEIGHT_NORMAL; } else { guint i; for (i = 1; value > ct_weight_map[i].ct_weight; ++i) ; weight = lerp(value, ct_weight_map[i-1].ct_weight, ct_weight_map[i].ct_weight, ct_weight_map[i-1].pango_weight, ct_weight_map[i].pango_weight); } } else weight = PANGO_WEIGHT_NORMAL; CFRelease (dict); return weight; } static gboolean ct_font_descriptor_is_small_caps (CTFontDescriptorRef desc) { CFIndex i, count; CFArrayRef array; CFStringRef str; gboolean retval = FALSE; /* See http://stackoverflow.com/a/4811371 for why this works and an * explanation of the magic number "3" used below. */ array = CTFontDescriptorCopyAttribute (desc, kCTFontFeaturesAttribute); if (!array) return FALSE; str = CFStringCreateWithCString (NULL, "CTFeatureTypeIdentifier", kCFStringEncodingASCII); count = CFArrayGetCount (array); for (i = 0; i < count; i++) { CFDictionaryRef dict = CFArrayGetValueAtIndex (array, i); CFNumberRef num; num = (CFNumberRef)CFDictionaryGetValue (dict, str); if (num) { int value = 0; if (CFNumberGetValue (num, kCFNumberSInt32Type, &value) && value == 3) { /* This font supports small caps. */ retval = TRUE; break; } } } CFRelease (str); CFRelease (array); return retval; } static inline gboolean pango_core_text_style_name_is_oblique (const char *style_name) { if (!style_name) return FALSE; return g_strrstr (style_name, "Oblique") != NULL; } PangoFontDescription * _pango_core_text_font_description_from_ct_font_descriptor (CTFontDescriptorRef desc) { SInt64 font_traits; char *family_name; char *style_name; PangoFontDescription *font_desc; CFNumberRef cf_number; CGFloat pointsize; font_desc = pango_font_description_new (); /* Family name */ /* FIXME: Should we actually retrieve the family name from the list of * families in a font map? */ family_name = ct_font_descriptor_get_family_name (desc, FALSE); pango_font_description_set_family (font_desc, family_name); g_free (family_name); /* Size (if we have one) */ cf_number = CTFontDescriptorCopyAttribute (desc, kCTFontSizeAttribute); if (cf_number != NULL && CFNumberGetValue (cf_number, kCFNumberCGFloatType, &pointsize)) pango_font_description_set_size (font_desc, (pointsize / (96./72.)) * 1024); /* Weight */ pango_font_description_set_weight (font_desc, ct_font_descriptor_get_weight (desc)); /* Font traits, style name; from this we deduce style and variant */ font_traits = ct_font_descriptor_get_traits (desc); style_name = ct_font_descriptor_get_style_name (desc); if ((font_traits & kCTFontItalicTrait) == kCTFontItalicTrait) pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC); else if (pango_core_text_style_name_is_oblique (style_name)) pango_font_description_set_style (font_desc, PANGO_STYLE_OBLIQUE); else pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL); if ((font_traits & kCTFontCondensedTrait) == kCTFontCondensedTrait) pango_font_description_set_stretch (font_desc, PANGO_STRETCH_CONDENSED); if (ct_font_descriptor_is_small_caps (desc)) pango_font_description_set_variant (font_desc, PANGO_VARIANT_SMALL_CAPS); else pango_font_description_set_variant (font_desc, PANGO_VARIANT_NORMAL); g_free (style_name); return font_desc; } /* * PangoCoreTextFace */ static inline gboolean pango_core_text_face_is_oblique (PangoCoreTextFace *face) { return pango_core_text_style_name_is_oblique (face->style_name); } static void pango_core_text_face_make_italic (PangoCoreTextFace *ctface, gboolean synthetic_italic) { CTFontDescriptorRef new_desc; ctface->traits |= kCTFontItalicTrait; if (synthetic_italic) ctface->synthetic_italic = TRUE; /* Update the font descriptor */ new_desc = cf_font_descriptor_copy_with_traits (ctface->ctfontdescriptor, ctface->traits); CFRelease (ctface->ctfontdescriptor); ctface->ctfontdescriptor = new_desc; } static inline PangoCoreTextFace * pango_core_text_face_copy (const PangoCoreTextFace *old) { PangoCoreTextFace *face; face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE, NULL); face->family = old->family; face->ctfontdescriptor = CFRetain (old->ctfontdescriptor); face->style_name = g_strdup (old->style_name); face->weight = old->weight; face->traits = old->traits; face->synthetic_italic = old->synthetic_italic; return face; } static inline PangoCoreTextFace * pango_core_text_face_from_ct_font_descriptor (CTFontDescriptorRef desc) { PangoCoreTextFace *face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE, NULL); face->synthetic_italic = FALSE; face->ctfontdescriptor = CFRetain (desc); face->style_name = ct_font_descriptor_get_style_name (desc); face->traits = ct_font_descriptor_get_traits (desc); face->weight = ct_font_descriptor_get_weight (desc); return face; } static PangoFontDescription * pango_core_text_face_describe (PangoFontFace *face) { PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face); return _pango_core_text_font_description_from_ct_font_descriptor (ctface->ctfontdescriptor); } 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; if (sizes) *sizes = NULL; } G_DEFINE_TYPE (PangoCoreTextFace, pango_core_text_face, PANGO_TYPE_FONT_FACE); static void pango_core_text_face_init (PangoCoreTextFace *face) { face->family = NULL; } static void pango_core_text_face_finalize (GObject *object) { PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (object); g_free (ctface->style_name); CFRelease (ctface->ctfontdescriptor); G_OBJECT_CLASS (pango_core_text_face_parent_class)->finalize (object); } static gboolean pango_core_text_face_is_synthesized (PangoFontFace *face) { PangoCoreTextFace *cface = PANGO_CORE_TEXT_FACE (face); return cface->synthetic_italic; } static PangoFontFamily * pango_core_text_face_get_family (PangoFontFace *face) { PangoCoreTextFace *cface = PANGO_CORE_TEXT_FACE (face); return PANGO_FONT_FAMILY (cface->family); } static void pango_core_text_face_class_init (PangoCoreTextFaceClass *klass) { GObjectClass *object_class = (GObjectClass *)klass; PangoFontFaceClass *pfclass = PANGO_FONT_FACE_CLASS(klass); object_class->finalize = pango_core_text_face_finalize; pfclass->describe = pango_core_text_face_describe; pfclass->get_face_name = pango_core_text_face_get_face_name; pfclass->list_sizes = pango_core_text_face_list_sizes; pfclass->is_synthesized = pango_core_text_face_is_synthesized; pfclass->get_family = pango_core_text_face_get_family; } /* * PangoCoreTextFamily */ 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; GList *synthetic_faces = NULL; GHashTable *italic_faces; 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); italic_faces = g_hash_table_new (g_direct_hash, g_direct_equal); count = CFArrayGetCount (ctfaces); for (i = 0; i < count; i++) { PangoCoreTextFace *face; CTFontDescriptorRef desc = CFArrayGetValueAtIndex (ctfaces, i); face = pango_core_text_face_from_ct_font_descriptor (desc); face->family = ctfamily; faces = g_list_prepend (faces, face); if ((face->traits & kCTFontItalicTrait) == kCTFontItalicTrait || pango_core_text_face_is_oblique (face)) g_hash_table_insert (italic_faces, GINT_TO_POINTER ((gint)face->weight), face); } CFRelease (font_descriptors); CFRelease (attributes); CFRelease (ctfaces); /* For all fonts for which a non-synthetic italic variant does * not exist on the system, we create synthesized versions here. */ for (l = faces; l; l = l->next) { PangoCoreTextFace *face = l->data; if (!g_hash_table_lookup (italic_faces, GINT_TO_POINTER ((gint)face->weight))) { PangoCoreTextFace *italic_face; italic_face = pango_core_text_face_copy (face); italic_face->family = ctfamily; pango_core_text_face_make_italic (italic_face, TRUE); /* Try to create a sensible face name. */ g_free (italic_face->style_name); if (strcasecmp (face->style_name, "regular") == 0) italic_face->style_name = g_strdup ("Oblique"); else italic_face->style_name = g_strdup_printf ("%s Oblique", face->style_name); synthetic_faces = g_list_prepend (synthetic_faces, italic_face); } } faces = g_list_concat (faces, synthetic_faces); 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); g_hash_table_destroy (italic_faces); } if (n_faces) *n_faces = ctfamily->n_faces; if (faces) *faces = g_memdup2 (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; } G_DEFINE_TYPE (PangoCoreTextFamily, pango_core_text_family, PANGO_TYPE_FONT_FAMILY); 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 (PangoCoreTextFamilyClass *klass) { GObjectClass *object_class = (GObjectClass *)klass; PangoFontFamilyClass *pfclass = PANGO_FONT_FAMILY_CLASS(klass); object_class->finalize = pango_core_text_family_finalize; pfclass->list_faces = pango_core_text_family_list_faces; pfclass->get_name = pango_core_text_family_get_name; pfclass->is_monospace = pango_core_text_family_is_monospace; } static void pango_core_text_family_init (PangoCoreTextFamily *family) { family->n_faces = -1; } static void pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class); static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap); 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->fontset_hash); 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); } /* 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 void get_context_matrix (PangoContext *context, PangoMatrix *matrix) { const PangoMatrix *set_matrix; 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; } /* * Helper functions for PangoCoreTextFontsetKey */ static const double ppi = 72.0; /* typographic points per inch */ static double pango_core_text_font_map_get_resolution (PangoCoreTextFontMap *fontmap, PangoContext *context) { if (PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->get_resolution) return PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->get_resolution (fontmap, context); /* FIXME: acquire DPI from CoreText using some deafault font */ g_warning ("FIXME: returning default DPI"); return ppi; } static int get_scaled_size (PangoCoreTextFontMap *fontmap, PangoContext *context, const PangoFontDescription *desc) { double size = pango_font_description_get_size (desc); const PangoMatrix *matrix = pango_context_get_matrix (context); double scale_factor = pango_matrix_get_font_scale_factor (matrix); if (!pango_font_description_get_size_is_absolute(desc)) { double dpi = pango_core_text_font_map_get_resolution (fontmap, context); size *= (dpi/ppi); } return .5 + scale_factor * size; } /* * PangoCoreTextFontsetKey */ struct _PangoCoreTextFontsetKey { PangoCoreTextFontMap *fontmap; PangoLanguage *language; PangoFontDescription *desc; PangoMatrix matrix; int pointsize; double resolution; PangoGravity gravity; gpointer context_key; }; static void pango_core_text_fontset_key_init (PangoCoreTextFontsetKey *key, PangoCoreTextFontMap *fontmap, PangoContext *context, const PangoFontDescription *desc, PangoLanguage *language) { if (!language && context) language = pango_context_get_language (context); key->fontmap = fontmap; get_context_matrix (context, &key->matrix); key->language = language; key->pointsize = get_scaled_size (fontmap, context, desc); key->resolution = pango_core_text_font_map_get_resolution (fontmap, context); key->gravity = pango_context_get_gravity (context); key->desc = pango_font_description_copy_static (desc); pango_font_description_unset_fields (key->desc, PANGO_FONT_MASK_SIZE); if (context && PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->context_key_get) key->context_key = (gpointer)PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->context_key_get (fontmap, context); else key->context_key = NULL; } static PangoCoreTextFontsetKey * pango_core_text_fontset_key_copy (const PangoCoreTextFontsetKey *old) { PangoCoreTextFontsetKey *key = g_slice_new (PangoCoreTextFontsetKey); key->fontmap = old->fontmap; key->matrix = old->matrix; key->language = old->language; key->pointsize = old->pointsize; key->resolution = old->resolution; key->gravity = old->gravity; key->desc = pango_font_description_copy (old->desc); 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 pango_core_text_fontset_key_free (PangoCoreTextFontsetKey *key) { pango_font_description_free (key->desc); if (key->context_key) PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, key->context_key); g_slice_free (PangoCoreTextFontsetKey, key); } static guint pango_core_text_fontset_key_hash (const PangoCoreTextFontsetKey *key) { guint32 hash = FNV1_32_INIT; hash = hash_bytes_fnv ((unsigned char *)(&key->matrix), sizeof (double) * 4, hash); hash ^= hash_bytes_fnv ((unsigned char *)(&key->resolution), sizeof (double), hash); if (key->context_key) hash ^= PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap, key->context_key); return (hash ^ GPOINTER_TO_UINT (key->language) ^ pango_font_description_hash (key->desc)); } static gboolean pango_core_text_fontset_key_equal (const PangoCoreTextFontsetKey *key_a, const PangoCoreTextFontsetKey *key_b) { if (key_a->language == key_b->language && key_a->pointsize == key_b->pointsize && key_a->resolution == key_b->resolution && key_a->gravity == key_b->gravity && pango_font_description_equal (key_a->desc, key_b->desc) && memcmp ((void *)&key_a->matrix, (void *)&key_b->matrix, 4 * sizeof (double)) == 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 key_a->context_key == key_b->context_key; } /* else */ return FALSE; } static PangoLanguage * pango_core_text_fontset_key_get_language (const PangoCoreTextFontsetKey *key) { return key->language; } static const PangoMatrix * pango_core_text_fontset_key_get_matrix (const PangoCoreTextFontsetKey *key) { return &key->matrix; } static PangoGravity pango_core_text_fontset_key_get_gravity (const PangoCoreTextFontsetKey *key) { return key->gravity; } static gpointer pango_core_text_fontset_key_get_context_key (const PangoCoreTextFontsetKey *key) { return key->context_key; } /* * PangoCoreTextFontKey */ struct _PangoCoreTextFontKey { PangoCoreTextFontMap *fontmap; CTFontDescriptorRef ctfontdescriptor; PangoMatrix matrix; PangoGravity gravity; int pointsize; double resolution; gboolean synthetic_italic; gpointer context_key; }; static void pango_core_text_font_key_init (PangoCoreTextFontKey *key, PangoCoreTextFontMap *ctfontmap, PangoCoreTextFontsetKey *fontset_key, CTFontDescriptorRef ctdescriptor, gboolean synthetic_italic) { key->fontmap = ctfontmap; key->ctfontdescriptor = ctdescriptor; key->matrix = *pango_core_text_fontset_key_get_matrix (fontset_key); key->pointsize = fontset_key->pointsize; key->resolution = fontset_key->resolution; key->synthetic_italic = synthetic_italic; key->gravity = pango_core_text_fontset_key_get_gravity (fontset_key); key->context_key = pango_core_text_fontset_key_get_context_key (fontset_key); } static PangoCoreTextFontKey * pango_core_text_font_key_copy (const PangoCoreTextFontKey *old) { PangoCoreTextFontKey *key = g_slice_new (PangoCoreTextFontKey); key->fontmap = old->fontmap; key->ctfontdescriptor = old->ctfontdescriptor; CFRetain (key->ctfontdescriptor); key->matrix = old->matrix; key->pointsize = old->pointsize; key->resolution = old->resolution; key->synthetic_italic = old->synthetic_italic; key->gravity = old->gravity; 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 pango_core_text_font_key_free (PangoCoreTextFontKey *key) { if (key->ctfontdescriptor) CFRelease (key->ctfontdescriptor); if (key->context_key) PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, key->context_key); g_slice_free (PangoCoreTextFontKey, key); } static guint pango_core_text_font_key_hash (const PangoCoreTextFontKey *key) { guint32 hash = FNV1_32_INIT; /* Not everything is included here, probably good enough for a hash */ 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); return (hash ^ CFHash (key->ctfontdescriptor)); } static gboolean pango_core_text_font_key_equal (const PangoCoreTextFontKey *key_a, const PangoCoreTextFontKey *key_b) { if (CFEqual (key_a->ctfontdescriptor, key_b->ctfontdescriptor) && memcmp (&key_a->matrix, &key_b->matrix, 4 * sizeof (double)) == 0 && key_a->gravity == key_b->gravity && key_a->pointsize == key_b->pointsize && key_a->resolution == key_b->resolution && key_a->synthetic_italic == key_b->synthetic_italic) { if (key_a->context_key && key_b->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 key_a->context_key == key_b->context_key; } else return FALSE; } int pango_core_text_font_key_get_size (const PangoCoreTextFontKey *key) { return key->pointsize; } double pango_core_text_font_key_get_resolution (const PangoCoreTextFontKey *key) { return key->resolution; } gboolean pango_core_text_font_key_get_synthetic_italic (const PangoCoreTextFontKey *key) { return key->synthetic_italic; } gpointer pango_core_text_font_key_get_context_key (const PangoCoreTextFontKey *key) { return key->context_key; } const PangoMatrix * pango_core_text_font_key_get_matrix (const PangoCoreTextFontKey *key) { return &key->matrix; } PangoGravity pango_core_text_font_key_get_gravity (const PangoCoreTextFontKey *key) { return key->gravity; } CTFontDescriptorRef pango_core_text_font_key_get_ctfontdescriptor (const PangoCoreTextFontKey *key) { return key->ctfontdescriptor; } static void pango_core_text_font_map_add (PangoCoreTextFontMap *ctfontmap, PangoCoreTextFontKey *key, PangoCoreTextFont *ctfont) { PangoCoreTextFontKey *key_copy; _pango_core_text_font_set_font_map (ctfont, ctfontmap); key_copy = pango_core_text_font_key_copy (key); _pango_core_text_font_set_font_key (ctfont, key_copy); g_hash_table_insert (ctfontmap->font_hash, key_copy, ctfont); } static PangoCoreTextFont * pango_core_text_font_map_new_font (PangoCoreTextFontMap *fontmap, PangoCoreTextFontsetKey *fontset_key, CTFontDescriptorRef ctfontdescriptor, gboolean synthetic_italic) { PangoCoreTextFontMapClass *klass; PangoCoreTextFont *font; PangoCoreTextFontKey key; pango_core_text_font_key_init (&key, fontmap, fontset_key, ctfontdescriptor, synthetic_italic); font = g_hash_table_lookup (fontmap->font_hash, &key); if (font) return g_object_ref (font); /* Call create_font */ klass = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap); font = klass->create_font (fontmap, &key); if (!font) return NULL; pango_core_text_font_map_add (fontmap, &key, font); return font; } static gboolean find_best_match (PangoCoreTextFamily *font_family, const PangoFontDescription *description, PangoCoreTextFace **best_face) { PangoFontDescription *new_desc; PangoFontDescription *best_description = NULL; int i; *best_face = NULL; for (i = 0; i < font_family->n_faces; i++) { new_desc = pango_font_face_describe (font_family->faces[i]); pango_font_description_set_gravity (new_desc, pango_font_description_get_gravity (description)); 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) { pango_font_description_free (best_description); return TRUE; } return FALSE; } static gboolean get_first_font (PangoFontset *fontset G_GNUC_UNUSED, PangoFont *font, gpointer data) { *(PangoFont **)data = font; return TRUE; } static guint pango_core_text_font_map_get_serial (PangoFontMap *fontmap) { PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap); return ctfontmap->serial; } static void pango_core_text_font_map_changed (PangoFontMap *fontmap) { PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap); ctfontmap->serial++; if (ctfontmap->serial == 0) ctfontmap->serial++; } static PangoFont * pango_core_text_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 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 PangoFontset * pango_core_text_font_map_load_fontset (PangoFontMap *fontmap, PangoContext *context, const PangoFontDescription *desc, PangoLanguage *language) { PangoCoreTextFontset *fontset; PangoCoreTextFontsetKey key; PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap); static gboolean warned_full_fallback = FALSE; /* MT-safe */ pango_core_text_fontset_key_init (&key, ctfontmap, context, desc, language); fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key); if (G_UNLIKELY (!fontset)) { gboolean insert_in_hash = TRUE; fontset = pango_core_text_fontset_new (&key, desc); if (G_UNLIKELY (!fontset)) { /* If no font(set) could be loaded, we fallback to "Apple Color * Emoji" for emoji font, fallback to "Sans" for other fonts, * which should always work on Mac. We try to adhere to the * requested style at first. */ PangoFontDescription *tmp_desc; /* Cannot use pango_core_text_fontset_key_free() here */ pango_font_description_free (key.desc); tmp_desc = pango_font_description_copy_static (desc); if (strcmp (pango_font_description_get_family (tmp_desc), "emoji") == 0) pango_font_description_set_family_static (tmp_desc, "Apple Color Emoji"); else pango_font_description_set_family_static (tmp_desc, "Sans"); pango_core_text_fontset_key_init (&key, ctfontmap, context, tmp_desc, language); fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key); if (G_LIKELY (fontset)) insert_in_hash = FALSE; else fontset = pango_core_text_fontset_new (&key, tmp_desc); if (G_UNLIKELY (!fontset)) { /* We could not load Sans in the requested style; reset * variant, weight and stretch to sensible defaults (we should * be able to adhere the PangoStyle with "Sans"). */ pango_font_description_set_variant (tmp_desc, PANGO_VARIANT_NORMAL); pango_font_description_set_weight (tmp_desc, PANGO_WEIGHT_NORMAL); pango_font_description_set_stretch (tmp_desc, PANGO_STRETCH_NORMAL); if (!warned_full_fallback) { char *ctmp; warned_full_fallback = TRUE; ctmp = pango_font_description_to_string (desc); g_warning ("couldn't load font \"%s\", modified variant/" "weight/stretch as fallback, expect ugly output.", ctmp); g_free (ctmp); } fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key); if (G_LIKELY (fontset)) insert_in_hash = FALSE; else fontset = pango_core_text_fontset_new (&key, tmp_desc); if (G_UNLIKELY (!fontset)) { /* If even that failed, display a sensible error message * and bail out, in contrast to failing randomly. */ g_error ("Could not load fallback font, bailing out."); } } if (tmp_desc) pango_font_description_free (tmp_desc); } if (insert_in_hash) g_hash_table_insert (ctfontmap->fontset_hash, pango_core_text_fontset_get_key (fontset), fontset); } /* Cannot use pango_core_text_fontset_key_free() here */ pango_font_description_free (key.desc); return g_object_ref (PANGO_FONTSET (fontset)); } static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap) { PangoCoreTextFamily *family; CTFontCollectionRef collection; CFArrayRef ctfaces; CFIndex i, count; ctfontmap->serial = 1; 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)pango_core_text_font_key_hash, (GEqualFunc)pango_core_text_font_key_equal, (GDestroyNotify)pango_core_text_font_key_free, NULL); ctfontmap->fontset_hash = g_hash_table_new_full ((GHashFunc)pango_core_text_fontset_key_hash, (GEqualFunc)pango_core_text_fontset_key_equal, NULL, (GDestroyNotify)g_object_unref); collection = CTFontCollectionCreateFromAvailableFonts (0); ctfaces = CTFontCollectionCreateMatchingFontDescriptors (collection); count = CFArrayGetCount (ctfaces); for (i = 0; i < count; i++) { SInt64 font_traits; char *buffer; char *family_name; CFNumberRef number; CFDictionaryRef dict; CTFontDescriptorRef desc = CFArrayGetValueAtIndex (ctfaces, i); buffer = ct_font_descriptor_get_family_name (desc, TRUE); if (!buffer) continue; family_name = g_utf8_casefold (buffer, -1); 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 = g_strdup (buffer); } g_free (buffer); 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, kCFNumberSInt64Type, &font_traits)) { if ((font_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) family->is_monospace = TRUE; } CFRelease (dict); } /* Insert aliases */ /* Keep in sync with get_real_family() */ gchar* aliases[] = { "Sans", "Serif", "system-ui", "cursive", "fantasy", "Monospace" }; for (int i = 0; i < G_N_ELEMENTS(aliases); i++) { family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL); family->family_name = g_strdup (aliases[i]); if (g_ascii_strcasecmp (family->family_name, "Monospace") == 0) family->is_monospace = TRUE; g_hash_table_insert (ctfontmap->families, g_utf8_casefold (family->family_name, -1), family); } } static PangoFontFace * pango_core_text_font_map_get_face (PangoFontMap *fontmap, PangoFont *font) { PangoCoreTextFont *cfont = PANGO_CORE_TEXT_FONT (font); return PANGO_FONT_FACE (_pango_core_text_font_get_face (cfont)); } 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->load_fontset = pango_core_text_font_map_load_fontset; fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_CORE_TEXT; fontmap_class->get_serial = pango_core_text_font_map_get_serial; fontmap_class->changed = pango_core_text_font_map_changed; fontmap_class->get_face = pango_core_text_font_map_get_face; } /* * PangoCoreTextFontSet */ static void pango_core_text_fontset_finalize (GObject *object); static void pango_core_text_fontset_init (PangoCoreTextFontset *fontset); static PangoLanguage * pango_core_text_fontset_get_language (PangoFontset *fontset); static PangoFont * pango_core_text_fontset_get_font (PangoFontset *fontset, guint wc); static void pango_core_text_fontset_foreach (PangoFontset *fontset, PangoFontsetForeachFunc func, gpointer data); struct _PangoCoreTextFontset { PangoFontset parent_instance; const gchar *orig_family; PangoFontDescription *orig_description; PangoCoreTextFontsetKey *key; CFArrayRef cascade_list; GPtrArray *fonts; GPtrArray *coverages; guint real_font_count; }; struct _PangoCoreTextFontsetClass { PangoFontsetClass parent_instance; }; typedef struct _PangoCoreTextFontsetClass PangoCoreTextFontsetClass; G_DEFINE_TYPE (PangoCoreTextFontset, pango_core_text_fontset, PANGO_TYPE_FONTSET); #if !defined(MAC_OS_X_VERSION_10_8) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 /* This symbol does exist in the CoreText library shipped with Snow * Leopard and Lion, however, it is not found in the public header files. */ CFArrayRef CTFontCopyDefaultCascadeList (CTFontRef font_ref); #endif static PangoCoreTextFontset * pango_core_text_fontset_new (PangoCoreTextFontsetKey *key, const PangoFontDescription *description) { PangoCoreTextFamily *font_family; PangoCoreTextFontset *fontset; PangoCoreTextFont *best_font = NULL; gchar **family_names; const gchar *family; gchar *name; int i; fontset = g_object_new (PANGO_TYPE_CORE_TEXT_FONTSET, NULL); family = pango_font_description_get_family (description); family_names = g_strsplit (family ? family : "", ",", -1); for (i = 0; family_names[i]; ++i) { name = g_utf8_casefold (family_names[i], -1); font_family = g_hash_table_lookup (key->fontmap->families, name); g_free (name); if (font_family) { PangoCoreTextFace *family_face; PangoCoreTextFont *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, &family_face)) { font = pango_core_text_font_map_new_font (key->fontmap, key, family_face->ctfontdescriptor, family_face->synthetic_italic); if (font) { g_ptr_array_add (fontset->fonts, font); if (best_font == NULL) best_font = font; } } } } g_strfreev (family_names); if (!best_font) { g_object_unref (fontset); return NULL; } /* Create a font set with best font */ fontset->key = pango_core_text_fontset_key_copy (key); fontset->orig_description = pango_font_description_copy (description); fontset->real_font_count = fontset->fonts->len; /* Add the cascade list for this language */ #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8 { CFArrayRef language_pref_list = NULL; CFStringRef languages[1]; if (key->language) { languages[0] = CFStringCreateWithCString (NULL, pango_language_to_string (key->language), kCFStringEncodingASCII); language_pref_list = CFArrayCreate (kCFAllocatorDefault, (const void **) languages, 1, &kCFTypeArrayCallBacks); } fontset->cascade_list = CTFontCopyDefaultCascadeListForLanguages (pango_core_text_font_get_ctfont (best_font), language_pref_list); if (language_pref_list) { CFRelease (languages[0]); CFRelease (language_pref_list); } } #else /* There is unfortunately no public API to retrieve the cascade list * on Mac OS X < 10.8, so we use the following undocumented public function. */ fontset->cascade_list = CTFontCopyDefaultCascadeList (pango_core_text_font_get_ctfont (best_font)); #endif /* length of cascade list + real_font_count for the "real" fonts at the front */ g_ptr_array_set_size (fontset->fonts, CFArrayGetCount (fontset->cascade_list) + fontset->real_font_count); g_ptr_array_set_size (fontset->coverages, CFArrayGetCount (fontset->cascade_list) + fontset->real_font_count); return fontset; } static PangoFont * pango_core_text_fontset_load_font (PangoCoreTextFontset *ctfontset, CTFontDescriptorRef ctdescriptor) { PangoCoreTextFontsetKey *key; PangoCoreTextFont *font; key = pango_core_text_fontset_get_key (ctfontset); /* For now, we will default the fallbacks to not have synthetic italic, * in the future this may be improved. */ font = pango_core_text_font_map_new_font (ctfontset->key->fontmap, ctfontset->key, ctdescriptor, FALSE); return PANGO_FONT (font); } static PangoFont * pango_core_text_fontset_get_font_at (PangoCoreTextFontset *ctfontset, unsigned int i) { /* These fonts are loaded as soon as the fontset is created */ if (i < ctfontset->real_font_count) return g_ptr_array_index (ctfontset->fonts, i); if (i >= ctfontset->fonts->len) return NULL; if (g_ptr_array_index (ctfontset->fonts, i) == NULL) { CTFontDescriptorRef ctdescriptor = CFArrayGetValueAtIndex (ctfontset->cascade_list, i - ctfontset->real_font_count); PangoFont *font = pango_core_text_fontset_load_font (ctfontset, ctdescriptor); g_ptr_array_index (ctfontset->fonts, i) = font; g_ptr_array_index (ctfontset->coverages, i) = NULL; } return g_ptr_array_index (ctfontset->fonts, i); } static void pango_core_text_fontset_class_init (PangoCoreTextFontsetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (klass); object_class->finalize = pango_core_text_fontset_finalize; fontset_class->get_font = pango_core_text_fontset_get_font; fontset_class->get_language = pango_core_text_fontset_get_language; fontset_class->foreach = pango_core_text_fontset_foreach; } static void pango_core_text_fontset_init (PangoCoreTextFontset *ctfontset) { ctfontset->key = NULL; ctfontset->cascade_list = NULL; ctfontset->fonts = g_ptr_array_new (); ctfontset->coverages = g_ptr_array_new (); ctfontset->real_font_count = 0; } static void pango_core_text_fontset_finalize (GObject *object) { PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (object); unsigned int i; for (i = 0; i < ctfontset->fonts->len; i++) { PangoFont *font = g_ptr_array_index (ctfontset->fonts, i); if (font) g_object_unref (font); } g_ptr_array_free (ctfontset->fonts, TRUE); for (i = 0; i < ctfontset->coverages->len; i++) { PangoCoverage *coverage = g_ptr_array_index (ctfontset->coverages, i); if (coverage) pango_coverage_unref (coverage); } g_ptr_array_free (ctfontset->coverages, TRUE); if (ctfontset->cascade_list) CFRelease (ctfontset->cascade_list); pango_font_description_free (ctfontset->orig_description); if (ctfontset->key) pango_core_text_fontset_key_free (ctfontset->key); G_OBJECT_CLASS (pango_core_text_fontset_parent_class)->finalize (object); } static PangoCoreTextFontsetKey * pango_core_text_fontset_get_key (PangoCoreTextFontset *fontset) { return fontset->key; } static PangoLanguage * pango_core_text_fontset_get_language (PangoFontset *fontset) { PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (fontset); return pango_core_text_fontset_key_get_language (pango_core_text_fontset_get_key (ctfontset)); } static PangoFont * pango_core_text_fontset_get_font (PangoFontset *fontset, guint wc) { PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (fontset); PangoCoverageLevel best_level = PANGO_COVERAGE_NONE; PangoCoverageLevel level; PangoFont *font; PangoCoverage *coverage; int result = -1; unsigned int i; for (i = 0; i < ctfontset->fonts->len; i++) { PangoFont *font = pango_core_text_fontset_get_font_at (ctfontset, i); if (!font) continue; coverage = g_ptr_array_index (ctfontset->coverages, i); if (coverage == NULL) { font = g_ptr_array_index (ctfontset->fonts, i); coverage = pango_font_get_coverage (font, ctfontset->key->language); g_ptr_array_index (ctfontset->coverages, i) = coverage; } level = pango_coverage_get (coverage, wc); if (result == -1 || level > best_level) { result = i; best_level = level; if (level == PANGO_COVERAGE_EXACT) break; } } if (G_UNLIKELY (result == -1)) return NULL; font = g_ptr_array_index (ctfontset->fonts, result); return g_object_ref (font); } static void pango_core_text_fontset_foreach (PangoFontset *fontset, PangoFontsetForeachFunc func, gpointer data) { PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (fontset); unsigned int i; for (i = 0; i < ctfontset->fonts->len; i++) { PangoFont *font = pango_core_text_fontset_get_font_at (ctfontset, i); if (!font) continue; if ((* func) (fontset, font, data)) return; } } PangoCoreTextFace * pango_core_text_font_map_find_face (PangoCoreTextFontMap *map, const PangoCoreTextFontKey *key) { CTFontDescriptorRef desc; gboolean synthetic_italic; char *family; char *family_name; char *style_name; PangoWeight weight; CTFontSymbolicTraits traits; PangoCoreTextFamily *font_family; PangoCoreTextFace *result = NULL; desc = pango_core_text_font_key_get_ctfontdescriptor (key); synthetic_italic = pango_core_text_font_key_get_synthetic_italic (key); family_name = ct_font_descriptor_get_family_name (desc, FALSE); style_name = ct_font_descriptor_get_style_name (desc); weight = ct_font_descriptor_get_weight (desc); traits = ct_font_descriptor_get_traits (desc); family = g_utf8_casefold (family_name, -1); font_family = g_hash_table_lookup (map->families, family); if (font_family) { pango_font_family_list_faces ((PangoFontFamily *)font_family, NULL, NULL); for (int i = 0; i < font_family->n_faces; i++) { PangoCoreTextFace *face = (PangoCoreTextFace *)font_family->faces[i]; if (face->weight == weight && face->traits == traits && face->synthetic_italic == synthetic_italic && strcmp (face->style_name, style_name) == 0) { result = face; break; } } } g_free (family); g_free (family_name); g_free (style_name); return result; }