1 /* Pango
2  * pangofc-font.c: Shared interfaces for fontconfig-based backends
3  *
4  * Copyright (C) 2003 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 
24 #include "pangofc-font-private.h"
25 #include "pangofc-fontmap.h"
26 #include "pangofc-private.h"
27 #include "pango-layout.h"
28 #include "pango-impl-utils.h"
29 
30 #include <hb-ot.h>
31 
32 enum {
33   PROP_0,
34   PROP_PATTERN,
35   PROP_FONTMAP
36 };
37 
38 typedef struct _PangoFcFontPrivate PangoFcFontPrivate;
39 
40 struct _PangoFcFontPrivate
41 {
42   PangoFcDecoder *decoder;
43   PangoFcFontKey *key;
44 };
45 
46 static gboolean pango_fc_font_real_has_char  (PangoFcFont *font,
47 					      gunichar     wc);
48 static guint    pango_fc_font_real_get_glyph (PangoFcFont *font,
49 					      gunichar     wc);
50 
51 static void                  pango_fc_font_finalize     (GObject          *object);
52 static void                  pango_fc_font_set_property (GObject          *object,
53 							 guint             prop_id,
54 							 const GValue     *value,
55 							 GParamSpec       *pspec);
56 static void                  pango_fc_font_get_property (GObject          *object,
57 							 guint             prop_id,
58 							 GValue           *value,
59 							 GParamSpec       *pspec);
60 static PangoCoverage *       pango_fc_font_get_coverage (PangoFont        *font,
61 							 PangoLanguage    *language);
62 static PangoFontMetrics *    pango_fc_font_get_metrics  (PangoFont        *font,
63 							 PangoLanguage    *language);
64 static PangoFontMap *        pango_fc_font_get_font_map (PangoFont        *font);
65 static PangoFontDescription *pango_fc_font_describe     (PangoFont        *font);
66 static PangoFontDescription *pango_fc_font_describe_absolute (PangoFont        *font);
67 static void                  pango_fc_font_get_features (PangoFont        *font,
68                                                          hb_feature_t     *features,
69                                                          guint             len,
70                                                          guint            *num_features);
71 static hb_font_t *           pango_fc_font_create_hb_font (PangoFont        *font);
72 
73 #define PANGO_FC_FONT_LOCK_FACE(font)	(PANGO_FC_FONT_GET_CLASS (font)->lock_face (font))
74 #define PANGO_FC_FONT_UNLOCK_FACE(font)	(PANGO_FC_FONT_GET_CLASS (font)->unlock_face (font))
75 
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(PangoFcFont,pango_fc_font,PANGO_TYPE_FONT,G_ADD_PRIVATE (PangoFcFont))76 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoFcFont, pango_fc_font, PANGO_TYPE_FONT,
77                                   G_ADD_PRIVATE (PangoFcFont))
78 
79 static void
80 pango_fc_font_class_init (PangoFcFontClass *class)
81 {
82   GObjectClass *object_class = G_OBJECT_CLASS (class);
83   PangoFontClass *font_class = PANGO_FONT_CLASS (class);
84 
85   class->has_char = pango_fc_font_real_has_char;
86   class->get_glyph = pango_fc_font_real_get_glyph;
87   class->get_unknown_glyph = NULL;
88 
89   object_class->finalize = pango_fc_font_finalize;
90   object_class->set_property = pango_fc_font_set_property;
91   object_class->get_property = pango_fc_font_get_property;
92   font_class->describe = pango_fc_font_describe;
93   font_class->describe_absolute = pango_fc_font_describe_absolute;
94   font_class->get_coverage = pango_fc_font_get_coverage;
95   font_class->get_metrics = pango_fc_font_get_metrics;
96   font_class->get_font_map = pango_fc_font_get_font_map;
97   font_class->get_features = pango_fc_font_get_features;
98   font_class->create_hb_font = pango_fc_font_create_hb_font;
99   font_class->get_features = pango_fc_font_get_features;
100 
101   /**
102    * PangoFcFont:pattern:
103    *
104    * The fontconfig pattern for this font.
105    */
106   g_object_class_install_property (object_class, PROP_PATTERN,
107 				   g_param_spec_pointer ("pattern",
108 							 "Pattern",
109 							 "The fontconfig pattern for this font",
110 							 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
111 							 G_PARAM_STATIC_STRINGS));
112 
113   /**
114    * PangoFcFont:fontmap:
115    *
116    * The PangoFc font map this font is associated with.
117    */
118   g_object_class_install_property (object_class, PROP_FONTMAP,
119 				   g_param_spec_object ("fontmap",
120 							"Font Map",
121 							"The PangoFc font map this font is associated with (Since: 1.26)",
122 							PANGO_TYPE_FC_FONT_MAP,
123 							G_PARAM_READWRITE |
124 							G_PARAM_STATIC_STRINGS));
125 }
126 
127 static void
pango_fc_font_init(PangoFcFont * fcfont)128 pango_fc_font_init (PangoFcFont *fcfont)
129 {
130   fcfont->priv = pango_fc_font_get_instance_private (fcfont);
131 }
132 
133 static void
free_metrics_info(PangoFcMetricsInfo * info)134 free_metrics_info (PangoFcMetricsInfo *info)
135 {
136   pango_font_metrics_unref (info->metrics);
137   g_slice_free (PangoFcMetricsInfo, info);
138 }
139 
140 static void
pango_fc_font_finalize(GObject * object)141 pango_fc_font_finalize (GObject *object)
142 {
143   PangoFcFont *fcfont = PANGO_FC_FONT (object);
144   PangoFcFontPrivate *priv = fcfont->priv;
145   PangoFcFontMap *fontmap;
146 
147   g_slist_foreach (fcfont->metrics_by_lang, (GFunc)free_metrics_info, NULL);
148   g_slist_free (fcfont->metrics_by_lang);
149 
150   fontmap = g_weak_ref_get ((GWeakRef *) &fcfont->fontmap);
151   if (fontmap)
152     {
153       _pango_fc_font_map_remove (PANGO_FC_FONT_MAP (fcfont->fontmap), fcfont);
154       g_weak_ref_clear ((GWeakRef *) &fcfont->fontmap);
155       g_object_unref (fontmap);
156     }
157 
158   FcPatternDestroy (fcfont->font_pattern);
159   pango_font_description_free (fcfont->description);
160 
161   if (priv->decoder)
162     _pango_fc_font_set_decoder (fcfont, NULL);
163 
164   G_OBJECT_CLASS (pango_fc_font_parent_class)->finalize (object);
165 }
166 
167 static gboolean
pattern_is_hinted(FcPattern * pattern)168 pattern_is_hinted (FcPattern *pattern)
169 {
170   FcBool hinting;
171 
172   if (FcPatternGetBool (pattern,
173 			FC_HINTING, 0, &hinting) != FcResultMatch)
174     hinting = FcTrue;
175 
176   return hinting;
177 }
178 
179 static gboolean
pattern_is_transformed(FcPattern * pattern)180 pattern_is_transformed (FcPattern *pattern)
181 {
182   FcMatrix *fc_matrix;
183 
184   if (FcPatternGetMatrix (pattern, FC_MATRIX, 0, &fc_matrix) == FcResultMatch)
185     {
186       return fc_matrix->xx != 1 || fc_matrix->xy != 0 ||
187              fc_matrix->yx != 0 || fc_matrix->yy != 1;
188     }
189   else
190     return FALSE;
191 }
192 
193 static void
pango_fc_font_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)194 pango_fc_font_set_property (GObject       *object,
195 			    guint          prop_id,
196 			    const GValue  *value,
197 			    GParamSpec    *pspec)
198 {
199   PangoFcFont *fcfont = PANGO_FC_FONT (object);
200 
201   switch (prop_id)
202     {
203     case PROP_PATTERN:
204       {
205 	FcPattern *pattern = g_value_get_pointer (value);
206 
207 	g_return_if_fail (pattern != NULL);
208 	g_return_if_fail (fcfont->font_pattern == NULL);
209 
210 	FcPatternReference (pattern);
211 	fcfont->font_pattern = pattern;
212 	fcfont->description = pango_fc_font_description_from_pattern (pattern, TRUE);
213 	fcfont->is_hinted = pattern_is_hinted (pattern);
214 	fcfont->is_transformed = pattern_is_transformed (pattern);
215       }
216       goto set_decoder;
217 
218     case PROP_FONTMAP:
219       {
220 	PangoFcFontMap *fcfontmap = PANGO_FC_FONT_MAP (g_value_get_object (value));
221 
222 	g_return_if_fail (fcfont->fontmap == NULL);
223 	g_weak_ref_set ((GWeakRef *) &fcfont->fontmap, fcfontmap);
224       }
225       goto set_decoder;
226 
227     default:
228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
229       return;
230     }
231 
232 set_decoder:
233   /* set decoder if both pattern and fontmap are set now */
234   if (fcfont->font_pattern && fcfont->fontmap)
235     _pango_fc_font_set_decoder (fcfont,
236 				pango_fc_font_map_find_decoder  ((PangoFcFontMap *) fcfont->fontmap,
237 								 fcfont->font_pattern));
238 }
239 
240 static void
pango_fc_font_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)241 pango_fc_font_get_property (GObject       *object,
242 			    guint          prop_id,
243 			    GValue        *value,
244 			    GParamSpec    *pspec)
245 {
246   switch (prop_id)
247     {
248     case PROP_PATTERN:
249       {
250 	PangoFcFont *fcfont = PANGO_FC_FONT (object);
251 	g_value_set_pointer (value, fcfont->font_pattern);
252       }
253       break;
254     case PROP_FONTMAP:
255       {
256 	PangoFcFont *fcfont = PANGO_FC_FONT (object);
257 	PangoFontMap *fontmap = g_weak_ref_get ((GWeakRef *) &fcfont->fontmap);
258 	g_value_take_object (value, fontmap);
259       }
260       break;
261     default:
262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
263       break;
264     }
265 }
266 
267 static PangoFontDescription *
pango_fc_font_describe(PangoFont * font)268 pango_fc_font_describe (PangoFont *font)
269 {
270   PangoFcFont *fcfont = (PangoFcFont *)font;
271 
272   return pango_font_description_copy (fcfont->description);
273 }
274 
275 static PangoFontDescription *
pango_fc_font_describe_absolute(PangoFont * font)276 pango_fc_font_describe_absolute (PangoFont *font)
277 {
278   PangoFcFont *fcfont = (PangoFcFont *)font;
279   PangoFontDescription *desc = pango_font_description_copy (fcfont->description);
280   double size;
281 
282   if (FcPatternGetDouble (fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
283     pango_font_description_set_absolute_size (desc, size * PANGO_SCALE);
284 
285   return desc;
286 }
287 
288 static PangoCoverage *
pango_fc_font_get_coverage(PangoFont * font,PangoLanguage * language G_GNUC_UNUSED)289 pango_fc_font_get_coverage (PangoFont     *font,
290 			    PangoLanguage *language G_GNUC_UNUSED)
291 {
292   PangoFcFont *fcfont = (PangoFcFont *)font;
293   PangoFcFontPrivate *priv = fcfont->priv;
294   FcCharSet *charset;
295   PangoFcFontMap *fontmap;
296   PangoCoverage *coverage;
297 
298   if (priv->decoder)
299     {
300       charset = pango_fc_decoder_get_charset (priv->decoder, fcfont);
301       return _pango_fc_font_map_fc_to_coverage (charset);
302     }
303 
304   fontmap = g_weak_ref_get ((GWeakRef *) &fcfont->fontmap);
305   if (!fontmap)
306     return pango_coverage_new ();
307 
308   coverage = _pango_fc_font_map_get_coverage (fontmap, fcfont);
309   g_object_unref (fontmap);
310   return coverage;
311 }
312 
313 /* For Xft, it would be slightly more efficient to simply to
314  * call Xft, and also more robust against changes in Xft.
315  * But for now, we simply use the same code for all backends.
316  *
317  * The code in this function is partly based on code from Xft,
318  * Copyright 2000 Keith Packard
319  */
320 static void
get_face_metrics(PangoFcFont * fcfont,PangoFontMetrics * metrics)321 get_face_metrics (PangoFcFont      *fcfont,
322 		  PangoFontMetrics *metrics)
323 {
324   hb_font_t *hb_font = pango_font_get_hb_font (PANGO_FONT (fcfont));
325   hb_font_extents_t extents;
326 
327   FcMatrix *fc_matrix;
328   gboolean have_transform = FALSE;
329 
330   hb_font_get_extents_for_direction (hb_font, HB_DIRECTION_LTR, &extents);
331 
332   if  (FcPatternGetMatrix (fcfont->font_pattern,
333 			   FC_MATRIX, 0, &fc_matrix) == FcResultMatch)
334     {
335       have_transform = (fc_matrix->xx != 1 || fc_matrix->xy != 0 ||
336 			fc_matrix->yx != 0 || fc_matrix->yy != 1);
337     }
338 
339   if (have_transform)
340     {
341       metrics->descent =  - extents.descender * fc_matrix->yy;
342       metrics->ascent = extents.ascender * fc_matrix->yy;
343       metrics->height = (extents.ascender - extents.descender + extents.line_gap) * fc_matrix->yy;
344     }
345   else
346     {
347       metrics->descent = - extents.descender;
348       metrics->ascent = extents.ascender;
349       metrics->height = extents.ascender - extents.descender + extents.line_gap;
350     }
351 
352   metrics->underline_thickness = PANGO_SCALE;
353   metrics->underline_position = - PANGO_SCALE;
354   metrics->strikethrough_thickness = PANGO_SCALE;
355   metrics->strikethrough_position = metrics->ascent / 2;
356 
357   /* FIXME: use the right hb version */
358 #if HB_VERSION_ATLEAST(2,5,4)
359   hb_position_t position;
360 
361   if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_UNDERLINE_SIZE, &position))
362     metrics->underline_thickness = position;
363 
364   if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_UNDERLINE_OFFSET, &position))
365     metrics->underline_position = position;
366 
367   if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_STRIKEOUT_SIZE, &position))
368     metrics->strikethrough_thickness = position;
369 
370   if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, &position))
371     metrics->strikethrough_position = position;
372 #endif
373 }
374 
375 PangoFontMetrics *
pango_fc_font_create_base_metrics_for_context(PangoFcFont * fcfont,PangoContext * context)376 pango_fc_font_create_base_metrics_for_context (PangoFcFont   *fcfont,
377 					       PangoContext  *context)
378 {
379   PangoFontMetrics *metrics;
380   metrics = pango_font_metrics_new ();
381 
382   get_face_metrics (fcfont, metrics);
383 
384   return metrics;
385 }
386 
387 static int
max_glyph_width(PangoLayout * layout)388 max_glyph_width (PangoLayout *layout)
389 {
390   int max_width = 0;
391   GSList *l, *r;
392 
393   for (l = pango_layout_get_lines_readonly (layout); l; l = l->next)
394     {
395       PangoLayoutLine *line = l->data;
396 
397       for (r = line->runs; r; r = r->next)
398 	{
399 	  PangoGlyphString *glyphs = ((PangoGlyphItem *)r->data)->glyphs;
400 	  int i;
401 
402 	  for (i = 0; i < glyphs->num_glyphs; i++)
403 	    if (glyphs->glyphs[i].geometry.width > max_width)
404 	      max_width = glyphs->glyphs[i].geometry.width;
405 	}
406     }
407 
408   return max_width;
409 }
410 
411 static PangoFontMetrics *
pango_fc_font_get_metrics(PangoFont * font,PangoLanguage * language)412 pango_fc_font_get_metrics (PangoFont     *font,
413 			   PangoLanguage *language)
414 {
415   PangoFcFont *fcfont = PANGO_FC_FONT (font);
416   PangoFcMetricsInfo *info = NULL; /* Quiet gcc */
417   GSList *tmp_list;
418   static int in_get_metrics;
419 
420   const char *sample_str = pango_language_get_sample_string (language);
421 
422   tmp_list = fcfont->metrics_by_lang;
423   while (tmp_list)
424     {
425       info = tmp_list->data;
426 
427       if (info->sample_str == sample_str)    /* We _don't_ need strcmp */
428 	break;
429 
430       tmp_list = tmp_list->next;
431     }
432 
433   if (!tmp_list)
434     {
435       PangoFontMap *fontmap;
436       PangoContext *context;
437 
438       fontmap = g_weak_ref_get ((GWeakRef *) &fcfont->fontmap);
439       if (!fontmap)
440 	return pango_font_metrics_new ();
441 
442       info = g_slice_new0 (PangoFcMetricsInfo);
443 
444       /* Note: we need to add info to the list before calling
445        * into PangoLayout below, to prevent recursion
446        */
447       fcfont->metrics_by_lang = g_slist_prepend (fcfont->metrics_by_lang,
448 						 info);
449 
450       info->sample_str = sample_str;
451 
452       context = pango_font_map_create_context (fontmap);
453       pango_context_set_language (context, language);
454 
455       info->metrics = pango_fc_font_create_base_metrics_for_context (fcfont, context);
456 
457       if (!in_get_metrics)
458         {
459           /* Compute derived metrics */
460           PangoLayout *layout;
461           PangoRectangle extents;
462           const char *sample_str = pango_language_get_sample_string (language);
463           PangoFontDescription *desc = pango_font_describe_with_absolute_size (font);
464           gulong sample_str_width;
465 
466           in_get_metrics = 1;
467 
468           layout = pango_layout_new (context);
469           pango_layout_set_font_description (layout, desc);
470           pango_font_description_free (desc);
471 
472           pango_layout_set_text (layout, sample_str, -1);
473           pango_layout_get_extents (layout, NULL, &extents);
474 
475           sample_str_width = pango_utf8_strwidth (sample_str);
476           g_assert (sample_str_width > 0);
477           info->metrics->approximate_char_width = extents.width / sample_str_width;
478 
479           pango_layout_set_text (layout, "0123456789", -1);
480           info->metrics->approximate_digit_width = max_glyph_width (layout);
481 
482           g_object_unref (layout);
483 
484           in_get_metrics = 0;
485         }
486 
487       g_object_unref (context);
488       g_object_unref (fontmap);
489     }
490 
491   return pango_font_metrics_ref (info->metrics);
492 }
493 
494 static PangoFontMap *
pango_fc_font_get_font_map(PangoFont * font)495 pango_fc_font_get_font_map (PangoFont *font)
496 {
497   PangoFcFont *fcfont = PANGO_FC_FONT (font);
498 
499   /* MT-unsafe.  Oh well...  The API is unsafe. */
500   return fcfont->fontmap;
501 }
502 
503 static gboolean
pango_fc_font_real_has_char(PangoFcFont * font,gunichar wc)504 pango_fc_font_real_has_char (PangoFcFont *font,
505 			     gunichar     wc)
506 {
507   FcCharSet *charset;
508 
509   if (FcPatternGetCharSet (font->font_pattern,
510 			   FC_CHARSET, 0, &charset) != FcResultMatch)
511     return FALSE;
512 
513   return FcCharSetHasChar (charset, wc);
514 }
515 
516 static guint
pango_fc_font_real_get_glyph(PangoFcFont * font,gunichar wc)517 pango_fc_font_real_get_glyph (PangoFcFont *font,
518 			      gunichar     wc)
519 {
520   hb_font_t *hb_font = pango_font_get_hb_font (PANGO_FONT (font));
521   hb_codepoint_t glyph = PANGO_GET_UNKNOWN_GLYPH (wc);
522 
523   hb_font_get_nominal_glyph (hb_font, wc, &glyph);
524 
525   return glyph;
526 }
527 
528 /**
529  * pango_fc_font_lock_face: (skip)
530  * @font: a `PangoFcFont`.
531  *
532  * Gets the FreeType `FT_Face` associated with a font.
533  *
534  * This face will be kept around until you call
535  * [method@PangoFc.Font.unlock_face].
536  *
537  * Return value: the FreeType `FT_Face` associated with @font.
538  *
539  * Since: 1.4
540  * Deprecated: 1.44: Use pango_font_get_hb_font() instead
541  */
542 FT_Face
pango_fc_font_lock_face(PangoFcFont * font)543 pango_fc_font_lock_face (PangoFcFont *font)
544 {
545   g_return_val_if_fail (PANGO_IS_FC_FONT (font), NULL);
546 
547   return PANGO_FC_FONT_LOCK_FACE (font);
548 }
549 
550 /**
551  * pango_fc_font_unlock_face:
552  * @font: a `PangoFcFont`.
553  *
554  * Releases a font previously obtained with
555  * [method@PangoFc.Font.lock_face].
556  *
557  * Since: 1.4
558  * Deprecated: 1.44: Use pango_font_get_hb_font() instead
559  */
560 void
pango_fc_font_unlock_face(PangoFcFont * font)561 pango_fc_font_unlock_face (PangoFcFont *font)
562 {
563   g_return_if_fail (PANGO_IS_FC_FONT (font));
564 
565   PANGO_FC_FONT_UNLOCK_FACE (font);
566 }
567 
568 /**
569  * pango_fc_font_has_char:
570  * @font: a `PangoFcFont`
571  * @wc: Unicode codepoint to look up
572  *
573  * Determines whether @font has a glyph for the codepoint @wc.
574  *
575  * Return value: %TRUE if @font has the requested codepoint.
576  *
577  * Since: 1.4
578  * Deprecated: 1.44: Use [method@Pango.Font.has_char]
579  */
580 gboolean
pango_fc_font_has_char(PangoFcFont * font,gunichar wc)581 pango_fc_font_has_char (PangoFcFont *font,
582 			gunichar     wc)
583 {
584   PangoFcFontPrivate *priv = font->priv;
585   FcCharSet *charset;
586 
587   g_return_val_if_fail (PANGO_IS_FC_FONT (font), FALSE);
588 
589   if (priv->decoder)
590     {
591       charset = pango_fc_decoder_get_charset (priv->decoder, font);
592       return FcCharSetHasChar (charset, wc);
593     }
594 
595   return PANGO_FC_FONT_GET_CLASS (font)->has_char (font, wc);
596 }
597 
598 /**
599  * pango_fc_font_get_glyph:
600  * @font: a `PangoFcFont`
601  * @wc: Unicode character to look up
602  *
603  * Gets the glyph index for a given Unicode character
604  * for @font.
605  *
606  * If you only want to determine whether the font has
607  * the glyph, use [method@PangoFc.Font.has_char].
608  *
609  * Return value: the glyph index, or 0, if the Unicode
610  *   character doesn't exist in the font.
611  *
612  * Since: 1.4
613  */
614 PangoGlyph
pango_fc_font_get_glyph(PangoFcFont * font,gunichar wc)615 pango_fc_font_get_glyph (PangoFcFont *font,
616 			 gunichar     wc)
617 {
618   PangoFcFontPrivate *priv = font->priv;
619 
620   /* Replace NBSP with a normal space; it should be invariant that
621    * they shape the same other than breaking properties.
622    */
623   if (wc == 0xA0)
624 	  wc = 0x20;
625 
626   if (priv->decoder)
627     return pango_fc_decoder_get_glyph (priv->decoder, font, wc);
628 
629   return PANGO_FC_FONT_GET_CLASS (font)->get_glyph (font, wc);
630 }
631 
632 
633 /**
634  * pango_fc_font_get_unknown_glyph:
635  * @font: a `PangoFcFont`
636  * @wc: the Unicode character for which a glyph is needed.
637  *
638  * Returns the index of a glyph suitable for drawing @wc
639  * as an unknown character.
640  *
641  * Use PANGO_GET_UNKNOWN_GLYPH() instead.
642  *
643  * Return value: a glyph index into @font.
644  *
645  * Since: 1.4
646  */
647 PangoGlyph
pango_fc_font_get_unknown_glyph(PangoFcFont * font,gunichar wc)648 pango_fc_font_get_unknown_glyph (PangoFcFont *font,
649 				 gunichar     wc)
650 {
651   if (font && PANGO_FC_FONT_GET_CLASS (font)->get_unknown_glyph)
652     return PANGO_FC_FONT_GET_CLASS (font)->get_unknown_glyph (font, wc);
653 
654   return PANGO_GET_UNKNOWN_GLYPH (wc);
655 }
656 
657 void
_pango_fc_font_shutdown(PangoFcFont * font)658 _pango_fc_font_shutdown (PangoFcFont *font)
659 {
660   g_return_if_fail (PANGO_IS_FC_FONT (font));
661 
662   if (PANGO_FC_FONT_GET_CLASS (font)->shutdown)
663     PANGO_FC_FONT_GET_CLASS (font)->shutdown (font);
664 }
665 
666 /**
667  * pango_fc_font_kern_glyphs:
668  * @font: a `PangoFcFont`
669  * @glyphs: a `PangoGlyphString`
670  *
671  * This function used to adjust each adjacent pair of glyphs
672  * in @glyphs according to kerning information in @font.
673  *
674  * Since 1.44, it does nothing.
675  *
676  * Since: 1.4
677  * Deprecated: 1.32
678  */
679 void
pango_fc_font_kern_glyphs(PangoFcFont * font,PangoGlyphString * glyphs)680 pango_fc_font_kern_glyphs (PangoFcFont      *font,
681 			   PangoGlyphString *glyphs)
682 {
683 }
684 
685 /**
686  * _pango_fc_font_get_decoder:
687  * @font: a `PangoFcFont`
688  *
689  * This will return any custom decoder set on this font.
690  *
691  * Return value: The custom decoder
692  *
693  * Since: 1.6
694  */
695 PangoFcDecoder *
_pango_fc_font_get_decoder(PangoFcFont * font)696 _pango_fc_font_get_decoder (PangoFcFont *font)
697 {
698   PangoFcFontPrivate *priv = font->priv;
699 
700   return priv->decoder;
701 }
702 
703 /**
704  * _pango_fc_font_set_decoder:
705  * @font: a `PangoFcFont`
706  * @decoder: a `PangoFcDecoder` to set for this font
707  *
708  * This sets a custom decoder for this font.
709  *
710  * Any previous decoder will be released before this one is set.
711  *
712  * Since: 1.6
713  */
714 void
_pango_fc_font_set_decoder(PangoFcFont * font,PangoFcDecoder * decoder)715 _pango_fc_font_set_decoder (PangoFcFont    *font,
716 			    PangoFcDecoder *decoder)
717 {
718   PangoFcFontPrivate *priv = font->priv;
719 
720   if (priv->decoder)
721     g_object_unref (priv->decoder);
722 
723   priv->decoder = decoder;
724 
725   if (priv->decoder)
726     g_object_ref (priv->decoder);
727 }
728 
729 PangoFcFontKey *
_pango_fc_font_get_font_key(PangoFcFont * fcfont)730 _pango_fc_font_get_font_key (PangoFcFont *fcfont)
731 {
732   PangoFcFontPrivate *priv = fcfont->priv;
733 
734   return priv->key;
735 }
736 
737 void
_pango_fc_font_set_font_key(PangoFcFont * fcfont,PangoFcFontKey * key)738 _pango_fc_font_set_font_key (PangoFcFont    *fcfont,
739 			     PangoFcFontKey *key)
740 {
741   PangoFcFontPrivate *priv = fcfont->priv;
742 
743   priv->key = key;
744 }
745 
746 /**
747  * pango_fc_font_get_raw_extents:
748  * @fcfont: a `PangoFcFont`
749  * @glyph: the glyph index to load
750  * @ink_rect: (out) (optional): location to store ink extents of the
751  *   glyph
752  * @logical_rect: (out) (optional): location to store logical extents
753  *   of the glyph
754  *
755  * Gets the extents of a single glyph from a font.
756  *
757  * The extents are in user space; that is, they are not transformed
758  * by any matrix in effect for the font.
759  *
760  * Long term, this functionality probably belongs in the default
761  * implementation of the get_glyph_extents() virtual function.
762  * The other possibility would be to to make it public in something
763  * like it's current form, and also expose glyph information
764  * caching functionality similar to pango_ft2_font_set_glyph_info().
765  *
766  * Since: 1.6
767  */
768 void
pango_fc_font_get_raw_extents(PangoFcFont * fcfont,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)769 pango_fc_font_get_raw_extents (PangoFcFont    *fcfont,
770 			       PangoGlyph      glyph,
771 			       PangoRectangle *ink_rect,
772 			       PangoRectangle *logical_rect)
773 {
774   g_return_if_fail (PANGO_IS_FC_FONT (fcfont));
775 
776   if (glyph == PANGO_GLYPH_EMPTY)
777     {
778       if (ink_rect)
779 	{
780 	  ink_rect->x = 0;
781 	  ink_rect->width = 0;
782 	  ink_rect->y = 0;
783 	  ink_rect->height = 0;
784 	}
785 
786       if (logical_rect)
787 	{
788 	  logical_rect->x = 0;
789 	  logical_rect->width = 0;
790 	  logical_rect->y = 0;
791 	  logical_rect->height = 0;
792 	}
793     }
794   else
795     {
796       hb_font_t *hb_font = pango_font_get_hb_font (PANGO_FONT (fcfont));
797       hb_glyph_extents_t extents;
798       hb_font_extents_t font_extents;
799 
800       hb_font_get_glyph_extents (hb_font, glyph, &extents);
801       hb_font_get_extents_for_direction (hb_font, HB_DIRECTION_LTR, &font_extents);
802 
803       if (ink_rect)
804 	{
805 	  ink_rect->x = extents.x_bearing;
806 	  ink_rect->width = extents.width;
807 	  ink_rect->y = -extents.y_bearing;
808 	  ink_rect->height = -extents.height;
809 	}
810 
811       if (logical_rect)
812 	{
813           hb_position_t x, y;
814 
815           hb_font_get_glyph_advance_for_direction (hb_font,
816                                                    glyph,
817                                                    HB_DIRECTION_LTR,
818                                                    &x, &y);
819 
820 	  logical_rect->x = 0;
821 	  logical_rect->width = x;
822 	  logical_rect->y = - font_extents.ascender;
823 	  logical_rect->height = font_extents.ascender - font_extents.descender;
824 	}
825     }
826 }
827 
828 static void
pango_fc_font_get_features(PangoFont * font,hb_feature_t * features,guint len,guint * num_features)829 pango_fc_font_get_features (PangoFont    *font,
830                             hb_feature_t *features,
831                             guint         len,
832                             guint        *num_features)
833 {
834   /* Setup features from fontconfig pattern. */
835   PangoFcFont *fc_font = PANGO_FC_FONT (font);
836   if (fc_font->font_pattern)
837     {
838       char *s;
839       while (*num_features < len &&
840              FcResultMatch == FcPatternGetString (fc_font->font_pattern,
841                                                   PANGO_FC_FONT_FEATURES,
842                                                   *num_features,
843                                                   (FcChar8 **) &s))
844         {
845           gboolean ret = hb_feature_from_string (s, -1, &features[*num_features]);
846           features[*num_features].start = 0;
847           features[*num_features].end   = (unsigned int) -1;
848           if (ret)
849             (*num_features)++;
850         }
851     }
852 }
853 
854 extern gpointer get_gravity_class (void);
855 
856 static PangoGravity
pango_fc_font_key_get_gravity(PangoFcFontKey * key)857 pango_fc_font_key_get_gravity (PangoFcFontKey *key)
858 {
859   const FcPattern *pattern;
860   PangoGravity gravity = PANGO_GRAVITY_SOUTH;
861   FcChar8 *s;
862 
863   pattern = pango_fc_font_key_get_pattern (key);
864   if (FcPatternGetString (pattern, PANGO_FC_GRAVITY, 0, (FcChar8 **)&s) == FcResultMatch)
865     {
866       GEnumValue *value = g_enum_get_value_by_nick (get_gravity_class (), (char *)s);
867       gravity = value->value;
868     }
869 
870   return gravity;
871 }
872 
873 static double
get_font_size(PangoFcFontKey * key)874 get_font_size (PangoFcFontKey *key)
875 {
876   const FcPattern *pattern;
877   double size;
878   double dpi;
879 
880   pattern = pango_fc_font_key_get_pattern (key);
881   if (FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
882     return size;
883 
884   /* Just in case FC_PIXEL_SIZE got unset between pango_fc_make_pattern()
885    * and here.  That would be very weird.
886    */
887 
888   if (FcPatternGetDouble (pattern, FC_DPI, 0, &dpi) != FcResultMatch)
889     dpi = 72;
890 
891   if (FcPatternGetDouble (pattern, FC_SIZE, 0, &size) == FcResultMatch)
892     return size * dpi / 72.;
893 
894   /* Whatever */
895   return 18.;
896 }
897 
898 static void
parse_variations(const char * variations,hb_ot_var_axis_info_t * axes,int n_axes,float * coords)899 parse_variations (const char            *variations,
900                   hb_ot_var_axis_info_t *axes,
901                   int                    n_axes,
902                   float                 *coords)
903 {
904   const char *p;
905   const char *end;
906   hb_variation_t var;
907   int i;
908 
909   p = variations;
910   while (p && *p)
911     {
912       end = strchr (p, ',');
913       if (hb_variation_from_string (p, end ? end - p: -1, &var))
914         {
915           for (i = 0; i < n_axes; i++)
916             {
917               if (axes[i].tag == var.tag)
918                 {
919                   coords[axes[i].axis_index] = var.value;
920                   break;
921                 }
922             }
923         }
924 
925       p = end ? end + 1 : NULL;
926     }
927 }
928 
929 static hb_font_t *
pango_fc_font_create_hb_font(PangoFont * font)930 pango_fc_font_create_hb_font (PangoFont *font)
931 {
932   PangoFcFont *fc_font = PANGO_FC_FONT (font);
933   PangoFcFontKey *key;
934   hb_face_t *hb_face;
935   hb_font_t *hb_font;
936   double x_scale_inv, y_scale_inv;
937   double x_scale, y_scale;
938   double size;
939 
940   x_scale_inv = y_scale_inv = 1.0;
941   size = 1.0;
942 
943   key = _pango_fc_font_get_font_key (fc_font);
944   if (key)
945     {
946       const FcPattern *pattern = pango_fc_font_key_get_pattern (key);
947       const PangoMatrix *matrix;
948       PangoMatrix matrix2;
949       PangoGravity gravity;
950       FcMatrix fc_matrix, *fc_matrix_val;
951       double x, y;
952       int i;
953 
954       matrix = pango_fc_font_key_get_matrix (key);
955       pango_matrix_get_font_scale_factors (matrix, &x_scale_inv, &y_scale_inv);
956 
957       FcMatrixInit (&fc_matrix);
958       for (i = 0; FcPatternGetMatrix (pattern, FC_MATRIX, i, &fc_matrix_val) == FcResultMatch; i++)
959         FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val);
960 
961       matrix2.xx = fc_matrix.xx;
962       matrix2.yx = fc_matrix.yx;
963       matrix2.xy = fc_matrix.xy;
964       matrix2.yy = fc_matrix.yy;
965       pango_matrix_get_font_scale_factors (&matrix2, &x, &y);
966 
967       x_scale_inv /= x;
968       y_scale_inv /= y;
969 
970       gravity = pango_fc_font_key_get_gravity (key);
971       if (PANGO_GRAVITY_IS_IMPROPER (gravity))
972         {
973           x_scale_inv = -x_scale_inv;
974           y_scale_inv = -y_scale_inv;
975         }
976       size = get_font_size (key);
977     }
978 
979   x_scale = 1. / x_scale_inv;
980   y_scale = 1. / y_scale_inv;
981 
982   hb_face = pango_fc_font_map_get_hb_face (PANGO_FC_FONT_MAP (fc_font->fontmap), fc_font);
983 
984   hb_font = hb_font_create (hb_face);
985   hb_font_set_scale (hb_font,
986                      size * PANGO_SCALE * x_scale,
987                      size * PANGO_SCALE * y_scale);
988 
989   if (key)
990     {
991       const FcPattern *pattern = pango_fc_font_key_get_pattern (key);
992       const char *variations;
993       int index;
994       unsigned int n_axes;
995       hb_ot_var_axis_info_t *axes;
996       float *coords;
997       int i;
998 
999       n_axes = hb_ot_var_get_axis_infos (hb_face, 0, NULL, NULL);
1000       if (n_axes == 0)
1001         goto done;
1002 
1003       axes = g_new0 (hb_ot_var_axis_info_t, n_axes);
1004       coords = g_new (float, n_axes);
1005 
1006       hb_ot_var_get_axis_infos (hb_face, 0, &n_axes, axes);
1007       for (i = 0; i < n_axes; i++)
1008         coords[axes[i].axis_index] = axes[i].default_value;
1009 
1010       if (FcPatternGetInteger (pattern, FC_INDEX, 0, &index) == FcResultMatch &&
1011           index != 0)
1012         {
1013           unsigned int instance = (index >> 16) - 1;
1014           hb_ot_var_named_instance_get_design_coords (hb_face, instance, &n_axes, coords);
1015         }
1016 
1017       if (FcPatternGetString (pattern, PANGO_FC_FONT_VARIATIONS, 0, (FcChar8 **)&variations) == FcResultMatch)
1018         parse_variations (variations, axes, n_axes, coords);
1019 
1020       variations = pango_fc_font_key_get_variations (key);
1021       if (variations)
1022         parse_variations (variations, axes, n_axes, coords);
1023 
1024       hb_font_set_var_coords_design (hb_font, coords, n_axes);
1025 
1026       g_free (coords);
1027       g_free (axes);
1028     }
1029 
1030 done:
1031   return hb_font;
1032 }
1033 
1034 /**
1035  * pango_fc_font_get_languages:
1036  * @font: a `PangoFcFont`
1037  *
1038  * Returns the languages that are supported by @font.
1039  *
1040  * This corresponds to the FC_LANG member of the FcPattern.
1041  *
1042  * The returned array is only valid as long as the font
1043  * and its fontmap are valid.
1044  *
1045  * Returns: (transfer none) (nullable): a %NULL-terminated
1046  *   array of `PangoLanguage`*
1047  *
1048  * Since: 1.48
1049  */
1050 PangoLanguage **
pango_fc_font_get_languages(PangoFcFont * font)1051 pango_fc_font_get_languages (PangoFcFont *font)
1052 {
1053   PangoFcFontMap *fontmap;
1054   PangoLanguage **languages;
1055 
1056   fontmap = g_weak_ref_get ((GWeakRef *) &font->fontmap);
1057   if (!fontmap)
1058     return NULL;
1059 
1060   languages  = _pango_fc_font_map_get_languages (fontmap, font);
1061   g_object_unref (fontmap);
1062 
1063   return languages;
1064 }
1065 
1066 /**
1067  * pango_fc_font_get_pattern: (skip)
1068  * @font: a `PangoFcFont`
1069  *
1070  * Returns the FcPattern that @font is based on.
1071  *
1072  * Returns: the fontconfig pattern for this font
1073  *
1074  * Since: 1.48
1075  */
1076 FcPattern *
pango_fc_font_get_pattern(PangoFcFont * font)1077 pango_fc_font_get_pattern (PangoFcFont *font)
1078 {
1079   return font->font_pattern;
1080 }
1081