public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Takao Fujiwara <tfujiwar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ibus] autotool: Dropped nodejs-emojione-json and import unicode-emoji instead
Date: Sun, 31 May 2026 02:06:32 GMT [thread overview]
Message-ID: <178019319260.1.16822556303133795888.rpms-ibus-8dd4dc1c86fb@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/ibus
Branch : autotool
Commit : 8dd4dc1c86fb516325ebc1a97a707a874a3d6d19
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date : 2017-05-09T15:21:22+09:00
Stats : +6992/-12 in 3 file(s)
URL : https://src.fedoraproject.org/rpms/ibus/c/8dd4dc1c86fb516325ebc1a97a707a874a3d6d19?branch=autotool
Log:
Dropped nodejs-emojione-json and import unicode-emoji instead
- Created emoji tab in ibus-setup
- Set default emoji font size from gsettings in ibus emoji command
- Added an option of emoji partial match in ibus-setup
- Hid emoji variants by default
- Added ibus-emoji man page
- emoji favorites category is updated by selecting emoji
---
diff --git a/emoji-test.txt b/emoji-test.txt
new file mode 100644
index 0000000..533739f
--- /dev/null
+++ b/emoji-test.txt
@@ -0,0 +1,3033 @@
+# emoji-test.txt
+# Date: 2016-11-16, 18:29:53 GMT
+# © 2016 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/terms_of_use.html
+#
+# Emoji Keyboard/Display Test Data for UTR #51
+# Version: 4.0
+#
+# For documentation and usage, see http://www.unicode.org/reports/tr51
+#
+# This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed.
+# Format
+# Code points; status # emoji name
+# Status
+# keyboard — see “Emoji Implementation Notes” in UTR#51
+# process — see “Emoji Implementation Notes” in UTR#51
+# Notes:
+# • This currently omits the 12 keycap bases, the 5 modifier characters, and 26 singleton Regional Indicator characters
+# • The file is in CLDR order, not codepoint order. This is recommended (but not required!) for keyboard palettes.
+# • The groups and subgroups are purely illustrative. See the Emoji Order chart for more information.
+
+# group: Smileys & People
+
+# subgroup: face-positive
+1F600 ; fully-qualified # 😀 grinning face
+1F601 ; fully-qualified # 😁 grinning face with smiling eyes
+1F602 ; fully-qualified # 😂 face with tears of joy
+1F923 ; fully-qualified # 🤣 rolling on the floor laughing
+1F603 ; fully-qualified # 😃 smiling face with open mouth
+1F604 ; fully-qualified # 😄 smiling face with open mouth & smiling eyes
+1F605 ; fully-qualified # 😅 smiling face with open mouth & cold sweat
+1F606 ; fully-qualified # 😆 smiling face with open mouth & closed eyes
+1F609 ; fully-qualified # 😉 winking face
+1F60A ; fully-qualified # 😊 smiling face with smiling eyes
+1F60B ; fully-qualified # 😋 face savouring delicious food
+1F60E ; fully-qualified # 😎 smiling face with sunglasses
+1F60D ; fully-qualified # 😍 smiling face with heart-eyes
+1F618 ; fully-qualified # 😘 face blowing a kiss
+1F617 ; fully-qualified # 😗 kissing face
+1F619 ; fully-qualified # 😙 kissing face with smiling eyes
+1F61A ; fully-qualified # 😚 kissing face with closed eyes
+263A FE0F ; fully-qualified # ☺️ smiling face
+1F642 ; fully-qualified # 🙂 slightly smiling face
+1F917 ; fully-qualified # 🤗 hugging face
+
+# subgroup: face-neutral
+1F914 ; fully-qualified # 🤔 thinking face
+1F610 ; fully-qualified # 😐 neutral face
+1F611 ; fully-qualified # 😑 expressionless face
+1F636 ; fully-qualified # 😶 face without mouth
+1F644 ; fully-qualified # 🙄 face with rolling eyes
+1F60F ; fully-qualified # 😏 smirking face
+1F623 ; fully-qualified # 😣 persevering face
+1F625 ; fully-qualified # 😥 disappointed but relieved face
+1F62E ; fully-qualified # 😮 face with open mouth
+1F910 ; fully-qualified # 🤐 zipper-mouth face
+1F62F ; fully-qualified # 😯 hushed face
+1F62A ; fully-qualified # 😪 sleepy face
+1F62B ; fully-qualified # 😫 tired face
+1F634 ; fully-qualified # 😴 sleeping face
+1F60C ; fully-qualified # 😌 relieved face
+1F913 ; fully-qualified # 🤓 nerd face
+1F61B ; fully-qualified # 😛 face with stuck-out tongue
+1F61C ; fully-qualified # 😜 face with stuck-out tongue & winking eye
+1F61D ; fully-qualified # 😝 face with stuck-out tongue & closed eyes
+1F924 ; fully-qualified # 🤤 drooling face
+1F612 ; fully-qualified # 😒 unamused face
+1F613 ; fully-qualified # 😓 face with cold sweat
+1F614 ; fully-qualified # 😔 pensive face
+1F615 ; fully-qualified # 😕 confused face
+1F643 ; fully-qualified # 🙃 upside-down face
+1F911 ; fully-qualified # 🤑 money-mouth face
+1F632 ; fully-qualified # 😲 astonished face
+
+# subgroup: face-negative
+2639 FE0F ; fully-qualified # ☹️ frowning face
+1F641 ; fully-qualified # 🙁 slightly frowning face
+1F616 ; fully-qualified # 😖 confounded face
+1F61E ; fully-qualified # 😞 disappointed face
+1F61F ; fully-qualified # 😟 worried face
+1F624 ; fully-qualified # 😤 face with steam from nose
+1F622 ; fully-qualified # 😢 crying face
+1F62D ; fully-qualified # 😭 loudly crying face
+1F626 ; fully-qualified # 😦 frowning face with open mouth
+1F627 ; fully-qualified # 😧 anguished face
+1F628 ; fully-qualified # 😨 fearful face
+1F629 ; fully-qualified # 😩 weary face
+1F62C ; fully-qualified # 😬 grimacing face
+1F630 ; fully-qualified # 😰 face with open mouth & cold sweat
+1F631 ; fully-qualified # 😱 face screaming in fear
+1F633 ; fully-qualified # 😳 flushed face
+1F635 ; fully-qualified # 😵 dizzy face
+1F621 ; fully-qualified # 😡 pouting face
+1F620 ; fully-qualified # 😠 angry face
+
+# subgroup: face-role
+1F607 ; fully-qualified # 😇 smiling face with halo
+1F920 ; fully-qualified # 🤠 cowboy hat face
+1F921 ; fully-qualified # 🤡 clown face
+1F925 ; fully-qualified # 🤥 lying face
+
+# subgroup: face-sick
+1F637 ; fully-qualified # 😷 face with medical mask
+1F912 ; fully-qualified # 🤒 face with thermometer
+1F915 ; fully-qualified # 🤕 face with head-bandage
+1F922 ; fully-qualified # 🤢 nauseated face
+1F927 ; fully-qualified # 🤧 sneezing face
+
+# subgroup: creature-face
+1F608 ; fully-qualified # 😈 smiling face with horns
+1F47F ; fully-qualified # 👿 angry face with horns
+1F479 ; fully-qualified # 👹 ogre
+1F47A ; fully-qualified # 👺 goblin
+1F480 ; fully-qualified # 💀 skull
+2620 FE0F ; fully-qualified # ☠️ skull and crossbones
+1F47B ; fully-qualified # 👻 ghost
+1F47D ; fully-qualified # 👽 alien
+1F47E ; fully-qualified # 👾 alien monster
+1F916 ; fully-qualified # 🤖 robot face
+1F4A9 ; fully-qualified # 💩 pile of poo
+
+# subgroup: cat-face
+1F63A ; fully-qualified # 😺 smiling cat face with open mouth
+1F638 ; fully-qualified # 😸 grinning cat face with smiling eyes
+1F639 ; fully-qualified # 😹 cat face with tears of joy
+1F63B ; fully-qualified # 😻 smiling cat face with heart-eyes
+1F63C ; fully-qualified # 😼 cat face with wry smile
+1F63D ; fully-qualified # 😽 kissing cat face with closed eyes
+1F640 ; fully-qualified # 🙀 weary cat face
+1F63F ; fully-qualified # 😿 crying cat face
+1F63E ; fully-qualified # 😾 pouting cat face
+
+# subgroup: monkey-face
+1F648 ; fully-qualified # 🙈 see-no-evil monkey
+1F649 ; fully-qualified # 🙉 hear-no-evil monkey
+1F64A ; fully-qualified # 🙊 speak-no-evil monkey
+
+# subgroup: person
+1F466 ; fully-qualified # 👦 boy
+1F466 1F3FB ; fully-qualified # 👦🏻 boy: light skin tone
+1F466 1F3FC ; fully-qualified # 👦🏼 boy: medium-light skin tone
+1F466 1F3FD ; fully-qualified # 👦🏽 boy: medium skin tone
+1F466 1F3FE ; fully-qualified # 👦🏾 boy: medium-dark skin tone
+1F466 1F3FF ; fully-qualified # 👦🏿 boy: dark skin tone
+1F467 ; fully-qualified # 👧 girl
+1F467 1F3FB ; fully-qualified # 👧🏻 girl: light skin tone
+1F467 1F3FC ; fully-qualified # 👧🏼 girl: medium-light skin tone
+1F467 1F3FD ; fully-qualified # 👧🏽 girl: medium skin tone
+1F467 1F3FE ; fully-qualified # 👧🏾 girl: medium-dark skin tone
+1F467 1F3FF ; fully-qualified # 👧🏿 girl: dark skin tone
+1F468 ; fully-qualified # 👨 man
+1F468 1F3FB ; fully-qualified # 👨🏻 man: light skin tone
+1F468 1F3FC ; fully-qualified # 👨🏼 man: medium-light skin tone
+1F468 1F3FD ; fully-qualified # 👨🏽 man: medium skin tone
+1F468 1F3FE ; fully-qualified # 👨🏾 man: medium-dark skin tone
+1F468 1F3FF ; fully-qualified # 👨🏿 man: dark skin tone
+1F469 ; fully-qualified # 👩 woman
+1F469 1F3FB ; fully-qualified # 👩🏻 woman: light skin tone
+1F469 1F3FC ; fully-qualified # 👩🏼 woman: medium-light skin tone
+1F469 1F3FD ; fully-qualified # 👩🏽 woman: medium skin tone
+1F469 1F3FE ; fully-qualified # 👩🏾 woman: medium-dark skin tone
+1F469 1F3FF ; fully-qualified # 👩🏿 woman: dark skin tone
+1F474 ; fully-qualified # 👴 old man
+1F474 1F3FB ; fully-qualified # 👴🏻 old man: light skin tone
+1F474 1F3FC ; fully-qualified # 👴🏼 old man: medium-light skin tone
+1F474 1F3FD ; fully-qualified # 👴🏽 old man: medium skin tone
+1F474 1F3FE ; fully-qualified # 👴🏾 old man: medium-dark skin tone
+1F474 1F3FF ; fully-qualified # 👴🏿 old man: dark skin tone
+1F475 ; fully-qualified # 👵 old woman
+1F475 1F3FB ; fully-qualified # 👵🏻 old woman: light skin tone
+1F475 1F3FC ; fully-qualified # 👵🏼 old woman: medium-light skin tone
+1F475 1F3FD ; fully-qualified # 👵🏽 old woman: medium skin tone
+1F475 1F3FE ; fully-qualified # 👵🏾 old woman: medium-dark skin tone
+1F475 1F3FF ; fully-qualified # 👵🏿 old woman: dark skin tone
+1F476 ; fully-qualified # 👶 baby
+1F476 1F3FB ; fully-qualified # 👶🏻 baby: light skin tone
+1F476 1F3FC ; fully-qualified # 👶🏼 baby: medium-light skin tone
+1F476 1F3FD ; fully-qualified # 👶🏽 baby: medium skin tone
+1F476 1F3FE ; fully-qualified # 👶🏾 baby: medium-dark skin tone
+1F476 1F3FF ; fully-qualified # 👶🏿 baby: dark skin tone
+1F47C ; fully-qualified # 👼 baby angel
+1F47C 1F3FB ; fully-qualified # 👼🏻 baby angel: light skin tone
+1F47C 1F3FC ; fully-qualified # 👼🏼 baby angel: medium-light skin tone
+1F47C 1F3FD ; fully-qualified # 👼🏽 baby angel: medium skin tone
+1F47C 1F3FE ; fully-qualified # 👼🏾 baby angel: medium-dark skin tone
+1F47C 1F3FF ; fully-qualified # 👼🏿 baby angel: dark skin tone
+
+# subgroup: person-role
+1F468 200D 2695 FE0F ; fully-qualified # 👨⚕️ man health worker
+1F468 200D 2695 ; non-fully-qualified # 👨⚕ man health worker
+1F468 1F3FB 200D 2695 FE0F ; fully-qualified # 👨🏻⚕️ man health worker: light skin tone
+1F468 1F3FB 200D 2695 ; non-fully-qualified # 👨🏻⚕ man health worker: light skin tone
+1F468 1F3FC 200D 2695 FE0F ; fully-qualified # 👨🏼⚕️ man health worker: medium-light skin tone
+1F468 1F3FC 200D 2695 ; non-fully-qualified # 👨🏼⚕ man health worker: medium-light skin tone
+1F468 1F3FD 200D 2695 FE0F ; fully-qualified # 👨🏽⚕️ man health worker: medium skin tone
+1F468 1F3FD 200D 2695 ; non-fully-qualified # 👨🏽⚕ man health worker: medium skin tone
+1F468 1F3FE 200D 2695 FE0F ; fully-qualified # 👨🏾⚕️ man health worker: medium-dark skin tone
+1F468 1F3FE 200D 2695 ; non-fully-qualified # 👨🏾⚕ man health worker: medium-dark skin tone
+1F468 1F3FF 200D 2695 FE0F ; fully-qualified # 👨🏿⚕️ man health worker: dark skin tone
+1F468 1F3FF 200D 2695 ; non-fully-qualified # 👨🏿⚕ man health worker: dark skin tone
+1F469 200D 2695 FE0F ; fully-qualified # 👩⚕️ woman health worker
+1F469 200D 2695 ; non-fully-qualified # 👩⚕ woman health worker
+1F469 1F3FB 200D 2695 FE0F ; fully-qualified # 👩🏻⚕️ woman health worker: light skin tone
+1F469 1F3FB 200D 2695 ; non-fully-qualified # 👩🏻⚕ woman health worker: light skin tone
+1F469 1F3FC 200D 2695 FE0F ; fully-qualified # 👩🏼⚕️ woman health worker: medium-light skin tone
+1F469 1F3FC 200D 2695 ; non-fully-qualified # 👩🏼⚕ woman health worker: medium-light skin tone
+1F469 1F3FD 200D 2695 FE0F ; fully-qualified # 👩🏽⚕️ woman health worker: medium skin tone
+1F469 1F3FD 200D 2695 ; non-fully-qualified # 👩🏽⚕ woman health worker: medium skin tone
+1F469 1F3FE 200D 2695 FE0F ; fully-qualified # 👩🏾⚕️ woman health worker: medium-dark skin tone
+1F469 1F3FE 200D 2695 ; non-fully-qualified # 👩🏾⚕ woman health worker: medium-dark skin tone
+1F469 1F3FF 200D 2695 FE0F ; fully-qualified # 👩🏿⚕️ woman health worker: dark skin tone
+1F469 1F3FF 200D 2695 ; non-fully-qualified # 👩🏿⚕ woman health worker: dark skin tone
+1F468 200D 1F393 ; fully-qualified # 👨🎓 man student
+1F468 1F3FB 200D 1F393 ; fully-qualified # 👨🏻🎓 man student: light skin tone
+1F468 1F3FC 200D 1F393 ; fully-qualified # 👨🏼🎓 man student: medium-light skin tone
+1F468 1F3FD 200D 1F393 ; fully-qualified # 👨🏽🎓 man student: medium skin tone
+1F468 1F3FE 200D 1F393 ; fully-qualified # 👨🏾🎓 man student: medium-dark skin tone
+1F468 1F3FF 200D 1F393 ; fully-qualified # 👨🏿🎓 man student: dark skin tone
+1F469 200D 1F393 ; fully-qualified # 👩🎓 woman student
+1F469 1F3FB 200D 1F393 ; fully-qualified # 👩🏻🎓 woman student: light skin tone
+1F469 1F3FC 200D 1F393 ; fully-qualified # 👩🏼🎓 woman student: medium-light skin tone
+1F469 1F3FD 200D 1F393 ; fully-qualified # 👩🏽🎓 woman student: medium skin tone
+1F469 1F3FE 200D 1F393 ; fully-qualified # 👩🏾🎓 woman student: medium-dark skin tone
+1F469 1F3FF 200D 1F393 ; fully-qualified # 👩🏿🎓 woman student: dark skin tone
+1F468 200D 1F3EB ; fully-qualified # 👨🏫 man teacher
+1F468 1F3FB 200D 1F3EB ; fully-qualified # 👨🏻🏫 man teacher: light skin tone
+1F468 1F3FC 200D 1F3EB ; fully-qualified # 👨🏼🏫 man teacher: medium-light skin tone
+1F468 1F3FD 200D 1F3EB ; fully-qualified # 👨🏽🏫 man teacher: medium skin tone
+1F468 1F3FE 200D 1F3EB ; fully-qualified # 👨🏾🏫 man teacher: medium-dark skin tone
+1F468 1F3FF 200D 1F3EB ; fully-qualified # 👨🏿🏫 man teacher: dark skin tone
+1F469 200D 1F3EB ; fully-qualified # 👩🏫 woman teacher
+1F469 1F3FB 200D 1F3EB ; fully-qualified # 👩🏻🏫 woman teacher: light skin tone
+1F469 1F3FC 200D 1F3EB ; fully-qualified # 👩🏼🏫 woman teacher: medium-light skin tone
+1F469 1F3FD 200D 1F3EB ; fully-qualified # 👩🏽🏫 woman teacher: medium skin tone
+1F469 1F3FE 200D 1F3EB ; fully-qualified # 👩🏾🏫 woman teacher: medium-dark skin tone
+1F469 1F3FF 200D 1F3EB ; fully-qualified # 👩🏿🏫 woman teacher: dark skin tone
+1F468 200D 2696 FE0F ; fully-qualified # 👨⚖️ man judge
+1F468 200D 2696 ; non-fully-qualified # 👨⚖ man judge
+1F468 1F3FB 200D 2696 FE0F ; fully-qualified # 👨🏻⚖️ man judge: light skin tone
+1F468 1F3FB 200D 2696 ; non-fully-qualified # 👨🏻⚖ man judge: light skin tone
+1F468 1F3FC 200D 2696 FE0F ; fully-qualified # 👨🏼⚖️ man judge: medium-light skin tone
+1F468 1F3FC 200D 2696 ; non-fully-qualified # 👨🏼⚖ man judge: medium-light skin tone
+1F468 1F3FD 200D 2696 FE0F ; fully-qualified # 👨🏽⚖️ man judge: medium skin tone
+1F468 1F3FD 200D 2696 ; non-fully-qualified # 👨🏽⚖ man judge: medium skin tone
+1F468 1F3FE 200D 2696 FE0F ; fully-qualified # 👨🏾⚖️ man judge: medium-dark skin tone
+1F468 1F3FE 200D 2696 ; non-fully-qualified # 👨🏾⚖ man judge: medium-dark skin tone
+1F468 1F3FF 200D 2696 FE0F ; fully-qualified # 👨🏿⚖️ man judge: dark skin tone
+1F468 1F3FF 200D 2696 ; non-fully-qualified # 👨🏿⚖ man judge: dark skin tone
+1F469 200D 2696 FE0F ; fully-qualified # 👩⚖️ woman judge
+1F469 200D 2696 ; non-fully-qualified # 👩⚖ woman judge
+1F469 1F3FB 200D 2696 FE0F ; fully-qualified # 👩🏻⚖️ woman judge: light skin tone
+1F469 1F3FB 200D 2696 ; non-fully-qualified # 👩🏻⚖ woman judge: light skin tone
+1F469 1F3FC 200D 2696 FE0F ; fully-qualified # 👩🏼⚖️ woman judge: medium-light skin tone
+1F469 1F3FC 200D 2696 ; non-fully-qualified # 👩🏼⚖ woman judge: medium-light skin tone
+1F469 1F3FD 200D 2696 FE0F ; fully-qualified # 👩🏽⚖️ woman judge: medium skin tone
+1F469 1F3FD 200D 2696 ; non-fully-qualified # 👩🏽⚖ woman judge: medium skin tone
+1F469 1F3FE 200D 2696 FE0F ; fully-qualified # 👩🏾⚖️ woman judge: medium-dark skin tone
+1F469 1F3FE 200D 2696 ; non-fully-qualified # 👩🏾⚖ woman judge: medium-dark skin tone
+1F469 1F3FF 200D 2696 FE0F ; fully-qualified # 👩🏿⚖️ woman judge: dark skin tone
+1F469 1F3FF 200D 2696 ; non-fully-qualified # 👩🏿⚖ woman judge: dark skin tone
+1F468 200D 1F33E ; fully-qualified # 👨🌾 man farmer
+1F468 1F3FB 200D 1F33E ; fully-qualified # 👨🏻🌾 man farmer: light skin tone
+1F468 1F3FC 200D 1F33E ; fully-qualified # 👨🏼🌾 man farmer: medium-light skin tone
+1F468 1F3FD 200D 1F33E ; fully-qualified # 👨🏽🌾 man farmer: medium skin tone
+1F468 1F3FE 200D 1F33E ; fully-qualified # 👨🏾🌾 man farmer: medium-dark skin tone
+1F468 1F3FF 200D 1F33E ; fully-qualified # 👨🏿🌾 man farmer: dark skin tone
+1F469 200D 1F33E ; fully-qualified # 👩🌾 woman farmer
+1F469 1F3FB 200D 1F33E ; fully-qualified # 👩🏻🌾 woman farmer: light skin tone
+1F469 1F3FC 200D 1F33E ; fully-qualified # 👩🏼🌾 woman farmer: medium-light skin tone
+1F469 1F3FD 200D 1F33E ; fully-qualified # 👩🏽🌾 woman farmer: medium skin tone
+1F469 1F3FE 200D 1F33E ; fully-qualified # 👩🏾🌾 woman farmer: medium-dark skin tone
+1F469 1F3FF 200D 1F33E ; fully-qualified # 👩🏿🌾 woman farmer: dark skin tone
+1F468 200D 1F373 ; fully-qualified # 👨🍳 man cook
+1F468 1F3FB 200D 1F373 ; fully-qualified # 👨🏻🍳 man cook: light skin tone
+1F468 1F3FC 200D 1F373 ; fully-qualified # 👨🏼🍳 man cook: medium-light skin tone
+1F468 1F3FD 200D 1F373 ; fully-qualified # 👨🏽🍳 man cook: medium skin tone
+1F468 1F3FE 200D 1F373 ; fully-qualified # 👨🏾🍳 man cook: medium-dark skin tone
+1F468 1F3FF 200D 1F373 ; fully-qualified # 👨🏿🍳 man cook: dark skin tone
+1F469 200D 1F373 ; fully-qualified # 👩🍳 woman cook
+1F469 1F3FB 200D 1F373 ; fully-qualified # 👩🏻🍳 woman cook: light skin tone
+1F469 1F3FC 200D 1F373 ; fully-qualified # 👩🏼🍳 woman cook: medium-light skin tone
+1F469 1F3FD 200D 1F373 ; fully-qualified # 👩🏽🍳 woman cook: medium skin tone
+1F469 1F3FE 200D 1F373 ; fully-qualified # 👩🏾🍳 woman cook: medium-dark skin tone
+1F469 1F3FF 200D 1F373 ; fully-qualified # 👩🏿🍳 woman cook: dark skin tone
+1F468 200D 1F527 ; fully-qualified # 👨🔧 man mechanic
+1F468 1F3FB 200D 1F527 ; fully-qualified # 👨🏻🔧 man mechanic: light skin tone
+1F468 1F3FC 200D 1F527 ; fully-qualified # 👨🏼🔧 man mechanic: medium-light skin tone
+1F468 1F3FD 200D 1F527 ; fully-qualified # 👨🏽🔧 man mechanic: medium skin tone
+1F468 1F3FE 200D 1F527 ; fully-qualified # 👨🏾🔧 man mechanic: medium-dark skin tone
+1F468 1F3FF 200D 1F527 ; fully-qualified # 👨🏿🔧 man mechanic: dark skin tone
+1F469 200D 1F527 ; fully-qualified # 👩🔧 woman mechanic
+1F469 1F3FB 200D 1F527 ; fully-qualified # 👩🏻🔧 woman mechanic: light skin tone
+1F469 1F3FC 200D 1F527 ; fully-qualified # 👩🏼🔧 woman mechanic: medium-light skin tone
+1F469 1F3FD 200D 1F527 ; fully-qualified # 👩🏽🔧 woman mechanic: medium skin tone
+1F469 1F3FE 200D 1F527 ; fully-qualified # 👩🏾🔧 woman mechanic: medium-dark skin tone
+1F469 1F3FF 200D 1F527 ; fully-qualified # 👩🏿🔧 woman mechanic: dark skin tone
+1F468 200D 1F3ED ; fully-qualified # 👨🏭 man factory worker
+1F468 1F3FB 200D 1F3ED ; fully-qualified # 👨🏻🏭 man factory worker: light skin tone
+1F468 1F3FC 200D 1F3ED ; fully-qualified # 👨🏼🏭 man factory worker: medium-light skin tone
+1F468 1F3FD 200D 1F3ED ; fully-qualified # 👨🏽🏭 man factory worker: medium skin tone
+1F468 1F3FE 200D 1F3ED ; fully-qualified # 👨🏾🏭 man factory worker: medium-dark skin tone
+1F468 1F3FF 200D 1F3ED ; fully-qualified # 👨🏿🏭 man factory worker: dark skin tone
+1F469 200D 1F3ED ; fully-qualified # 👩🏭 woman factory worker
+1F469 1F3FB 200D 1F3ED ; fully-qualified # 👩🏻🏭 woman factory worker: light skin tone
+1F469 1F3FC 200D 1F3ED ; fully-qualified # 👩🏼🏭 woman factory worker: medium-light skin tone
+1F469 1F3FD 200D 1F3ED ; fully-qualified # 👩🏽🏭 woman factory worker: medium skin tone
+1F469 1F3FE 200D 1F3ED ; fully-qualified # 👩🏾🏭 woman factory worker: medium-dark skin tone
+1F469 1F3FF 200D 1F3ED ; fully-qualified # 👩🏿🏭 woman factory worker: dark skin tone
+1F468 200D 1F4BC ; fully-qualified # 👨💼 man office worker
+1F468 1F3FB 200D 1F4BC ; fully-qualified # 👨🏻💼 man office worker: light skin tone
+1F468 1F3FC 200D 1F4BC ; fully-qualified # 👨🏼💼 man office worker: medium-light skin tone
+1F468 1F3FD 200D 1F4BC ; fully-qualified # 👨🏽💼 man office worker: medium skin tone
+1F468 1F3FE 200D 1F4BC ; fully-qualified # 👨🏾💼 man office worker: medium-dark skin tone
+1F468 1F3FF 200D 1F4BC ; fully-qualified # 👨🏿💼 man office worker: dark skin tone
+1F469 200D 1F4BC ; fully-qualified # 👩💼 woman office worker
+1F469 1F3FB 200D 1F4BC ; fully-qualified # 👩🏻💼 woman office worker: light skin tone
+1F469 1F3FC 200D 1F4BC ; fully-qualified # 👩🏼💼 woman office worker: medium-light skin tone
+1F469 1F3FD 200D 1F4BC ; fully-qualified # 👩🏽💼 woman office worker: medium skin tone
+1F469 1F3FE 200D 1F4BC ; fully-qualified # 👩🏾💼 woman office worker: medium-dark skin tone
+1F469 1F3FF 200D 1F4BC ; fully-qualified # 👩🏿💼 woman office worker: dark skin tone
+1F468 200D 1F52C ; fully-qualified # 👨🔬 man scientist
+1F468 1F3FB 200D 1F52C ; fully-qualified # 👨🏻🔬 man scientist: light skin tone
+1F468 1F3FC 200D 1F52C ; fully-qualified # 👨🏼🔬 man scientist: medium-light skin tone
+1F468 1F3FD 200D 1F52C ; fully-qualified # 👨🏽🔬 man scientist: medium skin tone
+1F468 1F3FE 200D 1F52C ; fully-qualified # 👨🏾🔬 man scientist: medium-dark skin tone
+1F468 1F3FF 200D 1F52C ; fully-qualified # 👨🏿🔬 man scientist: dark skin tone
+1F469 200D 1F52C ; fully-qualified # 👩🔬 woman scientist
+1F469 1F3FB 200D 1F52C ; fully-qualified # 👩🏻🔬 woman scientist: light skin tone
+1F469 1F3FC 200D 1F52C ; fully-qualified # 👩🏼🔬 woman scientist: medium-light skin tone
+1F469 1F3FD 200D 1F52C ; fully-qualified # 👩🏽🔬 woman scientist: medium skin tone
+1F469 1F3FE 200D 1F52C ; fully-qualified # 👩🏾🔬 woman scientist: medium-dark skin tone
+1F469 1F3FF 200D 1F52C ; fully-qualified # 👩🏿🔬 woman scientist: dark skin tone
+1F468 200D 1F4BB ; fully-qualified # 👨💻 man technologist
+1F468 1F3FB 200D 1F4BB ; fully-qualified # 👨🏻💻 man technologist: light skin tone
+1F468 1F3FC 200D 1F4BB ; fully-qualified # 👨🏼💻 man technologist: medium-light skin tone
+1F468 1F3FD 200D 1F4BB ; fully-qualified # 👨🏽💻 man technologist: medium skin tone
+1F468 1F3FE 200D 1F4BB ; fully-qualified # 👨🏾💻 man technologist: medium-dark skin tone
+1F468 1F3FF 200D 1F4BB ; fully-qualified # 👨🏿💻 man technologist: dark skin tone
+1F469 200D 1F4BB ; fully-qualified # 👩💻 woman technologist
+1F469 1F3FB 200D 1F4BB ; fully-qualified # 👩🏻💻 woman technologist: light skin tone
+1F469 1F3FC 200D 1F4BB ; fully-qualified # 👩🏼💻 woman technologist: medium-light skin tone
+1F469 1F3FD 200D 1F4BB ; fully-qualified # 👩🏽💻 woman technologist: medium skin tone
+1F469 1F3FE 200D 1F4BB ; fully-qualified # 👩🏾💻 woman technologist: medium-dark skin tone
+1F469 1F3FF 200D 1F4BB ; fully-qualified # 👩🏿💻 woman technologist: dark skin tone
+1F468 200D 1F3A4 ; fully-qualified # 👨🎤 man singer
+1F468 1F3FB 200D 1F3A4 ; fully-qualified # 👨🏻🎤 man singer: light skin tone
+1F468 1F3FC 200D 1F3A4 ; fully-qualified # 👨🏼🎤 man singer: medium-light skin tone
+1F468 1F3FD 200D 1F3A4 ; fully-qualified # 👨🏽🎤 man singer: medium skin tone
+1F468 1F3FE 200D 1F3A4 ; fully-qualified # 👨🏾🎤 man singer: medium-dark skin tone
+1F468 1F3FF 200D 1F3A4 ; fully-qualified # 👨🏿🎤 man singer: dark skin tone
+1F469 200D 1F3A4 ; fully-qualified # 👩🎤 woman singer
+1F469 1F3FB 200D 1F3A4 ; fully-qualified # 👩🏻🎤 woman singer: light skin tone
+1F469 1F3FC 200D 1F3A4 ; fully-qualified # 👩🏼🎤 woman singer: medium-light skin tone
+1F469 1F3FD 200D 1F3A4 ; fully-qualified # 👩🏽🎤 woman singer: medium skin tone
+1F469 1F3FE 200D 1F3A4 ; fully-qualified # 👩🏾🎤 woman singer: medium-dark skin tone
+1F469 1F3FF 200D 1F3A4 ; fully-qualified # 👩🏿🎤 woman singer: dark skin tone
+1F468 200D 1F3A8 ; fully-qualified # 👨🎨 man artist
+1F468 1F3FB 200D 1F3A8 ; fully-qualified # 👨🏻🎨 man artist: light skin tone
+1F468 1F3FC 200D 1F3A8 ; fully-qualified # 👨🏼🎨 man artist: medium-light skin tone
+1F468 1F3FD 200D 1F3A8 ; fully-qualified # 👨🏽🎨 man artist: medium skin tone
+1F468 1F3FE 200D 1F3A8 ; fully-qualified # 👨🏾🎨 man artist: medium-dark skin tone
+1F468 1F3FF 200D 1F3A8 ; fully-qualified # 👨🏿🎨 man artist: dark skin tone
+1F469 200D 1F3A8 ; fully-qualified # 👩🎨 woman artist
+1F469 1F3FB 200D 1F3A8 ; fully-qualified # 👩🏻🎨 woman artist: light skin tone
+1F469 1F3FC 200D 1F3A8 ; fully-qualified # 👩🏼🎨 woman artist: medium-light skin tone
+1F469 1F3FD 200D 1F3A8 ; fully-qualified # 👩🏽🎨 woman artist: medium skin tone
+1F469 1F3FE 200D 1F3A8 ; fully-qualified # 👩🏾🎨 woman artist: medium-dark skin tone
+1F469 1F3FF 200D 1F3A8 ; fully-qualified # 👩🏿🎨 woman artist: dark skin tone
+1F468 200D 2708 FE0F ; fully-qualified # 👨✈️ man pilot
+1F468 200D 2708 ; non-fully-qualified # 👨✈ man pilot
+1F468 1F3FB 200D 2708 FE0F ; fully-qualified # 👨🏻✈️ man pilot: light skin tone
+1F468 1F3FB 200D 2708 ; non-fully-qualified # 👨🏻✈ man pilot: light skin tone
+1F468 1F3FC 200D 2708 FE0F ; fully-qualified # 👨🏼✈️ man pilot: medium-light skin tone
+1F468 1F3FC 200D 2708 ; non-fully-qualified # 👨🏼✈ man pilot: medium-light skin tone
+1F468 1F3FD 200D 2708 FE0F ; fully-qualified # 👨🏽✈️ man pilot: medium skin tone
+1F468 1F3FD 200D 2708 ; non-fully-qualified # 👨🏽✈ man pilot: medium skin tone
+1F468 1F3FE 200D 2708 FE0F ; fully-qualified # 👨🏾✈️ man pilot: medium-dark skin tone
+1F468 1F3FE 200D 2708 ; non-fully-qualified # 👨🏾✈ man pilot: medium-dark skin tone
+1F468 1F3FF 200D 2708 FE0F ; fully-qualified # 👨🏿✈️ man pilot: dark skin tone
+1F468 1F3FF 200D 2708 ; non-fully-qualified # 👨🏿✈ man pilot: dark skin tone
+1F469 200D 2708 FE0F ; fully-qualified # 👩✈️ woman pilot
+1F469 200D 2708 ; non-fully-qualified # 👩✈ woman pilot
+1F469 1F3FB 200D 2708 FE0F ; fully-qualified # 👩🏻✈️ woman pilot: light skin tone
+1F469 1F3FB 200D 2708 ; non-fully-qualified # 👩🏻✈ woman pilot: light skin tone
+1F469 1F3FC 200D 2708 FE0F ; fully-qualified # 👩🏼✈️ woman pilot: medium-light skin tone
+1F469 1F3FC 200D 2708 ; non-fully-qualified # 👩🏼✈ woman pilot: medium-light skin tone
+1F469 1F3FD 200D 2708 FE0F ; fully-qualified # 👩🏽✈️ woman pilot: medium skin tone
+1F469 1F3FD 200D 2708 ; non-fully-qualified # 👩🏽✈ woman pilot: medium skin tone
+1F469 1F3FE 200D 2708 FE0F ; fully-qualified # 👩🏾✈️ woman pilot: medium-dark skin tone
+1F469 1F3FE 200D 2708 ; non-fully-qualified # 👩🏾✈ woman pilot: medium-dark skin tone
+1F469 1F3FF 200D 2708 FE0F ; fully-qualified # 👩🏿✈️ woman pilot: dark skin tone
+1F469 1F3FF 200D 2708 ; non-fully-qualified # 👩🏿✈ woman pilot: dark skin tone
+1F468 200D 1F680 ; fully-qualified # 👨🚀 man astronaut
+1F468 1F3FB 200D 1F680 ; fully-qualified # 👨🏻🚀 man astronaut: light skin tone
+1F468 1F3FC 200D 1F680 ; fully-qualified # 👨🏼🚀 man astronaut: medium-light skin tone
+1F468 1F3FD 200D 1F680 ; fully-qualified # 👨🏽🚀 man astronaut: medium skin tone
+1F468 1F3FE 200D 1F680 ; fully-qualified # 👨🏾🚀 man astronaut: medium-dark skin tone
+1F468 1F3FF 200D 1F680 ; fully-qualified # 👨🏿🚀 man astronaut: dark skin tone
+1F469 200D 1F680 ; fully-qualified # 👩🚀 woman astronaut
+1F469 1F3FB 200D 1F680 ; fully-qualified # 👩🏻🚀 woman astronaut: light skin tone
+1F469 1F3FC 200D 1F680 ; fully-qualified # 👩🏼🚀 woman astronaut: medium-light skin tone
+1F469 1F3FD 200D 1F680 ; fully-qualified # 👩🏽🚀 woman astronaut: medium skin tone
+1F469 1F3FE 200D 1F680 ; fully-qualified # 👩🏾🚀 woman astronaut: medium-dark skin tone
+1F469 1F3FF 200D 1F680 ; fully-qualified # 👩🏿🚀 woman astronaut: dark skin tone
+1F468 200D 1F692 ; fully-qualified # 👨🚒 man firefighter
+1F468 1F3FB 200D 1F692 ; fully-qualified # 👨🏻🚒 man firefighter: light skin tone
+1F468 1F3FC 200D 1F692 ; fully-qualified # 👨🏼🚒 man firefighter: medium-light skin tone
+1F468 1F3FD 200D 1F692 ; fully-qualified # 👨🏽🚒 man firefighter: medium skin tone
+1F468 1F3FE 200D 1F692 ; fully-qualified # 👨🏾🚒 man firefighter: medium-dark skin tone
+1F468 1F3FF 200D 1F692 ; fully-qualified # 👨🏿🚒 man firefighter: dark skin tone
+1F469 200D 1F692 ; fully-qualified # 👩🚒 woman firefighter
+1F469 1F3FB 200D 1F692 ; fully-qualified # 👩🏻🚒 woman firefighter: light skin tone
+1F469 1F3FC 200D 1F692 ; fully-qualified # 👩🏼🚒 woman firefighter: medium-light skin tone
+1F469 1F3FD 200D 1F692 ; fully-qualified # 👩🏽🚒 woman firefighter: medium skin tone
+1F469 1F3FE 200D 1F692 ; fully-qualified # 👩🏾🚒 woman firefighter: medium-dark skin tone
+1F469 1F3FF 200D 1F692 ; fully-qualified # 👩🏿🚒 woman firefighter: dark skin tone
+1F46E ; fully-qualified # 👮 police officer
+1F46E 1F3FB ; fully-qualified # 👮🏻 police officer: light skin tone
+1F46E 1F3FC ; fully-qualified # 👮🏼 police officer: medium-light skin tone
+1F46E 1F3FD ; fully-qualified # 👮🏽 police officer: medium skin tone
+1F46E 1F3FE ; fully-qualified # 👮🏾 police officer: medium-dark skin tone
+1F46E 1F3FF ; fully-qualified # 👮🏿 police officer: dark skin tone
+1F46E 200D 2642 FE0F ; fully-qualified # 👮♂️ man police officer
+1F46E 200D 2642 ; non-fully-qualified # 👮♂ man police officer
+1F46E 1F3FB 200D 2642 FE0F ; fully-qualified # 👮🏻♂️ man police officer: light skin tone
+1F46E 1F3FB 200D 2642 ; non-fully-qualified # 👮🏻♂ man police officer: light skin tone
+1F46E 1F3FC 200D 2642 FE0F ; fully-qualified # 👮🏼♂️ man police officer: medium-light skin tone
+1F46E 1F3FC 200D 2642 ; non-fully-qualified # 👮🏼♂ man police officer: medium-light skin tone
+1F46E 1F3FD 200D 2642 FE0F ; fully-qualified # 👮🏽♂️ man police officer: medium skin tone
+1F46E 1F3FD 200D 2642 ; non-fully-qualified # 👮🏽♂ man police officer: medium skin tone
+1F46E 1F3FE 200D 2642 FE0F ; fully-qualified # 👮🏾♂️ man police officer: medium-dark skin tone
+1F46E 1F3FE 200D 2642 ; non-fully-qualified # 👮🏾♂ man police officer: medium-dark skin tone
+1F46E 1F3FF 200D 2642 FE0F ; fully-qualified # 👮🏿♂️ man police officer: dark skin tone
+1F46E 1F3FF 200D 2642 ; non-fully-qualified # 👮🏿♂ man police officer: dark skin tone
+1F46E 200D 2640 FE0F ; fully-qualified # 👮♀️ woman police officer
+1F46E 200D 2640 ; non-fully-qualified # 👮♀ woman police officer
+1F46E 1F3FB 200D 2640 FE0F ; fully-qualified # 👮🏻♀️ woman police officer: light skin tone
+1F46E 1F3FB 200D 2640 ; non-fully-qualified # 👮🏻♀ woman police officer: light skin tone
+1F46E 1F3FC 200D 2640 FE0F ; fully-qualified # 👮🏼♀️ woman police officer: medium-light skin tone
+1F46E 1F3FC 200D 2640 ; non-fully-qualified # 👮🏼♀ woman police officer: medium-light skin tone
+1F46E 1F3FD 200D 2640 FE0F ; fully-qualified # 👮🏽♀️ woman police officer: medium skin tone
+1F46E 1F3FD 200D 2640 ; non-fully-qualified # 👮🏽♀ woman police officer: medium skin tone
+1F46E 1F3FE 200D 2640 FE0F ; fully-qualified # 👮🏾♀️ woman police officer: medium-dark skin tone
+1F46E 1F3FE 200D 2640 ; non-fully-qualified # 👮🏾♀ woman police officer: medium-dark skin tone
+1F46E 1F3FF 200D 2640 FE0F ; fully-qualified # 👮🏿♀️ woman police officer: dark skin tone
+1F46E 1F3FF 200D 2640 ; non-fully-qualified # 👮🏿♀ woman police officer: dark skin tone
+1F575 FE0F ; fully-qualified # 🕵️ detective
+1F575 1F3FB ; fully-qualified # 🕵🏻 detective: light skin tone
+1F575 1F3FC ; fully-qualified # 🕵🏼 detective: medium-light skin tone
+1F575 1F3FD ; fully-qualified # 🕵🏽 detective: medium skin tone
+1F575 1F3FE ; fully-qualified # 🕵🏾 detective: medium-dark skin tone
+1F575 1F3FF ; fully-qualified # 🕵🏿 detective: dark skin tone
+1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️♂️ man detective
+1F575 200D 2642 ; non-fully-qualified # 🕵♂ man detective
+1F575 FE0F 200D 2642 ; non-fully-qualified # 🕵️♂ man detective
+1F575 200D 2642 FE0F ; non-fully-qualified # 🕵♂️ man detective
+1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻♂️ man detective: light skin tone
+1F575 1F3FB 200D 2642 ; non-fully-qualified # 🕵🏻♂ man detective: light skin tone
+1F575 1F3FC 200D 2642 FE0F ; fully-qualified # 🕵🏼♂️ man detective: medium-light skin tone
+1F575 1F3FC 200D 2642 ; non-fully-qualified # 🕵🏼♂ man detective: medium-light skin tone
+1F575 1F3FD 200D 2642 FE0F ; fully-qualified # 🕵🏽♂️ man detective: medium skin tone
+1F575 1F3FD 200D 2642 ; non-fully-qualified # 🕵🏽♂ man detective: medium skin tone
+1F575 1F3FE 200D 2642 FE0F ; fully-qualified # 🕵🏾♂️ man detective: medium-dark skin tone
+1F575 1F3FE 200D 2642 ; non-fully-qualified # 🕵🏾♂ man detective: medium-dark skin tone
+1F575 1F3FF 200D 2642 FE0F ; fully-qualified # 🕵🏿♂️ man detective: dark skin tone
+1F575 1F3FF 200D 2642 ; non-fully-qualified # 🕵🏿♂ man detective: dark skin tone
+1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️♀️ woman detective
+1F575 200D 2640 ; non-fully-qualified # 🕵♀ woman detective
+1F575 FE0F 200D 2640 ; non-fully-qualified # 🕵️♀ woman detective
+1F575 200D 2640 FE0F ; non-fully-qualified # 🕵♀️ woman detective
+1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻♀️ woman detective: light skin tone
+1F575 1F3FB 200D 2640 ; non-fully-qualified # 🕵🏻♀ woman detective: light skin tone
+1F575 1F3FC 200D 2640 FE0F ; fully-qualified # 🕵🏼♀️ woman detective: medium-light skin tone
+1F575 1F3FC 200D 2640 ; non-fully-qualified # 🕵🏼♀ woman detective: medium-light skin tone
+1F575 1F3FD 200D 2640 FE0F ; fully-qualified # 🕵🏽♀️ woman detective: medium skin tone
+1F575 1F3FD 200D 2640 ; non-fully-qualified # 🕵🏽♀ woman detective: medium skin tone
+1F575 1F3FE 200D 2640 FE0F ; fully-qualified # 🕵🏾♀️ woman detective: medium-dark skin tone
+1F575 1F3FE 200D 2640 ; non-fully-qualified # 🕵🏾♀ woman detective: medium-dark skin tone
+1F575 1F3FF 200D 2640 FE0F ; fully-qualified # 🕵🏿♀️ woman detective: dark skin tone
+1F575 1F3FF 200D 2640 ; non-fully-qualified # 🕵🏿♀ woman detective: dark skin tone
+1F482 ; fully-qualified # 💂 guard
+1F482 1F3FB ; fully-qualified # 💂🏻 guard: light skin tone
+1F482 1F3FC ; fully-qualified # 💂🏼 guard: medium-light skin tone
+1F482 1F3FD ; fully-qualified # 💂🏽 guard: medium skin tone
+1F482 1F3FE ; fully-qualified # 💂🏾 guard: medium-dark skin tone
+1F482 1F3FF ; fully-qualified # 💂🏿 guard: dark skin tone
+1F482 200D 2642 FE0F ; fully-qualified # 💂♂️ man guard
+1F482 200D 2642 ; non-fully-qualified # 💂♂ man guard
+1F482 1F3FB 200D 2642 FE0F ; fully-qualified # 💂🏻♂️ man guard: light skin tone
+1F482 1F3FB 200D 2642 ; non-fully-qualified # 💂🏻♂ man guard: light skin tone
+1F482 1F3FC 200D 2642 FE0F ; fully-qualified # 💂🏼♂️ man guard: medium-light skin tone
+1F482 1F3FC 200D 2642 ; non-fully-qualified # 💂🏼♂ man guard: medium-light skin tone
+1F482 1F3FD 200D 2642 FE0F ; fully-qualified # 💂🏽♂️ man guard: medium skin tone
+1F482 1F3FD 200D 2642 ; non-fully-qualified # 💂🏽♂ man guard: medium skin tone
+1F482 1F3FE 200D 2642 FE0F ; fully-qualified # 💂🏾♂️ man guard: medium-dark skin tone
+1F482 1F3FE 200D 2642 ; non-fully-qualified # 💂🏾♂ man guard: medium-dark skin tone
+1F482 1F3FF 200D 2642 FE0F ; fully-qualified # 💂🏿♂️ man guard: dark skin tone
+1F482 1F3FF 200D 2642 ; non-fully-qualified # 💂🏿♂ man guard: dark skin tone
+1F482 200D 2640 FE0F ; fully-qualified # 💂♀️ woman guard
+1F482 200D 2640 ; non-fully-qualified # 💂♀ woman guard
+1F482 1F3FB 200D 2640 FE0F ; fully-qualified # 💂🏻♀️ woman guard: light skin tone
+1F482 1F3FB 200D 2640 ; non-fully-qualified # 💂🏻♀ woman guard: light skin tone
+1F482 1F3FC 200D 2640 FE0F ; fully-qualified # 💂🏼♀️ woman guard: medium-light skin tone
+1F482 1F3FC 200D 2640 ; non-fully-qualified # 💂🏼♀ woman guard: medium-light skin tone
+1F482 1F3FD 200D 2640 FE0F ; fully-qualified # 💂🏽♀️ woman guard: medium skin tone
+1F482 1F3FD 200D 2640 ; non-fully-qualified # 💂🏽♀ woman guard: medium skin tone
+1F482 1F3FE 200D 2640 FE0F ; fully-qualified # 💂🏾♀️ woman guard: medium-dark skin tone
+1F482 1F3FE 200D 2640 ; non-fully-qualified # 💂🏾♀ woman guard: medium-dark skin tone
+1F482 1F3FF 200D 2640 FE0F ; fully-qualified # 💂🏿♀️ woman guard: dark skin tone
+1F482 1F3FF 200D 2640 ; non-fully-qualified # 💂🏿♀ woman guard: dark skin tone
+1F477 ; fully-qualified # 👷 construction worker
+1F477 1F3FB ; fully-qualified # 👷🏻 construction worker: light skin tone
+1F477 1F3FC ; fully-qualified # 👷🏼 construction worker: medium-light skin tone
+1F477 1F3FD ; fully-qualified # 👷🏽 construction worker: medium skin tone
+1F477 1F3FE ; fully-qualified # 👷🏾 construction worker: medium-dark skin tone
+1F477 1F3FF ; fully-qualified # 👷🏿 construction worker: dark skin tone
+1F477 200D 2642 FE0F ; fully-qualified # 👷♂️ man construction worker
+1F477 200D 2642 ; non-fully-qualified # 👷♂ man construction worker
+1F477 1F3FB 200D 2642 FE0F ; fully-qualified # 👷🏻♂️ man construction worker: light skin tone
+1F477 1F3FB 200D 2642 ; non-fully-qualified # 👷🏻♂ man construction worker: light skin tone
+1F477 1F3FC 200D 2642 FE0F ; fully-qualified # 👷🏼♂️ man construction worker: medium-light skin tone
+1F477 1F3FC 200D 2642 ; non-fully-qualified # 👷🏼♂ man construction worker: medium-light skin tone
+1F477 1F3FD 200D 2642 FE0F ; fully-qualified # 👷🏽♂️ man construction worker: medium skin tone
+1F477 1F3FD 200D 2642 ; non-fully-qualified # 👷🏽♂ man construction worker: medium skin tone
+1F477 1F3FE 200D 2642 FE0F ; fully-qualified # 👷🏾♂️ man construction worker: medium-dark skin tone
+1F477 1F3FE 200D 2642 ; non-fully-qualified # 👷🏾♂ man construction worker: medium-dark skin tone
+1F477 1F3FF 200D 2642 FE0F ; fully-qualified # 👷🏿♂️ man construction worker: dark skin tone
+1F477 1F3FF 200D 2642 ; non-fully-qualified # 👷🏿♂ man construction worker: dark skin tone
+1F477 200D 2640 FE0F ; fully-qualified # 👷♀️ woman construction worker
+1F477 200D 2640 ; non-fully-qualified # 👷♀ woman construction worker
+1F477 1F3FB 200D 2640 FE0F ; fully-qualified # 👷🏻♀️ woman construction worker: light skin tone
+1F477 1F3FB 200D 2640 ; non-fully-qualified # 👷🏻♀ woman construction worker: light skin tone
+1F477 1F3FC 200D 2640 FE0F ; fully-qualified # 👷🏼♀️ woman construction worker: medium-light skin tone
+1F477 1F3FC 200D 2640 ; non-fully-qualified # 👷🏼♀ woman construction worker: medium-light skin tone
+1F477 1F3FD 200D 2640 FE0F ; fully-qualified # 👷🏽♀️ woman construction worker: medium skin tone
+1F477 1F3FD 200D 2640 ; non-fully-qualified # 👷🏽♀ woman construction worker: medium skin tone
+1F477 1F3FE 200D 2640 FE0F ; fully-qualified # 👷🏾♀️ woman construction worker: medium-dark skin tone
+1F477 1F3FE 200D 2640 ; non-fully-qualified # 👷🏾♀ woman construction worker: medium-dark skin tone
+1F477 1F3FF 200D 2640 FE0F ; fully-qualified # 👷🏿♀️ woman construction worker: dark skin tone
+1F477 1F3FF 200D 2640 ; non-fully-qualified # 👷🏿♀ woman construction worker: dark skin tone
+1F473 ; fully-qualified # 👳 person wearing turban
+1F473 1F3FB ; fully-qualified # 👳🏻 person wearing turban: light skin tone
+1F473 1F3FC ; fully-qualified # 👳🏼 person wearing turban: medium-light skin tone
+1F473 1F3FD ; fully-qualified # 👳🏽 person wearing turban: medium skin tone
+1F473 1F3FE ; fully-qualified # 👳🏾 person wearing turban: medium-dark skin tone
+1F473 1F3FF ; fully-qualified # 👳🏿 person wearing turban: dark skin tone
+1F473 200D 2642 FE0F ; fully-qualified # 👳♂️ man wearing turban
+1F473 200D 2642 ; non-fully-qualified # 👳♂ man wearing turban
+1F473 1F3FB 200D 2642 FE0F ; fully-qualified # 👳🏻♂️ man wearing turban: light skin tone
+1F473 1F3FB 200D 2642 ; non-fully-qualified # 👳🏻♂ man wearing turban: light skin tone
+1F473 1F3FC 200D 2642 FE0F ; fully-qualified # 👳🏼♂️ man wearing turban: medium-light skin tone
+1F473 1F3FC 200D 2642 ; non-fully-qualified # 👳🏼♂ man wearing turban: medium-light skin tone
+1F473 1F3FD 200D 2642 FE0F ; fully-qualified # 👳🏽♂️ man wearing turban: medium skin tone
+1F473 1F3FD 200D 2642 ; non-fully-qualified # 👳🏽♂ man wearing turban: medium skin tone
+1F473 1F3FE 200D 2642 FE0F ; fully-qualified # 👳🏾♂️ man wearing turban: medium-dark skin tone
+1F473 1F3FE 200D 2642 ; non-fully-qualified # 👳🏾♂ man wearing turban: medium-dark skin tone
+1F473 1F3FF 200D 2642 FE0F ; fully-qualified # 👳🏿♂️ man wearing turban: dark skin tone
+1F473 1F3FF 200D 2642 ; non-fully-qualified # 👳🏿♂ man wearing turban: dark skin tone
+1F473 200D 2640 FE0F ; fully-qualified # 👳♀️ woman wearing turban
+1F473 200D 2640 ; non-fully-qualified # 👳♀ woman wearing turban
+1F473 1F3FB 200D 2640 FE0F ; fully-qualified # 👳🏻♀️ woman wearing turban: light skin tone
+1F473 1F3FB 200D 2640 ; non-fully-qualified # 👳🏻♀ woman wearing turban: light skin tone
+1F473 1F3FC 200D 2640 FE0F ; fully-qualified # 👳🏼♀️ woman wearing turban: medium-light skin tone
+1F473 1F3FC 200D 2640 ; non-fully-qualified # 👳🏼♀ woman wearing turban: medium-light skin tone
+1F473 1F3FD 200D 2640 FE0F ; fully-qualified # 👳🏽♀️ woman wearing turban: medium skin tone
+1F473 1F3FD 200D 2640 ; non-fully-qualified # 👳🏽♀ woman wearing turban: medium skin tone
+1F473 1F3FE 200D 2640 FE0F ; fully-qualified # 👳🏾♀️ woman wearing turban: medium-dark skin tone
+1F473 1F3FE 200D 2640 ; non-fully-qualified # 👳🏾♀ woman wearing turban: medium-dark skin tone
+1F473 1F3FF 200D 2640 FE0F ; fully-qualified # 👳🏿♀️ woman wearing turban: dark skin tone
+1F473 1F3FF 200D 2640 ; non-fully-qualified # 👳🏿♀ woman wearing turban: dark skin tone
+1F471 ; fully-qualified # 👱 blond-haired person
+1F471 1F3FB ; fully-qualified # 👱🏻 blond-haired person: light skin tone
+1F471 1F3FC ; fully-qualified # 👱🏼 blond-haired person: medium-light skin tone
+1F471 1F3FD ; fully-qualified # 👱🏽 blond-haired person: medium skin tone
+1F471 1F3FE ; fully-qualified # 👱🏾 blond-haired person: medium-dark skin tone
+1F471 1F3FF ; fully-qualified # 👱🏿 blond-haired person: dark skin tone
+1F471 200D 2642 FE0F ; fully-qualified # 👱♂️ blond-haired man
+1F471 200D 2642 ; non-fully-qualified # 👱♂ blond-haired man
+1F471 1F3FB 200D 2642 FE0F ; fully-qualified # 👱🏻♂️ blond-haired man: light skin tone
+1F471 1F3FB 200D 2642 ; non-fully-qualified # 👱🏻♂ blond-haired man: light skin tone
+1F471 1F3FC 200D 2642 FE0F ; fully-qualified # 👱🏼♂️ blond-haired man: medium-light skin tone
+1F471 1F3FC 200D 2642 ; non-fully-qualified # 👱🏼♂ blond-haired man: medium-light skin tone
+1F471 1F3FD 200D 2642 FE0F ; fully-qualified # 👱🏽♂️ blond-haired man: medium skin tone
+1F471 1F3FD 200D 2642 ; non-fully-qualified # 👱🏽♂ blond-haired man: medium skin tone
+1F471 1F3FE 200D 2642 FE0F ; fully-qualified # 👱🏾♂️ blond-haired man: medium-dark skin tone
+1F471 1F3FE 200D 2642 ; non-fully-qualified # 👱🏾♂ blond-haired man: medium-dark skin tone
+1F471 1F3FF 200D 2642 FE0F ; fully-qualified # 👱🏿♂️ blond-haired man: dark skin tone
+1F471 1F3FF 200D 2642 ; non-fully-qualified # 👱🏿♂ blond-haired man: dark skin tone
+1F471 200D 2640 FE0F ; fully-qualified # 👱♀️ blond-haired woman
+1F471 200D 2640 ; non-fully-qualified # 👱♀ blond-haired woman
+1F471 1F3FB 200D 2640 FE0F ; fully-qualified # 👱🏻♀️ blond-haired woman: light skin tone
+1F471 1F3FB 200D 2640 ; non-fully-qualified # 👱🏻♀ blond-haired woman: light skin tone
+1F471 1F3FC 200D 2640 FE0F ; fully-qualified # 👱🏼♀️ blond-haired woman: medium-light skin tone
+1F471 1F3FC 200D 2640 ; non-fully-qualified # 👱🏼♀ blond-haired woman: medium-light skin tone
+1F471 1F3FD 200D 2640 FE0F ; fully-qualified # 👱🏽♀️ blond-haired woman: medium skin tone
+1F471 1F3FD 200D 2640 ; non-fully-qualified # 👱🏽♀ blond-haired woman: medium skin tone
+1F471 1F3FE 200D 2640 FE0F ; fully-qualified # 👱🏾♀️ blond-haired woman: medium-dark skin tone
+1F471 1F3FE 200D 2640 ; non-fully-qualified # 👱🏾♀ blond-haired woman: medium-dark skin tone
+1F471 1F3FF 200D 2640 FE0F ; fully-qualified # 👱🏿♀️ blond-haired woman: dark skin tone
+1F471 1F3FF 200D 2640 ; non-fully-qualified # 👱🏿♀ blond-haired woman: dark skin tone
+1F385 ; fully-qualified # 🎅 Santa Claus
+1F385 1F3FB ; fully-qualified # 🎅🏻 Santa Claus: light skin tone
+1F385 1F3FC ; fully-qualified # 🎅🏼 Santa Claus: medium-light skin tone
+1F385 1F3FD ; fully-qualified # 🎅🏽 Santa Claus: medium skin tone
+1F385 1F3FE ; fully-qualified # 🎅🏾 Santa Claus: medium-dark skin tone
+1F385 1F3FF ; fully-qualified # 🎅🏿 Santa Claus: dark skin tone
+1F936 ; fully-qualified # 🤶 Mrs. Claus
+1F936 1F3FB ; fully-qualified # 🤶🏻 Mrs. Claus: light skin tone
+1F936 1F3FC ; fully-qualified # 🤶🏼 Mrs. Claus: medium-light skin tone
+1F936 1F3FD ; fully-qualified # 🤶🏽 Mrs. Claus: medium skin tone
+1F936 1F3FE ; fully-qualified # 🤶🏾 Mrs. Claus: medium-dark skin tone
+1F936 1F3FF ; fully-qualified # 🤶🏿 Mrs. Claus: dark skin tone
+1F478 ; fully-qualified # 👸 princess
+1F478 1F3FB ; fully-qualified # 👸🏻 princess: light skin tone
+1F478 1F3FC ; fully-qualified # 👸🏼 princess: medium-light skin tone
+1F478 1F3FD ; fully-qualified # 👸🏽 princess: medium skin tone
+1F478 1F3FE ; fully-qualified # 👸🏾 princess: medium-dark skin tone
+1F478 1F3FF ; fully-qualified # 👸🏿 princess: dark skin tone
+1F934 ; fully-qualified # 🤴 prince
+1F934 1F3FB ; fully-qualified # 🤴🏻 prince: light skin tone
+1F934 1F3FC ; fully-qualified # 🤴🏼 prince: medium-light skin tone
+1F934 1F3FD ; fully-qualified # 🤴🏽 prince: medium skin tone
+1F934 1F3FE ; fully-qualified # 🤴🏾 prince: medium-dark skin tone
+1F934 1F3FF ; fully-qualified # 🤴🏿 prince: dark skin tone
+1F470 ; fully-qualified # 👰 bride with veil
+1F470 1F3FB ; fully-qualified # 👰🏻 bride with veil: light skin tone
+1F470 1F3FC ; fully-qualified # 👰🏼 bride with veil: medium-light skin tone
+1F470 1F3FD ; fully-qualified # 👰🏽 bride with veil: medium skin tone
+1F470 1F3FE ; fully-qualified # 👰🏾 bride with veil: medium-dark skin tone
+1F470 1F3FF ; fully-qualified # 👰🏿 bride with veil: dark skin tone
+1F935 ; fully-qualified # 🤵 man in tuxedo
+1F935 1F3FB ; fully-qualified # 🤵🏻 man in tuxedo: light skin tone
+1F935 1F3FC ; fully-qualified # 🤵🏼 man in tuxedo: medium-light skin tone
+1F935 1F3FD ; fully-qualified # 🤵🏽 man in tuxedo: medium skin tone
+1F935 1F3FE ; fully-qualified # 🤵🏾 man in tuxedo: medium-dark skin tone
+1F935 1F3FF ; fully-qualified # 🤵🏿 man in tuxedo: dark skin tone
+1F930 ; fully-qualified # 🤰 pregnant woman
+1F930 1F3FB ; fully-qualified # 🤰🏻 pregnant woman: light skin tone
+1F930 1F3FC ; fully-qualified # 🤰🏼 pregnant woman: medium-light skin tone
+1F930 1F3FD ; fully-qualified # 🤰🏽 pregnant woman: medium skin tone
+1F930 1F3FE ; fully-qualified # 🤰🏾 pregnant woman: medium-dark skin tone
+1F930 1F3FF ; fully-qualified # 🤰🏿 pregnant woman: dark skin tone
+1F472 ; fully-qualified # 👲 man with Chinese cap
+1F472 1F3FB ; fully-qualified # 👲🏻 man with Chinese cap: light skin tone
+1F472 1F3FC ; fully-qualified # 👲🏼 man with Chinese cap: medium-light skin tone
+1F472 1F3FD ; fully-qualified # 👲🏽 man with Chinese cap: medium skin tone
+1F472 1F3FE ; fully-qualified # 👲🏾 man with Chinese cap: medium-dark skin tone
+1F472 1F3FF ; fully-qualified # 👲🏿 man with Chinese cap: dark skin tone
+
+# subgroup: person-gesture
+1F64D ; fully-qualified # 🙍 person frowning
+1F64D 1F3FB ; fully-qualified # 🙍🏻 person frowning: light skin tone
+1F64D 1F3FC ; fully-qualified # 🙍🏼 person frowning: medium-light skin tone
+1F64D 1F3FD ; fully-qualified # 🙍🏽 person frowning: medium skin tone
+1F64D 1F3FE ; fully-qualified # 🙍🏾 person frowning: medium-dark skin tone
+1F64D 1F3FF ; fully-qualified # 🙍🏿 person frowning: dark skin tone
+1F64D 200D 2642 FE0F ; fully-qualified # 🙍♂️ man frowning
+1F64D 200D 2642 ; non-fully-qualified # 🙍♂ man frowning
+1F64D 1F3FB 200D 2642 FE0F ; fully-qualified # 🙍🏻♂️ man frowning: light skin tone
+1F64D 1F3FB 200D 2642 ; non-fully-qualified # 🙍🏻♂ man frowning: light skin tone
+1F64D 1F3FC 200D 2642 FE0F ; fully-qualified # 🙍🏼♂️ man frowning: medium-light skin tone
+1F64D 1F3FC 200D 2642 ; non-fully-qualified # 🙍🏼♂ man frowning: medium-light skin tone
+1F64D 1F3FD 200D 2642 FE0F ; fully-qualified # 🙍🏽♂️ man frowning: medium skin tone
+1F64D 1F3FD 200D 2642 ; non-fully-qualified # 🙍🏽♂ man frowning: medium skin tone
+1F64D 1F3FE 200D 2642 FE0F ; fully-qualified # 🙍🏾♂️ man frowning: medium-dark skin tone
+1F64D 1F3FE 200D 2642 ; non-fully-qualified # 🙍🏾♂ man frowning: medium-dark skin tone
+1F64D 1F3FF 200D 2642 FE0F ; fully-qualified # 🙍🏿♂️ man frowning: dark skin tone
+1F64D 1F3FF 200D 2642 ; non-fully-qualified # 🙍🏿♂ man frowning: dark skin tone
+1F64D 200D 2640 FE0F ; fully-qualified # 🙍♀️ woman frowning
+1F64D 200D 2640 ; non-fully-qualified # 🙍♀ woman frowning
+1F64D 1F3FB 200D 2640 FE0F ; fully-qualified # 🙍🏻♀️ woman frowning: light skin tone
+1F64D 1F3FB 200D 2640 ; non-fully-qualified # 🙍🏻♀ woman frowning: light skin tone
+1F64D 1F3FC 200D 2640 FE0F ; fully-qualified # 🙍🏼♀️ woman frowning: medium-light skin tone
+1F64D 1F3FC 200D 2640 ; non-fully-qualified # 🙍🏼♀ woman frowning: medium-light skin tone
+1F64D 1F3FD 200D 2640 FE0F ; fully-qualified # 🙍🏽♀️ woman frowning: medium skin tone
+1F64D 1F3FD 200D 2640 ; non-fully-qualified # 🙍🏽♀ woman frowning: medium skin tone
+1F64D 1F3FE 200D 2640 FE0F ; fully-qualified # 🙍🏾♀️ woman frowning: medium-dark skin tone
+1F64D 1F3FE 200D 2640 ; non-fully-qualified # 🙍🏾♀ woman frowning: medium-dark skin tone
+1F64D 1F3FF 200D 2640 FE0F ; fully-qualified # 🙍🏿♀️ woman frowning: dark skin tone
+1F64D 1F3FF 200D 2640 ; non-fully-qualified # 🙍🏿♀ woman frowning: dark skin tone
+1F64E ; fully-qualified # 🙎 person pouting
+1F64E 1F3FB ; fully-qualified # 🙎🏻 person pouting: light skin tone
+1F64E 1F3FC ; fully-qualified # 🙎🏼 person pouting: medium-light skin tone
+1F64E 1F3FD ; fully-qualified # 🙎🏽 person pouting: medium skin tone
+1F64E 1F3FE ; fully-qualified # 🙎🏾 person pouting: medium-dark skin tone
+1F64E 1F3FF ; fully-qualified # 🙎🏿 person pouting: dark skin tone
+1F64E 200D 2642 FE0F ; fully-qualified # 🙎♂️ man pouting
+1F64E 200D 2642 ; non-fully-qualified # 🙎♂ man pouting
+1F64E 1F3FB 200D 2642 FE0F ; fully-qualified # 🙎🏻♂️ man pouting: light skin tone
+1F64E 1F3FB 200D 2642 ; non-fully-qualified # 🙎🏻♂ man pouting: light skin tone
+1F64E 1F3FC 200D 2642 FE0F ; fully-qualified # 🙎🏼♂️ man pouting: medium-light skin tone
+1F64E 1F3FC 200D 2642 ; non-fully-qualified # 🙎🏼♂ man pouting: medium-light skin tone
+1F64E 1F3FD 200D 2642 FE0F ; fully-qualified # 🙎🏽♂️ man pouting: medium skin tone
+1F64E 1F3FD 200D 2642 ; non-fully-qualified # 🙎🏽♂ man pouting: medium skin tone
+1F64E 1F3FE 200D 2642 FE0F ; fully-qualified # 🙎🏾♂️ man pouting: medium-dark skin tone
+1F64E 1F3FE 200D 2642 ; non-fully-qualified # 🙎🏾♂ man pouting: medium-dark skin tone
+1F64E 1F3FF 200D 2642 FE0F ; fully-qualified # 🙎🏿♂️ man pouting: dark skin tone
+1F64E 1F3FF 200D 2642 ; non-fully-qualified # 🙎🏿♂ man pouting: dark skin tone
+1F64E 200D 2640 FE0F ; fully-qualified # 🙎♀️ woman pouting
+1F64E 200D 2640 ; non-fully-qualified # 🙎♀ woman pouting
+1F64E 1F3FB 200D 2640 FE0F ; fully-qualified # 🙎🏻♀️ woman pouting: light skin tone
+1F64E 1F3FB 200D 2640 ; non-fully-qualified # 🙎🏻♀ woman pouting: light skin tone
+1F64E 1F3FC 200D 2640 FE0F ; fully-qualified # 🙎🏼♀️ woman pouting: medium-light skin tone
+1F64E 1F3FC 200D 2640 ; non-fully-qualified # 🙎🏼♀ woman pouting: medium-light skin tone
+1F64E 1F3FD 200D 2640 FE0F ; fully-qualified # 🙎🏽♀️ woman pouting: medium skin tone
+1F64E 1F3FD 200D 2640 ; non-fully-qualified # 🙎🏽♀ woman pouting: medium skin tone
+1F64E 1F3FE 200D 2640 FE0F ; fully-qualified # 🙎🏾♀️ woman pouting: medium-dark skin tone
+1F64E 1F3FE 200D 2640 ; non-fully-qualified # 🙎🏾♀ woman pouting: medium-dark skin tone
+1F64E 1F3FF 200D 2640 FE0F ; fully-qualified # 🙎🏿♀️ woman pouting: dark skin tone
+1F64E 1F3FF 200D 2640 ; non-fully-qualified # 🙎🏿♀ woman pouting: dark skin tone
+1F645 ; fully-qualified # 🙅 person gesturing NO
+1F645 1F3FB ; fully-qualified # 🙅🏻 person gesturing NO: light skin tone
+1F645 1F3FC ; fully-qualified # 🙅🏼 person gesturing NO: medium-light skin tone
+1F645 1F3FD ; fully-qualified # 🙅🏽 person gesturing NO: medium skin tone
+1F645 1F3FE ; fully-qualified # 🙅🏾 person gesturing NO: medium-dark skin tone
+1F645 1F3FF ; fully-qualified # 🙅🏿 person gesturing NO: dark skin tone
+1F645 200D 2642 FE0F ; fully-qualified # 🙅♂️ man gesturing NO
+1F645 200D 2642 ; non-fully-qualified # 🙅♂ man gesturing NO
+1F645 1F3FB 200D 2642 FE0F ; fully-qualified # 🙅🏻♂️ man gesturing NO: light skin tone
+1F645 1F3FB 200D 2642 ; non-fully-qualified # 🙅🏻♂ man gesturing NO: light skin tone
+1F645 1F3FC 200D 2642 FE0F ; fully-qualified # 🙅🏼♂️ man gesturing NO: medium-light skin tone
+1F645 1F3FC 200D 2642 ; non-fully-qualified # 🙅🏼♂ man gesturing NO: medium-light skin tone
+1F645 1F3FD 200D 2642 FE0F ; fully-qualified # 🙅🏽♂️ man gesturing NO: medium skin tone
+1F645 1F3FD 200D 2642 ; non-fully-qualified # 🙅🏽♂ man gesturing NO: medium skin tone
+1F645 1F3FE 200D 2642 FE0F ; fully-qualified # 🙅🏾♂️ man gesturing NO: medium-dark skin tone
+1F645 1F3FE 200D 2642 ; non-fully-qualified # 🙅🏾♂ man gesturing NO: medium-dark skin tone
+1F645 1F3FF 200D 2642 FE0F ; fully-qualified # 🙅🏿♂️ man gesturing NO: dark skin tone
+1F645 1F3FF 200D 2642 ; non-fully-qualified # 🙅🏿♂ man gesturing NO: dark skin tone
+1F645 200D 2640 FE0F ; fully-qualified # 🙅♀️ woman gesturing NO
+1F645 200D 2640 ; non-fully-qualified # 🙅♀ woman gesturing NO
+1F645 1F3FB 200D 2640 FE0F ; fully-qualified # 🙅🏻♀️ woman gesturing NO: light skin tone
+1F645 1F3FB 200D 2640 ; non-fully-qualified # 🙅🏻♀ woman gesturing NO: light skin tone
+1F645 1F3FC 200D 2640 FE0F ; fully-qualified # 🙅🏼♀️ woman gesturing NO: medium-light skin tone
+1F645 1F3FC 200D 2640 ; non-fully-qualified # 🙅🏼♀ woman gesturing NO: medium-light skin tone
+1F645 1F3FD 200D 2640 FE0F ; fully-qualified # 🙅🏽♀️ woman gesturing NO: medium skin tone
+1F645 1F3FD 200D 2640 ; non-fully-qualified # 🙅🏽♀ woman gesturing NO: medium skin tone
+1F645 1F3FE 200D 2640 FE0F ; fully-qualified # 🙅🏾♀️ woman gesturing NO: medium-dark skin tone
+1F645 1F3FE 200D 2640 ; non-fully-qualified # 🙅🏾♀ woman gesturing NO: medium-dark skin tone
+1F645 1F3FF 200D 2640 FE0F ; fully-qualified # 🙅🏿♀️ woman gesturing NO: dark skin tone
+1F645 1F3FF 200D 2640 ; non-fully-qualified # 🙅🏿♀ woman gesturing NO: dark skin tone
+1F646 ; fully-qualified # 🙆 person gesturing OK
+1F646 1F3FB ; fully-qualified # 🙆🏻 person gesturing OK: light skin tone
+1F646 1F3FC ; fully-qualified # 🙆🏼 person gesturing OK: medium-light skin tone
+1F646 1F3FD ; fully-qualified # 🙆🏽 person gesturing OK: medium skin tone
+1F646 1F3FE ; fully-qualified # 🙆🏾 person gesturing OK: medium-dark skin tone
+1F646 1F3FF ; fully-qualified # 🙆🏿 person gesturing OK: dark skin tone
+1F646 200D 2642 FE0F ; fully-qualified # 🙆♂️ man gesturing OK
+1F646 200D 2642 ; non-fully-qualified # 🙆♂ man gesturing OK
+1F646 1F3FB 200D 2642 FE0F ; fully-qualified # 🙆🏻♂️ man gesturing OK: light skin tone
+1F646 1F3FB 200D 2642 ; non-fully-qualified # 🙆🏻♂ man gesturing OK: light skin tone
+1F646 1F3FC 200D 2642 FE0F ; fully-qualified # 🙆🏼♂️ man gesturing OK: medium-light skin tone
+1F646 1F3FC 200D 2642 ; non-fully-qualified # 🙆🏼♂ man gesturing OK: medium-light skin tone
+1F646 1F3FD 200D 2642 FE0F ; fully-qualified # 🙆🏽♂️ man gesturing OK: medium skin tone
+1F646 1F3FD 200D 2642 ; non-fully-qualified # 🙆🏽♂ man gesturing OK: medium skin tone
+1F646 1F3FE 200D 2642 FE0F ; fully-qualified # 🙆🏾♂️ man gesturing OK: medium-dark skin tone
+1F646 1F3FE 200D 2642 ; non-fully-qualified # 🙆🏾♂ man gesturing OK: medium-dark skin tone
+1F646 1F3FF 200D 2642 FE0F ; fully-qualified # 🙆🏿♂️ man gesturing OK: dark skin tone
+1F646 1F3FF 200D 2642 ; non-fully-qualified # 🙆🏿♂ man gesturing OK: dark skin tone
+1F646 200D 2640 FE0F ; fully-qualified # 🙆♀️ woman gesturing OK
+1F646 200D 2640 ; non-fully-qualified # 🙆♀ woman gesturing OK
+1F646 1F3FB 200D 2640 FE0F ; fully-qualified # 🙆🏻♀️ woman gesturing OK: light skin tone
+1F646 1F3FB 200D 2640 ; non-fully-qualified # 🙆🏻♀ woman gesturing OK: light skin tone
+1F646 1F3FC 200D 2640 FE0F ; fully-qualified # 🙆🏼♀️ woman gesturing OK: medium-light skin tone
+1F646 1F3FC 200D 2640 ; non-fully-qualified # 🙆🏼♀ woman gesturing OK: medium-light skin tone
+1F646 1F3FD 200D 2640 FE0F ; fully-qualified # 🙆🏽♀️ woman gesturing OK: medium skin tone
+1F646 1F3FD 200D 2640 ; non-fully-qualified # 🙆🏽♀ woman gesturing OK: medium skin tone
+1F646 1F3FE 200D 2640 FE0F ; fully-qualified # 🙆🏾♀️ woman gesturing OK: medium-dark skin tone
+1F646 1F3FE 200D 2640 ; non-fully-qualified # 🙆🏾♀ woman gesturing OK: medium-dark skin tone
+1F646 1F3FF 200D 2640 FE0F ; fully-qualified # 🙆🏿♀️ woman gesturing OK: dark skin tone
+1F646 1F3FF 200D 2640 ; non-fully-qualified # 🙆🏿♀ woman gesturing OK: dark skin tone
+1F481 ; fully-qualified # 💁 person tipping hand
+1F481 1F3FB ; fully-qualified # 💁🏻 person tipping hand: light skin tone
+1F481 1F3FC ; fully-qualified # 💁🏼 person tipping hand: medium-light skin tone
+1F481 1F3FD ; fully-qualified # 💁🏽 person tipping hand: medium skin tone
+1F481 1F3FE ; fully-qualified # 💁🏾 person tipping hand: medium-dark skin tone
+1F481 1F3FF ; fully-qualified # 💁🏿 person tipping hand: dark skin tone
+1F481 200D 2642 FE0F ; fully-qualified # 💁♂️ man tipping hand
+1F481 200D 2642 ; non-fully-qualified # 💁♂ man tipping hand
+1F481 1F3FB 200D 2642 FE0F ; fully-qualified # 💁🏻♂️ man tipping hand: light skin tone
+1F481 1F3FB 200D 2642 ; non-fully-qualified # 💁🏻♂ man tipping hand: light skin tone
+1F481 1F3FC 200D 2642 FE0F ; fully-qualified # 💁🏼♂️ man tipping hand: medium-light skin tone
+1F481 1F3FC 200D 2642 ; non-fully-qualified # 💁🏼♂ man tipping hand: medium-light skin tone
+1F481 1F3FD 200D 2642 FE0F ; fully-qualified # 💁🏽♂️ man tipping hand: medium skin tone
+1F481 1F3FD 200D 2642 ; non-fully-qualified # 💁🏽♂ man tipping hand: medium skin tone
+1F481 1F3FE 200D 2642 FE0F ; fully-qualified # 💁🏾♂️ man tipping hand: medium-dark skin tone
+1F481 1F3FE 200D 2642 ; non-fully-qualified # 💁🏾♂ man tipping hand: medium-dark skin tone
+1F481 1F3FF 200D 2642 FE0F ; fully-qualified # 💁🏿♂️ man tipping hand: dark skin tone
+1F481 1F3FF 200D 2642 ; non-fully-qualified # 💁🏿♂ man tipping hand: dark skin tone
+1F481 200D 2640 FE0F ; fully-qualified # 💁♀️ woman tipping hand
+1F481 200D 2640 ; non-fully-qualified # 💁♀ woman tipping hand
+1F481 1F3FB 200D 2640 FE0F ; fully-qualified # 💁🏻♀️ woman tipping hand: light skin tone
+1F481 1F3FB 200D 2640 ; non-fully-qualified # 💁🏻♀ woman tipping hand: light skin tone
+1F481 1F3FC 200D 2640 FE0F ; fully-qualified # 💁🏼♀️ woman tipping hand: medium-light skin tone
+1F481 1F3FC 200D 2640 ; non-fully-qualified # 💁🏼♀ woman tipping hand: medium-light skin tone
+1F481 1F3FD 200D 2640 FE0F ; fully-qualified # 💁🏽♀️ woman tipping hand: medium skin tone
+1F481 1F3FD 200D 2640 ; non-fully-qualified # 💁🏽♀ woman tipping hand: medium skin tone
+1F481 1F3FE 200D 2640 FE0F ; fully-qualified # 💁🏾♀️ woman tipping hand: medium-dark skin tone
+1F481 1F3FE 200D 2640 ; non-fully-qualified # 💁🏾♀ woman tipping hand: medium-dark skin tone
+1F481 1F3FF 200D 2640 FE0F ; fully-qualified # 💁🏿♀️ woman tipping hand: dark skin tone
+1F481 1F3FF 200D 2640 ; non-fully-qualified # 💁🏿♀ woman tipping hand: dark skin tone
+1F64B ; fully-qualified # 🙋 person raising hand
+1F64B 1F3FB ; fully-qualified # 🙋🏻 person raising hand: light skin tone
+1F64B 1F3FC ; fully-qualified # 🙋🏼 person raising hand: medium-light skin tone
+1F64B 1F3FD ; fully-qualified # 🙋🏽 person raising hand: medium skin tone
+1F64B 1F3FE ; fully-qualified # 🙋🏾 person raising hand: medium-dark skin tone
+1F64B 1F3FF ; fully-qualified # 🙋🏿 person raising hand: dark skin tone
+1F64B 200D 2642 FE0F ; fully-qualified # 🙋♂️ man raising hand
+1F64B 200D 2642 ; non-fully-qualified # 🙋♂ man raising hand
+1F64B 1F3FB 200D 2642 FE0F ; fully-qualified # 🙋🏻♂️ man raising hand: light skin tone
+1F64B 1F3FB 200D 2642 ; non-fully-qualified # 🙋🏻♂ man raising hand: light skin tone
+1F64B 1F3FC 200D 2642 FE0F ; fully-qualified # 🙋🏼♂️ man raising hand: medium-light skin tone
+1F64B 1F3FC 200D 2642 ; non-fully-qualified # 🙋🏼♂ man raising hand: medium-light skin tone
+1F64B 1F3FD 200D 2642 FE0F ; fully-qualified # 🙋🏽♂️ man raising hand: medium skin tone
+1F64B 1F3FD 200D 2642 ; non-fully-qualified # 🙋🏽♂ man raising hand: medium skin tone
+1F64B 1F3FE 200D 2642 FE0F ; fully-qualified # 🙋🏾♂️ man raising hand: medium-dark skin tone
+1F64B 1F3FE 200D 2642 ; non-fully-qualified # 🙋🏾♂ man raising hand: medium-dark skin tone
+1F64B 1F3FF 200D 2642 FE0F ; fully-qualified # 🙋🏿♂️ man raising hand: dark skin tone
+1F64B 1F3FF 200D 2642 ; non-fully-qualified # 🙋🏿♂ man raising hand: dark skin tone
+1F64B 200D 2640 FE0F ; fully-qualified # 🙋♀️ woman raising hand
+1F64B 200D 2640 ; non-fully-qualified # 🙋♀ woman raising hand
+1F64B 1F3FB 200D 2640 FE0F ; fully-qualified # 🙋🏻♀️ woman raising hand: light skin tone
+1F64B 1F3FB 200D 2640 ; non-fully-qualified # 🙋🏻♀ woman raising hand: light skin tone
+1F64B 1F3FC 200D 2640 FE0F ; fully-qualified # 🙋🏼♀️ woman raising hand: medium-light skin tone
+1F64B 1F3FC 200D 2640 ; non-fully-qualified # 🙋🏼♀ woman raising hand: medium-light skin tone
+1F64B 1F3FD 200D 2640 FE0F ; fully-qualified # 🙋🏽♀️ woman raising hand: medium skin tone
+1F64B 1F3FD 200D 2640 ; non-fully-qualified # 🙋🏽♀ woman raising hand: medium skin tone
+1F64B 1F3FE 200D 2640 FE0F ; fully-qualified # 🙋🏾♀️ woman raising hand: medium-dark skin tone
+1F64B 1F3FE 200D 2640 ; non-fully-qualified # 🙋🏾♀ woman raising hand: medium-dark skin tone
+1F64B 1F3FF 200D 2640 FE0F ; fully-qualified # 🙋🏿♀️ woman raising hand: dark skin tone
+1F64B 1F3FF 200D 2640 ; non-fully-qualified # 🙋🏿♀ woman raising hand: dark skin tone
+1F647 ; fully-qualified # 🙇 person bowing
+1F647 1F3FB ; fully-qualified # 🙇🏻 person bowing: light skin tone
+1F647 1F3FC ; fully-qualified # 🙇🏼 person bowing: medium-light skin tone
+1F647 1F3FD ; fully-qualified # 🙇🏽 person bowing: medium skin tone
+1F647 1F3FE ; fully-qualified # 🙇🏾 person bowing: medium-dark skin tone
+1F647 1F3FF ; fully-qualified # 🙇🏿 person bowing: dark skin tone
+1F647 200D 2642 FE0F ; fully-qualified # 🙇♂️ man bowing
+1F647 200D 2642 ; non-fully-qualified # 🙇♂ man bowing
+1F647 1F3FB 200D 2642 FE0F ; fully-qualified # 🙇🏻♂️ man bowing: light skin tone
+1F647 1F3FB 200D 2642 ; non-fully-qualified # 🙇🏻♂ man bowing: light skin tone
+1F647 1F3FC 200D 2642 FE0F ; fully-qualified # 🙇🏼♂️ man bowing: medium-light skin tone
+1F647 1F3FC 200D 2642 ; non-fully-qualified # 🙇🏼♂ man bowing: medium-light skin tone
+1F647 1F3FD 200D 2642 FE0F ; fully-qualified # 🙇🏽♂️ man bowing: medium skin tone
+1F647 1F3FD 200D 2642 ; non-fully-qualified # 🙇🏽♂ man bowing: medium skin tone
+1F647 1F3FE 200D 2642 FE0F ; fully-qualified # 🙇🏾♂️ man bowing: medium-dark skin tone
+1F647 1F3FE 200D 2642 ; non-fully-qualified # 🙇🏾♂ man bowing: medium-dark skin tone
+1F647 1F3FF 200D 2642 FE0F ; fully-qualified # 🙇🏿♂️ man bowing: dark skin tone
+1F647 1F3FF 200D 2642 ; non-fully-qualified # 🙇🏿♂ man bowing: dark skin tone
+1F647 200D 2640 FE0F ; fully-qualified # 🙇♀️ woman bowing
+1F647 200D 2640 ; non-fully-qualified # 🙇♀ woman bowing
+1F647 1F3FB 200D 2640 FE0F ; fully-qualified # 🙇🏻♀️ woman bowing: light skin tone
+1F647 1F3FB 200D 2640 ; non-fully-qualified # 🙇🏻♀ woman bowing: light skin tone
+1F647 1F3FC 200D 2640 FE0F ; fully-qualified # 🙇🏼♀️ woman bowing: medium-light skin tone
+1F647 1F3FC 200D 2640 ; non-fully-qualified # 🙇🏼♀ woman bowing: medium-light skin tone
+1F647 1F3FD 200D 2640 FE0F ; fully-qualified # 🙇🏽♀️ woman bowing: medium skin tone
+1F647 1F3FD 200D 2640 ; non-fully-qualified # 🙇🏽♀ woman bowing: medium skin tone
+1F647 1F3FE 200D 2640 FE0F ; fully-qualified # 🙇🏾♀️ woman bowing: medium-dark skin tone
+1F647 1F3FE 200D 2640 ; non-fully-qualified # 🙇🏾♀ woman bowing: medium-dark skin tone
+1F647 1F3FF 200D 2640 FE0F ; fully-qualified # 🙇🏿♀️ woman bowing: dark skin tone
+1F647 1F3FF 200D 2640 ; non-fully-qualified # 🙇🏿♀ woman bowing: dark skin tone
+1F926 ; fully-qualified # 🤦 person facepalming
+1F926 1F3FB ; fully-qualified # 🤦🏻 person facepalming: light skin tone
+1F926 1F3FC ; fully-qualified # 🤦🏼 person facepalming: medium-light skin tone
+1F926 1F3FD ; fully-qualified # 🤦🏽 person facepalming: medium skin tone
+1F926 1F3FE ; fully-qualified # 🤦🏾 person facepalming: medium-dark skin tone
+1F926 1F3FF ; fully-qualified # 🤦🏿 person facepalming: dark skin tone
+1F926 200D 2642 FE0F ; fully-qualified # 🤦♂️ man facepalming
+1F926 200D 2642 ; non-fully-qualified # 🤦♂ man facepalming
+1F926 1F3FB 200D 2642 FE0F ; fully-qualified # 🤦🏻♂️ man facepalming: light skin tone
+1F926 1F3FB 200D 2642 ; non-fully-qualified # 🤦🏻♂ man facepalming: light skin tone
+1F926 1F3FC 200D 2642 FE0F ; fully-qualified # 🤦🏼♂️ man facepalming: medium-light skin tone
+1F926 1F3FC 200D 2642 ; non-fully-qualified # 🤦🏼♂ man facepalming: medium-light skin tone
+1F926 1F3FD 200D 2642 FE0F ; fully-qualified # 🤦🏽♂️ man facepalming: medium skin tone
+1F926 1F3FD 200D 2642 ; non-fully-qualified # 🤦🏽♂ man facepalming: medium skin tone
+1F926 1F3FE 200D 2642 FE0F ; fully-qualified # 🤦🏾♂️ man facepalming: medium-dark skin tone
+1F926 1F3FE 200D 2642 ; non-fully-qualified # 🤦🏾♂ man facepalming: medium-dark skin tone
+1F926 1F3FF 200D 2642 FE0F ; fully-qualified # 🤦🏿♂️ man facepalming: dark skin tone
+1F926 1F3FF 200D 2642 ; non-fully-qualified # 🤦🏿♂ man facepalming: dark skin tone
+1F926 200D 2640 FE0F ; fully-qualified # 🤦♀️ woman facepalming
+1F926 200D 2640 ; non-fully-qualified # 🤦♀ woman facepalming
+1F926 1F3FB 200D 2640 FE0F ; fully-qualified # 🤦🏻♀️ woman facepalming: light skin tone
+1F926 1F3FB 200D 2640 ; non-fully-qualified # 🤦🏻♀ woman facepalming: light skin tone
+1F926 1F3FC 200D 2640 FE0F ; fully-qualified # 🤦🏼♀️ woman facepalming: medium-light skin tone
+1F926 1F3FC 200D 2640 ; non-fully-qualified # 🤦🏼♀ woman facepalming: medium-light skin tone
+1F926 1F3FD 200D 2640 FE0F ; fully-qualified # 🤦🏽♀️ woman facepalming: medium skin tone
+1F926 1F3FD 200D 2640 ; non-fully-qualified # 🤦🏽♀ woman facepalming: medium skin tone
+1F926 1F3FE 200D 2640 FE0F ; fully-qualified # 🤦🏾♀️ woman facepalming: medium-dark skin tone
+1F926 1F3FE 200D 2640 ; non-fully-qualified # 🤦🏾♀ woman facepalming: medium-dark skin tone
+1F926 1F3FF 200D 2640 FE0F ; fully-qualified # 🤦🏿♀️ woman facepalming: dark skin tone
+1F926 1F3FF 200D 2640 ; non-fully-qualified # 🤦🏿♀ woman facepalming: dark skin tone
+1F937 ; fully-qualified # 🤷 person shrugging
+1F937 1F3FB ; fully-qualified # 🤷🏻 person shrugging: light skin tone
+1F937 1F3FC ; fully-qualified # 🤷🏼 person shrugging: medium-light skin tone
+1F937 1F3FD ; fully-qualified # 🤷🏽 person shrugging: medium skin tone
+1F937 1F3FE ; fully-qualified # 🤷🏾 person shrugging: medium-dark skin tone
+1F937 1F3FF ; fully-qualified # 🤷🏿 person shrugging: dark skin tone
+1F937 200D 2642 FE0F ; fully-qualified # 🤷♂️ man shrugging
+1F937 200D 2642 ; non-fully-qualified # 🤷♂ man shrugging
+1F937 1F3FB 200D 2642 FE0F ; fully-qualified # 🤷🏻♂️ man shrugging: light skin tone
+1F937 1F3FB 200D 2642 ; non-fully-qualified # 🤷🏻♂ man shrugging: light skin tone
+1F937 1F3FC 200D 2642 FE0F ; fully-qualified # 🤷🏼♂️ man shrugging: medium-light skin tone
+1F937 1F3FC 200D 2642 ; non-fully-qualified # 🤷🏼♂ man shrugging: medium-light skin tone
+1F937 1F3FD 200D 2642 FE0F ; fully-qualified # 🤷🏽♂️ man shrugging: medium skin tone
+1F937 1F3FD 200D 2642 ; non-fully-qualified # 🤷🏽♂ man shrugging: medium skin tone
+1F937 1F3FE 200D 2642 FE0F ; fully-qualified # 🤷🏾♂️ man shrugging: medium-dark skin tone
+1F937 1F3FE 200D 2642 ; non-fully-qualified # 🤷🏾♂ man shrugging: medium-dark skin tone
+1F937 1F3FF 200D 2642 FE0F ; fully-qualified # 🤷🏿♂️ man shrugging: dark skin tone
+1F937 1F3FF 200D 2642 ; non-fully-qualified # 🤷🏿♂ man shrugging: dark skin tone
+1F937 200D 2640 FE0F ; fully-qualified # 🤷♀️ woman shrugging
+1F937 200D 2640 ; non-fully-qualified # 🤷♀ woman shrugging
+1F937 1F3FB 200D 2640 FE0F ; fully-qualified # 🤷🏻♀️ woman shrugging: light skin tone
+1F937 1F3FB 200D 2640 ; non-fully-qualified # 🤷🏻♀ woman shrugging: light skin tone
+1F937 1F3FC 200D 2640 FE0F ; fully-qualified # 🤷🏼♀️ woman shrugging: medium-light skin tone
+1F937 1F3FC 200D 2640 ; non-fully-qualified # 🤷🏼♀ woman shrugging: medium-light skin tone
+1F937 1F3FD 200D 2640 FE0F ; fully-qualified # 🤷🏽♀️ woman shrugging: medium skin tone
+1F937 1F3FD 200D 2640 ; non-fully-qualified # 🤷🏽♀ woman shrugging: medium skin tone
+1F937 1F3FE 200D 2640 FE0F ; fully-qualified # 🤷🏾♀️ woman shrugging: medium-dark skin tone
+1F937 1F3FE 200D 2640 ; non-fully-qualified # 🤷🏾♀ woman shrugging: medium-dark skin tone
+1F937 1F3FF 200D 2640 FE0F ; fully-qualified # 🤷🏿♀️ woman shrugging: dark skin tone
+1F937 1F3FF 200D 2640 ; non-fully-qualified # 🤷🏿♀ woman shrugging: dark skin tone
+
+# subgroup: person-activity
+1F486 ; fully-qualified # 💆 person getting massage
+1F486 1F3FB ; fully-qualified # 💆🏻 person getting massage: light skin tone
+1F486 1F3FC ; fully-qualified # 💆🏼 person getting massage: medium-light skin tone
+1F486 1F3FD ; fully-qualified # 💆🏽 person getting massage: medium skin tone
+1F486 1F3FE ; fully-qualified # 💆🏾 person getting massage: medium-dark skin tone
+1F486 1F3FF ; fully-qualified # 💆🏿 person getting massage: dark skin tone
+1F486 200D 2642 FE0F ; fully-qualified # 💆♂️ man getting massage
+1F486 200D 2642 ; non-fully-qualified # 💆♂ man getting massage
+1F486 1F3FB 200D 2642 FE0F ; fully-qualified # 💆🏻♂️ man getting massage: light skin tone
+1F486 1F3FB 200D 2642 ; non-fully-qualified # 💆🏻♂ man getting massage: light skin tone
+1F486 1F3FC 200D 2642 FE0F ; fully-qualified # 💆🏼♂️ man getting massage: medium-light skin tone
+1F486 1F3FC 200D 2642 ; non-fully-qualified # 💆🏼♂ man getting massage: medium-light skin tone
+1F486 1F3FD 200D 2642 FE0F ; fully-qualified # 💆🏽♂️ man getting massage: medium skin tone
+1F486 1F3FD 200D 2642 ; non-fully-qualified # 💆🏽♂ man getting massage: medium skin tone
+1F486 1F3FE 200D 2642 FE0F ; fully-qualified # 💆🏾♂️ man getting massage: medium-dark skin tone
+1F486 1F3FE 200D 2642 ; non-fully-qualified # 💆🏾♂ man getting massage: medium-dark skin tone
+1F486 1F3FF 200D 2642 FE0F ; fully-qualified # 💆🏿♂️ man getting massage: dark skin tone
+1F486 1F3FF 200D 2642 ; non-fully-qualified # 💆🏿♂ man getting massage: dark skin tone
+1F486 200D 2640 FE0F ; fully-qualified # 💆♀️ woman getting massage
+1F486 200D 2640 ; non-fully-qualified # 💆♀ woman getting massage
+1F486 1F3FB 200D 2640 FE0F ; fully-qualified # 💆🏻♀️ woman getting massage: light skin tone
+1F486 1F3FB 200D 2640 ; non-fully-qualified # 💆🏻♀ woman getting massage: light skin tone
+1F486 1F3FC 200D 2640 FE0F ; fully-qualified # 💆🏼♀️ woman getting massage: medium-light skin tone
+1F486 1F3FC 200D 2640 ; non-fully-qualified # 💆🏼♀ woman getting massage: medium-light skin tone
+1F486 1F3FD 200D 2640 FE0F ; fully-qualified # 💆🏽♀️ woman getting massage: medium skin tone
+1F486 1F3FD 200D 2640 ; non-fully-qualified # 💆🏽♀ woman getting massage: medium skin tone
+1F486 1F3FE 200D 2640 FE0F ; fully-qualified # 💆🏾♀️ woman getting massage: medium-dark skin tone
+1F486 1F3FE 200D 2640 ; non-fully-qualified # 💆🏾♀ woman getting massage: medium-dark skin tone
+1F486 1F3FF 200D 2640 FE0F ; fully-qualified # 💆🏿♀️ woman getting massage: dark skin tone
+1F486 1F3FF 200D 2640 ; non-fully-qualified # 💆🏿♀ woman getting massage: dark skin tone
+1F487 ; fully-qualified # 💇 person getting haircut
+1F487 1F3FB ; fully-qualified # 💇🏻 person getting haircut: light skin tone
+1F487 1F3FC ; fully-qualified # 💇🏼 person getting haircut: medium-light skin tone
+1F487 1F3FD ; fully-qualified # 💇🏽 person getting haircut: medium skin tone
+1F487 1F3FE ; fully-qualified # 💇🏾 person getting haircut: medium-dark skin tone
+1F487 1F3FF ; fully-qualified # 💇🏿 person getting haircut: dark skin tone
+1F487 200D 2642 FE0F ; fully-qualified # 💇♂️ man getting haircut
+1F487 200D 2642 ; non-fully-qualified # 💇♂ man getting haircut
+1F487 1F3FB 200D 2642 FE0F ; fully-qualified # 💇🏻♂️ man getting haircut: light skin tone
+1F487 1F3FB 200D 2642 ; non-fully-qualified # 💇🏻♂ man getting haircut: light skin tone
+1F487 1F3FC 200D 2642 FE0F ; fully-qualified # 💇🏼♂️ man getting haircut: medium-light skin tone
+1F487 1F3FC 200D 2642 ; non-fully-qualified # 💇🏼♂ man getting haircut: medium-light skin tone
+1F487 1F3FD 200D 2642 FE0F ; fully-qualified # 💇🏽♂️ man getting haircut: medium skin tone
+1F487 1F3FD 200D 2642 ; non-fully-qualified # 💇🏽♂ man getting haircut: medium skin tone
+1F487 1F3FE 200D 2642 FE0F ; fully-qualified # 💇🏾♂️ man getting haircut: medium-dark skin tone
+1F487 1F3FE 200D 2642 ; non-fully-qualified # 💇🏾♂ man getting haircut: medium-dark skin tone
+1F487 1F3FF 200D 2642 FE0F ; fully-qualified # 💇🏿♂️ man getting haircut: dark skin tone
+1F487 1F3FF 200D 2642 ; non-fully-qualified # 💇🏿♂ man getting haircut: dark skin tone
+1F487 200D 2640 FE0F ; fully-qualified # 💇♀️ woman getting haircut
+1F487 200D 2640 ; non-fully-qualified # 💇♀ woman getting haircut
+1F487 1F3FB 200D 2640 FE0F ; fully-qualified # 💇🏻♀️ woman getting haircut: light skin tone
+1F487 1F3FB 200D 2640 ; non-fully-qualified # 💇🏻♀ woman getting haircut: light skin tone
+1F487 1F3FC 200D 2640 FE0F ; fully-qualified # 💇🏼♀️ woman getting haircut: medium-light skin tone
+1F487 1F3FC 200D 2640 ; non-fully-qualified # 💇🏼♀ woman getting haircut: medium-light skin tone
+1F487 1F3FD 200D 2640 FE0F ; fully-qualified # 💇🏽♀️ woman getting haircut: medium skin tone
+1F487 1F3FD 200D 2640 ; non-fully-qualified # 💇🏽♀ woman getting haircut: medium skin tone
+1F487 1F3FE 200D 2640 FE0F ; fully-qualified # 💇🏾♀️ woman getting haircut: medium-dark skin tone
+1F487 1F3FE 200D 2640 ; non-fully-qualified # 💇🏾♀ woman getting haircut: medium-dark skin tone
+1F487 1F3FF 200D 2640 FE0F ; fully-qualified # 💇🏿♀️ woman getting haircut: dark skin tone
+1F487 1F3FF 200D 2640 ; non-fully-qualified # 💇🏿♀ woman getting haircut: dark skin tone
+1F6B6 ; fully-qualified # 🚶 person walking
+1F6B6 1F3FB ; fully-qualified # 🚶🏻 person walking: light skin tone
+1F6B6 1F3FC ; fully-qualified # 🚶🏼 person walking: medium-light skin tone
+1F6B6 1F3FD ; fully-qualified # 🚶🏽 person walking: medium skin tone
+1F6B6 1F3FE ; fully-qualified # 🚶🏾 person walking: medium-dark skin tone
+1F6B6 1F3FF ; fully-qualified # 🚶🏿 person walking: dark skin tone
+1F6B6 200D 2642 FE0F ; fully-qualified # 🚶♂️ man walking
+1F6B6 200D 2642 ; non-fully-qualified # 🚶♂ man walking
+1F6B6 1F3FB 200D 2642 FE0F ; fully-qualified # 🚶🏻♂️ man walking: light skin tone
+1F6B6 1F3FB 200D 2642 ; non-fully-qualified # 🚶🏻♂ man walking: light skin tone
+1F6B6 1F3FC 200D 2642 FE0F ; fully-qualified # 🚶🏼♂️ man walking: medium-light skin tone
+1F6B6 1F3FC 200D 2642 ; non-fully-qualified # 🚶🏼♂ man walking: medium-light skin tone
+1F6B6 1F3FD 200D 2642 FE0F ; fully-qualified # 🚶🏽♂️ man walking: medium skin tone
+1F6B6 1F3FD 200D 2642 ; non-fully-qualified # 🚶🏽♂ man walking: medium skin tone
+1F6B6 1F3FE 200D 2642 FE0F ; fully-qualified # 🚶🏾♂️ man walking: medium-dark skin tone
+1F6B6 1F3FE 200D 2642 ; non-fully-qualified # 🚶🏾♂ man walking: medium-dark skin tone
+1F6B6 1F3FF 200D 2642 FE0F ; fully-qualified # 🚶🏿♂️ man walking: dark skin tone
+1F6B6 1F3FF 200D 2642 ; non-fully-qualified # 🚶🏿♂ man walking: dark skin tone
+1F6B6 200D 2640 FE0F ; fully-qualified # 🚶♀️ woman walking
+1F6B6 200D 2640 ; non-fully-qualified # 🚶♀ woman walking
+1F6B6 1F3FB 200D 2640 FE0F ; fully-qualified # 🚶🏻♀️ woman walking: light skin tone
+1F6B6 1F3FB 200D 2640 ; non-fully-qualified # 🚶🏻♀ woman walking: light skin tone
+1F6B6 1F3FC 200D 2640 FE0F ; fully-qualified # 🚶🏼♀️ woman walking: medium-light skin tone
+1F6B6 1F3FC 200D 2640 ; non-fully-qualified # 🚶🏼♀ woman walking: medium-light skin tone
+1F6B6 1F3FD 200D 2640 FE0F ; fully-qualified # 🚶🏽♀️ woman walking: medium skin tone
+1F6B6 1F3FD 200D 2640 ; non-fully-qualified # 🚶🏽♀ woman walking: medium skin tone
+1F6B6 1F3FE 200D 2640 FE0F ; fully-qualified # 🚶🏾♀️ woman walking: medium-dark skin tone
+1F6B6 1F3FE 200D 2640 ; non-fully-qualified # 🚶🏾♀ woman walking: medium-dark skin tone
+1F6B6 1F3FF 200D 2640 FE0F ; fully-qualified # 🚶🏿♀️ woman walking: dark skin tone
+1F6B6 1F3FF 200D 2640 ; non-fully-qualified # 🚶🏿♀ woman walking: dark skin tone
+1F3C3 ; fully-qualified # 🏃 person running
+1F3C3 1F3FB ; fully-qualified # 🏃🏻 person running: light skin tone
+1F3C3 1F3FC ; fully-qualified # 🏃🏼 person running: medium-light skin tone
+1F3C3 1F3FD ; fully-qualified # 🏃🏽 person running: medium skin tone
+1F3C3 1F3FE ; fully-qualified # 🏃🏾 person running: medium-dark skin tone
+1F3C3 1F3FF ; fully-qualified # 🏃🏿 person running: dark skin tone
+1F3C3 200D 2642 FE0F ; fully-qualified # 🏃♂️ man running
+1F3C3 200D 2642 ; non-fully-qualified # 🏃♂ man running
+1F3C3 1F3FB 200D 2642 FE0F ; fully-qualified # 🏃🏻♂️ man running: light skin tone
+1F3C3 1F3FB 200D 2642 ; non-fully-qualified # 🏃🏻♂ man running: light skin tone
+1F3C3 1F3FC 200D 2642 FE0F ; fully-qualified # 🏃🏼♂️ man running: medium-light skin tone
+1F3C3 1F3FC 200D 2642 ; non-fully-qualified # 🏃🏼♂ man running: medium-light skin tone
+1F3C3 1F3FD 200D 2642 FE0F ; fully-qualified # 🏃🏽♂️ man running: medium skin tone
+1F3C3 1F3FD 200D 2642 ; non-fully-qualified # 🏃🏽♂ man running: medium skin tone
+1F3C3 1F3FE 200D 2642 FE0F ; fully-qualified # 🏃🏾♂️ man running: medium-dark skin tone
+1F3C3 1F3FE 200D 2642 ; non-fully-qualified # 🏃🏾♂ man running: medium-dark skin tone
+1F3C3 1F3FF 200D 2642 FE0F ; fully-qualified # 🏃🏿♂️ man running: dark skin tone
+1F3C3 1F3FF 200D 2642 ; non-fully-qualified # 🏃🏿♂ man running: dark skin tone
+1F3C3 200D 2640 FE0F ; fully-qualified # 🏃♀️ woman running
+1F3C3 200D 2640 ; non-fully-qualified # 🏃♀ woman running
+1F3C3 1F3FB 200D 2640 FE0F ; fully-qualified # 🏃🏻♀️ woman running: light skin tone
+1F3C3 1F3FB 200D 2640 ; non-fully-qualified # 🏃🏻♀ woman running: light skin tone
+1F3C3 1F3FC 200D 2640 FE0F ; fully-qualified # 🏃🏼♀️ woman running: medium-light skin tone
+1F3C3 1F3FC 200D 2640 ; non-fully-qualified # 🏃🏼♀ woman running: medium-light skin tone
+1F3C3 1F3FD 200D 2640 FE0F ; fully-qualified # 🏃🏽♀️ woman running: medium skin tone
+1F3C3 1F3FD 200D 2640 ; non-fully-qualified # 🏃🏽♀ woman running: medium skin tone
+1F3C3 1F3FE 200D 2640 FE0F ; fully-qualified # 🏃🏾♀️ woman running: medium-dark skin tone
+1F3C3 1F3FE 200D 2640 ; non-fully-qualified # 🏃🏾♀ woman running: medium-dark skin tone
+1F3C3 1F3FF 200D 2640 FE0F ; fully-qualified # 🏃🏿♀️ woman running: dark skin tone
+1F3C3 1F3FF 200D 2640 ; non-fully-qualified # 🏃🏿♀ woman running: dark skin tone
+1F483 ; fully-qualified # 💃 woman dancing
+1F483 1F3FB ; fully-qualified # 💃🏻 woman dancing: light skin tone
+1F483 1F3FC ; fully-qualified # 💃🏼 woman dancing: medium-light skin tone
+1F483 1F3FD ; fully-qualified # 💃🏽 woman dancing: medium skin tone
+1F483 1F3FE ; fully-qualified # 💃🏾 woman dancing: medium-dark skin tone
+1F483 1F3FF ; fully-qualified # 💃🏿 woman dancing: dark skin tone
+1F57A ; fully-qualified # 🕺 man dancing
+1F57A 1F3FB ; fully-qualified # 🕺🏻 man dancing: light skin tone
+1F57A 1F3FC ; fully-qualified # 🕺🏼 man dancing: medium-light skin tone
+1F57A 1F3FD ; fully-qualified # 🕺🏽 man dancing: medium skin tone
+1F57A 1F3FE ; fully-qualified # 🕺🏾 man dancing: medium-dark skin tone
+1F57A 1F3FF ; fully-qualified # 🕺🏿 man dancing: dark skin tone
+1F46F ; fully-qualified # 👯 people with bunny ears partying
+1F46F 200D 2642 FE0F ; fully-qualified # 👯♂️ men with bunny ears partying
+1F46F 200D 2642 ; non-fully-qualified # 👯♂ men with bunny ears partying
+1F46F 200D 2640 FE0F ; fully-qualified # 👯♀️ women with bunny ears partying
+1F46F 200D 2640 ; non-fully-qualified # 👯♀ women with bunny ears partying
+1F574 FE0F ; fully-qualified # 🕴️ man in business suit levitating
+1F574 1F3FB ; fully-qualified # 🕴🏻 man in business suit levitating: light skin tone
+1F574 1F3FC ; fully-qualified # 🕴🏼 man in business suit levitating: medium-light skin tone
+1F574 1F3FD ; fully-qualified # 🕴🏽 man in business suit levitating: medium skin tone
+1F574 1F3FE ; fully-qualified # 🕴🏾 man in business suit levitating: medium-dark skin tone
+1F574 1F3FF ; fully-qualified # 🕴🏿 man in business suit levitating: dark skin tone
+1F5E3 FE0F ; fully-qualified # 🗣️ speaking head
+1F464 ; fully-qualified # 👤 bust in silhouette
+1F465 ; fully-qualified # 👥 busts in silhouette
+
+# subgroup: person-sport
+1F93A ; fully-qualified # 🤺 person fencing
+1F3C7 ; fully-qualified # 🏇 horse racing
+1F3C7 1F3FB ; fully-qualified # 🏇🏻 horse racing: light skin tone
+1F3C7 1F3FC ; fully-qualified # 🏇🏼 horse racing: medium-light skin tone
+1F3C7 1F3FD ; fully-qualified # 🏇🏽 horse racing: medium skin tone
+1F3C7 1F3FE ; fully-qualified # 🏇🏾 horse racing: medium-dark skin tone
+1F3C7 1F3FF ; fully-qualified # 🏇🏿 horse racing: dark skin tone
+26F7 FE0F ; fully-qualified # ⛷️ skier
+1F3C2 ; fully-qualified # 🏂 snowboarder
+1F3C2 1F3FB ; fully-qualified # 🏂🏻 snowboarder: light skin tone
+1F3C2 1F3FC ; fully-qualified # 🏂🏼 snowboarder: medium-light skin tone
+1F3C2 1F3FD ; fully-qualified # 🏂🏽 snowboarder: medium skin tone
+1F3C2 1F3FE ; fully-qualified # 🏂🏾 snowboarder: medium-dark skin tone
+1F3C2 1F3FF ; fully-qualified # 🏂🏿 snowboarder: dark skin tone
+1F3CC FE0F ; fully-qualified # 🏌️ person golfing
+1F3CC 1F3FB ; fully-qualified # 🏌🏻 person golfing: light skin tone
+1F3CC 1F3FC ; fully-qualified # 🏌🏼 person golfing: medium-light skin tone
+1F3CC 1F3FD ; fully-qualified # 🏌🏽 person golfing: medium skin tone
+1F3CC 1F3FE ; fully-qualified # 🏌🏾 person golfing: medium-dark skin tone
+1F3CC 1F3FF ; fully-qualified # 🏌🏿 person golfing: dark skin tone
+1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️♂️ man golfing
+1F3CC 200D 2642 ; non-fully-qualified # 🏌♂ man golfing
+1F3CC FE0F 200D 2642 ; non-fully-qualified # 🏌️♂ man golfing
+1F3CC 200D 2642 FE0F ; non-fully-qualified # 🏌♂️ man golfing
+1F3CC 1F3FB 200D 2642 FE0F ; fully-qualified # 🏌🏻♂️ man golfing: light skin tone
+1F3CC 1F3FB 200D 2642 ; non-fully-qualified # 🏌🏻♂ man golfing: light skin tone
+1F3CC 1F3FC 200D 2642 FE0F ; fully-qualified # 🏌🏼♂️ man golfing: medium-light skin tone
+1F3CC 1F3FC 200D 2642 ; non-fully-qualified # 🏌🏼♂ man golfing: medium-light skin tone
+1F3CC 1F3FD 200D 2642 FE0F ; fully-qualified # 🏌🏽♂️ man golfing: medium skin tone
+1F3CC 1F3FD 200D 2642 ; non-fully-qualified # 🏌🏽♂ man golfing: medium skin tone
+1F3CC 1F3FE 200D 2642 FE0F ; fully-qualified # 🏌🏾♂️ man golfing: medium-dark skin tone
+1F3CC 1F3FE 200D 2642 ; non-fully-qualified # 🏌🏾♂ man golfing: medium-dark skin tone
+1F3CC 1F3FF 200D 2642 FE0F ; fully-qualified # 🏌🏿♂️ man golfing: dark skin tone
+1F3CC 1F3FF 200D 2642 ; non-fully-qualified # 🏌🏿♂ man golfing: dark skin tone
+1F3CC FE0F 200D 2640 FE0F ; fully-qualified # 🏌️♀️ woman golfing
+1F3CC 200D 2640 ; non-fully-qualified # 🏌♀ woman golfing
+1F3CC FE0F 200D 2640 ; non-fully-qualified # 🏌️♀ woman golfing
+1F3CC 200D 2640 FE0F ; non-fully-qualified # 🏌♀️ woman golfing
+1F3CC 1F3FB 200D 2640 FE0F ; fully-qualified # 🏌🏻♀️ woman golfing: light skin tone
+1F3CC 1F3FB 200D 2640 ; non-fully-qualified # 🏌🏻♀ woman golfing: light skin tone
+1F3CC 1F3FC 200D 2640 FE0F ; fully-qualified # 🏌🏼♀️ woman golfing: medium-light skin tone
+1F3CC 1F3FC 200D 2640 ; non-fully-qualified # 🏌🏼♀ woman golfing: medium-light skin tone
+1F3CC 1F3FD 200D 2640 FE0F ; fully-qualified # 🏌🏽♀️ woman golfing: medium skin tone
+1F3CC 1F3FD 200D 2640 ; non-fully-qualified # 🏌🏽♀ woman golfing: medium skin tone
+1F3CC 1F3FE 200D 2640 FE0F ; fully-qualified # 🏌🏾♀️ woman golfing: medium-dark skin tone
+1F3CC 1F3FE 200D 2640 ; non-fully-qualified # 🏌🏾♀ woman golfing: medium-dark skin tone
+1F3CC 1F3FF 200D 2640 FE0F ; fully-qualified # 🏌🏿♀️ woman golfing: dark skin tone
+1F3CC 1F3FF 200D 2640 ; non-fully-qualified # 🏌🏿♀ woman golfing: dark skin tone
+1F3C4 ; fully-qualified # 🏄 person surfing
+1F3C4 1F3FB ; fully-qualified # 🏄🏻 person surfing: light skin tone
+1F3C4 1F3FC ; fully-qualified # 🏄🏼 person surfing: medium-light skin tone
+1F3C4 1F3FD ; fully-qualified # 🏄🏽 person surfing: medium skin tone
+1F3C4 1F3FE ; fully-qualified # 🏄🏾 person surfing: medium-dark skin tone
+1F3C4 1F3FF ; fully-qualified # 🏄🏿 person surfing: dark skin tone
+1F3C4 200D 2642 FE0F ; fully-qualified # 🏄♂️ man surfing
+1F3C4 200D 2642 ; non-fully-qualified # 🏄♂ man surfing
+1F3C4 1F3FB 200D 2642 FE0F ; fully-qualified # 🏄🏻♂️ man surfing: light skin tone
+1F3C4 1F3FB 200D 2642 ; non-fully-qualified # 🏄🏻♂ man surfing: light skin tone
+1F3C4 1F3FC 200D 2642 FE0F ; fully-qualified # 🏄🏼♂️ man surfing: medium-light skin tone
+1F3C4 1F3FC 200D 2642 ; non-fully-qualified # 🏄🏼♂ man surfing: medium-light skin tone
+1F3C4 1F3FD 200D 2642 FE0F ; fully-qualified # 🏄🏽♂️ man surfing: medium skin tone
+1F3C4 1F3FD 200D 2642 ; non-fully-qualified # 🏄🏽♂ man surfing: medium skin tone
+1F3C4 1F3FE 200D 2642 FE0F ; fully-qualified # 🏄🏾♂️ man surfing: medium-dark skin tone
+1F3C4 1F3FE 200D 2642 ; non-fully-qualified # 🏄🏾♂ man surfing: medium-dark skin tone
+1F3C4 1F3FF 200D 2642 FE0F ; fully-qualified # 🏄🏿♂️ man surfing: dark skin tone
+1F3C4 1F3FF 200D 2642 ; non-fully-qualified # 🏄🏿♂ man surfing: dark skin tone
+1F3C4 200D 2640 FE0F ; fully-qualified # 🏄♀️ woman surfing
+1F3C4 200D 2640 ; non-fully-qualified # 🏄♀ woman surfing
+1F3C4 1F3FB 200D 2640 FE0F ; fully-qualified # 🏄🏻♀️ woman surfing: light skin tone
+1F3C4 1F3FB 200D 2640 ; non-fully-qualified # 🏄🏻♀ woman surfing: light skin tone
+1F3C4 1F3FC 200D 2640 FE0F ; fully-qualified # 🏄🏼♀️ woman surfing: medium-light skin tone
+1F3C4 1F3FC 200D 2640 ; non-fully-qualified # 🏄🏼♀ woman surfing: medium-light skin tone
+1F3C4 1F3FD 200D 2640 FE0F ; fully-qualified # 🏄🏽♀️ woman surfing: medium skin tone
+1F3C4 1F3FD 200D 2640 ; non-fully-qualified # 🏄🏽♀ woman surfing: medium skin tone
+1F3C4 1F3FE 200D 2640 FE0F ; fully-qualified # 🏄🏾♀️ woman surfing: medium-dark skin tone
+1F3C4 1F3FE 200D 2640 ; non-fully-qualified # 🏄🏾♀ woman surfing: medium-dark skin tone
+1F3C4 1F3FF 200D 2640 FE0F ; fully-qualified # 🏄🏿♀️ woman surfing: dark skin tone
+1F3C4 1F3FF 200D 2640 ; non-fully-qualified # 🏄🏿♀ woman surfing: dark skin tone
+1F6A3 ; fully-qualified # 🚣 person rowing boat
+1F6A3 1F3FB ; fully-qualified # 🚣🏻 person rowing boat: light skin tone
+1F6A3 1F3FC ; fully-qualified # 🚣🏼 person rowing boat: medium-light skin tone
+1F6A3 1F3FD ; fully-qualified # 🚣🏽 person rowing boat: medium skin tone
+1F6A3 1F3FE ; fully-qualified # 🚣🏾 person rowing boat: medium-dark skin tone
+1F6A3 1F3FF ; fully-qualified # 🚣🏿 person rowing boat: dark skin tone
+1F6A3 200D 2642 FE0F ; fully-qualified # 🚣♂️ man rowing boat
+1F6A3 200D 2642 ; non-fully-qualified # 🚣♂ man rowing boat
+1F6A3 1F3FB 200D 2642 FE0F ; fully-qualified # 🚣🏻♂️ man rowing boat: light skin tone
+1F6A3 1F3FB 200D 2642 ; non-fully-qualified # 🚣🏻♂ man rowing boat: light skin tone
+1F6A3 1F3FC 200D 2642 FE0F ; fully-qualified # 🚣🏼♂️ man rowing boat: medium-light skin tone
+1F6A3 1F3FC 200D 2642 ; non-fully-qualified # 🚣🏼♂ man rowing boat: medium-light skin tone
+1F6A3 1F3FD 200D 2642 FE0F ; fully-qualified # 🚣🏽♂️ man rowing boat: medium skin tone
+1F6A3 1F3FD 200D 2642 ; non-fully-qualified # 🚣🏽♂ man rowing boat: medium skin tone
+1F6A3 1F3FE 200D 2642 FE0F ; fully-qualified # 🚣🏾♂️ man rowing boat: medium-dark skin tone
+1F6A3 1F3FE 200D 2642 ; non-fully-qualified # 🚣🏾♂ man rowing boat: medium-dark skin tone
+1F6A3 1F3FF 200D 2642 FE0F ; fully-qualified # 🚣🏿♂️ man rowing boat: dark skin tone
+1F6A3 1F3FF 200D 2642 ; non-fully-qualified # 🚣🏿♂ man rowing boat: dark skin tone
+1F6A3 200D 2640 FE0F ; fully-qualified # 🚣♀️ woman rowing boat
+1F6A3 200D 2640 ; non-fully-qualified # 🚣♀ woman rowing boat
+1F6A3 1F3FB 200D 2640 FE0F ; fully-qualified # 🚣🏻♀️ woman rowing boat: light skin tone
+1F6A3 1F3FB 200D 2640 ; non-fully-qualified # 🚣🏻♀ woman rowing boat: light skin tone
+1F6A3 1F3FC 200D 2640 FE0F ; fully-qualified # 🚣🏼♀️ woman rowing boat: medium-light skin tone
+1F6A3 1F3FC 200D 2640 ; non-fully-qualified # 🚣🏼♀ woman rowing boat: medium-light skin tone
+1F6A3 1F3FD 200D 2640 FE0F ; fully-qualified # 🚣🏽♀️ woman rowing boat: medium skin tone
+1F6A3 1F3FD 200D 2640 ; non-fully-qualified # 🚣🏽♀ woman rowing boat: medium skin tone
+1F6A3 1F3FE 200D 2640 FE0F ; fully-qualified # 🚣🏾♀️ woman rowing boat: medium-dark skin tone
+1F6A3 1F3FE 200D 2640 ; non-fully-qualified # 🚣🏾♀ woman rowing boat: medium-dark skin tone
+1F6A3 1F3FF 200D 2640 FE0F ; fully-qualified # 🚣🏿♀️ woman rowing boat: dark skin tone
+1F6A3 1F3FF 200D 2640 ; non-fully-qualified # 🚣🏿♀ woman rowing boat: dark skin tone
+1F3CA ; fully-qualified # 🏊 person swimming
+1F3CA 1F3FB ; fully-qualified # 🏊🏻 person swimming: light skin tone
+1F3CA 1F3FC ; fully-qualified # 🏊🏼 person swimming: medium-light skin tone
+1F3CA 1F3FD ; fully-qualified # 🏊🏽 person swimming: medium skin tone
+1F3CA 1F3FE ; fully-qualified # 🏊🏾 person swimming: medium-dark skin tone
+1F3CA 1F3FF ; fully-qualified # 🏊🏿 person swimming: dark skin tone
+1F3CA 200D 2642 FE0F ; fully-qualified # 🏊♂️ man swimming
+1F3CA 200D 2642 ; non-fully-qualified # 🏊♂ man swimming
+1F3CA 1F3FB 200D 2642 FE0F ; fully-qualified # 🏊🏻♂️ man swimming: light skin tone
+1F3CA 1F3FB 200D 2642 ; non-fully-qualified # 🏊🏻♂ man swimming: light skin tone
+1F3CA 1F3FC 200D 2642 FE0F ; fully-qualified # 🏊🏼♂️ man swimming: medium-light skin tone
+1F3CA 1F3FC 200D 2642 ; non-fully-qualified # 🏊🏼♂ man swimming: medium-light skin tone
+1F3CA 1F3FD 200D 2642 FE0F ; fully-qualified # 🏊🏽♂️ man swimming: medium skin tone
+1F3CA 1F3FD 200D 2642 ; non-fully-qualified # 🏊🏽♂ man swimming: medium skin tone
+1F3CA 1F3FE 200D 2642 FE0F ; fully-qualified # 🏊🏾♂️ man swimming: medium-dark skin tone
+1F3CA 1F3FE 200D 2642 ; non-fully-qualified # 🏊🏾♂ man swimming: medium-dark skin tone
+1F3CA 1F3FF 200D 2642 FE0F ; fully-qualified # 🏊🏿♂️ man swimming: dark skin tone
+1F3CA 1F3FF 200D 2642 ; non-fully-qualified # 🏊🏿♂ man swimming: dark skin tone
+1F3CA 200D 2640 FE0F ; fully-qualified # 🏊♀️ woman swimming
+1F3CA 200D 2640 ; non-fully-qualified # 🏊♀ woman swimming
+1F3CA 1F3FB 200D 2640 FE0F ; fully-qualified # 🏊🏻♀️ woman swimming: light skin tone
+1F3CA 1F3FB 200D 2640 ; non-fully-qualified # 🏊🏻♀ woman swimming: light skin tone
+1F3CA 1F3FC 200D 2640 FE0F ; fully-qualified # 🏊🏼♀️ woman swimming: medium-light skin tone
+1F3CA 1F3FC 200D 2640 ; non-fully-qualified # 🏊🏼♀ woman swimming: medium-light skin tone
+1F3CA 1F3FD 200D 2640 FE0F ; fully-qualified # 🏊🏽♀️ woman swimming: medium skin tone
+1F3CA 1F3FD 200D 2640 ; non-fully-qualified # 🏊🏽♀ woman swimming: medium skin tone
+1F3CA 1F3FE 200D 2640 FE0F ; fully-qualified # 🏊🏾♀️ woman swimming: medium-dark skin tone
+1F3CA 1F3FE 200D 2640 ; non-fully-qualified # 🏊🏾♀ woman swimming: medium-dark skin tone
+1F3CA 1F3FF 200D 2640 FE0F ; fully-qualified # 🏊🏿♀️ woman swimming: dark skin tone
+1F3CA 1F3FF 200D 2640 ; non-fully-qualified # 🏊🏿♀ woman swimming: dark skin tone
+26F9 FE0F ; fully-qualified # ⛹️ person bouncing ball
+26F9 1F3FB ; fully-qualified # ⛹🏻 person bouncing ball: light skin tone
+26F9 1F3FC ; fully-qualified # ⛹🏼 person bouncing ball: medium-light skin tone
+26F9 1F3FD ; fully-qualified # ⛹🏽 person bouncing ball: medium skin tone
+26F9 1F3FE ; fully-qualified # ⛹🏾 person bouncing ball: medium-dark skin tone
+26F9 1F3FF ; fully-qualified # ⛹🏿 person bouncing ball: dark skin tone
+26F9 FE0F 200D 2642 FE0F ; fully-qualified # ⛹️♂️ man bouncing ball
+26F9 200D 2642 ; non-fully-qualified # ⛹♂ man bouncing ball
+26F9 FE0F 200D 2642 ; non-fully-qualified # ⛹️♂ man bouncing ball
+26F9 200D 2642 FE0F ; non-fully-qualified # ⛹♂️ man bouncing ball
+26F9 1F3FB 200D 2642 FE0F ; fully-qualified # ⛹🏻♂️ man bouncing ball: light skin tone
+26F9 1F3FB 200D 2642 ; non-fully-qualified # ⛹🏻♂ man bouncing ball: light skin tone
+26F9 1F3FC 200D 2642 FE0F ; fully-qualified # ⛹🏼♂️ man bouncing ball: medium-light skin tone
+26F9 1F3FC 200D 2642 ; non-fully-qualified # ⛹🏼♂ man bouncing ball: medium-light skin tone
+26F9 1F3FD 200D 2642 FE0F ; fully-qualified # ⛹🏽♂️ man bouncing ball: medium skin tone
+26F9 1F3FD 200D 2642 ; non-fully-qualified # ⛹🏽♂ man bouncing ball: medium skin tone
+26F9 1F3FE 200D 2642 FE0F ; fully-qualified # ⛹🏾♂️ man bouncing ball: medium-dark skin tone
+26F9 1F3FE 200D 2642 ; non-fully-qualified # ⛹🏾♂ man bouncing ball: medium-dark skin tone
+26F9 1F3FF 200D 2642 FE0F ; fully-qualified # ⛹🏿♂️ man bouncing ball: dark skin tone
+26F9 1F3FF 200D 2642 ; non-fully-qualified # ⛹🏿♂ man bouncing ball: dark skin tone
+26F9 FE0F 200D 2640 FE0F ; fully-qualified # ⛹️♀️ woman bouncing ball
+26F9 200D 2640 ; non-fully-qualified # ⛹♀ woman bouncing ball
+26F9 FE0F 200D 2640 ; non-fully-qualified # ⛹️♀ woman bouncing ball
+26F9 200D 2640 FE0F ; non-fully-qualified # ⛹♀️ woman bouncing ball
+26F9 1F3FB 200D 2640 FE0F ; fully-qualified # ⛹🏻♀️ woman bouncing ball: light skin tone
+26F9 1F3FB 200D 2640 ; non-fully-qualified # ⛹🏻♀ woman bouncing ball: light skin tone
+26F9 1F3FC 200D 2640 FE0F ; fully-qualified # ⛹🏼♀️ woman bouncing ball: medium-light skin tone
+26F9 1F3FC 200D 2640 ; non-fully-qualified # ⛹🏼♀ woman bouncing ball: medium-light skin tone
+26F9 1F3FD 200D 2640 FE0F ; fully-qualified # ⛹🏽♀️ woman bouncing ball: medium skin tone
+26F9 1F3FD 200D 2640 ; non-fully-qualified # ⛹🏽♀ woman bouncing ball: medium skin tone
+26F9 1F3FE 200D 2640 FE0F ; fully-qualified # ⛹🏾♀️ woman bouncing ball: medium-dark skin tone
+26F9 1F3FE 200D 2640 ; non-fully-qualified # ⛹🏾♀ woman bouncing ball: medium-dark skin tone
+26F9 1F3FF 200D 2640 FE0F ; fully-qualified # ⛹🏿♀️ woman bouncing ball: dark skin tone
+26F9 1F3FF 200D 2640 ; non-fully-qualified # ⛹🏿♀ woman bouncing ball: dark skin tone
+1F3CB FE0F ; fully-qualified # 🏋️ person lifting weights
+1F3CB 1F3FB ; fully-qualified # 🏋🏻 person lifting weights: light skin tone
+1F3CB 1F3FC ; fully-qualified # 🏋🏼 person lifting weights: medium-light skin tone
+1F3CB 1F3FD ; fully-qualified # 🏋🏽 person lifting weights: medium skin tone
+1F3CB 1F3FE ; fully-qualified # 🏋🏾 person lifting weights: medium-dark skin tone
+1F3CB 1F3FF ; fully-qualified # 🏋🏿 person lifting weights: dark skin tone
+1F3CB FE0F 200D 2642 FE0F ; fully-qualified # 🏋️♂️ man lifting weights
+1F3CB 200D 2642 ; non-fully-qualified # 🏋♂ man lifting weights
+1F3CB FE0F 200D 2642 ; non-fully-qualified # 🏋️♂ man lifting weights
+1F3CB 200D 2642 FE0F ; non-fully-qualified # 🏋♂️ man lifting weights
+1F3CB 1F3FB 200D 2642 FE0F ; fully-qualified # 🏋🏻♂️ man lifting weights: light skin tone
+1F3CB 1F3FB 200D 2642 ; non-fully-qualified # 🏋🏻♂ man lifting weights: light skin tone
+1F3CB 1F3FC 200D 2642 FE0F ; fully-qualified # 🏋🏼♂️ man lifting weights: medium-light skin tone
+1F3CB 1F3FC 200D 2642 ; non-fully-qualified # 🏋🏼♂ man lifting weights: medium-light skin tone
+1F3CB 1F3FD 200D 2642 FE0F ; fully-qualified # 🏋🏽♂️ man lifting weights: medium skin tone
+1F3CB 1F3FD 200D 2642 ; non-fully-qualified # 🏋🏽♂ man lifting weights: medium skin tone
+1F3CB 1F3FE 200D 2642 FE0F ; fully-qualified # 🏋🏾♂️ man lifting weights: medium-dark skin tone
+1F3CB 1F3FE 200D 2642 ; non-fully-qualified # 🏋🏾♂ man lifting weights: medium-dark skin tone
+1F3CB 1F3FF 200D 2642 FE0F ; fully-qualified # 🏋🏿♂️ man lifting weights: dark skin tone
+1F3CB 1F3FF 200D 2642 ; non-fully-qualified # 🏋🏿♂ man lifting weights: dark skin tone
+1F3CB FE0F 200D 2640 FE0F ; fully-qualified # 🏋️♀️ woman lifting weights
+1F3CB 200D 2640 ; non-fully-qualified # 🏋♀ woman lifting weights
+1F3CB FE0F 200D 2640 ; non-fully-qualified # 🏋️♀ woman lifting weights
+1F3CB 200D 2640 FE0F ; non-fully-qualified # 🏋♀️ woman lifting weights
+1F3CB 1F3FB 200D 2640 FE0F ; fully-qualified # 🏋🏻♀️ woman lifting weights: light skin tone
+1F3CB 1F3FB 200D 2640 ; non-fully-qualified # 🏋🏻♀ woman lifting weights: light skin tone
+1F3CB 1F3FC 200D 2640 FE0F ; fully-qualified # 🏋🏼♀️ woman lifting weights: medium-light skin tone
+1F3CB 1F3FC 200D 2640 ; non-fully-qualified # 🏋🏼♀ woman lifting weights: medium-light skin tone
+1F3CB 1F3FD 200D 2640 FE0F ; fully-qualified # 🏋🏽♀️ woman lifting weights: medium skin tone
+1F3CB 1F3FD 200D 2640 ; non-fully-qualified # 🏋🏽♀ woman lifting weights: medium skin tone
+1F3CB 1F3FE 200D 2640 FE0F ; fully-qualified # 🏋🏾♀️ woman lifting weights: medium-dark skin tone
+1F3CB 1F3FE 200D 2640 ; non-fully-qualified # 🏋🏾♀ woman lifting weights: medium-dark skin tone
+1F3CB 1F3FF 200D 2640 FE0F ; fully-qualified # 🏋🏿♀️ woman lifting weights: dark skin tone
+1F3CB 1F3FF 200D 2640 ; non-fully-qualified # 🏋🏿♀ woman lifting weights: dark skin tone
+1F6B4 ; fully-qualified # 🚴 person biking
+1F6B4 1F3FB ; fully-qualified # 🚴🏻 person biking: light skin tone
+1F6B4 1F3FC ; fully-qualified # 🚴🏼 person biking: medium-light skin tone
+1F6B4 1F3FD ; fully-qualified # 🚴🏽 person biking: medium skin tone
+1F6B4 1F3FE ; fully-qualified # 🚴🏾 person biking: medium-dark skin tone
+1F6B4 1F3FF ; fully-qualified # 🚴🏿 person biking: dark skin tone
+1F6B4 200D 2642 FE0F ; fully-qualified # 🚴♂️ man biking
+1F6B4 200D 2642 ; non-fully-qualified # 🚴♂ man biking
+1F6B4 1F3FB 200D 2642 FE0F ; fully-qualified # 🚴🏻♂️ man biking: light skin tone
+1F6B4 1F3FB 200D 2642 ; non-fully-qualified # 🚴🏻♂ man biking: light skin tone
+1F6B4 1F3FC 200D 2642 FE0F ; fully-qualified # 🚴🏼♂️ man biking: medium-light skin tone
+1F6B4 1F3FC 200D 2642 ; non-fully-qualified # 🚴🏼♂ man biking: medium-light skin tone
+1F6B4 1F3FD 200D 2642 FE0F ; fully-qualified # 🚴🏽♂️ man biking: medium skin tone
+1F6B4 1F3FD 200D 2642 ; non-fully-qualified # 🚴🏽♂ man biking: medium skin tone
+1F6B4 1F3FE 200D 2642 FE0F ; fully-qualified # 🚴🏾♂️ man biking: medium-dark skin tone
+1F6B4 1F3FE 200D 2642 ; non-fully-qualified # 🚴🏾♂ man biking: medium-dark skin tone
+1F6B4 1F3FF 200D 2642 FE0F ; fully-qualified # 🚴🏿♂️ man biking: dark skin tone
+1F6B4 1F3FF 200D 2642 ; non-fully-qualified # 🚴🏿♂ man biking: dark skin tone
+1F6B4 200D 2640 FE0F ; fully-qualified # 🚴♀️ woman biking
+1F6B4 200D 2640 ; non-fully-qualified # 🚴♀ woman biking
+1F6B4 1F3FB 200D 2640 FE0F ; fully-qualified # 🚴🏻♀️ woman biking: light skin tone
+1F6B4 1F3FB 200D 2640 ; non-fully-qualified # 🚴🏻♀ woman biking: light skin tone
+1F6B4 1F3FC 200D 2640 FE0F ; fully-qualified # 🚴🏼♀️ woman biking: medium-light skin tone
+1F6B4 1F3FC 200D 2640 ; non-fully-qualified # 🚴🏼♀ woman biking: medium-light skin tone
+1F6B4 1F3FD 200D 2640 FE0F ; fully-qualified # 🚴🏽♀️ woman biking: medium skin tone
+1F6B4 1F3FD 200D 2640 ; non-fully-qualified # 🚴🏽♀ woman biking: medium skin tone
+1F6B4 1F3FE 200D 2640 FE0F ; fully-qualified # 🚴🏾♀️ woman biking: medium-dark skin tone
+1F6B4 1F3FE 200D 2640 ; non-fully-qualified # 🚴🏾♀ woman biking: medium-dark skin tone
+1F6B4 1F3FF 200D 2640 FE0F ; fully-qualified # 🚴🏿♀️ woman biking: dark skin tone
+1F6B4 1F3FF 200D 2640 ; non-fully-qualified # 🚴🏿♀ woman biking: dark skin tone
+1F6B5 ; fully-qualified # 🚵 person mountain biking
+1F6B5 1F3FB ; fully-qualified # 🚵🏻 person mountain biking: light skin tone
+1F6B5 1F3FC ; fully-qualified # 🚵🏼 person mountain biking: medium-light skin tone
+1F6B5 1F3FD ; fully-qualified # 🚵🏽 person mountain biking: medium skin tone
+1F6B5 1F3FE ; fully-qualified # 🚵🏾 person mountain biking: medium-dark skin tone
+1F6B5 1F3FF ; fully-qualified # 🚵🏿 person mountain biking: dark skin tone
+1F6B5 200D 2642 FE0F ; fully-qualified # 🚵♂️ man mountain biking
+1F6B5 200D 2642 ; non-fully-qualified # 🚵♂ man mountain biking
+1F6B5 1F3FB 200D 2642 FE0F ; fully-qualified # 🚵🏻♂️ man mountain biking: light skin tone
+1F6B5 1F3FB 200D 2642 ; non-fully-qualified # 🚵🏻♂ man mountain biking: light skin tone
+1F6B5 1F3FC 200D 2642 FE0F ; fully-qualified # 🚵🏼♂️ man mountain biking: medium-light skin tone
+1F6B5 1F3FC 200D 2642 ; non-fully-qualified # 🚵🏼♂ man mountain biking: medium-light skin tone
+1F6B5 1F3FD 200D 2642 FE0F ; fully-qualified # 🚵🏽♂️ man mountain biking: medium skin tone
+1F6B5 1F3FD 200D 2642 ; non-fully-qualified # 🚵🏽♂ man mountain biking: medium skin tone
+1F6B5 1F3FE 200D 2642 FE0F ; fully-qualified # 🚵🏾♂️ man mountain biking: medium-dark skin tone
+1F6B5 1F3FE 200D 2642 ; non-fully-qualified # 🚵🏾♂ man mountain biking: medium-dark skin tone
+1F6B5 1F3FF 200D 2642 FE0F ; fully-qualified # 🚵🏿♂️ man mountain biking: dark skin tone
+1F6B5 1F3FF 200D 2642 ; non-fully-qualified # 🚵🏿♂ man mountain biking: dark skin tone
+1F6B5 200D 2640 FE0F ; fully-qualified # 🚵♀️ woman mountain biking
+1F6B5 200D 2640 ; non-fully-qualified # 🚵♀ woman mountain biking
+1F6B5 1F3FB 200D 2640 FE0F ; fully-qualified # 🚵🏻♀️ woman mountain biking: light skin tone
+1F6B5 1F3FB 200D 2640 ; non-fully-qualified # 🚵🏻♀ woman mountain biking: light skin tone
+1F6B5 1F3FC 200D 2640 FE0F ; fully-qualified # 🚵🏼♀️ woman mountain biking: medium-light skin tone
+1F6B5 1F3FC 200D 2640 ; non-fully-qualified # 🚵🏼♀ woman mountain biking: medium-light skin tone
+1F6B5 1F3FD 200D 2640 FE0F ; fully-qualified # 🚵🏽♀️ woman mountain biking: medium skin tone
+1F6B5 1F3FD 200D 2640 ; non-fully-qualified # 🚵🏽♀ woman mountain biking: medium skin tone
+1F6B5 1F3FE 200D 2640 FE0F ; fully-qualified # 🚵🏾♀️ woman mountain biking: medium-dark skin tone
+1F6B5 1F3FE 200D 2640 ; non-fully-qualified # 🚵🏾♀ woman mountain biking: medium-dark skin tone
+1F6B5 1F3FF 200D 2640 FE0F ; fully-qualified # 🚵🏿♀️ woman mountain biking: dark skin tone
+1F6B5 1F3FF 200D 2640 ; non-fully-qualified # 🚵🏿♀ woman mountain biking: dark skin tone
+1F3CE FE0F ; fully-qualified # 🏎️ racing car
+1F3CD FE0F ; fully-qualified # 🏍️ motorcycle
+1F938 ; fully-qualified # 🤸 person cartwheeling
+1F938 1F3FB ; fully-qualified # 🤸🏻 person cartwheeling: light skin tone
+1F938 1F3FC ; fully-qualified # 🤸🏼 person cartwheeling: medium-light skin tone
+1F938 1F3FD ; fully-qualified # 🤸🏽 person cartwheeling: medium skin tone
+1F938 1F3FE ; fully-qualified # 🤸🏾 person cartwheeling: medium-dark skin tone
+1F938 1F3FF ; fully-qualified # 🤸🏿 person cartwheeling: dark skin tone
+1F938 200D 2642 FE0F ; fully-qualified # 🤸♂️ man cartwheeling
+1F938 200D 2642 ; non-fully-qualified # 🤸♂ man cartwheeling
+1F938 1F3FB 200D 2642 FE0F ; fully-qualified # 🤸🏻♂️ man cartwheeling: light skin tone
+1F938 1F3FB 200D 2642 ; non-fully-qualified # 🤸🏻♂ man cartwheeling: light skin tone
+1F938 1F3FC 200D 2642 FE0F ; fully-qualified # 🤸🏼♂️ man cartwheeling: medium-light skin tone
+1F938 1F3FC 200D 2642 ; non-fully-qualified # 🤸🏼♂ man cartwheeling: medium-light skin tone
+1F938 1F3FD 200D 2642 FE0F ; fully-qualified # 🤸🏽♂️ man cartwheeling: medium skin tone
+1F938 1F3FD 200D 2642 ; non-fully-qualified # 🤸🏽♂ man cartwheeling: medium skin tone
+1F938 1F3FE 200D 2642 FE0F ; fully-qualified # 🤸🏾♂️ man cartwheeling: medium-dark skin tone
+1F938 1F3FE 200D 2642 ; non-fully-qualified # 🤸🏾♂ man cartwheeling: medium-dark skin tone
+1F938 1F3FF 200D 2642 FE0F ; fully-qualified # 🤸🏿♂️ man cartwheeling: dark skin tone
+1F938 1F3FF 200D 2642 ; non-fully-qualified # 🤸🏿♂ man cartwheeling: dark skin tone
+1F938 200D 2640 FE0F ; fully-qualified # 🤸♀️ woman cartwheeling
+1F938 200D 2640 ; non-fully-qualified # 🤸♀ woman cartwheeling
+1F938 1F3FB 200D 2640 FE0F ; fully-qualified # 🤸🏻♀️ woman cartwheeling: light skin tone
+1F938 1F3FB 200D 2640 ; non-fully-qualified # 🤸🏻♀ woman cartwheeling: light skin tone
+1F938 1F3FC 200D 2640 FE0F ; fully-qualified # 🤸🏼♀️ woman cartwheeling: medium-light skin tone
+1F938 1F3FC 200D 2640 ; non-fully-qualified # 🤸🏼♀ woman cartwheeling: medium-light skin tone
+1F938 1F3FD 200D 2640 FE0F ; fully-qualified # 🤸🏽♀️ woman cartwheeling: medium skin tone
+1F938 1F3FD 200D 2640 ; non-fully-qualified # 🤸🏽♀ woman cartwheeling: medium skin tone
+1F938 1F3FE 200D 2640 FE0F ; fully-qualified # 🤸🏾♀️ woman cartwheeling: medium-dark skin tone
+1F938 1F3FE 200D 2640 ; non-fully-qualified # 🤸🏾♀ woman cartwheeling: medium-dark skin tone
+1F938 1F3FF 200D 2640 FE0F ; fully-qualified # 🤸🏿♀️ woman cartwheeling: dark skin tone
+1F938 1F3FF 200D 2640 ; non-fully-qualified # 🤸🏿♀ woman cartwheeling: dark skin tone
+1F93C ; fully-qualified # 🤼 people wrestling
+1F93C 200D 2642 FE0F ; fully-qualified # 🤼♂️ men wrestling
+1F93C 200D 2642 ; non-fully-qualified # 🤼♂ men wrestling
+1F93C 200D 2640 FE0F ; fully-qualified # 🤼♀️ women wrestling
+1F93C 200D 2640 ; non-fully-qualified # 🤼♀ women wrestling
+1F93D ; fully-qualified # 🤽 person playing water polo
+1F93D 1F3FB ; fully-qualified # 🤽🏻 person playing water polo: light skin tone
+1F93D 1F3FC ; fully-qualified # 🤽🏼 person playing water polo: medium-light skin tone
+1F93D 1F3FD ; fully-qualified # 🤽🏽 person playing water polo: medium skin tone
+1F93D 1F3FE ; fully-qualified # 🤽🏾 person playing water polo: medium-dark skin tone
+1F93D 1F3FF ; fully-qualified # 🤽🏿 person playing water polo: dark skin tone
+1F93D 200D 2642 FE0F ; fully-qualified # 🤽♂️ man playing water polo
+1F93D 200D 2642 ; non-fully-qualified # 🤽♂ man playing water polo
+1F93D 1F3FB 200D 2642 FE0F ; fully-qualified # 🤽🏻♂️ man playing water polo: light skin tone
+1F93D 1F3FB 200D 2642 ; non-fully-qualified # 🤽🏻♂ man playing water polo: light skin tone
+1F93D 1F3FC 200D 2642 FE0F ; fully-qualified # 🤽🏼♂️ man playing water polo: medium-light skin tone
+1F93D 1F3FC 200D 2642 ; non-fully-qualified # 🤽🏼♂ man playing water polo: medium-light skin tone
+1F93D 1F3FD 200D 2642 FE0F ; fully-qualified # 🤽🏽♂️ man playing water polo: medium skin tone
+1F93D 1F3FD 200D 2642 ; non-fully-qualified # 🤽🏽♂ man playing water polo: medium skin tone
+1F93D 1F3FE 200D 2642 FE0F ; fully-qualified # 🤽🏾♂️ man playing water polo: medium-dark skin tone
+1F93D 1F3FE 200D 2642 ; non-fully-qualified # 🤽🏾♂ man playing water polo: medium-dark skin tone
+1F93D 1F3FF 200D 2642 FE0F ; fully-qualified # 🤽🏿♂️ man playing water polo: dark skin tone
+1F93D 1F3FF 200D 2642 ; non-fully-qualified # 🤽🏿♂ man playing water polo: dark skin tone
+1F93D 200D 2640 FE0F ; fully-qualified # 🤽♀️ woman playing water polo
+1F93D 200D 2640 ; non-fully-qualified # 🤽♀ woman playing water polo
+1F93D 1F3FB 200D 2640 FE0F ; fully-qualified # 🤽🏻♀️ woman playing water polo: light skin tone
+1F93D 1F3FB 200D 2640 ; non-fully-qualified # 🤽🏻♀ woman playing water polo: light skin tone
+1F93D 1F3FC 200D 2640 FE0F ; fully-qualified # 🤽🏼♀️ woman playing water polo: medium-light skin tone
+1F93D 1F3FC 200D 2640 ; non-fully-qualified # 🤽🏼♀ woman playing water polo: medium-light skin tone
+1F93D 1F3FD 200D 2640 FE0F ; fully-qualified # 🤽🏽♀️ woman playing water polo: medium skin tone
+1F93D 1F3FD 200D 2640 ; non-fully-qualified # 🤽🏽♀ woman playing water polo: medium skin tone
+1F93D 1F3FE 200D 2640 FE0F ; fully-qualified # 🤽🏾♀️ woman playing water polo: medium-dark skin tone
+1F93D 1F3FE 200D 2640 ; non-fully-qualified # 🤽🏾♀ woman playing water polo: medium-dark skin tone
+1F93D 1F3FF 200D 2640 FE0F ; fully-qualified # 🤽🏿♀️ woman playing water polo: dark skin tone
+1F93D 1F3FF 200D 2640 ; non-fully-qualified # 🤽🏿♀ woman playing water polo: dark skin tone
+1F93E ; fully-qualified # 🤾 person playing handball
+1F93E 1F3FB ; fully-qualified # 🤾🏻 person playing handball: light skin tone
+1F93E 1F3FC ; fully-qualified # 🤾🏼 person playing handball: medium-light skin tone
+1F93E 1F3FD ; fully-qualified # 🤾🏽 person playing handball: medium skin tone
+1F93E 1F3FE ; fully-qualified # 🤾🏾 person playing handball: medium-dark skin tone
+1F93E 1F3FF ; fully-qualified # 🤾🏿 person playing handball: dark skin tone
+1F93E 200D 2642 FE0F ; fully-qualified # 🤾♂️ man playing handball
+1F93E 200D 2642 ; non-fully-qualified # 🤾♂ man playing handball
+1F93E 1F3FB 200D 2642 FE0F ; fully-qualified # 🤾🏻♂️ man playing handball: light skin tone
+1F93E 1F3FB 200D 2642 ; non-fully-qualified # 🤾🏻♂ man playing handball: light skin tone
+1F93E 1F3FC 200D 2642 FE0F ; fully-qualified # 🤾🏼♂️ man playing handball: medium-light skin tone
+1F93E 1F3FC 200D 2642 ; non-fully-qualified # 🤾🏼♂ man playing handball: medium-light skin tone
+1F93E 1F3FD 200D 2642 FE0F ; fully-qualified # 🤾🏽♂️ man playing handball: medium skin tone
+1F93E 1F3FD 200D 2642 ; non-fully-qualified # 🤾🏽♂ man playing handball: medium skin tone
+1F93E 1F3FE 200D 2642 FE0F ; fully-qualified # 🤾🏾♂️ man playing handball: medium-dark skin tone
+1F93E 1F3FE 200D 2642 ; non-fully-qualified # 🤾🏾♂ man playing handball: medium-dark skin tone
+1F93E 1F3FF 200D 2642 FE0F ; fully-qualified # 🤾🏿♂️ man playing handball: dark skin tone
+1F93E 1F3FF 200D 2642 ; non-fully-qualified # 🤾🏿♂ man playing handball: dark skin tone
+1F93E 200D 2640 FE0F ; fully-qualified # 🤾♀️ woman playing handball
+1F93E 200D 2640 ; non-fully-qualified # 🤾♀ woman playing handball
+1F93E 1F3FB 200D 2640 FE0F ; fully-qualified # 🤾🏻♀️ woman playing handball: light skin tone
+1F93E 1F3FB 200D 2640 ; non-fully-qualified # 🤾🏻♀ woman playing handball: light skin tone
+1F93E 1F3FC 200D 2640 FE0F ; fully-qualified # 🤾🏼♀️ woman playing handball: medium-light skin tone
+1F93E 1F3FC 200D 2640 ; non-fully-qualified # 🤾🏼♀ woman playing handball: medium-light skin tone
+1F93E 1F3FD 200D 2640 FE0F ; fully-qualified # 🤾🏽♀️ woman playing handball: medium skin tone
+1F93E 1F3FD 200D 2640 ; non-fully-qualified # 🤾🏽♀ woman playing handball: medium skin tone
+1F93E 1F3FE 200D 2640 FE0F ; fully-qualified # 🤾🏾♀️ woman playing handball: medium-dark skin tone
+1F93E 1F3FE 200D 2640 ; non-fully-qualified # 🤾🏾♀ woman playing handball: medium-dark skin tone
+1F93E 1F3FF 200D 2640 FE0F ; fully-qualified # 🤾🏿♀️ woman playing handball: dark skin tone
+1F93E 1F3FF 200D 2640 ; non-fully-qualified # 🤾🏿♀ woman playing handball: dark skin tone
+1F939 ; fully-qualified # 🤹 person juggling
+1F939 1F3FB ; fully-qualified # 🤹🏻 person juggling: light skin tone
+1F939 1F3FC ; fully-qualified # 🤹🏼 person juggling: medium-light skin tone
+1F939 1F3FD ; fully-qualified # 🤹🏽 person juggling: medium skin tone
+1F939 1F3FE ; fully-qualified # 🤹🏾 person juggling: medium-dark skin tone
+1F939 1F3FF ; fully-qualified # 🤹🏿 person juggling: dark skin tone
+1F939 200D 2642 FE0F ; fully-qualified # 🤹♂️ man juggling
+1F939 200D 2642 ; non-fully-qualified # 🤹♂ man juggling
+1F939 1F3FB 200D 2642 FE0F ; fully-qualified # 🤹🏻♂️ man juggling: light skin tone
+1F939 1F3FB 200D 2642 ; non-fully-qualified # 🤹🏻♂ man juggling: light skin tone
+1F939 1F3FC 200D 2642 FE0F ; fully-qualified # 🤹🏼♂️ man juggling: medium-light skin tone
+1F939 1F3FC 200D 2642 ; non-fully-qualified # 🤹🏼♂ man juggling: medium-light skin tone
+1F939 1F3FD 200D 2642 FE0F ; fully-qualified # 🤹🏽♂️ man juggling: medium skin tone
+1F939 1F3FD 200D 2642 ; non-fully-qualified # 🤹🏽♂ man juggling: medium skin tone
+1F939 1F3FE 200D 2642 FE0F ; fully-qualified # 🤹🏾♂️ man juggling: medium-dark skin tone
+1F939 1F3FE 200D 2642 ; non-fully-qualified # 🤹🏾♂ man juggling: medium-dark skin tone
+1F939 1F3FF 200D 2642 FE0F ; fully-qualified # 🤹🏿♂️ man juggling: dark skin tone
+1F939 1F3FF 200D 2642 ; non-fully-qualified # 🤹🏿♂ man juggling: dark skin tone
+1F939 200D 2640 FE0F ; fully-qualified # 🤹♀️ woman juggling
+1F939 200D 2640 ; non-fully-qualified # 🤹♀ woman juggling
+1F939 1F3FB 200D 2640 FE0F ; fully-qualified # 🤹🏻♀️ woman juggling: light skin tone
+1F939 1F3FB 200D 2640 ; non-fully-qualified # 🤹🏻♀ woman juggling: light skin tone
+1F939 1F3FC 200D 2640 FE0F ; fully-qualified # 🤹🏼♀️ woman juggling: medium-light skin tone
+1F939 1F3FC 200D 2640 ; non-fully-qualified # 🤹🏼♀ woman juggling: medium-light skin tone
+1F939 1F3FD 200D 2640 FE0F ; fully-qualified # 🤹🏽♀️ woman juggling: medium skin tone
+1F939 1F3FD 200D 2640 ; non-fully-qualified # 🤹🏽♀ woman juggling: medium skin tone
+1F939 1F3FE 200D 2640 FE0F ; fully-qualified # 🤹🏾♀️ woman juggling: medium-dark skin tone
+1F939 1F3FE 200D 2640 ; non-fully-qualified # 🤹🏾♀ woman juggling: medium-dark skin tone
+1F939 1F3FF 200D 2640 FE0F ; fully-qualified # 🤹🏿♀️ woman juggling: dark skin tone
+1F939 1F3FF 200D 2640 ; non-fully-qualified # 🤹🏿♀ woman juggling: dark skin tone
+
+# subgroup: family
+1F46B ; fully-qualified # 👫 man and woman holding hands
+1F46C ; fully-qualified # 👬 two men holding hands
+1F46D ; fully-qualified # 👭 two women holding hands
+1F48F ; fully-qualified # 💏 kiss
+1F469 200D 2764 FE0F 200D 1F48B 200D 1F468 ; fully-qualified # 👩❤️💋👨 kiss: woman, man
+1F469 200D 2764 200D 1F48B 200D 1F468 ; non-fully-qualified # 👩❤💋👨 kiss: woman, man
+1F468 200D 2764 FE0F 200D 1F48B 200D 1F468 ; fully-qualified # 👨❤️💋👨 kiss: man, man
+1F468 200D 2764 200D 1F48B 200D 1F468 ; non-fully-qualified # 👨❤💋👨 kiss: man, man
+1F469 200D 2764 FE0F 200D 1F48B 200D 1F469 ; fully-qualified # 👩❤️💋👩 kiss: woman, woman
+1F469 200D 2764 200D 1F48B 200D 1F469 ; non-fully-qualified # 👩❤💋👩 kiss: woman, woman
+1F491 ; fully-qualified # 💑 couple with heart
+1F469 200D 2764 FE0F 200D 1F468 ; fully-qualified # 👩❤️👨 couple with heart: woman, man
+1F469 200D 2764 200D 1F468 ; non-fully-qualified # 👩❤👨 couple with heart: woman, man
+1F468 200D 2764 FE0F 200D 1F468 ; fully-qualified # 👨❤️👨 couple with heart: man, man
+1F468 200D 2764 200D 1F468 ; non-fully-qualified # 👨❤👨 couple with heart: man, man
+1F469 200D 2764 FE0F 200D 1F469 ; fully-qualified # 👩❤️👩 couple with heart: woman, woman
+1F469 200D 2764 200D 1F469 ; non-fully-qualified # 👩❤👩 couple with heart: woman, woman
+1F46A ; fully-qualified # 👪 family
+1F468 200D 1F469 200D 1F466 ; fully-qualified # 👨👩👦 family: man, woman, boy
+1F468 200D 1F469 200D 1F467 ; fully-qualified # 👨👩👧 family: man, woman, girl
+1F468 200D 1F469 200D 1F467 200D 1F466 ; fully-qualified # 👨👩👧👦 family: man, woman, girl, boy
+1F468 200D 1F469 200D 1F466 200D 1F466 ; fully-qualified # 👨👩👦👦 family: man, woman, boy, boy
+1F468 200D 1F469 200D 1F467 200D 1F467 ; fully-qualified # 👨👩👧👧 family: man, woman, girl, girl
+1F468 200D 1F468 200D 1F466 ; fully-qualified # 👨👨👦 family: man, man, boy
+1F468 200D 1F468 200D 1F467 ; fully-qualified # 👨👨👧 family: man, man, girl
+1F468 200D 1F468 200D 1F467 200D 1F466 ; fully-qualified # 👨👨👧👦 family: man, man, girl, boy
+1F468 200D 1F468 200D 1F466 200D 1F466 ; fully-qualified # 👨👨👦👦 family: man, man, boy, boy
+1F468 200D 1F468 200D 1F467 200D 1F467 ; fully-qualified # 👨👨👧👧 family: man, man, girl, girl
+1F469 200D 1F469 200D 1F466 ; fully-qualified # 👩👩👦 family: woman, woman, boy
+1F469 200D 1F469 200D 1F467 ; fully-qualified # 👩👩👧 family: woman, woman, girl
+1F469 200D 1F469 200D 1F467 200D 1F466 ; fully-qualified # 👩👩👧👦 family: woman, woman, girl, boy
+1F469 200D 1F469 200D 1F466 200D 1F466 ; fully-qualified # 👩👩👦👦 family: woman, woman, boy, boy
+1F469 200D 1F469 200D 1F467 200D 1F467 ; fully-qualified # 👩👩👧👧 family: woman, woman, girl, girl
+1F468 200D 1F466 ; fully-qualified # 👨👦 family: man, boy
+1F468 200D 1F466 200D 1F466 ; fully-qualified # 👨👦👦 family: man, boy, boy
+1F468 200D 1F467 ; fully-qualified # 👨👧 family: man, girl
+1F468 200D 1F467 200D 1F466 ; fully-qualified # 👨👧👦 family: man, girl, boy
+1F468 200D 1F467 200D 1F467 ; fully-qualified # 👨👧👧 family: man, girl, girl
+1F469 200D 1F466 ; fully-qualified # 👩👦 family: woman, boy
+1F469 200D 1F466 200D 1F466 ; fully-qualified # 👩👦👦 family: woman, boy, boy
+1F469 200D 1F467 ; fully-qualified # 👩👧 family: woman, girl
+1F469 200D 1F467 200D 1F466 ; fully-qualified # 👩👧👦 family: woman, girl, boy
+1F469 200D 1F467 200D 1F467 ; fully-qualified # 👩👧👧 family: woman, girl, girl
+
+# subgroup: body
+1F4AA ; fully-qualified # 💪 flexed biceps
+1F4AA 1F3FB ; fully-qualified # 💪🏻 flexed biceps: light skin tone
+1F4AA 1F3FC ; fully-qualified # 💪🏼 flexed biceps: medium-light skin tone
+1F4AA 1F3FD ; fully-qualified # 💪🏽 flexed biceps: medium skin tone
+1F4AA 1F3FE ; fully-qualified # 💪🏾 flexed biceps: medium-dark skin tone
+1F4AA 1F3FF ; fully-qualified # 💪🏿 flexed biceps: dark skin tone
+1F933 ; fully-qualified # 🤳 selfie
+1F933 1F3FB ; fully-qualified # 🤳🏻 selfie: light skin tone
+1F933 1F3FC ; fully-qualified # 🤳🏼 selfie: medium-light skin tone
+1F933 1F3FD ; fully-qualified # 🤳🏽 selfie: medium skin tone
+1F933 1F3FE ; fully-qualified # 🤳🏾 selfie: medium-dark skin tone
+1F933 1F3FF ; fully-qualified # 🤳🏿 selfie: dark skin tone
+1F448 ; fully-qualified # 👈 backhand index pointing left
+1F448 1F3FB ; fully-qualified # 👈🏻 backhand index pointing left: light skin tone
+1F448 1F3FC ; fully-qualified # 👈🏼 backhand index pointing left: medium-light skin tone
+1F448 1F3FD ; fully-qualified # 👈🏽 backhand index pointing left: medium skin tone
+1F448 1F3FE ; fully-qualified # 👈🏾 backhand index pointing left: medium-dark skin tone
+1F448 1F3FF ; fully-qualified # 👈🏿 backhand index pointing left: dark skin tone
+1F449 ; fully-qualified # 👉 backhand index pointing right
+1F449 1F3FB ; fully-qualified # 👉🏻 backhand index pointing right: light skin tone
+1F449 1F3FC ; fully-qualified # 👉🏼 backhand index pointing right: medium-light skin tone
+1F449 1F3FD ; fully-qualified # 👉🏽 backhand index pointing right: medium skin tone
+1F449 1F3FE ; fully-qualified # 👉🏾 backhand index pointing right: medium-dark skin tone
+1F449 1F3FF ; fully-qualified # 👉🏿 backhand index pointing right: dark skin tone
+261D FE0F ; fully-qualified # ☝️ index pointing up
+261D 1F3FB ; fully-qualified # ☝🏻 index pointing up: light skin tone
+261D 1F3FC ; fully-qualified # ☝🏼 index pointing up: medium-light skin tone
+261D 1F3FD ; fully-qualified # ☝🏽 index pointing up: medium skin tone
+261D 1F3FE ; fully-qualified # ☝🏾 index pointing up: medium-dark skin tone
+261D 1F3FF ; fully-qualified # ☝🏿 index pointing up: dark skin tone
+1F446 ; fully-qualified # 👆 backhand index pointing up
+1F446 1F3FB ; fully-qualified # 👆🏻 backhand index pointing up: light skin tone
+1F446 1F3FC ; fully-qualified # 👆🏼 backhand index pointing up: medium-light skin tone
+1F446 1F3FD ; fully-qualified # 👆🏽 backhand index pointing up: medium skin tone
+1F446 1F3FE ; fully-qualified # 👆🏾 backhand index pointing up: medium-dark skin tone
+1F446 1F3FF ; fully-qualified # 👆🏿 backhand index pointing up: dark skin tone
+1F595 ; fully-qualified # 🖕 middle finger
+1F595 1F3FB ; fully-qualified # 🖕🏻 middle finger: light skin tone
+1F595 1F3FC ; fully-qualified # 🖕🏼 middle finger: medium-light skin tone
+1F595 1F3FD ; fully-qualified # 🖕🏽 middle finger: medium skin tone
+1F595 1F3FE ; fully-qualified # 🖕🏾 middle finger: medium-dark skin tone
+1F595 1F3FF ; fully-qualified # 🖕🏿 middle finger: dark skin tone
+1F447 ; fully-qualified # 👇 backhand index pointing down
+1F447 1F3FB ; fully-qualified # 👇🏻 backhand index pointing down: light skin tone
+1F447 1F3FC ; fully-qualified # 👇🏼 backhand index pointing down: medium-light skin tone
+1F447 1F3FD ; fully-qualified # 👇🏽 backhand index pointing down: medium skin tone
+1F447 1F3FE ; fully-qualified # 👇🏾 backhand index pointing down: medium-dark skin tone
+1F447 1F3FF ; fully-qualified # 👇🏿 backhand index pointing down: dark skin tone
+270C FE0F ; fully-qualified # ✌️ victory hand
+270C 1F3FB ; fully-qualified # ✌🏻 victory hand: light skin tone
+270C 1F3FC ; fully-qualified # ✌🏼 victory hand: medium-light skin tone
+270C 1F3FD ; fully-qualified # ✌🏽 victory hand: medium skin tone
+270C 1F3FE ; fully-qualified # ✌🏾 victory hand: medium-dark skin tone
+270C 1F3FF ; fully-qualified # ✌🏿 victory hand: dark skin tone
+1F91E ; fully-qualified # 🤞 crossed fingers
+1F91E 1F3FB ; fully-qualified # 🤞🏻 crossed fingers: light skin tone
+1F91E 1F3FC ; fully-qualified # 🤞🏼 crossed fingers: medium-light skin tone
+1F91E 1F3FD ; fully-qualified # 🤞🏽 crossed fingers: medium skin tone
+1F91E 1F3FE ; fully-qualified # 🤞🏾 crossed fingers: medium-dark skin tone
+1F91E 1F3FF ; fully-qualified # 🤞🏿 crossed fingers: dark skin tone
+1F596 ; fully-qualified # 🖖 vulcan salute
+1F596 1F3FB ; fully-qualified # 🖖🏻 vulcan salute: light skin tone
+1F596 1F3FC ; fully-qualified # 🖖🏼 vulcan salute: medium-light skin tone
+1F596 1F3FD ; fully-qualified # 🖖🏽 vulcan salute: medium skin tone
+1F596 1F3FE ; fully-qualified # 🖖🏾 vulcan salute: medium-dark skin tone
+1F596 1F3FF ; fully-qualified # 🖖🏿 vulcan salute: dark skin tone
+1F918 ; fully-qualified # 🤘 sign of the horns
+1F918 1F3FB ; fully-qualified # 🤘🏻 sign of the horns: light skin tone
+1F918 1F3FC ; fully-qualified # 🤘🏼 sign of the horns: medium-light skin tone
+1F918 1F3FD ; fully-qualified # 🤘🏽 sign of the horns: medium skin tone
+1F918 1F3FE ; fully-qualified # 🤘🏾 sign of the horns: medium-dark skin tone
+1F918 1F3FF ; fully-qualified # 🤘🏿 sign of the horns: dark skin tone
+1F919 ; fully-qualified # 🤙 call me hand
+1F919 1F3FB ; fully-qualified # 🤙🏻 call me hand: light skin tone
+1F919 1F3FC ; fully-qualified # 🤙🏼 call me hand: medium-light skin tone
+1F919 1F3FD ; fully-qualified # 🤙🏽 call me hand: medium skin tone
+1F919 1F3FE ; fully-qualified # 🤙🏾 call me hand: medium-dark skin tone
+1F919 1F3FF ; fully-qualified # 🤙🏿 call me hand: dark skin tone
+1F590 FE0F ; fully-qualified # 🖐️ raised hand with fingers splayed
+1F590 1F3FB ; fully-qualified # 🖐🏻 raised hand with fingers splayed: light skin tone
+1F590 1F3FC ; fully-qualified # 🖐🏼 raised hand with fingers splayed: medium-light skin tone
+1F590 1F3FD ; fully-qualified # 🖐🏽 raised hand with fingers splayed: medium skin tone
+1F590 1F3FE ; fully-qualified # 🖐🏾 raised hand with fingers splayed: medium-dark skin tone
+1F590 1F3FF ; fully-qualified # 🖐🏿 raised hand with fingers splayed: dark skin tone
+270B ; fully-qualified # ✋ raised hand
+270B 1F3FB ; fully-qualified # ✋🏻 raised hand: light skin tone
+270B 1F3FC ; fully-qualified # ✋🏼 raised hand: medium-light skin tone
+270B 1F3FD ; fully-qualified # ✋🏽 raised hand: medium skin tone
+270B 1F3FE ; fully-qualified # ✋🏾 raised hand: medium-dark skin tone
+270B 1F3FF ; fully-qualified # ✋🏿 raised hand: dark skin tone
+1F44C ; fully-qualified # 👌 OK hand
+1F44C 1F3FB ; fully-qualified # 👌🏻 OK hand: light skin tone
+1F44C 1F3FC ; fully-qualified # 👌🏼 OK hand: medium-light skin tone
+1F44C 1F3FD ; fully-qualified # 👌🏽 OK hand: medium skin tone
+1F44C 1F3FE ; fully-qualified # 👌🏾 OK hand: medium-dark skin tone
+1F44C 1F3FF ; fully-qualified # 👌🏿 OK hand: dark skin tone
+1F44D ; fully-qualified # 👍 thumbs up
+1F44D 1F3FB ; fully-qualified # 👍🏻 thumbs up: light skin tone
+1F44D 1F3FC ; fully-qualified # 👍🏼 thumbs up: medium-light skin tone
+1F44D 1F3FD ; fully-qualified # 👍🏽 thumbs up: medium skin tone
+1F44D 1F3FE ; fully-qualified # 👍🏾 thumbs up: medium-dark skin tone
+1F44D 1F3FF ; fully-qualified # 👍🏿 thumbs up: dark skin tone
+1F44E ; fully-qualified # 👎 thumbs down
+1F44E 1F3FB ; fully-qualified # 👎🏻 thumbs down: light skin tone
+1F44E 1F3FC ; fully-qualified # 👎🏼 thumbs down: medium-light skin tone
+1F44E 1F3FD ; fully-qualified # 👎🏽 thumbs down: medium skin tone
+1F44E 1F3FE ; fully-qualified # 👎🏾 thumbs down: medium-dark skin tone
+1F44E 1F3FF ; fully-qualified # 👎🏿 thumbs down: dark skin tone
+270A ; fully-qualified # ✊ raised fist
+270A 1F3FB ; fully-qualified # ✊🏻 raised fist: light skin tone
+270A 1F3FC ; fully-qualified # ✊🏼 raised fist: medium-light skin tone
+270A 1F3FD ; fully-qualified # ✊🏽 raised fist: medium skin tone
+270A 1F3FE ; fully-qualified # ✊🏾 raised fist: medium-dark skin tone
+270A 1F3FF ; fully-qualified # ✊🏿 raised fist: dark skin tone
+1F44A ; fully-qualified # 👊 oncoming fist
+1F44A 1F3FB ; fully-qualified # 👊🏻 oncoming fist: light skin tone
+1F44A 1F3FC ; fully-qualified # 👊🏼 oncoming fist: medium-light skin tone
+1F44A 1F3FD ; fully-qualified # 👊🏽 oncoming fist: medium skin tone
+1F44A 1F3FE ; fully-qualified # 👊🏾 oncoming fist: medium-dark skin tone
+1F44A 1F3FF ; fully-qualified # 👊🏿 oncoming fist: dark skin tone
+1F91B ; fully-qualified # 🤛 left-facing fist
+1F91B 1F3FB ; fully-qualified # 🤛🏻 left-facing fist: light skin tone
+1F91B 1F3FC ; fully-qualified # 🤛🏼 left-facing fist: medium-light skin tone
+1F91B 1F3FD ; fully-qualified # 🤛🏽 left-facing fist: medium skin tone
+1F91B 1F3FE ; fully-qualified # 🤛🏾 left-facing fist: medium-dark skin tone
+1F91B 1F3FF ; fully-qualified # 🤛🏿 left-facing fist: dark skin tone
+1F91C ; fully-qualified # 🤜 right-facing fist
+1F91C 1F3FB ; fully-qualified # 🤜🏻 right-facing fist: light skin tone
+1F91C 1F3FC ; fully-qualified # 🤜🏼 right-facing fist: medium-light skin tone
+1F91C 1F3FD ; fully-qualified # 🤜🏽 right-facing fist: medium skin tone
+1F91C 1F3FE ; fully-qualified # 🤜🏾 right-facing fist: medium-dark skin tone
+1F91C 1F3FF ; fully-qualified # 🤜🏿 right-facing fist: dark skin tone
+1F91A ; fully-qualified # 🤚 raised back of hand
+1F91A 1F3FB ; fully-qualified # 🤚🏻 raised back of hand: light skin tone
+1F91A 1F3FC ; fully-qualified # 🤚🏼 raised back of hand: medium-light skin tone
+1F91A 1F3FD ; fully-qualified # 🤚🏽 raised back of hand: medium skin tone
+1F91A 1F3FE ; fully-qualified # 🤚🏾 raised back of hand: medium-dark skin tone
+1F91A 1F3FF ; fully-qualified # 🤚🏿 raised back of hand: dark skin tone
+1F44B ; fully-qualified # 👋 waving hand
+1F44B 1F3FB ; fully-qualified # 👋🏻 waving hand: light skin tone
+1F44B 1F3FC ; fully-qualified # 👋🏼 waving hand: medium-light skin tone
+1F44B 1F3FD ; fully-qualified # 👋🏽 waving hand: medium skin tone
+1F44B 1F3FE ; fully-qualified # 👋🏾 waving hand: medium-dark skin tone
+1F44B 1F3FF ; fully-qualified # 👋🏿 waving hand: dark skin tone
+1F44F ; fully-qualified # 👏 clapping hands
+1F44F 1F3FB ; fully-qualified # 👏🏻 clapping hands: light skin tone
+1F44F 1F3FC ; fully-qualified # 👏🏼 clapping hands: medium-light skin tone
+1F44F 1F3FD ; fully-qualified # 👏🏽 clapping hands: medium skin tone
+1F44F 1F3FE ; fully-qualified # 👏🏾 clapping hands: medium-dark skin tone
+1F44F 1F3FF ; fully-qualified # 👏🏿 clapping hands: dark skin tone
+270D FE0F ; fully-qualified # ✍️ writing hand
+270D 1F3FB ; fully-qualified # ✍🏻 writing hand: light skin tone
+270D 1F3FC ; fully-qualified # ✍🏼 writing hand: medium-light skin tone
+270D 1F3FD ; fully-qualified # ✍🏽 writing hand: medium skin tone
+270D 1F3FE ; fully-qualified # ✍🏾 writing hand: medium-dark skin tone
+270D 1F3FF ; fully-qualified # ✍🏿 writing hand: dark skin tone
+1F450 ; fully-qualified # 👐 open hands
+1F450 1F3FB ; fully-qualified # 👐🏻 open hands: light skin tone
+1F450 1F3FC ; fully-qualified # 👐🏼 open hands: medium-light skin tone
+1F450 1F3FD ; fully-qualified # 👐🏽 open hands: medium skin tone
+1F450 1F3FE ; fully-qualified # 👐🏾 open hands: medium-dark skin tone
+1F450 1F3FF ; fully-qualified # 👐🏿 open hands: dark skin tone
+1F64C ; fully-qualified # 🙌 raising hands
+1F64C 1F3FB ; fully-qualified # 🙌🏻 raising hands: light skin tone
+1F64C 1F3FC ; fully-qualified # 🙌🏼 raising hands: medium-light skin tone
+1F64C 1F3FD ; fully-qualified # 🙌🏽 raising hands: medium skin tone
+1F64C 1F3FE ; fully-qualified # 🙌🏾 raising hands: medium-dark skin tone
+1F64C 1F3FF ; fully-qualified # 🙌🏿 raising hands: dark skin tone
+1F64F ; fully-qualified # 🙏 folded hands
+1F64F 1F3FB ; fully-qualified # 🙏🏻 folded hands: light skin tone
+1F64F 1F3FC ; fully-qualified # 🙏🏼 folded hands: medium-light skin tone
+1F64F 1F3FD ; fully-qualified # 🙏🏽 folded hands: medium skin tone
+1F64F 1F3FE ; fully-qualified # 🙏🏾 folded hands: medium-dark skin tone
+1F64F 1F3FF ; fully-qualified # 🙏🏿 folded hands: dark skin tone
+1F91D ; fully-qualified # 🤝 handshake
+1F485 ; fully-qualified # 💅 nail polish
+1F485 1F3FB ; fully-qualified # 💅🏻 nail polish: light skin tone
+1F485 1F3FC ; fully-qualified # 💅🏼 nail polish: medium-light skin tone
+1F485 1F3FD ; fully-qualified # 💅🏽 nail polish: medium skin tone
+1F485 1F3FE ; fully-qualified # 💅🏾 nail polish: medium-dark skin tone
+1F485 1F3FF ; fully-qualified # 💅🏿 nail polish: dark skin tone
+1F442 ; fully-qualified # 👂 ear
+1F442 1F3FB ; fully-qualified # 👂🏻 ear: light skin tone
+1F442 1F3FC ; fully-qualified # 👂🏼 ear: medium-light skin tone
+1F442 1F3FD ; fully-qualified # 👂🏽 ear: medium skin tone
+1F442 1F3FE ; fully-qualified # 👂🏾 ear: medium-dark skin tone
+1F442 1F3FF ; fully-qualified # 👂🏿 ear: dark skin tone
+1F443 ; fully-qualified # 👃 nose
+1F443 1F3FB ; fully-qualified # 👃🏻 nose: light skin tone
+1F443 1F3FC ; fully-qualified # 👃🏼 nose: medium-light skin tone
+1F443 1F3FD ; fully-qualified # 👃🏽 nose: medium skin tone
+1F443 1F3FE ; fully-qualified # 👃🏾 nose: medium-dark skin tone
+1F443 1F3FF ; fully-qualified # 👃🏿 nose: dark skin tone
+1F463 ; fully-qualified # 👣 footprints
+1F440 ; fully-qualified # 👀 eyes
+1F441 FE0F ; fully-qualified # 👁️ eye
+1F441 FE0F 200D 1F5E8 FE0F ; fully-qualified # 👁️🗨️ eye in speech bubble
+1F441 200D 1F5E8 ; non-fully-qualified # 👁🗨 eye in speech bubble
+1F441 FE0F 200D 1F5E8 ; non-fully-qualified # 👁️🗨 eye in speech bubble
+1F441 200D 1F5E8 FE0F ; non-fully-qualified # 👁🗨️ eye in speech bubble
+1F445 ; fully-qualified # 👅 tongue
+1F444 ; fully-qualified # 👄 mouth
+
+# subgroup: emotion
+1F48B ; fully-qualified # 💋 kiss mark
+1F498 ; fully-qualified # 💘 heart with arrow
+2764 FE0F ; fully-qualified # ❤️ red heart
+1F493 ; fully-qualified # 💓 beating heart
+1F494 ; fully-qualified # 💔 broken heart
+1F495 ; fully-qualified # 💕 two hearts
+1F496 ; fully-qualified # 💖 sparkling heart
+1F497 ; fully-qualified # 💗 growing heart
+1F499 ; fully-qualified # 💙 blue heart
+1F49A ; fully-qualified # 💚 green heart
+1F49B ; fully-qualified # 💛 yellow heart
+1F49C ; fully-qualified # 💜 purple heart
+1F5A4 ; fully-qualified # 🖤 black heart
+1F49D ; fully-qualified # 💝 heart with ribbon
+1F49E ; fully-qualified # 💞 revolving hearts
+1F49F ; fully-qualified # 💟 heart decoration
+2763 FE0F ; fully-qualified # ❣️ heavy heart exclamation
+1F48C ; fully-qualified # 💌 love letter
+1F4A4 ; fully-qualified # 💤 zzz
+1F4A2 ; fully-qualified # 💢 anger symbol
+1F4A3 ; fully-qualified # 💣 bomb
+1F4A5 ; fully-qualified # 💥 collision
+1F4A6 ; fully-qualified # 💦 sweat droplets
+1F4A8 ; fully-qualified # 💨 dashing away
+1F4AB ; fully-qualified # 💫 dizzy
+1F4AC ; fully-qualified # 💬 speech balloon
+1F5E8 FE0F ; fully-qualified # 🗨️ left speech bubble
+1F5EF FE0F ; fully-qualified # 🗯️ right anger bubble
+1F4AD ; fully-qualified # 💭 thought balloon
+1F573 FE0F ; fully-qualified # 🕳️ hole
+
+# subgroup: clothing
+1F453 ; fully-qualified # 👓 glasses
+1F576 FE0F ; fully-qualified # 🕶️ sunglasses
+1F454 ; fully-qualified # 👔 necktie
+1F455 ; fully-qualified # 👕 t-shirt
+1F456 ; fully-qualified # 👖 jeans
+1F457 ; fully-qualified # 👗 dress
+1F458 ; fully-qualified # 👘 kimono
+1F459 ; fully-qualified # 👙 bikini
+1F45A ; fully-qualified # 👚 woman’s clothes
+1F45B ; fully-qualified # 👛 purse
+1F45C ; fully-qualified # 👜 handbag
+1F45D ; fully-qualified # 👝 clutch bag
+1F6CD FE0F ; fully-qualified # 🛍️ shopping bags
+1F392 ; fully-qualified # 🎒 school backpack
+1F45E ; fully-qualified # 👞 man’s shoe
+1F45F ; fully-qualified # 👟 running shoe
+1F460 ; fully-qualified # 👠 high-heeled shoe
+1F461 ; fully-qualified # 👡 woman’s sandal
+1F462 ; fully-qualified # 👢 woman’s boot
+1F451 ; fully-qualified # 👑 crown
+1F452 ; fully-qualified # 👒 woman’s hat
+1F3A9 ; fully-qualified # 🎩 top hat
+1F393 ; fully-qualified # 🎓 graduation cap
+26D1 FE0F ; fully-qualified # ⛑️ rescue worker’s helmet
+1F4FF ; fully-qualified # 📿 prayer beads
+1F484 ; fully-qualified # 💄 lipstick
+1F48D ; fully-qualified # 💍 ring
+1F48E ; fully-qualified # 💎 gem stone
+
+# Smileys & People subtotal: 1281
+# Smileys & People subtotal: 391 w/o modifiers
+
+# group: Animals & Nature
+
+# subgroup: animal-mammal
+1F435 ; fully-qualified # 🐵 monkey face
+1F412 ; fully-qualified # 🐒 monkey
+1F98D ; fully-qualified # 🦍 gorilla
+1F436 ; fully-qualified # 🐶 dog face
+1F415 ; fully-qualified # 🐕 dog
+1F429 ; fully-qualified # 🐩 poodle
+1F43A ; fully-qualified # 🐺 wolf face
+1F98A ; fully-qualified # 🦊 fox face
+1F431 ; fully-qualified # 🐱 cat face
+1F408 ; fully-qualified # 🐈 cat
+1F981 ; fully-qualified # 🦁 lion face
+1F42F ; fully-qualified # 🐯 tiger face
+1F405 ; fully-qualified # 🐅 tiger
+1F406 ; fully-qualified # 🐆 leopard
+1F434 ; fully-qualified # 🐴 horse face
+1F40E ; fully-qualified # 🐎 horse
+1F98C ; fully-qualified # 🦌 deer
+1F984 ; fully-qualified # 🦄 unicorn face
+1F42E ; fully-qualified # 🐮 cow face
+1F402 ; fully-qualified # 🐂 ox
+1F403 ; fully-qualified # 🐃 water buffalo
+1F404 ; fully-qualified # 🐄 cow
+1F437 ; fully-qualified # 🐷 pig face
+1F416 ; fully-qualified # 🐖 pig
+1F417 ; fully-qualified # 🐗 boar
+1F43D ; fully-qualified # 🐽 pig nose
+1F40F ; fully-qualified # 🐏 ram
+1F411 ; fully-qualified # 🐑 sheep
+1F410 ; fully-qualified # 🐐 goat
+1F42A ; fully-qualified # 🐪 camel
+1F42B ; fully-qualified # 🐫 two-hump camel
+1F418 ; fully-qualified # 🐘 elephant
+1F98F ; fully-qualified # 🦏 rhinoceros
+1F42D ; fully-qualified # 🐭 mouse face
+1F401 ; fully-qualified # 🐁 mouse
+1F400 ; fully-qualified # 🐀 rat
+1F439 ; fully-qualified # 🐹 hamster face
+1F430 ; fully-qualified # 🐰 rabbit face
+1F407 ; fully-qualified # 🐇 rabbit
+1F43F FE0F ; fully-qualified # 🐿️ chipmunk
+1F987 ; fully-qualified # 🦇 bat
+1F43B ; fully-qualified # 🐻 bear face
+1F428 ; fully-qualified # 🐨 koala
+1F43C ; fully-qualified # 🐼 panda face
+1F43E ; fully-qualified # 🐾 paw prints
+
+# subgroup: animal-bird
+1F983 ; fully-qualified # 🦃 turkey
+1F414 ; fully-qualified # 🐔 chicken
+1F413 ; fully-qualified # 🐓 rooster
+1F423 ; fully-qualified # 🐣 hatching chick
+1F424 ; fully-qualified # 🐤 baby chick
+1F425 ; fully-qualified # 🐥 front-facing baby chick
+1F426 ; fully-qualified # 🐦 bird
+1F427 ; fully-qualified # 🐧 penguin
+1F54A FE0F ; fully-qualified # 🕊️ dove
+1F985 ; fully-qualified # 🦅 eagle
+1F986 ; fully-qualified # 🦆 duck
+1F989 ; fully-qualified # 🦉 owl
+
+# subgroup: animal-amphibian
+1F438 ; fully-qualified # 🐸 frog face
+
+# subgroup: animal-reptile
+1F40A ; fully-qualified # 🐊 crocodile
+1F422 ; fully-qualified # 🐢 turtle
+1F98E ; fully-qualified # 🦎 lizard
+1F40D ; fully-qualified # 🐍 snake
+1F432 ; fully-qualified # 🐲 dragon face
+1F409 ; fully-qualified # 🐉 dragon
+
+# subgroup: animal-marine
+1F433 ; fully-qualified # 🐳 spouting whale
+1F40B ; fully-qualified # 🐋 whale
+1F42C ; fully-qualified # 🐬 dolphin
+1F41F ; fully-qualified # 🐟 fish
+1F420 ; fully-qualified # 🐠 tropical fish
+1F421 ; fully-qualified # 🐡 blowfish
+1F988 ; fully-qualified # 🦈 shark
+1F419 ; fully-qualified # 🐙 octopus
+1F41A ; fully-qualified # 🐚 spiral shell
+1F980 ; fully-qualified # 🦀 crab
+1F990 ; fully-qualified # 🦐 shrimp
+1F991 ; fully-qualified # 🦑 squid
+
+# subgroup: animal-bug
+1F98B ; fully-qualified # 🦋 butterfly
+1F40C ; fully-qualified # 🐌 snail
+1F41B ; fully-qualified # 🐛 bug
+1F41C ; fully-qualified # 🐜 ant
+1F41D ; fully-qualified # 🐝 honeybee
+1F41E ; fully-qualified # 🐞 lady beetle
+1F577 FE0F ; fully-qualified # 🕷️ spider
+1F578 FE0F ; fully-qualified # 🕸️ spider web
+1F982 ; fully-qualified # 🦂 scorpion
+
+# subgroup: plant-flower
+1F490 ; fully-qualified # 💐 bouquet
+1F338 ; fully-qualified # 🌸 cherry blossom
+1F4AE ; fully-qualified # 💮 white flower
+1F3F5 FE0F ; fully-qualified # 🏵️ rosette
+1F339 ; fully-qualified # 🌹 rose
+1F940 ; fully-qualified # 🥀 wilted flower
+1F33A ; fully-qualified # 🌺 hibiscus
+1F33B ; fully-qualified # 🌻 sunflower
+1F33C ; fully-qualified # 🌼 blossom
+1F337 ; fully-qualified # 🌷 tulip
+
+# subgroup: plant-other
+1F331 ; fully-qualified # 🌱 seedling
+1F332 ; fully-qualified # 🌲 evergreen tree
+1F333 ; fully-qualified # 🌳 deciduous tree
+1F334 ; fully-qualified # 🌴 palm tree
+1F335 ; fully-qualified # 🌵 cactus
+1F33E ; fully-qualified # 🌾 sheaf of rice
+1F33F ; fully-qualified # 🌿 herb
+2618 FE0F ; fully-qualified # ☘️ shamrock
+1F340 ; fully-qualified # 🍀 four leaf clover
+1F341 ; fully-qualified # 🍁 maple leaf
+1F342 ; fully-qualified # 🍂 fallen leaf
+1F343 ; fully-qualified # 🍃 leaf fluttering in wind
+
+# Animals & Nature subtotal: 107
+# Animals & Nature subtotal: 107 w/o modifiers
+
+# group: Food & Drink
+
+# subgroup: food-fruit
+1F347 ; fully-qualified # 🍇 grapes
+1F348 ; fully-qualified # 🍈 melon
+1F349 ; fully-qualified # 🍉 watermelon
+1F34A ; fully-qualified # 🍊 tangerine
+1F34B ; fully-qualified # 🍋 lemon
+1F34C ; fully-qualified # 🍌 banana
+1F34D ; fully-qualified # 🍍 pineapple
+1F34E ; fully-qualified # 🍎 red apple
+1F34F ; fully-qualified # 🍏 green apple
+1F350 ; fully-qualified # 🍐 pear
+1F351 ; fully-qualified # 🍑 peach
+1F352 ; fully-qualified # 🍒 cherries
+1F353 ; fully-qualified # 🍓 strawberry
+1F95D ; fully-qualified # 🥝 kiwi fruit
+1F345 ; fully-qualified # 🍅 tomato
+
+# subgroup: food-vegetable
+1F951 ; fully-qualified # 🥑 avocado
+1F346 ; fully-qualified # 🍆 eggplant
+1F954 ; fully-qualified # 🥔 potato
+1F955 ; fully-qualified # 🥕 carrot
+1F33D ; fully-qualified # 🌽 ear of corn
+1F336 FE0F ; fully-qualified # 🌶️ hot pepper
+1F952 ; fully-qualified # 🥒 cucumber
+1F344 ; fully-qualified # 🍄 mushroom
+1F95C ; fully-qualified # 🥜 peanuts
+1F330 ; fully-qualified # 🌰 chestnut
+
+# subgroup: food-prepared
+1F35E ; fully-qualified # 🍞 bread
+1F950 ; fully-qualified # 🥐 croissant
+1F956 ; fully-qualified # 🥖 baguette bread
+1F95E ; fully-qualified # 🥞 pancakes
+1F9C0 ; fully-qualified # 🧀 cheese wedge
+1F356 ; fully-qualified # 🍖 meat on bone
+1F357 ; fully-qualified # 🍗 poultry leg
+1F953 ; fully-qualified # 🥓 bacon
+1F354 ; fully-qualified # 🍔 hamburger
+1F35F ; fully-qualified # 🍟 french fries
+1F355 ; fully-qualified # 🍕 pizza
+1F32D ; fully-qualified # 🌭 hot dog
+1F32E ; fully-qualified # 🌮 taco
+1F32F ; fully-qualified # 🌯 burrito
+1F959 ; fully-qualified # 🥙 stuffed flatbread
+1F95A ; fully-qualified # 🥚 egg
+1F373 ; fully-qualified # 🍳 cooking
+1F958 ; fully-qualified # 🥘 shallow pan of food
+1F372 ; fully-qualified # 🍲 pot of food
+1F957 ; fully-qualified # 🥗 green salad
+1F37F ; fully-qualified # 🍿 popcorn
+
+# subgroup: food-asian
+1F371 ; fully-qualified # 🍱 bento box
+1F358 ; fully-qualified # 🍘 rice cracker
+1F359 ; fully-qualified # 🍙 rice ball
+1F35A ; fully-qualified # 🍚 cooked rice
+1F35B ; fully-qualified # 🍛 curry rice
+1F35C ; fully-qualified # 🍜 steaming bowl
+1F35D ; fully-qualified # 🍝 spaghetti
+1F360 ; fully-qualified # 🍠 roasted sweet potato
+1F362 ; fully-qualified # 🍢 oden
+1F363 ; fully-qualified # 🍣 sushi
+1F364 ; fully-qualified # 🍤 fried shrimp
+1F365 ; fully-qualified # 🍥 fish cake with swirl
+1F361 ; fully-qualified # 🍡 dango
+
+# subgroup: food-sweet
+1F366 ; fully-qualified # 🍦 soft ice cream
+1F367 ; fully-qualified # 🍧 shaved ice
+1F368 ; fully-qualified # 🍨 ice cream
+1F369 ; fully-qualified # 🍩 doughnut
+1F36A ; fully-qualified # 🍪 cookie
+1F382 ; fully-qualified # 🎂 birthday cake
+1F370 ; fully-qualified # 🍰 shortcake
+1F36B ; fully-qualified # 🍫 chocolate bar
+1F36C ; fully-qualified # 🍬 candy
+1F36D ; fully-qualified # 🍭 lollipop
+1F36E ; fully-qualified # 🍮 custard
+1F36F ; fully-qualified # 🍯 honey pot
+
+# subgroup: drink
+1F37C ; fully-qualified # 🍼 baby bottle
+1F95B ; fully-qualified # 🥛 glass of milk
+2615 ; fully-qualified # ☕ hot beverage
+1F375 ; fully-qualified # 🍵 teacup without handle
+1F376 ; fully-qualified # 🍶 sake
+1F37E ; fully-qualified # 🍾 bottle with popping cork
+1F377 ; fully-qualified # 🍷 wine glass
+1F378 ; fully-qualified # 🍸 cocktail glass
+1F379 ; fully-qualified # 🍹 tropical drink
+1F37A ; fully-qualified # 🍺 beer mug
+1F37B ; fully-qualified # 🍻 clinking beer mugs
+1F942 ; fully-qualified # 🥂 clinking glasses
+1F943 ; fully-qualified # 🥃 tumbler glass
+
+# subgroup: dishware
+1F37D FE0F ; fully-qualified # 🍽️ fork and knife with plate
+1F374 ; fully-qualified # 🍴 fork and knife
+1F944 ; fully-qualified # 🥄 spoon
+1F52A ; fully-qualified # 🔪 kitchen knife
+1F3FA ; fully-qualified # 🏺 amphora
+
+# Food & Drink subtotal: 89
+# Food & Drink subtotal: 89 w/o modifiers
+
+# group: Travel & Places
+
+# subgroup: place-map
+1F30D ; fully-qualified # 🌍 globe showing Europe-Africa
+1F30E ; fully-qualified # 🌎 globe showing Americas
+1F30F ; fully-qualified # 🌏 globe showing Asia-Australia
+1F310 ; fully-qualified # 🌐 globe with meridians
+1F5FA FE0F ; fully-qualified # 🗺️ world map
+1F5FE ; fully-qualified # 🗾 map of Japan
+
+# subgroup: place-geographic
+1F3D4 FE0F ; fully-qualified # 🏔️ snow-capped mountain
+26F0 FE0F ; fully-qualified # ⛰️ mountain
+1F30B ; fully-qualified # 🌋 volcano
+1F5FB ; fully-qualified # 🗻 mount fuji
+1F3D5 FE0F ; fully-qualified # 🏕️ camping
+1F3D6 FE0F ; fully-qualified # 🏖️ beach with umbrella
+1F3DC FE0F ; fully-qualified # 🏜️ desert
+1F3DD FE0F ; fully-qualified # 🏝️ desert island
+1F3DE FE0F ; fully-qualified # 🏞️ national park
+
+# subgroup: place-building
+1F3DF FE0F ; fully-qualified # 🏟️ stadium
+1F3DB FE0F ; fully-qualified # 🏛️ classical building
+1F3D7 FE0F ; fully-qualified # 🏗️ building construction
+1F3D8 FE0F ; fully-qualified # 🏘️ house
+1F3D9 FE0F ; fully-qualified # 🏙️ cityscape
+1F3DA FE0F ; fully-qualified # 🏚️ derelict house
+1F3E0 ; fully-qualified # 🏠 house
+1F3E1 ; fully-qualified # 🏡 house with garden
+1F3E2 ; fully-qualified # 🏢 office building
+1F3E3 ; fully-qualified # 🏣 Japanese post office
+1F3E4 ; fully-qualified # 🏤 post office
+1F3E5 ; fully-qualified # 🏥 hospital
+1F3E6 ; fully-qualified # 🏦 bank
+1F3E8 ; fully-qualified # 🏨 hotel
+1F3E9 ; fully-qualified # 🏩 love hotel
+1F3EA ; fully-qualified # 🏪 convenience store
+1F3EB ; fully-qualified # 🏫 school
+1F3EC ; fully-qualified # 🏬 department store
+1F3ED ; fully-qualified # 🏭 factory
+1F3EF ; fully-qualified # 🏯 Japanese castle
+1F3F0 ; fully-qualified # 🏰 castle
+1F492 ; fully-qualified # 💒 wedding
+1F5FC ; fully-qualified # 🗼 Tokyo tower
+1F5FD ; fully-qualified # 🗽 Statue of Liberty
+
+# subgroup: place-religious
+26EA ; fully-qualified # ⛪ church
+1F54C ; fully-qualified # 🕌 mosque
+1F54D ; fully-qualified # 🕍 synagogue
+26E9 FE0F ; fully-qualified # ⛩️ shinto shrine
+1F54B ; fully-qualified # 🕋 kaaba
+
+# subgroup: place-other
+26F2 ; fully-qualified # ⛲ fountain
+26FA ; fully-qualified # ⛺ tent
+1F301 ; fully-qualified # 🌁 foggy
+1F303 ; fully-qualified # 🌃 night with stars
+1F304 ; fully-qualified # 🌄 sunrise over mountains
+1F305 ; fully-qualified # 🌅 sunrise
+1F306 ; fully-qualified # 🌆 cityscape at dusk
+1F307 ; fully-qualified # 🌇 sunset
+1F309 ; fully-qualified # 🌉 bridge at night
+2668 FE0F ; fully-qualified # ♨️ hot springs
+1F30C ; fully-qualified # 🌌 milky way
+1F3A0 ; fully-qualified # 🎠 carousel horse
+1F3A1 ; fully-qualified # 🎡 ferris wheel
+1F3A2 ; fully-qualified # 🎢 roller coaster
+1F488 ; fully-qualified # 💈 barber pole
+1F3AA ; fully-qualified # 🎪 circus tent
+1F3AD ; fully-qualified # 🎭 performing arts
+1F5BC FE0F ; fully-qualified # 🖼️ framed picture
+1F3A8 ; fully-qualified # 🎨 artist palette
+1F3B0 ; fully-qualified # 🎰 slot machine
+
+# subgroup: transport-ground
+1F682 ; fully-qualified # 🚂 locomotive
+1F683 ; fully-qualified # 🚃 railway car
+1F684 ; fully-qualified # 🚄 high-speed train
+1F685 ; fully-qualified # 🚅 high-speed train with bullet nose
+1F686 ; fully-qualified # 🚆 train
+1F687 ; fully-qualified # 🚇 metro
+1F688 ; fully-qualified # 🚈 light rail
+1F689 ; fully-qualified # 🚉 station
+1F68A ; fully-qualified # 🚊 tram
+1F69D ; fully-qualified # 🚝 monorail
+1F69E ; fully-qualified # 🚞 mountain railway
+1F68B ; fully-qualified # 🚋 tram car
+1F68C ; fully-qualified # 🚌 bus
+1F68D ; fully-qualified # 🚍 oncoming bus
+1F68E ; fully-qualified # 🚎 trolleybus
+1F690 ; fully-qualified # 🚐 minibus
+1F691 ; fully-qualified # 🚑 ambulance
+1F692 ; fully-qualified # 🚒 fire engine
+1F693 ; fully-qualified # 🚓 police car
+1F694 ; fully-qualified # 🚔 oncoming police car
+1F695 ; fully-qualified # 🚕 taxi
+1F696 ; fully-qualified # 🚖 oncoming taxi
+1F697 ; fully-qualified # 🚗 automobile
+1F698 ; fully-qualified # 🚘 oncoming automobile
+1F699 ; fully-qualified # 🚙 sport utility vehicle
+1F69A ; fully-qualified # 🚚 delivery truck
+1F69B ; fully-qualified # 🚛 articulated lorry
+1F69C ; fully-qualified # 🚜 tractor
+1F6B2 ; fully-qualified # 🚲 bicycle
+1F6F4 ; fully-qualified # 🛴 kick scooter
+1F6F5 ; fully-qualified # 🛵 motor scooter
+1F68F ; fully-qualified # 🚏 bus stop
+1F6E3 FE0F ; fully-qualified # 🛣️ motorway
+1F6E4 FE0F ; fully-qualified # 🛤️ railway track
+26FD ; fully-qualified # ⛽ fuel pump
+1F6A8 ; fully-qualified # 🚨 police car light
+1F6A5 ; fully-qualified # 🚥 horizontal traffic light
+1F6A6 ; fully-qualified # 🚦 vertical traffic light
+1F6A7 ; fully-qualified # 🚧 construction
+1F6D1 ; fully-qualified # 🛑 stop sign
+
+# subgroup: transport-water
+2693 ; fully-qualified # ⚓ anchor
+26F5 ; fully-qualified # ⛵ sailboat
+1F6F6 ; fully-qualified # 🛶 canoe
+1F6A4 ; fully-qualified # 🚤 speedboat
+1F6F3 FE0F ; fully-qualified # 🛳️ passenger ship
+26F4 FE0F ; fully-qualified # ⛴️ ferry
+1F6E5 FE0F ; fully-qualified # 🛥️ motor boat
+1F6A2 ; fully-qualified # 🚢 ship
+
+# subgroup: transport-air
+2708 FE0F ; fully-qualified # ✈️ airplane
+1F6E9 FE0F ; fully-qualified # 🛩️ small airplane
+1F6EB ; fully-qualified # 🛫 airplane departure
+1F6EC ; fully-qualified # 🛬 airplane arrival
+1F4BA ; fully-qualified # 💺 seat
+1F681 ; fully-qualified # 🚁 helicopter
+1F69F ; fully-qualified # 🚟 suspension railway
+1F6A0 ; fully-qualified # 🚠 mountain cableway
+1F6A1 ; fully-qualified # 🚡 aerial tramway
+1F680 ; fully-qualified # 🚀 rocket
+1F6F0 FE0F ; fully-qualified # 🛰️ satellite
+
+# subgroup: hotel
+1F6CE FE0F ; fully-qualified # 🛎️ bellhop bell
+1F6AA ; fully-qualified # 🚪 door
+1F6CC ; fully-qualified # 🛌 person in bed
+1F6CC 1F3FB ; fully-qualified # 🛌🏻 person in bed: light skin tone
+1F6CC 1F3FC ; fully-qualified # 🛌🏼 person in bed: medium-light skin tone
+1F6CC 1F3FD ; fully-qualified # 🛌🏽 person in bed: medium skin tone
+1F6CC 1F3FE ; fully-qualified # 🛌🏾 person in bed: medium-dark skin tone
+1F6CC 1F3FF ; fully-qualified # 🛌🏿 person in bed: dark skin tone
+1F6CF FE0F ; fully-qualified # 🛏️ bed
+1F6CB FE0F ; fully-qualified # 🛋️ couch and lamp
+1F6BD ; fully-qualified # 🚽 toilet
+1F6BF ; fully-qualified # 🚿 shower
+1F6C0 ; fully-qualified # 🛀 person taking bath
+1F6C0 1F3FB ; fully-qualified # 🛀🏻 person taking bath: light skin tone
+1F6C0 1F3FC ; fully-qualified # 🛀🏼 person taking bath: medium-light skin tone
+1F6C0 1F3FD ; fully-qualified # 🛀🏽 person taking bath: medium skin tone
+1F6C0 1F3FE ; fully-qualified # 🛀🏾 person taking bath: medium-dark skin tone
+1F6C0 1F3FF ; fully-qualified # 🛀🏿 person taking bath: dark skin tone
+1F6C1 ; fully-qualified # 🛁 bathtub
+
+# subgroup: time
+231B ; fully-qualified # ⌛ hourglass
+23F3 ; fully-qualified # ⏳ hourglass with flowing sand
+231A ; fully-qualified # ⌚ watch
+23F0 ; fully-qualified # ⏰ alarm clock
+23F1 FE0F ; fully-qualified # ⏱️ stopwatch
+23F2 FE0F ; fully-qualified # ⏲️ timer clock
+1F570 FE0F ; fully-qualified # 🕰️ mantelpiece clock
+1F55B ; fully-qualified # 🕛 twelve o’clock
+1F567 ; fully-qualified # 🕧 twelve-thirty
+1F550 ; fully-qualified # 🕐 one o’clock
+1F55C ; fully-qualified # 🕜 one-thirty
+1F551 ; fully-qualified # 🕑 two o’clock
+1F55D ; fully-qualified # 🕝 two-thirty
+1F552 ; fully-qualified # 🕒 three o’clock
+1F55E ; fully-qualified # 🕞 three-thirty
+1F553 ; fully-qualified # 🕓 four o’clock
+1F55F ; fully-qualified # 🕟 four-thirty
+1F554 ; fully-qualified # 🕔 five o’clock
+1F560 ; fully-qualified # 🕠 five-thirty
+1F555 ; fully-qualified # 🕕 six o’clock
+1F561 ; fully-qualified # 🕡 six-thirty
+1F556 ; fully-qualified # 🕖 seven o’clock
+1F562 ; fully-qualified # 🕢 seven-thirty
+1F557 ; fully-qualified # 🕗 eight o’clock
+1F563 ; fully-qualified # 🕣 eight-thirty
+1F558 ; fully-qualified # 🕘 nine o’clock
+1F564 ; fully-qualified # 🕤 nine-thirty
+1F559 ; fully-qualified # 🕙 ten o’clock
+1F565 ; fully-qualified # 🕥 ten-thirty
+1F55A ; fully-qualified # 🕚 eleven o’clock
+1F566 ; fully-qualified # 🕦 eleven-thirty
+
+# subgroup: sky & weather
+1F311 ; fully-qualified # 🌑 new moon
+1F312 ; fully-qualified # 🌒 waxing crescent moon
+1F313 ; fully-qualified # 🌓 first quarter moon
+1F314 ; fully-qualified # 🌔 waxing gibbous moon
+1F315 ; fully-qualified # 🌕 full moon
+1F316 ; fully-qualified # 🌖 waning gibbous moon
+1F317 ; fully-qualified # 🌗 last quarter moon
+1F318 ; fully-qualified # 🌘 waning crescent moon
+1F319 ; fully-qualified # 🌙 crescent moon
+1F31A ; fully-qualified # 🌚 new moon face
+1F31B ; fully-qualified # 🌛 first quarter moon with face
+1F31C ; fully-qualified # 🌜 last quarter moon with face
+1F321 FE0F ; fully-qualified # 🌡️ thermometer
+2600 FE0F ; fully-qualified # ☀️ sun
+1F31D ; fully-qualified # 🌝 full moon with face
+1F31E ; fully-qualified # 🌞 sun with face
+2B50 ; fully-qualified # ⭐ white medium star
+1F31F ; fully-qualified # 🌟 glowing star
+1F320 ; fully-qualified # 🌠 shooting star
+2601 FE0F ; fully-qualified # ☁️ cloud
+26C5 ; fully-qualified # ⛅ sun behind cloud
+26C8 FE0F ; fully-qualified # ⛈️ cloud with lightning and rain
+1F324 FE0F ; fully-qualified # 🌤️ sun behind small cloud
+1F325 FE0F ; fully-qualified # 🌥️ sun behind large cloud
+1F326 FE0F ; fully-qualified # 🌦️ sun behind rain cloud
+1F327 FE0F ; fully-qualified # 🌧️ cloud with rain
+1F328 FE0F ; fully-qualified # 🌨️ cloud with snow
+1F329 FE0F ; fully-qualified # 🌩️ cloud with lightning
+1F32A FE0F ; fully-qualified # 🌪️ tornado
+1F32B FE0F ; fully-qualified # 🌫️ fog
+1F32C FE0F ; fully-qualified # 🌬️ wind face
+1F300 ; fully-qualified # 🌀 cyclone
+1F308 ; fully-qualified # 🌈 rainbow
+1F302 ; fully-qualified # 🌂 closed umbrella
+2602 FE0F ; fully-qualified # ☂️ umbrella
+2614 ; fully-qualified # ☔ umbrella with rain drops
+26F1 FE0F ; fully-qualified # ⛱️ umbrella on ground
+26A1 ; fully-qualified # ⚡ high voltage
+2744 FE0F ; fully-qualified # ❄️ snowflake
+2603 FE0F ; fully-qualified # ☃️ snowman
+26C4 ; fully-qualified # ⛄ snowman without snow
+2604 FE0F ; fully-qualified # ☄️ comet
+1F525 ; fully-qualified # 🔥 fire
+1F4A7 ; fully-qualified # 💧 droplet
+1F30A ; fully-qualified # 🌊 water wave
+
+# Travel & Places subtotal: 218
+# Travel & Places subtotal: 208 w/o modifiers
+
+# group: Activities
+
+# subgroup: event
+1F383 ; fully-qualified # 🎃 jack-o-lantern
+1F384 ; fully-qualified # 🎄 Christmas tree
+1F386 ; fully-qualified # 🎆 fireworks
+1F387 ; fully-qualified # 🎇 sparkler
+2728 ; fully-qualified # ✨ sparkles
+1F388 ; fully-qualified # 🎈 balloon
+1F389 ; fully-qualified # 🎉 party popper
+1F38A ; fully-qualified # 🎊 confetti ball
+1F38B ; fully-qualified # 🎋 tanabata tree
+1F38D ; fully-qualified # 🎍 pine decoration
+1F38E ; fully-qualified # 🎎 Japanese dolls
+1F38F ; fully-qualified # 🎏 carp streamer
+1F390 ; fully-qualified # 🎐 wind chime
+1F391 ; fully-qualified # 🎑 moon viewing ceremony
+1F380 ; fully-qualified # 🎀 ribbon
+1F381 ; fully-qualified # 🎁 wrapped gift
+1F397 FE0F ; fully-qualified # 🎗️ reminder ribbon
+1F39F FE0F ; fully-qualified # 🎟️ admission tickets
+1F3AB ; fully-qualified # 🎫 ticket
+
+# subgroup: award-medal
+1F396 FE0F ; fully-qualified # 🎖️ military medal
+1F3C6 ; fully-qualified # 🏆 trophy
+1F3C5 ; fully-qualified # 🏅 sports medal
+1F947 ; fully-qualified # 🥇 1st place medal
+1F948 ; fully-qualified # 🥈 2nd place medal
+1F949 ; fully-qualified # 🥉 3rd place medal
+
+# subgroup: sport
+26BD ; fully-qualified # ⚽ soccer ball
+26BE ; fully-qualified # ⚾ baseball
+1F3C0 ; fully-qualified # 🏀 basketball
+1F3D0 ; fully-qualified # 🏐 volleyball
+1F3C8 ; fully-qualified # 🏈 american football
+1F3C9 ; fully-qualified # 🏉 rugby football
+1F3BE ; fully-qualified # 🎾 tennis
+1F3B1 ; fully-qualified # 🎱 pool 8 ball
+1F3B3 ; fully-qualified # 🎳 bowling
+1F3CF ; fully-qualified # 🏏 cricket
+1F3D1 ; fully-qualified # 🏑 field hockey
+1F3D2 ; fully-qualified # 🏒 ice hockey
+1F3D3 ; fully-qualified # 🏓 ping pong
+1F3F8 ; fully-qualified # 🏸 badminton
+1F94A ; fully-qualified # 🥊 boxing glove
+1F94B ; fully-qualified # 🥋 martial arts uniform
+1F945 ; fully-qualified # 🥅 goal net
+1F3AF ; fully-qualified # 🎯 direct hit
+26F3 ; fully-qualified # ⛳ flag in hole
+26F8 FE0F ; fully-qualified # ⛸️ ice skate
+1F3A3 ; fully-qualified # 🎣 fishing pole
+1F3BD ; fully-qualified # 🎽 running shirt
+1F3BF ; fully-qualified # 🎿 skis
+
+# subgroup: game
+1F3AE ; fully-qualified # 🎮 video game
+1F579 FE0F ; fully-qualified # 🕹️ joystick
+1F3B2 ; fully-qualified # 🎲 game die
+2660 FE0F ; fully-qualified # ♠️ spade suit
+2665 FE0F ; fully-qualified # ♥️ heart suit
+2666 FE0F ; fully-qualified # ♦️ diamond suit
+2663 FE0F ; fully-qualified # ♣️ club suit
+1F0CF ; fully-qualified # 🃏 joker
+1F004 ; fully-qualified # 🀄 mahjong red dragon
+1F3B4 ; fully-qualified # 🎴 flower playing cards
+
+# Activities subtotal: 58
+# Activities subtotal: 58 w/o modifiers
+
+# group: Objects
+
+# subgroup: sound
+1F507 ; fully-qualified # 🔇 muted speaker
+1F508 ; fully-qualified # 🔈 speaker low volume
+1F509 ; fully-qualified # 🔉 speaker medium volume
+1F50A ; fully-qualified # 🔊 speaker high volume
+1F4E2 ; fully-qualified # 📢 loudspeaker
+1F4E3 ; fully-qualified # 📣 megaphone
+1F4EF ; fully-qualified # 📯 postal horn
+1F514 ; fully-qualified # 🔔 bell
+1F515 ; fully-qualified # 🔕 bell with slash
+
+# subgroup: music
+1F3BC ; fully-qualified # 🎼 musical score
+1F3B5 ; fully-qualified # 🎵 musical note
+1F3B6 ; fully-qualified # 🎶 musical notes
+1F399 FE0F ; fully-qualified # 🎙️ studio microphone
+1F39A FE0F ; fully-qualified # 🎚️ level slider
+1F39B FE0F ; fully-qualified # 🎛️ control knobs
+1F3A4 ; fully-qualified # 🎤 microphone
+1F3A7 ; fully-qualified # 🎧 headphone
+1F4FB ; fully-qualified # 📻 radio
+
+# subgroup: musical-instrument
+1F3B7 ; fully-qualified # 🎷 saxophone
+1F3B8 ; fully-qualified # 🎸 guitar
+1F3B9 ; fully-qualified # 🎹 musical keyboard
+1F3BA ; fully-qualified # 🎺 trumpet
+1F3BB ; fully-qualified # 🎻 violin
+1F941 ; fully-qualified # 🥁 drum
+
+# subgroup: phone
+1F4F1 ; fully-qualified # 📱 mobile phone
+1F4F2 ; fully-qualified # 📲 mobile phone with arrow
+260E FE0F ; fully-qualified # ☎️ telephone
+1F4DE ; fully-qualified # 📞 telephone receiver
+1F4DF ; fully-qualified # 📟 pager
+1F4E0 ; fully-qualified # 📠 fax machine
+
+# subgroup: computer
+1F50B ; fully-qualified # 🔋 battery
+1F50C ; fully-qualified # 🔌 electric plug
+1F4BB ; fully-qualified # 💻 laptop computer
+1F5A5 FE0F ; fully-qualified # 🖥️ desktop computer
+1F5A8 FE0F ; fully-qualified # 🖨️ printer
+2328 FE0F ; fully-qualified # ⌨️ keyboard
+1F5B1 FE0F ; fully-qualified # 🖱️ computer mouse
+1F5B2 FE0F ; fully-qualified # 🖲️ trackball
+1F4BD ; fully-qualified # 💽 computer disk
+1F4BE ; fully-qualified # 💾 floppy disk
+1F4BF ; fully-qualified # 💿 optical disk
+1F4C0 ; fully-qualified # 📀 dvd
+
+# subgroup: light & video
+1F3A5 ; fully-qualified # 🎥 movie camera
+1F39E FE0F ; fully-qualified # 🎞️ film frames
+1F4FD FE0F ; fully-qualified # 📽️ film projector
+1F3AC ; fully-qualified # 🎬 clapper board
+1F4FA ; fully-qualified # 📺 television
+1F4F7 ; fully-qualified # 📷 camera
+1F4F8 ; fully-qualified # 📸 camera with flash
+1F4F9 ; fully-qualified # 📹 video camera
+1F4FC ; fully-qualified # 📼 videocassette
+1F50D ; fully-qualified # 🔍 left-pointing magnifying glass
+1F50E ; fully-qualified # 🔎 right-pointing magnifying glass
+1F52C ; fully-qualified # 🔬 microscope
+1F52D ; fully-qualified # 🔭 telescope
+1F4E1 ; fully-qualified # 📡 satellite antenna
+1F56F FE0F ; fully-qualified # 🕯️ candle
+1F4A1 ; fully-qualified # 💡 light bulb
+1F526 ; fully-qualified # 🔦 flashlight
+1F3EE ; fully-qualified # 🏮 red paper lantern
+
+# subgroup: book-paper
+1F4D4 ; fully-qualified # 📔 notebook with decorative cover
+1F4D5 ; fully-qualified # 📕 closed book
+1F4D6 ; fully-qualified # 📖 open book
+1F4D7 ; fully-qualified # 📗 green book
+1F4D8 ; fully-qualified # 📘 blue book
+1F4D9 ; fully-qualified # 📙 orange book
+1F4DA ; fully-qualified # 📚 books
+1F4D3 ; fully-qualified # 📓 notebook
+1F4D2 ; fully-qualified # 📒 ledger
+1F4C3 ; fully-qualified # 📃 page with curl
+1F4DC ; fully-qualified # 📜 scroll
+1F4C4 ; fully-qualified # 📄 page facing up
+1F4F0 ; fully-qualified # 📰 newspaper
+1F5DE FE0F ; fully-qualified # 🗞️ rolled-up newspaper
+1F4D1 ; fully-qualified # 📑 bookmark tabs
+1F516 ; fully-qualified # 🔖 bookmark
+1F3F7 FE0F ; fully-qualified # 🏷️ label
+
+# subgroup: money
+1F4B0 ; fully-qualified # 💰 money bag
+1F4B4 ; fully-qualified # 💴 yen banknote
+1F4B5 ; fully-qualified # 💵 dollar banknote
+1F4B6 ; fully-qualified # 💶 euro banknote
+1F4B7 ; fully-qualified # 💷 pound banknote
+1F4B8 ; fully-qualified # 💸 money with wings
+1F4B3 ; fully-qualified # 💳 credit card
+1F4B9 ; fully-qualified # 💹 chart increasing with yen
+1F4B1 ; fully-qualified # 💱 currency exchange
+1F4B2 ; fully-qualified # 💲 heavy dollar sign
+
+# subgroup: mail
+2709 FE0F ; fully-qualified # ✉️ envelope
+1F4E7 ; fully-qualified # 📧 e-mail
+1F4E8 ; fully-qualified # 📨 incoming envelope
+1F4E9 ; fully-qualified # 📩 envelope with arrow
+1F4E4 ; fully-qualified # 📤 outbox tray
+1F4E5 ; fully-qualified # 📥 inbox tray
+1F4E6 ; fully-qualified # 📦 package
+1F4EB ; fully-qualified # 📫 closed mailbox with raised flag
+1F4EA ; fully-qualified # 📪 closed mailbox with lowered flag
+1F4EC ; fully-qualified # 📬 open mailbox with raised flag
+1F4ED ; fully-qualified # 📭 open mailbox with lowered flag
+1F4EE ; fully-qualified # 📮 postbox
+1F5F3 FE0F ; fully-qualified # 🗳️ ballot box with ballot
+
+# subgroup: writing
+270F FE0F ; fully-qualified # ✏️ pencil
+2712 FE0F ; fully-qualified # ✒️ black nib
+1F58B FE0F ; fully-qualified # 🖋️ fountain pen
+1F58A FE0F ; fully-qualified # 🖊️ pen
+1F58C FE0F ; fully-qualified # 🖌️ paintbrush
+1F58D FE0F ; fully-qualified # 🖍️ crayon
+1F4DD ; fully-qualified # 📝 memo
+
+# subgroup: office
+1F4BC ; fully-qualified # 💼 briefcase
+1F4C1 ; fully-qualified # 📁 file folder
+1F4C2 ; fully-qualified # 📂 open file folder
+1F5C2 FE0F ; fully-qualified # 🗂️ card index dividers
+1F4C5 ; fully-qualified # 📅 calendar
+1F4C6 ; fully-qualified # 📆 tear-off calendar
+1F5D2 FE0F ; fully-qualified # 🗒️ spiral notepad
+1F5D3 FE0F ; fully-qualified # 🗓️ spiral calendar
+1F4C7 ; fully-qualified # 📇 card index
+1F4C8 ; fully-qualified # 📈 chart increasing
+1F4C9 ; fully-qualified # 📉 chart decreasing
+1F4CA ; fully-qualified # 📊 bar chart
+1F4CB ; fully-qualified # 📋 clipboard
+1F4CC ; fully-qualified # 📌 pushpin
+1F4CD ; fully-qualified # 📍 round pushpin
+1F4CE ; fully-qualified # 📎 paperclip
+1F587 FE0F ; fully-qualified # 🖇️ linked paperclips
+1F4CF ; fully-qualified # 📏 straight ruler
+1F4D0 ; fully-qualified # 📐 triangular ruler
+2702 FE0F ; fully-qualified # ✂️ scissors
+1F5C3 FE0F ; fully-qualified # 🗃️ card file box
+1F5C4 FE0F ; fully-qualified # 🗄️ file cabinet
+1F5D1 FE0F ; fully-qualified # 🗑️ wastebasket
+
+# subgroup: lock
+1F512 ; fully-qualified # 🔒 locked
+1F513 ; fully-qualified # 🔓 unlocked
+1F50F ; fully-qualified # 🔏 locked with pen
+1F510 ; fully-qualified # 🔐 locked with key
+1F511 ; fully-qualified # 🔑 key
+1F5DD FE0F ; fully-qualified # 🗝️ old key
+
+# subgroup: tool
+1F528 ; fully-qualified # 🔨 hammer
+26CF FE0F ; fully-qualified # ⛏️ pick
+2692 FE0F ; fully-qualified # ⚒️ hammer and pick
+1F6E0 FE0F ; fully-qualified # 🛠️ hammer and wrench
+1F5E1 FE0F ; fully-qualified # 🗡️ dagger
+2694 FE0F ; fully-qualified # ⚔️ crossed swords
+1F52B ; fully-qualified # 🔫 pistol
+1F3F9 ; fully-qualified # 🏹 bow and arrow
+1F6E1 FE0F ; fully-qualified # 🛡️ shield
+1F527 ; fully-qualified # 🔧 wrench
+1F529 ; fully-qualified # 🔩 nut and bolt
+2699 FE0F ; fully-qualified # ⚙️ gear
+1F5DC FE0F ; fully-qualified # 🗜️ clamp
+2697 FE0F ; fully-qualified # ⚗️ alembic
+2696 FE0F ; fully-qualified # ⚖️ balance scale
+1F517 ; fully-qualified # 🔗 link
+26D3 FE0F ; fully-qualified # ⛓️ chains
+
+# subgroup: medical
+1F489 ; fully-qualified # 💉 syringe
+1F48A ; fully-qualified # 💊 pill
+
+# subgroup: other-object
+1F6AC ; fully-qualified # 🚬 cigarette
+26B0 FE0F ; fully-qualified # ⚰️ coffin
+26B1 FE0F ; fully-qualified # ⚱️ funeral urn
+1F5FF ; fully-qualified # 🗿 moai
+1F6E2 FE0F ; fully-qualified # 🛢️ oil drum
+1F52E ; fully-qualified # 🔮 crystal ball
+1F6D2 ; fully-qualified # 🛒 shopping cart
+
+# Objects subtotal: 162
+# Objects subtotal: 162 w/o modifiers
+
+# group: Symbols
+
+# subgroup: transport-sign
+1F3E7 ; fully-qualified # 🏧 ATM sign
+1F6AE ; fully-qualified # 🚮 litter in bin sign
+1F6B0 ; fully-qualified # 🚰 potable water
+267F ; fully-qualified # ♿ wheelchair symbol
+1F6B9 ; fully-qualified # 🚹 men’s room
+1F6BA ; fully-qualified # 🚺 women’s room
+1F6BB ; fully-qualified # 🚻 restroom
+1F6BC ; fully-qualified # 🚼 baby symbol
+1F6BE ; fully-qualified # 🚾 water closet
+1F6C2 ; fully-qualified # 🛂 passport control
+1F6C3 ; fully-qualified # 🛃 customs
+1F6C4 ; fully-qualified # 🛄 baggage claim
+1F6C5 ; fully-qualified # 🛅 left luggage
+
+# subgroup: warning
+26A0 FE0F ; fully-qualified # ⚠️ warning
+1F6B8 ; fully-qualified # 🚸 children crossing
+26D4 ; fully-qualified # ⛔ no entry
+1F6AB ; fully-qualified # 🚫 prohibited
+1F6B3 ; fully-qualified # 🚳 no bicycles
+1F6AD ; fully-qualified # 🚭 no smoking
+1F6AF ; fully-qualified # 🚯 no littering
+1F6B1 ; fully-qualified # 🚱 non-potable water
+1F6B7 ; fully-qualified # 🚷 no pedestrians
+1F4F5 ; fully-qualified # 📵 no mobile phones
+1F51E ; fully-qualified # 🔞 no one under eighteen
+2622 FE0F ; fully-qualified # ☢️ radioactive
+2623 FE0F ; fully-qualified # ☣️ biohazard
+
+# subgroup: arrow
+2B06 FE0F ; fully-qualified # ⬆️ up arrow
+2197 FE0F ; fully-qualified # ↗️ up-right arrow
+27A1 FE0F ; fully-qualified # ➡️ right arrow
+2198 FE0F ; fully-qualified # ↘️ down-right arrow
+2B07 FE0F ; fully-qualified # ⬇️ down arrow
+2199 FE0F ; fully-qualified # ↙️ down-left arrow
+2B05 FE0F ; fully-qualified # ⬅️ left arrow
+2196 FE0F ; fully-qualified # ↖️ up-left arrow
+2195 FE0F ; fully-qualified # ↕️ up-down arrow
+2194 FE0F ; fully-qualified # ↔️ left-right arrow
+21A9 FE0F ; fully-qualified # ↩️ right arrow curving left
+21AA FE0F ; fully-qualified # ↪️ left arrow curving right
+2934 FE0F ; fully-qualified # ⤴️ right arrow curving up
+2935 FE0F ; fully-qualified # ⤵️ right arrow curving down
+1F503 ; fully-qualified # 🔃 clockwise vertical arrows
+1F504 ; fully-qualified # 🔄 anticlockwise arrows button
+1F519 ; fully-qualified # 🔙 BACK arrow
+1F51A ; fully-qualified # 🔚 END arrow
+1F51B ; fully-qualified # 🔛 ON! arrow
+1F51C ; fully-qualified # 🔜 SOON arrow
+1F51D ; fully-qualified # 🔝 TOP arrow
+
+# subgroup: religion
+1F6D0 ; fully-qualified # 🛐 place of worship
+269B FE0F ; fully-qualified # ⚛️ atom symbol
+1F549 FE0F ; fully-qualified # 🕉️ om
+2721 FE0F ; fully-qualified # ✡️ star of David
+2638 FE0F ; fully-qualified # ☸️ wheel of dharma
+262F FE0F ; fully-qualified # ☯️ yin yang
+271D FE0F ; fully-qualified # ✝️ latin cross
+2626 FE0F ; fully-qualified # ☦️ orthodox cross
+262A FE0F ; fully-qualified # ☪️ star and crescent
+262E FE0F ; fully-qualified # ☮️ peace symbol
+1F54E ; fully-qualified # 🕎 menorah
+1F52F ; fully-qualified # 🔯 dotted six-pointed star
+
+# subgroup: zodiac
+2648 ; fully-qualified # ♈ Aries
+2649 ; fully-qualified # ♉ Taurus
+264A ; fully-qualified # ♊ Gemini
+264B ; fully-qualified # ♋ Cancer
+264C ; fully-qualified # ♌ Leo
+264D ; fully-qualified # ♍ Virgo
+264E ; fully-qualified # ♎ Libra
+264F ; fully-qualified # ♏ Scorpius
+2650 ; fully-qualified # ♐ Sagittarius
+2651 ; fully-qualified # ♑ Capricorn
+2652 ; fully-qualified # ♒ Aquarius
+2653 ; fully-qualified # ♓ Pisces
+26CE ; fully-qualified # ⛎ Ophiuchus
+
+# subgroup: av-symbol
+1F500 ; fully-qualified # 🔀 shuffle tracks button
+1F501 ; fully-qualified # 🔁 repeat button
+1F502 ; fully-qualified # 🔂 repeat single button
+25B6 FE0F ; fully-qualified # ▶️ play button
+23E9 ; fully-qualified # ⏩ fast-forward button
+23ED FE0F ; fully-qualified # ⏭️ next track button
+23EF FE0F ; fully-qualified # ⏯️ play or pause button
+25C0 FE0F ; fully-qualified # ◀️ reverse button
+23EA ; fully-qualified # ⏪ fast reverse button
+23EE FE0F ; fully-qualified # ⏮️ last track button
+1F53C ; fully-qualified # 🔼 up button
+23EB ; fully-qualified # ⏫ fast up button
+1F53D ; fully-qualified # 🔽 down button
+23EC ; fully-qualified # ⏬ fast down button
+23F8 FE0F ; fully-qualified # ⏸️ pause button
+23F9 FE0F ; fully-qualified # ⏹️ stop button
+23FA FE0F ; fully-qualified # ⏺️ record button
+23CF FE0F ; fully-qualified # ⏏️ eject button
+1F3A6 ; fully-qualified # 🎦 cinema
+1F505 ; fully-qualified # 🔅 dim button
+1F506 ; fully-qualified # 🔆 bright button
+1F4F6 ; fully-qualified # 📶 antenna bars
+1F4F3 ; fully-qualified # 📳 vibration mode
+1F4F4 ; fully-qualified # 📴 mobile phone off
+
+# subgroup: other-symbol
+267B FE0F ; fully-qualified # ♻️ recycling symbol
+1F4DB ; fully-qualified # 📛 name badge
+269C FE0F ; fully-qualified # ⚜️ fleur-de-lis
+1F530 ; fully-qualified # 🔰 Japanese symbol for beginner
+1F531 ; fully-qualified # 🔱 trident emblem
+2B55 ; fully-qualified # ⭕ heavy large circle
+2705 ; fully-qualified # ✅ white heavy check mark
+2611 FE0F ; fully-qualified # ☑️ ballot box with check
+2714 FE0F ; fully-qualified # ✔️ heavy check mark
+2716 FE0F ; fully-qualified # ✖️ heavy multiplication x
+274C ; fully-qualified # ❌ cross mark
+274E ; fully-qualified # ❎ cross mark button
+2795 ; fully-qualified # ➕ heavy plus sign
+2640 FE0F ; fully-qualified # ♀️ female sign
+2642 FE0F ; fully-qualified # ♂️ male sign
+2695 FE0F ; fully-qualified # ⚕️ medical symbol
+2796 ; fully-qualified # ➖ heavy minus sign
+2797 ; fully-qualified # ➗ heavy division sign
+27B0 ; fully-qualified # ➰ curly loop
+27BF ; fully-qualified # ➿ double curly loop
+303D FE0F ; fully-qualified # 〽️ part alternation mark
+2733 FE0F ; fully-qualified # ✳️ eight-spoked asterisk
+2734 FE0F ; fully-qualified # ✴️ eight-pointed star
+2747 FE0F ; fully-qualified # ❇️ sparkle
+203C FE0F ; fully-qualified # ‼️ double exclamation mark
+2049 FE0F ; fully-qualified # ⁉️ exclamation question mark
+2753 ; fully-qualified # ❓ question mark
+2754 ; fully-qualified # ❔ white question mark
+2755 ; fully-qualified # ❕ white exclamation mark
+2757 ; fully-qualified # ❗ exclamation mark
+3030 FE0F ; fully-qualified # 〰️ wavy dash
+00A9 FE0F ; fully-qualified # ©️ copyright
+00AE FE0F ; fully-qualified # ®️ registered
+2122 FE0F ; fully-qualified # ™️ trade mark
+
+# subgroup: keycap
+0023 FE0F 20E3 ; fully-qualified # #️⃣ keycap: #
+002A FE0F 20E3 ; fully-qualified # *️⃣ keycap: *
+0030 FE0F 20E3 ; fully-qualified # 0️⃣ keycap: 0
+0031 FE0F 20E3 ; fully-qualified # 1️⃣ keycap: 1
+0032 FE0F 20E3 ; fully-qualified # 2️⃣ keycap: 2
+0033 FE0F 20E3 ; fully-qualified # 3️⃣ keycap: 3
+0034 FE0F 20E3 ; fully-qualified # 4️⃣ keycap: 4
+0035 FE0F 20E3 ; fully-qualified # 5️⃣ keycap: 5
+0036 FE0F 20E3 ; fully-qualified # 6️⃣ keycap: 6
+0037 FE0F 20E3 ; fully-qualified # 7️⃣ keycap: 7
+0038 FE0F 20E3 ; fully-qualified # 8️⃣ keycap: 8
+0039 FE0F 20E3 ; fully-qualified # 9️⃣ keycap: 9
+1F51F ; fully-qualified # 🔟 keycap 10
+
+# subgroup: alphanum
+1F4AF ; fully-qualified # 💯 hundred points
+1F520 ; fully-qualified # 🔠 input latin uppercase
+1F521 ; fully-qualified # 🔡 input latin lowercase
+1F522 ; fully-qualified # 🔢 input numbers
+1F523 ; fully-qualified # 🔣 input symbols
+1F524 ; fully-qualified # 🔤 input latin letters
+1F170 FE0F ; fully-qualified # 🅰️ A button (blood type)
+1F18E ; fully-qualified # 🆎 AB button (blood type)
+1F171 FE0F ; fully-qualified # 🅱️ B button (blood type)
+1F191 ; fully-qualified # 🆑 CL button
+1F192 ; fully-qualified # 🆒 COOL button
+1F193 ; fully-qualified # 🆓 FREE button
+2139 FE0F ; fully-qualified # ℹ️ information
+1F194 ; fully-qualified # 🆔 ID button
+24C2 FE0F ; fully-qualified # Ⓜ️ circled M
+1F195 ; fully-qualified # 🆕 NEW button
+1F196 ; fully-qualified # 🆖 NG button
+1F17E FE0F ; fully-qualified # 🅾️ O button (blood type)
+1F197 ; fully-qualified # 🆗 OK button
+1F17F FE0F ; fully-qualified # 🅿️ P button
+1F198 ; fully-qualified # 🆘 SOS button
+1F199 ; fully-qualified # 🆙 UP! button
+1F19A ; fully-qualified # 🆚 VS button
+1F201 ; fully-qualified # 🈁 Japanese “here” button
+1F202 FE0F ; fully-qualified # 🈂️ Japanese “service charge” button
+1F237 FE0F ; fully-qualified # 🈷️ Japanese “monthly amount” button
+1F236 ; fully-qualified # 🈶 Japanese “not free of charge” button
+1F22F ; fully-qualified # 🈯 Japanese “reserved” button
+1F250 ; fully-qualified # 🉐 Japanese “bargain” button
+1F239 ; fully-qualified # 🈹 Japanese “discount” button
+1F21A ; fully-qualified # 🈚 Japanese “free of charge” button
+1F232 ; fully-qualified # 🈲 Japanese “prohibited” button
+1F251 ; fully-qualified # 🉑 Japanese “acceptable” button
+1F238 ; fully-qualified # 🈸 Japanese “application” button
+1F234 ; fully-qualified # 🈴 Japanese “passing grade” button
+1F233 ; fully-qualified # 🈳 Japanese “vacancy” button
+3297 FE0F ; fully-qualified # ㊗️ Japanese “congratulations” button
+3299 FE0F ; fully-qualified # ㊙️ Japanese “secret” button
+1F23A ; fully-qualified # 🈺 Japanese “open for business” button
+1F235 ; fully-qualified # 🈵 Japanese “no vacancy” button
+
+# subgroup: geometric
+25AA FE0F ; fully-qualified # ▪️ black small square
+25AB FE0F ; fully-qualified # ▫️ white small square
+25FB FE0F ; fully-qualified # ◻️ white medium square
+25FC FE0F ; fully-qualified # ◼️ black medium square
+25FD ; fully-qualified # ◽ white medium-small square
+25FE ; fully-qualified # ◾ black medium-small square
+2B1B ; fully-qualified # ⬛ black large square
+2B1C ; fully-qualified # ⬜ white large square
+1F536 ; fully-qualified # 🔶 large orange diamond
+1F537 ; fully-qualified # 🔷 large blue diamond
+1F538 ; fully-qualified # 🔸 small orange diamond
+1F539 ; fully-qualified # 🔹 small blue diamond
+1F53A ; fully-qualified # 🔺 red triangle pointed up
+1F53B ; fully-qualified # 🔻 red triangle pointed down
+1F4A0 ; fully-qualified # 💠 diamond with a dot
+1F518 ; fully-qualified # 🔘 radio button
+1F532 ; fully-qualified # 🔲 black square button
+1F533 ; fully-qualified # 🔳 white square button
+26AA ; fully-qualified # ⚪ white circle
+26AB ; fully-qualified # ⚫ black circle
+1F534 ; fully-qualified # 🔴 red circle
+1F535 ; fully-qualified # 🔵 blue circle
+
+# Symbols subtotal: 205
+# Symbols subtotal: 205 w/o modifiers
+
+# group: Flags
+
+# subgroup: flag
+1F3C1 ; fully-qualified # 🏁 chequered flag
+1F6A9 ; fully-qualified # 🚩 triangular flag
+1F38C ; fully-qualified # 🎌 crossed flags
+1F3F4 ; fully-qualified # 🏴 black flag
+1F3F3 FE0F ; fully-qualified # 🏳️ white flag
+1F3F3 FE0F 200D 1F308 ; fully-qualified # 🏳️🌈 rainbow flag
+1F3F3 200D 1F308 ; non-fully-qualified # 🏳🌈 rainbow flag
+
+# subgroup: country-flag
+1F1E6 1F1E8 ; fully-qualified # 🇦🇨 Ascension Island
+1F1E6 1F1E9 ; fully-qualified # 🇦🇩 Andorra
+1F1E6 1F1EA ; fully-qualified # 🇦🇪 United Arab Emirates
+1F1E6 1F1EB ; fully-qualified # 🇦🇫 Afghanistan
+1F1E6 1F1EC ; fully-qualified # 🇦🇬 Antigua & Barbuda
+1F1E6 1F1EE ; fully-qualified # 🇦🇮 Anguilla
+1F1E6 1F1F1 ; fully-qualified # 🇦🇱 Albania
+1F1E6 1F1F2 ; fully-qualified # 🇦🇲 Armenia
+1F1E6 1F1F4 ; fully-qualified # 🇦🇴 Angola
+1F1E6 1F1F6 ; fully-qualified # 🇦🇶 Antarctica
+1F1E6 1F1F7 ; fully-qualified # 🇦🇷 Argentina
+1F1E6 1F1F8 ; fully-qualified # 🇦🇸 American Samoa
+1F1E6 1F1F9 ; fully-qualified # 🇦🇹 Austria
+1F1E6 1F1FA ; fully-qualified # 🇦🇺 Australia
+1F1E6 1F1FC ; fully-qualified # 🇦🇼 Aruba
+1F1E6 1F1FD ; fully-qualified # 🇦🇽 Åland Islands
+1F1E6 1F1FF ; fully-qualified # 🇦🇿 Azerbaijan
+1F1E7 1F1E6 ; fully-qualified # 🇧🇦 Bosnia & Herzegovina
+1F1E7 1F1E7 ; fully-qualified # 🇧🇧 Barbados
+1F1E7 1F1E9 ; fully-qualified # 🇧🇩 Bangladesh
+1F1E7 1F1EA ; fully-qualified # 🇧🇪 Belgium
+1F1E7 1F1EB ; fully-qualified # 🇧🇫 Burkina Faso
+1F1E7 1F1EC ; fully-qualified # 🇧🇬 Bulgaria
+1F1E7 1F1ED ; fully-qualified # 🇧🇭 Bahrain
+1F1E7 1F1EE ; fully-qualified # 🇧🇮 Burundi
+1F1E7 1F1EF ; fully-qualified # 🇧🇯 Benin
+1F1E7 1F1F1 ; fully-qualified # 🇧🇱 St. Barthélemy
+1F1E7 1F1F2 ; fully-qualified # 🇧🇲 Bermuda
+1F1E7 1F1F3 ; fully-qualified # 🇧🇳 Brunei
+1F1E7 1F1F4 ; fully-qualified # 🇧🇴 Bolivia
+1F1E7 1F1F6 ; fully-qualified # 🇧🇶 Caribbean Netherlands
+1F1E7 1F1F7 ; fully-qualified # 🇧🇷 Brazil
+1F1E7 1F1F8 ; fully-qualified # 🇧🇸 Bahamas
+1F1E7 1F1F9 ; fully-qualified # 🇧🇹 Bhutan
+1F1E7 1F1FB ; fully-qualified # 🇧🇻 Bouvet Island
+1F1E7 1F1FC ; fully-qualified # 🇧🇼 Botswana
+1F1E7 1F1FE ; fully-qualified # 🇧🇾 Belarus
+1F1E7 1F1FF ; fully-qualified # 🇧🇿 Belize
+1F1E8 1F1E6 ; fully-qualified # 🇨🇦 Canada
+1F1E8 1F1E8 ; fully-qualified # 🇨🇨 Cocos (Keeling) Islands
+1F1E8 1F1E9 ; fully-qualified # 🇨🇩 Congo - Kinshasa
+1F1E8 1F1EB ; fully-qualified # 🇨🇫 Central African Republic
+1F1E8 1F1EC ; fully-qualified # 🇨🇬 Congo - Brazzaville
+1F1E8 1F1ED ; fully-qualified # 🇨🇭 Switzerland
+1F1E8 1F1EE ; fully-qualified # 🇨🇮 Côte d’Ivoire
+1F1E8 1F1F0 ; fully-qualified # 🇨🇰 Cook Islands
+1F1E8 1F1F1 ; fully-qualified # 🇨🇱 Chile
+1F1E8 1F1F2 ; fully-qualified # 🇨🇲 Cameroon
+1F1E8 1F1F3 ; fully-qualified # 🇨🇳 China
+1F1E8 1F1F4 ; fully-qualified # 🇨🇴 Colombia
+1F1E8 1F1F5 ; fully-qualified # 🇨🇵 Clipperton Island
+1F1E8 1F1F7 ; fully-qualified # 🇨🇷 Costa Rica
+1F1E8 1F1FA ; fully-qualified # 🇨🇺 Cuba
+1F1E8 1F1FB ; fully-qualified # 🇨🇻 Cape Verde
+1F1E8 1F1FC ; fully-qualified # 🇨🇼 Curaçao
+1F1E8 1F1FD ; fully-qualified # 🇨🇽 Christmas Island
+1F1E8 1F1FE ; fully-qualified # 🇨🇾 Cyprus
+1F1E8 1F1FF ; fully-qualified # 🇨🇿 Czech Republic
+1F1E9 1F1EA ; fully-qualified # 🇩🇪 Germany
+1F1E9 1F1EC ; fully-qualified # 🇩🇬 Diego Garcia
+1F1E9 1F1EF ; fully-qualified # 🇩🇯 Djibouti
+1F1E9 1F1F0 ; fully-qualified # 🇩🇰 Denmark
+1F1E9 1F1F2 ; fully-qualified # 🇩🇲 Dominica
+1F1E9 1F1F4 ; fully-qualified # 🇩🇴 Dominican Republic
+1F1E9 1F1FF ; fully-qualified # 🇩🇿 Algeria
+1F1EA 1F1E6 ; fully-qualified # 🇪🇦 Ceuta & Melilla
+1F1EA 1F1E8 ; fully-qualified # 🇪🇨 Ecuador
+1F1EA 1F1EA ; fully-qualified # 🇪🇪 Estonia
+1F1EA 1F1EC ; fully-qualified # 🇪🇬 Egypt
+1F1EA 1F1ED ; fully-qualified # 🇪🇭 Western Sahara
+1F1EA 1F1F7 ; fully-qualified # 🇪🇷 Eritrea
+1F1EA 1F1F8 ; fully-qualified # 🇪🇸 Spain
+1F1EA 1F1F9 ; fully-qualified # 🇪🇹 Ethiopia
+1F1EA 1F1FA ; fully-qualified # 🇪🇺 European Union
+1F1EB 1F1EE ; fully-qualified # 🇫🇮 Finland
+1F1EB 1F1EF ; fully-qualified # 🇫🇯 Fiji
+1F1EB 1F1F0 ; fully-qualified # 🇫🇰 Falkland Islands
+1F1EB 1F1F2 ; fully-qualified # 🇫🇲 Micronesia
+1F1EB 1F1F4 ; fully-qualified # 🇫🇴 Faroe Islands
+1F1EB 1F1F7 ; fully-qualified # 🇫🇷 France
+1F1EC 1F1E6 ; fully-qualified # 🇬🇦 Gabon
+1F1EC 1F1E7 ; fully-qualified # 🇬🇧 United Kingdom
+1F1EC 1F1E9 ; fully-qualified # 🇬🇩 Grenada
+1F1EC 1F1EA ; fully-qualified # 🇬🇪 Georgia
+1F1EC 1F1EB ; fully-qualified # 🇬🇫 French Guiana
+1F1EC 1F1EC ; fully-qualified # 🇬🇬 Guernsey
+1F1EC 1F1ED ; fully-qualified # 🇬🇭 Ghana
+1F1EC 1F1EE ; fully-qualified # 🇬🇮 Gibraltar
+1F1EC 1F1F1 ; fully-qualified # 🇬🇱 Greenland
+1F1EC 1F1F2 ; fully-qualified # 🇬🇲 Gambia
+1F1EC 1F1F3 ; fully-qualified # 🇬🇳 Guinea
+1F1EC 1F1F5 ; fully-qualified # 🇬🇵 Guadeloupe
+1F1EC 1F1F6 ; fully-qualified # 🇬🇶 Equatorial Guinea
+1F1EC 1F1F7 ; fully-qualified # 🇬🇷 Greece
+1F1EC 1F1F8 ; fully-qualified # 🇬🇸 South Georgia & South Sandwich Islands
+1F1EC 1F1F9 ; fully-qualified # 🇬🇹 Guatemala
+1F1EC 1F1FA ; fully-qualified # 🇬🇺 Guam
+1F1EC 1F1FC ; fully-qualified # 🇬🇼 Guinea-Bissau
+1F1EC 1F1FE ; fully-qualified # 🇬🇾 Guyana
+1F1ED 1F1F0 ; fully-qualified # 🇭🇰 Hong Kong SAR China
+1F1ED 1F1F2 ; fully-qualified # 🇭🇲 Heard & McDonald Islands
+1F1ED 1F1F3 ; fully-qualified # 🇭🇳 Honduras
+1F1ED 1F1F7 ; fully-qualified # 🇭🇷 Croatia
+1F1ED 1F1F9 ; fully-qualified # 🇭🇹 Haiti
+1F1ED 1F1FA ; fully-qualified # 🇭🇺 Hungary
+1F1EE 1F1E8 ; fully-qualified # 🇮🇨 Canary Islands
+1F1EE 1F1E9 ; fully-qualified # 🇮🇩 Indonesia
+1F1EE 1F1EA ; fully-qualified # 🇮🇪 Ireland
+1F1EE 1F1F1 ; fully-qualified # 🇮🇱 Israel
+1F1EE 1F1F2 ; fully-qualified # 🇮🇲 Isle of Man
+1F1EE 1F1F3 ; fully-qualified # 🇮🇳 India
+1F1EE 1F1F4 ; fully-qualified # 🇮🇴 British Indian Ocean Territory
+1F1EE 1F1F6 ; fully-qualified # 🇮🇶 Iraq
+1F1EE 1F1F7 ; fully-qualified # 🇮🇷 Iran
+1F1EE 1F1F8 ; fully-qualified # 🇮🇸 Iceland
+1F1EE 1F1F9 ; fully-qualified # 🇮🇹 Italy
+1F1EF 1F1EA ; fully-qualified # 🇯🇪 Jersey
+1F1EF 1F1F2 ; fully-qualified # 🇯🇲 Jamaica
+1F1EF 1F1F4 ; fully-qualified # 🇯🇴 Jordan
+1F1EF 1F1F5 ; fully-qualified # 🇯🇵 Japan
+1F1F0 1F1EA ; fully-qualified # 🇰🇪 Kenya
+1F1F0 1F1EC ; fully-qualified # 🇰🇬 Kyrgyzstan
+1F1F0 1F1ED ; fully-qualified # 🇰🇭 Cambodia
+1F1F0 1F1EE ; fully-qualified # 🇰🇮 Kiribati
+1F1F0 1F1F2 ; fully-qualified # 🇰🇲 Comoros
+1F1F0 1F1F3 ; fully-qualified # 🇰🇳 St. Kitts & Nevis
+1F1F0 1F1F5 ; fully-qualified # 🇰🇵 North Korea
+1F1F0 1F1F7 ; fully-qualified # 🇰🇷 South Korea
+1F1F0 1F1FC ; fully-qualified # 🇰🇼 Kuwait
+1F1F0 1F1FE ; fully-qualified # 🇰🇾 Cayman Islands
+1F1F0 1F1FF ; fully-qualified # 🇰🇿 Kazakhstan
+1F1F1 1F1E6 ; fully-qualified # 🇱🇦 Laos
+1F1F1 1F1E7 ; fully-qualified # 🇱🇧 Lebanon
+1F1F1 1F1E8 ; fully-qualified # 🇱🇨 St. Lucia
+1F1F1 1F1EE ; fully-qualified # 🇱🇮 Liechtenstein
+1F1F1 1F1F0 ; fully-qualified # 🇱🇰 Sri Lanka
+1F1F1 1F1F7 ; fully-qualified # 🇱🇷 Liberia
+1F1F1 1F1F8 ; fully-qualified # 🇱🇸 Lesotho
+1F1F1 1F1F9 ; fully-qualified # 🇱🇹 Lithuania
+1F1F1 1F1FA ; fully-qualified # 🇱🇺 Luxembourg
+1F1F1 1F1FB ; fully-qualified # 🇱🇻 Latvia
+1F1F1 1F1FE ; fully-qualified # 🇱🇾 Libya
+1F1F2 1F1E6 ; fully-qualified # 🇲🇦 Morocco
+1F1F2 1F1E8 ; fully-qualified # 🇲🇨 Monaco
+1F1F2 1F1E9 ; fully-qualified # 🇲🇩 Moldova
+1F1F2 1F1EA ; fully-qualified # 🇲🇪 Montenegro
+1F1F2 1F1EB ; fully-qualified # 🇲🇫 St. Martin
+1F1F2 1F1EC ; fully-qualified # 🇲🇬 Madagascar
+1F1F2 1F1ED ; fully-qualified # 🇲🇭 Marshall Islands
+1F1F2 1F1F0 ; fully-qualified # 🇲🇰 Macedonia
+1F1F2 1F1F1 ; fully-qualified # 🇲🇱 Mali
+1F1F2 1F1F2 ; fully-qualified # 🇲🇲 Myanmar (Burma)
+1F1F2 1F1F3 ; fully-qualified # 🇲🇳 Mongolia
+1F1F2 1F1F4 ; fully-qualified # 🇲🇴 Macau SAR China
+1F1F2 1F1F5 ; fully-qualified # 🇲🇵 Northern Mariana Islands
+1F1F2 1F1F6 ; fully-qualified # 🇲🇶 Martinique
+1F1F2 1F1F7 ; fully-qualified # 🇲🇷 Mauritania
+1F1F2 1F1F8 ; fully-qualified # 🇲🇸 Montserrat
+1F1F2 1F1F9 ; fully-qualified # 🇲🇹 Malta
+1F1F2 1F1FA ; fully-qualified # 🇲🇺 Mauritius
+1F1F2 1F1FB ; fully-qualified # 🇲🇻 Maldives
+1F1F2 1F1FC ; fully-qualified # 🇲🇼 Malawi
+1F1F2 1F1FD ; fully-qualified # 🇲🇽 Mexico
+1F1F2 1F1FE ; fully-qualified # 🇲🇾 Malaysia
+1F1F2 1F1FF ; fully-qualified # 🇲🇿 Mozambique
+1F1F3 1F1E6 ; fully-qualified # 🇳🇦 Namibia
+1F1F3 1F1E8 ; fully-qualified # 🇳🇨 New Caledonia
+1F1F3 1F1EA ; fully-qualified # 🇳🇪 Niger
+1F1F3 1F1EB ; fully-qualified # 🇳🇫 Norfolk Island
+1F1F3 1F1EC ; fully-qualified # 🇳🇬 Nigeria
+1F1F3 1F1EE ; fully-qualified # 🇳🇮 Nicaragua
+1F1F3 1F1F1 ; fully-qualified # 🇳🇱 Netherlands
+1F1F3 1F1F4 ; fully-qualified # 🇳🇴 Norway
+1F1F3 1F1F5 ; fully-qualified # 🇳🇵 Nepal
+1F1F3 1F1F7 ; fully-qualified # 🇳🇷 Nauru
+1F1F3 1F1FA ; fully-qualified # 🇳🇺 Niue
+1F1F3 1F1FF ; fully-qualified # 🇳🇿 New Zealand
+1F1F4 1F1F2 ; fully-qualified # 🇴🇲 Oman
+1F1F5 1F1E6 ; fully-qualified # 🇵🇦 Panama
+1F1F5 1F1EA ; fully-qualified # 🇵🇪 Peru
+1F1F5 1F1EB ; fully-qualified # 🇵🇫 French Polynesia
+1F1F5 1F1EC ; fully-qualified # 🇵🇬 Papua New Guinea
+1F1F5 1F1ED ; fully-qualified # 🇵🇭 Philippines
+1F1F5 1F1F0 ; fully-qualified # 🇵🇰 Pakistan
+1F1F5 1F1F1 ; fully-qualified # 🇵🇱 Poland
+1F1F5 1F1F2 ; fully-qualified # 🇵🇲 St. Pierre & Miquelon
+1F1F5 1F1F3 ; fully-qualified # 🇵🇳 Pitcairn Islands
+1F1F5 1F1F7 ; fully-qualified # 🇵🇷 Puerto Rico
+1F1F5 1F1F8 ; fully-qualified # 🇵🇸 Palestinian Territories
+1F1F5 1F1F9 ; fully-qualified # 🇵🇹 Portugal
+1F1F5 1F1FC ; fully-qualified # 🇵🇼 Palau
+1F1F5 1F1FE ; fully-qualified # 🇵🇾 Paraguay
+1F1F6 1F1E6 ; fully-qualified # 🇶🇦 Qatar
+1F1F7 1F1EA ; fully-qualified # 🇷🇪 Réunion
+1F1F7 1F1F4 ; fully-qualified # 🇷🇴 Romania
+1F1F7 1F1F8 ; fully-qualified # 🇷🇸 Serbia
+1F1F7 1F1FA ; fully-qualified # 🇷🇺 Russia
+1F1F7 1F1FC ; fully-qualified # 🇷🇼 Rwanda
+1F1F8 1F1E6 ; fully-qualified # 🇸🇦 Saudi Arabia
+1F1F8 1F1E7 ; fully-qualified # 🇸🇧 Solomon Islands
+1F1F8 1F1E8 ; fully-qualified # 🇸🇨 Seychelles
+1F1F8 1F1E9 ; fully-qualified # 🇸🇩 Sudan
+1F1F8 1F1EA ; fully-qualified # 🇸🇪 Sweden
+1F1F8 1F1EC ; fully-qualified # 🇸🇬 Singapore
+1F1F8 1F1ED ; fully-qualified # 🇸🇭 St. Helena
+1F1F8 1F1EE ; fully-qualified # 🇸🇮 Slovenia
+1F1F8 1F1EF ; fully-qualified # 🇸🇯 Svalbard & Jan Mayen
+1F1F8 1F1F0 ; fully-qualified # 🇸🇰 Slovakia
+1F1F8 1F1F1 ; fully-qualified # 🇸🇱 Sierra Leone
+1F1F8 1F1F2 ; fully-qualified # 🇸🇲 San Marino
+1F1F8 1F1F3 ; fully-qualified # 🇸🇳 Senegal
+1F1F8 1F1F4 ; fully-qualified # 🇸🇴 Somalia
+1F1F8 1F1F7 ; fully-qualified # 🇸🇷 Suriname
+1F1F8 1F1F8 ; fully-qualified # 🇸🇸 South Sudan
+1F1F8 1F1F9 ; fully-qualified # 🇸🇹 São Tomé & Príncipe
+1F1F8 1F1FB ; fully-qualified # 🇸🇻 El Salvador
+1F1F8 1F1FD ; fully-qualified # 🇸🇽 Sint Maarten
+1F1F8 1F1FE ; fully-qualified # 🇸🇾 Syria
+1F1F8 1F1FF ; fully-qualified # 🇸🇿 Swaziland
+1F1F9 1F1E6 ; fully-qualified # 🇹🇦 Tristan da Cunha
+1F1F9 1F1E8 ; fully-qualified # 🇹🇨 Turks & Caicos Islands
+1F1F9 1F1E9 ; fully-qualified # 🇹🇩 Chad
+1F1F9 1F1EB ; fully-qualified # 🇹🇫 French Southern Territories
+1F1F9 1F1EC ; fully-qualified # 🇹🇬 Togo
+1F1F9 1F1ED ; fully-qualified # 🇹🇭 Thailand
+1F1F9 1F1EF ; fully-qualified # 🇹🇯 Tajikistan
+1F1F9 1F1F0 ; fully-qualified # 🇹🇰 Tokelau
+1F1F9 1F1F1 ; fully-qualified # 🇹🇱 Timor-Leste
+1F1F9 1F1F2 ; fully-qualified # 🇹🇲 Turkmenistan
+1F1F9 1F1F3 ; fully-qualified # 🇹🇳 Tunisia
+1F1F9 1F1F4 ; fully-qualified # 🇹🇴 Tonga
+1F1F9 1F1F7 ; fully-qualified # 🇹🇷 Turkey
+1F1F9 1F1F9 ; fully-qualified # 🇹🇹 Trinidad & Tobago
+1F1F9 1F1FB ; fully-qualified # 🇹🇻 Tuvalu
+1F1F9 1F1FC ; fully-qualified # 🇹🇼 Taiwan
+1F1F9 1F1FF ; fully-qualified # 🇹🇿 Tanzania
+1F1FA 1F1E6 ; fully-qualified # 🇺🇦 Ukraine
+1F1FA 1F1EC ; fully-qualified # 🇺🇬 Uganda
+1F1FA 1F1F2 ; fully-qualified # 🇺🇲 U.S. Outlying Islands
+1F1FA 1F1F3 ; fully-qualified # 🇺🇳 United Nations
+1F1FA 1F1F8 ; fully-qualified # 🇺🇸 United States
+1F1FA 1F1FE ; fully-qualified # 🇺🇾 Uruguay
+1F1FA 1F1FF ; fully-qualified # 🇺🇿 Uzbekistan
+1F1FB 1F1E6 ; fully-qualified # 🇻🇦 Vatican City
+1F1FB 1F1E8 ; fully-qualified # 🇻🇨 St. Vincent & Grenadines
+1F1FB 1F1EA ; fully-qualified # 🇻🇪 Venezuela
+1F1FB 1F1EC ; fully-qualified # 🇻🇬 British Virgin Islands
+1F1FB 1F1EE ; fully-qualified # 🇻🇮 U.S. Virgin Islands
+1F1FB 1F1F3 ; fully-qualified # 🇻🇳 Vietnam
+1F1FB 1F1FA ; fully-qualified # 🇻🇺 Vanuatu
+1F1FC 1F1EB ; fully-qualified # 🇼🇫 Wallis & Futuna
+1F1FC 1F1F8 ; fully-qualified # 🇼🇸 Samoa
+1F1FD 1F1F0 ; fully-qualified # 🇽🇰 Kosovo
+1F1FE 1F1EA ; fully-qualified # 🇾🇪 Yemen
+1F1FE 1F1F9 ; fully-qualified # 🇾🇹 Mayotte
+1F1FF 1F1E6 ; fully-qualified # 🇿🇦 South Africa
+1F1FF 1F1F2 ; fully-qualified # 🇿🇲 Zambia
+1F1FF 1F1FC ; fully-qualified # 🇿🇼 Zimbabwe
+
+# Flags subtotal: 264
+# Flags subtotal: 264 w/o modifiers
+
+#EOF
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index 4038949..6f1dcb0 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -5996,11 +5996,17 @@ index 7350dcc..2375734 100644
--
2.9.3
-From 39744bf98c4d6c2677b1e44cdb40cde7d0029a25 Mon Sep 17 00:00:00 2001
+From aba81ace1487bea57e80406413ae31cbb37ac7d2 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 19 Apr 2017 15:47:36 +0900
+Date: Thu, 20 Apr 2017 12:10:47 +0900
Subject: [PATCH] ui/gtk3: Fix build failures
+Add-on fix to 290f786
+if /usr/share/vala*/vapi/ibus-emoji-dialog*.vapi does not exist.
+
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/323780043
---
ui/gtk3/Makefile.am | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
@@ -6027,3 +6033,3939 @@ index 6f0fb62..d2ae0d6 100644
--
2.9.3
+From 61fa8eabd812a75b3cadd883ad4a4e92cf6877d3 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 25 Apr 2017 12:10:00 +0900
+Subject: [PATCH] ui/gtk3: Get emoji font size from gsettings
+
+If Emojier CLI does not specify the font size likes
+--font 'Noto Emoji Color Regular', get the default size from GSettings.
+
+R=Shawn.P.Huang@gmail.com, alexepico@gmail.com
+
+Review URL: https://codereview.appspot.com/317540043
+---
+ setup/setup.ui | 2 +-
+ ui/gtk3/emojier.vala | 41 ++++++++++++++++++++++++++---------------
+ ui/gtk3/emojierapp.vala | 14 ++++++++++----
+ ui/gtk3/panel.vala | 2 +-
+ 4 files changed, 38 insertions(+), 21 deletions(-)
+
+diff --git a/setup/setup.ui b/setup/setup.ui
+index 4ef3423..54ef916 100644
+--- a/setup/setup.ui
++++ b/setup/setup.ui
+@@ -137,7 +137,7 @@
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
+ <property name="halign">start</property>
+- <property name="label" translatable="yes">Emoji dialog:</property>
++ <property name="label" translatable="yes">Emoji choice:</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 5a9bf8f..76dca6c 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -166,13 +166,14 @@ class IBusEmojier : Gtk.Window {
+ BACKWARD,
+ }
+
+- private static const uint EMOJI_GRID_PAGE = 10;
++ private const uint EMOJI_GRID_PAGE = 10;
+
+ // Set the actual default values in the constructor
+ // because these fields are used for class_init() and static functions,
+ // e.g. set_emoji_font(), can be called before class_init() is called.
+ private static string m_current_lang_id;
+- private static string m_emoji_font;
++ private static string m_emoji_font_family;
++ private static int m_emoji_font_size;
+ private static string[] m_favorites;
+ private static int m_emoji_max_seq_len;
+ private static GLib.HashTable<string, GLib.SList>?
+@@ -220,8 +221,10 @@ class IBusEmojier : Gtk.Window {
+
+ if (m_current_lang_id == null)
+ m_current_lang_id = "en";
+- if (m_emoji_font == null)
+- m_emoji_font = "Monospace 16";
++ if (m_emoji_font_family == null)
++ m_emoji_font_family = "Monospace";
++ if (m_emoji_font_size == 0)
++ m_emoji_font_size = 16;
+ if (m_favorites == null)
+ m_favorites = {};
+
+@@ -242,6 +245,8 @@ class IBusEmojier : Gtk.Window {
+ "#IBusEmojierWhiteLabel { background-color: " +
+ "rgba(%u, %u, %u, %lf); ".printf(
+ bg_red, bg_green, bg_blue, bg_alpha) +
++ "font-family: %s; font-size: %dpt; ".printf(
++ m_emoji_font_family, m_emoji_font_size) +
+ "border-width: 4px; border-radius: 3px; } ";
+
+ uint fg_red = (uint)(m_rgba.selected_fg.red * 255);
+@@ -255,6 +260,8 @@ class IBusEmojier : Gtk.Window {
+ data += "#IBusEmojierSelectedLabel { color: " +
+ "rgba(%u, %u, %u, %lf); ".printf(
+ fg_red, fg_green, fg_blue, fg_alpha) +
++ "font-family: %s; font-size: %dpt; ".printf(
++ m_emoji_font_family, m_emoji_font_size) +
+ "background-color: " +
+ "rgba(%u, %u, %u, %lf); ".printf(
+ bg_red, bg_green, bg_blue, bg_alpha) +
+@@ -273,7 +280,7 @@ class IBusEmojier : Gtk.Window {
+ css_provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+- m_title = new ETitleLabelBox(_("Emoji Chooser"));
++ m_title = new ETitleLabelBox(_("Emoji Choice"));
+ set_titlebar(m_title);
+ m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+ add(m_vbox);
+@@ -734,17 +741,14 @@ class IBusEmojier : Gtk.Window {
+ label = new ESelectedLabel(candidate.text) as Gtk.Label;
+ else
+ label = new EWhiteLabel(candidate.text) as Gtk.Label;
+- string emoji_font = m_emoji_font;
+ if (candidate.text.char_count() > 2) {
+- Pango.FontDescription font_desc =
+- Pango.FontDescription.from_string(emoji_font);
+- string font_family = font_desc.get_family();
+- int font_size = font_desc.get_size() / Pango.SCALE;
+- emoji_font = "%s %d".printf(font_family, font_size -2);
++ string font_family = m_emoji_font_family;
++ int font_size = m_emoji_font_size - 2;
++ string emoji_font = "%s %d".printf(font_family, font_size);
++ string markup = "<span font=\"%s\">%s</span>".
++ printf(emoji_font, candidate.get_text());
++ label.set_markup(markup);
+ }
+- string markup = "<span font=\"%s\">%s</span>".
+- printf(emoji_font, candidate.get_text());
+- label.set_markup(markup);
+ label.set_halign(Gtk.Align.FILL);
+ label.set_valign(Gtk.Align.FILL);
+ Gtk.EventBox candidate_ebox = new Gtk.EventBox();
+@@ -1268,7 +1272,14 @@ class IBusEmojier : Gtk.Window {
+
+ public static void set_emoji_font(string? emoji_font) {
+ return_if_fail(emoji_font != null && emoji_font != "");
+- m_emoji_font = emoji_font;
++ Pango.FontDescription font_desc =
++ Pango.FontDescription.from_string(emoji_font);
++ string font_family = font_desc.get_family();
++ if (font_family != null)
++ m_emoji_font_family = font_family;
++ int font_size = font_desc.get_size() / Pango.SCALE;
++ if (font_size != 0)
++ m_emoji_font_size = font_size;
+ }
+
+
+diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
+index 4564a25..cbdd8ba 100644
+--- a/ui/gtk3/emojierapp.vala
++++ b/ui/gtk3/emojierapp.vala
+@@ -60,6 +60,9 @@ public class EmojiApplication : Application {
+
+
+ private int _command_line (ApplicationCommandLine command_line) {
++ // Set default font size
++ IBusEmojier.set_emoji_font(m_settings_emoji.get_string("font"));
++
+ const OptionEntry[] options = {
+ { "font", 0, 0, OptionArg.STRING, out emoji_font,
+ /* TRANSLATORS: "FONT" should be capital and translatable.
+@@ -88,6 +91,10 @@ public class EmojiApplication : Application {
+ _args[i] = args[i];
+ }
+
++ // Need to initialize for the second instance.
++ emoji_font = null;
++ annotation_lang = null;
++
+ try {
+ unowned string[] tmp = _args;
+ option.parse(ref tmp);
+@@ -101,13 +108,12 @@ public class EmojiApplication : Application {
+ return Posix.EXIT_SUCCESS;
+ }
+
+- if (emoji_font == null)
+- emoji_font = m_settings_emoji.get_string("font");
+ if (annotation_lang == null)
+ annotation_lang = m_settings_emoji.get_string("lang");
+-
+ IBusEmojier.set_annotation_lang(annotation_lang);
+- IBusEmojier.set_emoji_font(emoji_font);
++
++ if (emoji_font != null)
++ IBusEmojier.set_emoji_font(emoji_font);
+
+ activate_dialog(command_line);
+
+diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
+index 2375734..745489b 100644
+--- a/ui/gtk3/panel.vala
++++ b/ui/gtk3/panel.vala
+@@ -1239,7 +1239,7 @@ class Panel : IBus.PanelService {
+ m_sys_menu.append(item);
+
+ #if EMOJI_DICT
+- item = new Gtk.MenuItem.with_label(_("Emoji Dialog"));
++ item = new Gtk.MenuItem.with_label(_("Emoji Choice"));
+ item.activate.connect((i) => {
+ Gdk.Event event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
+ handle_emoji_typing(event);
+--
+2.9.3
+
+From 2f73064d03481c9f34d278dd19d1a4427d6ecb27 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Fri, 28 Apr 2017 13:10:03 +0900
+Subject: [PATCH] Change to use Unicode Emoji files instead of EmojiOne
+ files
+
+EmojiOne Json file have changed the file format since 3.0 [1]
+Now IBus migrated to use Unicode Emoji files since the EmojiOne file
+had been used for the emoji categories but it can be done with
+Unicode emoji-test.txt [2]
+Use Unicode Emoji 4.0 until 5.0 will be released officially.
+Unicode Emoji 5.0 is available but fonts does not support it yet.
+
+[1] https://github.com/Ranks/emojione/releases/tag/v3.0.0
+[2] http://www.unicode.org/Public/emoji/4.0/
+
+Review URL: https://codereview.appspot.com/321860043
+---
+ bindings/vala/Makefile.am | 3 +-
+ configure.ac | 27 +-
+ data/annotations/Makefile.am | 3 +-
+ data/annotations/en_ascii.xml | 56 ++++
+ po/POTFILES.in | 3 +-
+ po/POTFILES.skip | 2 +-
+ src/Makefile.am | 17 +-
+ src/emoji-parser.c | 632 ++++++++++++++++++++++++++++++++++++++----
+ src/ibusemoji.c | 47 +---
+ src/ibusemoji.h | 14 -
+ src/ibusemojigen.h | 39 +++
+ ui/gtk3/Makefile.am | 5 +-
+ ui/gtk3/emojier.vala | 106 +++++--
+ 13 files changed, 780 insertions(+), 174 deletions(-)
+ create mode 100644 data/annotations/en_ascii.xml
+ create mode 100644 src/ibusemojigen.h
+
+diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
+index f3eb0d4..4e34afc 100644
+--- a/bindings/vala/Makefile.am
++++ b/bindings/vala/Makefile.am
+@@ -43,8 +43,7 @@ vapidir = $(datadir)/vala/vapi
+ vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
+
+ MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
+-# for make distclean
+-CONFIG_CLEAN_FILES = $(VAPIGEN_VAPIS)
++DISTCLEANFILES = $(VAPIGEN_VAPIS)
+
+ EXTRA_DIST = \
+ $(VAPIGEN_VAPIS) \
+diff --git a/configure.ac b/configure.ac
+index 0a5f2d5..ea568a0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -612,14 +612,14 @@ AC_ARG_ENABLE(emoji-dict,
+ )
+ AM_CONDITIONAL([ENABLE_EMOJI_DICT], [test x"$enable_emoji_dict" = x"yes"])
+
+-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_ARG_WITH(unicode-emoji-dir,
++ AS_HELP_STRING([--with-unicode-emoji-dir[=DIR]],
++ [Set the directory of Unicode Emoji.
++ (default: "/usr/share/unicode/emoji")]),
++ UNICODE_EMOJI_DIR=$with_unicode_emoji_dir,
++ UNICODE_EMOJI_DIR="/usr/share/unicode/emoji"
+ )
+-AC_SUBST(EMOJI_JSON_FILE)
++AC_SUBST(UNICODE_EMOJI_DIR)
+
+ AC_ARG_WITH(emoji-annotation-dir,
+ AS_HELP_STRING([--with-emoji-annotation-dir[=DIR]],
+@@ -631,17 +631,14 @@ AC_ARG_WITH(emoji-annotation-dir,
+ AC_SUBST(EMOJI_ANNOTATION_DIR)
+
+ if test x"$enable_emoji_dict" = x"yes"; then
+- if test ! -f $EMOJI_JSON_FILE ; then
+- AC_MSG_ERROR(Not found $EMOJI_JSON_FILE. You can get emoji.json \
+-with "npm install -g emojione".)
++ if test ! -f $UNICODE_EMOJI_DIR/emoji-test.txt ; then
++ AC_MSG_ERROR(Not found $UNICODE_EMOJI_DIR/emoji-test.txt. You can get \
++the emoji files from http://www.unicode.org/Public/emoji/4.0/)
+ fi
+ if test ! -f $EMOJI_ANNOTATION_DIR/en.xml ; then
+ AC_MSG_ERROR(Not found $EMOJI_ANNOTATION_DIR/en.xml. You can get \
+ https://github.com/fujiwarat/cldr-emoji-annotation)
+ fi
+- PKG_CHECK_MODULES(JSON_GLIB1, [
+- json-glib-1.0
+- ])
+ enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
+ fi
+
+@@ -730,8 +727,8 @@ Build options:
+ Enable surrounding-text $enable_surrounding_text
+ Enable libnotify $enable_libnotify
+ Enable Emoji dict $enable_emoji_dict
+- emoji.json path $EMOJI_JSON_FILE
+- CLDR annotation dir $EMOJI_ANNOTATION_DIR
++ Uicode Emoji directory $UNICODE_EMOJI_DIR
++ CLDR annotation directory $EMOJI_ANNOTATION_DIR
+ Run test cases $enable_tests
+ ])
+
+diff --git a/data/annotations/Makefile.am b/data/annotations/Makefile.am
+index d87b933..0b7779b 100644
+--- a/data/annotations/Makefile.am
++++ b/data/annotations/Makefile.am
+@@ -2,7 +2,7 @@
+ #
+ # ibus - The Input Bus
+ #
+-# Copyright (c) 2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2016-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ # Copyright (c) 2016 Red Hat, Inc.
+ #
+ # This library is free software; you can redistribute it and/or
+@@ -22,6 +22,7 @@
+
+ EXTRA_DIST = \
+ en.xml \
++ en_ascii.xml \
+ $(NULL)
+
+ -include $(top_srcdir)/git.mk
+diff --git a/data/annotations/en_ascii.xml b/data/annotations/en_ascii.xml
+new file mode 100644
+index 0000000..f1bdcbb
+--- /dev/null
++++ b/data/annotations/en_ascii.xml
+@@ -0,0 +1,56 @@
++<?xml version='1.0' encoding='UTF-8' ?>
++<!DOCTYPE ldml SYSTEM '../../common/dtd/ldml.dtd'>
++<!-- vim:set et sts=4: -->
++<!--
++ Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ Copyright (C) 2017 Red Hat, Inc.
++
++ This library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ This library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with this library; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
++ USA
++ -->
++<ldml>
++ <identity>
++ <version number="$Revision: 1 $"/>
++ <language type='en'/>
++ </identity>
++ <annotations>
++ <annotation cp="😂">:') | :'-)</annotation>
++ <annotation cp="😃">:D | :-D | =D</annotation>
++ <annotation cp="😅">':) | ':-) | '=) | ':D | ':-D | '=D</annotation>
++ <annotation cp="😆">>:) | >;) | >:-) | >=)</annotation>
++ <annotation cp="😉">;) | ;-) | *-) | *) | ;-] | ;] | ;D | ;^)</annotation>
++ <annotation cp="😎">B-) | B) | 8) | 8-) | B-D | 8-D</annotation>
++ <annotation cp="😘">:* | :-* | =* | :^*</annotation>
++ <annotation cp="🙂">:) | :-) | =] | =) | :]</annotation>
++ <annotation cp="😑">-_- | -__- | -___-</annotation>
++ <annotation cp="😶">:-X | :X | :-# | :# | =X | =x | :x | :-x | =#</annotation>
++ <annotation cp="😣">>.<</annotation>
++ <annotation cp="😮">:-O | :O | :-o | :o | O_O | >:O</annotation>
++ <annotation cp="😛">:P | :-P | =P | :-p | :p | =p | :-Þ | :Þ | :þ | :-þ | :-b | :b | d:</annotation>
++ <annotation cp="😜">>:P | X-P | x-p</annotation>
++ <annotation cp="😓">':( | ':-( | '=(</annotation>
++ <annotation cp="😕">>:\ | >:/ | :-/ | :-. | :/ | :\ | =/ | =\ | :L | =L</annotation>
++ <annotation cp="😞">>:[ | :-( | :( | :-[ | :[ | =(</annotation>
++ <annotation cp="😢">:'( | :'-( | ;( | ;-(</annotation>
++ <annotation cp="😨">D:</annotation>
++ <annotation cp="😳">:$ | =$</annotation>
++ <annotation cp="😵">#-) | #) | %-) | %) | X) | X-)</annotation>
++ <annotation cp="😠">>:( | >:-( | :@</annotation>
++ <annotation cp="😇">O:-) | 0:-3 | 0:3 | 0:-) | 0:) | 0;^) | O:) | O;-) | O=) | 0;-) | O:-3 | O:3</annotation>
++ <annotation cp="🙆">*\0/* | \0/ | *\O/* | \O/</annotation>
++ <annotation cp="❤"><3</annotation>
++ <annotation cp="💔"></3</annotation>
++ </annotations>
++</ldml>
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 25be4f4..00f7c7f 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -41,6 +41,7 @@ setup/keyboardshortcut.py
+ setup/main.py
+ src/ibusbus.c
+ src/ibusconfig.c
++src/ibusemojigen.h
+ src/ibusengine.c
+ src/ibusfactory.c
+ src/ibushotkey.c
+@@ -57,6 +58,7 @@ ui/gtk3/application.vala
+ ui/gtk3/candidatearea.vala
+ ui/gtk3/candidatepanel.vala
+ ui/gtk3/emojier.vala
++ui/gtk3/emojierapp.vala
+ ui/gtk3/handle.vala
+ ui/gtk3/iconwidget.vala
+ ui/gtk3/keybindingmanager.vala
+@@ -66,4 +68,3 @@ ui/gtk3/property.vala
+ ui/gtk3/propertypanel.vala
+ ui/gtk3/separator.vala
+ ui/gtk3/switcher.vala
+-ui/gtk3/emojierapp.vala
+diff --git a/po/POTFILES.skip b/po/POTFILES.skip
+index 891d2cc..7190221 100644
+--- a/po/POTFILES.skip
++++ b/po/POTFILES.skip
+@@ -12,6 +12,7 @@ ui/gtk3/application.c
+ ui/gtk3/candidatearea.c
+ ui/gtk3/candidatepanel.c
+ ui/gtk3/emojier.c
++ui/gtk3/emojierapp.c
+ ui/gtk3/handle.c
+ ui/gtk3/iconwidget.c
+ ui/gtk3/keybindingmanager.c
+@@ -21,4 +22,3 @@ ui/gtk3/property.c
+ ui/gtk3/propertypanel.c
+ ui/gtk3/separator.c
+ ui/gtk3/switcher.c
+-ui/gtk3/emojierapp.c
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 7053e3e..27cd168 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -166,6 +166,7 @@ ibusinclude_HEADERS = \
+ ibus_private_headers = \
+ gtkimcontextsimpleseqs.h \
+ ibuscomposetable.h \
++ ibusemojigen.h \
+ ibusenginesimpleprivate.h \
+ ibusinternal.h \
+ keyname-table.h \
+@@ -247,17 +248,26 @@ dicts/emoji-en.dict: emoji-parser
+ echo "WARNING: Not found $(EMOJI_ANNOTATION_DIR)/en.xml" 1>&2; \
+ fi; \
+ for f in $(LANG_FILES) ; do \
++ if test -f \
++ "$(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$$f.xml" ; then \
++ xml_derived_option="--xml-derived $(EMOJI_ANNOTATION_DIR)/../annotationsDerived/$$f.xml"; \
++ plus_comment="derived"; \
++ fi; \
+ if test x"$$f" = xen ; then \
+ $(builddir)/emoji-parser \
++ --unicode-emoji-dir $(UNICODE_EMOJI_DIR) \
+ --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
+- --json $(EMOJI_JSON_FILE) \
++ $$xml_derived_option \
++ --xml-ascii $(top_srcdir)/data/annotations/en_ascii.xml \
++ --out-category ibusemojigen.h \
+ --out $@; \
+ else \
+ $(builddir)/emoji-parser \
+ --xml $(EMOJI_ANNOTATION_DIR)/$$f.xml \
++ $$xml_derived_option \
+ --out dicts/emoji-$$f.dict; \
+ fi; \
+- echo "Generated dicts/emoji-$$f.dict"; \
++ echo "Generated $$plus_comment dicts/emoji-$$f.dict"; \
+ done
+
+ install-data-hook: $(dict_DATA)
+@@ -301,11 +311,9 @@ emoji_parser_SOURCES = \
+ $(NULL)
+ emoji_parser_CFLAGS = \
+ $(GLIB2_CFLAGS) \
+- $(JSON_GLIB1_CFLAGS) \
+ $(NULL)
+ emoji_parser_LDADD = \
+ $(GLIB2_LIBS) \
+- $(JSON_GLIB1_LIBS) \
+ $(libibus) \
+ $(NULL)
+
+@@ -329,6 +337,7 @@ CLEANFILES += \
+ $(NULL)
+
+ DISTCLEANFILES = \
++ ibusemojigen.h \
+ ibusversion.h \
+ $(NULL)
+
+diff --git a/src/emoji-parser.c b/src/emoji-parser.c
+index e5dce3f..5e6155b 100644
+--- a/src/emoji-parser.c
++++ b/src/emoji-parser.c
+@@ -21,34 +21,52 @@
+ */
+
+ /* Convert /usr/share/unicode/cldr/common/annotations/\*.xml and
+- * /usr/lib/node_modules/emojione/emoji.json
++ * /usr/share/unicode/emoji/emoji-test.txt
+ * to the dictionary file which look up the Emoji from the annotation.
+ * Get *.xml from https://github.com/fujiwarat/cldr-emoji-annotation
+ * or http://www.unicode.org/repos/cldr/trunk/common/annotations .
+- * 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".
++ * Get emoji-test.txt from http://unicode.org/Public/emoji/4.0/ .
++ * en.xml is used for the Unicode annotations and emoji-test.txt is used
++ * for the category, e.g. "Smileys & People".
++ * ASCII emoji annotations are saved in ../data/annotations/en_ascii.xml
+ */
+
+ #include <glib.h>
++
++#ifdef HAVE_JSON_GLIB1
+ #include <json-glib/json-glib.h>
++#endif
+
+ #include <string.h>
+
+ #include "ibusemoji.h"
+
++/* This file has 21 lines about the license at the top of the file. */
++#define LICENSE_LINES 21
++
++typedef enum {
++ EMOJI_STRICT,
++ EMOJI_VARIANT,
++ EMOJI_NOVARIANT
++} EmojiDataSearchType;
++
+ typedef struct _EmojiData EmojiData;
+ struct _EmojiData {
+- gchar *emoji;
+- gchar *emoji_alternates;
+- GSList *annotations;
+- gboolean is_annotation;
+- gchar *description;
+- gboolean is_tts;
+- gchar *category;
+- GSList *list;
++ gchar *emoji;
++ gchar *emoji_alternates;
++ GSList *annotations;
++ gboolean is_annotation;
++ gchar *description;
++ gboolean is_tts;
++ gchar *category;
++ gchar *subcategory;
++ gboolean is_derived;
++ GSList *list;
++ EmojiDataSearchType search_type;
+ };
+
++static gchar *unicode_emoji_version;
++
+ static void
+ reset_emoji_element (EmojiData *data)
+ {
+@@ -59,68 +77,175 @@ reset_emoji_element (EmojiData *data)
+ g_slist_free_full (data->annotations, g_free);
+ data->annotations = NULL;
+ g_clear_pointer (&data->description, g_free);
+- g_clear_pointer (&data->category, g_free);
++}
++
++gint
++strcmp_novariant (const gchar *a,
++ const gchar *b,
++ gunichar a_variant,
++ gunichar b_variant)
++{
++ gint retval;
++ gchar *p = NULL;
++ GString *buff = NULL;;
++ gchar *substr = NULL;
++
++ if (a_variant > 0) {
++ if ((p = g_utf8_strchr (a, -1, a_variant)) != NULL) {
++ buff = g_string_new (NULL);
++ if (a != p) {
++ substr = g_strndup (a, p - a);
++ g_string_append (buff, substr);
++ g_free (substr);
++ }
++ p = g_utf8_next_char (p);
++ if (*p != '\0')
++ g_string_append (buff, p);
++ retval = g_strcmp0 (buff->str, b);
++ g_string_free (buff, TRUE);
++ return retval;
++ } else {
++ return -1;
++ }
++ } else if (b_variant > 0) {
++ if ((p = g_utf8_strchr (b, -1, b_variant)) != NULL) {
++ buff = g_string_new (NULL);
++ if (b != p) {
++ substr = g_strndup (b, p - b);
++ g_string_append (buff, substr);
++ g_free (substr);
++ }
++ p = g_utf8_next_char (p);
++ if (*p != '\0')
++ g_string_append (buff, p);
++ retval = g_strcmp0 (a, buff->str);
++ g_string_free (buff, TRUE);
++ return retval;
++ } else {
++ return -1;
++ }
++ }
++ return g_strcmp0 (a, b);
+ }
+
+ gint
+ find_emoji_data_list (IBusEmojiData *a,
+- const gchar *b)
++ EmojiData *b)
+ {
+- g_return_val_if_fail (IBUS_IS_EMOJI_DATA (a), 0);
+- return g_strcmp0 (ibus_emoji_data_get_emoji (a), b);
++ const gchar *a_str;
++
++ g_return_val_if_fail (IBUS_IS_EMOJI_DATA (a), 0);
++
++ a_str = ibus_emoji_data_get_emoji (a);
++ switch (b->search_type) {
++ case EMOJI_VARIANT:
++ if (strcmp_novariant (a_str, b->emoji, 0xfe0e, 0) == 0)
++ return 0;
++ else if (strcmp_novariant (a_str, b->emoji, 0xfe0f, 0) == 0)
++ return 0;
++ else
++ return g_strcmp0 (a_str, b->emoji);
++ break;
++ case EMOJI_NOVARIANT:
++ if (strcmp_novariant (a_str, b->emoji, 0, 0xfe0e) == 0)
++ return 0;
++ else if (strcmp_novariant (a_str, b->emoji, 0, 0xfe0f) == 0)
++ return 0;
++ else
++ return g_strcmp0 (a_str, b->emoji);
++ break;
++ default:;
++ }
++ return g_strcmp0 (a_str, b->emoji);
+ }
+
+ static void
+-update_emoji_list (EmojiData *data)
++emoji_data_update_object (EmojiData *data,
++ IBusEmojiData *emoji)
+ {
+- GSList *list = g_slist_find_custom (
+- data->list,
+- data->emoji,
+- (GCompareFunc) find_emoji_data_list);
+- if (list) {
+- IBusEmojiData *emoji = list->data;
+- GSList *src_annotations = data->annotations;
+- GSList *dest_annotations = ibus_emoji_data_get_annotations (emoji);
+- GSList *l;
+- gboolean updated_annotations = FALSE;
+- for (l = src_annotations; l; l = l->next) {
+- GSList *duplicated = g_slist_find_custom (dest_annotations,
+- l->data,
+- (GCompareFunc) g_strcmp0);
+- if (duplicated == NULL) {
+- dest_annotations = g_slist_append (dest_annotations,
+- g_strdup (l->data));
+- updated_annotations = TRUE;
+- }
++ GSList *src_annotations = data->annotations;
++ GSList *dest_annotations = ibus_emoji_data_get_annotations (emoji);
++ GSList *l;
++ gboolean updated_annotations = FALSE;
++ for (l = src_annotations; l; l = l->next) {
++ GSList *duplicated = g_slist_find_custom (dest_annotations,
++ l->data,
++ (GCompareFunc) g_strcmp0);
++ if (duplicated == NULL) {
++ dest_annotations = g_slist_append (dest_annotations,
++ g_strdup (l->data));
++ updated_annotations = TRUE;
+ }
+- if (updated_annotations) {
+- ibus_emoji_data_set_annotations (
++ }
++ if (updated_annotations) {
++ ibus_emoji_data_set_annotations (
+ emoji,
+ g_slist_copy_deep (dest_annotations,
+ (GCopyFunc) g_strdup,
+ NULL));
++ }
++ if (data->description)
++ ibus_emoji_data_set_description (emoji, data->description);
++}
++
++static void
++emoji_data_new_object (EmojiData *data)
++{
++ IBusEmojiData *emoji =
++ ibus_emoji_data_new ("emoji",
++ data->emoji,
++ "annotations",
++ data->annotations,
++ "description",
++ data->description ? data->description
++ : g_strdup (""),
++ "category",
++ data->category ? data->category
++ : g_strdup (""),
++ NULL);
++ data->list = g_slist_append (data->list, emoji);
++}
++
++static void
++update_emoji_list (EmojiData *data,
++ gboolean base_update)
++{
++ GSList *list;
++ data->search_type = EMOJI_STRICT;
++ list = g_slist_find_custom (
++ data->list,
++ data,
++ (GCompareFunc) find_emoji_data_list);
++ if (list) {
++ emoji_data_update_object (data, list->data);
++ return;
++ } else if (base_update) {
++ emoji_data_new_object (data);
++ return;
++ }
++ if (g_utf8_strchr (data->emoji, -1, 0xfe0e) == NULL &&
++ g_utf8_strchr (data->emoji, -1, 0xfe0f) == NULL) {
++ data->search_type = EMOJI_VARIANT;
++ list = g_slist_find_custom (
++ data->list,
++ data,
++ (GCompareFunc) find_emoji_data_list);
++ if (list) {
++ emoji_data_update_object (data, list->data);
++ return;
+ }
+- if (data->description)
+- ibus_emoji_data_set_description (emoji, data->description);
+ } else {
+- IBusEmojiData *emoji =
+- ibus_emoji_data_new ("emoji",
+- data->emoji,
+- "annotations",
+- data->annotations,
+- "description",
+- data->description ? data->description
+- : g_strdup (""),
+- "category",
+- data->category ? data->category
+- : g_strdup (""),
+- "emoji-alternates",
+- data->emoji_alternates
+- ? data->emoji_alternates
+- : g_strdup (""),
+- NULL);
+- data->list = g_slist_append (data->list, emoji);
++ data->search_type = EMOJI_NOVARIANT;
++ list = g_slist_find_custom (
++ data->list,
++ data,
++ (GCompareFunc) find_emoji_data_list);
++ if (list) {
++ emoji_data_update_object (data, list->data);
++ return;
++ }
+ }
++ emoji_data_new_object (data);
+ }
+
+ static void
+@@ -183,7 +308,7 @@ unicode_annotations_end_element_cb (GMarkupParseContext *context,
+ if (!data->is_annotation)
+ return;
+
+- update_emoji_list (data);
++ update_emoji_list (data, FALSE);
+ data->is_annotation = FALSE;
+ data->is_tts = FALSE;
+ }
+@@ -227,7 +352,8 @@ unicode_annotations_text_cb (GMarkupParseContext *context,
+
+ static gboolean
+ unicode_annotations_parse_xml_file (const gchar *filename,
+- GSList **list)
++ GSList **list,
++ gboolean is_derived)
+ {
+ gchar *content = NULL;
+ gsize length = 0;
+@@ -251,6 +377,7 @@ unicode_annotations_parse_xml_file (const gchar *filename,
+ }
+
+ data.list = *list;
++ data.is_derived = is_derived;
+
+ context = g_markup_parse_context_new (&parser, 0, &data, NULL);
+ if (!g_markup_parse_context_parse (context, content, length, &error)) {
+@@ -276,6 +403,233 @@ failed_to_parse_unicode_annotations:
+ }
+
+ static gboolean
++unicode_emoji_test_parse_unicode (const gchar *line,
++ EmojiData *data)
++{
++ GString *emoji = NULL;
++ gchar *endptr = NULL;
++ guint32 uch;
++ static gchar outbuf[8] = { 0, };
++
++ g_return_val_if_fail (line != NULL, FALSE);
++
++ emoji = g_string_new (NULL);
++ while (line && *line) {
++ uch = g_ascii_strtoull (line, &endptr, 16);
++ outbuf[g_unichar_to_utf8 (uch, outbuf)] = '\0';
++ g_string_append (emoji, outbuf);
++ if (*endptr == '\0') {
++ break;
++ }
++ line = endptr + 1;
++ while (*line == ' ')
++ line++;
++ endptr = NULL;
++ }
++
++ data->emoji = g_string_free (emoji, FALSE);
++ return TRUE;
++}
++
++static gboolean
++unicode_emoji_test_parse_description (const gchar *line,
++ EmojiData *data)
++{
++ g_return_val_if_fail (line != NULL, FALSE);
++
++ /* skip spaces */
++ while (*line == ' ')
++ line++;
++ /* skip emoji */
++ while (*line != ' ')
++ line++;
++ /* skip spaces */
++ while (*line == ' ')
++ line++;
++ if (*line == '\0')
++ return FALSE;
++ data->description = g_strdup (line);
++ return TRUE;
++}
++
++#define EMOJI_VERSION_TAG "# Version: "
++#define EMOJI_GROUP_TAG "# group: "
++#define EMOJI_SUBGROUP_TAG "# subgroup: "
++#define EMOJI_NON_FULLY_QUALIFIED_TAG "non-fully-qualified"
++
++static gboolean
++unicode_emoji_test_parse_line (const gchar *line,
++ EmojiData *data)
++{
++ int tag_length;
++ gchar **segments = NULL;
++
++ g_return_val_if_fail (line != NULL, FALSE);
++
++ tag_length = strlen (EMOJI_VERSION_TAG);
++ if (strlen (line) > tag_length &&
++ g_ascii_strncasecmp (line, EMOJI_VERSION_TAG, tag_length) == 0) {
++ unicode_emoji_version = g_strdup (line + tag_length);
++ return TRUE;
++ }
++ tag_length = strlen (EMOJI_GROUP_TAG);
++ if (strlen (line) > tag_length &&
++ g_ascii_strncasecmp (line, EMOJI_GROUP_TAG, tag_length) == 0) {
++ g_free (data->category);
++ g_clear_pointer (&data->subcategory, g_free);
++ data->category = g_strdup (line + tag_length);
++ return TRUE;
++ }
++ tag_length = strlen (EMOJI_SUBGROUP_TAG);
++ if (strlen (line) > tag_length &&
++ g_ascii_strncasecmp (line, EMOJI_SUBGROUP_TAG, tag_length) == 0) {
++ g_free (data->subcategory);
++ data->subcategory = g_strdup (line + tag_length);
++ return TRUE;
++ }
++ if (*line == '#')
++ return TRUE;
++ segments = g_strsplit (line, "; ", 2);
++ if (segments[1] == NULL) {
++ g_warning ("No qualified line\n");
++ goto failed_to_parse_unicode_emoji_test_line;
++ return FALSE;
++ }
++ tag_length = strlen (EMOJI_NON_FULLY_QUALIFIED_TAG);
++ /* Ignore the non-fully-qualified emoji */
++ if (g_ascii_strncasecmp (segments[1], EMOJI_NON_FULLY_QUALIFIED_TAG,
++ tag_length) == 0) {
++ g_strfreev (segments);
++ return TRUE;
++ }
++ unicode_emoji_test_parse_unicode (segments[0], data);
++ g_strfreev (segments);
++ segments = g_strsplit (line, "# ", 2);
++ if (segments[1] == NULL) {
++ g_warning ("No description line\n");
++ goto failed_to_parse_unicode_emoji_test_line;
++ return FALSE;
++ }
++ unicode_emoji_test_parse_description (segments[1], data);
++ g_strfreev (segments);
++ if (data->annotations == NULL) {
++ if (data->subcategory) {
++ int i;
++ gchar *amp;
++ segments = g_strsplit(data->subcategory, "-", -1);
++ for (i = 0; segments[i]; i++) {
++ if ((amp = strchr (segments[i], '&')) != NULL) {
++ if (amp - segments[i] <= 1) {
++ g_warning ("Wrong ampersand");
++ goto failed_to_parse_unicode_emoji_test_line;
++ }
++ data->annotations = g_slist_append (
++ data->annotations,
++ g_strndup (segments[i], amp - segments[i] - 1));
++ data->annotations = g_slist_append (
++ data->annotations,
++ g_strdup (amp + 1));
++ continue;
++ }
++ data->annotations = g_slist_append (data->annotations,
++ g_strdup (segments[i]));
++ }
++ g_strfreev (segments);
++ } else {
++ g_warning ("No subcategory line\n");
++ goto failed_to_parse_unicode_emoji_test_line;
++ }
++ }
++ update_emoji_list (data, TRUE);
++ reset_emoji_element (data);
++ return TRUE;
++
++failed_to_parse_unicode_emoji_test_line:
++ if (segments)
++ g_strfreev (segments);
++ reset_emoji_element (data);
++ return FALSE;
++}
++
++#undef EMOJI_VERSION_TAG
++#undef EMOJI_GROUP_TAG
++#undef EMOJI_SUBGROUP_TAG
++#undef EMOJI_NON_FULLY_QUALIFIED_TAG
++
++static gboolean
++unicode_emoji_test_parse_file (const gchar *filename,
++ GSList **list)
++{
++ gchar *content = NULL;
++ gsize length = 0;
++ GError *error = NULL;
++ gchar *head, *end, *line;
++ int n = 1;
++ EmojiData data = { 0, };
++
++ g_return_val_if_fail (filename != NULL, FALSE);
++ g_return_val_if_fail (list != 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_emoji_test;
++ }
++ head = end = content;
++ while (*end == '\n' && end - content < length) {
++ end++;
++ n++;
++ }
++ head = end;
++ data.list = *list;
++ while (end - content < length) {
++ while (*end != '\n' && end - content < length)
++ end++;
++ if (end - content >= length)
++ break;
++ line = g_strndup (head, end - head);
++ if (!unicode_emoji_test_parse_line (line, &data))
++ g_warning ("parse error #%d in %s version %s: %s",
++ n, filename,
++ unicode_emoji_version ? unicode_emoji_version : "(null)",
++ line);
++ while (*end == '\n' && end - content < length) {
++ end++;
++ n++;
++ }
++ g_free (line);
++ head = end;
++ }
++ g_free (content);
++ g_free (unicode_emoji_version);
++ *list = data.list;
++ return TRUE;
++
++failed_to_parse_unicode_emoji_test:
++ if (error)
++ g_error_free (error);
++ g_clear_pointer (&content, g_free);
++ return FALSE;
++}
++
++static gboolean
++unicode_emoji_parse_dir (const gchar *dirname,
++ GSList **list)
++{
++ gchar *filename = NULL;
++ g_return_val_if_fail (dirname != NULL, FALSE);
++ g_return_val_if_fail (list != NULL, FALSE);
++
++ filename = g_build_path ("/", dirname, "emoji-test.txt", NULL);
++ if (!unicode_emoji_test_parse_file (filename, list)) {
++ g_free (filename);
++ return FALSE;
++ }
++ g_free (filename);
++ return TRUE;
++}
++
++#ifdef HAVE_JSON_GLIB1
++static gboolean
+ parse_emojione_unicode (JsonNode *node,
+ EmojiData *data,
+ gboolean is_alternates)
+@@ -426,6 +780,31 @@ parse_emojione_category (JsonNode *node,
+ return TRUE;
+ }
+
++#ifdef EMOJIONE_ALIASES_ASCII_PRINT
++static gchar *
++text_to_entity (const gchar *text)
++{
++ gchar *p;
++ GString *buff = g_string_new (NULL);
++ for (p = text; *p; p++) {
++ switch (*p) {
++ case '<':
++ g_string_append (buff, "<");
++ break;
++ case '>':
++ g_string_append (buff, ">");
++ break;
++ case '&':
++ g_string_append (buff, "&");
++ break;
++ default:
++ g_string_append_c (buff, *p);
++ }
++ }
++ g_string_free (buff, FALSE);
++}
++#endif
++
+ static gboolean
+ parse_emojione_aliases_ascii (JsonNode *node,
+ EmojiData *data)
+@@ -441,11 +820,23 @@ parse_emojione_aliases_ascii (JsonNode *node,
+ aliases_ascii = json_node_get_array (node);
+ length = json_array_get_length (aliases_ascii);
+ for (i = 0; i < length; i++) {
++#ifdef EMOJIONE_ALIASES_ASCII_PRINT
++ if (i == 0)
++ printf (" <annotation cp=\"%s\">", data->emoji);
++#endif
+ 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) {
++#ifdef EMOJIONE_ALIASES_ASCII_PRINT
++ gchar *entity = text_to_entity (alias);
++ if (i != length - 1)
++ printf ("%s | ", entity);
++ else
++ printf ("%s</annotation>\n", entity);
++ g_free (entity);
++#endif
+ data->annotations = g_slist_prepend (data->annotations,
+ g_strdup (alias));
+ }
+@@ -535,7 +926,7 @@ parse_emojione_element (JsonNode *node,
+ }
+ g_list_free (members);
+
+- update_emoji_list (data);
++ update_emoji_list (data, TRUE);
+
+ return TRUE;
+ }
+@@ -591,27 +982,136 @@ fail_to_json_file:
+ g_object_unref (parser);
+ return FALSE;
+ }
++#endif /* HAVE_JSON_GLIB1 */
++
++static void
++emoji_data_list_unify_categories (IBusEmojiData *data,
++ GSList **list)
++{
++ g_return_if_fail (IBUS_IS_EMOJI_DATA (data));
++ g_return_if_fail (list != NULL);
++
++ const gchar *category = ibus_emoji_data_get_category (data);
++ if (*category == '\0')
++ return;
++ if (g_slist_find_custom (*list, category, (GCompareFunc)g_strcmp0) == NULL)
++ *list = g_slist_append (*list, g_strdup (category));
++}
++
++static void
++category_list_dump (const gchar *category,
++ GString *buff)
++{
++ g_return_if_fail (buff != NULL);
++
++ const gchar *line = g_strdup_printf (" N_(\"%s\"),\n", category);
++ g_string_append (buff, line);
++}
++
++static void
++category_file_save (const gchar *filename,
++ GSList *list)
++{
++ gchar *content = NULL;
++ gsize length = 0;
++ GError *error = NULL;
++ gchar *p;
++ GString *buff = NULL;
++ int i;
++ GSList *list_categories = NULL;
++
++ g_return_if_fail (filename != NULL);
++ g_return_if_fail (list != NULL);
++
++ g_slist_foreach (list, (GFunc)emoji_data_list_unify_categories, &list_categories);
++ if (list_categories == NULL) {
++ g_warning ("Not found categories in IBusEmojiData list");
++ return;
++ }
++
++ if (!g_file_get_contents (__FILE__, &content, &length, &error)) {
++ g_warning ("Failed to load %s: %s", __FILE__, error->message);
++ g_clear_pointer (&error, g_error_free);
++ }
++ buff = g_string_new (NULL);
++ p = content;
++ for (i = 0; i < LICENSE_LINES; i++, p++) {
++ if ((p = strchr (p, '\n')) == NULL)
++ break;
++ }
++ if (p != NULL) {
++ g_string_append (buff, g_strndup (content, p - content));
++ g_string_append_c (buff, '\n');
++ }
++ g_clear_pointer (&content, g_free);
++
++ g_string_append (buff, g_strdup ("\n"));
++ g_string_append (buff, g_strdup_printf ("/* This file is generated by %s. */", __FILE__));
++ g_string_append (buff, g_strdup ("\n"));
++ g_string_append (buff, g_strdup ("include <glib/gi18n.h>\n"));
++ g_string_append (buff, g_strdup ("\n"));
++ g_string_append (buff, g_strdup ("#ifndef __IBUS_EMOJI_GEN_H_\n"));
++ g_string_append (buff, g_strdup ("#define __IBUS_EMOJI_GEN_H_\n"));
++ g_string_append (buff, g_strdup ("const static char *unicode_emoji_categories[] = {\n"));
++ list_categories = g_slist_sort (list_categories, (GCompareFunc)g_strcmp0);
++ g_slist_foreach (list_categories, (GFunc)category_list_dump, buff);
++ g_slist_free (list_categories);
++ g_string_append (buff, g_strdup ("};\n"));
++ g_string_append (buff, g_strdup ("#endif\n"));
++
++ if (!g_file_set_contents (filename, buff->str, -1, &error)) {
++ g_warning ("Failed to save emoji category file %s: %s", filename, error->message);
++ g_error_free (error);
++ }
++
++ g_string_free (buff, TRUE);
++}
+
+ int
+ main (int argc, char *argv[])
+ {
+ gchar *prgname;
++#ifdef HAVE_JSON_GLIB1
+ gchar *json_file = NULL;
++#endif
++ gchar *emoji_dir = NULL;
+ gchar *xml_file = NULL;
++ gchar *xml_derived_file = NULL;
++ gchar *xml_ascii_file = NULL;
+ gchar *output = NULL;
++ gchar *output_category = NULL;
+ GOptionEntry entries[] = {
++#ifdef HAVE_JSON_GLIB1
+ { "json", 'j', 0, G_OPTION_ARG_STRING, &json_file,
+ "Parse Emoji One JSON file",
+ "JSON"
+ },
++#endif
++ { "unicode-emoji-dir", 'd', 0, G_OPTION_ARG_STRING, &emoji_dir,
++ "Parse Emoji files in DIRECTORY which includes emoji-test.txt " \
++ "emoji-sequences.txt emoji-zwj-sequences.txt in unicode.org",
++ "DIRECTORY"
++ },
+ { "out", 'o', 0, G_OPTION_ARG_STRING, &output,
+ "Save the emoji data as FILE",
+ "FILE"
+ },
++ { "out-category", 'C', 0, G_OPTION_ARG_STRING, &output_category,
++ "Save the translatable categories as FILE",
++ "FILE"
++ },
+ { "xml", 'x', 0, G_OPTION_ARG_STRING, &xml_file,
+ "Parse Unocode.org ANNOTATIONS file",
+ "ANNOTATIONS"
+ },
++ { "xml-derived", 'X', 0, G_OPTION_ARG_STRING, &xml_derived_file,
++ "Parse Unocode.org derived ANNOTATIONS file",
++ "ANNOTATIONS"
++ },
++ { "xml-ascii", 'A', 0, G_OPTION_ARG_STRING, &xml_ascii_file,
++ "Parse ASCII ANNOTATIONS file",
++ "ANNOTATIONS"
++ },
+ { NULL }
+ };
+ GOptionContext *context;
+@@ -638,12 +1138,22 @@ main (int argc, char *argv[])
+ }
+ g_option_context_free (context);
+
++#ifdef HAVE_JSON_GLIB1
+ if (json_file)
+ emojione_parse_json_file (json_file, &list);
++#endif
++ if (emoji_dir)
++ unicode_emoji_parse_dir (emoji_dir, &list);
+ if (xml_file)
+- unicode_annotations_parse_xml_file (xml_file, &list);
++ unicode_annotations_parse_xml_file (xml_file, &list, FALSE);
++ if (xml_derived_file)
++ unicode_annotations_parse_xml_file (xml_derived_file, &list, TRUE);
++ if (xml_ascii_file)
++ unicode_annotations_parse_xml_file (xml_ascii_file, &list, FALSE);
+ if (list != NULL && output)
+ ibus_emoji_data_save (output, list);
++ if (list != NULL && output_category)
++ category_file_save (output_category, list);
+ if (list)
+ g_slist_free (list);
+
+diff --git a/src/ibusemoji.c b/src/ibusemoji.c
+index 4be092a..d2e16c5 100644
+--- a/src/ibusemoji.c
++++ b/src/ibusemoji.c
+@@ -29,7 +29,7 @@
+ #include "ibusinternal.h"
+
+ #define IBUS_EMOJI_DATA_MAGIC "IBusEmojiData"
+-#define IBUS_EMOJI_DATA_VERSION (3)
++#define IBUS_EMOJI_DATA_VERSION (4)
+
+ enum {
+ PROP_0 = 0,
+@@ -37,7 +37,6 @@ enum {
+ PROP_ANNOTATIONS,
+ PROP_DESCRIPTION,
+ PROP_CATEGORY,
+- PROP_EMOJI_ALTERNATES
+ };
+
+ struct _IBusEmojiDataPrivate {
+@@ -45,7 +44,6 @@ struct _IBusEmojiDataPrivate {
+ GSList *annotations;
+ gchar *description;
+ gchar *category;
+- gchar *emoji_alternates;
+ };
+
+ #define IBUS_EMOJI_DATA_GET_PRIVATE(o) \
+@@ -108,7 +106,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+- * IBusEmojiData:annotations:
++ * IBusEmojiData:annotations: (transfer container) (element-type utf8):
+ *
+ * The emoji annotations
+ */
+@@ -117,7 +115,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
+ g_param_spec_pointer ("annotations",
+ "emoji annotations",
+ "The emoji annotation list",
+- G_PARAM_READWRITE));
++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * IBusEmojiData:description:
+@@ -130,7 +128,7 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
+ "emoji description",
+ "The emoji description",
+ "",
+- G_PARAM_READWRITE));
++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * IBusEmojiData:category:
+@@ -144,19 +142,6 @@ ibus_emoji_data_class_init (IBusEmojiDataClass *class)
+ "The emoji category",
+ "",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+-
+- /**
+- * IBusEmojiData:emoji_alternates:
+- *
+- * The emoji alternate characters
+- */
+- g_object_class_install_property (gobject_class,
+- PROP_EMOJI_ALTERNATES,
+- g_param_spec_string ("emoji-alternates",
+- "emoji alternate charasters",
+- "The emoji alternate characters UTF-8",
+- "",
+- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ }
+
+ static void
+@@ -209,10 +194,6 @@ ibus_emoji_data_set_property (IBusEmojiData *emoji,
+ g_assert (emoji->priv->category == NULL);
+ emoji->priv->category = g_value_dup_string (value);
+ break;
+- case PROP_EMOJI_ALTERNATES:
+- g_assert (emoji->priv->emoji_alternates == NULL);
+- emoji->priv->emoji_alternates = g_value_dup_string (value);
+- break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (emoji, prop_id, pspec);
+ }
+@@ -240,9 +221,6 @@ ibus_emoji_data_get_property (IBusEmojiData *emoji,
+ case PROP_CATEGORY:
+ g_value_set_string (value, ibus_emoji_data_get_category (emoji));
+ break;
+- case PROP_EMOJI_ALTERNATES:
+- g_value_set_string (value, ibus_emoji_data_get_emoji_alternates(emoji));
+- break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (emoji, prop_id, pspec);
+ }
+@@ -270,8 +248,6 @@ ibus_emoji_data_serialize (IBusEmojiData *emoji,
+ }
+ g_variant_builder_add (builder, "s", NOTNULL (emoji->priv->description));
+ g_variant_builder_add (builder, "s", NOTNULL (emoji->priv->category));
+- g_variant_builder_add (builder, "s",
+- NOTNULL (emoji->priv->emoji_alternates));
+ #undef NOTNULL
+ return TRUE;
+ }
+@@ -303,10 +279,6 @@ ibus_emoji_data_deserialize (IBusEmojiData *emoji,
+ &emoji->priv->description);
+ ibus_g_variant_get_child_string (variant, retval++,
+ &emoji->priv->category);
+- if (g_variant_n_children (variant) < retval + 1)
+- return retval;
+- ibus_g_variant_get_child_string (variant, retval++,
+- &emoji->priv->emoji_alternates);
+ return retval;
+ }
+
+@@ -325,7 +297,6 @@ ibus_emoji_data_copy (IBusEmojiData *dest,
+ NULL);
+ dest->priv->description = g_strdup (src->priv->description);
+ dest->priv->category = g_strdup (src->priv->category);
+- dest->priv->emoji_alternates = g_strdup (src->priv->emoji_alternates);
+ return TRUE;
+ }
+
+@@ -345,7 +316,6 @@ ibus_emoji_data_new (const gchar *first_property_name, ...)
+ g_assert (emoji->priv->emoji != NULL);
+ g_assert (emoji->priv->description != NULL);
+ g_assert (emoji->priv->category != NULL);
+- g_assert (emoji->priv->emoji_alternates != NULL);
+ return emoji;
+ }
+
+@@ -402,15 +372,6 @@ ibus_emoji_data_get_category (IBusEmojiData *emoji)
+ return emoji->priv->category;
+ }
+
+-const gchar *
+-ibus_emoji_data_get_emoji_alternates (IBusEmojiData *emoji)
+-{
+- g_return_val_if_fail (IBUS_IS_EMOJI_DATA (emoji), NULL);
+-
+- return emoji->priv->emoji_alternates;
+-}
+-
+-
+ static void
+ variant_foreach_add_emoji (IBusEmojiData *emoji,
+ GVariantBuilder *builder)
+diff --git a/src/ibusemoji.h b/src/ibusemoji.h
+index 233cadd..eb24fdd 100644
+--- a/src/ibusemoji.h
++++ b/src/ibusemoji.h
+@@ -156,20 +156,6 @@ const gchar * ibus_emoji_data_get_category (IBusEmojiData *emoji);
+
+
+ /**
+- * ibus_emoji_data_get_emoji_alternates:
+- * @emoji : An #IBusEmojiData
+- *
+- * Gets the emoji alternate characters in #IBusEmojiData. It should not be
+- * freed. The alternates are defined in "unicode_alt" in EmojiOne json.
+- *
+- * Returns: emoji alternates property in #IBusEmojiData
+- *
+- */
+-const gchar * ibus_emoji_data_get_emoji_alternates
+- (IBusEmojiData *emoji);
+-
+-
+-/**
+ * ibus_emoji_dict_save:
+ * @path: A path of the saved dictionary file.
+ * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary
+diff --git a/src/ibusemojigen.h b/src/ibusemojigen.h
+new file mode 100644
+index 0000000..74a6015
+--- /dev/null
++++ b/src/ibusemojigen.h
+@@ -0,0 +1,39 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2016-2017 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
++ */
++
++
++/* This file is generated by emoji-parser.c. */
++include <glib/gi18n.h>
++
++#ifndef __IBUS_EMOJI_GEN_H_
++#define __IBUS_EMOJI_GEN_H_
++const static char *unicode_emoji_categories[] = {
++ N_("Activities"),
++ N_("Animals & Nature"),
++ N_("Flags"),
++ N_("Food & Drink"),
++ N_("Objects"),
++ N_("Smileys & People"),
++ N_("Symbols"),
++ N_("Travel & Places"),
++};
++#endif
+diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
+index d2ae0d6..c557474 100644
+--- a/ui/gtk3/Makefile.am
++++ b/ui/gtk3/Makefile.am
+@@ -82,7 +82,7 @@ AM_VALAFLAGS = \
+ $(NULL)
+
+ MAINTAINERCLEANFILES =
+-CONFIG_CLEAN_FILES =
++DISTCLEANFILES =
+ noinst_DATA =
+
+ if ENABLE_LIBNOTIFY
+@@ -250,8 +250,7 @@ vapidir = $(datadir)/vala/vapi
+ noinst_DATA += $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
+
+ MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
+-# for make distclean
+-CONFIG_CLEAN_FILES += $(VAPIGEN_VAPIS)
++DISTCLEANFILES += $(VAPIGEN_VAPIS)
+ EXTRA_DIST += $(VAPIGEN_VAPIS)
+
+ endif
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 76dca6c..8f55bd4 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -167,6 +167,7 @@ class IBusEmojier : Gtk.Window {
+ }
+
+ private const uint EMOJI_GRID_PAGE = 10;
++ private const string EMOJI_CATEGORY_OTHERS = N_("Others");
+
+ // Set the actual default values in the constructor
+ // because these fields are used for class_init() and static functions,
+@@ -372,8 +373,7 @@ class IBusEmojier : Gtk.Window {
+
+
+ private static void update_annotation_to_emojis_dict(IBus.EmojiData data) {
+- string emoji = (data.get_emoji_alternates() != "") ?
+- data.get_emoji_alternates() : data.get_emoji();
++ string emoji = data.get_emoji();
+ unowned GLib.SList<string> annotations = data.get_annotations();
+ foreach (string annotation in annotations) {
+ bool has_emoji = false;
+@@ -434,23 +434,70 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
++ private static string utf8_entity(string str) {
++ GLib.StringBuilder buff = new GLib.StringBuilder();
++ int length = str.char_count();
++ for (int i = 0; i < length; i++) {
++ unichar ch = str.get_char(0);
++ switch(ch) {
++ case '<':
++ buff.append("<");
++ break;
++ case '>':
++ buff.append(">");
++ break;
++ case '&':
++ buff.append("&");
++ break;
++ default:
++ buff.append_unichar(ch);
++ break;
++ }
++ str = str.next_char();
++ }
++ return buff.str;
++ }
++
++
++ private static void
++ update_annotations_with_description (IBus.EmojiData data,
++ string description) {
++ unowned GLib.SList<string> annotations = data.get_annotations();
++ bool update_annotations = false;
++ string former = null;
++ string later = null;
++ int index = description.index_of(": ");
++ if (index > 0) {
++ former = description.substring(0, index);
++ if (annotations.find_custom(former, GLib.strcmp) == null) {
++ annotations.append(former);
++ update_annotations = true;
++ }
++ later = description.substring(index + 2);
++ } else {
++ later = description.dup();
++ }
++ var words = later.split(" ");
++ // If the description has less than 3 words, add it to annotations
++ // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
++ if (words.length < 3 &&
++ annotations.find_custom(
++ later,
++ GLib.strcmp) == null) {
++ annotations.append(later);
++ update_annotations = true;
++ }
++ if (update_annotations)
++ data.set_annotations(annotations.copy_deep(GLib.strdup));
++ }
++
++
+ private static void update_emoji_to_data_dict(IBus.EmojiData data,
+ string lang) {
+- string emoji = (data.get_emoji_alternates() != "") ?
+- data.get_emoji_alternates() : data.get_emoji();
++ string emoji = data.get_emoji();
+ if (lang == "en") {
+ string description = utf8_down(data.get_description());
+- unowned GLib.SList<string> annotations = data.get_annotations();
+- var words = description.split(" ");
+- // If the description has less than 3 words, add it to annotations
+- // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
+- if (words.length < 3 &&
+- annotations.find_custom(
+- description,
+- GLib.strcmp) == null) {
+- annotations.append(description);
+- data.set_annotations(annotations.copy_deep(GLib.strdup));
+- }
++ update_annotations_with_description (data, description);
+ m_emoji_to_data_dict.replace(emoji, data);
+ } else {
+ unowned IBus.EmojiData? en_data =
+@@ -462,16 +509,8 @@ class IBusEmojier : Gtk.Window {
+ string trans_description = data.get_description();
+ en_data.set_description(trans_description);
+ trans_description = utf8_down(trans_description);
++ update_annotations_with_description (data, trans_description);
+ unowned GLib.SList<string> annotations = data.get_annotations();
+- var words = trans_description.split(" ");
+- // If the description has less than 3 words, add it to annotations
+- // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
+- if (words.length < 3 &&
+- annotations.find_custom(
+- trans_description,
+- GLib.strcmp) == null) {
+- annotations.append(trans_description);
+- }
+ unowned GLib.SList<string> en_annotations
+ = en_data.get_annotations();
+ foreach (string annotation in en_annotations) {
+@@ -489,10 +528,11 @@ class IBusEmojier : Gtk.Window {
+
+ private static void update_category_to_emojis_dict(IBus.EmojiData data,
+ string lang) {
+- string emoji = (data.get_emoji_alternates() != "") ?
+- data.get_emoji_alternates() : data.get_emoji();
++ string emoji = data.get_emoji();
+ string category = data.get_category();
+- if (lang == "en" && category != "") {
++ if (category == "")
++ category = EMOJI_CATEGORY_OTHERS;
++ if (lang == "en") {
+ bool has_emoji = false;
+ unowned GLib.SList<string> hits =
+ m_category_to_emojis_dict.lookup(category);
+@@ -562,9 +602,17 @@ class IBusEmojier : Gtk.Window {
+ m_category_to_emojis_dict.get_keys();
+ // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
+ categories.sort((a, b) => {
++ if (a == EMOJI_CATEGORY_OTHERS && b != EMOJI_CATEGORY_OTHERS)
++ return 1;
++ else if (a != EMOJI_CATEGORY_OTHERS && b == EMOJI_CATEGORY_OTHERS)
++ return -1;
+ return GLib.strcmp(_(a), _(b));
+ });
+ foreach (unowned string category in categories) {
++ // "Others" category includes next unicode chars and fonts do not support
++ // the base and varints yet.
++ if (category == EMOJI_CATEGORY_OTHERS)
++ continue;
+ EBoxRow row = new EBoxRow(category);
+ string locale_category = _(category);
+ EPaddedLabelBox widget =
+@@ -718,7 +766,7 @@ class IBusEmojier : Gtk.Window {
+ uint page_end_pos = uint.min(page_start_pos + page_size, ncandidates);
+ if (m_backward != null) {
+ string backward_desc =
+- "%s (%u / %u)".printf(m_backward, cursor, ncandidates - 1);
++ "%s (%u / %u)".printf(_(m_backward), cursor, ncandidates - 1);
+ EPaddedLabelBox label =
+ new EPaddedLabelBox(backward_desc,
+ Gtk.Align.CENTER,
+@@ -746,7 +794,7 @@ class IBusEmojier : Gtk.Window {
+ int font_size = m_emoji_font_size - 2;
+ string emoji_font = "%s %d".printf(font_family, font_size);
+ string markup = "<span font=\"%s\">%s</span>".
+- printf(emoji_font, candidate.get_text());
++ printf(emoji_font, utf8_entity(candidate.get_text()));
+ label.set_markup(markup);
+ }
+ label.set_halign(Gtk.Align.FILL);
+--
+2.9.3
+
+From 0836c3ffd6764969dfaa9953f52a24c1cbf2c983 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Sat, 6 May 2017 00:22:50 +0900
+Subject: [PATCH] Support to match emoji annotations with partial string
+
+Now ibus-setup has a new tab of "Emoji" and it provides
+the options to choose a condtion and length to match
+emoji annotations partically.
+
+R=alexepico@gmail.com, penghuang@google.com
+
+Review URL: https://codereview.appspot.com/319700043
+---
+ data/ibus.schemas.in | 39 ++++
+ setup/main.py | 121 ++++++++++--
+ setup/setup.ui | 491 ++++++++++++++++++++++++++++++------------------
+ ui/gtk3/emojier.vala | 64 ++++++-
+ ui/gtk3/emojierapp.vala | 52 ++++-
+ ui/gtk3/panel.vala | 22 +++
+ 6 files changed, 582 insertions(+), 207 deletions(-)
+
+diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
+index 096dd71..0647004 100644
+--- a/data/ibus.schemas.in
++++ b/data/ibus.schemas.in
+@@ -404,6 +404,45 @@
+ </locale>
+ </schema>
+ <schema>
++ <key>/schemas/desktop/ibus/panel/emoji/has-partial-match</key>
++ <applyto>/desktop/ibus/panel/emoji/has-partial-match</applyto>
++ <owner>ibus</owner>
++ <type>bool</type>
++ <default>false</default>
++ <locale name="C">
++ <short>Whether emoji annotaions can be match partially or not</short>
++ <long>Whether emoji annotations can be matched with a partial
++ string instead of the exact match or not.</long>
++ </locale>
++ </schema>
++ <schema>
++ <key>/schemas/desktop/ibus/panel/emoji/partial-match-length</key>
++ <applyto>/desktop/ibus/panel/emoji/partial-match-length</applyto>
++ <owner>ibus</owner>
++ <type>int</type>
++ <default>3</default>
++ <locale name="C">
++ <short>Match emoji annotaions with the specified length</short>
++ <long>Match emoji annotations partially with more than
++ the specified number of characters instead of
++ the exact match.</long>
++ </locale>
++ </schema>
++ <schema>
++ <key>/schemas/desktop/ibus/panel/emoji/partial-match-condition</key>
++ <applyto>/desktop/ibus/panel/emoji/partial-match-condition</applyto>
++ <owner>ibus</owner>
++ <type>int</type>
++ <default>0</default>
++ <locale name="C">
++ <short>Choose a condition to match emoji annotations partially</short>
++ <long>Choose one of the following conditions to match emoji
++ annotations partially:
++ 0 == Prefix match, 1 == Suffix match, 2 == Containing match
++ </long>
++ </locale>
++ </schema>
++ <schema>
+ <key>/schemas/desktop/ibus/general/embed_preedit_text</key>
+ <applyto>/desktop/ibus/general/embed_preedit_text</applyto>
+ <owner>ibus</owner>
+diff --git a/setup/main.py b/setup/main.py
+index 7839cea..f0eee99 100644
+--- a/setup/main.py
++++ b/setup/main.py
+@@ -187,19 +187,6 @@ class Setup(object):
+ self.__fontbutton_custom_font,
+ 'sensitive',
+ Gio.SettingsBindFlags.GET)
+- self.__fontbutton_emoji_font = self.__builder.get_object(
+- 'fontbutton_emoji_font')
+- self.__fontbutton_emoji_font.set_preview_text('🙂🍎🚃💓📧⚽🐳');
+- self.__settings_emoji.bind('font',
+- self.__fontbutton_emoji_font,
+- 'font-name',
+- Gio.SettingsBindFlags.DEFAULT)
+- self.__button_emoji_lang = self.__builder.get_object(
+- 'button_emoji_lang')
+- self.__settings_emoji.bind('lang',
+- self.__button_emoji_lang,
+- 'lang',
+- Gio.SettingsBindFlags.DEFAULT)
+
+ # show icon on system tray
+ self.__checkbutton_show_icon_on_systray = self.__builder.get_object(
+@@ -287,6 +274,113 @@ class Setup(object):
+ self.__treeview.connect("notify::active-engine", self.__treeview_notify_cb)
+ self.__treeview.connect("notify::engines", self.__treeview_notify_cb)
+
++ def __init_emoji(self):
++ self.__fontbutton_emoji_font = self.__builder.get_object(
++ 'fontbutton_emoji_font')
++ self.__fontbutton_emoji_font.set_preview_text('🙂🍎🚃💓📧⚽🐳');
++ self.__settings_emoji.bind('font',
++ self.__fontbutton_emoji_font,
++ 'font-name',
++ Gio.SettingsBindFlags.DEFAULT)
++ self.__button_emoji_lang = self.__builder.get_object(
++ 'button_emoji_lang')
++ self.__settings_emoji.bind('lang',
++ self.__button_emoji_lang,
++ 'lang',
++ Gio.SettingsBindFlags.DEFAULT)
++ self.__checkbutton_emoji_partial_match = self.__builder.get_object(
++ 'checkbutton_emoji_partial_match')
++ checkbutton_label = self.__checkbutton_emoji_partial_match.get_child()
++ if type(checkbutton_label) == Gtk.Label:
++ checkbutton_label.set_property('wrap', True)
++ checkbutton_label.set_property('max-width-chars', 74)
++ self.__spinbutton_emoji_partial_match = self.__builder.get_object(
++ 'spinbutton_emoji_partial_match')
++ self.__settings_emoji.bind('has-partial-match',
++ self.__checkbutton_emoji_partial_match,
++ 'active',
++ Gio.SettingsBindFlags.DEFAULT)
++ self.__settings_emoji.bind('has-partial-match',
++ self.__spinbutton_emoji_partial_match,
++ 'sensitive',
++ Gio.SettingsBindFlags.GET)
++
++ def adjustment_value_changed_cb(obj):
++ key = 'partial-match-length'
++ value = int(adjustment.get_value())
++ if value == self.__settings_emoji.get_int(key):
++ return
++ self.__settings_emoji.set_int(key, value)
++ def settings_emoji_partial_match_length_cb(settings, key):
++ value = self.__settings_emoji.get_int(key)
++ old_value = int(self.__spinbutton_emoji_partial_match.get_value())
++ if value == old_value:
++ return
++ self.__spinbutton_emoji_partial_match.set_value(value)
++ settings_emoji_partial_match_length_cb(None, 'partial-match-length')
++ adjustment = self.__spinbutton_emoji_partial_match.get_adjustment()
++ adjustment.connect('value-changed', adjustment_value_changed_cb)
++ self.__settings_emoji.connect('changed::partial-match-length',
++ settings_emoji_partial_match_length_cb)
++
++ self.__hbox_emoji_partial_match = self.__builder.get_object(
++ 'hbox_emoji_partial_match')
++ self.__settings_emoji.bind('has-partial-match',
++ self.__hbox_emoji_partial_match,
++ 'sensitive',
++ Gio.SettingsBindFlags.GET)
++ self.__radiobutton_emoji_prefix_match = self.__builder.get_object(
++ 'radiobutton_emoji_prefix_match')
++ self.__radiobutton_emoji_suffix_match = self.__builder.get_object(
++ 'radiobutton_emoji_suffix_match')
++ self.__radiobutton_emoji_contain_match = self.__builder.get_object(
++ 'radiobutton_emoji_contain_match')
++
++ def radiobuton_emoji_partial_match_cb(obj):
++ key = 'partial-match-condition'
++ condition = 0
++ if not obj.get_active():
++ return
++ if obj == self.__radiobutton_emoji_prefix_match:
++ condition = 0
++ elif obj == self.__radiobutton_emoji_suffix_match:
++ condition = 1
++ elif obj == self.__radiobutton_emoji_contain_match:
++ condition = 2
++ else:
++ print('Wrong emoji partial match object')
++ return
++ self.__settings_emoji.set_int(key, condition)
++ def settings_emoji_partial_match_condition_cb(settings, key):
++ value = self.__settings_emoji.get_int(key)
++ obj = None
++ if value == 0:
++ obj = self.__radiobutton_emoji_prefix_match
++ elif value == 1:
++ obj = self.__radiobutton_emoji_suffix_match
++ elif value == 2:
++ obj = self.__radiobutton_emoji_contain_match
++ else:
++ print('Wrong emoji partial match condition')
++ return
++ if obj.get_active():
++ return
++ obj.set_active(True)
++
++ settings_emoji_partial_match_condition_cb(None,
++ 'partial-match-condition')
++ self.__radiobutton_emoji_prefix_match.connect(
++ 'toggled',
++ radiobuton_emoji_partial_match_cb)
++ self.__radiobutton_emoji_suffix_match.connect(
++ 'toggled',
++ radiobuton_emoji_partial_match_cb)
++ self.__radiobutton_emoji_contain_match.connect(
++ 'toggled',
++ radiobuton_emoji_partial_match_cb)
++ self.__settings_emoji.connect('changed::partial-match-condition',
++ settings_emoji_partial_match_condition_cb)
++
+ def __init_ui(self):
+ # add icon search path
+ self.__window = self.__builder.get_object("window_preferences")
+@@ -306,6 +400,7 @@ class Setup(object):
+ self.__init_hotkeys()
+ self.__init_panel()
+ self.__init_general()
++ self.__init_emoji()
+
+ def __gdk_window_set_cb(self, object, pspec):
+ window = object.get_window()
+diff --git a/setup/setup.ui b/setup/setup.ui
+index 54ef916..322f514 100644
+--- a/setup/setup.ui
++++ b/setup/setup.ui
+@@ -55,6 +55,14 @@
+ </row>
+ </data>
+ </object>
++ <object class="GtkAdjustment" id="adjustment_emoji_partial_match">
++ <property name="value">3.0</property>
++ <property name="lower">1.0</property>
++ <property name="upper">255.0</property>
++ <property name="step-increment">1.0</property>
++ <property name="page-increment">10.0</property>
++ <property name="page-size">10.0</property>
++ </object>
+ <object class="GtkWindow" id="window_preferences">
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+@@ -109,8 +117,6 @@
+ <property name="label" translatable="yes">Next input method:</property>
+ </object>
+ <packing>
+- <property name="top_attach">3</property>
+- <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+@@ -125,69 +131,13 @@
+ <property name="label" translatable="yes">Previous input method:</property>
+ </object>
+ <packing>
+- <property name="top_attach">4</property>
+- <property name="bottom_attach">5</property>
+- <property name="x_options">GTK_FILL</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+- <child>
+- <object class="GtkLabel" id="emoji_label">
+- <property name="visible">True</property>
+- <property name="can_focus">False</property>
+- <property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
+- <property name="halign">start</property>
+- <property name="label" translatable="yes">Emoji choice:</property>
+- </object>
+- <packing>
+- <property name="top_attach">5</property>
+- <property name="bottom_attach">6</property>
++ <property name="top_attach">1</property>
++ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+- <object class="GtkBox" id="hbox4">
+- <property name="orientation">horizontal</property>
+- <property name="no_show_all">True</property>
+- <property name="can_focus">False</property>
+- <property name="spacing">6</property>
+- <child>
+- <object class="GtkEntry" id="entry_trigger">
+- <property name="visible">True</property>
+- <property name="can_focus">True</property>
+- <property name="editable">False</property>
+- </object>
+- <packing>
+- <property name="expand">True</property>
+- <property name="fill">True</property>
+- <property name="position">0</property>
+- </packing>
+- </child>
+- <child>
+- <object class="GtkButton" id="button_trigger">
+- <property name="label" translatable="yes">...</property>
+- <property name="use_action_appearance">False</property>
+- <property name="visible">True</property>
+- <property name="can_focus">True</property>
+- <property name="receives_default">False</property>
+- <property name="use_action_appearance">False</property>
+- <property name="use_underline">True</property>
+- </object>
+- <packing>
+- <property name="expand">False</property>
+- <property name="fill">True</property>
+- <property name="position">1</property>
+- </packing>
+- </child>
+- </object>
+- <packing>
+- <property name="left_attach">1</property>
+- <property name="right_attach">2</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+- <child>
+ <object class="GtkBox" id="hbox5">
+ <property name="orientation">horizontal</property>
+ <property name="visible">True</property>
+@@ -225,8 +175,6 @@
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+- <property name="top_attach">3</property>
+- <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+@@ -270,51 +218,8 @@
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+- <property name="top_attach">4</property>
+- <property name="bottom_attach">5</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+- <child>
+- <object class="GtkBox" id="emoji_hbox">
+- <property name="orientation">horizontal</property>
+- <property name="visible">True</property>
+- <property name="can_focus">False</property>
+- <property name="spacing">6</property>
+- <child>
+- <object class="GtkEntry" id="entry_emoji_dialog">
+- <property name="visible">True</property>
+- <property name="can_focus">True</property>
+- <property name="editable">False</property>
+- </object>
+- <packing>
+- <property name="expand">True</property>
+- <property name="fill">True</property>
+- <property name="position">0</property>
+- </packing>
+- </child>
+- <child>
+- <object class="GtkButton" id="button_emoji_dialog">
+- <property name="label" translatable="yes">...</property>
+- <property name="use_action_appearance">False</property>
+- <property name="visible">True</property>
+- <property name="can_focus">True</property>
+- <property name="receives_default">False</property>
+- <property name="use_action_appearance">False</property>
+- <property name="use_underline">True</property>
+- </object>
+- <packing>
+- <property name="expand">False</property>
+- <property name="fill">True</property>
+- <property name="position">1</property>
+- </packing>
+- </child>
+- </object>
+- <packing>
+- <property name="left_attach">1</property>
+- <property name="right_attach">2</property>
+- <property name="top_attach">5</property>
+- <property name="bottom_attach">6</property>
++ <property name="top_attach">1</property>
++ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+@@ -327,6 +232,8 @@
+ <property name="label" translatable="yes">Enable or disable:</property>
+ </object>
+ <packing>
++ <property name="top_attach">2</property>
++ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+@@ -339,8 +246,8 @@
+ <property name="label" translatable="yes">Enable:</property>
+ </object>
+ <packing>
+- <property name="top_attach">1</property>
+- <property name="bottom_attach">2</property>
++ <property name="top_attach">3</property>
++ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+@@ -383,8 +290,8 @@
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+- <property name="top_attach">1</property>
+- <property name="bottom_attach">2</property>
++ <property name="top_attach">3</property>
++ <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+@@ -396,8 +303,8 @@
+ <property name="label" translatable="yes">Disable:</property>
+ </object>
+ <packing>
+- <property name="top_attach">2</property>
+- <property name="bottom_attach">3</property>
++ <property name="top_attach">4</property>
++ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+@@ -440,8 +347,8 @@
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+- <property name="top_attach">2</property>
+- <property name="bottom_attach">3</property>
++ <property name="top_attach">4</property>
++ <property name="bottom_attach">5</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+@@ -675,68 +582,6 @@
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+- <child>
+- <object class="GtkLabel" id="label_emoji_font">
+- <property name="visible">True</property>
+- <property name="can_focus">False</property>
+- <property name="tooltip_text" translatable="yes">Set a font of emoji candidates on the emoji dialog</property>
+- <property name="halign">start</property>
+- <property name="label" translatable="yes">Emoji font:</property>
+- <property name="justify">right</property>
+- </object>
+- <packing>
+- <property name="top_attach">7</property>
+- <property name="bottom_attach">8</property>
+- <property name="x_options">GTK_FILL</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+- <child>
+- <object class="GtkFontButton" id="fontbutton_emoji_font">
+- <property name="visible">True</property>
+- <property name="can_focus">True</property>
+- <property name="receives_default">True</property>
+- <property name="use_action_appearance">False</property>
+- </object>
+- <packing>
+- <property name="left_attach">1</property>
+- <property name="right_attach">2</property>
+- <property name="top_attach">7</property>
+- <property name="bottom_attach">8</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+- <child>
+- <object class="GtkLabel" id="label_emoji_lang">
+- <property name="visible">True</property>
+- <property name="can_focus">False</property>
+- <property name="tooltip_text" translatable="yes">Set a language of emoji annotations on the emoji dialog</property>
+- <property name="halign">start</property>
+- <property name="label" translatable="yes">Emoji annotation language:</property>
+- <property name="justify">right</property>
+- </object>
+- <packing>
+- <property name="top_attach">8</property>
+- <property name="bottom_attach">9</property>
+- <property name="x_options">GTK_FILL</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+- <child>
+- <object class="EmojiLangButton" id="button_emoji_lang">
+- <property name="visible">True</property>
+- <property name="can_focus">True</property>
+- <property name="receives_default">True</property>
+- <property name="use_action_appearance">False</property>
+- </object>
+- <packing>
+- <property name="left_attach">1</property>
+- <property name="right_attach">2</property>
+- <property name="top_attach">8</property>
+- <property name="bottom_attach">9</property>
+- <property name="y_options">GTK_FILL</property>
+- </packing>
+- </child>
+ </object>
+ </child>
+ <child type="label">
+@@ -1027,6 +872,292 @@
+ </packing>
+ </child>
+ <child>
++ <object class="GtkBox" id="vbox_emoji1">
++ <property name="orientation">vertical</property>
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="spacing">18</property>
++ <property name="margin_top">12</property>
++ <property name="margin_bottom">12</property>
++ <property name="margin_left">12</property>
++ <property name="margin_right">12</property>
++ <child>
++ <object class="GtkFrame" id="frame_emoji1">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="label_xalign">0</property>
++ <property name="shadow_type">none</property>
++ <child>
++ <object class="GtkTable" id="table_emoji1">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="n_rows">5</property>
++ <property name="n_columns">2</property>
++ <property name="column_spacing">12</property>
++ <property name="row_spacing">6</property>
++ <property name="margin_top">6</property>
++ <property name="margin_left">12</property>
++ <child>
++ <object class="GtkLabel" id="label_emoji1">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
++ <property name="halign">start</property>
++ <property name="label" translatable="yes">Emoji choice:</property>
++ </object>
++ <packing>
++ <property name="x_options">GTK_FILL</property>
++ <property name="y_options">GTK_FILL</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkBox" id="hbox_emoji1">
++ <property name="orientation">horizontal</property>
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="spacing">6</property>
++ <child>
++ <object class="GtkEntry" id="entry_emoji_dialog">
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="editable">False</property>
++ </object>
++ <packing>
++ <property name="expand">True</property>
++ <property name="fill">True</property>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkButton" id="button_emoji_dialog">
++ <property name="label" translatable="yes">...</property>
++ <property name="use_action_appearance">False</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="use_action_appearance">False</property>
++ <property name="use_underline">True</property>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="fill">True</property>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="left_attach">1</property>
++ <property name="right_attach">2</property>
++ <property name="y_options">GTK_FILL</property>
++ </packing>
++ </child>
++ </object>
++ </child>
++ <child type="label">
++ <object class="GtkLabel" id="label_emoji2">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="label" translatable="yes"><b>Keyboard Shortcuts</b></property>
++ <property name="use_markup">True</property>
++ </object>
++ </child>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="fill">True</property>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkFrame" id="frame_emoji2">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="label_xalign">0</property>
++ <property name="shadow_type">none</property>
++ <child>
++ <object class="GtkGrid" id="table_emoji2">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="column_spacing">12</property>
++ <property name="row_spacing">6</property>
++ <property name="margin_top">6</property>
++ <property name="margin_left">12</property>
++ <property name="row_homogeneous">false</property>
++ <child>
++ <object class="GtkLabel" id="label_emoji_font">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="tooltip_text" translatable="yes">Set a font of emoji candidates on the emoji dialog</property>
++ <property name="halign">start</property>
++ <property name="label" translatable="yes">Emoji font:</property>
++ <property name="justify">right</property>
++ </object>
++ <packing>
++ <property name="left_attach">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkFontButton" id="fontbutton_emoji_font">
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">True</property>
++ <property name="use_action_appearance">False</property>
++ </object>
++ <packing>
++ <property name="left_attach">1</property>
++ <!-- property name="right_attach">2</property -->
++ </packing>
++ </child>
++ <child>
++ <object class="GtkLabel" id="label_emoji_lang">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="tooltip_text" translatable="yes">Set a language of emoji annotations on the emoji dialog</property>
++ <property name="halign">start</property>
++ <property name="label" translatable="yes">Emoji annotation language:</property>
++ <property name="justify">right</property>
++ </object>
++ <packing>
++ <property name="left_attach">0</property>
++ <property name="top_attach">1</property>
++ </packing>
++ </child>
++ <child>
++ <object class="EmojiLangButton" id="button_emoji_lang">
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">True</property>
++ <property name="use_action_appearance">False</property>
++ </object>
++ <packing>
++ <property name="left_attach">1</property>
++ <property name="top_attach">1</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkCheckButton" id="checkbutton_emoji_partial_match">
++ <property name="label" translatable="yes">Match emoji annotations partially with the following condition and more than the number of characters:</property>
++ <property name="use_action_appearance">False</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="tooltip_text" translatable="yes">If emoji annotations can be matched with a partial string instead of the exact match</property>
++ <property name="use_action_appearance">False</property>
++ <property name="valign">start</property>
++ <property name="draw_indicator">True</property>
++ </object>
++ <packing>
++ <property name="left_attach">0</property>
++ <property name="width">2</property>
++ <property name="top_attach">2</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkSpinButton" id="spinbutton_emoji_partial_match">
++ <property name="adjustment">adjustment_emoji_partial_match</property>
++ <property name="value">3.0</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="halign">start</property>
++ </object>
++ <packing>
++ <property name="left_attach">0</property>
++ <property name="width">2</property>
++ <property name="top_attach">3</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkBox" id="hbox_emoji_partial_match">
++ <property name="orientation">horizontal</property>
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="spacing">6</property>
++ <child>
++ <object class="GtkRadioButton" id="radiobutton_emoji_prefix_match">
++ <property name="label" translatable="yes">Prefix match</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="halign">start</property>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="fill">True</property>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkRadioButton" id="radiobutton_emoji_suffix_match">
++ <property name="label" translatable="yes">Suffix match</property>
++ <property name="group">radiobutton_emoji_prefix_match</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="halign">start</property>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="fill">True</property>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkRadioButton" id="radiobutton_emoji_contain_match">
++ <property name="label" translatable="yes">Containing match</property>
++ <property name="group">radiobutton_emoji_prefix_match</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="halign">start</property>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="fill">True</property>
++ <property name="position">2</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="left_attach">0</property>
++ <property name="width">2</property>
++ <property name="top_attach">4</property>
++ </packing>
++ </child>
++ </object>
++ </child>
++ <child type="label">
++ <object class="GtkLabel" id="label_emoji3">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="label" translatable="yes"><b>Font and Style</b></property>
++ <property name="use_markup">True</property>
++ </object>
++ </child>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="fill">True</property>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">2</property>
++ </packing>
++ </child>
++ <child type="tab">
++ <object class="GtkLabel" id="emoji_tab">
++ <property name="visible">True</property>
++ <property name="can_focus">False</property>
++ <property name="label" translatable="yes">Emoji</property>
++ </object>
++ <packing>
++ <property name="position">2</property>
++ <property name="tab_fill">False</property>
++ </packing>
++ </child>
++ <child>
+ <object class="GtkBox" id="vbox7">
+ <property name="orientation">vertical</property>
+ <property name="visible">True</property>
+@@ -1134,7 +1265,7 @@
+ </child>
+ </object>
+ <packing>
+- <property name="position">2</property>
++ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+@@ -1214,7 +1345,7 @@ Homepage: https://github.com/ibus/ibus/wiki
+ </child>
+ </object>
+ <packing>
+- <property name="position">3</property>
++ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+@@ -1224,7 +1355,7 @@ Homepage: https://github.com/ibus/ibus/wiki
+ <property name="label" translatable="yes">About</property>
+ </object>
+ <packing>
+- <property name="position">3</property>
++ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 8f55bd4..76d5fed 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -177,6 +177,9 @@ class IBusEmojier : Gtk.Window {
+ private static int m_emoji_font_size;
+ private static string[] m_favorites;
+ private static int m_emoji_max_seq_len;
++ private static bool m_has_partial_match;
++ private static uint m_partial_match_length;
++ private static uint m_partial_match_condition;
+ private static GLib.HashTable<string, GLib.SList>?
+ m_annotation_to_emojis_dict;
+ private static GLib.HashTable<string, IBus.EmojiData>?
+@@ -738,16 +741,53 @@ class IBusEmojier : Gtk.Window {
+ }
+ // Call check_unicode_point() to get m_unicode_point
+ check_unicode_point();
+- unowned GLib.SList<string>? emojis =
+- m_annotation_to_emojis_dict.lookup(annotation);
+- if (emojis == null && m_unicode_point == null) {
++ GLib.SList<string>? total_emojis = null;
++ unowned GLib.SList<string>? sub_emojis = null;
++ int length = annotation.length;
++ if (m_has_partial_match && length >= m_partial_match_length) {
++ foreach (unowned string key in
++ m_annotation_to_emojis_dict.get_keys()) {
++ if (key.length < length)
++ continue;
++ bool matched = false;
++ switch(m_partial_match_condition) {
++ case 0:
++ if (key.has_prefix(annotation))
++ matched = true;
++ break;
++ case 1:
++ if (key.has_suffix(annotation))
++ matched = true;
++ break;
++ case 2:
++ if (key.str(annotation) != null)
++ matched = true;
++ break;
++ default:
++ break;
++ }
++ if (!matched)
++ continue;
++ sub_emojis = m_annotation_to_emojis_dict.lookup(key);
++ foreach (unowned string emoji in sub_emojis) {
++ if (total_emojis.find_custom(emoji, GLib.strcmp) == null) {
++ total_emojis.append(emoji);
++ }
++ }
++ }
++ } else {
++ sub_emojis = m_annotation_to_emojis_dict.lookup(annotation);
++ foreach (unowned string emoji in sub_emojis)
++ total_emojis.append(emoji);
++ }
++ if (total_emojis == null && m_unicode_point == null) {
+ hide_candidate_panel();
+ return;
+ }
+ m_lookup_table.clear();
+ // Call check_unicode_point() to update m_lookup_table
+ check_unicode_point();
+- foreach (unowned string emoji in emojis) {
++ foreach (unowned string emoji in total_emojis) {
+ IBus.Text text = new IBus.Text.from_string(emoji);
+ m_lookup_table.append_candidate(text);
+ }
+@@ -1331,6 +1371,22 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
++ public static void set_partial_match(bool has_partial_match) {
++ m_has_partial_match = has_partial_match;
++ }
++
++ public static void set_partial_match_length(int length) {
++ if (length < 1)
++ return;
++ m_partial_match_length = length;
++ }
++
++ public static void set_partial_match_condition(int condition) {
++ if (condition < 0)
++ return;
++ m_partial_match_condition = condition;
++ }
++
+ public static void set_favorites(string[]? unowned_favorites) {
+ m_favorites = {};
+ foreach (string favorite in unowned_favorites) {
+diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
+index cbdd8ba..dbdbc27 100644
+--- a/ui/gtk3/emojierapp.vala
++++ b/ui/gtk3/emojierapp.vala
+@@ -22,6 +22,9 @@
+
+ string emoji_font = null;
+ string annotation_lang = null;
++bool partial_match = false;
++int partial_match_length = -1;
++int partial_match_condition = -1;
+
+ public class EmojiApplication : Application {
+ private IBusEmojier? m_emojier;
+@@ -65,17 +68,28 @@ public class EmojiApplication : Application {
+
+ const OptionEntry[] options = {
+ { "font", 0, 0, OptionArg.STRING, out emoji_font,
+- /* TRANSLATORS: "FONT" should be capital and translatable.
+- * It's used for an argument command --font=FONT
+- */
+- N_("\"FONT\" for emoji chracters on emoji dialog"),
+- N_("FONT") },
++ /* TRANSLATORS: "FONT" should be capital and translatable.
++ * It's used for an argument command --font=FONT
++ */
++ N_("\"FONT\" for emoji chracters on emoji dialog"),
++ N_("FONT") },
+ { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
+- /* TRANSLATORS: "LANG" should be capital and translatable.
+- * It's used for an argument command --lang=LANG
+- */
+- N_("\"LANG\" for annotations on emoji dialog. E.g. \"en\""),
+- N_("LANG") },
++ /* TRANSLATORS: "LANG" should be capital and translatable.
++ * It's used for an argument command --lang=LANG
++ */
++ N_("\"LANG\" for annotations on emoji dialog. E.g. \"en\""),
++ N_("LANG") },
++ { "partial-match", 0, 0, OptionArg.NONE, out partial_match,
++ N_("Emoji annotaions can be match partially"),
++ null },
++ { "partial-match-length", 0, 0, OptionArg.INT,
++ out partial_match_length,
++ N_("Match with the length of the specified integer"),
++ null },
++ { "partial-match-condition", 0, 0, OptionArg.INT,
++ out partial_match_condition,
++ N_("Match with the condition of the specified integer"),
++ null },
+ { null }
+ };
+
+@@ -111,6 +125,24 @@ public class EmojiApplication : Application {
+ if (annotation_lang == null)
+ annotation_lang = m_settings_emoji.get_string("lang");
+ IBusEmojier.set_annotation_lang(annotation_lang);
++ IBusEmojier.set_partial_match(partial_match);
++ if (partial_match_length > 0) {
++ IBusEmojier.set_partial_match_length(partial_match_length);
++ } else {
++ IBusEmojier.set_partial_match_length(
++ m_settings_emoji.get_int("partial-match-length"));
++ }
++ if (partial_match_condition > 2) {
++ warning("Need condition between 0 and 2.");
++ IBusEmojier.set_partial_match_condition(
++ m_settings_emoji.get_int("partial-match-condition"));
++ }
++ else if (partial_match_condition >= 0) {
++ IBusEmojier.set_partial_match_condition(partial_match_condition);
++ } else {
++ IBusEmojier.set_partial_match_condition(
++ m_settings_emoji.get_int("partial-match-condition"));
++ }
+
+ if (emoji_font != null)
+ IBusEmojier.set_emoji_font(emoji_font);
+diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
+index 745489b..5f0009e 100644
+--- a/ui/gtk3/panel.vala
++++ b/ui/gtk3/panel.vala
+@@ -248,6 +248,18 @@ class Panel : IBus.PanelService {
+ m_settings_emoji.changed["lang"].connect((key) => {
+ set_emoji_lang();
+ });
++
++ m_settings_emoji.changed["has-partial-match"].connect((key) => {
++ set_emoji_partial_match();
++ });
++
++ m_settings_emoji.changed["partial-match-length"].connect((key) => {
++ set_emoji_partial_match();
++ });
++
++ m_settings_emoji.changed["partial-match-condition"].connect((key) => {
++ set_emoji_partial_match();
++ });
+ }
+
+ #if INDICATOR
+@@ -782,6 +794,15 @@ class Panel : IBus.PanelService {
+ });
+ }
+
++ private void set_emoji_partial_match() {
++ IBusEmojier.set_partial_match(
++ m_settings_emoji.get_boolean("has-partial-match"));
++ IBusEmojier.set_partial_match_length(
++ m_settings_emoji.get_int("partial-match-length"));
++ IBusEmojier.set_partial_match_condition(
++ m_settings_emoji.get_int("partial-match-condition"));
++ }
++
+ private int compare_versions(string version1, string version2) {
+ string[] version1_list = version1.split(".");
+ string[] version2_list = version2.split(".");
+@@ -897,6 +918,7 @@ class Panel : IBus.PanelService {
+ set_property_icon_delay_time();
+ set_emoji_favorites();
+ set_emoji_lang();
++ set_emoji_partial_match();
+ }
+
+ private void engine_contexts_insert(IBus.EngineDesc engine) {
+--
+2.9.3
+
+From edcb4a0259d2833904e7d1aac7c74e1aa49cea5b Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 8 May 2017 12:25:54 +0900
+Subject: [PATCH] ui/gtk3: Hide emoji variants by default
+
+The emoji variants, i.e. skin colors and additional items, would not
+be used in most cases and it is nice to hide it by default.
+Now added GtkMenu and changed GtkWindow to GtkApplicationWindow
+for the menu.
+
+R=penghuang@google.com
+
+Review URL: https://codereview.appspot.com/322970043
+---
+ ui/gtk3/emojier.vala | 299 ++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 211 insertions(+), 88 deletions(-)
+
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index 76d5fed..db7520a 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -20,7 +20,7 @@
+ * USA
+ */
+
+-class IBusEmojier : Gtk.Window {
++class IBusEmojier : Gtk.ApplicationWindow {
+ private class EEntry : Gtk.SearchEntry {
+ public EEntry() {
+ GLib.Object(
+@@ -97,6 +97,15 @@ class IBusEmojier : Gtk.Window {
+ set_label(text);
+ }
+ }
++ private class EGoldLabel : Gtk.Label {
++ public EGoldLabel(string text) {
++ GLib.Object(
++ name : "IBusEmojierGoldLabel"
++ );
++ if (text != "")
++ set_label(text);
++ }
++ }
+ private class EPaddedLabel : Gtk.Label {
+ public EPaddedLabel(string text,
+ Gtk.Align align) {
+@@ -150,11 +159,21 @@ class IBusEmojier : Gtk.Window {
+ var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+ set_custom_title(vbox);
+ var label = new Gtk.Label(title);
+- label.get_style_context().add_class("title");
++ label.get_style_context().add_class(Gtk.STYLE_CLASS_TITLE);
+ vbox.pack_start(label, true, false, 0);
+ m_lang_label = new Gtk.Label(null);
+- m_lang_label.get_style_context().add_class("subtitle");
++ m_lang_label.get_style_context().add_class(
++ Gtk.STYLE_CLASS_SUBTITLE);
+ vbox.pack_start(m_lang_label, true, false, 0);
++
++ var menu = new GLib.Menu();
++ menu.append(_("Show emoji variants"), "win.variant");
++ var menu_button = new Gtk.MenuButton();
++ menu_button.set_direction(Gtk.ArrowType.NONE);
++ menu_button.set_valign(Gtk.Align.CENTER);
++ menu_button.set_menu_model(menu);
++ menu_button.set_tooltip_text(_("Menu"));
++ pack_end(menu_button);
+ }
+ public void set_lang_label(string str) {
+ m_lang_label.set_text(str);
+@@ -167,7 +186,13 @@ class IBusEmojier : Gtk.Window {
+ }
+
+ private const uint EMOJI_GRID_PAGE = 10;
++ private const string EMOJI_CATEGORY_FAVORITES = N_("Favorites");
+ private const string EMOJI_CATEGORY_OTHERS = N_("Others");
++ private const unichar[] EMOJI_VARIANT_LIST = {
++ 0x1f3fb, 0x1f3fc, 0x1f3fd, 0x1f3fe, 0x1f3ff, 0x200d };
++ private const GLib.ActionEntry[] m_action_entries = {
++ { "variant", check_action_variant_cb, null, "false", null }
++ };
+
+ // Set the actual default values in the constructor
+ // because these fields are used for class_init() and static functions,
+@@ -180,18 +205,22 @@ class IBusEmojier : Gtk.Window {
+ private static bool m_has_partial_match;
+ private static uint m_partial_match_length;
+ private static uint m_partial_match_condition;
+- private static GLib.HashTable<string, GLib.SList>?
++ private static bool m_show_emoji_variant = false;
++ private static GLib.HashTable<string, GLib.SList<string>>?
+ m_annotation_to_emojis_dict;
+ private static GLib.HashTable<string, IBus.EmojiData>?
+ m_emoji_to_data_dict;
+- private static GLib.HashTable<string, GLib.SList>?
++ private static GLib.HashTable<string, GLib.SList<string>>?
+ m_category_to_emojis_dict;
++ private static GLib.HashTable<string, GLib.SList<string>>?
++ m_emoji_to_emoji_variants_dict;
+
+ private ThemedRGBA m_rgba;
+ private Gtk.Box m_vbox;
+ private ETitleLabelBox m_title;
+ private EEntry m_entry;
+ private string? m_backward;
++ private int m_backward_index = -1;
+ private EScrolledWindow? m_scrolled_window = null;
+ private EListBox m_list_box;
+ private bool m_is_running = false;
+@@ -200,7 +229,7 @@ class IBusEmojier : Gtk.Window {
+ private string? m_result;
+ private string? m_unicode_point = null;
+ private bool m_candidate_panel_is_visible;
+- int m_category_active_index;
++ private int m_category_active_index;
+ private IBus.LookupTable m_lookup_table;
+ private Gtk.Label[] m_candidates;
+ private bool m_enter_notify_enable = true;
+@@ -223,6 +252,7 @@ class IBusEmojier : Gtk.Window {
+ focus_visible : true
+ );
+
++ add_action_entries(m_action_entries, this);
+ if (m_current_lang_id == null)
+ m_current_lang_id = "en";
+ if (m_emoji_font_family == null)
+@@ -270,6 +300,13 @@ class IBusEmojier : Gtk.Window {
+ "rgba(%u, %u, %u, %lf); ".printf(
+ bg_red, bg_green, bg_blue, bg_alpha) +
+ "border-width: 4px; border-radius: 3px; }";
++ data += "#IBusEmojierGoldLabel { color: " +
++ "rgba(%u, %u, %u, %lf); ".printf(
++ fg_red, fg_green, fg_blue, fg_alpha) +
++ "font-family: %s; font-size: %dpt; ".printf(
++ m_emoji_font_family, m_emoji_font_size) +
++ "background-color: #b09c5f; " +
++ "border-width: 4px; border-radius: 3px; }";
+
+ Gtk.CssProvider css_provider = new Gtk.CssProvider();
+ try {
+@@ -317,10 +354,7 @@ class IBusEmojier : Gtk.Window {
+ });
+
+ candidate_clicked.connect((i, b, s) => {
+- IBus.Text candidate = m_lookup_table.get_candidate(i);
+- m_result = candidate.text;
+- m_loop.quit();
+- hide_candidate_panel();
++ candidate_panel_select_index(i);
+ });
+
+ if (m_annotation_to_emojis_dict == null) {
+@@ -345,14 +379,17 @@ class IBusEmojier : Gtk.Window {
+
+ private static void init_emoji_dict() {
+ m_annotation_to_emojis_dict =
+- new GLib.HashTable<string, GLib.SList>(GLib.str_hash,
+- GLib.str_equal);
++ new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
++ GLib.str_equal);
+ m_emoji_to_data_dict =
+ new GLib.HashTable<string, IBus.EmojiData>(GLib.str_hash,
+ GLib.str_equal);
+ m_category_to_emojis_dict =
+- new GLib.HashTable<string, GLib.SList>(GLib.str_hash,
+- GLib.str_equal);
++ new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
++ GLib.str_equal);
++ m_emoji_to_emoji_variants_dict =
++ new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
++ GLib.str_equal);
+ }
+
+
+@@ -390,14 +427,16 @@ class IBusEmojier : Gtk.Window {
+ }
+ if (!has_emoji) {
+ hits.append(emoji);
+- m_annotation_to_emojis_dict.replace(annotation, hits.copy());
++ m_annotation_to_emojis_dict.replace(
++ annotation,
++ hits.copy_deep(GLib.strdup));
+ }
+ }
+ }
+
+
+ private static string utf8_down(string str) {
+- GLib.StringBuilder buff = new GLib.StringBuilder();
++ var buff = new GLib.StringBuilder();
+ int length = str.char_count();
+ for (int i = 0; i < length; i++) {
+ buff.append_unichar(str.get_char(0).tolower());
+@@ -407,23 +446,8 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
+- private static string utf8_title(string str) {
+- StringBuilder buff = new StringBuilder();
+- int length = str.char_count();
+- for (int i = 0; i < length; i++) {
+- unichar ch = str.get_char(0);
+- if (i == 0)
+- buff.append_unichar(ch.toupper());
+- else
+- buff.append_unichar(ch);
+- str = str.next_char();
+- }
+- return buff.str;
+- }
+-
+-
+ private static string utf8_code_point(string str) {
+- StringBuilder buff = new StringBuilder();
++ var buff = new GLib.StringBuilder();
+ int length = str.char_count();
+ for (int i = 0; i < length; i++) {
+ unichar ch = str.get_char(0);
+@@ -438,7 +462,7 @@ class IBusEmojier : Gtk.Window {
+
+
+ private static string utf8_entity(string str) {
+- GLib.StringBuilder buff = new GLib.StringBuilder();
++ var buff = new GLib.StringBuilder();
+ int length = str.char_count();
+ for (int i = 0; i < length; i++) {
+ unichar ch = str.get_char(0);
+@@ -503,8 +527,19 @@ class IBusEmojier : Gtk.Window {
+ update_annotations_with_description (data, description);
+ m_emoji_to_data_dict.replace(emoji, data);
+ } else {
+- unowned IBus.EmojiData? en_data =
+- m_emoji_to_data_dict.lookup(emoji);
++ unowned IBus.EmojiData? en_data = null;
++ // If emoji presentation (+= 0xfe0f) is already saved in dict,
++ // update it instead of no presentation.
++ // emoji-test.txt has all emoji presentations but $lang.xml has
++ // some no emoji presentations.
++ if (emoji.chr(-1, 0xfe0f) == null) {
++ var buff = new GLib.StringBuilder();
++ buff.append(emoji);
++ buff.append_unichar(0xfe0f);
++ en_data = m_emoji_to_data_dict.lookup(buff.str);
++ }
++ if (en_data == null)
++ en_data = m_emoji_to_data_dict.lookup(emoji);
+ if (en_data == null) {
+ m_emoji_to_data_dict.insert(emoji, data);
+ return;
+@@ -536,6 +571,36 @@ class IBusEmojier : Gtk.Window {
+ if (category == "")
+ category = EMOJI_CATEGORY_OTHERS;
+ if (lang == "en") {
++ bool has_variant = false;
++ foreach (unichar ch in EMOJI_VARIANT_LIST) {
++ if (emoji.chr(-1, ch) != null) {
++ has_variant = true;
++ break;
++ }
++ }
++ // If emoji includes variants (skin colors and items),
++ // it's escaped in m_emoji_to_emoji_variants_dict and
++ // not shown by default.
++ if (has_variant) {
++ unichar base_ch = emoji.get_char();
++ string base_emoji = base_ch.to_string();
++ var buff = new GLib.StringBuilder();
++ buff.append_unichar(base_ch);
++ buff.append_unichar(0xfe0f);
++ if (m_emoji_to_data_dict.lookup(buff.str) != null)
++ base_emoji = buff.str;
++ unowned GLib.SList<string>? variants =
++ m_emoji_to_emoji_variants_dict.lookup(base_emoji);
++ if (variants.find_custom(emoji, GLib.strcmp) == null) {
++ if (variants == null)
++ variants.append(base_emoji);
++ variants.append(emoji);
++ m_emoji_to_emoji_variants_dict.replace(
++ base_emoji,
++ variants.copy_deep(GLib.strdup));
++ }
++ return;
++ }
+ bool has_emoji = false;
+ unowned GLib.SList<string> hits =
+ m_category_to_emojis_dict.lookup(category);
+@@ -547,7 +612,8 @@ class IBusEmojier : Gtk.Window {
+ }
+ if (!has_emoji) {
+ hits.append(emoji);
+- m_category_to_emojis_dict.replace(category, hits.copy());
++ m_category_to_emojis_dict.replace(category,
++ hits.copy_deep(GLib.strdup));
+ }
+ }
+ }
+@@ -588,14 +654,15 @@ class IBusEmojier : Gtk.Window {
+ m_list_box.row_activated.connect((box, gtkrow) => {
+ m_category_active_index = 0;
+ EBoxRow row = gtkrow as EBoxRow;
+- show_emoji_for_category(row);
++ show_emoji_for_category(row.text);
+ });
+
+ uint n = 1;
+ if (m_favorites.length > 0) {
+- EBoxRow row = new EBoxRow("@favorites");
++ EBoxRow row = new EBoxRow(EMOJI_CATEGORY_FAVORITES);
+ EPaddedLabelBox widget =
+- new EPaddedLabelBox(_("Favorites"), Gtk.Align.CENTER);
++ new EPaddedLabelBox(_(EMOJI_CATEGORY_FAVORITES),
++ Gtk.Align.CENTER);
+ row.add(widget);
+ m_list_box.add(row);
+ if (n++ == m_category_active_index)
+@@ -617,10 +684,8 @@ class IBusEmojier : Gtk.Window {
+ if (category == EMOJI_CATEGORY_OTHERS)
+ continue;
+ EBoxRow row = new EBoxRow(category);
+- string locale_category = _(category);
+ EPaddedLabelBox widget =
+- new EPaddedLabelBox(utf8_title(locale_category),
+- Gtk.Align.CENTER);
++ new EPaddedLabelBox(_(category), Gtk.Align.CENTER);
+ row.add(widget);
+ m_list_box.add(row);
+ if (n++ == m_category_active_index)
+@@ -635,23 +700,40 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
+- private void show_emoji_for_category(EBoxRow row) {
+- if (row.text == "@favorites") {
++ private void show_emoji_for_category(string category) {
++ if (category == EMOJI_CATEGORY_FAVORITES) {
+ m_lookup_table.clear();
+ foreach (unowned string favorate in m_favorites) {
+ IBus.Text text = new IBus.Text.from_string(favorate);
+ m_lookup_table.append_candidate(text);
+ }
+- m_backward = _("Favorites");
++ m_backward = category;
+ } else {
+ unowned GLib.SList<unowned string> emojis =
+- m_category_to_emojis_dict.lookup(row.text);
++ m_category_to_emojis_dict.lookup(category);
+ m_lookup_table.clear();
+ foreach (unowned string emoji in emojis) {
+ IBus.Text text = new IBus.Text.from_string(emoji);
+ m_lookup_table.append_candidate(text);
+ }
+- m_backward = utf8_title(row.text);
++ m_backward = category;
++ }
++ // Restore the cursor position before the special table of
++ // emoji variants is shown.
++ if (m_backward_index >= 0) {
++ m_lookup_table.set_cursor_pos((uint)m_backward_index);
++ m_backward_index = -1;
++ }
++ show_candidate_panel();
++ }
++
++
++ private void show_emoji_variants(GLib.SList<string>? emojis) {
++ m_backward_index = (int)m_lookup_table.get_cursor_pos();
++ m_lookup_table.clear();
++ foreach (unowned string emoji in emojis) {
++ IBus.Text text = new IBus.Text.from_string(emoji);
++ m_lookup_table.append_candidate(text);
+ }
+ show_candidate_panel();
+ }
+@@ -696,8 +778,8 @@ class IBusEmojier : Gtk.Window {
+ private bool check_unicode_point() {
+ string annotation = m_entry.get_text();
+ m_unicode_point = null;
+- GLib.StringBuilder buff = new GLib.StringBuilder();
+- GLib.StringBuilder retval = new GLib.StringBuilder();
++ var buff = new GLib.StringBuilder();
++ var retval = new GLib.StringBuilder();
+ for (int i = 0; i < annotation.char_count(); i++) {
+ unichar ch = annotation.get_char(i);
+ if (ch == 0)
+@@ -727,7 +809,7 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
+- public void update_candidate_window() {
++ private void update_candidate_window() {
+ string annotation = m_entry.get_text();
+ if (annotation.length == 0) {
+ hide_candidate_panel();
+@@ -795,6 +877,22 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
++ private void show_code_point_description(string text) {
++ EPaddedLabelBox widget_code = new EPaddedLabelBox(
++ _("Code point: %s").printf(utf8_code_point(text)),
++ Gtk.Align.START);
++ m_vbox.add(widget_code);
++ widget_code.show_all();
++ if (m_emoji_to_emoji_variants_dict.lookup(text) != null) {
++ EPaddedLabelBox widget_has_variant = new EPaddedLabelBox(
++ _("Has emoji variants"),
++ Gtk.Align.START);
++ m_vbox.add(widget_has_variant);
++ widget_has_variant.show_all();
++ }
++ }
++
++
+ private void show_candidate_panel() {
+ remove_all_children();
+ set_fixed_size();
+@@ -823,18 +921,27 @@ class IBusEmojier : Gtk.Window {
+ EGrid grid = new EGrid();
+ int n = 0;
+ for (uint i = page_start_pos; i < page_end_pos; i++) {
+- IBus.Text candidate = m_lookup_table.get_candidate(i);
++ string text = m_lookup_table.get_candidate(i).text;
++ bool has_variant =
++ (m_emoji_to_emoji_variants_dict.lookup(text) != null);
+ Gtk.Label label;
+- if (i == cursor)
+- label = new ESelectedLabel(candidate.text) as Gtk.Label;
+- else
+- label = new EWhiteLabel(candidate.text) as Gtk.Label;
+- if (candidate.text.char_count() > 2) {
++ // If 'i' is the cursor position, use the selected color.
++ // If the emoji has emoji variants, use the gold color.
++ // Otherwise the white color.
++ if (i == cursor) {
++ label = new ESelectedLabel(text) as Gtk.Label;
++ } else if (m_show_emoji_variant && has_variant &&
++ m_backward_index < 0) {
++ label = new EGoldLabel(text) as Gtk.Label;
++ } else {
++ label = new EWhiteLabel(text) as Gtk.Label;
++ }
++ if (text.char_count() > 2) {
+ string font_family = m_emoji_font_family;
+ int font_size = m_emoji_font_size - 2;
+ string emoji_font = "%s %d".printf(font_family, font_size);
+ string markup = "<span font=\"%s\">%s</span>".
+- printf(emoji_font, utf8_entity(candidate.get_text()));
++ printf(emoji_font, utf8_entity(text));
+ label.set_markup(markup);
+ }
+ label.set_halign(Gtk.Align.FILL);
+@@ -879,9 +986,8 @@ class IBusEmojier : Gtk.Window {
+ show_arrow_buttons();
+ m_vbox.add(grid);
+ grid.show_all();
+- IBus.Text candidate = m_lookup_table.get_candidate(cursor);
+- unowned IBus.EmojiData? data =
+- m_emoji_to_data_dict.lookup(candidate.text);
++ string text = m_lookup_table.get_candidate(cursor).text;
++ unowned IBus.EmojiData? data = m_emoji_to_data_dict.lookup(text);
+ if (data == null) {
+ // TODO: Provide a custom description and annotation for
+ // the favorite emojis.
+@@ -890,12 +996,7 @@ class IBusEmojier : Gtk.Window {
+ Gtk.Align.START);
+ m_vbox.add(widget);
+ widget.show_all();
+- EPaddedLabelBox widget_code = new EPaddedLabelBox(
+- _("Code point: %s").printf(
+- utf8_code_point(candidate.text)),
+- Gtk.Align.START);
+- m_vbox.add(widget_code);
+- widget_code.show_all();
++ show_code_point_description(text);
+ return;
+ } else {
+ unowned string description = data.get_description();
+@@ -907,7 +1008,7 @@ class IBusEmojier : Gtk.Window {
+ }
+ unowned GLib.SList<unowned string>? annotations =
+ data.get_annotations();
+- GLib.StringBuilder buff = new GLib.StringBuilder();
++ var buff = new GLib.StringBuilder();
+ int i = 0;
+ foreach (unowned string annotation in annotations) {
+ if (i++ == 0)
+@@ -929,12 +1030,7 @@ class IBusEmojier : Gtk.Window {
+ m_vbox.add(widget);
+ widget.show_all();
+ }
+- EPaddedLabelBox widget_code = new EPaddedLabelBox(
+- _("Code point: %s").printf(
+- utf8_code_point(candidate.text)),
+- Gtk.Align.START);
+- m_vbox.add(widget_code);
+- widget_code.show_all();
++ show_code_point_description(text);
+ }
+ }
+
+@@ -961,6 +1057,20 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
++ private void candidate_panel_select_index(uint index) {
++ string text = m_lookup_table.get_candidate(index).text;
++ unowned GLib.SList<string>? emojis =
++ m_emoji_to_emoji_variants_dict.lookup(text);
++ if (m_show_emoji_variant && emojis != null &&
++ m_backward_index < 0) {
++ show_emoji_variants(emojis);
++ } else {
++ m_result = text;
++ m_loop.quit();
++ hide_candidate_panel();
++ }
++ }
++
+ private void candidate_panel_cursor_down() {
+ enter_notify_disable_with_timer();
+ uint ncandidates = m_lookup_table.get_number_of_candidates();
+@@ -1094,8 +1204,11 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
+- private bool key_press_cursor_escape() {
+- if (m_candidate_panel_is_visible) {
++ private bool key_press_escape() {
++ if (m_backward_index >= 0 && m_backward != null) {
++ show_emoji_for_category(m_backward);
++ return true;
++ } else if (m_candidate_panel_is_visible) {
+ hide_candidate_panel();
+ return true;
+ } else if (m_entry.get_text().length == 0) {
+@@ -1108,6 +1221,19 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
++ private bool key_press_enter() {
++ if (m_candidate_panel_is_visible) {
++ uint index = m_lookup_table.get_cursor_pos();
++ candidate_panel_select_index(index);
++ } else if (m_category_active_index > 0) {
++ Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
++ EBoxRow row = gtkrow as EBoxRow;
++ show_emoji_for_category(row.text);
++ }
++ return true;
++ }
++
++
+ private void entry_enter_keyval(uint keyval) {
+ unichar ch = IBus.keyval_to_unicode(keyval);
+ if (ch.iscntrl())
+@@ -1130,6 +1256,13 @@ class IBusEmojier : Gtk.Window {
+ }
+
+
++ private void check_action_variant_cb(GLib.SimpleAction action,
++ GLib.Variant? parameter) {
++ m_show_emoji_variant = !action.get_state().get_boolean();
++ action.set_state(new GLib.Variant.boolean(m_show_emoji_variant));
++ }
++
++
+ public string run(string input_context_path) {
+ assert (m_loop == null);
+
+@@ -1187,21 +1320,11 @@ class IBusEmojier : Gtk.Window {
+ */
+ switch (keyval) {
+ case Gdk.Key.Escape:
+- if (key_press_cursor_escape())
++ if (key_press_escape())
+ return true;
+ break;
+ case Gdk.Key.Return:
+- if (m_candidate_panel_is_visible) {
+- uint index = m_lookup_table.get_cursor_pos();
+- IBus.Text text = m_lookup_table.get_candidate(index);
+- m_result = text.text;
+- m_loop.quit();
+- hide_candidate_panel();
+- } else if (m_category_active_index > 0) {
+- Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
+- EBoxRow row = gtkrow as EBoxRow;
+- show_emoji_for_category(row);
+- }
++ key_press_enter();
+ return true;
+ case Gdk.Key.BackSpace:
+ if (m_entry.get_text().len() > 0) {
+@@ -1283,7 +1406,7 @@ class IBusEmojier : Gtk.Window {
+ return true;
+ break;
+ case Gdk.Key.u:
+- if (key_press_cursor_escape())
++ if (key_press_escape())
+ return true;
+ break;
+ case Gdk.Key.a:
+--
+2.9.3
+
+From 5f653d3102ada9139a2bc7504d6beaee1bc9984b Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 9 May 2017 13:54:01 +0900
+Subject: [PATCH] ui/gtk3: Add ibus-emoji man mapge
+
+R=Shawn.P.Huang@gmail.com
+
+Review URL: https://codereview.appspot.com/323810043
+---
+ setup/ibus-setup.1.in | 21 +++++++++++--
+ tools/ibus.1.in | 20 ++++++++++--
+ ui/gtk3/Makefile.am | 22 ++++++++++++++
+ ui/gtk3/ibus-emoji.1.in | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 140 insertions(+), 4 deletions(-)
+ create mode 100644 ui/gtk3/ibus-emoji.1.in
+
+diff --git a/setup/ibus-setup.1.in b/setup/ibus-setup.1.in
+index dab032e..f9b5f71 100644
+--- a/setup/ibus-setup.1.in
++++ b/setup/ibus-setup.1.in
+@@ -1,10 +1,10 @@
+ .\" This file is distributed under the same license as the ibus
+ .\" package.
+ .\" Copyright (C) LI Daobing <lidaobing@gmail.com>, 2008.
+-.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013-2015.
++.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013-2017.
+ .\" Copyright (c) Peng Huang <shawn.p.huang@gmail.com>, 2013.
+ .\"
+-.TH "IBUS-SETUP" 1 "November 2008" "@VERSION@" "User Commands"
++.TH "IBUS-SETUP" 1 "November 2017" "@VERSION@" "User Commands"
+ .SH NAME
+ .B ibus-setup
+ \- configuration program for ibus
+@@ -26,6 +26,23 @@ is the configuration program for IBus.
+ .PP
+ Homepage: https://github.com/ibus/ibus/wiki
+
++.SH "ADVANCED"
++.TP
++\fBUse system keyboard layout\fR
++If this option is enabled, use your system keymap instead and IBus
++does not change it with any input method engines. The system keymap is
++decided by your desktop session. IBus calls
++.B setxkbmap (1)
++command internally to set the selected keymap and override the
++system keymap if this options is disabled.
++You can check the current keymap with
++.B setxkbmap -query
++command.
++.TP
++\fBShare the same input method among all applications\fR
++If this option is disabled, You can choose an input metod engine
++by input context.
++
+ .SH BUGS
+ If you find a bug, please report it at http://code.google.com/p/ibus/issues/list
+
+diff --git a/tools/ibus.1.in b/tools/ibus.1.in
+index a420567..d34a77a 100644
+--- a/tools/ibus.1.in
++++ b/tools/ibus.1.in
+@@ -1,9 +1,9 @@
+ .\" This file is distributed under the same license as the ibus
+ .\" package.
+-.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013.
++.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2013-2017.
+ .\" Copyright (c) Peng Huang <shawn.p.huang@gmail.com>, 2013.
+ .\"
+-.TH "IBUS" 1 "May 2013" "@VERSION@" "User Commands"
++.TH "IBUS" 1 "May 2017" "@VERSION@" "User Commands"
+ .SH NAME
+ .B ibus
+ \- command line utility for ibus
+@@ -95,6 +95,22 @@ configuration file.
+ \fBwatch\fR
+ Under construction.
+ .TP
++\fBemoji\fR [\fB\-\-font=FONT|\-\-lang=LANG|\-\-help|\-\-partial\-match\fR]
++Launch IBus Emojier (
++.B ibus\-emoji (1)
++) and save the selected emoji to your clipboard. Can choose an emoji font with
++.B \-\-font
++option. Can choose a language of emoji annotations with
++.B \-\-lang
++option. If LANG="en", IBus Emojier loads /usr/share/ibus/dicts/emoji-en.dict .
++.B \-\-partial\-match
++option enables to match annotations with a partial string. These settings
++are avaiable with
++.B ibus\-setup (1)
++utility.
+
+ .SH BUGS
+ If you find a bug, please report it at https://github.com/ibus/ibus/issues
++
++.SH "SEE ALSO"
++.BR ibus-emoji (1)
+diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
+index c557474..270ad1c 100644
+--- a/ui/gtk3/Makefile.am
++++ b/ui/gtk3/Makefile.am
+@@ -148,6 +148,8 @@ emoji_headers = \
+ ibusemojidialog.h \
+ $(NULL)
+
++man_one_in_files = ibus-emoji.1.in
++
+ # References:
+ # libappindicator/src/notification-item.xml
+ # libappindicator/src/notification-watcher.xml
+@@ -155,6 +157,7 @@ emoji_headers = \
+ # kdelibs/kdeui/knotifications/src/org.kde.StatusNotifierWatcher.xml
+ EXTRA_DIST = \
+ $(emoji_headers) \
++ $(man_one_in_files) \
+ IBusEmojiDialog-1.0.metadata \
+ gtkpanel.xml.in \
+ ibus-emoji-dialog-1.0.deps \
+@@ -253,8 +256,27 @@ MAINTAINERCLEANFILES += $(VAPIGEN_VAPIS)
+ DISTCLEANFILES += $(VAPIGEN_VAPIS)
+ EXTRA_DIST += $(VAPIGEN_VAPIS)
+
++# end of ENABLE_VAPIGEN
+ endif
++# end of HAVE_INTROSPECTION
+ endif
++
++man_one_files = $(man_one_in_files:.1.in=.1)
++man_one_DATA =$(man_one_files:.1=.1.gz)
++man_onedir = $(mandir)/man1
++%.1: %.1.in
++ $(AM_V_GEN) sed \
++ -e 's|@VERSION[@]|$(VERSION)|g' $< > $@.tmp && \
++ mv $@.tmp $@
++%.1.gz: %.1
++ $(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@
++
++CLEANFILES += \
++ $(man_one_DATA) \
++ $(man_one_files) \
++ $(NULL)
++
++# end of ENABLE_EMOJI_DICT
+ endif
+
+ -include $(top_srcdir)/git.mk
+diff --git a/ui/gtk3/ibus-emoji.1.in b/ui/gtk3/ibus-emoji.1.in
+new file mode 100644
+index 0000000..3523271
+--- /dev/null
++++ b/ui/gtk3/ibus-emoji.1.in
+@@ -0,0 +1,81 @@
++.\" This file is distributed under the same license as the ibus
++.\" package.
++.\" Copyright (C) Takao Fujiwara <takao.fujiwara1@gmail.com>, 2017.
++.\"
++.TH "IBUS EMOJI" 1 "May 2017" "@VERSION@" "User Commands"
++.SH NAME
++.B ibus emoji utility
++\- Call the IBus emoji utility by
++.B IBus Emojier
++
++.SH "SYNOPSIS"
++.B /usr/libexec/ibus\-ui\-emojier
++[\fIOPTION\fR]...
++
++.SH "DESCRIPTION"
++
++.PP
++IBus Emojier provides a GUI to select an emoji by typing an emoji annotaion
++or choosing a character with mouse click and it's designed to work as
++an extended IBus lookup window using Space, Enter, and Arrow keys.
++The text entry accepts an emoji annotation or Unicode points.
++If IBus panel is launched, IBus panel calls IBus Emojier with a
++shortcut key or the right click menu and output the selected emoji
++on the previous focused text on an application.
++If IBus panel is not available likes GNOME desktop, You can still use
++.B ibus emoji
++command to launch IBus Emojier and it saves the selected emoji in
++your clipboard and you can paste the emoji by a paste key likes
++Control\-v.
++
++.PP
++Refer
++.B emoji
++section in
++.B ibus (1)
++for the command line options.
++
++.PP
++Homepage: https://github.com/ibus/ibus/wiki
++
++.SH "SETTINGS"
++.TP
++\fBANNOTATION LANGUAGE\fR
++You can choose a language of emoji annotations with
++.B ibus\-setup (1).
++.TP
++\fBFONT\fR
++You can choose an emoji font with
++.B ibus\-setup (1).
++E.g. "Noto Color Emoji", "Android Emoji" font.
++
++.SH "KEYBOARD OPERATIONS"
++.TP
++\fBControl-Shift-e\fR
++Launch IBus Emojier. The shortcut key can be customized by
++.B ibus\-setup (1).
++.TP
++\fBLeft or right arrow\fR
++Move the selected category or emoji if an annotation is not typed.
++Otherwise move the cursor in typed annotation.
++.TP
++\fBDown or up arrow\fR
++Move the selected category or emoji.
++.TP
++\fBEnter\fR
++Commit the selected emoji.
++.TP
++\fBEscape\fR
++Go back to the category list from emoji list, erase a typed annotation,
++and close IBus Emojier.
++.TP
++\fBSpace\fR
++Move the selected category or emoji. If Shift\-Space is typed,
++a space chararacter can be inserted between words in an annotation or
++Unicode points. E.g. 1f466 Shift\-Space 1f3fb
++.TP
++\fBPageDown or PageUp\fR
++Move to the next or previous page in emoji list.
++
++.SH BUGS
++If you find a bug, please report it at https://github.com/ibus/ibus/issues
+--
+2.9.3
+
+From 1159093dea3fdb8ceb62157f06b3560f8fc28378 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 9 May 2017 13:58:02 +0900
+Subject: [PATCH] Update favorites category automatically by selecting
+ emoji
+
+When you commit an emoji with Emojier, Favorite category
+will be shown on GUI.
+Now a custom annotation also can be assigned to a favorite emoji
+by gsetting command.
+
+R=penghuang@google.com
+
+Review URL: https://codereview.appspot.com/312660043
+---
+ data/ibus.schemas.in | 13 ++++++++++
+ src/ibusemoji.h | 2 +-
+ ui/gtk3/emojier.vala | 63 +++++++++++++++++++++++++++++++++++++++++++++--
+ ui/gtk3/emojierapp.vala | 18 ++++++++++++++
+ ui/gtk3/ibusemojidialog.h | 20 ++++++++++-----
+ ui/gtk3/panel.vala | 21 +++++++++++++++-
+ 6 files changed, 127 insertions(+), 10 deletions(-)
+
+diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
+index 0647004..09b6b2c 100644
+--- a/data/ibus.schemas.in
++++ b/data/ibus.schemas.in
+@@ -404,6 +404,19 @@
+ </locale>
+ </schema>
+ <schema>
++ <key>/schemas/desktop/ibus/panel/emoji/favorite-annotations</key>
++ <applyto>/desktop/ibus/panel/emoji/favorite-annotations</applyto>
++ <owner>ibus</owner>
++ <type>list</type>
++ <default>[]</default>
++ <list_type>string</list_type>
++ <locale name="C">
++ <short>favorite emoji annotation list on emoji dialog</short>
++ <long>You can assign an annotation for a favorite emoji
++ in this list.</long>
++ </locale>
++ </schema>
++ <schema>
+ <key>/schemas/desktop/ibus/panel/emoji/has-partial-match</key>
+ <applyto>/desktop/ibus/panel/emoji/has-partial-match</applyto>
+ <owner>ibus</owner>
+diff --git a/src/ibusemoji.h b/src/ibusemoji.h
+index eb24fdd..4edee72 100644
+--- a/src/ibusemoji.h
++++ b/src/ibusemoji.h
+@@ -188,7 +188,7 @@ GHashTable * ibus_emoji_dict_load (const gchar *path);
+ * @dict: (element-type utf8 IBusEmojiData) (transfer full): An Emoji dictionary
+ * @emoji: an emoji character
+ *
+- * Returns: An #IBusEmojiData of @emoji.
++ * Returns: (transfer none): An #IBusEmojiData of @emoji.
+ * This API was prepared for the old dict foramat with Gir and Vala
+ * but no longer needed.
+ * Use ibus_emoji_data_load() instead.
+diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
+index db7520a..4169550 100644
+--- a/ui/gtk3/emojier.vala
++++ b/ui/gtk3/emojier.vala
+@@ -201,6 +201,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+ private static string m_emoji_font_family;
+ private static int m_emoji_font_size;
+ private static string[] m_favorites;
++ private static string[] m_favorite_annotations;
+ private static int m_emoji_max_seq_len;
+ private static bool m_has_partial_match;
+ private static uint m_partial_match_length;
+@@ -261,6 +262,8 @@ class IBusEmojier : Gtk.ApplicationWindow {
+ m_emoji_font_size = 16;
+ if (m_favorites == null)
+ m_favorites = {};
++ if (m_favorite_annotations == null)
++ m_favorite_annotations = {};
+
+ Gdk.Display display = Gdk.Display.get_default();
+ Gdk.Screen screen = (display != null) ?
+@@ -374,6 +377,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
+ }
+ make_emoji_dict(m_current_lang_id);
+ }
++ update_favorite_emoji_dict();
+ }
+
+
+@@ -1263,6 +1267,49 @@ class IBusEmojier : Gtk.ApplicationWindow {
+ }
+
+
++ public static void update_favorite_emoji_dict() {
++ if (m_emoji_to_data_dict == null ||
++ m_annotation_to_emojis_dict == null)
++ return;
++
++ for(int i = 0; i < m_favorites.length; i++) {
++ var favorite = m_favorites[i];
++
++ string? annotation = "";
++ if (i < m_favorite_annotations.length) {
++ annotation = m_favorite_annotations[i];
++ }
++ if (annotation == "")
++ continue;
++ unowned IBus.EmojiData? data =
++ m_emoji_to_data_dict.lookup(favorite);
++ if (data == null) {
++ GLib.SList<string> new_annotations = new GLib.SList<string>();
++ new_annotations.append(annotation);
++ IBus.EmojiData new_data = GLib.Object.new(
++ typeof(IBus.EmojiData),
++ "emoji", favorite.dup(),
++ "annotations", new_annotations,
++ "description", annotation.dup()
++ ) as IBus.EmojiData;
++ m_emoji_to_data_dict.insert(favorite, new_data);
++ } else {
++ unowned GLib.SList<string> annotations = data.get_annotations();
++ if (annotations.find_custom(annotation, GLib.strcmp) == null) {
++ annotations.append(annotation);
++ data.set_annotations(annotations.copy());
++ }
++ }
++ unowned GLib.SList<string> emojis =
++ m_annotation_to_emojis_dict.lookup(annotation);
++ if (emojis.find_custom(favorite, GLib.strcmp) == null) {
++ emojis.append(favorite);
++ m_annotation_to_emojis_dict.replace(annotation, emojis.copy());
++ }
++ }
++ }
++
++
+ public string run(string input_context_path) {
+ assert (m_loop == null);
+
+@@ -1510,10 +1557,22 @@ class IBusEmojier : Gtk.ApplicationWindow {
+ m_partial_match_condition = condition;
+ }
+
+- public static void set_favorites(string[]? unowned_favorites) {
++ public static void set_favorites(string[]? unowned_favorites,
++ string[]? unowned_favorite_annotations) {
+ m_favorites = {};
+- foreach (string favorite in unowned_favorites) {
++ m_favorite_annotations = {};
++ for(int i = 0; i < unowned_favorites.length; i++) {
++ string? favorite = unowned_favorites[i];
++ // Avoid gsetting value error by manual setting
++ GLib.return_if_fail(favorite != null);
++ GLib.return_if_fail(favorite != "");
+ m_favorites += favorite;
+ }
++ for(int i = 0; i < unowned_favorite_annotations.length; i++) {
++ string? favorite_annotation = unowned_favorite_annotations[i];
++ GLib.return_if_fail(favorite_annotation != null);
++ m_favorite_annotations += favorite_annotation;
++ }
++ update_favorite_emoji_dict();
+ }
+ }
+diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
+index dbdbc27..24816bb 100644
+--- a/ui/gtk3/emojierapp.vala
++++ b/ui/gtk3/emojierapp.vala
+@@ -50,6 +50,20 @@ public class EmojiApplication : Application {
+ Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
+ clipboard.set_text(emoji, -1);
+ clipboard.store();
++
++ var emojier_favorites = m_settings_emoji.get_strv("favorites");
++ bool has_favorite = false;
++ foreach (unowned string favorite in emojier_favorites) {
++ if (favorite == emoji) {
++ has_favorite = true;
++ break;
++ }
++ }
++ if (!has_favorite) {
++ emojier_favorites += emoji;
++ m_settings_emoji.set_strv("favorites", emojier_favorites);
++ }
++
+ m_emojier = null;
+ command_line.print("%s\n", _("Copied an emoji to your clipboard."));
+ }
+@@ -147,6 +161,10 @@ public class EmojiApplication : Application {
+ if (emoji_font != null)
+ IBusEmojier.set_emoji_font(emoji_font);
+
++ IBusEmojier.set_favorites(
++ m_settings_emoji.get_strv("favorites"),
++ m_settings_emoji.get_strv("favorite-annotations"));
++
+ activate_dialog(command_line);
+
+ return Posix.EXIT_SUCCESS;
+diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
+index 1499a3c..fb0f5a6 100644
+--- a/ui/gtk3/ibusemojidialog.h
++++ b/ui/gtk3/ibusemojidialog.h
+@@ -145,18 +145,26 @@ void ibus_emojier_set_annotation_lang (const gchar* lang);
+ */
+ void ibus_emojier_set_emoji_font (const gchar* emoji_font);
+
+-#if 0
+-/* TODO: set customized annotations */
+ /**
+ * ibus_emojier_set_favorites:
+ * @favorites: (array length=favorites_length): A custom emoji list.
+ * @favorites_length: A length of @favorites
+- *
+- * Set emoji font on the emoji dialog
++ * @favorite_annotations: (array length=favorite_annotations_length):
++ * (nullable):
++ * A custom annotation listfor @favorites.
++ * @favorite_annotations_length: A length of @favorite_annotations
++ *
++ * Set custom emojis on the emoji dialog. @favorite_annotations
++ * can be null. If you don't assign an annotation for a specific emoji,
++ * you can pass the annotation as "", e.g.
++ * favorite_annotations = { "", "", "my annotation" };
+ */
+ void ibus_emojier_set_favorites (gchar** favorites,
+ int
+- favorites_length);
+-#endif
++ favorites_length,
++ gchar**
++ favorite_annotations,
++ int
++ favorite_annotations_length);
+ G_END_DECLS
+ #endif
+diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
+index 5f0009e..e4936df 100644
+--- a/ui/gtk3/panel.vala
++++ b/ui/gtk3/panel.vala
+@@ -76,6 +76,7 @@ class Panel : IBus.PanelService {
+ private IBusEmojier? m_emojier;
+ private uint m_emojier_set_emoji_lang_id;
+ private uint m_emojier_focus_commit_text_id;
++ private string[] m_emojier_favorites = {};
+ private PropertyManager m_property_manager;
+ private PropertyPanel m_property_panel;
+ private GLib.Pid m_setup_pid = 0;
+@@ -245,6 +246,10 @@ class Panel : IBus.PanelService {
+ set_emoji_favorites();
+ });
+
++ m_settings_emoji.changed["favorite-annotations"].connect((key) => {
++ set_emoji_favorites();
++ });
++
+ m_settings_emoji.changed["lang"].connect((key) => {
+ set_emoji_lang();
+ });
+@@ -778,7 +783,10 @@ class Panel : IBus.PanelService {
+ }
+
+ private void set_emoji_favorites() {
+- IBusEmojier.set_favorites(m_settings_emoji.get_strv("favorites"));
++ m_emojier_favorites = m_settings_emoji.get_strv("favorites");
++ IBusEmojier.set_favorites(
++ m_emojier_favorites,
++ m_settings_emoji.get_strv("favorite-annotations"));
+ }
+
+ private void set_emoji_lang() {
+@@ -1437,6 +1445,17 @@ class Panel : IBus.PanelService {
+ IBus.Text text = new IBus.Text.from_string(selected_string);
+ commit_text(text);
+ m_emojier = null;
++ bool has_favorite = false;
++ foreach (unowned string favorite in m_emojier_favorites) {
++ if (favorite == selected_string) {
++ has_favorite = true;
++ break;
++ }
++ }
++ if (!has_favorite) {
++ m_emojier_favorites += selected_string;
++ m_settings_emoji.set_strv("favorites", m_emojier_favorites);
++ }
+ return true;
+ }
+
+--
+2.9.3
+
+From a897dab2957568f842b78d54dc40dd294fb6191f Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 9 May 2017 14:47:08 +0900
+Subject: [PATCH] ui/gtk3: Rename ibus-emoji.1.in to ibus-emoji.7.in
+
+---
+ tools/ibus.1.in | 2 +-
+ ui/gtk3/Makefile.am | 18 +++++++++---------
+ ui/gtk3/{ibus-emoji.1.in => ibus-emoji.7.in} | 0
+ 3 files changed, 10 insertions(+), 10 deletions(-)
+ rename ui/gtk3/{ibus-emoji.1.in => ibus-emoji.7.in} (100%)
+
+diff --git a/tools/ibus.1.in b/tools/ibus.1.in
+index d34a77a..9275abb 100644
+--- a/tools/ibus.1.in
++++ b/tools/ibus.1.in
+@@ -113,4 +113,4 @@ utility.
+ If you find a bug, please report it at https://github.com/ibus/ibus/issues
+
+ .SH "SEE ALSO"
+-.BR ibus-emoji (1)
++.BR ibus-emoji (7)
+diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
+index 270ad1c..c79641a 100644
+--- a/ui/gtk3/Makefile.am
++++ b/ui/gtk3/Makefile.am
+@@ -148,7 +148,7 @@ emoji_headers = \
+ ibusemojidialog.h \
+ $(NULL)
+
+-man_one_in_files = ibus-emoji.1.in
++man_seven_in_files = ibus-emoji.7.in
+
+ # References:
+ # libappindicator/src/notification-item.xml
+@@ -157,7 +157,7 @@ man_one_in_files = ibus-emoji.1.in
+ # kdelibs/kdeui/knotifications/src/org.kde.StatusNotifierWatcher.xml
+ EXTRA_DIST = \
+ $(emoji_headers) \
+- $(man_one_in_files) \
++ $(man_seven_in_files) \
+ IBusEmojiDialog-1.0.metadata \
+ gtkpanel.xml.in \
+ ibus-emoji-dialog-1.0.deps \
+@@ -261,19 +261,19 @@ endif
+ # end of HAVE_INTROSPECTION
+ endif
+
+-man_one_files = $(man_one_in_files:.1.in=.1)
+-man_one_DATA =$(man_one_files:.1=.1.gz)
+-man_onedir = $(mandir)/man1
+-%.1: %.1.in
++man_seven_files = $(man_seven_in_files:.7.in=.7)
++man_seven_DATA =$(man_seven_files:.7=.7.gz)
++man_sevendir = $(mandir)/man7
++%.7: %.7.in
+ $(AM_V_GEN) sed \
+ -e 's|@VERSION[@]|$(VERSION)|g' $< > $@.tmp && \
+ mv $@.tmp $@
+-%.1.gz: %.1
++%.7.gz: %.7
+ $(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@
+
+ CLEANFILES += \
+- $(man_one_DATA) \
+- $(man_one_files) \
++ $(man_seven_DATA) \
++ $(man_seven_files) \
+ $(NULL)
+
+ # end of ENABLE_EMOJI_DICT
+diff --git a/ui/gtk3/ibus-emoji.1.in b/ui/gtk3/ibus-emoji.7.in
+similarity index 100%
+rename from ui/gtk3/ibus-emoji.1.in
+rename to ui/gtk3/ibus-emoji.7.in
+--
+2.9.3
+
diff --git a/ibus.spec b/ibus.spec
index f39c857..bdecd45 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -28,7 +28,7 @@
Name: ibus
Version: 1.5.15
-Release: 7%{?dist}
+Release: 8%{?dist}
Summary: Intelligent Input Bus for Linux OS
License: LGPLv2+
Group: System Environment/Libraries
@@ -36,6 +36,8 @@ URL: https://github.com/ibus/%name/wiki
Source0: https://github.com/ibus/%name/releases/download/%{version}/%{name}-%{version}.tar.gz
Source1: %{name}-xinput
Source2: %{name}.conf.5
+# FIXME: Use unicode-emoji package
+Source3: http://www.unicode.org/Public/emoji/4.0/emoji-test.txt
# Will remove the annotation tarball once the rpm is available on Fedora
# Upstreamed patches.
# Patch0: %%{name}-HEAD.patch
@@ -68,10 +70,6 @@ BuildRequires: libwayland-client-devel
%if %with_kde5
BuildRequires: qt5-qtbase-devel
%endif
-%ifarch %{nodejs_arches}
-BuildRequires: nodejs-emojione-json
-BuildRequires: json-glib-devel
-%endif
BuildRequires: cldr-emoji-annotation
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
@@ -232,6 +230,7 @@ The ibus-devel-docs package contains developer documentation for IBus
# %%patch0 -p1
# cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c ||
%patch0 -p1
+cp %SOURCE3 data/annotations
%build
#autoreconf -f -i -v
@@ -254,9 +253,7 @@ autoreconf -f -i -v
--disable-appindicator \
%endif
--enable-introspection \
-%ifnarch %{nodejs_arches}
- --disable-emoji-dict \
-%endif
+ --with-unicode-emoji-dir=$PWD/data/annotations \
%{nil}
make -C ui/gtk3 maintainer-clean-generic
@@ -359,15 +356,14 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
%{_datadir}/GConf/gsettings/*
%{_datadir}/glib-2.0/schemas/*.xml
%{_datadir}/ibus/component
-%ifarch %{nodejs_arches}
%{_datadir}/ibus/dicts
-%endif
%{_datadir}/ibus/engine
%{_datadir}/ibus/keymaps
%{_datadir}/icons/hicolor/*/apps/*
%{_datadir}/man/man1/ibus.1.gz
%{_datadir}/man/man1/ibus-daemon.1.gz
%{_datadir}/man/man5/ibus.conf.5.gz
+%{_datadir}/man/man7/ibus-emoji.7.gz
%{_libexecdir}/ibus-engine-simple
%{_libexecdir}/ibus-dconf
%{_libexecdir}/ibus-ui-emojier
@@ -428,6 +424,15 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || :
%{_datadir}/gtk-doc/html/*
%changelog
+* Tue May 09 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.15-8
+- Dropped nodejs-emojione-json and import unicode-emoji instead
+- Created emoji tab in ibus-setup
+- Set default emoji font size from gsettings in ibus emoji command
+- Added an option of emoji partial match in ibus-setup
+- Hid emoji variants by default
+- Added ibus-emoji man page
+- emoji favorites category is updated by selecting emoji
+
* Thu Apr 13 2017 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.15-7
- Supported ibus emoji command on Wayland
- Changed modal dialog to modeless dialog
reply other threads:[~2026-05-31 2:06 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178019319260.1.16822556303133795888.rpms-ibus-8dd4dc1c86fb@fedoraproject.org \
--to=tfujiwar@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox