/* Pango
* pangocoretext-fontmap.c
*
* Copyright (C) 2000-2003 Red Hat, Inc.
* Copyright (C) 2005-2007 Imendio AB
* Copyright (C) 2010 Kristian Rietveld <kris@gtk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "pango-fontmap.h"
#include "pangocoretext-private.h"
#include "pango-impl-utils.h"
#include <Carbon/Carbon.h>
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 'm':
case 'M':
if (g_ascii_strcasecmp (family_name, "monospace") == 0)
return "Courier";
break;
case 's':
case 'S':
if (g_ascii_strcasecmp (family_name, "sans") == 0)
return "Helvetica";
else if (g_ascii_strcasecmp (family_name, "serif") == 0)
return "Times";
break;
}
return family_name;
}
static gchar *
gchar_from_cf_string (CFStringRef str)
{
CFIndex len;
gchar *buffer;
/* GetLength returns the number of UTF-16 pairs, so this number
* times 2 should definitely gives us enough space for UTF8.
* We add one for the terminating zero.
*/
len = CFStringGetLength (str) * 2 + 1;
buffer = g_new0 (char, len);
CFStringGetCString (str, buffer, len, kCFStringEncodingUTF8);
return buffer;
}
static 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;
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);
/* 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;
*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,
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_memdup (ctfamily->faces, ctfamily->n_faces * sizeof (PangoFontFace *));
}
static const char *
pango_core_text_family_get_name (PangoFontFamily *family)
{
PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family);
return ctfamily->family_name;
}
static gboolean
pango_core_text_family_is_monospace (PangoFontFamily *family)
{
PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family);
return ctfamily->is_monospace;
}
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);
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]);
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 "Sans",
* 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);
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 (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 (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 */
family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
family->family_name = g_strdup ("Sans");
g_hash_table_insert (ctfontmap->families,
g_utf8_casefold (family->family_name, -1), family);
family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
family->family_name = g_strdup ("Serif");
g_hash_table_insert (ctfontmap->families,
g_utf8_casefold (family->family_name, -1), family);
family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
family->family_name = g_strdup ("Monospace");
family->is_monospace = TRUE;
g_hash_table_insert (ctfontmap->families,
g_utf8_casefold (family->family_name, -1), family);
}
static void
pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (class);
object_class->finalize = pango_core_text_font_map_finalize;
fontmap_class->load_font = pango_core_text_font_map_load_font;
fontmap_class->list_families = pango_core_text_font_map_list_families;
fontmap_class->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;
}
/*
* 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;
};
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;
GPtrArray *fonts;
int i;
fonts = g_ptr_array_new ();
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 (fonts, font);
if (best_font == NULL) best_font = font;
}
}
}
}
g_strfreev (family_names);
if (!best_font)
{
g_ptr_array_free (fonts, false);
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 = fonts;
fontset->coverages = g_ptr_array_new ();
/* 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 + 1 for the "real" font at the front */
g_ptr_array_set_size (fontset->fonts, CFArrayGetCount (fontset->cascade_list) + fonts->len);
g_ptr_array_set_size (fontset->coverages, CFArrayGetCount (fontset->cascade_list) + fonts->len);
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);
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 (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;
}
}