[gtk/wip/fanc999/fontchooser.win32.gtk3: 11/11] gtk/gtkfontfeatures.c: Add support for PangoWin32Font->FT_Face



commit 3b10632fe0a305e43da25eccb73e6ce6d0a14a88
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Wed Dec 26 18:21:02 2018 +0800

    gtk/gtkfontfeatures.c: Add support for PangoWin32Font->FT_Face
    
    This is so that we can turn the LOGFONT that is retrieved from the
    PangoFont so that we can load it with FreeType and feed it to HarfBuzz.
    
    There is ongoing work in Pango to do shaping on all supported platforms
    using HarfBuzz, but since this is GTK+-3.24.x, we will still need to
    support the code path without it as well.  PangoWin32 also needs to be
    updated to apply the updates here to the PangoFont in question,
    otherwise we will only be able to view the OpenType tags that are
    tweakable.

 gtk/gtkfontchooserwidget.c   |   9 +-
 gtk/gtkfontfeatures.c        | 243 +++++++++++++++++++++++++++++++++++++++++--
 gtk/gtkfontfeaturesprivate.h |  19 +++-
 3 files changed, 260 insertions(+), 11 deletions(-)
---
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index 20d6380af9..6040fd9ce8 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -713,7 +713,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   gtk_widget_init_template (GTK_WIDGET (fontchooser));
 
-#if defined(HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
+#ifdef HAVE_HARFBUZZ
   priv->axes = g_hash_table_new_full (axis_hash, axis_equal, NULL, axis_free);
 #endif
 
@@ -754,7 +754,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
 
   /* Load data and set initial style-dependent parameters */
   gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
-#if defined(HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
+#ifdef HAVE_HARFBUZZ
   gtk_font_chooser_widget_populate_features (fontchooser);
 #endif
   gtk_font_chooser_widget_set_cell_size (fontchooser);
@@ -1063,6 +1063,9 @@ gtk_font_chooser_widget_finalize (GObject *object)
 
   g_free (priv->font_features);
 
+  if (priv->ft_ext_items)
+    gtk_font_chooser_widget_release_extra_ft_items (fontchooser);
+
   G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object);
 }
 
@@ -1379,7 +1382,7 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget       *fontchooser
 
       gtk_font_chooser_widget_update_marks (fontchooser);
 
-#if defined(HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
+#ifdef HAVE_HARFBUZZ
       if (gtk_font_chooser_widget_update_font_features (fontchooser))
         has_tweak = TRUE;
       if (gtk_font_chooser_widget_update_font_variations (fontchooser))
diff --git a/gtk/gtkfontfeatures.c b/gtk/gtkfontfeatures.c
index 4133405527..c92cf9eee9 100644
--- a/gtk/gtkfontfeatures.c
+++ b/gtk/gtkfontfeatures.c
@@ -33,13 +33,49 @@
 #include "gtkradiobutton.h"
 #include "gtkgesturemultipress.h"
 
-#if defined (HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
-#include <pango/pangofc-font.h>
+#ifdef HAVE_HARFBUZZ
+
+#ifdef HAVE_PANGOFT
+# include <pango/pangofc-font.h>
+
+/* On Windows, we need to check whether the PangoFont is a FontConfig Font or a Win32/GDI font,
+ * and acquire/release the FT_Face accordingly.
+ */
+# ifdef GDK_WINDOWING_WIN32
+#  define FT_FACE_FROM_PANGO_FONT(w,f) \
+          PANGO_IS_FC_FONT(f) ?          \
+          pango_fc_font_lock_face (PANGO_FC_FONT (f)) : \
+          gtk_font_chooser_widget_win32_acquire_ftface (w, f)
+
+#  define PANGO_FONT_RELEASE_FT_FACE(w,f) \
+          PANGO_IS_FC_FONT(f) ?           \
+          pango_fc_font_unlock_face (PANGO_FC_FONT (f)) : \
+          gtk_font_chooser_widget_win32_release_ftface (w);
+
+#  define RELEASE_EXTRA_FT_ITEMS(w) release_extra_ft_items(w)
+
+# else /* On non-Windows, acquire/release the FT_Face as we did before */
+#  define FT_FACE_FROM_PANGO_FONT(w,f) pango_fc_font_lock_face (PANGO_FC_FONT (f))
+#  define PANGO_FONT_RELEASE_FT_FACE(w,f) pango_fc_font_unlock_face (PANGO_FC_FONT (f))
+#  define RELEASE_EXTRA_FT_ITEMS(w)
+# endif
+#elif defined (GDK_WINDOWING_WIN32)
+# include <pango/pangowin32.h>
+
+# define FT_FACE_FROM_PANGO_FONT(w,f) gtk_font_chooser_widget_win32_acquire_ftface (w, f)
+# define PANGO_FONT_RELEASE_FT_FACE(w,f) gtk_font_chooser_widget_win32_release_ftface (w)
+# define RELEASE_EXTRA_FT_ITEMS(w) release_extra_ft_items(w)
+
+/* Check for TTC fonts and get their font data */
+# define FONT_TABLE_TTCF  (('t' << 0) + ('t' << 8) + ('c' << 16) + ('f' << 24))
+#endif
+
 #include <hb.h>
 #include <hb-ot.h>
 #include <hb-ft.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
+#include FT_MODULE_H
 #include FT_MULTIPLE_MASTERS_H
 
 #include "language-names.h"
@@ -85,6 +121,199 @@ axis_remove (gpointer key,
   gtk_widget_destroy (a->spin);
 }
 
+/* Until Pango uses HarfBuzz for shaping on all platforms, we need to go through FreeType */
+#ifdef GDK_WINDOWING_WIN32
+
+typedef struct _gtk_win32_ft_items
+{
+  FT_Library ft_lib;
+  FT_Byte *font_data_stream;
+  FT_Face face;
+  HDC hdc;
+  LOGFONTW *logfont;
+  HFONT hfont;
+  PangoWin32FontCache *cache;
+} gtk_win32_ft_items;
+
+#define WIN32_FT_FACE_FAIL(msg) \
+{ \
+  g_warning(msg); \
+  goto ft_face_fail; \
+}
+
+FT_Face
+gtk_font_chooser_widget_win32_acquire_ftface (GtkFontChooserWidget *fontchooser,
+                                              PangoFont            *font)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+  PangoWin32FontCache *cache = NULL;
+  PangoFontMap *font_map = NULL;
+  LOGFONTW *logfont = NULL;
+  HFONT hfont = NULL;
+  HDC hdc = NULL;
+  DWORD is_ttc_font;
+  FT_Face face;
+  FT_Byte *font_stream = NULL;
+  unsigned char buf[4];
+  gsize font_size;
+  gtk_win32_ft_items *item = NULL;
+
+  if (priv->font_map != NULL)
+    font_map = priv->font_map;
+  else
+    font_map = pango_cairo_font_map_get_default ();
+
+  cache = pango_win32_font_map_get_font_cache (font_map);
+
+  if (!cache)
+    WIN32_FT_FACE_FAIL ("Failed to acquire PangoWin32FontCache");
+
+  logfont = pango_win32_font_logfontw (font);
+
+  if (logfont == NULL)
+    WIN32_FT_FACE_FAIL ("Unable to acquire LOGFONT from PangoFont");
+
+  hfont = pango_win32_font_cache_loadw (cache, logfont);
+
+  if (hfont == NULL)
+    WIN32_FT_FACE_FAIL ("Unable to acquire HFONT from PangoWin32FontCache with LOGFONT (LOGFONT invalid?)");
+
+  hdc = GetDC (NULL);
+
+  if (hdc == NULL)
+    WIN32_FT_FACE_FAIL ("Failed to acquire DC");
+
+  if (priv->ft_ext_items == NULL)
+    priv->ft_ext_items = g_new0 (gtk_win32_ft_items, 1);
+
+  item = (gtk_win32_ft_items *) priv->ft_ext_items;
+
+  if (item->ft_lib == NULL)
+    {
+      if (FT_Init_FreeType (&item->ft_lib) != FT_Err_Ok)
+        WIN32_FT_FACE_FAIL ("Failed to initialize FreeType for PangoWin32Font->FT_Face transformation");
+    }
+
+  if (SelectObject (hdc, hfont) == HGDI_ERROR)
+    WIN32_FT_FACE_FAIL ("SelectObject() for the PangoFont failed");
+
+  /* is_ttc_font is GDI_ERROR if the HFONT does not refer to a font in a TTC when,
+   * specifying FONT_TABLE_TTCF for the Font Table type, otherwise it is 1,
+   * so try again without specifying FONT_TABLE_TTCF if is_ttc_font is not 1
+   */
+  is_ttc_font = GetFontData (hdc, FONT_TABLE_TTCF, 0, &buf, 1);
+
+  if (is_ttc_font == 1)
+    font_size = GetFontData (hdc, FONT_TABLE_TTCF, 0, NULL, 0);
+  else
+    font_size = GetFontData (hdc, 0, 0, NULL, 0);
+
+  if (font_size == GDI_ERROR)
+    WIN32_FT_FACE_FAIL ("Could not acquire font size from GetFontData()");
+
+  /* Now, get the font data stream that we need for creating the FT_Face */
+  font_stream = g_malloc (font_size);
+  if (GetFontData (hdc,
+                   is_ttc_font == 1 ? FONT_TABLE_TTCF : 0,
+                   0,
+                   font_stream,
+                   font_size) == GDI_ERROR)
+    {
+      WIN32_FT_FACE_FAIL ("Unable to get data stream of font!");
+    }
+
+  /* Finally, create the FT_Face we need */
+  if (FT_New_Memory_Face (item->ft_lib,
+                          font_stream,
+                          font_size,
+                          0,
+                          &face) == FT_Err_Ok)
+    {
+      /* We need to track these because we can only release/free those items *after* we
+       * are done with them in FreeType
+       */
+      item->cache = cache;
+      item->logfont = logfont;
+      item->hfont = hfont;
+      item->hdc = hdc;
+      item->font_data_stream = font_stream;
+      item->face = face;
+      return item->face;
+    }
+
+  else
+    WIN32_FT_FACE_FAIL ("Unable to create FT_Face from font data stream!");
+
+ft_face_fail:
+  if (font_stream != NULL)
+    g_free (font_stream);
+
+  if (hdc != NULL)
+    ReleaseDC (NULL, hdc);
+
+  if (cache != NULL && hfont != NULL)
+    pango_win32_font_cache_unload (cache, hfont);
+
+  if (logfont != NULL)
+    g_free (logfont);
+
+  return NULL;
+}
+
+#undef WIN32_FT_FACE_FAIL
+
+void
+gtk_font_chooser_widget_win32_release_ftface (GtkFontChooserWidget *widget)
+{
+  GtkFontChooserWidgetPrivate *priv = widget->priv;
+  gtk_win32_ft_items *item = (gtk_win32_ft_items *) priv->ft_ext_items;
+
+  FT_Done_Face (item->face);
+  g_free (item->font_data_stream);
+  ReleaseDC (NULL, item->hdc);
+  pango_win32_font_cache_unload (item->cache, item->hfont);
+  g_free (item->logfont);
+}
+
+static void
+release_extra_ft_items (GtkFontChooserWidget *fontchooser)
+{
+  GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+
+  if (priv->ft_ext_items != NULL)
+    {
+      gtk_win32_ft_items *item = (gtk_win32_ft_items *) priv->ft_ext_items;
+
+      if (item->ft_lib != NULL)
+        FT_Done_Library (item->ft_lib);
+
+      g_free (priv->ft_ext_items);
+      priv->ft_ext_items = NULL;
+    }
+}
+
+#endif /* GDK_WINDOWING_WIN32 */
+
+static FT_Face
+get_ft_face_from_pango_font (GtkFontChooserWidget *fontchooser,
+                             PangoFont            *font)
+{
+  return FT_FACE_FROM_PANGO_FONT (fontchooser, font);
+}
+
+static void
+release_ft_face_from_pango_font (GtkFontChooserWidget *fontchooser,
+                                 PangoFont            *font)
+{
+  PANGO_FONT_RELEASE_FT_FACE (fontchooser, font);
+}
+
+void
+gtk_font_chooser_widget_release_extra_ft_items (GtkFontChooserWidget *fontchooser)
+{
+  RELEASE_EXTRA_FT_ITEMS (fontchooser);
+}
+
 /* OpenType variations */
 
 #define FixedToFloat(f) (((float)(f))/65536.0)
@@ -241,6 +470,7 @@ gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchoose
   FT_MM_Var *ft_mm_var;
   FT_Error ret;
   gboolean has_axis = FALSE;
+  PangoFontMap *font_map = NULL;
 
   if (priv->updating_variations)
     return FALSE;
@@ -254,7 +484,7 @@ gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchoose
   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));
+  ft_face = get_ft_face_from_pango_font (fontchooser, pango_font);
 
   ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
   if (ret == 0)
@@ -286,7 +516,7 @@ gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchoose
       free (ft_mm_var);
     }
 
-  pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
+  release_ft_face_from_pango_font (fontchooser, pango_font);
 
   g_object_unref (pango_font);
 
@@ -775,6 +1005,7 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
   int i, j;
   GList *l;
   gboolean has_feature = FALSE;
+  PangoFontMap *font_map = NULL;
 
   for (l = priv->feature_items; l; l = l->next)
     {
@@ -789,7 +1020,7 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
   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));
+  ft_face = get_ft_face_from_pango_font (fontchooser, pango_font);
 
   hb_font = hb_ft_font_create (ft_face, NULL);
 
@@ -850,7 +1081,7 @@ gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
       hb_face_destroy (hb_face);
     }
 
-  pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
+  release_ft_face_from_pango_font (fontchooser, pango_font);
 
   g_object_unref (pango_font);
 
diff --git a/gtk/gtkfontfeaturesprivate.h b/gtk/gtkfontfeaturesprivate.h
index d24c0ba31b..db00b062b8 100644
--- a/gtk/gtkfontfeaturesprivate.h
+++ b/gtk/gtkfontfeaturesprivate.h
@@ -70,6 +70,8 @@ struct _GtkFontChooserWidgetPrivate
   GList *feature_items;
 
   GAction *tweak_action;
+
+  gpointer ft_ext_items;
 };
 
 typedef struct {
@@ -87,6 +89,19 @@ void        gtk_font_chooser_widget_take_font_desc            (GtkFontChooserWid
 gboolean    gtk_font_chooser_widget_update_font_features      (GtkFontChooserWidget *fontchooser);
 gboolean    gtk_font_chooser_widget_update_font_variations    (GtkFontChooserWidget *fontchooser);
 void        gtk_font_chooser_widget_update_preview_attributes (GtkFontChooserWidget *fontchooser);
+void        gtk_font_chooser_widget_release_extra_ft_items    (GtkFontChooserWidget *fontchooser);
+
+#if defined (GDK_WINDOWING_WIN32) && (HAVE_HARFBUZZ)
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+_GDK_EXTERN
+FT_Face     gtk_font_chooser_widget_win32_acquire_ftface      (GtkFontChooserWidget *fontchooser,
+                                                               PangoFont            *font);
+
+_GDK_EXTERN
+void        gtk_font_chooser_widget_win32_release_ftface      (GtkFontChooserWidget *fontchooser);
+#endif
 
-gboolean    output_cb (GtkSpinButton *spin,
-                       gpointer       data);
+gboolean    output_cb              (GtkSpinButton *spin,
+                                    gpointer       data);


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