[pango/simple-fontmap: 1/7] Add PangoHbFont and PangoSimpleFontMap




commit 59685346b486c2ab31a3e6d5be03f6b48639f9f1
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Aug 27 22:08:14 2021 -0400

    Add PangoHbFont and PangoSimpleFontMap
    
    This is an attempt to make a font backend that does
    not depend on platform specifics and can be used in
    tests.
    
    We add PangoHbFont and PangoHbFace to wrap hb_font_t
    and hb_face_t, and a very basic fontmap implementation.

 pango/meson.build                |   6 +
 pango/pango-hbcoverage-private.h |  40 +++
 pango/pango-hbface-private.h     |  26 ++
 pango/pango-hbface.c             | 370 +++++++++++++++++++++
 pango/pango-hbface.h             |  50 +++
 pango/pango-hbfont-private.h     |  32 ++
 pango/pango-hbfont.c             | 678 +++++++++++++++++++++++++++++++++++++++
 pango/pango-hbfont.h             |  42 +++
 pango/pango-simplefontmap.c      | 445 +++++++++++++++++++++++++
 pango/pango-simplefontmap.h      |  51 +++
 pango/pango.h                    |   1 +
 11 files changed, 1741 insertions(+)
---
diff --git a/pango/meson.build b/pango/meson.build
index 084de229..a2d717ed 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -27,6 +27,9 @@ pango_sources = [
   'pango-utils.c',
   'reorder-items.c',
   'shape.c',
+  'pango-hbfont.c',
+  'pango-hbface.c',
+  'pango-simplefontmap.c',
 ]
 
 pango_headers = [
@@ -56,6 +59,9 @@ pango_headers = [
   'pango-tabs.h',
   'pango-types.h',
   'pango-utils.h',
+  'pango-hbface.h',
+  'pango-hbfont.h',
+  'pango-simplefontmap.h',
 ]
 
 pango_installed_headers = pango_headers + [ 'pango-version-macros.h' ]
diff --git a/pango/pango-hbcoverage-private.h b/pango/pango-hbcoverage-private.h
new file mode 100644
index 00000000..5b0a19bf
--- /dev/null
+++ b/pango/pango-hbcoverage-private.h
@@ -0,0 +1,40 @@
+/* Pango
+ * pango-hbcoverage-private.h: Coverage sets for hb fonts
+ *
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __PANGO_HB_COVERAGE_PRIVATE_H__
+#define __PANGO_HB_COVERAGE_PRIVATE_H__
+
+#include <glib-object.h>
+#include <pango-coverage.h>
+
+G_BEGIN_DECLS
+
+#define PANGO_TYPE_HB_COVERAGE      (pango_hb_coverage_get_type ())
+
+PANGO_AVAILABLE_IN_1_50
+G_DECLARE_FINAL_TYPE (PangoHbCoverage, pango_hb_coverage, PANGO, HB_COVERAGE, PangoCoverage)
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbCoverage *       pango_hb_coverage_new (hb_font_t *hb_font);
+
+G_END_DECLS
+
+#endif /* __PANGO_HB_COVERAGE_PRIVATE_H__ */
diff --git a/pango/pango-hbface-private.h b/pango/pango-hbface-private.h
new file mode 100644
index 00000000..4c61c3e4
--- /dev/null
+++ b/pango/pango-hbface-private.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "pango-hbface.h"
+#include "pango-fontmap.h"
+#include <hb.h>
+
+struct _PangoHbFace
+{
+  PangoFontFace parent_instance;
+
+  hb_blob_t *blob;
+  unsigned int index;
+  hb_face_t *face;
+  PangoFontDescription *description;
+  char *name;
+  const char *family;
+  PangoMatrix matrix; /* For synthesized oblique */
+
+  PangoFontMap *map;
+};
+
+void            pango_hb_face_set_font_map      (PangoHbFace    *self,
+                                                 PangoFontMap   *map);
+
+gboolean        pango_hb_face_is_monospace      (PangoHbFace    *self);
+gboolean        pango_hb_face_is_variable       (PangoHbFace    *self);
diff --git a/pango/pango-hbface.c b/pango/pango-hbface.c
new file mode 100644
index 00000000..dfe40474
--- /dev/null
+++ b/pango/pango-hbface.c
@@ -0,0 +1,370 @@
+/* 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-hbface-private.h"
+
+#include <hb-ot.h>
+#include <hb-ft.h>
+
+/* {{{ PangoFontFace implementation */
+
+struct _PangoHbFaceClass
+{
+  PangoFontFaceClass parent_class;
+};
+
+G_DEFINE_TYPE (PangoHbFace, pango_hb_face, PANGO_TYPE_FONT_FACE)
+
+static void
+pango_hb_face_init (PangoHbFace *self)
+{
+}
+
+static void
+pango_hb_face_finalize (GObject *object)
+{
+  PangoHbFace *self = PANGO_HB_FACE (object);
+
+  hb_blob_destroy (self->blob);
+  hb_face_destroy (self->face);
+  pango_font_description_free (self->description);
+  if (self->map)
+    g_object_remove_weak_pointer (G_OBJECT (self->map), (gpointer *)&self->map);
+  g_free (self->name);
+
+  G_OBJECT_CLASS (pango_hb_face_parent_class)->finalize (object);
+}
+
+static const char *
+pango_hb_face_get_face_name (PangoFontFace *face)
+{
+  PangoHbFace *self = PANGO_HB_FACE (face);
+
+  return self->name;
+}
+
+static PangoFontDescription *
+pango_hb_face_describe (PangoFontFace *face)
+{
+  PangoHbFace *self = PANGO_HB_FACE (face);
+
+  return pango_font_description_copy (self->description);
+}
+
+static PangoFontFamily *
+pango_hb_face_get_family (PangoFontFace *face)
+{
+  PangoHbFace *self = PANGO_HB_FACE (face);
+
+  if (self->map)
+    return pango_font_map_get_family (self->map, self->family);
+
+  return NULL;
+}
+
+static void
+pango_hb_face_class_init (PangoHbFaceClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PangoFontFaceClass *face_class = PANGO_FONT_FACE_CLASS (class);
+
+  object_class->finalize = pango_hb_face_finalize;
+
+  face_class->get_face_name = pango_hb_face_get_face_name;
+  face_class->describe = pango_hb_face_describe;
+  face_class->list_sizes = NULL;
+  face_class->is_synthesized = NULL;
+  face_class->get_family = pango_hb_face_get_family;
+}
+
+/* }}} */
+/* {{{ Utilities */
+
+static char *
+get_name_from_hb_face (hb_face_t       *face,
+                       hb_ot_name_id_t  name_id)
+{
+  char buf[256];
+  unsigned int len = sizeof (buf);
+  hb_language_t language;
+
+  language = hb_language_from_string ("en", -1);
+
+  hb_ot_name_get_utf8 (face, name_id, language, &len, buf);
+
+  return g_strdup (buf);
+}
+
+static PangoHbFace *
+pango_hb_face_new_from_blob (hb_blob_t    *blob,
+                             unsigned int  index)
+{
+  PangoHbFace *self;
+  char *name;
+
+  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
+
+  self->blob = hb_blob_reference (blob);
+  self->index = index;
+  self->face = hb_face_create (self->blob, index);
+
+  name = get_name_from_hb_face (self->face, HB_OT_NAME_ID_FULL_NAME);
+  self->description = pango_font_description_from_string (name);
+  g_free (name);
+
+  self->family = pango_font_description_get_family (self->description);
+
+  self->name = get_name_from_hb_face (self->face, HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY);
+  if (!self->name || self->name[0] == '\0')
+    {
+      g_free (self->name);
+      self->name = get_name_from_hb_face (self->face, HB_OT_NAME_ID_FONT_SUBFAMILY);
+    }
+
+  self->matrix = (PangoMatrix) PANGO_MATRIX_INIT;
+
+  return self;
+}
+
+/* }}} */
+/* {{{ Private API */
+
+/*< private >
+ * pango_hb_face_set_font_map:
+ * @self: a `PangoHbFace`
+ * @map: a `PangoFontMap`
+ *
+ * Sets the font map of a `PangoHbFace`.
+ *
+ * This should only be called by fontmap implementations.
+ */
+void
+pango_hb_face_set_font_map (PangoHbFace  *self,
+                            PangoFontMap *map)
+{
+  self->map = map;
+  g_object_add_weak_pointer (G_OBJECT (map), (gpointer *)&self->map);
+}
+
+static FT_Library ft_library;
+
+/*< private >
+ * pango_hb_face_is_monospace:
+ * @face: a `PangoHbFace`
+ *
+ * Returns whether @face should be considered monospace.
+ *
+ * Returns: `TRUE` if @face is monospace
+ */
+gboolean
+pango_hb_face_is_monospace (PangoHbFace *face)
+{
+  unsigned int blob_length;
+  const char *blob_data;
+  FT_Face ft_face;
+  gboolean res;
+
+  /* FIXME: get this without freetype */
+  if (!ft_library)
+    FT_Init_FreeType (&ft_library);
+
+  blob_data = hb_blob_get_data (face->blob, &blob_length);
+
+  if (FT_New_Memory_Face (ft_library,
+                          (const FT_Byte *) blob_data,
+                          blob_length,
+                          face->index,
+                          &ft_face))
+    {
+      g_error ("FT_New_Memory_Face failed");
+      return FALSE;
+    }
+
+  res = FT_IS_FIXED_WIDTH (ft_face);
+
+  FT_Done_Face (ft_face);
+
+  return res;
+}
+
+/*< private >
+ * pango_hb_face_is_variable:
+ * @face: a `PangoHbFace`
+ *
+ * Returns whether @face should be considered a variable font.
+ *
+ * Returns: `TRUE` if @face is variable
+ */
+gboolean
+pango_hb_face_is_variable (PangoHbFace *face)
+{
+  return hb_ot_var_get_axis_count (face->face) > 0;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_hb_face_new_from_bytes:
+ * @bytes: a `GBytes` containing font data
+ * @index: face index
+ *
+ * Creates a new `PangoHbFace` from font data in memory.
+ *
+ * Returns: a newly created `PangoHbFace`
+ */
+PangoHbFace *
+pango_hb_face_new_from_bytes (GBytes       *bytes,
+                              unsigned int  index)
+{
+  hb_blob_t *blob;
+  PangoHbFace *face;
+
+  blob = hb_blob_create (g_bytes_get_data (bytes, NULL),
+                         g_bytes_get_size (bytes),
+                         HB_MEMORY_MODE_READONLY,
+                         g_bytes_ref (bytes),
+                         (hb_destroy_func_t) g_bytes_unref);
+
+  face = pango_hb_face_new_from_blob (blob, index);
+
+  hb_blob_destroy (blob);
+
+  return face;
+}
+
+/**
+ * pango_hb_face_new_from_file:
+ * @file: font filename
+ * @index: face index
+ *
+ * Creates a new `PangoHbFace` from a font file.
+ *
+ * Returns: a newly created `PangoHbFace`
+ */
+PangoHbFace *
+pango_hb_face_new_from_file (const char   *file,
+                             unsigned int  index)
+{
+  hb_blob_t *blob;
+  PangoHbFace *face;
+
+  blob = hb_blob_create_from_file (file);
+
+  face = pango_hb_face_new_from_blob (blob, index);
+
+  hb_blob_destroy (blob);
+
+  return face;
+}
+
+/**
+ * pango_hb_face_new_transformed:
+ * @face: a `PangoHbFace`
+ * @name: the name for the transformed face
+ * @transform: the transform to apply
+ *
+ * Creates a new `PangoHbFace` that applies the
+ * given @transform to the glyphs in @face.
+ *
+ * Note that the @name should not include the family
+ * name. For example, when using this to create a
+ * synthetic Italic, the name should just be "Italic".
+ *
+ * Returns: (transfer full): a newly created `PangoHbFace`
+ */
+PangoHbFace *
+pango_hb_face_new_transformed (PangoHbFace       *face,
+                               const char        *name,
+                               const PangoMatrix *transform)
+{
+  PangoHbFace *self;
+  char *fullname;
+
+  g_return_val_if_fail (PANGO_IS_HB_FACE (face), NULL);
+  g_return_val_if_fail (name && *name, NULL);
+  g_return_val_if_fail (transform != NULL, NULL);
+
+  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
+
+  self->blob = hb_blob_reference (face->blob);
+  self->index = face->index;
+  self->face = hb_face_reference (face->face);
+
+  fullname = g_strconcat (face->family, " ", name, NULL);
+  self->description = pango_font_description_from_string (fullname);
+  g_free (fullname);
+
+  self->family = pango_font_description_get_family (self->description);
+  self->name = g_strdup (name);
+  self->matrix = *transform;
+
+  return self;
+}
+
+/**
+ * pango_hb_face_new_alias:
+ * @face: a `PangoHbFace`
+ * @fullname: the full name of the new face
+ *
+ * Creates a new `PangoHbFace that acts as an alias
+ * for the given @face.
+ *
+ * Note that @fullname should include the family name.
+ * For example, when using this to create a monospace
+ * italic font, @fullname should be "Monospace Italic".
+ *
+ * Returns: (transfer full): a newly created `PangoHbFace`
+ */
+PangoHbFace *
+pango_hb_face_new_alias (PangoHbFace *face,
+                         const char  *fullname)
+{
+  PangoHbFace *self;
+
+  g_return_val_if_fail (PANGO_IS_HB_FACE (face), NULL);
+  g_return_val_if_fail (fullname && *fullname, NULL);
+
+  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
+
+  self->blob = hb_blob_reference (face->blob);
+  self->index = face->index;
+  self->face = hb_face_reference (face->face);
+
+  self->description = pango_font_description_from_string (fullname);
+
+  self->family = pango_font_description_get_family (self->description);
+
+  if (g_str_has_prefix (fullname, self->family) &&
+      fullname[strlen (self->family)] != '\0')
+    self->name = g_strstrip (g_strdup (fullname + strlen (self->family)));
+  else
+    self->name = g_strdup ("Regular");
+
+  self->matrix = (PangoMatrix) PANGO_MATRIX_INIT;
+
+  return self;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-hbface.h b/pango/pango-hbface.h
new file mode 100644
index 00000000..dc06ca30
--- /dev/null
+++ b/pango/pango-hbface.h
@@ -0,0 +1,50 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <pango/pango-font.h>
+
+#include <hb.h>
+
+G_BEGIN_DECLS
+
+#define PANGO_TYPE_HB_FACE      (pango_hb_face_get_type ())
+PANGO_AVAILABLE_IN_1_50
+G_DECLARE_FINAL_TYPE (PangoHbFace, pango_hb_face, PANGO, HB_FACE, PangoFontFace)
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_from_file     (const char             *file,
+                                                 unsigned int            index);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_from_bytes    (GBytes                 *bytes,
+                                                 unsigned int            index);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_transformed   (PangoHbFace            *face,
+                                                 const char             *name,
+                                                 const PangoMatrix      *transform);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_alias         (PangoHbFace            *face,
+                                                 const char             *fullname);
+
+G_END_DECLS
diff --git a/pango/pango-hbfont-private.h b/pango/pango-hbfont-private.h
new file mode 100644
index 00000000..11764242
--- /dev/null
+++ b/pango/pango-hbfont-private.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "pango-hbfont.h"
+#include "pango-hbface.h"
+#include <hb.h>
+
+typedef struct _HexBoxInfo HexBoxInfo;
+struct _HexBoxInfo
+{
+  PangoFont *font;
+  int rows;
+  double digit_width;
+  double digit_height;
+  double pad_x;
+  double pad_y;
+  double line_width;
+  double box_descent;
+  double box_height;
+};
+
+struct _PangoHbFont
+{
+  PangoFont parent_instance;
+
+  PangoHbFace *face;
+  int size; /* pixel size, scaled by PANGO_SCALE */
+  char *variations;
+  PangoGravity gravity;
+  PangoMatrix matrix;
+
+  HexBoxInfo *hex_box_info;
+};
diff --git a/pango/pango-hbfont.c b/pango/pango-hbfont.c
new file mode 100644
index 00000000..fdcc5468
--- /dev/null
+++ b/pango/pango-hbfont.c
@@ -0,0 +1,678 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "pango-hbfont-private.h"
+
+#include "pango-font-private.h"
+#include "pango-coverage-private.h"
+#include "pango-hbface-private.h"
+#include "pango-impl-utils.h"
+
+#include <hb-ot.h>
+
+/* {{{ PangoHbCoverage */
+
+/* Implement PangoCoverage as wrapper around hb_font_get_nominal_glyph() */
+
+typedef struct
+{
+  PangoCoverage parent_instance;
+  hb_font_t *hb_font;
+} PangoHbCoverage;
+
+typedef struct
+{
+  PangoCoverageClass parent_class;
+} PangoHbCoverageClass;
+
+GType pango_hb_coverage_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (PangoHbCoverage, pango_hb_coverage, PANGO_TYPE_COVERAGE)
+
+static void
+pango_hb_coverage_init (PangoHbCoverage *self)
+{
+}
+
+static PangoCoverageLevel
+pango_hb_coverage_get (PangoCoverage *coverage,
+                       int            index)
+{
+  PangoHbCoverage *self = (PangoHbCoverage*)coverage;
+  hb_codepoint_t glyph;
+
+  return hb_font_get_nominal_glyph (self->hb_font, index, &glyph)
+         ? PANGO_COVERAGE_EXACT
+         : PANGO_COVERAGE_NONE;
+}
+
+static void
+pango_hb_coverage_set (PangoCoverage      *coverage,
+                       int                 index,
+                       PangoCoverageLevel  level)
+{
+}
+
+static PangoCoverage *
+pango_hb_coverage_copy (PangoCoverage *coverage)
+{
+  PangoHbCoverage *self = (PangoHbCoverage*)coverage;
+  PangoHbCoverage *copy;
+
+  copy = g_object_new (pango_hb_coverage_get_type (), NULL);
+  copy->hb_font = hb_font_reference (self->hb_font);
+
+  return (PangoCoverage *)copy;
+}
+
+static void
+pango_hb_coverage_finalize (GObject *object)
+{
+  PangoHbCoverage *self = (PangoHbCoverage*)object;
+
+  hb_font_destroy (self->hb_font);
+
+  G_OBJECT_CLASS (pango_hb_coverage_parent_class)->finalize (object);
+}
+
+static void
+pango_hb_coverage_class_init (PangoHbCoverageClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PangoCoverageClass *coverage_class = PANGO_COVERAGE_CLASS (class);
+
+  object_class->finalize = pango_hb_coverage_finalize;
+
+  coverage_class->get = pango_hb_coverage_get;
+  coverage_class->set = pango_hb_coverage_set;
+  coverage_class->copy = pango_hb_coverage_copy;
+}
+
+/* }}} */
+/* {{{ Utilities */
+static int
+get_average_char_width (PangoFont  *font,
+                        const char *text)
+{
+  hb_font_t *hb_font = pango_font_get_hb_font (font);
+  int width = 0;
+
+  for (const char *p = text; *p; p = g_utf8_next_char (p))
+    {
+      gunichar wc;
+      hb_codepoint_t glyph;
+      PangoRectangle extents;
+
+      wc = g_utf8_get_char (p);
+      if (!hb_font_get_nominal_glyph (hb_font, wc, &glyph))
+        continue;
+
+      pango_font_get_glyph_extents (font, glyph, &extents, NULL);
+
+      width += extents.x + extents.width;
+    }
+
+  return width / pango_utf8_strwidth (text);
+}
+
+static void
+get_max_char_size (PangoFont  *font,
+                   const char *text,
+                   int        *width,
+                   int        *height)
+{
+  hb_font_t *hb_font = pango_font_get_hb_font (font);
+  int w = 0;
+  int h = 0;
+
+  for (const char *p = text; *p; p = g_utf8_next_char (p))
+    {
+      gunichar wc;
+      hb_codepoint_t glyph;
+      PangoRectangle extents;
+
+      wc = g_utf8_get_char (p);
+      if (!hb_font_get_nominal_glyph (hb_font, wc, &glyph))
+        continue;
+
+      pango_font_get_glyph_extents (font, glyph, &extents, NULL);
+
+      w = MAX (w, extents.x + extents.width);
+      h = MAX (h, extents.height);
+    }
+
+  if (width)
+    *width = w;
+
+  if (height)
+    *height = h;
+}
+
+static void
+parse_variations (const char            *variations,
+                  hb_ot_var_axis_info_t *axes,
+                  int                    n_axes,
+                  float                 *coords)
+{
+  const char *p;
+  const char *end;
+  hb_variation_t var;
+  int i;
+
+  p = variations;
+  while (p && *p)
+    {
+      end = strchr (p, ',');
+      if (hb_variation_from_string (p, end ? end - p: -1, &var))
+        {
+          for (i = 0; i < n_axes; i++)
+            {
+              if (axes[i].tag == var.tag)
+                {
+                  coords[axes[i].axis_index] = var.value;
+                  break;
+                }
+            }
+        }
+
+      p = end ? end + 1 : NULL;
+    }
+}
+/* }}} */
+/* {{{ hex box sizing */
+
+/* This code needs to stay in sync with the hexbox rendering
+ * code in pangocairo-render.c.
+ */
+static HexBoxInfo *
+create_hex_box_info (PangoHbFont *self)
+{
+  const char hexdigits[] = "0123456789ABCDEF";
+  hb_font_t *hb_font;
+  PangoFont *mini_font;
+  HexBoxInfo *hbi;
+  int rows;
+  double pad;
+  int width = 0;
+  int height = 0;
+  hb_font_extents_t font_extents;
+  double font_ascent, font_descent;
+  double mini_size;
+  PangoFontDescription *desc;
+  PangoContext *context;
+
+  if (!self->face->map)
+    return NULL;
+
+  desc = pango_font_describe_with_absolute_size (PANGO_FONT (self));
+  hb_font = pango_font_get_hb_font (PANGO_FONT (self));
+
+  /* Create mini_font description */
+
+  /* We inherit most font properties for the mini font.
+   * Just change family and size, so you get bold
+   * hex digits in the hexbox for a bold font.
+   */
+
+  /* We should rotate the box, not glyphs */
+  pango_font_description_unset_fields (desc, PANGO_FONT_MASK_GRAVITY);
+
+  pango_font_description_set_family_static (desc, "monospace");
+
+  rows = 2;
+  mini_size = self->size / 2.2;
+
+  if (mini_size < 6.0)
+    {
+      rows = 1;
+      mini_size = MIN (MAX (self->size - 1, 0), 6.0);
+    }
+
+  pango_font_description_set_absolute_size (desc, mini_size);
+
+  /* Load mini_font */
+  context = pango_font_map_create_context (self->face->map);
+  pango_context_set_matrix (context, &self->matrix);
+  pango_context_set_language (context, pango_script_get_sample_language (PANGO_SCRIPT_LATIN));
+
+  mini_font = pango_font_map_load_font (self->face->map, context, desc);
+
+  g_object_unref (context);
+
+  pango_font_description_free (desc);
+
+  get_max_char_size (mini_font, hexdigits, &width, &height);
+
+  hb_font_get_extents_for_direction (hb_font, HB_DIRECTION_LTR, &font_extents);
+  font_ascent = font_extents.ascender / (double) PANGO_SCALE;
+  font_descent = - font_extents.descender / (double) PANGO_SCALE;
+
+  pad = (font_ascent + font_descent) / 43.;
+  pad = MIN (pad, mini_size / (double) PANGO_SCALE);
+
+  hbi = g_new (HexBoxInfo, 1);
+  hbi->font = mini_font;
+  hbi->rows = rows;
+
+  hbi->digit_width  = width / (double) PANGO_SCALE;
+  hbi->digit_height = height / (double) PANGO_SCALE;
+
+  hbi->pad_x = pad;
+  hbi->pad_y = pad;
+
+  hbi->line_width = MIN (hbi->pad_x, hbi->pad_y);
+
+  hbi->box_height = 3 * hbi->pad_y + rows * (hbi->pad_y + hbi->digit_height);
+
+  if (rows == 1 || hbi->box_height <= font_ascent)
+    hbi->box_descent = 2 * hbi->pad_y;
+  else if (hbi->box_height <= font_ascent + font_descent - 2 * hbi->pad_y)
+    hbi->box_descent = 2 * hbi->pad_y + hbi->box_height - font_ascent;
+  else
+    hbi->box_descent = font_descent * hbi->box_height / (font_ascent + font_descent);
+
+  return hbi;
+}
+
+static void
+get_space_extents (PangoHbFont    *self,
+                   PangoRectangle *ink_rect,
+                   PangoRectangle *logical_rect)
+{
+  hb_font_t *hb_font = pango_font_get_hb_font (PANGO_FONT (self));
+  const char hexdigits[] = "0123456789ABCDEF";
+  int width;
+  hb_direction_t direction;
+  hb_font_extents_t font_extents;
+
+  direction = PANGO_GRAVITY_IS_VERTICAL (self->gravity)
+              ? HB_DIRECTION_TTB
+              : HB_DIRECTION_LTR;
+
+  hb_font_get_extents_for_direction (hb_font, direction, &font_extents);
+
+  /* We don't render missing spaces as hex boxes, so come up with some
+   * width to use. For lack of anything better, use average hex digit width.
+   */
+  width = get_average_char_width (PANGO_FONT (self), hexdigits);
+
+  if (ink_rect)
+    {
+      ink_rect->x = ink_rect->y = ink_rect->height = 0;
+      ink_rect->width = width;
+    }
+
+  if (logical_rect)
+    {
+      logical_rect->x = 0;
+      logical_rect->y = - font_extents.ascender;
+      logical_rect->height = font_extents.ascender - font_extents.descender;
+      logical_rect->width = width;
+    }
+}
+
+static void
+get_glyph_extents_missing (PangoHbFont    *self,
+                           PangoGlyph      glyph,
+                           PangoRectangle *ink_rect,
+                           PangoRectangle *logical_rect)
+{
+  gunichar ch;
+  int rows, cols;
+  HexBoxInfo *hbi;
+
+  ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
+
+  if (!self->hex_box_info)
+    self->hex_box_info = create_hex_box_info (self);
+
+  if (ch == 0x20 || ch == 0x2423)
+    {
+      get_space_extents (self, ink_rect, logical_rect);
+      return;
+    }
+
+  hbi = self->hex_box_info;
+
+  if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF))
+    {
+      rows = hbi->rows;
+      cols = 1;
+    }
+  else if (pango_get_ignorable_size (ch, &rows, &cols))
+    {
+      /* We special-case ignorables when rendering hex boxes */
+    }
+  else
+    {
+      rows = hbi->rows;
+      cols = (ch > 0xffff ? 6 : 4) / rows;
+    }
+
+  if (ink_rect)
+    {
+      ink_rect->x = PANGO_SCALE * hbi->pad_x;
+      ink_rect->y = PANGO_SCALE * (hbi->box_descent - hbi->box_height);
+      ink_rect->width = PANGO_SCALE * (3 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
+      ink_rect->height = PANGO_SCALE * hbi->box_height;
+    }
+
+  if (logical_rect)
+    {
+      logical_rect->x = 0;
+      logical_rect->y = PANGO_SCALE * (hbi->box_descent - (hbi->box_height + hbi->pad_y));
+      logical_rect->width = PANGO_SCALE * (5 * hbi->pad_x + cols * (hbi->digit_width + hbi->pad_x));
+      logical_rect->height = PANGO_SCALE * (hbi->box_height + 2 * hbi->pad_y);
+    }
+}
+
+/* }}} */
+/* {{{ PangoFont implementation */
+
+struct _PangoHbFontClass
+{
+  PangoFontClass parent_class;
+};
+
+G_DEFINE_TYPE (PangoHbFont, pango_hb_font, PANGO_TYPE_FONT)
+
+static void
+pango_hb_font_init (PangoHbFont *self)
+{
+  self->gravity = PANGO_GRAVITY_SOUTH;
+  self->matrix = (PangoMatrix) PANGO_MATRIX_INIT;
+}
+
+static void
+hex_box_info_destroy (HexBoxInfo *hex_box_info)
+{
+  g_object_unref (hex_box_info->font);
+  g_free (hex_box_info);
+}
+
+static void
+pango_hb_font_finalize (GObject *object)
+{
+  PangoHbFont *self = PANGO_HB_FONT (object);
+
+  g_object_unref (self->face);
+  g_free (self->variations);
+  g_clear_pointer (&self->hex_box_info, hex_box_info_destroy);
+
+  G_OBJECT_CLASS (pango_hb_font_parent_class)->finalize (object);
+}
+
+static PangoFontDescription *
+pango_hb_font_describe (PangoFont *font)
+{
+  PangoHbFont *self = PANGO_HB_FONT (font);
+  PangoFontDescription *desc;
+
+  desc = pango_font_face_describe (PANGO_FONT_FACE (self->face));
+  pango_font_description_set_absolute_size (desc, self->size);
+
+  return desc;
+}
+
+static PangoCoverage *
+pango_hb_font_get_coverage (PangoFont     *font,
+                            PangoLanguage *language G_GNUC_UNUSED)
+{
+  hb_font_t *hb_font = pango_font_get_hb_font (font);
+  PangoHbCoverage *coverage;
+
+  coverage = g_object_new (pango_hb_coverage_get_type (), NULL);
+  coverage->hb_font = hb_font_reference (hb_font);
+
+  return (PangoCoverage *)coverage;
+}
+
+static void
+pango_hb_font_get_glyph_extents (PangoFont      *font,
+                                 PangoGlyph      glyph,
+                                 PangoRectangle *ink_rect,
+                                 PangoRectangle *logical_rect)
+{
+  PangoHbFont *self = PANGO_HB_FONT (font);
+  hb_font_t *hb_font = pango_font_get_hb_font (font);
+  hb_glyph_extents_t extents;
+  hb_direction_t direction;
+  hb_font_extents_t font_extents;
+
+  direction = PANGO_GRAVITY_IS_VERTICAL (self->gravity)
+              ? HB_DIRECTION_TTB
+              : HB_DIRECTION_LTR;
+
+  hb_font_get_extents_for_direction (hb_font, direction, &font_extents);
+
+  if (glyph == PANGO_GLYPH_EMPTY)
+    {
+      if (ink_rect)
+        ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0;
+
+      if (logical_rect)
+        {
+          logical_rect->x = logical_rect->width = 0;
+          logical_rect->y = - font_extents.ascender;
+          logical_rect->height = font_extents.ascender - font_extents.descender;
+        }
+
+      return;
+    }
+  else if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
+    {
+      get_glyph_extents_missing (self, glyph, ink_rect, logical_rect);
+
+      return;
+    }
+
+  hb_font_get_glyph_extents (hb_font, glyph, &extents);
+
+  if (ink_rect)
+    {
+      ink_rect->x = extents.x_bearing;
+      ink_rect->width = extents.width;
+      ink_rect->y = - extents.y_bearing;
+      ink_rect->height = - extents.height;
+
+      pango_matrix_transform_rectangle (&self->face->matrix, ink_rect);
+    }
+
+  if (logical_rect)
+    {
+      hb_position_t x, y;
+
+      hb_font_get_glyph_advance_for_direction (hb_font, glyph, direction, &x, &y);
+
+      logical_rect->x = 0;
+      logical_rect->width = x;
+      logical_rect->y = - font_extents.ascender;
+      logical_rect->height = font_extents.ascender - font_extents.descender;
+    }
+}
+
+static PangoFontMetrics *
+pango_hb_font_get_metrics (PangoFont     *font,
+                           PangoLanguage *language)
+{
+  hb_font_t *hb_font = pango_font_get_hb_font (font);
+  PangoFontMetrics *metrics;
+  hb_font_extents_t extents;
+  hb_position_t position;
+
+  metrics = pango_font_metrics_new ();
+
+  hb_font_get_extents_for_direction (hb_font, HB_DIRECTION_LTR, &extents);
+
+  metrics->descent = - extents.descender;
+  metrics->ascent = extents.ascender;
+  metrics->height = extents.ascender - extents.descender + extents.line_gap;
+
+  metrics->underline_thickness = PANGO_SCALE;
+  metrics->underline_position = - PANGO_SCALE;
+  metrics->strikethrough_thickness = PANGO_SCALE;
+  metrics->strikethrough_position = metrics->ascent / 2;
+
+  if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_UNDERLINE_SIZE, &position))
+    metrics->underline_thickness = position;
+
+  if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_UNDERLINE_OFFSET, &position))
+    metrics->underline_position = position;
+
+  if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_STRIKEOUT_SIZE, &position))
+    metrics->strikethrough_thickness = position;
+
+  if (hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, &position))
+    metrics->strikethrough_position = position;
+
+  metrics->approximate_char_width = get_average_char_width (font, pango_language_get_sample_string 
(language));
+  get_max_char_size (font, "0123456789", &metrics->approximate_digit_width, NULL);
+
+  return metrics;
+}
+
+static PangoFontMap *
+pango_hb_font_get_font_map (PangoFont *font)
+{
+  PangoHbFont *self = PANGO_HB_FONT (font);
+
+  return self->face->map;
+}
+
+static hb_font_t *
+pango_hb_font_create_hb_font (PangoFont *font)
+{
+  PangoHbFont *self = PANGO_HB_FONT (font);
+  hb_face_t *hb_face = self->face->face;
+  hb_font_t *hb_font;
+  unsigned int n_axes;
+  hb_ot_var_axis_info_t axes[32];
+  float coords[32];
+
+  hb_font = hb_font_create (hb_face);
+
+  hb_font_set_scale (hb_font,
+                     (double)self->size,
+                     (double)self->size);
+
+  n_axes = G_N_ELEMENTS (axes);
+  hb_ot_var_get_axis_infos (hb_face, 0, &n_axes, axes);
+  if (n_axes > 0 && self->variations)
+    {
+      for (int i = 0; i < n_axes; i++)
+        coords[axes[i].axis_index] = axes[i].default_value;
+
+      parse_variations (self->variations, axes, n_axes, coords);
+      hb_font_set_var_coords_design (hb_font, coords, n_axes);
+    }
+
+  return hb_font;
+}
+
+static PangoLanguage **
+pango_hb_font_get_languages (PangoFont *font)
+{
+  // TODO Use HB_OT_META_TAG_SUPPORTED_LANGUAGES
+  return NULL;
+}
+
+static gboolean
+pango_hb_font_has_char (PangoFont *font,
+                        gunichar   wc)
+{
+  hb_font_t *hb_font = pango_font_get_hb_font (font);
+  hb_codepoint_t glyph;
+
+  return hb_font_get_nominal_glyph (hb_font, wc, &glyph);
+}
+
+static PangoFontFace *
+pango_hb_font_get_face (PangoFont *font)
+{
+  PangoHbFont *self = PANGO_HB_FONT (font);
+
+  return PANGO_FONT_FACE (self->face);
+}
+
+static void
+pango_hb_font_class_init (PangoHbFontClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PangoFontClass *font_class = PANGO_FONT_CLASS (class);
+  PangoFontClassPrivate *pclass;
+
+  object_class->finalize = pango_hb_font_finalize;
+
+  font_class->describe = pango_hb_font_describe;
+  font_class->describe_absolute = pango_hb_font_describe;
+  font_class->get_coverage = pango_hb_font_get_coverage;
+  font_class->get_glyph_extents = pango_hb_font_get_glyph_extents;
+  font_class->get_metrics = pango_hb_font_get_metrics;
+  font_class->get_font_map = pango_hb_font_get_font_map;
+  font_class->create_hb_font = pango_hb_font_create_hb_font;
+
+  pclass = g_type_class_get_private ((GTypeClass *) class, PANGO_TYPE_FONT);
+
+  pclass->get_languages = pango_hb_font_get_languages;
+  pclass->has_char = pango_hb_font_has_char;
+  pclass->get_face = pango_hb_font_get_face;
+}
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_hb_font_new:
+ * @face: the `PangoHbFace` to use
+ * @size: the desired size in pixels, scaled by `PANGO_SCALE`
+ * @variations: (nullable): font variations to apply
+ * @gravity: the gravity to use when rendering
+ * @matrix: (nullable): transformation matrix to use when rendering
+ *
+ * Creates a new `PangoHbFont`.
+ *
+ * Returns: a newly created `PangoHbFont`
+ */
+PangoHbFont *
+pango_hb_font_new (PangoHbFace       *face,
+                   int                size,
+                   const char        *variations,
+                   PangoGravity       gravity,
+                   const PangoMatrix *matrix)
+{
+  PangoHbFont *self;
+
+  self = g_object_new (PANGO_TYPE_HB_FONT, NULL);
+
+  self->face = g_object_ref (face);
+
+  self->size = size;
+  self->variations = g_strdup (variations);
+  self->gravity = gravity;
+  if (matrix)
+    self->matrix = *matrix;
+
+  return self;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-hbfont.h b/pango/pango-hbfont.h
new file mode 100644
index 00000000..102e6533
--- /dev/null
+++ b/pango/pango-hbfont.h
@@ -0,0 +1,42 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <pango/pango-font.h>
+#include <pango/pango-hbface.h>
+
+#include <hb.h>
+
+G_BEGIN_DECLS
+
+#define PANGO_TYPE_HB_FONT      (pango_hb_font_get_type ())
+
+PANGO_AVAILABLE_IN_1_50
+G_DECLARE_FINAL_TYPE (PangoHbFont, pango_hb_font, PANGO, HB_FONT, PangoFont)
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFont *           pango_hb_font_new               (PangoHbFace                 *face,
+                                                         int                          size,
+                                                         const char                  *variations,
+                                                         PangoGravity                 gravity,
+                                                         const PangoMatrix           *matrix);
+
+G_END_DECLS
diff --git a/pango/pango-simplefontmap.c b/pango/pango-simplefontmap.c
new file mode 100644
index 00000000..20f81b85
--- /dev/null
+++ b/pango/pango-simplefontmap.c
@@ -0,0 +1,445 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * 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 <math.h>
+
+#include <gio/gio.h>
+
+#include "pango-simplefontmap.h"
+#include "pango-hbface-private.h"
+#include "pango-hbfont-private.h"
+#include "pango-context.h"
+#include "pango-impl-utils.h"
+
+#include <hb-ot.h>
+
+struct _PangoSimpleFontMap
+{
+  PangoFontMap parent_instance;
+
+  GPtrArray *families;
+  GPtrArray *faces;
+
+  double dpi;
+};
+
+struct _PangoSimpleFontMapClass
+{
+  PangoFontMapClass parent_class;
+};
+
+G_DEFINE_TYPE (PangoSimpleFontMap, pango_simple_font_map, PANGO_TYPE_FONT_MAP)
+
+/* {{{ PangoSimpleFamily */
+
+#define PANGO_TYPE_SIMPLE_FAMILY      (pango_simple_family_get_type ())
+G_DECLARE_FINAL_TYPE (PangoSimpleFamily, pango_simple_family, PANGO, SIMPLE_FAMILY, PangoFontFamily)
+
+struct _PangoSimpleFamily
+{
+  PangoFontFamily parent_instance;
+
+  PangoSimpleFontMap *map;
+  char *name;
+  gboolean monospace;
+  gboolean variable;
+};
+
+struct _PangoSimpleFamilyClass
+{
+  PangoFontFamilyClass parent_class;
+};
+
+G_DEFINE_TYPE (PangoSimpleFamily, pango_simple_family, PANGO_TYPE_FONT_FAMILY)
+
+static void
+pango_simple_family_init (PangoSimpleFamily *self)
+{
+}
+
+static void
+pango_simple_family_finalize (GObject *object)
+{
+  PangoSimpleFamily *self = PANGO_SIMPLE_FAMILY (object);
+
+  g_free (self->name);
+  if (self->map)
+    g_object_remove_weak_pointer (G_OBJECT (self->map), (gpointer *)&self->map);
+
+  G_OBJECT_CLASS (pango_simple_family_parent_class)->finalize (object);
+}
+
+static const char *
+pango_simple_family_get_name (PangoFontFamily *family)
+{
+  PangoSimpleFamily *self = PANGO_SIMPLE_FAMILY (family);
+
+  return self->name;
+}
+
+static void
+pango_simple_family_list_faces (PangoFontFamily  *family,
+                                PangoFontFace  ***faces,
+                                int              *n_faces)
+{
+  PangoSimpleFamily *self = PANGO_SIMPLE_FAMILY (family);
+  GPtrArray *result;
+
+  if (!self->map)
+    {
+      if (faces)
+        *faces = NULL;
+      if (n_faces)
+        *n_faces = 0;
+      return;
+    }
+
+  result = g_ptr_array_new ();
+  for (int i = 0; i < self->map->faces->len; i++)
+    {
+      PangoHbFace *face = g_ptr_array_index (self->map->faces, i);
+
+      if (strcmp (self->name, face->family) == 0)
+        g_ptr_array_add (result, face);
+    }
+
+  if (n_faces)
+    *n_faces = result->len;
+
+  if (faces)
+    *faces = (PangoFontFace **) g_ptr_array_free (result, FALSE);
+}
+
+static PangoFontFace *
+pango_simple_family_get_face (PangoFontFamily *family,
+                               const char      *name)
+{
+  PangoSimpleFamily *self = PANGO_SIMPLE_FAMILY (family);
+
+  if (!self->map)
+    return NULL;
+
+  for (int i = 0; i < self->map->faces->len; i++)
+    {
+      PangoHbFace *face = g_ptr_array_index (self->map->faces, i);
+
+      if (strcmp (self->name, face->family) == 0 &&
+          strcmp (name, face->name) == 0)
+        return PANGO_FONT_FACE (face);
+    }
+
+  return NULL;
+}
+
+static gboolean
+pango_simple_family_is_monospace (PangoFontFamily *family)
+{
+  PangoSimpleFamily *self = PANGO_SIMPLE_FAMILY (family);
+
+  return self->monospace;
+}
+
+static gboolean
+pango_simple_family_is_variable (PangoFontFamily *family)
+{
+  PangoSimpleFamily *self = PANGO_SIMPLE_FAMILY (family);
+
+  return self->variable;
+}
+
+static void
+pango_simple_family_class_init (PangoSimpleFamilyClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PangoFontFamilyClass *family_class = PANGO_FONT_FAMILY_CLASS (class);
+
+  object_class->finalize = pango_simple_family_finalize;
+
+  family_class->list_faces = pango_simple_family_list_faces;
+  family_class->get_name = pango_simple_family_get_name;
+  family_class->get_face = pango_simple_family_get_face;
+  family_class->is_monospace = pango_simple_family_is_monospace;
+  family_class->is_variable = pango_simple_family_is_variable;
+}
+
+static PangoSimpleFamily *
+pango_simple_family_new (const char         *name,
+                         PangoSimpleFontMap *map,
+                         gboolean            monospace,
+                         gboolean            variable)
+{
+  PangoSimpleFamily *self;
+
+  self = g_object_new (PANGO_TYPE_SIMPLE_FAMILY, NULL);
+
+  self->name = g_strdup (name);
+  self->map = map;
+  g_object_add_weak_pointer (G_OBJECT (map), (gpointer *)&self->map);
+
+  self->monospace = monospace;
+  self->variable = variable;
+
+  return self;
+}
+
+/* }}} */
+/* {{{ PangoFontMap implementation */
+
+static void
+pango_simple_font_map_init (PangoSimpleFontMap *self)
+{
+  self->families = g_ptr_array_new_with_free_func (g_object_unref);
+  self->faces = g_ptr_array_new_with_free_func (g_object_unref);
+
+  self->dpi = 96.;
+}
+
+static void
+pango_simple_font_map_finalize (GObject *object)
+{
+  PangoSimpleFontMap *self = PANGO_SIMPLE_FONT_MAP (object);
+
+  g_ptr_array_free (self->families, TRUE);
+  g_ptr_array_free (self->faces, TRUE);
+
+  G_OBJECT_CLASS (pango_simple_font_map_parent_class)->finalize (object);
+}
+
+static PangoFont *
+pango_simple_font_map_load_font (PangoFontMap               *map,
+                                 PangoContext               *context,
+                                 const PangoFontDescription *desc)
+{
+  PangoSimpleFontMap *self = PANGO_SIMPLE_FONT_MAP (map);
+  PangoFontDescription *best = NULL;
+  PangoHbFace *face = NULL;
+  int size;
+  const char *variations;
+  PangoGravity gravity;
+  const PangoMatrix *matrix;
+  PangoFontDescription *copy = pango_font_description_copy_static (desc);
+  const char *family = pango_font_description_get_family (desc);
+
+  pango_font_description_unset_fields (copy, PANGO_FONT_MASK_SIZE |
+                                             PANGO_FONT_MASK_VARIATIONS |
+                                             PANGO_FONT_MASK_GRAVITY);
+
+  for (int pass = 0; pass <= 1; pass++)
+    {
+      for (int i = 0; i < self->faces->len; i++)
+        {
+          PangoHbFace *face2 = g_ptr_array_index (self->faces, i);
+
+          if ((pass > 0 || g_ascii_strcasecmp (face2->family, family) == 0) &&
+              pango_font_description_better_match (copy, best, face2->description))
+            {
+              face = face2;
+              best = face2->description;
+            }
+        }
+    }
+
+  pango_font_description_free (copy);
+
+  if (face == NULL)
+    {
+      char *s = pango_font_description_to_string (desc);
+      g_warning ("No match for pattern '%s', falling back to default face\n", s);
+      g_free (s);
+      face = g_ptr_array_index (self->faces, 0);
+    }
+
+  if (pango_font_description_get_size_is_absolute (desc))
+    size = pango_font_description_get_size (desc);
+  else
+    size = pango_font_description_get_size (desc) * self->dpi / 72.;
+
+  variations = pango_font_description_get_variations (desc);
+
+  gravity = pango_context_get_gravity (context);
+  matrix = pango_context_get_matrix (context);
+
+  return PANGO_FONT (pango_hb_font_new (face, size, variations, gravity, matrix));
+}
+
+static PangoFontset *
+pango_simple_font_map_load_fontset (PangoFontMap               *map,
+                                    PangoContext               *context,
+                                    const PangoFontDescription *description,
+                                    PangoLanguage              *language)
+{
+  PangoFontsetSimple *fontset;
+
+  // TODO: Do the FcFontSort thing
+  fontset = pango_fontset_simple_new (language);
+  pango_fontset_simple_append (fontset, pango_font_map_load_font (map, context, description));
+
+  return PANGO_FONTSET (fontset);
+}
+
+static void
+pango_simple_font_map_list_families (PangoFontMap      *map,
+                                     PangoFontFamily ***families,
+                                     int               *n_families)
+{
+  PangoSimpleFontMap *self = PANGO_SIMPLE_FONT_MAP (map);
+
+  if (self->families->len == 0)
+    {
+      if (families)
+        *families = NULL;
+      if (n_families)
+        *n_families = 0;
+
+      return;
+    }
+
+  if (n_families)
+    *n_families = self->families->len;
+
+  if (families)
+    *families = g_memdup2 (self->families->pdata, self->families->len * sizeof (PangoFontFamily *));
+}
+
+static PangoFontFamily *
+pango_simple_font_map_get_family (PangoFontMap *map,
+                                  const char   *name)
+{
+  PangoSimpleFontMap *self = PANGO_SIMPLE_FONT_MAP (map);
+
+  for (int i = 0; i < self->families->len; i++)
+    {
+      PangoSimpleFamily *family = g_ptr_array_index (self->families, i);
+
+      if (strcmp (name, family->name) == 0)
+        return PANGO_FONT_FAMILY (family);
+    }
+
+  return NULL;
+}
+
+static void
+pango_simple_font_map_class_init (PangoSimpleFontMapClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PangoFontMapClass *map_class = PANGO_FONT_MAP_CLASS (class);
+
+  object_class->finalize = pango_simple_font_map_finalize;
+
+  map_class->load_font = pango_simple_font_map_load_font;
+  map_class->load_fontset = pango_simple_font_map_load_fontset;
+  map_class->list_families = pango_simple_font_map_list_families;
+  map_class->get_family = pango_simple_font_map_get_family;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_simple_font_map_new:
+ *
+ * Creates a new `PangoSimpleFontmMap`.
+ *
+ * Returns: A newly created `PangoSimpleFontMap
+ */
+PangoSimpleFontMap *
+pango_simple_font_map_new (void)
+{
+  return g_object_new (PANGO_TYPE_SIMPLE_FONT_MAP, NULL);
+}
+
+/**
+ * pango_simple_font_map_add_face:
+ * @self: a `PangoSimpleFontMap`
+ * @face: a `PangoHbFace`
+ *
+ * Adds @face to the `PangoSimpleFontMap`.
+ *
+ * This is most useful for creating transformed
+ * faces or aliases. See [method@Pango.HbFace.new_transformed]
+ * or [method@Pango.HbFace.new_alias].
+ */
+void
+pango_simple_font_map_add_face (PangoSimpleFontMap *self,
+                                PangoHbFace        *face)
+{
+  PangoFontMap *map = PANGO_FONT_MAP (self);
+
+  pango_hb_face_set_font_map (face, map);
+
+  if (!pango_simple_font_map_get_family (map, face->family))
+    {
+      PangoSimpleFamily *family;
+
+      family = pango_simple_family_new (face->family,
+                                        self,
+                                        pango_hb_face_is_monospace (face),
+                                        pango_hb_face_is_variable (face));
+
+      g_ptr_array_add (self->families, family);
+    }
+
+  g_ptr_array_add (self->faces, g_object_ref (face));
+}
+
+/**
+ * pango_simple_font_map_add_file:
+ * @file: font filename
+ * @index: face index
+ *
+ * Creates a new `PangoHbFace` and adds it.
+ */
+void
+pango_simple_font_map_add_file (PangoSimpleFontMap *self,
+                                const char         *file,
+                                unsigned int        index)
+{
+  PangoHbFace *face;
+
+  face = pango_hb_face_new_from_file (file, index);
+  pango_simple_font_map_add_face (self, face);
+  g_object_unref (face);
+}
+
+/**
+ * pango_simple_font_map_set_resolution:
+ * @self: a `PangoSimpleFontMap`
+ * @dpi: the new resolution, in "dots per inch"
+ *
+ * Sets the resolution for the fontmap.
+ *
+ * This is a scale factor between points specified in a
+ * `PangoFontDescription` and Cairo units. The default value
+ * is 96, meaning that a 10 point font will be 13 units high.
+ * (10 * 96. / 72. = 13.3).
+ */
+void
+pango_simple_font_map_set_resolution (PangoSimpleFontMap *self,
+                                      double              dpi)
+{
+  self->dpi = dpi;
+
+  pango_font_map_changed (PANGO_FONT_MAP (self));
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-simplefontmap.h b/pango/pango-simplefontmap.h
new file mode 100644
index 00000000..e7f18307
--- /dev/null
+++ b/pango/pango-simplefontmap.h
@@ -0,0 +1,51 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <pango-font.h>
+#include <pango-fontmap.h>
+#include <pango-hbface.h>
+#include <hb.h>
+
+G_BEGIN_DECLS
+
+#define PANGO_TYPE_SIMPLE_FONT_MAP   (pango_simple_font_map_get_type ())
+
+PANGO_AVAILABLE_IN_1_50
+G_DECLARE_FINAL_TYPE (PangoSimpleFontMap, pango_simple_font_map, PANGO, SIMPLE_FONT_MAP, PangoFontMap)
+
+PANGO_AVAILABLE_IN_1_50
+PangoSimpleFontMap *    pango_simple_font_map_new       (void);
+
+PANGO_AVAILABLE_IN_1_50
+void                    pango_simple_font_map_add_file  (PangoSimpleFontMap   *self,
+                                                         const char           *file,
+                                                         unsigned int          index);
+
+PANGO_AVAILABLE_IN_1_50
+void                    pango_simple_font_map_add_face  (PangoSimpleFontMap   *self,
+                                                         PangoHbFace          *face);
+
+PANGO_AVAILABLE_IN_1_50
+void                    pango_simple_font_map_set_resolution (PangoSimpleFontMap *self,
+                                                              double              dpi);
+
+G_END_DECLS
diff --git a/pango/pango.h b/pango/pango.h
index 8dc86b13..cf594e55 100644
--- a/pango/pango.h
+++ b/pango/pango.h
@@ -45,6 +45,7 @@
 #include <pango/pango-markup.h>
 #include <pango/pango-renderer.h>
 #include <pango/pango-script.h>
+#include <pango/pango-simplefontmap.h>
 #include <pango/pango-tabs.h>
 #include <pango/pango-types.h>
 #include <pango/pango-utils.h>


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