diff --git a/ibus-xx-emoji-harfbuzz.patch b/ibus-xx-emoji-harfbuzz.patch deleted file mode 100644 index 15d1a05..0000000 --- a/ibus-xx-emoji-harfbuzz.patch +++ /dev/null @@ -1,1764 +0,0 @@ -From 631ef573ebdd4f3fad8f036fcb33929e04372ab2 Mon Sep 17 00:00:00 2001 -From: fujiwarat -Date: Thu, 1 Mar 2018 16:05:04 +0900 -Subject: [PATCH] Integrate custom rendering to use HarfBuzz glyph info - -IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering -on Cairo context. -Now the most issues in Pango were fixed and I appreciate the changes [1]. -However the latest changes in Pango, Fontconfig prevent users from -setting emoji fonts with GtkFontChooser. -This patch can enable the selected emoji font to draw emoji chars -on Emojier. -It's under the considerations if the font setting is deleted from ibus-setup. -Need configure --enable-harfbuzz-for-emoji option to enable this feature. - -[1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669 - https://bugzilla.gnome.org/show_bug.cgi?id=781123 ---- - bindings/vala/IBusFontSet-1.0.metadata | 1 + - bindings/vala/Makefile.am | 83 +++ - bindings/vala/ibus-fontset-1.0.deps | 1 + - configure.ac | 29 + - ui/gtk3/Makefile.am | 36 ++ - ui/gtk3/emojier.vala | 111 ++++ - ui/gtk3/ibusfontset.c | 1030 ++++++++++++++++++++++++++++++++ - ui/gtk3/ibusfontset.h | 302 ++++++++++ - 8 files changed, 1593 insertions(+) - create mode 100644 bindings/vala/IBusFontSet-1.0.metadata - create mode 100644 bindings/vala/ibus-fontset-1.0.deps - create mode 100644 ui/gtk3/ibusfontset.c - create mode 100644 ui/gtk3/ibusfontset.h - -diff --git a/bindings/vala/IBusFontSet-1.0.metadata b/bindings/vala/IBusFontSet-1.0.metadata -new file mode 100644 -index 00000000..73037d7f ---- /dev/null -+++ b/bindings/vala/IBusFontSet-1.0.metadata -@@ -0,0 +1 @@ -+IBusFontSet cheader_filename="ibusfontset.h" -diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am -index fc8e2f01..f7b9e97a 100644 ---- a/bindings/vala/Makefile.am -+++ b/bindings/vala/Makefile.am -@@ -83,8 +83,10 @@ EXTRA_DIST = \ - IBus-1.0.metadata \ - IBus-1.0-custom.vala \ - IBusEmojiDialog-1.0.metadata \ -+ IBusFontSet-1.0.metadata \ - ibus-1.0.deps \ - ibus-emoji-dialog-1.0.deps \ -+ ibus-fontset-1.0.deps \ - config.vapi \ - xi.vapi \ - $(NULL) -@@ -131,6 +133,15 @@ libibus_emoji_dialog_1_0_la_LDFLAGS = \ - if test ! -f $@ ; then \ - $(LN_S) $(top_srcdir)/ui/gtk3/$@ .; \ - fi; -+ibusfontset.c: $(ibus_vapi) ibusfontset.h -+ if test ! -f $@ ; then \ -+ $(LN_S) $(top_srcdir)/ui/gtk3/$@ .; \ -+ fi; -+ibusfontset.h: $(ibus_vapi) -+ if test ! -f $@ ; then \ -+ $(LN_S) $(top_srcdir)/ui/gtk3/$@ .; \ -+ fi; -+ - - MAINTAINERCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES) - DISTCLEANFILES += $(libibus_emoji_dialog_1_0_la_SOURCES) -@@ -184,6 +195,78 @@ DISTCLEANFILES += $(ibus_emoji_dialog_vapi) - - endif - #end of HAVE_INTROSPECTION -+ -+ -+if ENABLE_HARFBUZZ_FOR_EMOJI -+libibus_fontset = libibus-fontset-1.0.la -+noinst_LTLIBRARIES += $(libibus_fontset) -+ -+libibus_fontset_1_0_la_SOURCES = \ -+ ibusfontset.c \ -+ $(NULL) -+libibus_fontset_1_0_la_CFLAGS = \ -+ $(AM_CFLAGS) \ -+ @CAIRO_CFLAGS@ \ -+ @FONTCONFIG_CFLAGS@ \ -+ @GLIB2_CFLAGS@ \ -+ @HARFBUZZ_CFLAGS@ \ -+ @PANGO_CFLAGS@ \ -+ $(NULL) -+libibus_fontset_1_0_la_LIBADD = \ -+ @CAIRO_LIBS@ \ -+ @FONTCONFIG_LIBS@ \ -+ @GLIB2_LIBS@ \ -+ @HARFBUZZ_LIBS@ \ -+ @PANGO_LIBS@ \ -+ $(NULL) -+libibus_fontset_1_0_la_LDFLAGS = \ -+ -no-undefined \ -+ -export-symbols-regex "ibus_.*" \ -+ $(NULL) -+ -+MAINTAINERCLEANFILES += ibusfontset.c ibusfontset.h -+DISTCLEANFILES += ibusfontset.c ibusfontset.h -+ -+if HAVE_INTROSPECTION -+IBusFontSet-1.0.gir: $(libibus_fontset) Makefile -+IBusFontSet_1_0_gir_SCANNERFLAGS = \ -+ --pkg-export=ibus-1.0 \ -+ --pkg=cairo \ -+ --pkg=fontconfig \ -+ --pkg=harfbuzz \ -+ $(IBUS_GIR_SCANNERFLAGS) \ -+ $(NULL) -+IBusFontSet_1_0_gir_LIBS = $(libibus_fontset) $(libibus) -+IBusFontSet_1_0_gir_INCLUDES = cairo-1.0 GLib-2.0 GObject-2.0 -+IBusFontSet_1_0_gir_FILES = \ -+ ibusfontset.h \ -+ $(NULL) -+IBusFontSet_1_0_gir_CFLAGS = \ -+ -I$(srcdir) \ -+ -I$(builddir) \ -+ -I$(top_srcdir)/src \ -+ $(NULL) -+ibus_fontset_gir = IBusFontSet-1.0.gir -+INTROSPECTION_GIRS += $(ibus_fontset_gir) -+noinst_DATA += $(ibus_fontset_gir) -+EXTRA_DIST += $(ibus_fontset_gir) -+MAINTAINERCLEANFILES += $(ibus_fontset_gir) -+DISTCLEANFILES += $(ibus_fontset_gir) -+ -+ibus-fontset-1.0.vapi: $(ibus_fontset_gir) IBusFontSet-1.0.metadata -+ibus_fontset_vapi = ibus-fontset-1.0.vapi -+ibus_fontset_1_0_vapi_METADATADIRS = $(srcdir) -+ibus_fontset_1_0_vapi_FILES = IBusFontSet-1.0.gir -+VAPIGEN_VAPIS += $(ibus_fontset_vapi) -+noinst_DATA += $(ibus_fontset_vapi) -+EXTRA_DIST += $(ibus_fontset_vapi) -+MAINTAINERCLEANFILES += $(ibus_fontset_vapi) -+DISTCLEANFILES += $(ibus_fontset_vapi) -+ -+endif -+# end of HAVE_INTROSPECTION -+endif -+# end of ENABLE_HARFBUZZ_FOR_EMOJI - endif - # end of ENABLE_EMOJI_DICT - -diff --git a/bindings/vala/ibus-fontset-1.0.deps b/bindings/vala/ibus-fontset-1.0.deps -new file mode 100644 -index 00000000..129fe166 ---- /dev/null -+++ b/bindings/vala/ibus-fontset-1.0.deps -@@ -0,0 +1 @@ -+cairo -diff --git a/configure.ac b/configure.ac -index bd41069b..243396ff 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -688,6 +688,34 @@ the UCD files from https://www.unicode.org/Public/UNIDATA/) - enable_unicode_dict="yes (enabled, use --disable-unicode-dict to disable)" - fi - -+AC_ARG_ENABLE(harfbuzz-for-emoji, -+ AS_HELP_STRING([--enable-harfbuzz-for-emoji], -+ [Enable HarBuzz to draw emoji characters. -+ Current Pango has a problem to draw emoji variants and -+ this way enables to use HarfBuzz directly in GtkLabel.]), -+ [enable_harfbuzz_for_emoji=$enableval], -+ [enable_harfbuzz_for_emoji=no] -+) -+AM_CONDITIONAL([ENABLE_HARFBUZZ_FOR_EMOJI], -+ [test x"$enable_harfbuzz_for_emoji" = x"yes"]) -+ -+if test x"$enable_harfbuzz_for_emoji" = x"yes"; then -+ PKG_CHECK_MODULES(CAIRO, [ -+ cairo -+ ]) -+ PKG_CHECK_MODULES(FONTCONFIG, [ -+ fontconfig -+ ]) -+ PKG_CHECK_MODULES(HARFBUZZ, [ -+ harfbuzz -+ ]) -+ PKG_CHECK_MODULES(PANGO, [ -+ pango -+ ]) -+else -+ enable_harfbuzz_for_emoji="no (disabled, use --enable-harfbuzz-for-emoji to enable)" -+fi -+ - # Check iso-codes. - PKG_CHECK_MODULES(ISOCODES, [ - iso-codes -@@ -780,6 +808,7 @@ Build options: - CLDR annotation directory $EMOJI_ANNOTATION_DIR - Enable Unicode dict $enable_unicode_dict - UCD directory $UCD_DIR -+ Enable HarfBuzz for Emoji $enable_harfbuzz_for_emoji - Run test cases $enable_tests - ]) - -diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am -index bf9f98d7..388f80f9 100644 ---- a/ui/gtk3/Makefile.am -+++ b/ui/gtk3/Makefile.am -@@ -159,6 +159,8 @@ EXTRA_DIST = \ - extension.vala \ - gtkextension.xml.in \ - gtkpanel.xml.in \ -+ ibusfontset.c \ -+ ibusfontset.h \ - notification-item.xml \ - notification-watcher.xml \ - ibus-ui-emojier.desktop.in.in \ -@@ -249,6 +251,40 @@ panelbinding.o: $(srcdir)/panelbinding.c - $(AM_V_CC_no)$(COMPILE) -c -o $@ $< - $(NULL) - -+if ENABLE_HARFBUZZ_FOR_EMOJI -+ibus_ui_gtk3_SOURCES += \ -+ ibusfontset.c \ -+ $(NULL) -+ -+ibus_ui_emojier_SOURCES += \ -+ ibusfontset.c \ -+ $(NULL) -+ -+ibus_extension_gtk3_SOURCES += \ -+ ibusfontset.c \ -+ $(NULL) -+ -+AM_CFLAGS += \ -+ @CAIRO_CFLAGS@ \ -+ @FONTCONFIG_CFLAGS@ \ -+ @HARFBUZZ_CFLAGS@ \ -+ $(NULL) -+ -+AM_LDADD += \ -+ @CAIRO_LIBS@ \ -+ @FONTCONFIG_LIBS@ \ -+ @HARFBUZZ_LIBS@ \ -+ $(NULL) -+ -+AM_VALAFLAGS += \ -+ -D ENABLE_HARFBUZZ_FOR_EMOJI \ -+ --pkg=cairo \ -+ --pkg=ibus-fontset-1.0 \ -+ $(NULL) -+ -+endif -+# end of ENABLE_HARFBUZZ_FOR_EMOJI -+ - man_seven_files = $(man_seven_in_files:.7.in=.7) - man_seven_DATA =$(man_seven_files:.7=.7.gz) - man_sevendir = $(mandir)/man7 -diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala -index 8707e432..49619d1c 100644 ---- a/ui/gtk3/emojier.vala -+++ b/ui/gtk3/emojier.vala -@@ -99,16 +99,103 @@ public class IBusEmojier : Gtk.ApplicationWindow { - } - } - private class EWhiteLabel : Gtk.Label { -+#if ENABLE_HARFBUZZ_FOR_EMOJI -+ private IBus.RequisitionEx m_requisition; -+#else - private int m_minimum_width = 0; - private int m_natural_width = 0; - private int m_minimum_height = 0; - private int m_natural_height = 0; -+#endif - public EWhiteLabel(string text) { - GLib.Object( - name : "IBusEmojierWhiteLabel" - ); - set_label(text); - } -+#if ENABLE_HARFBUZZ_FOR_EMOJI -+ private void get_preferred_size_with_hb(out int minimum_width, -+ out int natural_width, -+ out int minimum_height, -+ out int natural_height) { -+ minimum_width = 0; -+ natural_width = 0; -+ minimum_height = 0; -+ natural_height = 0; -+ var text = this.get_text(); -+ GLib.return_if_fail (text != null); -+ if (text == "") -+ return; -+ var context = this.get_pango_context(); -+ var language = context.get_language(); -+ update_fontset(language); -+ Cairo.RectangleInt widest = Cairo.RectangleInt(); -+ m_requisition = m_fontset.get_preferred_size_hb(text, out widest); -+ minimum_width = widest.width; -+ natural_width = widest.width; -+ minimum_height = widest.height; -+ natural_height = widest.height; -+ if (minimum_width <= minimum_height) -+ natural_width = minimum_width = minimum_height; -+ if (text.length == 1) { -+ switch(text.get_char()) { -+ case '\t': -+ natural_width = minimum_width = minimum_height; -+ break; -+ case '\v': -+ natural_height = minimum_height = minimum_width; -+ break; -+ } -+ } -+ } -+ public override void get_preferred_width(out int minimum_width, -+ out int natural_width) { -+ get_preferred_size_with_hb(out minimum_width, -+ out natural_width, -+ null, null); -+ } -+ public override void get_preferred_height(out int minimum_height, -+ out int natural_height) { -+ get_preferred_size_with_hb(null, null, -+ out minimum_height, -+ out natural_height); -+ } -+ public override bool draw(Cairo.Context cr) { -+ var style_context = get_style_context(); -+ Gtk.Allocation allocation; -+ get_allocation(out allocation); -+ style_context.render_background(cr, -+ 0, 0, -+ allocation.width, -+ allocation.height); -+ if (m_fontset == null) -+ return true; -+ if (m_requisition == null) -+ return true; -+ if (m_requisition.cairo_lines == null) -+ return true; -+ Gdk.RGBA *normal_fg = null; -+ style_context.get(Gtk.StateFlags.NORMAL, -+ "color", -+ out normal_fg); -+ cr.set_operator(Cairo.Operator.OVER); -+ cr.set_source_rgba(normal_fg.red, normal_fg.green, normal_fg.blue, -+ normal_fg.alpha); -+ cr.save(); -+ double x = 0.0; -+ double y = 0.0; -+ if (allocation.width > m_requisition.width) -+ x = (allocation.width - m_requisition.width) / 2.0; -+ if (allocation.height > m_requisition.height) -+ y = (allocation.height - m_requisition.height) / 2.0; -+ cr.translate(x, y); -+ m_fontset.draw_cairo_with_requisition_ex(cr, m_requisition); -+ cr.restore(); -+ normal_fg.free(); -+ normal_fg = null; -+ return true; -+ } -+#else - public override void get_preferred_width(out int minimum_width, - out int natural_width) { - if (m_minimum_height == 0 && m_natural_height == 0) { -@@ -161,6 +248,7 @@ public class IBusEmojier : Gtk.ApplicationWindow { - m_minimum_height = minimum_height; - m_natural_height = natural_height; - } -+#endif - } - private class ESelectedLabel : EWhiteLabel { - public ESelectedLabel(string text) { -@@ -313,6 +401,9 @@ public class IBusEmojier : Gtk.ApplicationWindow { - private static bool m_show_unicode = false; - private static LoadProgressObject m_unicode_progress_object; - private static bool m_loaded_unicode = false; -+#if ENABLE_HARFBUZZ_FOR_EMOJI -+ private static IBus.FontSet m_fontset; -+#endif - - private ThemedRGBA m_rgba; - private Gtk.Box m_vbox; -@@ -2116,6 +2207,22 @@ public class IBusEmojier : Gtk.ApplicationWindow { - } - - -+#if ENABLE_HARFBUZZ_FOR_EMOJI -+ private static void update_fontset(Pango.Language language) { -+ if (m_fontset != null) { -+ m_fontset.set_family(m_emoji_font_family); -+ m_fontset.set_size(m_emoji_font_size); -+ m_fontset.set_language(language.to_string()); -+ m_fontset.update_fcfontset(); -+ } else { -+ m_fontset = new IBus.FontSet.with_font( -+ m_emoji_font_family, -+ m_emoji_font_size, -+ language.to_string()); -+ } -+ } -+#endif -+ - public static bool has_loaded_emoji_dict() { - if (m_emoji_to_data_dict == null) - return false; -@@ -2146,6 +2253,10 @@ public class IBusEmojier : Gtk.ApplicationWindow { - int font_size = font_desc.get_size() / Pango.SCALE; - if (font_size != 0) - m_emoji_font_size = font_size; -+#if ENABLE_HARFBUZZ_FOR_EMOJI -+ var widget = new Gtk.Label(""); -+ update_fontset(widget.get_pango_context().get_language()); -+#endif - } - - -diff --git a/ui/gtk3/ibusfontset.c b/ui/gtk3/ibusfontset.c -new file mode 100644 -index 00000000..16ceef05 ---- /dev/null -+++ b/ui/gtk3/ibusfontset.c -@@ -0,0 +1,1030 @@ -+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ -+/* vim:set et sts=4: */ -+/* ibus - The Input Bus -+ * Copyright (C) 2017 Takao Fujiwara -+ * Copyright (C) 2017 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA -+ */ -+ -+#include -+#include -+#include -+#include FT_FREETYPE_H -+#include -+#include -+#include -+ -+#include "ibusfontset.h" -+ -+#define XPAD 2 -+#define YPAD 2 -+#define UNKNOWN_FONT_SIZE 7 -+#define IBUS_FONTSET_GET_PRIVATE(o) \ -+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FONTSET, IBusFontSetPrivate)) -+#define MONOSPACE "monospace" -+#define SERIF "serif" -+#define SANS "sans" -+ -+typedef struct _FcFontSetEx { -+ int nfont; -+ int sfont; -+ FcPattern **fonts; -+ FT_Face *ft_faces; -+} FcFontSetEx; -+ -+static gboolean m_color_supported; -+static FT_Library m_ftlibrary; -+static FcFontSetEx *m_fcfontset; -+static gchar *m_family; -+static guint m_size; -+static gchar *m_language; -+static GHashTable *m_font_index_per_char_table; -+static GHashTable *m_scaled_font_table; -+static GHashTable *m_hb_font_table; -+ -+enum { -+ PROP_0, -+ PROP_FAMILY, -+ PROP_SIZE, -+ PROP_LANGUAGE -+}; -+ -+typedef struct { -+ gunichar ch; -+ FcPattern *fcfont; -+} FontPerChar; -+ -+struct _IBusFontSetPrivate { -+ gchar *family; -+ guint size; -+ gchar *language; -+}; -+ -+static GObject * ibus_fontset_constructor (GType type, -+ guint n, -+ GObjectConstructParam *args); -+static void ibus_fontset_destroy (IBusFontSet *fontset); -+static void ibus_fontset_set_property (IBusFontSet *fontset, -+ guint prop_id, -+ const GValue *value, -+ GParamSpec *pspec); -+static void ibus_fontset_get_property (IBusFontSet *fontset, -+ guint prop_id, -+ GValue *value, -+ GParamSpec *pspec); -+static cairo_scaled_font_t * -+ ibus_fontset_cairo_scaled_font_new_with_font -+ (const gchar *family, -+ guint size, -+ gboolean has_color); -+ -+G_DEFINE_BOXED_TYPE (IBusCairoLine, -+ ibus_cairo_line, -+ ibus_cairo_line_copy, -+ ibus_cairo_line_free); -+G_DEFINE_BOXED_TYPE (IBusRequisitionEx, -+ ibus_requisition_ex, -+ ibus_requisition_ex_copy, -+ ibus_requisition_ex_free); -+G_DEFINE_TYPE (IBusFontSet, ibus_fontset, IBUS_TYPE_OBJECT) -+ -+static void -+ibus_fontset_class_init (IBusFontSetClass *class) -+{ -+ GObjectClass *gobject_class = G_OBJECT_CLASS (class); -+ IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class); -+ cairo_glyph_t dummy; -+ IBusGlyph dummy2; -+ -+ m_color_supported = (FcGetVersion () >= 21205); -+ gobject_class->constructor = ibus_fontset_constructor; -+ gobject_class->get_property = -+ (GObjectGetPropertyFunc) ibus_fontset_get_property; -+ gobject_class->set_property = -+ (GObjectSetPropertyFunc) ibus_fontset_set_property; -+ object_class->destroy = (IBusObjectDestroyFunc) ibus_fontset_destroy; -+ -+ /* install properties */ -+ /** -+ * IBusFontSet:family: -+ * -+ * Font family of this IBusFontSet. -+ */ -+ g_object_class_install_property (gobject_class, -+ PROP_FAMILY, -+ g_param_spec_string ("family", -+ "family", -+ "family", -+ "", -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * IBusFontSet:size: -+ * -+ * Font size of this IBusFontSet. -+ */ -+ g_object_class_install_property (gobject_class, -+ PROP_SIZE, -+ g_param_spec_uint ("size", -+ "size", -+ "size", -+ 0, G_MAXUINT16, 0, -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ /** -+ * IBusFontSet:language: -+ * -+ * Font language of this IBusFontSet. -+ */ -+ g_object_class_install_property (gobject_class, -+ PROP_LANGUAGE, -+ g_param_spec_string ("language", -+ "language", -+ "language", -+ "", -+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); -+ -+ g_type_class_add_private (class, sizeof (IBusFontSetPrivate)); -+ FT_Init_FreeType (&m_ftlibrary); -+ m_scaled_font_table = g_hash_table_new_full ( -+ g_str_hash, g_str_equal, -+ g_free, -+ (GDestroyNotify) cairo_scaled_font_destroy); -+ m_hb_font_table = g_hash_table_new_full ( -+ g_str_hash, g_str_equal, -+ g_free, -+ (GDestroyNotify) hb_font_destroy); -+ -+ /* hb_glyph_t is not available in Vala so override it with IBusGlyph. */ -+ g_assert (sizeof (dummy) == sizeof (dummy2)); -+ g_assert (sizeof (dummy.index) == sizeof (dummy2.index)); -+ g_assert (sizeof (dummy.x) == sizeof (dummy2.x)); -+ g_assert (sizeof (dummy.y) == sizeof (dummy2.y)); -+} -+ -+static void -+ibus_fontset_init (IBusFontSet *fontset) -+{ -+ fontset->priv = IBUS_FONTSET_GET_PRIVATE (fontset); -+} -+ -+ -+static GObject * -+ibus_fontset_constructor (GType type, -+ guint n, -+ GObjectConstructParam *args) -+{ -+ GObject *object; -+ IBusFontSet *fontset; -+ const gchar *family; -+ guint size; -+ -+ object = G_OBJECT_CLASS (ibus_fontset_parent_class)->constructor ( -+ type, n ,args); -+ fontset = IBUS_FONTSET (object); -+ family = ibus_fontset_get_family (fontset); -+ size = ibus_fontset_get_size (fontset); -+ ibus_fontset_update_fcfontset (fontset); -+ if (family != NULL && size > 0) { -+ /* cache the font */ -+ ibus_fontset_cairo_scaled_font_new_with_font (family, -+ size, -+ TRUE); -+ } -+ return object; -+} -+ -+static void -+ibus_fontset_destroy (IBusFontSet *fontset) -+{ -+ g_clear_pointer (&fontset->priv->family, g_free); -+ g_clear_pointer (&fontset->priv->language, g_free); -+} -+ -+static void -+ibus_fontset_set_property (IBusFontSet *fontset, -+ guint prop_id, -+ const GValue *value, -+ GParamSpec *pspec) -+{ -+ switch (prop_id) { -+ case PROP_FAMILY: -+ ibus_fontset_set_family (fontset, g_value_get_string (value)); -+ break; -+ case PROP_SIZE: -+ ibus_fontset_set_size (fontset, g_value_get_uint (value)); -+ break; -+ case PROP_LANGUAGE: -+ ibus_fontset_set_language (fontset, g_value_get_string (value)); -+ break; -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec); -+ } -+} -+ -+static void -+ibus_fontset_get_property (IBusFontSet *fontset, -+ guint prop_id, -+ GValue *value, -+ GParamSpec *pspec) -+{ -+ switch (prop_id) { -+ case PROP_FAMILY: -+ g_value_set_string (value, ibus_fontset_get_family (fontset)); -+ break; -+ case PROP_SIZE: -+ g_value_set_uint (value, ibus_fontset_get_size (fontset)); -+ break; -+ case PROP_LANGUAGE: -+ g_value_set_string (value, ibus_fontset_get_language (fontset)); -+ break; -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec); -+ } -+} -+ -+static cairo_scaled_font_t * -+ibus_fontset_cairo_scaled_font_new_with_font (const gchar *family, -+ guint size, -+ gboolean has_color) -+{ -+ gchar *font_name; -+ cairo_scaled_font_t *scaled_font = NULL; -+ FcPattern *pattern, *resolved; -+ FcResult result; -+ cairo_font_options_t *font_options; -+ double pixel_size = 0.; -+ FcMatrix fc_matrix, *fc_matrix_val; -+ cairo_font_face_t *cairo_face = NULL; -+ cairo_matrix_t font_matrix; -+ cairo_matrix_t ctm; -+ int i; -+ -+ g_return_val_if_fail (family != NULL, NULL); -+ g_return_val_if_fail (m_scaled_font_table != NULL, NULL); -+ -+ if (m_color_supported) { -+ font_name = g_strdup_printf ("%s %u:color=%s", -+ family, size, -+ has_color ? "TRUE" : "FALSE"); -+ } else { -+ font_name = g_strdup_printf ("%s %u", family, size); -+ } -+ scaled_font = g_hash_table_lookup (m_scaled_font_table, font_name); -+ if (scaled_font != NULL) { -+ g_free (font_name); -+ return scaled_font; -+ } -+ pattern = FcPatternCreate (); -+ FcPatternAddString (pattern, FC_FAMILY, (FcChar8*) family); -+ FcPatternAddDouble (pattern, FC_SIZE, (double) size); -+/* FC_VERSION is for the build check of FC_COLOR and m_color_supported is -+ * for the runtime check. -+ */ -+#if FC_VERSION >= 21205 -+ if (m_color_supported) -+ FcPatternAddBool (pattern, FC_COLOR, has_color); -+#endif -+ FcPatternAddDouble (pattern, FC_DPI, 96); -+ FcConfigSubstitute(NULL, pattern, FcMatchPattern); -+ font_options = cairo_font_options_create (); -+ cairo_ft_font_options_substitute (font_options, pattern); -+ FcDefaultSubstitute (pattern); -+ resolved = FcFontMatch (NULL, pattern, &result); -+ FcPatternDestroy (pattern); -+ FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size); -+ if (pixel_size == 0.) -+ g_warning ("Failed to scaled the font: %s %u", family, size); -+ cairo_face = cairo_ft_font_face_create_for_pattern (resolved); -+ FcMatrixInit (&fc_matrix); -+ for (i = 0; -+ FcPatternGetMatrix (resolved, FC_MATRIX, i, &fc_matrix_val) -+ == FcResultMatch; -+ i++) { -+ FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val); -+ } -+ FcPatternDestroy (resolved); -+ cairo_matrix_init (&font_matrix, -+ fc_matrix.xx, -fc_matrix.yx, -+ -fc_matrix.xy, fc_matrix.yy, -+ 0., 0.); -+ if (pixel_size != 0.) -+ cairo_matrix_scale (&font_matrix, pixel_size, pixel_size); -+ cairo_matrix_init_identity (&ctm); -+ scaled_font = cairo_scaled_font_create (cairo_face, -+ &font_matrix, &ctm, -+ font_options); -+ cairo_font_face_destroy (cairo_face); -+ if (font_name) -+ g_hash_table_insert(m_scaled_font_table, font_name, scaled_font); -+ -+ return scaled_font; -+} -+ -+static hb_font_t * -+ibus_fontset_hb_font_new_with_font_path (const gchar *font_path) -+{ -+ hb_font_t *hb_font; -+ GError *error = NULL; -+ GMappedFile *mf; -+ char *font_data = NULL; -+ gsize len; -+ hb_blob_t *hb_blob; -+ hb_face_t *hb_face; -+ -+ g_return_val_if_fail (font_path != NULL, NULL); -+ g_return_val_if_fail (m_hb_font_table != NULL, NULL); -+ -+ hb_font = g_hash_table_lookup (m_hb_font_table, font_path); -+ if (hb_font != NULL) -+ return hb_font; -+ -+ mf = g_mapped_file_new (font_path, FALSE, &error); -+ if (mf == NULL) { -+ g_warning ("Not found font %s", font_path); -+ return NULL; -+ } -+ font_data = g_mapped_file_get_contents (mf); -+ len = g_mapped_file_get_length (mf); -+ if (len == 0) { -+ g_warning ("zero size font %s", font_path); -+ g_mapped_file_unref (mf); -+ return NULL; -+ } -+ hb_blob = hb_blob_create (font_data, len, -+ HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, -+ mf, (hb_destroy_func_t)g_mapped_file_unref); -+ hb_face = hb_face_create (hb_blob, 0); -+ hb_blob_destroy (hb_blob); -+ hb_font = hb_font_create (hb_face); -+ unsigned int upem = hb_face_get_upem (hb_face); -+ hb_font_set_scale (hb_font, upem, upem); -+ hb_face_destroy (hb_face); -+ hb_ot_font_set_funcs (hb_font); -+ g_hash_table_insert (m_hb_font_table, g_strdup (font_path), hb_font); -+ -+ return hb_font; -+} -+ -+static void -+get_font_extents_with_scaled_font (cairo_scaled_font_t *scaled_font, -+ PangoRectangle *font_rect) -+{ -+ cairo_font_extents_t font_extents; -+ -+ g_assert (scaled_font != NULL && font_rect != NULL); -+ -+ cairo_scaled_font_extents (scaled_font, &font_extents); -+ font_rect->x = 0; -+ font_rect->y = - pango_units_from_double (font_extents.ascent); -+ font_rect->width = 0; -+ font_rect->height = pango_units_from_double ( -+ font_extents.ascent + font_extents.descent); -+} -+ -+static void -+get_glyph_extents_with_scaled_hb_font (const gchar *str, -+ cairo_scaled_font_t *scaled_font, -+ hb_font_t *hb_font, -+ PangoRectangle *font_rect, -+ IBusCairoLine **cairo_lines, -+ FcChar8 *fallback_family) -+{ -+ gboolean has_unknown_glyph = FALSE; -+ hb_buffer_t *hb_buffer; -+ unsigned int len, n, i; -+ hb_glyph_info_t *info; -+ hb_glyph_position_t *pos; -+ double x; -+ cairo_glyph_t *glyph; -+ cairo_text_extents_t text_extents = { 0, }; -+ -+ g_return_if_fail (str != NULL); -+ -+ hb_buffer = hb_buffer_create (); -+ hb_buffer_add_utf8 (hb_buffer, str, -1, 0, -1); -+ hb_buffer_guess_segment_properties (hb_buffer); -+ for (n = 0; *cairo_lines && (*cairo_lines)[n].scaled_font; n++); -+ if (n == 0) -+ *cairo_lines = g_new0 (IBusCairoLine, 2); -+ else -+ *cairo_lines = g_renew (IBusCairoLine, *cairo_lines, n + 2); -+ (*cairo_lines)[n + 1].scaled_font = NULL; -+ (*cairo_lines)[n + 1].num_glyphs = 0; -+ (*cairo_lines)[n + 1].glyphs = NULL; -+ hb_shape (hb_font, hb_buffer, NULL, 0); -+ len = hb_buffer_get_length (hb_buffer); -+ info = hb_buffer_get_glyph_infos (hb_buffer, NULL); -+ pos = hb_buffer_get_glyph_positions (hb_buffer, NULL); -+ (*cairo_lines)[n].scaled_font = scaled_font; -+ (*cairo_lines)[n].num_glyphs = len; -+ (*cairo_lines)[n].glyphs = (IBusGlyph*) cairo_glyph_allocate (len + 1); -+ x = 0.; -+ for (i = 0; i < len; i++) { -+ hb_codepoint_t c = info[i].codepoint; -+ if (c) { -+ (*cairo_lines)[n].glyphs[i].index = info[i].codepoint; -+ (*cairo_lines)[n].glyphs[i].x = x; -+ (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE; -+ glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]); -+ cairo_scaled_font_glyph_extents (scaled_font, glyph, -+ 1, &text_extents); -+ x += text_extents.width; -+ } else { -+ has_unknown_glyph = TRUE; -+ c = g_utf8_get_char (str); -+ (*cairo_lines)[n].glyphs[i].index = PANGO_GET_UNKNOWN_GLYPH (c); -+ (*cairo_lines)[n].glyphs[i].x = x; -+ (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE; -+ glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]); -+ cairo_scaled_font_glyph_extents (scaled_font, glyph, -+ 1, &text_extents); -+ x += 10; -+ } -+ } -+ (*cairo_lines)[n].glyphs[i].index = -1; -+ (*cairo_lines)[n].glyphs[i].x = 0; -+ (*cairo_lines)[n].glyphs[i].y = 0; -+ glyph = (cairo_glyph_t *) (*cairo_lines)[n].glyphs; -+ cairo_scaled_font_glyph_extents (scaled_font, glyph, -+ len, &text_extents); -+ if (text_extents.width) { -+ font_rect->width = pango_units_from_double (text_extents.width); -+ } else { -+ font_rect->width = font_rect->height; -+ } -+ if (has_unknown_glyph && fallback_family != NULL) { -+ cairo_scaled_font_t *unknown_font; -+ unknown_font = ibus_fontset_cairo_scaled_font_new_with_font ( -+ (const gchar *) fallback_family, -+ UNKNOWN_FONT_SIZE, -+ FALSE); -+ (*cairo_lines)[n].scaled_font = unknown_font; -+ } -+ hb_buffer_destroy (hb_buffer); -+} -+ -+static void -+get_string_extents_with_font (const gchar *str, -+ FontPerChar *buff, -+ cairo_rectangle_int_t *rect, -+ IBusCairoLine **cairo_lines) -+{ -+ FcChar8 *family = NULL; -+ FcChar8 *font_path = NULL; -+ gboolean has_color = TRUE; -+ guint size = 0; -+ cairo_scaled_font_t *scaled_font = NULL; -+ PangoRectangle font_rect = { 0, }; -+ hb_font_t *hb_font; -+ -+ g_return_if_fail (str != NULL); -+ g_return_if_fail (buff != NULL && buff->fcfont != NULL); -+ -+ FcPatternGetString (buff->fcfont, FC_FAMILY, 0, &family); -+ g_return_if_fail (family != NULL); -+#if FC_VERSION >= 21205 -+ if (m_color_supported) -+ FcPatternGetBool (buff->fcfont, FC_COLOR, 0, &has_color); -+#endif -+ size = m_size; -+ if (size == 0) { -+ g_warning ("Font size is not right for font %s.", family); -+ size = 14; -+ } -+ scaled_font = ibus_fontset_cairo_scaled_font_new_with_font ( -+ (const gchar *) family, -+ size, -+ has_color); -+ g_return_if_fail (scaled_font != NULL); -+ get_font_extents_with_scaled_font (scaled_font, &font_rect); -+ -+ FcPatternGetString (buff->fcfont, FC_FILE, 0, &font_path); -+ g_return_if_fail (font_path != NULL); -+ hb_font = ibus_fontset_hb_font_new_with_font_path ( -+ (const gchar *) font_path); -+ if (hb_font == NULL) -+ return; -+ get_glyph_extents_with_scaled_hb_font (str, -+ scaled_font, -+ hb_font, -+ &font_rect, -+ cairo_lines, -+ family); -+ rect->width += font_rect.width / PANGO_SCALE; -+ rect->height += font_rect.height / PANGO_SCALE; -+} -+ -+static FT_Face -+ibus_fontset_get_ftface_from_fcfont (IBusFontSet *fontset, -+ FcPattern *fcfont) -+{ -+ FcChar8 *font_file = NULL; -+ FT_Face ft_face; -+ guint size = ibus_fontset_get_size (fontset); -+ -+ g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); -+ g_return_val_if_fail (fcfont != NULL, NULL); -+ -+ size = ibus_fontset_get_size (fontset); -+ FcPatternGetString (fcfont, FC_FILE, 0, &font_file); -+ FT_New_Face (m_ftlibrary, (const gchar *) font_file, 0, &ft_face); -+ FT_Set_Pixel_Sizes (ft_face, size, size); -+ return ft_face; -+} -+ -+static void -+_cairo_show_unknown_glyphs (cairo_t *cr, -+ const cairo_glyph_t *glyphs, -+ guint num_glyphs, -+ guint width, -+ guint height) -+{ -+ gunichar ch; -+ gboolean invalid_input; -+ int rows = 2; -+ int cols; -+ int row, col; -+ char buf[7]; -+ double cx = 0.; -+ double cy; -+ const double box_descent = 3.; -+ double x0; -+ double y0; -+ const double digit_width = 5.; -+ const double digit_height= 6.; -+ char hexbox_string[2] = {0, 0}; -+ -+ g_assert (glyphs != NULL); -+ g_assert (num_glyphs > 0); -+ -+ ch = glyphs[0].index & ~PANGO_GLYPH_UNKNOWN_FLAG; -+ invalid_input = G_UNLIKELY (glyphs[0].index == PANGO_GLYPH_INVALID_INPUT || -+ ch > 0x10FFFF); -+ if (G_UNLIKELY (invalid_input)) { -+ g_warning ("Unsupported U+%06X", ch); -+ return; -+ } -+ -+ cairo_save (cr); -+ -+ cols = (ch > 0xffff ? 6 : 4) / rows; -+ g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch); -+ cy = (double) height; -+ x0 = cx + box_descent + XPAD / 2; -+ y0 = cy - box_descent - YPAD / 2; -+ -+ for (row = 0; row < rows; row++) { -+ double y = y0 - (rows - 1 - row) * (digit_height + YPAD); -+ for (col = 0; col < cols; col++) { -+ double x = x0 + col * (digit_width + XPAD); -+ cairo_move_to (cr, x, y); -+ hexbox_string[0] = buf[row * cols + col]; -+ cairo_show_text (cr, hexbox_string); -+ } -+ } -+ cairo_move_to (cr, XPAD, YPAD); -+ cairo_line_to (cr, width - XPAD, YPAD); -+ cairo_line_to (cr, width - XPAD, -+ height - YPAD); -+ cairo_line_to (cr, XPAD, height - YPAD); -+ cairo_line_to (cr, XPAD, YPAD); -+ cairo_set_line_width (cr, 1.); -+ cairo_stroke (cr); -+ -+ cairo_restore (cr); -+} -+ -+static void -+ibus_fcfontset_destroy_ex (FcFontSetEx *fcfontset_ex) -+{ -+ FcFontSet *fcfontset = NULL; -+ int i; -+ -+ g_return_if_fail (fcfontset_ex != NULL); -+ -+ for (i = 0; i < fcfontset_ex->nfont; i++) -+ FT_Done_Face (fcfontset_ex->ft_faces[i]); -+ fcfontset = FcFontSetCreate (); -+ fcfontset->nfont = fcfontset_ex->nfont; -+ fcfontset->sfont = fcfontset_ex->sfont; -+ fcfontset->fonts = fcfontset_ex->fonts; -+ FcFontSetDestroy (fcfontset); -+ g_free (fcfontset_ex->ft_faces); -+ g_free (fcfontset_ex); -+} -+ -+IBusCairoLine * -+ibus_cairo_line_copy (IBusCairoLine *cairo_lines) -+{ -+ IBusCairoLine *ret; -+ guint n, i, j, num_glyphs; -+ if (!cairo_lines) -+ return NULL; -+ -+ for (n = 0; cairo_lines[n].scaled_font; n++); -+ ret = g_new0 (IBusCairoLine, n + 1); -+ for (i = 0; i < n; i++) { -+ ret[i].scaled_font = cairo_lines[i].scaled_font; -+ num_glyphs = cairo_lines[i].num_glyphs; -+ ret[i].num_glyphs = num_glyphs; -+ ret[i].glyphs = (IBusGlyph *) cairo_glyph_allocate (num_glyphs + 1); -+ for (j = 0; j < num_glyphs; j++) { -+ ret[i].glyphs[j] = cairo_lines[i].glyphs[j]; -+ } -+ ret[i].glyphs[j].index = -1; -+ ret[i].glyphs[j].x = 0; -+ ret[i].glyphs[j].y = 0; -+ } -+ ret[i].scaled_font = NULL; -+ ret[i].num_glyphs = 0; -+ ret[i].glyphs = NULL; -+ return ret; -+} -+ -+void -+ibus_cairo_line_free (IBusCairoLine *cairo_lines) -+{ -+ guint i; -+ if (!cairo_lines) -+ return; -+ for (i = 0; cairo_lines[i].scaled_font; i++) { -+ g_free (cairo_lines[i].glyphs); -+ } -+ g_free (cairo_lines); -+} -+ -+IBusRequisitionEx * -+ibus_requisition_ex_copy (IBusRequisitionEx *req) -+{ -+ IBusRequisitionEx *ret; -+ if (!req) -+ return NULL; -+ ret = g_new0 (IBusRequisitionEx, 1); -+ ret->width = req->width; -+ ret->height = req->height; -+ ret->cairo_lines = ibus_cairo_line_copy (req->cairo_lines); -+ return ret; -+} -+ -+void -+ibus_requisition_ex_free (IBusRequisitionEx *req) -+{ -+ if (!req) -+ return; -+ g_clear_pointer (&req->cairo_lines, ibus_cairo_line_free); -+ g_free (req); -+} -+ -+IBusFontSet * -+ibus_fontset_new (const gchar *first_property_name, ...) -+{ -+ va_list var_args; -+ IBusFontSet *fontset; -+ -+ g_assert (first_property_name); -+ -+ va_start (var_args, first_property_name); -+ fontset = (IBusFontSet *)g_object_new_valist (IBUS_TYPE_FONTSET, -+ first_property_name, -+ var_args); -+ va_end (var_args); -+ g_assert (fontset->priv->family); -+ g_assert (fontset->priv->language); -+ return fontset; -+} -+ -+IBusFontSet * -+ibus_fontset_new_with_font (const gchar *family, -+ guint size, -+ const gchar *language) -+{ -+ return ibus_fontset_new ("family", family, -+ "size", size, -+ "language", language, -+ NULL); -+} -+ -+void -+ibus_fontset_exit () -+{ -+ g_clear_pointer (&m_ftlibrary, FT_Done_FreeType); -+} -+ -+const gchar * -+ibus_fontset_get_family (IBusFontSet *fontset) -+{ -+ g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); -+ return fontset->priv->family; -+} -+ -+void -+ibus_fontset_set_family (IBusFontSet *fontset, -+ const gchar *family) -+{ -+ g_return_if_fail (IBUS_IS_FONTSET (fontset)); -+ g_free (fontset->priv->family); -+ fontset->priv->family = g_strdup (family); -+} -+ -+guint -+ibus_fontset_get_size (IBusFontSet *fontset) -+{ -+ g_return_val_if_fail (IBUS_IS_FONTSET (fontset), 0); -+ return fontset->priv->size; -+} -+ -+void -+ibus_fontset_set_size (IBusFontSet *fontset, -+ guint size) -+{ -+ g_return_if_fail (IBUS_IS_FONTSET (fontset)); -+ fontset->priv->size = size; -+} -+ -+const gchar * -+ibus_fontset_get_language (IBusFontSet *fontset) -+{ -+ g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); -+ return fontset->priv->language; -+} -+ -+void -+ibus_fontset_set_language (IBusFontSet *fontset, -+ const gchar *language) -+{ -+ g_return_if_fail (IBUS_IS_FONTSET (fontset)); -+ g_free (fontset->priv->language); -+ fontset->priv->language = g_strdup (language); -+} -+ -+gboolean -+ibus_fontset_update_fcfontset (IBusFontSet *fontset) -+{ -+ FcPattern *pattern; -+ const gchar *family; -+ guint size; -+ const gchar *language; -+ gboolean update_fontset = FALSE; -+ FcFontSet *fcfontset = NULL; -+ FcResult result; -+ -+ g_return_val_if_fail (IBUS_IS_FONTSET (fontset), FALSE); -+ -+ pattern = FcPatternCreate (); -+ family = fontset->priv->family; -+ size = fontset->priv->size; -+ language = fontset->priv->language; -+ -+ if (g_strcmp0 (m_family, family)) { -+ g_free (m_family); -+ m_family = g_strdup (family); -+ update_fontset = TRUE; -+ } -+ if (m_size != size) { -+ m_size = size; -+ update_fontset = TRUE; -+ } -+ if (g_strcmp0 (m_language, language)) { -+ g_free (m_language); -+ m_language = g_strdup (language); -+ update_fontset = TRUE; -+ } -+ if (!update_fontset && m_fcfontset != NULL) -+ return FALSE; -+ -+ if (m_font_index_per_char_table) -+ g_hash_table_destroy (m_font_index_per_char_table); -+ if (m_fcfontset) -+ g_clear_pointer (&m_fcfontset, ibus_fcfontset_destroy_ex); -+ -+ m_font_index_per_char_table = g_hash_table_new (g_direct_hash, -+ g_direct_equal); -+ if (g_strcmp0 (family, "")) -+ FcPatternAddString (pattern, FC_FAMILY, (const FcChar8*) family); -+ if (size > 0) -+ FcPatternAddDouble (pattern, FC_SIZE, (double) size); -+ if (g_strcmp0 (language, "")) -+ FcPatternAddString (pattern, FC_LANG, (const FcChar8*) language); -+ FcPatternAddInteger (pattern, FC_WEIGHT, FC_WEIGHT_NORMAL); -+ FcPatternAddInteger (pattern, FC_WIDTH, FC_WIDTH_NORMAL); -+ FcPatternAddInteger (pattern, FC_DPI, 96); -+#if FC_VERSION >= 21205 -+ if (m_color_supported && -+ (!g_ascii_strncasecmp (family, MONOSPACE, strlen (MONOSPACE)) || -+ !g_ascii_strncasecmp (family, SERIF, strlen (SERIF)) || -+ !g_ascii_strncasecmp (family, SANS, strlen (SANS)))) { -+ FcPatternAddBool (pattern, FC_COLOR, TRUE); -+ } -+#endif -+ FcConfigSubstitute (NULL, pattern, FcMatchPattern); -+ FcConfigSubstitute (NULL, pattern, FcMatchFont); -+ FcDefaultSubstitute (pattern); -+ fcfontset = FcFontSort (NULL, pattern, FcTrue, NULL, &result); -+ FcPatternDestroy (pattern); -+ if (result == FcResultNoMatch || fcfontset->nfont == 0) { -+ g_warning ("No FcFontSet for %s", family ? family : "(null)"); -+ return FALSE; -+ } -+ m_fcfontset = g_new0 (FcFontSetEx, 1); -+ m_fcfontset->nfont = fcfontset->nfont; -+ m_fcfontset->sfont = fcfontset->sfont; -+ m_fcfontset->fonts = fcfontset->fonts; -+ m_fcfontset->ft_faces = g_new0 (FT_Face, fcfontset->nfont); -+ fcfontset->nfont = 0; -+ fcfontset->sfont = 0; -+ fcfontset->fonts = NULL; -+ FcFontSetDestroy (fcfontset); -+ return TRUE; -+} -+ -+void -+ibus_fontset_unref (IBusFontSet *fontset) -+{ -+ g_object_unref (fontset); -+} -+ -+IBusRequisitionEx * -+ibus_fontset_get_preferred_size_hb (IBusFontSet *fontset, -+ const gchar *text, -+ cairo_rectangle_int_t *widest) -+{ -+ gchar *copied_text; -+ gchar *p; -+ FontPerChar *buff; -+ IBusCairoLine *cairo_lines = NULL; -+ IBusRequisitionEx *req = NULL; -+ GString *str = NULL; -+ int text_length; -+ int i, n = 0; -+ -+ g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); -+ g_return_val_if_fail (m_fcfontset != NULL, NULL); -+ -+ copied_text = g_strdup (text); -+ text_length = g_utf8_strlen (text, -1); -+ buff = g_slice_alloc0 (sizeof (FontPerChar) * text_length); -+ str = g_string_new (NULL); -+ -+ for (p = copied_text; *p != '\0'; p = g_utf8_next_char (p)) { -+ gunichar c = g_utf8_get_char (p); -+ gboolean has_glyphs = FALSE; -+ buff[n].ch = c; -+ if ((c == 0xfe0eu || c == 0xfe0fu) && n > 0) { -+ buff[n].fcfont = buff[n-1].fcfont; -+ ++n; -+ continue; -+ } -+ i = GPOINTER_TO_INT (g_hash_table_lookup (m_font_index_per_char_table, -+ GINT_TO_POINTER (c))); -+ if (i > 0) { -+ i--; -+ if (i >= m_fcfontset->nfont) { -+ g_warning ("i:%d >= m_fcfontset->nfont:%d", -+ i, m_fcfontset->nfont); -+ } else { -+ buff[n].fcfont = m_fcfontset->fonts[i]; -+ has_glyphs = TRUE; -+ } -+ } -+ for (; i < m_fcfontset->nfont; i++) { -+ if (!has_glyphs && g_unichar_iscntrl (c) && !g_unichar_isspace (c)) -+ break; -+ FT_Face ft_face = m_fcfontset->ft_faces[i]; -+ if (!has_glyphs && ft_face == 0) { -+ ft_face = ibus_fontset_get_ftface_from_fcfont ( -+ fontset, -+ m_fcfontset->fonts[i]); -+ m_fcfontset->ft_faces[i] = ft_face; -+ } -+ if (has_glyphs || FT_Get_Char_Index (ft_face, c) != 0) { -+ FcChar8 *font_file = NULL; -+ FcPatternGetString (m_fcfontset->fonts[i], FC_FILE, 0, &font_file); -+ buff[n].fcfont = m_fcfontset->fonts[i]; -+ if (!has_glyphs) { -+ g_hash_table_insert (m_font_index_per_char_table, -+ GINT_TO_POINTER (c), -+ GINT_TO_POINTER (i + 1)); -+ } -+ if (n > 0 && buff[n - 1].fcfont != buff[n].fcfont) { -+ get_string_extents_with_font (str->str, -+ &buff[n - 1], -+ widest, -+ &cairo_lines); -+ g_string_free (str, TRUE); -+ str = g_string_new (NULL); -+ g_string_append_unichar (str, c); -+ } else { -+ g_string_append_unichar (str, c); -+ } -+ ++n; -+ has_glyphs = TRUE; -+ break; -+ } -+ } -+ if (!has_glyphs) { -+ if (n > 0) { -+ buff[n].fcfont = buff[n - 1].fcfont; -+ } else { -+ /* Search a font for non-glyph char to draw the code points -+ * likes Pango. -+ */ -+ for (i = 0; i < m_fcfontset->nfont; i++) { -+ FT_Face ft_face = m_fcfontset->ft_faces[i]; -+ if (ft_face == 0) { -+ ft_face = ibus_fontset_get_ftface_from_fcfont ( -+ fontset, -+ m_fcfontset->fonts[i]); -+ m_fcfontset->ft_faces[i] = ft_face; -+ } -+ /* Check alphabets instead of space or digits -+ * because 'Noto Emoji Color' font's digits are -+ * white color and cannot change the font color. -+ * the font does not have alphabets. -+ */ -+ if (FT_Get_Char_Index (ft_face, 'A') != 0) { -+ FcChar8 *font_file = NULL; -+ FcPatternGetString (m_fcfontset->fonts[i], FC_FILE, 0, &font_file); -+ buff[n].fcfont = m_fcfontset->fonts[i]; -+ g_hash_table_insert (m_font_index_per_char_table, -+ GINT_TO_POINTER (c), -+ GINT_TO_POINTER (i + 1)); -+ has_glyphs = TRUE; -+ break; -+ } -+ } -+ if (!has_glyphs) { -+ buff[n].fcfont = m_fcfontset->fonts[0]; -+ g_hash_table_insert (m_font_index_per_char_table, -+ GINT_TO_POINTER (c), -+ GINT_TO_POINTER (1)); -+ g_warning ("Not found fonts for unicode %04X at %d in %s", -+ c, n, text); -+ } -+ } -+ n++; -+ g_string_append_unichar (str, c); -+ } -+ } -+ if (str->str) { -+ get_string_extents_with_font (str->str, -+ &buff[n - 1], -+ widest, -+ &cairo_lines); -+ g_string_free (str, TRUE); -+ } -+ g_slice_free1 (sizeof (FontPerChar) * text_length, buff); -+ g_free (copied_text); -+ widest->width += XPAD * 2; -+ widest->height += YPAD * 2; -+ req = g_new0 (IBusRequisitionEx, 1); -+ req->width = widest->width; -+ req->height = widest->height; -+ req->cairo_lines = cairo_lines; -+ return req; -+} -+ -+void -+ibus_fontset_draw_cairo_with_requisition_ex (IBusFontSet *fontset, -+ cairo_t *cr, -+ IBusRequisitionEx *ex) -+{ -+ IBusCairoLine *cairo_lines; -+ int i; -+ -+ g_return_if_fail (IBUS_IS_FONTSET (fontset)); -+ g_return_if_fail (cr != NULL); -+ g_return_if_fail (ex != NULL); -+ -+ cairo_lines = ex->cairo_lines; -+ g_return_if_fail (cairo_lines != NULL); -+ -+ for (i = 0; cairo_lines[i].scaled_font; i++) { -+ const cairo_glyph_t *glyphs = (cairo_glyph_t *) cairo_lines[i].glyphs; -+ guint num_glyphs = cairo_lines[i].num_glyphs; -+ -+ cairo_ft_scaled_font_lock_face (cairo_lines[i].scaled_font); -+ cairo_set_scaled_font (cr, cairo_lines[i].scaled_font); -+ if (num_glyphs > 0 && glyphs[0].index & PANGO_GLYPH_UNKNOWN_FLAG) { -+ _cairo_show_unknown_glyphs (cr, glyphs, num_glyphs, -+ ex->width, ex->height); -+ } else { -+ cairo_show_glyphs (cr, glyphs, num_glyphs); -+ } -+ cairo_ft_scaled_font_unlock_face (cairo_lines[i].scaled_font); -+ } -+} -diff --git a/ui/gtk3/ibusfontset.h b/ui/gtk3/ibusfontset.h -new file mode 100644 -index 00000000..efcaa286 ---- /dev/null -+++ b/ui/gtk3/ibusfontset.h -@@ -0,0 +1,302 @@ -+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ -+/* vim:set et sts=4: */ -+/* ibus - The Input Bus -+ * Copyright (C) 2017 Takao Fujiwara -+ * Copyright (C) 2017 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA -+ */ -+ -+#ifndef __IBUS_HARFBUZZ_H_ -+#define __IBUS_HARFBUZZ_H_ -+ -+/** -+ * SECTION: ibusfontset -+ * @short_description: Object for HarfBuzz and Fontconfig. -+ * @title: IBusFontSet -+ * @stability: Unstable -+ * -+ * IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering -+ * on Cairo context. -+ * Current Pango changes fonts by emoji variants and draws the separated -+ * glyphs [1] but actually the emoji characters with variants can be drawn -+ * as one glyph so this class manages Fontconfig fontsets to select a font, -+ * HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs. -+ * -+ * [1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669 -+ * https://bugzilla.gnome.org/show_bug.cgi?id=781123 -+ */ -+ -+#include -+#include -+ -+#define IBUS_TYPE_CAIRO_LINE (ibus_cairo_line_get_type ()) -+#define IBUS_TYPE_REQUISITION_EX (ibus_requisition_ex_get_type ()) -+#define IBUS_TYPE_FONTSET (ibus_fontset_get_type ()) -+#define IBUS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_CAST (\ -+ (obj), \ -+ IBUS_TYPE_FONTSET, \ -+ IBusFontSet)) -+#define IBUS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (\ -+ (klass), \ -+ IBUS_TYPE_FONTSET, \ -+ IBusFontSetClass)) -+#define IBUS_IS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (\ -+ (obj), \ -+ IBUS_TYPE_FONTSET)) -+#define IBUS_IS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (\ -+ (klass), \ -+ IBUS_TYPE_FONTSET)) -+#define IBUS_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (\ -+ (obj), \ -+ IBUS_TYPE_FONTSET, \ -+ IBusFontSetClass)) -+ -+G_BEGIN_DECLS -+ -+typedef struct _IBusGlyph IBusGlyph; -+typedef struct _IBusCairoLine IBusCairoLine; -+typedef struct _IBusRequisitionEx IBusRequisitionEx; -+typedef struct _IBusFontSet IBusFontSet; -+typedef struct _IBusFontSetPrivate IBusFontSetPrivate; -+typedef struct _IBusFontSetClass IBusFontSetClass; -+ -+struct _IBusGlyph { -+ unsigned long index; -+ double x; -+ double y; -+}; -+ -+struct _IBusCairoLine { -+ IBusGlyph *glyphs; -+ guint num_glyphs; -+ cairo_scaled_font_t *scaled_font; -+ gpointer pdummy[5]; -+}; -+ -+struct _IBusRequisitionEx { -+ guint width; -+ guint height; -+ IBusCairoLine *cairo_lines; -+ gpointer pdummy[5]; -+}; -+ -+struct _IBusFontSet { -+ IBusObject parent_instance; -+ IBusFontSetPrivate *priv; -+}; -+ -+struct _IBusFontSetClass { -+ IBusObjectClass parent_class; -+ /* signals */ -+ /*< private >*/ -+ /* padding */ -+ gpointer pdummy[10]; -+}; -+ -+GType ibus_cairo_line_get_type (void) G_GNUC_CONST; -+ -+/** -+ * ibus_cairo_line_copy: -+ * @cairo_lines: #IBusCairoLine -+ * -+ * Creates a copy of @cairo_liens, which should be freed with -+ * ibus_cairo_line_free(). Primarily used by language bindings, -+ * not that useful otherwise (since @req can just be copied -+ * by assignment in C). -+ * -+ * Returns: the newly allocated #IBusCairoLine, which should -+ * be freed with ibus_cairo_line_free(), or %NULL -+ * if @cairo_lines was %NULL. -+ **/ -+IBusCairoLine * ibus_cairo_line_copy (IBusCairoLine *cairo_lines); -+ -+/** -+ * ibus_cairo_line_free: -+ * @cairo_lines: #IBusCairoLine -+ * -+ * Free an #IBusCairoLine. -+ */ -+void ibus_cairo_line_free (IBusCairoLine *cairo_lines); -+ -+ -+GType ibus_requisition_ex_get_type (void) G_GNUC_CONST; -+ -+/** -+ * ibus_requisition_ex_copy: -+ * @req: #IBusRequisitionEx -+ * -+ * Creates a copy of @req, which should be freed with -+ * ibus_requisition_ex_free(). Primarily used by language bindings, -+ * not that useful otherwise (since @req can just be copied -+ * by assignment in C). -+ * -+ * Returns: the newly allocated #IBusRequisitionEx, which should -+ * be freed with ibus_requisition_ex_free(), or %NULL -+ * if @req was %NULL. -+ **/ -+IBusRequisitionEx * -+ ibus_requisition_ex_copy (IBusRequisitionEx *req); -+ -+/** -+ * ibus_requisition_ex_free: -+ * @req: #IBusRequisitionEx -+ * -+ * Free an #IBusRequisitionEx. -+ */ -+void ibus_requisition_ex_free (IBusRequisitionEx *req); -+ -+ -+GType ibus_fontset_get_type (void); -+ -+/** -+ * ibus_fontset_new: -+ * @first_property_name: -+ * -+ * Creates a new #IBusFcFontSet. -+ * -+ * Returns: (transfer full): A newly allocated #IBusFontSet and includes -+ * #FcFontSet internally. E.g. ibus_fontset_new ("family", -+ * "Noto Emoji Color", "size", 16, "language", "ja-jp"); -+ */ -+IBusFontSet * ibus_fontset_new (const gchar -+ *first_property_name, -+ ...); -+ -+/** -+ * ibus_fontset_new_with_font: -+ * @family: font family -+ * @size: font size -+ * @language: font language -+ * -+ * Creates a new #IBusFcFontSet. -+ * -+ * Returns: (transfer full): A newly allocated #IBusFcFontSet and includes -+ * #FcFontSet internally. -+ */ -+IBusFontSet * ibus_fontset_new_with_font (const gchar *family, -+ guint size, -+ const gchar *language); -+/** -+ * ibus_fontset_get_family: -+ * @fontset: #IBusFcFontSet -+ * -+ * Return the base font family of #FcFontSet -+ * -+ * Returns: Base font family of #FcFontSet -+ */ -+const gchar * ibus_fontset_get_family (IBusFontSet *fontset); -+ -+/** -+ * ibus_fontset_set_family: -+ * @fontset: #IBusFcFontSet -+ * @family: base font family for #FcFontSet -+ * -+ * Set the base font family for #FcFontSet -+ */ -+void ibus_fontset_set_family (IBusFontSet *fontset, -+ const gchar *family); -+/** -+ * ibus_fontset_get_size: -+ * @fontset: #IBusFcFontSet -+ * -+ * Return the font size of #FcFontSet -+ * -+ * Returns: Font size of #FcFontSet -+ */ -+guint ibus_fontset_get_size (IBusFontSet *fontset); -+ -+/** -+ * ibus_fontset_set_size: -+ * @fontset: #IBusFcFontSet -+ * @size: font size for #FcFontSet -+ * -+ * Set the font size for #FcFontSet -+ */ -+void ibus_fontset_set_size (IBusFontSet *fontset, -+ guint size); -+/** -+ * ibus_fontset_get_language: -+ * @fontset: #IBusFcFontSet -+ * -+ * Return the font language of #FcFontSet -+ * -+ * Returns: Font language of #FcFontSet -+ */ -+const gchar * ibus_fontset_get_language (IBusFontSet *fontset); -+ -+/** -+ * ibus_fontset_set_language: -+ * @fontset: #IBusFcFontSet -+ * @language: font langauge for #FcFontSet -+ * -+ * Set the font language for #FcFontSet -+ */ -+void ibus_fontset_set_language (IBusFontSet *fontset, -+ const gchar *language); -+ -+/** -+ * ibus_fontset_update_fcfontset: -+ * @fontset: #IBusFcFontSet -+ * -+ * Update #FcFontSet from font family, size and langauge of @fontset. -+ * Returns: %TRUE if #FcFontSet is updated. %FALSE otherwise. -+ */ -+gboolean ibus_fontset_update_fcfontset (IBusFontSet *fontset); -+ -+/** -+ * ibus_fontset_get_preferred_size_hb: -+ * @fontset: #IBusFcFontSet -+ * @text: a string to be calculate the preferred rectangle size. -+ * @widest: (out): #cairo_rectangle_int_t is updated. -+ * -+ * Calculate @widest for @text. -+ * -+ * Returns: #IBusRequisitionEx which includes the glyphs and coordinates. -+ */ -+IBusRequisitionEx * -+ ibus_fontset_get_preferred_size_hb -+ (IBusFontSet *fontset, -+ const gchar *text, -+ cairo_rectangle_int_t -+ *widest); -+ -+/** -+ * ibus_fontset_draw_cairo_lines: -+ * @fontset: #IBusFcFontSet -+ * @cr: #cairo_t in #GtkWidget.draw(). -+ * @ex: #IBusRequisitionEx which includes glyph, x, y values, char width -+ * and height. -+ * -+ * Draw glyphs in @ex using cairo @cr. -+ */ -+void ibus_fontset_draw_cairo_with_requisition_ex -+ (IBusFontSet *fontset, -+ cairo_t *cr, -+ IBusRequisitionEx -+ *ex); -+ -+/** -+ * ibus_fontset_unref: -+ * @fontset: #IBusFcFontSet -+ * -+ * Call g_object_unref(). -+ * FIXME: Seems Vala needs this API. -+ */ -+void ibus_fontset_unref (IBusFontSet *fontset); -+ -+G_END_DECLS -+#endif --- -2.14.3 -