diff options
Diffstat (limited to 'pango2/pango-font-description.c')
-rw-r--r-- | pango2/pango-font-description.c | 1743 |
1 files changed, 1743 insertions, 0 deletions
diff --git a/pango2/pango-font-description.c b/pango2/pango-font-description.c new file mode 100644 index 00000000..db22853b --- /dev/null +++ b/pango2/pango-font-description.c @@ -0,0 +1,1743 @@ +/* Pango2 + * pango-font-description.c: + * + * Copyright (C) 1999 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include <stdlib.h> + +#include "pango-font-description-private.h" + +struct _Pango2FontDescription +{ + char *family_name; + + Pango2Style style; + Pango2Variant variant; + Pango2Weight weight; + Pango2Stretch stretch; + Pango2Gravity gravity; + + int size; + + char *variations; + char *faceid; + + guint16 mask; + guint static_family : 1; + guint static_variations : 1; + guint static_faceid : 1; + guint size_is_absolute : 1; +}; + +G_DEFINE_BOXED_TYPE (Pango2FontDescription, pango2_font_description, + pango2_font_description_copy, + pango2_font_description_free); + +static const Pango2FontDescription pfd_defaults = { + NULL, /* family_name */ + + PANGO2_STYLE_NORMAL, /* style */ + PANGO2_VARIANT_NORMAL, /* variant */ + PANGO2_WEIGHT_NORMAL, /* weight */ + PANGO2_STRETCH_NORMAL, /* stretch */ + PANGO2_GRAVITY_SOUTH, /* gravity */ + 0, /* size */ + NULL, /* variations */ + NULL, /* faceid */ + + 0, /* mask */ + 0, /* static_family */ + 0, /* static_variations */ + 0, /* static_faceid */ + 0, /* size_is_absolute */ +}; + +/** + * pango2_font_description_new: + * + * Creates a new font description structure with all fields unset. + * + * Return value: the newly allocated `Pango2FontDescription`, which + * should be freed using [method@Pango2.FontDescription.free] + */ +Pango2FontDescription * +pango2_font_description_new (void) +{ + Pango2FontDescription *desc = g_slice_new (Pango2FontDescription); + + *desc = pfd_defaults; + + return desc; +} + +/** + * pango2_font_description_set_family: + * @desc: a `Pango2FontDescription`. + * @family: a string representing the family name. + * + * Sets the family name field of a font description. + * + * The family + * name represents a family of related font styles, and will + * resolve to a particular `Pango2FontFamily`. In some uses of + * `Pango2FontDescription`, it is also possible to use a comma + * separated list of family names for this field. + */ +void +pango2_font_description_set_family (Pango2FontDescription *desc, + const char *family) +{ + g_return_if_fail (desc != NULL); + + pango2_font_description_set_family_static (desc, family ? g_strdup (family) : NULL); + if (family) + desc->static_family = FALSE; +} + +/** + * pango2_font_description_set_family_static: + * @desc: a `Pango2FontDescription` + * @family: a string representing the family name + * + * Sets the family name field of a font description, without copying the string. + * + * This is like [method@Pango2.FontDescription.set_family], except that no + * copy of @family is made. The caller must make sure that the + * string passed in stays around until @desc has been freed or the + * name is set again. This function can be used if @family is a static + * string such as a C string literal, or if @desc is only needed temporarily. + */ +void +pango2_font_description_set_family_static (Pango2FontDescription *desc, + const char *family) +{ + g_return_if_fail (desc != NULL); + + if (desc->family_name == family) + return; + + if (desc->family_name && !desc->static_family) + g_free (desc->family_name); + + if (family) + { + desc->family_name = (char *)family; + desc->static_family = TRUE; + desc->mask |= PANGO2_FONT_MASK_FAMILY; + } + else + { + desc->family_name = pfd_defaults.family_name; + desc->static_family = pfd_defaults.static_family; + desc->mask &= ~PANGO2_FONT_MASK_FAMILY; + } +} + +/** + * pango2_font_description_get_family: + * @desc: a `Pango2FontDescription`. + * + * Gets the family name field of a font description. + * + * See [method@Pango2.FontDescription.set_family]. + * + * Return value: (nullable): the family name field for the font + * description, or %NULL if not previously set. This has the same + * life-time as the font description itself and should not be freed. + */ +const char * +pango2_font_description_get_family (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, NULL); + + return desc->family_name; +} + +/** + * pango2_font_description_set_style: + * @desc: a `Pango2FontDescription` + * @style: the style for the font description + * + * Sets the style field of a `Pango2FontDescription`. + * + * The [enum@Pango2.Style] enumeration describes whether the font is + * slanted and the manner in which it is slanted; it can be either + * %PANGO2_STYLE_NORMAL, %PANGO2_STYLE_ITALIC, or %PANGO2_STYLE_OBLIQUE. + * + * Most fonts will either have a italic style or an oblique style, + * but not both, and font matching in Pango2 will match italic + * specifications with oblique fonts and vice-versa if an exact + * match is not found. + */ +void +pango2_font_description_set_style (Pango2FontDescription *desc, + Pango2Style style) +{ + g_return_if_fail (desc != NULL); + + desc->style = style; + desc->mask |= PANGO2_FONT_MASK_STYLE; +} + +/** + * pango2_font_description_get_style: + * @desc: a `Pango2FontDescription` + * + * Gets the style field of a `Pango2FontDescription`. + * + * See [method@Pango2.FontDescription.set_style]. + * + * Return value: the style field for the font description. + * Use [method@Pango2.FontDescription.get_set_fields] to + * find out if the field was explicitly set or not. + */ +Pango2Style +pango2_font_description_get_style (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.style); + + return desc->style; +} + +/** + * pango2_font_description_set_variant: + * @desc: a `Pango2FontDescription` + * @variant: the variant type for the font description. + * + * Sets the variant field of a font description. + * + * The [enum@Pango2.Variant] can either be %PANGO2_VARIANT_NORMAL + * or %PANGO2_VARIANT_SMALL_CAPS. + */ +void +pango2_font_description_set_variant (Pango2FontDescription *desc, + Pango2Variant variant) +{ + g_return_if_fail (desc != NULL); + + desc->variant = variant; + desc->mask |= PANGO2_FONT_MASK_VARIANT; +} + +/** + * pango2_font_description_get_variant: + * @desc: a `Pango2FontDescription`. + * + * Gets the variant field of a `Pango2FontDescription`. + * + * See [method@Pango2.FontDescription.set_variant]. + * + * Return value: the variant field for the font description. + * Use [method@Pango2.FontDescription.get_set_fields] to find + * out if the field was explicitly set or not. + */ +Pango2Variant +pango2_font_description_get_variant (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.variant); + + return desc->variant; +} + +/** + * pango2_font_description_set_weight: + * @desc: a `Pango2FontDescription` + * @weight: the weight for the font description. + * + * Sets the weight field of a font description. + * + * The weight field + * specifies how bold or light the font should be. In addition + * to the values of the [enum@Pango2.Weight] enumeration, other + * intermediate numeric values are possible. + */ +void +pango2_font_description_set_weight (Pango2FontDescription *desc, + Pango2Weight weight) +{ + g_return_if_fail (desc != NULL); + + desc->weight = weight; + desc->mask |= PANGO2_FONT_MASK_WEIGHT; +} + +/** + * pango2_font_description_get_weight: + * @desc: a `Pango2FontDescription` + * + * Gets the weight field of a font description. + * + * See [method@Pango2.FontDescription.set_weight]. + * + * Return value: the weight field for the font description. + * Use [method@Pango2.FontDescription.get_set_fields] to find + * out if the field was explicitly set or not. + */ +Pango2Weight +pango2_font_description_get_weight (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.weight); + + return desc->weight; +} + +/** + * pango2_font_description_set_stretch: + * @desc: a `Pango2FontDescription` + * @stretch: the stretch for the font description + * + * Sets the stretch field of a font description. + * + * The [enum@Pango2.Stretch] field specifies how narrow or + * wide the font should be. + */ +void +pango2_font_description_set_stretch (Pango2FontDescription *desc, + Pango2Stretch stretch) +{ + g_return_if_fail (desc != NULL); + + desc->stretch = stretch; + desc->mask |= PANGO2_FONT_MASK_STRETCH; +} + +/** + * pango2_font_description_get_stretch: + * @desc: a `Pango2FontDescription`. + * + * Gets the stretch field of a font description. + * + * See [method@Pango2.FontDescription.set_stretch]. + * + * Return value: the stretch field for the font description. + * Use [method@Pango2.FontDescription.get_set_fields] to find + * out if the field was explicitly set or not. + */ +Pango2Stretch +pango2_font_description_get_stretch (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.stretch); + + return desc->stretch; +} + +/** + * pango2_font_description_set_size: + * @desc: a `Pango2FontDescription` + * @size: the size of the font in points, scaled by %PANGO2_SCALE. + * (That is, a @size value of 10 * PANGO2_SCALE is a 10 point font. + * The conversion factor between points and device units depends on + * system configuration and the output device. For screen display, a + * logical DPI of 96 is common, in which case a 10 point font corresponds + * to a 10 * (96 / 72) = 13.3 pixel font. + * Use [method@Pango2.FontDescription.set_absolute_size] if you need + * a particular size in device units. + * + * Sets the size field of a font description in fractional points. + * + * This is mutually exclusive with + * [method@Pango2.FontDescription.set_absolute_size]. + */ +void +pango2_font_description_set_size (Pango2FontDescription *desc, + int size) +{ + g_return_if_fail (desc != NULL); + g_return_if_fail (size >= 0); + + desc->size = size; + desc->size_is_absolute = FALSE; + desc->mask |= PANGO2_FONT_MASK_SIZE; +} + +/** + * pango2_font_description_get_size: + * @desc: a `Pango2FontDescription` + * + * Gets the size field of a font description. + * + * See [method@Pango2.FontDescription.set_size]. + * + * Return value: the size field for the font description in points + * or device units. You must call + * [method@Pango2.FontDescription.get_size_is_absolute] to find out + * which is the case. Returns 0 if the size field has not previously + * been set or it has been set to 0 explicitly. + * Use [method@Pango2.FontDescription.get_set_fields] to find out + * if the field was explicitly set or not. + */ +int +pango2_font_description_get_size (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.size); + + return desc->size; +} + +/** + * pango2_font_description_set_absolute_size: + * @desc: a `Pango2FontDescription` + * @size: the new size, in Pango2 units. There are %PANGO2_SCALE Pango2 units + * in one device unit. For an output backend where a device unit is a pixel, + * a @size value of 10 * PANGO2_SCALE gives a 10 pixel font. + * + * Sets the size field of a font description, in device units. + * + * This is mutually exclusive with [method@Pango2.FontDescription.set_size] + * which sets the font size in points. + */ +void +pango2_font_description_set_absolute_size (Pango2FontDescription *desc, + double size) +{ + g_return_if_fail (desc != NULL); + g_return_if_fail (size >= 0); + + desc->size = size; + desc->size_is_absolute = TRUE; + desc->mask |= PANGO2_FONT_MASK_SIZE; +} + +/** + * pango2_font_description_get_size_is_absolute: + * @desc: a `Pango2FontDescription` + * + * Determines whether the size of the font is in points (not absolute) + * or device units (absolute). + * + * See [method@Pango2.FontDescription.set_size] + * and [method@Pango2.FontDescription.set_absolute_size]. + * + * Return value: whether the size for the font description is in + * points or device units. Use [method@Pango2.FontDescription.get_set_fields] + * to find out if the size field of the font description was explicitly + * set or not. + */ +gboolean +pango2_font_description_get_size_is_absolute (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.size_is_absolute); + + return desc->size_is_absolute; +} + +/** + * pango2_font_description_set_gravity: + * @desc: a `Pango2FontDescription` + * @gravity: the gravity for the font description. + * + * Sets the gravity field of a font description. + * + * The gravity field + * specifies how the glyphs should be rotated. If @gravity is + * %PANGO2_GRAVITY_AUTO, this actually unsets the gravity mask on + * the font description. + * + * This function is seldom useful to the user. Gravity should normally + * be set on a `Pango2Context`. + */ +void +pango2_font_description_set_gravity (Pango2FontDescription *desc, + Pango2Gravity gravity) +{ + g_return_if_fail (desc != NULL); + + if (gravity == PANGO2_GRAVITY_AUTO) + { + pango2_font_description_unset_fields (desc, PANGO2_FONT_MASK_GRAVITY); + return; + } + + desc->gravity = gravity; + desc->mask |= PANGO2_FONT_MASK_GRAVITY; +} + +/** + * pango2_font_description_get_gravity: + * @desc: a `Pango2FontDescription` + * + * Gets the gravity field of a font description. + * + * See [method@Pango2.FontDescription.set_gravity]. + * + * Return value: the gravity field for the font description. + * Use [method@Pango2.FontDescription.get_set_fields] to find out + * if the field was explicitly set or not. + */ +Pango2Gravity +pango2_font_description_get_gravity (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.gravity); + + return desc->gravity; +} + +/** + * pango2_font_description_set_variations_static: + * @desc: a `Pango2FontDescription` + * @variations: a string representing the variations + * + * Sets the variations field of a font description. + * + * This is like [method@Pango2.FontDescription.set_variations], except + * that no copy of @variations is made. The caller must make sure that + * the string passed in stays around until @desc has been freed + * or the name is set again. This function can be used if + * @variations is a static string such as a C string literal, + * or if @desc is only needed temporarily. + */ +void +pango2_font_description_set_variations_static (Pango2FontDescription *desc, + const char *variations) +{ + g_return_if_fail (desc != NULL); + + if (desc->variations == variations) + return; + + if (desc->variations && !desc->static_variations) + g_free (desc->variations); + + if (variations) + { + desc->variations = (char *)variations; + desc->static_variations = TRUE; + desc->mask |= PANGO2_FONT_MASK_VARIATIONS; + } + else + { + desc->variations = pfd_defaults.variations; + desc->static_variations = pfd_defaults.static_variations; + desc->mask &= ~PANGO2_FONT_MASK_VARIATIONS; + } +} + +/** + * pango2_font_description_set_variations: + * @desc: a `Pango2FontDescription`. + * @variations: (nullable): a string representing the variations + * + * Sets the variations field of a font description. + * + * OpenType font variations allow to select a font instance by + * specifying values for a number of axes, such as width or weight. + * + * The format of the variations string is + * + * AXIS1=VALUE,AXIS2=VALUE... + * + * with each AXIS a 4 character tag that identifies a font axis, + * and each VALUE a floating point number. Unknown axes are ignored, + * and values are clamped to their allowed range. + * + * Pango2 does not currently have a way to find supported axes of + * a font. Both harfbuzz and freetype have API for this. See + * for example [hb_ot_var_get_axis_infos](https://harfbuzz.github.io/harfbuzz-hb-ot-var.html#hb-ot-var-get-axis-infos). + */ +void +pango2_font_description_set_variations (Pango2FontDescription *desc, + const char *variations) +{ + g_return_if_fail (desc != NULL); + + pango2_font_description_set_variations_static (desc, g_strdup (variations)); + if (variations) + desc->static_variations = FALSE; +} + +/** + * pango2_font_description_get_variations: + * @desc: a `Pango2FontDescription` + * + * Gets the variations field of a font description. + * + * See [method@Pango2.FontDescription.set_variations]. + * + * Return value: (nullable): the variations field for the font + * description, or %NULL if not previously set. This has the same + * life-time as the font description itself and should not be freed. + */ +const char * +pango2_font_description_get_variations (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, NULL); + + return desc->variations; +} + +/** + * pango2_font_description_get_set_fields: + * @desc: a `Pango2FontDescription` + * + * Determines which fields in a font description have been set. + * + * Return value: a bitmask with bits set corresponding to the + * fields in @desc that have been set. + */ +Pango2FontMask +pango2_font_description_get_set_fields (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, pfd_defaults.mask); + + return desc->mask; +} + +/** + * pango2_font_description_unset_fields: + * @desc: a `Pango2FontDescription` + * @to_unset: bitmask of fields in the @desc to unset. + * + * Unsets some of the fields in a `Pango2FontDescription`. + * + * The unset fields will get back to their default values. + */ +void +pango2_font_description_unset_fields (Pango2FontDescription *desc, + Pango2FontMask to_unset) +{ + Pango2FontDescription unset_desc; + + g_return_if_fail (desc != NULL); + + unset_desc = pfd_defaults; + unset_desc.mask = to_unset; + + pango2_font_description_merge_static (desc, &unset_desc, TRUE); + + desc->mask &= ~to_unset; +} + +/** + * pango2_font_description_merge: + * @desc: a `Pango2FontDescription` + * @desc_to_merge: (nullable): the `Pango2FontDescription` to merge from, + * or %NULL + * @replace_existing: if %TRUE, replace fields in @desc with the + * corresponding values from @desc_to_merge, even if they + * are already exist. + * + * Merges the fields that are set in @desc_to_merge into the fields in + * @desc. + * + * If @replace_existing is %FALSE, only fields in @desc that + * are not already set are affected. If %TRUE, then fields that are + * already set will be replaced as well. + * + * If @desc_to_merge is %NULL, this function performs nothing. + */ +void +pango2_font_description_merge (Pango2FontDescription *desc, + const Pango2FontDescription *desc_to_merge, + gboolean replace_existing) +{ + gboolean family_merged; + gboolean variations_merged; + gboolean faceid_merged; + + g_return_if_fail (desc != NULL); + + if (desc_to_merge == NULL) + return; + + family_merged = desc_to_merge->family_name && (replace_existing || !desc->family_name); + variations_merged = desc_to_merge->variations && (replace_existing || !desc->variations); + faceid_merged = desc_to_merge->faceid && (replace_existing || !desc->faceid); + + pango2_font_description_merge_static (desc, desc_to_merge, replace_existing); + + if (family_merged) + { + desc->family_name = g_strdup (desc->family_name); + desc->static_family = FALSE; + } + + if (variations_merged) + { + desc->variations = g_strdup (desc->variations); + desc->static_variations = FALSE; + } + + if (faceid_merged) + { + desc->faceid = g_strdup (desc->faceid); + desc->static_faceid = FALSE; + } +} + +/** + * pango2_font_description_merge_static: + * @desc: a `Pango2FontDescription` + * @desc_to_merge: the `Pango2FontDescription` to merge from + * @replace_existing: if %TRUE, replace fields in @desc with the + * corresponding values from @desc_to_merge, even if they + * are already exist. + * + * Merges the fields that are set in @desc_to_merge into the fields in + * @desc, without copying allocated fields. + * + * This is like [method@Pango2.FontDescription.merge], but only a shallow copy + * is made of the family name and other allocated fields. @desc can only + * be used until @desc_to_merge is modified or freed. This is meant to + * be used when the merged font description is only needed temporarily. + */ +void +pango2_font_description_merge_static (Pango2FontDescription *desc, + const Pango2FontDescription *desc_to_merge, + gboolean replace_existing) +{ + Pango2FontMask new_mask; + + g_return_if_fail (desc != NULL); + g_return_if_fail (desc_to_merge != NULL); + + if (replace_existing) + new_mask = desc_to_merge->mask; + else + new_mask = desc_to_merge->mask & ~desc->mask; + + if (new_mask & PANGO2_FONT_MASK_FAMILY) + pango2_font_description_set_family_static (desc, desc_to_merge->family_name); + if (new_mask & PANGO2_FONT_MASK_STYLE) + desc->style = desc_to_merge->style; + if (new_mask & PANGO2_FONT_MASK_VARIANT) + desc->variant = desc_to_merge->variant; + if (new_mask & PANGO2_FONT_MASK_WEIGHT) + desc->weight = desc_to_merge->weight; + if (new_mask & PANGO2_FONT_MASK_STRETCH) + desc->stretch = desc_to_merge->stretch; + if (new_mask & PANGO2_FONT_MASK_SIZE) + { + desc->size = desc_to_merge->size; + desc->size_is_absolute = desc_to_merge->size_is_absolute; + } + if (new_mask & PANGO2_FONT_MASK_GRAVITY) + desc->gravity = desc_to_merge->gravity; + if (new_mask & PANGO2_FONT_MASK_VARIATIONS) + pango2_font_description_set_variations_static (desc, desc_to_merge->variations); + if (new_mask & PANGO2_FONT_MASK_FACEID) + pango2_font_description_set_faceid_static (desc, desc_to_merge->faceid); + + desc->mask |= new_mask; +} + +gboolean +pango2_font_description_is_similar (const Pango2FontDescription *a, + const Pango2FontDescription *b) +{ + return a->variant == b->variant && + a->gravity == b->gravity; +} + +int +pango2_font_description_compute_distance (const Pango2FontDescription *a, + const Pango2FontDescription *b) +{ + if (a->style == b->style) + { + return abs ((int)(a->weight) - (int)(b->weight)) + + abs ((int)(a->stretch) - (int)(b->stretch)); + } + else if (a->style != PANGO2_STYLE_NORMAL && + b->style != PANGO2_STYLE_NORMAL) + { + /* Equate oblique and italic, but with a big penalty + */ + return 1000000 + abs ((int)(a->weight) - (int)(b->weight)) + + abs ((int)(a->stretch) - (int)(b->stretch)); + } + else + return G_MAXINT; +} + +/** + * pango2_font_description_copy: + * @desc: (nullable): a `Pango2FontDescription`, may be %NULL + * + * Make a copy of a `Pango2FontDescription`. + * + * Return value: (nullable): the newly allocated `Pango2FontDescription`, + * which should be freed with [method@Pango2.FontDescription.free] + */ +Pango2FontDescription * +pango2_font_description_copy (const Pango2FontDescription *desc) +{ + Pango2FontDescription *result; + + if (desc == NULL) + return NULL; + + result = g_slice_new (Pango2FontDescription); + + *result = *desc; + + if (result->family_name) + { + result->family_name = g_strdup (result->family_name); + result->static_family = FALSE; + } + + result->variations = g_strdup (result->variations); + result->static_variations = FALSE; + + result->faceid = g_strdup (result->faceid); + result->static_faceid = FALSE; + + return result; +} + +/** + * pango2_font_description_copy_static: + * @desc: (nullable): a `Pango2FontDescription`, may be %NULL + * + * Make a copy of a `Pango2FontDescription`, but don't duplicate + * allocated fields. + * + * This is like [method@Pango2.FontDescription.copy], but only a shallow + * copy is made of the family name and other allocated fields. The result + * can only be used until @desc is modified or freed. This is meant + * to be used when the copy is only needed temporarily. + * + * Return value: (nullable): the newly allocated `Pango2FontDescription`, + * which should be freed with [method@Pango2.FontDescription.free] + */ +Pango2FontDescription * +pango2_font_description_copy_static (const Pango2FontDescription *desc) +{ + Pango2FontDescription *result; + + if (desc == NULL) + return NULL; + + result = g_slice_new (Pango2FontDescription); + + *result = *desc; + if (result->family_name) + result->static_family = TRUE; + + if (result->variations) + result->static_variations = TRUE; + + if (result->faceid) + result->static_faceid = TRUE; + + return result; +} + +/** + * pango2_font_description_equal: + * @desc1: a `Pango2FontDescription` + * @desc2: another `Pango2FontDescription` + * + * Compares two font descriptions for equality. + * + * Two font descriptions are considered equal if the fonts they describe + * are provably identical. This means that their masks do not have to match, + * as long as other fields are all the same. (Two font descriptions may + * result in identical fonts being loaded, but still compare %FALSE.) + * + * Return value: %TRUE if the two font descriptions are identical, + * %FALSE otherwise. + */ +gboolean +pango2_font_description_equal (const Pango2FontDescription *desc1, + const Pango2FontDescription *desc2) +{ + g_return_val_if_fail (desc1 != NULL, FALSE); + g_return_val_if_fail (desc2 != NULL, FALSE); + + return desc1->style == desc2->style && + desc1->variant == desc2->variant && + desc1->weight == desc2->weight && + desc1->stretch == desc2->stretch && + desc1->size == desc2->size && + desc1->size_is_absolute == desc2->size_is_absolute && + desc1->gravity == desc2->gravity && + (desc1->family_name == desc2->family_name || + (desc1->family_name && desc2->family_name && g_ascii_strcasecmp (desc1->family_name, desc2->family_name) == 0)) && + (g_strcmp0 (desc1->variations, desc2->variations) == 0) && + (g_strcmp0 (desc1->faceid, desc2->faceid) == 0); +} + +#define TOLOWER(c) \ + (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) + +static guint +case_insensitive_hash (const char *key) +{ + const char *p = key; + guint h = TOLOWER (*p); + + if (h) + { + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + TOLOWER (*p); + } + + return h; +} + +/** + * pango2_font_description_hash: + * @desc: a `Pango2FontDescription` + * + * Computes a hash of a `Pango2FontDescription` structure. + * + * This is suitable to be used, for example, as an argument + * to [GLib.HashTable.new]. The hash value is independent of @desc->mask. + * + * Return value: the hash value. + */ +guint +pango2_font_description_hash (const Pango2FontDescription *desc) +{ + guint hash = 0; + + g_return_val_if_fail (desc != NULL, 0); + + if (desc->family_name) + hash = case_insensitive_hash (desc->family_name); + if (desc->variations) + hash ^= g_str_hash (desc->variations); + if (desc->faceid) + hash ^= g_str_hash (desc->faceid); + hash ^= desc->size; + hash ^= desc->size_is_absolute ? 0xc33ca55a : 0; + hash ^= desc->style << 16; + hash ^= desc->variant << 18; + hash ^= desc->weight << 16; + hash ^= desc->stretch << 26; + hash ^= desc->gravity << 28; + + return hash; +} + +/** + * pango2_font_description_free: + * @desc: (nullable): a `Pango2FontDescription`, may be %NULL + * + * Frees a font description. + */ +void +pango2_font_description_free (Pango2FontDescription *desc) +{ + if (desc == NULL) + return; + + if (desc->family_name && !desc->static_family) + g_free (desc->family_name); + + if (desc->variations && !desc->static_variations) + g_free (desc->variations); + + if (desc->faceid && !desc->static_faceid) + g_free (desc->faceid); + + g_slice_free (Pango2FontDescription, desc); +} + +typedef struct +{ + int value; + const char str[16]; +} FieldMap; + +static const FieldMap style_map[] = { + { PANGO2_STYLE_NORMAL, "" }, + { PANGO2_STYLE_NORMAL, "Roman" }, + { PANGO2_STYLE_OBLIQUE, "Oblique" }, + { PANGO2_STYLE_ITALIC, "Italic" } +}; + +static const FieldMap variant_map[] = { + { PANGO2_VARIANT_NORMAL, "" }, + { PANGO2_VARIANT_SMALL_CAPS, "Small-Caps" }, + { PANGO2_VARIANT_ALL_SMALL_CAPS, "All-Small-Caps" }, + { PANGO2_VARIANT_PETITE_CAPS, "Petite-Caps" }, + { PANGO2_VARIANT_ALL_PETITE_CAPS, "All-Petite-Caps" }, + { PANGO2_VARIANT_UNICASE, "Unicase" }, + { PANGO2_VARIANT_TITLE_CAPS, "Title-Caps" } +}; + +static const FieldMap weight_map[] = { + { PANGO2_WEIGHT_THIN, "Thin" }, + { PANGO2_WEIGHT_ULTRALIGHT, "Ultra-Light" }, + { PANGO2_WEIGHT_ULTRALIGHT, "Extra-Light" }, + { PANGO2_WEIGHT_LIGHT, "Light" }, + { PANGO2_WEIGHT_SEMILIGHT, "Semi-Light" }, + { PANGO2_WEIGHT_SEMILIGHT, "Demi-Light" }, + { PANGO2_WEIGHT_BOOK, "Book" }, + { PANGO2_WEIGHT_NORMAL, "" }, + { PANGO2_WEIGHT_NORMAL, "Regular" }, + { PANGO2_WEIGHT_MEDIUM, "Medium" }, + { PANGO2_WEIGHT_SEMIBOLD, "Semi-Bold" }, + { PANGO2_WEIGHT_SEMIBOLD, "Demi-Bold" }, + { PANGO2_WEIGHT_BOLD, "Bold" }, + { PANGO2_WEIGHT_ULTRABOLD, "Ultra-Bold" }, + { PANGO2_WEIGHT_ULTRABOLD, "Extra-Bold" }, + { PANGO2_WEIGHT_HEAVY, "Heavy" }, + { PANGO2_WEIGHT_HEAVY, "Black" }, + { PANGO2_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, + { PANGO2_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, + { PANGO2_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, + { PANGO2_WEIGHT_ULTRAHEAVY, "Extra-Black" } +}; + +static const FieldMap stretch_map[] = { + { PANGO2_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, + { PANGO2_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, + { PANGO2_STRETCH_CONDENSED, "Condensed" }, + { PANGO2_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, + { PANGO2_STRETCH_NORMAL, "" }, + { PANGO2_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, + { PANGO2_STRETCH_EXPANDED, "Expanded" }, + { PANGO2_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, + { PANGO2_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } +}; + +static const FieldMap gravity_map[] = { + { PANGO2_GRAVITY_SOUTH, "Not-Rotated" }, + { PANGO2_GRAVITY_SOUTH, "South" }, + { PANGO2_GRAVITY_NORTH, "Upside-Down" }, + { PANGO2_GRAVITY_NORTH, "North" }, + { PANGO2_GRAVITY_EAST, "Rotated-Left" }, + { PANGO2_GRAVITY_EAST, "East" }, + { PANGO2_GRAVITY_WEST, "Rotated-Right" }, + { PANGO2_GRAVITY_WEST, "West" } +}; + +static gboolean +field_matches (const char *s1, + const char *s2, + gsize n) +{ + int c1, c2; + + g_return_val_if_fail (s1 != NULL, 0); + g_return_val_if_fail (s2 != NULL, 0); + + while (n && *s1 && *s2) + { + c1 = (int)(guchar) TOLOWER (*s1); + c2 = (int)(guchar) TOLOWER (*s2); + if (c1 != c2) { + if (c1 == '-') { + s1++; + continue; + } + return FALSE; + } + s1++; s2++; + n--; + } + + return n == 0 && *s1 == '\0'; +} + +static gboolean +parse_int (const char *word, + size_t wordlen, + int *out) +{ + char *end; + long val = strtol (word, &end, 10); + int i = val; + + if (end != word && (end == word + wordlen) && val >= 0 && val == i) + { + if (out) + *out = i; + + return TRUE; + } + + return FALSE; +} + +static gboolean +find_field (const char *what, + const FieldMap *map, + int n_elements, + const char *str, + int len, + int *val) +{ + int i; + gboolean had_prefix = FALSE; + + if (what) + { + i = strlen (what); + if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') + { + str += i + 1; + len -= i + 1; + had_prefix = TRUE; + } + } + + for (i=0; i<n_elements; i++) + { + if (map[i].str[0] && field_matches (map[i].str, str, len)) + { + if (val) + *val = map[i].value; + return TRUE; + } + } + + if (!what || had_prefix) + return parse_int (str, len, val); + + return FALSE; +} + +static gboolean +find_field_any (const char *str, + int len, + Pango2FontDescription *desc) +{ + if (field_matches ("Normal", str, len)) + return TRUE; + +#define FIELD(NAME, MASK) \ + G_STMT_START { \ + if (find_field (G_STRINGIFY (NAME), NAME##_map, G_N_ELEMENTS (NAME##_map), str, len, \ + desc ? (int *)(void *)&desc->NAME : NULL)) \ + { \ + if (desc) \ + desc->mask |= MASK; \ + return TRUE; \ + } \ + } G_STMT_END + + FIELD (weight, PANGO2_FONT_MASK_WEIGHT); + FIELD (style, PANGO2_FONT_MASK_STYLE); + FIELD (stretch, PANGO2_FONT_MASK_STRETCH); + FIELD (variant, PANGO2_FONT_MASK_VARIANT); + FIELD (gravity, PANGO2_FONT_MASK_GRAVITY); + +#undef FIELD + + return FALSE; +} + +static const char * +getword (const char *str, + const char *last, + size_t *wordlen, + const char *stop) +{ + const char *result; + + while (last > str && g_ascii_isspace (*(last - 1))) + last--; + + result = last; + while (result > str && !g_ascii_isspace (*(result - 1)) && !strchr (stop, *(result - 1))) + result--; + + *wordlen = last - result; + + return result; +} + +static gboolean +parse_size (const char *word, + size_t wordlen, + int *pango2_size, + gboolean *size_is_absolute) +{ + char *end; + double size = g_ascii_strtod (word, &end); + + if (end != word && + (end == word + wordlen || + (end + 2 == word + wordlen && !strncmp (end, "px", 2)) + ) && size >= 0 && size <= 1000000) /* word is a valid float */ + { + if (pango2_size) + *pango2_size = (int)(size * PANGO2_SCALE + 0.5); + + if (size_is_absolute) + *size_is_absolute = end < word + wordlen; + + return TRUE; + } + + return FALSE; +} + +static gboolean +parse_variations (const char *word, + size_t wordlen, + char **variations) +{ + if (word[0] != '@') + { + *variations = NULL; + return FALSE; + } + + /* XXX: actually validate here */ + *variations = g_strndup (word + 1, wordlen - 1); + + return TRUE; +} + +static void +faceid_from_variations (Pango2FontDescription *desc) +{ + const char *p, *q; + + p = desc->variations; + + if (g_str_has_prefix (p, "faceid=")) + { + p += strlen ("faceid="); + q = strchr (p, ','); + if (q) + { + desc->faceid = g_strndup (p, q - p); + p = q + 1; + } + else + { + desc->faceid = g_strdup (p); + p = NULL; + } + desc->mask |= PANGO2_FONT_MASK_FACEID; + } + + if (p != desc->variations) + { + char *variations = g_strdup (p); + g_free (desc->variations); + desc->variations = variations; + if (variations == NULL || *variations == '\0') + desc->mask &= ~PANGO2_FONT_MASK_VARIATIONS; + } +} + +/** + * pango2_font_description_from_string: + * @str: string representation of a font description. + * + * Creates a new font description from a string representation. + * + * The string must have the form + * + * "\[FAMILY-LIST] \[STYLE-OPTIONS] \[SIZE] \[VARIATIONS]", + * + * where FAMILY-LIST is a comma-separated list of families optionally + * terminated by a comma, STYLE_OPTIONS is a whitespace-separated list + * of words where each word describes one of style, variant, weight, + * stretch, or gravity, and SIZE is a decimal number (size in points) + * or optionally followed by the unit modifier "px" for absolute size. + * VARIATIONS is a comma-separated list of font variation + * specifications of the form "\@axis=value" (the = sign is optional). + * + * The following words are understood as styles: + * "Normal", "Roman", "Oblique", "Italic". + * + * The following words are understood as variants: + * "Small-Caps", "All-Small-Caps", "Petite-Caps", "All-Petite-Caps", + * "Unicase", "Title-Caps". + * + * The following words are understood as weights: + * "Thin", "Ultra-Light", "Extra-Light", "Light", "Semi-Light", + * "Demi-Light", "Book", "Regular", "Medium", "Semi-Bold", "Demi-Bold", + * "Bold", "Ultra-Bold", "Extra-Bold", "Heavy", "Black", "Ultra-Black", + * "Extra-Black". + * + * The following words are understood as stretch values: + * "Ultra-Condensed", "Extra-Condensed", "Condensed", "Semi-Condensed", + * "Semi-Expanded", "Expanded", "Extra-Expanded", "Ultra-Expanded". + * + * The following words are understood as gravity values: + * "Not-Rotated", "South", "Upside-Down", "North", "Rotated-Left", + * "East", "Rotated-Right", "West". + * + * Any one of the options may be absent. If FAMILY-LIST is absent, then + * the family_name field of the resulting font description will be + * initialized to %NULL. If STYLE-OPTIONS is missing, then all style + * options will be set to the default values. If SIZE is missing, the + * size in the resulting font description will be set to 0. + * + * A typical example: + * + * "Cantarell Italic Light 15 \@wght=200" + * + * Return value: a new `Pango2FontDescription`. + */ +Pango2FontDescription * +pango2_font_description_from_string (const char *str) +{ + Pango2FontDescription *desc; + const char *p, *last; + size_t len, wordlen; + + g_return_val_if_fail (str != NULL, NULL); + + desc = pango2_font_description_new (); + + desc->mask = PANGO2_FONT_MASK_STYLE | + PANGO2_FONT_MASK_WEIGHT | + PANGO2_FONT_MASK_VARIANT | + PANGO2_FONT_MASK_STRETCH; + + len = strlen (str); + last = str + len; + p = getword (str, last, &wordlen, ""); + /* Look for variations at the end of the string */ + if (wordlen != 0) + { + if (parse_variations (p, wordlen, &desc->variations)) + { + desc->mask |= PANGO2_FONT_MASK_VARIATIONS; + last = p; + + faceid_from_variations (desc); + } + } + + p = getword (str, last, &wordlen, ","); + /* Look for a size */ + if (wordlen != 0) + { + gboolean size_is_absolute; + if (parse_size (p, wordlen, &desc->size, &size_is_absolute)) + { + desc->size_is_absolute = size_is_absolute; + desc->mask |= PANGO2_FONT_MASK_SIZE; + last = p; + } + } + + /* Now parse style words + */ + p = getword (str, last, &wordlen, ","); + while (wordlen != 0) + { + if (!find_field_any (p, wordlen, desc)) + break; + else + { + last = p; + p = getword (str, last, &wordlen, ","); + } + } + + /* Remainder (str => p) is family list. Trim off trailing commas and leading and trailing white space + */ + + while (last > str && g_ascii_isspace (*(last - 1))) + last--; + + if (last > str && *(last - 1) == ',') + last--; + + while (last > str && g_ascii_isspace (*(last - 1))) + last--; + + while (last > str && g_ascii_isspace (*str)) + str++; + + if (str != last) + { + int i; + char **families; + + desc->family_name = g_strndup (str, last - str); + + /* Now sanitize it to trim space from around individual family names. + * bug #499624 */ + + families = g_strsplit (desc->family_name, ",", -1); + + for (i = 0; families[i]; i++) + g_strstrip (families[i]); + + g_free (desc->family_name); + desc->family_name = g_strjoinv (",", families); + g_strfreev (families); + + desc->mask |= PANGO2_FONT_MASK_FAMILY; + } + + return desc; +} + +static void +append_field (GString *str, + const char *what, + const FieldMap *map, + int n_elements, + int val) +{ + int i; + for (i=0; i<n_elements; i++) + { + if (map[i].value != val) + continue; + + if (G_LIKELY (map[i].str[0])) + { + if (G_LIKELY (str->len > 0 && str->str[str->len -1] != ' ')) + g_string_append_c (str, ' '); + g_string_append (str, map[i].str); + } + return; + } + + if (G_LIKELY (str->len > 0 || str->str[str->len -1] != ' ')) + g_string_append_c (str, ' '); + g_string_append_printf (str, "%s=%d", what, val); +} + +/** + * pango2_font_description_to_string: + * @desc: a `Pango2FontDescription` + * + * Creates a string representation of a font description. + * + * See [func@Pango2.FontDescription.from_string] for a description + * of the format of the string representation. The family list in + * the string description will only have a terminating comma if + * the last word of the list is a valid style option. + * + * Return value: a newly allocated string + */ +char * +pango2_font_description_to_string (const Pango2FontDescription *desc) +{ + GString *result; + gboolean in_variations = FALSE; + + g_return_val_if_fail (desc != NULL, NULL); + + result = g_string_new (NULL); + + if (G_LIKELY (desc->family_name && desc->mask & PANGO2_FONT_MASK_FAMILY)) + { + const char *p; + size_t wordlen; + + g_string_append (result, desc->family_name); + + /* We need to add a trailing comma if the family name ends + * in a keyword like "Bold", or if the family name ends in + * a number and no keywords will be added. + */ + p = getword (desc->family_name, desc->family_name + strlen(desc->family_name), &wordlen, ","); + if (wordlen != 0 && + (find_field_any (p, wordlen, NULL) || + (parse_size (p, wordlen, NULL, NULL) && + desc->weight == PANGO2_WEIGHT_NORMAL && + desc->style == PANGO2_STYLE_NORMAL && + desc->stretch == PANGO2_STRETCH_NORMAL && + desc->variant == PANGO2_VARIANT_NORMAL && + (desc->mask & (PANGO2_FONT_MASK_GRAVITY | PANGO2_FONT_MASK_SIZE)) == 0))) + g_string_append_c (result, ','); + } + +#define FIELD(NAME, MASK) \ + append_field (result, G_STRINGIFY (NAME), NAME##_map, G_N_ELEMENTS (NAME##_map), desc->NAME) + + FIELD (weight, PANGO2_FONT_MASK_WEIGHT); + FIELD (style, PANGO2_FONT_MASK_STYLE); + FIELD (stretch, PANGO2_FONT_MASK_STRETCH); + FIELD (variant, PANGO2_FONT_MASK_VARIANT); + if (desc->mask & PANGO2_FONT_MASK_GRAVITY) + FIELD (gravity, PANGO2_FONT_MASK_GRAVITY); + +#undef FIELD + + if (result->len == 0) + g_string_append (result, "Normal"); + + if (desc->mask & PANGO2_FONT_MASK_SIZE) + { + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + if (result->len > 0 || result->str[result->len -1] != ' ') + g_string_append_c (result, ' '); + + g_ascii_dtostr (buf, sizeof (buf), (double)desc->size / PANGO2_SCALE); + g_string_append (result, buf); + + if (desc->size_is_absolute) + g_string_append (result, "px"); + } + + if (desc->mask & PANGO2_FONT_MASK_FACEID) + { + in_variations = TRUE; + g_string_append (result, " @"); + g_string_append_printf (result, "faceid=%s", desc->faceid); + } + + if ((desc->variations && desc->mask & PANGO2_FONT_MASK_VARIATIONS) && + desc->variations[0] != '\0') + { + if (!in_variations) + g_string_append (result, " @"); + else + g_string_append (result, ","); + g_string_append (result, desc->variations); + } + + return g_string_free (result, FALSE); +} + +static gboolean +parse_field (const char *what, + const FieldMap *map, + int n_elements, + const char *str, + int *val, + gboolean warn) +{ + gboolean found; + int len = strlen (str); + + if (G_UNLIKELY (*str == '\0')) + return FALSE; + + if (field_matches ("Normal", str, len)) + { + /* find the map entry with empty string */ + int i; + + for (i = 0; i < n_elements; i++) + if (map[i].str[0] == '\0') + { + *val = map[i].value; + return TRUE; + } + + *val = 0; + return TRUE; + } + + found = find_field (NULL, map, n_elements, str, len, val); + + if (!found && warn) + { + int i; + GString *s = g_string_new (NULL); + + for (i = 0; i < n_elements; i++) + { + if (i) + g_string_append_c (s, '/'); + g_string_append (s, map[i].str[0] == '\0' ? "Normal" : map[i].str); + } + + g_warning ("%s must be one of %s or a number", + what, + s->str); + + g_string_free (s, TRUE); + } + + return found; +} + +#define FIELD(NAME, MASK) \ + parse_field (G_STRINGIFY (NAME), NAME##_map, G_N_ELEMENTS (NAME##_map), str, (int *)(void *)NAME, warn) + +/*< private > + * pango2_parse_style: + * @str: a string to parse. + * @style: (out): a `Pango2Style` to store the result in. + * @warn: if %TRUE, issue a g_warning() on bad input. + * + * Parses a font style. + * + * The allowed values are "normal", "italic" and "oblique", case + * variations being + * ignored. + * + * Return value: %TRUE if @str was successfully parsed. + */ +gboolean +pango2_parse_style (const char *str, + Pango2Style *style, + gboolean warn) +{ + return FIELD (style, PANGO2_FONT_MASK_STYLE); +} + +/*< private > + * pango2_parse_variant: + * @str: a string to parse. + * @variant: (out): a `Pango2Variant` to store the result in. + * @warn: if %TRUE, issue a g_warning() on bad input. + * + * Parses a font variant. + * + * The allowed values are "normal", "small-caps", "all-small-caps", + * "petite-caps", "all-petite-caps", "unicase" and "title-caps", + * case variations being ignored. + * + * Return value: %TRUE if @str was successfully parsed. + */ +gboolean +pango2_parse_variant (const char *str, + Pango2Variant *variant, + gboolean warn) +{ + return FIELD (variant, PANGO2_FONT_MASK_VARIANT); +} + +/*< private > + * pango2_parse_weight: + * @str: a string to parse. + * @weight: (out): a `Pango2Weight` to store the result in. + * @warn: if %TRUE, issue a g_warning() on bad input. + * + * Parses a font weight. + * + * The allowed values are "heavy", + * "ultrabold", "bold", "normal", "light", "ultraleight" + * and integers. Case variations are ignored. + * + * Return value: %TRUE if @str was successfully parsed. + */ +gboolean +pango2_parse_weight (const char *str, + Pango2Weight *weight, + gboolean warn) +{ + return FIELD (weight, PANGO2_FONT_MASK_WEIGHT); +} + +/*< private > + * pango2_parse_stretch: + * @str: a string to parse. + * @stretch: (out): a `Pango2Stretch` to store the result in. + * @warn: if %TRUE, issue a g_warning() on bad input. + * + * Parses a font stretch. + * + * The allowed values are + * "ultra_condensed", "extra_condensed", "condensed", + * "semi_condensed", "normal", "semi_expanded", "expanded", + * "extra_expanded" and "ultra_expanded". Case variations are + * ignored and the '_' characters may be omitted. + * + * Return value: %TRUE if @str was successfully parsed. + */ +gboolean +pango2_parse_stretch (const char *str, + Pango2Stretch *stretch, + gboolean warn) +{ + return FIELD (stretch, PANGO2_FONT_MASK_STRETCH); +} + +/** + * pango2_font_description_set_faceid_static: + * @desc: a `Pango2FontDescription` + * @faceid: the faceid string + * + * Sets the faceid field of a font description. + * + * This is like [method@Pango2.FontDescription.set_faceid], except + * that no copy of @faceid is made. The caller must make sure that + * the string passed in stays around until @desc has been freed + * or the name is set again. This function can be used if + * @faceid is a static string such as a C string literal, + * or if @desc is only needed temporarily. + */ +void +pango2_font_description_set_faceid_static (Pango2FontDescription *desc, + const char *faceid) +{ + g_return_if_fail (desc != NULL); + + if (desc->faceid == faceid) + return; + + if (desc->faceid && !desc->static_faceid) + g_free (desc->faceid); + + if (faceid) + { + desc->faceid = (char *)faceid; + desc->static_faceid = TRUE; + desc->mask |= PANGO2_FONT_MASK_FACEID; + } + else + { + desc->faceid = pfd_defaults.faceid; + desc->static_faceid = pfd_defaults.static_faceid; + desc->mask &= ~PANGO2_FONT_MASK_FACEID; + } +} + +/** + * pango2_font_description_set_faceid: + * @desc: a `Pango2FontDescription`. + * @faceid: (nullable): the faceid string + * + * Sets the faceid field of a font description. + * + * The faceid is mainly for internal use by Pango2, to ensure + * that font -> description -> font roundtrips end up with + * the same font they started with, if possible. + * + * Font descriptions originating from [method@Pango2.FontFace.describe] + * should ideally include a faceid. Pango2 takes the faceid + * into account when looking for the best matching face while + * loading a fontset or font. + * + * The format of this string is not guaranteed. + */ +void +pango2_font_description_set_faceid (Pango2FontDescription *desc, + const char *faceid) +{ + g_return_if_fail (desc != NULL); + + pango2_font_description_set_faceid_static (desc, g_strdup (faceid)); + if (faceid) + desc->static_faceid = FALSE; +} + +/** + * pango2_font_description_get_faceid: + * @desc: a `Pango2FontDescription` + * + * Gets the faceid field of a font description. + * + * See [method@Pango2.FontDescription.set_faceid]. + * + * Return value: (nullable): the faceid field for the font + * description, or %NULL if not previously set. This has the same + * life-time as the font description itself and should not be freed. + */ +const char * +pango2_font_description_get_faceid (const Pango2FontDescription *desc) +{ + g_return_val_if_fail (desc != NULL, NULL); + + return desc->faceid; +} |