1 /* Pango
2  * pangocoretext-fontmap.c
3  *
4  * Copyright (C) 2000-2003 Red Hat, Inc.
5  * Copyright (C) 2005-2007 Imendio AB
6  * Copyright (C) 2010  Kristian Rietveld  <kris@gtk.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include "config.h"
25 
26 #include "pango-fontmap.h"
27 #include "pangocoretext-private.h"
28 #include "pango-impl-utils.h"
29 
30 #include <Carbon/Carbon.h>
31 
32 typedef struct _FontHashKey      FontHashKey;
33 
34 typedef struct _PangoCoreTextFontset PangoCoreTextFontset;
35 
36 #define PANGO_TYPE_CORE_TEXT_FAMILY              (pango_core_text_family_get_type ())
37 #define PANGO_CORE_TEXT_FAMILY(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_CORE_TEXT_FAMILY, PangoCoreTextFamily))
38 #define PANGO_IS_CORE_TEXT_FAMILY(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_CORE_TEXT_FAMILY))
39 #define PANGO_CORE_TEXT_FAMILY_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_CORE_TEXT_FAMILY, PangoCoreTextFamilyClass))
40 #define PANGO_IS_CORE_TEXT_FAMILY_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_CORE_TEXT_FAMILY))
41 #define PANGO_CORE_TEXT_FAMILY_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), PANGO_CORE_TEXT_FAMILY, PangoCoreTextFamilyClass))
42 
43 #define PANGO_TYPE_CORE_TEXT_FONTSET           (pango_core_text_fontset_get_type ())
44 #define PANGO_CORE_TEXT_FONTSET(object)        (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_CORE_TEXT_FONTSET, PangoCoreTextFontset))
45 #define PANGO_IS_CORE_TEXT_FONTSET(object)     (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_CORE_TEXT_FONTSET))
46 
47 
48 struct _PangoCoreTextFamily
49 {
50   PangoFontFamily parent_instance;
51 
52   char *family_name;
53 
54   guint is_monospace : 1;
55 
56   PangoFontFace **faces;
57   gint n_faces;
58 };
59 
60 struct _PangoCoreTextFamilyClass
61 {
62   PangoFontFamilyClass parent_class;
63 };
64 
65 typedef struct _PangoCoreTextFamilyClass PangoCoreTextFamilyClass;
66 
67 #define PANGO_TYPE_CORE_TEXT_FACE              (pango_core_text_face_get_type ())
68 #define PANGO_CORE_TEXT_FACE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_CORE_TEXT_FACE, PangoCoreTextFace))
69 #define PANGO_IS_CORE_TEXT_FACE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_CORE_TEXT_FACE))
70 #define PANGO_CORE_TEXT_FACE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_CORE_TEXT_FACE, PangoCoreTextFaceClass))
71 #define PANGO_IS_CORE_TEXT_FACE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_CORE_TEXT_FACE))
72 #define PANGO_CORE_TEXT_FACE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), PANGO_CORE_TEXT_FACE, PangoCoreTextFaceClass))
73 
74 struct _PangoCoreTextFace
75 {
76   PangoFontFace parent_instance;
77 
78   PangoCoreTextFamily *family;
79 
80   CTFontDescriptorRef ctfontdescriptor;
81 
82   char *style_name;
83   PangoWeight weight;
84   CTFontSymbolicTraits traits;
85   guint synthetic_italic : 1;
86 };
87 
88 struct _PangoCoreTextFaceClass
89 {
90   PangoFontFaceClass parent_class;
91 };
92 
93 typedef struct _PangoCoreTextFaceClass PangoCoreTextFaceClass;
94 
95 static GType pango_core_text_family_get_type (void);
96 static GType pango_core_text_face_get_type (void);
97 static GType pango_core_text_fontset_get_type (void);
98 
99 static PangoCoreTextFontset    *pango_core_text_fontset_new     (PangoCoreTextFontsetKey    *key,
100                                                                  const PangoFontDescription *description);
101 static PangoCoreTextFontsetKey *pango_core_text_fontset_get_key (PangoCoreTextFontset       *fontset);
102 
103 /*
104  * Helper functions to translate CoreText data to Pango
105  */
106 
107 typedef struct
108 {
109     float ct_weight;
110     PangoWeight pango_weight;
111 } PangoCTWeight;
112 
113 #define ct_weight_min -0.7f
114 #define ct_weight_max  0.8f
115 
116 /* This map is based on empirical data from analyzing a large collection of
117  * fonts and comparing the opentype value with the value that OSX returns.
118  * see: https://bugzilla.gnome.org/show_bug.cgi?id=766148
119  */
120 
121 static const PangoCTWeight ct_weight_map[] = {
122     { ct_weight_min, PANGO_WEIGHT_THIN },
123     { -0.5, PANGO_WEIGHT_ULTRALIGHT },
124     { -0.23, PANGO_WEIGHT_LIGHT },
125     { -0.115, PANGO_WEIGHT_SEMILIGHT },
126     {  0.00, PANGO_WEIGHT_NORMAL },
127     {  0.2, PANGO_WEIGHT_MEDIUM },
128     {  0.3, PANGO_WEIGHT_SEMIBOLD },
129     {  0.4, PANGO_WEIGHT_BOLD },
130     {  0.6, PANGO_WEIGHT_ULTRABOLD },
131     {  ct_weight_max, PANGO_WEIGHT_HEAVY }
132 };
133 
134 static const char *
get_real_family(const char * family_name)135 get_real_family (const char *family_name)
136 {
137   switch (family_name[0])
138     {
139     case 'c':
140     case 'C':
141       if (g_ascii_strcasecmp (family_name, "cursive") == 0)
142 	return "Apple Chancery";
143       break;
144     case 'f':
145     case 'F':
146       if (g_ascii_strcasecmp (family_name, "fantasy") == 0)
147 	return "Papyrus";
148       break;
149     case 'm':
150     case 'M':
151       if (g_ascii_strcasecmp (family_name, "monospace") == 0)
152 	return "Courier";
153       break;
154     case 's':
155     case 'S':
156       if (g_ascii_strcasecmp (family_name, "sans") == 0)
157 	return "Helvetica";
158       else if (g_ascii_strcasecmp (family_name, "serif") == 0)
159 	return "Times";
160       else if (g_ascii_strcasecmp (family_name, "system-ui") == 0)
161 	return ".AppleSystemUIFont";
162       break;
163     }
164 
165   return family_name;
166 }
167 
168 static gchar *
gchar_from_cf_string(CFStringRef str)169 gchar_from_cf_string (CFStringRef str)
170 {
171   CFIndex len;
172   gchar *buffer;
173 
174   /* GetLength returns the number of UTF-16 pairs, so this number
175    * times 2 should definitely gives us enough space for UTF8.
176    * We add one for the terminating zero.
177    */
178   len = CFStringGetLength (str) * 2 + 1;
179   buffer = g_new0 (char, len);
180   CFStringGetCString (str, buffer, len, kCFStringEncodingUTF8);
181 
182   return buffer;
183 }
184 
185 static char *
ct_font_descriptor_get_family_name(CTFontDescriptorRef desc,gboolean may_fail)186 ct_font_descriptor_get_family_name (CTFontDescriptorRef desc,
187                                     gboolean            may_fail)
188 {
189   CFStringRef cf_str;
190   char *buffer;
191 
192   cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontFamilyNameAttribute);
193   if (!cf_str)
194     {
195       int i;
196 
197       /* No font family name is set, try to retrieve font name and deduce
198        * the family name from that instead.
199        */
200       cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontNameAttribute);
201       if (!cf_str)
202         {
203           if (may_fail)
204             return NULL;
205 
206           /* This font is likely broken, return a default family name ... */
207           return g_strdup ("Sans");
208         }
209 
210       buffer = gchar_from_cf_string (cf_str);
211       CFRelease (cf_str);
212 
213       for (i = 0; i < strlen (buffer); i++)
214         if (buffer[i] == '-')
215           break;
216 
217       if (i < strlen (buffer))
218         {
219           char *ret;
220 
221           ret = g_strndup (buffer, i);
222           g_free (buffer);
223 
224           return ret;
225         }
226       else
227         return buffer;
228     }
229   /* else */
230 
231   buffer = gchar_from_cf_string (cf_str);
232   CFRelease (cf_str);
233 
234   return buffer;
235 }
236 
237 static char *
ct_font_descriptor_get_style_name(CTFontDescriptorRef desc)238 ct_font_descriptor_get_style_name (CTFontDescriptorRef desc)
239 {
240   CFStringRef cf_str;
241   char *buffer;
242 
243   cf_str = CTFontDescriptorCopyAttribute (desc, kCTFontStyleNameAttribute);
244   if (!cf_str)
245     return NULL;
246 
247   buffer = gchar_from_cf_string (cf_str);
248   CFRelease (cf_str);
249 
250   return buffer;
251 }
252 
253 static CTFontSymbolicTraits
ct_font_descriptor_get_traits(CTFontDescriptorRef desc)254 ct_font_descriptor_get_traits (CTFontDescriptorRef desc)
255 {
256   CFDictionaryRef dict;
257   CFNumberRef cf_number;
258   SInt64 traits;
259 
260   /* This is interesting, the value stored is a CTFontSymbolicTraits which
261    * is defined as uint32_t.  CFNumber does not have an obvious type which
262    * deals with unsigned values.  Upon inspection with CFNumberGetType,
263    * it turns out this value is stored as SInt64, so we use that to
264    * obtain the value from the CFNumber.
265    */
266   dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute);
267   cf_number = (CFNumberRef)CFDictionaryGetValue (dict, kCTFontSymbolicTrait);
268   if (!CFNumberGetValue (cf_number, kCFNumberSInt64Type, &traits))
269     traits = 0;
270   CFRelease (dict);
271 
272   return (CTFontSymbolicTraits)traits;
273 }
274 
275 static CTFontDescriptorRef
cf_font_descriptor_copy_with_traits(CTFontDescriptorRef desc,const CTFontSymbolicTraits traits)276 cf_font_descriptor_copy_with_traits (CTFontDescriptorRef        desc,
277                                      const CTFontSymbolicTraits traits)
278 {
279   CFMutableDictionaryRef dict, traits_dict;
280   CFDictionaryRef tmp;
281   CTFontDescriptorRef new_desc;
282   SInt64 tmp_traits;
283 
284   tmp = CTFontDescriptorCopyAttributes (desc);
285   dict = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, tmp);
286   CFRelease (tmp);
287 
288   tmp = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute);
289   traits_dict = CFDictionaryCreateMutableCopy (kCFAllocatorDefault, 0, tmp);
290   CFRelease (tmp);
291 
292   tmp_traits = traits;
293   CFDictionarySetValue (traits_dict, (CFTypeRef) kCTFontSymbolicTrait,
294                         CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &tmp_traits));
295 
296   CFDictionarySetValue (dict, (CFTypeRef)kCTFontTraitsAttribute, traits_dict);
297 
298   new_desc = CTFontDescriptorCreateCopyWithAttributes (desc, dict);
299   CFRelease (dict);
300 
301   return new_desc;
302 }
303 
304 static int
lerp(float x,float x1,float x2,int y1,int y2)305 lerp(float x, float x1, float x2, int y1, int y2) {
306   float dx = x2 - x1;
307   int dy = y2 - y1;
308   return y1 + (dy*(x-x1) + dx/2) / dx;
309 }
310 
311 static PangoWeight
ct_font_descriptor_get_weight(CTFontDescriptorRef desc)312 ct_font_descriptor_get_weight (CTFontDescriptorRef desc)
313 {
314   CFDictionaryRef dict;
315   CFNumberRef cf_number;
316   CGFloat value;
317   PangoWeight weight = PANGO_WEIGHT_NORMAL;
318 
319   dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute);
320   cf_number = (CFNumberRef)CFDictionaryGetValue (dict,
321                                                  kCTFontWeightTrait);
322 
323   if (cf_number != NULL && CFNumberGetValue (cf_number, kCFNumberCGFloatType, &value))
324     {
325     if (value < ct_weight_min || value > ct_weight_max)
326       {
327         /* This is really an error */
328         weight = PANGO_WEIGHT_NORMAL;
329       }
330     else
331       {
332         guint i;
333         for (i = 1; value > ct_weight_map[i].ct_weight; ++i)
334           ;
335 
336         weight = lerp(value, ct_weight_map[i-1].ct_weight, ct_weight_map[i].ct_weight,
337                       ct_weight_map[i-1].pango_weight, ct_weight_map[i].pango_weight);
338 
339       }
340     }
341   else
342     weight = PANGO_WEIGHT_NORMAL;
343 
344   CFRelease (dict);
345 
346   return weight;
347 }
348 
349 static gboolean
ct_font_descriptor_is_small_caps(CTFontDescriptorRef desc)350 ct_font_descriptor_is_small_caps (CTFontDescriptorRef desc)
351 {
352   CFIndex i, count;
353   CFArrayRef array;
354   CFStringRef str;
355   gboolean retval = FALSE;
356 
357   /* See http://stackoverflow.com/a/4811371 for why this works and an
358    * explanation of the magic number "3" used below.
359    */
360   array = CTFontDescriptorCopyAttribute (desc, kCTFontFeaturesAttribute);
361   if (!array)
362     return FALSE;
363 
364   str = CFStringCreateWithCString (NULL, "CTFeatureTypeIdentifier",
365                                    kCFStringEncodingASCII);
366 
367   count = CFArrayGetCount (array);
368   for (i = 0; i < count; i++)
369     {
370       CFDictionaryRef dict = CFArrayGetValueAtIndex (array, i);
371       CFNumberRef num;
372 
373       num = (CFNumberRef)CFDictionaryGetValue (dict, str);
374       if (num)
375         {
376           int value = 0;
377 
378           if (CFNumberGetValue (num, kCFNumberSInt32Type, &value) &&
379               value == 3)
380             {
381               /* This font supports small caps. */
382               retval = TRUE;
383               break;
384             }
385         }
386     }
387 
388   CFRelease (str);
389   CFRelease (array);
390 
391   return retval;
392 }
393 
394 static inline gboolean
pango_core_text_style_name_is_oblique(const char * style_name)395 pango_core_text_style_name_is_oblique (const char *style_name)
396 {
397   if (!style_name)
398     return FALSE;
399 
400   return g_strrstr (style_name, "Oblique") != NULL;
401 }
402 
403 PangoFontDescription *
_pango_core_text_font_description_from_ct_font_descriptor(CTFontDescriptorRef desc)404 _pango_core_text_font_description_from_ct_font_descriptor (CTFontDescriptorRef desc)
405 {
406   SInt64 font_traits;
407   char *family_name;
408   char *style_name;
409   PangoFontDescription *font_desc;
410 
411   font_desc = pango_font_description_new ();
412 
413   /* Family name */
414 
415   /* FIXME: Should we actually retrieve the family name from the list of
416    * families in a font map?
417    */
418   family_name = ct_font_descriptor_get_family_name (desc, FALSE);
419   pango_font_description_set_family (font_desc, family_name);
420   g_free (family_name);
421 
422   /* Weight */
423   pango_font_description_set_weight (font_desc,
424                                      ct_font_descriptor_get_weight (desc));
425 
426   /* Font traits, style name; from this we deduce style and variant */
427   font_traits = ct_font_descriptor_get_traits (desc);
428   style_name = ct_font_descriptor_get_style_name (desc);
429 
430   if ((font_traits & kCTFontItalicTrait) == kCTFontItalicTrait)
431     pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
432   else if (pango_core_text_style_name_is_oblique (style_name))
433     pango_font_description_set_style (font_desc, PANGO_STYLE_OBLIQUE);
434   else
435     pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL);
436 
437   if ((font_traits & kCTFontCondensedTrait) == kCTFontCondensedTrait)
438     pango_font_description_set_stretch (font_desc, PANGO_STRETCH_CONDENSED);
439 
440   if (ct_font_descriptor_is_small_caps (desc))
441     pango_font_description_set_variant (font_desc, PANGO_VARIANT_SMALL_CAPS);
442   else
443     pango_font_description_set_variant (font_desc, PANGO_VARIANT_NORMAL);
444 
445   g_free (style_name);
446 
447   return font_desc;
448 }
449 
450 /*
451  * PangoCoreTextFace
452  */
453 
454 static inline gboolean
pango_core_text_face_is_oblique(PangoCoreTextFace * face)455 pango_core_text_face_is_oblique (PangoCoreTextFace *face)
456 {
457   return pango_core_text_style_name_is_oblique (face->style_name);
458 }
459 
460 static void
pango_core_text_face_make_italic(PangoCoreTextFace * ctface,gboolean synthetic_italic)461 pango_core_text_face_make_italic (PangoCoreTextFace *ctface,
462                                   gboolean           synthetic_italic)
463 {
464   CTFontDescriptorRef new_desc;
465 
466   ctface->traits |= kCTFontItalicTrait;
467   if (synthetic_italic)
468     ctface->synthetic_italic = TRUE;
469 
470   /* Update the font descriptor */
471   new_desc = cf_font_descriptor_copy_with_traits (ctface->ctfontdescriptor,
472                                                   ctface->traits);
473   CFRelease (ctface->ctfontdescriptor);
474   ctface->ctfontdescriptor = new_desc;
475 }
476 
477 static inline PangoCoreTextFace *
pango_core_text_face_copy(const PangoCoreTextFace * old)478 pango_core_text_face_copy (const PangoCoreTextFace *old)
479 {
480   PangoCoreTextFace *face;
481 
482   face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE, NULL);
483   face->family = old->family;
484   face->ctfontdescriptor = CFRetain (old->ctfontdescriptor);
485   face->style_name = g_strdup (old->style_name);
486   face->weight = old->weight;
487   face->traits = old->traits;
488   face->synthetic_italic = old->synthetic_italic;
489 
490   return face;
491 }
492 
493 static inline PangoCoreTextFace *
pango_core_text_face_from_ct_font_descriptor(CTFontDescriptorRef desc)494 pango_core_text_face_from_ct_font_descriptor (CTFontDescriptorRef desc)
495 {
496   PangoCoreTextFace *face = g_object_new (PANGO_TYPE_CORE_TEXT_FACE,
497                                           NULL);
498 
499   face->synthetic_italic = FALSE;
500 
501   face->ctfontdescriptor = CFRetain (desc);
502 
503   face->style_name = ct_font_descriptor_get_style_name (desc);
504   face->traits = ct_font_descriptor_get_traits (desc);
505   face->weight = ct_font_descriptor_get_weight (desc);
506 
507   return face;
508 }
509 
510 static PangoFontDescription *
pango_core_text_face_describe(PangoFontFace * face)511 pango_core_text_face_describe (PangoFontFace *face)
512 {
513   PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face);
514 
515   return _pango_core_text_font_description_from_ct_font_descriptor (ctface->ctfontdescriptor);
516 }
517 
518 static const char *
pango_core_text_face_get_face_name(PangoFontFace * face)519 pango_core_text_face_get_face_name (PangoFontFace *face)
520 {
521   PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (face);
522 
523   return ctface->style_name;
524 }
525 
526 static void
pango_core_text_face_list_sizes(PangoFontFace * face,int ** sizes,int * n_sizes)527 pango_core_text_face_list_sizes (PangoFontFace  *face,
528                                  int           **sizes,
529                                  int            *n_sizes)
530 {
531   *n_sizes = 0;
532   if (sizes)
533     *sizes = NULL;
534 }
535 
536 G_DEFINE_TYPE (PangoCoreTextFace, pango_core_text_face, PANGO_TYPE_FONT_FACE);
537 
538 static void
pango_core_text_face_init(PangoCoreTextFace * face)539 pango_core_text_face_init (PangoCoreTextFace *face)
540 {
541   face->family = NULL;
542 }
543 
544 static void
pango_core_text_face_finalize(GObject * object)545 pango_core_text_face_finalize (GObject *object)
546 {
547   PangoCoreTextFace *ctface = PANGO_CORE_TEXT_FACE (object);
548 
549   g_free (ctface->style_name);
550   CFRelease (ctface->ctfontdescriptor);
551 
552   G_OBJECT_CLASS (pango_core_text_face_parent_class)->finalize (object);
553 }
554 
555 static gboolean
pango_core_text_face_is_synthesized(PangoFontFace * face)556 pango_core_text_face_is_synthesized (PangoFontFace *face)
557 {
558   PangoCoreTextFace *cface = PANGO_CORE_TEXT_FACE (face);
559 
560   return cface->synthetic_italic;
561 }
562 
563 static PangoFontFamily *
pango_core_text_face_get_family(PangoFontFace * face)564 pango_core_text_face_get_family (PangoFontFace *face)
565 {
566   PangoCoreTextFace *cface = PANGO_CORE_TEXT_FACE (face);
567 
568   return PANGO_FONT_FAMILY (cface->family);
569 }
570 
571 static void
pango_core_text_face_class_init(PangoCoreTextFaceClass * klass)572 pango_core_text_face_class_init (PangoCoreTextFaceClass *klass)
573 {
574   GObjectClass *object_class = (GObjectClass *)klass;
575   PangoFontFaceClass *pfclass = PANGO_FONT_FACE_CLASS(klass);
576 
577   object_class->finalize = pango_core_text_face_finalize;
578 
579   pfclass->describe = pango_core_text_face_describe;
580   pfclass->get_face_name = pango_core_text_face_get_face_name;
581   pfclass->list_sizes = pango_core_text_face_list_sizes;
582   pfclass->is_synthesized = pango_core_text_face_is_synthesized;
583   pfclass->get_family = pango_core_text_face_get_family;
584 }
585 
586 /*
587  * PangoCoreTextFamily
588  */
589 
590 static void
pango_core_text_family_list_faces(PangoFontFamily * family,PangoFontFace *** faces,int * n_faces)591 pango_core_text_family_list_faces (PangoFontFamily  *family,
592                                    PangoFontFace  ***faces,
593                                    int              *n_faces)
594 {
595   PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family);
596 
597   if (ctfamily->n_faces < 0)
598     {
599       GList *l;
600       GList *faces = NULL;
601       GList *synthetic_faces = NULL;
602       GHashTable *italic_faces;
603       const char *real_family = get_real_family (ctfamily->family_name);
604       CTFontCollectionRef collection;
605       CFArrayRef ctfaces;
606       CFArrayRef font_descriptors;
607       CFDictionaryRef attributes;
608       CFIndex i, count;
609 
610       CFTypeRef keys[] = {
611           (CFTypeRef) kCTFontFamilyNameAttribute
612       };
613 
614       CFStringRef values[] = {
615           CFStringCreateWithCString (kCFAllocatorDefault,
616                                      real_family,
617                                      kCFStringEncodingUTF8)
618       };
619 
620       CTFontDescriptorRef descriptors[1];
621 
622       attributes = CFDictionaryCreate (kCFAllocatorDefault,
623                                        (const void **)keys,
624                                        (const void **)values,
625                                        1,
626                                        &kCFTypeDictionaryKeyCallBacks,
627                                        &kCFTypeDictionaryValueCallBacks);
628       descriptors[0] = CTFontDescriptorCreateWithAttributes (attributes);
629       font_descriptors = CFArrayCreate (kCFAllocatorDefault,
630                                         (const void **)descriptors,
631                                         1,
632                                         &kCFTypeArrayCallBacks);
633       collection = CTFontCollectionCreateWithFontDescriptors (font_descriptors,
634                                                               NULL);
635 
636       ctfaces = CTFontCollectionCreateMatchingFontDescriptors (collection);
637 
638       italic_faces = g_hash_table_new (g_direct_hash, g_direct_equal);
639 
640       count = CFArrayGetCount (ctfaces);
641       for (i = 0; i < count; i++)
642         {
643           PangoCoreTextFace *face;
644           CTFontDescriptorRef desc = CFArrayGetValueAtIndex (ctfaces, i);
645 
646           face = pango_core_text_face_from_ct_font_descriptor (desc);
647           face->family = ctfamily;
648 
649           faces = g_list_prepend (faces, face);
650 
651           if ((face->traits & kCTFontItalicTrait) == kCTFontItalicTrait ||
652               pango_core_text_face_is_oblique (face))
653             g_hash_table_insert (italic_faces,
654 				 GINT_TO_POINTER ((gint)face->weight),
655                                  face);
656         }
657 
658       CFRelease (font_descriptors);
659       CFRelease (attributes);
660       CFRelease (ctfaces);
661 
662       /* For all fonts for which a non-synthetic italic variant does
663        * not exist on the system, we create synthesized versions here.
664        */
665       for (l = faces; l; l = l->next)
666         {
667           PangoCoreTextFace *face = l->data;
668 
669           if (!g_hash_table_lookup (italic_faces,
670                                     GINT_TO_POINTER ((gint)face->weight)))
671             {
672               PangoCoreTextFace *italic_face;
673 
674               italic_face = pango_core_text_face_copy (face);
675 
676               italic_face->family = ctfamily;
677               pango_core_text_face_make_italic (italic_face, TRUE);
678 
679               /* Try to create a sensible face name. */
680               g_free (italic_face->style_name);
681               if (strcasecmp (face->style_name, "regular") == 0)
682                 italic_face->style_name = g_strdup ("Oblique");
683               else
684                 italic_face->style_name = g_strdup_printf ("%s Oblique",
685                                                            face->style_name);
686 
687               synthetic_faces = g_list_prepend (synthetic_faces, italic_face);
688             }
689         }
690 
691       faces = g_list_concat (faces, synthetic_faces);
692 
693       ctfamily->n_faces = g_list_length (faces);
694       ctfamily->faces = g_new (PangoFontFace *, ctfamily->n_faces);
695 
696       for (l = faces, i = 0; l; l = l->next, i++)
697 	ctfamily->faces[i] = l->data;
698 
699       g_list_free (faces);
700       g_hash_table_destroy (italic_faces);
701     }
702 
703   if (n_faces)
704     *n_faces = ctfamily->n_faces;
705 
706   if (faces)
707     *faces = g_memdup2 (ctfamily->faces, ctfamily->n_faces * sizeof (PangoFontFace *));
708 }
709 
710 static const char *
pango_core_text_family_get_name(PangoFontFamily * family)711 pango_core_text_family_get_name (PangoFontFamily *family)
712 {
713   PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family);
714 
715   return ctfamily->family_name;
716 }
717 
718 static gboolean
pango_core_text_family_is_monospace(PangoFontFamily * family)719 pango_core_text_family_is_monospace (PangoFontFamily *family)
720 {
721   PangoCoreTextFamily *ctfamily = PANGO_CORE_TEXT_FAMILY (family);
722 
723   return ctfamily->is_monospace;
724 }
725 
726 G_DEFINE_TYPE (PangoCoreTextFamily, pango_core_text_family, PANGO_TYPE_FONT_FAMILY);
727 
728 static void
pango_core_text_family_finalize(GObject * object)729 pango_core_text_family_finalize (GObject *object)
730 {
731   PangoCoreTextFamily *family = PANGO_CORE_TEXT_FAMILY (object);
732   int i;
733 
734   g_free (family->family_name);
735 
736   if (family->n_faces != -1)
737     {
738       for (i = 0; i < family->n_faces; i++)
739 	g_object_unref (family->faces[i]);
740 
741       g_free (family->faces);
742     }
743 
744   G_OBJECT_CLASS (pango_core_text_family_parent_class)->finalize (object);
745 }
746 
747 
748 static void
pango_core_text_family_class_init(PangoCoreTextFamilyClass * klass)749 pango_core_text_family_class_init (PangoCoreTextFamilyClass *klass)
750 {
751   GObjectClass *object_class = (GObjectClass *)klass;
752   PangoFontFamilyClass *pfclass = PANGO_FONT_FAMILY_CLASS(klass);
753 
754   object_class->finalize = pango_core_text_family_finalize;
755 
756   pfclass->list_faces = pango_core_text_family_list_faces;
757   pfclass->get_name = pango_core_text_family_get_name;
758   pfclass->is_monospace = pango_core_text_family_is_monospace;
759 }
760 
761 static void
pango_core_text_family_init(PangoCoreTextFamily * family)762 pango_core_text_family_init (PangoCoreTextFamily *family)
763 {
764   family->n_faces = -1;
765 }
766 
767 
768 
769 static void pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class);
770 static void pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap);
771 
772 
773 G_DEFINE_TYPE (PangoCoreTextFontMap, pango_core_text_font_map, PANGO_TYPE_FONT_MAP);
774 
775 static void
pango_core_text_font_map_finalize(GObject * object)776 pango_core_text_font_map_finalize (GObject *object)
777 {
778   PangoCoreTextFontMap *fontmap = PANGO_CORE_TEXT_FONT_MAP (object);
779 
780   g_hash_table_destroy (fontmap->fontset_hash);
781   g_hash_table_destroy (fontmap->font_hash);
782   g_hash_table_destroy (fontmap->families);
783 
784   G_OBJECT_CLASS (pango_core_text_font_map_parent_class)->finalize (object);
785 }
786 
787 /* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
788  *
789  * Not necessarily better than a lot of other hashes, but should be OK, and
790  * well tested with binary data.
791  */
792 
793 #define FNV_32_PRIME ((guint32)0x01000193)
794 #define FNV1_32_INIT ((guint32)0x811c9dc5)
795 
796 static guint32
hash_bytes_fnv(unsigned char * buffer,int len,guint32 hval)797 hash_bytes_fnv (unsigned char *buffer,
798 		int            len,
799 		guint32        hval)
800 {
801   while (len--)
802     {
803       hval *= FNV_32_PRIME;
804       hval ^= *buffer++;
805     }
806 
807   return hval;
808 }
809 
810 static void
get_context_matrix(PangoContext * context,PangoMatrix * matrix)811 get_context_matrix (PangoContext *context,
812 		    PangoMatrix *matrix)
813 {
814   const PangoMatrix *set_matrix;
815   const PangoMatrix identity = PANGO_MATRIX_INIT;
816 
817   if (context)
818     set_matrix = pango_context_get_matrix (context);
819   else
820     set_matrix = NULL;
821 
822   if (set_matrix)
823     *matrix = *set_matrix;
824   else
825     *matrix = identity;
826 }
827 
828 /*
829  * Helper functions for PangoCoreTextFontsetKey
830  */
831 static const double ppi = 72.0; /* typographic points per inch */
832 
833 static double
pango_core_text_font_map_get_resolution(PangoCoreTextFontMap * fontmap,PangoContext * context)834 pango_core_text_font_map_get_resolution (PangoCoreTextFontMap *fontmap,
835                                          PangoContext         *context)
836 {
837   if (PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->get_resolution)
838     return PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->get_resolution (fontmap, context);
839 
840   /* FIXME: acquire DPI from CoreText using some deafault font */
841   g_warning ("FIXME: returning default DPI");
842 
843   return ppi;
844 }
845 
846 static int
get_scaled_size(PangoCoreTextFontMap * fontmap,PangoContext * context,const PangoFontDescription * desc)847 get_scaled_size (PangoCoreTextFontMap       *fontmap,
848                  PangoContext               *context,
849                  const PangoFontDescription *desc)
850 {
851   double size = pango_font_description_get_size (desc);
852   PangoMatrix *matrix = pango_context_get_matrix (context);
853   double scale_factor = pango_matrix_get_font_scale_factor (matrix);
854 
855   if (!pango_font_description_get_size_is_absolute(desc))
856   {
857     double dpi = pango_core_text_font_map_get_resolution (fontmap, context);
858     size *= (dpi/ppi);
859   }
860 
861   return .5 +  scale_factor * size;
862 }
863 
864 
865 /*
866  * PangoCoreTextFontsetKey
867  */
868 struct _PangoCoreTextFontsetKey
869 {
870   PangoCoreTextFontMap *fontmap;
871   PangoLanguage *language;
872   PangoFontDescription *desc;
873   PangoMatrix matrix;
874   int pointsize;
875   double resolution;
876   PangoGravity gravity;
877   gpointer context_key;
878 };
879 
880 static void
pango_core_text_fontset_key_init(PangoCoreTextFontsetKey * key,PangoCoreTextFontMap * fontmap,PangoContext * context,const PangoFontDescription * desc,PangoLanguage * language)881 pango_core_text_fontset_key_init (PangoCoreTextFontsetKey    *key,
882                                   PangoCoreTextFontMap       *fontmap,
883                                   PangoContext               *context,
884                                   const PangoFontDescription *desc,
885                                   PangoLanguage              *language)
886 {
887   if (!language && context)
888     language = pango_context_get_language (context);
889 
890   key->fontmap = fontmap;
891   get_context_matrix (context, &key->matrix);
892   key->language = language;
893   key->pointsize = get_scaled_size (fontmap, context, desc);
894   key->resolution = pango_core_text_font_map_get_resolution (fontmap, context);
895   key->gravity = pango_context_get_gravity (context);
896   key->desc = pango_font_description_copy_static (desc);
897   pango_font_description_unset_fields (key->desc, PANGO_FONT_MASK_SIZE);
898 
899   if (context && PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->context_key_get)
900     key->context_key = (gpointer)PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap)->context_key_get (fontmap, context);
901   else
902     key->context_key = NULL;
903 }
904 
905 static PangoCoreTextFontsetKey *
pango_core_text_fontset_key_copy(const PangoCoreTextFontsetKey * old)906 pango_core_text_fontset_key_copy (const PangoCoreTextFontsetKey *old)
907 {
908   PangoCoreTextFontsetKey *key = g_slice_new (PangoCoreTextFontsetKey);
909 
910   key->fontmap = old->fontmap;
911   key->matrix = old->matrix;
912   key->language = old->language;
913   key->pointsize = old->pointsize;
914   key->resolution = old->resolution;
915   key->gravity = old->gravity;
916   key->desc = pango_font_description_copy (old->desc);
917   if (old->context_key)
918     key->context_key = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap, old->context_key);
919   else
920     key->context_key = NULL;
921 
922   return key;
923 }
924 
925 static void
pango_core_text_fontset_key_free(PangoCoreTextFontsetKey * key)926 pango_core_text_fontset_key_free (PangoCoreTextFontsetKey *key)
927 {
928   pango_font_description_free (key->desc);
929 
930   if (key->context_key)
931     PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, key->context_key);
932 
933   g_slice_free (PangoCoreTextFontsetKey, key);
934 }
935 
936 static guint
pango_core_text_fontset_key_hash(const PangoCoreTextFontsetKey * key)937 pango_core_text_fontset_key_hash (const PangoCoreTextFontsetKey *key)
938 {
939   guint32 hash = FNV1_32_INIT;
940 
941   hash = hash_bytes_fnv ((unsigned char *)(&key->matrix), sizeof (double) * 4, hash);
942   hash ^= hash_bytes_fnv ((unsigned char *)(&key->resolution), sizeof (double), hash);
943 
944   if (key->context_key)
945     hash ^= PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap, key->context_key);
946 
947   return (hash ^
948           GPOINTER_TO_UINT (key->language) ^
949           pango_font_description_hash (key->desc));
950 }
951 
952 static gboolean
pango_core_text_fontset_key_equal(const PangoCoreTextFontsetKey * key_a,const PangoCoreTextFontsetKey * key_b)953 pango_core_text_fontset_key_equal (const PangoCoreTextFontsetKey *key_a,
954                                    const PangoCoreTextFontsetKey *key_b)
955 {
956   if (key_a->language == key_b->language &&
957       key_a->pointsize == key_b->pointsize &&
958       key_a->resolution == key_b->resolution &&
959       key_a->gravity == key_b->gravity &&
960       pango_font_description_equal (key_a->desc, key_b->desc) &&
961       memcmp ((void *)&key_a->matrix, (void *)&key_b->matrix, 4 * sizeof (double)) == 0)
962     {
963       if (key_a->context_key)
964         return PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key_a->fontmap)->context_key_equal (key_a->fontmap,
965                                                                                        key_a->context_key,
966                                                                                        key_b->context_key);
967       else
968         return key_a->context_key == key_b->context_key;
969     }
970   /* else */
971   return FALSE;
972 }
973 
974 static PangoLanguage *
pango_core_text_fontset_key_get_language(const PangoCoreTextFontsetKey * key)975 pango_core_text_fontset_key_get_language (const PangoCoreTextFontsetKey *key)
976 {
977   return key->language;
978 }
979 
980 static const PangoMatrix *
pango_core_text_fontset_key_get_matrix(const PangoCoreTextFontsetKey * key)981 pango_core_text_fontset_key_get_matrix (const PangoCoreTextFontsetKey *key)
982 {
983   return &key->matrix;
984 }
985 
986 static PangoGravity
pango_core_text_fontset_key_get_gravity(const PangoCoreTextFontsetKey * key)987 pango_core_text_fontset_key_get_gravity (const PangoCoreTextFontsetKey *key)
988 {
989   return key->gravity;
990 }
991 
992 static gpointer
pango_core_text_fontset_key_get_context_key(const PangoCoreTextFontsetKey * key)993 pango_core_text_fontset_key_get_context_key (const PangoCoreTextFontsetKey *key)
994 {
995   return key->context_key;
996 }
997 
998 /*
999  * PangoCoreTextFontKey
1000  */
1001 struct _PangoCoreTextFontKey
1002 {
1003   PangoCoreTextFontMap *fontmap;
1004   CTFontDescriptorRef ctfontdescriptor;
1005   PangoMatrix matrix;
1006   PangoGravity gravity;
1007   int pointsize;
1008   double resolution;
1009   gboolean synthetic_italic;
1010   gpointer context_key;
1011 };
1012 
1013 static void
pango_core_text_font_key_init(PangoCoreTextFontKey * key,PangoCoreTextFontMap * ctfontmap,PangoCoreTextFontsetKey * fontset_key,CTFontDescriptorRef ctdescriptor,gboolean synthetic_italic)1014 pango_core_text_font_key_init (PangoCoreTextFontKey    *key,
1015                                PangoCoreTextFontMap    *ctfontmap,
1016                                PangoCoreTextFontsetKey *fontset_key,
1017                                CTFontDescriptorRef      ctdescriptor,
1018                                gboolean                 synthetic_italic)
1019 {
1020   key->fontmap = ctfontmap;
1021   key->ctfontdescriptor = ctdescriptor;
1022   key->matrix = *pango_core_text_fontset_key_get_matrix (fontset_key);
1023   key->pointsize = fontset_key->pointsize;
1024   key->resolution = fontset_key->resolution;
1025   key->synthetic_italic = synthetic_italic;
1026   key->gravity = pango_core_text_fontset_key_get_gravity (fontset_key);
1027   key->context_key = pango_core_text_fontset_key_get_context_key (fontset_key);
1028 }
1029 
1030 static PangoCoreTextFontKey *
pango_core_text_font_key_copy(const PangoCoreTextFontKey * old)1031 pango_core_text_font_key_copy (const PangoCoreTextFontKey *old)
1032 {
1033   PangoCoreTextFontKey *key = g_slice_new (PangoCoreTextFontKey);
1034 
1035   key->fontmap = old->fontmap;
1036   key->ctfontdescriptor = old->ctfontdescriptor;
1037   CFRetain (key->ctfontdescriptor);
1038   key->matrix = old->matrix;
1039   key->pointsize = old->pointsize;
1040   key->resolution = old->resolution;
1041   key->synthetic_italic = old->synthetic_italic;
1042   key->gravity = old->gravity;
1043   if (old->context_key)
1044     key->context_key = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap, old->context_key);
1045   else
1046     key->context_key = NULL;
1047 
1048   return key;
1049 }
1050 
1051 static void
pango_core_text_font_key_free(PangoCoreTextFontKey * key)1052 pango_core_text_font_key_free (PangoCoreTextFontKey *key)
1053 {
1054   if (key->ctfontdescriptor)
1055     CFRelease (key->ctfontdescriptor);
1056 
1057   if (key->context_key)
1058     PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap, key->context_key);
1059 
1060   g_slice_free (PangoCoreTextFontKey, key);
1061 }
1062 
1063 static guint
pango_core_text_font_key_hash(const PangoCoreTextFontKey * key)1064 pango_core_text_font_key_hash (const PangoCoreTextFontKey *key)
1065 {
1066   guint32 hash = FNV1_32_INIT;
1067 
1068   /* Not everything is included here, probably good enough for a hash */
1069 
1070   hash = hash_bytes_fnv ((unsigned char *)(&key->matrix), sizeof (double) * 4, hash);
1071 
1072   if (key->context_key)
1073     hash ^= PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap, key->context_key);
1074 
1075   return (hash ^ CFHash (key->ctfontdescriptor));
1076 }
1077 
1078 static gboolean
pango_core_text_font_key_equal(const PangoCoreTextFontKey * key_a,const PangoCoreTextFontKey * key_b)1079 pango_core_text_font_key_equal (const PangoCoreTextFontKey *key_a,
1080                                 const PangoCoreTextFontKey *key_b)
1081 {
1082   if (CFEqual (key_a->ctfontdescriptor, key_b->ctfontdescriptor) &&
1083       memcmp (&key_a->matrix, &key_b->matrix, 4 * sizeof (double)) == 0 &&
1084       key_a->gravity == key_b->gravity &&
1085       key_a->pointsize == key_b->pointsize &&
1086       key_a->resolution == key_b->resolution &&
1087       key_a->synthetic_italic == key_b->synthetic_italic)
1088     {
1089       if (key_a->context_key && key_b->context_key)
1090         return PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (key_a->fontmap)->context_key_equal (key_a->fontmap,
1091                                                                                        key_a->context_key,
1092                                                                                        key_b->context_key);
1093       else
1094         return key_a->context_key == key_b->context_key;
1095     }
1096   else
1097     return FALSE;
1098 }
1099 
1100 int
pango_core_text_font_key_get_size(const PangoCoreTextFontKey * key)1101 pango_core_text_font_key_get_size (const PangoCoreTextFontKey *key)
1102 {
1103   return key->pointsize;
1104 }
1105 
1106 double
pango_core_text_font_key_get_resolution(const PangoCoreTextFontKey * key)1107 pango_core_text_font_key_get_resolution (const PangoCoreTextFontKey *key)
1108 {
1109   return key->resolution;
1110 }
1111 
1112 gboolean
pango_core_text_font_key_get_synthetic_italic(const PangoCoreTextFontKey * key)1113 pango_core_text_font_key_get_synthetic_italic (const PangoCoreTextFontKey *key)
1114 {
1115   return key->synthetic_italic;
1116 }
1117 
1118 gpointer
pango_core_text_font_key_get_context_key(const PangoCoreTextFontKey * key)1119 pango_core_text_font_key_get_context_key (const PangoCoreTextFontKey *key)
1120 {
1121   return key->context_key;
1122 }
1123 
1124 const PangoMatrix *
pango_core_text_font_key_get_matrix(const PangoCoreTextFontKey * key)1125 pango_core_text_font_key_get_matrix (const PangoCoreTextFontKey *key)
1126 {
1127   return &key->matrix;
1128 }
1129 
1130 PangoGravity
pango_core_text_font_key_get_gravity(const PangoCoreTextFontKey * key)1131 pango_core_text_font_key_get_gravity (const PangoCoreTextFontKey *key)
1132 {
1133   return key->gravity;
1134 }
1135 
1136 CTFontDescriptorRef
pango_core_text_font_key_get_ctfontdescriptor(const PangoCoreTextFontKey * key)1137 pango_core_text_font_key_get_ctfontdescriptor (const PangoCoreTextFontKey *key)
1138 {
1139   return key->ctfontdescriptor;
1140 }
1141 
1142 
1143 
1144 static void
pango_core_text_font_map_add(PangoCoreTextFontMap * ctfontmap,PangoCoreTextFontKey * key,PangoCoreTextFont * ctfont)1145 pango_core_text_font_map_add (PangoCoreTextFontMap *ctfontmap,
1146                               PangoCoreTextFontKey *key,
1147                               PangoCoreTextFont    *ctfont)
1148 {
1149   PangoCoreTextFontKey *key_copy;
1150 
1151   _pango_core_text_font_set_font_map (ctfont, ctfontmap);
1152 
1153   key_copy = pango_core_text_font_key_copy (key);
1154   _pango_core_text_font_set_font_key (ctfont, key_copy);
1155   g_hash_table_insert (ctfontmap->font_hash, key_copy, ctfont);
1156 }
1157 
1158 static PangoCoreTextFont *
pango_core_text_font_map_new_font(PangoCoreTextFontMap * fontmap,PangoCoreTextFontsetKey * fontset_key,CTFontDescriptorRef ctfontdescriptor,gboolean synthetic_italic)1159 pango_core_text_font_map_new_font (PangoCoreTextFontMap    *fontmap,
1160                                    PangoCoreTextFontsetKey *fontset_key,
1161                                    CTFontDescriptorRef      ctfontdescriptor,
1162                                    gboolean                 synthetic_italic)
1163 {
1164   PangoCoreTextFontMapClass *klass;
1165   PangoCoreTextFont *font;
1166   PangoCoreTextFontKey key;
1167 
1168   pango_core_text_font_key_init (&key, fontmap, fontset_key, ctfontdescriptor,
1169                                  synthetic_italic);
1170 
1171   font = g_hash_table_lookup (fontmap->font_hash, &key);
1172   if (font)
1173     return g_object_ref (font);
1174 
1175   /* Call create_font */
1176   klass = PANGO_CORE_TEXT_FONT_MAP_GET_CLASS (fontmap);
1177   font = klass->create_font (fontmap, &key);
1178 
1179   if (!font)
1180     return NULL;
1181 
1182   pango_core_text_font_map_add (fontmap, &key, font);
1183 
1184   return font;
1185 }
1186 
1187 static gboolean
find_best_match(PangoCoreTextFamily * font_family,const PangoFontDescription * description,PangoCoreTextFace ** best_face)1188 find_best_match (PangoCoreTextFamily         *font_family,
1189                  const PangoFontDescription  *description,
1190                  PangoCoreTextFace          **best_face)
1191 {
1192   PangoFontDescription *new_desc;
1193   PangoFontDescription *best_description = NULL;
1194   int i;
1195 
1196   *best_face = NULL;
1197 
1198   for (i = 0; i < font_family->n_faces; i++)
1199     {
1200       new_desc = pango_font_face_describe (font_family->faces[i]);
1201       pango_font_description_set_gravity (new_desc, pango_font_description_get_gravity (description));
1202 
1203       if (pango_font_description_better_match (description, best_description,
1204                                                new_desc))
1205 	{
1206 	  pango_font_description_free (best_description);
1207 	  best_description = new_desc;
1208 	  *best_face = (PangoCoreTextFace *)font_family->faces[i];
1209 	}
1210       else
1211 	pango_font_description_free (new_desc);
1212     }
1213 
1214   if (best_description)
1215     {
1216       pango_font_description_free (best_description);
1217       return TRUE;
1218     }
1219   return FALSE;
1220 }
1221 
1222 static gboolean
get_first_font(PangoFontset * fontset G_GNUC_UNUSED,PangoFont * font,gpointer data)1223 get_first_font (PangoFontset *fontset G_GNUC_UNUSED,
1224                 PangoFont    *font,
1225                 gpointer      data)
1226 {
1227   *(PangoFont **)data = font;
1228 
1229   return TRUE;
1230 }
1231 
1232 static guint
pango_core_text_font_map_get_serial(PangoFontMap * fontmap)1233 pango_core_text_font_map_get_serial (PangoFontMap *fontmap)
1234 {
1235   PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap);
1236 
1237   return ctfontmap->serial;
1238 }
1239 
1240 static void
pango_core_text_font_map_changed(PangoFontMap * fontmap)1241 pango_core_text_font_map_changed (PangoFontMap *fontmap)
1242 {
1243   PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap);
1244 
1245   ctfontmap->serial++;
1246   if (ctfontmap->serial == 0)
1247     ctfontmap->serial++;
1248 }
1249 
1250 static PangoFont *
pango_core_text_font_map_load_font(PangoFontMap * fontmap,PangoContext * context,const PangoFontDescription * description)1251 pango_core_text_font_map_load_font (PangoFontMap               *fontmap,
1252                                     PangoContext               *context,
1253                                     const PangoFontDescription *description)
1254 {
1255   PangoLanguage *language;
1256   PangoFontset *fontset;
1257   PangoFont *font = NULL;
1258 
1259   if (context)
1260     language = pango_context_get_language (context);
1261   else
1262     language = NULL;
1263 
1264   fontset = pango_font_map_load_fontset (fontmap, context,
1265                                          description, language);
1266 
1267   if (fontset)
1268     {
1269       pango_fontset_foreach (fontset, get_first_font, &font);
1270 
1271       if (font)
1272         g_object_ref (font);
1273 
1274       g_object_unref (fontset);
1275     }
1276 
1277   return font;
1278 }
1279 
1280 static void
list_families_foreach(gpointer key,gpointer value,gpointer user_data)1281 list_families_foreach (gpointer key,
1282 		       gpointer value,
1283 		       gpointer user_data)
1284 {
1285   GSList **list = user_data;
1286 
1287   *list = g_slist_prepend (*list, value);
1288 }
1289 
1290 static void
pango_core_text_font_map_list_families(PangoFontMap * fontmap,PangoFontFamily *** families,int * n_families)1291 pango_core_text_font_map_list_families (PangoFontMap      *fontmap,
1292                                         PangoFontFamily ***families,
1293                                         int               *n_families)
1294 {
1295   GSList *family_list = NULL;
1296   GSList *tmp_list;
1297   PangoCoreTextFontMap *ctfontmap = (PangoCoreTextFontMap *)fontmap;
1298 
1299   if (!n_families)
1300     return;
1301 
1302   g_hash_table_foreach (ctfontmap->families,
1303                         list_families_foreach, &family_list);
1304 
1305   *n_families = g_slist_length (family_list);
1306 
1307   if (families)
1308     {
1309       int i = 0;
1310 
1311       *families = g_new (PangoFontFamily *, *n_families);
1312 
1313       tmp_list = family_list;
1314       while (tmp_list)
1315 	{
1316 	  (*families)[i] = tmp_list->data;
1317 	  i++;
1318 	  tmp_list = tmp_list->next;
1319 	}
1320     }
1321 
1322   g_slist_free (family_list);
1323 }
1324 
1325 static PangoFontset *
pango_core_text_font_map_load_fontset(PangoFontMap * fontmap,PangoContext * context,const PangoFontDescription * desc,PangoLanguage * language)1326 pango_core_text_font_map_load_fontset (PangoFontMap               *fontmap,
1327                                        PangoContext               *context,
1328                                        const PangoFontDescription *desc,
1329                                        PangoLanguage              *language)
1330 {
1331   PangoCoreTextFontset *fontset;
1332   PangoCoreTextFontsetKey key;
1333   PangoCoreTextFontMap *ctfontmap = PANGO_CORE_TEXT_FONT_MAP (fontmap);
1334   static gboolean warned_full_fallback = FALSE; /* MT-safe */
1335 
1336   pango_core_text_fontset_key_init (&key, ctfontmap,
1337                                     context, desc, language);
1338 
1339   fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key);
1340 
1341   if (G_UNLIKELY (!fontset))
1342     {
1343       gboolean insert_in_hash = TRUE;
1344 
1345       fontset = pango_core_text_fontset_new (&key, desc);
1346 
1347       if (G_UNLIKELY (!fontset))
1348         {
1349           /* If no font(set) could be loaded, we fallback to "Apple Color
1350            * Emoji" for emoji font, fallback to "Sans" for other fonts,
1351            * which should always work on Mac. We try to adhere to the
1352            * requested style at first.
1353            */
1354           PangoFontDescription *tmp_desc;
1355 
1356           /* Cannot use pango_core_text_fontset_key_free() here */
1357           pango_font_description_free (key.desc);
1358 
1359           tmp_desc = pango_font_description_copy_static (desc);
1360           if (strcmp (pango_font_description_get_family (tmp_desc), "emoji") == 0)
1361             pango_font_description_set_family_static (tmp_desc, "Apple Color Emoji");
1362           else
1363             pango_font_description_set_family_static (tmp_desc, "Sans");
1364 
1365           pango_core_text_fontset_key_init (&key, ctfontmap, context, tmp_desc,
1366                                             language);
1367 
1368           fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key);
1369           if (G_LIKELY (fontset))
1370             insert_in_hash = FALSE;
1371           else
1372             fontset = pango_core_text_fontset_new (&key, tmp_desc);
1373 
1374           if (G_UNLIKELY (!fontset))
1375             {
1376               /* We could not load Sans in the requested style; reset
1377                * variant, weight and stretch to sensible defaults (we should
1378                * be able to adhere the PangoStyle with "Sans").
1379                */
1380               pango_font_description_set_variant (tmp_desc, PANGO_VARIANT_NORMAL);
1381               pango_font_description_set_weight (tmp_desc, PANGO_WEIGHT_NORMAL);
1382               pango_font_description_set_stretch (tmp_desc, PANGO_STRETCH_NORMAL);
1383 
1384               if (!warned_full_fallback)
1385                 {
1386                   char *ctmp;
1387 
1388                   warned_full_fallback = TRUE;
1389 
1390                   ctmp = pango_font_description_to_string (desc);
1391                   g_warning ("couldn't load font \"%s\", modified variant/"
1392                              "weight/stretch as fallback, expect ugly output.",
1393                              ctmp);
1394                   g_free (ctmp);
1395                 }
1396 
1397               fontset = g_hash_table_lookup (ctfontmap->fontset_hash, &key);
1398               if (G_LIKELY (fontset))
1399                 insert_in_hash = FALSE;
1400               else
1401                 fontset = pango_core_text_fontset_new (&key, tmp_desc);
1402 
1403               if (G_UNLIKELY (!fontset))
1404                 {
1405                   /* If even that failed, display a sensible error message
1406                    * and bail out, in contrast to failing randomly.
1407                    */
1408                   g_error ("Could not load fallback font, bailing out.");
1409                 }
1410             }
1411 
1412           if (tmp_desc)
1413             pango_font_description_free (tmp_desc);
1414         }
1415 
1416       if (insert_in_hash)
1417         g_hash_table_insert (ctfontmap->fontset_hash,
1418                              pango_core_text_fontset_get_key (fontset),
1419                              fontset);
1420     }
1421 
1422   /* Cannot use pango_core_text_fontset_key_free() here */
1423   pango_font_description_free (key.desc);
1424 
1425   return g_object_ref (fontset);
1426 }
1427 
1428 static void
pango_core_text_font_map_init(PangoCoreTextFontMap * ctfontmap)1429 pango_core_text_font_map_init (PangoCoreTextFontMap *ctfontmap)
1430 {
1431   PangoCoreTextFamily *family;
1432   CTFontCollectionRef collection;
1433   CFArrayRef ctfaces;
1434   CFIndex i, count;
1435 
1436   ctfontmap->serial = 1;
1437   ctfontmap->families = g_hash_table_new_full (g_str_hash, g_str_equal,
1438                                                g_free, g_object_unref);
1439 
1440 
1441   ctfontmap->font_hash = g_hash_table_new_full ((GHashFunc)pango_core_text_font_key_hash,
1442                                                 (GEqualFunc)pango_core_text_font_key_equal,
1443                                                 (GDestroyNotify)pango_core_text_font_key_free,
1444                                                 NULL);
1445 
1446   ctfontmap->fontset_hash = g_hash_table_new_full ((GHashFunc)pango_core_text_fontset_key_hash,
1447                                                    (GEqualFunc)pango_core_text_fontset_key_equal,
1448                                                    NULL,
1449                                                    (GDestroyNotify)g_object_unref);
1450 
1451   collection = CTFontCollectionCreateFromAvailableFonts (0);
1452   ctfaces = CTFontCollectionCreateMatchingFontDescriptors (collection);
1453   count = CFArrayGetCount (ctfaces);
1454 
1455   for (i = 0; i < count; i++)
1456     {
1457       SInt64 font_traits;
1458       char *buffer;
1459       char *family_name;
1460       CFNumberRef number;
1461       CFDictionaryRef dict;
1462       CTFontDescriptorRef desc = CFArrayGetValueAtIndex (ctfaces, i);
1463 
1464       buffer = ct_font_descriptor_get_family_name (desc, TRUE);
1465       if (!buffer)
1466         continue;
1467 
1468       family_name = g_utf8_casefold (buffer, -1);
1469 
1470       family = g_hash_table_lookup (ctfontmap->families, family_name);
1471       if (!family)
1472         {
1473           family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
1474           g_hash_table_insert (ctfontmap->families, g_strdup (family_name),
1475                                family);
1476 
1477           family->family_name = g_strdup (buffer);
1478         }
1479 
1480       g_free (buffer);
1481 
1482       g_free (family_name);
1483 
1484       /* We assume that all faces in the family are monospaced or none. */
1485       dict = CTFontDescriptorCopyAttribute (desc, kCTFontTraitsAttribute);
1486       number = (CFNumberRef)CFDictionaryGetValue (dict,
1487                                                   kCTFontSymbolicTrait);
1488 
1489       if (CFNumberGetValue (number, kCFNumberSInt64Type, &font_traits))
1490         {
1491           if ((font_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait)
1492             family->is_monospace = TRUE;
1493         }
1494 
1495       CFRelease (dict);
1496     }
1497 
1498   /* Insert aliases */
1499   /* Keep in sync with get_real_family() */
1500   gchar* aliases[] = {
1501     "Sans", "Serif", "system-ui", "cursive", "fantasy", "Monospace"
1502   };
1503 
1504   for (int i = 0; i <  G_N_ELEMENTS(aliases); i++)
1505   {
1506     family = g_object_new (PANGO_TYPE_CORE_TEXT_FAMILY, NULL);
1507     family->family_name = g_strdup (aliases[i]);
1508     if (g_ascii_strcasecmp (family->family_name, "Monospace") == 0)
1509       family->is_monospace = TRUE;
1510     g_hash_table_insert (ctfontmap->families,
1511                          g_utf8_casefold (family->family_name, -1), family);
1512   }
1513 }
1514 
1515 static PangoFontFace *
pango_core_text_font_map_get_face(PangoFontMap * fontmap,PangoFont * font)1516 pango_core_text_font_map_get_face (PangoFontMap *fontmap,
1517                                    PangoFont    *font)
1518 {
1519   PangoCoreTextFont *cfont = PANGO_CORE_TEXT_FONT (font);
1520 
1521   return PANGO_FONT_FACE (_pango_core_text_font_get_face (cfont));
1522 }
1523 
1524 static void
pango_core_text_font_map_class_init(PangoCoreTextFontMapClass * class)1525 pango_core_text_font_map_class_init (PangoCoreTextFontMapClass *class)
1526 {
1527   GObjectClass *object_class = G_OBJECT_CLASS (class);
1528   PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (class);
1529 
1530   object_class->finalize = pango_core_text_font_map_finalize;
1531 
1532   fontmap_class->load_font = pango_core_text_font_map_load_font;
1533   fontmap_class->list_families = pango_core_text_font_map_list_families;
1534   fontmap_class->load_fontset = pango_core_text_font_map_load_fontset;
1535   fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_CORE_TEXT;
1536   fontmap_class->get_serial = pango_core_text_font_map_get_serial;
1537   fontmap_class->changed = pango_core_text_font_map_changed;
1538   fontmap_class->get_face = pango_core_text_font_map_get_face;
1539 }
1540 
1541 /*
1542  * PangoCoreTextFontSet
1543  */
1544 
1545 static void              pango_core_text_fontset_finalize     (GObject                   *object);
1546 static void              pango_core_text_fontset_init         (PangoCoreTextFontset      *fontset);
1547 static PangoLanguage *   pango_core_text_fontset_get_language (PangoFontset              *fontset);
1548 static  PangoFont *      pango_core_text_fontset_get_font     (PangoFontset              *fontset,
1549                                                                guint                      wc);
1550 static void              pango_core_text_fontset_foreach      (PangoFontset              *fontset,
1551                                                                PangoFontsetForeachFunc    func,
1552                                                                gpointer                   data);
1553 
1554 struct _PangoCoreTextFontset
1555 {
1556   PangoFontset parent_instance;
1557 
1558   const gchar *orig_family;
1559   PangoFontDescription *orig_description;
1560 
1561   PangoCoreTextFontsetKey *key;
1562   CFArrayRef cascade_list;
1563 
1564   GPtrArray *fonts;
1565   GPtrArray *coverages;
1566   guint real_font_count;
1567 };
1568 
1569 struct _PangoCoreTextFontsetClass
1570 {
1571   PangoFontsetClass parent_instance;
1572 };
1573 
1574 typedef struct _PangoCoreTextFontsetClass PangoCoreTextFontsetClass;
1575 
1576 G_DEFINE_TYPE (PangoCoreTextFontset,
1577                pango_core_text_fontset,
1578                PANGO_TYPE_FONTSET);
1579 
1580 #if !defined(MAC_OS_X_VERSION_10_8) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
1581 /* This symbol does exist in the CoreText library shipped with Snow
1582  * Leopard and Lion, however, it is not found in the public header files.
1583  */
1584 CFArrayRef CTFontCopyDefaultCascadeList (CTFontRef font_ref);
1585 #endif
1586 
1587 static PangoCoreTextFontset *
pango_core_text_fontset_new(PangoCoreTextFontsetKey * key,const PangoFontDescription * description)1588 pango_core_text_fontset_new (PangoCoreTextFontsetKey    *key,
1589                              const PangoFontDescription *description)
1590 {
1591   PangoCoreTextFamily *font_family;
1592   PangoCoreTextFontset *fontset;
1593   PangoCoreTextFont *best_font = NULL;
1594   gchar **family_names;
1595   const gchar *family;
1596   gchar *name;
1597   int i;
1598 
1599   fontset = g_object_new (PANGO_TYPE_CORE_TEXT_FONTSET, NULL);
1600   family = pango_font_description_get_family (description);
1601   family_names = g_strsplit (family ? family : "", ",", -1);
1602 
1603   for (i = 0; family_names[i]; ++i)
1604     {
1605       name = g_utf8_casefold (family_names[i], -1);
1606       font_family = g_hash_table_lookup (key->fontmap->families, name);
1607       g_free (name);
1608 
1609       if (font_family)
1610         {
1611           PangoCoreTextFace *family_face;
1612           PangoCoreTextFont *font;
1613 
1614           /* Force a listing of the available faces */
1615           pango_font_family_list_faces ((PangoFontFamily *)font_family, NULL, NULL);
1616 
1617           if (find_best_match (font_family, description, &family_face))
1618             {
1619               font = pango_core_text_font_map_new_font (key->fontmap,
1620                                                         key,
1621                                                         family_face->ctfontdescriptor,
1622                                                         family_face->synthetic_italic);
1623 
1624               if (font)
1625                 {
1626                   g_ptr_array_add (fontset->fonts, font);
1627                   if (best_font == NULL) best_font = font;
1628                 }
1629             }
1630         }
1631     }
1632 
1633   g_strfreev (family_names);
1634 
1635   if (!best_font)
1636     {
1637       g_object_unref (fontset);
1638       return NULL;
1639     }
1640 
1641   /* Create a font set with best font */
1642   fontset->key = pango_core_text_fontset_key_copy (key);
1643   fontset->orig_description = pango_font_description_copy (description);
1644 
1645   fontset->real_font_count = fontset->fonts->len;
1646 
1647   /* Add the cascade list for this language */
1648 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
1649     {
1650       CFArrayRef language_pref_list = NULL;
1651       CFStringRef languages[1];
1652 
1653       if (key->language)
1654         {
1655           languages[0] = CFStringCreateWithCString (NULL,
1656                                                     pango_language_to_string (key->language),
1657                                                     kCFStringEncodingASCII);
1658           language_pref_list = CFArrayCreate (kCFAllocatorDefault,
1659                                               (const void **) languages,
1660                                               1,
1661                                               &kCFTypeArrayCallBacks);
1662         }
1663 
1664       fontset->cascade_list = CTFontCopyDefaultCascadeListForLanguages (pango_core_text_font_get_ctfont (best_font), language_pref_list);
1665 
1666       if (language_pref_list)
1667         {
1668           CFRelease (languages[0]);
1669           CFRelease (language_pref_list);
1670         }
1671     }
1672 #else
1673   /* There is unfortunately no public API to retrieve the cascade list
1674    * on Mac OS X < 10.8, so we use the following undocumented public function.
1675    */
1676   fontset->cascade_list = CTFontCopyDefaultCascadeList (pango_core_text_font_get_ctfont (best_font));
1677 #endif
1678 
1679   /* length of cascade list + real_font_count for the "real" fonts at the front */
1680   g_ptr_array_set_size (fontset->fonts, CFArrayGetCount (fontset->cascade_list) + fontset->real_font_count);
1681   g_ptr_array_set_size (fontset->coverages, CFArrayGetCount (fontset->cascade_list) + fontset->real_font_count);
1682 
1683   return fontset;
1684 }
1685 
1686 static PangoFont *
pango_core_text_fontset_load_font(PangoCoreTextFontset * ctfontset,CTFontDescriptorRef ctdescriptor)1687 pango_core_text_fontset_load_font (PangoCoreTextFontset *ctfontset,
1688                                    CTFontDescriptorRef   ctdescriptor)
1689 {
1690   PangoCoreTextFontsetKey *key;
1691   PangoCoreTextFont *font;
1692 
1693   key = pango_core_text_fontset_get_key (ctfontset);
1694 
1695   /* For now, we will default the fallbacks to not have synthetic italic,
1696    * in the future this may be improved.
1697    */
1698   font = pango_core_text_font_map_new_font (ctfontset->key->fontmap,
1699                                             ctfontset->key,
1700                                             ctdescriptor,
1701                                             FALSE);
1702 
1703   return PANGO_FONT (font);
1704 }
1705 
1706 static PangoFont *
pango_core_text_fontset_get_font_at(PangoCoreTextFontset * ctfontset,unsigned int i)1707 pango_core_text_fontset_get_font_at (PangoCoreTextFontset *ctfontset,
1708                                      unsigned int          i)
1709 {
1710   /* These fonts are loaded as soon as the fontset is created */
1711   if (i < ctfontset->real_font_count)
1712     return g_ptr_array_index (ctfontset->fonts, i);
1713 
1714   if (i >= ctfontset->fonts->len)
1715     return NULL;
1716 
1717   if (g_ptr_array_index (ctfontset->fonts, i) == NULL)
1718     {
1719       CTFontDescriptorRef ctdescriptor = CFArrayGetValueAtIndex (ctfontset->cascade_list, i - ctfontset->real_font_count);
1720       PangoFont *font = pango_core_text_fontset_load_font (ctfontset, ctdescriptor);
1721       g_ptr_array_index (ctfontset->fonts, i) = font;
1722       g_ptr_array_index (ctfontset->coverages, i) = NULL;
1723     }
1724 
1725   return g_ptr_array_index (ctfontset->fonts, i);
1726 }
1727 
1728 static void
pango_core_text_fontset_class_init(PangoCoreTextFontsetClass * klass)1729 pango_core_text_fontset_class_init (PangoCoreTextFontsetClass *klass)
1730 {
1731   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1732   PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (klass);
1733 
1734   object_class->finalize = pango_core_text_fontset_finalize;
1735 
1736   fontset_class->get_font = pango_core_text_fontset_get_font;
1737   fontset_class->get_language = pango_core_text_fontset_get_language;
1738   fontset_class->foreach = pango_core_text_fontset_foreach;
1739 }
1740 
1741 static void
pango_core_text_fontset_init(PangoCoreTextFontset * ctfontset)1742 pango_core_text_fontset_init (PangoCoreTextFontset *ctfontset)
1743 {
1744   ctfontset->key = NULL;
1745   ctfontset->cascade_list = NULL;
1746   ctfontset->fonts = g_ptr_array_new ();
1747   ctfontset->coverages = g_ptr_array_new ();
1748   ctfontset->real_font_count = 0;
1749 }
1750 
1751 static void
pango_core_text_fontset_finalize(GObject * object)1752 pango_core_text_fontset_finalize (GObject *object)
1753 {
1754   PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (object);
1755   unsigned int i;
1756 
1757   for (i = 0; i < ctfontset->fonts->len; i++)
1758     {
1759       PangoFont *font = g_ptr_array_index (ctfontset->fonts, i);
1760       if (font)
1761         g_object_unref (font);
1762     }
1763   g_ptr_array_free (ctfontset->fonts, TRUE);
1764 
1765   for (i = 0; i < ctfontset->coverages->len; i++)
1766     {
1767       PangoCoverage *coverage = g_ptr_array_index (ctfontset->coverages, i);
1768       if (coverage)
1769         pango_coverage_unref (coverage);
1770     }
1771   g_ptr_array_free (ctfontset->coverages, TRUE);
1772 
1773   if (ctfontset->cascade_list)
1774     CFRelease (ctfontset->cascade_list);
1775 
1776   pango_font_description_free (ctfontset->orig_description);
1777 
1778   if (ctfontset->key)
1779     pango_core_text_fontset_key_free (ctfontset->key);
1780 
1781   G_OBJECT_CLASS (pango_core_text_fontset_parent_class)->finalize (object);
1782 }
1783 
1784 static PangoCoreTextFontsetKey *
pango_core_text_fontset_get_key(PangoCoreTextFontset * fontset)1785 pango_core_text_fontset_get_key (PangoCoreTextFontset *fontset)
1786 {
1787   return fontset->key;
1788 }
1789 
1790 static PangoLanguage *
pango_core_text_fontset_get_language(PangoFontset * fontset)1791 pango_core_text_fontset_get_language (PangoFontset *fontset)
1792 {
1793   PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (fontset);
1794 
1795   return pango_core_text_fontset_key_get_language (pango_core_text_fontset_get_key (ctfontset));
1796 }
1797 
1798 static PangoFont *
pango_core_text_fontset_get_font(PangoFontset * fontset,guint wc)1799 pango_core_text_fontset_get_font (PangoFontset *fontset,
1800                                   guint         wc)
1801 {
1802   PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (fontset);
1803   PangoCoverageLevel best_level = PANGO_COVERAGE_NONE;
1804   PangoCoverageLevel level;
1805   PangoFont *font;
1806   PangoCoverage *coverage;
1807   int result = -1;
1808   unsigned int i;
1809 
1810   for (i = 0; i < ctfontset->fonts->len; i++)
1811     {
1812       PangoFont *font = pango_core_text_fontset_get_font_at (ctfontset, i);
1813       if (!font)
1814         continue;
1815 
1816       coverage = g_ptr_array_index (ctfontset->coverages, i);
1817 
1818       if (coverage == NULL)
1819         {
1820           font = g_ptr_array_index (ctfontset->fonts, i);
1821 
1822           coverage = pango_font_get_coverage (font, ctfontset->key->language);
1823           g_ptr_array_index (ctfontset->coverages, i) = coverage;
1824         }
1825 
1826       level = pango_coverage_get (coverage, wc);
1827 
1828       if (result == -1 || level > best_level)
1829         {
1830           result = i;
1831           best_level = level;
1832           if (level == PANGO_COVERAGE_EXACT)
1833             break;
1834         }
1835     }
1836 
1837   if (G_UNLIKELY (result == -1))
1838     return NULL;
1839 
1840   font = g_ptr_array_index (ctfontset->fonts, result);
1841   return g_object_ref (font);
1842 }
1843 
1844 static void
pango_core_text_fontset_foreach(PangoFontset * fontset,PangoFontsetForeachFunc func,gpointer data)1845 pango_core_text_fontset_foreach (PangoFontset *fontset,
1846                                  PangoFontsetForeachFunc func,
1847                                  gpointer data)
1848 {
1849   PangoCoreTextFontset *ctfontset = PANGO_CORE_TEXT_FONTSET (fontset);
1850   unsigned int i;
1851 
1852   for (i = 0; i < ctfontset->fonts->len; i++)
1853     {
1854       PangoFont *font = pango_core_text_fontset_get_font_at (ctfontset, i);
1855       if (!font)
1856         continue;
1857 
1858       if ((* func) (fontset, font, data))
1859         return;
1860     }
1861 }
1862 
1863 PangoCoreTextFace *
pango_core_text_font_map_find_face(PangoCoreTextFontMap * map,const PangoCoreTextFontKey * key)1864 pango_core_text_font_map_find_face (PangoCoreTextFontMap       *map,
1865                                     const PangoCoreTextFontKey *key)
1866 {
1867   CTFontDescriptorRef desc;
1868   gboolean synthetic_italic;
1869   char *family;
1870   char *family_name;
1871   char *style_name;
1872   PangoWeight weight;
1873   CTFontSymbolicTraits traits;
1874   PangoCoreTextFamily *font_family;
1875   PangoCoreTextFace *result = NULL;
1876 
1877   desc = pango_core_text_font_key_get_ctfontdescriptor (key);
1878   synthetic_italic = pango_core_text_font_key_get_synthetic_italic (key);
1879 
1880   family_name = ct_font_descriptor_get_family_name (desc, FALSE);
1881   style_name = ct_font_descriptor_get_style_name (desc);
1882   weight = ct_font_descriptor_get_weight (desc);
1883   traits = ct_font_descriptor_get_traits (desc);
1884 
1885   family = g_utf8_casefold (family_name, -1);
1886 
1887   font_family = g_hash_table_lookup (map->families, family);
1888 
1889   if (font_family)
1890     {
1891       pango_font_family_list_faces ((PangoFontFamily *)font_family, NULL, NULL);
1892 
1893       for (int i = 0; i < font_family->n_faces; i++)
1894         {
1895           PangoCoreTextFace *face = (PangoCoreTextFace *)font_family->faces[i];
1896 
1897           if (face->weight == weight &&
1898               face->traits == traits &&
1899               face->synthetic_italic == synthetic_italic &&
1900               strcmp (face->style_name, style_name) == 0)
1901             {
1902               result = face;
1903               break;
1904             }
1905         }
1906     }
1907 
1908   g_free (family);
1909   g_free (family_name);
1910   g_free (style_name);
1911 
1912   return result;
1913 }
1914