diff options
Diffstat (limited to 'pango2/pango-context.c')
-rw-r--r-- | pango2/pango-context.c | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/pango2/pango-context.c b/pango2/pango-context.c new file mode 100644 index 00000000..58f2719a --- /dev/null +++ b/pango2/pango-context.c @@ -0,0 +1,1068 @@ +/* Pango2 + * pango-context.c: Contexts for itemization and shaping + * + * Copyright (C) 2000, 2006 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 <string.h> +#include <stdlib.h> + +#include <gio/gio.h> + +#include "pango-context.h" +#include "pango-context-private.h" +#include "pango-impl-utils.h" + +#include "pango-font-private.h" +#include "pango-font-metrics-private.h" +#include "pango-item-private.h" +#include "pango-fontset-private.h" +#include "pango-fontmap-private.h" +#include "pango-script-private.h" +#include "pango-emoji-private.h" + +/** + * Pango2Context: + * + * A `Pango2Context` stores global information used to control the + * itemization process. + * + * The information stored by `Pango2Context` includes the fontmap used + * to look up fonts, and default values such as the default language, + * default gravity, or default font. + * + * To obtain a `Pango2Context`, use [ctor@Pango2.Context.new] + * or [func@Pango2.cairo_create_context]. + */ + +struct _Pango2ContextClass +{ + GObjectClass parent_class; + +}; + +static void pango2_context_finalize (GObject *object); +static void context_changed (Pango2Context *context); + +enum { + PROP_FONT_MAP = 1, + PROP_FONT_DESCRIPTION, + PROP_LANGUAGE, + PROP_BASE_DIR, + PROP_BASE_GRAVITY, + PROP_GRAVITY_HINT, + PROP_MATRIX, + PROP_ROUND_GLYPH_POSITIONS, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +G_DEFINE_FINAL_TYPE (Pango2Context, pango2_context, G_TYPE_OBJECT) + +static void +pango2_context_init (Pango2Context *context) +{ + context->base_dir = PANGO2_DIRECTION_WEAK_LTR; + context->resolved_gravity = context->base_gravity = PANGO2_GRAVITY_SOUTH; + context->gravity_hint = PANGO2_GRAVITY_HINT_NATURAL; + + context->serial = 1; + context->set_language = NULL; + context->language = pango2_language_get_default (); + context->font_map = NULL; + context->round_glyph_positions = TRUE; + + context->font_desc = pango2_font_description_new (); + pango2_font_description_set_family_static (context->font_desc, "serif"); + pango2_font_description_set_style (context->font_desc, PANGO2_STYLE_NORMAL); + pango2_font_description_set_variant (context->font_desc, PANGO2_VARIANT_NORMAL); + pango2_font_description_set_weight (context->font_desc, PANGO2_WEIGHT_NORMAL); + pango2_font_description_set_stretch (context->font_desc, PANGO2_STRETCH_NORMAL); + pango2_font_description_set_size (context->font_desc, 12 * PANGO2_SCALE); +} + +static gboolean +pango2_has_mixed_deps (void) +{ + GModule *module; + gpointer func; + gboolean result = FALSE; + + module = g_module_open (NULL, 0); + + if (g_module_symbol (module, "pango2_ot_info_find_script", &func)) + result = TRUE; + + g_module_close (module); + + return result; +} + +static void +pango2_context_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + Pango2Context *context = PANGO2_CONTEXT (object); + + switch (property_id) + { + case PROP_FONT_MAP: + pango2_context_set_font_map (context, g_value_get_object (value)); + break; + + case PROP_FONT_DESCRIPTION: + pango2_context_set_font_description (context, g_value_get_boxed (value)); + break; + + case PROP_LANGUAGE: + pango2_context_set_language (context, g_value_get_boxed (value)); + break; + + case PROP_BASE_DIR: + pango2_context_set_base_dir (context, g_value_get_enum (value)); + break; + + case PROP_BASE_GRAVITY: + pango2_context_set_base_gravity (context, g_value_get_enum (value)); + break; + + case PROP_GRAVITY_HINT: + pango2_context_set_gravity_hint (context, g_value_get_enum (value)); + break; + + case PROP_MATRIX: + pango2_context_set_matrix (context, g_value_get_boxed (value)); + break; + + case PROP_ROUND_GLYPH_POSITIONS: + pango2_context_set_round_glyph_positions (context, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +pango2_context_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + Pango2Context *context = PANGO2_CONTEXT (object); + + switch (property_id) + { + case PROP_FONT_MAP: + g_value_set_object (value, pango2_context_get_font_map (context)); + break; + + case PROP_FONT_DESCRIPTION: + g_value_set_boxed (value, pango2_context_get_font_description (context)); + break; + + case PROP_LANGUAGE: + g_value_set_boxed (value, pango2_context_get_language (context)); + break; + + case PROP_BASE_DIR: + g_value_set_enum (value, pango2_context_get_base_dir (context)); + break; + + case PROP_BASE_GRAVITY: + g_value_set_enum (value, pango2_context_get_base_gravity (context)); + break; + + case PROP_GRAVITY_HINT: + g_value_set_enum (value, pango2_context_get_gravity_hint (context)); + break; + + case PROP_MATRIX: + g_value_set_boxed (value, pango2_context_get_matrix (context)); + break; + + case PROP_ROUND_GLYPH_POSITIONS: + g_value_set_boolean (value, pango2_context_get_round_glyph_positions (context)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +pango2_context_class_init (Pango2ContextClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + /* Put the check for mixed linkage here, for lack of a better place */ + if (pango2_has_mixed_deps ()) + g_error ("Pango2 1.x symbols detected.\n" + "Using Pango2 1.x and 2 in the same process is not supported."); + + object_class->finalize = pango2_context_finalize; + object_class->set_property = pango2_context_set_property; + object_class->get_property = pango2_context_get_property; + + /** + * Pango2Context:font-map: (attributes org.gtk.Property.get=pango2_context_get_font_map org.gtk.Property.set=pango2_context_set_font_map) + * + * The font map to be searched when fonts are looked up + * in this context. + */ + properties[PROP_FONT_MAP] = + g_param_spec_object ("font-map", NULL, NULL, PANGO2_TYPE_FONT_MAP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:font-description: (attributes org.gtk.Property.get=pango2_context_get_font_description org.gtk.Property.set=pango2_context_set_font_description) + * + * The default font description for the context. + */ + properties[PROP_FONT_DESCRIPTION] = + g_param_spec_boxed ("font-description", NULL, NULL, PANGO2_TYPE_FONT_DESCRIPTION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:language: (attributes org.gtk.Property.get=pango2_context_get_language org.gtk.Property.set=pango2_context_set_language) + * + * The global language for the context. + */ + properties[PROP_LANGUAGE] = + g_param_spec_boxed ("language", NULL, NULL, PANGO2_TYPE_LANGUAGE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:base-direction: (attributes org.gtk.Property.get=pango2_context_get_base_dir org.gtk.Property.set=pango2_context_set_base_dir) + * + * The base direction for the context. + * + * The base direction is used in applying the Unicode bidirectional + * algorithm; if the @direction is `PANGO2_DIRECTION_LTR` or + * `PANGO2_DIRECTION_RTL`, then the value will be used as the paragraph + * direction in the Unicode bidirectional algorithm. A value of + * `PANGO2_DIRECTION_WEAK_LTR` or `PANGO2_DIRECTION_WEAK_RTL` is used only + * for paragraphs that do not contain any strong characters themselves. + */ + properties[PROP_BASE_DIR] = + g_param_spec_enum ("base-direction", NULL, NULL, PANGO2_TYPE_DIRECTION, + PANGO2_DIRECTION_LTR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:base-gravity: (attributes org.gtk.Property.get=pango2_context_get_base_gravity org.gtk.Property.set=pango2_context_set_base_gravity) + * + * The base gravity for the context. + * + * The base gravity is used in laying vertical text out. + */ + properties[PROP_BASE_GRAVITY] = + g_param_spec_enum ("base-gravity", NULL, NULL, PANGO2_TYPE_GRAVITY, + PANGO2_GRAVITY_SOUTH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:gravity-hint: (attributes org.gtk.Property.get=pango2_context_get_gravity_hint org.gtk.Property.set=pango2_context_set_gravity_hint) + * + * The gravity hint for the context. + * + * The gravity hint is used in laying vertical text out, and + * is only relevant if gravity of the context as returned by + * [method@Pango2.Context.get_gravity] is set to `PANGO2_GRAVITY_EAST` + * or `PANGO2_GRAVITY_WEST`. + */ + properties[PROP_GRAVITY_HINT] = + g_param_spec_enum ("gravity-hint", NULL, NULL, PANGO2_TYPE_GRAVITY_HINT, + PANGO2_GRAVITY_HINT_NATURAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:matrix: (attributes org.gtk.Property.get=pango2_context_get_matrix org.gtk.Property.set=pango2_context_set_matrix) + * + * The 'user to device' transformation that will be applied when rendering + * with this context. + * + * This matrix is also known as the current transformation matrix, or 'ctm'. + * + * The transformation is needed in cases where the font rendering applies + * hinting that depends on knowing the position of text with respect to + * the pixel grid. + */ + properties[PROP_MATRIX] = + g_param_spec_boxed ("matrix", NULL, NULL, PANGO2_TYPE_MATRIX, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * Pango2Context:round-glyph-positions: (attributes org.gtk.Property.get=pango2_context_get_round_glyph_positions org.gtk.Property.set=pango2_context_set_round_glyph_positions) + * + * Determines whether font rendering with this context should + * round glyph positions and widths to integral positions, + * in device units. + * + * This is useful when the renderer can't handle subpixel + * positioning of glyphs. + */ + properties[PROP_ROUND_GLYPH_POSITIONS] = + g_param_spec_boolean ("round-glyph-positions", NULL, NULL, TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); +} + +static void +pango2_context_finalize (GObject *object) +{ + Pango2Context *context; + + context = PANGO2_CONTEXT (object); + + if (context->font_map) + g_object_unref (context->font_map); + + pango2_font_description_free (context->font_desc); + if (context->matrix) + pango2_matrix_free (context->matrix); + + if (context->metrics) + pango2_font_metrics_free (context->metrics); + + G_OBJECT_CLASS (pango2_context_parent_class)->finalize (object); +} + +/** + * pango2_context_new: + * + * Creates a new `Pango2Context` initialized to default values. + * + * If you want to use a specific [class@Pango2.FontMap] other than + * the default one, you should use [ctor@Pango2.Context.new_with_font_map] + * instead. + * + * If you are using Pango2 as part of a higher-level system, + * that system may have it's own way of create a `Pango2Context`. + * Pango2's own cairo support for instance, has [func@Pango2.cairo_create_context], + * and the GTK toolkit has, among others, gtk_widget_get_pango2_context(). + * Use those instead. + * + * Return value: the newly allocated `Pango2Context` + */ +Pango2Context * +pango2_context_new (void) +{ + return g_object_new (PANGO2_TYPE_CONTEXT, + "font-map", pango2_font_map_get_default (), + NULL); +} + +/** + * pango2_context_new_with_font_map: + * @font_map: the `Pango2FontMap` to use + * + * Creates a new `Pango2Context` with the given font map. + * + * If you are using Pango2 as part of a higher-level system, + * that system may have it's own way of create a `Pango2Context`. + * Pango2's own cairo support for instance, has [func@Pango2.cairo_create_context], + * and the GTK toolkit has, among others, gtk_widget_get_pango2_context(). + * Use those instead. + * + * Return value: the newly allocated `Pango2Context` + */ +Pango2Context * +pango2_context_new_with_font_map (Pango2FontMap *font_map) +{ + return g_object_new (PANGO2_TYPE_CONTEXT, + "font-map", font_map, + NULL); +} + +static void +update_resolved_gravity (Pango2Context *context) +{ + if (context->base_gravity == PANGO2_GRAVITY_AUTO) + context->resolved_gravity = pango2_gravity_get_for_matrix (context->matrix); + else + context->resolved_gravity = context->base_gravity; +} + +/** + * pango2_context_set_matrix: + * @context: a `Pango2Context` + * @matrix: (nullable): a `Pango2Matrix`, or %NULL to unset any existing + * matrix. (No matrix set is the same as setting the identity matrix.) + * + * Sets the 'user to device' transformation that will be applied when rendering + * with this context. + * + * This matrix is also known as the current transformation matrix, or 'ctm'. + * + * The transformation is needed in cases where the font rendering applies + * hinting that depends on knowing the position of text with respect to + * the pixel grid. + * + * Note that reported metrics are in the user space coordinates before + * the application of the matrix, not device-space coordinates after the + * application of the matrix. So, they don't scale with the matrix, though + * they may change slightly for different matrices, depending on how the + * text is fit to the pixel grid. + */ +void +pango2_context_set_matrix (Pango2Context *context, + const Pango2Matrix *matrix) +{ + g_return_if_fail (PANGO2_IS_CONTEXT (context)); + + if (context->matrix || matrix) + context_changed (context); + + if (context->matrix) + pango2_matrix_free (context->matrix); + if (matrix) + context->matrix = pango2_matrix_copy (matrix); + else + context->matrix = NULL; + + update_resolved_gravity (context); + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_MATRIX]); +} + +/** + * pango2_context_get_matrix: + * @context: a `Pango2Context` + * + * Gets the transformation matrix that will be applied when + * rendering with this context. + * + * See [method@Pango2.Context.set_matrix]. + * + * Return value: (nullable): the matrix, or %NULL if no matrix has + * been set (which is the same as the identity matrix). The returned + * matrix is owned by Pango2 and must not be modified or freed. + */ +const Pango2Matrix * +pango2_context_get_matrix (Pango2Context *context) +{ + g_return_val_if_fail (PANGO2_IS_CONTEXT (context), NULL); + + return context->matrix; +} + +/** + * pango2_context_set_font_map: + * @context: a `Pango2Context` + * @font_map: the `Pango2FontMap` to set. + * + * Sets the font map to be searched when fonts are looked-up + * in this context. + * + * This is only for internal use by Pango2 backends, a `Pango2Context` + * obtained via one of the recommended methods should already have a + * suitable font map. + */ +void +pango2_context_set_font_map (Pango2Context *context, + Pango2FontMap *font_map) +{ + g_return_if_fail (PANGO2_IS_CONTEXT (context)); + g_return_if_fail (!font_map || PANGO2_IS_FONT_MAP (font_map)); + + if (font_map == context->font_map) + return; + + context_changed (context); + + if (font_map) + g_object_ref (font_map); + + if (context->font_map) + g_object_unref (context->font_map); + + context->font_map = font_map; + context->fontmap_serial = pango2_font_map_get_serial (font_map); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_FONT_MAP]); +} + +/** + * pango2_context_get_font_map: + * @context: a `Pango2Context` + * + * Gets the `Pango2FontMap` used to look up fonts for this context. + * + * Return value: (transfer none): the font map for the `Pango2Context`. + * This value is owned by Pango2 and should not be unreferenced. + */ +Pango2FontMap * +pango2_context_get_font_map (Pango2Context *context) +{ + g_return_val_if_fail (PANGO2_IS_CONTEXT (context), NULL); + + return context->font_map; +} + +/** + * pango2_context_load_font: + * @context: a `Pango2Context` + * @desc: a `Pango2FontDescription` describing the font to load + * + * Loads the font in one of the fontmaps in the context + * that is the closest match for @desc. + * + * Returns: (transfer full) (nullable): the newly allocated `Pango2Font` + * that was loaded, or %NULL if no font matched. + */ +Pango2Font * +pango2_context_load_font (Pango2Context *context, + const Pango2FontDescription *desc) +{ + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (context->font_map != NULL, NULL); + + return pango2_font_map_load_font (context->font_map, context, desc); +} + +/** + * pango2_context_load_fontset: + * @context: a `Pango2Context` + * @desc: a `Pango2FontDescription` describing the fonts to load + * @language: a `Pango2Language` the fonts will be used for + * + * Load a set of fonts in the context that can be used to render + * a font matching @desc. + * + * Returns: (transfer full) (nullable): the newly allocated + * `Pango2Fontset` loaded, or %NULL if no font matched. + */ +Pango2Fontset * +pango2_context_load_fontset (Pango2Context *context, + const Pango2FontDescription *desc, + Pango2Language *language) +{ + g_return_val_if_fail (context != NULL, NULL); + + return pango2_font_map_load_fontset (context->font_map, context, desc, language); +} + +/** + * pango2_context_set_font_description: + * @context: a `Pango2Context` + * @desc: the new pango font description + * + * Set the default font description for the context + */ +void +pango2_context_set_font_description (Pango2Context *context, + const Pango2FontDescription *desc) +{ + g_return_if_fail (context != NULL); + g_return_if_fail (desc != NULL); + + if (desc != context->font_desc && + (!desc || !context->font_desc || !pango2_font_description_equal(desc, context->font_desc))) + { + context_changed (context); + + pango2_font_description_free (context->font_desc); + context->font_desc = pango2_font_description_copy (desc); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_FONT_DESCRIPTION]); + } +} + +/** + * pango2_context_get_font_description: + * @context: a `Pango2Context` + * + * Retrieve the default font description for the context. + * + * Return value: (transfer none): a pointer to the context's default font + * description. This value must not be modified or freed. + */ +Pango2FontDescription * +pango2_context_get_font_description (Pango2Context *context) +{ + g_return_val_if_fail (context != NULL, NULL); + + return context->font_desc; +} + +/** + * pango2_context_set_language: + * @context: a `Pango2Context` + * @language: the new language tag. + * + * Sets the global language tag for the context. + * + * The default language for the locale of the running process + * can be found using [func@Pango2.Language.get_default]. + */ +void +pango2_context_set_language (Pango2Context *context, + Pango2Language *language) +{ + g_return_if_fail (context != NULL); + + if (language != context->language) + context_changed (context); + + context->set_language = language; + if (language) + context->language = language; + else + context->language = pango2_language_get_default (); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_LANGUAGE]); +} + +/** + * pango2_context_get_language: + * @context: a `Pango2Context` + * + * Retrieves the global language tag for the context. + * + * Return value: the global language tag. + */ +Pango2Language * +pango2_context_get_language (Pango2Context *context) +{ + g_return_val_if_fail (context != NULL, NULL); + + return context->set_language; +} + +/** + * pango2_context_set_base_dir: + * @context: a `Pango2Context` + * @direction: the new base direction + * + * Sets the base direction for the context. + * + * The base direction is used in applying the Unicode bidirectional + * algorithm; if the @direction is %PANGO2_DIRECTION_LTR or + * %PANGO2_DIRECTION_RTL, then the value will be used as the paragraph + * direction in the Unicode bidirectional algorithm. A value of + * %PANGO2_DIRECTION_WEAK_LTR or %PANGO2_DIRECTION_WEAK_RTL is used only + * for paragraphs that do not contain any strong characters themselves. + */ +void +pango2_context_set_base_dir (Pango2Context *context, + Pango2Direction direction) +{ + g_return_if_fail (context != NULL); + + if (direction == context->base_dir) + return; + + context_changed (context); + + context->base_dir = direction; + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_BASE_DIR]); +} + +/** + * pango2_context_get_base_dir: + * @context: a `Pango2Context` + * + * Retrieves the base direction for the context. + * + * See [method@Pango2.Context.set_base_dir]. + * + * Return value: the base direction for the context. + */ +Pango2Direction +pango2_context_get_base_dir (Pango2Context *context) +{ + g_return_val_if_fail (context != NULL, PANGO2_DIRECTION_LTR); + + return context->base_dir; +} + +/** + * pango2_context_set_base_gravity: + * @context: a `Pango2Context` + * @gravity: the new base gravity + * + * Sets the base gravity for the context. + * + * The base gravity is used in laying vertical text out. + */ +void +pango2_context_set_base_gravity (Pango2Context *context, + Pango2Gravity gravity) +{ + g_return_if_fail (context != NULL); + + if (gravity == context->base_gravity) + return; + + context_changed (context); + + context->base_gravity = gravity; + + update_resolved_gravity (context); + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_BASE_GRAVITY]); +} + +/** + * pango2_context_get_base_gravity: + * @context: a `Pango2Context` + * + * Retrieves the base gravity for the context. + * + * See [method@Pango2.Context.set_base_gravity]. + * + * Return value: the base gravity for the context. + */ +Pango2Gravity +pango2_context_get_base_gravity (Pango2Context *context) +{ + g_return_val_if_fail (context != NULL, PANGO2_GRAVITY_SOUTH); + + return context->base_gravity; +} + +/** + * pango2_context_get_gravity: + * @context: a `Pango2Context` + * + * Retrieves the gravity for the context. + * + * This is similar to [method@Pango2.Context.get_base_gravity], + * except for when the base gravity is %PANGO2_GRAVITY_AUTO for + * which [func@Pango2.Gravity.get_for_matrix] is used to return the + * gravity from the current context matrix. + * + * Return value: the resolved gravity for the context. + */ +Pango2Gravity +pango2_context_get_gravity (Pango2Context *context) +{ + g_return_val_if_fail (context != NULL, PANGO2_GRAVITY_SOUTH); + + return context->resolved_gravity; +} + +/** + * pango2_context_set_gravity_hint: + * @context: a `Pango2Context` + * @hint: the new gravity hint + * + * Sets the gravity hint for the context. + * + * The gravity hint is used in laying vertical text out, and + * is only relevant if gravity of the context as returned by + * [method@Pango2.Context.get_gravity] is set to %PANGO2_GRAVITY_EAST + * or %PANGO2_GRAVITY_WEST. + */ +void +pango2_context_set_gravity_hint (Pango2Context *context, + Pango2GravityHint hint) +{ + g_return_if_fail (context != NULL); + + if (hint == context->gravity_hint) + return; + + context_changed (context); + + context->gravity_hint = hint; + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_GRAVITY_HINT]); +} + +/** + * pango2_context_get_gravity_hint: + * @context: a `Pango2Context` + * + * Retrieves the gravity hint for the context. + * + * See [method@Pango2.Context.set_gravity_hint] for details. + * + * Return value: the gravity hint for the context. + */ +Pango2GravityHint +pango2_context_get_gravity_hint (Pango2Context *context) +{ + g_return_val_if_fail (context != NULL, PANGO2_GRAVITY_HINT_NATURAL); + + return context->gravity_hint; +} + + +static gboolean +get_first_metrics_foreach (Pango2Fontset *fontset, + Pango2Font *font, + gpointer data) +{ + Pango2FontMetrics **fontset_metrics = data; + Pango2Language *language = pango2_fontset_get_language (fontset); + + *fontset_metrics = pango2_font_get_metrics (font, language); + + return TRUE; /* Stops iteration */ +} + +static Pango2FontMetrics * +get_base_metrics (Pango2Fontset *fontset) +{ + Pango2FontMetrics *metrics = NULL; + + /* Initialize the metrics from the first font in the fontset */ + pango2_fontset_foreach (fontset, get_first_metrics_foreach, &metrics); + + return metrics; +} + +static void +update_metrics_from_items (Pango2FontMetrics *metrics, + Pango2Language *language, + const char *text, + unsigned int text_len, + GList *items) + +{ + GHashTable *fonts_seen = g_hash_table_new (NULL, NULL); + Pango2GlyphString *glyphs = pango2_glyph_string_new (); + GList *l; + glong text_width; + + /* This should typically be called with a sample text string. */ + g_return_if_fail (text_len > 0); + + metrics->approximate_char_width = 0; + + for (l = items; l; l = l->next) + { + Pango2Item *item = l->data; + Pango2Font *font = item->analysis.font; + + if (font != NULL && g_hash_table_lookup (fonts_seen, font) == NULL) + { + Pango2FontMetrics *raw_metrics = pango2_font_get_metrics (font, language); + g_hash_table_insert (fonts_seen, font, font); + + /* metrics will already be initialized from the first font in the fontset */ + metrics->ascent = MAX (metrics->ascent, raw_metrics->ascent); + metrics->descent = MAX (metrics->descent, raw_metrics->descent); + metrics->height = MAX (metrics->height, raw_metrics->height); + pango2_font_metrics_free (raw_metrics); + } + + pango2_shape (text + item->offset, item->length, + text, text_len, + &item->analysis, glyphs, + PANGO2_SHAPE_NONE); + + metrics->approximate_char_width += pango2_glyph_string_get_width (glyphs); + } + + pango2_glyph_string_free (glyphs); + g_hash_table_destroy (fonts_seen); + + text_width = pango2_utf8_strwidth (text); + g_assert (text_width > 0); + metrics->approximate_char_width /= text_width; +} + +/** + * pango2_context_get_metrics: + * @context: a `Pango2Context` + * @desc: (nullable): a `Pango2FontDescription` structure. %NULL means that the + * font description from the context will be used. + * @language: (nullable): language tag used to determine which script to get + * the metrics for. %NULL means that the language tag from the context + * will be used. If no language tag is set on the context, metrics + * for the default language (as determined by [func@Pango2.Language.get_default] + * will be returned. + * + * Get overall metric information for a particular font description. + * + * Since the metrics may be substantially different for different scripts, + * a language tag can be provided to indicate that the metrics should be + * retrieved that correspond to the script(s) used by that language. + * + * The `Pango2FontDescription` is interpreted in the same way as by [func@itemize], + * and the family name may be a comma separated list of names. If characters + * from multiple of these families would be used to render the string, then + * the returned fonts would be a composite of the metrics for the fonts loaded + * for the individual families. + * + * Return value: a `Pango2FontMetrics` object. The caller must call + * [method@Pango2.FontMetrics.free] when finished using the object. + */ +Pango2FontMetrics * +pango2_context_get_metrics (Pango2Context *context, + const Pango2FontDescription *desc, + Pango2Language *language) +{ + Pango2Fontset *current_fonts = NULL; + Pango2FontMetrics *metrics; + const char *sample_str; + unsigned int text_len; + GList *items; + + g_return_val_if_fail (PANGO2_IS_CONTEXT (context), NULL); + + if (!desc) + desc = context->font_desc; + + if (!language) + language = context->language; + + if (desc == context->font_desc && + language == context->language && + context->metrics != NULL) + return pango2_font_metrics_copy (context->metrics); + + current_fonts = pango2_font_map_load_fontset (context->font_map, context, desc, language); + metrics = get_base_metrics (current_fonts); + + sample_str = pango2_language_get_sample_string (language); + text_len = strlen (sample_str); + items = pango2_itemize_with_font (context, context->base_dir, + sample_str, 0, text_len, + NULL, NULL, + desc); + + update_metrics_from_items (metrics, language, sample_str, text_len, items); + + g_list_foreach (items, (GFunc)pango2_item_free, NULL); + g_list_free (items); + + g_object_unref (current_fonts); + + if (desc == context->font_desc && + language == context->language) + { + pango2_font_metrics_free (context->metrics); + context->metrics = pango2_font_metrics_copy (metrics); + } + + return metrics; +} + +static void +context_changed (Pango2Context *context) +{ + context->serial++; + if (context->serial == 0) + context->serial++; + + g_clear_pointer (&context->metrics, pango2_font_metrics_free); +} + +/** + * pango2_context_changed: + * @context: a `Pango2Context` + * + * Forces a change in the context, which will cause any `Pango2Layout` + * using this context to re-layout. + * + * This function is only useful when implementing a new backend + * for Pango2, something applications won't do. Backends should + * call this function if they have attached extra data to the context + * and such data is changed. + */ +void +pango2_context_changed (Pango2Context *context) +{ + context_changed (context); +} + +static void +check_fontmap_changed (Pango2Context *context) +{ + guint old_serial = context->fontmap_serial; + + if (!context->font_map) + return; + + context->fontmap_serial = pango2_font_map_get_serial (context->font_map); + + if (old_serial != context->fontmap_serial) + context_changed (context); +} + +/** + * pango2_context_get_serial: + * @context: a `Pango2Context` + * + * Returns the current serial number of @context. + * + * The serial number is initialized to an small number larger than zero + * when a new context is created and is increased whenever the context + * is changed using any of the setter functions, or the `Pango2FontMap` it + * uses to find fonts has changed. The serial may wrap, but will never + * have the value 0. Since it can wrap, never compare it with "less than", + * always use "not equals". + * + * This can be used to automatically detect changes to a `Pango2Context`, + * and is only useful when implementing objects that need update when their + * `Pango2Context` changes, like `Pango2Layout`. + * + * Return value: The current serial number of @context. + */ +guint +pango2_context_get_serial (Pango2Context *context) +{ + check_fontmap_changed (context); + return context->serial; +} + +/** + * pango2_context_set_round_glyph_positions: + * @context: a `Pango2Context` + * @round_positions: whether to round glyph positions + * + * Sets whether font rendering with this context should + * round glyph positions and widths to integral positions, + * in device units. + * + * This is useful when the renderer can't handle subpixel + * positioning of glyphs. + * + * The default value is to round glyph positions, to remain + * compatible with previous Pango2 behavior. + */ +void +pango2_context_set_round_glyph_positions (Pango2Context *context, + gboolean round_positions) +{ + if (context->round_glyph_positions == round_positions) + return; + + context->round_glyph_positions = round_positions; + context_changed (context); + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_ROUND_GLYPH_POSITIONS]); +} + +/** + * pango2_context_get_round_glyph_positions: + * @context: a `Pango2Context` + * + * Returns whether font rendering with this context should + * round glyph positions and widths. + */ +gboolean +pango2_context_get_round_glyph_positions (Pango2Context *context) +{ + return context->round_glyph_positions; +} |