[gtk+/wip/matthiasc/font-variations: 1/5] Some initial support for font variations



commit 5f98d6659fa2fbda8a75adbf14848c6f31e89e24
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Sep 15 22:38:08 2017 -0400

    Some initial support for font variations
    
    Make the font chooser show sliders for axes of variable fonts.
    Note that we don't have Pango api for some of this, so we are
    using freetype and pango-fc apis directly. Therefore, font
    variation support will only be available on platforms where
    we use these libraries.

 gtk/gtkfontchooserwidget.c     |  267 +++++++++++++++++++++++++++++++++++++++-
 gtk/ui/gtkfontchooserwidget.ui |   22 +++-
 meson.build                    |    3 +
 3 files changed, 286 insertions(+), 6 deletions(-)
---
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index 5e5d582..de81a9b 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -51,6 +51,12 @@
 #include "gtksettings.h"
 #include "gtkdialog.h"
 
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+#include <pango/pangofc-font.h>
+#include <freetype/freetype.h>
+#include <freetype/ftmm.h>
+#endif
+
 /**
  * SECTION:gtkfontchooserwidget
  * @Short_description: A widget for selecting fonts
@@ -98,6 +104,8 @@ struct _GtkFontChooserWidgetPrivate
   GtkWidget *size_spin;
   GtkWidget *size_slider;
 
+  GtkWidget *font_grid;
+
   PangoFontMap         *font_map;
 
   PangoFontDescription *font_desc;
@@ -108,6 +116,9 @@ struct _GtkFontChooserWidgetPrivate
   GDestroyNotify    filter_data_destroy;
 
   guint last_fontconfig_timestamp;
+
+  GHashTable *axes;
+  gboolean updating_variations;
 };
 
 /* Keep in line with GtkTreeStore defined in gtkfontchooserwidget.ui */
@@ -168,6 +179,7 @@ static void     gtk_font_chooser_widget_cell_data_func         (GtkTreeViewColum
                                                                GtkTreeIter       *iter,
                                                                gpointer           user_data);
 
+
 static void gtk_font_chooser_widget_iface_init (GtkFontChooserIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (GtkFontChooserWidget, gtk_font_chooser_widget, GTK_TYPE_WIDGET,
@@ -483,6 +495,8 @@ cursor_changed_cb (GtkTreeView *treeview,
                       FONT_DESC_COLUMN, &desc,
                       -1);
 
+  // reset variations explicitly
+  pango_font_description_set_variations (priv->font_desc, NULL);
   gtk_font_chooser_widget_merge_font_desc (fontchooser,
                                            gtk_delayed_font_description_get (desc),
                                            &iter);
@@ -650,6 +664,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_spin);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_slider);
   gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, grid);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, font_grid);
 
   gtk_widget_class_bind_template_callback (widget_class, text_changed_cb);
   gtk_widget_class_bind_template_callback (widget_class, stop_search_cb);
@@ -664,6 +679,55 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
   gtk_widget_class_set_css_name (widget_class, I_("fontchooser"));
 }
 
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+
+typedef struct {
+  guint32 tag;
+  GtkAdjustment *adjustment;
+  GtkWidget *label;
+  GtkWidget *scale;
+  GtkWidget *spin;
+  GtkWidget *fontchooser;
+} Axis;
+
+static guint
+axis_hash (gconstpointer v)
+{
+  const Axis *a = v;
+
+  return a->tag;
+}
+
+static gboolean
+axis_equal (gconstpointer v1, gconstpointer v2)
+{
+  const Axis *a1 = v1;
+  const Axis *a2 = v2;
+
+  return a1->tag == a2->tag;
+}
+
+static void
+axis_remove (gpointer key,
+             gpointer value,
+             gpointer data)
+{
+  Axis *a = value;
+
+  gtk_widget_destroy (a->label);
+  gtk_widget_destroy (a->scale);
+  gtk_widget_destroy (a->spin);
+}
+
+static void
+axis_free (gpointer v)
+{
+  Axis *a = v;
+
+  g_free (a);
+}
+
+#endif
 static void
 gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 {
@@ -676,6 +740,10 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   gtk_widget_init_template (GTK_WIDGET (fontchooser));
 
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+  priv->axes = g_hash_table_new_full (axis_hash, axis_equal, NULL, axis_free);
+#endif
+
   /* Default preview string  */
   priv->preview_text = g_strdup (pango_language_get_sample_string (NULL));
   priv->show_preview_entry = TRUE;
@@ -963,7 +1031,7 @@ gtk_font_chooser_widget_cell_data_func (GtkTreeViewColumn *column,
   text = g_strconcat (preview_title, "\n", fontchooser->priv->preview_text, NULL);
   first_line_len = strlen (preview_title) + 1;
 
-  attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser, 
+  attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser,
                                                           gtk_delayed_font_description_get (desc),
                                                           first_line_len);
 
@@ -1021,6 +1089,9 @@ gtk_font_chooser_widget_finalize (GObject *object)
 
   g_clear_object (&priv->font_map);
 
+  if (priv->axes)
+    g_hash_table_unref (priv->axes);
+
   G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object);
 }
 
@@ -1211,6 +1282,197 @@ gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser)
     }
 }
 
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+
+#define FixedToFloat(f) (((float)(f))/65536.0)
+
+static void
+add_font_variations (GtkFontChooserWidget *fontchooser,
+                     GString              *s)
+{
+  GHashTableIter iter;
+  Axis *axis;
+  const char *sep = "";
+  char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+  g_hash_table_iter_init (&iter, fontchooser->priv->axes);
+  while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
+    {
+      char tag[5];
+      double value;
+
+      tag[0] = (axis->tag >> 24) & 0xff;
+      tag[1] = (axis->tag >> 16) & 0xff;
+      tag[2] = (axis->tag >> 8) & 0xff;
+      tag[3] = (axis->tag >> 0) & 0xff;
+      tag[4] = '\0';
+      value = gtk_adjustment_get_value (axis->adjustment);
+      g_string_append_printf (s, "%s%s=%s", sep, tag, g_ascii_dtostr (buf, sizeof(buf), value));
+      sep = ",";
+    }
+}
+
+static void
+adjustment_changed (GtkAdjustment *adjustment,
+                    Axis          *axis)
+{
+  GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (axis->fontchooser);
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  PangoFontDescription *font_desc;
+  GString *s;
+
+  priv->updating_variations = TRUE;
+
+  s = g_string_new ("");
+  add_font_variations (fontchooser, s);
+
+  if (s->len > 0)
+    {
+      font_desc = pango_font_description_new ();
+      pango_font_description_set_variations (font_desc, s->str);
+      gtk_font_chooser_widget_take_font_desc (fontchooser, font_desc);
+    }
+
+  g_string_free (s, TRUE);
+
+  priv->updating_variations = FALSE;
+}
+
+static gboolean
+should_show_axis (FT_Var_Axis *ax)
+{
+  /* FIXME use FT_Get_Var_Axis_Flags */
+  if (ax->tag == FT_MAKE_TAG ('o', 'p', 's', 'z'))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+is_named_instance (FT_Face face)
+{
+  return (face->face_index >> 16) > 0;
+}
+
+static struct {
+  guint32 tag;
+  const char *name;
+} axis_names[] = {
+  { FT_MAKE_TAG ('w', 'd', 't', 'h'), N_("Width") },
+  { FT_MAKE_TAG ('w', 'g', 'h', 't'), N_("Weight") },
+  { FT_MAKE_TAG ('i', 't', 'a', 'l'), N_("Italic") },
+  { FT_MAKE_TAG ('s', 'l', 'n', 't'), N_("Slant") },
+  { FT_MAKE_TAG ('o', 'p', 's', 'z'), N_("Optical Size") },
+};
+
+static void
+add_axis (GtkFontChooserWidget *fontchooser,
+          FT_Face               face,
+          FT_Var_Axis          *ax,
+          FT_Fixed              value,
+          int                   row)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  Axis *axis;
+  const char *name;
+  int i;
+
+  axis = g_new (Axis, 1);
+  axis->tag = ax->tag;
+  axis->fontchooser = GTK_WIDGET (fontchooser);
+
+  name = ax->name;
+  for (i = 0; i < G_N_ELEMENTS (axis_names); i++)
+    {
+      if (axis_names[i].tag == ax->tag)
+        {
+          name = _(axis_names[i].name);
+          break;
+        }
+    }
+  axis->label = gtk_label_new (name);
+  gtk_widget_set_halign (axis->label, GTK_ALIGN_START);
+  gtk_widget_set_valign (axis->label, GTK_ALIGN_BASELINE);
+  gtk_grid_attach (GTK_GRID (priv->font_grid), axis->label, 0, row, 1, 1);
+  axis->adjustment = gtk_adjustment_new ((double)FixedToFloat(value),
+                                         (double)FixedToFloat(ax->minimum),
+                                         (double)FixedToFloat(ax->maximum),
+                                         1.0, 10.0, 0.0);
+  axis->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, axis->adjustment);
+  gtk_scale_add_mark (GTK_SCALE (axis->scale), (double)FixedToFloat(ax->def), GTK_POS_TOP, NULL);
+  gtk_widget_set_valign (axis->scale, GTK_ALIGN_BASELINE);
+  gtk_widget_set_hexpand (axis->scale, TRUE);
+  gtk_widget_set_size_request (axis->scale, 100, -1);
+  gtk_scale_set_draw_value (GTK_SCALE (axis->scale), FALSE);
+  gtk_grid_attach (GTK_GRID (priv->font_grid), axis->scale, 1, row, 1, 1);
+  axis->spin = gtk_spin_button_new (axis->adjustment, 0, 0);
+  g_signal_connect (axis->spin, "output", G_CALLBACK (output_cb), fontchooser);
+  gtk_widget_set_valign (axis->spin, GTK_ALIGN_BASELINE);
+  gtk_grid_attach (GTK_GRID (priv->font_grid), axis->spin, 2, row, 1, 1);
+
+  g_hash_table_add (priv->axes, axis);
+
+  adjustment_changed (axis->adjustment, axis);
+  g_signal_connect (axis->adjustment, "value-changed", G_CALLBACK (adjustment_changed), axis);
+  if (is_named_instance (face) || !should_show_axis (ax))
+    {
+      gtk_widget_hide (axis->label);
+      gtk_widget_hide (axis->scale);
+      gtk_widget_hide (axis->spin);
+    }
+}
+
+static void
+gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchooser)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  PangoFont *pango_font;
+  FT_Face ft_face;
+  FT_MM_Var *ft_mm_var;
+  FT_Error ret;
+
+  if (priv->updating_variations)
+    return;
+
+  g_hash_table_foreach (priv->axes, axis_remove, NULL);
+  g_hash_table_remove_all (priv->axes);
+
+  pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
+                                        priv->font_desc);
+  ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
+
+  ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
+  if (ret == 0)
+    {
+      int i;
+      FT_Fixed *coords;
+
+      coords = g_new (FT_Fixed, ft_mm_var->num_axis);
+      for (i = 0; i < ft_mm_var->num_axis; i++)
+        coords[i] = ft_mm_var->axis[i].def;
+
+      if (ft_face->face_index > 0)
+        {
+          int instance_id = ft_face->face_index >> 16;
+          if (instance_id && instance_id <= ft_mm_var->num_namedstyles)
+            {
+              FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1];
+              memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords));
+            }
+        }
+
+      for (i = 0; i < ft_mm_var->num_axis; i++)
+        add_axis (fontchooser, ft_face, &ft_mm_var->axis[i], coords[i], i + 4);
+
+      g_free (coords);
+      free (ft_mm_var);
+    }
+
+  pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
+  g_object_unref (pango_font);
+}
+#endif
+
 static void
 gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget       *fontchooser,
                                          const PangoFontDescription *font_desc,
@@ -1256,6 +1518,9 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget       *fontchooser
     }
 
   gtk_font_chooser_widget_update_preview_attributes (fontchooser);
+#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+  gtk_font_chooser_widget_update_font_variations (fontchooser);
+#endif
 
   g_object_notify (G_OBJECT (fontchooser), "font");
   g_object_notify (G_OBJECT (fontchooser), "font-desc");
diff --git a/gtk/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui
index 93eefd1..47fb28c 100644
--- a/gtk/ui/gtkfontchooserwidget.ui
+++ b/gtk/ui/gtkfontchooserwidget.ui
@@ -61,7 +61,7 @@
           <object class="GtkStack" id="list_stack">
             <property name="visible">1</property>
             <child>
-              <object class="GtkGrid">
+              <object class="GtkGrid" id="font_grid">
                 <property name="visible">1</property>
                 <property name="row-spacing">6</property>
                 <property name="column-spacing">6</property>
@@ -108,7 +108,7 @@
                   <packing>
                     <property name="left-attach">0</property>
                     <property name="top-attach">1</property>
-                    <property name="width">2</property>
+                    <property name="width">3</property>
                   </packing>
                 </child>
                 <child>
@@ -121,7 +121,19 @@
                   <packing>
                     <property name="left-attach">0</property>
                     <property name="top-attach">2</property>
-                    <property name="width">2</property>
+                    <property name="width">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">1</property>
+                    <property name="label" translatable="yes">Size</property>
+                    <property name="xalign">0</property>
+                    <property name="valign">baseline</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">0</property>
+                    <property name="top-attach">3</property>
                   </packing>
                 </child>
                 <child>
@@ -135,7 +147,7 @@
                     <signal name="scroll-event" handler="resize_by_scroll_cb" swapped="no"/>
                   </object>
                   <packing>
-                    <property name="left-attach">0</property>
+                    <property name="left-attach">1</property>
                     <property name="top-attach">3</property>
                   </packing>
                 </child>
@@ -147,7 +159,7 @@
                     <signal name="output" handler="output_cb"/>
                   </object>
                   <packing>
-                    <property name="left-attach">1</property>
+                    <property name="left-attach">2</property>
                     <property name="top-attach">3</property>
                   </packing>
                 </child>
diff --git a/meson.build b/meson.build
index 1288caa..29d0415 100644
--- a/meson.build
+++ b/meson.build
@@ -346,6 +346,9 @@ else
   platform_gio_dep = giounix_dep
 endif
 
+cdata.set('HAVE_HARFBUZZ', harfbuzz_dep.found())
+cdata.set('HAVE_PANGOFT', pangoft_dep.found())
+
 backend_immodules = []
 
 pc_gdk_extra_libs = []


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