diff options
Diffstat (limited to 'pango2/pango-hbfont.c')
-rw-r--r-- | pango2/pango-hbfont.c | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/pango2/pango-hbfont.c b/pango2/pango-hbfont.c new file mode 100644 index 00000000..a8876224 --- /dev/null +++ b/pango2/pango-hbfont.c @@ -0,0 +1,1097 @@ +/* Pango2 + * + * Copyright (C) 2021 Matthias Clasen + * + * 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 "pango-hbfont-private.h" + +#include "pango-font-private.h" +#include "pango-font-metrics-private.h" +#include "pango-hbface-private.h" +#include "pango-hbfamily-private.h" +#include "pango-impl-utils.h" +#include "pango-language-set-private.h" + +#include <hb-ot.h> + +/** + * Pango2HbFont: + * + * `Pango2HbFont` is a `Pango2Font` implementation that wraps + * a `hb_font_t` object and implements all of the `Pango2Font` + * functionality using HarfBuzz. + * + * In addition to a `Pango2HbFace` and a size, a number of optional + * parameters can be tweaked when creating a `Pango2HbFont`. First + * there are OpenType font features, which can be used to e.g. + * select Small Caps. If the face has variation axes, then + * coordinates for these axes can be provided. Finally, there are + * rendering parameters such as the dpi and the global transformation + * matrix. + */ + +/* {{{ Utilities */ + +static int +get_average_char_width (Pango2Font *font, + const char *text) +{ + hb_font_t *hb_font = pango2_font_get_hb_font (font); + int width = 0; + + for (const char *p = text; *p; p = g_utf8_next_char (p)) + { + gunichar wc; + hb_codepoint_t glyph; + Pango2Rectangle extents; + + wc = g_utf8_get_char (p); + if (!hb_font_get_nominal_glyph (hb_font, wc, &glyph)) + continue; + + pango2_font_get_glyph_extents (font, glyph, &extents, NULL); + + width += extents.x + extents.width; + } + + return width / pango2_utf8_strwidth (text); +} + +static void +get_max_char_size (Pango2Font *font, + const char *text, + int *width, + int *height) +{ + hb_font_t *hb_font = pango2_font_get_hb_font (font); + int w = 0; + int h = 0; + + for (const char *p = text; *p; p = g_utf8_next_char (p)) + { + gunichar wc; + hb_codepoint_t glyph; + Pango2Rectangle extents; + + wc = g_utf8_get_char (p); + if (!hb_font_get_nominal_glyph (hb_font, wc, &glyph)) + continue; + + pango2_font_get_glyph_extents (font, glyph, &extents, NULL); + + w = MAX (w, extents.x + extents.width); + h = MAX (h, extents.height); + } + + if (width) + *width = w; + + if (height) + *height = h; +} + +static Pango2Variant +pango2_variant_from_features (hb_feature_t *features, + unsigned int n_features) +{ + Pango2Variant variant = PANGO2_VARIANT_NORMAL; + gboolean all_caps = FALSE; + + for (int i = 0; i < n_features; i++) + { + if (features[i].value != 1) + continue; + + switch (features[i].tag) + { + case HB_TAG('s','m','c','p'): + if (all_caps) + variant = PANGO2_VARIANT_ALL_SMALL_CAPS; + else + variant = PANGO2_VARIANT_SMALL_CAPS; + break; + case HB_TAG('c','2','s','c'): + if (variant == PANGO2_VARIANT_SMALL_CAPS) + variant = PANGO2_VARIANT_ALL_SMALL_CAPS; + else + all_caps = TRUE; + break; + case HB_TAG('p','c','a','p'): + if (all_caps) + variant = PANGO2_VARIANT_ALL_PETITE_CAPS; + else + variant = PANGO2_VARIANT_PETITE_CAPS; + break; + case HB_TAG('c','2','p','c'): + if (variant == PANGO2_VARIANT_PETITE_CAPS) + variant = PANGO2_VARIANT_ALL_PETITE_CAPS; + else + all_caps = TRUE; + break; + case HB_TAG('u','n','i','c'): + variant = PANGO2_VARIANT_UNICASE; + break; + case HB_TAG('t','i','t','l'): + variant = PANGO2_VARIANT_TITLE_CAPS; + break; + default: + break; + } + } + + return variant; +} + +static void +font_description_get_features (const Pango2FontDescription *description, + hb_feature_t *features, + unsigned int length, + unsigned int *n_features) +{ + +#define ADD_FEATURE(name) \ + features[*n_features].tag = HB_TAG (name[0], name[1], name[2], name[3]); \ + features[*n_features].value = 1; \ + features[*n_features].start = 0; \ + features[*n_features].end = (unsigned int) -1; \ + (*n_features)++ + + g_assert (length >= 2); + + *n_features = 0; + switch (pango2_font_description_get_variant (description)) + { + case PANGO2_VARIANT_SMALL_CAPS: + ADD_FEATURE ("smcp"); + break; + case PANGO2_VARIANT_ALL_SMALL_CAPS: + ADD_FEATURE ("smcp"); + ADD_FEATURE ("c2sc"); + break; + case PANGO2_VARIANT_PETITE_CAPS: + ADD_FEATURE ("pcap"); + break; + case PANGO2_VARIANT_ALL_PETITE_CAPS: + ADD_FEATURE ("pcap"); + ADD_FEATURE ("c2pc"); + break; + case PANGO2_VARIANT_UNICASE: + ADD_FEATURE ("unic"); + break; + case PANGO2_VARIANT_TITLE_CAPS: + ADD_FEATURE ("titl"); + break; + case PANGO2_VARIANT_NORMAL: + break; + default: + g_assert_not_reached (); + } + +#undef ADD_FEATURE +} + +static unsigned int +count_variations (const char *string) +{ + unsigned int n; + const char *p; + + n = 1; + p = string; + while ((p = strchr (p, ',')) != NULL) + n++; + + return n; +} + +static void +parse_variations (const char *str, + hb_variation_t *variations, + unsigned int length, + unsigned int *n_variations) +{ + const char *p; + + *n_variations = 0; + + p = str; + while (p && *p && *n_variations < length) + { + const char *end = strchr (p, ','); + if (hb_variation_from_string (p, end ? end - p: -1, &variations[*n_variations])) + (*n_variations)++; + p = end ? end + 1 : NULL; + } +} + +static char * +variations_to_string (hb_variation_t *variations, + unsigned int n_variations) +{ + GString *s; + char buf[128]; + + s = g_string_new (""); + + for (unsigned int i = 0; i < n_variations; i++) + { + hb_variation_to_string (&variations[i], buf, sizeof (buf)); + if (s->len > 0) + g_string_append_c (s, ','); + g_string_append (s, buf); + } + + return g_string_free (s, FALSE); +} + +static inline void +replace_variation (hb_variation_t *values, + unsigned int *len, + const hb_variation_t *v) +{ + for (int i = 0; i < *len; i++) + { + if (values[i].tag == v->tag) + { + values[i].value = v->value; + return; + } + } + + values[(*len)++] = *v; +} + +static unsigned int +merge_variations (const hb_variation_t *v1, + unsigned int l1, + const hb_variation_t *v2, + unsigned int l2, + hb_variation_t *v, + unsigned int l) +{ + unsigned int len = 0; + + for (int i = 0; i < l1; i++) + replace_variation (v, &len, &v1[i]); + + for (int i = 0; i < l2; i++) + replace_variation (v, &len, &v2[i]); + + return len; +} + +static inline void +collect_variation (hb_variation_t *variation, + unsigned int n_axes, + hb_ot_var_axis_info_t *axes, + float *coords) +{ + for (int j = 0; j < n_axes; j++) + { + if (axes[j].tag == variation->tag) + { + coords[axes[j].axis_index] = variation->value; + break; + } + } +} + +static inline void +collect_variations (hb_variation_t *variations, + unsigned int n_variations, + unsigned int n_axes, + hb_ot_var_axis_info_t *axes, + float *coords) +{ + for (int i = 0; i < n_variations; i++) + collect_variation (&variations[i], n_axes, axes, coords); +} + +/* }}} */ +/* {{{ hex box sizing */ + +/* This code needs to stay in sync with the hexbox rendering code in pangocairo-render.c */ +static HexBoxInfo * +create_hex_box_info (Pango2HbFont *self) +{ + Pango2Font *font = PANGO2_FONT (self); + const char hexdigits[] = "0123456789ABCDEF"; + hb_font_t *hb_font; + Pango2Font *mini_font; + HexBoxInfo *hbi; + int rows; + double pad; + int width = 0; + int height = 0; + hb_font_extents_t font_extents; + double font_ascent, font_descent; + double mini_size; + Pango2FontDescription *desc; + Pango2Context *context; + Pango2FontMap *map; + + if (!PANGO2_FONT_FACE (font->face)->family) + return NULL; + + map = PANGO2_FONT_FACE (font->face)->family->map; + + if (!map) + return NULL; + + desc = pango2_font_describe_with_absolute_size (font); + hb_font = pango2_font_get_hb_font (font); + + /* Create mini_font description */ + + /* We inherit most font properties for the mini font. + * Just change family and size, so you get bold + * hex digits in the hexbox for a bold font. + */ + + /* We should rotate the box, not glyphs */ + pango2_font_description_unset_fields (desc, PANGO2_FONT_MASK_GRAVITY); + + pango2_font_description_set_family_static (desc, "monospace"); + + rows = 2; + mini_size = font->size / 2.2; + + if (mini_size < 6.0) + { + rows = 1; + mini_size = MIN (MAX (font->size - 1, 0), 6.0); + } + + pango2_font_description_set_size (desc, mini_size); + + /* Load mini_font */ + context = pango2_context_new_with_font_map (map); + pango2_context_set_matrix (context, &font->ctm); + pango2_context_set_language (context, pango2_script_get_sample_language (G_UNICODE_SCRIPT_LATIN)); + + mini_font = pango2_font_map_load_font (map, context, desc); + + g_object_unref (context); + + pango2_font_description_free (desc); + + get_max_char_size (mini_font, hexdigits, &width, &height); + + hb_font_get_extents_for_direction (hb_font, HB_DIRECTION_LTR, &font_extents); + font_ascent = font_extents.ascender / (double) PANGO2_SCALE; + font_descent = - font_extents.descender / (double) PANGO2_SCALE; + + pad = (font_ascent + font_descent) / 43.; + pad = MIN (pad, mini_size / (double) PANGO2_SCALE); + + hbi = g_new (HexBoxInfo, 1); + hbi->font = mini_font; + hbi->rows = rows; + + hbi->digit_width = width / (double) PANGO2_SCALE; + hbi->digit_height = height / (double) PANGO2_SCALE; + + hbi->pad_x = pad; + hbi->pad_y = pad; + + hbi->line_width = MIN (hbi->pad_x, hbi->pad_y); + + hbi->box_height = 3 * hbi->pad_y + rows * (hbi->pad_y + hbi->digit_height); + + if (rows == 1 || hbi->box_height <= font_ascent) + hbi->box_descent = 2 * hbi->pad_y; + else if (hbi->box_height <= font_ascent + font_descent - 2 * hbi->pad_y) + hbi->box_descent = 2 * hbi->pad_y + hbi->box_height - font_ascent; + else + hbi->box_descent = font_descent * hbi->box_height / (font_ascent + font_descent); + + return hbi; +} + +static void +get_space_extents (Pango2HbFont *self, + Pango2Rectangle *ink_rect, + Pango2Rectangle *logical_rect) +{ + Pango2Font *font = PANGO2_FONT (self); + hb_font_t *hb_font = pango2_font_get_hb_font (font); + int width; + hb_direction_t direction; + hb_font_extents_t font_extents; + + direction = PANGO2_GRAVITY_IS_VERTICAL (font->gravity) + ? HB_DIRECTION_TTB + : HB_DIRECTION_LTR; + + hb_font_get_extents_for_direction (hb_font, direction, &font_extents); + + /* See https://docs.microsoft.com/en-us/typography/develop/character-design-standards/whitespace */ + + width = font->size / 4; + + if (ink_rect) + { + ink_rect->x = ink_rect->y = ink_rect->height = 0; + ink_rect->width = width; + } + + if (logical_rect) + { + logical_rect->x = 0; + logical_rect->y = - font_extents.ascender; + logical_rect->height = font_extents.ascender - font_extents.descender; + logical_rect->width = width; + } +} + +static void +get_glyph_extents_missing (Pango2HbFont *self, + Pango2Glyph glyph, + Pango2Rectangle *ink_rect, + Pango2Rectangle *logical_rect) +{ + gunichar ch; + int rows, cols; + HexBoxInfo *hbi; + + ch = glyph & ~PANGO2_GLYPH_UNKNOWN_FLAG; + + if (!self->hex_box_info) + self->hex_box_info = create_hex_box_info (self); + + if (ch == 0x20 || ch == 0x2423) + { + get_space_extents (self, ink_rect, logical_rect); + return; + } + + hbi = self->hex_box_info; + + if (G_UNLIKELY (glyph == PANGO2_GLYPH_INVALID_INPUT || ch > 0x10FFFF)) + { + rows = hbi->rows; + cols = 1; + } + else if (pango2_get_ignorable_size (ch, &rows, &cols)) + { + /* We special-case ignorables when rendering hex boxes */ + } + else + { + rows = hbi->rows; + cols = (ch > 0xffff ? 6 : 4) / rows; + } + + if (ink_rect) + { + ink_rect->x = PANGO2_SCALE * hbi->pad_x; + ink_rect->y = PANGO2_SCALE * (hbi->box_descent - hbi->box_height); + ink_rect->width = PANGO2_SCALE * (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x)); + ink_rect->height = PANGO2_SCALE * hbi->box_height; + } + + if (logical_rect) + { + logical_rect->x = 0; + logical_rect->y = PANGO2_SCALE * (hbi->box_descent - (hbi->box_height + hbi->pad_y)); + logical_rect->width = PANGO2_SCALE * (5 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x)); + logical_rect->height = PANGO2_SCALE * (hbi->box_height + 2 * hbi->pad_y); + } +} + + /* }}} */ +/* {{{ Pango2Font implementation */ + +struct _Pango2HbFontClass +{ + Pango2FontClass parent_class; +}; + +enum { + PROP_VARIATIONS = 1, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +G_DEFINE_FINAL_TYPE (Pango2HbFont, pango2_hb_font, PANGO2_TYPE_FONT) + +static void +pango2_hb_font_init (Pango2HbFont *self) +{ +} + +static void +hex_box_info_destroy (HexBoxInfo *hex_box_info) +{ + g_object_unref (hex_box_info->font); + g_free (hex_box_info); +} + +static void +pango2_hb_font_finalize (GObject *object) +{ + Pango2HbFont *self = PANGO2_HB_FONT (object); + + g_free (self->variations); + g_clear_pointer (&self->hex_box_info, hex_box_info_destroy); + + G_OBJECT_CLASS (pango2_hb_font_parent_class)->finalize (object); +} + + +static void +pango2_hb_font_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + Pango2HbFont *self = PANGO2_HB_FONT (object); + + switch (property_id) + { + case PROP_VARIATIONS: + { + char *str = NULL; + if (self->variations) + str = variations_to_string (self->variations, self->n_variations); + g_value_take_string (value, str); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static Pango2FontDescription * +pango2_hb_font_describe (Pango2Font *font) +{ + Pango2HbFont *self = PANGO2_HB_FONT (font); + Pango2HbFace *face = PANGO2_HB_FACE (font->face); + Pango2FontDescription *desc; + Pango2Variant variant; + + desc = pango2_font_face_describe (font->face); + pango2_font_description_set_gravity (desc, font->gravity); + pango2_font_description_set_size (desc, font->size); + + variant = pango2_variant_from_features (self->features, self->n_features); + if (variant != PANGO2_VARIANT_NORMAL) + pango2_font_description_set_variant (desc, variant); + if (self->n_variations > 0 || face->n_variations > 0) + { + hb_variation_t *variations; + unsigned int n_variations; + char *str; + + if (face->n_variations > 0) + { + variations = g_alloca (sizeof (hb_variation_t) * (self->n_variations + face->n_variations)); + n_variations = merge_variations (face->variations, face->n_variations, + self->variations, self->n_variations, + variations, self->n_variations + face->n_variations); + } + else + { + variations = self->variations; + n_variations = self->n_variations; + } + + str = variations_to_string (variations, n_variations); + pango2_font_description_set_variations (desc, str); + g_free (str); + } + + return desc; +} + +static void +pango2_hb_font_get_glyph_extents (Pango2Font *font, + Pango2Glyph glyph, + Pango2Rectangle *ink_rect, + Pango2Rectangle *logical_rect) +{ + Pango2HbFont *self = PANGO2_HB_FONT (font); + Pango2HbFace *face = PANGO2_HB_FACE (font->face); + hb_font_t *hb_font = pango2_font_get_hb_font (font); + hb_glyph_extents_t extents; + hb_direction_t direction; + hb_font_extents_t font_extents; + + direction = PANGO2_GRAVITY_IS_VERTICAL (font->gravity) + ? HB_DIRECTION_TTB + : HB_DIRECTION_LTR; + + hb_font_get_extents_for_direction (hb_font, direction, &font_extents); + + if (glyph == PANGO2_GLYPH_EMPTY) + { + if (ink_rect) + ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0; + + if (logical_rect) + { + logical_rect->x = logical_rect->width = 0; + logical_rect->y = - font_extents.ascender; + logical_rect->height = font_extents.ascender - font_extents.descender; + } + + return; + } + else if (glyph & PANGO2_GLYPH_UNKNOWN_FLAG) + { + get_glyph_extents_missing (self, glyph, ink_rect, logical_rect); + + return; + } + + hb_font_get_glyph_extents (hb_font, glyph, &extents); + + if (ink_rect) + { + Pango2Rectangle r; + Pango2Matrix m = PANGO2_MATRIX_INIT; + + r.x = extents.x_bearing; + r.y = - extents.y_bearing; + r.width = extents.width; + r.height = - extents.height; + + if (face->transform) + { + m.xx = face->transform->xx; + m.yx = - face->transform->yx; + m.xy = - face->transform->xy; + m.yy = face->transform->yy; + + pango2_matrix_transform_rectangle (&m, &r); + } + + switch (font->gravity) + { + case PANGO2_GRAVITY_AUTO: + case PANGO2_GRAVITY_SOUTH: + ink_rect->x = r.x; + ink_rect->y = r.y; + ink_rect->width = r.width; + ink_rect->height = r.height; + break; + case PANGO2_GRAVITY_NORTH: + ink_rect->x = - r.x; + ink_rect->y = - r.y; + ink_rect->width = - r.width; + ink_rect->height = - r.height; + break; + case PANGO2_GRAVITY_EAST: + ink_rect->x = r.y; + ink_rect->y = - r.x - r.width; + ink_rect->width = r.height; + ink_rect->height = r.width; + break; + case PANGO2_GRAVITY_WEST: + ink_rect->x = - r.y - r.height; + ink_rect->y = r.x; + ink_rect->width = r.height; + ink_rect->height = r.width; + break; + default: + g_assert_not_reached (); + } + + if (PANGO2_GRAVITY_IS_IMPROPER (font->gravity)) + { + Pango2Matrix matrix = (Pango2Matrix) PANGO2_MATRIX_INIT; + pango2_matrix_scale (&matrix, -1, -1); + pango2_matrix_transform_rectangle (&matrix, ink_rect); + } + } + + if (logical_rect) + { + hb_position_t h_advance; + hb_font_extents_t extents; + + h_advance = hb_font_get_glyph_h_advance (hb_font, glyph); + hb_font_get_h_extents (hb_font, &extents); + + logical_rect->x = 0; + logical_rect->height = extents.ascender - extents.descender; + + switch (font->gravity) + { + case PANGO2_GRAVITY_AUTO: + case PANGO2_GRAVITY_SOUTH: + logical_rect->y = - extents.ascender; + logical_rect->width = h_advance; + break; + case PANGO2_GRAVITY_NORTH: + logical_rect->y = extents.descender; + logical_rect->width = h_advance; + break; + case PANGO2_GRAVITY_EAST: + logical_rect->y = - logical_rect->height / 2; + logical_rect->width = logical_rect->height; + break; + case PANGO2_GRAVITY_WEST: + logical_rect->y = - logical_rect->height / 2; + logical_rect->width = - logical_rect->height; + break; + default: + g_assert_not_reached (); + } + + if (PANGO2_GRAVITY_IS_IMPROPER (font->gravity)) + { + logical_rect->height = - logical_rect->height; + logical_rect->y = - logical_rect->y; + } + } +} + +static Pango2FontMetrics * +pango2_hb_font_get_metrics (Pango2Font *font, + Pango2Language *language) +{ + Pango2HbFont *self = PANGO2_HB_FONT (font); + hb_font_t *hb_font = pango2_font_get_hb_font (font); + Pango2FontMetrics *metrics; + hb_font_extents_t extents; + hb_position_t position; + + metrics = pango2_font_metrics_new (); + + hb_font_get_extents_for_direction (hb_font, HB_DIRECTION_LTR, &extents); + + metrics->descent = - extents.descender; + metrics->ascent = extents.ascender; + metrics->height = extents.ascender - extents.descender + extents.line_gap; + + if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_UNDERLINE_SIZE, &position) && position != 0) + metrics->underline_thickness = position; + else + metrics->underline_thickness = PANGO2_SCALE; + + if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_UNDERLINE_OFFSET, &position) && position != 0) + metrics->underline_position = position; + else + metrics->underline_position = - PANGO2_SCALE; + + if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_STRIKEOUT_SIZE, &position) && position != 0) + metrics->strikethrough_thickness = position; + else + metrics->strikethrough_thickness = PANGO2_SCALE; + + if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, &position) && position != 0) + metrics->strikethrough_position = position; + else + metrics->strikethrough_position = metrics->ascent / 2; + + if (self->approximate_char_width == 0 || self->approximate_char_lang != language) + { + self->approximate_char_width = get_average_char_width (font, pango2_language_get_sample_string (language)); + self->approximate_char_lang = language; + } + + if (self->approximate_digit_width == 0) + get_max_char_size (font, "0123456789", &self->approximate_digit_width, NULL); + + metrics->approximate_char_width = self->approximate_char_width; + metrics->approximate_digit_width = self->approximate_digit_width; + + return metrics; +} + +static hb_font_t * +pango2_hb_font_create_hb_font (Pango2Font *font) +{ + Pango2HbFont *self = PANGO2_HB_FONT (font); + Pango2HbFace *face = PANGO2_HB_FACE (font->face); + hb_font_t *hb_font; + double x_scale, y_scale; + unsigned int n_axes; + hb_ot_var_axis_info_t *axes; + float *coords; + int size; + + hb_font = hb_font_create (pango2_hb_face_get_hb_face (face)); + + size = font->size * font->dpi / 72.f; + x_scale = face->x_scale; + y_scale = face->y_scale; + + if (PANGO2_GRAVITY_IS_IMPROPER (font->gravity)) + { + x_scale = - x_scale; + y_scale = - y_scale; + } + + hb_font_set_scale (hb_font, size * x_scale, size * y_scale); + hb_font_set_ptem (hb_font, font->size / PANGO2_SCALE); + +#if HB_VERSION_ATLEAST (3, 3, 0) + hb_font_set_synthetic_slant (hb_font, pango2_matrix_get_slant_ratio (face->transform)); +#endif + + if (face->instance_id >= 0) + hb_font_set_var_named_instance (hb_font, face->instance_id); + + if (self->n_variations > 0 || face->n_variations > 0) + { + n_axes = hb_ot_var_get_axis_count (face->face); + axes = g_alloca (sizeof (hb_ot_var_axis_info_t) * n_axes); + coords = g_alloca (sizeof (float) * n_axes); + + hb_ot_var_get_axis_infos (face->face, 0, &n_axes, axes); + + if (face->instance_id >= 0) + hb_ot_var_named_instance_get_design_coords (face->face, face->instance_id, &n_axes, coords); + else + { + for (int i = 0; i < n_axes; i++) + coords[axes[i].axis_index] = axes[i].default_value; + } + + collect_variations (face->variations, face->n_variations, n_axes, axes, coords); + collect_variations (self->variations, self->n_variations, n_axes, axes, coords); + + hb_font_set_var_coords_design (hb_font, coords, n_axes); + } + + return hb_font; +} + +static void +pango2_hb_font_get_transform (Pango2Font *font, + Pango2Matrix *matrix) +{ + Pango2HbFace *face = PANGO2_HB_FACE (font->face); + + if (face->transform) + { + *matrix = *face->transform; + pango2_matrix_scale (matrix, face->x_scale, face->y_scale); + } + else + *matrix = (Pango2Matrix) PANGO2_MATRIX_INIT; +} + +static void +pango2_hb_font_class_init (Pango2HbFontClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + Pango2FontClass *font_class = PANGO2_FONT_CLASS (class); + + object_class->finalize = pango2_hb_font_finalize; + object_class->get_property = pango2_hb_font_get_property; + + font_class->describe = pango2_hb_font_describe; + font_class->get_glyph_extents = pango2_hb_font_get_glyph_extents; + font_class->get_metrics = pango2_hb_font_get_metrics; + font_class->create_hb_font = pango2_hb_font_create_hb_font; + font_class->get_transform = pango2_hb_font_get_transform; + + /** + * Pango2HbFont:variations: (attributes org.gtk.Property.get=pango2_hb_font_get_variations) + * + * The variations that are applied for this font. + * + * This property contains a string representation of the variations. + */ + properties[PROP_VARIATIONS] = + g_param_spec_string ("variations", NULL, NULL, NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); +} + +/* }}} */ + /* {{{ Public API */ + +/** + * pango2_hb_font_new: + * @face: the `Pango2HbFace` to use + * @size: the desired size in points, scaled by `PANGO2_SCALE` + * @features: (nullable) (array length=n_features): OpenType font features to use for this font + * @n_features: length of @features + * @variations: (nullable) (array length=n_variations): font variations to apply + * @n_variations: length of @variations + * @gravity: the gravity to use when rendering + * @dpi: the dpi used when rendering + * @ctm: (nullable): transformation matrix to use when rendering + * + * Creates a new `Pango2HbFont`. + * + * Returns: a newly created `Pango2HbFont` + */ +Pango2HbFont * +pango2_hb_font_new (Pango2HbFace *face, + int size, + hb_feature_t *features, + unsigned int n_features, + hb_variation_t *variations, + unsigned int n_variations, + Pango2Gravity gravity, + float dpi, + const Pango2Matrix *ctm) +{ + Pango2HbFont *self; + Pango2Font *font; + + g_return_val_if_fail (PANGO2_IS_HB_FACE (face), NULL); + g_return_val_if_fail (size > 0, NULL); + g_return_val_if_fail (dpi > 0, NULL); + + self = g_object_new (PANGO2_TYPE_HB_FONT, NULL); + + font = PANGO2_FONT (self); + + pango2_font_set_face (font, PANGO2_FONT_FACE (face)); + pango2_font_set_size (font, size); + pango2_font_set_dpi (font, dpi); + pango2_font_set_gravity (font, gravity); + pango2_font_set_ctm (font, ctm); + + self->features = g_memdup2 (features, sizeof (hb_feature_t) * n_features); + self->n_features = n_features; + self->variations = g_memdup2 (variations, sizeof (hb_variation_t) * n_variations); + self->n_variations = n_variations; + + return self; +} + +/** + * pango2_hb_font_new_for_description: + * @face: the `Pango2HbFace` to use + * @description: a `Pango2FontDescription` + * @dpi: the dpi used when rendering + * @ctm: (nullable): transformation matrix to use when rendering + * + * Creates a new `Pango2HbFont` with size, features, variations and + * gravity taken from a font description. + * + * Returns: a newly created `Pango2HbFont` + */ +Pango2HbFont * +pango2_hb_font_new_for_description (Pango2HbFace *face, + const Pango2FontDescription *description, + float dpi, + const Pango2Matrix *ctm) +{ + int size; + hb_feature_t features[10]; + unsigned int n_features; + hb_variation_t *variations; + unsigned int n_variations; + unsigned int length; + Pango2Gravity gravity; + const char *str; + + g_return_val_if_fail (PANGO2_IS_HB_FACE (face), NULL); + g_return_val_if_fail (description != NULL, NULL); + g_return_val_if_fail (dpi > 0, NULL); + + if ((pango2_font_description_get_set_fields (description) & PANGO2_FONT_MASK_SIZE) == 0) + size = 10 * PANGO2_SCALE; + else if (pango2_font_description_get_size_is_absolute (description)) + size = pango2_font_description_get_size (description) * 72. / dpi; + else + size = pango2_font_description_get_size (description); + + g_return_val_if_fail (size > 0, NULL); + + font_description_get_features (description, features, G_N_ELEMENTS (features), &n_features); + + if ((pango2_font_description_get_set_fields (description) & PANGO2_FONT_MASK_VARIATIONS) != 0) + { + str = pango2_font_description_get_variations (description); + length = count_variations (str); + variations = g_alloca (sizeof (hb_variation_t) * length); + parse_variations (str, variations, length, &n_variations); + } + else + { + variations = NULL; + n_variations = 0; + } + + if ((pango2_font_description_get_set_fields (description) & PANGO2_FONT_MASK_GRAVITY) != 0 && + pango2_font_description_get_gravity (description) != PANGO2_GRAVITY_SOUTH) + gravity = pango2_font_description_get_gravity (description); + else + gravity = PANGO2_GRAVITY_AUTO; + + return pango2_hb_font_new (face, size, features, n_features, variations, n_variations, gravity, dpi, ctm); +} + +/** + * pango2_hb_font_get_features: + * @self: a `Pango2Font` + * @n_features: (nullable) (out caller-allocates): return location for + * the length of the returned array + * + * Obtain the OpenType features that are provided by the font. + * + * These are passed to the rendering system, together with features + * that have been explicitly set via attributes. + * + * Note that this does not include OpenType features which the + * rendering system enables by default. + * + * Returns: (nullable) (transfer none): the features + */ +const hb_feature_t * +pango2_hb_font_get_features (Pango2HbFont *self, + unsigned int *n_features) +{ + g_return_val_if_fail (PANGO2_IS_HB_FONT (self), NULL); + + if (n_features) + *n_features = self->n_features; + + return self->features; +} + + +/** + * pango2_hb_font_get_variations: + * @self: a `Pango2HbFont` + * @n_variations: (nullable) (out caller-allocates): return location for + * the length of the returned array + * + * Gets the variations of the font. + * + * Returns: (nullable) (transfer none): the variations + */ +const hb_variation_t * +pango2_hb_font_get_variations (Pango2HbFont *self, + unsigned int *n_variations) +{ + g_return_val_if_fail (PANGO2_IS_HB_FONT (self), NULL); + + if (n_variations) + *n_variations = self->n_variations; + + return self->variations; +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ |