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