summaryrefslogtreecommitdiff
path: root/pango2/pango-userfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango2/pango-userfont.c')
-rw-r--r--pango2/pango-userfont.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/pango2/pango-userfont.c b/pango2/pango-userfont.c
new file mode 100644
index 00000000..6bd4b20c
--- /dev/null
+++ b/pango2/pango-userfont.c
@@ -0,0 +1,442 @@
+/* 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-userfont-private.h"
+
+#include "pango-font-private.h"
+#include "pango-font-metrics-private.h"
+#include "pango-userface-private.h"
+#include "pango-hbfamily-private.h"
+#include "pango-impl-utils.h"
+#include "pango-language-set-private.h"
+
+#include <hb-ot.h>
+
+/**
+ * Pango2UserFont:
+ *
+ * `Pango2UserFont` is a `Pango2Font` implementation that uses callbacks.
+ */
+
+/* {{{ Pango2Font implementation */
+
+struct _Pango2UserFontClass
+{
+ Pango2FontClass parent_class;
+};
+
+G_DEFINE_FINAL_TYPE (Pango2UserFont, pango2_user_font, PANGO2_TYPE_FONT)
+
+static void
+pango2_user_font_init (Pango2UserFont *self G_GNUC_UNUSED)
+{
+}
+
+static void
+pango2_user_font_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (pango2_user_font_parent_class)->finalize (object);
+}
+
+static Pango2FontDescription *
+pango2_user_font_describe (Pango2Font *font)
+{
+ Pango2FontDescription *desc;
+
+ desc = pango2_font_face_describe (PANGO2_FONT_FACE (font->face));
+ pango2_font_description_set_gravity (desc, font->gravity);
+ pango2_font_description_set_size (desc, font->size);
+
+ return desc;
+}
+
+static void
+pango2_user_font_get_glyph_extents (Pango2Font *font,
+ Pango2Glyph glyph,
+ Pango2Rectangle *ink_rect,
+ Pango2Rectangle *logical_rect)
+{
+ 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 || (glyph & PANGO2_GLYPH_UNKNOWN_FLAG))
+ {
+ 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;
+ }
+
+ hb_font_get_glyph_extents (hb_font, glyph, &extents);
+
+ if (ink_rect)
+ {
+ Pango2Rectangle r;
+
+ r.x = extents.x_bearing;
+ r.y = - extents.y_bearing;
+ r.width = extents.width;
+ r.height = - extents.height;
+
+ 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_user_font_get_metrics (Pango2Font *font,
+ Pango2Language *language)
+{
+ hb_font_t *hb_font = pango2_font_get_hb_font (font);
+ Pango2FontMetrics *metrics;
+ hb_font_extents_t extents;
+
+ 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;
+
+ metrics->underline_thickness = PANGO2_SCALE;
+ metrics->underline_position = - PANGO2_SCALE;
+ metrics->strikethrough_thickness = PANGO2_SCALE;
+ metrics->strikethrough_position = metrics->ascent / 2;
+ metrics->approximate_char_width = 0; /* FIXME */
+ metrics->approximate_digit_width = 0;
+
+ return metrics;
+}
+
+static hb_bool_t
+nominal_glyph_func (hb_font_t *hb_font,
+ void *font_data,
+ hb_codepoint_t unicode,
+ hb_codepoint_t *glyph,
+ void *user_data)
+{
+ Pango2Font *font = font_data;
+ Pango2UserFace *face = PANGO2_USER_FACE (font->face);
+
+ return face->glyph_func (face, unicode, glyph, face->user_data);
+}
+
+static hb_position_t
+glyph_h_advance_func (hb_font_t *hb_font,
+ void *font_data,
+ hb_codepoint_t glyph,
+ void *user_data)
+{
+ Pango2Font *font = font_data;
+ Pango2UserFace *face = PANGO2_USER_FACE (font->face);
+ int size = font->size * font->dpi / 72.;
+ hb_position_t h_advance, v_advance;
+ hb_glyph_extents_t glyph_extents;
+ gboolean is_color;
+
+ face->glyph_info_func (face, size, glyph,
+ &glyph_extents,
+ &h_advance, &v_advance,
+ &is_color,
+ face->user_data);
+
+ return h_advance;
+}
+
+static hb_bool_t
+glyph_extents_func (hb_font_t *hb_font,
+ void *font_data,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ void *user_data)
+{
+ Pango2Font *font = font_data;
+ Pango2UserFace *face = PANGO2_USER_FACE (font->face);
+ int size = font->size * font->dpi / 72.;
+ hb_position_t h_advance, v_advance;
+ gboolean is_color;
+
+ face->glyph_info_func (face, size, glyph,
+ extents,
+ &h_advance, &v_advance,
+ &is_color,
+ face->user_data);
+
+ if (PANGO2_GRAVITY_IS_IMPROPER (font->gravity))
+ {
+ extents->x_bearing = - extents->x_bearing;
+ extents->y_bearing = - extents->y_bearing;
+ extents->width = - extents->width;
+ extents->height = - extents->height;
+ }
+
+ return TRUE;
+}
+
+static hb_bool_t
+font_extents_func (hb_font_t *hb_font,
+ void *font_data,
+ hb_font_extents_t *extents,
+ void *user_data)
+{
+ Pango2Font *font = font_data;
+ Pango2UserFace *face = PANGO2_USER_FACE (font->face);
+ int size = font->size * font->dpi / 72.;
+ gboolean ret;
+
+ ret = face->font_info_func (face, size, extents, face->user_data);
+
+ if (PANGO2_GRAVITY_IS_IMPROPER (font->gravity))
+ {
+ extents->ascender = - extents->ascender;
+ extents->descender = - extents->descender;
+ }
+
+ return ret;
+}
+
+static hb_font_t *
+pango2_user_font_create_hb_font (Pango2Font *font)
+{
+ double x_scale, y_scale;
+ int size;
+ hb_blob_t *blob;
+ hb_face_t *hb_face;
+ hb_font_t *hb_font;
+ hb_font_funcs_t *funcs;
+
+ blob = hb_blob_create ("", 0, HB_MEMORY_MODE_READONLY, NULL, NULL);
+ hb_face = hb_face_create (blob, 0);
+ hb_font = hb_font_create (hb_face);
+
+ funcs = hb_font_funcs_create ();
+
+ hb_font_funcs_set_nominal_glyph_func (funcs, nominal_glyph_func, NULL, NULL);
+ hb_font_funcs_set_glyph_h_advance_func (funcs, glyph_h_advance_func, NULL, NULL);
+ hb_font_funcs_set_glyph_extents_func (funcs, glyph_extents_func, NULL, NULL);
+ hb_font_funcs_set_font_h_extents_func (funcs, font_extents_func, NULL, NULL);
+ hb_font_funcs_set_font_v_extents_func (funcs, font_extents_func, NULL, NULL);
+
+ hb_font_set_funcs (hb_font, funcs, font, NULL);
+
+ hb_font_funcs_destroy (funcs);
+ hb_face_destroy (hb_face);
+ hb_blob_destroy (blob);
+
+ size = font->size * font->dpi / 72.f;
+ x_scale = y_scale = 1;
+
+ 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);
+
+ return hb_font;
+}
+
+static void
+pango2_user_font_class_init (Pango2UserFontClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ Pango2FontClass *font_class = PANGO2_FONT_CLASS (class);
+
+ object_class->finalize = pango2_user_font_finalize;
+
+ font_class->describe = pango2_user_font_describe;
+ font_class->get_glyph_extents = pango2_user_font_get_glyph_extents;
+ font_class->get_metrics = pango2_user_font_get_metrics;
+ font_class->create_hb_font = pango2_user_font_create_hb_font;
+}
+
+/* }}} */
+ /* {{{ Public API */
+
+/**
+ * pango2_user_font_new:
+ * @face: the `Pango2UserFace` to use
+ * @size: the desired size in points, scaled by `PANGO2_SCALE`
+ * @gravity: the gravity to use when rendering
+ * @dpi: the dpi used when rendering
+ * @ctm: (nullable): transformation matrix to use when rendering
+ *
+ * Creates a new `Pango2UserFont`.
+ *
+ * Returns: a newly created `Pango2UserFont`
+ */
+Pango2UserFont *
+pango2_user_font_new (Pango2UserFace *face,
+ int size,
+ Pango2Gravity gravity,
+ float dpi,
+ const Pango2Matrix *ctm)
+{
+ Pango2UserFont *self;
+ Pango2Font *font;
+
+ self = g_object_new (PANGO2_TYPE_USER_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);
+
+ return self;
+}
+
+/**
+ * pango2_user_font_new_for_description:
+ * @face: the `Pango2UserFace` to use
+ * @description: a `Pango2FontDescription`
+ * @dpi: the dpi used when rendering
+ * @ctm: (nullable): transformation matrix to use when rendering
+ *
+ * Creates a new `Pango2UserFont` with size and gravity taken
+ * from a font description.
+ *
+ * Returns: a newly created `Pango2HbFont`
+ */
+
+Pango2UserFont *
+pango2_user_font_new_for_description (Pango2UserFace *face,
+ const Pango2FontDescription *description,
+ float dpi,
+ const Pango2Matrix *ctm)
+{
+ int size;
+ Pango2Gravity gravity;
+
+ 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);
+
+ 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_user_font_new (face, size, gravity, dpi, ctm);
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */