1/* Pango
2 * fonts.c:
3 *
4 * Copyright (C) 1999 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#include <stdlib.h>
24#include <math.h>
25#include <string.h>
26
27#include <gio/gio.h>
28
29#include "pango-types.h"
30#include "pango-font-private.h"
31#include "pango-fontmap.h"
32#include "pango-impl-utils.h"
33
34struct _PangoFontDescription
35{
36 char *family_name;
37
38 PangoStyle style;
39 PangoVariant variant;
40 PangoWeight weight;
41 PangoStretch stretch;
42 PangoGravity gravity;
43
44 char *variations;
45
46 guint16 mask;
47 guint static_family : 1;
48 guint static_variations : 1;
49 guint size_is_absolute : 1;
50
51 int size;
52};
53
54G_DEFINE_BOXED_TYPE (PangoFontDescription, pango_font_description,
55 pango_font_description_copy,
56 pango_font_description_free);
57
58static const PangoFontDescription pfd_defaults = {
59 NULL, /* family_name */
60
61 PANGO_STYLE_NORMAL, /* style */
62 PANGO_VARIANT_NORMAL, /* variant */
63 PANGO_WEIGHT_NORMAL, /* weight */
64 PANGO_STRETCH_NORMAL, /* stretch */
65 PANGO_GRAVITY_SOUTH, /* gravity */
66 NULL, /* variations */
67
68 0, /* mask */
69 0, /* static_family */
70 0, /* static_variations*/
71 0, /* size_is_absolute */
72
73 0, /* size */
74};
75
76/**
77 * pango_font_description_new:
78 *
79 * Creates a new font description structure with all fields unset.
80 *
81 * Return value: the newly allocated `PangoFontDescription`, which
82 * should be freed using [method@Pango.FontDescription.free].
83 */
84PangoFontDescription *
85pango_font_description_new (void)
86{
87 PangoFontDescription *desc = g_slice_new (PangoFontDescription);
88
89 *desc = pfd_defaults;
90
91 return desc;
92}
93
94/**
95 * pango_font_description_set_family:
96 * @desc: a `PangoFontDescription`.
97 * @family: a string representing the family name.
98 *
99 * Sets the family name field of a font description.
100 *
101 * The family
102 * name represents a family of related font styles, and will
103 * resolve to a particular `PangoFontFamily`. In some uses of
104 * `PangoFontDescription`, it is also possible to use a comma
105 * separated list of family names for this field.
106 */
107void
108pango_font_description_set_family (PangoFontDescription *desc,
109 const char *family)
110{
111 g_return_if_fail (desc != NULL);
112
113 pango_font_description_set_family_static (desc, family: family ? g_strdup (str: family) : NULL);
114 if (family)
115 desc->static_family = FALSE;
116}
117
118/**
119 * pango_font_description_set_family_static:
120 * @desc: a `PangoFontDescription`
121 * @family: a string representing the family name
122 *
123 * Sets the family name field of a font description, without copying the string.
124 *
125 * This is like [method@Pango.FontDescription.set_family], except that no
126 * copy of @family is made. The caller must make sure that the
127 * string passed in stays around until @desc has been freed or the
128 * name is set again. This function can be used if @family is a static
129 * string such as a C string literal, or if @desc is only needed temporarily.
130 */
131void
132pango_font_description_set_family_static (PangoFontDescription *desc,
133 const char *family)
134{
135 g_return_if_fail (desc != NULL);
136
137 if (desc->family_name == family)
138 return;
139
140 if (desc->family_name && !desc->static_family)
141 g_free (mem: desc->family_name);
142
143 if (family)
144 {
145 desc->family_name = (char *)family;
146 desc->static_family = TRUE;
147 desc->mask |= PANGO_FONT_MASK_FAMILY;
148 }
149 else
150 {
151 desc->family_name = pfd_defaults.family_name;
152 desc->static_family = pfd_defaults.static_family;
153 desc->mask &= ~PANGO_FONT_MASK_FAMILY;
154 }
155}
156
157/**
158 * pango_font_description_get_family:
159 * @desc: a `PangoFontDescription`.
160 *
161 * Gets the family name field of a font description.
162 *
163 * See [method@Pango.FontDescription.set_family].
164 *
165 * Return value: (nullable): the family name field for the font
166 * description, or %NULL if not previously set. This has the same
167 * life-time as the font description itself and should not be freed.
168 */
169const char *
170pango_font_description_get_family (const PangoFontDescription *desc)
171{
172 g_return_val_if_fail (desc != NULL, NULL);
173
174 return desc->family_name;
175}
176
177/**
178 * pango_font_description_set_style:
179 * @desc: a `PangoFontDescription`
180 * @style: the style for the font description
181 *
182 * Sets the style field of a `PangoFontDescription`.
183 *
184 * The [enum@Pango.Style] enumeration describes whether the font is
185 * slanted and the manner in which it is slanted; it can be either
186 * %PANGO_STYLE_NORMAL, %PANGO_STYLE_ITALIC, or %PANGO_STYLE_OBLIQUE.
187 *
188 * Most fonts will either have a italic style or an oblique style,
189 * but not both, and font matching in Pango will match italic
190 * specifications with oblique fonts and vice-versa if an exact
191 * match is not found.
192 */
193void
194pango_font_description_set_style (PangoFontDescription *desc,
195 PangoStyle style)
196{
197 g_return_if_fail (desc != NULL);
198
199 desc->style = style;
200 desc->mask |= PANGO_FONT_MASK_STYLE;
201}
202
203/**
204 * pango_font_description_get_style:
205 * @desc: a `PangoFontDescription`
206 *
207 * Gets the style field of a `PangoFontDescription`.
208 *
209 * See [method@Pango.FontDescription.set_style].
210 *
211 * Return value: the style field for the font description.
212 * Use [method@Pango.FontDescription.get_set_fields] to
213 * find out if the field was explicitly set or not.
214 */
215PangoStyle
216pango_font_description_get_style (const PangoFontDescription *desc)
217{
218 g_return_val_if_fail (desc != NULL, pfd_defaults.style);
219
220 return desc->style;
221}
222
223/**
224 * pango_font_description_set_variant:
225 * @desc: a `PangoFontDescription`
226 * @variant: the variant type for the font description.
227 *
228 * Sets the variant field of a font description.
229 *
230 * The [enum@Pango.Variant] can either be %PANGO_VARIANT_NORMAL
231 * or %PANGO_VARIANT_SMALL_CAPS.
232 */
233void
234pango_font_description_set_variant (PangoFontDescription *desc,
235 PangoVariant variant)
236{
237 g_return_if_fail (desc != NULL);
238
239 desc->variant = variant;
240 desc->mask |= PANGO_FONT_MASK_VARIANT;
241}
242
243/**
244 * pango_font_description_get_variant:
245 * @desc: a `PangoFontDescription`.
246 *
247 * Gets the variant field of a `PangoFontDescription`.
248 *
249 * See [method@Pango.FontDescription.set_variant].
250 *
251 * Return value: the variant field for the font description.
252 * Use [method@Pango.FontDescription.get_set_fields] to find
253 * out if the field was explicitly set or not.
254 */
255PangoVariant
256pango_font_description_get_variant (const PangoFontDescription *desc)
257{
258 g_return_val_if_fail (desc != NULL, pfd_defaults.variant);
259
260 return desc->variant;
261}
262
263/**
264 * pango_font_description_set_weight:
265 * @desc: a `PangoFontDescription`
266 * @weight: the weight for the font description.
267 *
268 * Sets the weight field of a font description.
269 *
270 * The weight field
271 * specifies how bold or light the font should be. In addition
272 * to the values of the [enum@Pango.Weight] enumeration, other
273 * intermediate numeric values are possible.
274 */
275void
276pango_font_description_set_weight (PangoFontDescription *desc,
277 PangoWeight weight)
278{
279 g_return_if_fail (desc != NULL);
280
281 desc->weight = weight;
282 desc->mask |= PANGO_FONT_MASK_WEIGHT;
283}
284
285/**
286 * pango_font_description_get_weight:
287 * @desc: a `PangoFontDescription`
288 *
289 * Gets the weight field of a font description.
290 *
291 * See [method@Pango.FontDescription.set_weight].
292 *
293 * Return value: the weight field for the font description.
294 * Use [method@Pango.FontDescription.get_set_fields] to find
295 * out if the field was explicitly set or not.
296 */
297PangoWeight
298pango_font_description_get_weight (const PangoFontDescription *desc)
299{
300 g_return_val_if_fail (desc != NULL, pfd_defaults.weight);
301
302 return desc->weight;
303}
304
305/**
306 * pango_font_description_set_stretch:
307 * @desc: a `PangoFontDescription`
308 * @stretch: the stretch for the font description
309 *
310 * Sets the stretch field of a font description.
311 *
312 * The [enum@Pango.Stretch] field specifies how narrow or
313 * wide the font should be.
314 */
315void
316pango_font_description_set_stretch (PangoFontDescription *desc,
317 PangoStretch stretch)
318{
319 g_return_if_fail (desc != NULL);
320
321 desc->stretch = stretch;
322 desc->mask |= PANGO_FONT_MASK_STRETCH;
323}
324
325/**
326 * pango_font_description_get_stretch:
327 * @desc: a `PangoFontDescription`.
328 *
329 * Gets the stretch field of a font description.
330 *
331 * See [method@Pango.FontDescription.set_stretch].
332 *
333 * Return value: the stretch field for the font description.
334 * Use [method@Pango.FontDescription.get_set_fields] to find
335 * out if the field was explicitly set or not.
336 */
337PangoStretch
338pango_font_description_get_stretch (const PangoFontDescription *desc)
339{
340 g_return_val_if_fail (desc != NULL, pfd_defaults.stretch);
341
342 return desc->stretch;
343}
344
345/**
346 * pango_font_description_set_size:
347 * @desc: a `PangoFontDescription`
348 * @size: the size of the font in points, scaled by %PANGO_SCALE.
349 * (That is, a @size value of 10 * PANGO_SCALE is a 10 point font.
350 * The conversion factor between points and device units depends on
351 * system configuration and the output device. For screen display, a
352 * logical DPI of 96 is common, in which case a 10 point font corresponds
353 * to a 10 * (96 / 72) = 13.3 pixel font.
354 * Use [method@Pango.FontDescription.set_absolute_size] if you need
355 * a particular size in device units.
356 *
357 * Sets the size field of a font description in fractional points.
358 *
359 * This is mutually exclusive with
360 * [method@Pango.FontDescription.set_absolute_size].
361 */
362void
363pango_font_description_set_size (PangoFontDescription *desc,
364 gint size)
365{
366 g_return_if_fail (desc != NULL);
367 g_return_if_fail (size >= 0);
368
369 desc->size = size;
370 desc->size_is_absolute = FALSE;
371 desc->mask |= PANGO_FONT_MASK_SIZE;
372}
373
374/**
375 * pango_font_description_get_size:
376 * @desc: a `PangoFontDescription`
377 *
378 * Gets the size field of a font description.
379 *
380 * See [method@Pango.FontDescription.set_size].
381 *
382 * Return value: the size field for the font description in points
383 * or device units. You must call
384 * [method@Pango.FontDescription.get_size_is_absolute] to find out
385 * which is the case. Returns 0 if the size field has not previously
386 * been set or it has been set to 0 explicitly.
387 * Use [method@Pango.FontDescription.get_set_fields] to find out
388 * if the field was explicitly set or not.
389 */
390gint
391pango_font_description_get_size (const PangoFontDescription *desc)
392{
393 g_return_val_if_fail (desc != NULL, pfd_defaults.size);
394
395 return desc->size;
396}
397
398/**
399 * pango_font_description_set_absolute_size:
400 * @desc: a `PangoFontDescription`
401 * @size: the new size, in Pango units. There are %PANGO_SCALE Pango units
402 * in one device unit. For an output backend where a device unit is a pixel,
403 * a @size value of 10 * PANGO_SCALE gives a 10 pixel font.
404 *
405 * Sets the size field of a font description, in device units.
406 *
407 * This is mutually exclusive with [method@Pango.FontDescription.set_size]
408 * which sets the font size in points.
409 *
410 * Since: 1.8
411 */
412void
413pango_font_description_set_absolute_size (PangoFontDescription *desc,
414 double size)
415{
416 g_return_if_fail (desc != NULL);
417 g_return_if_fail (size >= 0);
418
419 desc->size = size;
420 desc->size_is_absolute = TRUE;
421 desc->mask |= PANGO_FONT_MASK_SIZE;
422}
423
424/**
425 * pango_font_description_get_size_is_absolute:
426 * @desc: a `PangoFontDescription`
427 *
428 * Determines whether the size of the font is in points (not absolute)
429 * or device units (absolute).
430 *
431 * See [method@Pango.FontDescription.set_size]
432 * and [method@Pango.FontDescription.set_absolute_size].
433 *
434 * Return value: whether the size for the font description is in
435 * points or device units. Use [method@Pango.FontDescription.get_set_fields]
436 * to find out if the size field of the font description was explicitly
437 * set or not.
438 *
439 * Since: 1.8
440 */
441gboolean
442pango_font_description_get_size_is_absolute (const PangoFontDescription *desc)
443{
444 g_return_val_if_fail (desc != NULL, pfd_defaults.size_is_absolute);
445
446 return desc->size_is_absolute;
447}
448
449/**
450 * pango_font_description_set_gravity:
451 * @desc: a `PangoFontDescription`
452 * @gravity: the gravity for the font description.
453 *
454 * Sets the gravity field of a font description.
455 *
456 * The gravity field
457 * specifies how the glyphs should be rotated. If @gravity is
458 * %PANGO_GRAVITY_AUTO, this actually unsets the gravity mask on
459 * the font description.
460 *
461 * This function is seldom useful to the user. Gravity should normally
462 * be set on a `PangoContext`.
463 *
464 * Since: 1.16
465 */
466void
467pango_font_description_set_gravity (PangoFontDescription *desc,
468 PangoGravity gravity)
469{
470 g_return_if_fail (desc != NULL);
471
472 if (gravity == PANGO_GRAVITY_AUTO)
473 {
474 pango_font_description_unset_fields (desc, to_unset: PANGO_FONT_MASK_GRAVITY);
475 return;
476 }
477
478 desc->gravity = gravity;
479 desc->mask |= PANGO_FONT_MASK_GRAVITY;
480}
481
482/**
483 * pango_font_description_get_gravity:
484 * @desc: a `PangoFontDescription`
485 *
486 * Gets the gravity field of a font description.
487 *
488 * See [method@Pango.FontDescription.set_gravity].
489 *
490 * Return value: the gravity field for the font description.
491 * Use [method@Pango.FontDescription.get_set_fields] to find out
492 * if the field was explicitly set or not.
493 *
494 * Since: 1.16
495 */
496PangoGravity
497pango_font_description_get_gravity (const PangoFontDescription *desc)
498{
499 g_return_val_if_fail (desc != NULL, pfd_defaults.gravity);
500
501 return desc->gravity;
502}
503
504/**
505 * pango_font_description_set_variations_static:
506 * @desc: a `PangoFontDescription`
507 * @variations: a string representing the variations
508 *
509 * Sets the variations field of a font description.
510 *
511 * This is like [method@Pango.FontDescription.set_variations], except
512 * that no copy of @variations is made. The caller must make sure that
513 * the string passed in stays around until @desc has been freed
514 * or the name is set again. This function can be used if
515 * @variations is a static string such as a C string literal,
516 * or if @desc is only needed temporarily.
517 *
518 * Since: 1.42
519 */
520void
521pango_font_description_set_variations_static (PangoFontDescription *desc,
522 const char *variations)
523{
524 g_return_if_fail (desc != NULL);
525
526 if (desc->variations == variations)
527 return;
528
529 if (desc->variations && !desc->static_variations)
530 g_free (mem: desc->variations);
531
532 if (variations)
533 {
534 desc->variations = (char *)variations;
535 desc->static_variations = TRUE;
536 desc->mask |= PANGO_FONT_MASK_VARIATIONS;
537 }
538 else
539 {
540 desc->variations = pfd_defaults.variations;
541 desc->static_variations = pfd_defaults.static_variations;
542 desc->mask &= ~PANGO_FONT_MASK_VARIATIONS;
543 }
544}
545
546/**
547 * pango_font_description_set_variations:
548 * @desc: a `PangoFontDescription`.
549 * @variations: (nullable): a string representing the variations
550 *
551 * Sets the variations field of a font description.
552 *
553 * OpenType font variations allow to select a font instance by
554 * specifying values for a number of axes, such as width or weight.
555 *
556 * The format of the variations string is
557 *
558 * AXIS1=VALUE,AXIS2=VALUE...
559 *
560 * with each AXIS a 4 character tag that identifies a font axis,
561 * and each VALUE a floating point number. Unknown axes are ignored,
562 * and values are clamped to their allowed range.
563 *
564 * Pango does not currently have a way to find supported axes of
565 * a font. Both harfbuzz and freetype have API for this. See
566 * for example [hb_ot_var_get_axis_infos](https://harfbuzz.github.io/harfbuzz-hb-ot-var.html#hb-ot-var-get-axis-infos).
567 *
568 * Since: 1.42
569 */
570void
571pango_font_description_set_variations (PangoFontDescription *desc,
572 const char *variations)
573{
574 g_return_if_fail (desc != NULL);
575
576 pango_font_description_set_variations_static (desc, variations: g_strdup (str: variations));
577 if (variations)
578 desc->static_variations = FALSE;
579}
580
581/**
582 * pango_font_description_get_variations:
583 * @desc: a `PangoFontDescription`
584 *
585 * Gets the variations field of a font description.
586 *
587 * See [method@Pango.FontDescription.set_variations].
588 *
589 * Return value: (nullable): the variations field for the font
590 * description, or %NULL if not previously set. This has the same
591 * life-time as the font description itself and should not be freed.
592 *
593 * Since: 1.42
594 */
595const char *
596pango_font_description_get_variations (const PangoFontDescription *desc)
597{
598 g_return_val_if_fail (desc != NULL, NULL);
599
600 return desc->variations;
601}
602
603/**
604 * pango_font_description_get_set_fields:
605 * @desc: a `PangoFontDescription`
606 *
607 * Determines which fields in a font description have been set.
608 *
609 * Return value: a bitmask with bits set corresponding to the
610 * fields in @desc that have been set.
611 */
612PangoFontMask
613pango_font_description_get_set_fields (const PangoFontDescription *desc)
614{
615 g_return_val_if_fail (desc != NULL, pfd_defaults.mask);
616
617 return desc->mask;
618}
619
620/**
621 * pango_font_description_unset_fields:
622 * @desc: a `PangoFontDescription`
623 * @to_unset: bitmask of fields in the @desc to unset.
624 *
625 * Unsets some of the fields in a `PangoFontDescription`.
626 *
627 * The unset fields will get back to their default values.
628 */
629void
630pango_font_description_unset_fields (PangoFontDescription *desc,
631 PangoFontMask to_unset)
632{
633 PangoFontDescription unset_desc;
634
635 g_return_if_fail (desc != NULL);
636
637 unset_desc = pfd_defaults;
638 unset_desc.mask = to_unset;
639
640 pango_font_description_merge_static (desc, desc_to_merge: &unset_desc, TRUE);
641
642 desc->mask &= ~to_unset;
643}
644
645/**
646 * pango_font_description_merge:
647 * @desc: a `PangoFontDescription`
648 * @desc_to_merge: (nullable): the `PangoFontDescription` to merge from,
649 * or %NULL
650 * @replace_existing: if %TRUE, replace fields in @desc with the
651 * corresponding values from @desc_to_merge, even if they
652 * are already exist.
653 *
654 * Merges the fields that are set in @desc_to_merge into the fields in
655 * @desc.
656 *
657 * If @replace_existing is %FALSE, only fields in @desc that
658 * are not already set are affected. If %TRUE, then fields that are
659 * already set will be replaced as well.
660 *
661 * If @desc_to_merge is %NULL, this function performs nothing.
662 */
663void
664pango_font_description_merge (PangoFontDescription *desc,
665 const PangoFontDescription *desc_to_merge,
666 gboolean replace_existing)
667{
668 gboolean family_merged;
669 gboolean variations_merged;
670
671 g_return_if_fail (desc != NULL);
672
673 if (desc_to_merge == NULL)
674 return;
675
676 family_merged = desc_to_merge->family_name && (replace_existing || !desc->family_name);
677 variations_merged = desc_to_merge->variations && (replace_existing || !desc->variations);
678
679 pango_font_description_merge_static (desc, desc_to_merge, replace_existing);
680
681 if (family_merged)
682 {
683 desc->family_name = g_strdup (str: desc->family_name);
684 desc->static_family = FALSE;
685 }
686
687 if (variations_merged)
688 {
689 desc->variations = g_strdup (str: desc->variations);
690 desc->static_variations = FALSE;
691 }
692}
693
694/**
695 * pango_font_description_merge_static:
696 * @desc: a `PangoFontDescription`
697 * @desc_to_merge: the `PangoFontDescription` to merge from
698 * @replace_existing: if %TRUE, replace fields in @desc with the
699 * corresponding values from @desc_to_merge, even if they
700 * are already exist.
701 *
702 * Merges the fields that are set in @desc_to_merge into the fields in
703 * @desc, without copying allocated fields.
704 *
705 * This is like [method@Pango.FontDescription.merge], but only a shallow copy
706 * is made of the family name and other allocated fields. @desc can only
707 * be used until @desc_to_merge is modified or freed. This is meant to
708 * be used when the merged font description is only needed temporarily.
709 */
710void
711pango_font_description_merge_static (PangoFontDescription *desc,
712 const PangoFontDescription *desc_to_merge,
713 gboolean replace_existing)
714{
715 PangoFontMask new_mask;
716
717 g_return_if_fail (desc != NULL);
718 g_return_if_fail (desc_to_merge != NULL);
719
720 if (replace_existing)
721 new_mask = desc_to_merge->mask;
722 else
723 new_mask = desc_to_merge->mask & ~desc->mask;
724
725 if (new_mask & PANGO_FONT_MASK_FAMILY)
726 pango_font_description_set_family_static (desc, family: desc_to_merge->family_name);
727 if (new_mask & PANGO_FONT_MASK_STYLE)
728 desc->style = desc_to_merge->style;
729 if (new_mask & PANGO_FONT_MASK_VARIANT)
730 desc->variant = desc_to_merge->variant;
731 if (new_mask & PANGO_FONT_MASK_WEIGHT)
732 desc->weight = desc_to_merge->weight;
733 if (new_mask & PANGO_FONT_MASK_STRETCH)
734 desc->stretch = desc_to_merge->stretch;
735 if (new_mask & PANGO_FONT_MASK_SIZE)
736 {
737 desc->size = desc_to_merge->size;
738 desc->size_is_absolute = desc_to_merge->size_is_absolute;
739 }
740 if (new_mask & PANGO_FONT_MASK_GRAVITY)
741 desc->gravity = desc_to_merge->gravity;
742 if (new_mask & PANGO_FONT_MASK_VARIATIONS)
743 pango_font_description_set_variations_static (desc, variations: desc_to_merge->variations);
744
745 desc->mask |= new_mask;
746}
747
748static gint
749compute_distance (const PangoFontDescription *a,
750 const PangoFontDescription *b)
751{
752 if (a->style == b->style)
753 {
754 return abs(x: (int)(a->weight) - (int)(b->weight));
755 }
756 else if (a->style != PANGO_STYLE_NORMAL &&
757 b->style != PANGO_STYLE_NORMAL)
758 {
759 /* Equate oblique and italic, but with a big penalty
760 */
761 return 1000000 + abs (x: (int)(a->weight) - (int)(b->weight));
762 }
763 else
764 return G_MAXINT;
765}
766
767/**
768 * pango_font_description_better_match:
769 * @desc: a `PangoFontDescription`
770 * @old_match: (nullable): a `PangoFontDescription`, or %NULL
771 * @new_match: a `PangoFontDescription`
772 *
773 * Determines if the style attributes of @new_match are a closer match
774 * for @desc than those of @old_match are, or if @old_match is %NULL,
775 * determines if @new_match is a match at all.
776 *
777 * Approximate matching is done for weight and style; other style attributes
778 * must match exactly. Style attributes are all attributes other than family
779 * and size-related attributes. Approximate matching for style considers
780 * %PANGO_STYLE_OBLIQUE and %PANGO_STYLE_ITALIC as matches, but not as good
781 * a match as when the styles are equal.
782 *
783 * Note that @old_match must match @desc.
784 *
785 * Return value: %TRUE if @new_match is a better match
786 */
787gboolean
788pango_font_description_better_match (const PangoFontDescription *desc,
789 const PangoFontDescription *old_match,
790 const PangoFontDescription *new_match)
791{
792 g_return_val_if_fail (desc != NULL, G_MAXINT);
793 g_return_val_if_fail (new_match != NULL, G_MAXINT);
794
795 if (new_match->variant == desc->variant &&
796 new_match->stretch == desc->stretch &&
797 new_match->gravity == desc->gravity)
798 {
799 int old_distance = old_match ? compute_distance (a: desc, b: old_match) : G_MAXINT;
800 int new_distance = compute_distance (a: desc, b: new_match);
801
802 if (new_distance < old_distance)
803 return TRUE;
804 }
805
806 return FALSE;
807}
808
809/**
810 * pango_font_description_copy:
811 * @desc: (nullable): a `PangoFontDescription`, may be %NULL
812 *
813 * Make a copy of a `PangoFontDescription`.
814 *
815 * Return value: (nullable): the newly allocated `PangoFontDescription`,
816 * which should be freed with [method@Pango.FontDescription.free],
817 * or %NULL if @desc was %NULL.
818 */
819PangoFontDescription *
820pango_font_description_copy (const PangoFontDescription *desc)
821{
822 PangoFontDescription *result;
823
824 if (desc == NULL)
825 return NULL;
826
827 result = g_slice_new (PangoFontDescription);
828
829 *result = *desc;
830
831 if (result->family_name)
832 {
833 result->family_name = g_strdup (str: result->family_name);
834 result->static_family = FALSE;
835 }
836
837 result->variations = g_strdup (str: result->variations);
838 result->static_variations = FALSE;
839
840 return result;
841}
842
843/**
844 * pango_font_description_copy_static:
845 * @desc: (nullable): a `PangoFontDescription`, may be %NULL
846 *
847 * Make a copy of a `PangoFontDescription`, but don't duplicate
848 * allocated fields.
849 *
850 * This is like [method@Pango.FontDescription.copy], but only a shallow
851 * copy is made of the family name and other allocated fields. The result
852 * can only be used until @desc is modified or freed. This is meant
853 * to be used when the copy is only needed temporarily.
854 *
855 * Return value: (nullable): the newly allocated `PangoFontDescription`,
856 * which should be freed with [method@Pango.FontDescription.free],
857 * or %NULL if @desc was %NULL.
858 */
859PangoFontDescription *
860pango_font_description_copy_static (const PangoFontDescription *desc)
861{
862 PangoFontDescription *result;
863
864 if (desc == NULL)
865 return NULL;
866
867 result = g_slice_new (PangoFontDescription);
868
869 *result = *desc;
870 if (result->family_name)
871 result->static_family = TRUE;
872
873
874 if (result->variations)
875 result->static_variations = TRUE;
876
877 return result;
878}
879
880/**
881 * pango_font_description_equal:
882 * @desc1: a `PangoFontDescription`
883 * @desc2: another `PangoFontDescription`
884 *
885 * Compares two font descriptions for equality.
886 *
887 * Two font descriptions are considered equal if the fonts they describe
888 * are provably identical. This means that their masks do not have to match,
889 * as long as other fields are all the same. (Two font descriptions may
890 * result in identical fonts being loaded, but still compare %FALSE.)
891 *
892 * Return value: %TRUE if the two font descriptions are identical,
893 * %FALSE otherwise.
894 */
895gboolean
896pango_font_description_equal (const PangoFontDescription *desc1,
897 const PangoFontDescription *desc2)
898{
899 g_return_val_if_fail (desc1 != NULL, FALSE);
900 g_return_val_if_fail (desc2 != NULL, FALSE);
901
902 return desc1->style == desc2->style &&
903 desc1->variant == desc2->variant &&
904 desc1->weight == desc2->weight &&
905 desc1->stretch == desc2->stretch &&
906 desc1->size == desc2->size &&
907 desc1->size_is_absolute == desc2->size_is_absolute &&
908 desc1->gravity == desc2->gravity &&
909 (desc1->family_name == desc2->family_name ||
910 (desc1->family_name && desc2->family_name && g_ascii_strcasecmp (s1: desc1->family_name, s2: desc2->family_name) == 0)) &&
911 (g_strcmp0 (str1: desc1->variations, str2: desc2->variations) == 0);
912}
913
914#define TOLOWER(c) \
915 (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
916
917static guint
918case_insensitive_hash (const char *key)
919{
920 const char *p = key;
921 guint h = TOLOWER (*p);
922
923 if (h)
924 {
925 for (p += 1; *p != '\0'; p++)
926 h = (h << 5) - h + TOLOWER (*p);
927 }
928
929 return h;
930}
931
932/**
933 * pango_font_description_hash:
934 * @desc: a `PangoFontDescription`
935 *
936 * Computes a hash of a `PangoFontDescription` structure.
937 *
938 * This is suitable to be used, for example, as an argument
939 * to g_hash_table_new(). The hash value is independent of @desc->mask.
940 *
941 * Return value: the hash value.
942 */
943guint
944pango_font_description_hash (const PangoFontDescription *desc)
945{
946 guint hash = 0;
947
948 g_return_val_if_fail (desc != NULL, 0);
949
950 if (desc->family_name)
951 hash = case_insensitive_hash (key: desc->family_name);
952 if (desc->variations)
953 hash ^= g_str_hash (v: desc->variations);
954 hash ^= desc->size;
955 hash ^= desc->size_is_absolute ? 0xc33ca55a : 0;
956 hash ^= desc->style << 16;
957 hash ^= desc->variant << 18;
958 hash ^= desc->weight << 16;
959 hash ^= desc->stretch << 26;
960 hash ^= desc->gravity << 28;
961
962 return hash;
963}
964
965/**
966 * pango_font_description_free:
967 * @desc: (nullable): a `PangoFontDescription`, may be %NULL
968 *
969 * Frees a font description.
970 */
971void
972pango_font_description_free (PangoFontDescription *desc)
973{
974 if (desc == NULL)
975 return;
976
977 if (desc->family_name && !desc->static_family)
978 g_free (mem: desc->family_name);
979
980 if (desc->variations && !desc->static_variations)
981 g_free (mem: desc->variations);
982
983 g_slice_free (PangoFontDescription, desc);
984}
985
986/**
987 * pango_font_descriptions_free:
988 * @descs: (nullable) (array length=n_descs) (transfer full): a pointer
989 * to an array of `PangoFontDescription`, may be %NULL
990 * @n_descs: number of font descriptions in @descs
991 *
992 * Frees an array of font descriptions.
993 */
994void
995pango_font_descriptions_free (PangoFontDescription **descs,
996 int n_descs)
997{
998 int i;
999
1000 if (descs == NULL)
1001 return;
1002
1003 for (i = 0; i<n_descs; i++)
1004 pango_font_description_free (desc: descs[i]);
1005 g_free (mem: descs);
1006}
1007
1008typedef struct
1009{
1010 int value;
1011 const char str[16];
1012} FieldMap;
1013
1014static const FieldMap style_map[] = {
1015 { PANGO_STYLE_NORMAL, "" },
1016 { PANGO_STYLE_NORMAL, "Roman" },
1017 { PANGO_STYLE_OBLIQUE, "Oblique" },
1018 { PANGO_STYLE_ITALIC, "Italic" }
1019};
1020
1021static const FieldMap variant_map[] = {
1022 { PANGO_VARIANT_NORMAL, "" },
1023 { PANGO_VARIANT_SMALL_CAPS, "Small-Caps" },
1024 { PANGO_VARIANT_ALL_SMALL_CAPS, "All-Small-Caps" },
1025 { PANGO_VARIANT_PETITE_CAPS, "Petite-Caps" },
1026 { PANGO_VARIANT_ALL_PETITE_CAPS, "All-Petite-Caps" },
1027 { PANGO_VARIANT_UNICASE, "Unicase" },
1028 { PANGO_VARIANT_TITLE_CAPS, "Title-Caps" }
1029};
1030
1031static const FieldMap weight_map[] = {
1032 { PANGO_WEIGHT_THIN, "Thin" },
1033 { PANGO_WEIGHT_ULTRALIGHT, "Ultra-Light" },
1034 { PANGO_WEIGHT_ULTRALIGHT, "Extra-Light" },
1035 { PANGO_WEIGHT_LIGHT, "Light" },
1036 { PANGO_WEIGHT_SEMILIGHT, "Semi-Light" },
1037 { PANGO_WEIGHT_SEMILIGHT, "Demi-Light" },
1038 { PANGO_WEIGHT_BOOK, "Book" },
1039 { PANGO_WEIGHT_NORMAL, "" },
1040 { PANGO_WEIGHT_NORMAL, "Regular" },
1041 { PANGO_WEIGHT_MEDIUM, "Medium" },
1042 { PANGO_WEIGHT_SEMIBOLD, "Semi-Bold" },
1043 { PANGO_WEIGHT_SEMIBOLD, "Demi-Bold" },
1044 { PANGO_WEIGHT_BOLD, "Bold" },
1045 { PANGO_WEIGHT_ULTRABOLD, "Ultra-Bold" },
1046 { PANGO_WEIGHT_ULTRABOLD, "Extra-Bold" },
1047 { PANGO_WEIGHT_HEAVY, "Heavy" },
1048 { PANGO_WEIGHT_HEAVY, "Black" },
1049 { PANGO_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" },
1050 { PANGO_WEIGHT_ULTRAHEAVY, "Extra-Heavy" },
1051 { PANGO_WEIGHT_ULTRAHEAVY, "Ultra-Black" },
1052 { PANGO_WEIGHT_ULTRAHEAVY, "Extra-Black" }
1053};
1054
1055static const FieldMap stretch_map[] = {
1056 { PANGO_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" },
1057 { PANGO_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" },
1058 { PANGO_STRETCH_CONDENSED, "Condensed" },
1059 { PANGO_STRETCH_SEMI_CONDENSED, "Semi-Condensed" },
1060 { PANGO_STRETCH_NORMAL, "" },
1061 { PANGO_STRETCH_SEMI_EXPANDED, "Semi-Expanded" },
1062 { PANGO_STRETCH_EXPANDED, "Expanded" },
1063 { PANGO_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" },
1064 { PANGO_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" }
1065};
1066
1067static const FieldMap gravity_map[] = {
1068 { PANGO_GRAVITY_SOUTH, "Not-Rotated" },
1069 { PANGO_GRAVITY_SOUTH, "South" },
1070 { PANGO_GRAVITY_NORTH, "Upside-Down" },
1071 { PANGO_GRAVITY_NORTH, "North" },
1072 { PANGO_GRAVITY_EAST, "Rotated-Left" },
1073 { PANGO_GRAVITY_EAST, "East" },
1074 { PANGO_GRAVITY_WEST, "Rotated-Right" },
1075 { PANGO_GRAVITY_WEST, "West" }
1076};
1077
1078static gboolean
1079field_matches (const gchar *s1,
1080 const gchar *s2,
1081 gsize n)
1082{
1083 gint c1, c2;
1084
1085 g_return_val_if_fail (s1 != NULL, 0);
1086 g_return_val_if_fail (s2 != NULL, 0);
1087
1088 while (n && *s1 && *s2)
1089 {
1090 c1 = (gint)(guchar) TOLOWER (*s1);
1091 c2 = (gint)(guchar) TOLOWER (*s2);
1092 if (c1 != c2) {
1093 if (c1 == '-') {
1094 s1++;
1095 continue;
1096 }
1097 return FALSE;
1098 }
1099 s1++; s2++;
1100 n--;
1101 }
1102
1103 return n == 0 && *s1 == '\0';
1104}
1105
1106static gboolean
1107parse_int (const char *word,
1108 size_t wordlen,
1109 int *out)
1110{
1111 char *end;
1112 long val = strtol (nptr: word, endptr: &end, base: 10);
1113 int i = val;
1114
1115 if (end != word && (end == word + wordlen) && val >= 0 && val == i)
1116 {
1117 if (out)
1118 *out = i;
1119
1120 return TRUE;
1121 }
1122
1123 return FALSE;
1124}
1125
1126static gboolean
1127find_field (const char *what,
1128 const FieldMap *map,
1129 int n_elements,
1130 const char *str,
1131 int len,
1132 int *val)
1133{
1134 int i;
1135 gboolean had_prefix = FALSE;
1136
1137 if (what)
1138 {
1139 i = strlen (s: what);
1140 if (len > i && 0 == strncmp (s1: what, s2: str, n: i) && str[i] == '=')
1141 {
1142 str += i + 1;
1143 len -= i + 1;
1144 had_prefix = TRUE;
1145 }
1146 }
1147
1148 for (i=0; i<n_elements; i++)
1149 {
1150 if (map[i].str[0] && field_matches (s1: map[i].str, s2: str, n: len))
1151 {
1152 if (val)
1153 *val = map[i].value;
1154 return TRUE;
1155 }
1156 }
1157
1158 if (!what || had_prefix)
1159 return parse_int (word: str, wordlen: len, out: val);
1160
1161 return FALSE;
1162}
1163
1164static gboolean
1165find_field_any (const char *str, int len, PangoFontDescription *desc)
1166{
1167 if (field_matches (s1: "Normal", s2: str, n: len))
1168 return TRUE;
1169
1170#define FIELD(NAME, MASK) \
1171 G_STMT_START { \
1172 if (find_field (G_STRINGIFY (NAME), NAME##_map, G_N_ELEMENTS (NAME##_map), str, len, \
1173 desc ? (int *)(void *)&desc->NAME : NULL)) \
1174 { \
1175 if (desc) \
1176 desc->mask |= MASK; \
1177 return TRUE; \
1178 } \
1179 } G_STMT_END
1180
1181 FIELD (weight, PANGO_FONT_MASK_WEIGHT);
1182 FIELD (style, PANGO_FONT_MASK_STYLE);
1183 FIELD (stretch, PANGO_FONT_MASK_STRETCH);
1184 FIELD (variant, PANGO_FONT_MASK_VARIANT);
1185 FIELD (gravity, PANGO_FONT_MASK_GRAVITY);
1186
1187#undef FIELD
1188
1189 return FALSE;
1190}
1191
1192static const char *
1193getword (const char *str, const char *last, size_t *wordlen, const char *stop)
1194{
1195 const char *result;
1196
1197 while (last > str && g_ascii_isspace (*(last - 1)))
1198 last--;
1199
1200 result = last;
1201 while (result > str && !g_ascii_isspace (*(result - 1)) && !strchr (s: stop, c: *(result - 1)))
1202 result--;
1203
1204 *wordlen = last - result;
1205
1206 return result;
1207}
1208
1209static gboolean
1210parse_size (const char *word,
1211 size_t wordlen,
1212 int *pango_size,
1213 gboolean *size_is_absolute)
1214{
1215 char *end;
1216 double size = g_ascii_strtod (nptr: word, endptr: &end);
1217
1218 if (end != word &&
1219 (end == word + wordlen ||
1220 (end + 2 == word + wordlen && !strncmp (s1: end, s2: "px", n: 2))
1221 ) && size >= 0 && size <= 1000000) /* word is a valid float */
1222 {
1223 if (pango_size)
1224 *pango_size = (int)(size * PANGO_SCALE + 0.5);
1225
1226 if (size_is_absolute)
1227 *size_is_absolute = end < word + wordlen;
1228
1229 return TRUE;
1230 }
1231
1232 return FALSE;
1233}
1234
1235static gboolean
1236parse_variations (const char *word,
1237 size_t wordlen,
1238 char **variations)
1239{
1240 if (word[0] != '@')
1241 {
1242 *variations = NULL;
1243 return FALSE;
1244 }
1245
1246 /* XXX: actually validate here */
1247 *variations = g_strndup (str: word + 1, n: wordlen - 1);
1248
1249 return TRUE;
1250}
1251
1252/**
1253 * pango_font_description_from_string:
1254 * @str: string representation of a font description.
1255 *
1256 * Creates a new font description from a string representation.
1257 *
1258 * The string must have the form
1259 *
1260 * "\[FAMILY-LIST] \[STYLE-OPTIONS] \[SIZE] \[VARIATIONS]",
1261 *
1262 * where FAMILY-LIST is a comma-separated list of families optionally
1263 * terminated by a comma, STYLE_OPTIONS is a whitespace-separated list
1264 * of words where each word describes one of style, variant, weight,
1265 * stretch, or gravity, and SIZE is a decimal number (size in points)
1266 * or optionally followed by the unit modifier "px" for absolute size.
1267 * VARIATIONS is a comma-separated list of font variation
1268 * specifications of the form "\@axis=value" (the = sign is optional).
1269 *
1270 * The following words are understood as styles:
1271 * "Normal", "Roman", "Oblique", "Italic".
1272 *
1273 * The following words are understood as variants:
1274 * "Small-Caps", "All-Small-Caps", "Petite-Caps", "All-Petite-Caps",
1275 * "Unicase", "Title-Caps".
1276 *
1277 * The following words are understood as weights:
1278 * "Thin", "Ultra-Light", "Extra-Light", "Light", "Semi-Light",
1279 * "Demi-Light", "Book", "Regular", "Medium", "Semi-Bold", "Demi-Bold",
1280 * "Bold", "Ultra-Bold", "Extra-Bold", "Heavy", "Black", "Ultra-Black",
1281 * "Extra-Black".
1282 *
1283 * The following words are understood as stretch values:
1284 * "Ultra-Condensed", "Extra-Condensed", "Condensed", "Semi-Condensed",
1285 * "Semi-Expanded", "Expanded", "Extra-Expanded", "Ultra-Expanded".
1286 *
1287 * The following words are understood as gravity values:
1288 * "Not-Rotated", "South", "Upside-Down", "North", "Rotated-Left",
1289 * "East", "Rotated-Right", "West".
1290 *
1291 * Any one of the options may be absent. If FAMILY-LIST is absent, then
1292 * the family_name field of the resulting font description will be
1293 * initialized to %NULL. If STYLE-OPTIONS is missing, then all style
1294 * options will be set to the default values. If SIZE is missing, the
1295 * size in the resulting font description will be set to 0.
1296 *
1297 * A typical example:
1298 *
1299 * "Cantarell Italic Light 15 \@wght=200"
1300 *
1301 * Return value: a new `PangoFontDescription`.
1302 */
1303PangoFontDescription *
1304pango_font_description_from_string (const char *str)
1305{
1306 PangoFontDescription *desc;
1307 const char *p, *last;
1308 size_t len, wordlen;
1309
1310 g_return_val_if_fail (str != NULL, NULL);
1311
1312 desc = pango_font_description_new ();
1313
1314 desc->mask = PANGO_FONT_MASK_STYLE |
1315 PANGO_FONT_MASK_WEIGHT |
1316 PANGO_FONT_MASK_VARIANT |
1317 PANGO_FONT_MASK_STRETCH;
1318
1319 len = strlen (s: str);
1320 last = str + len;
1321 p = getword (str, last, wordlen: &wordlen, stop: "");
1322 /* Look for variations at the end of the string */
1323 if (wordlen != 0)
1324 {
1325 if (parse_variations (word: p, wordlen, variations: &desc->variations))
1326 {
1327 desc->mask |= PANGO_FONT_MASK_VARIATIONS;
1328 last = p;
1329 }
1330 }
1331
1332 p = getword (str, last, wordlen: &wordlen, stop: ",");
1333 /* Look for a size */
1334 if (wordlen != 0)
1335 {
1336 gboolean size_is_absolute;
1337 if (parse_size (word: p, wordlen, pango_size: &desc->size, size_is_absolute: &size_is_absolute))
1338 {
1339 desc->size_is_absolute = size_is_absolute;
1340 desc->mask |= PANGO_FONT_MASK_SIZE;
1341 last = p;
1342 }
1343 }
1344
1345 /* Now parse style words
1346 */
1347 p = getword (str, last, wordlen: &wordlen, stop: ",");
1348 while (wordlen != 0)
1349 {
1350 if (!find_field_any (str: p, len: wordlen, desc))
1351 break;
1352 else
1353 {
1354 last = p;
1355 p = getword (str, last, wordlen: &wordlen, stop: ",");
1356 }
1357 }
1358
1359 /* Remainder (str => p) is family list. Trim off trailing commas and leading and trailing white space
1360 */
1361
1362 while (last > str && g_ascii_isspace (*(last - 1)))
1363 last--;
1364
1365 if (last > str && *(last - 1) == ',')
1366 last--;
1367
1368 while (last > str && g_ascii_isspace (*(last - 1)))
1369 last--;
1370
1371 while (last > str && g_ascii_isspace (*str))
1372 str++;
1373
1374 if (str != last)
1375 {
1376 int i;
1377 char **families;
1378
1379 desc->family_name = g_strndup (str, n: last - str);
1380
1381 /* Now sanitize it to trim space from around individual family names.
1382 * bug #499624 */
1383
1384 families = g_strsplit (string: desc->family_name, delimiter: ",", max_tokens: -1);
1385
1386 for (i = 0; families[i]; i++)
1387 g_strstrip (families[i]);
1388
1389 g_free (mem: desc->family_name);
1390 desc->family_name = g_strjoinv (separator: ",", str_array: families);
1391 g_strfreev (str_array: families);
1392
1393 desc->mask |= PANGO_FONT_MASK_FAMILY;
1394 }
1395
1396 return desc;
1397}
1398
1399static void
1400append_field (GString *str, const char *what, const FieldMap *map, int n_elements, int val)
1401{
1402 int i;
1403 for (i=0; i<n_elements; i++)
1404 {
1405 if (map[i].value != val)
1406 continue;
1407
1408 if (G_LIKELY (map[i].str[0]))
1409 {
1410 if (G_LIKELY (str->len > 0 && str->str[str->len -1] != ' '))
1411 g_string_append_c (str, ' ');
1412 g_string_append (string: str, val: map[i].str);
1413 }
1414 return;
1415 }
1416
1417 if (G_LIKELY (str->len > 0 || str->str[str->len -1] != ' '))
1418 g_string_append_c (str, ' ');
1419 g_string_append_printf (string: str, format: "%s=%d", what, val);
1420}
1421
1422/**
1423 * pango_font_description_to_string:
1424 * @desc: a `PangoFontDescription`
1425 *
1426 * Creates a string representation of a font description.
1427 *
1428 * See [func@Pango.FontDescription.from_string] for a description
1429 * of the format of the string representation. The family list in
1430 * the string description will only have a terminating comma if
1431 * the last word of the list is a valid style option.
1432 *
1433 * Return value: a new string that must be freed with g_free().
1434 */
1435char *
1436pango_font_description_to_string (const PangoFontDescription *desc)
1437{
1438 GString *result;
1439
1440 g_return_val_if_fail (desc != NULL, NULL);
1441
1442 result = g_string_new (NULL);
1443
1444 if (G_LIKELY (desc->family_name && desc->mask & PANGO_FONT_MASK_FAMILY))
1445 {
1446 const char *p;
1447 size_t wordlen;
1448
1449 g_string_append (string: result, val: desc->family_name);
1450
1451 /* We need to add a trailing comma if the family name ends
1452 * in a keyword like "Bold", or if the family name ends in
1453 * a number and no keywords will be added.
1454 */
1455 p = getword (str: desc->family_name, last: desc->family_name + strlen(s: desc->family_name), wordlen: &wordlen, stop: ",");
1456 if (wordlen != 0 &&
1457 (find_field_any (str: p, len: wordlen, NULL) ||
1458 (parse_size (word: p, wordlen, NULL, NULL) &&
1459 desc->weight == PANGO_WEIGHT_NORMAL &&
1460 desc->style == PANGO_STYLE_NORMAL &&
1461 desc->stretch == PANGO_STRETCH_NORMAL &&
1462 desc->variant == PANGO_VARIANT_NORMAL &&
1463 (desc->mask & (PANGO_FONT_MASK_GRAVITY | PANGO_FONT_MASK_SIZE)) == 0)))
1464 g_string_append_c (result, ',');
1465 }
1466
1467#define FIELD(NAME, MASK) \
1468 append_field (result, G_STRINGIFY (NAME), NAME##_map, G_N_ELEMENTS (NAME##_map), desc->NAME)
1469
1470 FIELD (weight, PANGO_FONT_MASK_WEIGHT);
1471 FIELD (style, PANGO_FONT_MASK_STYLE);
1472 FIELD (stretch, PANGO_FONT_MASK_STRETCH);
1473 FIELD (variant, PANGO_FONT_MASK_VARIANT);
1474 if (desc->mask & PANGO_FONT_MASK_GRAVITY)
1475 FIELD (gravity, PANGO_FONT_MASK_GRAVITY);
1476
1477#undef FIELD
1478
1479 if (result->len == 0)
1480 g_string_append (string: result, val: "Normal");
1481
1482 if (desc->mask & PANGO_FONT_MASK_SIZE)
1483 {
1484 char buf[G_ASCII_DTOSTR_BUF_SIZE];
1485
1486 if (result->len > 0 || result->str[result->len -1] != ' ')
1487 g_string_append_c (result, ' ');
1488
1489 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: (double)desc->size / PANGO_SCALE);
1490 g_string_append (string: result, val: buf);
1491
1492 if (desc->size_is_absolute)
1493 g_string_append (string: result, val: "px");
1494 }
1495
1496 if ((desc->variations && desc->mask & PANGO_FONT_MASK_VARIATIONS) &&
1497 desc->variations[0] != '\0')
1498 {
1499 g_string_append (string: result, val: " @");
1500 g_string_append (string: result, val: desc->variations);
1501 }
1502
1503 return g_string_free (string: result, FALSE);
1504}
1505
1506/**
1507 * pango_font_description_to_filename:
1508 * @desc: a `PangoFontDescription`
1509 *
1510 * Creates a filename representation of a font description.
1511 *
1512 * The filename is identical to the result from calling
1513 * [method@Pango.FontDescription.to_string], but with underscores
1514 * instead of characters that are untypical in filenames, and in
1515 * lower case only.
1516 *
1517 * Return value: a new string that must be freed with g_free().
1518 */
1519char *
1520pango_font_description_to_filename (const PangoFontDescription *desc)
1521{
1522 char *result;
1523 char *p;
1524
1525 g_return_val_if_fail (desc != NULL, NULL);
1526
1527 result = pango_font_description_to_string (desc);
1528
1529 p = result;
1530 while (*p)
1531 {
1532 if (G_UNLIKELY ((guchar) *p >= 128))
1533 /* skip over non-ASCII chars */;
1534 else if (strchr (s: "-+_.", c: *p) == NULL && !g_ascii_isalnum (*p))
1535 *p = '_';
1536 else
1537 *p = g_ascii_tolower (c: *p);
1538 p++;
1539 }
1540
1541 return result;
1542}
1543
1544static gboolean
1545parse_field (const char *what,
1546 const FieldMap *map,
1547 int n_elements,
1548 const char *str,
1549 int *val,
1550 gboolean warn)
1551{
1552 gboolean found;
1553 int len = strlen (s: str);
1554
1555 if (G_UNLIKELY (*str == '\0'))
1556 return FALSE;
1557
1558 if (field_matches (s1: "Normal", s2: str, n: len))
1559 {
1560 /* find the map entry with empty string */
1561 int i;
1562
1563 for (i = 0; i < n_elements; i++)
1564 if (map[i].str[0] == '\0')
1565 {
1566 *val = map[i].value;
1567 return TRUE;
1568 }
1569
1570 *val = 0;
1571 return TRUE;
1572 }
1573
1574 found = find_field (NULL, map, n_elements, str, len, val);
1575
1576 if (!found && warn)
1577 {
1578 int i;
1579 GString *s = g_string_new (NULL);
1580
1581 for (i = 0; i < n_elements; i++)
1582 {
1583 if (i)
1584 g_string_append_c (s, '/');
1585 g_string_append (string: s, val: map[i].str[0] == '\0' ? "Normal" : map[i].str);
1586 }
1587
1588 g_warning ("%s must be one of %s or a number",
1589 what,
1590 s->str);
1591
1592 g_string_free (string: s, TRUE);
1593 }
1594
1595 return found;
1596}
1597
1598#define FIELD(NAME, MASK) \
1599 parse_field (G_STRINGIFY (NAME), NAME##_map, G_N_ELEMENTS (NAME##_map), str, (int *)(void *)NAME, warn)
1600
1601/**
1602 * pango_parse_style:
1603 * @str: a string to parse.
1604 * @style: (out): a `PangoStyle` to store the result in.
1605 * @warn: if %TRUE, issue a g_warning() on bad input.
1606 *
1607 * Parses a font style.
1608 *
1609 * The allowed values are "normal", "italic" and "oblique", case
1610 * variations being
1611 * ignored.
1612 *
1613 * Return value: %TRUE if @str was successfully parsed.
1614 */
1615gboolean
1616pango_parse_style (const char *str,
1617 PangoStyle *style,
1618 gboolean warn)
1619{
1620 return FIELD (style, PANGO_FONT_MASK_STYLE);
1621}
1622
1623/**
1624 * pango_parse_variant:
1625 * @str: a string to parse.
1626 * @variant: (out): a `PangoVariant` to store the result in.
1627 * @warn: if %TRUE, issue a g_warning() on bad input.
1628 *
1629 * Parses a font variant.
1630 *
1631 * The allowed values are "normal", "small-caps", "all-small-caps",
1632 * "petite-caps", "all-petite-caps", "unicase" and "title-caps",
1633 * case variations being ignored.
1634 *
1635 * Return value: %TRUE if @str was successfully parsed.
1636 */
1637gboolean
1638pango_parse_variant (const char *str,
1639 PangoVariant *variant,
1640 gboolean warn)
1641{
1642 return FIELD (variant, PANGO_FONT_MASK_VARIANT);
1643}
1644
1645/**
1646 * pango_parse_weight:
1647 * @str: a string to parse.
1648 * @weight: (out): a `PangoWeight` to store the result in.
1649 * @warn: if %TRUE, issue a g_warning() on bad input.
1650 *
1651 * Parses a font weight.
1652 *
1653 * The allowed values are "heavy",
1654 * "ultrabold", "bold", "normal", "light", "ultraleight"
1655 * and integers. Case variations are ignored.
1656 *
1657 * Return value: %TRUE if @str was successfully parsed.
1658 */
1659gboolean
1660pango_parse_weight (const char *str,
1661 PangoWeight *weight,
1662 gboolean warn)
1663{
1664 return FIELD (weight, PANGO_FONT_MASK_WEIGHT);
1665}
1666
1667/**
1668 * pango_parse_stretch:
1669 * @str: a string to parse.
1670 * @stretch: (out): a `PangoStretch` to store the result in.
1671 * @warn: if %TRUE, issue a g_warning() on bad input.
1672 *
1673 * Parses a font stretch.
1674 *
1675 * The allowed values are
1676 * "ultra_condensed", "extra_condensed", "condensed",
1677 * "semi_condensed", "normal", "semi_expanded", "expanded",
1678 * "extra_expanded" and "ultra_expanded". Case variations are
1679 * ignored and the '_' characters may be omitted.
1680 *
1681 * Return value: %TRUE if @str was successfully parsed.
1682 */
1683gboolean
1684pango_parse_stretch (const char *str,
1685 PangoStretch *stretch,
1686 gboolean warn)
1687{
1688 return FIELD (stretch, PANGO_FONT_MASK_STRETCH);
1689}
1690
1691
1692/*
1693 * PangoFont
1694 */
1695
1696typedef struct {
1697 hb_font_t *hb_font;
1698} PangoFontPrivate;
1699
1700#define PANGO_FONT_GET_CLASS_PRIVATE(font) ((PangoFontClassPrivate *) \
1701 g_type_class_get_private ((GTypeClass *) PANGO_FONT_GET_CLASS (font), \
1702 PANGO_TYPE_FONT))
1703
1704G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoFont, pango_font, G_TYPE_OBJECT,
1705 G_ADD_PRIVATE (PangoFont)
1706 g_type_add_class_private (g_define_type_id, sizeof (PangoFontClassPrivate)))
1707
1708static void
1709pango_font_finalize (GObject *object)
1710{
1711 PangoFont *font = PANGO_FONT (object);
1712 PangoFontPrivate *priv = pango_font_get_instance_private (self: font);
1713
1714 hb_font_destroy (font: priv->hb_font);
1715
1716 G_OBJECT_CLASS (pango_font_parent_class)->finalize (object);
1717}
1718
1719static PangoLanguage **
1720pango_font_default_get_languages (PangoFont *font)
1721{
1722 return NULL;
1723}
1724
1725static gboolean
1726pango_font_default_is_hinted (PangoFont *font)
1727{
1728 return FALSE;
1729}
1730
1731static void
1732pango_font_default_get_scale_factors (PangoFont *font,
1733 double *x_scale,
1734 double *y_scale)
1735{
1736 *x_scale = *y_scale = 1.0;
1737}
1738
1739static gboolean
1740pango_font_default_has_char (PangoFont *font,
1741 gunichar wc)
1742{
1743 PangoCoverage *coverage = pango_font_get_coverage (font, language: pango_language_get_default ());
1744 PangoCoverageLevel result = pango_coverage_get (coverage, index_: wc);
1745 g_object_unref (object: coverage);
1746 return result != PANGO_COVERAGE_NONE;
1747}
1748
1749static PangoFontFace *
1750pango_font_default_get_face (PangoFont *font)
1751{
1752 PangoFontMap *map = pango_font_get_font_map (font);
1753
1754 return PANGO_FONT_MAP_GET_CLASS (map)->get_face (map,font);
1755}
1756
1757static void
1758pango_font_default_get_matrix (PangoFont *font,
1759 PangoMatrix *matrix)
1760{
1761 *matrix = (PangoMatrix) PANGO_MATRIX_INIT;
1762}
1763
1764static int
1765pango_font_default_get_absolute_size (PangoFont *font)
1766{
1767 PangoFontDescription *desc;
1768 int size;
1769
1770 desc = pango_font_describe_with_absolute_size (font);
1771 size = pango_font_description_get_size (desc);
1772 pango_font_description_free (desc);
1773
1774 return size;
1775}
1776
1777static void
1778pango_font_class_init (PangoFontClass *class G_GNUC_UNUSED)
1779{
1780 GObjectClass *object_class = G_OBJECT_CLASS (class);
1781 PangoFontClassPrivate *pclass;
1782
1783 object_class->finalize = pango_font_finalize;
1784
1785 pclass = g_type_class_get_private (klass: (GTypeClass *) class, PANGO_TYPE_FONT);
1786
1787 pclass->get_languages = pango_font_default_get_languages;
1788 pclass->is_hinted = pango_font_default_is_hinted;
1789 pclass->get_scale_factors = pango_font_default_get_scale_factors;
1790 pclass->has_char = pango_font_default_has_char;
1791 pclass->get_face = pango_font_default_get_face;
1792 pclass->get_matrix = pango_font_default_get_matrix;
1793 pclass->get_absolute_size = pango_font_default_get_absolute_size;
1794}
1795
1796static void
1797pango_font_init (PangoFont *font G_GNUC_UNUSED)
1798{
1799}
1800
1801/**
1802 * pango_font_describe:
1803 * @font: a `PangoFont`
1804 *
1805 * Returns a description of the font, with font size set in points.
1806 *
1807 * Use [method@Pango.Font.describe_with_absolute_size] if you want
1808 * the font size in device units.
1809 *
1810 * Return value: a newly-allocated `PangoFontDescription` object.
1811 */
1812PangoFontDescription *
1813pango_font_describe (PangoFont *font)
1814{
1815 g_return_val_if_fail (font != NULL, NULL);
1816
1817 return PANGO_FONT_GET_CLASS (font)->describe (font);
1818}
1819
1820/**
1821 * pango_font_describe_with_absolute_size:
1822 * @font: a `PangoFont`
1823 *
1824 * Returns a description of the font, with absolute font size set
1825 * in device units.
1826 *
1827 * Use [method@Pango.Font.describe] if you want the font size in points.
1828 *
1829 * Return value: a newly-allocated `PangoFontDescription` object.
1830 *
1831 * Since: 1.14
1832 */
1833PangoFontDescription *
1834pango_font_describe_with_absolute_size (PangoFont *font)
1835{
1836 g_return_val_if_fail (font != NULL, NULL);
1837
1838 if (G_UNLIKELY (!PANGO_FONT_GET_CLASS (font)->describe_absolute))
1839 {
1840 g_warning ("describe_absolute not implemented for this font class, report this as a bug");
1841 return pango_font_describe (font);
1842 }
1843
1844 return PANGO_FONT_GET_CLASS (font)->describe_absolute (font);
1845}
1846
1847/**
1848 * pango_font_get_coverage:
1849 * @font: a `PangoFont`
1850 * @language: the language tag
1851 *
1852 * Computes the coverage map for a given font and language tag.
1853 *
1854 * Return value: (transfer full): a newly-allocated `PangoCoverage`
1855 * object.
1856 */
1857PangoCoverage *
1858pango_font_get_coverage (PangoFont *font,
1859 PangoLanguage *language)
1860{
1861 g_return_val_if_fail (font != NULL, NULL);
1862
1863 return PANGO_FONT_GET_CLASS (font)->get_coverage (font, language);
1864}
1865
1866/**
1867 * pango_font_find_shaper:
1868 * @font: a `PangoFont`
1869 * @language: the language tag
1870 * @ch: a Unicode character.
1871 *
1872 * Finds the best matching shaper for a font for a particular
1873 * language tag and character point.
1874 *
1875 * Return value: (transfer none): the best matching shaper.
1876 * Deprecated: Shape engines are no longer used
1877 */
1878PangoEngineShape *
1879pango_font_find_shaper (PangoFont *font,
1880 PangoLanguage *language,
1881 guint32 ch)
1882{
1883 return NULL;
1884}
1885
1886/**
1887 * pango_font_get_glyph_extents:
1888 * @font: (nullable): a `PangoFont`
1889 * @glyph: the glyph index
1890 * @ink_rect: (out) (optional): rectangle used to store the extents of the glyph as drawn
1891 * @logical_rect: (out) (optional): rectangle used to store the logical extents of the glyph
1892 *
1893 * Gets the logical and ink extents of a glyph within a font.
1894 *
1895 * The coordinate system for each rectangle has its origin at the
1896 * base line and horizontal origin of the character with increasing
1897 * coordinates extending to the right and down. The macros PANGO_ASCENT(),
1898 * PANGO_DESCENT(), PANGO_LBEARING(), and PANGO_RBEARING() can be used to convert
1899 * from the extents rectangle to more traditional font metrics. The units
1900 * of the rectangles are in 1/PANGO_SCALE of a device unit.
1901 *
1902 * If @font is %NULL, this function gracefully sets some sane values in the
1903 * output variables and returns.
1904 */
1905void
1906pango_font_get_glyph_extents (PangoFont *font,
1907 PangoGlyph glyph,
1908 PangoRectangle *ink_rect,
1909 PangoRectangle *logical_rect)
1910{
1911 if (G_UNLIKELY (!font))
1912 {
1913 if (ink_rect)
1914 {
1915 ink_rect->x = PANGO_SCALE;
1916 ink_rect->y = - (PANGO_UNKNOWN_GLYPH_HEIGHT - 1) * PANGO_SCALE;
1917 ink_rect->height = (PANGO_UNKNOWN_GLYPH_HEIGHT - 2) * PANGO_SCALE;
1918 ink_rect->width = (PANGO_UNKNOWN_GLYPH_WIDTH - 2) * PANGO_SCALE;
1919 }
1920 if (logical_rect)
1921 {
1922 logical_rect->x = 0;
1923 logical_rect->y = - PANGO_UNKNOWN_GLYPH_HEIGHT * PANGO_SCALE;
1924 logical_rect->height = PANGO_UNKNOWN_GLYPH_HEIGHT * PANGO_SCALE;
1925 logical_rect->width = PANGO_UNKNOWN_GLYPH_WIDTH * PANGO_SCALE;
1926 }
1927 return;
1928 }
1929
1930 PANGO_FONT_GET_CLASS (font)->get_glyph_extents (font, glyph, ink_rect, logical_rect);
1931}
1932
1933/**
1934 * pango_font_get_metrics:
1935 * @font: (nullable): a `PangoFont`
1936 * @language: (nullable): language tag used to determine which script
1937 * to get the metrics for, or %NULL to indicate to get the metrics for
1938 * the entire font.
1939 *
1940 * Gets overall metric information for a font.
1941 *
1942 * Since the metrics may be substantially different for different scripts,
1943 * a language tag can be provided to indicate that the metrics should be
1944 * retrieved that correspond to the script(s) used by that language.
1945 *
1946 * If @font is %NULL, this function gracefully sets some sane values in the
1947 * output variables and returns.
1948 *
1949 * Return value: a `PangoFontMetrics` object. The caller must call
1950 * [method@Pango.FontMetrics.unref] when finished using the object.
1951 */
1952PangoFontMetrics *
1953pango_font_get_metrics (PangoFont *font,
1954 PangoLanguage *language)
1955{
1956 if (G_UNLIKELY (!font))
1957 {
1958 PangoFontMetrics *metrics = pango_font_metrics_new ();
1959
1960 metrics->ascent = PANGO_SCALE * PANGO_UNKNOWN_GLYPH_HEIGHT;
1961 metrics->descent = 0;
1962 metrics->height = 0;
1963 metrics->approximate_char_width = PANGO_SCALE * PANGO_UNKNOWN_GLYPH_WIDTH;
1964 metrics->approximate_digit_width = PANGO_SCALE * PANGO_UNKNOWN_GLYPH_WIDTH;
1965 metrics->underline_position = -PANGO_SCALE;
1966 metrics->underline_thickness = PANGO_SCALE;
1967 metrics->strikethrough_position = PANGO_SCALE * PANGO_UNKNOWN_GLYPH_HEIGHT / 2;
1968 metrics->strikethrough_thickness = PANGO_SCALE;
1969
1970 return metrics;
1971 }
1972
1973 return PANGO_FONT_GET_CLASS (font)->get_metrics (font, language);
1974}
1975
1976/**
1977 * pango_font_get_font_map:
1978 * @font: (nullable): a `PangoFont`
1979 *
1980 * Gets the font map for which the font was created.
1981 *
1982 * Note that the font maintains a *weak* reference to
1983 * the font map, so if all references to font map are
1984 * dropped, the font map will be finalized even if there
1985 * are fonts created with the font map that are still alive.
1986 * In that case this function will return %NULL.
1987 *
1988 * It is the responsibility of the user to ensure that the
1989 * font map is kept alive. In most uses this is not an issue
1990 * as a `PangoContext` holds a reference to the font map.
1991 *
1992 * Return value: (transfer none) (nullable): the `PangoFontMap`
1993 * for the font
1994 *
1995 * Since: 1.10
1996 */
1997PangoFontMap *
1998pango_font_get_font_map (PangoFont *font)
1999{
2000 if (G_UNLIKELY (!font))
2001 return NULL;
2002
2003 if (PANGO_FONT_GET_CLASS (font)->get_font_map)
2004 return PANGO_FONT_GET_CLASS (font)->get_font_map (font);
2005 else
2006 return NULL;
2007}
2008
2009/**
2010 * pango_font_get_face:
2011 * @font: a `PangoFont`
2012 *
2013 * Gets the `PangoFontFace` to which @font belongs.
2014 *
2015 * Returns: (transfer none): the `PangoFontFace`
2016 *
2017 * Since: 1.46
2018 */
2019PangoFontFace *
2020pango_font_get_face (PangoFont *font)
2021{
2022 PangoFontClassPrivate *pclass = PANGO_FONT_GET_CLASS_PRIVATE (font);
2023
2024 return pclass->get_face (font);
2025}
2026
2027/**
2028 * pango_font_get_hb_font: (skip)
2029 * @font: a `PangoFont`
2030 *
2031 * Get a `hb_font_t` object backing this font.
2032 *
2033 * Note that the objects returned by this function are cached
2034 * and immutable. If you need to make changes to the `hb_font_t`,
2035 * use [hb_font_create_sub_font()](https://harfbuzz.github.io/harfbuzz-hb-font.html#hb-font-create-sub-font).
2036 *
2037 * Returns: (transfer none) (nullable): the `hb_font_t` object
2038 * backing the font
2039 *
2040 * Since: 1.44
2041 */
2042hb_font_t *
2043pango_font_get_hb_font (PangoFont *font)
2044{
2045 PangoFontPrivate *priv = pango_font_get_instance_private (self: font);
2046
2047 g_return_val_if_fail (PANGO_IS_FONT (font), NULL);
2048
2049 if (priv->hb_font)
2050 return priv->hb_font;
2051
2052 priv->hb_font = PANGO_FONT_GET_CLASS (font)->create_hb_font (font);
2053
2054 hb_font_make_immutable (font: priv->hb_font);
2055
2056 return priv->hb_font;
2057}
2058
2059G_DEFINE_BOXED_TYPE (PangoFontMetrics, pango_font_metrics,
2060 pango_font_metrics_ref,
2061 pango_font_metrics_unref);
2062
2063/**
2064 * pango_font_metrics_new:
2065 *
2066 * Creates a new `PangoFontMetrics` structure.
2067 *
2068 * This is only for internal use by Pango backends and there is
2069 * no public way to set the fields of the structure.
2070 *
2071 * Return value: a newly-created `PangoFontMetrics` structure
2072 * with a reference count of 1.
2073 */
2074PangoFontMetrics *
2075pango_font_metrics_new (void)
2076{
2077 PangoFontMetrics *metrics = g_slice_new0 (PangoFontMetrics);
2078 metrics->ref_count = 1;
2079
2080 return metrics;
2081}
2082
2083/**
2084 * pango_font_metrics_ref:
2085 * @metrics: (nullable): a `PangoFontMetrics` structure, may be %NULL
2086 *
2087 * Increase the reference count of a font metrics structure by one.
2088 *
2089 * Return value: (nullable): @metrics
2090 */
2091PangoFontMetrics *
2092pango_font_metrics_ref (PangoFontMetrics *metrics)
2093{
2094 if (metrics == NULL)
2095 return NULL;
2096
2097 g_atomic_int_inc ((int *) &metrics->ref_count);
2098
2099 return metrics;
2100}
2101
2102/**
2103 * pango_font_metrics_unref:
2104 * @metrics: (nullable): a `PangoFontMetrics` structure, may be %NULL
2105 *
2106 * Decrease the reference count of a font metrics structure by one.
2107 *
2108 * If the result is zero, frees the structure and any associated memory.
2109 */
2110void
2111pango_font_metrics_unref (PangoFontMetrics *metrics)
2112{
2113 if (metrics == NULL)
2114 return;
2115
2116 g_return_if_fail (metrics->ref_count > 0 );
2117
2118 if (g_atomic_int_dec_and_test ((int *) &metrics->ref_count))
2119 g_slice_free (PangoFontMetrics, metrics);
2120}
2121
2122/**
2123 * pango_font_metrics_get_ascent:
2124 * @metrics: a `PangoFontMetrics` structure
2125 *
2126 * Gets the ascent from a font metrics structure.
2127 *
2128 * The ascent is the distance from the baseline to the logical top
2129 * of a line of text. (The logical top may be above or below the top
2130 * of the actual drawn ink. It is necessary to lay out the text to
2131 * figure where the ink will be.)
2132 *
2133 * Return value: the ascent, in Pango units.
2134 */
2135int
2136pango_font_metrics_get_ascent (PangoFontMetrics *metrics)
2137{
2138 g_return_val_if_fail (metrics != NULL, 0);
2139
2140 return metrics->ascent;
2141}
2142
2143/**
2144 * pango_font_metrics_get_descent:
2145 * @metrics: a `PangoFontMetrics` structure
2146 *
2147 * Gets the descent from a font metrics structure.
2148 *
2149 * The descent is the distance from the baseline to the logical bottom
2150 * of a line of text. (The logical bottom may be above or below the
2151 * bottom of the actual drawn ink. It is necessary to lay out the text
2152 * to figure where the ink will be.)
2153 *
2154 * Return value: the descent, in Pango units.
2155 */
2156int
2157pango_font_metrics_get_descent (PangoFontMetrics *metrics)
2158{
2159 g_return_val_if_fail (metrics != NULL, 0);
2160
2161 return metrics->descent;
2162}
2163
2164/**
2165 * pango_font_metrics_get_height:
2166 * @metrics: a `PangoFontMetrics` structure
2167 *
2168 * Gets the line height from a font metrics structure.
2169 *
2170 * The line height is the recommended distance between successive
2171 * baselines in wrapped text using this font.
2172 *
2173 * If the line height is not available, 0 is returned.
2174 *
2175 * Return value: the height, in Pango units
2176 *
2177 * Since: 1.44
2178 */
2179int
2180pango_font_metrics_get_height (PangoFontMetrics *metrics)
2181{
2182 g_return_val_if_fail (metrics != NULL, 0);
2183
2184 return metrics->height;
2185}
2186
2187/**
2188 * pango_font_metrics_get_approximate_char_width:
2189 * @metrics: a `PangoFontMetrics` structure
2190 *
2191 * Gets the approximate character width for a font metrics structure.
2192 *
2193 * This is merely a representative value useful, for example, for
2194 * determining the initial size for a window. Actual characters in
2195 * text will be wider and narrower than this.
2196 *
2197 * Return value: the character width, in Pango units.
2198 */
2199int
2200pango_font_metrics_get_approximate_char_width (PangoFontMetrics *metrics)
2201{
2202 g_return_val_if_fail (metrics != NULL, 0);
2203
2204 return metrics->approximate_char_width;
2205}
2206
2207/**
2208 * pango_font_metrics_get_approximate_digit_width:
2209 * @metrics: a `PangoFontMetrics` structure
2210 *
2211 * Gets the approximate digit width for a font metrics structure.
2212 *
2213 * This is merely a representative value useful, for example, for
2214 * determining the initial size for a window. Actual digits in
2215 * text can be wider or narrower than this, though this value
2216 * is generally somewhat more accurate than the result of
2217 * pango_font_metrics_get_approximate_char_width() for digits.
2218 *
2219 * Return value: the digit width, in Pango units.
2220 */
2221int
2222pango_font_metrics_get_approximate_digit_width (PangoFontMetrics *metrics)
2223{
2224 g_return_val_if_fail (metrics != NULL, 0);
2225
2226 return metrics->approximate_digit_width;
2227}
2228
2229/**
2230 * pango_font_metrics_get_underline_position:
2231 * @metrics: a `PangoFontMetrics` structure
2232 *
2233 * Gets the suggested position to draw the underline.
2234 *
2235 * The value returned is the distance *above* the baseline of the top
2236 * of the underline. Since most fonts have underline positions beneath
2237 * the baseline, this value is typically negative.
2238 *
2239 * Return value: the suggested underline position, in Pango units.
2240 *
2241 * Since: 1.6
2242 */
2243int
2244pango_font_metrics_get_underline_position (PangoFontMetrics *metrics)
2245{
2246 g_return_val_if_fail (metrics != NULL, 0);
2247
2248 return metrics->underline_position;
2249}
2250
2251/**
2252 * pango_font_metrics_get_underline_thickness:
2253 * @metrics: a `PangoFontMetrics` structure
2254 *
2255 * Gets the suggested thickness to draw for the underline.
2256 *
2257 * Return value: the suggested underline thickness, in Pango units.
2258 *
2259 * Since: 1.6
2260 */
2261int
2262pango_font_metrics_get_underline_thickness (PangoFontMetrics *metrics)
2263{
2264 g_return_val_if_fail (metrics != NULL, 0);
2265
2266 return metrics->underline_thickness;
2267}
2268
2269/**
2270 * pango_font_metrics_get_strikethrough_position:
2271 * @metrics: a `PangoFontMetrics` structure
2272 *
2273 * Gets the suggested position to draw the strikethrough.
2274 *
2275 * The value returned is the distance *above* the
2276 * baseline of the top of the strikethrough.
2277 *
2278 * Return value: the suggested strikethrough position, in Pango units.
2279 *
2280 * Since: 1.6
2281 */
2282int
2283pango_font_metrics_get_strikethrough_position (PangoFontMetrics *metrics)
2284{
2285 g_return_val_if_fail (metrics != NULL, 0);
2286
2287 return metrics->strikethrough_position;
2288}
2289
2290/**
2291 * pango_font_metrics_get_strikethrough_thickness:
2292 * @metrics: a `PangoFontMetrics` structure
2293 *
2294 * Gets the suggested thickness to draw for the strikethrough.
2295 *
2296 * Return value: the suggested strikethrough thickness, in Pango units.
2297 *
2298 * Since: 1.6
2299 */
2300int
2301pango_font_metrics_get_strikethrough_thickness (PangoFontMetrics *metrics)
2302{
2303 g_return_val_if_fail (metrics != NULL, 0);
2304
2305 return metrics->strikethrough_thickness;
2306}
2307
2308/*
2309 * PangoFontFamily
2310 */
2311
2312static GType
2313pango_font_family_get_item_type (GListModel *list)
2314{
2315 return PANGO_TYPE_FONT_FACE;
2316}
2317
2318static guint
2319pango_font_family_get_n_items (GListModel *list)
2320{
2321 PangoFontFamily *family = PANGO_FONT_FAMILY (list);
2322 int n_faces;
2323
2324 pango_font_family_list_faces (family, NULL, n_faces: &n_faces);
2325
2326 return (guint)n_faces;
2327}
2328
2329static gpointer
2330pango_font_family_get_item (GListModel *list,
2331 guint position)
2332{
2333 PangoFontFamily *family = PANGO_FONT_FAMILY (list);
2334 PangoFontFace **faces;
2335 int n_faces;
2336 PangoFontFace *face;
2337
2338 pango_font_family_list_faces (family, faces: &faces, n_faces: &n_faces);
2339
2340 if (position < n_faces)
2341 face = g_object_ref (faces[position]);
2342 else
2343 face = NULL;
2344
2345 g_free (mem: faces);
2346
2347 return face;
2348}
2349
2350static void
2351pango_font_family_list_model_init (GListModelInterface *iface)
2352{
2353 iface->get_item_type = pango_font_family_get_item_type;
2354 iface->get_n_items = pango_font_family_get_n_items;
2355 iface->get_item = pango_font_family_get_item;
2356}
2357
2358G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PangoFontFamily, pango_font_family, G_TYPE_OBJECT,
2359 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pango_font_family_list_model_init))
2360
2361static PangoFontFace *pango_font_family_real_get_face (PangoFontFamily *family,
2362 const char *name);
2363
2364static gboolean
2365pango_font_family_default_is_monospace (PangoFontFamily *family)
2366{
2367 return FALSE;
2368}
2369
2370static gboolean
2371pango_font_family_default_is_variable (PangoFontFamily *family)
2372{
2373 return FALSE;
2374}
2375
2376static void
2377pango_font_family_default_list_faces (PangoFontFamily *family,
2378 PangoFontFace ***faces,
2379 int *n_faces)
2380{
2381 if (faces)
2382 *faces = NULL;
2383
2384 if (n_faces)
2385 *n_faces = 0;
2386}
2387
2388enum {
2389 PROP_ITEM_TYPE = 1,
2390 PROP_N_ITEMS,
2391 N_PROPERTIES
2392};
2393
2394static GParamSpec *font_family_properties[N_PROPERTIES] = { NULL };
2395
2396static void
2397pango_font_family_get_property (GObject *object,
2398 guint property_id,
2399 GValue *value,
2400 GParamSpec *pspec)
2401{
2402 switch (property_id)
2403 {
2404 case PROP_ITEM_TYPE:
2405 g_value_set_gtype (value, PANGO_TYPE_FONT);
2406 break;
2407
2408 case PROP_N_ITEMS:
2409 g_value_set_uint (value, v_uint: pango_font_family_get_n_items (list: G_LIST_MODEL (ptr: object)));
2410 break;
2411
2412 default:
2413 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2414 }
2415}
2416
2417static void
2418pango_font_family_class_init (PangoFontFamilyClass *class)
2419{
2420 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
2421
2422 gobject_class->get_property = pango_font_family_get_property;
2423
2424 class->is_monospace = pango_font_family_default_is_monospace;
2425 class->is_variable = pango_font_family_default_is_variable;
2426 class->get_face = pango_font_family_real_get_face;
2427 class->list_faces = pango_font_family_default_list_faces;
2428
2429 /**
2430 * PangoFontFamily:item-type:
2431 *
2432 * The type of items contained in this list.
2433 */
2434 font_family_properties[PROP_ITEM_TYPE] =
2435 g_param_spec_gtype (name: "item-type", nick: "", blurb: "", G_TYPE_OBJECT,
2436 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2437
2438 /**
2439 * PangoFontFamily:n-items:
2440 *
2441 * The number of items contained in this list.
2442 */
2443 font_family_properties[PROP_N_ITEMS] =
2444 g_param_spec_uint (name: "n-items", nick: "", blurb: "", minimum: 0, G_MAXUINT, default_value: 0,
2445 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2446
2447 g_object_class_install_properties (oclass: gobject_class,
2448 n_pspecs: N_PROPERTIES,
2449 pspecs: font_family_properties);
2450}
2451
2452static void
2453pango_font_family_init (PangoFontFamily *family G_GNUC_UNUSED)
2454{
2455}
2456
2457/**
2458 * pango_font_family_get_name:
2459 * @family: a `PangoFontFamily`
2460 *
2461 * Gets the name of the family.
2462 *
2463 * The name is unique among all fonts for the font backend and can
2464 * be used in a `PangoFontDescription` to specify that a face from
2465 * this family is desired.
2466 *
2467 * Return value: the name of the family. This string is owned
2468 * by the family object and must not be modified or freed.
2469 */
2470const char *
2471pango_font_family_get_name (PangoFontFamily *family)
2472{
2473 g_return_val_if_fail (PANGO_IS_FONT_FAMILY (family), NULL);
2474
2475 return PANGO_FONT_FAMILY_GET_CLASS (family)->get_name (family);
2476}
2477
2478/**
2479 * pango_font_family_list_faces:
2480 * @family: a `PangoFontFamily`
2481 * @faces: (out) (optional) (array length=n_faces) (transfer container):
2482 * location to store an array of pointers to `PangoFontFace` objects,
2483 * or %NULL. This array should be freed with g_free() when it is no
2484 * longer needed.
2485 * @n_faces: (out): location to store number of elements in @faces.
2486 *
2487 * Lists the different font faces that make up @family.
2488 *
2489 * The faces in a family share a common design, but differ in slant, weight,
2490 * width and other aspects.
2491 *
2492 * Note that the returned faces are not in any particular order, and
2493 * multiple faces may have the same name or characteristics.
2494 *
2495 * `PangoFontFamily` also implemented the [iface@Gio.ListModel] interface
2496 * for enumerating faces.
2497 */
2498void
2499pango_font_family_list_faces (PangoFontFamily *family,
2500 PangoFontFace ***faces,
2501 int *n_faces)
2502{
2503 g_return_if_fail (PANGO_IS_FONT_FAMILY (family));
2504
2505 PANGO_FONT_FAMILY_GET_CLASS (family)->list_faces (family, faces, n_faces);
2506}
2507
2508static PangoFontFace *
2509pango_font_family_real_get_face (PangoFontFamily *family,
2510 const char *name)
2511{
2512 PangoFontFace **faces;
2513 int n_faces;
2514 PangoFontFace *face;
2515 int i;
2516
2517 pango_font_family_list_faces (family, faces: &faces, n_faces: &n_faces);
2518
2519 face = NULL;
2520 if (name == NULL && n_faces > 0)
2521 {
2522 face = faces[0];
2523 }
2524 else
2525 {
2526 for (i = 0; i < n_faces; i++)
2527 {
2528 if (strcmp (s1: name, s2: pango_font_face_get_face_name (face: faces[i])) == 0)
2529 {
2530 face = faces[i];
2531 break;
2532 }
2533 }
2534 }
2535
2536 g_free (mem: faces);
2537
2538 return face;
2539}
2540
2541/**
2542 * pango_font_family_get_face:
2543 * @family: a `PangoFontFamily`
2544 * @name: (nullable): the name of a face. If the name is %NULL,
2545 * the family's default face (fontconfig calls it "Regular")
2546 * will be returned.
2547 *
2548 * Gets the `PangoFontFace` of @family with the given name.
2549 *
2550 * Returns: (transfer none) (nullable): the `PangoFontFace`,
2551 * or %NULL if no face with the given name exists.
2552 *
2553 * Since: 1.46
2554 */
2555PangoFontFace *
2556pango_font_family_get_face (PangoFontFamily *family,
2557 const char *name)
2558{
2559 g_return_val_if_fail (PANGO_IS_FONT_FAMILY (family), NULL);
2560
2561 return PANGO_FONT_FAMILY_GET_CLASS (family)->get_face (family, name);
2562}
2563
2564/**
2565 * pango_font_family_is_monospace:
2566 * @family: a `PangoFontFamily`
2567 *
2568 * A monospace font is a font designed for text display where the the
2569 * characters form a regular grid.
2570 *
2571 * For Western languages this would
2572 * mean that the advance width of all characters are the same, but
2573 * this categorization also includes Asian fonts which include
2574 * double-width characters: characters that occupy two grid cells.
2575 * g_unichar_iswide() returns a result that indicates whether a
2576 * character is typically double-width in a monospace font.
2577 *
2578 * The best way to find out the grid-cell size is to call
2579 * [method@Pango.FontMetrics.get_approximate_digit_width], since the
2580 * results of [method@Pango.FontMetrics.get_approximate_char_width] may
2581 * be affected by double-width characters.
2582 *
2583 * Return value: %TRUE if the family is monospace.
2584 *
2585 * Since: 1.4
2586 */
2587gboolean
2588pango_font_family_is_monospace (PangoFontFamily *family)
2589{
2590 g_return_val_if_fail (PANGO_IS_FONT_FAMILY (family), FALSE);
2591
2592 return PANGO_FONT_FAMILY_GET_CLASS (family)->is_monospace (family);
2593}
2594
2595/**
2596 * pango_font_family_is_variable:
2597 * @family: a `PangoFontFamily`
2598 *
2599 * A variable font is a font which has axes that can be modified to
2600 * produce different faces.
2601 *
2602 * Such axes are also known as _variations_; see
2603 * [method@Pango.FontDescription.set_variations] for more information.
2604 *
2605 * Return value: %TRUE if the family is variable
2606 *
2607 * Since: 1.44
2608 */
2609gboolean
2610pango_font_family_is_variable (PangoFontFamily *family)
2611{
2612 g_return_val_if_fail (PANGO_IS_FONT_FAMILY (family), FALSE);
2613
2614 return PANGO_FONT_FAMILY_GET_CLASS (family)->is_variable (family);
2615}
2616
2617/*
2618 * PangoFontFace
2619 */
2620
2621G_DEFINE_ABSTRACT_TYPE (PangoFontFace, pango_font_face, G_TYPE_OBJECT)
2622
2623static void
2624pango_font_face_class_init (PangoFontFaceClass *class G_GNUC_UNUSED)
2625{
2626}
2627
2628static void
2629pango_font_face_init (PangoFontFace *face G_GNUC_UNUSED)
2630{
2631}
2632
2633/**
2634 * pango_font_face_describe:
2635 * @face: a `PangoFontFace`
2636 *
2637 * Returns a font description that matches the face.
2638 *
2639 * The resulting font description will have the family, style,
2640 * variant, weight and stretch of the face, but its size field
2641 * will be unset.
2642 *
2643 * Return value: a newly-created `PangoFontDescription` structure
2644 * holding the description of the face. Use [method@Pango.FontDescription.free]
2645 * to free the result.
2646 */
2647PangoFontDescription *
2648pango_font_face_describe (PangoFontFace *face)
2649{
2650 g_return_val_if_fail (PANGO_IS_FONT_FACE (face), NULL);
2651
2652 return PANGO_FONT_FACE_GET_CLASS (face)->describe (face);
2653}
2654
2655/**
2656 * pango_font_face_is_synthesized:
2657 * @face: a `PangoFontFace`
2658 *
2659 * Returns whether a `PangoFontFace` is synthesized.
2660 *
2661 * This will be the case if the underlying font rendering engine
2662 * creates this face from another face, by shearing, emboldening,
2663 * lightening or modifying it in some other way.
2664 *
2665 * Return value: whether @face is synthesized
2666 *
2667 * Since: 1.18
2668 */
2669gboolean
2670pango_font_face_is_synthesized (PangoFontFace *face)
2671{
2672 g_return_val_if_fail (PANGO_IS_FONT_FACE (face), FALSE);
2673
2674 if (PANGO_FONT_FACE_GET_CLASS (face)->is_synthesized != NULL)
2675 return PANGO_FONT_FACE_GET_CLASS (face)->is_synthesized (face);
2676 else
2677 return FALSE;
2678}
2679
2680/**
2681 * pango_font_face_get_face_name:
2682 * @face: a `PangoFontFace`.
2683 *
2684 * Gets a name representing the style of this face.
2685 *
2686 * Note that a font family may contain multiple faces
2687 * with the same name (e.g. a variable and a non-variable
2688 * face for the same style).
2689 *
2690 * Return value: the face name for the face. This string is
2691 * owned by the face object and must not be modified or freed.
2692 */
2693const char *
2694pango_font_face_get_face_name (PangoFontFace *face)
2695{
2696 g_return_val_if_fail (PANGO_IS_FONT_FACE (face), NULL);
2697
2698 return PANGO_FONT_FACE_GET_CLASS (face)->get_face_name (face);
2699}
2700
2701/**
2702 * pango_font_face_list_sizes:
2703 * @face: a `PangoFontFace`.
2704 * @sizes: (out) (array length=n_sizes) (nullable) (optional):
2705 * location to store a pointer to an array of int. This array
2706 * should be freed with g_free().
2707 * @n_sizes: location to store the number of elements in @sizes
2708 *
2709 * List the available sizes for a font.
2710 *
2711 * This is only applicable to bitmap fonts. For scalable fonts, stores
2712 * %NULL at the location pointed to by @sizes and 0 at the location pointed
2713 * to by @n_sizes. The sizes returned are in Pango units and are sorted
2714 * in ascending order.
2715 *
2716 * Since: 1.4
2717 */
2718void
2719pango_font_face_list_sizes (PangoFontFace *face,
2720 int **sizes,
2721 int *n_sizes)
2722{
2723 g_return_if_fail (PANGO_IS_FONT_FACE (face));
2724 g_return_if_fail (sizes == NULL || n_sizes != NULL);
2725
2726 if (n_sizes == NULL)
2727 return;
2728
2729 if (PANGO_FONT_FACE_GET_CLASS (face)->list_sizes != NULL)
2730 PANGO_FONT_FACE_GET_CLASS (face)->list_sizes (face, sizes, n_sizes);
2731 else
2732 {
2733 if (sizes != NULL)
2734 *sizes = NULL;
2735 *n_sizes = 0;
2736 }
2737}
2738
2739/**
2740 * pango_font_face_get_family:
2741 * @face: a `PangoFontFace`
2742 *
2743 * Gets the `PangoFontFamily` that @face belongs to.
2744 *
2745 * Returns: (transfer none): the `PangoFontFamily`
2746 *
2747 * Since: 1.46
2748 */
2749PangoFontFamily *
2750pango_font_face_get_family (PangoFontFace *face)
2751{
2752 g_return_val_if_fail (PANGO_IS_FONT_FACE (face), NULL);
2753
2754 return PANGO_FONT_FACE_GET_CLASS (face)->get_family (face);
2755}
2756
2757/**
2758 * pango_font_has_char:
2759 * @font: a `PangoFont`
2760 * @wc: a Unicode character
2761 *
2762 * Returns whether the font provides a glyph for this character.
2763 *
2764 * Returns: `TRUE` if @font can render @wc
2765 *
2766 * Since: 1.44
2767 */
2768gboolean
2769pango_font_has_char (PangoFont *font,
2770 gunichar wc)
2771{
2772 PangoFontClassPrivate *pclass = PANGO_FONT_GET_CLASS_PRIVATE (font);
2773
2774 return pclass->has_char (font, wc);
2775}
2776
2777/**
2778 * pango_font_get_features:
2779 * @font: a `PangoFont`
2780 * @features: (out caller-allocates) (array length=len): Array to features in
2781 * @len: the length of @features
2782 * @num_features: (inout): the number of used items in @features
2783 *
2784 * Obtain the OpenType features that are provided by the font.
2785 *
2786 * These are passed to the rendering system, together with features
2787 * that have been explicitly set via attributes.
2788 *
2789 * Note that this does not include OpenType features which the
2790 * rendering system enables by default.
2791 *
2792 * Since: 1.44
2793 */
2794void
2795pango_font_get_features (PangoFont *font,
2796 hb_feature_t *features,
2797 guint len,
2798 guint *num_features)
2799{
2800 if (PANGO_FONT_GET_CLASS (font)->get_features)
2801 PANGO_FONT_GET_CLASS (font)->get_features (font, features, len, num_features);
2802}
2803
2804/**
2805 * pango_font_get_languages:
2806 * @font: a `PangoFont`
2807 *
2808 * Returns the languages that are supported by @font.
2809 *
2810 * If the font backend does not provide this information,
2811 * %NULL is returned. For the fontconfig backend, this
2812 * corresponds to the FC_LANG member of the FcPattern.
2813 *
2814 * The returned array is only valid as long as the font
2815 * and its fontmap are valid.
2816 *
2817 * Returns: (transfer none) (nullable) (array zero-terminated=1) (element-type PangoLanguage): an array of `PangoLanguage`
2818 *
2819 * Since: 1.50
2820 */
2821PangoLanguage **
2822pango_font_get_languages (PangoFont *font)
2823{
2824 PangoFontClassPrivate *pclass = PANGO_FONT_GET_CLASS_PRIVATE (font);
2825
2826 return pclass->get_languages (font);
2827}
2828
2829/*< private >
2830 * pango_font_get_matrix:
2831 * @font: a `PangoFont`
2832 *
2833 * Gets the matrix for the transformation from 'font space' to 'user space'.
2834 */
2835void
2836pango_font_get_matrix (PangoFont *font,
2837 PangoMatrix *matrix)
2838{
2839 PangoFontClassPrivate *pclass = PANGO_FONT_GET_CLASS_PRIVATE (font);
2840
2841 pclass->get_matrix (font, matrix);
2842}
2843
2844/*< private >
2845 * pango_font_is_hinted:
2846 * @font: a `PangoFont`
2847 *
2848 * Gets whether this font is hinted.
2849 *
2850 * Returns: %TRUE if @font is hinted
2851 */
2852gboolean
2853pango_font_is_hinted (PangoFont *font)
2854{
2855 return PANGO_FONT_GET_CLASS_PRIVATE (font)->is_hinted (font);
2856}
2857
2858/*< private >
2859 * pango_font_get_scale_factors:
2860 * @font: a `PangoFont`
2861 * @x_scale: return location for X scale
2862 * @y_scale: return location for Y scale
2863 *
2864 * Gets the font scale factors of the ctm for this font.
2865 *
2866 * The ctm is the matrix set on the context that this font was
2867 * loaded for.
2868 */
2869void
2870pango_font_get_scale_factors (PangoFont *font,
2871 double *x_scale,
2872 double *y_scale)
2873{
2874 PANGO_FONT_GET_CLASS_PRIVATE (font)->get_scale_factors (font, x_scale, y_scale);
2875}
2876

source code of gtk/subprojects/pango/pango/fonts.c