public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Implement Emoji typing
@ 2026-05-31 2:06 Takao Fujiwara
0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31 2:06 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/ibus
Branch : autotool
Commit : 98247b22c0e5529aac786e06b026e1b074fd61a8
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date : 2016-07-26T19:49:47+09:00
Stats : +3421/-1 in 2 file(s)
URL : https://src.fedoraproject.org/rpms/ibus/c/98247b22c0e5529aac786e06b026e1b074fd61a8?branch=autotool
Log:
Implement Emoji typing
---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index cb117e3..89a27b3 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -279,3 +279,3417 @@ index 2ca3a5e..cc19350 100644
--
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='woman’s 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='man’s 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='woman’s sandal'>clothing; sandal; shoe; woman</annotation>
++ <annotation cp='[👢]' tts='woman’s boot'>boot; clothing; shoe; woman</annotation>
++ <annotation cp='[👑]' tts='crown'>clothing; king; queen</annotation>
++ <annotation cp='[👒]' tts='woman’s 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 car’s 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 o’clock'>00; 12; 12:00; clock; o’clock; twelve</annotation>
++ <annotation cp='[🕧]' tts='twelve-thirty'>12; 12:30; 30; clock; thirty; twelve</annotation>
++ <annotation cp='[🕐]' tts='one o’clock'>00; 1; 1:00; clock; o’clock; one</annotation>
++ <annotation cp='[🕜]' tts='one-thirty'>1; 1:30; 30; clock; one; thirty</annotation>
++ <annotation cp='[🕑]' tts='two o’clock'>00; 2; 2:00; clock; o’clock; two</annotation>
++ <annotation cp='[🕝]' tts='two-thirty'>2; 2:30; 30; clock; thirty; two</annotation>
++ <annotation cp='[🕒]' tts='three o’clock'>00; 3; 3:00; clock; o’clock; three</annotation>
++ <annotation cp='[🕞]' tts='three-thirty'>3; 3:30; 30; clock; thirty; three</annotation>
++ <annotation cp='[🕓]' tts='four o’clock'>00; 4; 4:00; clock; four; o’clock</annotation>
++ <annotation cp='[🕟]' tts='four-thirty'>30; 4; 4:30; clock; four; thirty</annotation>
++ <annotation cp='[🕔]' tts='five o’clock'>00; 5; 5:00; clock; five; o’clock</annotation>
++ <annotation cp='[🕠]' tts='five-thirty'>30; 5; 5:30; clock; five; thirty</annotation>
++ <annotation cp='[🕕]' tts='six o’clock'>00; 6; 6:00; clock; o’clock; six</annotation>
++ <annotation cp='[🕡]' tts='six-thirty'>30; 6; 6:30; clock; six; thirty</annotation>
++ <annotation cp='[🕖]' tts='seven o’clock'>00; 7; 7:00; clock; o’clock; seven</annotation>
++ <annotation cp='[🕢]' tts='seven-thirty'>30; 7; 7:30; clock; seven; thirty</annotation>
++ <annotation cp='[🕗]' tts='eight o’clock'>00; 8; 8:00; clock; eight; o’clock</annotation>
++ <annotation cp='[🕣]' tts='eight-thirty'>30; 8; 8:30; clock; eight; thirty</annotation>
++ <annotation cp='[🕘]' tts='nine o’clock'>00; 9; 9:00; clock; nine; o’clock</annotation>
++ <annotation cp='[🕤]' tts='nine-thirty'>30; 9; 9:30; clock; nine; thirty</annotation>
++ <annotation cp='[🕙]' tts='ten o’clock'>00; 10; 10:00; clock; o’clock; ten</annotation>
++ <annotation cp='[🕥]' tts='ten-thirty'>10; 10:30; 30; clock; ten; thirty</annotation>
++ <annotation cp='[🕚]' tts='eleven o’clock'>00; 11; 11:00; clock; eleven; o’clock</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='men’s room'>lavatory; man; restroom; wc</annotation>
++ <annotation cp='[🚺]' tts='women’s 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
+
diff --git a/ibus.spec b/ibus.spec
index f1fc515..4e73ff8 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -28,7 +28,7 @@
Name: ibus
Version: 1.5.13
-Release: 4%{?dist}
+Release: 5%{?dist}
Summary: Intelligent Input Bus for Linux OS
License: LGPLv2+
Group: System Environment/Libraries
@@ -67,6 +67,8 @@ BuildRequires: libwayland-client-devel
%if %with_kde5
BuildRequires: qt5-qtbase-devel
%endif
+BuildRequires: nodejs-emojione-json
+BuildRequires: json-glib-devel
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
Requires: %{name}-gtk2%{?_isa} = %{version}-%{release}
@@ -347,6 +349,7 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
%{_datadir}/GConf/gsettings/*
%{_datadir}/glib-2.0/schemas/*.xml
%{_datadir}/ibus/component
+%{_datadir}/ibus/dicts
%{_datadir}/ibus/engine
%{_datadir}/ibus/keymaps
%{_datadir}/icons/hicolor/*/apps/*
@@ -412,6 +415,9 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
%{_datadir}/gtk-doc/html/*
%changelog
+* Tue Jul 26 2016 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.13-5
+- Bug 1359753 - Implement Emoji typing
+
* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.5.13-4
- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-31 2:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31 2:06 [rpms/ibus] autotool: Implement Emoji typing Takao Fujiwara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox