From: Kristian Rietveld Date: Fri, 14 Oct 2011 09:25:21 +0000 (+0200) Subject: CoreText backend: implement font fallbacks X-Git-Tag: 1.30.0~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=37e74619215ede8a4fa7f5edabab14b517e673b2;p=platform%2Fupstream%2Fpango.git CoreText backend: implement font fallbacks We implement font fallbacks by making use of CTFontCopyDefaultCascadeList, a public, non-exposed, symbol in the CoreText library. We might want to handle this differently. The code now also uses a PangoCoreTextFontsetKey and PangoCoreTextFontKey, similar to the font config backend. This has also helped to clean up the code in general. This patch contains two fixes by Michael Natterer: 1. Fix a bug in pango_core_text_font_key_equal() where the return value of memcmp() was wrongly interpreted. 2. Cache PangoCoverage in PangoCoreTextFont. --- diff --git a/pango/pangocairo-coretext.h b/pango/pangocairo-coretext.h index 420e1c9..4310f45 100644 --- a/pango/pangocairo-coretext.h +++ b/pango/pangocairo-coretext.h @@ -46,9 +46,7 @@ GType pango_cairo_core_text_font_map_get_type (void) G_GNUC_CONST; PangoCoreTextFont * _pango_cairo_core_text_font_new (PangoCairoCoreTextFontMap *cafontmap, - PangoContext *context, - PangoCoreTextFace *face, - const PangoFontDescription *desc); + PangoCoreTextFontKey *key); G_END_DECLS diff --git a/pango/pangocairo-coretextfont.c b/pango/pangocairo-coretextfont.c index 7b58148..290a5cc 100644 --- a/pango/pangocairo-coretextfont.c +++ b/pango/pangocairo-coretextfont.c @@ -206,46 +206,24 @@ pango_cairo_core_text_font_init (PangoCairoCoreTextFont *cafont G_GNUC_UNUSED) PangoCoreTextFont * _pango_cairo_core_text_font_new (PangoCairoCoreTextFontMap *cafontmap, - PangoContext *context, - PangoCoreTextFace *face, - const PangoFontDescription *desc) + PangoCoreTextFontKey *key) { - const char *postscript_name; gboolean synthesize_italic = FALSE; PangoCairoCoreTextFont *cafont; PangoCoreTextFont *cfont; - CFStringRef cfstr; CTFontRef font_ref; + CTFontDescriptorRef ctdescriptor; CGFontRef font_id; double size, abs_size; - double dpi; cairo_matrix_t font_matrix; - postscript_name = _pango_core_text_face_get_postscript_name (face); + abs_size = pango_core_text_font_key_get_absolute_size (key); + size = pango_units_to_double (abs_size); - abs_size = size = pango_units_to_double (pango_font_description_get_size (desc)); + ctdescriptor = pango_core_text_font_key_get_ctfontdescriptor (key); + font_ref = CTFontCreateWithFontDescriptor (ctdescriptor, size, NULL); - if (context) - { - dpi = pango_cairo_context_get_resolution (context); - - if (dpi <= 0) - dpi = cafontmap->dpi; - } - else - dpi = cafontmap->dpi; - - if (pango_font_description_get_size_is_absolute (desc)) - size *= 72. / dpi; - else - abs_size *= dpi / 72.; - - cfstr = CFStringCreateWithCString (NULL, postscript_name, - kCFStringEncodingUTF8); - font_ref = CTFontCreateWithName (cfstr, size, NULL); - CFRelease (cfstr); - - if (_pango_core_text_face_get_synthetic_italic (face)) + if (pango_core_text_font_key_get_synthetic_italic (key)) synthesize_italic = TRUE; font_id = CTFontCopyGraphicsFont (font_ref, NULL); @@ -255,10 +233,7 @@ _pango_cairo_core_text_font_new (PangoCairoCoreTextFontMap *cafontmap, cafont = g_object_new (PANGO_TYPE_CAIRO_CORE_TEXT_FONT, NULL); cfont = PANGO_CORE_TEXT_FONT (cafont); - _pango_core_text_font_set_font_description (cfont, desc); - _pango_core_text_font_set_face (cfont, face); - - cafont->abs_size = abs_size * PANGO_SCALE; + cafont->abs_size = abs_size; _pango_core_text_font_set_ctfont (cfont, font_ref); @@ -270,14 +245,13 @@ _pango_cairo_core_text_font_new (PangoCairoCoreTextFontMap *cafontmap, else cairo_matrix_init_identity (&font_matrix); - /* Scale using absolute size */ - cairo_matrix_scale (&font_matrix, abs_size, abs_size); + cairo_matrix_scale (&font_matrix, size, size); _pango_cairo_font_private_initialize (&cafont->cf_priv, (PangoCairoFont *) cafont, - pango_font_description_get_gravity (desc), - _pango_cairo_context_get_merged_font_options (context), - pango_context_get_matrix (context), + pango_core_text_font_key_get_gravity (key), + pango_core_text_font_key_get_context_key (key), + pango_core_text_font_key_get_matrix (key), &font_matrix); return cfont; diff --git a/pango/pangocairo-coretextfontmap.c b/pango/pangocairo-coretextfontmap.c index e05a1d4..107c828 100644 --- a/pango/pangocairo-coretextfontmap.c +++ b/pango/pangocairo-coretextfontmap.c @@ -44,7 +44,7 @@ pango_cairo_core_text_font_map_set_resolution (PangoCairoFontMap *cfontmap, } static double -pango_cairo_core_text_font_map_get_resolution (PangoCairoFontMap *cfontmap) +pango_cairo_core_text_font_map_get_resolution_cairo (PangoCairoFontMap *cfontmap) { PangoCairoCoreTextFontMap *cafontmap = PANGO_CAIRO_CORE_TEXT_FONT_MAP (cfontmap); @@ -64,7 +64,7 @@ static void cairo_font_map_iface_init (PangoCairoFontMapIface *iface) { iface->set_resolution = pango_cairo_core_text_font_map_set_resolution; - iface->get_resolution = pango_cairo_core_text_font_map_get_resolution; + iface->get_resolution = pango_cairo_core_text_font_map_get_resolution_cairo; iface->get_font_type = pango_cairo_core_text_font_map_get_font_type; } @@ -74,13 +74,11 @@ G_DEFINE_TYPE_WITH_CODE (PangoCairoCoreTextFontMap, pango_cairo_core_text_font_m static PangoCoreTextFont * pango_cairo_core_text_font_map_create_font (PangoCoreTextFontMap *fontmap, - PangoContext *context, - PangoCoreTextFace *face, - const PangoFontDescription *desc) + PangoCoreTextFontKey *key) { return _pango_cairo_core_text_font_new (PANGO_CAIRO_CORE_TEXT_FONT_MAP (fontmap), - context, face, desc); + key); } static void @@ -89,6 +87,62 @@ pango_cairo_core_text_font_map_finalize (GObject *object) G_OBJECT_CLASS (pango_cairo_core_text_font_map_parent_class)->finalize (object); } +static double +pango_cairo_core_text_font_map_get_resolution_core_text (PangoCoreTextFontMap *ctfontmap, + PangoContext *context) +{ + PangoCairoCoreTextFontMap *cafontmap = PANGO_CAIRO_CORE_TEXT_FONT_MAP (ctfontmap); + double dpi; + + if (context) + { + dpi = pango_cairo_context_get_resolution (context); + + if (dpi <= 0) + dpi = cafontmap->dpi; + } + else + dpi = cafontmap->dpi; + + return dpi; +} + +static gconstpointer +pango_cairo_core_text_font_map_context_key_get (PangoCoreTextFontMap *fontmap G_GNUC_UNUSED, + PangoContext *context) +{ + return _pango_cairo_context_get_merged_font_options (context); +} + +static gpointer +pango_cairo_core_text_font_map_context_key_copy (PangoCoreTextFontMap *fontmap G_GNUC_UNUSED, + gconstpointer key) +{ + return cairo_font_options_copy (key); +} + +static void +pango_cairo_core_text_font_map_context_key_free (PangoCoreTextFontMap *fontmap G_GNUC_UNUSED, + gpointer key) +{ + cairo_font_options_destroy (key); +} + +static guint32 +pango_cairo_core_text_font_map_context_key_hash (PangoCoreTextFontMap *fontmap G_GNUC_UNUSED, + gconstpointer key) +{ + return (guint32)cairo_font_options_hash (key); +} + +static gboolean +pango_cairo_core_text_font_map_context_key_equal (PangoCoreTextFontMap *fontmap G_GNUC_UNUSED, + gconstpointer key_a, + gconstpointer key_b) +{ + return cairo_font_options_equal (key_a, key_b); +} + static void pango_cairo_core_text_font_map_class_init (PangoCairoCoreTextFontMapClass *class) { @@ -97,7 +151,13 @@ pango_cairo_core_text_font_map_class_init (PangoCairoCoreTextFontMapClass *class object_class->finalize = pango_cairo_core_text_font_map_finalize; + ctfontmapclass->get_resolution = pango_cairo_core_text_font_map_get_resolution_core_text; ctfontmapclass->create_font = pango_cairo_core_text_font_map_create_font; + ctfontmapclass->context_key_get = pango_cairo_core_text_font_map_context_key_get; + ctfontmapclass->context_key_copy = pango_cairo_core_text_font_map_context_key_copy; + ctfontmapclass->context_key_free = pango_cairo_core_text_font_map_context_key_free; + ctfontmapclass->context_key_hash = pango_cairo_core_text_font_map_context_key_hash; + ctfontmapclass->context_key_equal = pango_cairo_core_text_font_map_context_key_equal; } static void diff --git a/pango/pangocoretext-fontmap.c b/pango/pangocoretext-fontmap.c index 9f67acb..511380d 100644 --- a/pango/pangocoretext-fontmap.c +++ b/pango/pangocoretext-fontmap.c @@ -32,6 +32,7 @@ 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)) @@ -40,6 +41,11 @@ typedef struct _FontHashKey FontHashKey; #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; @@ -72,11 +78,9 @@ struct _PangoCoreTextFace PangoCoreTextFamily *family; - PangoCoverage *coverage; + CTFontDescriptorRef ctfontdescriptor; - char *postscript_name; char *style_name; - PangoWeight weight; CTFontSymbolicTraits traits; guint synthetic_italic : 1; @@ -91,6 +95,15 @@ 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 { @@ -153,88 +166,88 @@ gchar_from_cf_string (CFStringRef str) return buffer; } -static PangoCoverage * -pango_coverage_from_cf_charset (CFCharacterSetRef charset) +static char * +ct_font_descriptor_get_style_name (CTFontDescriptorRef desc) { - 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); - } + CFStringRef cf_str; + char *buffer; - CFRelease (bitmap); + cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontStyleNameAttribute); + buffer = gchar_from_cf_string (cf_str); + CFRelease (cf_str); - return coverage; + return buffer; } -static inline gboolean -pango_core_text_face_is_oblique (PangoCoreTextFace *face) +static CTFontSymbolicTraits +ct_font_descriptor_get_traits (CTFontDescriptorRef desc) { - return g_strrstr (face->style_name, "Oblique") != NULL; + 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 inline PangoCoreTextFace * -pango_core_text_face_from_ct_font_descriptor (CTFontDescriptorRef desc) +static CTFontDescriptorRef +cf_font_descriptor_copy_with_traits (CTFontDescriptorRef desc, + const CTFontSymbolicTraits traits) { - SInt64 font_traits; - char *buffer; - CFStringRef str; - CFNumberRef number; - CGFloat value; - CFDictionaryRef dict; - CFCharacterSetRef charset; - PangoCoreTextFace *face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE, - NULL); + CFMutableDictionaryRef dict, traits_dict; + CFDictionaryRef tmp; + CTFontDescriptorRef new_desc; + SInt64 tmp_traits; - face->synthetic_italic = FALSE; + tmp = CTFontDescriptorCopyAttributes (desc); + dict = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, tmp); + CFRelease (tmp); - /* Get font name */ - str = CTFontDescriptorCopyAttribute (desc, kCTFontNameAttribute); - buffer = gchar_from_cf_string (str); + tmp = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); + traits_dict = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, tmp); + CFRelease (tmp); - /* We strdup again to save space. */ - face->postscript_name = g_strdup (buffer); + tmp_traits = traits; + CFDictionarySetValue (traits_dict, (CFTypeRef) kCTFontSymbolicTrait, + CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &tmp_traits)); - CFRelease (str); - g_free (buffer); + CFDictionarySetValue (dict, (CFTypeRef)kCTFontTraitsAttribute, traits_dict); - /* Get style name */ - str = CTFontDescriptorCopyAttribute (desc, kCTFontStyleNameAttribute); - buffer = gchar_from_cf_string (str); + new_desc = CTFontDescriptorCreateCopyWithAttributes (desc, dict); + CFRelease (dict); - face->style_name = g_strdup (buffer); + return new_desc; +} - CFRelease (str); - g_free (buffer); +static PangoWeight +ct_font_descriptor_get_weight (CTFontDescriptorRef desc) +{ + CFDictionaryRef dict; + CFNumberRef cf_number; + CGFloat value; + PangoWeight weight; - /* Get font traits, symbolic traits */ dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute); - number = (CFNumberRef)CFDictionaryGetValue (dict, - kCTFontWeightTrait); - if (CFNumberGetValue (number, kCFNumberCGFloatType, &value)) + cf_number = (CFNumberRef)CFDictionaryGetValue (dict, + kCTFontWeightTrait); + + if (CFNumberGetValue (cf_number, kCFNumberCGFloatType, &value)) { if (value < ct_weight_min || value > ct_weight_max) { - face->weight = PANGO_WEIGHT_NORMAL; /* This is really an error */ + /* This is really an error */ + weight = PANGO_WEIGHT_NORMAL; } else { @@ -242,36 +255,207 @@ pango_core_text_face_from_ct_font_descriptor (CTFontDescriptorRef desc) for (i = 0; i < G_N_ELEMENTS(ct_weight_limits); i++) if (value < ct_weight_limits[i].bound) { - face->weight = ct_weight_limits[i].weight; - break; + weight = ct_weight_limits[i].weight; + break; } } } - else - face->weight = PANGO_WEIGHT_NORMAL; + else + weight = PANGO_WEIGHT_NORMAL; - /* 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. - */ - number = (CFNumberRef)CFDictionaryGetValue (dict, kCTFontSymbolicTrait); - if (CFNumberGetValue (number, kCFNumberSInt64Type, &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); + return weight; +} + +static inline gboolean +pango_core_text_style_name_is_oblique (const char *style_name) +{ + return g_strrstr (style_name, "Oblique") != NULL; +} + +PangoFontDescription * +_pango_core_text_font_description_from_ct_font_descriptor (CTFontDescriptorRef desc) +{ + SInt64 font_traits; + char *buffer; + char *style_name; + CFStringRef cf_str; + PangoFontDescription *font_desc; + + 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? + */ + cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontFamilyNameAttribute); + buffer = gchar_from_cf_string (cf_str); + + pango_font_description_set_family (font_desc, buffer); + + g_free (buffer); + CFRelease (cf_str); + + /* 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); + + /* FIXME: How can this be figured using CoreText? */ +#if 0 + if (font_traits & NSSmallCapsFontMask) + pango_font_description_set_variant (font_desc, PANGO_VARIANT_SMALL_CAPS); + else +#endif + 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; + *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 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; +} + +/* + * PangoCoreTextFamily + */ + static void pango_core_text_family_list_faces (PangoFontFamily *family, PangoFontFace ***faces, @@ -356,16 +540,13 @@ pango_core_text_family_list_faces (PangoFontFamily *family, { PangoCoreTextFace *italic_face; - italic_face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE, NULL); + italic_face = pango_core_text_face_copy (face); italic_face->family = ctfamily; - italic_face->postscript_name = g_strdup (face->postscript_name); - italic_face->weight = face->weight; - italic_face->traits = face->traits | kCTFontItalicTrait; - italic_face->synthetic_italic = TRUE; - italic_face->coverage = pango_coverage_ref (face->coverage); + 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 @@ -456,236 +637,279 @@ pango_core_text_family_init (PangoCoreTextFamily *family) family->n_faces = -1; } -static PangoFontDescription * -pango_core_text_face_describe (PangoFontFace *face) -{ - PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face); - PangoFontDescription *description; - PangoStyle pango_style; - PangoVariant pango_variant; - description = pango_font_description_new (); - pango_font_description_set_family (description, ctface->family->family_name); +static void pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class); +static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap); - if (ctface->traits & kCTFontItalicTrait) - pango_style = PANGO_STYLE_ITALIC; - else if (pango_core_text_face_is_oblique (ctface)) - pango_style = PANGO_STYLE_OBLIQUE; - else - pango_style = PANGO_STYLE_NORMAL; +G_DEFINE_TYPE (PangoCoreTextFontMap, pango_core_text_font_map, PANGO_TYPE_FONT_MAP); - /* 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; +static void +pango_core_text_font_map_finalize (GObject *object) +{ + PangoCoreTextFontMap *fontmap = PANGO_CORE_TEXT_FONT_MAP (object); - pango_font_description_set_weight (description, ctface->weight); - pango_font_description_set_style (description, pango_style); - pango_font_description_set_variant (description, pango_variant); + g_hash_table_destroy (fontmap->fontset_hash); + g_hash_table_destroy (fontmap->font_hash); + g_hash_table_destroy (fontmap->families); - return description; + G_OBJECT_CLASS (pango_core_text_font_map_parent_class)->finalize (object); } -static const char * -pango_core_text_face_get_face_name (PangoFontFace *face) +/* 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) { - PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face); + while (len--) + { + hval *= FNV_32_PRIME; + hval ^= *buffer++; + } - return ctface->style_name; + return hval; } static void -pango_core_text_face_list_sizes (PangoFontFace *face, - int **sizes, - int *n_sizes) +get_context_matrix (PangoContext *context, + PangoMatrix *matrix) { - *n_sizes = 0; - *sizes = NULL; -} + const PangoMatrix *set_matrix; + static const PangoMatrix identity = PANGO_MATRIX_INIT; -G_DEFINE_TYPE (PangoCoreTextFace, pango_core_text_face, PANGO_TYPE_FONT_FACE); + if (context) + set_matrix = pango_context_get_matrix (context); + else + set_matrix = NULL; -static void -pango_core_text_face_init (PangoCoreTextFace *face) -{ - face->family = NULL; - face->coverage = NULL; + if (set_matrix) + *matrix = *set_matrix; + else + *matrix = identity; } +/* + * Helper functions for PangoCoreTextFontsetKey + */ - -static void -pango_core_text_face_finalize (GObject *object) +static double +pango_core_text_font_map_get_resolution (PangoCoreTextFontMap *fontmap, + PangoContext *context) { - PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (object); - - if (ctface->coverage) - pango_coverage_unref (ctface->coverage); + if (PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->get_resolution) + return PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->get_resolution (fontmap, context); - g_free (ctface->postscript_name); - g_free (ctface->style_name); + /* FIXME: acquire DPI from CoreText using some deafault font */ + g_warning ("FIXME: returning default DPI"); - G_OBJECT_CLASS (pango_core_text_face_parent_class)->finalize (object); + return 72.0; } -static gboolean -pango_core_text_face_is_synthesized (PangoFontFace *face) -{ - PangoCoreTextFace *cface = PANGO_CORE_TEXT_FACE (face); - - return cface->synthetic_italic; -} - -static void -pango_core_text_face_class_init (PangoCoreTextFaceClass *klass) +static int +get_scaled_size (PangoCoreTextFontMap *fontmap, + PangoContext *context, + const PangoFontDescription *desc) { - GObjectClass *object_class = (GObjectClass *)klass; - PangoFontFaceClass *pfclass = PANGO_FONT_FACE_CLASS(klass); + double size = pango_font_description_get_size (desc); - object_class->finalize = pango_core_text_face_finalize; + if (!pango_font_description_get_size_is_absolute (desc)) + { + double dpi = pango_core_text_font_map_get_resolution (fontmap, context); + size = size * dpi / 72.; + } - 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; + return .5 + pango_matrix_get_font_scale_factor (pango_context_get_matrix (context)) * size; } -const char * -_pango_core_text_face_get_postscript_name (PangoCoreTextFace *face) -{ - return face->postscript_name; -} -gboolean -_pango_core_text_face_get_synthetic_italic (PangoCoreTextFace *face) +/* + * PangoCoreTextFontsetKey + */ +struct _PangoCoreTextFontsetKey { - return face->synthetic_italic; -} + PangoCoreTextFontMap *fontmap; + PangoLanguage *language; + PangoFontDescription *desc; + PangoMatrix matrix; + int pixelsize; + double resolution; + PangoGravity gravity; + gpointer context_key; +}; -PangoCoverage * -_pango_core_text_face_get_coverage (PangoCoreTextFace *face, - PangoLanguage *language) +static void +pango_core_text_fontset_key_init (PangoCoreTextFontsetKey *key, + PangoCoreTextFontMap *fontmap, + PangoContext *context, + const PangoFontDescription *desc, + PangoLanguage *language) { - return face->coverage; + if (!language && context) + language = pango_context_get_language (context); + + key->fontmap = fontmap; + get_context_matrix (context, &key->matrix); + key->language = language; + key->pixelsize = 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 void pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class); -static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap); +static PangoCoreTextFontsetKey * +pango_core_text_fontset_key_copy (const PangoCoreTextFontsetKey *old) +{ + PangoCoreTextFontsetKey *key = g_slice_new (PangoCoreTextFontsetKey); -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); + key->fontmap = old->fontmap; + key->matrix = old->matrix; + key->language = old->language; + key->pixelsize = old->pixelsize; + 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; -G_DEFINE_TYPE (PangoCoreTextFontMap, pango_core_text_font_map, PANGO_TYPE_FONT_MAP); + return key; +} static void -pango_core_text_font_map_finalize (GObject *object) +pango_core_text_fontset_key_free (PangoCoreTextFontsetKey *key) { - PangoCoreTextFontMap *fontmap = PANGO_CORE_TEXT_FONT_MAP (object); + pango_font_description_free (key->desc); - g_hash_table_destroy (fontmap->font_hash); - g_hash_table_destroy (fontmap->families); + if (key->context_key) + PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, key->context_key); - G_OBJECT_CLASS (pango_core_text_font_map_parent_class)->finalize (object); + g_slice_free (PangoCoreTextFontsetKey, key); } -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. - */ +static guint +pango_core_text_fontset_key_hash (const PangoCoreTextFontsetKey *key) +{ + guint32 hash = FNV1_32_INIT; -#define FNV_32_PRIME ((guint32)0x01000193) -#define FNV1_32_INIT ((guint32)0x811c9dc5) + hash = hash_bytes_fnv ((unsigned char *)(&key->matrix), sizeof (double) * 4, hash); + hash ^= hash_bytes_fnv ((unsigned char *)(&key->resolution), sizeof (double), hash); -static guint32 -hash_bytes_fnv (unsigned char *buffer, - int len, - guint32 hval) -{ - while (len--) - { - hval *= FNV_32_PRIME; - hval ^= *buffer++; - } + if (key->context_key) + hash ^= PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap, key->context_key); - return hval; + return (hash ^ + GPOINTER_TO_UINT (key->language) ^ + pango_font_description_hash (key->desc)); } static gboolean -font_hash_key_equal (const FontHashKey *key_a, - const FontHashKey *key_b) +pango_core_text_fontset_key_equal (const PangoCoreTextFontsetKey *key_a, + const PangoCoreTextFontsetKey *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 && + if (key_a->language == key_b->language && + key_a->pixelsize == key_b->pixelsize && + key_a->resolution == key_b->resolution && + key_a->gravity == key_b->gravity && pango_font_description_equal (key_a->desc, key_b->desc) && - strcmp (key_a->postscript_name, key_b->postscript_name) == 0) + 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 TRUE; + return key_a->context_key == key_b->context_key; } - else - return FALSE; + /* else */ + return FALSE; } -static guint -font_hash_key_hash (const FontHashKey *key) +static PangoLanguage * +pango_core_text_fontset_key_get_language (const PangoCoreTextFontsetKey *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); + return key->language; +} - if (key->context_key) - hash ^= PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap, - key->context_key); +static const PangoMatrix * +pango_core_text_fontset_key_get_matrix (const PangoCoreTextFontsetKey *key) +{ + return &key->matrix; +} - hash ^= g_str_hash (key->postscript_name); +static PangoGravity +pango_core_text_fontset_key_get_gravity (const PangoCoreTextFontsetKey *key) +{ + return key->gravity; +} - return (hash ^ pango_font_description_hash (key->desc)); +static gpointer +pango_core_text_fontset_key_get_context_key (const PangoCoreTextFontsetKey *key) +{ + return key->context_key; } -static void -font_hash_key_free (FontHashKey *key) +/* + * PangoCoreTextFontKey + */ +struct _PangoCoreTextFontKey { - if (key->context_key) - PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, - key->context_key); + PangoCoreTextFontMap *fontmap; + CTFontDescriptorRef ctfontdescriptor; + PangoMatrix matrix; + PangoGravity gravity; + int pixelsize; + double resolution; + gboolean synthetic_italic; + gpointer context_key; +}; - g_slice_free (FontHashKey, 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->pixelsize = fontset_key->pixelsize; + 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 FontHashKey * -font_hash_key_copy (FontHashKey *old) +static PangoCoreTextFontKey * +pango_core_text_font_key_copy (const PangoCoreTextFontKey *old) { - FontHashKey *key = g_slice_new (FontHashKey); + PangoCoreTextFontKey *key = g_slice_new (PangoCoreTextFontKey); key->fontmap = old->fontmap; + key->ctfontdescriptor = old->ctfontdescriptor; + CFRetain (key->ctfontdescriptor); key->matrix = old->matrix; - key->desc = pango_font_description_copy (old->desc); - key->postscript_name = g_strdup (old->postscript_name); + key->pixelsize = old->pixelsize; + 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 @@ -694,73 +918,140 @@ font_hash_key_copy (FontHashKey *old) return key; } - static void -get_context_matrix (PangoContext *context, - PangoMatrix *matrix) +pango_core_text_font_key_free (PangoCoreTextFontKey *key) { - const PangoMatrix *set_matrix; - static const PangoMatrix identity = PANGO_MATRIX_INIT; + if (key->ctfontdescriptor) + CFRelease (key->ctfontdescriptor); - if (context) - set_matrix = pango_context_get_matrix (context); - else - set_matrix = NULL; + if (key->context_key) + PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, key->context_key); - if (set_matrix) - *matrix = *set_matrix; - else - *matrix = identity; + g_slice_free (PangoCoreTextFontKey, key); } -static void -font_hash_key_for_context (PangoCoreTextFontMap *fcfontmap, - PangoContext *context, - FontHashKey *key) +static guint +pango_core_text_font_key_hash (const PangoCoreTextFontKey *key) { - key->fontmap = fcfontmap; - get_context_matrix (context, &key->matrix); + 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); - 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); + 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->pixelsize == key_b->pixelsize && + 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 - key->context_key = NULL; + return FALSE; +} + +int +pango_core_text_font_key_get_absolute_size (const PangoCoreTextFontKey *key) +{ + return key->pixelsize; +} + +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, - PangoContext *context, + PangoCoreTextFontKey *key, PangoCoreTextFont *ctfont) { - FontHashKey key; - FontHashKey *key_copy; - PangoCoreTextFace *face; + PangoCoreTextFontKey *key_copy; _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)); + 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_lookup (PangoCoreTextFontMap *ctfontmap, - PangoContext *context, - PangoFontDescription *desc, - PangoCoreTextFace *face) +pango_core_text_font_map_new_font (PangoCoreTextFontMap *fontmap, + PangoCoreTextFontsetKey *fontset_key, + CTFontDescriptorRef ctfontdescriptor, + gboolean synthetic_italic) { - FontHashKey key; + 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); - font_hash_key_for_context (ctfontmap, context, &key); - key.postscript_name = (char *)_pango_core_text_face_get_postscript_name (face); - key.desc = desc; + /* Call create_font */ + klass = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap); + font = klass->create_font (fontmap, &key); - return g_hash_table_lookup (ctfontmap->font_hash, &key); + if (!font) + return NULL; + + pango_core_text_font_map_add (fontmap, &key, font); + + return font; } static gboolean @@ -795,73 +1086,44 @@ find_best_match (PangoCoreTextFamily *font_family, return FALSE; } +static gboolean +get_first_font (PangoFontset *fontset G_GNUC_UNUSED, + PangoFont *font, + gpointer data) +{ + *(PangoFont **)data = font; + + return TRUE; +} + 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; - gboolean is_absolute; + PangoLanguage *language; + PangoFontset *fontset; + PangoFont *font = NULL; - size = pango_font_description_get_size (description); - if (size < 0) - return NULL; + if (context) + language = pango_context_get_language (context); + else + language = NULL; - is_absolute = pango_font_description_get_size_is_absolute (description); + fontset = pango_font_map_load_fontset (fontmap, context, + description, language); - 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) + if (fontset) { - 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; - - if (is_absolute) - pango_font_description_set_absolute_size (best_description, size); - else - 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_fontset_foreach (fontset, get_first_font, &font); - pango_font_description_free (best_description); + if (font) + g_object_ref (font); - return (PangoFont *)best_font; + g_object_unref (fontset); } - return NULL; + return font; } static void @@ -909,6 +1171,63 @@ pango_core_text_font_map_list_families (PangoFontMap *fontmap, 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); + + pango_core_text_fontset_key_init (&key, ctfontmap, + context, desc, language); + + fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key); + + if (G_UNLIKELY (!fontset)) + { + fontset = pango_core_text_fontset_new (&key, desc); + + if (G_LIKELY (fontset)) + g_hash_table_insert (ctfontmap->fontset_hash, + pango_core_text_fontset_get_key (fontset), + fontset); + else + { + /* If no font(set) could be loaded, we fallback to "Sans", + * which should always work on Mac. + */ + 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); + 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_UNLIKELY (!fontset)) + { + fontset = pango_core_text_fontset_new (&key, tmp_desc); + 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 (fontset); +} + static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap) { @@ -921,18 +1240,23 @@ pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap) 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, + 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++) { - int font_traits; + SInt64 font_traits; char *buffer; char *family_name; CFStringRef str; @@ -965,9 +1289,9 @@ pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap) number = (CFNumberRef)CFDictionaryGetValue (dict, kCTFontSymbolicTrait); - if (CFNumberGetValue (number, kCFNumberIntType, &font_traits)) + if (CFNumberGetValue (number, kCFNumberSInt64Type, &font_traits)) { - if (font_traits & kCTFontMonoSpaceTrait) + if ((font_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) family->is_monospace = TRUE; } @@ -1002,5 +1326,295 @@ pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class) 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; } + +/* + * 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; +}; + +typedef PangoFontsetClass PangoCoreTextFontsetClass; + +static PangoFontsetClass *core_text_fontset_parent_class; + + +/* 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); + +static PangoCoreTextFontset * +pango_core_text_fontset_new (PangoCoreTextFontsetKey *key, + const PangoFontDescription *description) +{ + PangoCoreTextFamily *font_family; + PangoCoreTextFontset *fontset; + PangoCoreTextFont *best_font = NULL; + const gchar *family; + gchar *name; + + family = pango_font_description_get_family (description); + family = family ? family : ""; + name = g_utf8_casefold (family, -1); + font_family = g_hash_table_lookup (key->fontmap->families, name); + g_free (name); + + if (font_family) + { + PangoFontDescription *best_description; + PangoCoreTextFace *best_face; + gint size; + gboolean is_absolute; + + /* 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; + + size = pango_font_description_get_size (description); + if (size < 0) + return NULL; + + is_absolute = pango_font_description_get_size_is_absolute (description); + if (is_absolute) + pango_font_description_set_absolute_size (best_description, size); + else + pango_font_description_set_size (best_description, size); + + best_font = pango_core_text_font_map_new_font (key->fontmap, + key, + best_face->ctfontdescriptor, + best_face->synthetic_italic); + + pango_font_description_free (best_description); + } + else + return NULL; + + if (!best_font) + return NULL; + + /* Create a font set with best font */ + fontset = g_object_new (PANGO_TYPE_CORE_TEXT_FONTSET, NULL); + fontset->key = pango_core_text_fontset_key_copy (key); + fontset->orig_description = pango_font_description_copy (description); + + fontset->fonts = g_ptr_array_new (); + g_ptr_array_add (fontset->fonts, best_font); + fontset->coverages = g_ptr_array_new (); + + /* Add the cascade list for this language */ + fontset->cascade_list = CTFontCopyDefaultCascadeList (pango_core_text_font_get_ctfont (best_font)); + + /* length of cascade list + 1 for the "real" font at the front */ + g_ptr_array_set_size (fontset->fonts, CFArrayGetCount (fontset->cascade_list) + 1); + g_ptr_array_set_size (fontset->coverages, CFArrayGetCount (fontset->cascade_list) + 1); + + 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) +{ + /* The first font is loaded as soon as the fontset is created */ + if (i == 0) + 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 - 1); + 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); + + core_text_fontset_parent_class = g_type_class_peek_parent (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 (); +} + +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); + + 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 (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; + } +} + +G_DEFINE_TYPE (PangoCoreTextFontset, + pango_core_text_fontset, + PANGO_TYPE_FONTSET); diff --git a/pango/pangocoretext-private.h b/pango/pangocoretext-private.h index 4ed0e8e..3e0dafd 100644 --- a/pango/pangocoretext-private.h +++ b/pango/pangocoretext-private.h @@ -44,10 +44,14 @@ typedef struct _PangoCoreTextFace PangoCoreTextFace; typedef struct _PangoCoreTextFontMap PangoCoreTextFontMap; typedef struct _PangoCoreTextFontMapClass PangoCoreTextFontMapClass; +typedef struct _PangoCoreTextFontsetKey PangoCoreTextFontsetKey; +typedef struct _PangoCoreTextFontKey PangoCoreTextFontKey; + struct _PangoCoreTextFontMap { PangoFontMap parent_instance; + GHashTable *fontset_hash; GHashTable *font_hash; GHashTable *families; @@ -70,22 +74,15 @@ struct _PangoCoreTextFontMapClass gconstpointer key_b); PangoCoreTextFont * (* create_font) (PangoCoreTextFontMap *fontmap, - PangoContext *context, - PangoCoreTextFace *face, - const PangoFontDescription *desc); + PangoCoreTextFontKey *key); + + double (* get_resolution) (PangoCoreTextFontMap *fontmap, + PangoContext *context); }; GType pango_core_text_font_map_get_type (void) G_GNUC_CONST; -const char * _pango_core_text_face_get_postscript_name (PangoCoreTextFace *face); -gboolean _pango_core_text_face_get_synthetic_italic (PangoCoreTextFace *face); -PangoCoverage * _pango_core_text_face_get_coverage (PangoCoreTextFace *face, - PangoLanguage *language); - -void _pango_core_text_font_set_font_description (PangoCoreTextFont *afont, - const PangoFontDescription *desc); -PangoFontDescription *_pango_core_text_font_get_font_description (PangoCoreTextFont *afont); void _pango_core_text_font_set_font_map (PangoCoreTextFont *afont, PangoCoreTextFontMap *fontmap); void _pango_core_text_font_set_face (PangoCoreTextFont *afont, @@ -94,9 +91,21 @@ PangoCoreTextFace * _pango_core_text_font_get_face (PangoCoreTextF gpointer _pango_core_text_font_get_context_key (PangoCoreTextFont *afont); void _pango_core_text_font_set_context_key (PangoCoreTextFont *afont, gpointer context_key); +void _pango_core_text_font_set_font_key (PangoCoreTextFont *font, + PangoCoreTextFontKey *key); void _pango_core_text_font_set_ctfont (PangoCoreTextFont *font, CTFontRef font_ref); +PangoFontDescription *_pango_core_text_font_description_from_ct_font_descriptor (CTFontDescriptorRef desc); + +int pango_core_text_font_key_get_absolute_size (const PangoCoreTextFontKey *key); +double pango_core_text_font_key_get_resolution (const PangoCoreTextFontKey *key); +gboolean pango_core_text_font_key_get_synthetic_italic (const PangoCoreTextFontKey *key); +gpointer pango_core_text_font_key_get_context_key (const PangoCoreTextFontKey *key); +const PangoMatrix *pango_core_text_font_key_get_matrix (const PangoCoreTextFontKey *key); +PangoGravity pango_core_text_font_key_get_gravity (const PangoCoreTextFontKey *key); +CTFontDescriptorRef pango_core_text_font_key_get_ctfontdescriptor (const PangoCoreTextFontKey *key); + G_END_DECLS #endif /* __PANGOCORETEXT_PRIVATE_H__ */ diff --git a/pango/pangocoretext.c b/pango/pangocoretext.c index 36e5a37..5023e93 100644 --- a/pango/pangocoretext.c +++ b/pango/pangocoretext.c @@ -30,10 +30,12 @@ G_DEFINE_TYPE (PangoCoreTextFont, pango_core_text_font, PANGO_TYPE_FONT); struct _PangoCoreTextFontPrivate { PangoCoreTextFace *face; - PangoFontDescription *desc; gpointer context_key; CTFontRef font_ref; + PangoCoreTextFontKey *key; + + PangoCoverage *coverage; PangoFontMap *fontmap; }; @@ -44,12 +46,13 @@ pango_core_text_font_finalize (GObject *object) PangoCoreTextFont *ctfont = (PangoCoreTextFont *)object; PangoCoreTextFontPrivate *priv = ctfont->priv; - pango_font_description_free (priv->desc); - g_assert (priv->fontmap != NULL); g_object_remove_weak_pointer (G_OBJECT (priv->fontmap), (gpointer *) (gpointer) &priv->fontmap); priv->fontmap = NULL; + if (priv->coverage) + pango_coverage_unref (priv->coverage); + G_OBJECT_CLASS (pango_core_text_font_parent_class)->finalize (object); } @@ -58,8 +61,47 @@ pango_core_text_font_describe (PangoFont *font) { PangoCoreTextFont *ctfont = (PangoCoreTextFont *)font; PangoCoreTextFontPrivate *priv = ctfont->priv; + CTFontDescriptorRef ctfontdesc; + + ctfontdesc = pango_core_text_font_key_get_ctfontdescriptor (priv->key); + + return _pango_core_text_font_description_from_ct_font_descriptor (ctfontdesc); +} + +static PangoCoverage * +ct_font_descriptor_get_coverage (CTFontDescriptorRef desc) +{ + CFCharacterSetRef charset; + CFIndex i, length; + CFDataRef bitmap; + const UInt8 *ptr; + PangoCoverage *coverage; - return pango_font_description_copy (priv->desc); + coverage = pango_coverage_new (); + + charset = CTFontDescriptorCopyAttribute (desc, kCTFontCharacterSetAttribute); + 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); + CFRelease (charset); + + return coverage; } static PangoCoverage * @@ -69,8 +111,16 @@ pango_core_text_font_get_coverage (PangoFont *font, PangoCoreTextFont *ctfont = (PangoCoreTextFont *)font; PangoCoreTextFontPrivate *priv = ctfont->priv; - return pango_coverage_ref (_pango_core_text_face_get_coverage (priv->face, - language)); + if (!priv->coverage) + { + CTFontDescriptorRef ctfontdesc; + + ctfontdesc = pango_core_text_font_key_get_ctfontdescriptor (priv->key); + + priv->coverage = ct_font_descriptor_get_coverage (ctfontdesc); + } + + return pango_coverage_ref (priv->coverage); } static PangoEngineShape * @@ -115,23 +165,6 @@ pango_core_text_font_class_init (PangoCoreTextFontClass *class) } void -_pango_core_text_font_set_font_description (PangoCoreTextFont *font, - const PangoFontDescription *desc) -{ - PangoCoreTextFontPrivate *priv = font->priv; - - priv->desc = pango_font_description_copy (desc); -} - -PangoFontDescription * -_pango_core_text_font_get_font_description (PangoCoreTextFont *font) -{ - PangoCoreTextFontPrivate *priv = font->priv; - - return priv->desc; -} - -void _pango_core_text_font_set_font_map (PangoCoreTextFont *font, PangoCoreTextFontMap *fontmap) { @@ -177,6 +210,21 @@ _pango_core_text_font_set_context_key (PangoCoreTextFont *font, } void +_pango_core_text_font_set_font_key (PangoCoreTextFont *font, + PangoCoreTextFontKey *key) +{ + PangoCoreTextFontPrivate *priv = font->priv; + + priv->key = key; + + if (priv->coverage) + { + pango_coverage_unref (priv->coverage); + priv->coverage = NULL; + } +} + +void _pango_core_text_font_set_ctfont (PangoCoreTextFont *font, CTFontRef font_ref) {