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