ibus/ibus-HEAD.patch
2016-07-26 19:49:47 +09:00

3696 lines
172 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 0432aa66b8728bc266da3c2cca84587bc44b3557 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Tue, 8 Mar 2016 11:16:24 +0900
Subject: [PATCH] Don't warn if DISPLAY is not set
This is normal under Wayland, and not worth warning about.
The warnings disrupt unit tests in GNOME continuous, which
treat warnings as fatal.
BUG=https://github.com/ibus/ibus/pull/1844
R=shawn.p.huang@gmail.com
Review URL: https://codereview.appspot.com/289430043
Patch from Matthias Clasen <mclasen@redhat.com>.
---
src/ibusshare.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/ibusshare.c b/src/ibusshare.c
index 63800a6..b793a96 100644
--- a/src/ibusshare.c
+++ b/src/ibusshare.c
@@ -113,10 +113,7 @@ ibus_get_socket_path (void)
display = g_strdup (_display);
}
- if (display == NULL) {
- g_warning ("DISPLAY is empty! We use default DISPLAY (:0.0)");
- }
- else {
+ if (display) {
p = display;
hostname = display;
for (; *p != ':' && *p != '\0'; p++);
--
2.7.4
From 84c18f1d382548c52138822a11473d2dac79e485 Mon Sep 17 00:00:00 2001
From: Rui Matos <tiagomatos@gmail.com>
Date: Wed, 25 May 2016 11:21:09 +0900
Subject: [PATCH] Install a DBus service file
With the transition to user scoped DBus sessions (vs. login sessions)
there's a need to start ibus-daemon via DBus activation so that the
process gets properly tracked and disposed of when the login session
ends. Otherwise the ibus-daemon process lingers on and keeps the whole
login session up.
We already connect and own a well known name on DBus. The remaining
missing piece is the DBus service file which we introduce here.
BUG=https://github.com/ibus/ibus/pull/1853
R=Shawn.P.Huang@gmail.com
Review URL: https://codereview.appspot.com/295340043
Patch from Rui Matos <tiagomatos@gmail.com>.
---
bus/Makefile.am | 11 +++++++++++
bus/org.freedesktop.IBus.service.in | 3 +++
2 files changed, 14 insertions(+)
create mode 100644 bus/org.freedesktop.IBus.service.in
diff --git a/bus/Makefile.am b/bus/Makefile.am
index 26cb2f8..4dabacf 100644
--- a/bus/Makefile.am
+++ b/bus/Makefile.am
@@ -174,4 +174,15 @@ man_onedir = $(mandir)/man1
%.1.gz: %.1
$(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@
+
+dbusservice_in_files = org.freedesktop.IBus.service.in
+dbusservice_DATA = $(dbusservice_in_files:.service.in=.service)
+dbusservicedir=${datadir}/dbus-1/services
+
+org.freedesktop.IBus.service: org.freedesktop.IBus.service.in
+ $(AM_V_GEN) sed -e "s|\@bindir\@|$(bindir)|" $< > $@.tmp && mv $@.tmp $@
+
+EXTRA_DIST += $(dbusservice_in_files)
+CLEANFILES += $(dbusservice_DATA)
+
-include $(top_srcdir)/git.mk
diff --git a/bus/org.freedesktop.IBus.service.in b/bus/org.freedesktop.IBus.service.in
new file mode 100644
index 0000000..cc88834
--- /dev/null
+++ b/bus/org.freedesktop.IBus.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.IBus
+Exec=@bindir@/ibus-daemon --replace --xim --panel disable
--
2.7.4
From 3ef21fef0135f7b4fe9611d201f15611734f6c51 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 3 Jun 2016 11:52:29 +0900
Subject: [PATCH] client/gtk2: Fix SEGV with Wayland display
Delete gdk_display_get_name() in GTK clients because ibus-daemon
does not use gdk_display_get_name().
GdkX11Dislay and GdkX11Window does not work with Wayland.
BUG=https://github.com/ibus/ibus/issues/1859
R=Shawn.P.Huang@gmail.com
Review URL: https://codereview.appspot.com/301760043
---
client/gtk2/ibusimcontext.c | 5 -----
setup/main.py | 7 +++++--
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
index 9d927e6..0df0062 100644
--- a/client/gtk2/ibusimcontext.c
+++ b/client/gtk2/ibusimcontext.c
@@ -583,11 +583,6 @@ ibus_im_context_class_init (IBusIMContextClass *class)
/* init bus object */
if (_bus == NULL) {
- const gchar *dname = gdk_display_get_name (gdk_display_get_default ());
- /* ibus-daemon uses DISPLAY variable. */
- if (g_strcmp0 (dname, "Wayland") == 0)
- dname = g_getenv ("DISPLAY");
- ibus_set_display (dname);
_bus = ibus_bus_new_async ();
/* init the global fake context */
diff --git a/setup/main.py b/setup/main.py
index 26c2b0f..e1f7a9d 100644
--- a/setup/main.py
+++ b/setup/main.py
@@ -275,8 +275,11 @@ class Setup(object):
self.__init_general()
def __gdk_window_set_cb(self, object, pspec):
- str = '%u' % GdkX11.X11Window.get_xid(object.get_window())
- GLib.setenv('IBUS_SETUP_XID', str, True)
+ window = object.get_window()
+ if type(window) != GdkX11.X11Window:
+ return
+ s = '%u' % GdkX11.X11Window.get_xid(window)
+ GLib.setenv('IBUS_SETUP_XID', s, True)
def __combobox_notify_active_engine_cb(self, combobox, property):
engine = self.__combobox.get_active_engine()
--
2.7.4
From a598ae29223d1ca25e76bf7d7de9703f63ea337e Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Fri, 3 Jun 2016 19:44:11 +0900
Subject: [PATCH] ui/gtk3: Fix panel CSS format for GTK 3.20
CSS node names have been changed since GTK 3.20 and the font size and
widget color no longer work with the previous node names.
BUG=https://github.com/ibus/ibus/issues/1856
Review URL: https://codereview.appspot.com/297380043
---
ui/gtk3/candidatearea.vala | 16 +++++++++++++---
ui/gtk3/handle.vala | 10 ++++++++--
ui/gtk3/panel.vala | 9 +++++++--
3 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/ui/gtk3/candidatearea.vala b/ui/gtk3/candidatearea.vala
index c969312..3848f0d 100644
--- a/ui/gtk3/candidatearea.vala
+++ b/ui/gtk3/candidatearea.vala
@@ -3,7 +3,7 @@
* ibus - The Input Bus
*
* Copyright(c) 2011-2015 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright(c) 2015 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright(c) 2015-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -31,6 +31,10 @@ class CandidateArea : Gtk.Box {
private uint m_focus_candidate;
private bool m_show_cursor;
+ private bool m_use_latest_css_format =
+ ((Gtk.MAJOR_VERSION > 3) ||
+ (Gtk.MAJOR_VERSION == 3) && (Gtk.MINOR_VERSION >= 20));
+
private const string LABELS[] = {
"1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.",
"9.", "0.", "a.", "b.", "c.", "d.", "e.", "f."
@@ -103,7 +107,8 @@ class CandidateArea : Gtk.Box {
Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(candidates[i]);
if (i == focus_candidate && show_cursor) {
Gtk.StyleContext context = m_candidates[i].get_style_context();
- Gdk.RGBA color = context.get_color(Gtk.StateFlags.SELECTED);
+ Gdk.RGBA *color = null;
+ context.get(Gtk.StateFlags.SELECTED, "color", out color);
Pango.Attribute pango_attr = Pango.attr_foreground_new(
(uint16)(color.red * uint16.MAX),
(uint16)(color.green * uint16.MAX),
@@ -112,7 +117,12 @@ class CandidateArea : Gtk.Box {
pango_attr.end_index = candidates[i].get_text().length;
attrs.insert((owned)pango_attr);
- color = context.get_background_color(Gtk.StateFlags.SELECTED);
+ color = null;
+ string bg_prop =
+ m_use_latest_css_format
+ ? "-gtk-secondary-caret-color"
+ : "background-color";
+ context.get(Gtk.StateFlags.SELECTED, bg_prop, out color);
pango_attr = Pango.attr_background_new(
(uint16)(color.red * uint16.MAX),
(uint16)(color.green * uint16.MAX),
diff --git a/ui/gtk3/handle.vala b/ui/gtk3/handle.vala
index 1edb537..bef5e8b 100644
--- a/ui/gtk3/handle.vala
+++ b/ui/gtk3/handle.vala
@@ -2,7 +2,8 @@
*
* ibus - The Input Bus
*
- * Copyright(c) 2011-2015 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2011-2016 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,6 +30,11 @@ class Handle : Gtk.EventBox {
public signal void move_end();
public Handle() {
+ // Call base class constructor
+ GLib.Object(
+ name : "IBusHandle"
+ );
+
set_size_request(6, -1);
Gdk.EventMask mask = Gdk.EventMask.EXPOSURE_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK |
@@ -42,7 +48,7 @@ class Handle : Gtk.EventBox {
Gtk.CssProvider css_provider = new Gtk.CssProvider();
try {
css_provider.load_from_data(
- "GtkEventBox { background-color: gray }", -1);
+ "#IBusHandle { background-color: gray }", -1);
} catch (GLib.Error error) {
warning("Parse error in Handle: %s", error.message);
}
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
index 2ca3a5e..cc19350 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -3,7 +3,7 @@
* ibus - The Input Bus
*
* Copyright(c) 2011-2014 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright(c) 2015 Takao Fujwiara <takao.fujiwara1@gmail.com>
+ * Copyright(c) 2015-2016 Takao Fujwiara <takao.fujiwara1@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -543,7 +543,12 @@ class Panel : IBus.PanelService {
return;
}
- string data_format = "GtkLabel { font: %s; }";
+ string data_format = "label { font: %s; }";
+ if (Gtk.MAJOR_VERSION < 3 ||
+ (Gtk.MAJOR_VERSION == 3 && Gtk.MINOR_VERSION < 20)) {
+ data_format = "GtkLabel { font: %s; }";
+ }
+
string data = data_format.printf(font_name);
m_css_provider = new Gtk.CssProvider();
--
2.7.4
From 160d3c975af91eea6b8271b757be769b8ceef98d Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Tue, 21 Jun 2016 18:10:21 +0900
Subject: [PATCH 1/4] engine: Implement Emoji typing with XKB engines
Now Ctrl+Shift+e can convert an Emoji annotation to the Emoji characters
likes Ctrl+Shift+u.
The annotations are described as "Keywords" in the Unicode Emoji Data:
http://www.unicode.org/emoji/charts/emoji-list.html
'emoji-parser' compiles 'emoji-list.html' and generates 'emoji.dict'
Review URL: https://codereview.appspot.com/295610043
---
configure.ac | 16 ++
src/Makefile.am | 31 ++++
src/emoji-parser.c | 217 ++++++++++++++++++++++++
src/ibusenginesimple.c | 443 ++++++++++++++++++++++++++++++++++++++++++++-----
src/ibusutil.c | 193 ++++++++++++++++++++-
src/ibusutil.h | 31 +++-
6 files changed, 890 insertions(+), 41 deletions(-)
create mode 100644 src/emoji-parser.c
diff --git a/configure.ac b/configure.ac
index 1e1f5dd..3128ef9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -599,6 +599,21 @@ if test x"$enable_libnotify" = x"yes"; then
enable_libnotify="yes (enabled, use --disable-libnotify to disable)"
fi
+# --disable-emoji-dict option.
+AC_ARG_ENABLE(emoji-dict,
+ AS_HELP_STRING([--disable-emoji-dict],
+ [Do not build Emoji dict files]),
+ [enable_emoji_dict=$enableval],
+ [enable_emoji_dict=yes]
+)
+AM_CONDITIONAL([ENABLE_EMOJI_DICT], [test x"$enable_emoji_dict" = x"yes"])
+if test x"$enable_emoji_dict" = x"yes"; then
+ PKG_CHECK_MODULES(LIBXML2, [
+ libxml-2.0
+ ])
+ enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
+fi
+
# Check iso-codes.
PKG_CHECK_MODULES(ISOCODES, [
iso-codes
@@ -682,6 +697,7 @@ Build options:
Panel icon "$IBUS_ICON_KEYBOARD"
Enable surrounding-text $enable_surrounding_text
Enable libnotify $enable_libnotify
+ Enable Emoji dict $enable_emoji_dict
Run test cases $enable_tests
])
diff --git a/src/Makefile.am b/src/Makefile.am
index adaebe9..a33b67d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -231,7 +231,38 @@ ibusmarshalers.c: ibusmarshalers.h ibusmarshalers.list
$(GLIB_GENMARSHAL) --prefix=_ibus_marshal $(srcdir)/ibusmarshalers.list --body --internal) > $@.tmp && \
mv $@.tmp $@
+if ENABLE_EMOJI_DICT
+AM_CPPFLAGS += -DENABLE_EMOJI_DICT
+
+dictdir = $(pkgdatadir)/dicts
+dict_DATA = emoji.dict
+
+noinst_PROGRAMS = emoji-parser
+
+emoji.dict: emoji-parser emoji-list.html
+ $(builddir)/emoji-parser emoji-list.html $@
+
+emoji_parser_SOURCES = \
+ emoji-parser.c \
+ $(NULL)
+emoji_parser_CFLAGS = \
+ $(GLIB2_CFLAGS) \
+ $(LIBXML2_CFLAGS) \
+ $(NULL)
+emoji_parser_LDADD = \
+ $(GLIB2_LIBS) \
+ $(LIBXML2_LIBS) \
+ $(libibus) \
+ $(NULL)
+
+CLEANFILES += \
+ $(dict_DATA) \
+ $(NULL)
+endif
+
EXTRA_DIST = \
+ emoji-list.html \
+ emoji-parser.c \
ibusversion.h.in \
ibusmarshalers.list \
ibusenumtypes.h.template \
diff --git a/src/emoji-parser.c b/src/emoji-parser.c
new file mode 100644
index 0000000..cf92fee
--- /dev/null
+++ b/src/emoji-parser.c
@@ -0,0 +1,217 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* ibus - The Input Bus
+ * Copyright (C) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2016 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
+ */
+
+/* Convert http://www.unicode.org/emoji/charts/emoji-list.html
+ * to the dictionary file which look up the Emoji from the annotation.
+ */
+
+#include <glib.h>
+#include <libxml/HTMLparser.h>
+#include <libgen.h>
+
+#include "ibusutil.h"
+
+typedef struct _EmojiData EmojiData;
+struct _EmojiData {
+ gchar *class;
+ gchar *emoji;
+ GSList *annotates;
+ GSList *prev_annotates;
+ GHashTable *dict;
+};
+
+const gchar *progname;
+
+static gboolean parse_node (xmlNode *node,
+ gboolean is_child,
+ const gchar *prop_name,
+ EmojiData *data);
+
+static void
+usage (void)
+{
+ g_print ("%s emoji-list.html emoji.dict\n", progname);
+}
+
+static void
+reset_emoji_element (EmojiData *data)
+{
+ g_clear_pointer (&data->class, g_free);
+ g_clear_pointer (&data->emoji, g_free);
+ if (data->annotates) {
+ g_slist_free_full (data->prev_annotates, g_free);
+ data->prev_annotates = data->annotates;
+ data->annotates = NULL;
+ }
+}
+
+static void
+free_dict_words (gpointer list)
+{
+ g_slist_free_full (list, g_free);
+}
+
+static gboolean
+parse_attr (xmlAttr *attr,
+ EmojiData *data)
+{
+ if (g_strcmp0 ((const gchar *) attr->name, "class") == 0 && attr->children)
+ parse_node (attr->children, TRUE, (const gchar *) attr->name, data);
+ if (g_strcmp0 ((const gchar *) attr->name, "target") == 0 && attr->children)
+ parse_node (attr->children, TRUE, (const gchar *) attr->name, data);
+ if (attr->next)
+ parse_attr (attr->next, data);
+ return TRUE;
+}
+
+static gboolean
+parse_node (xmlNode *node,
+ gboolean is_child,
+ const gchar *prop_name,
+ EmojiData *data)
+{
+ if (g_strcmp0 ((const gchar *) node->name, "tr") == 0) {
+ GSList *annotates = data->annotates;
+ while (annotates) {
+ GSList *emojis = g_hash_table_lookup (data->dict, annotates->data);
+ if (emojis) {
+ emojis = g_slist_copy_deep (emojis, (GCopyFunc) g_strdup, NULL);
+ }
+ emojis = g_slist_append (emojis, g_strdup (data->emoji));
+ g_hash_table_replace (data->dict,
+ g_strdup (annotates->data),
+ emojis);
+ annotates = annotates->next;
+ }
+ reset_emoji_element (data);
+ }
+ /* if node->name is "text" and is_child is FALSE,
+ * it's '\n' or Space between <td> and <td>.
+ */
+ if (g_strcmp0 ((const gchar *) node->name, "text") == 0 && is_child) {
+ /* Get "chars" in <td class="chars"> */
+ if (g_strcmp0 (prop_name, "class") == 0) {
+ if (g_strcmp0 (data->class, (const gchar *) node->content) != 0) {
+ g_clear_pointer (&data->class, g_free);
+ data->class = g_strdup ((const gchar *) node->content);
+ }
+ }
+ /* Get "annotate" in <td class="name"><a target="annotate"> */
+ if (g_strcmp0 (prop_name, "target") == 0 &&
+ g_strcmp0 (data->class, "name") == 0) {
+ g_clear_pointer (&data->class, g_free);
+ data->class = g_strdup ((const gchar *) node->content);
+ }
+ /* Get "emoji" in <td class="chars">emoji</td> */
+ if (g_strcmp0 (prop_name, "td") == 0 &&
+ g_strcmp0 (data->class, "chars") == 0) {
+ data->emoji = g_strdup ((const gchar *) node->content);
+ }
+ /* We ignore "NAME" for <td class="name">NAME</td> but
+ * takes "ANNOTATE" for
+ * <td class="name"><a target="annotate">ANNOTATE</a></td>
+ */
+ if (g_strcmp0 (prop_name, "td") == 0 &&
+ g_strcmp0 (data->class, "name") == 0) {
+ g_slist_free_full (data->annotates, g_free);
+ data->annotates = NULL;
+ }
+ /* Get "ANNOTATE" in
+ * <td class="name"><a target="annotate">ANNOTATE</a></td>
+ */
+ if (g_strcmp0 (prop_name, "a") == 0 &&
+ g_strcmp0 (data->class, "annotate") == 0) {
+ data->annotates =
+ g_slist_append (data->annotates,
+ g_strdup ((const gchar *) node->content));
+ }
+ }
+ /* Get "foo" in <td class="foo"> */
+ if (g_strcmp0 ((const gchar *) node->name, "td") == 0 &&
+ node->properties != NULL) {
+ parse_attr (node->properties, data);
+ }
+ /* Get "foo" in <a target="foo"> */
+ if (g_strcmp0 ((const gchar *) node->name, "a") == 0 &&
+ node->properties != NULL) {
+ parse_attr (node->properties, data);
+ }
+ if (node->children) {
+ parse_node (node->children, TRUE, (const gchar *) node->name, data);
+ } else {
+ /* If annotate is NULL likes <td class="name"></td>,
+ * the previous emoji cell has the same annotate.
+ */
+ if (g_strcmp0 ((const gchar *) node->name, "td") == 0 &&
+ g_strcmp0 (data->class, "name") == 0) {
+ data->annotates = g_slist_copy_deep (data->prev_annotates,
+ (GCopyFunc) g_strdup,
+ NULL);
+ }
+ }
+ if (node->next)
+ parse_node (node->next, FALSE, (const gchar *) node->name, data);
+
+ return TRUE;
+}
+
+static GHashTable *
+parse_html (const gchar *filename)
+{
+ xmlDoc *doc = htmlParseFile (filename, "utf-8");
+ EmojiData data = { 0, };
+
+ if (doc == NULL || doc->children == NULL) {
+ g_warning ("Parse Error in document type: %x",
+ doc ? doc->type : 0);
+ return FALSE;
+ }
+
+ data.dict = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ free_dict_words);
+ parse_node (doc->children, TRUE, (const gchar *) doc->name, &data);
+
+ reset_emoji_element (&data);
+ g_slist_free_full (data.prev_annotates, g_free);
+
+ return data.dict;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GHashTable *dict;
+ progname = basename (argv[0]);
+
+ if (argc < 3) {
+ usage ();
+ return -1;
+ }
+
+ dict = parse_html (argv[1]);
+ ibus_emoji_dict_save (argv[2], dict);
+ g_hash_table_destroy (dict);
+
+ return 0;
+}
diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c
index 1b688b0..8efe5a9 100644
--- a/src/ibusenginesimple.c
+++ b/src/ibusenginesimple.c
@@ -3,7 +3,7 @@
/* ibus - The Input Bus
* Copyright (C) 2014 Peng Huang <shawn.p.huang@gmail.com>
* Copyright (C) 2015-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2016 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
@@ -31,6 +31,7 @@
#include "ibuskeys.h"
#include "ibuskeysyms.h"
+#include "ibusutil.h"
/* This file contains the table of the compose sequences,
* static const guint16 gtk_compose_seqs_compact[] = {}
@@ -42,16 +43,27 @@
#include <stdlib.h>
#define X11_DATADIR X11_DATA_PREFIX "/share/X11/locale"
+#define EMOJI_SOURCE_LEN 100
#define IBUS_ENGINE_SIMPLE_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_ENGINE_SIMPLE, IBusEngineSimplePrivate))
-struct _IBusEngineSimplePrivate {
- guint16 compose_buffer[IBUS_MAX_COMPOSE_LEN + 1];
- gunichar tentative_match;
- gint tentative_match_len;
+typedef struct {
+ GHashTable *dict;
+ int max_seq_len;
+} IBusEngineDict;
- guint in_hex_sequence : 1;
- guint modifiers_dropped : 1;
+struct _IBusEngineSimplePrivate {
+ guint16 compose_buffer[EMOJI_SOURCE_LEN];
+ gunichar tentative_match;
+ gchar *tentative_emoji;
+ gint tentative_match_len;
+
+ guint in_hex_sequence : 1;
+ guint in_emoji_sequence : 1;
+ guint modifiers_dropped : 1;
+ IBusEngineDict *emoji_dict;
+ IBusLookupTable *lookup_table;
+ gboolean lookup_table_visible;
};
/* From the values below, the value 30 means the number of different first keysyms
@@ -97,6 +109,8 @@ static gboolean ibus_engine_simple_process_key_event
guint modifiers);
static void ibus_engine_simple_commit_char (IBusEngineSimple *simple,
gunichar ch);
+static void ibus_engine_simple_commit_str (IBusEngineSimple *simple,
+ const gchar *str);
static void ibus_engine_simple_update_preedit_text
(IBusEngineSimple *simple);
@@ -128,6 +142,18 @@ ibus_engine_simple_init (IBusEngineSimple *simple)
static void
ibus_engine_simple_destroy (IBusEngineSimple *simple)
{
+ IBusEngineSimplePrivate *priv = simple->priv;
+
+ if (priv->emoji_dict) {
+ if (priv->emoji_dict->dict)
+ g_clear_pointer (&priv->emoji_dict->dict, g_hash_table_destroy);
+ g_slice_free (IBusEngineDict, priv->emoji_dict);
+ priv->emoji_dict = NULL;
+ }
+
+ g_clear_pointer (&priv->lookup_table, g_object_unref);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+
IBUS_OBJECT_CLASS(ibus_engine_simple_parent_class)->destroy (
IBUS_OBJECT (simple));
}
@@ -146,6 +172,11 @@ ibus_engine_simple_reset (IBusEngine *engine)
priv->tentative_match_len = 0;
ibus_engine_hide_preedit_text ((IBusEngine *)simple);
}
+ if (priv->tentative_emoji || priv->in_emoji_sequence) {
+ priv->in_emoji_sequence = FALSE;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ ibus_engine_hide_preedit_text ((IBusEngine *)simple);
+ }
}
static void
@@ -162,23 +193,60 @@ ibus_engine_simple_commit_char (IBusEngineSimple *simple,
priv->tentative_match_len = 0;
ibus_engine_simple_update_preedit_text (simple);
}
+ if (priv->tentative_emoji || priv->in_emoji_sequence) {
+ priv->in_emoji_sequence = FALSE;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ ibus_engine_simple_update_preedit_text (simple);
+ }
ibus_engine_commit_text ((IBusEngine *)simple,
ibus_text_new_from_unichar (ch));
}
static void
+ibus_engine_simple_commit_str (IBusEngineSimple *simple,
+ const gchar *str)
+{
+ IBusEngineSimplePrivate *priv = simple->priv;
+ gchar *backup_str;
+
+ g_return_if_fail (str && *str);
+
+ backup_str = g_strdup (str);
+
+ if (priv->tentative_match || priv->in_hex_sequence) {
+ priv->in_hex_sequence = FALSE;
+ priv->tentative_match = 0;
+ priv->tentative_match_len = 0;
+ ibus_engine_simple_update_preedit_text (simple);
+ }
+ if (priv->tentative_emoji || priv->in_emoji_sequence) {
+ priv->in_emoji_sequence = FALSE;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ ibus_engine_simple_update_preedit_text (simple);
+ }
+
+ ibus_engine_commit_text ((IBusEngine *)simple,
+ ibus_text_new_from_string (backup_str));
+ g_free (backup_str);
+}
+
+static void
ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
{
IBusEngineSimplePrivate *priv = simple->priv;
- gunichar outbuf[IBUS_MAX_COMPOSE_LEN + 2];
+ gunichar outbuf[EMOJI_SOURCE_LEN + 1];
int len = 0;
- if (priv->in_hex_sequence) {
+ if (priv->in_hex_sequence || priv->in_emoji_sequence) {
int hexchars = 0;
- outbuf[0] = L'u';
+ if (priv->in_hex_sequence)
+ outbuf[0] = L'u';
+ else
+ outbuf[0] = L'@';
+
len = 1;
while (priv->compose_buffer[hexchars] != 0) {
@@ -187,10 +255,22 @@ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple)
++len;
++hexchars;
}
- g_assert (len <= IBUS_MAX_COMPOSE_LEN + 1);
+
+ if (priv->in_hex_sequence)
+ g_assert (len <= IBUS_MAX_COMPOSE_LEN + 1);
+ else
+ g_assert (len <= EMOJI_SOURCE_LEN + 1);
}
- else if (priv->tentative_match)
+ else if (priv->tentative_match) {
outbuf[len++] = priv->tentative_match;
+ } else if (priv->tentative_emoji && *priv->tentative_emoji) {
+ IBusText *text = ibus_text_new_from_string (priv->tentative_emoji);
+ len = strlen (priv->tentative_emoji);
+ ibus_text_append_attribute (text,
+ IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, len);
+ ibus_engine_update_preedit_text ((IBusEngine *)simple, text, len, TRUE);
+ return;
+ }
outbuf[len] = L'\0';
if (len == 0) {
@@ -277,6 +357,104 @@ check_hex (IBusEngineSimple *simple,
return TRUE;
}
+static IBusEngineDict *
+load_emoji_dict ()
+{
+ IBusEngineDict *emoji_dict;
+ GList *keys;
+ int max_length = 0;
+
+ emoji_dict = g_slice_new0 (IBusEngineDict);
+ emoji_dict->dict = ibus_emoji_dict_load (IBUS_DATA_DIR "/dicts/emoji.dict");
+ if (!emoji_dict->dict)
+ return emoji_dict;
+
+ keys = g_hash_table_get_keys (emoji_dict->dict);
+ for (; keys; keys = keys->next) {
+ int length = strlen (keys->data);
+ if (max_length < length)
+ max_length = length;
+ }
+ emoji_dict->max_seq_len = max_length;
+
+ return emoji_dict;
+}
+
+static gboolean
+check_emoji_table (IBusEngineSimple *simple,
+ gint n_compose,
+ gint index)
+{
+ IBusEngineSimplePrivate *priv = simple->priv;
+ IBusEngineDict *emoji_dict = priv->emoji_dict;
+ GString *str = NULL;
+ gint i;
+ gchar buf[7];
+ GSList *words = NULL;
+
+ g_assert (IBUS_IS_ENGINE_SIMPLE (simple));
+
+ if (priv->lookup_table == NULL) {
+ priv->lookup_table = ibus_lookup_table_new (10, 0, TRUE, TRUE);
+ g_object_ref_sink (priv->lookup_table);
+ }
+ if (emoji_dict == NULL)
+ emoji_dict = priv->emoji_dict = load_emoji_dict (simple);
+
+ if (emoji_dict == NULL || emoji_dict->dict == NULL)
+ return FALSE;
+
+ if (n_compose > emoji_dict->max_seq_len)
+ return FALSE;
+
+ str = g_string_new (NULL);
+ priv->lookup_table_visible = FALSE;
+
+ i = 0;
+ while (i < n_compose) {
+ gunichar ch;
+
+ ch = ibus_keyval_to_unicode (priv->compose_buffer[i]);
+
+ if (ch == 0)
+ return FALSE;
+
+ if (!g_unichar_isprint (ch))
+ return FALSE;
+
+ buf[g_unichar_to_utf8 (ch, buf)] = '\0';
+
+ g_string_append (str, buf);
+
+ ++i;
+ }
+
+ if (str->str) {
+ words = g_hash_table_lookup (emoji_dict->dict, str->str);
+ }
+ g_string_free (str, TRUE);
+
+ if (words != NULL) {
+ int i = 0;
+ ibus_lookup_table_clear (priv->lookup_table);
+ priv->lookup_table_visible = TRUE;
+
+ while (words) {
+ if (i == index) {
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->tentative_emoji = g_strdup (words->data);
+ }
+ IBusText *text = ibus_text_new_from_string (words->data);
+ ibus_lookup_table_append_candidate (priv->lookup_table, text);
+ words = words->next;
+ i++;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static int
compare_seq_index (const void *key, const void *value)
{
@@ -626,10 +804,10 @@ ibus_check_algorithmically (const guint16 *compose_buffer,
static gboolean
no_sequence_matches (IBusEngineSimple *simple,
- gint n_compose,
- guint keyval,
- guint keycode,
- guint modifiers)
+ gint n_compose,
+ guint keyval,
+ guint keycode,
+ guint modifiers)
{
IBusEngineSimplePrivate *priv = simple->priv;
@@ -642,8 +820,7 @@ no_sequence_matches (IBusEngineSimple *simple,
gint len = priv->tentative_match_len;
int i;
- ibus_engine_simple_commit_char (simple,
- priv->tentative_match);
+ ibus_engine_simple_commit_char (simple, priv->tentative_match);
priv->compose_buffer[0] = 0;
for (i=0; i < n_compose - len - 1; i++) {
@@ -655,8 +832,11 @@ no_sequence_matches (IBusEngineSimple *simple,
return ibus_engine_simple_process_key_event (
(IBusEngine *)simple, keyval, keycode, modifiers);
- }
- else {
+ } else if (priv->tentative_emoji && *priv->tentative_emoji) {
+ ibus_engine_simple_commit_str (simple, priv->tentative_emoji);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->compose_buffer[0] = 0;
+ } else {
priv->compose_buffer[0] = 0;
if (n_compose > 1) {
/* Invalid sequence */
@@ -676,6 +856,7 @@ no_sequence_matches (IBusEngineSimple *simple,
else
return FALSE;
}
+ return FALSE;
}
static gboolean
@@ -687,6 +868,39 @@ is_hex_keyval (guint keyval)
}
static gboolean
+is_graph_keyval (guint keyval)
+{
+ gunichar ch = ibus_keyval_to_unicode (keyval);
+
+ return g_unichar_isgraph (ch);
+}
+
+static void
+ibus_engine_simple_update_lookup_and_aux_table (IBusEngineSimple *simple)
+{
+ IBusEngineSimplePrivate *priv;
+ guint index, candidates;
+ gchar *aux_label = NULL;
+ IBusText *text = NULL;
+
+ g_return_if_fail (IBUS_IS_ENGINE_SIMPLE (simple));
+
+ priv = simple->priv;
+ index = ibus_lookup_table_get_cursor_pos (priv->lookup_table) + 1;
+ candidates = ibus_lookup_table_get_number_of_candidates(priv->lookup_table);
+ aux_label = g_strdup_printf ("(%u / %u)", index, candidates);
+ text = ibus_text_new_from_string (aux_label);
+ g_free (aux_label);
+
+ ibus_engine_update_auxiliary_text (IBUS_ENGINE (simple),
+ text,
+ priv->lookup_table_visible);
+ ibus_engine_update_lookup_table (IBUS_ENGINE (simple),
+ priv->lookup_table,
+ priv->lookup_table_visible);
+}
+
+static gboolean
ibus_engine_simple_process_key_event (IBusEngine *engine,
guint keyval,
guint keycode,
@@ -697,10 +911,13 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
gint n_compose = 0;
gboolean have_hex_mods;
gboolean is_hex_start;
+ gboolean is_emoji_start = FALSE;
gboolean is_hex_end;
+ gboolean is_space;
gboolean is_backspace;
gboolean is_escape;
guint hex_keyval;
+ guint printable_keyval;
gint i;
gboolean compose_finish;
gunichar output_char;
@@ -714,17 +931,16 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
keyval == IBUS_KEY_Shift_L || keyval == IBUS_KEY_Shift_R)) {
if (priv->tentative_match &&
g_unichar_validate (priv->tentative_match)) {
- ibus_engine_simple_commit_char (simple,
- priv->tentative_match);
- }
- else if (n_compose == 0) {
+ ibus_engine_simple_commit_char (simple, priv->tentative_match);
+ } else if (n_compose == 0) {
priv->modifiers_dropped = TRUE;
- }
- else {
+ } else {
/* invalid hex sequence */
/* FIXME beep_window (event->window); */
priv->tentative_match = 0;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
priv->compose_buffer[0] = 0;
ibus_engine_simple_update_preedit_text (simple);
@@ -732,6 +948,26 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
return TRUE;
}
+ /* Handle Shift + Space */
+ else if (priv->in_emoji_sequence &&
+ (keyval == IBUS_KEY_Control_L || keyval == IBUS_KEY_Control_R)) {
+ if (priv->tentative_emoji && *priv->tentative_emoji) {
+ ibus_engine_simple_commit_str (simple, priv->tentative_emoji);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ } else if (n_compose == 0) {
+ priv->modifiers_dropped = TRUE;
+ } else {
+ /* invalid hex sequence */
+ /* FIXME beep_window (event->window); */
+ priv->tentative_match = 0;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
+ priv->compose_buffer[0] = 0;
+
+ ibus_engine_simple_update_preedit_text (simple);
+ }
+ }
else
return FALSE;
}
@@ -741,25 +977,33 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
if (keyval == ibus_compose_ignore[i])
return FALSE;
- if (priv->in_hex_sequence && priv->modifiers_dropped)
+ if ((priv->in_hex_sequence || priv->in_emoji_sequence)
+ && priv->modifiers_dropped) {
have_hex_mods = TRUE;
- else
+ } else {
have_hex_mods = (modifiers & (HEX_MOD_MASK)) == HEX_MOD_MASK;
+ }
is_hex_start = keyval == IBUS_KEY_U;
+#ifdef ENABLE_EMOJI_DICT
+ is_emoji_start = keyval == IBUS_KEY_E;
+#endif
is_hex_end = (keyval == IBUS_KEY_space ||
keyval == IBUS_KEY_KP_Space ||
keyval == IBUS_KEY_Return ||
keyval == IBUS_KEY_ISO_Enter ||
keyval == IBUS_KEY_KP_Enter);
+ is_space = (keyval == IBUS_KEY_space || keyval == IBUS_KEY_KP_Space);
is_backspace = keyval == IBUS_KEY_BackSpace;
is_escape = keyval == IBUS_KEY_Escape;
hex_keyval = is_hex_keyval (keyval) ? keyval : 0;
+ printable_keyval = is_graph_keyval (keyval) ? keyval : 0;
/* gtkimcontextsimple causes a buffer overflow in priv->compose_buffer.
* Add the check code here.
*/
- if (n_compose >= IBUS_MAX_COMPOSE_LEN) {
+ if ((n_compose >= IBUS_MAX_COMPOSE_LEN && priv->in_hex_sequence) ||
+ (n_compose >= EMOJI_SOURCE_LEN && priv->in_emoji_sequence)) {
if (is_backspace) {
priv->compose_buffer[--n_compose] = 0;
}
@@ -767,7 +1011,9 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
/* invalid hex sequence */
// beep_window (event->window);
priv->tentative_match = 0;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = FALSE;
priv->compose_buffer[0] = 0;
}
else if (is_escape) {
@@ -789,12 +1035,16 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
* ISO_Level3_Switch.
*/
if (!have_hex_mods ||
- (n_compose > 0 && !priv->in_hex_sequence) ||
- (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
+ (n_compose > 0 && !priv->in_hex_sequence && !priv->in_emoji_sequence) ||
+ (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start &&
+ !priv->in_emoji_sequence && !is_emoji_start) ||
(priv->in_hex_sequence && !hex_keyval &&
- !is_hex_start && !is_hex_end && !is_escape && !is_backspace)) {
+ !is_hex_start && !is_hex_end && !is_escape && !is_backspace) ||
+ (priv->in_emoji_sequence && !printable_keyval &&
+ !is_emoji_start && !is_hex_end && !is_escape && !is_backspace)) {
if (modifiers & (IBUS_MOD1_MASK | IBUS_CONTROL_MASK) ||
- (priv->in_hex_sequence && priv->modifiers_dropped &&
+ ((priv->in_hex_sequence || priv->in_emoji_sequence) &&
+ priv->modifiers_dropped &&
(keyval == IBUS_KEY_Return ||
keyval == IBUS_KEY_ISO_Enter ||
keyval == IBUS_KEY_KP_Enter))) {
@@ -816,6 +1066,20 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
return TRUE;
}
+ if (priv->in_emoji_sequence && have_hex_mods && is_backspace) {
+ if (n_compose > 0) {
+ n_compose--;
+ priv->compose_buffer[n_compose] = 0;
+ check_emoji_table (simple, n_compose, -1);
+ ibus_engine_simple_update_lookup_and_aux_table (simple);
+ } else {
+ priv->in_emoji_sequence = FALSE;
+ }
+
+ ibus_engine_simple_update_preedit_text (simple);
+
+ return TRUE;
+ }
/* Check for hex sequence restart */
if (priv->in_hex_sequence && have_hex_mods && is_hex_start) {
@@ -833,13 +1097,41 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
}
}
}
+ if (priv->in_emoji_sequence && have_hex_mods && is_emoji_start) {
+ if (priv->tentative_emoji && *priv->tentative_emoji) {
+ ibus_engine_simple_commit_str (simple, priv->tentative_emoji);
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ }
+ else {
+ if (n_compose > 0) {
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->in_emoji_sequence = FALSE;
+ priv->compose_buffer[0] = 0;
+ }
+ }
+ }
/* Check for hex sequence start */
if (!priv->in_hex_sequence && have_hex_mods && is_hex_start) {
priv->compose_buffer[0] = 0;
priv->in_hex_sequence = TRUE;
+ priv->in_emoji_sequence = FALSE;
priv->modifiers_dropped = FALSE;
priv->tentative_match = 0;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+
+ // g_debug ("Start HEX MODE");
+
+ ibus_engine_simple_update_preedit_text (simple);
+
+ return TRUE;
+ } else if (!priv->in_emoji_sequence && have_hex_mods && is_emoji_start) {
+ priv->compose_buffer[0] = 0;
+ priv->in_hex_sequence = FALSE;
+ priv->in_emoji_sequence = TRUE;
+ priv->modifiers_dropped = FALSE;
+ priv->tentative_match = 0;
+ g_clear_pointer (&priv->tentative_emoji, g_free);
// g_debug ("Start HEX MODE");
@@ -864,9 +1156,20 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
// beep_window (event->window);
return TRUE;
}
- }
- else
+ } else if (priv->in_emoji_sequence) {
+ if (printable_keyval) {
+ priv->compose_buffer[n_compose++] = printable_keyval;
+ }
+ else if (is_space && (modifiers & IBUS_SHIFT_MASK)) {
+ priv->compose_buffer[n_compose++] = IBUS_KEY_space;
+ }
+ else if (is_escape) {
+ ibus_engine_simple_reset (engine);
+ return TRUE;
+ }
+ } else {
priv->compose_buffer[n_compose++] = keyval;
+ }
priv->compose_buffer[n_compose] = 0;
@@ -880,8 +1183,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
ibus_engine_simple_commit_char (simple,
priv->tentative_match);
priv->compose_buffer[0] = 0;
- }
- else {
+ } else {
// FIXME
/* invalid hex sequence */
// beep_window (event->window);
@@ -899,6 +1201,73 @@ ibus_engine_simple_process_key_event (IBusEngine *engine,
return TRUE;
}
}
+ else if (priv->in_emoji_sequence) {
+ if (have_hex_mods && n_compose > 0) {
+ gboolean update_lookup_table = FALSE;
+
+ if (priv->lookup_table_visible) {
+ switch (keyval) {
+ case IBUS_KEY_space:
+ case IBUS_KEY_KP_Space:
+ if ((modifiers & IBUS_SHIFT_MASK) == 0) {
+ ibus_lookup_table_cursor_down (priv->lookup_table);
+ update_lookup_table = TRUE;
+ }
+ break;
+ case IBUS_KEY_Down:
+ ibus_lookup_table_cursor_down (priv->lookup_table);
+ update_lookup_table = TRUE;
+ break;
+ case IBUS_KEY_Up:
+ ibus_lookup_table_cursor_up (priv->lookup_table);
+ update_lookup_table = TRUE;
+ break;
+ case IBUS_KEY_Page_Down:
+ ibus_lookup_table_page_down (priv->lookup_table);
+ update_lookup_table = TRUE;
+ break;
+ case IBUS_KEY_Page_Up:
+ ibus_lookup_table_page_up (priv->lookup_table);
+ update_lookup_table = TRUE;
+ break;
+ default:;
+ }
+ }
+
+ if (!update_lookup_table) {
+ if (is_hex_end && !is_space) {
+ if (priv->lookup_table) {
+ int index = (int) ibus_lookup_table_get_cursor_pos (
+ priv->lookup_table);
+ check_emoji_table (simple, n_compose, index);
+ priv->lookup_table_visible = FALSE;
+ update_lookup_table = TRUE;
+ }
+ }
+ else if (check_emoji_table (simple, n_compose, -1)) {
+ update_lookup_table = TRUE;
+ }
+ }
+
+ if (update_lookup_table)
+ ibus_engine_simple_update_lookup_and_aux_table (simple);
+ if (is_hex_end && !is_space) {
+ if (priv->tentative_emoji && *priv->tentative_emoji) {
+ ibus_engine_simple_commit_str (simple,
+ priv->tentative_emoji);
+ priv->compose_buffer[0] = 0;
+ } else {
+ g_clear_pointer (&priv->tentative_emoji, g_free);
+ priv->in_emoji_sequence = FALSE;
+ priv->compose_buffer[0] = 0;
+ }
+ }
+
+ ibus_engine_simple_update_preedit_text (simple);
+
+ return TRUE;
+ }
+ }
else {
GSList *list = global_tables;
while (list) {
diff --git a/src/ibusutil.c b/src/ibusutil.c
index b9f3fdd..bfaa4f4 100644
--- a/src/ibusutil.c
+++ b/src/ibusutil.c
@@ -2,8 +2,8 @@
/* vim:set et sts=4: */
/* bus - The Input Bus
* Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2010-2015 Takao Fujiwara <takao.fujiwara1@gmail.com>
- * Copyright (C) 2008-2015 Red Hat, Inc.
+ * Copyright (C) 2010-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2016 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
@@ -34,6 +34,9 @@
#include <libintl.h>
#endif
+#define IBUS_DICT_MAGIC "IBusDict"
+#define IBUS_DICT_VERSION (1)
+
/* gettext macro */
#define N_(t) t
@@ -125,6 +128,74 @@ _load_lang()
ibus_xml_free (node);
}
+static void
+free_dict_words (gpointer list)
+{
+ g_slist_free_full (list, g_free);
+}
+
+static void
+variant_foreach_add_emoji (gchar *annotation,
+ GSList *emojis,
+ GVariantBuilder *builder)
+{
+ int i;
+ int length = (int) g_slist_length (emojis);
+ gchar **buff = g_new0 (gchar *, length);
+ GSList *l = emojis;
+
+ for (i = 0; i < length; i++, l = l->next)
+ buff[i] = (gchar *) l->data;
+
+ g_variant_builder_add (builder,
+ "{sv}",
+ annotation,
+ g_variant_new_strv ((const gchar * const *) buff,
+ length));
+ g_free (buff);
+}
+
+static GVariant *
+ibus_emoji_dict_serialize (GHashTable *dict)
+{
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_hash_table_foreach (dict, (GHFunc) variant_foreach_add_emoji, &builder);
+ return g_variant_builder_end (&builder);
+}
+
+static GHashTable *
+ibus_emoji_dict_deserialize (GVariant *variant)
+{
+ GHashTable *dict = NULL;
+ GVariantIter iter;
+ gchar *annotate = NULL;
+ GVariant *emojis_variant = NULL;
+
+ dict = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ free_dict_words);
+
+ g_variant_iter_init (&iter, variant);
+ while (g_variant_iter_loop (&iter, "{sv}", &annotate, &emojis_variant)) {
+ gsize i;
+ gsize length = 0;
+ const gchar **array = g_variant_get_strv (emojis_variant, &length);
+ GSList *emojis = NULL;
+
+ for (i = 0; i < length; i++) {
+ emojis = g_slist_append (emojis, g_strdup (array[i]));
+ }
+ g_hash_table_insert (dict, annotate, emojis);
+ annotate = NULL;
+ g_clear_pointer (&emojis_variant, g_variant_unref);
+ }
+
+ return dict;
+}
+
const gchar *
ibus_get_untranslated_language_name (const gchar *_locale)
{
@@ -171,3 +242,121 @@ ibus_g_variant_get_child_string (GVariant *variant, gsize index, char **str)
g_free (*str);
g_variant_get_child (variant, index, "s", str);
}
+
+void
+ibus_emoji_dict_save (const gchar *path, GHashTable *dict)
+{
+ GVariant *variant;
+ const gchar *header = IBUS_DICT_MAGIC;
+ const guint16 version = IBUS_DICT_VERSION;
+ const gchar *contents;
+ gsize length;
+ GError *error = NULL;
+
+ variant = g_variant_new ("(sqv)",
+ header,
+ version,
+ ibus_emoji_dict_serialize (dict));
+
+ contents = g_variant_get_data (variant);
+ length = g_variant_get_size (variant);
+
+ if (!g_file_set_contents (path, contents, length, &error)) {
+ g_warning ("Failed to save emoji dict %s: %s", path, error->message);
+ g_error_free (error);
+ }
+
+ g_variant_unref (variant);
+}
+
+GHashTable *
+ibus_emoji_dict_load (const gchar *path)
+{
+ gchar *contents = NULL;
+ gsize length = 0;
+ GError *error = NULL;
+ GVariant *variant_table = NULL;
+ GVariant *variant = NULL;
+ const gchar *header = NULL;
+ guint16 version = 0;
+ GHashTable *retval = NULL;
+
+ if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
+ g_warning ("Emoji dict does not exist: %s", path);
+ goto out_load_cache;
+ }
+
+ if (!g_file_get_contents (path, &contents, &length, &error)) {
+ g_warning ("Failed to get dict content %s: %s", path, error->message);
+ g_error_free (error);
+ goto out_load_cache;
+ }
+
+ variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sq)"),
+ contents,
+ length,
+ FALSE,
+ NULL,
+ NULL);
+
+ if (variant_table == NULL) {
+ g_warning ("cache table is broken.");
+ goto out_load_cache;
+ }
+
+ g_variant_get (variant_table, "(&sq)", &header, &version);
+
+ if (g_strcmp0 (header, IBUS_DICT_MAGIC) != 0) {
+ g_warning ("cache is not IBusDict.");
+ goto out_load_cache;
+ }
+
+ if (version != IBUS_DICT_VERSION) {
+ g_warning ("cache version is different: %u != %u",
+ version, IBUS_DICT_VERSION);
+ goto out_load_cache;
+ }
+
+ version = 0;
+ header = NULL;
+ g_variant_unref (variant_table);
+
+ variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sqv)"),
+ contents,
+ length,
+ FALSE,
+ NULL,
+ NULL);
+
+ if (variant_table == NULL) {
+ g_warning ("cache table is broken.");
+ goto out_load_cache;
+ }
+
+ g_variant_get (variant_table, "(&sqv)",
+ NULL,
+ NULL,
+ &variant);
+
+ if (variant == NULL) {
+ g_warning ("cache dict is broken.");
+ goto out_load_cache;
+ }
+
+ retval = ibus_emoji_dict_deserialize (variant);
+
+out_load_cache:
+ if (variant)
+ g_variant_unref (variant);
+ if (variant_table)
+ g_variant_unref (variant_table);
+
+ return retval;
+}
+
+GSList *
+ibus_emoji_dict_lookup (GHashTable *dict,
+ const gchar *annotation)
+{
+ return (GSList *) g_hash_table_lookup (dict, annotation);
+}
diff --git a/src/ibusutil.h b/src/ibusutil.h
index 2c1360c..e619b67 100644
--- a/src/ibusutil.h
+++ b/src/ibusutil.h
@@ -2,8 +2,8 @@
/* vim:set et sts=4: */
/* bus - The Input Bus
* Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2010-2015 Takao Fujiwara <takao.fujiwara1@gmail.com>
- * Copyright (C) 2008-2015 Red Hat, Inc.
+ * Copyright (C) 2010-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2008-2016 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
@@ -53,4 +53,31 @@ const gchar * ibus_get_untranslated_language_name
*/
const gchar * ibus_get_language_name (const gchar *_locale);
+/**
+ * ibus_emoji_dict_save:
+ * @path: A path of the saved dictionary file.
+ * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary
+ *
+ * Save the Emoji dictionary to the cache file.
+ */
+void ibus_emoji_dict_save (const gchar *path,
+ GHashTable *dict);
+/**
+ * ibus_emoji_dict_load:
+ * @path: A path of the saved dictionary file.
+ *
+ * Returns: (element-type utf8 gpointer) (transfer none): An Emoji dictionary file loaded from the saved cache file.
+ */
+GHashTable * ibus_emoji_dict_load (const gchar *path);
+
+/**
+ * ibus_emoji_dict_lookup:
+ * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary
+ * @annotation: Annotation for Emoji characters
+ *
+ * Returns: (element-type utf8) (transfer none): List of Emoji characters
+ * This API is for gobject-introspection.
+ */
+GSList * ibus_emoji_dict_lookup (GHashTable *dict,
+ const gchar *annotation);
#endif
--
2.7.4
From 0ed644cd2b6c1d15bdba0d1c6d45d162b9b34806 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 23 Jun 2016 11:52:48 +0900
Subject: [PATCH 2/4] engine: Add emoji-list.html
Now we copied http://unicode.org/emoji/charts/emoji-list.html to
http://ibus.github.io/files/ibus/emoji-list.html
and download the file in the build time.
We don't save emoji-list.html in the tarball because the file size is
more than 5MB.
We always don't get the latest emoji-list.html to avoid the build error.
BUG=https://github.com/ibus/ibus/pull/1864
R=shawn.p.huang@gmail.com
Review URL: https://codereview.appspot.com/298580043
---
src/Makefile.am | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index a33b67d..22e031f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,6 +29,15 @@ endif
SUBDIRS = . $(TESTS_DIR)
+IBUS_V_wget = $(ibus__v_wget_@AM_V@)
+ibus__v_wget_ = $(ibus__v_wget_@AM_DEFAULT_V@)
+ibus__v_wget_0 = -nv
+ibus__v_wget_1 =
+IBUS_V_diff = $(ibus__v_diff_@AM_V@)
+ibus__v_diff_ = $(ibus__v_diff_@AM_DEFAULT_V@)
+ibus__v_diff_0 = -q
+ibus__v_diff_1 =
+
# libibus = libibus-@IBUS_API_VERSION@.la
libibus = libibus-1.0.la
@@ -239,6 +248,18 @@ dict_DATA = emoji.dict
noinst_PROGRAMS = emoji-parser
+emoji-list.html:
+ $(AM_V_at)wget $(IBUS_V_wget) \
+ http://ibus.github.io/files/ibus/emoji-list.html
+ $(AM_V_at)wget $(IBUS_V_wget) \
+ http://unicode.org/emoji/charts/emoji-list.html \
+ -O latest-emoji-list.html
+ $(AM_V_at)diff $(IBUS_V_diff) emoji-list.html latest-emoji-list.html; \
+ if test $$? -ne 0; then \
+ echo "#### WARNING: emoji-list.html is old." >&2; \
+ fi; \
+ rm latest-emoji-list.html;
+
emoji.dict: emoji-parser emoji-list.html
$(builddir)/emoji-parser emoji-list.html $@
@@ -257,11 +278,11 @@ emoji_parser_LDADD = \
CLEANFILES += \
$(dict_DATA) \
+ emoji-list.html \
$(NULL)
endif
EXTRA_DIST = \
- emoji-list.html \
emoji-parser.c \
ibusversion.h.in \
ibusmarshalers.list \
--
2.7.4
From 0ee1896a2b3e75494f8f9fd9d04c27436f0877b8 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Thu, 7 Jul 2016 12:47:34 +0900
Subject: [PATCH 3/4] engine: Use annotations/en.xml from unocode.org but not
emoji-list.html
Downloading emoji-list would cause a different build by build site.
Now save annotations/en.xml from unicode.org and get emoji.json from
Emoji One.
en.xml is used for Unicode annotations and emoji.json is used for
aliases_ascii, e.g. ":)", and category, e.g. "people".
BUG=https://github.com/ibus/ibus/issues/1865
R=Shawn.P.Huang@gmail.com
Review URL: https://codereview.appspot.com/299530044
---
COPYING.unicode | 52 +++
Makefile.am | 1 +
configure.ac | 14 +-
data/Makefile.am | 5 +-
data/annotations/Makefile.am | 27 ++
data/annotations/en.xml | 1042 ++++++++++++++++++++++++++++++++++++++++++
ibus.spec.in | 14 +
src/Makefile.am | 31 +-
src/emoji-parser.c | 605 +++++++++++++++++++-----
9 files changed, 1634 insertions(+), 157 deletions(-)
create mode 100644 COPYING.unicode
create mode 100644 data/annotations/Makefile.am
create mode 100644 data/annotations/en.xml
diff --git a/COPYING.unicode b/COPYING.unicode
new file mode 100644
index 0000000..28d3060
--- /dev/null
+++ b/COPYING.unicode
@@ -0,0 +1,52 @@
+(Apply to data/annotations/en.xml)
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+ Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/. Unicode Data Files do not include PDF
+online code charts under the directory http://www.unicode.org/Public/.
+Software includes any source code published in the Unicode Standard or under
+the directories http://www.unicode.org/Public/,
+http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/.
+
+ NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES
+("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND
+AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF
+YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA
+FILES OR SOFTWARE.
+
+ COPYRIGHT AND PERMISSION NOTICE
+
+ Copyright © 1991-2016 Unicode, Inc. All rights reserved. Distributed under
+the Terms of Use in http://www.unicode.org/copyright.html.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+copy of the Unicode data files and any associated documentation (the "Data
+Files") or Unicode software and any associated documentation (the "Software")
+to deal in the Data Files or Software without restriction, including without
+limitation the rights to use, copy, modify, merge, publish, distribute, and/or
+sell copies of the Data Files or Software, and to permit persons to whom the
+Data Files or Software are furnished to do so, provided that (a) the above
+copyright notice(s) and this permission notice appear with all copies of the
+Data Files or Software, (b) both the above copyright notice(s) and this
+permission notice appear in associated documentation, and (c) there is clear
+notice in each modified Data File or in the Software as well as in the
+documentation associated with the Data File(s) or Software that the data or
+software has been modified.
+
+ THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD
+PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
+THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE
+DATA FILES OR SOFTWARE.
+
+ Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in these Data Files or Software without prior written authorization
+of the copyright holder.
diff --git a/Makefile.am b/Makefile.am
index 3c4702c..425d1ec 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -69,6 +69,7 @@ pkgconfig_DATA = ibus-@IBUS_API_VERSION@.pc
ibus_pc_in = ibus-@IBUS_API_VERSION@.pc.in
EXTRA_DIST = \
+ COPYING.unicode \
autogen.sh \
$(ibus_pc_in) \
ibus.spec.in \
diff --git a/configure.ac b/configure.ac
index 3128ef9..f789819 100644
--- a/configure.ac
+++ b/configure.ac
@@ -608,12 +608,20 @@ AC_ARG_ENABLE(emoji-dict,
)
AM_CONDITIONAL([ENABLE_EMOJI_DICT], [test x"$enable_emoji_dict" = x"yes"])
if test x"$enable_emoji_dict" = x"yes"; then
- PKG_CHECK_MODULES(LIBXML2, [
- libxml-2.0
+ PKG_CHECK_MODULES(JSON_GLIB1, [
+ json-glib-1.0
])
enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
fi
+AC_ARG_WITH(emoji-json-file,
+ AS_HELP_STRING([--with-emoji-json-file[=DIR/emoji.json]],
+ [Set emoji.json. (default: "/usr/lib/node_modules/emojione/emoji.json")]),
+ EMOJI_JSON_FILE=$with_emoji_json_file,
+ EMOJI_JSON_FILE="/usr/lib/node_modules/emojione/emoji.json"
+)
+AC_SUBST(EMOJI_JSON_FILE)
+
# Check iso-codes.
PKG_CHECK_MODULES(ISOCODES, [
iso-codes
@@ -639,6 +647,7 @@ engine/Makefile
util/Makefile
util/IMdkit/Makefile
data/Makefile
+data/annotations/Makefile.am
data/icons/Makefile
data/keymaps/Makefile
data/dconf/Makefile
@@ -698,6 +707,7 @@ Build options:
Enable surrounding-text $enable_surrounding_text
Enable libnotify $enable_libnotify
Enable Emoji dict $enable_emoji_dict
+ emoji.json path $EMOJI_JSON_FILE
Run test cases $enable_tests
])
diff --git a/data/Makefile.am b/data/Makefile.am
index e41c9a2..d9d613f 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -2,8 +2,8 @@
#
# ibus - The Input Bus
#
-# Copyright (c) 2007-2010 Peng Huang <shawn.p.huang@gmail.com>
-# Copyright (c) 2007-2010 Red Hat, Inc.
+# Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
+# Copyright (c) 2007-2016 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
@@ -21,6 +21,7 @@
# USA
SUBDIRS = \
+ annotations \
icons \
keymaps \
$(NULL)
diff --git a/data/annotations/Makefile.am b/data/annotations/Makefile.am
new file mode 100644
index 0000000..d87b933
--- /dev/null
+++ b/data/annotations/Makefile.am
@@ -0,0 +1,27 @@
+# vim:set noet ts=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
+# Copyright (c) 2016 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
+
+EXTRA_DIST = \
+ en.xml \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/data/annotations/en.xml b/data/annotations/en.xml
new file mode 100644
index 0000000..ff7aa89
--- /dev/null
+++ b/data/annotations/en.xml
@@ -0,0 +1,1042 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!DOCTYPE ldml SYSTEM '../../common/dtd/ldml.dtd'>
+<!-- © 1991-2015 Unicode, Inc.
+ Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+ For terms of use, see http://www.unicode.org/copyright.html
+ CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+
+ DO NOT CHANGE MANUALLY! Generated by: GenerateOtherAnnotations
+ -->
+<ldml>
+ <identity>
+ <version number="$Revision: 12320 $"/>
+ <language type='en'/>
+ </identity>
+ <annotations>
+ <annotation cp='[😀]' tts='grinning face'>face; grin</annotation>
+ <annotation cp='[😁]' tts='grinning face with smiling eyes'>eye; face; grin; smile</annotation>
+ <annotation cp='[😂]' tts='face with tears of joy'>face; joy; laugh; tear</annotation>
+ <annotation cp='[😃]' tts='smiling face with open mouth'>face; mouth; open; smile</annotation>
+ <annotation cp='[😄]' tts='smiling face with open mouth and smiling eyes'>eye; face; mouth; open; smile</annotation>
+ <annotation cp='[😅]' tts='smiling face with open mouth and cold sweat'>cold; face; open; smile; sweat</annotation>
+ <annotation cp='[😆]' tts='smiling face with open mouth and tightly-closed eyes'>face; laugh; mouth; open; satisfied; smile</annotation>
+ <annotation cp='[😉]' tts='winking face'>face; wink</annotation>
+ <annotation cp='[😊]' tts='smiling face with smiling eyes'>blush; eye; face; smile</annotation>
+ <annotation cp='[😋]' tts='face savouring delicious food'>delicious; face; savouring; smile; um; yum</annotation>
+ <annotation cp='[😎]' tts='smiling face with sunglasses'>bright; cool; eye; eyewear; face; glasses; smile; sun; sunglasses; weather</annotation>
+ <annotation cp='[😍]' tts='smiling face with heart-shaped eyes'>eye; face; heart; love; smile</annotation>
+ <annotation cp='[😘]' tts='face throwing a kiss'>face; heart; kiss</annotation>
+ <annotation cp='[😗]' tts='kissing face'>face; kiss</annotation>
+ <annotation cp='[😙]' tts='kissing face with smiling eyes'>eye; face; kiss; smile</annotation>
+ <annotation cp='[😚]' tts='kissing face with closed eyes'>closed; eye; face; kiss</annotation>
+ <annotation cp='[☺]' tts='smiling face'>face; outlined; relaxed; smile</annotation>
+ <annotation cp='[🙂]' tts='slightly smiling face'>face; smile</annotation>
+ <annotation cp='[🤗]' tts='hugging face'>face; hug; hugging</annotation>
+ <annotation cp='[😇]' tts='smiling face with halo'>angel; face; fairy tale; fantasy; halo; innocent; smile</annotation>
+ <annotation cp='[🤔]' tts='thinking face'>face; thinking</annotation>
+ <annotation cp='[😐]' tts='neutral face'>deadpan; face; neutral</annotation>
+ <annotation cp='[😑]' tts='expressionless face'>expressionless; face; inexpressive; unexpressive</annotation>
+ <annotation cp='[😶]' tts='face without mouth'>face; mouth; quiet; silent</annotation>
+ <annotation cp='[🙄]' tts='face with rolling eyes'>eyes; face; rolling</annotation>
+ <annotation cp='[😏]' tts='smirking face'>face; smirk</annotation>
+ <annotation cp='[😣]' tts='persevering face'>face; persevere</annotation>
+ <annotation cp='[😥]' tts='disappointed but relieved face'>disappointed; face; relieved; whew</annotation>
+ <annotation cp='[😮]' tts='face with open mouth'>face; mouth; open; sympathy</annotation>
+ <annotation cp='[🤐]' tts='zipper-mouth face'>face; mouth; zipper</annotation>
+ <annotation cp='[😯]' tts='hushed face'>face; hushed; stunned; surprised</annotation>
+ <annotation cp='[😪]' tts='sleepy face'>face; sleep</annotation>
+ <annotation cp='[😫]' tts='tired face'>face; tired</annotation>
+ <annotation cp='[😴]' tts='sleeping face'>face; sleep; zzz</annotation>
+ <annotation cp='[😌]' tts='relieved face'>face; relieved</annotation>
+ <annotation cp='[🤓]' tts='nerd face'>face; geek; nerd</annotation>
+ <annotation cp='[😛]' tts='face with stuck-out tongue'>face; tongue</annotation>
+ <annotation cp='[😜]' tts='face with stuck-out tongue and winking eye'>eye; face; joke; tongue; wink</annotation>
+ <annotation cp='[😝]' tts='face with stuck-out tongue and tightly-closed eyes'>eye; face; horrible; taste; tongue</annotation>
+ <annotation cp='[☹]' tts='frowning face'>face; frown</annotation>
+ <annotation cp='[🙁]' tts='slightly frowning face'>face; frown</annotation>
+ <annotation cp='[😒]' tts='unamused face'>face; unamused; unhappy</annotation>
+ <annotation cp='[😓]' tts='face with cold sweat'>cold; face; sweat</annotation>
+ <annotation cp='[😔]' tts='pensive face'>dejected; face; pensive</annotation>
+ <annotation cp='[😕]' tts='confused face'>confused; face</annotation>
+ <annotation cp='[😖]' tts='confounded face'>confounded; face</annotation>
+ <annotation cp='[🙃]' tts='upside-down face'>face; upside-down</annotation>
+ <annotation cp='[😷]' tts='face with medical mask'>cold; doctor; face; mask; medicine; sick</annotation>
+ <annotation cp='[🤒]' tts='face with thermometer'>face; ill; sick; thermometer</annotation>
+ <annotation cp='[🤕]' tts='face with head-bandage'>bandage; face; hurt; injury</annotation>
+ <annotation cp='[🤑]' tts='money-mouth face'>face; money; mouth</annotation>
+ <annotation cp='[😲]' tts='astonished face'>astonished; face; shocked; totally</annotation>
+ <annotation cp='[😞]' tts='disappointed face'>disappointed; face</annotation>
+ <annotation cp='[😟]' tts='worried face'>face; worried</annotation>
+ <annotation cp='[😤]' tts='face with steam from nose'>face; triumph; won</annotation>
+ <annotation cp='[😢]' tts='crying face'>cry; face; sad; tear</annotation>
+ <annotation cp='[😭]' tts='loudly crying face'>cry; face; sad; sob; tear</annotation>
+ <annotation cp='[😦]' tts='frowning face with open mouth'>face; frown; mouth; open</annotation>
+ <annotation cp='[😧]' tts='anguished face'>anguished; face</annotation>
+ <annotation cp='[😨]' tts='fearful face'>face; fear; fearful; scared</annotation>
+ <annotation cp='[😩]' tts='weary face'>face; tired; weary</annotation>
+ <annotation cp='[😬]' tts='grimacing face'>face; grimace</annotation>
+ <annotation cp='[😰]' tts='face with open mouth and cold sweat'>blue; cold; face; mouth; open; rushed; sweat</annotation>
+ <annotation cp='[😱]' tts='face screaming in fear'>face; fear; fearful; munch; scared; scream</annotation>
+ <annotation cp='[😳]' tts='flushed face'>dazed; face; flushed</annotation>
+ <annotation cp='[😵]' tts='dizzy face'>dizzy; face</annotation>
+ <annotation cp='[😡]' tts='pouting face'>angry; face; mad; pouting; rage; red</annotation>
+ <annotation cp='[😠]' tts='angry face'>angry; face; mad</annotation>
+ <annotation cp='[😈]' tts='smiling face with horns'>face; fairy tale; fantasy; horns; smile</annotation>
+ <annotation cp='[👿]' tts='imp'>demon; devil; face; fairy tale; fantasy</annotation>
+ <annotation cp='[👹]' tts='ogre'>creature; face; fairy tale; fantasy; japanese; monster</annotation>
+ <annotation cp='[👺]' tts='goblin'>creature; face; fairy tale; fantasy; japanese; monster</annotation>
+ <annotation cp='[💀]' tts='skull'>body; death; face; fairy tale; monster</annotation>
+ <annotation cp='[☠]' tts='skull and crossbones'>body; crossbones; death; face; monster; skull</annotation>
+ <annotation cp='[👻]' tts='ghost'>creature; face; fairy tale; fantasy; monster</annotation>
+ <annotation cp='[👽]' tts='alien'>creature; extraterrestrial; face; fairy tale; fantasy; monster; space; ufo</annotation>
+ <annotation cp='[👾]' tts='alien monster'>alien; creature; extraterrestrial; face; fairy tale; fantasy; monster; space; ufo</annotation>
+ <annotation cp='[🤖]' tts='robot face'>face; monster; robot</annotation>
+ <annotation cp='[💩]' tts='pile of poo'>comic; dung; face; monster; poo; poop</annotation>
+ <annotation cp='[😺]' tts='smiling cat face with open mouth'>cat; face; mouth; open; smile</annotation>
+ <annotation cp='[😸]' tts='grinning cat face with smiling eyes'>cat; eye; face; grin; smile</annotation>
+ <annotation cp='[😹]' tts='cat face with tears of joy'>cat; face; joy; tear</annotation>
+ <annotation cp='[😻]' tts='smiling cat face with heart-shaped eyes'>cat; eye; face; heart; love; smile</annotation>
+ <annotation cp='[😼]' tts='cat face with wry smile'>cat; face; ironic; smile; wry</annotation>
+ <annotation cp='[😽]' tts='kissing cat face with closed eyes'>cat; eye; face; kiss</annotation>
+ <annotation cp='[🙀]' tts='weary cat face'>cat; face; oh; surprised; weary</annotation>
+ <annotation cp='[😿]' tts='crying cat face'>cat; cry; face; sad; tear</annotation>
+ <annotation cp='[😾]' tts='pouting cat face'>cat; face; pouting</annotation>
+ <annotation cp='[🙈]' tts='see-no-evil'>evil; face; forbidden; gesture; monkey; no; not; prohibited; see</annotation>
+ <annotation cp='[🙉]' tts='hear-no-evil'>evil; face; forbidden; gesture; hear; monkey; no; not; prohibited</annotation>
+ <annotation cp='[🙊]' tts='speak-no-evil'>evil; face; forbidden; gesture; monkey; no; not; prohibited; speak</annotation>
+ <annotation cp='[👦]' tts='boy'>boy</annotation>
+ <annotation cp='[👧]' tts='girl'>maiden; virgin; virgo; zodiac</annotation>
+ <annotation cp='[👨]' tts='man'>man</annotation>
+ <annotation cp='[👩]' tts='woman'>woman</annotation>
+ <annotation cp='[👴]' tts='old man'>man; old</annotation>
+ <annotation cp='[👵]' tts='old woman'>old; woman</annotation>
+ <annotation cp='[👶]' tts='baby'>baby</annotation>
+ <annotation cp='[👱]' tts='person with blond hair'>blond</annotation>
+ <annotation cp='[👮]' tts='police officer'>cop; officer; police</annotation>
+ <annotation cp='[👲]' tts='man with chinese cap'>gua pi mao; hat; man</annotation>
+ <annotation cp='[👳]' tts='man with turban'>man; turban</annotation>
+ <annotation cp='[👷]' tts='construction worker'>construction; hat; worker</annotation>
+ <annotation cp='[⛑]' tts='helmet with white cross'>aid; cross; face; hat; helmet</annotation>
+ <annotation cp='[👸]' tts='princess'>fairy tale; fantasy</annotation>
+ <annotation cp='[💂]' tts='guardsman'>guard</annotation>
+ <annotation cp='[🕵]' tts='detective'>sleuth; spy</annotation>
+ <annotation cp='[🎅]' tts='santa claus'>celebration; christmas; fairy tale; fantasy; father; santa</annotation>
+ <annotation cp='[👼]' tts='baby angel'>angel; baby; face; fairy tale; fantasy</annotation>
+ <annotation cp='[💆]' tts='face massage'>massage; salon</annotation>
+ <annotation cp='[💇]' tts='haircut'>barber; beauty; parlor</annotation>
+ <annotation cp='[👰]' tts='bride with veil'>bride; veil; wedding</annotation>
+ <annotation cp='[🙍]' tts='person frowning'>frown; gesture</annotation>
+ <annotation cp='[🙎]' tts='person pouting'>gesture; pouting</annotation>
+ <annotation cp='[🙅]' tts='gesturing no'>forbidden; gesture; hand; no; not; prohibited</annotation>
+ <annotation cp='[🙆]' tts='gesturing ok'>gesture; hand; ok</annotation>
+ <annotation cp='[💁]' tts='information desk person'>hand; help; information; sassy</annotation>
+ <annotation cp='[🙋]' tts='happy person raising hand'>gesture; hand; happy; raised</annotation>
+ <annotation cp='[🙇]' tts='person bowing'>apology; bow; gesture; sorry</annotation>
+ <annotation cp='[🙌]' tts='person raising hands'>body; celebration; gesture; hand; hooray; raised</annotation>
+ <annotation cp='[🙏]' tts='folded hands'>ask; body; bow; folded; gesture; hand; please; pray; thanks</annotation>
+ <annotation cp='[🗣]' tts='speaking head'>face; head; silhouette; speak; speaking</annotation>
+ <annotation cp='[👤]' tts='bust in silhouette'>bust; silhouette</annotation>
+ <annotation cp='[👥]' tts='busts in silhouette'>bust; silhouette</annotation>
+ <annotation cp='[🚶]' tts='pedestrian'>hike; walk; walking</annotation>
+ <annotation cp='[🏃]' tts='runner'>marathon; running</annotation>
+ <annotation cp='[👯]' tts='women partying'>bunny; dancer; ear; girl; woman</annotation>
+ <annotation cp='[💃]' tts='dancer'>dancer</annotation>
+ <annotation cp='[🕴]' tts='man in business suit levitating'>business; man; suit</annotation>
+ <annotation cp='[💏]' tts='kiss'>couple; romance</annotation>
+ <annotation cp='[💑]' tts='couple with heart'>couple; heart; love; romance</annotation>
+ <annotation cp='[👪]' tts='family'>child; father; mother</annotation>
+ <annotation cp='[👫]' tts='man and woman holding hands'>couple; hand; hold; man; woman</annotation>
+ <annotation cp='[👬]' tts='two men holding hands'>couple; gemini; hand; hold; man; twins; zodiac</annotation>
+ <annotation cp='[👭]' tts='two women holding hands'>couple; hand; hold; woman</annotation>
+ <annotation cp='[🏻]' tts='skin type-1-2'>emoji modifier; fitzpatrick; skin; tone</annotation>
+ <annotation cp='[🏼]' tts='skin type-3'>emoji modifier; fitzpatrick; skin; tone</annotation>
+ <annotation cp='[🏽]' tts='skin type-4'>emoji modifier; fitzpatrick; skin; tone</annotation>
+ <annotation cp='[🏾]' tts='skin type-5'>emoji modifier; fitzpatrick; skin; tone</annotation>
+ <annotation cp='[🏿]' tts='skin type-6'>emoji modifier; fitzpatrick; skin; tone</annotation>
+ <annotation cp='[💪]' tts='flexed biceps'>biceps; body; comic; flex; muscle</annotation>
+ <annotation cp='[👈]' tts='backhand index pointing left'>backhand; body; finger; hand; index; point</annotation>
+ <annotation cp='[👉]' tts='backhand index pointing right'>backhand; body; finger; hand; index; point</annotation>
+ <annotation cp='[☝]' tts='index pointing up'>body; finger; hand; index; point; up</annotation>
+ <annotation cp='[👆]' tts='backhand index pointing up'>backhand; body; finger; hand; index; point; up</annotation>
+ <annotation cp='[🖕]' tts='middle finger'>body; finger; hand</annotation>
+ <annotation cp='[👇]' tts='backhand index pointing down'>backhand; body; down; finger; hand; index; point</annotation>
+ <annotation cp='[✌]' tts='victory hand'>body; hand; v; victory</annotation>
+ <annotation cp='[🖖]' tts='vulcan salute'>body; finger; hand; spock; vulcan</annotation>
+ <annotation cp='[🤘]' tts='sign of the horns'>body; finger; hand; horns; rock-on</annotation>
+ <annotation cp='[🖐]' tts='raised hand with fingers splayed'>body; finger; hand; splayed</annotation>
+ <annotation cp='[✋]' tts='raised hand'>body; hand</annotation>
+ <annotation cp='[👌]' tts='ok hand'>body; hand; ok</annotation>
+ <annotation cp='[👍]' tts='thumbs up'>+1; body; hand; thumb; up</annotation>
+ <annotation cp='[👎]' tts='thumbs down'>-1; body; down; hand; thumb</annotation>
+ <annotation cp='[✊]' tts='raised fist'>body; clenched; fist; hand; punch</annotation>
+ <annotation cp='[👊]' tts='oncoming fist'>body; clenched; fist; hand; punch</annotation>
+ <annotation cp='[👋]' tts='waving hand'>body; hand; wave; waving</annotation>
+ <annotation cp='[👏]' tts='clapping hands'>body; clap; hand</annotation>
+ <annotation cp='[👐]' tts='open hands'>body; hand; open</annotation>
+ <annotation cp='[✍]' tts='writing hand'>body; hand; write</annotation>
+ <annotation cp='[💅]' tts='nail polish'>body; care; cosmetics; manicure; nail; polish</annotation>
+ <annotation cp='[👂]' tts='ear'>body</annotation>
+ <annotation cp='[👃]' tts='nose'>body</annotation>
+ <annotation cp='[👣]' tts='footprints'>body; clothing; footprint; print</annotation>
+ <annotation cp='[👀]' tts='eyes'>body; eye; face</annotation>
+ <annotation cp='[👁]' tts='eye'>body</annotation>
+ <annotation cp='[👅]' tts='tongue'>body</annotation>
+ <annotation cp='[👄]' tts='mouth'>body; lips</annotation>
+ <annotation cp='[💋]' tts='kiss mark'>heart; kiss; lips; mark; romance</annotation>
+ <annotation cp='[💘]' tts='heart with arrow'>arrow; cupid; heart; romance</annotation>
+ <annotation cp='[❤]' tts='red heart'>heart</annotation>
+ <annotation cp='[💓]' tts='beating heart'>beating; heart; heartbeat; pulsating</annotation>
+ <annotation cp='[💔]' tts='broken heart'>break; broken; heart</annotation>
+ <annotation cp='[💕]' tts='two hearts'>heart; love</annotation>
+ <annotation cp='[💖]' tts='sparkling heart'>excited; heart; sparkle</annotation>
+ <annotation cp='[💗]' tts='growing heart'>excited; growing; heart; heartpulse; nervous</annotation>
+ <annotation cp='[💙]' tts='blue heart'>blue; heart</annotation>
+ <annotation cp='[💚]' tts='green heart'>green; heart</annotation>
+ <annotation cp='[💛]' tts='yellow heart'>heart; yellow</annotation>
+ <annotation cp='[💜]' tts='purple heart'>heart; purple</annotation>
+ <annotation cp='[💝]' tts='heart with ribbon'>heart; ribbon; valentine</annotation>
+ <annotation cp='[💞]' tts='revolving hearts'>heart; revolving</annotation>
+ <annotation cp='[💟]' tts='heart decoration'>heart</annotation>
+ <annotation cp='[❣]' tts='heavy heart exclamation mark ornament'>exclamation; heart; mark; punctuation</annotation>
+ <annotation cp='[💌]' tts='love letter'>heart; letter; love; mail; romance</annotation>
+ <annotation cp='[💤]' tts='zzz'>comic; sleep</annotation>
+ <annotation cp='[💢]' tts='anger symbol'>angry; comic; mad</annotation>
+ <annotation cp='[💣]' tts='bomb'>comic</annotation>
+ <annotation cp='[💥]' tts='collision'>boom; comic</annotation>
+ <annotation cp='[💦]' tts='sweat droplets'>comic; splashing; sweat</annotation>
+ <annotation cp='[💨]' tts='dashing'>comic; dash; running</annotation>
+ <annotation cp='[💫]' tts='dizzy'>comic; star</annotation>
+ <annotation cp='[💬]' tts='speech balloon'>balloon; bubble; comic; dialog; speech</annotation>
+ <annotation cp='[🗨]' tts='left speech bubble'>dialog; speech</annotation>
+ <annotation cp='[🗯]' tts='right anger bubble'>angry; balloon; bubble; mad</annotation>
+ <annotation cp='[💭]' tts='thought balloon'>balloon; bubble; comic; thought</annotation>
+ <annotation cp='[🕳]' tts='hole'>hole</annotation>
+ <annotation cp='[👓]' tts='glasses'>clothing; eye; eyeglasses; eyewear</annotation>
+ <annotation cp='[🕶]' tts='sunglasses'>dark; eye; eyewear; glasses</annotation>
+ <annotation cp='[👔]' tts='necktie'>clothing</annotation>
+ <annotation cp='[👕]' tts='t-shirt'>clothing; shirt; tshirt</annotation>
+ <annotation cp='[👖]' tts='jeans'>clothing; pants; trousers</annotation>
+ <annotation cp='[👗]' tts='dress'>clothing</annotation>
+ <annotation cp='[👘]' tts='kimono'>clothing</annotation>
+ <annotation cp='[👙]' tts='bikini'>clothing; swim</annotation>
+ <annotation cp='[👚]' tts='womans clothes'>clothing; woman</annotation>
+ <annotation cp='[👛]' tts='purse'>clothing; coin</annotation>
+ <annotation cp='[👜]' tts='handbag'>bag; clothing</annotation>
+ <annotation cp='[👝]' tts='pouch'>bag; clothing</annotation>
+ <annotation cp='[🛍]' tts='shopping bags'>bag; hotel; shopping</annotation>
+ <annotation cp='[🎒]' tts='school backpack'>bag; satchel; school</annotation>
+ <annotation cp='[👞]' tts='mans shoe'>clothing; man; shoe</annotation>
+ <annotation cp='[👟]' tts='running shoe'>athletic; clothing; shoe; sneaker</annotation>
+ <annotation cp='[👠]' tts='high-heeled shoe'>clothing; heel; shoe; woman</annotation>
+ <annotation cp='[👡]' tts='womans sandal'>clothing; sandal; shoe; woman</annotation>
+ <annotation cp='[👢]' tts='womans boot'>boot; clothing; shoe; woman</annotation>
+ <annotation cp='[👑]' tts='crown'>clothing; king; queen</annotation>
+ <annotation cp='[👒]' tts='womans hat'>clothing; hat; woman</annotation>
+ <annotation cp='[🎩]' tts='top hat'>clothing; hat; top; tophat</annotation>
+ <annotation cp='[🎓]' tts='graduation cap'>cap; celebration; clothing; graduation; hat</annotation>
+ <annotation cp='[📿]' tts='prayer beads'>beads; clothing; necklace; prayer; religion</annotation>
+ <annotation cp='[💄]' tts='lipstick'>cosmetics; makeup</annotation>
+ <annotation cp='[💍]' tts='ring'>diamond; romance</annotation>
+ <annotation cp='[💎]' tts='gem stone'>diamond; gem; jewel; romance</annotation>
+ <annotation cp='[🐵]' tts='monkey face'>face; monkey</annotation>
+ <annotation cp='[🐒]' tts='monkey'>monkey</annotation>
+ <annotation cp='[🐶]' tts='dog face'>dog; face; pet</annotation>
+ <annotation cp='[🐕]' tts='dog'>pet</annotation>
+ <annotation cp='[🐩]' tts='poodle'>dog</annotation>
+ <annotation cp='[🐺]' tts='wolf face'>face; wolf</annotation>
+ <annotation cp='[🐱]' tts='cat face'>cat; face; pet</annotation>
+ <annotation cp='[🐈]' tts='cat'>pet</annotation>
+ <annotation cp='[🦁]' tts='lion face'>face; leo; lion; zodiac</annotation>
+ <annotation cp='[🐯]' tts='tiger face'>face; tiger</annotation>
+ <annotation cp='[🐅]' tts='tiger'>tiger</annotation>
+ <annotation cp='[🐆]' tts='leopard'>leopard</annotation>
+ <annotation cp='[🐴]' tts='horse face'>face; horse</annotation>
+ <annotation cp='[🐎]' tts='horse'>racehorse; racing</annotation>
+ <annotation cp='[🦄]' tts='unicorn face'>face; unicorn</annotation>
+ <annotation cp='[🐮]' tts='cow face'>cow; face</annotation>
+ <annotation cp='[🐂]' tts='ox'>bull; taurus; zodiac</annotation>
+ <annotation cp='[🐃]' tts='water buffalo'>buffalo; water</annotation>
+ <annotation cp='[🐄]' tts='cow'>cow</annotation>
+ <annotation cp='[🐷]' tts='pig face'>face; pig</annotation>
+ <annotation cp='[🐖]' tts='pig'>sow</annotation>
+ <annotation cp='[🐗]' tts='boar'>pig</annotation>
+ <annotation cp='[🐽]' tts='pig nose'>face; nose; pig</annotation>
+ <annotation cp='[🐏]' tts='ram'>aries; sheep; zodiac</annotation>
+ <annotation cp='[🐑]' tts='sheep'>ewe</annotation>
+ <annotation cp='[🐐]' tts='goat'>capricorn; zodiac</annotation>
+ <annotation cp='[🐪]' tts='camel'>dromedary; hump</annotation>
+ <annotation cp='[🐫]' tts='two-hump camel'>bactrian; camel; hump</annotation>
+ <annotation cp='[🐘]' tts='elephant'>elephant</annotation>
+ <annotation cp='[🐭]' tts='mouse face'>face; mouse</annotation>
+ <annotation cp='[🐁]' tts='mouse'>mouse</annotation>
+ <annotation cp='[🐀]' tts='rat'>rat</annotation>
+ <annotation cp='[🐹]' tts='hamster face'>face; hamster; pet</annotation>
+ <annotation cp='[🐰]' tts='rabbit face'>bunny; face; pet; rabbit</annotation>
+ <annotation cp='[🐇]' tts='rabbit'>bunny; pet</annotation>
+ <annotation cp='[🐿]' tts='chipmunk'>chipmunk</annotation>
+ <annotation cp='[🐻]' tts='bear face'>bear; face</annotation>
+ <annotation cp='[🐨]' tts='koala'>bear</annotation>
+ <annotation cp='[🐼]' tts='panda face'>face; panda</annotation>
+ <annotation cp='[🐾]' tts='paw prints'>feet; paw; print</annotation>
+ <annotation cp='[🦃]' tts='turkey'>turkey</annotation>
+ <annotation cp='[🐔]' tts='chicken'>chicken</annotation>
+ <annotation cp='[🐓]' tts='rooster'>rooster</annotation>
+ <annotation cp='[🐣]' tts='hatching chick'>baby; chick; hatching</annotation>
+ <annotation cp='[🐤]' tts='baby chick'>baby; chick</annotation>
+ <annotation cp='[🐥]' tts='front-facing baby chick'>baby; chick</annotation>
+ <annotation cp='[🐦]' tts='bird'>bird</annotation>
+ <annotation cp='[🐧]' tts='penguin'>penguin</annotation>
+ <annotation cp='[🕊]' tts='dove'>bird; fly; peace</annotation>
+ <annotation cp='[🐸]' tts='frog face'>face; frog</annotation>
+ <annotation cp='[🐊]' tts='crocodile'>crocodile</annotation>
+ <annotation cp='[🐢]' tts='turtle'>turtle</annotation>
+ <annotation cp='[🐍]' tts='snake'>bearer; ophiuchus; serpent; zodiac</annotation>
+ <annotation cp='[🐲]' tts='dragon face'>dragon; face; fairy tale</annotation>
+ <annotation cp='[🐉]' tts='dragon'>fairy tale</annotation>
+ <annotation cp='[🐳]' tts='spouting whale'>face; spouting; whale</annotation>
+ <annotation cp='[🐋]' tts='whale'>whale</annotation>
+ <annotation cp='[🐬]' tts='dolphin'>flipper</annotation>
+ <annotation cp='[🐟]' tts='fish'>pisces; zodiac</annotation>
+ <annotation cp='[🐠]' tts='tropical fish'>fish; tropical</annotation>
+ <annotation cp='[🐡]' tts='blowfish'>fish</annotation>
+ <annotation cp='[🐙]' tts='octopus'>octopus</annotation>
+ <annotation cp='[🐚]' tts='spiral shell'>shell; spiral</annotation>
+ <annotation cp='[🦀]' tts='crab'>cancer; zodiac</annotation>
+ <annotation cp='[🐌]' tts='snail'>snail</annotation>
+ <annotation cp='[🐛]' tts='bug'>insect</annotation>
+ <annotation cp='[🐜]' tts='ant'>insect</annotation>
+ <annotation cp='[🐝]' tts='honeybee'>bee; insect</annotation>
+ <annotation cp='[🐞]' tts='lady beetle'>beetle; insect; ladybird; ladybug</annotation>
+ <annotation cp='[🕷]' tts='spider'>insect</annotation>
+ <annotation cp='[🕸]' tts='spider web'>spider; web</annotation>
+ <annotation cp='[🦂]' tts='scorpion'>scorpio; scorpius; zodiac</annotation>
+ <annotation cp='[💐]' tts='bouquet'>flower; plant; romance</annotation>
+ <annotation cp='[🌸]' tts='cherry blossom'>blossom; cherry; flower; plant</annotation>
+ <annotation cp='[💮]' tts='white flower'>flower</annotation>
+ <annotation cp='[🏵]' tts='rosette'>plant</annotation>
+ <annotation cp='[🌹]' tts='rose'>flower; plant</annotation>
+ <annotation cp='[🌺]' tts='hibiscus'>flower; plant</annotation>
+ <annotation cp='[🌻]' tts='sunflower'>flower; plant; sun</annotation>
+ <annotation cp='[🌼]' tts='blossom'>flower; plant</annotation>
+ <annotation cp='[🌷]' tts='tulip'>flower; plant</annotation>
+ <annotation cp='[🌱]' tts='seedling'>plant; young</annotation>
+ <annotation cp='[🌲]' tts='evergreen'>plant; tree</annotation>
+ <annotation cp='[🌳]' tts='deciduous tree'>deciduous; plant; shedding; tree</annotation>
+ <annotation cp='[🌴]' tts='palm tree'>palm; plant; tree</annotation>
+ <annotation cp='[🌵]' tts='cactus'>plant</annotation>
+ <annotation cp='[🌾]' tts='sheaf of rice'>ear; plant; rice</annotation>
+ <annotation cp='[🌿]' tts='herb'>leaf; plant</annotation>
+ <annotation cp='[☘]' tts='shamrock'>plant</annotation>
+ <annotation cp='[🍀]' tts='four leaf clover'>4; clover; four; leaf; plant</annotation>
+ <annotation cp='[🍁]' tts='maple leaf'>falling; leaf; maple; plant</annotation>
+ <annotation cp='[🍂]' tts='fallen leaf'>falling; leaf; plant</annotation>
+ <annotation cp='[🍃]' tts='leaf fluttering in wind'>blow; flutter; leaf; plant; wind</annotation>
+ <annotation cp='[🍇]' tts='grapes'>fruit; grape; plant</annotation>
+ <annotation cp='[🍈]' tts='melon'>fruit; plant</annotation>
+ <annotation cp='[🍉]' tts='watermelon'>fruit; plant</annotation>
+ <annotation cp='[🍊]' tts='tangerine'>fruit; orange; plant</annotation>
+ <annotation cp='[🍋]' tts='lemon'>citrus; fruit; plant</annotation>
+ <annotation cp='[🍌]' tts='banana'>fruit; plant</annotation>
+ <annotation cp='[🍍]' tts='pineapple'>fruit; plant</annotation>
+ <annotation cp='[🍎]' tts='red apple'>apple; fruit; plant; red</annotation>
+ <annotation cp='[🍏]' tts='green apple'>apple; fruit; green; plant</annotation>
+ <annotation cp='[🍐]' tts='pear'>fruit; plant</annotation>
+ <annotation cp='[🍑]' tts='peach'>fruit; plant</annotation>
+ <annotation cp='[🍒]' tts='cherries'>cherry; fruit; plant</annotation>
+ <annotation cp='[🍓]' tts='strawberry'>berry; fruit; plant</annotation>
+ <annotation cp='[🍅]' tts='tomato'>plant; vegetable</annotation>
+ <annotation cp='[🍆]' tts='eggplant'>aubergine; plant; vegetable</annotation>
+ <annotation cp='[🌽]' tts='ear of corn'>corn; ear; maize; maze; plant</annotation>
+ <annotation cp='[🌶]' tts='hot pepper'>hot; pepper; plant</annotation>
+ <annotation cp='[🍄]' tts='mushroom'>plant</annotation>
+ <annotation cp='[🌰]' tts='chestnut'>plant</annotation>
+ <annotation cp='[🍞]' tts='bread'>loaf</annotation>
+ <annotation cp='[🧀]' tts='cheese wedge'>cheese</annotation>
+ <annotation cp='[🍖]' tts='meat on bone'>bone; meat</annotation>
+ <annotation cp='[🍗]' tts='poultry leg'>bone; chicken; leg; poultry</annotation>
+ <annotation cp='[🍔]' tts='hamburger'>burger</annotation>
+ <annotation cp='[🍟]' tts='french fries'>french; fries</annotation>
+ <annotation cp='[🍕]' tts='pizza'>cheese; slice</annotation>
+ <annotation cp='[🌭]' tts='hot dog'>frankfurter; hotdog; sausage</annotation>
+ <annotation cp='[🌮]' tts='taco'>mexican</annotation>
+ <annotation cp='[🌯]' tts='burrito'>mexican</annotation>
+ <annotation cp='[🍿]' tts='popcorn'>popcorn</annotation>
+ <annotation cp='[🍲]' tts='pot of food'>pot; stew</annotation>
+ <annotation cp='[🍱]' tts='bento box'>bento; box</annotation>
+ <annotation cp='[🍘]' tts='rice cracker'>cracker; rice</annotation>
+ <annotation cp='[🍙]' tts='rice ball'>ball; japanese; rice</annotation>
+ <annotation cp='[🍚]' tts='cooked rice'>cooked; rice</annotation>
+ <annotation cp='[🍛]' tts='curry rice'>curry; rice</annotation>
+ <annotation cp='[🍜]' tts='steaming bowl'>bowl; noodle; ramen; steaming</annotation>
+ <annotation cp='[🍝]' tts='spaghetti'>pasta</annotation>
+ <annotation cp='[🍠]' tts='roasted sweet potato'>potato; roasted; sweet</annotation>
+ <annotation cp='[🍢]' tts='oden'>kebab; seafood; skewer; stick</annotation>
+ <annotation cp='[🍣]' tts='sushi'>sushi</annotation>
+ <annotation cp='[🍤]' tts='fried shrimp'>fried; prawn; shrimp; tempura</annotation>
+ <annotation cp='[🍥]' tts='fish cake with swirl'>cake; fish; pastry; swirl</annotation>
+ <annotation cp='[🍡]' tts='dango'>dessert; japanese; skewer; stick; sweet</annotation>
+ <annotation cp='[🍦]' tts='soft ice cream'>cream; dessert; ice; icecream; soft; sweet</annotation>
+ <annotation cp='[🍧]' tts='shaved ice'>dessert; ice; shaved; sweet</annotation>
+ <annotation cp='[🍨]' tts='ice cream'>cream; dessert; ice; sweet</annotation>
+ <annotation cp='[🍩]' tts='doughnut'>dessert; donut; sweet</annotation>
+ <annotation cp='[🍪]' tts='cookie'>dessert; sweet</annotation>
+ <annotation cp='[🎂]' tts='birthday cake'>birthday; cake; celebration; dessert; pastry; sweet</annotation>
+ <annotation cp='[🍰]' tts='shortcake'>cake; dessert; pastry; slice; sweet</annotation>
+ <annotation cp='[🍫]' tts='chocolate bar'>bar; chocolate; dessert; sweet</annotation>
+ <annotation cp='[🍬]' tts='candy'>dessert; sweet</annotation>
+ <annotation cp='[🍭]' tts='lollipop'>candy; dessert; sweet</annotation>
+ <annotation cp='[🍮]' tts='custard'>dessert; pudding; sweet</annotation>
+ <annotation cp='[🍯]' tts='honey pot'>honey; honeypot; pot; sweet</annotation>
+ <annotation cp='[🍼]' tts='baby bottle'>baby; bottle; drink; milk</annotation>
+ <annotation cp='[☕]' tts='hot beverage'>beverage; coffee; drink; hot; steaming; tea</annotation>
+ <annotation cp='[🍵]' tts='teacup without handle'>beverage; cup; drink; tea; teacup</annotation>
+ <annotation cp='[🍶]' tts='sake'>bar; beverage; bottle; cup; drink</annotation>
+ <annotation cp='[🍾]' tts='bottle with popping cork'>bar; bottle; cork; drink; popping</annotation>
+ <annotation cp='[🍷]' tts='wine glass'>bar; beverage; drink; glass; wine</annotation>
+ <annotation cp='[🍸]' tts='cocktail glass'>bar; cocktail; drink; glass</annotation>
+ <annotation cp='[🍹]' tts='tropical drink'>bar; drink; tropical</annotation>
+ <annotation cp='[🍺]' tts='beer mug'>bar; beer; drink; mug</annotation>
+ <annotation cp='[🍻]' tts='clinking beer mugs'>bar; beer; clink; drink; mug</annotation>
+ <annotation cp='[🍽]' tts='fork and knife with plate'>cooking; fork; knife; plate</annotation>
+ <annotation cp='[🍴]' tts='fork and knife'>cooking; fork; knife</annotation>
+ <annotation cp='[🍳]' tts='cooking'>egg; frying; pan</annotation>
+ <annotation cp='[🏺]' tts='amphora'>aquarius; cooking; drink; jug; tool; weapon; zodiac</annotation>
+ <annotation cp='[🌍]' tts='globe showing europe-africa'>africa; earth; europe; globe; world</annotation>
+ <annotation cp='[🌎]' tts='globe showing americas'>americas; earth; globe; world</annotation>
+ <annotation cp='[🌏]' tts='globe showing asia-australia'>asia; australia; earth; globe; world</annotation>
+ <annotation cp='[🌐]' tts='globe with meridians'>earth; globe; meridians; world</annotation>
+ <annotation cp='[🗺]' tts='world map'>map; world</annotation>
+ <annotation cp='[🏔]' tts='snow-capped mountain'>cold; mountain; snow</annotation>
+ <annotation cp='[⛰]' tts='mountain'>mountain</annotation>
+ <annotation cp='[🌋]' tts='volcano'>eruption; mountain; weather</annotation>
+ <annotation cp='[🗻]' tts='mount fuji'>fuji; mountain</annotation>
+ <annotation cp='[🏕]' tts='camping'>camping</annotation>
+ <annotation cp='[🏖]' tts='beach with umbrella'>beach; umbrella</annotation>
+ <annotation cp='[🏜]' tts='desert'>desert</annotation>
+ <annotation cp='[🏝]' tts='desert island'>desert; island</annotation>
+ <annotation cp='[🏞]' tts='national park'>park</annotation>
+ <annotation cp='[🏟]' tts='stadium'>stadium</annotation>
+ <annotation cp='[🏛]' tts='classical building'>building; classical</annotation>
+ <annotation cp='[🏗]' tts='building construction'>building; construction</annotation>
+ <annotation cp='[🏘]' tts='house buildings'>building; house</annotation>
+ <annotation cp='[🏙]' tts='cityscape'>building; city</annotation>
+ <annotation cp='[🏚]' tts='derelict house building'>building; derelict; house</annotation>
+ <annotation cp='[🏠]' tts='house building'>building; home; house</annotation>
+ <annotation cp='[🏡]' tts='house with garden'>building; garden; home; house</annotation>
+ <annotation cp='[⛪]' tts='church'>building; christian; cross; religion</annotation>
+ <annotation cp='[🕋]' tts='kaaba'>islam; muslim; religion</annotation>
+ <annotation cp='[🕌]' tts='mosque'>islam; muslim; religion</annotation>
+ <annotation cp='[🕍]' tts='synagogue'>jew; jewish; religion; temple</annotation>
+ <annotation cp='[⛩]' tts='shinto shrine'>religion; shinto; shrine</annotation>
+ <annotation cp='[🏢]' tts='office building'>building</annotation>
+ <annotation cp='[🏣]' tts='japanese post office'>building; japanese; post</annotation>
+ <annotation cp='[🏤]' tts='post office'>building; european; post</annotation>
+ <annotation cp='[🏥]' tts='hospital'>building; doctor; medicine</annotation>
+ <annotation cp='[🏦]' tts='bank'>building</annotation>
+ <annotation cp='[🏨]' tts='hotel'>building</annotation>
+ <annotation cp='[🏩]' tts='love hotel'>building; hotel; love</annotation>
+ <annotation cp='[🏪]' tts='convenience store'>building; convenience; store</annotation>
+ <annotation cp='[🏫]' tts='school'>building</annotation>
+ <annotation cp='[🏬]' tts='department store'>building; department; store</annotation>
+ <annotation cp='[🏭]' tts='factory'>building</annotation>
+ <annotation cp='[🏯]' tts='japanese castle'>building; castle; japanese</annotation>
+ <annotation cp='[🏰]' tts='castle'>building; european</annotation>
+ <annotation cp='[💒]' tts='wedding'>chapel; romance</annotation>
+ <annotation cp='[🗼]' tts='tokyo tower'>tokyo; tower</annotation>
+ <annotation cp='[🗽]' tts='statue of liberty'>liberty; statue</annotation>
+ <annotation cp='[🗾]' tts='map of japan'>japan; map</annotation>
+ <annotation cp='[⛲]' tts='fountain'>fountain</annotation>
+ <annotation cp='[⛺]' tts='tent'>camping</annotation>
+ <annotation cp='[🌁]' tts='foggy'>fog; weather</annotation>
+ <annotation cp='[🌃]' tts='night with stars'>night; star; weather</annotation>
+ <annotation cp='[🌄]' tts='sunrise over mountains'>morning; mountain; sun; sunrise; weather</annotation>
+ <annotation cp='[🌅]' tts='sunrise'>morning; sun; weather</annotation>
+ <annotation cp='[🌆]' tts='cityscape at dusk'>building; city; dusk; evening; landscape; sun; sunset; weather</annotation>
+ <annotation cp='[🌇]' tts='sunset'>building; dusk; sun; weather</annotation>
+ <annotation cp='[🌉]' tts='bridge at night'>bridge; night; weather</annotation>
+ <annotation cp='[♨]' tts='hot springs'>hot; hotsprings; springs; steaming</annotation>
+ <annotation cp='[🌌]' tts='milky way'>space; weather</annotation>
+ <annotation cp='[🎠]' tts='carousel horse'>carousel; horse</annotation>
+ <annotation cp='[🎡]' tts='ferris wheel'>amusement park; ferris; wheel</annotation>
+ <annotation cp='[🎢]' tts='roller coaster'>amusement park; coaster; roller</annotation>
+ <annotation cp='[💈]' tts='barber pole'>barber; haircut; pole</annotation>
+ <annotation cp='[🎪]' tts='circus tent'>circus; tent</annotation>
+ <annotation cp='[🎭]' tts='performing arts'>art; mask; performing; theater; theatre</annotation>
+ <annotation cp='[🖼]' tts='frame with picture'>art; frame; museum; painting; picture</annotation>
+ <annotation cp='[🎨]' tts='artist palette'>art; museum; painting; palette</annotation>
+ <annotation cp='[🎰]' tts='slot machine'>game; slot</annotation>
+ <annotation cp='[🚂]' tts='locomotive'>engine; railway; steam; train; vehicle</annotation>
+ <annotation cp='[🚃]' tts='railway car'>car; electric; railway; train; tram; trolleybus; vehicle</annotation>
+ <annotation cp='[🚄]' tts='high-speed train'>railway; shinkansen; speed; train; vehicle</annotation>
+ <annotation cp='[🚅]' tts='high-speed train with bullet nose'>bullet; railway; shinkansen; speed; train; vehicle</annotation>
+ <annotation cp='[🚆]' tts='train'>railway; vehicle</annotation>
+ <annotation cp='[🚇]' tts='metro'>subway; vehicle</annotation>
+ <annotation cp='[🚈]' tts='light rail'>railway; vehicle</annotation>
+ <annotation cp='[🚉]' tts='station'>railway; train; vehicle</annotation>
+ <annotation cp='[🚊]' tts='tram'>trolleybus; vehicle</annotation>
+ <annotation cp='[🚝]' tts='monorail'>vehicle</annotation>
+ <annotation cp='[🚞]' tts='mountain railway'>car; mountain; railway; vehicle</annotation>
+ <annotation cp='[🚋]' tts='tram car'>car; tram; trolleybus; vehicle</annotation>
+ <annotation cp='[🚌]' tts='bus'>vehicle</annotation>
+ <annotation cp='[🚍]' tts='oncoming bus'>bus; oncoming; vehicle</annotation>
+ <annotation cp='[🚎]' tts='trolleybus'>bus; tram; trolley; vehicle</annotation>
+ <annotation cp='[🚏]' tts='bus stop'>bus; busstop; stop</annotation>
+ <annotation cp='[🚐]' tts='minibus'>bus; vehicle</annotation>
+ <annotation cp='[🚑]' tts='ambulance'>vehicle</annotation>
+ <annotation cp='[🚒]' tts='fire engine'>engine; fire; truck; vehicle</annotation>
+ <annotation cp='[🚓]' tts='police car'>car; patrol; police; vehicle</annotation>
+ <annotation cp='[🚔]' tts='oncoming police car'>car; oncoming; police; vehicle</annotation>
+ <annotation cp='[🚕]' tts='taxi'>vehicle</annotation>
+ <annotation cp='[🚖]' tts='oncoming taxi'>oncoming; taxi; vehicle</annotation>
+ <annotation cp='[🚗]' tts='automobile'>car; vehicle</annotation>
+ <annotation cp='[🚘]' tts='oncoming automobile'>automobile; car; oncoming; vehicle</annotation>
+ <annotation cp='[🚙]' tts='recreational vehicle'>recreational; rv; vehicle</annotation>
+ <annotation cp='[🚚]' tts='delivery truck'>delivery; truck; vehicle</annotation>
+ <annotation cp='[🚛]' tts='articulated lorry'>lorry; semi; truck; vehicle</annotation>
+ <annotation cp='[🚜]' tts='tractor'>vehicle</annotation>
+ <annotation cp='[🚲]' tts='bicycle'>bike; vehicle</annotation>
+ <annotation cp='[⛽]' tts='fuel pump'>fuel; fuelpump; gas; pump; station</annotation>
+ <annotation cp='[🛣]' tts='motorway'>highway; road</annotation>
+ <annotation cp='[🛤]' tts='railway track'>railway; train</annotation>
+ <annotation cp='[🚨]' tts='police cars light'>beacon; car; light; police; revolving; vehicle</annotation>
+ <annotation cp='[🚥]' tts='horizontal traffic light'>light; signal; traffic</annotation>
+ <annotation cp='[🚦]' tts='vertical traffic light'>light; signal; traffic</annotation>
+ <annotation cp='[🚧]' tts='construction'>barrier</annotation>
+ <annotation cp='[⚓]' tts='anchor'>ship; tool</annotation>
+ <annotation cp='[⛵]' tts='sailboat'>boat; resort; sea; vehicle; yacht</annotation>
+ <annotation cp='[🚣]' tts='rowboat'>boat; vehicle</annotation>
+ <annotation cp='[🚤]' tts='speedboat'>boat; vehicle</annotation>
+ <annotation cp='[🛳]' tts='passenger ship'>passenger; ship; vehicle</annotation>
+ <annotation cp='[⛴]' tts='ferry'>boat</annotation>
+ <annotation cp='[🛥]' tts='motor boat'>boat; motorboat; vehicle</annotation>
+ <annotation cp='[🚢]' tts='ship'>vehicle</annotation>
+ <annotation cp='[✈]' tts='airplane'>vehicle</annotation>
+ <annotation cp='[🛩]' tts='small airplane'>airplane; vehicle</annotation>
+ <annotation cp='[🛫]' tts='airplane departure'>airplane; check-in; departure; departures; vehicle</annotation>
+ <annotation cp='[🛬]' tts='airplane arrival'>airplane; arrivals; arriving; landing; vehicle</annotation>
+ <annotation cp='[💺]' tts='seat'>chair</annotation>
+ <annotation cp='[🚁]' tts='helicopter'>vehicle</annotation>
+ <annotation cp='[🚟]' tts='suspension railway'>railway; suspension; vehicle</annotation>
+ <annotation cp='[🚠]' tts='mountain cableway'>cable; gondola; mountain; vehicle</annotation>
+ <annotation cp='[🚡]' tts='aerial tramway'>aerial; cable; car; gondola; ropeway; tramway; vehicle</annotation>
+ <annotation cp='[🚀]' tts='rocket'>space; vehicle</annotation>
+ <annotation cp='[🛰]' tts='satellite'>space; vehicle</annotation>
+ <annotation cp='[🛎]' tts='bellhop bell'>bell; bellhop; hotel</annotation>
+ <annotation cp='[🚪]' tts='door'>door</annotation>
+ <annotation cp='[🛌]' tts='person in bed'>hotel; sleep</annotation>
+ <annotation cp='[🛏]' tts='bed'>hotel; sleep</annotation>
+ <annotation cp='[🛋]' tts='couch and lamp'>couch; hotel; lamp</annotation>
+ <annotation cp='[🚽]' tts='toilet'>toilet</annotation>
+ <annotation cp='[🚿]' tts='shower'>water</annotation>
+ <annotation cp='[🛀]' tts='person taking bath'>bathtub; bath</annotation>
+ <annotation cp='[🛁]' tts='bathtub'>bath</annotation>
+ <annotation cp='[⌛]' tts='hourglass'>sand; timer</annotation>
+ <annotation cp='[⏳]' tts='hourglass with flowing sand'>hourglass; sand; timer</annotation>
+ <annotation cp='[⌚]' tts='watch'>clock</annotation>
+ <annotation cp='[⏰]' tts='alarm clock'>alarm; clock</annotation>
+ <annotation cp='[⏱]' tts='stopwatch'>clock</annotation>
+ <annotation cp='[⏲]' tts='timer clock'>clock; timer</annotation>
+ <annotation cp='[🕰]' tts='mantelpiece clock'>clock</annotation>
+ <annotation cp='[🕛]' tts='twelve oclock'>00; 12; 12:00; clock; oclock; twelve</annotation>
+ <annotation cp='[🕧]' tts='twelve-thirty'>12; 12:30; 30; clock; thirty; twelve</annotation>
+ <annotation cp='[🕐]' tts='one oclock'>00; 1; 1:00; clock; oclock; one</annotation>
+ <annotation cp='[🕜]' tts='one-thirty'>1; 1:30; 30; clock; one; thirty</annotation>
+ <annotation cp='[🕑]' tts='two oclock'>00; 2; 2:00; clock; oclock; two</annotation>
+ <annotation cp='[🕝]' tts='two-thirty'>2; 2:30; 30; clock; thirty; two</annotation>
+ <annotation cp='[🕒]' tts='three oclock'>00; 3; 3:00; clock; oclock; three</annotation>
+ <annotation cp='[🕞]' tts='three-thirty'>3; 3:30; 30; clock; thirty; three</annotation>
+ <annotation cp='[🕓]' tts='four oclock'>00; 4; 4:00; clock; four; oclock</annotation>
+ <annotation cp='[🕟]' tts='four-thirty'>30; 4; 4:30; clock; four; thirty</annotation>
+ <annotation cp='[🕔]' tts='five oclock'>00; 5; 5:00; clock; five; oclock</annotation>
+ <annotation cp='[🕠]' tts='five-thirty'>30; 5; 5:30; clock; five; thirty</annotation>
+ <annotation cp='[🕕]' tts='six oclock'>00; 6; 6:00; clock; oclock; six</annotation>
+ <annotation cp='[🕡]' tts='six-thirty'>30; 6; 6:30; clock; six; thirty</annotation>
+ <annotation cp='[🕖]' tts='seven oclock'>00; 7; 7:00; clock; oclock; seven</annotation>
+ <annotation cp='[🕢]' tts='seven-thirty'>30; 7; 7:30; clock; seven; thirty</annotation>
+ <annotation cp='[🕗]' tts='eight oclock'>00; 8; 8:00; clock; eight; oclock</annotation>
+ <annotation cp='[🕣]' tts='eight-thirty'>30; 8; 8:30; clock; eight; thirty</annotation>
+ <annotation cp='[🕘]' tts='nine oclock'>00; 9; 9:00; clock; nine; oclock</annotation>
+ <annotation cp='[🕤]' tts='nine-thirty'>30; 9; 9:30; clock; nine; thirty</annotation>
+ <annotation cp='[🕙]' tts='ten oclock'>00; 10; 10:00; clock; oclock; ten</annotation>
+ <annotation cp='[🕥]' tts='ten-thirty'>10; 10:30; 30; clock; ten; thirty</annotation>
+ <annotation cp='[🕚]' tts='eleven oclock'>00; 11; 11:00; clock; eleven; oclock</annotation>
+ <annotation cp='[🕦]' tts='eleven-thirty'>11; 11:30; 30; clock; eleven; thirty</annotation>
+ <annotation cp='[🌑]' tts='new moon'>dark; moon; space; weather</annotation>
+ <annotation cp='[🌒]' tts='waxing crescent moon'>crescent; moon; space; waxing; weather</annotation>
+ <annotation cp='[🌓]' tts='first quarter moon'>moon; quarter; space; weather</annotation>
+ <annotation cp='[🌔]' tts='waxing gibbous moon'>gibbous; moon; space; waxing; weather</annotation>
+ <annotation cp='[🌕]' tts='full moon'>full; moon; space; weather</annotation>
+ <annotation cp='[🌖]' tts='waning gibbous moon'>gibbous; moon; space; waning; weather</annotation>
+ <annotation cp='[🌗]' tts='last quarter moon'>moon; quarter; space; weather</annotation>
+ <annotation cp='[🌘]' tts='waning crescent moon'>crescent; moon; space; waning; weather</annotation>
+ <annotation cp='[🌙]' tts='crescent moon'>crescent; moon; space; weather</annotation>
+ <annotation cp='[🌚]' tts='new moon face'>face; moon; space; weather</annotation>
+ <annotation cp='[🌛]' tts='first quarter moon with face'>face; moon; quarter; space; weather</annotation>
+ <annotation cp='[🌜]' tts='last quarter moon with face'>face; moon; quarter; space; weather</annotation>
+ <annotation cp='[🌡]' tts='thermometer'>weather</annotation>
+ <annotation cp='[☀]' tts='sun'>bright; rays; space; sunny; weather</annotation>
+ <annotation cp='[🌝]' tts='full moon with face'>bright; face; full; moon; space; weather</annotation>
+ <annotation cp='[🌞]' tts='sun with face'>bright; face; space; sun; weather</annotation>
+ <annotation cp='[⭐]' tts='white medium star'>star</annotation>
+ <annotation cp='[🌟]' tts='glowing star'>glittery; glow; shining; sparkle; star</annotation>
+ <annotation cp='[🌠]' tts='shooting star'>falling; shooting; space; star</annotation>
+ <annotation cp='[☁]' tts='cloud'>weather</annotation>
+ <annotation cp='[⛅]' tts='sun behind cloud'>cloud; sun; weather</annotation>
+ <annotation cp='[⛈]' tts='cloud with lightning and rain'>cloud; rain; thunder; weather</annotation>
+ <annotation cp='[🌤]' tts='sun behind small cloud'>cloud; sun; weather</annotation>
+ <annotation cp='[🌥]' tts='sun behind large cloud'>cloud; sun; weather</annotation>
+ <annotation cp='[🌦]' tts='sun behind cloud with rain'>cloud; rain; sun; weather</annotation>
+ <annotation cp='[🌧]' tts='cloud with rain'>cloud; rain; weather</annotation>
+ <annotation cp='[🌨]' tts='cloud with snow'>cloud; cold; snow; weather</annotation>
+ <annotation cp='[🌩]' tts='cloud with lightning'>cloud; lightning; weather</annotation>
+ <annotation cp='[🌪]' tts='tornado'>cloud; weather; whirlwind</annotation>
+ <annotation cp='[🌫]' tts='fog'>cloud; weather</annotation>
+ <annotation cp='[🌬]' tts='wind face'>blow; cloud; face; weather; wind</annotation>
+ <annotation cp='[🌀]' tts='cyclone'>dizzy; twister; typhoon; weather</annotation>
+ <annotation cp='[🌈]' tts='rainbow'>rain; weather</annotation>
+ <annotation cp='[🌂]' tts='closed umbrella'>clothing; rain; umbrella; weather</annotation>
+ <annotation cp='[☂]' tts='umbrella'>clothing; rain; weather</annotation>
+ <annotation cp='[☔]' tts='umbrella with rain drops'>clothing; drop; rain; umbrella; weather</annotation>
+ <annotation cp='[⛱]' tts='umbrella on ground'>rain; sun; umbrella; weather</annotation>
+ <annotation cp='[⚡]' tts='high voltage'>danger; electric; electricity; lightning; voltage; zap</annotation>
+ <annotation cp='[❄]' tts='snowflake'>cold; snow; weather</annotation>
+ <annotation cp='[☃]' tts='snowman'>cold; snow; weather</annotation>
+ <annotation cp='[⛄]' tts='snowman without snow'>cold; snow; snowman; weather</annotation>
+ <annotation cp='[☄]' tts='comet'>space</annotation>
+ <annotation cp='[🔥]' tts='fire'>flame; tool</annotation>
+ <annotation cp='[💧]' tts='droplet'>cold; comic; drop; sweat; weather</annotation>
+ <annotation cp='[🌊]' tts='water wave'>ocean; water; wave; weather</annotation>
+ <annotation cp='[🎃]' tts='jack-o-lantern'>celebration; halloween; jack; lantern</annotation>
+ <annotation cp='[🎄]' tts='christmas tree'>celebration; christmas; tree</annotation>
+ <annotation cp='[🎆]' tts='fireworks'>celebration</annotation>
+ <annotation cp='[🎇]' tts='sparkler'>celebration; fireworks; sparkle</annotation>
+ <annotation cp='[✨]' tts='sparkles'>sparkle; star</annotation>
+ <annotation cp='[🎈]' tts='balloon'>celebration</annotation>
+ <annotation cp='[🎉]' tts='party popper'>celebration; party; popper; tada</annotation>
+ <annotation cp='[🎊]' tts='confetti ball'>ball; celebration; confetti</annotation>
+ <annotation cp='[🎋]' tts='tanabata tree'>banner; celebration; japanese; tree</annotation>
+ <annotation cp='[🎌]' tts='crossed flags'>celebration; cross; crossed; japanese</annotation>
+ <annotation cp='[🎍]' tts='pine decoration'>bamboo; celebration; japanese; pine; plant</annotation>
+ <annotation cp='[🎎]' tts='japanese dolls'>celebration; doll; festival; japanese</annotation>
+ <annotation cp='[🎏]' tts='carp streamer'>carp; celebration; streamer</annotation>
+ <annotation cp='[🎐]' tts='wind chime'>bell; celebration; chime; wind</annotation>
+ <annotation cp='[🎑]' tts='moon ceremony'>celebration; ceremony; moon</annotation>
+ <annotation cp='[🎀]' tts='ribbon'>celebration</annotation>
+ <annotation cp='[🎁]' tts='wrapped present'>box; celebration; gift; present; wrapped</annotation>
+ <annotation cp='[🎖]' tts='military medal'>celebration; medal; military</annotation>
+ <annotation cp='[🎗]' tts='reminder ribbon'>celebration; reminder; ribbon</annotation>
+ <annotation cp='[🎞]' tts='film frames'>cinema; film; frames; movie</annotation>
+ <annotation cp='[🎟]' tts='admission tickets'>admission; ticket</annotation>
+ <annotation cp='[🎫]' tts='ticket'>admission</annotation>
+ <annotation cp='[🏷]' tts='label'>label</annotation>
+ <annotation cp='[⚽]' tts='soccer ball'>ball; soccer</annotation>
+ <annotation cp='[⚾]' tts='baseball'>ball</annotation>
+ <annotation cp='[🏀]' tts='basketball'>ball; hoop</annotation>
+ <annotation cp='[🏈]' tts='american football'>american; ball; football</annotation>
+ <annotation cp='[🏉]' tts='rugby football'>ball; football; rugby</annotation>
+ <annotation cp='[🎾]' tts='tennis'>ball; racquet</annotation>
+ <annotation cp='[🎱]' tts='billiards'>8; 8 ball; ball; billiard; eight; game</annotation>
+ <annotation cp='[🎳]' tts='bowling'>ball; game</annotation>
+ <annotation cp='[⛳]' tts='flag in hole'>golf; hole</annotation>
+ <annotation cp='[🏌]' tts='golfer'>ball; golf</annotation>
+ <annotation cp='[⛸]' tts='ice skate'>ice; skate</annotation>
+ <annotation cp='[🎣]' tts='fishing pole'>fish; pole</annotation>
+ <annotation cp='[🎽]' tts='running shirt'>running; sash; shirt</annotation>
+ <annotation cp='[🎿]' tts='skis'>ski; snow</annotation>
+ <annotation cp='[⛷]' tts='skier'>ski; snow</annotation>
+ <annotation cp='[🏂]' tts='snowboarder'>ski; snow; snowboard</annotation>
+ <annotation cp='[🏄]' tts='surfer'>surfing</annotation>
+ <annotation cp='[🏇]' tts='horse racing'>horse; jockey; racehorse; racing</annotation>
+ <annotation cp='[🏊]' tts='swimmer'>swim</annotation>
+ <annotation cp='[⛹]' tts='person with ball'>ball</annotation>
+ <annotation cp='[🏋]' tts='weight lifter'>lifter; weight</annotation>
+ <annotation cp='[🚴]' tts='bicyclist'>bicycle; bike; cyclist</annotation>
+ <annotation cp='[🚵]' tts='mountain biker'>bicycle; bicyclist; bike; cyclist; mountain</annotation>
+ <annotation cp='[🏎]' tts='racing car'>car; racing</annotation>
+ <annotation cp='[🏍]' tts='motorcycle'>racing</annotation>
+ <annotation cp='[🏅]' tts='sports medal'>medal</annotation>
+ <annotation cp='[🏆]' tts='trophy'>prize</annotation>
+ <annotation cp='[🏏]' tts='cricket'>ball; bat; game</annotation>
+ <annotation cp='[🏐]' tts='volleyball'>ball; game</annotation>
+ <annotation cp='[🏑]' tts='field hockey'>ball; field; game; hockey; stick</annotation>
+ <annotation cp='[🏒]' tts='ice hockey stick and puck'>game; hockey; ice; puck; stick</annotation>
+ <annotation cp='[🏓]' tts='ping pong'>ball; bat; game; paddle; table tennis</annotation>
+ <annotation cp='[🏸]' tts='badminton'>birdie; game; racquet; shuttlecock</annotation>
+ <annotation cp='[🎯]' tts='direct hit'>bull; bullseye; dart; eye; game; hit; target</annotation>
+ <annotation cp='[🎮]' tts='video game'>controller; game</annotation>
+ <annotation cp='[🕹]' tts='joystick'>game; video game</annotation>
+ <annotation cp='[🎲]' tts='game die'>dice; die; game</annotation>
+ <annotation cp='[♠]' tts='spade suit'>card; game; spade; suit</annotation>
+ <annotation cp='[♥]' tts='heart suit'>card; game; heart; hearts; suit</annotation>
+ <annotation cp='[♦]' tts='diamond suit'>card; diamond; diamonds; game; suit</annotation>
+ <annotation cp='[♣]' tts='club suit'>card; club; clubs; game; suit</annotation>
+ <annotation cp='[🃏]' tts='joker'>card; game; playing</annotation>
+ <annotation cp='[🀄]' tts='mahjong red dragon'>game; mahjong; red</annotation>
+ <annotation cp='[🎴]' tts='flower playing cards'>card; flower; game; japanese; playing</annotation>
+ <annotation cp='[🔇]' tts='speaker off'>mute; quiet; silent; speaker; volume</annotation>
+ <annotation cp='[🔈]' tts='speaker'>volume</annotation>
+ <annotation cp='[🔉]' tts='speaker on'>low; speaker; volume; wave</annotation>
+ <annotation cp='[🔊]' tts='speaker loud'>3; high; loud; speaker; three; volume</annotation>
+ <annotation cp='[📢]' tts='loudspeaker'>loud; public address</annotation>
+ <annotation cp='[📣]' tts='megaphone'>cheering</annotation>
+ <annotation cp='[📯]' tts='postal horn'>horn; post; postal</annotation>
+ <annotation cp='[🔔]' tts='bell'>bell</annotation>
+ <annotation cp='[🔕]' tts='bell with slash'>bell; forbidden; mute; no; not; prohibited; quiet; silent</annotation>
+ <annotation cp='[🎼]' tts='musical score'>music; score</annotation>
+ <annotation cp='[🎵]' tts='musical note'>music; note</annotation>
+ <annotation cp='[🎶]' tts='musical notes'>music; note; notes</annotation>
+ <annotation cp='[🎙]' tts='studio microphone'>mic; microphone; music; studio</annotation>
+ <annotation cp='[🎚]' tts='level slider'>level; music; slider</annotation>
+ <annotation cp='[🎛]' tts='control knobs'>control; knobs; music</annotation>
+ <annotation cp='[🎤]' tts='microphone'>karaoke; mic</annotation>
+ <annotation cp='[🎧]' tts='headphone'>earbud</annotation>
+ <annotation cp='[🎷]' tts='saxophone'>instrument; music; sax</annotation>
+ <annotation cp='[🎸]' tts='guitar'>instrument; music</annotation>
+ <annotation cp='[🎹]' tts='musical keyboard'>instrument; keyboard; music; piano</annotation>
+ <annotation cp='[🎺]' tts='trumpet'>instrument; music</annotation>
+ <annotation cp='[🎻]' tts='violin'>instrument; music</annotation>
+ <annotation cp='[📻]' tts='radio'>video</annotation>
+ <annotation cp='[📱]' tts='mobile phone'>cell; mobile; phone; telephone</annotation>
+ <annotation cp='[📲]' tts='mobile phone with arrow'>arrow; call; cell; mobile; phone; receive; telephone</annotation>
+ <annotation cp='[☎]' tts='telephone'>phone</annotation>
+ <annotation cp='[📞]' tts='telephone receiver'>phone; receiver; telephone</annotation>
+ <annotation cp='[📟]' tts='pager'>pager</annotation>
+ <annotation cp='[📠]' tts='fax machine'>fax</annotation>
+ <annotation cp='[🔋]' tts='battery'>battery</annotation>
+ <annotation cp='[🔌]' tts='electric plug'>electric; electricity; plug</annotation>
+ <annotation cp='[💻]' tts='laptop computer'>computer; pc; personal</annotation>
+ <annotation cp='[🖥]' tts='desktop computer'>computer; desktop</annotation>
+ <annotation cp='[🖨]' tts='printer'>computer</annotation>
+ <annotation cp='[⌨]' tts='keyboard'>computer</annotation>
+ <annotation cp='[🖱]' tts='computer mouse'>3; button; computer; mouse; three</annotation>
+ <annotation cp='[🖲]' tts='trackball'>computer</annotation>
+ <annotation cp='[💽]' tts='minidisc'>computer; disk; minidisk; optical</annotation>
+ <annotation cp='[💾]' tts='floppy disk'>computer; disk; floppy</annotation>
+ <annotation cp='[💿]' tts='optical disc'>dvd; blu-ray; cd; computer; disk; optical</annotation>
+ <annotation cp='[📀]' tts='dvd'>blu-ray; cd; computer; disk; optical</annotation>
+ <annotation cp='[🎥]' tts='movie camera'>camera; cinema; movie</annotation>
+ <annotation cp='[🎬]' tts='clapper board'>clapper; movie</annotation>
+ <annotation cp='[📽]' tts='film projector'>cinema; film; movie; projector; video</annotation>
+ <annotation cp='[📺]' tts='television'>tv; video</annotation>
+ <annotation cp='[📷]' tts='camera'>video</annotation>
+ <annotation cp='[📸]' tts='camera with flash'>camera; flash; video</annotation>
+ <annotation cp='[📹]' tts='video camera'>camera; video</annotation>
+ <annotation cp='[📼]' tts='videocassette'>tape; vhs; video</annotation>
+ <annotation cp='[🔍]' tts='left-pointing magnifying glass'>glass; magnifying; search; tool</annotation>
+ <annotation cp='[🔎]' tts='right-pointing magnifying glass'>glass; magnifying; search; tool</annotation>
+ <annotation cp='[🔬]' tts='microscope'>tool</annotation>
+ <annotation cp='[🔭]' tts='telescope'>tool</annotation>
+ <annotation cp='[📡]' tts='satellite antenna'>antenna; dish; satellite</annotation>
+ <annotation cp='[🕯]' tts='candle'>light</annotation>
+ <annotation cp='[💡]' tts='light bulb'>bulb; comic; electric; idea; light</annotation>
+ <annotation cp='[🔦]' tts='flashlight'>electric; light; tool; torch</annotation>
+ <annotation cp='[🏮]' tts='red paper lantern'>bar; japanese; lantern; light; red</annotation>
+ <annotation cp='[📔]' tts='notebook with decorative cover'>book; cover; decorated; notebook</annotation>
+ <annotation cp='[📕]' tts='closed book'>book; closed</annotation>
+ <annotation cp='[📖]' tts='open book'>book; open</annotation>
+ <annotation cp='[📗]' tts='green book'>book; green</annotation>
+ <annotation cp='[📘]' tts='blue book'>blue; book</annotation>
+ <annotation cp='[📙]' tts='orange book'>book; orange</annotation>
+ <annotation cp='[📚]' tts='books'>book</annotation>
+ <annotation cp='[📓]' tts='notebook'>notebook</annotation>
+ <annotation cp='[📒]' tts='ledger'>notebook</annotation>
+ <annotation cp='[📃]' tts='page with curl'>curl; document; page</annotation>
+ <annotation cp='[📜]' tts='scroll'>paper</annotation>
+ <annotation cp='[📄]' tts='page facing up'>document; page</annotation>
+ <annotation cp='[📰]' tts='newspaper'>news; paper</annotation>
+ <annotation cp='[🗞]' tts='rolled-up newspaper'>news; newspaper; paper; rolled</annotation>
+ <annotation cp='[📑]' tts='bookmark tabs'>bookmark; mark; marker; tabs</annotation>
+ <annotation cp='[🔖]' tts='bookmark'>mark</annotation>
+ <annotation cp='[💰]' tts='money bag'>bag; dollar; money; moneybag</annotation>
+ <annotation cp='[💴]' tts='yen banknote'>bank; banknote; bill; currency; money; note; yen</annotation>
+ <annotation cp='[💵]' tts='dollar banknote'>bank; banknote; bill; currency; dollar; money; note</annotation>
+ <annotation cp='[💶]' tts='euro banknote'>bank; banknote; bill; currency; euro; money; note</annotation>
+ <annotation cp='[💷]' tts='pound banknote'>bank; banknote; bill; currency; money; note; pound</annotation>
+ <annotation cp='[💸]' tts='money with wings'>bank; banknote; bill; dollar; fly; money; note; wings</annotation>
+ <annotation cp='[💳]' tts='credit card'>bank; card; credit; money</annotation>
+ <annotation cp='[💹]' tts='chart increasing with yen'>bank; chart; currency; graph; growth; market; money; rise; trend; upward; yen</annotation>
+ <annotation cp='[✉]' tts='envelope'>e-mail; email</annotation>
+ <annotation cp='[📧]' tts='e-mail'>email; letter; mail</annotation>
+ <annotation cp='[📨]' tts='incoming envelope'>e-mail; email; envelope; incoming; letter; mail; receive</annotation>
+ <annotation cp='[📩]' tts='envelope with arrow'>arrow; down; e-mail; email; envelope; letter; mail; outgoing; sent</annotation>
+ <annotation cp='[📤]' tts='outbox tray'>box; letter; mail; outbox; sent; tray</annotation>
+ <annotation cp='[📥]' tts='inbox tray'>box; inbox; letter; mail; receive; tray</annotation>
+ <annotation cp='[📦]' tts='package'>box; parcel</annotation>
+ <annotation cp='[📫]' tts='closed mailbox with raised flag'>closed; mail; mailbox; postbox</annotation>
+ <annotation cp='[📪]' tts='closed mailbox with lowered flag'>closed; lowered; mail; mailbox; postbox</annotation>
+ <annotation cp='[📬]' tts='open mailbox with raised flag'>mail; mailbox; open; postbox</annotation>
+ <annotation cp='[📭]' tts='open mailbox with lowered flag'>lowered; mail; mailbox; open; postbox</annotation>
+ <annotation cp='[📮]' tts='postbox'>mail; mailbox</annotation>
+ <annotation cp='[🗳]' tts='ballot box with ballot'>ballot; box</annotation>
+ <annotation cp='[✏]' tts='pencil'>pencil</annotation>
+ <annotation cp='[✒]' tts='black nib'>nib; pen</annotation>
+ <annotation cp='[🖋]' tts='fountain pen'>fountain; pen</annotation>
+ <annotation cp='[🖊]' tts='pen'>ballpoint</annotation>
+ <annotation cp='[🖌]' tts='paintbrush'>painting</annotation>
+ <annotation cp='[🖍]' tts='crayon'>crayon</annotation>
+ <annotation cp='[📝]' tts='memo'>pencil</annotation>
+ <annotation cp='[💼]' tts='briefcase'>briefcase</annotation>
+ <annotation cp='[📁]' tts='file folder'>file; folder</annotation>
+ <annotation cp='[📂]' tts='open file folder'>file; folder; open</annotation>
+ <annotation cp='[🗂]' tts='card index dividers'>card; dividers; index</annotation>
+ <annotation cp='[📅]' tts='calendar'>date</annotation>
+ <annotation cp='[📆]' tts='tear-off calendar'>calendar</annotation>
+ <annotation cp='[🗒]' tts='spiral notepad'>note; pad; spiral</annotation>
+ <annotation cp='[🗓]' tts='spiral calendar'>calendar; pad; spiral</annotation>
+ <annotation cp='[📇]' tts='card index'>card; index; rolodex</annotation>
+ <annotation cp='[📈]' tts='chart increasing'>chart; graph; growth; trend; upward</annotation>
+ <annotation cp='[📉]' tts='chart decreasing'>chart; down; graph; trend</annotation>
+ <annotation cp='[📊]' tts='bar chart'>bar; chart; graph</annotation>
+ <annotation cp='[📋]' tts='clipboard'>clipboard</annotation>
+ <annotation cp='[📌]' tts='pushpin'>pin</annotation>
+ <annotation cp='[📍]' tts='round pushpin'>pin; pushpin</annotation>
+ <annotation cp='[📎]' tts='paperclip'>paperclip</annotation>
+ <annotation cp='[🖇]' tts='linked paperclips'>link; paperclip</annotation>
+ <annotation cp='[📏]' tts='straight ruler'>ruler; straight edge</annotation>
+ <annotation cp='[📐]' tts='triangular ruler'>ruler; set; triangle</annotation>
+ <annotation cp='[✂]' tts='scissors'>tool</annotation>
+ <annotation cp='[🗃]' tts='card file box'>box; card; file</annotation>
+ <annotation cp='[🗄]' tts='file cabinet'>cabinet; file</annotation>
+ <annotation cp='[🗑]' tts='wastebasket'>wastebasket</annotation>
+ <annotation cp='[🔒]' tts='lock'>closed</annotation>
+ <annotation cp='[🔓]' tts='open lock'>lock; open; unlock</annotation>
+ <annotation cp='[🔏]' tts='lock with pen'>ink; lock; nib; pen; privacy</annotation>
+ <annotation cp='[🔐]' tts='closed lock with key'>closed; key; lock; secure</annotation>
+ <annotation cp='[🔑]' tts='key'>lock; password</annotation>
+ <annotation cp='[🗝]' tts='old key'>clue; key; lock; old</annotation>
+ <annotation cp='[🔨]' tts='hammer'>tool</annotation>
+ <annotation cp='[⛏]' tts='pick'>mining; tool</annotation>
+ <annotation cp='[⚒]' tts='hammer and pick'>hammer; pick; tool</annotation>
+ <annotation cp='[🛠]' tts='hammer and wrench'>hammer; tool; wrench</annotation>
+ <annotation cp='[🔧]' tts='wrench'>tool</annotation>
+ <annotation cp='[🔩]' tts='nut and bolt'>bolt; nut; tool</annotation>
+ <annotation cp='[⚙]' tts='gear'>tool</annotation>
+ <annotation cp='[🗜]' tts='compression'>tool; vice</annotation>
+ <annotation cp='[⚗]' tts='alembic'>chemistry; tool</annotation>
+ <annotation cp='[⚖]' tts='balance scale'>balance; justice; libra; scales; tool; weight; zodiac</annotation>
+ <annotation cp='[🔗]' tts='link'>link</annotation>
+ <annotation cp='[⛓]' tts='chains'>chain</annotation>
+ <annotation cp='[💉]' tts='syringe'>doctor; medicine; needle; shot; sick; tool</annotation>
+ <annotation cp='[💊]' tts='pill'>doctor; medicine; sick</annotation>
+ <annotation cp='[🗡]' tts='dagger'>knife; weapon</annotation>
+ <annotation cp='[🔪]' tts='kitchen knife'>cooking; hocho; knife; tool; weapon</annotation>
+ <annotation cp='[⚔]' tts='crossed swords'>crossed; swords; weapon</annotation>
+ <annotation cp='[🔫]' tts='pistol'>gun; handgun; revolver; tool; weapon</annotation>
+ <annotation cp='[🛡]' tts='shield'>weapon</annotation>
+ <annotation cp='[🏹]' tts='bow and arrow'>archer; arrow; bow; sagittarius; tool; weapon; zodiac</annotation>
+ <annotation cp='[🏁]' tts='chequered flag'>checkered; chequered; racing</annotation>
+ <annotation cp='[🏳]' tts='waving white flag'>waving</annotation>
+ <annotation cp='[🏴]' tts='waving black flag'>waving</annotation>
+ <annotation cp='[🚩]' tts='triangular flag'>post</annotation>
+ <annotation cp='[🚬]' tts='smoking'>smoking</annotation>
+ <annotation cp='[⚰]' tts='coffin'>death</annotation>
+ <annotation cp='[⚱]' tts='funeral urn'>death; funeral; urn</annotation>
+ <annotation cp='[🗿]' tts='moai'>face; moyai; statue</annotation>
+ <annotation cp='[🛢]' tts='oil drum'>drum; oil</annotation>
+ <annotation cp='[🔮]' tts='crystal ball'>ball; crystal; fairy tale; fantasy; fortune; tool</annotation>
+ <annotation cp='[🏧]' tts='ATM sign'>atm; automated; bank; teller</annotation>
+ <annotation cp='[🚮]' tts='litter in bin sign'>litter; litterbox</annotation>
+ <annotation cp='[🚰]' tts='potable water'>drink; potable; water</annotation>
+ <annotation cp='[♿]' tts='wheelchair'>access</annotation>
+ <annotation cp='[🚹]' tts='mens room'>lavatory; man; restroom; wc</annotation>
+ <annotation cp='[🚺]' tts='womens room'>lavatory; restroom; wc; woman</annotation>
+ <annotation cp='[🚻]' tts='restroom'>lavatory; wc</annotation>
+ <annotation cp='[🚼]' tts='baby symbol'>baby; changing</annotation>
+ <annotation cp='[🚾]' tts='water closet'>closet; lavatory; restroom; water; wc</annotation>
+ <annotation cp='[🛂]' tts='passport control'>control; passport</annotation>
+ <annotation cp='[🛃]' tts='customs'>customs</annotation>
+ <annotation cp='[🛄]' tts='baggage claim'>baggage; claim</annotation>
+ <annotation cp='[🛅]' tts='left luggage'>baggage; locker; luggage</annotation>
+ <annotation cp='[⚠]' tts='warning'>warning</annotation>
+ <annotation cp='[🚸]' tts='children crossing'>child; crossing; pedestrian; traffic</annotation>
+ <annotation cp='[⛔]' tts='no entry'>entry; forbidden; no; not; prohibited; traffic</annotation>
+ <annotation cp='[🚫]' tts='prohibited'>entry; forbidden; no; not</annotation>
+ <annotation cp='[🚳]' tts='no bicycles'>bicycle; bike; forbidden; no; not; prohibited; vehicle</annotation>
+ <annotation cp='[🚭]' tts='no smoking'>forbidden; no; not; prohibited; smoking</annotation>
+ <annotation cp='[🚯]' tts='no littering'>forbidden; litter; no; not; prohibited</annotation>
+ <annotation cp='[🚱]' tts='non-potable water'>drink; forbidden; no; not; potable; prohibited; water</annotation>
+ <annotation cp='[🚷]' tts='no pedestrians'>forbidden; no; not; pedestrian; prohibited</annotation>
+ <annotation cp='[☢]' tts='radioactive'>radioactive</annotation>
+ <annotation cp='[☣]' tts='biohazard'>biohazard</annotation>
+ <annotation cp='[⬆]' tts='up arrow'>arrow; cardinal; direction; north</annotation>
+ <annotation cp='[↗]' tts='up-right arrow'>arrow; direction; intercardinal; northeast</annotation>
+ <annotation cp='[➡]' tts='right arrow'>arrow; cardinal; direction; east</annotation>
+ <annotation cp='[↘]' tts='down-right arrow'>arrow; direction; intercardinal; southeast</annotation>
+ <annotation cp='[⬇]' tts='down arrow'>arrow; cardinal; direction; down; south</annotation>
+ <annotation cp='[↙]' tts='down-left arrow'>arrow; direction; intercardinal; southwest</annotation>
+ <annotation cp='[⬅]' tts='left arrow'>arrow; cardinal; direction; west</annotation>
+ <annotation cp='[↖]' tts='up-left arrow'>arrow; direction; intercardinal; northwest</annotation>
+ <annotation cp='[↕]' tts='up-down arrow'>arrow</annotation>
+ <annotation cp='[↔]' tts='left-right arrow'>arrow</annotation>
+ <annotation cp='[↩]' tts='right arrow curving left'>arrow</annotation>
+ <annotation cp='[↪]' tts='left arrow curving right'>arrow</annotation>
+ <annotation cp='[⤴]' tts='right arrow curving up'>arrow</annotation>
+ <annotation cp='[⤵]' tts='right arrow curving down'>arrow; down</annotation>
+ <annotation cp='[🔃]' tts='clockwise vertical arrows'>arrow; clockwise; reload</annotation>
+ <annotation cp='[🔄]' tts='anticlockwise arrows button'>anticlockwise; arrow; counterclockwise; withershins</annotation>
+ <annotation cp='[🔙]' tts='back arrow'>arrow; back</annotation>
+ <annotation cp='[🔚]' tts='end arrow'>arrow; end</annotation>
+ <annotation cp='[🔛]' tts='on! arrow'>arrow; mark; on</annotation>
+ <annotation cp='[🔜]' tts='soon arrow'>arrow; soon</annotation>
+ <annotation cp='[🔝]' tts='top arrow'>arrow; top; up</annotation>
+ <annotation cp='[🛐]' tts='place of worship'>religion; worship</annotation>
+ <annotation cp='[⚛]' tts='atom symbol'>atheist; atom</annotation>
+ <annotation cp='[🕉]' tts='om'>hindu; religion</annotation>
+ <annotation cp='[✡]' tts='star of david'>david; jew; jewish; religion; star</annotation>
+ <annotation cp='[☸]' tts='wheel of dharma'>buddhist; dharma; religion; wheel</annotation>
+ <annotation cp='[☯]' tts='yin yang'>religion; tao; taoist; yang; yin</annotation>
+ <annotation cp='[✝]' tts='latin cross'>christian; cross; religion</annotation>
+ <annotation cp='[☦]' tts='orthodox cross'>christian; cross; religion</annotation>
+ <annotation cp='[☪]' tts='star and crescent'>islam; muslim; religion</annotation>
+ <annotation cp='[☮]' tts='peace symbol'>peace</annotation>
+ <annotation cp='[🕎]' tts='menorah'>candelabrum; candlestick; religion</annotation>
+ <annotation cp='[🔯]' tts='dotted six-pointed star'>fortune; star</annotation>
+ <annotation cp='[♻]' tts='recycling symbol'>recycle</annotation>
+ <annotation cp='[📛]' tts='name badge'>badge; name</annotation>
+ <annotation cp='[⚜]' tts='fleur-de-lis'>fleur-de-lis</annotation>
+ <annotation cp='[🔰]' tts='japanese symbol for beginner'>beginner; chevron; green; japanese; leaf; tool; yellow</annotation>
+ <annotation cp='[🔱]' tts='trident emblem'>anchor; emblem; ship; tool; trident</annotation>
+ <annotation cp='[⭕]' tts='heavy large circle'>circle; o</annotation>
+ <annotation cp='[✅]' tts='white heavy check mark'>check; mark</annotation>
+ <annotation cp='[☑]' tts='ballot box with check'>ballot; box; check</annotation>
+ <annotation cp='[✔]' tts='heavy check mark'>check; mark</annotation>
+ <annotation cp='[✖]' tts='heavy multiplication x'>cancel; multiplication; multiply; x</annotation>
+ <annotation cp='[❌]' tts='cross mark'>cancel; mark; multiplication; multiply; x</annotation>
+ <annotation cp='[❎]' tts='cross mark button'>mark; square</annotation>
+ <annotation cp='[]' tts='heavy plus sign'>math; plus</annotation>
+ <annotation cp='[]' tts='heavy minus sign'>math; minus</annotation>
+ <annotation cp='[➗]' tts='heavy division sign'>division; math</annotation>
+ <annotation cp='[➰]' tts='curly loop'>curl; loop</annotation>
+ <annotation cp='[➿]' tts='double curly loop'>curl; double; loop</annotation>
+ <annotation cp='[〽]' tts='part alternation mark'>mark; part</annotation>
+ <annotation cp='[✳]' tts='eight-spoked asterisk'>asterisk</annotation>
+ <annotation cp='[✴]' tts='eight-pointed star'>star</annotation>
+ <annotation cp='[❇]' tts='sparkle'>sparkle</annotation>
+ <annotation cp='[💱]' tts='currency exchange'>bank; currency; exchange; money</annotation>
+ <annotation cp='[💲]' tts='heavy dollar sign'>currency; dollar; money</annotation>
+ <annotation cp='[‼]' tts='double exclamation mark'>bangbang; exclamation; mark; punctuation</annotation>
+ <annotation cp='[⁉]' tts='exclamation question mark'>exclamation; interrobang; mark; punctuation; question</annotation>
+ <annotation cp='[❓]' tts='question mark'>mark; punctuation; question</annotation>
+ <annotation cp='[❔]' tts='white question mark'>mark; outlined; punctuation; question</annotation>
+ <annotation cp='[❕]' tts='white exclamation mark'>exclamation; mark; outlined; punctuation</annotation>
+ <annotation cp='[❗]' tts='exclamation mark'>exclamation; mark; punctuation</annotation>
+ <annotation cp='[〰]' tts='wavy dash'>dash; punctuation; wavy</annotation>
+ <annotation cp='[©]' tts='copyright'>copyright</annotation>
+ <annotation cp='[®]' tts='registered'>registered</annotation>
+ <annotation cp='[™]' tts='trade mark'>mark; tm; trademark</annotation>
+ <annotation cp='[♈]' tts='aries'>ram; zodiac</annotation>
+ <annotation cp='[♉]' tts='taurus'>bull; ox; zodiac</annotation>
+ <annotation cp='[♊]' tts='gemini'>twins; zodiac</annotation>
+ <annotation cp='[♋]' tts='cancer'>crab; zodiac</annotation>
+ <annotation cp='[♌]' tts='leo'>lion; zodiac</annotation>
+ <annotation cp='[♍]' tts='virgo'>maiden; virgin; zodiac</annotation>
+ <annotation cp='[♎]' tts='libra'>balance; justice; scales; zodiac</annotation>
+ <annotation cp='[♏]' tts='scorpius'>scorpio; scorpion; zodiac</annotation>
+ <annotation cp='[♐]' tts='sagittarius'>archer; zodiac</annotation>
+ <annotation cp='[♑]' tts='capricorn'>goat; zodiac</annotation>
+ <annotation cp='[♒]' tts='aquarius'>bearer; water; zodiac</annotation>
+ <annotation cp='[♓]' tts='pisces'>fish; zodiac</annotation>
+ <annotation cp='[⛎]' tts='ophiuchus'>bearer; serpent; snake; zodiac</annotation>
+ <annotation cp='[🔀]' tts='shuffle tracks button'>arrow; crossed</annotation>
+ <annotation cp='[🔁]' tts='repeat button'>arrow; clockwise; repeat</annotation>
+ <annotation cp='[🔂]' tts='repeat single button'>arrow; clockwise; once</annotation>
+ <annotation cp='[▶]' tts='play button'>arrow; play; right; triangle</annotation>
+ <annotation cp='[⏩]' tts='fast-forword button'>arrow; double; fast; forward</annotation>
+ <annotation cp='[⏭]' tts='next track button'>arrow; next scene; next track; triangle</annotation>
+ <annotation cp='[⏯]' tts='play or pause button'>arrow; pause; play; right; triangle</annotation>
+ <annotation cp='[◀]' tts='reverse button'>arrow; left; reverse; triangle</annotation>
+ <annotation cp='[⏪]' tts='fast reverse button'>arrow; double; rewind</annotation>
+ <annotation cp='[⏮]' tts='last track button'>arrow; previous scene; previous track; triangle</annotation>
+ <annotation cp='[🔼]' tts='up button'>arrow; button; red</annotation>
+ <annotation cp='[⏫]' tts='fast up button'>arrow; double</annotation>
+ <annotation cp='[🔽]' tts='down button'>arrow; button; down; red</annotation>
+ <annotation cp='[⏬]' tts='fast down button'>arrow; double; down</annotation>
+ <annotation cp='[⏸]' tts='pause button'>bar; double; pause; vertical</annotation>
+ <annotation cp='[⏹]' tts='stop button'>square; stop</annotation>
+ <annotation cp='[⏺]' tts='record button'>circle; record</annotation>
+ <annotation cp='[⏏]' tts='eject button'>eject</annotation>
+ <annotation cp='[🎦]' tts='cinema'>camera; film; movie</annotation>
+ <annotation cp='[🔅]' tts='dim button'>brightness; dim; low</annotation>
+ <annotation cp='[🔆]' tts='bright button'>bright; brightness</annotation>
+ <annotation cp='[📶]' tts='antenna bars'>antenna; bar; cell; mobile; phone; signal; telephone</annotation>
+ <annotation cp='[📵]' tts='no mobile phones'>cell; forbidden; mobile; no; not; phone; prohibited; telephone</annotation>
+ <annotation cp='[📳]' tts='vibration mode'>cell; mobile; mode; phone; telephone; vibration</annotation>
+ <annotation cp='[📴]' tts='mobile phone off'>cell; mobile; off; phone; telephone</annotation>
+ <annotation cp='[{#⃣}]' tts='keycap number'>hash; keycap; pound</annotation>
+ <annotation cp='[{*⃣}]' tts='keycap asterisk'>asterisk; keycap; star</annotation>
+ <annotation cp='[{0⃣}]' tts='keycap digit zero'>0; keycap; zero</annotation>
+ <annotation cp='[{1⃣}]' tts='keycap digit one'>1; keycap; one</annotation>
+ <annotation cp='[{2⃣}]' tts='keycap digit two'>2; keycap; two</annotation>
+ <annotation cp='[{3⃣}]' tts='keycap digit three'>3; keycap; three</annotation>
+ <annotation cp='[{4⃣}]' tts='keycap digit four'>4; four; keycap</annotation>
+ <annotation cp='[{5⃣}]' tts='keycap digit five'>5; five; keycap</annotation>
+ <annotation cp='[{6⃣}]' tts='keycap digit six'>6; keycap; six</annotation>
+ <annotation cp='[{7⃣}]' tts='keycap digit seven'>7; keycap; seven</annotation>
+ <annotation cp='[{8⃣}]' tts='keycap digit eight'>8; eight; keycap</annotation>
+ <annotation cp='[{9⃣}]' tts='keycap digit nine'>9; keycap; nine</annotation>
+ <annotation cp='[🔟]' tts='keycap ten'>10; keycap; ten</annotation>
+ <annotation cp='[💯]' tts='hundred points'>100; full; hundred; score</annotation>
+ <annotation cp='[🔞]' tts='no one under eighteen'>18; age restriction; eighteen; forbidden; no; not; prohibited; underage</annotation>
+ <annotation cp='[🔠]' tts='input latin uppercase'>input; latin; letters; uppercase</annotation>
+ <annotation cp='[🔡]' tts='input latin lowercase'>abcd; input; latin; letters; lowercase</annotation>
+ <annotation cp='[🔢]' tts='input numbers'>1234; input; numbers</annotation>
+ <annotation cp='[🔣]' tts='input symbols'>input</annotation>
+ <annotation cp='[🔤]' tts='input latin letters'>abc; alphabet; input; latin; letters</annotation>
+ <annotation cp='[🅰]' tts='a button'>a; blood</annotation>
+ <annotation cp='[🆎]' tts='ab button'>ab; blood</annotation>
+ <annotation cp='[🅱]' tts='b button'>b; blood</annotation>
+ <annotation cp='[🆑]' tts='squared cl'>cl</annotation>
+ <annotation cp='[🆒]' tts='squared cool'>cool</annotation>
+ <annotation cp='[🆓]' tts='squared free'>free</annotation>
+ <annotation cp='[]' tts='information source'>i; information</annotation>
+ <annotation cp='[🆔]' tts='squared id'>id; identity</annotation>
+ <annotation cp='[Ⓜ]' tts='circled letter m'>circle; m</annotation>
+ <annotation cp='[🆕]' tts='squared new'>new</annotation>
+ <annotation cp='[🆖]' tts='squared ng'>ng</annotation>
+ <annotation cp='[🅾]' tts='o button'>blood; o</annotation>
+ <annotation cp='[🆗]' tts='squared ok'>ok</annotation>
+ <annotation cp='[🅿]' tts='p button'>parking</annotation>
+ <annotation cp='[🆘]' tts='squared sos'>help; sos</annotation>
+ <annotation cp='[🆙]' tts='up! button'>mark; up</annotation>
+ <annotation cp='[🆚]' tts='squared vs'>versus; vs</annotation>
+ <annotation cp='[🈁]' tts='squared katakana koko'>japanese</annotation>
+ <annotation cp='[🈂]' tts='squared katakana sa'>japanese</annotation>
+ <annotation cp='[🈷]' tts='squared moon ideograph'>japanese</annotation>
+ <annotation cp='[🈶]' tts='squared exist ideograph'>japanese</annotation>
+ <annotation cp='[🈯]' tts='squared finger ideograph'>japanese</annotation>
+ <annotation cp='[🉐]' tts='circled advantage ideograph'>japanese</annotation>
+ <annotation cp='[🈹]' tts='squared divide ideograph'>japanese</annotation>
+ <annotation cp='[🈚]' tts='squared negation ideograph'>japanese</annotation>
+ <annotation cp='[🈲]' tts='squared prohibit ideograph'>japanese</annotation>
+ <annotation cp='[🉑]' tts='circled accept ideograph'>chinese</annotation>
+ <annotation cp='[🈸]' tts='squared apply ideograph'>chinese</annotation>
+ <annotation cp='[🈴]' tts='squared together ideograph'>chinese</annotation>
+ <annotation cp='[🈳]' tts='squared empty ideograph'>chinese</annotation>
+ <annotation cp='[㊗]' tts='circled congratulate ideograph'>chinese; congratulation; congratulations; ideograph</annotation>
+ <annotation cp='[㊙]' tts='circled secret ideograph'>chinese; ideograph; secret</annotation>
+ <annotation cp='[🈺]' tts='squared operating ideograph'>chinese</annotation>
+ <annotation cp='[🈵]' tts='squared fullness ideograph'>chinese</annotation>
+ <annotation cp='[▪]' tts='black small square'>geometric; square</annotation>
+ <annotation cp='[▫]' tts='white small square'>geometric; square</annotation>
+ <annotation cp='[◻]' tts='white medium square'>geometric; square</annotation>
+ <annotation cp='[◼]' tts='black medium square'>geometric; square</annotation>
+ <annotation cp='[◽]' tts='white medium-small square'>geometric; square</annotation>
+ <annotation cp='[◾]' tts='black medium-small square'>geometric; square</annotation>
+ <annotation cp='[⬛]' tts='black large square'>geometric; square</annotation>
+ <annotation cp='[⬜]' tts='white large square'>geometric; square</annotation>
+ <annotation cp='[🔶]' tts='large orange diamond'>diamond; geometric; orange</annotation>
+ <annotation cp='[🔷]' tts='large blue diamond'>blue; diamond; geometric</annotation>
+ <annotation cp='[🔸]' tts='small orange diamond'>diamond; geometric; orange</annotation>
+ <annotation cp='[🔹]' tts='small blue diamond'>blue; diamond; geometric</annotation>
+ <annotation cp='[🔺]' tts='red triangle pointed up'>geometric; red</annotation>
+ <annotation cp='[🔻]' tts='red triangle pointed down'>down; geometric; red</annotation>
+ <annotation cp='[💠]' tts='diamond with a dot'>comic; diamond; geometric; inside</annotation>
+ <annotation cp='[🔘]' tts='radio button'>button; geometric; radio</annotation>
+ <annotation cp='[🔲]' tts='black square button'>button; geometric; square</annotation>
+ <annotation cp='[🔳]' tts='white square button'>button; geometric; outlined; square</annotation>
+ <annotation cp='[⚪]' tts='white circle'>circle; geometric</annotation>
+ <annotation cp='[⚫]' tts='black circle'>circle; geometric</annotation>
+ <annotation cp='[🔴]' tts='red circle'>circle; geometric; red</annotation>
+ <annotation cp='[🔵]' tts='blue circle'>blue; circle; geometric</annotation>
+ </annotations>
+</ldml>
\ No newline at end of file
diff --git a/ibus.spec.in b/ibus.spec.in
index 7fdaccb..40aead1 100644
--- a/ibus.spec.in
+++ b/ibus.spec.in
@@ -5,6 +5,7 @@
# Build flags
%define build_python_library 0
+%define build_emoji_dictionary 1
%define glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999")
%define gconf2_version 2.12.0
@@ -40,6 +41,10 @@ BuildRequires: dconf-devel
BuildRequires: pygobject2-devel
BuildRequires: intltool
BuildRequires: iso-codes-devel
+%if %build_emoji_dictionary
+BuildRequires: nodejs-emojione
+BuildRequires: json-glib
+%endif
Requires: %{name}-libs = %{version}-%{release}
Requires: %{name}-gtk2 = %{version}-%{release}
@@ -151,6 +156,12 @@ OPTIONS="$OPTIONS --enable-python-library"
%else
OPTIONS="$OPTIONS --disable-python-library"
%endif
+%if %build_emoji_dictionary
+OPTIONS="$OPTIONS --enable-emoji-dict"
+%else
+OPTIONS="$OPTIONS --disable-emoji-dict"
+%endif
+
%configure $OPTIONS
@@ -258,6 +269,9 @@ dconf update &> /dev/null || :
%{_bindir}/ibus-setup
%{_datadir}/ibus/component/gtkpanel.xml
%{_datadir}/ibus/component/simple.xml
+%if %build_emoji_dictionary
+%{_datadir}/ibus/dicts
+%endif
%{_datadir}/ibus/keymaps/*
%{_datadir}/ibus/setup/*
diff --git a/src/Makefile.am b/src/Makefile.am
index 22e031f..b58ab95 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,15 +29,6 @@ endif
SUBDIRS = . $(TESTS_DIR)
-IBUS_V_wget = $(ibus__v_wget_@AM_V@)
-ibus__v_wget_ = $(ibus__v_wget_@AM_DEFAULT_V@)
-ibus__v_wget_0 = -nv
-ibus__v_wget_1 =
-IBUS_V_diff = $(ibus__v_diff_@AM_V@)
-ibus__v_diff_ = $(ibus__v_diff_@AM_DEFAULT_V@)
-ibus__v_diff_0 = -q
-ibus__v_diff_1 =
-
# libibus = libibus-@IBUS_API_VERSION@.la
libibus = libibus-1.0.la
@@ -248,37 +239,25 @@ dict_DATA = emoji.dict
noinst_PROGRAMS = emoji-parser
-emoji-list.html:
- $(AM_V_at)wget $(IBUS_V_wget) \
- http://ibus.github.io/files/ibus/emoji-list.html
- $(AM_V_at)wget $(IBUS_V_wget) \
- http://unicode.org/emoji/charts/emoji-list.html \
- -O latest-emoji-list.html
- $(AM_V_at)diff $(IBUS_V_diff) emoji-list.html latest-emoji-list.html; \
- if test $$? -ne 0; then \
- echo "#### WARNING: emoji-list.html is old." >&2; \
- fi; \
- rm latest-emoji-list.html;
-
-emoji.dict: emoji-parser emoji-list.html
- $(builddir)/emoji-parser emoji-list.html $@
+emoji.dict: emoji-parser
+ $(builddir)/emoji-parser --xml $(srcdir)/../data/annotations/en.xml \
+ --json $(EMOJI_JSON_FILE) --out $@
emoji_parser_SOURCES = \
emoji-parser.c \
$(NULL)
emoji_parser_CFLAGS = \
$(GLIB2_CFLAGS) \
- $(LIBXML2_CFLAGS) \
+ $(JSON_GLIB1_CFLAGS) \
$(NULL)
emoji_parser_LDADD = \
$(GLIB2_LIBS) \
- $(LIBXML2_LIBS) \
+ $(JSON_GLIB1_LIBS) \
$(libibus) \
$(NULL)
CLEANFILES += \
$(dict_DATA) \
- emoji-list.html \
$(NULL)
endif
diff --git a/src/emoji-parser.c b/src/emoji-parser.c
index cf92fee..e97f266 100644
--- a/src/emoji-parser.c
+++ b/src/emoji-parser.c
@@ -20,47 +20,59 @@
* USA
*/
-/* Convert http://www.unicode.org/emoji/charts/emoji-list.html
+/* Convert ../data/annotations/en.xml and
+ * /usr/lib/node_modules/emojione/emoji.json
* to the dictionary file which look up the Emoji from the annotation.
+ * Get emoji.json with 'npm install -g emojione'.
+ * en.xml is used for the Unicode annotations and emoji.json is used
+ * for the aliases_ascii, e.g. ":)", and category, e.g. "people".
*/
#include <glib.h>
-#include <libxml/HTMLparser.h>
-#include <libgen.h>
+#include <json-glib/json-glib.h>
+
+#include <string.h>
#include "ibusutil.h"
typedef struct _EmojiData EmojiData;
struct _EmojiData {
- gchar *class;
gchar *emoji;
- GSList *annotates;
- GSList *prev_annotates;
+ GSList *annotations;
+ gboolean is_annotation;
GHashTable *dict;
};
-const gchar *progname;
-
-static gboolean parse_node (xmlNode *node,
- gboolean is_child,
- const gchar *prop_name,
- EmojiData *data);
-
static void
-usage (void)
+reset_emoji_element (EmojiData *data)
{
- g_print ("%s emoji-list.html emoji.dict\n", progname);
+ g_assert (data != NULL);
+
+ g_clear_pointer (&data->emoji, g_free);
+ g_slist_free_full (data->annotations, g_free);
+ data->annotations = NULL;
}
static void
-reset_emoji_element (EmojiData *data)
+update_emoji_dict (EmojiData *data)
{
- g_clear_pointer (&data->class, g_free);
- g_clear_pointer (&data->emoji, g_free);
- if (data->annotates) {
- g_slist_free_full (data->prev_annotates, g_free);
- data->prev_annotates = data->annotates;
- data->annotates = NULL;
+ GSList *annotations = data->annotations;
+ while (annotations) {
+ const gchar *annotation = (const gchar *) annotations->data;
+ GSList *emojis = g_hash_table_lookup (data->dict, annotation);
+ if (emojis) {
+ GSList *duplicated = g_slist_find_custom (emojis,
+ data->emoji,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated != NULL)
+ continue;
+ emojis = g_slist_copy_deep (emojis, (GCopyFunc) g_strdup, NULL);
+ }
+ emojis = g_slist_append (emojis, g_strdup (data->emoji));
+ g_hash_table_replace (data->dict,
+ g_strdup (annotation),
+ emojis);
+ annotations = annotations->next;
}
}
@@ -70,147 +82,486 @@ free_dict_words (gpointer list)
g_slist_free_full (list, g_free);
}
-static gboolean
-parse_attr (xmlAttr *attr,
- EmojiData *data)
-{
- if (g_strcmp0 ((const gchar *) attr->name, "class") == 0 && attr->children)
- parse_node (attr->children, TRUE, (const gchar *) attr->name, data);
- if (g_strcmp0 ((const gchar *) attr->name, "target") == 0 && attr->children)
- parse_node (attr->children, TRUE, (const gchar *) attr->name, data);
- if (attr->next)
- parse_attr (attr->next, data);
- return TRUE;
-}
+static void
+unicode_annotations_start_element_cb (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ EmojiData *data = (EmojiData *) user_data;
+ int i;
+ const gchar *attribute;
+ const gchar *value;
-static gboolean
-parse_node (xmlNode *node,
- gboolean is_child,
- const gchar *prop_name,
- EmojiData *data)
-{
- if (g_strcmp0 ((const gchar *) node->name, "tr") == 0) {
- GSList *annotates = data->annotates;
- while (annotates) {
- GSList *emojis = g_hash_table_lookup (data->dict, annotates->data);
- if (emojis) {
- emojis = g_slist_copy_deep (emojis, (GCopyFunc) g_strdup, NULL);
+ g_assert (data != NULL);
+
+ if (g_strcmp0 (element_name, "annotation") != 0)
+ return;
+
+ reset_emoji_element (data);
+
+ for (i = 0; (attribute = attribute_names[i]) != NULL; i++) {
+ value = attribute_values[i];
+
+ if (g_strcmp0 (attribute, "cp") == 0) {
+ if (value == NULL || *value == '\0') {
+ g_warning ("cp='' in unicode.org annotations file");
+ return;
+ } else if (value[0] != '[' || value[strlen(value) - 1] != ']') {
+ g_warning ("cp!='[emoji]' in unicode.org annotations file");
+ return;
}
- emojis = g_slist_append (emojis, g_strdup (data->emoji));
- g_hash_table_replace (data->dict,
- g_strdup (annotates->data),
- emojis);
- annotates = annotates->next;
+ data->emoji = g_strndup (value + 1, strlen(value) - 2);
}
- reset_emoji_element (data);
- }
- /* if node->name is "text" and is_child is FALSE,
- * it's '\n' or Space between <td> and <td>.
- */
- if (g_strcmp0 ((const gchar *) node->name, "text") == 0 && is_child) {
- /* Get "chars" in <td class="chars"> */
- if (g_strcmp0 (prop_name, "class") == 0) {
- if (g_strcmp0 (data->class, (const gchar *) node->content) != 0) {
- g_clear_pointer (&data->class, g_free);
- data->class = g_strdup ((const gchar *) node->content);
+ else if (g_strcmp0 (attribute, "tts") == 0) {
+ GSList *duplicated = g_slist_find_custom (data->annotations,
+ value,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated == NULL) {
+ data->annotations = g_slist_prepend (data->annotations,
+ g_strdup (value));
}
}
- /* Get "annotate" in <td class="name"><a target="annotate"> */
- if (g_strcmp0 (prop_name, "target") == 0 &&
- g_strcmp0 (data->class, "name") == 0) {
- g_clear_pointer (&data->class, g_free);
- data->class = g_strdup ((const gchar *) node->content);
- }
- /* Get "emoji" in <td class="chars">emoji</td> */
- if (g_strcmp0 (prop_name, "td") == 0 &&
- g_strcmp0 (data->class, "chars") == 0) {
- data->emoji = g_strdup ((const gchar *) node->content);
- }
- /* We ignore "NAME" for <td class="name">NAME</td> but
- * takes "ANNOTATE" for
- * <td class="name"><a target="annotate">ANNOTATE</a></td>
- */
- if (g_strcmp0 (prop_name, "td") == 0 &&
- g_strcmp0 (data->class, "name") == 0) {
- g_slist_free_full (data->annotates, g_free);
- data->annotates = NULL;
+ }
+
+ data->is_annotation = TRUE;
+}
+
+static void
+unicode_annotations_end_element_cb (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ EmojiData *data = (EmojiData *) user_data;
+
+ g_assert (data != NULL);
+ if (!data->is_annotation)
+ return;
+
+ update_emoji_dict (data);
+ data->is_annotation = FALSE;
+}
+
+void
+unicode_annotations_text_cb (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ EmojiData *data = (EmojiData *) user_data;
+ gchar **annotations = NULL;
+ const gchar *annotation;
+ int i;
+
+ g_assert (data != NULL);
+ if (!data->is_annotation)
+ return;
+ annotations = g_strsplit (text, "; ", -1);
+ for (i = 0; (annotation = annotations[i]) != NULL; i++) {
+ GSList *duplicated = g_slist_find_custom (data->annotations,
+ annotation,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated == NULL) {
+ data->annotations = g_slist_prepend (data->annotations,
+ g_strdup (annotation));
}
- /* Get "ANNOTATE" in
- * <td class="name"><a target="annotate">ANNOTATE</a></td>
- */
- if (g_strcmp0 (prop_name, "a") == 0 &&
- g_strcmp0 (data->class, "annotate") == 0) {
- data->annotates =
- g_slist_append (data->annotates,
- g_strdup ((const gchar *) node->content));
+ }
+ g_strfreev (annotations);
+}
+
+static gboolean
+unicode_annotations_parse_xml_file (const gchar *filename,
+ GHashTable *dict)
+{
+ gchar *content = NULL;
+ gsize length = 0;
+ GError *error = NULL;
+ const static GMarkupParser parser = {
+ unicode_annotations_start_element_cb,
+ unicode_annotations_end_element_cb,
+ unicode_annotations_text_cb,
+ NULL,
+ NULL
+ };
+ GMarkupParseContext *context = NULL;
+ EmojiData data = { 0, };
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (dict != NULL, FALSE);
+
+ if (!g_file_get_contents (filename, &content, &length, &error)) {
+ g_warning ("Failed to load %s: %s", filename, error->message);
+ goto failed_to_parse_unicode_annotations;
+ }
+
+ data.dict = dict;
+
+ context = g_markup_parse_context_new (&parser, 0, &data, NULL);
+ if (!g_markup_parse_context_parse (context, content, length, &error)) {
+ g_warning ("Failed to parse %s: %s", filename, error->message);
+ goto failed_to_parse_unicode_annotations;
+ }
+
+ reset_emoji_element (&data);
+ g_markup_parse_context_free (context);
+ g_free (content);
+ return TRUE;
+
+failed_to_parse_unicode_annotations:
+ if (error)
+ g_error_free (error);
+ if (data.dict)
+ g_hash_table_destroy (data.dict);
+ if (context)
+ g_markup_parse_context_free (context);
+ g_free (content);
+ return FALSE;
+}
+
+static gboolean
+parse_emojione_unicode (JsonNode *node,
+ EmojiData *data)
+{
+ const gchar *str, *unicode;
+ gchar *endptr = NULL;
+ guint32 uch;
+ static gchar outbuf[8] = { 0, };
+ GString *emoji;
+
+ if (json_node_get_node_type (node) != JSON_NODE_VALUE) {
+ g_warning ("'unicode' element is not string");
+ return FALSE;
+ }
+
+ emoji = g_string_new (NULL);
+ str = unicode = json_node_get_string (node);
+ while (str && *str) {
+ uch = g_ascii_strtoull (str, &endptr, 16);
+ outbuf[g_unichar_to_utf8 (uch, outbuf)] = '\0';
+ g_string_append (emoji, outbuf);
+ if (*endptr == '\0') {
+ break;
+ } else {
+ switch (*endptr) {
+ case '-':
+ endptr++;
+ break;
+ default:
+ g_warning ("Failed to parse unicode %s", unicode);
+ }
}
+ str = endptr;
+ endptr = NULL;
+ }
+
+ data->emoji = g_string_free (emoji, FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+parse_emojione_shortname (JsonNode *node,
+ EmojiData *data)
+{
+#if 0
+ const gchar *shortname;
+ gchar *head, *s;
+ int length;
+ GSList *duplicated;
+
+ if (json_node_get_node_type (node) != JSON_NODE_VALUE) {
+ g_warning ("'shortname' element is not string");
+ return FALSE;
}
- /* Get "foo" in <td class="foo"> */
- if (g_strcmp0 ((const gchar *) node->name, "td") == 0 &&
- node->properties != NULL) {
- parse_attr (node->properties, data);
+
+ /* The format is ':short_name:' */
+ shortname = json_node_get_string (node);
+ if (shortname == 0 || *shortname == '\0')
+ return TRUE;
+ if (*shortname != ':') {
+ g_warning ("'shortname' format is different: %s", shortname);
+ return FALSE;
}
- /* Get "foo" in <a target="foo"> */
- if (g_strcmp0 ((const gchar *) node->name, "a") == 0 &&
- node->properties != NULL) {
- parse_attr (node->properties, data);
+
+ length = strlen (shortname);
+ head = g_new0 (gchar, length);
+ strcpy (head, shortname + 1);
+ for (s = head; *s; s++) {
+ if (*s == ':') {
+ *s = '\0';
+ break;
+ } else if (*s == '_') {
+ *s = ' ';
+ }
}
- if (node->children) {
- parse_node (node->children, TRUE, (const gchar *) node->name, data);
+
+ if (head == NULL || *head == '\0') {
+ g_warning ("'shortname' format is different: %s", shortname);
+ g_free (head);
+ return FALSE;
+ }
+
+ duplicated = g_slist_find_custom (data->annotations,
+ head,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated == NULL) {
+ data->annotations = g_slist_prepend (data->annotations,
+ head);
} else {
- /* If annotate is NULL likes <td class="name"></td>,
- * the previous emoji cell has the same annotate.
- */
- if (g_strcmp0 ((const gchar *) node->name, "td") == 0 &&
- g_strcmp0 (data->class, "name") == 0) {
- data->annotates = g_slist_copy_deep (data->prev_annotates,
- (GCopyFunc) g_strdup,
- NULL);
+ g_free (head);
+ }
+
+#endif
+ return TRUE;
+}
+
+static gboolean
+parse_emojione_category (JsonNode *node,
+ EmojiData *data)
+{
+ const gchar *category;
+ GSList *duplicated;
+
+ if (json_node_get_node_type (node) != JSON_NODE_VALUE) {
+ g_warning ("'category' element is not string");
+ return FALSE;
+ }
+
+ category = json_node_get_string (node);
+
+ if (category == NULL || *category == '\0')
+ return TRUE;
+
+ duplicated = g_slist_find_custom (data->annotations,
+ category,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated == NULL) {
+ data->annotations = g_slist_prepend (data->annotations,
+ g_strdup (category));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+parse_emojione_aliases_ascii (JsonNode *node,
+ EmojiData *data)
+{
+ JsonArray *aliases_ascii;
+ guint i, length;
+
+ if (json_node_get_node_type (node) != JSON_NODE_ARRAY) {
+ g_warning ("'aliases_ascii' element is not array");
+ return FALSE;
+ }
+
+ aliases_ascii = json_node_get_array (node);
+ length = json_array_get_length (aliases_ascii);
+ for (i = 0; i < length; i++) {
+ const gchar *alias = json_array_get_string_element (aliases_ascii, i);
+ GSList *duplicated = g_slist_find_custom (data->annotations,
+ alias,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated == NULL) {
+ data->annotations = g_slist_prepend (data->annotations,
+ g_strdup (alias));
}
}
- if (node->next)
- parse_node (node->next, FALSE, (const gchar *) node->name, data);
return TRUE;
}
-static GHashTable *
-parse_html (const gchar *filename)
+static gboolean
+parse_emojione_keywords (JsonNode *node,
+ EmojiData *data)
{
- xmlDoc *doc = htmlParseFile (filename, "utf-8");
- EmojiData data = { 0, };
+#if 0
+ JsonArray *keywords;
+ guint i, length;
- if (doc == NULL || doc->children == NULL) {
- g_warning ("Parse Error in document type: %x",
- doc ? doc->type : 0);
+ if (json_node_get_node_type (node) != JSON_NODE_ARRAY) {
+ g_warning ("'keywords' element is not array");
return FALSE;
}
- data.dict = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- free_dict_words);
- parse_node (doc->children, TRUE, (const gchar *) doc->name, &data);
+ keywords = json_node_get_array (node);
+ length = json_array_get_length (keywords);
+ for (i = 0; i < length; i++) {
+ const gchar *keyword = json_array_get_string_element (keywords, i);
+ GSList *duplicated = g_slist_find_custom (data->annotations,
+ keyword,
+ (GCompareFunc) g_strcmp0);
+ if (duplicated == NULL) {
+ data->annotations = g_slist_prepend (data->annotations,
+ g_strdup (keyword));
+ }
+ }
+#endif
+ return TRUE;
+}
+
+static gboolean
+parse_emojione_emoji_data (JsonNode *node,
+ const gchar *member,
+ EmojiData *data)
+{
+ if (g_strcmp0 (member, "unicode") == 0)
+ return parse_emojione_unicode (node, data);
+ else if (g_strcmp0 (member, "shortname") == 0)
+ return parse_emojione_shortname (node, data);
+ else if (g_strcmp0 (member, "category") == 0)
+ return parse_emojione_category (node, data);
+ else if (g_strcmp0 (member, "aliases_ascii") == 0)
+ return parse_emojione_aliases_ascii (node, data);
+ else if (g_strcmp0 (member, "keywords") == 0)
+ return parse_emojione_keywords (node, data);
+ return TRUE;
+}
+
+static gboolean
+parse_emojione_element (JsonNode *node,
+ EmojiData *data)
+{
+ JsonObject *object;
+ GList *members, *m;
+
+ if (json_node_get_node_type (node) != JSON_NODE_OBJECT) {
+ return FALSE;
+ }
+
+ reset_emoji_element (data);
+
+ object = json_node_get_object (node);
+ m = members = json_object_get_members (object);
+ while (m) {
+ const gchar *member = (const gchar *) m->data;
+ if (!parse_emojione_emoji_data (json_object_get_member (object, member),
+ member,
+ data)) {
+ g_list_free (members);
+ return FALSE;
+ }
+ m = m->next;
+ }
+ g_list_free (members);
+
+ update_emoji_dict (data);
+
+ return TRUE;
+}
+
+static gboolean
+emojione_parse_json_file (const gchar *filename,
+ GHashTable *dict)
+{
+ JsonParser *parser = json_parser_new ();
+ JsonNode *node;
+ JsonObject *object;
+ GList *members, *m;
+ GError *error = NULL;
+ EmojiData data = { 0, };
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (dict != NULL, FALSE);
+
+ if (!json_parser_load_from_file (parser, filename, &error)) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ goto fail_to_json_file;
+ }
+
+ node = json_parser_get_root (parser);
+ if (json_node_get_node_type (node) != JSON_NODE_OBJECT) {
+ g_warning ("Json file does not have Json object %s", filename);
+ goto fail_to_json_file;
+ }
+
+ object = json_node_get_object (node);
+ members = json_object_get_members (object);
+ data.dict = dict;
+
+ m = members;
+ while (m) {
+ const gchar *member = (const gchar *) m->data;
+ if (!parse_emojione_element (json_object_get_member (object, member),
+ &data)) {
+ g_warning ("Failed to parse member '%s' in %s", member, filename);
+ }
+ m = m->next;
+ }
+
+ g_list_free (members);
reset_emoji_element (&data);
- g_slist_free_full (data.prev_annotates, g_free);
+ g_object_unref (parser);
+
+ return TRUE;
- return data.dict;
+fail_to_json_file:
+ g_object_unref (parser);
+ return FALSE;
}
int
main (int argc, char *argv[])
{
+ gchar *prgname;
+ gchar *json_file = NULL;
+ gchar *xml_file = NULL;
+ gchar *output = NULL;
+ GOptionEntry entries[] = {
+ { "json", 'j', 0, G_OPTION_ARG_STRING, &json_file,
+ "Parse Emoji One JSON file",
+ "JSON"
+ },
+ { "out", 'o', 0, G_OPTION_ARG_STRING, &output,
+ "Save the emoji dictionary as FILE",
+ "FILE"
+ },
+ { "xml", 'x', 0, G_OPTION_ARG_STRING, &xml_file,
+ "Parse Unocode.org ANNOTATIONS file",
+ "ANNOTATIONS"
+ },
+ { NULL }
+ };
+ GOptionContext *context;
+ GError *error = NULL;
GHashTable *dict;
- progname = basename (argv[0]);
+
+ prgname = g_path_get_basename (argv[0]);
+ g_set_prgname (prgname);
+ g_free (prgname);
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
if (argc < 3) {
- usage ();
+ g_print ("%s", g_option_context_get_help (context, TRUE, NULL));
+ g_option_context_free (context);
+ return -1;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_warning ("Failed options: %s", error->message);
+ g_error_free (error);
return -1;
}
+ g_option_context_free (context);
- dict = parse_html (argv[1]);
- ibus_emoji_dict_save (argv[2], dict);
+ dict = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ free_dict_words);
+ if (xml_file)
+ unicode_annotations_parse_xml_file (xml_file, dict);
+ if (json_file)
+ emojione_parse_json_file (json_file, dict);
+ if (g_hash_table_size (dict) > 0 && output)
+ ibus_emoji_dict_save (output, dict);
g_hash_table_destroy (dict);
return 0;
--
2.7.4
From f88c48750538eaaf7c7b182ba3763b45c2745074 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Wed, 20 Jul 2016 11:43:49 +0900
Subject: [PATCH 4/4] Fix typo in configure.ac
R=shawn.p.huang@gmail.com
Review URL: https://codereview.appspot.com/303110043
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index f789819..76897f0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -647,7 +647,7 @@ engine/Makefile
util/Makefile
util/IMdkit/Makefile
data/Makefile
-data/annotations/Makefile.am
+data/annotations/Makefile
data/icons/Makefile
data/keymaps/Makefile
data/dconf/Makefile
--
2.7.4