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 -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 +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 @@ + False + The shortcut keys for showing emoji dialog + start +- Emoji dialog: ++ Emoji choice: + + + 5 +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? +@@ -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 = "%s". ++ printf(emoji_font, candidate.get_text()); ++ label.set_markup(markup); + } +- string markup = "%s". +- 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 +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 ++# Copyright (c) 2016-2017 Takao Fujiwara + # 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 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ :') | :'-) ++ :D | :-D | =D ++ ':) | ':-) | '=) | ':D | ':-D | '=D ++ >:) | >;) | >:-) | >=) ++ ;) | ;-) | *-) | *) | ;-] | ;] | ;D | ;^) ++ B-) | B) | 8) | 8-) | B-D | 8-D ++ :* | :-* | =* | :^* ++ :) | :-) | =] | =) | :] ++ -_- | -__- | -___- ++ :-X | :X | :-# | :# | =X | =x | :x | :-x | =# ++ >.< ++ :-O | :O | :-o | :o | O_O | >:O ++ :P | :-P | =P | :-p | :p | =p | :-Þ | :Þ | :þ | :-þ | :-b | :b | d: ++ >:P | X-P | x-p ++ ':( | ':-( | '=( ++ >:\ | >:/ | :-/ | :-. | :/ | :\ | =/ | =\ | :L | =L ++ >:[ | :-( | :( | :-[ | :[ | =( ++ :'( | :'-( | ;( | ;-( ++ D: ++ :$ | =$ ++ #-) | #) | %-) | %) | X) | X-) ++ >:( | >:-( | :@ ++ O:-) | 0:-3 | 0:3 | 0:-) | 0:) | 0;^) | O:) | O;-) | O=) | 0;-) | O:-3 | O:3 ++ *\0/* | \0/ | *\O/* | \O/ ++ <3 ++ </3 ++ ++ +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 ++ ++#ifdef HAVE_JSON_GLIB1 + #include ++#endif + + #include + + #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 (" ", 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\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 \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 ++ * 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 ++ ++#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 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 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 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 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 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 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 to strcmp? +- if (words.length < 3 && +- annotations.find_custom( +- trans_description, +- GLib.strcmp) == null) { +- annotations.append(trans_description); +- } + unowned GLib.SList 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 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 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 = "%s". +- 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 +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 @@ + + + ++ /schemas/desktop/ibus/panel/emoji/has-partial-match ++ /desktop/ibus/panel/emoji/has-partial-match ++ ibus ++ bool ++ false ++ ++ Whether emoji annotaions can be match partially or not ++ Whether emoji annotations can be matched with a partial ++ string instead of the exact match or not. ++ ++ ++ ++ /schemas/desktop/ibus/panel/emoji/partial-match-length ++ /desktop/ibus/panel/emoji/partial-match-length ++ ibus ++ int ++ 3 ++ ++ Match emoji annotaions with the specified length ++ Match emoji annotations partially with more than ++ the specified number of characters instead of ++ the exact match. ++ ++ ++ ++ /schemas/desktop/ibus/panel/emoji/partial-match-condition ++ /desktop/ibus/panel/emoji/partial-match-condition ++ ibus ++ int ++ 0 ++ ++ Choose a condition to match emoji annotations partially ++ Choose one of the following conditions to match emoji ++ annotations partially: ++ 0 == Prefix match, 1 == Suffix match, 2 == Containing match ++ ++ ++ ++ + /schemas/desktop/ibus/general/embed_preedit_text + /desktop/ibus/general/embed_preedit_text + ibus +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 @@ + + + ++ ++ 3.0 ++ 1.0 ++ 255.0 ++ 1.0 ++ 10.0 ++ 10.0 ++ + + False + 12 +@@ -109,8 +117,6 @@ + Next input method: + + +- 3 +- 4 + GTK_FILL + GTK_FILL + +@@ -125,69 +131,13 @@ + Previous input method: + + +- 4 +- 5 +- GTK_FILL +- GTK_FILL +- +- +- +- +- True +- False +- The shortcut keys for showing emoji dialog +- start +- Emoji choice: +- +- +- 5 +- 6 ++ 1 ++ 2 + GTK_FILL + GTK_FILL + + + +- +- horizontal +- True +- False +- 6 +- +- +- True +- True +- False +- +- +- True +- True +- 0 +- +- +- +- +- ... +- False +- True +- True +- False +- False +- True +- +- +- False +- True +- 1 +- +- +- +- +- 1 +- 2 +- GTK_FILL +- +- +- + + horizontal + True +@@ -225,8 +175,6 @@ + + 1 + 2 +- 3 +- 4 + GTK_FILL + + +@@ -270,51 +218,8 @@ + + 1 + 2 +- 4 +- 5 +- GTK_FILL +- +- +- +- +- horizontal +- True +- False +- 6 +- +- +- True +- True +- False +- +- +- True +- True +- 0 +- +- +- +- +- ... +- False +- True +- True +- False +- False +- True +- +- +- False +- True +- 1 +- +- +- +- +- 1 +- 2 +- 5 +- 6 ++ 1 ++ 2 + GTK_FILL + + +@@ -327,6 +232,8 @@ + Enable or disable: + + ++ 2 ++ 3 + GTK_FILL + GTK_FILL + +@@ -339,8 +246,8 @@ + Enable: + + +- 1 +- 2 ++ 3 ++ 4 + GTK_FILL + GTK_FILL + +@@ -383,8 +290,8 @@ + + 1 + 2 +- 1 +- 2 ++ 3 ++ 4 + GTK_FILL + + +@@ -396,8 +303,8 @@ + Disable: + + +- 2 +- 3 ++ 4 ++ 5 + GTK_FILL + GTK_FILL + +@@ -440,8 +347,8 @@ + + 1 + 2 +- 2 +- 3 ++ 4 ++ 5 + GTK_FILL + + +@@ -675,68 +582,6 @@ + GTK_FILL + + +- +- +- True +- False +- Set a font of emoji candidates on the emoji dialog +- start +- Emoji font: +- right +- +- +- 7 +- 8 +- GTK_FILL +- GTK_FILL +- +- +- +- +- True +- True +- True +- False +- +- +- 1 +- 2 +- 7 +- 8 +- GTK_FILL +- +- +- +- +- True +- False +- Set a language of emoji annotations on the emoji dialog +- start +- Emoji annotation language: +- right +- +- +- 8 +- 9 +- GTK_FILL +- GTK_FILL +- +- +- +- +- True +- True +- True +- False +- +- +- 1 +- 2 +- 8 +- 9 +- GTK_FILL +- +- + + + +@@ -1027,6 +872,292 @@ + + + ++ ++ vertical ++ True ++ False ++ 18 ++ 12 ++ 12 ++ 12 ++ 12 ++ ++ ++ True ++ False ++ 0 ++ none ++ ++ ++ True ++ False ++ 5 ++ 2 ++ 12 ++ 6 ++ 6 ++ 12 ++ ++ ++ True ++ False ++ The shortcut keys for showing emoji dialog ++ start ++ Emoji choice: ++ ++ ++ GTK_FILL ++ GTK_FILL ++ ++ ++ ++ ++ horizontal ++ True ++ False ++ 6 ++ ++ ++ True ++ True ++ False ++ ++ ++ True ++ True ++ 0 ++ ++ ++ ++ ++ ... ++ False ++ True ++ True ++ False ++ False ++ True ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ 1 ++ 2 ++ GTK_FILL ++ ++ ++ ++ ++ ++ ++ True ++ False ++ <b>Keyboard Shortcuts</b> ++ True ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 0 ++ none ++ ++ ++ True ++ False ++ 12 ++ 6 ++ 6 ++ 12 ++ false ++ ++ ++ True ++ False ++ Set a font of emoji candidates on the emoji dialog ++ start ++ Emoji font: ++ right ++ ++ ++ 0 ++ ++ ++ ++ ++ True ++ True ++ True ++ False ++ ++ ++ 1 ++ ++ ++ ++ ++ ++ True ++ False ++ Set a language of emoji annotations on the emoji dialog ++ start ++ Emoji annotation language: ++ right ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ True ++ True ++ False ++ ++ ++ 1 ++ 1 ++ ++ ++ ++ ++ Match emoji annotations partially with the following condition and more than the number of characters: ++ False ++ True ++ True ++ False ++ If emoji annotations can be matched with a partial string instead of the exact match ++ False ++ start ++ True ++ ++ ++ 0 ++ 2 ++ 2 ++ ++ ++ ++ ++ adjustment_emoji_partial_match ++ 3.0 ++ True ++ True ++ False ++ start ++ ++ ++ 0 ++ 2 ++ 3 ++ ++ ++ ++ ++ horizontal ++ True ++ False ++ 6 ++ ++ ++ Prefix match ++ True ++ True ++ False ++ start ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ Suffix match ++ radiobutton_emoji_prefix_match ++ True ++ True ++ False ++ start ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ Containing match ++ radiobutton_emoji_prefix_match ++ True ++ True ++ False ++ start ++ ++ ++ False ++ True ++ 2 ++ ++ ++ ++ ++ 0 ++ 2 ++ 4 ++ ++ ++ ++ ++ ++ ++ True ++ False ++ <b>Font and Style</b> ++ True ++ ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ 2 ++ ++ ++ ++ ++ True ++ False ++ Emoji ++ ++ ++ 2 ++ False ++ ++ ++ + + vertical + True +@@ -1134,7 +1265,7 @@ + + + +- 2 ++ 3 + + + +@@ -1214,7 +1345,7 @@ Homepage: https://github.com/ibus/ibus/wiki + + + +- 3 ++ 4 + + + +@@ -1224,7 +1355,7 @@ Homepage: https://github.com/ibus/ibus/wiki + About + + +- 3 ++ 4 + False + + +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? + m_annotation_to_emojis_dict; + private static GLib.HashTable? +@@ -738,16 +741,53 @@ class IBusEmojier : Gtk.Window { + } + // Call check_unicode_point() to get m_unicode_point + check_unicode_point(); +- unowned GLib.SList? emojis = +- m_annotation_to_emojis_dict.lookup(annotation); +- if (emojis == null && m_unicode_point == null) { ++ GLib.SList? total_emojis = null; ++ unowned GLib.SList? 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 +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? ++ private static bool m_show_emoji_variant = false; ++ private static GLib.HashTable>? + m_annotation_to_emojis_dict; + private static GLib.HashTable? + m_emoji_to_data_dict; +- private static GLib.HashTable? ++ private static GLib.HashTable>? + m_category_to_emojis_dict; ++ private static GLib.HashTable>? ++ 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(GLib.str_hash, +- GLib.str_equal); ++ new GLib.HashTable>(GLib.str_hash, ++ GLib.str_equal); + m_emoji_to_data_dict = + new GLib.HashTable(GLib.str_hash, + GLib.str_equal); + m_category_to_emojis_dict = +- new GLib.HashTable(GLib.str_hash, +- GLib.str_equal); ++ new GLib.HashTable>(GLib.str_hash, ++ GLib.str_equal); ++ m_emoji_to_emoji_variants_dict = ++ new GLib.HashTable>(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? 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 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 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? 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 = "%s". +- 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? 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? 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 +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 , 2008. +-.\" Copyright (C) Takao Fujiwara , 2013-2015. ++.\" Copyright (C) Takao Fujiwara , 2013-2017. + .\" Copyright (c) Peng Huang , 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 , 2013. ++.\" Copyright (C) Takao Fujiwara , 2013-2017. + .\" Copyright (c) Peng Huang , 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 , 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 +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 @@ + + + ++ /schemas/desktop/ibus/panel/emoji/favorite-annotations ++ /desktop/ibus/panel/emoji/favorite-annotations ++ ibus ++ list ++ [] ++ string ++ ++ favorite emoji annotation list on emoji dialog ++ You can assign an annotation for a favorite emoji ++ in this list. ++ ++ ++ + /schemas/desktop/ibus/panel/emoji/has-partial-match + /desktop/ibus/panel/emoji/has-partial-match + ibus +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 new_annotations = new GLib.SList(); ++ 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 annotations = data.get_annotations(); ++ if (annotations.find_custom(annotation, GLib.strcmp) == null) { ++ annotations.append(annotation); ++ data.set_annotations(annotations.copy()); ++ } ++ } ++ unowned GLib.SList 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 +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 - 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 - 1.5.15-7 - Supported ibus emoji command on Wayland - Changed modal dialog to modeless dialog