1 /* Pango
2  * pango-fontmap.c: Font handling
3  *
4  * Copyright (C) 2000 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 
24 #include <gio/gio.h>
25 
26 #include "pango-fontmap-private.h"
27 #include "pango-fontset-private.h"
28 #include "pango-impl-utils.h"
29 #include <stdlib.h>
30 
31 static PangoFontset *pango_font_map_real_load_fontset (PangoFontMap               *fontmap,
32                                                        PangoContext               *context,
33                                                        const PangoFontDescription *desc,
34                                                        PangoLanguage              *language);
35 
36 
37 static PangoFontFamily *pango_font_map_real_get_family (PangoFontMap *fontmap,
38                                                         const char   *name);
39 
40 static void pango_font_map_real_changed (PangoFontMap *fontmap);
41 
42 static void pango_font_map_list_model_init (GListModelInterface *iface);
43 
44 typedef struct {
45   guint n_families;
46 } PangoFontMapPrivate;
47 
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(PangoFontMap,pango_font_map,G_TYPE_OBJECT,G_ADD_PRIVATE (PangoFontMap)G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,pango_font_map_list_model_init))48 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoFontMap, pango_font_map, G_TYPE_OBJECT,
49                                   G_ADD_PRIVATE (PangoFontMap)
50                                   G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pango_font_map_list_model_init))
51 
52 static void
53 pango_font_map_class_init (PangoFontMapClass *class)
54 {
55   class->load_fontset = pango_font_map_real_load_fontset;
56   class->get_family = pango_font_map_real_get_family;
57   class->changed = pango_font_map_real_changed;
58 }
59 
60 static void
pango_font_map_init(PangoFontMap * fontmap G_GNUC_UNUSED)61 pango_font_map_init (PangoFontMap *fontmap G_GNUC_UNUSED)
62 {
63 }
64 
65 /**
66  * pango_font_map_create_context:
67  * @fontmap: a `PangoFontMap`
68  *
69  * Creates a `PangoContext` connected to @fontmap.
70  *
71  * This is equivalent to [ctor@Pango.Context.new] followed by
72  * [method@Pango.Context.set_font_map].
73  *
74  * If you are using Pango as part of a higher-level system,
75  * that system may have it's own way of create a `PangoContext`.
76  * For instance, the GTK toolkit has, among others,
77  * gtk_widget_get_pango_context(). Use those instead.
78  *
79  * Return value: (transfer full): the newly allocated `PangoContext`,
80  *   which should be freed with g_object_unref().
81  *
82  * Since: 1.22
83  */
84 PangoContext *
pango_font_map_create_context(PangoFontMap * fontmap)85 pango_font_map_create_context (PangoFontMap *fontmap)
86 {
87   PangoContext *context;
88 
89   g_return_val_if_fail (fontmap != NULL, NULL);
90 
91   context = pango_context_new ();
92   pango_context_set_font_map (context, fontmap);
93 
94   return context;
95 }
96 
97 /**
98  * pango_font_map_load_font:
99  * @fontmap: a `PangoFontMap`
100  * @context: the `PangoContext` the font will be used with
101  * @desc: a `PangoFontDescription` describing the font to load
102  *
103  * Load the font in the fontmap that is the closest match for @desc.
104  *
105  * Returns: (transfer full) (nullable): the newly allocated `PangoFont`
106  *   loaded, or %NULL if no font matched.
107  */
108 PangoFont *
pango_font_map_load_font(PangoFontMap * fontmap,PangoContext * context,const PangoFontDescription * desc)109 pango_font_map_load_font  (PangoFontMap               *fontmap,
110                            PangoContext               *context,
111                            const PangoFontDescription *desc)
112 {
113   g_return_val_if_fail (fontmap != NULL, NULL);
114 
115   return PANGO_FONT_MAP_GET_CLASS (fontmap)->load_font (fontmap, context, desc);
116 }
117 
118 /**
119  * pango_font_map_list_families:
120  * @fontmap: a `PangoFontMap`
121  * @families: (out) (array length=n_families) (transfer container): location to
122  *   store a pointer to an array of `PangoFontFamily` *.
123  *   This array should be freed with g_free().
124  * @n_families: (out): location to store the number of elements in @families
125  *
126  * List all families for a fontmap.
127  */
128 void
pango_font_map_list_families(PangoFontMap * fontmap,PangoFontFamily *** families,int * n_families)129 pango_font_map_list_families (PangoFontMap      *fontmap,
130                               PangoFontFamily ***families,
131                               int               *n_families)
132 {
133   PangoFontMapPrivate *priv = pango_font_map_get_instance_private (fontmap);
134   g_return_if_fail (fontmap != NULL);
135 
136   PANGO_FONT_MAP_GET_CLASS (fontmap)->list_families (fontmap, families, n_families);
137 
138   /* keep this value for GListModel::changed */
139   priv->n_families = *n_families;
140 }
141 
142 /**
143  * pango_font_map_load_fontset:
144  * @fontmap: a `PangoFontMap`
145  * @context: the `PangoContext` the font will be used with
146  * @desc: a `PangoFontDescription` describing the font to load
147  * @language: a `PangoLanguage` the fonts will be used for
148  *
149  * Load a set of fonts in the fontmap that can be used to render
150  * a font matching @desc.
151  *
152  * Returns: (transfer full) (nullable): the newly allocated
153  *   `PangoFontset` loaded, or %NULL if no font matched.
154  */
155 PangoFontset *
pango_font_map_load_fontset(PangoFontMap * fontmap,PangoContext * context,const PangoFontDescription * desc,PangoLanguage * language)156 pango_font_map_load_fontset (PangoFontMap               *fontmap,
157                              PangoContext               *context,
158                              const PangoFontDescription *desc,
159                              PangoLanguage              *language)
160 {
161   g_return_val_if_fail (fontmap != NULL, NULL);
162 
163   return PANGO_FONT_MAP_GET_CLASS (fontmap)->load_fontset (fontmap, context, desc, language);
164 }
165 
166 static void
pango_font_map_fontset_add_fonts(PangoFontMap * fontmap,PangoContext * context,PangoFontsetSimple * fonts,PangoFontDescription * desc,const char * family)167 pango_font_map_fontset_add_fonts (PangoFontMap          *fontmap,
168                                   PangoContext          *context,
169                                   PangoFontsetSimple    *fonts,
170                                   PangoFontDescription  *desc,
171                                   const char            *family)
172 {
173   PangoFont *font;
174 
175   pango_font_description_set_family_static (desc, family);
176   font = pango_font_map_load_font (fontmap, context, desc);
177   if (font)
178     pango_fontset_simple_append (fonts, font);
179 }
180 
181 static PangoFontset *
pango_font_map_real_load_fontset(PangoFontMap * fontmap,PangoContext * context,const PangoFontDescription * desc,PangoLanguage * language)182 pango_font_map_real_load_fontset (PangoFontMap               *fontmap,
183                                   PangoContext               *context,
184                                   const PangoFontDescription *desc,
185                                   PangoLanguage              *language)
186 {
187   PangoFontDescription *tmp_desc = pango_font_description_copy_static (desc);
188   const char *family;
189   char **families;
190   int i;
191   PangoFontsetSimple *fonts;
192   static GHashTable *warned_fonts = NULL; /* MT-safe */
193   G_LOCK_DEFINE_STATIC (warned_fonts);
194 
195   family = pango_font_description_get_family (desc);
196   families = g_strsplit (family ? family : "", ",", -1);
197 
198   fonts = pango_fontset_simple_new (language);
199 
200   for (i = 0; families[i]; i++)
201     pango_font_map_fontset_add_fonts (fontmap,
202                                       context,
203                                       fonts,
204                                       tmp_desc,
205                                       families[i]);
206 
207   g_strfreev (families);
208 
209   /* The font description was completely unloadable, try with
210    * family == "Sans"
211    */
212   if (pango_fontset_simple_size (fonts) == 0)
213     {
214       char *ctmp1, *ctmp2;
215 
216       pango_font_description_set_family_static (tmp_desc,
217                                                 pango_font_description_get_family (desc));
218 
219       ctmp1 = pango_font_description_to_string (desc);
220       pango_font_description_set_family_static (tmp_desc, "Sans");
221 
222       G_LOCK (warned_fonts);
223       if (!warned_fonts || !g_hash_table_lookup (warned_fonts, ctmp1))
224         {
225           if (!warned_fonts)
226             warned_fonts = g_hash_table_new (g_str_hash, g_str_equal);
227 
228           g_hash_table_insert (warned_fonts, g_strdup (ctmp1), GINT_TO_POINTER (1));
229 
230           ctmp2 = pango_font_description_to_string (tmp_desc);
231           g_warning ("couldn't load font \"%s\", falling back to \"%s\", "
232                      "expect ugly output.", ctmp1, ctmp2);
233           g_free (ctmp2);
234         }
235       G_UNLOCK (warned_fonts);
236       g_free (ctmp1);
237 
238       pango_font_map_fontset_add_fonts (fontmap,
239                                         context,
240                                         fonts,
241                                         tmp_desc,
242                                         "Sans");
243     }
244 
245   /* We couldn't try with Sans and the specified style. Try Sans Normal
246    */
247   if (pango_fontset_simple_size (fonts) == 0)
248     {
249       char *ctmp1, *ctmp2;
250 
251       pango_font_description_set_family_static (tmp_desc, "Sans");
252       ctmp1 = pango_font_description_to_string (tmp_desc);
253       pango_font_description_set_style (tmp_desc, PANGO_STYLE_NORMAL);
254       pango_font_description_set_weight (tmp_desc, PANGO_WEIGHT_NORMAL);
255       pango_font_description_set_variant (tmp_desc, PANGO_VARIANT_NORMAL);
256       pango_font_description_set_stretch (tmp_desc, PANGO_STRETCH_NORMAL);
257 
258       G_LOCK (warned_fonts);
259       if (!warned_fonts || !g_hash_table_lookup (warned_fonts, ctmp1))
260         {
261           g_hash_table_insert (warned_fonts, g_strdup (ctmp1), GINT_TO_POINTER (1));
262 
263           ctmp2 = pango_font_description_to_string (tmp_desc);
264 
265           g_warning ("couldn't load font \"%s\", falling back to \"%s\", "
266                      "expect ugly output.", ctmp1, ctmp2);
267           g_free (ctmp2);
268         }
269       G_UNLOCK (warned_fonts);
270       g_free (ctmp1);
271 
272       pango_font_map_fontset_add_fonts (fontmap,
273                                         context,
274                                         fonts,
275                                         tmp_desc,
276                                         "Sans");
277     }
278 
279   pango_font_description_free (tmp_desc);
280 
281   /* Everything failed, we are screwed, there is no way to continue,
282    * but lets just not crash here.
283    */
284   if (pango_fontset_simple_size (fonts) == 0)
285       g_warning ("All font fallbacks failed!!!!");
286 
287   return PANGO_FONTSET (fonts);
288 }
289 
290 /**
291  * pango_font_map_get_shape_engine_type:
292  * @fontmap: a `PangoFontMap`
293  *
294  * Returns the render ID for shape engines for this fontmap.
295  * See the `render_type` field of `PangoEngineInfo`.
296   *
297  * Return value (transfer none): the ID string for shape engines
298  *   for this fontmap
299  *
300  * Since: 1.4
301  * Deprecated: 1.38
302  */
303 const char *
pango_font_map_get_shape_engine_type(PangoFontMap * fontmap)304 pango_font_map_get_shape_engine_type (PangoFontMap *fontmap)
305 {
306   g_return_val_if_fail (PANGO_IS_FONT_MAP (fontmap), NULL);
307 
308   return PANGO_FONT_MAP_GET_CLASS (fontmap)->shape_engine_type;
309 }
310 
311 /**
312  * pango_font_map_get_serial:
313  * @fontmap: a `PangoFontMap`
314  *
315  * Returns the current serial number of @fontmap.
316  *
317  * The serial number is initialized to an small number larger than zero
318  * when a new fontmap is created and is increased whenever the fontmap
319  * is changed. It may wrap, but will never have the value 0. Since it can
320  * wrap, never compare it with "less than", always use "not equals".
321  *
322  * The fontmap can only be changed using backend-specific API, like changing
323  * fontmap resolution.
324  *
325  * This can be used to automatically detect changes to a `PangoFontMap`,
326  * like in `PangoContext`.
327  *
328  * Return value: The current serial number of @fontmap.
329  *
330  * Since: 1.32.4
331  */
332 guint
pango_font_map_get_serial(PangoFontMap * fontmap)333 pango_font_map_get_serial (PangoFontMap *fontmap)
334 {
335   g_return_val_if_fail (PANGO_IS_FONT_MAP (fontmap), 0);
336 
337   if (PANGO_FONT_MAP_GET_CLASS (fontmap)->get_serial)
338     return PANGO_FONT_MAP_GET_CLASS (fontmap)->get_serial (fontmap);
339   else
340     return 1;
341 }
342 
343 static void
pango_font_map_real_changed(PangoFontMap * fontmap)344 pango_font_map_real_changed (PangoFontMap *fontmap)
345 {
346   PangoFontMapPrivate *priv = pango_font_map_get_instance_private (fontmap);
347   guint removed, added;
348 
349   removed = priv->n_families;
350   added = g_list_model_get_n_items (G_LIST_MODEL (fontmap));
351 
352   g_list_model_items_changed (G_LIST_MODEL (fontmap), 0, removed, added);
353 }
354 
355 /**
356  * pango_font_map_changed:
357  * @fontmap: a `PangoFontMap`
358  *
359  * Forces a change in the context, which will cause any `PangoContext`
360  * using this fontmap to change.
361  *
362  * This function is only useful when implementing a new backend
363  * for Pango, something applications won't do. Backends should
364  * call this function if they have attached extra data to the
365  * context and such data is changed.
366  *
367  * Since: 1.34
368  */
369 void
pango_font_map_changed(PangoFontMap * fontmap)370 pango_font_map_changed (PangoFontMap *fontmap)
371 {
372   g_return_if_fail (PANGO_IS_FONT_MAP (fontmap));
373 
374   if (PANGO_FONT_MAP_GET_CLASS (fontmap)->changed)
375     PANGO_FONT_MAP_GET_CLASS (fontmap)->changed (fontmap);
376 }
377 
378 static PangoFontFamily *
pango_font_map_real_get_family(PangoFontMap * fontmap,const char * name)379 pango_font_map_real_get_family (PangoFontMap *fontmap,
380                                 const char   *name)
381 {
382   PangoFontFamily **families;
383   int n_families;
384   PangoFontFamily *family;
385   int i;
386 
387   pango_font_map_list_families (fontmap, &families, &n_families);
388 
389   family = NULL;
390 
391   for (i = 0; i < n_families; i++)
392     {
393       if (strcmp (name, pango_font_family_get_name (families[i])) == 0)
394         {
395           family = families[i];
396           break;
397         }
398     }
399 
400   g_free (families);
401 
402   return family;
403 }
404 
405 /**
406  * pango_font_map_get_family:
407  * @fontmap: a `PangoFontMap`
408  * @name: a family name
409  *
410  * Gets a font family by name.
411  *
412  * Returns: (transfer none): the `PangoFontFamily`
413  *
414  * Since: 1.46
415  */
416 PangoFontFamily *
pango_font_map_get_family(PangoFontMap * fontmap,const char * name)417 pango_font_map_get_family (PangoFontMap *fontmap,
418                            const char   *name)
419 {
420   g_return_val_if_fail (PANGO_IS_FONT_MAP (fontmap), NULL);
421 
422   return PANGO_FONT_MAP_GET_CLASS (fontmap)->get_family (fontmap, name);
423 }
424 
425 static GType
pango_font_map_get_item_type(GListModel * list)426 pango_font_map_get_item_type (GListModel *list)
427 {
428   return PANGO_TYPE_FONT_FAMILY;
429 }
430 
431 static guint
pango_font_map_get_n_items(GListModel * list)432 pango_font_map_get_n_items (GListModel *list)
433 {
434   PangoFontMap *fontmap = PANGO_FONT_MAP (list);
435   int n_families;
436 
437   pango_font_map_list_families (fontmap, NULL, &n_families);
438 
439   return (guint)n_families;
440 }
441 
442 static gpointer
pango_font_map_get_item(GListModel * list,guint position)443 pango_font_map_get_item (GListModel *list,
444                          guint       position)
445 {
446   PangoFontMap *fontmap = PANGO_FONT_MAP (list);
447   PangoFontFamily **families;
448   int n_families;
449   PangoFontFamily *family;
450 
451   pango_font_map_list_families (fontmap, &families, &n_families);
452 
453   if (position < n_families)
454     family = g_object_ref (families[position]);
455   else
456     family = NULL;
457 
458   g_free (families);
459 
460   return family;
461 }
462 
463 static void
pango_font_map_list_model_init(GListModelInterface * iface)464 pango_font_map_list_model_init (GListModelInterface *iface)
465 {
466   iface->get_item_type = pango_font_map_get_item_type;
467   iface->get_n_items = pango_font_map_get_n_items;
468   iface->get_item = pango_font_map_get_item;
469 }
470