diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-10-29 19:19:31 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2022-01-28 09:03:03 -0500 |
commit | 5fd63d21e97e512b81b678b01ae3a8b6245d783c (patch) | |
tree | 48af0296f9c6c55b30458b09b29ff9998eb0c7b5 | |
parent | 25dead66ee992a529660f30b5fa692da345b7991 (diff) | |
download | pango-5fd63d21e97e512b81b678b01ae3a8b6245d783c.tar.gz |
Add PangoHbFont
This is a simple wrapper around hb_font_t.
This is an attempt to make a font backend that does
not depend on platform specifics and can be used in
tests.
-rw-r--r-- | pango/meson.build | 2 | ||||
-rw-r--r-- | pango/pango-hbfont-private.h | 56 | ||||
-rw-r--r-- | pango/pango-hbfont.c | 1047 | ||||
-rw-r--r-- | pango/pango-hbfont.h | 50 | ||||
-rw-r--r-- | pango/pango.h | 1 |
5 files changed, 1156 insertions, 0 deletions
diff --git a/pango/meson.build b/pango/meson.build index ef498d6b..0c6e204a 100644 --- a/pango/meson.build +++ b/pango/meson.build @@ -37,6 +37,7 @@ pango_sources = [ 'pango-hbface.c', 'pango-hbfamily.c', 'pango-generic-family.c', + 'pango-hbfont.c', ] pango_headers = [ @@ -69,6 +70,7 @@ pango_headers = [ 'pango-types.h', 'pango-utils.h', 'pango-hbface.h', + 'pango-hbfont.h', ] pango_installed_headers = pango_headers + [ 'pango-version-macros.h' ] diff --git a/pango/pango-hbfont-private.h b/pango/pango-hbfont-private.h new file mode 100644 index 00000000..fea6162f --- /dev/null +++ b/pango/pango-hbfont-private.h @@ -0,0 +1,56 @@ +/* Pango + * + * 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. + */ + +#pragma once + +#include "pango-hbfont.h" +#include "pango-hbface.h" +#include <hb.h> + +typedef struct _HexBoxInfo HexBoxInfo; +struct _HexBoxInfo +{ + PangoFont *font; + int rows; + double digit_width; + double digit_height; + double pad_x; + double pad_y; + double line_width; + double box_descent; + double box_height; +}; + +struct _PangoHbFont +{ + PangoFont parent_instance; + + PangoHbFace *face; + int size; /* point size, scaled by PANGO_SCALE */ + float dpi; + hb_feature_t *features; + unsigned int n_features; + hb_variation_t *variations; + unsigned int n_variations; + PangoGravity gravity; + PangoMatrix matrix; + + HexBoxInfo *hex_box_info; +}; diff --git a/pango/pango-hbfont.c b/pango/pango-hbfont.c new file mode 100644 index 00000000..ccfb2e61 --- /dev/null +++ b/pango/pango-hbfont.c @@ -0,0 +1,1047 @@ +/* Pango + * + * 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-coverage-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> + +/** + * PangoHbFont: + * + * `PangoHbFont` is a `PangoFont` implementation that wraps + * a `hb_font_t` object and implements all of the `PangoFont` + * functionality using HarfBuzz. + * + * In addition to a `PangoHbFace` and a size, a number of optional + * parameters can be tweaked when creating a `PangoHbFont`. 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 (PangoFont *font, + const char *text) +{ + hb_font_t *hb_font = pango_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; + PangoRectangle extents; + + wc = g_utf8_get_char (p); + if (!hb_font_get_nominal_glyph (hb_font, wc, &glyph)) + continue; + + pango_font_get_glyph_extents (font, glyph, &extents, NULL); + + width += extents.x + extents.width; + } + + return width / pango_utf8_strwidth (text); +} + +static void +get_max_char_size (PangoFont *font, + const char *text, + int *width, + int *height) +{ + hb_font_t *hb_font = pango_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; + PangoRectangle extents; + + wc = g_utf8_get_char (p); + if (!hb_font_get_nominal_glyph (hb_font, wc, &glyph)) + continue; + + pango_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 PangoFontMap * +pango_hb_font_get_font_map (PangoFont *font) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + PangoHbFamily *family = PANGO_HB_FAMILY (self->face->family); + + return family->map; +} + +static PangoVariant +pango_variant_from_features (hb_feature_t *features, + unsigned int n_features) +{ + PangoVariant variant = PANGO_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 = PANGO_VARIANT_ALL_SMALL_CAPS; + else + variant = PANGO_VARIANT_SMALL_CAPS; + break; + case HB_TAG('c','2','s','c'): + if (variant == PANGO_VARIANT_SMALL_CAPS) + variant = PANGO_VARIANT_ALL_SMALL_CAPS; + else + all_caps = TRUE; + break; + case HB_TAG('p','c','a','p'): + if (all_caps) + variant = PANGO_VARIANT_ALL_PETITE_CAPS; + else + variant = PANGO_VARIANT_PETITE_CAPS; + break; + case HB_TAG('c','2','p','c'): + if (variant == PANGO_VARIANT_PETITE_CAPS) + variant = PANGO_VARIANT_ALL_PETITE_CAPS; + else + all_caps = TRUE; + break; + case HB_TAG('u','n','i','c'): + variant = PANGO_VARIANT_UNICASE; + break; + case HB_TAG('t','i','t','l'): + variant = PANGO_VARIANT_TITLE_CAPS; + break; + default: + break; + } + } + + return variant; +} + +static void +font_description_get_features (const PangoFontDescription *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 (pango_font_description_get_variant (description)) + { + case PANGO_VARIANT_SMALL_CAPS: + ADD_FEATURE ("smcp"); + break; + case PANGO_VARIANT_ALL_SMALL_CAPS: + ADD_FEATURE ("smcp"); + ADD_FEATURE ("c2sc"); + break; + case PANGO_VARIANT_PETITE_CAPS: + ADD_FEATURE ("pcap"); + break; + case PANGO_VARIANT_ALL_PETITE_CAPS: + ADD_FEATURE ("pcap"); + ADD_FEATURE ("c2pc"); + break; + case PANGO_VARIANT_UNICASE: + ADD_FEATURE ("unic"); + break; + case PANGO_VARIANT_TITLE_CAPS: + ADD_FEATURE ("titl"); + break; + case PANGO_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 (PangoHbFont *self) +{ + const char hexdigits[] = "0123456789ABCDEF"; + hb_font_t *hb_font; + PangoFont *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; + PangoFontDescription *desc; + PangoContext *context; + PangoFontMap *map; + + map = pango_hb_font_get_font_map (PANGO_FONT (self)); + + if (!map) + return NULL; + + desc = pango_font_describe_with_absolute_size (PANGO_FONT (self)); + hb_font = pango_font_get_hb_font (PANGO_FONT (self)); + + /* 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 */ + pango_font_description_unset_fields (desc, PANGO_FONT_MASK_GRAVITY); + + pango_font_description_set_family_static (desc, "monospace"); + + rows = 2; + mini_size = self->size / 2.2; + + if (mini_size < 6.0) + { + rows = 1; + mini_size = MIN (MAX (self->size - 1, 0), 6.0); + } + + pango_font_description_set_size (desc, mini_size); + + /* Load mini_font */ + context = pango_font_map_create_context (map); + pango_context_set_matrix (context, &self->matrix); + pango_context_set_language (context, pango_script_get_sample_language (PANGO_SCRIPT_LATIN)); + + mini_font = pango_font_map_load_font (map, context, desc); + + g_object_unref (context); + + pango_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) PANGO_SCALE; + font_descent = - font_extents.descender / (double) PANGO_SCALE; + + pad = (font_ascent + font_descent) / 43.; + pad = MIN (pad, mini_size / (double) PANGO_SCALE); + + hbi = g_new (HexBoxInfo, 1); + hbi->font = mini_font; + hbi->rows = rows; + + hbi->digit_width = width / (double) PANGO_SCALE; + hbi->digit_height = height / (double) PANGO_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 (PangoHbFont *self, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + hb_font_t *hb_font = pango_font_get_hb_font (PANGO_FONT (self)); + int width; + hb_direction_t direction; + hb_font_extents_t font_extents; + + direction = PANGO_GRAVITY_IS_VERTICAL (self->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 = self->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 (PangoHbFont *self, + PangoGlyph glyph, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + gunichar ch; + int rows, cols; + HexBoxInfo *hbi; + + ch = glyph & ~PANGO_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 == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF)) + { + rows = hbi->rows; + cols = 1; + } + else if (pango_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 = PANGO_SCALE * hbi->pad_x; + ink_rect->y = PANGO_SCALE * (hbi->box_descent - hbi->box_height); + ink_rect->width = PANGO_SCALE * (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x)); + ink_rect->height = PANGO_SCALE * hbi->box_height; + } + + if (logical_rect) + { + logical_rect->x = 0; + logical_rect->y = PANGO_SCALE * (hbi->box_descent - (hbi->box_height + hbi->pad_y)); + logical_rect->width = PANGO_SCALE * (5 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x)); + logical_rect->height = PANGO_SCALE * (hbi->box_height + 2 * hbi->pad_y); + } +} + + /* }}} */ +/* {{{ PangoFont implementation */ + +struct _PangoHbFontClass +{ + PangoFontClass parent_class; +}; + +G_DEFINE_TYPE (PangoHbFont, pango_hb_font, PANGO_TYPE_FONT) + +static void +pango_hb_font_init (PangoHbFont *self) +{ + self->gravity = PANGO_GRAVITY_AUTO; + self->matrix = (PangoMatrix) PANGO_MATRIX_INIT; +} + +static void +hex_box_info_destroy (HexBoxInfo *hex_box_info) +{ + g_object_unref (hex_box_info->font); + g_free (hex_box_info); +} + +static void +pango_hb_font_finalize (GObject *object) +{ + PangoHbFont *self = PANGO_HB_FONT (object); + + g_object_unref (self->face); + g_free (self->variations); + g_clear_pointer (&self->hex_box_info, hex_box_info_destroy); + + G_OBJECT_CLASS (pango_hb_font_parent_class)->finalize (object); +} + +static PangoFontDescription * +pango_hb_font_describe (PangoFont *font) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + PangoFontDescription *desc; + PangoVariant variant; + + desc = pango_font_face_describe (PANGO_FONT_FACE (self->face)); + pango_font_description_set_gravity (desc, self->gravity); + variant = pango_variant_from_features (self->features, self->n_features); + if (variant != PANGO_VARIANT_NORMAL) + pango_font_description_set_variant (desc, variant); + if (self->n_variations > 0) + { + hb_variation_t *variations; + unsigned int n_variations; + char *str; + + if (self->face->n_variations > 0) + { + variations = g_alloca (sizeof (hb_variation_t) * (self->n_variations + self->face->n_variations)); + n_variations = merge_variations (self->face->variations, self->face->n_variations, + self->variations, self->n_variations, + variations, self->n_variations + self->face->n_variations); + } + else + { + variations = self->variations; + n_variations = self->n_variations; + } + + str = variations_to_string (variations, n_variations); + pango_font_description_set_variations (desc, str); + g_free (str); + } + pango_font_description_set_size (desc, self->size); + + return desc; +} + +static PangoFontDescription * +pango_hb_font_describe_absolute (PangoFont *font) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + PangoFontDescription *desc; + + desc = pango_hb_font_describe (font); + pango_font_description_set_absolute_size (desc, self->size * self->dpi / 72.); + + return desc; +} + +static PangoCoverage * +pango_hb_font_get_coverage (PangoFont *font, + PangoLanguage *language G_GNUC_UNUSED) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + + return pango_coverage_new_for_hb_face (pango_hb_face_get_hb_face (self->face)); +} + +static void +pango_hb_font_get_glyph_extents (PangoFont *font, + PangoGlyph glyph, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + hb_font_t *hb_font = pango_font_get_hb_font (font); + hb_glyph_extents_t extents; + hb_direction_t direction; + hb_font_extents_t font_extents; + + direction = PANGO_GRAVITY_IS_VERTICAL (self->gravity) + ? HB_DIRECTION_TTB + : HB_DIRECTION_LTR; + + hb_font_get_extents_for_direction (hb_font, direction, &font_extents); + + if (glyph == PANGO_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 & PANGO_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) + { + PangoRectangle r; + PangoMatrix m = PANGO_MATRIX_INIT; + + r.x = extents.x_bearing; + r.y = - extents.y_bearing; + r.width = extents.width; + r.height = - extents.height; + + if (self->face->matrix) + { + m.xx = self->face->matrix->xx; + m.yx = - self->face->matrix->yx; + m.xy = - self->face->matrix->xy; + m.yy = self->face->matrix->yy; + } + + pango_matrix_transform_rectangle (&m, &r); + + switch (self->gravity) + { + case PANGO_GRAVITY_AUTO: + case PANGO_GRAVITY_SOUTH: + ink_rect->x = r.x; + ink_rect->y = r.y; + ink_rect->width = r.width; + ink_rect->height = r.height; + break; + case PANGO_GRAVITY_NORTH: + ink_rect->x = - r.x; + ink_rect->y = - r.y; + ink_rect->width = - r.width; + ink_rect->height = - r.height; + break; + case PANGO_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 PANGO_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 (PANGO_GRAVITY_IS_IMPROPER (self->gravity)) + { + PangoMatrix matrix = (PangoMatrix) PANGO_MATRIX_INIT; + pango_matrix_scale (&matrix, -1, -1); + pango_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 (self->gravity) + { + case PANGO_GRAVITY_AUTO: + case PANGO_GRAVITY_SOUTH: + logical_rect->y = - extents.ascender; + logical_rect->width = h_advance; + break; + case PANGO_GRAVITY_NORTH: + logical_rect->y = extents.descender; + logical_rect->width = h_advance; + break; + case PANGO_GRAVITY_EAST: + logical_rect->y = - logical_rect->height / 2; + logical_rect->width = logical_rect->height; + break; + case PANGO_GRAVITY_WEST: + logical_rect->y = - logical_rect->height / 2; + logical_rect->width = - logical_rect->height; + break; + default: + g_assert_not_reached (); + } + + if (PANGO_GRAVITY_IS_IMPROPER (self->gravity)) + { + logical_rect->height = - logical_rect->height; + logical_rect->y = - logical_rect->y; + } + } +} + +static PangoFontMetrics * +pango_hb_font_get_metrics (PangoFont *font, + PangoLanguage *language) +{ + hb_font_t *hb_font = pango_font_get_hb_font (font); + PangoFontMetrics *metrics; + hb_font_extents_t extents; + hb_position_t position; + + metrics = pango_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 = PANGO_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 = - PANGO_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 = PANGO_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; + + metrics->approximate_char_width = get_average_char_width (font, pango_language_get_sample_string (language)); + get_max_char_size (font, "0123456789", &metrics->approximate_digit_width, NULL); + + return metrics; +} + +static hb_font_t * +pango_hb_font_create_hb_font (PangoFont *font) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + hb_face_t *hb_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_face = pango_hb_face_get_hb_face (self->face); + hb_font = hb_font_create (hb_face); + + size = self->size * self->dpi / 72.f; + x_scale = self->face->x_scale; + y_scale = self->face->y_scale; + + if (PANGO_GRAVITY_IS_IMPROPER (self->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, self->size / PANGO_SCALE); + +#if HB_VERSION_ATLEAST (3, 3, 0) + hb_font_set_synthetic_slant (hb_font, pango_matrix_get_slant_ratio (&self->face->matrix)); +#endif + + if (self->face->instance_id >= 0) + hb_font_set_var_named_instance (hb_font, self->face->instance_id); + + if (self->n_variations > 0 || self->face->n_variations > 0) + { + n_axes = hb_ot_var_get_axis_count (self->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 (hb_face, 0, &n_axes, axes); + + if (self->face->instance_id >= 0) + hb_ot_var_named_instance_get_design_coords (self->face->face, self->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 (self->face->variations, self->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 gboolean +pango_hb_font_has_char (PangoFont *font, + gunichar wc) +{ + hb_font_t *hb_font = pango_font_get_hb_font (font); + hb_codepoint_t glyph; + + return hb_font_get_nominal_glyph (hb_font, wc, &glyph); +} + +static PangoFontFace * +pango_hb_font_get_face (PangoFont *font) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + + return PANGO_FONT_FACE (self->face); +} + +static void +pango_hb_font_get_matrix (PangoFont *font, + PangoMatrix *matrix) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + + if (self->face->matrix) + { + *matrix = *self->face->matrix; + pango_matrix_scale (matrix, self->face->x_scale, self->face->y_scale); + } + else + *matrix = (PangoMatrix) PANGO_MATRIX_INIT; +} + +static void +pango_hb_font_get_features (PangoFont *font, + hb_feature_t *features, + guint len, + guint *num_features) +{ + PangoHbFont *self = PANGO_HB_FONT (font); + + *num_features = MIN (len, self->n_features); + memcpy (features, self->features, sizeof (hb_feature_t) * *num_features); +} + +static void +pango_hb_font_class_init (PangoHbFontClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + PangoFontClass *font_class = PANGO_FONT_CLASS (class); + PangoFontClassPrivate *pclass; + + object_class->finalize = pango_hb_font_finalize; + + font_class->describe = pango_hb_font_describe; + font_class->describe_absolute = pango_hb_font_describe_absolute; + font_class->get_coverage = pango_hb_font_get_coverage; + font_class->get_glyph_extents = pango_hb_font_get_glyph_extents; + font_class->get_metrics = pango_hb_font_get_metrics; + font_class->get_font_map = pango_hb_font_get_font_map; + font_class->create_hb_font = pango_hb_font_create_hb_font; + font_class->get_features = pango_hb_font_get_features; + + pclass = g_type_class_get_private ((GTypeClass *) class, PANGO_TYPE_FONT); + + pclass->has_char = pango_hb_font_has_char; + pclass->get_face = pango_hb_font_get_face; + pclass->get_matrix = pango_hb_font_get_matrix; +} + +/* }}} */ + /* {{{ Public API */ + +/** + * pango_hb_font_new: + * @face: the `PangoHbFace` to use + * @size: the desired size in points, scaled by `PANGO_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 + * @matrix: (nullable): transformation matrix to use when rendering + * + * Creates a new `PangoHbFont`. + * + * Returns: a newly created `PangoHbFont` + * + * Since: 1.52 + */ +PangoHbFont * +pango_hb_font_new (PangoHbFace *face, + int size, + hb_feature_t *features, + unsigned int n_features, + hb_variation_t *variations, + unsigned int n_variations, + PangoGravity gravity, + float dpi, + const PangoMatrix *matrix) +{ + PangoHbFont *self; + + self = g_object_new (PANGO_TYPE_HB_FONT, NULL); + + self->face = g_object_ref (face); + + self->size = size; + self->dpi = dpi; + 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; + if (gravity != PANGO_GRAVITY_AUTO) + self->gravity = gravity; + if (matrix) + self->matrix = *matrix; + + return self; +} + +/** + * pango_hb_font_new_for_description: + * @face: the `PangoHbFace` to use + * @description: a `PangoFontDescription` + * @dpi: the dpi used when rendering + * @matrix: (nullable): transformation matrix to use when rendering + * + * Creates a new `PangoHbFont` with size, features, variations and + * gravity taken from a font description. + * + * Returns: a newly created `PangoHbFont` + * + * Since: 1.52 + */ +PangoHbFont * +pango_hb_font_new_for_description (PangoHbFace *face, + const PangoFontDescription *description, + float dpi, + const PangoMatrix *matrix) +{ + int size; + hb_feature_t features[10]; + unsigned int n_features; + hb_variation_t *variations; + unsigned int n_variations; + unsigned int length; + PangoGravity gravity; + const char *str; + + if (pango_font_description_get_size_is_absolute (description)) + size = pango_font_description_get_size (description) * 72. / dpi; + else + size = pango_font_description_get_size (description); + + font_description_get_features (description, features, G_N_ELEMENTS (features), &n_features); + + if ((pango_font_description_get_set_fields (description) & PANGO_FONT_MASK_VARIATIONS) != 0) + { + str = pango_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 ((pango_font_description_get_set_fields (description) & PANGO_FONT_MASK_GRAVITY) != 0 && + pango_font_description_get_gravity (description) != PANGO_GRAVITY_SOUTH) + gravity = pango_font_description_get_gravity (description); + else + gravity = PANGO_GRAVITY_AUTO; + + return pango_hb_font_new (face, size, features, n_features, variations, n_variations, gravity, dpi, matrix); +} + +/* }}} */ + +/* vim:set foldmethod=marker expandtab: */ diff --git a/pango/pango-hbfont.h b/pango/pango-hbfont.h new file mode 100644 index 00000000..1c747d97 --- /dev/null +++ b/pango/pango-hbfont.h @@ -0,0 +1,50 @@ +/* Pango + * + * 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. + */ + +#pragma once + +#include <pango/pango-types.h> +#include <pango/pango-hbface.h> +#include <hb.h> + +G_BEGIN_DECLS + +#define PANGO_TYPE_HB_FONT (pango_hb_font_get_type ()) + +PANGO_AVAILABLE_IN_1_52 +G_DECLARE_FINAL_TYPE (PangoHbFont, pango_hb_font, PANGO, HB_FONT, PangoFont) + +PANGO_AVAILABLE_IN_1_52 +PangoHbFont * pango_hb_font_new (PangoHbFace *face, + int size, + hb_feature_t *features, + unsigned int n_features, + hb_variation_t *variations, + unsigned int n_variations, + PangoGravity gravity, + float dpi, + const PangoMatrix *matrix); + +PANGO_AVAILABLE_IN_1_52 +PangoHbFont * pango_hb_font_new_for_description (PangoHbFace *face, + const PangoFontDescription *description, + float dpi, + const PangoMatrix *matrix); +G_END_DECLS diff --git a/pango/pango.h b/pango/pango.h index 87036972..a1af0802 100644 --- a/pango/pango.h +++ b/pango/pango.h @@ -41,6 +41,7 @@ #include <pango/pango-glyph-item.h> #include <pango/pango-gravity.h> #include <pango/pango-hbface.h> +#include <pango/pango-hbfont.h> #include <pango/pango-item.h> #include <pango/pango-language.h> #include <pango/pango-layout.h> |