public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Enabled HarfBuzz rendering without Pango glyph calc for emoji
@ 2026-05-31  2:06 Takao Fujiwara
  0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31  2:06 UTC (permalink / raw)
  To: git-commits

A new commit has been pushed.

Repo   : rpms/ibus
Branch : autotool
Commit : be63774f6b9d1b832489201b689d43e2177fa828
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date   : 2017-07-13T19:00:12+09:00
Stats  : +2134/-1 in 2 file(s)
URL    : https://src.fedoraproject.org/rpms/ibus/c/be63774f6b9d1b832489201b689d43e2177fa828?branch=autotool

Log:
Enabled HarfBuzz rendering without Pango glyph calc for emoji

---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 6392feb..779a979 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -1,3 +1,34 @@
+From e6bab7ab78c69d238a70a64e60963dd5a6711ffe Mon Sep 17 00:00:00 2001
+From: Felix Yan <felixonmars@archlinux.org>
+Date: Fri, 19 May 2017 12:13:04 +0900
+Subject: [PATCH] Fix a typo in configure.ac
+
+BUG=https://github.com/ibus/ibus/pull/1927
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/317640043
+
+Patch from Felix Yan <felixonmars@archlinux.org>.
+---
+ configure.ac | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 219b89d..2cc96d1 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -727,7 +727,7 @@ Build options:
+   Enable surrounding-text       $enable_surrounding_text
+   Enable libnotify              $enable_libnotify
+   Enable Emoji dict             $enable_emoji_dict
+-  Uicode Emoji directory        $UNICODE_EMOJI_DIR
++  Unicode Emoji directory       $UNICODE_EMOJI_DIR
+   CLDR annotation directory     $EMOJI_ANNOTATION_DIR
+   Run test cases                $enable_tests
+ ])
+-- 
+2.9.3
+
 From 4fe3050efa7335f82870fb1d5a1d170d20afc160 Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
 Date: Mon, 22 May 2017 12:04:28 +0900
@@ -588,3 +619,2092 @@ index 1d105fd..95912bf 100644
 -- 
 2.9.3
 
+From e10a2e344a947c3125190bdd55bbb7d0f6bc188e Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Thu, 13 Jul 2017 18:46:26 +0900
+Subject: [PATCH] Integrate custom rendering to use HarfBuzz glyph info
+
+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.
+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
+---
+ .../vala}/IBusEmojiDialog-1.0.metadata             |   0
+ bindings/vala/IBusFontSet-1.0.metadata             |   1 +
+ bindings/vala/Makefile.am                          | 244 +++++-
+ .../vala}/ibus-emoji-dialog-1.0.deps               |   0
+ bindings/vala/ibus-fontset-1.0.deps                |   1 +
+ configure.ac                                       |  29 +
+ po/POTFILES.skip                                   |   5 +
+ ui/gtk3/Makefile.am                                | 131 ++-
+ ui/gtk3/emojier.vala                               | 119 ++-
+ ui/gtk3/ibusemojidialog.h                          |  26 +
+ ui/gtk3/ibusfontset.c                              | 922 +++++++++++++++++++++
+ ui/gtk3/ibusfontset.h                              | 302 +++++++
+ 12 files changed, 1674 insertions(+), 106 deletions(-)
+ rename {ui/gtk3 => bindings/vala}/IBusEmojiDialog-1.0.metadata (100%)
+ create mode 100644 bindings/vala/IBusFontSet-1.0.metadata
+ rename {ui/gtk3 => bindings/vala}/ibus-emoji-dialog-1.0.deps (100%)
+ 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/ui/gtk3/IBusEmojiDialog-1.0.metadata b/bindings/vala/IBusEmojiDialog-1.0.metadata
+similarity index 100%
+rename from ui/gtk3/IBusEmojiDialog-1.0.metadata
+rename to bindings/vala/IBusEmojiDialog-1.0.metadata
+diff --git a/bindings/vala/IBusFontSet-1.0.metadata b/bindings/vala/IBusFontSet-1.0.metadata
+new file mode 100644
+index 0000000..73037d7
+--- /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 4e34afc..261e1f3 100644
+--- a/bindings/vala/Makefile.am
++++ b/bindings/vala/Makefile.am
+@@ -3,7 +3,8 @@
+ # ibus - The Input Bus
+ #
+ # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
+-# Copyright (c) 2007-2016 Red Hat, Inc.
++# Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2007-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
+@@ -22,15 +23,47 @@
+ 
+ -include $(VAPIGEN_MAKEFILE)
+ 
++libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
++
++noinst_LTLIBRARIES =
++noinst_DATA =
++INTROSPECTION_GIRS =
++girdir = $(datadir)/gir-1.0
++
++AM_CPPFLAGS = \
++    -I$(top_srcdir)/src \
++    -I$(top_builddir)/src \
++    -include $(CONFIG_HEADER) \
++    $(NULL)
++AM_CFLAGS = \
++    -DG_LOG_DOMAIN=\"IBUS\" \
++    -DPKGDATADIR=\"$(pkgdatadir)\" \
++    -DIBUS_DISABLE_DEPRECATED \
++    -Wno-unused-variable \
++    -Wno-unused-but-set-variable \
++    -Wno-unused-function \
++    $(NULL)
++AM_VALAFLAGS = \
++    --vapidir=$(builddir) \
++    --vapidir=$(srcdir) \
++    --pkg=posix \
++    --pkg=gtk+-3.0 \
++    --pkg=gdk-x11-3.0 \
++    --pkg=ibus-1.0 \
++    --pkg=config \
++    --pkg=xi \
++    --target-glib="$(VALA_TARGET_GLIB_VERSION)" \
++    $(NULL)
++
+ vapi_deps = \
+ 	IBus-1.0.metadata \
+-	IBus-1.0-custom.vala \
+ 	$(top_builddir)/src/IBus-1.0.gir \
+ 	$(NULL)
+ 
+ ibus-1.0.vapi: $(vapi_deps)
+ 
+-VAPIGEN_VAPIS = ibus-1.0.vapi
++ibus_vapi = ibus-1.0.vapi
++VAPIGEN_VAPIS = $(ibus_vapi)
+ 
+ ibus_1_0_vapi_DEPS = gio-2.0
+ ibus_1_0_vapi_METADATADIRS = $(srcdir)
+@@ -40,18 +73,201 @@ ibus_1_0_vapi_FILES = \
+ 	$(NULL)
+ 
+ vapidir = $(datadir)/vala/vapi
+-vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
++vapi_DATA = $(ibus_vapi) $(ibus_vapi:.vapi=.deps)
+ 
+-MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
+-DISTCLEANFILES = $(VAPIGEN_VAPIS)
++MAINTAINERCLEANFILES = $(ibus_vapi)
++DISTCLEANFILES = $(ibus_vapi)
+ 
+-EXTRA_DIST = \
+-	$(VAPIGEN_VAPIS) \
+-	IBus-1.0.metadata \
+-	IBus-1.0-custom.vala \
+-	ibus-1.0.deps \
+-	config.vapi \
+-	xi.vapi \
+-	$(NULL)
++EXTRA_DIST =                                    \
++    $(ibus_vapi)                                \
++    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)
++
++if ENABLE_EMOJI_DICT
++AM_VALAFLAGS += --define=EMOJI_DICT
++
++libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
++noinst_LTLIBRARIES += $(libibus_emoji_dialog)
++
++libibus_emoji_dialog_1_0_la_SOURCES =           \
++    candidatearea.vala                          \
++    emojier.vala                                \
++    iconwidget.vala                             \
++    pango.vala                                  \
++    separator.vala                              \
++    $(NULL)
++libibus_emoji_dialog_1_0_la_CFLAGS =            \
++    $(AM_CFLAGS)                                \
++    @GLIB2_CFLAGS@                              \
++    @GIO2_CFLAGS@                               \
++    @GTHREAD2_CFLAGS@                           \
++    @GTK3_CFLAGS@                               \
++    @X11_CFLAGS@                                \
++    -DBINDIR=\"$(bindir)\"                      \
++    $(NULL)
++libibus_emoji_dialog_1_0_la_LIBADD =            \
++    @GLIB2_LIBS@                                \
++    @GIO2_LIBS@                                 \
++    @GTHREAD2_LIBS@                             \
++    @GTK3_LIBS@                                 \
++    @X11_LIBS@                                  \
++    -lXi                                        \
++    $(libibus)                                  \
++    $(NULL)
++libibus_emoji_dialog_1_0_la_LDFLAGS =           \
++    -no-undefined                               \
++    -export-symbols-regex "ibus_.*"             \
++    $(NULL)
++
++# per file setting is needed to avoid conflicting LN_S by calling
++# duplicated times in parallel make
++%.vala: $(ibus_vapi)
++	if test ! -f $@ ; then                                              \
++	    $(LN_S) $(top_srcdir)/ui/gtk3/$@ .;                             \
++	fi;
++ibusfontset.c: $(ibus_vapi)
++	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)
++
++if HAVE_INTROSPECTION
++-include $(INTROSPECTION_MAKEFILE)
++INTROSPECTION_SCANNER_ARGS =
++INTROSPECTION_COMPILER_ARGS =                   \
++    --includedir=$(srcdir)                      \
++    --includedir=.                              \
++    --includedir=$(top_srcdir)/src              \
++    $(NULL)
++
++
++emoji_headers =                                 \
++    $(top_srcdir)/ui/gtk3/ibusemojidialog.h     \
++    $(NULL)
++
++IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
++IBusEmojiDialog_1_0_gir_SCANNERFLAGS =          \
++    --pkg-export=ibus-1.0                       \
++    --pkg=gtk+-3.0                              \
++    $(IBUS_GIR_SCANNERFLAGS)                    \
++    $(NULL)
++IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
++IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
++IBusEmojiDialog_1_0_gir_FILES = $(emoji_headers)
++IBusEmojiDialog_1_0_gir_CFLAGS =                \
++    -I$(srcdir)                                 \
++    -I$(builddir)                               \
++    -I$(top_srcdir)/src                         \
++    $(NULL)
++
++ibus_emoji_dialog_gir = IBusEmojiDialog-1.0.gir
++INTROSPECTION_GIRS += $(ibus_emoji_dialog_gir)
++noinst_DATA += $(ibus_emoji_dialog_gir)
++EXTRA_DIST += $(ibus_emoji_dialog_gir)
++MAINTAINERCLEANFILES += $(ibus_emoji_dialog_gir)
++DISTCLEANFILES += $(ibus_emoji_dialog_gir)
++
++ibus-emoji-dialog-1.0.vapi: $(ibus_emoji_dialog_gir) IBusEmojiDialog-1.0.metadata
++ibus_emoji_dialog_vapi = ibus-emoji-dialog-1.0.vapi
++ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0
++ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
++ibus_emoji_dialog_1_0_vapi_FILES = IBusEmojiDialog-1.0.gir
++VAPIGEN_VAPIS += $(ibus_emoji_dialog_vapi)
++noinst_DATA += $(ibus_emoji_dialog_vapi)
++EXTRA_DIST += $(ibus_emoji_dialog_vapi)
++MAINTAINERCLEANFILES += $(ibus_emoji_dialog_vapi)
++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
+ 
+ -include $(top_srcdir)/git.mk
+diff --git a/ui/gtk3/ibus-emoji-dialog-1.0.deps b/bindings/vala/ibus-emoji-dialog-1.0.deps
+similarity index 100%
+rename from ui/gtk3/ibus-emoji-dialog-1.0.deps
+rename to bindings/vala/ibus-emoji-dialog-1.0.deps
+diff --git a/bindings/vala/ibus-fontset-1.0.deps b/bindings/vala/ibus-fontset-1.0.deps
+new file mode 100644
+index 0000000..129fe16
+--- /dev/null
++++ b/bindings/vala/ibus-fontset-1.0.deps
+@@ -0,0 +1 @@
++cairo
+diff --git a/configure.ac b/configure.ac
+index cb48ad4..d2aa222 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -653,6 +653,34 @@ https://github.com/fujiwarat/cldr-emoji-annotation)
+     enable_emoji_dict="yes (enabled, use --disable-emoji-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
+@@ -740,6 +768,7 @@ Build options:
+   Enable Emoji dict             $enable_emoji_dict
+   Unicode Emoji directory       $UNICODE_EMOJI_DIR
+   CLDR annotation directory     $EMOJI_ANNOTATION_DIR
++  Enable HarfBuzz for Emoji     $enable_harfbuzz_for_emoji
+   Run test cases                $enable_tests
+ ])
+ 
+diff --git a/po/POTFILES.skip b/po/POTFILES.skip
+index 7190221..10b8829 100644
+--- a/po/POTFILES.skip
++++ b/po/POTFILES.skip
+@@ -2,6 +2,11 @@
+ # Please keep this file in alphabetical order.
+ # Files under ui/gtk2/ are not shipped in the distribution, but kept
+ # in the git repository for reference.
++bindings/vala/candidatearea.c
++bindings/vala/emojier.c
++bindings/vala/iconwidget.c
++bindings/vala/pango.c
++bindings/vala/separator.c
+ ibus/_config.py
+ tools/main.c
+ ui/gtk2/candidatepanel.py
+diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
+index c79641a..cd1e9c2 100644
+--- a/ui/gtk3/Makefile.am
++++ b/ui/gtk3/Makefile.am
+@@ -81,10 +81,6 @@ AM_VALAFLAGS = \
+ 	--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
+ 	$(NULL)
+ 
+-MAINTAINERCLEANFILES =
+-DISTCLEANFILES =
+-noinst_DATA =
+-
+ if ENABLE_LIBNOTIFY
+ AM_CFLAGS += \
+ 	@LIBNOTIFY_CFLAGS@ \
+@@ -158,9 +154,10 @@ man_seven_in_files = ibus-emoji.7.in
+ EXTRA_DIST =                            \
+     $(emoji_headers)                    \
+     $(man_seven_in_files)               \
+-    IBusEmojiDialog-1.0.metadata        \
++    emojierapp.vala                     \
+     gtkpanel.xml.in                     \
+-    ibus-emoji-dialog-1.0.deps          \
++    ibusfontset.c                       \
++    ibusfontset.h                       \
+     notification-item.xml               \
+     notification-watcher.xml            \
+     $(NULL)
+@@ -168,98 +165,70 @@ EXTRA_DIST =                            \
+ if ENABLE_EMOJI_DICT
+ AM_VALAFLAGS += --define=EMOJI_DICT
+ 
+-libibus_emoji_dialog = libibus-emoji-dialog-1.0.la
+-
+-noinst_LTLIBRARIES = $(libibus_emoji_dialog)
+-
+-libibus_emoji_dialog_1_0_la_CFLAGS = $(AM_CFLAGS)
+-libibus_emoji_dialog_1_0_la_LDFLAGS =   \
+-    -no-undefined                       \
+-    -export-symbols-regex "ibus_.*"     \
+-    -version-info @LT_VERSION_INFO@     \
+-    $(NULL)
+-libibus_emoji_dialog_1_0_la_SOURCES =   \
+-    candidatearea.vala                  \
+-    emojier.vala                        \
+-    iconwidget.vala                     \
+-    pango.vala                          \
+-    separator.vala                      \
+-    $(NULL)
+-
+ libexec_PROGRAMS += ibus-ui-emojier
+ 
+-ibus_ui_emojier_SOURCES =                       \
+-    $(libibus_emoji_dialog_1_0_la_SOURCES)      \
++ibus_ui_emojier_VALASOURCES =                   \
+     emojierapp.vala                             \
++    candidatearea.vala                          \
++    emojier.vala                                \
++    iconwidget.vala                             \
++    pango.vala                                  \
++    separator.vala                              \
++    $(NULL)
++ibus_ui_emojier_SOURCES =                       \
++    $(ibus_ui_emojier_VALASOURCES:.vala=.c)     \
+     $(NULL)
+ 
+ ibus_ui_emojier_LDADD =                         \
+     $(AM_LDADD)                                 \
+     $(NULL)
+ 
+--include $(INTROSPECTION_MAKEFILE)
+-INTROSPECTION_SCANNER_ARGS =
+-INTROSPECTION_COMPILER_ARGS =      \
+-    --includedir=$(srcdir)         \
+-    --includedir=.                 \
+-    --includedir=$(top_srcdir)/src \
+-    $(NULL)
+-
+-if HAVE_INTROSPECTION
+-introspection_sources =                \
+-    $(emoji_headers)                   \
+-    $(NULL)
+-IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
+-IBusEmojiDialog_1_0_gir_SCANNERFLAGS = \
+-    --pkg-export=ibus-1.0              \
+-    --pkg=gtk+-3.0                     \
+-    $(IBUS_GIR_SCANNERFLAGS)           \
++ibus_ui_emojier_VALAFLAGS =                     \
++    $(AM_VALAFLAGS)                             \
+     $(NULL)
+-IBusEmojiDialog-1.0.gir: $(libibus_emoji_dialog) Makefile
+-IBusEmojiDialog_1_0_gir_INCLUDES = Gtk-3.0 GLib-2.0 GObject-2.0 Gio-2.0
+-IBusEmojiDialog_1_0_gir_LIBS = $(libibus_emoji_dialog) $(libibus)
+-IBusEmojiDialog_1_0_gir_FILES =                      \
+-    $(addprefix $(srcdir)/,$(introspection_sources)) \
+-    $(NULL)
+-IBusEmojiDialog_1_0_gir_CFLAGS =       \
+-    -DIBUS_COMPILATION                 \
+-    -I$(srcdir)                        \
+-    -I$(builddir)                      \
+-    -I$(top_srcdir)/src                \
+-    $(NULL)
+-INTROSPECTION_GIRS = IBusEmojiDialog-1.0.gir
+-
+-girdir = $(datadir)/gir-1.0
+-noinst_DATA += $(INTROSPECTION_GIRS)
+-CLEANFILES += $(INTROSPECTION_GIRS)
+ 
+-typelibsdir = $(libdir)/girepository-1.0
+-noinst_DATA += $(INTROSPECTION_GIRS:.gir=.typelib)
+-CLEANFILES += $(INTROSPECTION_GIRS:.gir=.typelib)
+-
+-
+-if ENABLE_VAPIGEN
+--include $(VAPIGEN_MAKEFILE)
++# This line and foo_VALASOURCES line can delete the duplicated entries
++# of emojier.c: emojier.vala
++emojierapp.c: $(ibus_ui_emojier_VALASOURCES)
++	$(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \
++$(VALAFLAGS) -C $(ibus_ui_emojier_VALASOURCES)
++	$(NULL)
++# make dist creates .c files in a different srcdir
++emojierapp.o: $(srcdir)/emojierapp.c
++	$(AM_V_CC)source='$<' object='$@' libtool=no \
++	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
++	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
++	$(NULL)
+ 
+-ibus-emoji-dialog-1.0.vapi: $(INTROSPECTION_GIRS) IBusEmojiDialog-1.0.metadata
++if ENABLE_HARFBUZZ_FOR_EMOJI
++ibus_ui_gtk3_SOURCES +=                         \
++    ibusfontset.c                               \
++    $(NULL)
+ 
+-VAPIGEN_VAPIS = ibus-emoji-dialog-1.0.vapi
++ibus_ui_emojier_SOURCES +=                      \
++    ibusfontset.c                               \
++    $(NULL)
+ 
+-ibus_emoji_dialog_1_0_vapi_DEPS = gtk+-3.0 gio-2.0
+-ibus_emoji_dialog_1_0_vapi_METADATADIRS = $(srcdir)
+-ibus_emoji_dialog_1_0_vapi_FILES = $(INTROSPECTION_GIRS)
++AM_CFLAGS += \
++    @CAIRO_CFLAGS@                              \
++    @FONTCONFIG_CFLAGS@                         \
++    @HARFBUZZ_CFLAGS@                           \
++    $(NULL)
+ 
+-vapidir = $(datadir)/vala/vapi
+-noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
++AM_LDADD += \
++    @CAIRO_LIBS@                                \
++    @FONTCONFIG_LIBS@                           \
++    @HARFBUZZ_LIBS@                             \
++    $(NULL)
+ 
+-MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
+-DISTCLEANFILES += $(VAPIGEN_VAPIS)
+-EXTRA_DIST += $(VAPIGEN_VAPIS)
++AM_VALAFLAGS += \
++    -D ENABLE_HARFBUZZ_FOR_EMOJI                \
++    --pkg=cairo                                 \
++    --pkg=ibus-fontset-1.0                      \
++    $(NULL)
+ 
+-# end of ENABLE_VAPIGEN
+-endif
+-# end of HAVE_INTROSPECTION
+ 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)
+@@ -276,7 +245,7 @@ CLEANFILES += \
+     $(man_seven_files) \
+     $(NULL)
+ 
+-# end of ENABLE_EMOJI_DICT
+ endif
++# end of ENABLE_EMOJI_DICT
+ 
+ -include $(top_srcdir)/git.mk
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 95912bf..72e0093 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -80,6 +80,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
+         }
+     }
+     private class EWhiteLabel : Gtk.Label {
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++        IBus.RequisitionEx m_requisition;
++#endif
+         public EWhiteLabel(string text) {
+             GLib.Object(
+                 name : "IBusEmojierWhiteLabel"
+@@ -87,8 +90,78 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             if (text != "")
+                 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();
++            if (text == null || 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;
++        }
++        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) {
++            if (m_fontset == null)
++                return true;
++            if (m_requisition == null)
++                return true; 
++            if (m_requisition.cairo_lines == null)
++                return true; 
++            var style_context = get_style_context();
++            Gtk.Allocation allocation;
++            get_allocation(out allocation);
++            style_context.render_background(cr,
++                                            0, 0,
++                                            allocation.width,
++                                            allocation.height);
++            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;
++        }
++#endif
+     }
+-    private class ESelectedLabel : Gtk.Label {
++    private class ESelectedLabel : EWhiteLabel {
+         public ESelectedLabel(string text) {
+             GLib.Object(
+                 name : "IBusEmojierSelectedLabel"
+@@ -97,7 +170,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                 set_label(text);
+         }
+     }
+-    private class EGoldLabel : Gtk.Label {
++    private class EGoldLabel : EWhiteLabel {
+         public EGoldLabel(string text) {
+             GLib.Object(
+                 name : "IBusEmojierGoldLabel"
+@@ -212,6 +285,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             m_category_to_emojis_dict;
+     private static GLib.HashTable<string, GLib.SList<string>>?
+             m_emoji_to_emoji_variants_dict;
++#if ENABLE_HARFBUZZ_FOR_EMOJI
++    private static IBus.FontSet m_fontset;
++#endif
+ 
+     private ThemedRGBA m_rgba;
+     private Gtk.Box m_vbox;
+@@ -1120,6 +1196,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                     m_category_active_index = (int)list.length();
+         }
+         Gtk.Adjustment adjustment = m_list_box.get_adjustment();
++        m_scrolled_window.set_hadjustment(new Gtk.Adjustment(0, 0, 0, 0, 0, 0));
+         m_scrolled_window.set_vadjustment(adjustment);
+         show_category_list();
+     }
+@@ -1137,7 +1214,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             else if (keyval == Gdk.Key.Right)
+                 m_lookup_table.cursor_down();
+             show_candidate_panel();
+-        } else if (m_entry.get_text().len() > 0) {
++        } else if (m_entry.get_text().length > 0) {
+             int step = 0;
+             if (keyval == Gdk.Key.Left)
+                 step = -1;
+@@ -1192,7 +1269,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             show_candidate_panel();
+             return true;
+         }
+-        if (m_entry.get_text().len() > 0) {
++        if (m_entry.get_text().length > 0) {
+             int step = 0;
+             if (keyval == Gdk.Key.Home)
+                 step = -1;
+@@ -1391,7 +1468,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             key_press_enter();
+             return true;
+         case Gdk.Key.BackSpace:
+-            if (m_entry.get_text().len() > 0) {
++            if (m_entry.get_text().length > 0) {
+                 if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
+                     GLib.Signal.emit_by_name(m_entry, "delete-from-cursor",
+                                              Gtk.DeleteType.WORD_ENDS, -1);
+@@ -1403,7 +1480,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+             break;
+         case Gdk.Key.Delete:
+         case Gdk.Key.KP_Delete:
+-            if (m_entry.get_text().len() > 0) {
++            if (m_entry.get_text().length > 0) {
+                 if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
+                     GLib.Signal.emit_by_name(m_entry, "delete-from-cursor",
+                                              Gtk.DeleteType.WORD_ENDS, 1);
+@@ -1417,7 +1494,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+         case Gdk.Key.space:
+         case Gdk.Key.KP_Space:
+             if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
+-                if (m_entry.get_text().len() > 0)
++                if (m_entry.get_text().length > 0)
+                     entry_enter_keyval(keyval);
+             } else if (m_candidate_panel_is_visible) {
+                 enter_notify_disable_with_timer();
+@@ -1493,7 +1570,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                     return true;
+                 break;
+             case Gdk.Key.u:
+-                if (m_entry.get_text().len() > 0) {
++                if (m_entry.get_text().length > 0) {
+                     GLib.Signal.emit_by_name(m_entry,
+                                              "delete-from-cursor",
+                                              Gtk.DeleteType.PARAGRAPH_ENDS,
+@@ -1502,13 +1579,13 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                 }
+                 break;
+             case Gdk.Key.a:
+-                if (m_entry.get_text().len() > 0) {
++                if (m_entry.get_text().length > 0) {
+                     m_entry.select_region(0, -1);
+                     return true;
+                 }
+                 break;
+             case Gdk.Key.x:
+-                if (m_entry.get_text().len() > 0) {
++                if (m_entry.get_text().length > 0) {
+                     GLib.Signal.emit_by_name(m_entry, "cut-clipboard");
+                     return true;
+                 }
+@@ -1525,7 +1602,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+                         clipboard.store();
+                         return true;
+                     }
+-                } else if (m_entry.get_text().len() > 0) {
++                } else if (m_entry.get_text().length > 0) {
+                     GLib.Signal.emit_by_name(m_entry, "copy-clipboard");
+                     return true;
+                 }
+@@ -1581,6 +1658,22 @@ 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;
+@@ -1611,6 +1704,10 @@ 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/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
+index 24d195c..ed8886a 100644
+--- a/ui/gtk3/ibusemojidialog.h
++++ b/ui/gtk3/ibusemojidialog.h
+@@ -170,5 +170,31 @@ void          ibus_emojier_set_favorites          (gchar**      favorites,
+                                                    favorite_annotations,
+                                                    int
+                                                    favorite_annotations_length);
++
++/**
++ * ibus_emojier_set_partial_match:
++ * @has_partial_match: Enable the partial match if %TRUE. Otherwise if %FALSE.
++ *
++ * Set partial match for emoji annotations.
++ */
++void          ibus_emojier_set_partial_match      (gboolean  has_partial_match);
++
++/**
++ * ibus_emojier_set_partial_match_length:
++ * @length: minimum lenght to match partially.
++ *
++ * Set the minimum lenght to match partially.
++ */
++void          ibus_emojier_set_partial_match_length
++                                                  (gint         length);
++
++/**
++ * ibus_emojier_set_partial_match_condition:
++ * @condition: condition id between 0 and 2.
++ *
++ * Set the partial match condition with the integer.
++ */
++void          ibus_emojier_set_partial_match_condition
++                                                  (gint         condition);
+ G_END_DECLS
+ #endif
+diff --git a/ui/gtk3/ibusfontset.c b/ui/gtk3/ibusfontset.c
+new file mode 100644
+index 0000000..72dfa28
+--- /dev/null
++++ b/ui/gtk3/ibusfontset.c
+@@ -0,0 +1,922 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * 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 <cairo-ft.h>
++#include <fontconfig/fontconfig.h>
++#include <ft2build.h>
++#include FT_FREETYPE_H
++#include <glib.h>
++#include <hb-ot.h>
++#include <pango/pango.h>
++
++#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))
++
++
++static FT_Library  m_ftlibrary;
++static FcFontSet  *m_fcfontset;
++static gchar      *m_family;
++static guint       m_size;
++static gchar      *m_language;
++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);
++
++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;
++
++    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);
++    }
++    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)
++{
++    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);
++
++    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);
++    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);
++        (*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;
++    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);
++    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);
++    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;
++}
++
++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);
++}
++
++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;
++    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_fcfontset)
++        g_clear_pointer (&m_fcfontset, FcFontSetDestroy);
++
++    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);
++    FcConfigSubstitute (NULL, pattern, FcMatchPattern);
++    FcConfigSubstitute (NULL, pattern, FcMatchFont);
++    FcDefaultSubstitute (pattern);
++    m_fcfontset = FcFontSort (NULL, pattern, FcTrue, NULL, &result);
++    if (result == FcResultNoMatch || m_fcfontset->nfont == 0) {
++        g_warning ("No FcFontSet for %s", family ? family : "(null)");
++        return FALSE;
++    }
++    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;
++        }
++        for (i = 0; i < m_fcfontset->nfont; i++) {
++            if (g_unichar_iscntrl (c) && !g_unichar_isspace (c))
++                break;
++            FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont (
++                    fontset,
++                    m_fcfontset->fonts[i]);
++            if (FT_Get_Char_Index (ft_face, c) != 0) {
++                buff[n].fcfont = m_fcfontset->fonts[i];
++                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;
++                FT_Done_Face (ft_face);
++                break;
++            }
++            FT_Done_Face (ft_face);
++        }
++        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 = ibus_fontset_get_ftface_from_fcfont (
++                            fontset,
++                            m_fcfontset->fonts[i]);
++                    /* 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) {
++                        buff[n].fcfont = m_fcfontset->fonts[i];
++                        FT_Done_Face (ft_face);
++                        has_glyphs = TRUE;
++                        break;
++                    }
++                    FT_Done_Face (ft_face);
++                }
++                if (!has_glyphs) {
++                    buff[n].fcfont = m_fcfontset->fonts[0];
++                    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 0000000..efcaa28
+--- /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 <takao.fujiwara1@gmail.com>
++ * 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 <ibus.h>
++#include <cairo.h>
++
++#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.9.3
+

diff --git a/ibus.spec b/ibus.spec
index 4392281..271a094 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -9,6 +9,8 @@
 %global with_kde5 0
 %endif
 
+%global with_emoji_harfbuzz 1
+
 %global ibus_api_version 1.0
 
 # for bytecompile in %%{_datadir}/ibus/setup
@@ -28,7 +30,7 @@
 
 Name:           ibus
 Version:        1.5.16
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 Group:          System Environment/Libraries
@@ -70,6 +72,11 @@ BuildRequires:  qt5-qtbase-devel
 %endif
 BuildRequires:  cldr-emoji-annotation
 BuildRequires:  unicode-emoji
+%if %with_emoji_harfbuzz
+BuildRequires:  cairo-devel
+BuildRequires:  fontconfig-devel
+BuildRequires:  harfbuzz-devel
+%endif
 
 Requires:       %{name}-libs%{?_isa}   = %{version}-%{release}
 Requires:       %{name}-gtk2%{?_isa}   = %{version}-%{release}
@@ -250,6 +257,9 @@ autoreconf -f -i -v
 %if ! %with_kde5
     --disable-appindicator \
 %endif
+%if %with_emoji_harfbuzz
+    --enable-harfbuzz-for-emoji \
+%endif
     --enable-introspection \
     %{nil}
 
@@ -420,6 +430,9 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
 %{_datadir}/gtk-doc/html/*
 
 %changelog
+* Thu Jul 13 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.16-3
+- Enabled HarfBuzz rendering without Pango glyph calc for emoji
+
 * Mon May 29 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.16-2
 - Added ctrl-c,v,x for annotations and ctrl-shift-c for emoji
 - Added Malay and Mongolian keymaps

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-05-31  2:06 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31  2:06 [rpms/ibus] autotool: Enabled HarfBuzz rendering without Pango glyph calc for emoji Takao Fujiwara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox