[pango/userfont: 7/15] Add PangoUserFace and PangoUserFont




commit 47eb3766394fa5d724596af22f9ffce9ba4a0a6b
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Jan 28 00:12:59 2022 -0500

    Add PangoUserFace and PangoUserFont
    
    Move the user font code into its own classes,
    and make PangoHbFontMap and friends deal with
    both PangoHbFace and PangoUserface.

 pango/meson.build                    |   4 +
 pango/pango-generic-family-private.h |   3 +-
 pango/pango-generic-family.c         |   4 +-
 pango/pango-hbface-private.h         |  34 +--
 pango/pango-hbface.c                 |  83 +------
 pango/pango-hbface.h                 |  38 ---
 pango/pango-hbfamily-private.h       |   7 +-
 pango/pango-hbfamily.c               | 100 +++++---
 pango/pango-hbfont-private.h         |  20 +-
 pango/pango-hbfont.c                 |  79 +-----
 pango/pango-hbfontmap.c              | 105 +++++---
 pango/pango-hbfontmap.h              |   4 +-
 pango/pango-userface-private.h       |  54 ++++
 pango/pango-userface.c               | 295 ++++++++++++++++++++++
 pango/pango-userface.h               |  73 ++++++
 pango/pango-userfont-private.h       |  39 +++
 pango/pango-userfont.c               | 470 +++++++++++++++++++++++++++++++++++
 pango/pango-userfont.h               |  47 ++++
 pango/pango.h                        |   2 +
 pango/pangofc-hbfontmap.c            |   2 +-
 20 files changed, 1170 insertions(+), 293 deletions(-)
---
diff --git a/pango/meson.build b/pango/meson.build
index 3be27319..5cf392ac 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -39,6 +39,8 @@ pango_sources = [
   'pango-generic-family.c',
   'pango-hbfont.c',
   'pango-hbfontmap.c',
+  'pango-userface.c',
+  'pango-userfont.c',
 ]
 
 pango_headers = [
@@ -73,6 +75,8 @@ pango_headers = [
   'pango-hbface.h',
   'pango-hbfont.h',
   'pango-hbfontmap.h',
+  'pango-userface.h',
+  'pango-userfont.h',
 ]
 
 pango_installed_headers = pango_headers + [ 'pango-version-macros.h' ]
diff --git a/pango/pango-generic-family-private.h b/pango/pango-generic-family-private.h
index 4ba8c865..78e17610 100644
--- a/pango/pango-generic-family-private.h
+++ b/pango/pango-generic-family-private.h
@@ -21,7 +21,6 @@
 #pragma once
 
 #include "pango-font.h"
-#include "pango-hbface.h"
 #include "pango-generic-family.h"
 #include "pango-hbfamily-private.h"
 
@@ -39,7 +38,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 ceca7399..fd555eab 100644
--- a/pango/pango-generic-family.c
+++ b/pango/pango-generic-family.c
@@ -133,13 +133,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 7899d264..b1118006 100644
--- a/pango/pango-hbface-private.h
+++ b/pango/pango-hbface-private.h
@@ -25,40 +25,40 @@
 #include "pango-language-set-private.h"
 #include <hb.h>
 
-typedef struct _PangoUserFontFuncs PangoUserFontFuncs;
-struct _PangoUserFontFuncs
-{
-  PangoHbFaceGetFontExtentsFunc font_extents_func;
-  PangoHbFaceGetNominalGlyphFunc glyph_func;
-  PangoHbFaceGetGlyphAdvanceFunc advance_func;
-  PangoHbFaceGetGlyphExtentsFunc glyph_extents_func;
-  PangoHbFaceRenderGlyphFunc render_func;
-  gpointer user_data;
-  GDestroyNotify destroy;
+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;
   gboolean embolden;
   gboolean synthetic;
-
-  PangoUserFontFuncs *user_font;
 };
 
 void                    pango_hb_face_set_family        (PangoHbFace          *self,
diff --git a/pango/pango-hbface.c b/pango/pango-hbface.c
index ed433129..5adc1061 100644
--- a/pango/pango-hbface.c
+++ b/pango/pango-hbface.c
@@ -216,16 +216,9 @@ ensure_psname (PangoHbFace *self)
   if (self->psname)
     return;
 
-  if (self->user_font)
-    {
-      self->psname = g_strconcat (pango_font_description_get_family (self->description), "_", self->name, 
NULL);
-    }
-  else
-    {
-      ensure_hb_face (self);
+  ensure_hb_face (self);
 
-      self->psname = get_name_from_hb_face (self->face, HB_OT_NAME_ID_POSTSCRIPT_NAME, 
HB_OT_NAME_ID_INVALID);
-    }
+  self->psname = get_name_from_hb_face (self->face, HB_OT_NAME_ID_POSTSCRIPT_NAME, HB_OT_NAME_ID_INVALID);
 
   /* PostScript name should not contain problematic chars, but just in case,
    * make sure we don't have any ' ', '=' or ',' that would give us parsing
@@ -302,13 +295,6 @@ pango_hb_face_finalize (GObject *object)
   if (self->matrix)
     g_free (self->matrix);
 
-  if (self->user_font)
-    {
-      if (self->user_font->destroy)
-        self->user_font->destroy (self->user_font->user_data);
-      g_free (self->user_font);
-    }
-
   G_OBJECT_CLASS (pango_hb_face_parent_class)->finalize (object);
 }
 
@@ -352,9 +338,6 @@ pango_hb_face_is_monospace (PangoFontFace *face)
 {
   PangoHbFace *self = PANGO_HB_FACE (face);
 
-  if (self->user_font)
-    return FALSE;
-
   ensure_hb_face (self);
 
   return hb_face_is_monospace (self->face);
@@ -365,9 +348,6 @@ pango_hb_face_is_variable (PangoFontFace *face)
 {
   PangoHbFace *self = PANGO_HB_FACE (face);
 
-  if (self->user_font)
-    return FALSE;
-
   /* We don't consider named instances as variable, i.e.
    * a font chooser UI should not expose axes for them.
    *
@@ -519,9 +499,6 @@ pango_hb_face_has_char (PangoHbFace *self,
   hb_codepoint_t glyph;
   gboolean ret;
 
-  if (self->user_font)
-    return self->user_font->glyph_func (self, wc, &glyph, self->user_font->user_data);
-
   ensure_hb_face (self);
 
   hb_font = hb_font_create (self->face);
@@ -893,62 +870,6 @@ pango_hb_face_new_instance (PangoHbFace                *face,
   return self;
 }
 
-/**
- * pango_hb_face_new_user:
- * @font_extents_func: the `PangoHbFaceGetFontExtentsFunc`
- * @glyph_func: the `PangoHbFaceGetNominalGlyphFunc`
- * @advance_func: the `PangoHbFaceGetGlyphAdvanceFunc`
- * @glyph_extents_func: the `PangoHbFaceGetGlyphExtentsFunc`
- * @render_func: the `PangoHbFaceRenderGlyphFunc`
- * @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.
- *
- * Returns: (transfer full): a newly created `PangoHbFace`
- *
- * Since: 1.52
- */
-PangoHbFace *
-pango_hb_face_new_user (PangoHbFaceGetFontExtentsFunc    font_extents_func,
-                        PangoHbFaceGetNominalGlyphFunc   glyph_func,
-                        PangoHbFaceGetGlyphAdvanceFunc   advance_func,
-                        PangoHbFaceGetGlyphExtentsFunc   glyph_extents_func,
-                        PangoHbFaceRenderGlyphFunc       render_func,
-                        gpointer                         user_data,
-                        GDestroyNotify                   destroy,
-                        const char                      *name,
-                        const PangoFontDescription      *description)
-{
-  PangoHbFace *self;
-
-  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
-
-  self->user_font = g_new0 (PangoUserFontFuncs, 1);
-
-  self->user_font->font_extents_func = font_extents_func;
-  self->user_font->glyph_func = glyph_func;
-  self->user_font->advance_func = advance_func;
-  self->user_font->glyph_extents_func = glyph_extents_func,
-  self->user_font->render_func = render_func;
-  self->user_font->user_data = user_data;
-  self->user_font->destroy = destroy;
-
-  self->synthetic = TRUE;
-
-  self->name = g_strdup (name);
-  self->description = pango_font_description_copy (description);
-  self->languages = PANGO_LANGUAGE_SET (pango_language_set_simple_new ());
-
-  return self;
-}
-
 /**
  * pango_hb_face_get_hb_face:
  * @self: a `PangoHbFace`
diff --git a/pango/pango-hbface.h b/pango/pango-hbface.h
index 9d428f8c..04cac041 100644
--- a/pango/pango-hbface.h
+++ b/pango/pango-hbface.h
@@ -59,44 +59,6 @@ PangoHbFace *   pango_hb_face_new_instance      (PangoHbFace                *fac
                                                  const char                 *name,
                                                  const PangoFontDescription *description);
 
-typedef void          (* PangoHbFaceGetFontExtentsFunc)  (PangoHbFace       *face,
-                                                          int                size,
-                                                          hb_font_extents_t *extents,
-                                                          gpointer           user_data);
-
-typedef gboolean      (* PangoHbFaceGetNominalGlyphFunc) (PangoHbFace    *face,
-                                                          hb_codepoint_t  unicode,
-                                                          hb_codepoint_t *glyph,
-                                                          gpointer        user_data);
-
-typedef hb_position_t (* PangoHbFaceGetGlyphAdvanceFunc) (PangoHbFace    *face,
-                                                          int             size,
-                                                          hb_codepoint_t  glyph,
-                                                          gpointer        user_data);
-
-typedef gboolean      (* PangoHbFaceGetGlyphExtentsFunc) (PangoHbFace        *face,
-                                                          int                 size,
-                                                          hb_codepoint_t      glyph,
-                                                          hb_glyph_extents_t *extents,
-                                                          gpointer            user_data);
-
-typedef void          (* PangoHbFaceRenderGlyphFunc)     (PangoHbFace    *face,
-                                                          int             size,
-                                                          hb_codepoint_t  glyph,
-                                                          gpointer        user_data,
-                                                          gpointer        backend_data);
-
-PANGO_AVAILABLE_IN_1_52
-PangoHbFace *   pango_hb_face_new_user          (PangoHbFaceGetFontExtentsFunc    font_extents_func,
-                                                 PangoHbFaceGetNominalGlyphFunc   glyph_func,
-                                                 PangoHbFaceGetGlyphAdvanceFunc   advance_func,
-                                                 PangoHbFaceGetGlyphExtentsFunc   glyph_extents_func,
-                                                 PangoHbFaceRenderGlyphFunc       render_glyph,
-                                                 gpointer                         user_data,
-                                                 GDestroyNotify                   destroy,
-                                                 const char                      *name,
-                                                 const PangoFontDescription      *description);
-
 PANGO_AVAILABLE_IN_1_52
 hb_face_t *     pango_hb_face_get_hb_face       (PangoHbFace            *self);
 
diff --git a/pango/pango-hbfamily-private.h b/pango/pango-hbfamily-private.h
index 7b1dfa9a..40f9232a 100644
--- a/pango/pango-hbfamily-private.h
+++ b/pango/pango-hbfamily-private.h
@@ -21,7 +21,6 @@
 #pragma once
 
 #include "pango-font.h"
-#include "pango-hbface.h"
 
 #define PANGO_TYPE_HB_FAMILY (pango_hb_family_get_type ())
 
@@ -44,12 +43,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 814ec5f8..ebed6200 100644
--- a/pango/pango-hbfamily.c
+++ b/pango/pango-hbfamily.c
@@ -25,6 +25,7 @@
 #include "pango-hbfamily-private.h"
 #include "pango-impl-utils.h"
 #include "pango-hbface-private.h"
+#include "pango-userface-private.h"
 #include "pango-font-private.h"
 
 /* {{{ GListModel implementation */
@@ -45,7 +46,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);
 
@@ -66,28 +67,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,
@@ -95,11 +127,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;
@@ -250,7 +282,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)
@@ -278,21 +310,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);
 }
@@ -300,20 +335,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);
@@ -335,47 +373,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 6c929099..e24f8889 100644
--- a/pango/pango-hbfont-private.h
+++ b/pango/pango-hbfont-private.h
@@ -38,19 +38,33 @@ 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;
diff --git a/pango/pango-hbfont.c b/pango/pango-hbfont.c
index cb6f7d9b..916c466e 100644
--- a/pango/pango-hbfont.c
+++ b/pango/pango-hbfont.c
@@ -812,57 +812,6 @@ pango_hb_font_get_metrics (PangoFont     *font,
   return metrics;
 }
 
-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)
-{
-  PangoHbFont *self = font_data;
-  PangoUserFontFuncs *user_font = self->face->user_font;
-
-  return user_font->glyph_func (self->face, unicode, glyph, user_font->user_data);
-}
-
-static hb_position_t
-glyph_h_advance_func (hb_font_t *font, void *font_data,
-                      hb_codepoint_t glyph,
-                      void *user_data)
-{
-  PangoHbFont *self = font_data;
-  PangoUserFontFuncs *user_font = self->face->user_font;
-  int size = self->size * self->dpi / 72.;
-
-  return user_font->advance_func (self->face, size, glyph, user_font->user_data);
-}
-
-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)
-{
-  PangoHbFont *self = font_data;
-  PangoUserFontFuncs *user_font = self->face->user_font;
-  int size = self->size * self->dpi / 72.;
-
-  return user_font->glyph_extents_func (self->face, size, glyph, extents, user_font->user_data);
-}
-
-static hb_bool_t
-font_extents_func (hb_font_t *font, void *font_data,
-                   hb_font_extents_t *extents,
-                   void *user_data)
-{
-  PangoHbFont *self = font_data;
-  PangoUserFontFuncs *user_font = self->face->user_font;
-  int size = self->size * self->dpi / 72.;
-
-  user_font->font_extents_func (self->face, size, extents, user_font->user_data);
-
-  return TRUE;
-}
-
 static hb_font_t *
 pango_hb_font_create_hb_font (PangoFont *font)
 {
@@ -874,33 +823,7 @@ pango_hb_font_create_hb_font (PangoFont *font)
   float *coords;
   int size;
 
-  if (self->face->user_font)
-    {
-      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);
-    }
-  else
-    {
-      hb_font = hb_font_create (pango_hb_face_get_hb_face (self->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;
diff --git a/pango/pango-hbfontmap.c b/pango/pango-hbfontmap.c
index cfe486bc..4bc6d59a 100644
--- a/pango/pango-hbfontmap.c
+++ b/pango/pango-hbfontmap.c
@@ -28,6 +28,8 @@
 #include "pango-generic-family-private.h"
 #include "pango-hbface-private.h"
 #include "pango-hbfont-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"
@@ -152,6 +154,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)
@@ -180,7 +194,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,
@@ -192,10 +206,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;
             }
         }
@@ -214,12 +228,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;
@@ -265,12 +279,12 @@ pango_fontset_cached_foreach (PangoFontset            *fontset,
       gpointer item = g_ptr_array_index (self->items, i);
       PangoFont *font = NULL;
 
-      if (PANGO_IS_HB_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))
@@ -315,13 +329,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
@@ -446,17 +467,18 @@ 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_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);
 }
@@ -481,6 +503,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);
 
@@ -528,7 +553,7 @@ synthesize_bold_and_italic_faces (PangoHbFontMap *map)
     }
 }
 
- /* }}} */
+/* }}} */
 /* {{{ PangoFontMap implementation */
 
 G_DEFINE_TYPE_WITH_CODE (PangoHbFontMap, pango_hb_font_map, PANGO_TYPE_FONT_MAP,
@@ -609,7 +634,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;
 
@@ -798,7 +823,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);
     }
 
@@ -837,7 +862,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`.
  *
@@ -848,20 +873,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)
     {
@@ -879,7 +912,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`.
  *
@@ -889,15 +922,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);
 
@@ -930,7 +965,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 3ced207d..6fab53a0 100644
--- a/pango/pango-hbfontmap.h
+++ b/pango/pango-hbfontmap.h
@@ -40,11 +40,11 @@ void                    pango_hb_font_map_add_file              (PangoHbFontMap
 
 PANGO_AVAILABLE_IN_1_52
 void                    pango_hb_font_map_add_face              (PangoHbFontMap *self,
-                                                                 PangoHbFace    *face);
+                                                                 PangoFontFace  *face);
 
 PANGO_AVAILABLE_IN_1_52
 void                    pango_hb_font_map_remove_face           (PangoHbFontMap *self,
-                                                                 PangoHbFace    *face);
+                                                                 PangoFontFace  *face);
 
 PANGO_AVAILABLE_IN_1_52
 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 00000000..3bbb8dd3
--- /dev/null
+++ b/pango/pango-userface-private.h
@@ -0,0 +1,54 @@
+/* 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.
+ */
+
+#pragma once
+
+#include "pango-userface.h"
+#include "pango-fontmap.h"
+#include <hb.h>
+
+struct _PangoUserFace
+{
+  PangoFontFace parent_instance;
+
+  PangoFontDescription *description;
+  char *name;
+  PangoFontFamily *family;
+  char *psname;
+  char *faceid;
+
+  /* up to here shared with PangoHbFace */
+
+  PangoUserFaceGetFontExtentsFunc font_extents_func;
+  PangoUserFaceGetNominalGlyphFunc glyph_func;
+  PangoUserFaceGetGlyphAdvanceFunc advance_func;
+  PangoUserFaceGetGlyphExtentsFunc glyph_extents_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 00000000..fcf3fa55
--- /dev/null
+++ b/pango/pango-userface.c
@@ -0,0 +1,295 @@
+/* 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-private.h"
+#include "pango-userface-private.h"
+
+#include <string.h>
+#include <hb-ot.h>
+
+/**
+ * PangoUserFace:
+ *
+ * `PangoUserFace` is a `PangoFontFace` implementation that uses callbacks.
+ */
+
+ /* {{{ 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;
+}
+
+/* }}} */
+/* {{{ 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->list_sizes = NULL;
+  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 */
+
+/**
+ * pango_user_face_new:
+ * @font_extents_func: the `PangoUserFaceGetFontExtentsFunc`
+ * @glyph_func: the `PangoUserFaceGetNominalGlyphFunc`
+ * @advance_func: the `PangoUserFaceGetGlyphAdvanceFunc`
+ * @glyph_extents_func: the `PangoUserFaceGetGlyphExtentsFunc`
+ * @render_func: the `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.
+ *
+ * Returns: (transfer full): a newly created `PangoUserFace`
+ *
+ * Since: 1.52
+ */
+PangoUserFace *
+pango_user_face_new (PangoUserFaceGetFontExtentsFunc   font_extents_func,
+                     PangoUserFaceGetNominalGlyphFunc  glyph_func,
+                     PangoUserFaceGetGlyphAdvanceFunc  advance_func,
+                     PangoUserFaceGetGlyphExtentsFunc  glyph_extents_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_extents_func = font_extents_func;
+  self->glyph_func = glyph_func;
+  self->advance_func = advance_func;
+  self->glyph_extents_func = glyph_extents_func,
+  self->render_func = 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 00000000..cbf9f0a4
--- /dev/null
+++ b/pango/pango-userface.h
@@ -0,0 +1,73 @@
+/* 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.
+ */
+
+#pragma once
+
+#include <pango/pango-types.h>
+#include <pango/pango-font.h>
+
+G_BEGIN_DECLS
+
+typedef struct _PangoUserFont PangoUserFont;
+
+#define PANGO_TYPE_USER_FACE      (pango_user_face_get_type ())
+
+PANGO_AVAILABLE_IN_1_52
+G_DECLARE_FINAL_TYPE (PangoUserFace, pango_user_face, PANGO, USER_FACE, PangoFontFace)
+
+typedef void          (* PangoUserFaceGetFontExtentsFunc)  (PangoUserFace       *face,
+                                                            int                size,
+                                                            hb_font_extents_t *extents,
+                                                            gpointer           user_data);
+
+typedef gboolean      (* PangoUserFaceGetNominalGlyphFunc) (PangoUserFace    *face,
+                                                            hb_codepoint_t  unicode,
+                                                            hb_codepoint_t *glyph,
+                                                            gpointer        user_data);
+
+typedef hb_position_t (* PangoUserFaceGetGlyphAdvanceFunc) (PangoUserFace    *face,
+                                                            int             size,
+                                                            hb_codepoint_t  glyph,
+                                                            gpointer        user_data);
+
+typedef gboolean      (* PangoUserFaceGetGlyphExtentsFunc) (PangoUserFace        *face,
+                                                            int                 size,
+                                                            hb_codepoint_t      glyph,
+                                                            hb_glyph_extents_t *extents,
+                                                            gpointer            user_data);
+
+typedef void          (* PangoUserFaceRenderGlyphFunc)     (PangoUserFace    *face,
+                                                            int             size,
+                                                            hb_codepoint_t  glyph,
+                                                            gpointer        user_data,
+                                                            gpointer        backend_data);
+
+PANGO_AVAILABLE_IN_1_52
+PangoUserFace *   pango_user_face_new          (PangoUserFaceGetFontExtentsFunc    font_extents_func,
+                                                PangoUserFaceGetNominalGlyphFunc   glyph_func,
+                                                PangoUserFaceGetGlyphAdvanceFunc   advance_func,
+                                                PangoUserFaceGetGlyphExtentsFunc   glyph_extents_func,
+                                                PangoUserFaceRenderGlyphFunc       render_glyph,
+                                                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 00000000..316807d6
--- /dev/null
+++ b/pango/pango-userfont-private.h
@@ -0,0 +1,39 @@
+/* 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.
+ */
+
+#pragma once
+
+#include "pango-userfont.h"
+#include "pango-userface.h"
+#include <hb.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 00000000..6a46d5e2
--- /dev/null
+++ b/pango/pango-userfont.c
@@ -0,0 +1,470 @@
+/* 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-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;
+
+      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.;
+
+  return self->face->advance_func (self->face, size, glyph, self->face->user_data);
+}
+
+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.;
+
+  return self->face->glyph_extents_func (self->face, size, glyph, extents, 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.;
+
+  self->face->font_extents_func (self->face, size, extents, self->face->user_data);
+
+  return TRUE;
+}
+
+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);
+  PangoFontClassPrivate *pclass;
+
+  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;
+
+  pclass = g_type_class_get_private ((GTypeClass *) class, PANGO_TYPE_FONT);
+
+  pclass->has_char = pango_user_font_has_char;
+  pclass->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 00000000..6b351573
--- /dev/null
+++ b/pango/pango-userfont.h
@@ -0,0 +1,47 @@
+/* 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.
+ */
+
+#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_1_52
+G_DECLARE_FINAL_TYPE (PangoUserFont, pango_user_font, PANGO, USER_FONT, PangoFont)
+
+PANGO_AVAILABLE_IN_1_52
+PangoUserFont * pango_user_font_new                 (PangoUserFace              *face,
+                                                     int                         size,
+                                                     PangoGravity                gravity,
+                                                     float                       dpi,
+                                                     const PangoMatrix          *matrix);
+
+PANGO_AVAILABLE_IN_1_52
+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 9d95b5e5..aa0b7975 100644
--- a/pango/pango.h
+++ b/pango/pango.h
@@ -52,6 +52,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/pangofc-hbfontmap.c b/pango/pangofc-hbfontmap.c
index ceda2cbc..2f005059 100644
--- a/pango/pangofc-hbfontmap.c
+++ b/pango/pangofc-hbfontmap.c
@@ -346,7 +346,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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]