[pango/pango2: 19/112] Introduce user fonts
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/pango2: 19/112] Introduce user fonts
- Date: Sun, 12 Jun 2022 23:20:23 +0000 (UTC)
commit 5e26bf1c5a0e5ededab107ba8b79f65783f64887
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Jan 27 00:46:15 2022 -0500
Introduce user fonts
Add a way to create callback-based faces
and fonts. The cairo implementation of this
uses cairos user fonts.
New APIs: PangoUserFace and PangoUserFont
meson.build | 4 +
pango/meson.build | 4 +
pango/pango-generic-family-private.h | 3 +-
pango/pango-generic-family.c | 4 +-
pango/pango-hbface-private.h | 24 +-
pango/pango-hbface.c | 3 +-
pango/pango-hbface.h | 2 +
pango/pango-hbfamily-private.h | 6 +-
pango/pango-hbfamily.c | 100 ++++---
pango/pango-hbfont-private.h | 23 +-
pango/pango-hbfont.c | 22 +-
pango/pango-hbfont.h | 1 +
pango/pango-hbfontmap.c | 111 +++++---
pango/pango-hbfontmap.h | 4 +-
pango/pango-userface-private.h | 54 ++++
pango/pango-userface.c | 491 +++++++++++++++++++++++++++++++++++
pango/pango-userface.h | 81 ++++++
pango/pango-userfont-private.h | 39 +++
pango/pango-userfont.c | 482 ++++++++++++++++++++++++++++++++++
pango/pango-userfont.h | 46 ++++
pango/pango.h | 2 +
pango/pangocairo-font.c | 115 ++++++--
pango/pangofc-hbfontmap.c | 2 +-
pango/shape.c | 85 +++---
tests/testhbfont.c | 26 +-
25 files changed, 1581 insertions(+), 153 deletions(-)
---
diff --git a/meson.build b/meson.build
index 07cdfd610..026617176 100644
--- a/meson.build
+++ b/meson.build
@@ -400,6 +400,10 @@ if not cairo_dep.found()
cairo_found_type = cairo_dep.type_name()
endif
+if cc.has_function('cairo_user_font_face_set_render_color_glyph_func', args: '-lcairo', dependencies:
cairo_dep)
+ pango_conf.set('HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC', 1)
+endif
+
pango_font_backends = []
pango_cairo_backends = []
diff --git a/pango/meson.build b/pango/meson.build
index 50a4bd5a9..fefc32412 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -46,6 +46,8 @@ pango_sources = [
'pango-generic-family.c',
'pango-hbfont.c',
'pango-hbfontmap.c',
+ 'pango-userface.c',
+ 'pango-userfont.c',
]
pango_headers = [
@@ -86,6 +88,8 @@ pango_headers = [
'pango-hbface.h',
'pango-hbfont.h',
'pango-hbfontmap.h',
+ 'pango-userface.h',
+ 'pango-userfont.h',
]
pango_gir_includes = [
diff --git a/pango/pango-generic-family-private.h b/pango/pango-generic-family-private.h
index efc2bfb23..5584e935c 100644
--- a/pango/pango-generic-family-private.h
+++ b/pango/pango-generic-family-private.h
@@ -20,7 +20,6 @@
#pragma once
#include "pango-font.h"
-#include "pango-hbface.h"
#include "pango-generic-family.h"
#include "pango-hbfamily-private.h"
@@ -38,7 +37,7 @@ struct _PangoGenericFamily
void pango_generic_family_set_font_map (PangoGenericFamily *self,
PangoFontMap *map);
-PangoHbFace * pango_generic_family_find_face (PangoGenericFamily *self,
+PangoFontFace * pango_generic_family_find_face (PangoGenericFamily *self,
PangoFontDescription *description,
PangoLanguage *language,
gunichar wc);
diff --git a/pango/pango-generic-family.c b/pango/pango-generic-family.c
index d8c3dc7f4..586310def 100644
--- a/pango/pango-generic-family.c
+++ b/pango/pango-generic-family.c
@@ -199,13 +199,13 @@ pango_generic_family_set_font_map (PangoGenericFamily *self,
*
* Returns: (transfer none) (nullable): the face
*/
-PangoHbFace *
+PangoFontFace *
pango_generic_family_find_face (PangoGenericFamily *self,
PangoFontDescription *description,
PangoLanguage *language,
gunichar wc)
{
- PangoHbFace *face = NULL;
+ PangoFontFace *face = NULL;
for (int i = 0; i < self->families->len; i++)
{
diff --git a/pango/pango-hbface-private.h b/pango/pango-hbface-private.h
index 7eb8bc1a3..71a95ac49 100644
--- a/pango/pango-hbface-private.h
+++ b/pango/pango-hbface-private.h
@@ -26,21 +26,35 @@
#include <hb.h>
+typedef struct _CommonFace CommonFace;
+struct _CommonFace {
+ PangoFontFace parent_instance;
+
+ PangoFontDescription *description;
+ char *name;
+ PangoFontFamily *family;
+ char *psname;
+ char *faceid;
+};
+
struct _PangoHbFace
{
PangoFontFace parent_instance;
+ PangoFontDescription *description;
+ char *name;
+ PangoFontFamily *family;
+ char *psname;
+ char *faceid;
+
+ /* up to here shared with PangoUserFace */
+
unsigned int index;
int instance_id;
char *file;
hb_face_t *face;
- char *psname;
- char *faceid;
hb_variation_t *variations;
unsigned int n_variations;
- char *name;
- PangoFontFamily *family;
- PangoFontDescription *description;
PangoMatrix *matrix;
double x_scale, y_scale;
PangoLanguageSet *languages;
diff --git a/pango/pango-hbface.c b/pango/pango-hbface.c
index 78cba56da..727f9571f 100644
--- a/pango/pango-hbface.c
+++ b/pango/pango-hbface.c
@@ -286,7 +286,8 @@ pango_hb_face_finalize (GObject *object)
{
PangoHbFace *self = PANGO_HB_FACE (object);
- hb_face_destroy (self->face);
+ if (self->face)
+ hb_face_destroy (self->face);
pango_font_description_free (self->description);
g_free (self->name);
g_free (self->file);
diff --git a/pango/pango-hbface.h b/pango/pango-hbface.h
index d2dd9973b..7e485fa4d 100644
--- a/pango/pango-hbface.h
+++ b/pango/pango-hbface.h
@@ -26,6 +26,8 @@
G_BEGIN_DECLS
+typedef struct _PangoHbFont PangoHbFont;
+
#define PANGO_TYPE_HB_FACE (pango_hb_face_get_type ())
PANGO_AVAILABLE_IN_ALL
diff --git a/pango/pango-hbfamily-private.h b/pango/pango-hbfamily-private.h
index 30c2f2e86..bf5545b82 100644
--- a/pango/pango-hbfamily-private.h
+++ b/pango/pango-hbfamily-private.h
@@ -42,12 +42,12 @@ void pango_hb_family_set_font_map (PangoHbFamily
PangoFontMap *map);
void pango_hb_family_add_face (PangoHbFamily *self,
- PangoHbFace *face);
+ PangoFontFace *face);
void pango_hb_family_remove_face (PangoHbFamily *self,
- PangoHbFace *face);
+ PangoFontFace *face);
-PangoHbFace * pango_hb_family_find_face (PangoHbFamily *self,
+PangoFontFace * pango_hb_family_find_face (PangoHbFamily *self,
PangoFontDescription *description,
PangoLanguage *language,
gunichar wc);
diff --git a/pango/pango-hbfamily.c b/pango/pango-hbfamily.c
index a0f745130..05edcefbf 100644
--- a/pango/pango-hbfamily.c
+++ b/pango/pango-hbfamily.c
@@ -27,6 +27,7 @@
#include "pango-hbface-private.h"
#include "pango-font-face-private.h"
#include "pango-font-description-private.h"
+#include "pango-userface-private.h"
/* {{{ GListModel implementation */
@@ -46,7 +47,7 @@ pango_hb_family_get_n_items (GListModel *list)
static gpointer
pango_hb_family_get_item (GListModel *list,
- guint position)
+ guint position)
{
PangoHbFamily *self = PANGO_HB_FAMILY (list);
@@ -67,28 +68,59 @@ pango_hb_family_list_model_init (GListModelInterface *iface)
/* }}} */
/* {{{ Utilities */
+static void
+face_set_family (PangoFontFace *face,
+ PangoFontFamily *family)
+{
+ if (PANGO_IS_HB_FACE (face))
+ pango_hb_face_set_family (PANGO_HB_FACE (face), family);
+ else
+ pango_user_face_set_family (PANGO_USER_FACE (face), family);
+}
+
+static const char *
+face_get_faceid (PangoFontFace *face)
+{
+ if (PANGO_IS_HB_FACE (face))
+ return pango_hb_face_get_faceid (PANGO_HB_FACE (face));
+ else
+ return pango_user_face_get_faceid (PANGO_USER_FACE (face));
+}
+
+static gboolean
+face_has_char (PangoFontFace *face,
+ gunichar wc)
+{
+ if (PANGO_IS_HB_FACE (face))
+ return pango_hb_face_has_char (PANGO_HB_FACE (face), wc);
+ else
+ return pango_user_face_has_char (PANGO_USER_FACE (face), wc);
+}
+
static int
-sort_face_func (PangoHbFace *face1,
- PangoHbFace *face2)
+sort_face_func (PangoFontFace *face1,
+ PangoFontFace *face2)
{
+ CommonFace *cf1 = (CommonFace *)face1;
+ CommonFace *cf2 = (CommonFace *)face2;
int a, b;
- a = pango_font_description_get_style (face1->description);
- b = pango_font_description_get_style (face2->description);
+ a = pango_font_description_get_style (cf1->description);
+ b = pango_font_description_get_style (cf2->description);
if (a != b)
return a - b;
- a = pango_font_description_get_weight (face1->description);
- b = pango_font_description_get_weight (face2->description);
+ a = pango_font_description_get_weight (cf1->description);
+ b = pango_font_description_get_weight (cf2->description);
if (a != b)
return a - b;
- a = pango_font_description_get_stretch (face1->description);
- b = pango_font_description_get_stretch (face2->description);
+ a = pango_font_description_get_stretch (cf1->description);
+ b = pango_font_description_get_stretch (cf2->description);
if (a != b)
return a - b;
- return strcmp (face1->name, face2->name);
+ return strcmp (cf1->name, cf2->name);
}
/* return 2 if face is a named instance,
@@ -96,11 +128,11 @@ sort_face_func (PangoHbFace *face1,
* 0 otherwise
*/
static int
-hb_face_get_variableness (PangoHbFace *face)
+face_get_variableness (PangoFontFace *face)
{
if (pango_font_face_is_variable (PANGO_FONT_FACE (face)))
{
- if (face->instance_id != -1)
+ if (PANGO_HB_FACE (face)->instance_id != -1)
return 2;
else
return 1;
@@ -236,7 +268,7 @@ pango_hb_family_new (const char *name)
* @map: (nullable): a `PangoFontMap`
*
* Sets the map of @self.
- */
+ G*/
void
pango_hb_family_set_font_map (PangoHbFamily *self,
PangoFontMap *map)
@@ -264,21 +296,24 @@ pango_hb_family_set_font_map (PangoHbFamily *self,
*/
void
pango_hb_family_add_face (PangoHbFamily *self,
- PangoHbFace *face)
+ PangoFontFace *face)
{
int position;
+ g_return_if_fail (PANGO_IS_HB_FACE (face) || PANGO_IS_USER_FACE (face));
+
position = 0;
while (position < self->faces->len)
{
- PangoHbFace *f = g_ptr_array_index (self->faces, position);
+ PangoFontFace *f = g_ptr_array_index (self->faces, position);
if (sort_face_func (face, f) < 0)
break;
position++;
}
g_ptr_array_insert (self->faces, position, face);
- pango_hb_face_set_family (face, PANGO_FONT_FAMILY (self));
+
+ face_set_family (face, PANGO_FONT_FAMILY (self));
g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
}
@@ -286,20 +321,23 @@ pango_hb_family_add_face (PangoHbFamily *self,
/*< private >
* pango_hb_family_remove_face:
* @self: a `PangoHbFamily`
- * @face: a `PangoHbFace`
+ * @face: a `PangoFontFace`
*
* Remove a `PangoFontFace` from a `PangoHbFamily`.
*/
void
pango_hb_family_remove_face (PangoHbFamily *self,
- PangoHbFace *face)
+ PangoFontFace *face)
{
unsigned int position;
+ g_return_if_fail (PANGO_IS_HB_FACE (face) || PANGO_IS_USER_FACE (face));
+
if (!g_ptr_array_find (self->faces, face, &position))
return;
- pango_hb_face_set_family (face, NULL);
+ face_set_family (face, NULL);
+
g_ptr_array_remove_index (self->faces, position);
g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
@@ -321,47 +359,49 @@ pango_hb_family_remove_face (PangoHbFamily *self,
*
* Returns: (transfer none) (nullable): the face
*/
-PangoHbFace *
+PangoFontFace *
pango_hb_family_find_face (PangoHbFamily *family,
PangoFontDescription *description,
PangoLanguage *language,
gunichar wc)
{
- PangoHbFace *face = NULL;
+ PangoFontFace *face = NULL;
int best_distance = G_MAXINT;
int best_variableness = 0;
/* First look for an exact match if the description has a faceid */
if (pango_font_description_get_set_fields (description) & PANGO_FONT_MASK_FACEID)
{
+ const char *faceid = pango_font_description_get_faceid (description);
+
for (int i = 0; i < family->faces->len; i++)
{
- PangoHbFace *face2 = g_ptr_array_index (family->faces, i);
+ PangoFontFace *face2 = g_ptr_array_index (family->faces, i);
+ const char *faceid2 = face_get_faceid (face2);
- if (g_strcmp0 (pango_font_description_get_faceid (description),
- pango_hb_face_get_faceid (face2)) == 0)
+ if (g_strcmp0 (faceid, faceid2) == 0)
return face2;
}
}
for (int i = 0; i < family->faces->len; i++)
{
- PangoHbFace *face2 = g_ptr_array_index (family->faces, i);
+ PangoFontFace *face2 = g_ptr_array_index (family->faces, i);
int distance;
int variableness;
- if (language && !pango_font_face_supports_language (PANGO_FONT_FACE (face2), language))
+ if (language && !pango_font_face_supports_language (face2, language))
continue;
- if (wc && !pango_hb_face_has_char (face2, wc))
+ if (wc && !face_has_char (face2, wc))
continue;
- if (!pango_font_description_is_similar (description, face2->description))
+ if (!pango_font_description_is_similar (description, ((CommonFace *)face2)->description))
continue;
- distance = pango_font_description_compute_distance (description, face2->description);
+ distance = pango_font_description_compute_distance (description, ((CommonFace *)face2)->description);
- variableness = hb_face_get_variableness (face2);
+ variableness = face_get_variableness (PANGO_FONT_FACE (face2));
if (distance < best_distance ||
(distance == best_distance && variableness > best_variableness))
{
diff --git a/pango/pango-hbfont-private.h b/pango/pango-hbfont-private.h
index eb945dc2b..dbaa55cb7 100644
--- a/pango/pango-hbfont-private.h
+++ b/pango/pango-hbfont-private.h
@@ -40,19 +40,36 @@ struct _HexBoxInfo
double box_height;
};
+typedef struct _CommonFont CommonFont;
+struct _CommonFont
+{
+ PangoFont parent_instance;
+
+ int size;
+ float dpi;
+ PangoGravity gravity;
+ PangoMatrix matrix;
+};
+
struct _PangoHbFont
{
PangoFont parent_instance;
- PangoHbFace *face;
int size; /* point size, scaled by PANGO_SCALE */
float dpi;
+ PangoGravity gravity;
+ PangoMatrix matrix;
+
+ /* up to here shared with PangoUserFont */
+
+ PangoHbFace *face;
hb_feature_t *features;
unsigned int n_features;
hb_variation_t *variations;
unsigned int n_variations;
- PangoGravity gravity;
- PangoMatrix matrix;
HexBoxInfo *hex_box_info;
+ PangoLanguage *approximate_char_lang;
+ int approximate_char_width;
+ int approximate_digit_width;
};
diff --git a/pango/pango-hbfont.c b/pango/pango-hbfont.c
index 76423697e..1a957318d 100644
--- a/pango/pango-hbfont.c
+++ b/pango/pango-hbfont.c
@@ -48,7 +48,7 @@
* matrix.
*/
- /* {{{ Utilities */
+/* {{{ Utilities */
static int
get_average_char_width (PangoFont *font,
@@ -764,6 +764,7 @@ static PangoFontMetrics *
pango_hb_font_get_metrics (PangoFont *font,
PangoLanguage *language)
{
+ PangoHbFont *self = PANGO_HB_FONT (font);
hb_font_t *hb_font = pango_font_get_hb_font (font);
PangoFontMetrics *metrics;
hb_font_extents_t extents;
@@ -797,8 +798,17 @@ pango_hb_font_get_metrics (PangoFont *font,
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);
+ if (self->approximate_char_width == 0 || self->approximate_char_lang != language)
+ {
+ self->approximate_char_width = get_average_char_width (font, pango_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;
}
@@ -807,7 +817,6 @@ 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;
@@ -815,8 +824,7 @@ pango_hb_font_create_hb_font (PangoFont *font)
float *coords;
int size;
- hb_face = pango_hb_face_get_hb_face (self->face);
- hb_font = hb_font_create (hb_face);
+ hb_font = hb_font_create (pango_hb_face_get_hb_face (self->face));
size = self->size * self->dpi / 72.f;
x_scale = self->face->x_scale;
@@ -844,7 +852,7 @@ pango_hb_font_create_hb_font (PangoFont *font)
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);
+ hb_ot_var_get_axis_infos (self->face->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);
diff --git a/pango/pango-hbfont.h b/pango/pango-hbfont.h
index 8ff0a6500..017a77acc 100644
--- a/pango/pango-hbfont.h
+++ b/pango/pango-hbfont.h
@@ -47,4 +47,5 @@ PangoHbFont * pango_hb_font_new_for_description (PangoHbFace
const PangoFontDescription *description,
float dpi,
const PangoMatrix *matrix);
+
G_END_DECLS
diff --git a/pango/pango-hbfontmap.c b/pango/pango-hbfontmap.c
index b2c086105..d5b83c82b 100644
--- a/pango/pango-hbfontmap.c
+++ b/pango/pango-hbfontmap.c
@@ -29,6 +29,8 @@
#include "pango-hbface-private.h"
#include "pango-hbfont-private.h"
#include "pango-fontset-private.h"
+#include "pango-userface-private.h"
+#include "pango-userfont-private.h"
#include "pango-fontset.h"
#include "pango-trace-private.h"
#include "pango-context.h"
@@ -153,6 +155,18 @@ pango_fontset_cached_finalize (GObject *object)
G_OBJECT_CLASS (pango_fontset_cached_parent_class)->finalize (object);
}
+static PangoFont *
+font_new_for_description (PangoFontFace *face,
+ const PangoFontDescription *description,
+ float dpi,
+ const PangoMatrix *matrix)
+{
+ if (PANGO_IS_HB_FACE (face))
+ return PANGO_FONT (pango_hb_font_new_for_description (PANGO_HB_FACE (face), description, dpi, matrix));
+ else
+ return PANGO_FONT (pango_user_font_new_for_description (PANGO_USER_FACE (face), description, dpi,
matrix));
+}
+
static PangoFont *
pango_fontset_cached_get_font (PangoFontset *fontset,
guint wc)
@@ -169,7 +183,7 @@ pango_fontset_cached_get_font (PangoFontset *fontset,
{
gpointer item = g_ptr_array_index (self->items, i);
- if (PANGO_IS_HB_FONT (item))
+ if (PANGO_IS_FONT (item))
{
PangoFont *font = PANGO_FONT (item);
if (pango_font_has_char (font, wc))
@@ -181,7 +195,7 @@ pango_fontset_cached_get_font (PangoFontset *fontset,
else if (PANGO_IS_GENERIC_FAMILY (item))
{
PangoGenericFamily *family = PANGO_GENERIC_FAMILY (item);
- PangoHbFace *face;
+ PangoFontFace *face;
/* Here is where we implement delayed picking for generic families.
* If a face does not cover the character and its family is generic,
@@ -193,10 +207,10 @@ pango_fontset_cached_get_font (PangoFontset *fontset,
wc);
if (face)
{
- retval = PANGO_FONT (pango_hb_font_new_for_description (face,
- self->description,
- self->dpi,
- self->matrix));
+ retval = font_new_for_description (face,
+ self->description,
+ self->dpi,
+ self->matrix);
break;
}
}
@@ -215,12 +229,12 @@ pango_fontset_cached_get_first_font (PangoFontsetCached *self)
item = g_ptr_array_index (self->items, 0);
- if (PANGO_IS_HB_FONT (item))
+ if (PANGO_IS_FONT (item))
return g_object_ref (PANGO_FONT (item));
else if (PANGO_IS_GENERIC_FAMILY (item))
{
- PangoHbFace *face = pango_generic_family_find_face (PANGO_GENERIC_FAMILY (item), self->description,
self->language, 0);
- return PANGO_FONT (pango_hb_font_new_for_description (face, self->description, self->dpi,
self->matrix));
+ PangoFontFace *face = pango_generic_family_find_face (PANGO_GENERIC_FAMILY (item), self->description,
self->language, 0);
+ return font_new_for_description (face, self->description, self->dpi, self->matrix);
}
return NULL;
@@ -266,12 +280,14 @@ pango_fontset_cached_foreach (PangoFontset *fontset,
gpointer item = g_ptr_array_index (self->items, i);
PangoFont *font = NULL;
- if (PANGO_IS_HB_FONT (item))
- font = g_object_ref (PANGO_FONT (item));
+ if (PANGO_IS_FONT (item))
+ {
+ font = g_object_ref (PANGO_FONT (item));
+ }
else if (PANGO_IS_GENERIC_FAMILY (item))
{
- PangoHbFace *face = pango_generic_family_find_face (PANGO_GENERIC_FAMILY (item),
self->description, self->language, 0);
- font = PANGO_FONT (pango_hb_font_new_for_description (face, self->description, self->dpi,
self->matrix));
+ PangoFontFace *face = pango_generic_family_find_face (PANGO_GENERIC_FAMILY (item),
self->description, self->language, 0);
+ font = font_new_for_description (face, self->description, self->dpi, self->matrix);
}
if ((*func) (fontset, font, data))
@@ -316,13 +332,20 @@ pango_fontset_cached_new (const PangoFontDescription *description,
static void
pango_fontset_cached_add_face (PangoFontsetCached *self,
- PangoHbFace *face)
+ PangoFontFace *face)
{
- g_ptr_array_add (self->items,
- pango_hb_font_new_for_description (face,
- self->description,
- self->dpi,
- self->matrix));
+ if (PANGO_IS_HB_FACE (face))
+ g_ptr_array_add (self->items,
+ pango_hb_font_new_for_description (PANGO_HB_FACE (face),
+ self->description,
+ self->dpi,
+ self->matrix));
+ else
+ g_ptr_array_add (self->items,
+ pango_user_font_new_for_description (PANGO_USER_FACE (face),
+ self->description,
+ self->dpi,
+ self->matrix));
}
static void
@@ -447,18 +470,19 @@ add_style_variation (PangoHbFamily *family,
{
PangoMatrix italic_matrix = { 1, 0.2, 0, 1, 0, 0 };
PangoFontDescription *desc;
+ PangoHbFace *variation;
desc = pango_font_description_new ();
pango_font_description_set_family (desc, pango_font_family_get_name (PANGO_FONT_FAMILY (family)));
pango_font_description_set_style (desc, style);
pango_font_description_set_weight (desc, weight);
- pango_hb_family_add_face (family,
- pango_hb_face_new_synthetic (face,
- style == PANGO_STYLE_ITALIC ? &italic_matrix : NULL,
- weight == PANGO_WEIGHT_BOLD,
- NULL,
- desc));
+ variation = pango_hb_face_new_synthetic (face,
+ style == PANGO_STYLE_ITALIC ? &italic_matrix : NULL,
+ weight == PANGO_WEIGHT_BOLD,
+ NULL,
+ desc);
+ pango_hb_family_add_face (family, PANGO_FONT_FACE (variation));
pango_font_description_free (desc);
}
@@ -486,6 +510,9 @@ synthesize_bold_and_italic_faces (PangoHbFontMap *map)
PangoStyle style;
int dist;
+ if (!PANGO_IS_HB_FACE (face))
+ continue;
+
weight = pango_font_description_get_weight (face->description);
style = pango_font_description_get_style (face->description);
@@ -533,7 +560,7 @@ synthesize_bold_and_italic_faces (PangoHbFontMap *map)
}
}
- /* }}} */
+/* }}} */
/* {{{ PangoFontMap implementation */
G_DEFINE_TYPE_WITH_CODE (PangoHbFontMap, pango_hb_font_map, PANGO_TYPE_FONT_MAP,
@@ -614,7 +641,7 @@ pango_hb_font_map_load_fontset (PangoFontMap *map,
char **families;
PangoFontDescription *copy;
PangoFontFamily *family;
- PangoHbFace *face;
+ PangoFontFace *face;
gboolean has_generic = FALSE;
gint64 before G_GNUC_UNUSED;
@@ -788,7 +815,7 @@ pango_hb_font_map_repopulate (PangoHbFontMap *self,
for (int i = 0; i < self->added_faces->len; i++)
{
- PangoHbFace *face = PANGO_HB_FACE (g_ptr_array_index (self->added_faces, i));
+ PangoFontFace *face = g_ptr_array_index (self->added_faces, i);
pango_hb_font_map_add_face (self, face);
}
@@ -825,7 +852,7 @@ pango_hb_font_map_new (void)
/**
* pango_hb_font_map_add_face:
* @self: a `PangoHbFontMap`
- * @face: (transfer full): a `PangoHbFace`
+ * @face: (transfer full): a `PangoFontFace`
*
* Adds @face to the `PangoHbFontMap`.
*
@@ -834,20 +861,28 @@ pango_hb_font_map_new (void)
*/
void
pango_hb_font_map_add_face (PangoHbFontMap *self,
- PangoHbFace *face)
+ PangoFontFace *face)
{
PangoFontMap *map = PANGO_FONT_MAP (self);
const char *family_name;
PangoHbFamily *family;
+ const PangoFontDescription *description;
+
+ g_return_if_fail (PANGO_IS_HB_FACE (face) || PANGO_IS_USER_FACE (face));
- if (pango_font_description_get_set_fields (face->description) &
+ if (PANGO_IS_HB_FACE (face))
+ description = PANGO_HB_FACE (face)->description;
+ else
+ description = PANGO_USER_FACE (face)->description;
+
+ if (pango_font_description_get_set_fields (description) &
(PANGO_FONT_MASK_VARIANT | PANGO_FONT_MASK_GRAVITY))
- g_warning ("Font description for PangoHbFace includes things that it shouldn't");
+ g_warning ("Font description for PangoFontFace includes things that it shouldn't");
if (!self->in_populate)
g_ptr_array_add (self->added_faces, g_object_ref (face));
- family_name = pango_font_description_get_family (face->description);
+ family_name = pango_font_description_get_family (description);
family = PANGO_HB_FAMILY (pango_font_map_get_family (map, family_name));
if (!family)
{
@@ -865,7 +900,7 @@ pango_hb_font_map_add_face (PangoHbFontMap *self,
/**
* pango_hb_font_map_remove_face:
* @self: a `PangoHbFontMap`
- * @face: a `PangoHbFace` that belongs to @map
+ * @face: a `PangoFontFace` that belongs to @map
*
* Removes @face from the `PangoHbFontMap`.
*
@@ -873,15 +908,17 @@ pango_hb_font_map_add_face (PangoHbFontMap *self,
*/
void
pango_hb_font_map_remove_face (PangoHbFontMap *self,
- PangoHbFace *face)
+ PangoFontFace *face)
{
PangoHbFamily *family;
unsigned int position;
+ g_return_if_fail (PANGO_IS_HB_FACE (face) || PANGO_IS_USER_FACE (face));
+
if (!g_ptr_array_find (self->added_faces, face, &position))
return;
- family = PANGO_HB_FAMILY (pango_font_face_get_family (PANGO_FONT_FACE (face)));
+ family = PANGO_HB_FAMILY (pango_font_face_get_family (face));
pango_hb_family_remove_face (family, face);
@@ -912,7 +949,7 @@ pango_hb_font_map_add_file (PangoHbFontMap *self,
PangoHbFace *face;
face = pango_hb_face_new_from_file (file, 0, -1, NULL, NULL);
- pango_hb_font_map_add_face (self, face);
+ pango_hb_font_map_add_face (self, PANGO_FONT_FACE (face));
}
/**
diff --git a/pango/pango-hbfontmap.h b/pango/pango-hbfontmap.h
index b7a933d5f..ae456ad98 100644
--- a/pango/pango-hbfontmap.h
+++ b/pango/pango-hbfontmap.h
@@ -39,11 +39,11 @@ void pango_hb_font_map_add_file (PangoHbFontMap
PANGO_AVAILABLE_IN_ALL
void pango_hb_font_map_add_face (PangoHbFontMap *self,
- PangoHbFace *face);
+ PangoFontFace *face);
PANGO_AVAILABLE_IN_ALL
void pango_hb_font_map_remove_face (PangoHbFontMap *self,
- PangoHbFace *face);
+ PangoFontFace *face);
PANGO_AVAILABLE_IN_ALL
void pango_hb_font_map_add_family (PangoHbFontMap *self,
diff --git a/pango/pango-userface-private.h b/pango/pango-userface-private.h
new file mode 100644
index 000000000..087874956
--- /dev/null
+++ b/pango/pango-userface-private.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "pango-userface.h"
+#include "pango-fontmap.h"
+#include "pango-font-face-private.h"
+
+
+struct _PangoUserFace
+{
+ PangoFontFace parent_instance;
+
+ PangoFontDescription *description;
+ char *name;
+ PangoFontFamily *family;
+ char *psname;
+ char *faceid;
+
+ /* up to here shared with PangoHbFace */
+
+ PangoUserFaceGetFontInfoFunc font_info_func;
+ PangoUserFaceUnicodeToGlyphFunc glyph_func;
+ PangoUserFaceGetGlyphInfoFunc glyph_info_func;
+ PangoUserFaceTextToGlyphFunc shape_func;
+ PangoUserFaceRenderGlyphFunc render_func;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+void pango_user_face_set_family (PangoUserFace *self,
+ PangoFontFamily *family);
+
+gboolean pango_user_face_has_char (PangoUserFace *self,
+ gunichar wc);
+
+const char * pango_user_face_get_faceid (PangoUserFace *self);
diff --git a/pango/pango-userface.c b/pango/pango-userface.c
new file mode 100644
index 000000000..eec60a428
--- /dev/null
+++ b/pango/pango-userface.c
@@ -0,0 +1,491 @@
+/* Pango
+ *
+ * Copyright (C) 2022 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-font-face-private.h"
+#include "pango-userface-private.h"
+#include "pango-userfont-private.h"
+#include "pango-utils.h"
+#include "pango-impl-utils.h"
+
+#include <string.h>
+#include <hb-ot.h>
+
+/**
+ * PangoUserFace:
+ *
+ * `PangoUserFace` is a `PangoFontFace` implementation that uses callbacks.
+ *
+ * It allows to draw the glyphs in a font using custom code. This can
+ * be used to implement fonts in non-standard formats, but can also be
+ * used by games and other application to draw "funky" fonts.
+ *
+ * To get a font instance at a specific size from a `PangoUserFace`,
+ * use [ctor Pango UserFont new].
+ */
+
+ /* {{{ Utilities */
+
+static void
+ensure_psname (PangoUserFace *self)
+{
+ char *p;
+
+ if (self->psname)
+ return;
+
+ self->psname = g_strconcat (pango_font_description_get_family (self->description), "_", self->name, NULL);
+
+ /* PostScript name should not contain problematic chars, but just in case,
+ * make sure we don't have any ' ', '=' or ',' that would give us parsing
+ * problems.
+ */
+ p = self->psname;
+ while ((p = strpbrk (p, " =,")) != NULL)
+ *p = '?';
+}
+
+static const char *
+style_from_font_description (const PangoFontDescription *desc)
+{
+ PangoStyle style = pango_font_description_get_style (desc);
+ PangoWeight weight = pango_font_description_get_weight (desc);
+
+ switch (style)
+ {
+ case PANGO_STYLE_ITALIC:
+ if (weight == PANGO_WEIGHT_BOLD)
+ return "Bold Italic";
+ else
+ return "Italic";
+ break;
+ case PANGO_STYLE_OBLIQUE:
+ if (weight == PANGO_WEIGHT_BOLD)
+ return "Bold Oblique";
+ else
+ return "Oblique";
+ break;
+ case PANGO_STYLE_NORMAL:
+ if (weight == PANGO_WEIGHT_BOLD)
+ return "Bold";
+ else
+ return "Regular";
+ break;
+ default: ;
+ }
+
+ return NULL;
+}
+
+static gboolean
+default_shape_func (PangoUserFace *face,
+ int size,
+ const char *text,
+ int length,
+ const PangoAnalysis *analysis,
+ PangoGlyphString *glyphs,
+ PangoShapeFlags flags,
+ gpointer user_data)
+{
+ int n_chars;
+ const char *p;
+ int cluster = 0;
+ int i;
+ int last_cluster;
+ gboolean is_color;
+ hb_glyph_extents_t ext;
+ hb_position_t dummy;
+
+ n_chars = g_utf8_strlen (text, length);
+
+ pango_glyph_string_set_size (glyphs, n_chars);
+
+ last_cluster = -1;
+
+ p = text;
+ for (i = 0; i < n_chars; i++)
+ {
+ gunichar wc;
+ PangoGlyph glyph;
+ PangoRectangle logical_rect;
+
+ wc = g_utf8_get_char (p);
+
+ if (g_unichar_type (wc) != G_UNICODE_NON_SPACING_MARK)
+ cluster = p - text;
+
+ if (pango_is_zero_width (wc))
+ glyph = PANGO_GLYPH_EMPTY;
+ else if (!face->glyph_func (face, wc, &glyph, face->user_data))
+ glyph = PANGO_GET_UNKNOWN_GLYPH (wc);
+
+ face->glyph_info_func (face, size, glyph, &ext, &dummy, &dummy, &is_color, face->user_data);
+ pango_font_get_glyph_extents (analysis->font, glyph, NULL, &logical_rect);
+
+ glyphs->glyphs[i].glyph = glyph;
+
+ glyphs->glyphs[i].attr.is_cluster_start = cluster != last_cluster;
+ glyphs->glyphs[i].attr.is_color = is_color;
+
+ glyphs->glyphs[i].geometry.x_offset = 0;
+ glyphs->glyphs[i].geometry.y_offset = 0;
+ glyphs->glyphs[i].geometry.width = logical_rect.width;
+
+ glyphs->log_clusters[i] = cluster;
+ last_cluster = cluster;
+
+ p = g_utf8_next_char (p);
+ }
+
+ if (analysis->level & 1)
+ pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);
+
+ return TRUE;
+}
+
+static gboolean
+default_render_func (PangoUserFace *face,
+ int size,
+ hb_codepoint_t glyph,
+ gpointer user_data,
+ const char *backend_id,
+ gpointer backend_data)
+{
+ /* Draw nothing... not very exciting */
+ return TRUE;
+}
+
+/* }}} */
+/* {{{ PangoFontFace implementation */
+
+struct _PangoUserFaceClass
+{
+ PangoFontFaceClass parent_class;
+};
+
+G_DEFINE_TYPE (PangoUserFace, pango_user_face, PANGO_TYPE_FONT_FACE)
+
+static void
+pango_user_face_init (PangoUserFace *self)
+{
+}
+
+static void
+pango_user_face_finalize (GObject *object)
+{
+ PangoUserFace *self = PANGO_USER_FACE (object);
+
+ pango_font_description_free (self->description);
+ g_free (self->name);
+ g_free (self->faceid);
+ if (self->destroy)
+ self->destroy (self->user_data);
+
+ G_OBJECT_CLASS (pango_user_face_parent_class)->finalize (object);
+}
+
+static const char *
+pango_user_face_get_face_name (PangoFontFace *face)
+{
+ PangoUserFace *self = PANGO_USER_FACE (face);
+
+ return self->name;
+}
+
+static PangoFontDescription *
+pango_user_face_describe (PangoFontFace *face)
+{
+ PangoUserFace *self = PANGO_USER_FACE (face);
+
+ if ((pango_font_description_get_set_fields (self->description) & PANGO_FONT_MASK_FACEID) == 0)
+ pango_font_description_set_faceid (self->description, pango_user_face_get_faceid (self));
+
+ return pango_font_description_copy (self->description);
+}
+
+static PangoFontFamily *
+pango_user_face_get_family (PangoFontFace *face)
+{
+ PangoUserFace *self = PANGO_USER_FACE (face);
+
+ return self->family;
+}
+
+static gboolean
+pango_user_face_is_synthesized (PangoFontFace *face)
+{
+ return TRUE;
+}
+
+static gboolean
+pango_user_face_is_monospace (PangoFontFace *face)
+{
+ return FALSE;
+}
+
+static gboolean
+pango_user_face_is_variable (PangoFontFace *face)
+{
+ return FALSE;
+}
+
+static void
+pango_user_face_class_init (PangoUserFaceClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ PangoFontFaceClass *face_class = PANGO_FONT_FACE_CLASS (class);
+
+ object_class->finalize = pango_user_face_finalize;
+
+ face_class->get_face_name = pango_user_face_get_face_name;
+ face_class->describe = pango_user_face_describe;
+ face_class->is_synthesized = pango_user_face_is_synthesized;
+ face_class->get_family = pango_user_face_get_family;
+ face_class->is_monospace = pango_user_face_is_monospace;
+ face_class->is_variable = pango_user_face_is_variable;
+}
+
+/* }}} */
+/* {{{ Private API */
+
+/*< private >
+ * pango_user_face_set_family:
+ * @self: a `PangoUserFace`
+ * @family: a `PangoFontFamily`
+ *
+ * Sets the font family of a `PangoUserFace`.
+ *
+ * This should only be called by fontmap implementations.
+ */
+void
+pango_user_face_set_family (PangoUserFace *self,
+ PangoFontFamily *family)
+{
+ self->family = family;
+}
+
+/*< private >
+ * pango_user_face_has_char:
+ * @self: a `PangoUserFace`
+ * @wc: a Unicode character
+ *
+ * Returns whether the face provides a glyph for this character.
+ *
+ * Returns: `TRUE` if @font can render @wc
+ */
+gboolean
+pango_user_face_has_char (PangoUserFace *self,
+ gunichar wc)
+{
+ hb_codepoint_t glyph;
+
+ return self->glyph_func (self, wc, &glyph, self->user_data);
+}
+
+/*< private >
+ * pango_user_face_get_faceid:
+ * @self: a `PangoUserFace`
+ *
+ * Returns the faceid of the face.
+ *
+ * Returns: (transfer none): the faceid
+ */
+const char *
+pango_user_face_get_faceid (PangoUserFace *self)
+{
+ if (!self->faceid)
+ {
+ ensure_psname (self);
+ self->faceid = g_strconcat ("user:", self->psname, NULL);
+ }
+
+ return self->faceid;
+}
+
+/* }}} */
+ /* {{{ Public API */
+
+/**
+ * PangoUserFaceGetFontInfoFunc:
+ * @face: the `PangoUserFace`
+ * @size: the size of the font that is being created
+ * @extents: (out caller-allocates): return location for font extents
+ * user_data: user data that was passed to [ctor Pango UserFace new]
+ *
+ * A `PangoUserFaceGetFontInfoFunc` is called to obtain
+ * font extents for user fonts.
+ *
+ * Returns: `TRUE` on success
+ */
+
+/**
+ * PangoUserFaceUnicodeToGlyphFunc:
+ * @face: the `PangoUserFace`
+ * @unicode: the Unicode character
+ * @glyph: (out caller-allocates): return location for the glyph that
+ * @user_data: user data that was passed to [ctor Pango UserFace new]
+ *
+ * A `PangoUserFaceUnicodeToGlyphFunc` is called to determine if a user
+ * font can render a character, and what glyph it will use.
+ *
+ * Returns: `TRUE` on success
+ */
+
+/**
+ * PangoUserFaceGetGlyphInfoFunc:
+ * @face: the `PangoUserFace`
+ * @size: the size of the font that is queried
+ * @glyph: the glyph that is being queried
+ * @extents: (out caller-allocates): return location for the glyphs ink rectangle
+ * @h_advance: (out caller-allocates): return location for the h advance
+ * @v_advance: (out caller-allocates): return location for the v advance
+ * @is_color_glyph: (out caller-allocates): return location for information about
+ * whether @glyph has color
+ * @user_data: user data that was passed to [ctor Pango UserFace new]
+ *
+ * A `PangoUserFaceGetGlyphInfoFunc` is called to query information about
+ * a glyph in a user font.
+ *
+ * Returns: `TRUE` on success
+ */
+
+/**
+ * PangoUserFaceTextToGlyphFunc:
+ * @face: the `PangoUserFace`
+ * @size: the size of the font that is used
+ * @text: the text to shape
+ * @length: the length of @text
+ * @analysis: `PangoAnalysis` for @text
+ * @glyphs: (out caller-allocates): the `PangoGlyphString` to populate
+ * @flags: `PangoShapeFlags` to use
+ * @user_data: user data that was pased to [ctor Pango UserFace new]
+ *
+ * A `PangoUserFaceTextToGlyphFunc` is used to shape a segment of text
+ * with a user font.
+ *
+ * This callback is optional when creating a user font. If it isn't
+ * provided, Pango will rely on the `PangoUserFaceUnicodeToGlyphFunc`
+ * and the `PangoUserFaceGetGlyphInfo` callback to translate Unicode
+ * characters to glyphs 1-1, and position the glyphs according to their
+ * advance widths.
+ *
+ * If this callback is provided, it replaces all of Pango's own shaping.
+ * The function can implement ligatures, reordering, and other features
+ * that turn the text-to-glyph mapping into an m-n relationship. The
+ * function is responsible for filling not just the glyphs and their
+ * positions, but also cluster information and glyph attributes in
+ * [struct@Pango.GlyphVisAttr].
+ *
+ * Returns: `TRUE` on success
+ */
+
+/**
+ * PangoUserFaceRenderGlyphFunc:
+ * @face: the `PangoUserFace`
+ * @size: the size of the font that is used
+ * @glyph: the glyph that is being queried
+ * @user_data: user data that was pased to [ctor Pango UserFace new]
+ * @backend_id: a string identifying the [class@Pango.Renderer] in use
+ * @backend_data: backend-specific data
+ *
+ * A `PangoUserFaceRenderGlyphFunc` is called to render a glyph with a
+ * user font.
+ *
+ * This callback is optional when creating a user font. IF it isn't
+ * provided, the font will not produce any visible output.
+ *
+ * The @backend_id identifies the [class@Pango.Renderer] in use.
+ * Implementations should return `FALSE` for unsupported backends.
+ *
+ * The cairo backend uses the string "cairo" as @backend_id, and
+ * provides a `cairo_t` as @backend_data. The context is set up
+ * to render in `font space`, i.e. The transformation is set up
+ * to map the unit square to @size x @size. If supported, Pango
+ * uses `cairo_user_font_face_set_render_color_glyph_func` to
+ * allow glyphs to be rendered with colors. For more information,
+ * see the cairo documentation about user fonts.
+ *
+ * Returns: `TRUE` on success
+ */
+
+/**
+ * pango_user_face_new:
+ * @font_info_func: (scope notified): a `PangoUserFaceGetFontInfoFunc`
+ * @glyph_func: (scope notified): a `PangoUserFaceUnicodeToGlyphFunc`
+ * @glyph_info_func: (scope notified): a `PangoUserFaceGetGlyphInfoFunc`
+ * @shape_func: (scope notified) (nullable): a `PangoUserFaceTextToGlyphFunc`
+ * @render_func: (scope notified) (nullable): a `PangoUserFaceRenderGlyphFunc`
+ * @user_data: user data that will be assed to the callbacks
+ * @destroy: destroy notify for @user_data
+ * @name: name for the face
+ * @description: (nullable): `PangoFontDescription` for the font
+ *
+ * Creates a new user font face.
+ *
+ * A user font face does not rely on font data from a font file,
+ * but instead uses callbacks to determine glyph extents, positions
+ * and rendering.
+ *
+ * If @shape_func is `NULL`, Pango will rely on @glyph_func and
+ * @glyph_info_func to find and position a glyph for each character.
+ *
+ * If @render_func is `NULL`, the font will not produce any visible
+ * glyphs.
+ *
+ * Returns: (transfer full): a newly created `PangoUserFace`
+ *
+ * Since: 1.52
+ */
+PangoUserFace *
+pango_user_face_new (PangoUserFaceGetFontInfoFunc font_info_func,
+ PangoUserFaceUnicodeToGlyphFunc glyph_func,
+ PangoUserFaceGetGlyphInfoFunc glyph_info_func,
+ PangoUserFaceTextToGlyphFunc shape_func,
+ PangoUserFaceRenderGlyphFunc render_func,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ const char *name,
+ const PangoFontDescription *description)
+{
+ PangoUserFace *self;
+
+ self = g_object_new (PANGO_TYPE_USER_FACE, NULL);
+
+ self->font_info_func = font_info_func;
+ self->glyph_func = glyph_func;
+ self->glyph_info_func = glyph_info_func;
+ self->shape_func = shape_func ? shape_func : default_shape_func;
+ self->render_func = render_func ? render_func : default_render_func;
+ self->user_data = user_data;
+ self->destroy = destroy;
+
+ if (!name)
+ name = style_from_font_description (description);
+
+ self->name = g_strdup (name);
+ self->description = pango_font_description_copy (description);
+
+ return self;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-userface.h b/pango/pango-userface.h
new file mode 100644
index 000000000..6d6def4b7
--- /dev/null
+++ b/pango/pango-userface.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-types.h>
+#include <pango/pango-glyph.h>
+#include <pango/pango-font-face.h>
+
+G_BEGIN_DECLS
+
+typedef struct _PangoUserFont PangoUserFont;
+
+#define PANGO_TYPE_USER_FACE (pango_user_face_get_type ())
+
+PANGO_AVAILABLE_IN_ALL
+PANGO_DECLARE_INTERNAL_TYPE (PangoUserFace, pango_user_face, PANGO, USER_FACE, PangoFontFace)
+
+typedef gboolean (* PangoUserFaceGetFontInfoFunc) (PangoUserFace *face,
+ int size,
+ hb_font_extents_t *extents,
+ gpointer user_data);
+
+typedef gboolean (* PangoUserFaceUnicodeToGlyphFunc) (PangoUserFace *face,
+ hb_codepoint_t unicode,
+ hb_codepoint_t *glyph,
+ gpointer user_data);
+
+typedef gboolean (* PangoUserFaceGetGlyphInfoFunc) (PangoUserFace *face,
+ int size,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ hb_position_t *h_advance,
+ hb_position_t *v_advance,
+ gboolean *is_color_glyph,
+ gpointer user_data);
+
+typedef gboolean (* PangoUserFaceTextToGlyphFunc) (PangoUserFace *face,
+ int size,
+ const char *text,
+ int length,
+ const PangoAnalysis *analysis,
+ PangoGlyphString *glyphs,
+ PangoShapeFlags flags,
+ gpointer user_data);
+
+typedef gboolean (* PangoUserFaceRenderGlyphFunc) (PangoUserFace *face,
+ int size,
+ hb_codepoint_t glyph,
+ gpointer user_data,
+ const char *backend_id,
+ gpointer backend_data);
+
+PANGO_AVAILABLE_IN_ALL
+PangoUserFace * pango_user_face_new (PangoUserFaceGetFontInfoFunc font_info_func,
+ PangoUserFaceUnicodeToGlyphFunc glyph_func,
+ PangoUserFaceGetGlyphInfoFunc glyph_info_func,
+ PangoUserFaceTextToGlyphFunc shape_func,
+ PangoUserFaceRenderGlyphFunc render_func,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ const char *name,
+ const PangoFontDescription *description);
+
+G_END_DECLS
diff --git a/pango/pango-userfont-private.h b/pango/pango-userfont-private.h
new file mode 100644
index 000000000..a3669a09f
--- /dev/null
+++ b/pango/pango-userfont-private.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "pango-userfont.h"
+#include "pango-userface.h"
+#include "pango-font-private.h"
+
+
+struct _PangoUserFont
+{
+ PangoFont parent_instance;
+
+ int size; /* point size, scaled by PANGO_SCALE */
+ float dpi;
+ PangoGravity gravity;
+ PangoMatrix matrix;
+
+ /* up to here shared with PangoHbFont */
+
+ PangoUserFace *face;
+};
diff --git a/pango/pango-userfont.c b/pango/pango-userfont.c
new file mode 100644
index 000000000..e3027f9a1
--- /dev/null
+++ b/pango/pango-userfont.c
@@ -0,0 +1,482 @@
+/* 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-userfont-private.h"
+
+#include "pango-font-private.h"
+#include "pango-font-metrics-private.h"
+#include "pango-coverage-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>
+
+/**
+ * PangoUserFont:
+ *
+ * `PangoUserFont` is a `PangoFont` implementation that uses callbacks.
+ */
+
+/* {{{ PangoFont implementation */
+
+struct _PangoUserFontClass
+{
+ PangoFontClass parent_class;
+};
+
+G_DEFINE_TYPE (PangoUserFont, pango_user_font, PANGO_TYPE_FONT)
+
+static void
+pango_user_font_init (PangoUserFont *self)
+{
+ self->gravity = PANGO_GRAVITY_AUTO;
+ self->matrix = (PangoMatrix) PANGO_MATRIX_INIT;
+}
+
+static void
+pango_user_font_finalize (GObject *object)
+{
+ PangoUserFont *self = PANGO_USER_FONT (object);
+
+ g_object_unref (self->face);
+
+ G_OBJECT_CLASS (pango_user_font_parent_class)->finalize (object);
+}
+
+static PangoFontDescription *
+pango_user_font_describe (PangoFont *font)
+{
+ PangoUserFont *self = PANGO_USER_FONT (font);
+ PangoFontDescription *desc;
+
+ desc = pango_font_face_describe (PANGO_FONT_FACE (self->face));
+ pango_font_description_set_gravity (desc, self->gravity);
+ pango_font_description_set_size (desc, self->size);
+
+ return desc;
+}
+
+static PangoFontDescription *
+pango_user_font_describe_absolute (PangoFont *font)
+{
+ PangoUserFont *self = PANGO_USER_FONT (font);
+ PangoFontDescription *desc;
+
+ desc = pango_user_font_describe (font);
+ pango_font_description_set_absolute_size (desc, self->size * self->dpi / 72.);
+
+ return desc;
+}
+
+static PangoCoverage *
+pango_user_font_get_coverage (PangoFont *font,
+ PangoLanguage *language G_GNUC_UNUSED)
+{
+ /* FIXME */
+ return pango_coverage_new ();
+}
+
+static void
+pango_user_font_get_glyph_extents (PangoFont *font,
+ PangoGlyph glyph,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ PangoUserFont *self = PANGO_USER_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 || (glyph & PANGO_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)
+ {
+ PangoRectangle r;
+
+ r.x = extents.x_bearing;
+ r.y = - extents.y_bearing;
+ r.width = extents.width;
+ r.height = - extents.height;
+
+ 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->y = - extents.ascender;
+ logical_rect->width = h_advance;
+ 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_user_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;
+
+ 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;
+
+ metrics->underline_thickness = PANGO_SCALE;
+ metrics->underline_position = - PANGO_SCALE;
+ metrics->strikethrough_thickness = PANGO_SCALE;
+ metrics->strikethrough_position = metrics->ascent / 2;
+ metrics->approximate_char_width = 0; /* FIXME */
+ metrics->approximate_digit_width = 0;
+
+ return metrics;
+}
+
+static PangoFontMap *
+pango_user_font_get_font_map (PangoFont *font)
+{
+ PangoUserFont *self = PANGO_USER_FONT (font);
+ PangoHbFamily *family = PANGO_HB_FAMILY (self->face->family);
+
+ return family->map;
+}
+
+static hb_bool_t
+nominal_glyph_func (hb_font_t *font, void *font_data,
+ hb_codepoint_t unicode,
+ hb_codepoint_t *glyph,
+ void *user_data)
+{
+ PangoUserFont *self = font_data;
+
+ return self->face->glyph_func (self->face, unicode, glyph, self->face->user_data);
+}
+
+static hb_position_t
+glyph_h_advance_func (hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ void *user_data)
+{
+ PangoUserFont *self = font_data;
+ int size = self->size * self->dpi / 72.;
+ hb_position_t h_advance, v_advance;
+ hb_glyph_extents_t glyph_extents;
+ gboolean is_color;
+
+ self->face->glyph_info_func (self->face, size, glyph,
+ &glyph_extents,
+ &h_advance, &v_advance,
+ &is_color,
+ self->face->user_data);
+
+ return h_advance;
+}
+
+static hb_bool_t
+glyph_extents_func (hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents,
+ void *user_data)
+{
+ PangoUserFont *self = font_data;
+ int size = self->size * self->dpi / 72.;
+ hb_position_t h_advance, v_advance;
+ gboolean is_color;
+
+ return self->face->glyph_info_func (self->face, size, glyph,
+ extents,
+ &h_advance, &v_advance,
+ &is_color,
+ self->face->user_data);
+}
+
+static hb_bool_t
+font_extents_func (hb_font_t *font, void *font_data,
+ hb_font_extents_t *extents,
+ void *user_data)
+{
+ PangoUserFont *self = font_data;
+ int size = self->size * self->dpi / 72.;
+
+ return self->face->font_info_func (self->face, size, extents, self->face->user_data);
+}
+
+static hb_font_t *
+pango_user_font_create_hb_font (PangoFont *font)
+{
+ PangoUserFont *self = PANGO_USER_FONT (font);
+ hb_font_t *hb_font;
+ double x_scale, y_scale;
+ int size;
+ hb_blob_t *blob;
+ hb_face_t *face;
+ hb_font_funcs_t *funcs;
+
+ blob = hb_blob_create ("", 0, HB_MEMORY_MODE_READONLY, NULL, NULL);
+ face = hb_face_create (blob, 0);
+ hb_font = hb_font_create (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_set_funcs (hb_font, funcs, self, NULL);
+
+ hb_font_funcs_destroy (funcs);
+ hb_face_destroy (face);
+ hb_blob_destroy (blob);
+
+ size = self->size * self->dpi / 72.f;
+ x_scale = y_scale = 1;
+
+ 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);
+
+ return hb_font;
+}
+
+static gboolean
+pango_user_font_has_char (PangoFont *font,
+ gunichar wc)
+{
+ hb_font_t *user_font = pango_font_get_hb_font (font);
+ hb_codepoint_t glyph;
+
+ return hb_font_get_nominal_glyph (user_font, wc, &glyph);
+}
+
+static PangoFontFace *
+pango_user_font_get_face (PangoFont *font)
+{
+ PangoUserFont *self = PANGO_USER_FONT (font);
+
+ return PANGO_FONT_FACE (self->face);
+}
+
+static void
+pango_user_font_class_init (PangoUserFontClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ PangoFontClass *font_class = PANGO_FONT_CLASS (class);
+
+ object_class->finalize = pango_user_font_finalize;
+
+ font_class->describe = pango_user_font_describe;
+ font_class->describe_absolute = pango_user_font_describe_absolute;
+ font_class->get_coverage = pango_user_font_get_coverage;
+ font_class->get_glyph_extents = pango_user_font_get_glyph_extents;
+ font_class->get_metrics = pango_user_font_get_metrics;
+ font_class->get_font_map = pango_user_font_get_font_map;
+ font_class->create_hb_font = pango_user_font_create_hb_font;
+ font_class->has_char = pango_user_font_has_char;
+ font_class->get_face = pango_user_font_get_face;
+}
+
+/* }}} */
+ /* {{{ Public API */
+
+/**
+ * pango_user_font_new:
+ * @face: the `PangoUserFace` to use
+ * @size: the desired size in points, scaled by `PANGO_SCALE`
+ * @gravity: the gravity to use when rendering
+ * @dpi: the dpi used when rendering
+ * @matrix: (nullable): transformation matrix to use when rendering
+ *
+ * Creates a new `PangoUserFont`.
+ *
+ * Returns: a newly created `PangoUserFont`
+ *
+ * Since: 1.52
+ */
+PangoUserFont *
+pango_user_font_new (PangoUserFace *face,
+ int size,
+ PangoGravity gravity,
+ float dpi,
+ const PangoMatrix *matrix)
+{
+ PangoUserFont *self;
+
+ self = g_object_new (PANGO_TYPE_USER_FONT, NULL);
+
+ self->face = g_object_ref (face);
+
+ self->size = size;
+ self->dpi = dpi;
+ if (gravity != PANGO_GRAVITY_AUTO)
+ self->gravity = gravity;
+ if (matrix)
+ self->matrix = *matrix;
+
+ return self;
+}
+
+/**
+ * pango_user_font_new_for_description:
+ * @face: the `PangoUserFace` to use
+ * @description: a `PangoFontDescription`
+ * @dpi: the dpi used when rendering
+ * @matrix: (nullable): transformation matrix to use when rendering
+ *
+ * Creates a new `PangoUserFont` with size and gravity taken
+ * from a font description.
+ *
+ * Returns: a newly created `PangoHbFont`
+ *
+ * Since: 1.52
+ */
+
+PangoUserFont *
+pango_user_font_new_for_description (PangoUserFace *face,
+ const PangoFontDescription *description,
+ float dpi,
+ const PangoMatrix *matrix)
+{
+ int size;
+ PangoGravity gravity;
+
+ 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);
+
+ 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_user_font_new (face, size, gravity, dpi, matrix);
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-userfont.h b/pango/pango-userfont.h
new file mode 100644
index 000000000..e60cf508d
--- /dev/null
+++ b/pango/pango-userfont.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pango/pango-types.h>
+#include <pango/pango-userface.h>
+#include <hb.h>
+
+G_BEGIN_DECLS
+
+#define PANGO_TYPE_USER_FONT (pango_user_font_get_type ())
+
+PANGO_AVAILABLE_IN_ALL
+PANGO_DECLARE_INTERNAL_TYPE (PangoUserFont, pango_user_font, PANGO, USER_FONT, PangoFont)
+
+PANGO_AVAILABLE_IN_ALL
+PangoUserFont * pango_user_font_new (PangoUserFace *face,
+ int size,
+ PangoGravity gravity,
+ float dpi,
+ const PangoMatrix *matrix);
+
+PANGO_AVAILABLE_IN_ALL
+PangoUserFont * pango_user_font_new_for_description (PangoUserFace *face,
+ const PangoFontDescription *description,
+ float dpi,
+ const PangoMatrix *matrix);
+
+G_END_DECLS
diff --git a/pango/pango.h b/pango/pango.h
index 569636a1f..07169f42c 100644
--- a/pango/pango.h
+++ b/pango/pango.h
@@ -59,6 +59,8 @@
#include <pango/pango-script.h>
#include <pango/pango-tabs.h>
#include <pango/pango-types.h>
+#include <pango/pango-userface.h>
+#include <pango/pango-userfont.h>
#include <pango/pango-utils.h>
#include <pango/pango-version-macros.h>
diff --git a/pango/pangocairo-font.c b/pango/pangocairo-font.c
index 134e57fed..021c9d626 100644
--- a/pango/pangocairo-font.c
+++ b/pango/pangocairo-font.c
@@ -31,6 +31,8 @@
#include "pango-impl-utils.h"
#include "pango-hbfont-private.h"
#include "pango-hbface-private.h"
+#include "pango-userfont-private.h"
+#include "pango-userface-private.h"
#include "pangocairo-fc.h"
#pragma GCC diagnostic push
@@ -74,6 +76,76 @@ _pango_cairo_font_private_scaled_font_data_destroy (PangoCairoFontPrivateScaledF
static FT_Library ft_library;
+static cairo_user_data_key_t cairo_user_data;
+
+static cairo_status_t
+render_func (cairo_scaled_font_t *scaled_font,
+ unsigned long glyph,
+ cairo_t *cr,
+ cairo_text_extents_t *extents)
+{
+ cairo_font_face_t *font_face;
+ PangoUserFont *font;
+ hb_glyph_extents_t glyph_extents;
+ hb_position_t h_advance;
+ hb_position_t v_advance;
+ gboolean is_color;
+
+ font_face = cairo_scaled_font_get_font_face (scaled_font);
+ font = cairo_font_face_get_user_data (font_face, &cairo_user_data);
+
+ extents->x_bearing = 0;
+ extents->y_bearing = 0;
+ extents->width = 0;
+ extents->height = 0;
+ extents->x_advance = 0;
+ extents->y_advance = 0;
+
+ if (!font->face->glyph_info_func (font->face, 1024,
+ (hb_codepoint_t)glyph,
+ &glyph_extents,
+ &h_advance, &v_advance,
+ &is_color,
+ font->face->user_data))
+ {
+ return CAIRO_STATUS_USER_FONT_ERROR;
+ }
+
+ extents->x_bearing = glyph_extents.x_bearing / (double) 1024;
+ extents->y_bearing = glyph_extents.y_bearing / (double) 1024;
+ extents->width = glyph_extents.width / (double) 1024;
+ extents->height = glyph_extents.height / (double) 1024;
+ extents->x_advance = h_advance / (double) 1024;
+ extents->y_advance = v_advance / (double) 1024;
+
+ if (!font->face->render_func (font->face, font->size,
+ (hb_codepoint_t)glyph,
+ font->face->user_data,
+ "cairo",
+ cr))
+ {
+ return CAIRO_STATUS_USER_FONT_ERROR;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_font_face_t *
+create_font_face_for_user_font (PangoUserFont *font)
+{
+ cairo_font_face_t *cairo_face;
+
+ cairo_face = cairo_user_font_face_create ();
+ cairo_font_face_set_user_data (cairo_face, &cairo_user_data, font, NULL);
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+ cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_func);
+#else
+ cairo_user_font_face_set_render_glyph_func (cairo_face, render_func);
+#endif
+
+ return cairo_face;
+}
+
static cairo_font_face_t *
create_font_face_for_hb_font (PangoHbFont *font)
{
@@ -145,8 +217,10 @@ _pango_cairo_font_private_get_scaled_font (PangoCairoFontPrivate *cf_priv)
if (PANGO_IS_CAIRO_FONT (cf_priv->cfont))
font_face = (* PANGO_CAIRO_FONT_GET_IFACE (cf_priv->cfont)->create_font_face) (cf_priv->cfont);
- else
+ else if (PANGO_IS_HB_FONT (cf_priv->cfont))
font_face = create_font_face_for_hb_font (PANGO_HB_FONT (cf_priv->cfont));
+ else if (PANGO_IS_USER_FONT (cf_priv->cfont))
+ font_face = create_font_face_for_user_font (PANGO_USER_FONT (cf_priv->cfont));
if (G_UNLIKELY (font_face == NULL))
goto done;
@@ -694,26 +768,35 @@ _pango_font_get_cairo_font_private (PangoFont *font)
cf_priv = g_object_get_data (G_OBJECT (font), "pango-hb-font-cairo_private");
if (!cf_priv)
{
- PangoHbFont *hbfont = PANGO_HB_FONT (font);
+ CommonFont *cf = (CommonFont *)font;
cairo_font_options_t *font_options;
cairo_matrix_t font_matrix;
+ double x_scale, y_scale;
int size;
- if (hbfont->face->matrix)
- cairo_matrix_init (&font_matrix,
- hbfont->face->matrix->xx,
- - hbfont->face->matrix->yx,
- - hbfont->face->matrix->xy,
- hbfont->face->matrix->yy,
- 0., 0.);
- else
- cairo_matrix_init (&font_matrix, 1., 0., 0., 1., 0., 0.);
+ cairo_matrix_init (&font_matrix, 1., 0., 0., 1., 0., 0.);
+ x_scale = y_scale = 1;
+
+ if (PANGO_IS_HB_FONT (font))
+ {
+ PangoHbFont *hbfont = PANGO_HB_FONT (font);
+ if (hbfont->face->matrix)
+ cairo_matrix_init (&font_matrix,
+ hbfont->face->matrix->xx,
+ - hbfont->face->matrix->yx,
+ - hbfont->face->matrix->xy,
+ hbfont->face->matrix->yy,
+ 0., 0.);
+
+ x_scale = hbfont->face->x_scale;
+ y_scale = hbfont->face->y_scale;
+ }
- size = hbfont->size * hbfont->dpi / 72.;
+ size = cf->size * cf->dpi / 72.;
cairo_matrix_scale (&font_matrix,
- hbfont->face->x_scale * size / (double)PANGO_SCALE,
- hbfont->face->y_scale * size / (double)PANGO_SCALE);
+ x_scale * size / (double)PANGO_SCALE,
+ y_scale * size / (double)PANGO_SCALE);
font_options = cairo_font_options_create ();
cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
@@ -722,9 +805,9 @@ _pango_font_get_cairo_font_private (PangoFont *font)
cf_priv = g_new0 (PangoCairoFontPrivate, 1);
_pango_cairo_font_private_initialize (cf_priv,
(PangoCairoFont *)font,
- hbfont->gravity,
+ cf->gravity,
font_options,
- &hbfont->matrix,
+ &cf->matrix,
&font_matrix);
cairo_font_options_destroy (font_options);
diff --git a/pango/pangofc-hbfontmap.c b/pango/pangofc-hbfontmap.c
index b06d77baa..f288ef6b4 100644
--- a/pango/pangofc-hbfontmap.c
+++ b/pango/pangofc-hbfontmap.c
@@ -352,7 +352,7 @@ pango_fc_hb_font_map_populate (PangoHbFontMap *map)
if (!face)
continue;
- pango_hb_font_map_add_face (PANGO_HB_FONT_MAP (self), face);
+ pango_hb_font_map_add_face (PANGO_HB_FONT_MAP (self), PANGO_FONT_FACE (face));
}
g_hash_table_unref (lang_hash);
diff --git a/pango/shape.c b/pango/shape.c
index e408aa9b3..81447d6e3 100644
--- a/pango/shape.c
+++ b/pango/shape.c
@@ -30,6 +30,8 @@
#include "pango-item-private.h"
#include "pango-font-private.h"
+#include "pango-userfont-private.h"
+#include "pango-userface-private.h"
#include <hb-ot.h>
@@ -529,6 +531,25 @@ pango_hb_shape (const char *item_text,
hb_font_destroy (hb_font);
}
+/* }}} */
+/* {{{ User shaping */
+
+static void
+pango_user_shape (const char *text,
+ unsigned int length,
+ const PangoAnalysis *analysis,
+ PangoGlyphString *glyphs,
+ PangoShapeFlags flags)
+{
+ PangoUserFont *font = PANGO_USER_FONT (analysis->font);
+
+ font->face->shape_func (font->face, font->size,
+ text, length,
+ analysis,
+ glyphs, flags,
+ font->face->user_data);
+}
+
/* }}} */
/* {{{ Fallback shaping */
@@ -616,47 +637,47 @@ pango_shape_internal (const char *item_text,
g_return_if_fail (paragraph_text <= item_text);
g_return_if_fail (paragraph_text + paragraph_length >= item_text + item_length);
- if (analysis->font)
+ if (PANGO_IS_USER_FONT (analysis->font))
+ pango_user_shape (item_text, item_length, analysis, glyphs, flags);
+ else if (analysis->font)
+ pango_hb_shape (item_text, item_length,
+ paragraph_text, paragraph_length,
+ analysis,
+ log_attrs, num_chars,
+ glyphs, flags);
+ else
+ glyphs->num_glyphs = 0;
+
+ if (analysis->font && glyphs->num_glyphs == 0)
{
- pango_hb_shape (item_text, item_length,
- paragraph_text, paragraph_length,
- analysis,
- log_attrs, num_chars,
- glyphs, flags);
+ /* If a font has been correctly chosen, but no glyphs are output,
+ * there's probably something wrong with the font.
+ *
+ * Trying to be informative, we print out the font description,
+ * and the text, but to not flood the terminal with
+ * zillions of the message, we set a flag to only err once per
+ * font.
+ */
+ GQuark warned_quark = g_quark_from_static_string ("pango-shape-fail-warned");
- if (G_UNLIKELY (glyphs->num_glyphs == 0))
+ if (!g_object_get_qdata (G_OBJECT (analysis->font), warned_quark))
{
- /* If a font has been correctly chosen, but no glyphs are output,
- * there's probably something wrong with the font.
- *
- * Trying to be informative, we print out the font description,
- * and the text, but to not flood the terminal with
- * zillions of the message, we set a flag to only err once per
- * font.
- */
- GQuark warned_quark = g_quark_from_static_string ("pango-shape-fail-warned");
-
- if (!g_object_get_qdata (G_OBJECT (analysis->font), warned_quark))
- {
- PangoFontDescription *desc;
- char *font_name;
+ PangoFontDescription *desc;
+ char *font_name;
- desc = pango_font_describe (analysis->font);
- font_name = pango_font_description_to_string (desc);
- pango_font_description_free (desc);
+ desc = pango_font_describe (analysis->font);
+ font_name = pango_font_description_to_string (desc);
+ pango_font_description_free (desc);
- g_warning ("shaping failure, expect ugly output. font='%s', text='%.*s'",
- font_name, item_length, item_text);
+ g_warning ("shaping failure, expect ugly output. font='%s', text='%.*s'",
+ font_name, item_length, item_text);
- g_free (font_name);
+ g_free (font_name);
- g_object_set_qdata (G_OBJECT (analysis->font), warned_quark,
- GINT_TO_POINTER (1));
- }
+ g_object_set_qdata (G_OBJECT (analysis->font), warned_quark,
+ GINT_TO_POINTER (1));
}
}
- else
- glyphs->num_glyphs = 0;
if (G_UNLIKELY (!glyphs->num_glyphs))
{
diff --git a/tests/testhbfont.c b/tests/testhbfont.c
index 2e689e660..6b6860a95 100644
--- a/tests/testhbfont.c
+++ b/tests/testhbfont.c
@@ -51,7 +51,7 @@ test_hbfont_monospace (void)
g_assert_false (pango_font_family_is_variable (family));
g_assert_false (pango_font_family_is_monospace (family));
- pango_hb_font_map_add_face (map, pango_hb_face_new_from_file (path, 0, -2, NULL, NULL));
+ pango_hb_font_map_add_face (map, PANGO_FONT_FACE (pango_hb_face_new_from_file (path, 0, -2, NULL, NULL)));
g_assert_true (pango_font_family_is_variable (family));
@@ -394,7 +394,7 @@ test_hbfont_load (void)
pango_font_description_free (desc);
g_free (path);
- pango_hb_font_map_add_face (map, face_fat);
+ pango_hb_font_map_add_face (map, PANGO_FONT_FACE (face_fat));
path = g_test_build_filename (G_TEST_DIST, "fonts", "DejaVuSans.ttf", NULL);
desc = pango_font_description_new ();
@@ -402,7 +402,7 @@ test_hbfont_load (void)
face_wild = pango_hb_face_new_from_file (path, 0, -1, "Wild", desc);
pango_font_description_free (desc);
- pango_hb_font_map_add_face (map, face_wild);
+ pango_hb_font_map_add_face (map, PANGO_FONT_FACE (face_wild));
desc = pango_font_face_describe (PANGO_FONT_FACE (face_wild));
pango_font_description_set_size (desc, 12 * PANGO_SCALE);
@@ -460,7 +460,7 @@ test_hbfont_load_variation (void)
pango_font_description_free (desc);
g_free (path);
- pango_hb_font_map_add_face (map, face_fat);
+ pango_hb_font_map_add_face (map, PANGO_FONT_FACE (face_fat));
desc = pango_font_description_new ();
pango_font_description_set_family (desc, "Cat");
@@ -469,7 +469,7 @@ test_hbfont_load_variation (void)
face_wild = pango_hb_face_new_instance (face_fat, &v, 1, "Wild", desc);
pango_font_description_free (desc);
- pango_hb_font_map_add_face (map, face_wild);
+ pango_hb_font_map_add_face (map, PANGO_FONT_FACE (face_wild));
desc = pango_font_face_describe (PANGO_FONT_FACE (face_wild));
@@ -630,12 +630,13 @@ compare_family_name (gconstpointer a,
static gboolean
is_generic_family (PangoFontFamily *fam)
{
- const char *name = pango_font_family_get_name (fam);
-
- if (strcmp (G_OBJECT_TYPE_NAME (fam), "PangoGenericFamily") == 0)
- return TRUE;
+ return g_str_equal (G_OBJECT_TYPE_NAME (fam), "PangoGenericFamily");
+}
- return g_strv_contains ((const char *[]){ "Sans", "Serif", "Monospace", "System-ui", NULL}, name);
+static gboolean
+is_generic_family_name (const char *family)
+{
+ return g_strv_contains ((const char *[]){ "Sans", "Serif", "Monospace", "System-ui", NULL}, family);
}
static const char * const *
@@ -646,9 +647,10 @@ collect_nongeneric_families (PangoFontMap *map)
for (int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (map)); i++)
{
PangoFontFamily *fam = g_list_model_get_item (G_LIST_MODEL (map), i);
+ const char *name = pango_font_family_get_name (fam);
- if (!is_generic_family (fam))
- g_ptr_array_add (array, (gpointer)pango_font_family_get_name (fam));
+ if (!(is_generic_family (fam) || is_generic_family_name (name)))
+ g_ptr_array_add (array, (gpointer)name);
g_object_unref (fam);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]