1/* Pango
2 * pangofc-fontmap.c: Base fontmap type for fontconfig-based backends
3 *
4 * Copyright (C) 2000-2003 Red Hat, Inc.
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/**
23 * PangoFcFontMap:
24 *
25 * `PangoFcFontMap` is a base class for font map implementations using the
26 * Fontconfig and FreeType libraries.
27 *
28 * It is used in the Xft and FreeType backends shipped with Pango,
29 * but can also be used when creating new backends. Any backend
30 * deriving from this base class will take advantage of the wide
31 * range of shapers implemented using FreeType that come with Pango.
32 */
33#define FONTSET_CACHE_SIZE 256
34
35#include "config.h"
36#include <math.h>
37
38#include <gio/gio.h>
39
40#include "pango-context.h"
41#include "pango-font-private.h"
42#include "pangofc-fontmap-private.h"
43#include "pangofc-private.h"
44#include "pango-impl-utils.h"
45#include "pango-enum-types.h"
46#include "pango-coverage-private.h"
47#include "pango-trace-private.h"
48#include <hb-ft.h>
49
50
51/* Overview:
52 *
53 * All programming is a practice in caching data. PangoFcFontMap is the
54 * major caching container of a Pango system on a Linux desktop. Here is
55 * a short overview of how it all works.
56 *
57 * In short, Fontconfig search patterns are constructed and a fontset loaded
58 * using them. Here is how we achieve that:
59 *
60 * - All FcPattern's referenced by any object in the fontmap are uniquified
61 * and cached in the fontmap. This both speeds lookups based on patterns
62 * faster, and saves memory. This is handled by fontmap->priv->pattern_hash.
63 * The patterns are cached indefinitely.
64 *
65 * - The results of a FcFontSort() are used to populate fontsets. However,
66 * FcFontSort() relies on the search pattern only, which includes the font
67 * size but not the full font matrix. The fontset however depends on the
68 * matrix. As a result, multiple fontsets may need results of the
69 * FcFontSort() on the same input pattern (think rotating text). As such,
70 * we cache FcFontSort() results in fontmap->priv->patterns_hash which
71 * is a refcounted structure. This level of abstraction also allows for
72 * optimizations like calling FcFontMatch() instead of FcFontSort(), and
73 * only calling FcFontSort() if any patterns other than the first match
74 * are needed. Another possible optimization would be to call FcFontSort()
75 * without trimming, and do the trimming lazily as we go. Only pattern sets
76 * already referenced by a fontset are cached.
77 *
78 * - A number of most-recently-used fontsets are cached and reused when
79 * needed. This is achieved using fontmap->priv->fontset_hash and
80 * fontmap->priv->fontset_cache.
81 *
82 * - All fonts created by any of our fontsets are also cached and reused.
83 * This is what fontmap->priv->font_hash does.
84 *
85 * - Data that only depends on the font file and face index is cached and
86 * reused by multiple fonts. This includes coverage and cmap cache info.
87 * This is done using fontmap->priv->font_face_data_hash.
88 *
89 * Upon a cache_clear() request, all caches are emptied. All objects (fonts,
90 * fontsets, faces, families) having a reference from outside will still live
91 * and may reference the fontmap still, but will not be reused by the fontmap.
92 *
93 *
94 * Todo:
95 *
96 * - Make PangoCoverage a GObject and subclass it as PangoFcCoverage which
97 * will directly use FcCharset. (#569622)
98 *
99 * - Lazy trimming of FcFontSort() results. Requires fontconfig with
100 * FcCharSetMerge().
101 */
102
103typedef enum {
104 /* Initial state; Fontconfig is not initialized yet */
105 DEFAULT_CONFIG_NOT_INITIALIZED,
106
107 /* We have a thread doing Fontconfig initialization in the background */
108 DEFAULT_CONFIG_INITIALIZING,
109
110 /* FcInit() finished and its default configuration is loaded */
111 DEFAULT_CONFIG_INITIALIZED
112} DefaultConfig;
113
114/* We call FcInit in a thread and set fc_initialized
115 * when done, and are protected by a mutex. The thread
116 * signals the cond when FcInit is done.
117 */
118static GMutex fc_init_mutex;
119static GCond fc_init_cond;
120static DefaultConfig fc_initialized = DEFAULT_CONFIG_NOT_INITIALIZED;
121
122
123typedef struct _PangoFcFontFaceData PangoFcFontFaceData;
124typedef struct _PangoFcFace PangoFcFace;
125typedef struct _PangoFcFamily PangoFcFamily;
126typedef struct _PangoFcFindFuncInfo PangoFcFindFuncInfo;
127typedef struct _PangoFcPatterns PangoFcPatterns;
128typedef struct _PangoFcFontset PangoFcFontset;
129
130#define PANGO_FC_TYPE_FAMILY (pango_fc_family_get_type ())
131#define PANGO_FC_FAMILY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_FC_TYPE_FAMILY, PangoFcFamily))
132#define PANGO_FC_IS_FAMILY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_FC_TYPE_FAMILY))
133
134#define PANGO_FC_TYPE_FACE (pango_fc_face_get_type ())
135#define PANGO_FC_FACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_FC_TYPE_FACE, PangoFcFace))
136#define PANGO_FC_IS_FACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_FC_TYPE_FACE))
137
138#define PANGO_FC_TYPE_FONTSET (pango_fc_fontset_get_type ())
139#define PANGO_FC_FONTSET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_FC_TYPE_FONTSET, PangoFcFontset))
140#define PANGO_FC_IS_FONTSET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_FC_TYPE_FONTSET))
141
142struct _PangoFcFontMapPrivate
143{
144 GHashTable *fontset_hash; /* Maps PangoFcFontsetKey -> PangoFcFontset */
145 GQueue *fontset_cache; /* Recently used fontsets */
146
147 GHashTable *font_hash; /* Maps PangoFcFontKey -> PangoFcFont */
148
149 GHashTable *patterns_hash; /* Maps FcPattern -> PangoFcPatterns */
150
151 /* pattern_hash is used to make sure we only store one copy of
152 * each identical pattern. (Speeds up lookup).
153 */
154 GHashTable *pattern_hash;
155
156 GHashTable *font_face_data_hash; /* Maps font file name/id -> data */
157
158 /* List of all families available */
159 PangoFcFamily **families;
160 int n_families; /* -1 == uninitialized */
161
162 double dpi;
163
164 /* Decoders */
165 GSList *findfuncs;
166
167 guint closed : 1;
168
169 FcConfig *config;
170 FcFontSet *fonts;
171};
172
173struct _PangoFcFontFaceData
174{
175 /* Key */
176 char *filename;
177 int id; /* needed to handle TTC files with multiple faces */
178
179 /* Data */
180 FcPattern *pattern; /* Referenced pattern that owns filename */
181 PangoCoverage *coverage;
182 PangoLanguage **languages;
183
184 hb_face_t *hb_face;
185};
186
187struct _PangoFcFace
188{
189 PangoFontFace parent_instance;
190
191 PangoFcFamily *family;
192 char *style;
193 FcPattern *pattern;
194
195 guint fake : 1;
196 guint regular : 1;
197};
198
199struct _PangoFcFamily
200{
201 PangoFontFamily parent_instance;
202
203 PangoFcFontMap *fontmap;
204 char *family_name;
205
206 FcFontSet *patterns;
207 PangoFcFace **faces;
208 int n_faces; /* -1 == uninitialized */
209
210 int spacing; /* FC_SPACING */
211 gboolean variable;
212};
213
214struct _PangoFcFindFuncInfo
215{
216 PangoFcDecoderFindFunc findfunc;
217 gpointer user_data;
218 GDestroyNotify dnotify;
219 gpointer ddata;
220};
221
222static GType pango_fc_family_get_type (void);
223static GType pango_fc_face_get_type (void);
224static GType pango_fc_fontset_get_type (void);
225
226static void pango_fc_font_map_finalize (GObject *object);
227static PangoFont * pango_fc_font_map_load_font (PangoFontMap *fontmap,
228 PangoContext *context,
229 const PangoFontDescription *description);
230static PangoFontset *pango_fc_font_map_load_fontset (PangoFontMap *fontmap,
231 PangoContext *context,
232 const PangoFontDescription *desc,
233 PangoLanguage *language);
234static void pango_fc_font_map_list_families (PangoFontMap *fontmap,
235 PangoFontFamily ***families,
236 int *n_families);
237static PangoFontFamily *pango_fc_font_map_get_family (PangoFontMap *fontmap,
238 const char *name);
239
240static double pango_fc_font_map_get_resolution (PangoFcFontMap *fcfontmap,
241 PangoContext *context);
242static PangoFont *pango_fc_font_map_new_font (PangoFcFontMap *fontmap,
243 PangoFcFontsetKey *fontset_key,
244 FcPattern *match);
245
246static PangoFontFace *pango_fc_font_map_get_face (PangoFontMap *fontmap,
247 PangoFont *font);
248
249static void pango_fc_font_map_changed (PangoFontMap *fontmap);
250
251static guint pango_fc_font_face_data_hash (PangoFcFontFaceData *key);
252static gboolean pango_fc_font_face_data_equal (PangoFcFontFaceData *key1,
253 PangoFcFontFaceData *key2);
254
255static void pango_fc_fontset_key_init (PangoFcFontsetKey *key,
256 PangoFcFontMap *fcfontmap,
257 PangoContext *context,
258 const PangoFontDescription *desc,
259 PangoLanguage *language);
260static PangoFcFontsetKey *pango_fc_fontset_key_copy (const PangoFcFontsetKey *key);
261static void pango_fc_fontset_key_free (PangoFcFontsetKey *key);
262static guint pango_fc_fontset_key_hash (const PangoFcFontsetKey *key);
263static gboolean pango_fc_fontset_key_equal (const PangoFcFontsetKey *key_a,
264 const PangoFcFontsetKey *key_b);
265
266static void pango_fc_font_key_init (PangoFcFontKey *key,
267 PangoFcFontMap *fcfontmap,
268 PangoFcFontsetKey *fontset_key,
269 FcPattern *pattern);
270static PangoFcFontKey *pango_fc_font_key_copy (const PangoFcFontKey *key);
271static void pango_fc_font_key_free (PangoFcFontKey *key);
272static guint pango_fc_font_key_hash (const PangoFcFontKey *key);
273static gboolean pango_fc_font_key_equal (const PangoFcFontKey *key_a,
274 const PangoFcFontKey *key_b);
275
276static PangoFcPatterns *pango_fc_patterns_new (FcPattern *pat,
277 PangoFcFontMap *fontmap);
278static PangoFcPatterns *pango_fc_patterns_ref (PangoFcPatterns *pats);
279static void pango_fc_patterns_unref (PangoFcPatterns *pats);
280static FcPattern *pango_fc_patterns_get_pattern (PangoFcPatterns *pats);
281static FcPattern *pango_fc_patterns_get_font_pattern (PangoFcPatterns *pats,
282 int i,
283 gboolean *prepare);
284
285static FcPattern *uniquify_pattern (PangoFcFontMap *fcfontmap,
286 FcPattern *pattern);
287
288gpointer get_gravity_class (void);
289
290gpointer
291get_gravity_class (void)
292{
293 static GEnumClass *class = NULL; /* MT-safe */
294
295 if (g_once_init_enter (&class))
296 g_once_init_leave (&class, (gpointer)g_type_class_ref (PANGO_TYPE_GRAVITY));
297
298 return class;
299}
300
301static guint
302pango_fc_font_face_data_hash (PangoFcFontFaceData *key)
303{
304 return g_str_hash (v: key->filename) ^ key->id;
305}
306
307static gboolean
308pango_fc_font_face_data_equal (PangoFcFontFaceData *key1,
309 PangoFcFontFaceData *key2)
310{
311 return key1->id == key2->id &&
312 (key1 == key2 || 0 == strcmp (s1: key1->filename, s2: key2->filename));
313}
314
315static void
316pango_fc_font_face_data_free (PangoFcFontFaceData *data)
317{
318 FcPatternDestroy (p: data->pattern);
319
320 if (data->coverage)
321 g_object_unref (object: data->coverage);
322
323 g_free (mem: data->languages);
324
325 hb_face_destroy (face: data->hb_face);
326
327 g_slice_free (PangoFcFontFaceData, data);
328}
329
330/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
331 *
332 * Not necessarily better than a lot of other hashes, but should be OK, and
333 * well tested with binary data.
334 */
335
336#define FNV_32_PRIME ((guint32)0x01000193)
337#define FNV1_32_INIT ((guint32)0x811c9dc5)
338
339static guint32
340hash_bytes_fnv (unsigned char *buffer,
341 int len,
342 guint32 hval)
343{
344 while (len--)
345 {
346 hval *= FNV_32_PRIME;
347 hval ^= *buffer++;
348 }
349
350 return hval;
351}
352
353static void
354get_context_matrix (PangoContext *context,
355 PangoMatrix *matrix)
356{
357 const PangoMatrix *set_matrix;
358 const PangoMatrix identity = PANGO_MATRIX_INIT;
359
360 set_matrix = context ? pango_context_get_matrix (context) : NULL;
361 *matrix = set_matrix ? *set_matrix : identity;
362 matrix->x0 = matrix->y0 = 0.;
363}
364
365static int
366get_scaled_size (PangoFcFontMap *fcfontmap,
367 PangoContext *context,
368 const PangoFontDescription *desc)
369{
370 double size = pango_font_description_get_size (desc);
371
372 if (!pango_font_description_get_size_is_absolute (desc))
373 {
374 double dpi = pango_fc_font_map_get_resolution (fcfontmap, context);
375
376 size = size * dpi / 72.;
377 }
378
379 return .5 + pango_matrix_get_font_scale_factor (matrix: pango_context_get_matrix (context)) * size;
380}
381
382
383
384struct _PangoFcFontsetKey {
385 PangoFcFontMap *fontmap;
386 PangoLanguage *language;
387 PangoFontDescription *desc;
388 PangoMatrix matrix;
389 int pixelsize;
390 double resolution;
391 gpointer context_key;
392 char *variations;
393};
394
395struct _PangoFcFontKey {
396 PangoFcFontMap *fontmap;
397 FcPattern *pattern;
398 PangoMatrix matrix;
399 gpointer context_key;
400 char *variations;
401};
402
403static void
404pango_fc_fontset_key_init (PangoFcFontsetKey *key,
405 PangoFcFontMap *fcfontmap,
406 PangoContext *context,
407 const PangoFontDescription *desc,
408 PangoLanguage *language)
409{
410 if (!language && context)
411 language = pango_context_get_language (context);
412
413 key->fontmap = fcfontmap;
414 get_context_matrix (context, matrix: &key->matrix);
415 key->pixelsize = get_scaled_size (fcfontmap, context, desc);
416 key->resolution = pango_fc_font_map_get_resolution (fcfontmap, context);
417 key->language = language;
418 key->variations = g_strdup (str: pango_font_description_get_variations (desc));
419 key->desc = pango_font_description_copy_static (desc);
420 pango_font_description_unset_fields (desc: key->desc, to_unset: PANGO_FONT_MASK_SIZE | PANGO_FONT_MASK_VARIATIONS);
421
422 if (context && PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get)
423 key->context_key = (gpointer)PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->context_key_get (fcfontmap, context);
424 else
425 key->context_key = NULL;
426}
427
428static gboolean
429pango_fc_fontset_key_equal (const PangoFcFontsetKey *key_a,
430 const PangoFcFontsetKey *key_b)
431{
432 if (key_a->language == key_b->language &&
433 key_a->pixelsize == key_b->pixelsize &&
434 key_a->resolution == key_b->resolution &&
435 ((key_a->variations == NULL && key_b->variations == NULL) ||
436 (key_a->variations && key_b->variations && (strcmp (s1: key_a->variations, s2: key_b->variations) == 0))) &&
437 pango_font_description_equal (desc1: key_a->desc, desc2: key_b->desc) &&
438 0 == memcmp (s1: &key_a->matrix, s2: &key_b->matrix, n: 4 * sizeof (double)))
439 {
440 if (key_a->context_key)
441 return PANGO_FC_FONT_MAP_GET_CLASS (key_a->fontmap)->context_key_equal (key_a->fontmap,
442 key_a->context_key,
443 key_b->context_key);
444 else
445 return key_a->context_key == key_b->context_key;
446 }
447 else
448 return FALSE;
449}
450
451static guint
452pango_fc_fontset_key_hash (const PangoFcFontsetKey *key)
453{
454 guint32 hash = FNV1_32_INIT;
455
456 /* We do a bytewise hash on the doubles */
457 hash = hash_bytes_fnv (buffer: (unsigned char *)(&key->matrix), len: sizeof (double) * 4, hval: hash);
458 hash = hash_bytes_fnv (buffer: (unsigned char *)(&key->resolution), len: sizeof (double), hval: hash);
459
460 hash ^= key->pixelsize;
461
462 if (key->variations)
463 hash ^= g_str_hash (v: key->variations);
464
465 if (key->context_key)
466 hash ^= PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap,
467 key->context_key);
468
469 return (hash ^
470 GPOINTER_TO_UINT (key->language) ^
471 pango_font_description_hash (desc: key->desc));
472}
473
474static void
475pango_fc_fontset_key_free (PangoFcFontsetKey *key)
476{
477 pango_font_description_free (desc: key->desc);
478 g_free (mem: key->variations);
479
480 if (key->context_key)
481 PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap,
482 key->context_key);
483
484 g_slice_free (PangoFcFontsetKey, key);
485}
486
487static PangoFcFontsetKey *
488pango_fc_fontset_key_copy (const PangoFcFontsetKey *old)
489{
490 PangoFcFontsetKey *key = g_slice_new (PangoFcFontsetKey);
491
492 key->fontmap = old->fontmap;
493 key->language = old->language;
494 key->desc = pango_font_description_copy (desc: old->desc);
495 key->matrix = old->matrix;
496 key->pixelsize = old->pixelsize;
497 key->resolution = old->resolution;
498 key->variations = g_strdup (str: old->variations);
499
500 if (old->context_key)
501 key->context_key = PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap,
502 old->context_key);
503 else
504 key->context_key = NULL;
505
506 return key;
507}
508
509/**
510 * pango_fc_fontset_key_get_language:
511 * @key: the fontset key
512 *
513 * Gets the language member of @key.
514 *
515 * Returns: the language
516 *
517 * Since: 1.24
518 */
519PangoLanguage *
520pango_fc_fontset_key_get_language (const PangoFcFontsetKey *key)
521{
522 return key->language;
523}
524
525/**
526 * pango_fc_fontset_key_get_description:
527 * @key: the fontset key
528 *
529 * Gets the font description of @key.
530 *
531 * Returns: the font description, which is owned by @key and should not be modified.
532 *
533 * Since: 1.24
534 */
535const PangoFontDescription *
536pango_fc_fontset_key_get_description (const PangoFcFontsetKey *key)
537{
538 return key->desc;
539}
540
541/**
542 * pango_fc_fontset_key_get_matrix:
543 * @key: the fontset key
544 *
545 * Gets the matrix member of @key.
546 *
547 * Returns: the matrix, which is owned by @key and should not be modified.
548 *
549 * Since: 1.24
550 */
551const PangoMatrix *
552pango_fc_fontset_key_get_matrix (const PangoFcFontsetKey *key)
553{
554 return &key->matrix;
555}
556
557/**
558 * pango_fc_fontset_key_get_absolute_size:
559 * @key: the fontset key
560 *
561 * Gets the absolute font size of @key in Pango units.
562 *
563 * This is adjusted for both resolution and transformation matrix.
564 *
565 * Returns: the pixel size of @key.
566 *
567 * Since: 1.24
568 */
569double
570pango_fc_fontset_key_get_absolute_size (const PangoFcFontsetKey *key)
571{
572 return key->pixelsize;
573}
574
575/**
576 * pango_fc_fontset_key_get_resolution:
577 * @key: the fontset key
578 *
579 * Gets the resolution of @key
580 *
581 * Returns: the resolution of @key
582 *
583 * Since: 1.24
584 */
585double
586pango_fc_fontset_key_get_resolution (const PangoFcFontsetKey *key)
587{
588 return key->resolution;
589}
590
591/**
592 * pango_fc_fontset_key_get_context_key:
593 * @key: the font key
594 *
595 * Gets the context key member of @key.
596 *
597 * Returns: the context key, which is owned by @key and should not be modified.
598 *
599 * Since: 1.24
600 */
601gpointer
602pango_fc_fontset_key_get_context_key (const PangoFcFontsetKey *key)
603{
604 return key->context_key;
605}
606
607/*
608 * PangoFcFontKey
609 */
610
611static gboolean
612pango_fc_font_key_equal (const PangoFcFontKey *key_a,
613 const PangoFcFontKey *key_b)
614{
615 if (key_a->pattern == key_b->pattern &&
616 ((key_a->variations == NULL && key_b->variations == NULL) ||
617 (key_a->variations && key_b->variations && (strcmp (s1: key_a->variations, s2: key_b->variations) == 0))) &&
618 0 == memcmp (s1: &key_a->matrix, s2: &key_b->matrix, n: 4 * sizeof (double)))
619 {
620 if (key_a->context_key && key_b->context_key)
621 return PANGO_FC_FONT_MAP_GET_CLASS (key_a->fontmap)->context_key_equal (key_a->fontmap,
622 key_a->context_key,
623 key_b->context_key);
624 else
625 return key_a->context_key == key_b->context_key;
626 }
627 else
628 return FALSE;
629}
630
631static guint
632pango_fc_font_key_hash (const PangoFcFontKey *key)
633{
634 guint32 hash = FNV1_32_INIT;
635
636 /* We do a bytewise hash on the doubles */
637 hash = hash_bytes_fnv (buffer: (unsigned char *)(&key->matrix), len: sizeof (double) * 4, hval: hash);
638
639 if (key->variations)
640 hash ^= g_str_hash (v: key->variations);
641
642 if (key->context_key)
643 hash ^= PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_hash (key->fontmap,
644 key->context_key);
645
646 return (hash ^ GPOINTER_TO_UINT (key->pattern));
647}
648
649static void
650pango_fc_font_key_free (PangoFcFontKey *key)
651{
652 if (key->pattern)
653 FcPatternDestroy (p: key->pattern);
654
655 if (key->context_key)
656 PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_free (key->fontmap,
657 key->context_key);
658
659 g_free (mem: key->variations);
660
661 g_slice_free (PangoFcFontKey, key);
662}
663
664static PangoFcFontKey *
665pango_fc_font_key_copy (const PangoFcFontKey *old)
666{
667 PangoFcFontKey *key = g_slice_new (PangoFcFontKey);
668
669 key->fontmap = old->fontmap;
670 FcPatternReference (p: old->pattern);
671 key->pattern = old->pattern;
672 key->matrix = old->matrix;
673 key->variations = g_strdup (str: old->variations);
674 if (old->context_key)
675 key->context_key = PANGO_FC_FONT_MAP_GET_CLASS (key->fontmap)->context_key_copy (key->fontmap,
676 old->context_key);
677 else
678 key->context_key = NULL;
679
680 return key;
681}
682
683static void
684pango_fc_font_key_init (PangoFcFontKey *key,
685 PangoFcFontMap *fcfontmap,
686 PangoFcFontsetKey *fontset_key,
687 FcPattern *pattern)
688{
689 key->fontmap = fcfontmap;
690 key->pattern = pattern;
691 key->matrix = *pango_fc_fontset_key_get_matrix (key: fontset_key);
692 key->variations = fontset_key->variations;
693 key->context_key = pango_fc_fontset_key_get_context_key (key: fontset_key);
694}
695
696/* Public API */
697
698/**
699 * pango_fc_font_key_get_pattern:
700 * @key: the font key
701 *
702 * Gets the fontconfig pattern member of @key.
703 *
704 * Returns: the pattern, which is owned by @key and should not be modified.
705 *
706 * Since: 1.24
707 */
708const FcPattern *
709pango_fc_font_key_get_pattern (const PangoFcFontKey *key)
710{
711 return key->pattern;
712}
713
714/**
715 * pango_fc_font_key_get_matrix:
716 * @key: the font key
717 *
718 * Gets the matrix member of @key.
719 *
720 * Returns: the matrix, which is owned by @key and should not be modified.
721 *
722 * Since: 1.24
723 */
724const PangoMatrix *
725pango_fc_font_key_get_matrix (const PangoFcFontKey *key)
726{
727 return &key->matrix;
728}
729
730/**
731 * pango_fc_font_key_get_context_key:
732 * @key: the font key
733 *
734 * Gets the context key member of @key.
735 *
736 * Returns: the context key, which is owned by @key and should not be modified.
737 *
738 * Since: 1.24
739 */
740gpointer
741pango_fc_font_key_get_context_key (const PangoFcFontKey *key)
742{
743 return key->context_key;
744}
745
746const char *
747pango_fc_font_key_get_variations (const PangoFcFontKey *key)
748{
749 return key->variations;
750}
751
752/*
753 * PangoFcPatterns
754 */
755
756struct _PangoFcPatterns {
757 PangoFcFontMap *fontmap;
758
759 /* match and fontset are initialized in a thread,
760 * and are protected by a mutex. The thread signals
761 * the cond when match or fontset become available.
762 */
763 GMutex mutex;
764 GCond cond;
765
766 FcPattern *pattern;
767 FcPattern *match;
768 FcFontSet *fontset;
769};
770
771static FcFontSet *
772font_set_copy (FcFontSet *fontset)
773{
774 FcFontSet *copy;
775 int i;
776
777 if (!fontset)
778 return NULL;
779
780 copy = malloc (size: sizeof (FcFontSet));
781 copy->sfont = copy->nfont = fontset->nfont;
782 copy->fonts = malloc (size: sizeof (FcPattern *) * copy->nfont);
783 memcpy (dest: copy->fonts, src: fontset->fonts, n: sizeof (FcPattern *) * copy->nfont);
784 for (i = 0; i < copy->nfont; i++)
785 FcPatternReference (p: copy->fonts[i]);
786
787 return copy;
788}
789
790typedef struct {
791 FcConfig *config;
792 FcFontSet *fonts;
793 FcPattern *pattern;
794 PangoFcPatterns *patterns;
795} ThreadData;
796
797static FcFontSet *pango_fc_font_map_get_config_fonts (PangoFcFontMap *fcfontmap);
798
799static ThreadData *
800thread_data_new (PangoFcPatterns *patterns)
801{
802 ThreadData *td;
803 PangoFcFontMap *fontmap = patterns->fontmap;
804
805 /* We don't want the fontmap dying on us */
806 g_object_ref (fontmap);
807
808 td = g_new (ThreadData, 1);
809 td->patterns = pango_fc_patterns_ref (pats: patterns);
810 td->pattern = FcPatternDuplicate (p: patterns->pattern);
811 td->config = FcConfigReference (config: pango_fc_font_map_get_config (fcfontmap: patterns->fontmap));
812 td->fonts = font_set_copy (fontset: pango_fc_font_map_get_config_fonts (fcfontmap: patterns->fontmap));
813
814 return td;
815}
816
817static void
818thread_data_free (gpointer data)
819{
820 ThreadData *td = data;
821 PangoFcFontMap *fontmap = td->patterns->fontmap;
822
823 g_clear_pointer (&td->fonts, FcFontSetDestroy);
824 FcPatternDestroy (p: td->pattern);
825 FcConfigDestroy (config: td->config);
826 pango_fc_patterns_unref (pats: td->patterns);
827 g_free (mem: td);
828
829 g_object_unref (object: fontmap);
830}
831
832static gpointer
833match_in_thread (gpointer task_data)
834{
835 ThreadData *td = task_data;
836 FcResult result;
837 FcPattern *match;
838 gint64 before G_GNUC_UNUSED;
839
840 before = PANGO_TRACE_CURRENT_TIME;
841
842 match = FcFontSetMatch (config: td->config,
843 sets: &td->fonts, nsets: 1,
844 p: td->pattern,
845 result: &result);
846
847 pango_trace_mark (before, "FcFontSetMatch", NULL);
848
849 g_mutex_lock (mutex: &td->patterns->mutex);
850 td->patterns->match = match;
851 g_cond_signal (cond: &td->patterns->cond);
852 g_mutex_unlock (mutex: &td->patterns->mutex);
853
854 thread_data_free (data: td);
855
856 return NULL;
857}
858
859static gpointer
860sort_in_thread (gpointer task_data)
861{
862 ThreadData *td = task_data;
863 FcResult result;
864 FcFontSet *fontset;
865 gint64 before G_GNUC_UNUSED;
866
867 before = PANGO_TRACE_CURRENT_TIME;
868
869 fontset = FcFontSetSort (config: td->config,
870 sets: &td->fonts, nsets: 1,
871 p: td->pattern,
872 FcTrue,
873 NULL,
874 result: &result);
875
876 pango_trace_mark (before, "FcFontSetSort", NULL);
877
878 g_mutex_lock (mutex: &td->patterns->mutex);
879 td->patterns->fontset = fontset;
880 g_cond_signal (cond: &td->patterns->cond);
881 g_mutex_unlock (mutex: &td->patterns->mutex);
882
883 thread_data_free (data: td);
884
885 return NULL;
886}
887
888static PangoFcPatterns *
889pango_fc_patterns_new (FcPattern *pat, PangoFcFontMap *fontmap)
890{
891 PangoFcPatterns *pats;
892 GThread *thread;
893
894 pat = uniquify_pattern (fcfontmap: fontmap, pattern: pat);
895 pats = g_hash_table_lookup (hash_table: fontmap->priv->patterns_hash, key: pat);
896 if (pats)
897 return pango_fc_patterns_ref (pats);
898
899 pats = g_atomic_rc_box_new0 (PangoFcPatterns);
900
901 pats->fontmap = fontmap;
902
903 FcPatternReference (p: pat);
904 pats->pattern = pat;
905
906 g_mutex_init (mutex: &pats->mutex);
907 g_cond_init (cond: &pats->cond);
908
909 thread = g_thread_new (name: "[pango] FcFontSetMatch", func: match_in_thread, data: thread_data_new (patterns: pats));
910 g_thread_unref (thread);
911
912 thread = g_thread_new (name: "[pango] FcFontSetSort", func: sort_in_thread, data: thread_data_new (patterns: pats));
913 g_thread_unref (thread);
914
915 g_hash_table_insert (hash_table: fontmap->priv->patterns_hash,
916 key: pats->pattern, value: pats);
917
918 return pats;
919}
920
921static PangoFcPatterns *
922pango_fc_patterns_ref (PangoFcPatterns *pats)
923{
924 return g_atomic_rc_box_acquire (pats);
925}
926
927static void
928free_patterns (gpointer data)
929{
930 PangoFcPatterns *pats = data;
931
932 /* Only remove from fontmap hash if we are in it. This is not necessarily
933 * the case after a cache_clear() call. */
934 if (pats->fontmap->priv->patterns_hash &&
935 pats == g_hash_table_lookup (hash_table: pats->fontmap->priv->patterns_hash, key: pats->pattern))
936 g_hash_table_remove (hash_table: pats->fontmap->priv->patterns_hash,
937 key: pats->pattern);
938
939 if (pats->pattern)
940 FcPatternDestroy (p: pats->pattern);
941
942 if (pats->match)
943 FcPatternDestroy (p: pats->match);
944
945 if (pats->fontset)
946 FcFontSetDestroy (s: pats->fontset);
947
948 g_cond_clear (cond: &pats->cond);
949 g_mutex_clear (mutex: &pats->mutex);
950}
951
952static void
953pango_fc_patterns_unref (PangoFcPatterns *pats)
954{
955 g_atomic_rc_box_release_full (mem_block: pats, clear_func: free_patterns);
956}
957
958static FcPattern *
959pango_fc_patterns_get_pattern (PangoFcPatterns *pats)
960{
961 return pats->pattern;
962}
963
964static gboolean
965pango_fc_is_supported_font_format (FcPattern* pattern)
966{
967 FcResult res;
968 const char *fontformat;
969 const char *file;
970
971 /* Harfbuzz loads woff fonts, but we don't get any glyphs */
972 res = FcPatternGetString (p: pattern, FC_FILE, n: 0, s: (FcChar8 **)(void*)&file);
973 if (res == FcResultMatch &&
974 (g_str_has_suffix (str: file, suffix: ".woff") ||
975 g_str_has_suffix (str: file, suffix: ".woff2")))
976 return FALSE;
977
978 res = FcPatternGetString (p: pattern, FC_FONTFORMAT, n: 0, s: (FcChar8 **)(void*)&fontformat);
979 if (res != FcResultMatch)
980 return FALSE;
981
982 /* Harfbuzz supports only SFNT fonts. */
983 /* FIXME: "CFF" is used for both CFF in OpenType and bare CFF files, but
984 * HarfBuzz does not support the later and FontConfig does not seem
985 * to have a way to tell them apart.
986 */
987 if (g_ascii_strcasecmp (s1: fontformat, s2: "TrueType") == 0 ||
988 g_ascii_strcasecmp (s1: fontformat, s2: "CFF") == 0)
989 return TRUE;
990 return FALSE;
991}
992
993static FcFontSet *
994filter_by_format (FcFontSet **sets, int nsets)
995{
996 FcFontSet *result;
997 int set;
998
999 result = FcFontSetCreate ();
1000
1001 for (set = 0; set < nsets; set++)
1002 {
1003 FcFontSet *fontset = sets[set];
1004 int i;
1005
1006 if (!fontset)
1007 continue;
1008
1009 for (i = 0; i < fontset->nfont; i++)
1010 {
1011 if (!pango_fc_is_supported_font_format (pattern: fontset->fonts[i]))
1012 continue;
1013
1014 FcPatternReference (p: fontset->fonts[i]);
1015 FcFontSetAdd (s: result, font: fontset->fonts[i]);
1016 }
1017 }
1018
1019 return result;
1020}
1021
1022static FcPattern *
1023pango_fc_patterns_get_font_pattern (PangoFcPatterns *pats, int i, gboolean *prepare)
1024{
1025 FcPattern *match = NULL;
1026 FcFontSet *fontset = NULL;
1027
1028 if (i == 0)
1029 {
1030 gint64 before G_GNUC_UNUSED;
1031 gboolean waited = FALSE;
1032
1033 before = PANGO_TRACE_CURRENT_TIME;
1034
1035 g_mutex_lock (mutex: &pats->mutex);
1036
1037 while (!pats->match && !pats->fontset)
1038 {
1039 waited = TRUE;
1040 g_cond_wait (cond: &pats->cond, mutex: &pats->mutex);
1041 }
1042
1043 match = pats->match;
1044 fontset = pats->fontset;
1045
1046 g_mutex_unlock (mutex: &pats->mutex);
1047
1048 if (waited)
1049 pango_trace_mark (before, "wait for FcFontMatch", NULL);
1050
1051 if (match)
1052 {
1053 *prepare = FALSE;
1054 return match;
1055 }
1056 }
1057 else
1058 {
1059 gint64 before G_GNUC_UNUSED;
1060 gboolean waited = FALSE;
1061
1062 before = PANGO_TRACE_CURRENT_TIME;
1063
1064 g_mutex_lock (mutex: &pats->mutex);
1065
1066 while (!pats->fontset)
1067 {
1068 waited = TRUE;
1069 g_cond_wait (cond: &pats->cond, mutex: &pats->mutex);
1070 }
1071
1072 fontset = pats->fontset;
1073
1074 g_mutex_unlock (mutex: &pats->mutex);
1075
1076 if (waited)
1077 pango_trace_mark (before, "wait for FcFontSort", NULL);
1078 }
1079
1080 if (fontset)
1081 {
1082 if (i < fontset->nfont)
1083 {
1084 *prepare = TRUE;
1085 return fontset->fonts[i];
1086 }
1087 }
1088
1089 return NULL;
1090}
1091
1092
1093/*
1094 * PangoFcFontset
1095 */
1096
1097static void pango_fc_fontset_finalize (GObject *object);
1098static PangoLanguage * pango_fc_fontset_get_language (PangoFontset *fontset);
1099static PangoFont * pango_fc_fontset_get_font (PangoFontset *fontset,
1100 guint wc);
1101static void pango_fc_fontset_foreach (PangoFontset *fontset,
1102 PangoFontsetForeachFunc func,
1103 gpointer data);
1104
1105struct _PangoFcFontset
1106{
1107 PangoFontset parent_instance;
1108
1109 PangoFcFontsetKey *key;
1110
1111 PangoFcPatterns *patterns;
1112 int patterns_i;
1113
1114 GPtrArray *fonts;
1115 GPtrArray *coverages;
1116
1117 GList *cache_link;
1118};
1119
1120typedef PangoFontsetClass PangoFcFontsetClass;
1121
1122G_DEFINE_TYPE (PangoFcFontset, pango_fc_fontset, PANGO_TYPE_FONTSET)
1123
1124static PangoFcFontset *
1125pango_fc_fontset_new (PangoFcFontsetKey *key,
1126 PangoFcPatterns *patterns)
1127{
1128 PangoFcFontset *fontset;
1129
1130 fontset = g_object_new (PANGO_FC_TYPE_FONTSET, NULL);
1131
1132 fontset->key = pango_fc_fontset_key_copy (old: key);
1133 fontset->patterns = pango_fc_patterns_ref (pats: patterns);
1134
1135 return fontset;
1136}
1137
1138static PangoFcFontsetKey *
1139pango_fc_fontset_get_key (PangoFcFontset *fontset)
1140{
1141 return fontset->key;
1142}
1143
1144static PangoFont *
1145pango_fc_fontset_load_next_font (PangoFcFontset *fontset)
1146{
1147 FcPattern *pattern, *font_pattern;
1148 PangoFont *font;
1149 gboolean prepare;
1150
1151 pattern = pango_fc_patterns_get_pattern (pats: fontset->patterns);
1152 font_pattern = pango_fc_patterns_get_font_pattern (pats: fontset->patterns,
1153 i: fontset->patterns_i++,
1154 prepare: &prepare);
1155 if (G_UNLIKELY (!font_pattern))
1156 return NULL;
1157
1158 if (prepare)
1159 {
1160 font_pattern = FcFontRenderPrepare (config: fontset->key->fontmap->priv->config, pat: pattern, font: font_pattern);
1161
1162 if (G_UNLIKELY (!font_pattern))
1163 return NULL;
1164 }
1165
1166 font = pango_fc_font_map_new_font (fontmap: fontset->key->fontmap,
1167 fontset_key: fontset->key,
1168 match: font_pattern);
1169
1170 if (prepare)
1171 FcPatternDestroy (p: font_pattern);
1172
1173 return font;
1174}
1175
1176static PangoFont *
1177pango_fc_fontset_get_font_at (PangoFcFontset *fontset,
1178 unsigned int i)
1179{
1180 while (i >= fontset->fonts->len)
1181 {
1182 PangoFont *font = pango_fc_fontset_load_next_font (fontset);
1183 g_ptr_array_add (array: fontset->fonts, data: font);
1184 g_ptr_array_add (array: fontset->coverages, NULL);
1185 if (!font)
1186 return NULL;
1187 }
1188
1189 return g_ptr_array_index (fontset->fonts, i);
1190}
1191
1192static void
1193pango_fc_fontset_class_init (PangoFcFontsetClass *class)
1194{
1195 GObjectClass *object_class = G_OBJECT_CLASS (class);
1196 PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (class);
1197
1198 object_class->finalize = pango_fc_fontset_finalize;
1199
1200 fontset_class->get_font = pango_fc_fontset_get_font;
1201 fontset_class->get_language = pango_fc_fontset_get_language;
1202 fontset_class->foreach = pango_fc_fontset_foreach;
1203}
1204
1205static void
1206pango_fc_fontset_init (PangoFcFontset *fontset)
1207{
1208 fontset->fonts = g_ptr_array_new ();
1209 fontset->coverages = g_ptr_array_new ();
1210}
1211
1212static void
1213pango_fc_fontset_finalize (GObject *object)
1214{
1215 PangoFcFontset *fontset = PANGO_FC_FONTSET (object);
1216 unsigned int i;
1217
1218 for (i = 0; i < fontset->fonts->len; i++)
1219 {
1220 PangoFont *font = g_ptr_array_index(fontset->fonts, i);
1221 if (font)
1222 g_object_unref (object: font);
1223 }
1224 g_ptr_array_free (array: fontset->fonts, TRUE);
1225
1226 for (i = 0; i < fontset->coverages->len; i++)
1227 {
1228 PangoCoverage *coverage = g_ptr_array_index (fontset->coverages, i);
1229 if (coverage)
1230 g_object_unref (object: coverage);
1231 }
1232 g_ptr_array_free (array: fontset->coverages, TRUE);
1233
1234 if (fontset->key)
1235 pango_fc_fontset_key_free (key: fontset->key);
1236
1237 if (fontset->patterns)
1238 pango_fc_patterns_unref (pats: fontset->patterns);
1239
1240 G_OBJECT_CLASS (pango_fc_fontset_parent_class)->finalize (object);
1241}
1242
1243static PangoLanguage *
1244pango_fc_fontset_get_language (PangoFontset *fontset)
1245{
1246 PangoFcFontset *fcfontset = PANGO_FC_FONTSET (fontset);
1247
1248 return pango_fc_fontset_key_get_language (key: pango_fc_fontset_get_key (fontset: fcfontset));
1249}
1250
1251static PangoFont *
1252pango_fc_fontset_get_font (PangoFontset *fontset,
1253 guint wc)
1254{
1255 PangoFcFontset *fcfontset = PANGO_FC_FONTSET (fontset);
1256 PangoCoverageLevel best_level = PANGO_COVERAGE_NONE;
1257 PangoCoverageLevel level;
1258 PangoFont *font;
1259 PangoCoverage *coverage;
1260 int result = -1;
1261 unsigned int i;
1262
1263 for (i = 0;
1264 pango_fc_fontset_get_font_at (fontset: fcfontset, i);
1265 i++)
1266 {
1267 coverage = g_ptr_array_index (fcfontset->coverages, i);
1268
1269 if (coverage == NULL)
1270 {
1271 font = g_ptr_array_index (fcfontset->fonts, i);
1272
1273 coverage = pango_font_get_coverage (font, language: fcfontset->key->language);
1274 g_ptr_array_index (fcfontset->coverages, i) = coverage;
1275 }
1276
1277 level = pango_coverage_get (coverage, index_: wc);
1278
1279 if (result == -1 || level > best_level)
1280 {
1281 result = i;
1282 best_level = level;
1283 if (level == PANGO_COVERAGE_EXACT)
1284 break;
1285 }
1286 }
1287
1288 if (G_UNLIKELY (result == -1))
1289 return NULL;
1290
1291 font = g_ptr_array_index (fcfontset->fonts, result);
1292 return g_object_ref (font);
1293}
1294
1295static void
1296pango_fc_fontset_foreach (PangoFontset *fontset,
1297 PangoFontsetForeachFunc func,
1298 gpointer data)
1299{
1300 PangoFcFontset *fcfontset = PANGO_FC_FONTSET (fontset);
1301 PangoFont *font;
1302 unsigned int i;
1303
1304 for (i = 0;
1305 (font = pango_fc_fontset_get_font_at (fontset: fcfontset, i));
1306 i++)
1307 {
1308 if ((*func) (fontset, font, data))
1309 return;
1310 }
1311}
1312
1313
1314/*
1315 * PangoFcFontMap
1316 */
1317
1318static GType
1319pango_fc_font_map_get_item_type (GListModel *list)
1320{
1321 return PANGO_TYPE_FONT_FAMILY;
1322}
1323
1324static void ensure_families (PangoFcFontMap *fcfontmap);
1325
1326static guint
1327pango_fc_font_map_get_n_items (GListModel *list)
1328{
1329 PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (list);
1330
1331 ensure_families (fcfontmap);
1332
1333 return fcfontmap->priv->n_families;
1334}
1335
1336static gpointer
1337pango_fc_font_map_get_item (GListModel *list,
1338 guint position)
1339{
1340 PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (list);
1341
1342 ensure_families (fcfontmap);
1343
1344 if (position < fcfontmap->priv->n_families)
1345 return g_object_ref (fcfontmap->priv->families[position]);
1346
1347 return NULL;
1348}
1349
1350static void
1351pango_fc_font_map_list_model_init (GListModelInterface *iface)
1352{
1353 iface->get_item_type = pango_fc_font_map_get_item_type;
1354 iface->get_n_items = pango_fc_font_map_get_n_items;
1355 iface->get_item = pango_fc_font_map_get_item;
1356}
1357
1358G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoFcFontMap, pango_fc_font_map, PANGO_TYPE_FONT_MAP,
1359 G_ADD_PRIVATE (PangoFcFontMap)
1360 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pango_fc_font_map_list_model_init))
1361
1362static gpointer
1363init_in_thread (gpointer task_data)
1364{
1365 gint64 before G_GNUC_UNUSED;
1366
1367 before = PANGO_TRACE_CURRENT_TIME;
1368
1369 FcInit ();
1370
1371 pango_trace_mark (before, "FcInit", NULL);
1372
1373 g_mutex_lock (mutex: &fc_init_mutex);
1374 fc_initialized = DEFAULT_CONFIG_INITIALIZED;
1375 g_cond_broadcast (cond: &fc_init_cond);
1376 g_mutex_unlock (mutex: &fc_init_mutex);
1377
1378 return NULL;
1379}
1380
1381static void
1382start_init_in_thread (PangoFcFontMap *fcfontmap)
1383{
1384 g_mutex_lock (mutex: &fc_init_mutex);
1385
1386 if (fc_initialized == DEFAULT_CONFIG_NOT_INITIALIZED)
1387 {
1388 GThread *thread;
1389
1390 fc_initialized = DEFAULT_CONFIG_INITIALIZING;
1391 thread = g_thread_new (name: "[pango] FcInit", func: init_in_thread, NULL);
1392 g_thread_unref (thread);
1393 }
1394
1395 g_mutex_unlock (mutex: &fc_init_mutex);
1396}
1397
1398static void
1399wait_for_fc_init (void)
1400{
1401 gint64 before G_GNUC_UNUSED;
1402 gboolean waited = FALSE;
1403
1404 before = PANGO_TRACE_CURRENT_TIME;
1405
1406 g_mutex_lock (mutex: &fc_init_mutex);
1407 while (fc_initialized < DEFAULT_CONFIG_INITIALIZED)
1408 {
1409 waited = TRUE;
1410 g_cond_wait (cond: &fc_init_cond, mutex: &fc_init_mutex);
1411 }
1412 g_mutex_unlock (mutex: &fc_init_mutex);
1413
1414 if (waited)
1415 pango_trace_mark (before, "wait for FcInit", NULL);
1416}
1417
1418static void
1419pango_fc_font_map_init (PangoFcFontMap *fcfontmap)
1420{
1421 PangoFcFontMapPrivate *priv;
1422
1423 priv = fcfontmap->priv = pango_fc_font_map_get_instance_private (self: fcfontmap);
1424
1425 priv->n_families = -1;
1426
1427 priv->font_hash = g_hash_table_new (hash_func: (GHashFunc)pango_fc_font_key_hash,
1428 key_equal_func: (GEqualFunc)pango_fc_font_key_equal);
1429
1430 priv->fontset_hash = g_hash_table_new_full (hash_func: (GHashFunc)pango_fc_fontset_key_hash,
1431 key_equal_func: (GEqualFunc)pango_fc_fontset_key_equal,
1432 NULL,
1433 value_destroy_func: (GDestroyNotify)g_object_unref);
1434 priv->fontset_cache = g_queue_new ();
1435
1436 priv->patterns_hash = g_hash_table_new (NULL, NULL);
1437
1438 priv->pattern_hash = g_hash_table_new_full (hash_func: (GHashFunc) FcPatternHash,
1439 key_equal_func: (GEqualFunc) FcPatternEqual,
1440 key_destroy_func: (GDestroyNotify) FcPatternDestroy,
1441 NULL);
1442
1443 priv->font_face_data_hash = g_hash_table_new_full (hash_func: (GHashFunc)pango_fc_font_face_data_hash,
1444 key_equal_func: (GEqualFunc)pango_fc_font_face_data_equal,
1445 key_destroy_func: (GDestroyNotify)pango_fc_font_face_data_free,
1446 NULL);
1447 priv->dpi = -1;
1448
1449 start_init_in_thread (fcfontmap);
1450}
1451
1452static void
1453pango_fc_font_map_fini (PangoFcFontMap *fcfontmap)
1454{
1455 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1456 int i;
1457
1458 g_clear_pointer (&priv->fonts, FcFontSetDestroy);
1459
1460 g_queue_free (queue: priv->fontset_cache);
1461 priv->fontset_cache = NULL;
1462
1463 g_hash_table_destroy (hash_table: priv->fontset_hash);
1464 priv->fontset_hash = NULL;
1465
1466 g_hash_table_destroy (hash_table: priv->patterns_hash);
1467 priv->patterns_hash = NULL;
1468
1469 g_hash_table_destroy (hash_table: priv->font_hash);
1470 priv->font_hash = NULL;
1471
1472 g_hash_table_destroy (hash_table: priv->font_face_data_hash);
1473 priv->font_face_data_hash = NULL;
1474
1475 g_hash_table_destroy (hash_table: priv->pattern_hash);
1476 priv->pattern_hash = NULL;
1477
1478 for (i = 0; i < priv->n_families; i++)
1479 g_object_unref (object: priv->families[i]);
1480 g_free (mem: priv->families);
1481 priv->n_families = -1;
1482 priv->families = NULL;
1483}
1484
1485static void
1486pango_fc_font_map_class_init (PangoFcFontMapClass *class)
1487{
1488 GObjectClass *object_class = G_OBJECT_CLASS (class);
1489 PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (class);
1490
1491 object_class->finalize = pango_fc_font_map_finalize;
1492 fontmap_class->load_font = pango_fc_font_map_load_font;
1493 fontmap_class->load_fontset = pango_fc_font_map_load_fontset;
1494 fontmap_class->list_families = pango_fc_font_map_list_families;
1495 fontmap_class->get_family = pango_fc_font_map_get_family;
1496 fontmap_class->get_face = pango_fc_font_map_get_face;
1497 fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_FC;
1498 fontmap_class->changed = pango_fc_font_map_changed;
1499}
1500
1501
1502/**
1503 * pango_fc_font_map_add_decoder_find_func:
1504 * @fcfontmap: The `PangoFcFontMap` to add this method to.
1505 * @findfunc: The `PangoFcDecoderFindFunc` callback function
1506 * @user_data: User data.
1507 * @dnotify: A `GDestroyNotify` callback that will be called when the
1508 * fontmap is finalized and the decoder is released.
1509 *
1510 * This function saves a callback method in the `PangoFcFontMap` that
1511 * will be called whenever new fonts are created.
1512 *
1513 * If the function returns a `PangoFcDecoder`, that decoder will be used
1514 * to determine both coverage via a `FcCharSet` and a one-to-one mapping
1515 * of characters to glyphs. This will allow applications to have
1516 * application-specific encodings for various fonts.
1517 *
1518 * Since: 1.6
1519 */
1520void
1521pango_fc_font_map_add_decoder_find_func (PangoFcFontMap *fcfontmap,
1522 PangoFcDecoderFindFunc findfunc,
1523 gpointer user_data,
1524 GDestroyNotify dnotify)
1525{
1526 PangoFcFontMapPrivate *priv;
1527 PangoFcFindFuncInfo *info;
1528
1529 g_return_if_fail (PANGO_IS_FC_FONT_MAP (fcfontmap));
1530
1531 priv = fcfontmap->priv;
1532
1533 info = g_slice_new (PangoFcFindFuncInfo);
1534
1535 info->findfunc = findfunc;
1536 info->user_data = user_data;
1537 info->dnotify = dnotify;
1538
1539 priv->findfuncs = g_slist_append (list: priv->findfuncs, data: info);
1540}
1541
1542/**
1543 * pango_fc_font_map_find_decoder:
1544 * @fcfontmap: The `PangoFcFontMap` to use.
1545 * @pattern: The `FcPattern` to find the decoder for.
1546 *
1547 * Finds the decoder to use for @pattern.
1548 *
1549 * Decoders can be added to a font map using
1550 * [method@PangoFc.FontMap.add_decoder_find_func].
1551 *
1552 * Returns: (transfer full) (nullable): a newly created `PangoFcDecoder`
1553 * object or %NULL if no decoder is set for @pattern.
1554 *
1555 * Since: 1.26
1556 */
1557PangoFcDecoder *
1558pango_fc_font_map_find_decoder (PangoFcFontMap *fcfontmap,
1559 FcPattern *pattern)
1560{
1561 GSList *l;
1562
1563 g_return_val_if_fail (PANGO_IS_FC_FONT_MAP (fcfontmap), NULL);
1564 g_return_val_if_fail (pattern != NULL, NULL);
1565
1566 for (l = fcfontmap->priv->findfuncs; l && l->data; l = l->next)
1567 {
1568 PangoFcFindFuncInfo *info = l->data;
1569 PangoFcDecoder *decoder;
1570
1571 decoder = info->findfunc (pattern, info->user_data);
1572 if (decoder)
1573 return decoder;
1574 }
1575
1576 return NULL;
1577}
1578
1579static void
1580pango_fc_font_map_finalize (GObject *object)
1581{
1582 PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (object);
1583
1584 pango_fc_font_map_shutdown (fcfontmap);
1585
1586 if (fcfontmap->substitute_destroy)
1587 fcfontmap->substitute_destroy (fcfontmap->substitute_data);
1588
1589 G_OBJECT_CLASS (pango_fc_font_map_parent_class)->finalize (object);
1590}
1591
1592/* Add a mapping from key to fcfont */
1593static void
1594pango_fc_font_map_add (PangoFcFontMap *fcfontmap,
1595 PangoFcFontKey *key,
1596 PangoFcFont *fcfont)
1597{
1598 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1599 PangoFcFontKey *key_copy;
1600
1601 key_copy = pango_fc_font_key_copy (old: key);
1602 _pango_fc_font_set_font_key (fcfont, key: key_copy);
1603 g_hash_table_insert (hash_table: priv->font_hash, key: key_copy, value: fcfont);
1604}
1605
1606/* Remove mapping from fcfont->key to fcfont */
1607/* Closely related to shutdown_font() */
1608void
1609_pango_fc_font_map_remove (PangoFcFontMap *fcfontmap,
1610 PangoFcFont *fcfont)
1611{
1612 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1613 PangoFcFontKey *key;
1614
1615 key = _pango_fc_font_get_font_key (fcfont);
1616 if (key)
1617 {
1618 /* Only remove from fontmap hash if we are in it. This is not necessarily
1619 * the case after a cache_clear() call. */
1620 if (priv->font_hash &&
1621 fcfont == g_hash_table_lookup (hash_table: priv->font_hash, key))
1622 {
1623 g_hash_table_remove (hash_table: priv->font_hash, key);
1624 }
1625 _pango_fc_font_set_font_key (fcfont, NULL);
1626 pango_fc_font_key_free (key);
1627 }
1628}
1629
1630static PangoFcFamily *
1631create_family (PangoFcFontMap *fcfontmap,
1632 const char *family_name,
1633 int spacing)
1634{
1635 PangoFcFamily *family = g_object_new (PANGO_FC_TYPE_FAMILY, NULL);
1636 family->fontmap = fcfontmap;
1637 family->family_name = g_strdup (str: family_name);
1638 family->spacing = spacing;
1639 family->variable = FALSE;
1640 family->patterns = FcFontSetCreate ();
1641
1642 return family;
1643}
1644
1645static gboolean
1646is_alias_family (const char *family_name)
1647{
1648 switch (family_name[0])
1649 {
1650 case 'c':
1651 case 'C':
1652 return (g_ascii_strcasecmp (s1: family_name, s2: "cursive") == 0);
1653 case 'f':
1654 case 'F':
1655 return (g_ascii_strcasecmp (s1: family_name, s2: "fantasy") == 0);
1656 case 'm':
1657 case 'M':
1658 return (g_ascii_strcasecmp (s1: family_name, s2: "monospace") == 0);
1659 case 's':
1660 case 'S':
1661 return (g_ascii_strcasecmp (s1: family_name, s2: "sans") == 0 ||
1662 g_ascii_strcasecmp (s1: family_name, s2: "serif") == 0 ||
1663 g_ascii_strcasecmp (s1: family_name, s2: "system-ui") == 0);
1664 default:
1665 return FALSE;
1666 }
1667
1668 return FALSE;
1669}
1670
1671static void
1672ensure_families (PangoFcFontMap *fcfontmap)
1673{
1674 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1675 FcFontSet *fontset;
1676 int i;
1677 int count;
1678
1679 wait_for_fc_init ();
1680
1681 if (priv->n_families < 0)
1682 {
1683 FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_SPACING, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT,
1684 FC_VARIABLE,
1685 FC_FONTFORMAT,
1686 NULL);
1687 FcPattern *pat = FcPatternCreate ();
1688 GHashTable *temp_family_hash;
1689 FcFontSet *fonts;
1690
1691 fonts = pango_fc_font_map_get_config_fonts (fcfontmap);
1692 fontset = FcFontSetList (config: priv->config, sets: &fonts, nsets: 1, p: pat, os);
1693
1694 FcPatternDestroy (p: pat);
1695 FcObjectSetDestroy (os);
1696
1697 priv->families = g_new (PangoFcFamily *, fontset->nfont + 4); /* 4 standard aliases */
1698 temp_family_hash = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
1699
1700 count = 0;
1701 for (i = 0; i < fontset->nfont; i++)
1702 {
1703 char *s;
1704 FcResult res;
1705 int spacing;
1706 int variable;
1707 PangoFcFamily *temp_family;
1708
1709 res = FcPatternGetString (p: fontset->fonts[i], FC_FAMILY, n: 0, s: (FcChar8 **)(void*)&s);
1710 g_assert (res == FcResultMatch);
1711
1712 temp_family = g_hash_table_lookup (hash_table: temp_family_hash, key: s);
1713 if (!is_alias_family (family_name: s) && !temp_family)
1714 {
1715 res = FcPatternGetInteger (p: fontset->fonts[i], FC_SPACING, n: 0, i: &spacing);
1716 g_assert (res == FcResultMatch || res == FcResultNoMatch);
1717 if (res == FcResultNoMatch)
1718 spacing = FC_PROPORTIONAL;
1719
1720 temp_family = create_family (fcfontmap, family_name: s, spacing);
1721 g_hash_table_insert (hash_table: temp_family_hash, key: g_strdup (str: s), value: temp_family);
1722 priv->families[count++] = temp_family;
1723 }
1724
1725 if (temp_family)
1726 {
1727 variable = FALSE;
1728 variable = FcPatternGetBool (p: fontset->fonts[i], FC_VARIABLE, n: 0, b: &variable);
1729 if (variable)
1730 temp_family->variable = TRUE;
1731
1732 FcPatternReference (p: fontset->fonts[i]);
1733 FcFontSetAdd (s: temp_family->patterns, font: fontset->fonts[i]);
1734 }
1735 }
1736
1737 FcFontSetDestroy (s: fontset);
1738 g_hash_table_destroy (hash_table: temp_family_hash);
1739
1740 priv->families[count++] = create_family (fcfontmap, family_name: "Sans", FC_PROPORTIONAL);
1741 priv->families[count++] = create_family (fcfontmap, family_name: "Serif", FC_PROPORTIONAL);
1742 priv->families[count++] = create_family (fcfontmap, family_name: "Monospace", FC_MONO);
1743 priv->families[count++] = create_family (fcfontmap, family_name: "System-ui", FC_PROPORTIONAL);
1744
1745 priv->n_families = count;
1746 }
1747}
1748
1749static void
1750pango_fc_font_map_list_families (PangoFontMap *fontmap,
1751 PangoFontFamily ***families,
1752 int *n_families)
1753{
1754 PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (fontmap);
1755 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1756
1757 if (priv->closed)
1758 {
1759 if (families)
1760 *families = NULL;
1761 if (n_families)
1762 *n_families = 0;
1763
1764 return;
1765 }
1766
1767 ensure_families (fcfontmap);
1768
1769 if (n_families)
1770 *n_families = priv->n_families;
1771
1772 if (families)
1773 *families = g_memdup2 (mem: priv->families, byte_size: priv->n_families * sizeof (PangoFontFamily *));
1774}
1775
1776static PangoFontFamily *
1777pango_fc_font_map_get_family (PangoFontMap *fontmap,
1778 const char *name)
1779{
1780 PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (fontmap);
1781 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1782 int i;
1783
1784 if (priv->closed)
1785 return NULL;
1786
1787 ensure_families (fcfontmap);
1788
1789 for (i = 0; i < priv->n_families; i++)
1790 {
1791 PangoFontFamily *family = PANGO_FONT_FAMILY (priv->families[i]);
1792 if (strcmp (s1: name, s2: pango_font_family_get_name (family)) == 0)
1793 return family;
1794 }
1795
1796 return NULL;
1797}
1798
1799static double
1800pango_fc_convert_weight_to_fc (PangoWeight pango_weight)
1801{
1802 return FcWeightFromOpenTypeDouble (ot_weight: pango_weight);
1803}
1804
1805static int
1806pango_fc_convert_slant_to_fc (PangoStyle pango_style)
1807{
1808 switch (pango_style)
1809 {
1810 case PANGO_STYLE_NORMAL:
1811 return FC_SLANT_ROMAN;
1812 case PANGO_STYLE_ITALIC:
1813 return FC_SLANT_ITALIC;
1814 case PANGO_STYLE_OBLIQUE:
1815 return FC_SLANT_OBLIQUE;
1816 default:
1817 return FC_SLANT_ROMAN;
1818 }
1819}
1820
1821static int
1822pango_fc_convert_width_to_fc (PangoStretch pango_stretch)
1823{
1824 switch (pango_stretch)
1825 {
1826 case PANGO_STRETCH_NORMAL:
1827 return FC_WIDTH_NORMAL;
1828 case PANGO_STRETCH_ULTRA_CONDENSED:
1829 return FC_WIDTH_ULTRACONDENSED;
1830 case PANGO_STRETCH_EXTRA_CONDENSED:
1831 return FC_WIDTH_EXTRACONDENSED;
1832 case PANGO_STRETCH_CONDENSED:
1833 return FC_WIDTH_CONDENSED;
1834 case PANGO_STRETCH_SEMI_CONDENSED:
1835 return FC_WIDTH_SEMICONDENSED;
1836 case PANGO_STRETCH_SEMI_EXPANDED:
1837 return FC_WIDTH_SEMIEXPANDED;
1838 case PANGO_STRETCH_EXPANDED:
1839 return FC_WIDTH_EXPANDED;
1840 case PANGO_STRETCH_EXTRA_EXPANDED:
1841 return FC_WIDTH_EXTRAEXPANDED;
1842 case PANGO_STRETCH_ULTRA_EXPANDED:
1843 return FC_WIDTH_ULTRAEXPANDED;
1844 default:
1845 return FC_WIDTH_NORMAL;
1846 }
1847}
1848
1849static FcPattern *
1850pango_fc_make_pattern (const PangoFontDescription *description,
1851 PangoLanguage *language,
1852 int pixel_size,
1853 double dpi,
1854 const char *variations)
1855{
1856 FcPattern *pattern;
1857 const char *prgname;
1858 int slant;
1859 double weight;
1860 PangoGravity gravity;
1861 PangoVariant variant;
1862 char **families;
1863 int i;
1864 int width;
1865
1866 prgname = g_get_prgname ();
1867 slant = pango_fc_convert_slant_to_fc (pango_style: pango_font_description_get_style (desc: description));
1868 weight = pango_fc_convert_weight_to_fc (pango_weight: pango_font_description_get_weight (desc: description));
1869 width = pango_fc_convert_width_to_fc (pango_stretch: pango_font_description_get_stretch (desc: description));
1870
1871 gravity = pango_font_description_get_gravity (desc: description);
1872 variant = pango_font_description_get_variant (desc: description);
1873
1874 /* The reason for passing in FC_SIZE as well as FC_PIXEL_SIZE is
1875 * to work around a bug in libgnomeprint where it doesn't look
1876 * for FC_PIXEL_SIZE. See http://bugzilla.gnome.org/show_bug.cgi?id=169020
1877 *
1878 * Putting FC_SIZE in here slightly reduces the efficiency
1879 * of caching of patterns and fonts when working with multiple different
1880 * dpi values.
1881 *
1882 * Do not pass FC_VERTICAL_LAYOUT true as HarfBuzz shaping assumes false.
1883 */
1884 pattern = FcPatternBuild (NULL,
1885 PANGO_FC_VERSION, FcTypeInteger, pango_version(),
1886 FC_WEIGHT, FcTypeDouble, weight,
1887 FC_SLANT, FcTypeInteger, slant,
1888 FC_WIDTH, FcTypeInteger, width,
1889 FC_VARIABLE, FcTypeBool, FcDontCare,
1890 FC_DPI, FcTypeDouble, dpi,
1891 FC_SIZE, FcTypeDouble, pixel_size * (72. / 1024. / dpi),
1892 FC_PIXEL_SIZE, FcTypeDouble, pixel_size / 1024.,
1893 NULL);
1894
1895 if (variations)
1896 FcPatternAddString (p: pattern, FC_FONT_VARIATIONS, s: (FcChar8*) variations);
1897
1898 if (pango_font_description_get_family (desc: description))
1899 {
1900 families = g_strsplit (string: pango_font_description_get_family (desc: description), delimiter: ",", max_tokens: -1);
1901
1902 for (i = 0; families[i]; i++)
1903 FcPatternAddString (p: pattern, FC_FAMILY, s: (FcChar8*) families[i]);
1904
1905 g_strfreev (str_array: families);
1906 }
1907
1908 if (language)
1909 FcPatternAddString (p: pattern, FC_LANG, s: (FcChar8 *) pango_language_to_string (language));
1910
1911 if (gravity != PANGO_GRAVITY_SOUTH)
1912 {
1913 GEnumValue *value = g_enum_get_value (enum_class: get_gravity_class (), value: gravity);
1914 FcPatternAddString (p: pattern, PANGO_FC_GRAVITY, s: (FcChar8*) value->value_nick);
1915 }
1916
1917 if (prgname)
1918 FcPatternAddString (p: pattern, PANGO_FC_PRGNAME, s: (FcChar8*) prgname);
1919
1920 switch (variant)
1921 {
1922 case PANGO_VARIANT_SMALL_CAPS:
1923 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "smcp=1");
1924 break;
1925 case PANGO_VARIANT_ALL_SMALL_CAPS:
1926 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "smcp=1");
1927 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "c2sc=1");
1928 break;
1929 case PANGO_VARIANT_PETITE_CAPS:
1930 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "pcap=1");
1931 break;
1932 case PANGO_VARIANT_ALL_PETITE_CAPS:
1933 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "pcap=1");
1934 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "c2pc=1");
1935 break;
1936 case PANGO_VARIANT_UNICASE:
1937 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "unic=1");
1938 break;
1939 case PANGO_VARIANT_TITLE_CAPS:
1940 FcPatternAddString (p: pattern, FC_FONT_FEATURES, s: (FcChar8*) "titl=1");
1941 break;
1942 case PANGO_VARIANT_NORMAL:
1943 break;
1944 default:
1945 g_assert_not_reached ();
1946 }
1947
1948 return pattern;
1949}
1950
1951static FcPattern *
1952uniquify_pattern (PangoFcFontMap *fcfontmap,
1953 FcPattern *pattern)
1954{
1955 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1956 FcPattern *old_pattern;
1957
1958 old_pattern = g_hash_table_lookup (hash_table: priv->pattern_hash, key: pattern);
1959 if (old_pattern)
1960 {
1961 return old_pattern;
1962 }
1963 else
1964 {
1965 FcPatternReference (p: pattern);
1966 g_hash_table_insert (hash_table: priv->pattern_hash, key: pattern, value: pattern);
1967 return pattern;
1968 }
1969}
1970
1971static PangoFont *
1972pango_fc_font_map_new_font (PangoFcFontMap *fcfontmap,
1973 PangoFcFontsetKey *fontset_key,
1974 FcPattern *match)
1975{
1976 PangoFcFontMapClass *class;
1977 PangoFcFontMapPrivate *priv = fcfontmap->priv;
1978 FcPattern *pattern;
1979 PangoFcFont *fcfont;
1980 PangoFcFontKey key;
1981
1982 if (priv->closed)
1983 return NULL;
1984
1985 match = uniquify_pattern (fcfontmap, pattern: match);
1986
1987 pango_fc_font_key_init (key: &key, fcfontmap, fontset_key, pattern: match);
1988
1989 fcfont = g_hash_table_lookup (hash_table: priv->font_hash, key: &key);
1990 if (fcfont)
1991 return g_object_ref (PANGO_FONT (fcfont));
1992
1993 class = PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap);
1994
1995 if (class->create_font)
1996 {
1997 fcfont = class->create_font (fcfontmap, &key);
1998 }
1999 else
2000 {
2001 const PangoMatrix *pango_matrix = pango_fc_fontset_key_get_matrix (key: fontset_key);
2002 FcMatrix fc_matrix, *fc_matrix_val;
2003 int i;
2004
2005 /* Fontconfig has the Y axis pointing up, Pango, down.
2006 */
2007 fc_matrix.xx = pango_matrix->xx;
2008 fc_matrix.xy = - pango_matrix->xy;
2009 fc_matrix.yx = - pango_matrix->yx;
2010 fc_matrix.yy = pango_matrix->yy;
2011
2012 pattern = FcPatternDuplicate (p: match);
2013
2014 for (i = 0; FcPatternGetMatrix (p: pattern, FC_MATRIX, n: i, s: &fc_matrix_val) == FcResultMatch; i++)
2015 FcMatrixMultiply (result: &fc_matrix, a: &fc_matrix, b: fc_matrix_val);
2016
2017 FcPatternDel (p: pattern, FC_MATRIX);
2018 FcPatternAddMatrix (p: pattern, FC_MATRIX, s: &fc_matrix);
2019
2020 fcfont = class->new_font (fcfontmap, uniquify_pattern (fcfontmap, pattern));
2021
2022 FcPatternDestroy (p: pattern);
2023 }
2024
2025 if (!fcfont)
2026 return NULL;
2027
2028 /* In case the backend didn't set the fontmap */
2029 if (!fcfont->fontmap)
2030 g_object_set (object: fcfont,
2031 first_property_name: "fontmap", fcfontmap,
2032 NULL);
2033
2034 /* cache it on fontmap */
2035 pango_fc_font_map_add (fcfontmap, key: &key, fcfont);
2036
2037 return (PangoFont *)fcfont;
2038}
2039
2040static PangoFontFace *
2041pango_fc_font_map_get_face (PangoFontMap *fontmap,
2042 PangoFont *font)
2043{
2044 PangoFcFont *fcfont = PANGO_FC_FONT (font);
2045 FcResult res;
2046 const char *s;
2047 PangoFontFamily *family;
2048
2049 res = FcPatternGetString (p: fcfont->font_pattern, FC_FAMILY, n: 0, s: (FcChar8 **) &s);
2050 g_assert (res == FcResultMatch);
2051
2052 family = pango_font_map_get_family (fontmap, name: s);
2053
2054 res = FcPatternGetString (p: fcfont->font_pattern, FC_STYLE, n: 0, s: (FcChar8 **)(void*)&s);
2055 g_assert (res == FcResultMatch);
2056
2057 return pango_font_family_get_face (family, name: s);
2058}
2059
2060static void
2061pango_fc_default_substitute (PangoFcFontMap *fontmap,
2062 PangoFcFontsetKey *fontsetkey,
2063 FcPattern *pattern)
2064{
2065 if (PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->fontset_key_substitute)
2066 PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->fontset_key_substitute (fontmap, fontsetkey, pattern);
2067 else if (PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->default_substitute)
2068 PANGO_FC_FONT_MAP_GET_CLASS (fontmap)->default_substitute (fontmap, pattern);
2069}
2070
2071void
2072pango_fc_font_map_set_default_substitute (PangoFcFontMap *fontmap,
2073 PangoFcSubstituteFunc func,
2074 gpointer data,
2075 GDestroyNotify notify)
2076{
2077 if (fontmap->substitute_destroy)
2078 fontmap->substitute_destroy (fontmap->substitute_data);
2079
2080 fontmap->substitute_func = func;
2081 fontmap->substitute_data = data;
2082 fontmap->substitute_destroy = notify;
2083
2084 pango_fc_font_map_substitute_changed (fontmap);
2085}
2086
2087void
2088pango_fc_font_map_substitute_changed (PangoFcFontMap *fontmap) {
2089 pango_fc_font_map_cache_clear(fcfontmap: fontmap);
2090 pango_font_map_changed(PANGO_FONT_MAP (fontmap));
2091}
2092
2093static double
2094pango_fc_font_map_get_resolution (PangoFcFontMap *fcfontmap,
2095 PangoContext *context)
2096{
2097 if (PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->get_resolution)
2098 return PANGO_FC_FONT_MAP_GET_CLASS (fcfontmap)->get_resolution (fcfontmap, context);
2099
2100 if (fcfontmap->priv->dpi < 0)
2101 {
2102 FcResult result = FcResultNoMatch;
2103 FcPattern *tmp = FcPatternBuild (NULL,
2104 FC_FAMILY, FcTypeString, "Sans",
2105 FC_SIZE, FcTypeDouble, 10.,
2106 NULL);
2107 if (tmp)
2108 {
2109 pango_fc_default_substitute (fontmap: fcfontmap, NULL, pattern: tmp);
2110 result = FcPatternGetDouble (p: tmp, FC_DPI, n: 0, d: &fcfontmap->priv->dpi);
2111 FcPatternDestroy (p: tmp);
2112 }
2113
2114 if (result != FcResultMatch)
2115 {
2116 g_warning ("Error getting DPI from fontconfig, using 72.0");
2117 fcfontmap->priv->dpi = 72.0;
2118 }
2119 }
2120
2121 return fcfontmap->priv->dpi;
2122}
2123
2124static FcPattern *
2125pango_fc_fontset_key_make_pattern (PangoFcFontsetKey *key)
2126{
2127 return pango_fc_make_pattern (description: key->desc,
2128 language: key->language,
2129 pixel_size: key->pixelsize,
2130 dpi: key->resolution,
2131 variations: key->variations);
2132}
2133
2134static PangoFcPatterns *
2135pango_fc_font_map_get_patterns (PangoFontMap *fontmap,
2136 PangoFcFontsetKey *key)
2137{
2138 PangoFcFontMap *fcfontmap = (PangoFcFontMap *)fontmap;
2139 PangoFcPatterns *patterns;
2140 FcPattern *pattern;
2141
2142 pattern = pango_fc_fontset_key_make_pattern (key);
2143 pango_fc_default_substitute (fontmap: fcfontmap, fontsetkey: key, pattern);
2144
2145 patterns = pango_fc_patterns_new (pat: pattern, fontmap: fcfontmap);
2146
2147 FcPatternDestroy (p: pattern);
2148
2149 return patterns;
2150}
2151
2152static gboolean
2153get_first_font (PangoFontset *fontset G_GNUC_UNUSED,
2154 PangoFont *font,
2155 gpointer data)
2156{
2157 *(PangoFont **)data = font;
2158
2159 return TRUE;
2160}
2161
2162static PangoFont *
2163pango_fc_font_map_load_font (PangoFontMap *fontmap,
2164 PangoContext *context,
2165 const PangoFontDescription *description)
2166{
2167 PangoLanguage *language;
2168 PangoFontset *fontset;
2169 PangoFont *font = NULL;
2170
2171 if (context)
2172 language = pango_context_get_language (context);
2173 else
2174 language = NULL;
2175
2176 fontset = pango_font_map_load_fontset (fontmap, context, desc: description, language);
2177
2178 if (fontset)
2179 {
2180 pango_fontset_foreach (fontset, func: get_first_font, data: &font);
2181
2182 if (font)
2183 g_object_ref (font);
2184
2185 g_object_unref (object: fontset);
2186 }
2187
2188 return font;
2189}
2190
2191static void
2192pango_fc_fontset_cache (PangoFcFontset *fontset,
2193 PangoFcFontMap *fcfontmap)
2194{
2195 PangoFcFontMapPrivate *priv = fcfontmap->priv;
2196 GQueue *cache = priv->fontset_cache;
2197
2198 if (fontset->cache_link)
2199 {
2200 if (fontset->cache_link == cache->head)
2201 return;
2202
2203 /* Already in cache, move to head
2204 */
2205 if (fontset->cache_link == cache->tail)
2206 cache->tail = fontset->cache_link->prev;
2207
2208 cache->head = g_list_remove_link (list: cache->head, llink: fontset->cache_link);
2209 cache->length--;
2210 }
2211 else
2212 {
2213 /* Add to cache initially
2214 */
2215 if (cache->length == FONTSET_CACHE_SIZE)
2216 {
2217 PangoFcFontset *tmp_fontset = g_queue_pop_tail (queue: cache);
2218 tmp_fontset->cache_link = NULL;
2219 g_hash_table_remove (hash_table: priv->fontset_hash, key: tmp_fontset->key);
2220 }
2221
2222 fontset->cache_link = g_list_prepend (NULL, data: fontset);
2223 }
2224
2225 g_queue_push_head_link (queue: cache, link_: fontset->cache_link);
2226}
2227
2228static PangoFontset *
2229pango_fc_font_map_load_fontset (PangoFontMap *fontmap,
2230 PangoContext *context,
2231 const PangoFontDescription *desc,
2232 PangoLanguage *language)
2233{
2234 PangoFcFontMap *fcfontmap = (PangoFcFontMap *)fontmap;
2235 PangoFcFontMapPrivate *priv = fcfontmap->priv;
2236 PangoFcFontset *fontset;
2237 PangoFcFontsetKey key;
2238
2239 pango_fc_fontset_key_init (key: &key, fcfontmap, context, desc, language);
2240
2241 fontset = g_hash_table_lookup (hash_table: priv->fontset_hash, key: &key);
2242
2243 if (G_UNLIKELY (!fontset))
2244 {
2245 PangoFcPatterns *patterns = pango_fc_font_map_get_patterns (fontmap, key: &key);
2246
2247 if (!patterns)
2248 return NULL;
2249
2250 fontset = pango_fc_fontset_new (key: &key, patterns);
2251 g_hash_table_insert (hash_table: priv->fontset_hash, key: pango_fc_fontset_get_key (fontset), value: fontset);
2252
2253 pango_fc_patterns_unref (pats: patterns);
2254 }
2255
2256 pango_fc_fontset_cache (fontset, fcfontmap);
2257
2258 pango_font_description_free (desc: key.desc);
2259 g_free (mem: key.variations);
2260
2261 return g_object_ref (PANGO_FONTSET (fontset));
2262}
2263
2264/**
2265 * pango_fc_font_map_cache_clear:
2266 * @fcfontmap: a `PangoFcFontMap`
2267 *
2268 * Clear all cached information and fontsets for this font map.
2269 *
2270 * This should be called whenever there is a change in the
2271 * output of the default_substitute() virtual function of the
2272 * font map, or if fontconfig has been reinitialized to new
2273 * configuration.
2274 *
2275 * Since: 1.4
2276 */
2277void
2278pango_fc_font_map_cache_clear (PangoFcFontMap *fcfontmap)
2279{
2280 guint removed, added;
2281
2282 if (G_UNLIKELY (fcfontmap->priv->closed))
2283 return;
2284
2285 removed = fcfontmap->priv->n_families;
2286
2287 pango_fc_font_map_fini (fcfontmap);
2288 pango_fc_font_map_init (fcfontmap);
2289
2290 ensure_families (fcfontmap);
2291
2292 added = fcfontmap->priv->n_families;
2293
2294 g_list_model_items_changed (list: G_LIST_MODEL (ptr: fcfontmap), position: 0, removed, added);
2295 if (removed != added)
2296 g_object_notify (G_OBJECT (fcfontmap), property_name: "n-items");
2297
2298 pango_font_map_changed (PANGO_FONT_MAP (fcfontmap));
2299}
2300
2301static void
2302pango_fc_font_map_changed (PangoFontMap *fontmap)
2303{
2304 /* we emit GListModel::changed in pango_fc_font_map_cache_clear() */
2305}
2306
2307/**
2308 * pango_fc_font_map_config_changed:
2309 * @fcfontmap: a `PangoFcFontMap`
2310 *
2311 * Informs font map that the fontconfig configuration (i.e., FcConfig
2312 * object) used by this font map has changed.
2313 *
2314 * This currently calls [method@PangoFc.FontMap.cache_clear] which
2315 * ensures that list of fonts, etc will be regenerated using the
2316 * updated configuration.
2317 *
2318 * Since: 1.38
2319 */
2320void
2321pango_fc_font_map_config_changed (PangoFcFontMap *fcfontmap)
2322{
2323 pango_fc_font_map_cache_clear (fcfontmap);
2324}
2325
2326/**
2327 * pango_fc_font_map_set_config: (skip)
2328 * @fcfontmap: a `PangoFcFontMap`
2329 * @fcconfig: (nullable): a `FcConfig`
2330 *
2331 * Set the `FcConfig` for this font map to use.
2332 *
2333 * The default value
2334 * is %NULL, which causes Fontconfig to use its global "current config".
2335 * You can create a new `FcConfig` object and use this API to attach it
2336 * to a font map.
2337 *
2338 * This is particularly useful for example, if you want to use application
2339 * fonts with Pango. For that, you would create a fresh `FcConfig`, add your
2340 * app fonts to it, and attach it to a new Pango font map.
2341 *
2342 * If @fcconfig is different from the previous config attached to the font map,
2343 * [method@PangoFc.FontMap.config_changed] is called.
2344 *
2345 * This function acquires a reference to the `FcConfig` object; the caller
2346 * does **not** need to retain a reference.
2347 *
2348 * Since: 1.38
2349 */
2350void
2351pango_fc_font_map_set_config (PangoFcFontMap *fcfontmap,
2352 FcConfig *fcconfig)
2353{
2354 FcConfig *oldconfig;
2355
2356 g_return_if_fail (PANGO_IS_FC_FONT_MAP (fcfontmap));
2357
2358 oldconfig = fcfontmap->priv->config;
2359
2360 if (fcconfig)
2361 FcConfigReference (config: fcconfig);
2362
2363 fcfontmap->priv->config = fcconfig;
2364
2365 g_clear_pointer (&fcfontmap->priv->fonts, FcFontSetDestroy);
2366
2367 if (oldconfig != fcconfig)
2368 pango_fc_font_map_config_changed (fcfontmap);
2369
2370 if (oldconfig)
2371 FcConfigDestroy (config: oldconfig);
2372}
2373
2374/**
2375 * pango_fc_font_map_get_config: (skip)
2376 * @fcfontmap: a `PangoFcFontMap`
2377 *
2378 * Fetches the `FcConfig` attached to a font map.
2379 *
2380 * See also: [method@PangoFc.FontMap.set_config].
2381 *
2382 * Returns: (nullable): the `FcConfig` object attached to
2383 * @fcfontmap, which might be %NULL. The return value is
2384 * owned by Pango and should not be freed.
2385 *
2386 * Since: 1.38
2387 */
2388FcConfig *
2389pango_fc_font_map_get_config (PangoFcFontMap *fcfontmap)
2390{
2391 g_return_val_if_fail (PANGO_IS_FC_FONT_MAP (fcfontmap), NULL);
2392
2393 wait_for_fc_init ();
2394
2395 return fcfontmap->priv->config;
2396}
2397
2398static FcFontSet *
2399pango_fc_font_map_get_config_fonts (PangoFcFontMap *fcfontmap)
2400{
2401 if (fcfontmap->priv->fonts == NULL)
2402 {
2403 FcFontSet *sets[2];
2404
2405 wait_for_fc_init ();
2406
2407 sets[0] = FcConfigGetFonts (config: fcfontmap->priv->config, set: 0);
2408 sets[1] = FcConfigGetFonts (config: fcfontmap->priv->config, set: 1);
2409 fcfontmap->priv->fonts = filter_by_format (sets, nsets: 2);
2410 }
2411
2412 return fcfontmap->priv->fonts;
2413}
2414
2415static PangoFcFontFaceData *
2416pango_fc_font_map_get_font_face_data (PangoFcFontMap *fcfontmap,
2417 FcPattern *font_pattern)
2418{
2419 PangoFcFontMapPrivate *priv = fcfontmap->priv;
2420 PangoFcFontFaceData key;
2421 PangoFcFontFaceData *data;
2422
2423 if (FcPatternGetString (p: font_pattern, FC_FILE, n: 0, s: (FcChar8 **)(void*)&key.filename) != FcResultMatch)
2424 return NULL;
2425
2426 if (FcPatternGetInteger (p: font_pattern, FC_INDEX, n: 0, i: &key.id) != FcResultMatch)
2427 return NULL;
2428
2429 data = g_hash_table_lookup (hash_table: priv->font_face_data_hash, key: &key);
2430 if (G_LIKELY (data))
2431 return data;
2432
2433 data = g_slice_new0 (PangoFcFontFaceData);
2434 data->filename = key.filename;
2435 data->id = key.id;
2436
2437 data->pattern = font_pattern;
2438 FcPatternReference (p: data->pattern);
2439
2440 g_hash_table_insert (hash_table: priv->font_face_data_hash, key: data, value: data);
2441
2442 return data;
2443}
2444
2445typedef struct {
2446 PangoCoverage parent_instance;
2447
2448 FcCharSet *charset;
2449} PangoFcCoverage;
2450
2451typedef struct {
2452 PangoCoverageClass parent_class;
2453} PangoFcCoverageClass;
2454
2455GType pango_fc_coverage_get_type (void) G_GNUC_CONST;
2456
2457G_DEFINE_TYPE (PangoFcCoverage, pango_fc_coverage, PANGO_TYPE_COVERAGE)
2458
2459static void
2460pango_fc_coverage_init (PangoFcCoverage *coverage)
2461{
2462}
2463
2464static PangoCoverageLevel
2465pango_fc_coverage_real_get (PangoCoverage *coverage,
2466 int index)
2467{
2468 PangoFcCoverage *fc_coverage = (PangoFcCoverage*)coverage;
2469
2470 return FcCharSetHasChar (fcs: fc_coverage->charset, ucs4: index)
2471 ? PANGO_COVERAGE_EXACT
2472 : PANGO_COVERAGE_NONE;
2473}
2474
2475static void
2476pango_fc_coverage_real_set (PangoCoverage *coverage,
2477 int index,
2478 PangoCoverageLevel level)
2479{
2480 PangoFcCoverage *fc_coverage = (PangoFcCoverage*)coverage;
2481
2482 if (level == PANGO_COVERAGE_NONE)
2483 FcCharSetDelChar (fcs: fc_coverage->charset, ucs4: index);
2484 else
2485 FcCharSetAddChar (fcs: fc_coverage->charset, ucs4: index);
2486}
2487
2488static PangoCoverage *
2489pango_fc_coverage_real_copy (PangoCoverage *coverage)
2490{
2491 PangoFcCoverage *fc_coverage = (PangoFcCoverage*)coverage;
2492 PangoFcCoverage *copy;
2493
2494 copy = g_object_new (object_type: pango_fc_coverage_get_type (), NULL);
2495 copy->charset = FcCharSetCopy (src: fc_coverage->charset);
2496
2497 return (PangoCoverage *)copy;
2498}
2499
2500static void
2501pango_fc_coverage_finalize (GObject *object)
2502{
2503 PangoFcCoverage *fc_coverage = (PangoFcCoverage*)object;
2504
2505 FcCharSetDestroy (fcs: fc_coverage->charset);
2506
2507 G_OBJECT_CLASS (pango_fc_coverage_parent_class)->finalize (object);
2508}
2509
2510static void
2511pango_fc_coverage_class_init (PangoFcCoverageClass *class)
2512{
2513 GObjectClass *object_class = G_OBJECT_CLASS (class);
2514 PangoCoverageClass *coverage_class = PANGO_COVERAGE_CLASS (class);
2515
2516 object_class->finalize = pango_fc_coverage_finalize;
2517
2518 coverage_class->get = pango_fc_coverage_real_get;
2519 coverage_class->set = pango_fc_coverage_real_set;
2520 coverage_class->copy = pango_fc_coverage_real_copy;
2521}
2522
2523PangoCoverage *
2524_pango_fc_font_map_get_coverage (PangoFcFontMap *fcfontmap,
2525 PangoFcFont *fcfont)
2526{
2527 PangoFcFontFaceData *data;
2528 FcCharSet *charset;
2529
2530 data = pango_fc_font_map_get_font_face_data (fcfontmap, font_pattern: fcfont->font_pattern);
2531 if (G_UNLIKELY (!data))
2532 return NULL;
2533
2534 if (G_UNLIKELY (data->coverage == NULL))
2535 {
2536 /*
2537 * Pull the coverage out of the pattern, this
2538 * doesn't require loading the font
2539 */
2540 if (FcPatternGetCharSet (p: fcfont->font_pattern, FC_CHARSET, n: 0, c: &charset) != FcResultMatch)
2541 return NULL;
2542
2543 data->coverage = _pango_fc_font_map_fc_to_coverage (charset);
2544 }
2545
2546 return g_object_ref (data->coverage);
2547}
2548
2549/**
2550 * _pango_fc_font_map_fc_to_coverage:
2551 * @charset: `FcCharSet` to convert to a `PangoCoverage` object.
2552 *
2553 * Convert the given `FcCharSet` into a new `PangoCoverage` object.
2554 *
2555 * The caller is responsible for freeing the newly created object.
2556 *
2557 * Since: 1.6
2558 */
2559PangoCoverage *
2560_pango_fc_font_map_fc_to_coverage (FcCharSet *charset)
2561{
2562 PangoFcCoverage *coverage;
2563
2564 coverage = g_object_new (object_type: pango_fc_coverage_get_type (), NULL);
2565 coverage->charset = FcCharSetCopy (src: charset);
2566
2567 return (PangoCoverage *)coverage;
2568}
2569
2570static PangoLanguage **
2571_pango_fc_font_map_fc_to_languages (FcLangSet *langset)
2572{
2573 FcStrSet *strset;
2574 FcStrList *list;
2575 FcChar8 *s;
2576 GPtrArray *langs;
2577
2578 langs = g_ptr_array_new ();
2579
2580 strset = FcLangSetGetLangs (ls: langset);
2581 list = FcStrListCreate (set: strset);
2582
2583 FcStrListFirst (list);
2584 while ((s = FcStrListNext (list)))
2585 {
2586 PangoLanguage *l = pango_language_from_string (language: (const char *)s);
2587 g_ptr_array_add (array: langs, data: l);
2588 }
2589
2590 FcStrListDone (list);
2591 FcStrSetDestroy (set: strset);
2592
2593 g_ptr_array_add (array: langs, NULL);
2594
2595 return (PangoLanguage **) g_ptr_array_free (array: langs, FALSE);
2596}
2597
2598PangoLanguage **
2599_pango_fc_font_map_get_languages (PangoFcFontMap *fcfontmap,
2600 PangoFcFont *fcfont)
2601{
2602 PangoFcFontFaceData *data;
2603 FcLangSet *langset;
2604
2605 data = pango_fc_font_map_get_font_face_data (fcfontmap, font_pattern: fcfont->font_pattern);
2606 if (G_UNLIKELY (!data))
2607 return NULL;
2608
2609 if (G_UNLIKELY (data->languages == NULL))
2610 {
2611 /*
2612 * Pull the languages out of the pattern, this
2613 * doesn't require loading the font
2614 */
2615 if (FcPatternGetLangSet (p: fcfont->font_pattern, FC_LANG, n: 0, ls: &langset) != FcResultMatch)
2616 return NULL;
2617
2618 data->languages = _pango_fc_font_map_fc_to_languages (langset);
2619 }
2620
2621 return data->languages;
2622}
2623
2624/**
2625 * pango_fc_font_map_create_context:
2626 * @fcfontmap: a `PangoFcFontMap`
2627 *
2628 * Creates a new context for this fontmap.
2629 *
2630 * This function is intended only for backend implementations deriving
2631 * from `PangoFcFontMap`; it is possible that a backend will store
2632 * additional information needed for correct operation on the `PangoContext`
2633 * after calling this function.
2634 *
2635 * Return value: (transfer full): a new `PangoContext`
2636 *
2637 * Since: 1.4
2638 *
2639 * Deprecated: 1.22: Use pango_font_map_create_context() instead.
2640 */
2641PangoContext *
2642pango_fc_font_map_create_context (PangoFcFontMap *fcfontmap)
2643{
2644 g_return_val_if_fail (PANGO_IS_FC_FONT_MAP (fcfontmap), NULL);
2645
2646 return pango_font_map_create_context (PANGO_FONT_MAP (fcfontmap));
2647}
2648
2649static void
2650shutdown_font (gpointer key,
2651 PangoFcFont *fcfont,
2652 PangoFcFontMap *fcfontmap)
2653{
2654 _pango_fc_font_shutdown (fcfont);
2655
2656 _pango_fc_font_set_font_key (fcfont, NULL);
2657 pango_fc_font_key_free (key);
2658}
2659
2660/**
2661 * pango_fc_font_map_shutdown:
2662 * @fcfontmap: a `PangoFcFontMap`
2663 *
2664 * Clears all cached information for the fontmap and marks
2665 * all fonts open for the fontmap as dead.
2666 *
2667 * See the shutdown() virtual function of `PangoFcFont`.
2668 *
2669 * This function might be used by a backend when the underlying
2670 * windowing system for the font map exits. This function is only
2671 * intended to be called only for backend implementations deriving
2672 * from `PangoFcFontMap`.
2673 *
2674 * Since: 1.4
2675 */
2676void
2677pango_fc_font_map_shutdown (PangoFcFontMap *fcfontmap)
2678{
2679 PangoFcFontMapPrivate *priv = fcfontmap->priv;
2680 int i;
2681
2682 if (priv->closed)
2683 return;
2684
2685 g_hash_table_foreach (hash_table: priv->font_hash, func: (GHFunc) shutdown_font, user_data: fcfontmap);
2686 for (i = 0; i < priv->n_families; i++)
2687 priv->families[i]->fontmap = NULL;
2688
2689 pango_fc_font_map_fini (fcfontmap);
2690
2691 while (priv->findfuncs)
2692 {
2693 PangoFcFindFuncInfo *info;
2694 info = priv->findfuncs->data;
2695 if (info->dnotify)
2696 info->dnotify (info->user_data);
2697
2698 g_slice_free (PangoFcFindFuncInfo, info);
2699 priv->findfuncs = g_slist_delete_link (list: priv->findfuncs, link_: priv->findfuncs);
2700 }
2701
2702 priv->closed = TRUE;
2703}
2704
2705static PangoWeight
2706pango_fc_convert_weight_to_pango (double fc_weight)
2707{
2708 return FcWeightToOpenTypeDouble (fc_weight);
2709}
2710
2711static PangoStyle
2712pango_fc_convert_slant_to_pango (int fc_style)
2713{
2714 switch (fc_style)
2715 {
2716 case FC_SLANT_ROMAN:
2717 return PANGO_STYLE_NORMAL;
2718 case FC_SLANT_ITALIC:
2719 return PANGO_STYLE_ITALIC;
2720 case FC_SLANT_OBLIQUE:
2721 return PANGO_STYLE_OBLIQUE;
2722 default:
2723 return PANGO_STYLE_NORMAL;
2724 }
2725}
2726
2727static PangoStretch
2728pango_fc_convert_width_to_pango (int fc_stretch)
2729{
2730 switch (fc_stretch)
2731 {
2732 case FC_WIDTH_NORMAL:
2733 return PANGO_STRETCH_NORMAL;
2734 case FC_WIDTH_ULTRACONDENSED:
2735 return PANGO_STRETCH_ULTRA_CONDENSED;
2736 case FC_WIDTH_EXTRACONDENSED:
2737 return PANGO_STRETCH_EXTRA_CONDENSED;
2738 case FC_WIDTH_CONDENSED:
2739 return PANGO_STRETCH_CONDENSED;
2740 case FC_WIDTH_SEMICONDENSED:
2741 return PANGO_STRETCH_SEMI_CONDENSED;
2742 case FC_WIDTH_SEMIEXPANDED:
2743 return PANGO_STRETCH_SEMI_EXPANDED;
2744 case FC_WIDTH_EXPANDED:
2745 return PANGO_STRETCH_EXPANDED;
2746 case FC_WIDTH_EXTRAEXPANDED:
2747 return PANGO_STRETCH_EXTRA_EXPANDED;
2748 case FC_WIDTH_ULTRAEXPANDED:
2749 return PANGO_STRETCH_ULTRA_EXPANDED;
2750 default:
2751 return PANGO_STRETCH_NORMAL;
2752 }
2753}
2754
2755/**
2756 * pango_fc_font_description_from_pattern:
2757 * @pattern: a `FcPattern`
2758 * @include_size: if %TRUE, the pattern will include the size from
2759 * the @pattern; otherwise the resulting pattern will be unsized.
2760 * (only %FC_SIZE is examined, not %FC_PIXEL_SIZE)
2761 *
2762 * Creates a `PangoFontDescription` that matches the specified
2763 * Fontconfig pattern as closely as possible.
2764 *
2765 * Many possible Fontconfig pattern values, such as %FC_RASTERIZER
2766 * or %FC_DPI, don't make sense in the context of `PangoFontDescription`,
2767 * so will be ignored.
2768 *
2769 * Return value: a new `PangoFontDescription`. Free with
2770 * pango_font_description_free().
2771 *
2772 * Since: 1.4
2773 */
2774PangoFontDescription *
2775pango_fc_font_description_from_pattern (FcPattern *pattern, gboolean include_size)
2776{
2777 return font_description_from_pattern (pattern, include_size, FALSE);
2778}
2779
2780PangoFontDescription *
2781font_description_from_pattern (FcPattern *pattern,
2782 gboolean include_size,
2783 gboolean shallow)
2784{
2785 PangoFontDescription *desc;
2786 PangoStyle style;
2787 PangoWeight weight;
2788 PangoStretch stretch;
2789 double size;
2790 PangoGravity gravity;
2791 PangoVariant variant;
2792 gboolean all_caps;
2793 const char *s;
2794 int i;
2795 double d;
2796 FcResult res;
2797
2798 desc = pango_font_description_new ();
2799
2800 res = FcPatternGetString (p: pattern, FC_FAMILY, n: 0, s: (FcChar8 **) &s);
2801 g_assert (res == FcResultMatch);
2802
2803 if (shallow)
2804 pango_font_description_set_family_static (desc, family: s);
2805 else
2806 pango_font_description_set_family (desc, family: s);
2807
2808 if (FcPatternGetInteger (p: pattern, FC_SLANT, n: 0, i: &i) == FcResultMatch)
2809 style = pango_fc_convert_slant_to_pango (fc_style: i);
2810 else
2811 style = PANGO_STYLE_NORMAL;
2812
2813 pango_font_description_set_style (desc, style);
2814
2815 if (FcPatternGetDouble (p: pattern, FC_WEIGHT, n: 0, d: &d) == FcResultMatch)
2816 weight = pango_fc_convert_weight_to_pango (fc_weight: d);
2817 else
2818 weight = PANGO_WEIGHT_NORMAL;
2819
2820 pango_font_description_set_weight (desc, weight);
2821
2822 if (FcPatternGetInteger (p: pattern, FC_WIDTH, n: 0, i: &i) == FcResultMatch)
2823 stretch = pango_fc_convert_width_to_pango (fc_stretch: i);
2824 else
2825 stretch = PANGO_STRETCH_NORMAL;
2826
2827 pango_font_description_set_stretch (desc, stretch);
2828
2829 variant = PANGO_VARIANT_NORMAL;
2830 all_caps = FALSE;
2831
2832 for (int i = 0; i < 32; i++)
2833 {
2834 if (FcPatternGetString (p: pattern, FC_FONT_FEATURES, n: i, s: (FcChar8 **)&s) == FcResultMatch)
2835 {
2836 if (strcmp (s1: s, s2: "smcp=1") == 0)
2837 {
2838 if (all_caps)
2839 variant = PANGO_VARIANT_ALL_SMALL_CAPS;
2840 else
2841 variant = PANGO_VARIANT_SMALL_CAPS;
2842 }
2843 else if (strcmp (s1: s, s2: "c2sc=1") == 0)
2844 {
2845 if (variant == PANGO_VARIANT_SMALL_CAPS)
2846 variant = PANGO_VARIANT_ALL_SMALL_CAPS;
2847 else
2848 all_caps = TRUE;
2849 }
2850 else if (strcmp (s1: s, s2: "pcap=1") == 0)
2851 {
2852 if (all_caps)
2853 variant = PANGO_VARIANT_ALL_PETITE_CAPS;
2854 else
2855 variant = PANGO_VARIANT_PETITE_CAPS;
2856 }
2857 else if (strcmp (s1: s, s2: "c2pc=1") == 0)
2858 {
2859 if (variant == PANGO_VARIANT_PETITE_CAPS)
2860 variant = PANGO_VARIANT_ALL_PETITE_CAPS;
2861 else
2862 all_caps = TRUE;
2863 }
2864 else if (strcmp (s1: s, s2: "unic=1") == 0)
2865 {
2866 variant = PANGO_VARIANT_UNICASE;
2867 }
2868 else if (strcmp (s1: s, s2: "titl=1") == 0)
2869 {
2870 variant = PANGO_VARIANT_TITLE_CAPS;
2871 }
2872 }
2873 else
2874 break;
2875 }
2876
2877 pango_font_description_set_variant (desc, variant);
2878
2879 if (include_size && FcPatternGetDouble (p: pattern, FC_SIZE, n: 0, d: &size) == FcResultMatch)
2880 {
2881 FcMatrix *fc_matrix;
2882 double scale_factor = 1;
2883 volatile double scaled_size;
2884
2885 if (FcPatternGetMatrix (p: pattern, FC_MATRIX, n: 0, s: &fc_matrix) == FcResultMatch)
2886 {
2887 PangoMatrix mat = PANGO_MATRIX_INIT;
2888
2889 mat.xx = fc_matrix->xx;
2890 mat.xy = fc_matrix->xy;
2891 mat.yx = fc_matrix->yx;
2892 mat.yy = fc_matrix->yy;
2893
2894 scale_factor = pango_matrix_get_font_scale_factor (matrix: &mat);
2895 }
2896
2897 /* We need to use a local variable to ensure that the compiler won't
2898 * implicitly cast it to integer while the result is kept in registers,
2899 * leading to a wrong approximation in i386 (with 387 FPU)
2900 */
2901 scaled_size = scale_factor * size * PANGO_SCALE;
2902 pango_font_description_set_size (desc, size: scaled_size);
2903 }
2904
2905 /* gravity is a bit different. we don't want to set it if it was not set on
2906 * the pattern */
2907 if (FcPatternGetString (p: pattern, PANGO_FC_GRAVITY, n: 0, s: (FcChar8 **)&s) == FcResultMatch)
2908 {
2909 GEnumValue *value = g_enum_get_value_by_nick (enum_class: get_gravity_class (), nick: (char *)s);
2910 gravity = value->value;
2911
2912 pango_font_description_set_gravity (desc, gravity);
2913 }
2914
2915 if (include_size && FcPatternGetString (p: pattern, FC_FONT_VARIATIONS, n: 0, s: (FcChar8 **)&s) == FcResultMatch)
2916 {
2917 if (s && *s)
2918 {
2919 if (shallow)
2920 pango_font_description_set_variations_static (desc, variations: s);
2921 else
2922 pango_font_description_set_variations (desc, variations: s);
2923 }
2924 }
2925
2926 return desc;
2927}
2928
2929/*
2930 * PangoFcFace
2931 */
2932
2933typedef PangoFontFaceClass PangoFcFaceClass;
2934
2935G_DEFINE_TYPE (PangoFcFace, pango_fc_face, PANGO_TYPE_FONT_FACE)
2936
2937static PangoFontDescription *
2938make_alias_description (PangoFcFamily *fcfamily,
2939 gboolean bold,
2940 gboolean italic)
2941{
2942 PangoFontDescription *desc = pango_font_description_new ();
2943
2944 pango_font_description_set_family (desc, family: fcfamily->family_name);
2945 pango_font_description_set_style (desc, style: italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
2946 pango_font_description_set_weight (desc, weight: bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
2947
2948 return desc;
2949}
2950
2951static PangoFontDescription *
2952pango_fc_face_describe (PangoFontFace *face)
2953{
2954 PangoFcFace *fcface = PANGO_FC_FACE (face);
2955 PangoFcFamily *fcfamily = fcface->family;
2956 PangoFontDescription *desc = NULL;
2957
2958 if (G_UNLIKELY (!fcfamily))
2959 return pango_font_description_new ();
2960
2961 if (fcface->fake)
2962 {
2963 if (strcmp (s1: fcface->style, s2: "Regular") == 0)
2964 return make_alias_description (fcfamily, FALSE, FALSE);
2965 else if (strcmp (s1: fcface->style, s2: "Bold") == 0)
2966 return make_alias_description (fcfamily, TRUE, FALSE);
2967 else if (strcmp (s1: fcface->style, s2: "Italic") == 0)
2968 return make_alias_description (fcfamily, FALSE, TRUE);
2969 else /* Bold Italic */
2970 return make_alias_description (fcfamily, TRUE, TRUE);
2971 }
2972
2973 g_assert (fcface->pattern);
2974 desc = pango_fc_font_description_from_pattern (pattern: fcface->pattern, FALSE);
2975
2976 return desc;
2977}
2978
2979static const char *
2980pango_fc_face_get_face_name (PangoFontFace *face)
2981{
2982 PangoFcFace *fcface = PANGO_FC_FACE (face);
2983
2984 return fcface->style;
2985}
2986
2987static int
2988compare_ints (gconstpointer ap,
2989 gconstpointer bp)
2990{
2991 int a = *(int *)ap;
2992 int b = *(int *)bp;
2993
2994 if (a == b)
2995 return 0;
2996 else if (a > b)
2997 return 1;
2998 else
2999 return -1;
3000}
3001
3002static void
3003pango_fc_face_list_sizes (PangoFontFace *face,
3004 int **sizes,
3005 int *n_sizes)
3006{
3007 PangoFcFace *fcface = PANGO_FC_FACE (face);
3008 FcPattern *pattern;
3009 FcFontSet *fontset;
3010 FcObjectSet *objectset;
3011 FcFontSet *fonts;
3012
3013 if (sizes)
3014 *sizes = NULL;
3015 *n_sizes = 0;
3016 if (G_UNLIKELY (!fcface->family || !fcface->family->fontmap))
3017 return;
3018
3019 pattern = FcPatternCreate ();
3020 FcPatternAddString (p: pattern, FC_FAMILY, s: (FcChar8*)(void*)fcface->family->family_name);
3021 FcPatternAddString (p: pattern, FC_STYLE, s: (FcChar8*)(void*)fcface->style);
3022
3023 objectset = FcObjectSetCreate ();
3024 FcObjectSetAdd (os: objectset, FC_PIXEL_SIZE);
3025
3026 fonts = pango_fc_font_map_get_config_fonts (fcfontmap: fcface->family->fontmap);
3027 fontset = FcFontSetList (config: fcface->family->fontmap->priv->config, sets: &fonts, nsets: 1, p: pattern, os: objectset);
3028
3029 if (fontset)
3030 {
3031 GArray *size_array;
3032 double size, dpi = -1.0;
3033 int i, size_i, j;
3034
3035 size_array = g_array_new (FALSE, FALSE, element_size: sizeof (int));
3036
3037 for (i = 0; i < fontset->nfont; i++)
3038 {
3039 for (j = 0;
3040 FcPatternGetDouble (p: fontset->fonts[i], FC_PIXEL_SIZE, n: j, d: &size) == FcResultMatch;
3041 j++)
3042 {
3043 if (dpi < 0)
3044 dpi = pango_fc_font_map_get_resolution (fcfontmap: fcface->family->fontmap, NULL);
3045
3046 size_i = (int) (PANGO_SCALE * size * 72.0 / dpi);
3047 g_array_append_val (size_array, size_i);
3048 }
3049 }
3050
3051 g_array_sort (array: size_array, compare_func: compare_ints);
3052
3053 if (size_array->len == 0)
3054 {
3055 *n_sizes = 0;
3056 if (sizes)
3057 *sizes = NULL;
3058 g_array_free (array: size_array, TRUE);
3059 }
3060 else
3061 {
3062 *n_sizes = size_array->len;
3063 if (sizes)
3064 {
3065 *sizes = (int *) size_array->data;
3066 g_array_free (array: size_array, FALSE);
3067 }
3068 else
3069 g_array_free (array: size_array, TRUE);
3070 }
3071
3072 FcFontSetDestroy (s: fontset);
3073 }
3074 else
3075 {
3076 *n_sizes = 0;
3077 if (sizes)
3078 *sizes = NULL;
3079 }
3080
3081 FcPatternDestroy (p: pattern);
3082 FcObjectSetDestroy (os: objectset);
3083}
3084
3085static gboolean
3086pango_fc_face_is_synthesized (PangoFontFace *face)
3087{
3088 PangoFcFace *fcface = PANGO_FC_FACE (face);
3089
3090 return fcface->fake;
3091}
3092
3093static PangoFontFamily *
3094pango_fc_face_get_family (PangoFontFace *face)
3095{
3096 PangoFcFace *fcface = PANGO_FC_FACE (face);
3097
3098 return PANGO_FONT_FAMILY (fcface->family);
3099}
3100
3101static void
3102pango_fc_face_finalize (GObject *object)
3103{
3104 PangoFcFace *fcface = PANGO_FC_FACE (object);
3105
3106 g_free (mem: fcface->style);
3107 FcPatternDestroy (p: fcface->pattern);
3108
3109 G_OBJECT_CLASS (pango_fc_face_parent_class)->finalize (object);
3110}
3111
3112static void
3113pango_fc_face_init (PangoFcFace *self)
3114{
3115}
3116
3117static void
3118pango_fc_face_class_init (PangoFcFaceClass *class)
3119{
3120 GObjectClass *object_class = G_OBJECT_CLASS (class);
3121
3122 object_class->finalize = pango_fc_face_finalize;
3123
3124 class->describe = pango_fc_face_describe;
3125 class->get_face_name = pango_fc_face_get_face_name;
3126 class->list_sizes = pango_fc_face_list_sizes;
3127 class->is_synthesized = pango_fc_face_is_synthesized;
3128 class->get_family = pango_fc_face_get_family;
3129}
3130
3131
3132/*
3133 * PangoFcFamily
3134 */
3135
3136typedef PangoFontFamilyClass PangoFcFamilyClass;
3137
3138static GType
3139pango_fc_family_get_item_type (GListModel *list)
3140{
3141 return PANGO_TYPE_FONT_FACE;
3142}
3143
3144static void ensure_faces (PangoFcFamily *family);
3145
3146static guint
3147pango_fc_family_get_n_items (GListModel *list)
3148{
3149 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (list);
3150
3151 ensure_faces (family: fcfamily);
3152
3153 return (guint)fcfamily->n_faces;
3154}
3155
3156static gpointer
3157pango_fc_family_get_item (GListModel *list,
3158 guint position)
3159{
3160 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (list);
3161
3162 ensure_faces (family: fcfamily);
3163
3164 if (position < fcfamily->n_faces)
3165 return g_object_ref (fcfamily->faces[position]);
3166
3167 return NULL;
3168}
3169
3170static void
3171pango_fc_family_list_model_init (GListModelInterface *iface)
3172{
3173 iface->get_item_type = pango_fc_family_get_item_type;
3174 iface->get_n_items = pango_fc_family_get_n_items;
3175 iface->get_item = pango_fc_family_get_item;
3176}
3177
3178G_DEFINE_TYPE_WITH_CODE (PangoFcFamily, pango_fc_family, PANGO_TYPE_FONT_FAMILY,
3179 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pango_fc_family_list_model_init))
3180
3181static PangoFcFace *
3182create_face (PangoFcFamily *fcfamily,
3183 const char *style,
3184 FcPattern *pattern,
3185 gboolean fake)
3186{
3187 PangoFcFace *face = g_object_new (PANGO_FC_TYPE_FACE, NULL);
3188 face->style = g_strdup (str: style);
3189 if (pattern)
3190 FcPatternReference (p: pattern);
3191 face->pattern = pattern;
3192 face->family = fcfamily;
3193 face->fake = fake;
3194
3195 return face;
3196}
3197
3198static int
3199compare_face (const void *p1, const void *p2)
3200{
3201 const PangoFcFace *f1 = *(const void **)p1;
3202 const PangoFcFace *f2 = *(const void **)p2;
3203 int w1, w2;
3204 int s1, s2;
3205
3206 if (FcPatternGetInteger (p: f1->pattern, FC_WEIGHT, n: 0, i: &w1) != FcResultMatch)
3207 w1 = FC_WEIGHT_MEDIUM;
3208
3209 if (FcPatternGetInteger (p: f1->pattern, FC_SLANT, n: 0, i: &s1) != FcResultMatch)
3210 s1 = FC_SLANT_ROMAN;
3211
3212 if (FcPatternGetInteger (p: f2->pattern, FC_WEIGHT, n: 0, i: &w2) != FcResultMatch)
3213 w2 = FC_WEIGHT_MEDIUM;
3214
3215 if (FcPatternGetInteger (p: f2->pattern, FC_SLANT, n: 0, i: &s2) != FcResultMatch)
3216 s2 = FC_SLANT_ROMAN;
3217
3218 if (s1 != s2)
3219 return s1 - s2; /* roman < italic < oblique */
3220
3221 return w1 - w2; /* from light to heavy */
3222}
3223
3224static void
3225ensure_faces (PangoFcFamily *fcfamily)
3226{
3227 PangoFcFontMap *fcfontmap = fcfamily->fontmap;
3228 PangoFcFontMapPrivate *priv = fcfontmap->priv;
3229
3230 if (fcfamily->n_faces < 0)
3231 {
3232 FcFontSet *fontset;
3233 int i;
3234
3235 if (is_alias_family (family_name: fcfamily->family_name) || priv->closed)
3236 {
3237 fcfamily->n_faces = 4;
3238 fcfamily->faces = g_new (PangoFcFace *, fcfamily->n_faces);
3239
3240 i = 0;
3241 fcfamily->faces[i++] = create_face (fcfamily, style: "Regular", NULL, TRUE);
3242 fcfamily->faces[i++] = create_face (fcfamily, style: "Bold", NULL, TRUE);
3243 fcfamily->faces[i++] = create_face (fcfamily, style: "Italic", NULL, TRUE);
3244 fcfamily->faces[i++] = create_face (fcfamily, style: "Bold Italic", NULL, TRUE);
3245 fcfamily->faces[0]->regular = 1;
3246 }
3247 else
3248 {
3249 enum {
3250 REGULAR,
3251 ITALIC,
3252 BOLD,
3253 BOLD_ITALIC
3254 };
3255 /* Regular, Italic, Bold, Bold Italic */
3256 gboolean has_face [4] = { FALSE, FALSE, FALSE, FALSE };
3257 PangoFcFace **faces;
3258 gint num = 0;
3259 int regular_weight;
3260 int regular_idx;
3261
3262 fontset = fcfamily->patterns;
3263
3264 /* at most we have 3 additional artificial faces */
3265 faces = g_new (PangoFcFace *, fontset->nfont + 3);
3266
3267 regular_weight = 0;
3268 regular_idx = -1;
3269
3270 for (i = 0; i < fontset->nfont; i++)
3271 {
3272 const char *style, *font_style = NULL;
3273 int weight, slant;
3274
3275 if (FcPatternGetInteger(p: fontset->fonts[i], FC_WEIGHT, n: 0, i: &weight) != FcResultMatch)
3276 weight = FC_WEIGHT_MEDIUM;
3277
3278 if (FcPatternGetInteger(p: fontset->fonts[i], FC_SLANT, n: 0, i: &slant) != FcResultMatch)
3279 slant = FC_SLANT_ROMAN;
3280
3281 {
3282 gboolean variable;
3283 if (FcPatternGetBool(p: fontset->fonts[i], FC_VARIABLE, n: 0, b: &variable) != FcResultMatch)
3284 variable = FALSE;
3285 if (variable) /* skip the variable face */
3286 continue;
3287 }
3288
3289 if (FcPatternGetString (p: fontset->fonts[i], FC_STYLE, n: 0, s: (FcChar8 **)(void*)&font_style) != FcResultMatch)
3290 font_style = NULL;
3291
3292 if (font_style && strcmp (s1: font_style, s2: "Regular") == 0)
3293 {
3294 regular_weight = FC_WEIGHT_MEDIUM;
3295 regular_idx = num;
3296 }
3297
3298 if (weight <= FC_WEIGHT_MEDIUM)
3299 {
3300 if (slant == FC_SLANT_ROMAN)
3301 {
3302 has_face[REGULAR] = TRUE;
3303 style = "Regular";
3304 if (weight > regular_weight)
3305 {
3306 regular_weight = weight;
3307 regular_idx = num;
3308 }
3309 }
3310 else
3311 {
3312 has_face[ITALIC] = TRUE;
3313 style = "Italic";
3314 }
3315 }
3316 else
3317 {
3318 if (slant == FC_SLANT_ROMAN)
3319 {
3320 has_face[BOLD] = TRUE;
3321 style = "Bold";
3322 }
3323 else
3324 {
3325 has_face[BOLD_ITALIC] = TRUE;
3326 style = "Bold Italic";
3327 }
3328 }
3329
3330 if (!font_style)
3331 font_style = style;
3332 faces[num++] = create_face (fcfamily, style: font_style, pattern: fontset->fonts[i], FALSE);
3333 }
3334
3335 if (has_face[REGULAR])
3336 {
3337 if (!has_face[ITALIC])
3338 faces[num++] = create_face (fcfamily, style: "Italic", NULL, TRUE);
3339 if (!has_face[BOLD])
3340 faces[num++] = create_face (fcfamily, style: "Bold", NULL, TRUE);
3341
3342 }
3343 if ((has_face[REGULAR] || has_face[ITALIC] || has_face[BOLD]) && !has_face[BOLD_ITALIC])
3344 faces[num++] = create_face (fcfamily, style: "Bold Italic", NULL, TRUE);
3345
3346 if (regular_idx != -1)
3347 faces[regular_idx]->regular = 1;
3348
3349 faces = g_renew (PangoFcFace *, faces, num);
3350
3351 qsort (base: faces, nmemb: num, size: sizeof (PangoFcFace *), compar: compare_face);
3352
3353 fcfamily->n_faces = num;
3354 fcfamily->faces = faces;
3355 }
3356 }
3357}
3358
3359static void
3360pango_fc_family_list_faces (PangoFontFamily *family,
3361 PangoFontFace ***faces,
3362 int *n_faces)
3363{
3364 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
3365
3366 if (faces)
3367 *faces = NULL;
3368
3369 if (n_faces)
3370 *n_faces = 0;
3371
3372 if (G_UNLIKELY (!fcfamily->fontmap))
3373 return;
3374
3375 ensure_faces (fcfamily);
3376
3377 if (n_faces)
3378 *n_faces = fcfamily->n_faces;
3379
3380 if (faces)
3381 *faces = g_memdup2 (mem: fcfamily->faces, byte_size: fcfamily->n_faces * sizeof (PangoFontFace *));
3382}
3383
3384static PangoFontFace *
3385pango_fc_family_get_face (PangoFontFamily *family,
3386 const char *name)
3387{
3388 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
3389 int i;
3390
3391 ensure_faces (fcfamily);
3392
3393 for (i = 0; i < fcfamily->n_faces; i++)
3394 {
3395 PangoFontFace *face = PANGO_FONT_FACE (fcfamily->faces[i]);
3396
3397 if ((name != NULL && strcmp (s1: name, s2: pango_font_face_get_face_name (face)) == 0) ||
3398 (name == NULL && PANGO_FC_FACE (face)->regular))
3399 return face;
3400 }
3401
3402 return NULL;
3403}
3404
3405static const char *
3406pango_fc_family_get_name (PangoFontFamily *family)
3407{
3408 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
3409
3410 return fcfamily->family_name;
3411}
3412
3413static gboolean
3414pango_fc_family_is_monospace (PangoFontFamily *family)
3415{
3416 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
3417
3418 return fcfamily->spacing == FC_MONO ||
3419 fcfamily->spacing == FC_DUAL ||
3420 fcfamily->spacing == FC_CHARCELL;
3421}
3422
3423static gboolean
3424pango_fc_family_is_variable (PangoFontFamily *family)
3425{
3426 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (family);
3427
3428 return fcfamily->variable;
3429}
3430
3431static void
3432pango_fc_family_finalize (GObject *object)
3433{
3434 int i;
3435 PangoFcFamily *fcfamily = PANGO_FC_FAMILY (object);
3436
3437 g_free (mem: fcfamily->family_name);
3438
3439 for (i = 0; i < fcfamily->n_faces; i++)
3440 {
3441 fcfamily->faces[i]->family = NULL;
3442 g_object_unref (object: fcfamily->faces[i]);
3443 }
3444 FcFontSetDestroy (s: fcfamily->patterns);
3445 g_free (mem: fcfamily->faces);
3446
3447 G_OBJECT_CLASS (pango_fc_family_parent_class)->finalize (object);
3448}
3449
3450static void
3451pango_fc_family_class_init (PangoFcFamilyClass *class)
3452{
3453 GObjectClass *object_class = G_OBJECT_CLASS (class);
3454
3455 object_class->finalize = pango_fc_family_finalize;
3456
3457 class->list_faces = pango_fc_family_list_faces;
3458 class->get_face = pango_fc_family_get_face;
3459 class->get_name = pango_fc_family_get_name;
3460 class->is_monospace = pango_fc_family_is_monospace;
3461 class->is_variable = pango_fc_family_is_variable;
3462}
3463
3464static void
3465pango_fc_family_init (PangoFcFamily *fcfamily)
3466{
3467 fcfamily->n_faces = -1;
3468}
3469
3470/**
3471 * pango_fc_font_map_get_hb_face: (skip)
3472 * @fcfontmap: a `PangoFcFontMap`
3473 * @fcfont: a `PangoFcFont`
3474 *
3475 * Retrieves the `hb_face_t` for the given `PangoFcFont`.
3476 *
3477 * Returns: (transfer none) (nullable): the `hb_face_t`
3478 * for the given font
3479 *
3480 * Since: 1.44
3481 */
3482hb_face_t *
3483pango_fc_font_map_get_hb_face (PangoFcFontMap *fcfontmap,
3484 PangoFcFont *fcfont)
3485{
3486 PangoFcFontFaceData *data;
3487
3488 data = pango_fc_font_map_get_font_face_data (fcfontmap, font_pattern: fcfont->font_pattern);
3489
3490 if (!data->hb_face)
3491 {
3492 hb_blob_t *blob;
3493
3494 blob = hb_blob_create_from_file (file_name: data->filename);
3495 data->hb_face = hb_face_create (blob, index: data->id);
3496 hb_blob_destroy (blob);
3497 }
3498
3499 return data->hb_face;
3500}
3501

source code of gtk/subprojects/pango/pango/pangofc-fontmap.c