[pango/simple-fontmap: 4/16] Add PangoHbFace




commit 7c6974f510bb70f4a6f2d3661a83e4b5db076971
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Oct 29 19:18:27 2021 -0400

    Add PangoHbFace
    
    PangoHbFace is a simple wrapper around hb_face_t.
    
    This is an attempt to make a font backend that does
    not depend on platform specifics and can be used in
    tests.

 pango/meson.build            |   2 +
 pango/pango-hbface-private.h |  39 +++
 pango/pango-hbface.c         | 595 +++++++++++++++++++++++++++++++++++++++++++
 pango/pango-hbface.h         |  66 +++++
 pango/pango.h                |   1 +
 5 files changed, 703 insertions(+)
---
diff --git a/pango/meson.build b/pango/meson.build
index 4e6f746e..23a21613 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -30,6 +30,7 @@ pango_sources = [
   'serializer.c',
   'json/gtkjsonparser.c',
   'json/gtkjsonprinter.c',
+  'pango-hbface.c',
 ]
 
 pango_headers = [
@@ -59,6 +60,7 @@ pango_headers = [
   'pango-tabs.h',
   'pango-types.h',
   'pango-utils.h',
+  'pango-hbface.h',
 ]
 
 pango_installed_headers = pango_headers + [ 'pango-version-macros.h' ]
diff --git a/pango/pango-hbface-private.h b/pango/pango-hbface-private.h
new file mode 100644
index 00000000..9bd34eb5
--- /dev/null
+++ b/pango/pango-hbface-private.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "pango-hbface.h"
+#include "pango-fontmap.h"
+#include <hb.h>
+
+struct _PangoHbFace
+{
+  PangoFontFace parent_instance;
+
+  unsigned int index;
+  int instance_id;
+  char *file;
+  PangoHbFace *orig;
+
+  hb_face_t *face;
+  char *name;
+  PangoFontFamily *family;
+  PangoFontDescription *description;
+  PangoMatrix matrix;
+  double x_scale, y_scale;
+  PangoLanguage **languages;
+  gboolean embolden;
+  gboolean synthetic;
+};
+
+void            pango_hb_face_set_family        (PangoHbFace          *self,
+                                                 PangoFontFamily      *family);
+PANGO_AVAILABLE_IN_1_50
+hb_face_t *     pango_hb_face_get_hb_face       (PangoHbFace          *self);
+
+PangoLanguage **pango_hb_face_get_languages     (PangoHbFace          *self);
+PANGO_AVAILABLE_IN_1_50
+void            pango_hb_face_set_languages     (PangoHbFace          *self,
+                                                 PangoLanguage       **languages,
+                                                 gsize                 n_languages);
+PANGO_AVAILABLE_IN_1_50
+void            pango_hb_face_set_matrix        (PangoHbFace          *self,
+                                                 const PangoMatrix    *matrix);
diff --git a/pango/pango-hbface.c b/pango/pango-hbface.c
new file mode 100644
index 00000000..49a2ca2a
--- /dev/null
+++ b/pango/pango-hbface.c
@@ -0,0 +1,595 @@
+/* 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>
+
+/* {{{ 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)
+{
+  self->matrix = (PangoMatrix) PANGO_MATRIX_INIT;
+  self->x_scale = self->y_scale = 1.;
+}
+
+static void
+pango_hb_face_finalize (GObject *object)
+{
+  PangoHbFace *self = PANGO_HB_FACE (object);
+
+  hb_face_destroy (self->face);
+  pango_font_description_free (self->description);
+  g_free (self->name);
+  g_free (self->languages);
+  g_free (self->file);
+  if (self->orig)
+    g_object_unref (self->orig);
+
+  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);
+
+  return self->family;
+}
+
+static gboolean
+pango_hb_face_is_synthesized (PangoFontFace *face)
+{
+  PangoHbFace *self = PANGO_HB_FACE (face);
+
+  return self->synthetic;
+}
+
+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 = pango_hb_face_is_synthesized;
+  face_class->get_family = pango_hb_face_get_family;
+}
+
+/* }}} */
+/* {{{ Utilities */
+
+static char *
+strip_family_name (const char *fullname)
+{
+  PangoFontDescription *desc;
+  char *name;
+
+  desc = pango_font_description_from_string (fullname);
+
+  pango_font_description_unset_fields (desc, PANGO_FONT_MASK_FAMILY);
+
+  name = pango_font_description_to_string (desc);
+  if (strcmp (name, "Normal") == 0)
+    {
+      g_free (name);
+      name = g_strdup ("Regular");
+    }
+
+  pango_font_description_free (desc);
+
+  return name;
+}
+
+static char *
+get_name_from_hb_face (hb_face_t       *face,
+                       hb_ot_name_id_t  name_id,
+                       hb_ot_name_id_t  fallback_id)
+{
+  char buf[256];
+  unsigned int len;
+  hb_language_t language;
+
+  language = hb_language_from_string ("en", -1);
+
+  len = sizeof (buf);
+  if (hb_ot_name_get_utf8 (face, name_id, language, &len, buf) > 0)
+    return g_strdup (buf);
+
+  if (fallback_id != HB_OT_NAME_ID_INVALID)
+    {
+      len = sizeof (buf);
+      if (hb_ot_name_get_utf8 (face, fallback_id, language, &len, buf) > 0)
+        return g_strdup (buf);
+    }
+
+  return NULL;
+}
+
+static void
+ensure_hb_face (PangoHbFace *self)
+{
+  if (self->face)
+    return;
+
+  if (self->orig)
+    {
+      self->face = hb_face_reference (pango_hb_face_get_hb_face (self->orig));
+    }
+  else if (self->file)
+    {
+      hb_blob_t *blob = hb_blob_create_from_file (self->file);
+      self->face = hb_face_create (blob, self->index);
+      hb_blob_destroy (blob);
+      hb_face_make_immutable (self->face);
+    }
+  else
+    g_assert_not_reached ();
+}
+
+static void
+set_name_and_description (PangoHbFace          *self,
+                          int                   instance_id,
+                          const char           *name,
+                          PangoFontDescription *description)
+{
+  if (name)
+    {
+      self->name = g_strdup (name);
+    }
+  else
+    {
+      hb_ot_name_id_t name_id;
+
+      ensure_hb_face (self);
+
+      if (instance_id != -1)
+        name_id = hb_ot_var_named_instance_get_subfamily_name_id (self->face, instance_id);
+      else
+        name_id = HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
+
+      self->name = get_name_from_hb_face (self->face, name_id, HB_OT_NAME_ID_FONT_SUBFAMILY);
+    }
+
+  if (description)
+    {
+      self->description = pango_font_description_copy (description);
+    }
+  else
+    {
+      char *family;
+      char *fullname;
+
+      ensure_hb_face (self);
+
+      family = get_name_from_hb_face (self->face,
+                                      HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY,
+                                      HB_OT_NAME_ID_FONT_FAMILY);
+      fullname = g_strconcat (family, " ", self->name, NULL);
+
+      /* FIXME can we recover weight, width, slant, etc from the blob ? */
+      self->description = pango_font_description_from_string (fullname);
+
+      g_free (fullname);
+      g_free (family);
+    }
+}
+
+/* }}} */
+/* {{{ Private API */
+
+hb_face_t *
+pango_hb_face_get_hb_face (PangoHbFace *self)
+{
+  ensure_hb_face (self);
+
+  return self->face;
+}
+
+/*< private >
+ * pango_hb_face_set_family:
+ * @self: a `PangoHbFace`
+ * @family: a `PangoFontFamily`
+ *
+ * Sets the font family of a `PangoHbFace`.
+ *
+ * This should only be called by fontmap implementations.
+ */
+void
+pango_hb_face_set_family (PangoHbFace     *self,
+                          PangoFontFamily *family)
+{
+  g_return_if_fail (self->family == NULL);
+  self->family = family;
+}
+
+/*< private >
+ * pango_hb_face_get_languages:
+ * @face: a `PangoHbFace`
+ *
+ * Returns the languages supported by @face.
+ *
+ * Returns: (transfer one) (nullable): a NULL-terminated
+ *   array of `PangoLanguage *`
+ */
+PangoLanguage **
+pango_hb_face_get_languages (PangoHbFace *face)
+{
+  if (face->orig)
+    return pango_hb_face_get_languages (face->orig);
+
+  return face->languages;
+}
+
+/*< private >
+ * pango_hb_face_set_languages:
+ * @self: a `PangoHbFace`
+ * @languages: (array length=n_languages): the languages supported by @face
+ * @n_languages: the number of languages
+ *
+ * Sets the languages that are supported by @face.
+ *
+ * This should only be called by fontmap implementations.
+ */
+void
+pango_hb_face_set_languages (PangoHbFace    *self,
+                             PangoLanguage **languages,
+                             gsize           n_languages)
+{
+  // It would be nicer if HB_OT_META_TAG_SUPPORTED_LANGUAGES was useful
+  g_free (self->languages);
+  self->languages = g_new (PangoLanguage *, n_languages + 1);
+  memcpy (self->languages, languages, n_languages * sizeof (PangoLanguage *));
+  self->languages[n_languages] = NULL;
+}
+
+/*< private  >
+ * pango_hb_face_set_matrix:
+ * @self: a `PangoHbFace`
+ * @matrix: the `PangoMatrix`
+ *
+ * Sets the font matrix for @self.
+ *
+ * This should only be called by fontmap implementations.
+ */
+void
+pango_hb_face_set_matrix (PangoHbFace       *self,
+                          const PangoMatrix *matrix)
+{
+  if (self->orig)
+    self->matrix = self->orig->matrix;
+
+  pango_matrix_concat (&self->matrix, matrix);
+  pango_matrix_get_font_scale_factors (&self->matrix, &self->x_scale, &self->y_scale);
+  pango_matrix_scale (&self->matrix, 1./self->x_scale, 1./self->y_scale);
+}
+
+/* }}} */
+ /* {{{ Public API */
+
+/**
+ * pango_hb_face_new_from_bytes:
+ * @bytes: a `GBytes` containing font data
+ * @index: face index
+ * @instance_id: named instance id, or -1
+ * @name: (nullable): name for the face
+ * @description: (nullable): `PangoFontDescription` for the font
+ *
+ * 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,
+                              int                   instance_id,
+                              const char           *name,
+                              PangoFontDescription *description)
+{
+  PangoHbFace *self;
+  hb_blob_t *blob;
+
+  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
+
+  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);
+
+  self->face = hb_face_create (blob, index);
+
+  hb_blob_destroy (blob);
+
+  self->index = index;
+  self->instance_id = instance_id;
+
+  set_name_and_description (self, instance_id, name, description);
+
+  return self;
+}
+
+/**
+ * pango_hb_face_new_from_file:
+ * @file: font filename
+ * @index: face index
+ * @instance_id: named instance id, or -1
+ * @name: (nullable): name for the face
+ * @description: (nullable): `PangoFontDescription` for the font
+ *
+ * Creates a new `PangoHbFace` from a font file.
+ *
+ * If @desc and @name are provided, then the returned `PangoHbFace`
+ * object will be lazily initialized as needed.
+ *
+ * Returns: a newly created `PangoHbFace`
+ */
+PangoHbFace *
+pango_hb_face_new_from_file (const char           *file,
+                             unsigned int          index,
+                             int                   instance_id,
+                             const char           *name,
+                             PangoFontDescription *description)
+{
+  PangoHbFace *self;
+
+  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
+
+  self->file = g_strdup (file);
+  self->index = index;
+  self->instance_id = instance_id;
+
+  set_name_and_description (self, instance_id, name, description);
+
+  return self;
+}
+
+/**
+ * pango_hb_face_new_transformed:
+ * @face: a `PangoHbFace`
+ * @transform: the transform to apply
+ * @name: the name for the transformed face
+ *
+ * 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 PangoMatrix *transform,
+                               const char        *name)
+{
+  PangoHbFace *self;
+  const char *family_name;
+  char *fullname;
+  PangoFontDescription *desc;
+
+  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->orig = g_object_ref (face);
+
+  self->index = face->index;
+  self->instance_id = face->instance_id;
+
+  family_name = pango_font_description_get_family (face->description);
+  self->name = g_strdup (name);
+
+  fullname = g_strconcat (family_name, " ", self->name, NULL);
+  self->description = pango_font_description_copy (face->description);
+  desc = pango_font_description_from_string (fullname);
+  pango_font_description_merge (self->description, desc, TRUE);
+  pango_font_description_free (desc);
+  g_free (fullname);
+
+  pango_hb_face_set_matrix (self, transform);
+
+  self->synthetic = TRUE;
+
+  return self;
+}
+
+/**
+ * pango_hb_face_new_emboldened:
+ * @face: a `PangoHbFace`
+ *
+ * Creates a new `PangoHbFace` that renders the glyphs
+ * from @face bolder.
+ *
+ * Returns: (transfer full): a newly created `PangoHbFace`
+ */
+PangoHbFace *
+pango_hb_face_new_emboldened (PangoHbFace *face)
+{
+  PangoHbFace *self;
+  const char *family_name;
+  char *fullname;
+  PangoFontDescription *desc;
+
+  g_return_val_if_fail (PANGO_IS_HB_FACE (face), NULL);
+
+  self = g_object_new (PANGO_TYPE_HB_FACE, NULL);
+
+  self->orig = g_object_ref (face);
+
+  self->index = face->index;
+  self->instance_id = face->instance_id;
+  self->embolden = TRUE;
+
+  family_name = pango_font_description_get_family (face->description);
+  self->name = g_strdup ("Bold");
+
+  fullname = g_strconcat (family_name, " ", self->name, NULL);
+  self->description = pango_font_description_copy (face->description);
+  desc = pango_font_description_from_string (fullname);
+  pango_font_description_merge (self->description, desc, TRUE);
+  pango_font_description_free (desc);
+  g_free (fullname);
+
+  self->synthetic = TRUE;
+
+  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;
+  PangoFontDescription *desc;
+
+  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->orig = g_object_ref (face);
+
+  self->index = face->index;
+  self->instance_id = face->instance_id;
+
+  self->name = strip_family_name (fullname);
+
+  self->description = pango_font_description_copy (face->description);
+  desc = pango_font_description_from_string (fullname);
+  pango_font_description_merge (self->description, desc, TRUE);
+  pango_font_description_free (desc);
+
+  self->synthetic = TRUE;
+
+  return self;
+}
+
+typedef struct {
+  guint16 major;
+  guint16 minor;
+  gint32 italicAngle;
+  gint16 underlinePosition;
+  gint16 underlineThickness;
+  guint8 isFixedPitch[4];
+} PostTable;
+
+/**
+ * pango_hb_face_is_monospace:
+ * @self: a `PangoHbFace`
+ *
+ * Returns whether @self should be considered monospace.
+ *
+ * Returns: `TRUE` if @face is monospace
+ */
+gboolean
+pango_hb_face_is_monospace (PangoHbFace *self)
+{
+  hb_blob_t *post_blob;
+  guint8 *raw_post;
+  unsigned int post_len;
+  gboolean res = FALSE;
+
+  ensure_hb_face (self);
+
+  post_blob = hb_face_reference_table (self->face, HB_TAG ('p', 'o', 's', 't'));
+  raw_post = (guint8 *) hb_blob_get_data (post_blob, &post_len);
+
+  if (post_len >= sizeof (PostTable))
+    {
+      PostTable *post = (PostTable *)raw_post;
+
+      res = post->isFixedPitch[0] != 0 ||
+            post->isFixedPitch[1] != 0 ||
+            post->isFixedPitch[2] != 0 ||
+            post->isFixedPitch[3] != 0;
+    }
+
+  hb_blob_destroy (post_blob);
+
+  return res;
+}
+
+/**
+ * pango_hb_face_is_variable:
+ * @self: a `PangoHbFace`
+ *
+ * Returns whether @self should be considered a variable font.
+ *
+ * Returns: `TRUE` if @face is variable
+ */
+gboolean
+pango_hb_face_is_variable (PangoHbFace *self)
+{
+  ensure_hb_face (self);
+
+  return hb_ot_var_get_axis_count (self->face) > 0;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-hbface.h b/pango/pango-hbface.h
new file mode 100644
index 00000000..0763cef9
--- /dev/null
+++ b/pango/pango-hbface.h
@@ -0,0 +1,66 @@
+/* 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_bytes    (GBytes                 *bytes,
+                                                 unsigned int            index,
+                                                 int                     instance_id,
+                                                 const char             *name,
+                                                 PangoFontDescription   *description);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_from_file     (const char             *file,
+                                                 unsigned int            index,
+                                                 int                     instance_id,
+                                                 const char             *name,
+                                                 PangoFontDescription   *description);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_transformed   (PangoHbFace            *face,
+                                                 const PangoMatrix      *transform,
+                                                 const char             *name);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_emboldened    (PangoHbFace            *face);
+
+PANGO_AVAILABLE_IN_1_50
+PangoHbFace *   pango_hb_face_new_alias         (PangoHbFace            *face,
+                                                 const char             *fullname);
+
+PANGO_AVAILABLE_IN_1_50
+gboolean        pango_hb_face_is_monospace      (PangoHbFace            *self);
+
+PANGO_AVAILABLE_IN_1_50
+gboolean        pango_hb_face_is_variable       (PangoHbFace            *self);
+
+G_END_DECLS
diff --git a/pango/pango.h b/pango/pango.h
index 8dc86b13..019c12c9 100644
--- a/pango/pango.h
+++ b/pango/pango.h
@@ -38,6 +38,7 @@
 #include <pango/pango-glyph.h>
 #include <pango/pango-glyph-item.h>
 #include <pango/pango-gravity.h>
+#include <pango/pango-hbface.h>
 #include <pango/pango-item.h>
 #include <pango/pango-language.h>
 #include <pango/pango-layout.h>


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