public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Takao Fujiwara <tfujiwar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ibus] autotool: Enabled HarfBuzz rendering without Pango glyph calc for emoji
Date: Sun, 31 May 2026 02:06:34 GMT [thread overview]
Message-ID: <178019319476.1.11951189278677892441.rpms-ibus-be63774f6b9d@fedoraproject.org> (raw)
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
reply other threads:[~2026-05-31 2:06 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178019319476.1.11951189278677892441.rpms-ibus-be63774f6b9d@fedoraproject.org \
--to=tfujiwar@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox