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 +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 . +--- + 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 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 +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 +-# Copyright (c) 2007-2016 Red Hat, Inc. ++# Copyright (c) 2017 Takao Fujiwara ++# 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>? + 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 ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 ++ * USA ++ */ ++ ++#include ++#include ++#include ++#include FT_FREETYPE_H ++#include ++#include ++#include ++ ++#include "ibusfontset.h" ++ ++#define XPAD 2 ++#define YPAD 2 ++#define UNKNOWN_FONT_SIZE 7 ++#define IBUS_FONTSET_GET_PRIVATE(o) \ ++ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FONTSET, IBusFontSetPrivate)) ++ ++ ++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 ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 ++ * USA ++ */ ++ ++#ifndef __IBUS_HARFBUZZ_H_ ++#define __IBUS_HARFBUZZ_H_ ++ ++/** ++ * SECTION: ibusfontset ++ * @short_description: Object for HarfBuzz and Fontconfig. ++ * @title: IBusFontSet ++ * @stability: Unstable ++ * ++ * IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering ++ * on Cairo context. ++ * Current Pango changes fonts by emoji variants and draws the separated ++ * glyphs [1] but actually the emoji characters with variants can be drawn ++ * as one glyph so this class manages Fontconfig fontsets to select a font, ++ * HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs. ++ * ++ * [1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669 ++ * https://bugzilla.gnome.org/show_bug.cgi?id=781123 ++ */ ++ ++#include ++#include ++ ++#define IBUS_TYPE_CAIRO_LINE (ibus_cairo_line_get_type ()) ++#define IBUS_TYPE_REQUISITION_EX (ibus_requisition_ex_get_type ()) ++#define IBUS_TYPE_FONTSET (ibus_fontset_get_type ()) ++#define IBUS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_CAST (\ ++ (obj), \ ++ IBUS_TYPE_FONTSET, \ ++ IBusFontSet)) ++#define IBUS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (\ ++ (klass), \ ++ IBUS_TYPE_FONTSET, \ ++ IBusFontSetClass)) ++#define IBUS_IS_FONTSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (\ ++ (obj), \ ++ IBUS_TYPE_FONTSET)) ++#define IBUS_IS_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (\ ++ (klass), \ ++ IBUS_TYPE_FONTSET)) ++#define IBUS_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (\ ++ (obj), \ ++ IBUS_TYPE_FONTSET, \ ++ IBusFontSetClass)) ++ ++G_BEGIN_DECLS ++ ++typedef struct _IBusGlyph IBusGlyph; ++typedef struct _IBusCairoLine IBusCairoLine; ++typedef struct _IBusRequisitionEx IBusRequisitionEx; ++typedef struct _IBusFontSet IBusFontSet; ++typedef struct _IBusFontSetPrivate IBusFontSetPrivate; ++typedef struct _IBusFontSetClass IBusFontSetClass; ++ ++struct _IBusGlyph { ++ unsigned long index; ++ double x; ++ double y; ++}; ++ ++struct _IBusCairoLine { ++ IBusGlyph *glyphs; ++ guint num_glyphs; ++ cairo_scaled_font_t *scaled_font; ++ gpointer pdummy[5]; ++}; ++ ++struct _IBusRequisitionEx { ++ guint width; ++ guint height; ++ IBusCairoLine *cairo_lines; ++ gpointer pdummy[5]; ++}; ++ ++struct _IBusFontSet { ++ IBusObject parent_instance; ++ IBusFontSetPrivate *priv; ++}; ++ ++struct _IBusFontSetClass { ++ IBusObjectClass parent_class; ++ /* signals */ ++ /*< private >*/ ++ /* padding */ ++ gpointer pdummy[10]; ++}; ++ ++GType ibus_cairo_line_get_type (void) G_GNUC_CONST; ++ ++/** ++ * ibus_cairo_line_copy: ++ * @cairo_lines: #IBusCairoLine ++ * ++ * Creates a copy of @cairo_liens, which should be freed with ++ * ibus_cairo_line_free(). Primarily used by language bindings, ++ * not that useful otherwise (since @req can just be copied ++ * by assignment in C). ++ * ++ * Returns: the newly allocated #IBusCairoLine, which should ++ * be freed with ibus_cairo_line_free(), or %NULL ++ * if @cairo_lines was %NULL. ++ **/ ++IBusCairoLine * ibus_cairo_line_copy (IBusCairoLine *cairo_lines); ++ ++/** ++ * ibus_cairo_line_free: ++ * @cairo_lines: #IBusCairoLine ++ * ++ * Free an #IBusCairoLine. ++ */ ++void ibus_cairo_line_free (IBusCairoLine *cairo_lines); ++ ++ ++GType ibus_requisition_ex_get_type (void) G_GNUC_CONST; ++ ++/** ++ * ibus_requisition_ex_copy: ++ * @req: #IBusRequisitionEx ++ * ++ * Creates a copy of @req, which should be freed with ++ * ibus_requisition_ex_free(). Primarily used by language bindings, ++ * not that useful otherwise (since @req can just be copied ++ * by assignment in C). ++ * ++ * Returns: the newly allocated #IBusRequisitionEx, which should ++ * be freed with ibus_requisition_ex_free(), or %NULL ++ * if @req was %NULL. ++ **/ ++IBusRequisitionEx * ++ ibus_requisition_ex_copy (IBusRequisitionEx *req); ++ ++/** ++ * ibus_requisition_ex_free: ++ * @req: #IBusRequisitionEx ++ * ++ * Free an #IBusRequisitionEx. ++ */ ++void ibus_requisition_ex_free (IBusRequisitionEx *req); ++ ++ ++GType ibus_fontset_get_type (void); ++ ++/** ++ * ibus_fontset_new: ++ * @first_property_name: ++ * ++ * Creates a new #IBusFcFontSet. ++ * ++ * Returns: (transfer full): A newly allocated #IBusFontSet and includes ++ * #FcFontSet internally. E.g. ibus_fontset_new ("family", ++ * "Noto Emoji Color", "size", 16, "language", "ja-jp"); ++ */ ++IBusFontSet * ibus_fontset_new (const gchar ++ *first_property_name, ++ ...); ++ ++/** ++ * ibus_fontset_new_with_font: ++ * @family: font family ++ * @size: font size ++ * @language: font language ++ * ++ * Creates a new #IBusFcFontSet. ++ * ++ * Returns: (transfer full): A newly allocated #IBusFcFontSet and includes ++ * #FcFontSet internally. ++ */ ++IBusFontSet * ibus_fontset_new_with_font (const gchar *family, ++ guint size, ++ const gchar *language); ++/** ++ * ibus_fontset_get_family: ++ * @fontset: #IBusFcFontSet ++ * ++ * Return the base font family of #FcFontSet ++ * ++ * Returns: Base font family of #FcFontSet ++ */ ++const gchar * ibus_fontset_get_family (IBusFontSet *fontset); ++ ++/** ++ * ibus_fontset_set_family: ++ * @fontset: #IBusFcFontSet ++ * @family: base font family for #FcFontSet ++ * ++ * Set the base font family for #FcFontSet ++ */ ++void ibus_fontset_set_family (IBusFontSet *fontset, ++ const gchar *family); ++/** ++ * ibus_fontset_get_size: ++ * @fontset: #IBusFcFontSet ++ * ++ * Return the font size of #FcFontSet ++ * ++ * Returns: Font size of #FcFontSet ++ */ ++guint ibus_fontset_get_size (IBusFontSet *fontset); ++ ++/** ++ * ibus_fontset_set_size: ++ * @fontset: #IBusFcFontSet ++ * @size: font size for #FcFontSet ++ * ++ * Set the font size for #FcFontSet ++ */ ++void ibus_fontset_set_size (IBusFontSet *fontset, ++ guint size); ++/** ++ * ibus_fontset_get_language: ++ * @fontset: #IBusFcFontSet ++ * ++ * Return the font language of #FcFontSet ++ * ++ * Returns: Font language of #FcFontSet ++ */ ++const gchar * ibus_fontset_get_language (IBusFontSet *fontset); ++ ++/** ++ * ibus_fontset_set_language: ++ * @fontset: #IBusFcFontSet ++ * @language: font langauge for #FcFontSet ++ * ++ * Set the font language for #FcFontSet ++ */ ++void ibus_fontset_set_language (IBusFontSet *fontset, ++ const gchar *language); ++ ++/** ++ * ibus_fontset_update_fcfontset: ++ * @fontset: #IBusFcFontSet ++ * ++ * Update #FcFontSet from font family, size and langauge of @fontset. ++ * Returns: %TRUE if #FcFontSet is updated. %FALSE otherwise. ++ */ ++gboolean ibus_fontset_update_fcfontset (IBusFontSet *fontset); ++ ++/** ++ * ibus_fontset_get_preferred_size_hb: ++ * @fontset: #IBusFcFontSet ++ * @text: a string to be calculate the preferred rectangle size. ++ * @widest: (out): #cairo_rectangle_int_t is updated. ++ * ++ * Calculate @widest for @text. ++ * ++ * Returns: #IBusRequisitionEx which includes the glyphs and coordinates. ++ */ ++IBusRequisitionEx * ++ ibus_fontset_get_preferred_size_hb ++ (IBusFontSet *fontset, ++ const gchar *text, ++ cairo_rectangle_int_t ++ *widest); ++ ++/** ++ * ibus_fontset_draw_cairo_lines: ++ * @fontset: #IBusFcFontSet ++ * @cr: #cairo_t in #GtkWidget.draw(). ++ * @ex: #IBusRequisitionEx which includes glyph, x, y values, char width ++ * and height. ++ * ++ * Draw glyphs in @ex using cairo @cr. ++ */ ++void ibus_fontset_draw_cairo_with_requisition_ex ++ (IBusFontSet *fontset, ++ cairo_t *cr, ++ IBusRequisitionEx ++ *ex); ++ ++/** ++ * ibus_fontset_unref: ++ * @fontset: #IBusFcFontSet ++ * ++ * Call g_object_unref(). ++ * FIXME: Seems Vala needs this API. ++ */ ++void ibus_fontset_unref (IBusFontSet *fontset); ++ ++G_END_DECLS ++#endif +-- +2.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 - 1.5.16-3 +- Enabled HarfBuzz rendering without Pango glyph calc for emoji + * Mon May 29 2017 Takao Fujiwara - 1.5.16-2 - Added ctrl-c,v,x for annotations and ctrl-shift-c for emoji - Added Malay and Mongolian keymaps