├── src ├── main │ └── java │ │ ├── META-INF │ │ └── MANIFEST.MF │ │ └── com │ │ └── vdurmont │ │ └── emoji │ │ ├── Gender.java │ │ ├── Fitzpatrick.java │ │ ├── EmojiLoader.java │ │ ├── EmojiTrie.java │ │ ├── EmojiManager.java │ │ ├── Emoji.java │ │ └── EmojiParser.java └── test │ └── java │ └── com │ └── vdurmont │ └── emoji │ ├── TestTools.java │ ├── EmojiJsonTest.java │ ├── EmojiLoaderTest.java │ ├── EmojiManagerTest.java │ └── EmojiParserTest.java ├── .gitignore ├── .travis.yml ├── emoji-table-generator ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── vdurmont │ └── emoji │ └── TableGenerator.java ├── LICENSE.md ├── CHANGELOG.md ├── charConverter.js ├── pom.xml ├── README.md └── EMOJIS.md /src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: json-20170516.jar 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Java 2 | target 3 | out 4 | /src/main/java/META-INF/ 5 | 6 | # IntelliJ files 7 | *.iml 8 | .idea 9 | .gitignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: java 3 | dist: precise 4 | jdk: 5 | - openjdk6 6 | - oraclejdk7 7 | - openjdk7 8 | - oraclejdk8 9 | after_success: 10 | - mvn clean cobertura:cobertura coveralls:cobertura -------------------------------------------------------------------------------- /emoji-table-generator/README.md: -------------------------------------------------------------------------------- 1 | # emoji-table-genrator 2 | 3 | This is just a "quick'n'dirty" project to generate a markdown table with all the emojis. 4 | 5 | It is used for the table in the top level README :) 6 | 7 | 8 | Run with: 9 | 10 | ``` 11 | mvn exec:java -Dexec.mainClass="com.vdurmont.emoji.TableGenerator" 12 | ``` -------------------------------------------------------------------------------- /src/test/java/com/vdurmont/emoji/TestTools.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | public class TestTools { 4 | public static boolean containsEmojis( 5 | Iterable emojis, 6 | String... aliases 7 | ) { 8 | for (String alias : aliases) { 9 | boolean contains = containsEmoji(emojis, alias); 10 | if (!contains) { 11 | return false; 12 | } 13 | } 14 | return true; 15 | } 16 | 17 | public static boolean containsEmoji(Iterable emojis, String alias) { 18 | for (Emoji emoji : emojis) { 19 | for (String al : emoji.getAliases()) { 20 | if (alias.equals(al)) { 21 | return true; 22 | } 23 | } 24 | } 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /emoji-table-generator/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.vdurmont 6 | emoji-table-generator 7 | 1.0.0-SNAPSHOT 8 | jar 9 | 10 | emoji-table-generator 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | com.vdurmont 20 | emoji-java 21 | 5.1.1 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/Gender.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | /** 4 | * Создано 07.07.2020 5 | * @author Improver: Ivan Ivanov [https://vk.com/irisism] 6 | */ 7 | public enum Gender { 8 | MALE() , FEMALE(), PERSON(); 9 | 10 | public static Gender find(char[] chars, int startPos) { 11 | if (startPos >= chars.length) 12 | return null; 13 | var ch = chars[startPos]; 14 | return switch (ch) { 15 | case '♂' -> MALE; 16 | case '♀' -> FEMALE; 17 | default -> null; 18 | }; 19 | } 20 | 21 | public static Gender find2(String emoji) { 22 | return find2(emoji.toCharArray(), 0); 23 | } 24 | 25 | public static Gender find2(char[] chars, int startPos) { 26 | if (startPos + 2 > chars.length) 27 | return null; 28 | var ch = chars[startPos]; 29 | return switch (ch) { 30 | case '\uD83D' -> switch (chars[startPos + 1]) { 31 | case '\uDC68' -> MALE; 32 | case '\uDC69' -> FEMALE; 33 | default -> null; 34 | }; 35 | case '\uD83E' -> switch (chars[startPos + 1]) { 36 | case '\uDDD1' -> PERSON; 37 | default -> null; 38 | }; 39 | 40 | default -> null; 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-present Vincent DURMONT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /emoji-table-generator/src/main/java/com/vdurmont/emoji/TableGenerator.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | 6 | /** 7 | * This app generate the emoji table in the README ;) 8 | *

9 | * Run with: 10 | * mvn exec:java -Dexec.mainClass="com.vdurmont.emoji.TableGenerator" 11 | */ 12 | public class TableGenerator { 13 | public static void main(String[] args) throws IOException { 14 | StringBuilder sb = new StringBuilder(); 15 | 16 | // Table header 17 | sb.append("| Emoji | Aliases | Emoji | Aliases |\n"); 18 | sb.append("| :---: | ------- | :---: | ------- |\n"); 19 | 20 | // Emojis! 21 | int i = 0; 22 | for (Emoji emoji : EmojiManager.getAll()) { 23 | String aliases = getAliases(emoji); 24 | 25 | if (i % 2 == 0) { 26 | sb.append("| ") 27 | .append(emoji.getUnicode()) 28 | .append(" | ") 29 | .append(aliases) 30 | .append(" |"); 31 | } else { 32 | sb.append(" ") 33 | .append(emoji.getUnicode()) 34 | .append(" | ") 35 | .append(aliases) 36 | .append(" |\n"); 37 | } 38 | 39 | i++; 40 | } 41 | 42 | // Output! 43 | if (args.length > 0) { 44 | String path = args[0]; 45 | FileWriter writer = new FileWriter(path); 46 | writer.write(sb.toString()); 47 | System.out.println("Written on " + path); 48 | } else { 49 | System.out.println(sb.toString()); 50 | } 51 | } 52 | 53 | private static String getAliases(Emoji emoji) { 54 | StringBuilder result = new StringBuilder(); 55 | boolean first = true; 56 | for (String alias : emoji.getAliases()) { 57 | if (first) { 58 | first = false; 59 | } else { 60 | result.append(", "); 61 | } 62 | result.append(alias); 63 | } 64 | 65 | return result.toString(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/Fitzpatrick.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | /** 4 | * Enum that represents the Fitzpatrick modifiers supported by the emojis. 5 | * @author Improver: Ivan Ivanov [https://vk.com/irisism]
6 | * Creator: Vincent DURMONT [vdurmont@gmail.com] 7 | */ 8 | public enum Fitzpatrick { 9 | /** 10 | * Fitzpatrick modifier of type 1/2 (pale white/white) 11 | */ 12 | TYPE_1_2("\uD83C\uDFFB"), 13 | 14 | /** 15 | * Fitzpatrick modifier of type 3 (cream white) 16 | */ 17 | TYPE_3("\uD83C\uDFFC"), 18 | 19 | /** 20 | * Fitzpatrick modifier of type 4 (moderate brown) 21 | */ 22 | TYPE_4("\uD83C\uDFFD"), 23 | 24 | /** 25 | * Fitzpatrick modifier of type 5 (dark brown) 26 | */ 27 | TYPE_5("\uD83C\uDFFE"), 28 | 29 | /** 30 | * Fitzpatrick modifier of type 6 (black) 31 | */ 32 | TYPE_6("\uD83C\uDFFF"); 33 | 34 | /** 35 | * The unicode representation of the Fitzpatrick modifier 36 | */ 37 | public final String unicode; 38 | 39 | Fitzpatrick(String unicode) { 40 | this.unicode = unicode; 41 | } 42 | 43 | 44 | public static Fitzpatrick fitzpatrickFromUnicode(String unicode) { 45 | for (Fitzpatrick v : values()) { 46 | if (v.unicode.equals(unicode)) { 47 | return v; 48 | } 49 | } 50 | return null; 51 | } 52 | 53 | public static Fitzpatrick fitzpatrickFromType(String type) { 54 | try { 55 | return Fitzpatrick.valueOf(type.toUpperCase()); 56 | } catch (IllegalArgumentException e) { 57 | return null; 58 | } 59 | } 60 | 61 | public static Fitzpatrick find(char[] chars, int start) { 62 | if (chars.length < start + 1) 63 | return null; 64 | var ch = chars[start]; 65 | if (ch != '\uD83C') 66 | return null; 67 | ch = chars[start + 1]; 68 | switch (ch) { 69 | case '\uDFFB' : return TYPE_1_2; 70 | case '\uDFFC' : return TYPE_3; 71 | case '\uDFFD' : return TYPE_4; 72 | case '\uDFFE' : return TYPE_5; 73 | case '\uDFFF' : return TYPE_6; 74 | } 75 | return null; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/EmojiLoader.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Loads the emojis from a JSON database. 16 | * 17 | * @author Improver: Ivan Ivanov [https://vk.com/irisism]
18 | * Creator: Vincent DURMONT [vdurmont@gmail.com] 19 | */ 20 | public class EmojiLoader { 21 | /** 22 | * No need for a constructor, all the methods are static. 23 | */ 24 | private EmojiLoader() {} 25 | 26 | /** 27 | * Loads a JSONArray of emojis from an InputStream, parses it and returns the 28 | * associated list of {@link com.vdurmont.emoji.Emoji}s 29 | * 30 | * @param stream the stream of the JSONArray 31 | * 32 | * @return the list of {@link com.vdurmont.emoji.Emoji}s 33 | * @throws IOException if an error occurs while reading the stream or parsing 34 | * the JSONArray 35 | */ 36 | public static List loadEmojis(InputStream stream) throws IOException { 37 | JSONArray emojisJSON = new JSONArray(inputStreamToString(stream)); 38 | List emojis = new ArrayList<>(emojisJSON.length()); 39 | for (int i = 0; i < emojisJSON.length(); i++) { 40 | Emoji emoji = buildEmojiFromJSON(emojisJSON.getJSONObject(i)); 41 | if (emoji != null) { 42 | emojis.add(emoji); 43 | } 44 | } 45 | return emojis; 46 | } 47 | 48 | private static String inputStreamToString( 49 | InputStream stream 50 | ) throws IOException { 51 | StringBuilder sb = new StringBuilder(); 52 | InputStreamReader isr = new InputStreamReader(stream, StandardCharsets.UTF_8); 53 | BufferedReader br = new BufferedReader(isr); 54 | String read; 55 | while ((read = br.readLine()) != null) { 56 | sb.append(read); 57 | } 58 | br.close(); 59 | return sb.toString(); 60 | } 61 | 62 | protected static Emoji buildEmojiFromJSON(JSONObject json) { 63 | if (!json.has("emoji")) { 64 | return null; 65 | } 66 | var unicode = json.getString("emoji"); 67 | 68 | // Lifehach to filter out old emojis map from wrong gender_base records 69 | if (unicode.startsWith("\uD83D\uDC69\u200D") || unicode.startsWith("\uD83D\uDC68\u200D")) 70 | return null; 71 | var emojiChar = json.getString("emojiChar"); 72 | //byte[] bytes = unicode.getBytes(StandardCharsets.UTF_8); 73 | 74 | String description = null; 75 | if (json.has("description")) { 76 | description = json.getString("description"); 77 | } 78 | int sequenceType = 0; 79 | if (json.has("sequence_type")) { 80 | sequenceType = json.getInt("sequence_type"); 81 | } 82 | List aliases = jsonArrayToStringList(json.getJSONArray("aliases")); 83 | List tags = jsonArrayToStringList(json.getJSONArray("tags")); 84 | return new Emoji(description, sequenceType, aliases, tags, unicode, !unicode.equals(emojiChar)? emojiChar : unicode); 85 | } 86 | 87 | private static List jsonArrayToStringList(JSONArray array) { 88 | List strings = new ArrayList(array.length()); 89 | for (int i = 0; i < array.length(); i++) { 90 | strings.add(array.getString(i)); 91 | } 92 | return strings; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/EmojiTrie.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import java.util.Collection; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * 9 | * @author Improver: Ivan Ivanov [https://vk.com/irisism]
10 | * Creator: Vincent DURMONT [vdurmont@gmail.com] 11 | */ 12 | public class EmojiTrie { 13 | 14 | private final Node root = new Node(); 15 | final int maxDepth; 16 | 17 | public EmojiTrie(Collection emojis) { 18 | int maxDepth = 0; 19 | for (Emoji emoji : emojis) { 20 | Node tree = root; 21 | char[] chars = emoji.getUnicode().toCharArray(); 22 | maxDepth = Math.max(maxDepth, chars.length); 23 | for (char c : chars) { 24 | if (!tree.hasChild(c)) { 25 | tree.addChild(c); 26 | } 27 | tree = tree.getChild(c); 28 | } 29 | tree.setEmoji(emoji); 30 | } 31 | this.maxDepth = maxDepth; 32 | } 33 | 34 | 35 | /** 36 | * Checks if sequence of chars contain an emoji. 37 | * 38 | * @param sequence Sequence of char that may contain emoji in full or 39 | * partially. 40 | * @return <li> 41 | * Matches.EXACTLY if char sequence in its entirety is an emoji 42 | * </li> 43 | * <li> 44 | * Matches.POSSIBLY if char sequence matches prefix of an emoji 45 | * </li> 46 | * <li> 47 | * Matches.IMPOSSIBLE if char sequence matches no emoji or prefix of an 48 | * emoji 49 | * </li> 50 | */ 51 | public Matches isEmoji(char[] sequence) { 52 | return isEmoji(sequence, 0, sequence.length); 53 | } 54 | 55 | /** 56 | * Checks if the sequence of chars within the given bound indices contain an emoji. 57 | * 58 | * @see #isEmoji(char[]) 59 | */ 60 | public Matches isEmoji(char[] sequence, int start, int end) { 61 | if (start < 0 || start > end || end > sequence.length) { 62 | throw new ArrayIndexOutOfBoundsException("start " + start + ", end " + end + ", length " + sequence.length);} 63 | 64 | Node tree = root; 65 | for (int i = start; i < end; i++) { 66 | if (!tree.hasChild(sequence[i])) { 67 | return Matches.IMPOSSIBLE; 68 | } 69 | tree = tree.getChild(sequence[i]); 70 | } 71 | 72 | return tree.isEndOfEmoji() ? Matches.EXACTLY : Matches.POSSIBLY; 73 | } 74 | 75 | public Emoji getBestEmoji(char[] sequence, int start) { 76 | if (start < 0) { 77 | throw new ArrayIndexOutOfBoundsException("start " + start + ", length " + sequence.length);} 78 | var end = sequence.length; 79 | Node tree = root; 80 | for (int i = start; i < end; i++) { 81 | if (!tree.hasChild(sequence[i])) { 82 | return tree.isEndOfEmoji()? tree.emoji : null; 83 | } 84 | tree = tree.getChild(sequence[i]); 85 | } 86 | 87 | return tree.isEndOfEmoji() ? tree.emoji : null; 88 | } 89 | 90 | /** 91 | * Finds Emoji instance from emoji unicode 92 | * 93 | * @param unicode unicode of emoji to get 94 | * @return Emoji instance if unicode matches and emoji, null otherwise. 95 | */ 96 | public Emoji getEmoji(String unicode) { 97 | return getEmoji(unicode.toCharArray(), 0, unicode.length()); 98 | } 99 | 100 | Emoji getEmoji(char[] sequence, int start, int end) { 101 | if (start < 0 || start > end || end > sequence.length) { 102 | throw new ArrayIndexOutOfBoundsException( 103 | "start " + start + ", end " + end + ", length " + sequence.length); 104 | } 105 | 106 | Node tree = root; 107 | for (int i = 0; i < end; i++) { 108 | if (!tree.hasChild(sequence[i])) { 109 | return null; 110 | } 111 | tree = tree.getChild(sequence[i]); 112 | } 113 | return tree.getEmoji(); 114 | } 115 | 116 | public enum Matches { 117 | EXACTLY, POSSIBLY, IMPOSSIBLE; 118 | 119 | public boolean exactMatch() { 120 | return this == EXACTLY; 121 | } 122 | 123 | public boolean impossibleMatch() { 124 | return this == IMPOSSIBLE; 125 | } 126 | } 127 | 128 | private static class Node { 129 | private final Map children = new HashMap<>(); 130 | private Emoji emoji; 131 | 132 | private void setEmoji(Emoji emoji) { 133 | this.emoji = emoji; 134 | } 135 | 136 | private Emoji getEmoji() { 137 | return emoji; 138 | } 139 | 140 | private boolean hasChild(char child) { 141 | return children.containsKey(child); 142 | } 143 | 144 | private void addChild(char child) { 145 | children.put(child, new Node()); 146 | } 147 | 148 | private Node getChild(char child) { 149 | return children.get(child); 150 | } 151 | 152 | private boolean isEndOfEmoji() { 153 | return emoji != null; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/test/java/com/vdurmont/emoji/EmojiJsonTest.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.junit.runners.Parameterized; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.util.Arrays; 13 | import java.util.Collection; 14 | import java.util.LinkedList; 15 | import java.util.List; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | /** 21 | * Test that checks emoji json. 22 | *

23 | * Currently contains checks for: 24 | *

    25 | *
  • Unicode emoji presents in json
  • 26 | *
  • Right fitzpatric flag for emoji
  • 27 | *
28 | * 29 | *

30 | * The test data is taken from: Unicode test data 31 | * related to unicode 9.0 32 | */ 33 | @RunWith(Parameterized.class) 34 | public class EmojiJsonTest { 35 | @Parameterized.Parameters 36 | public static Collection emojis() throws IOException { 37 | final InputStream is = EmojiJsonTest.class.getClassLoader().getResourceAsStream("emoji-test.txt"); 38 | return EmojiTestDataReader.getEmojiList(is); 39 | } 40 | 41 | private static final int[] FITZPATRIC_CODEPOINTS = new int[]{ 42 | EmojiTestDataReader.convertFromCodepoint("1F3FB"), 43 | EmojiTestDataReader.convertFromCodepoint("1F3FC"), 44 | EmojiTestDataReader.convertFromCodepoint("1F3FD"), 45 | EmojiTestDataReader.convertFromCodepoint("1F3FE"), 46 | EmojiTestDataReader.convertFromCodepoint("1F3FF") 47 | }; 48 | 49 | @Parameterized.Parameter 50 | public String emoji; 51 | 52 | @Ignore("1665 emoji still has not been added") 53 | @Test 54 | public void checkEmojiExisting() { 55 | assertTrue("Asserting for emoji: " + emoji, EmojiManager.isEmoji(emoji)); 56 | } 57 | 58 | @Test 59 | public void checkEmojiFitzpatricFlag() { 60 | final int len = emoji.toCharArray().length; 61 | boolean shouldContainFitzpatric = false; 62 | int codepoint; 63 | for (int i = 0; i < len; i++) { 64 | codepoint = emoji.codePointAt(i); 65 | shouldContainFitzpatric = Arrays.binarySearch(FITZPATRIC_CODEPOINTS, codepoint) >= 0; 66 | if (shouldContainFitzpatric) { 67 | break; 68 | } 69 | } 70 | 71 | if (shouldContainFitzpatric) { 72 | EmojiParser.parseFromUnicode(emoji, new EmojiParser.EmojiTransformer() { 73 | public String transform(EmojiParser.EmojiResult unicodeCandidate) { 74 | if (unicodeCandidate.hasFitzpatrick()) { 75 | assertTrue("Asserting emoji contains fitzpatric: " + emoji + " " + unicodeCandidate.getEmoji(), 76 | unicodeCandidate.getEmoji().supportsFitzpatrick()); 77 | } 78 | return ""; 79 | } 80 | }); 81 | } 82 | } 83 | 84 | private static class EmojiTestDataReader { 85 | static List getEmojiList(final InputStream emojiFileStream) throws IOException { 86 | final BufferedReader reader = new BufferedReader(new InputStreamReader(emojiFileStream)); 87 | final List result = new LinkedList(); 88 | 89 | String line = reader.readLine(); 90 | String [] lineSplit; 91 | while (line != null) { 92 | if (!line.startsWith("#") && !line.startsWith(" ") && !line.startsWith("\n") && 93 | line.length() != 0) { 94 | lineSplit = line.split(";"); 95 | result.add(convertToEmoji(lineSplit[0].trim())); 96 | } 97 | line = reader.readLine(); 98 | } 99 | return result; 100 | } 101 | 102 | private static String convertToEmoji(final String input) { 103 | String[] emojiCodepoints = input.split(" "); 104 | StringBuilder sb = new StringBuilder(); 105 | for (String emojiCodepoint : emojiCodepoints) { 106 | int codePoint = convertFromCodepoint(emojiCodepoint); 107 | sb.append(Character.toChars(codePoint)); 108 | } 109 | return sb.toString(); 110 | } 111 | 112 | static int convertFromCodepoint(String emojiCodepointAsString) { 113 | return Integer.parseInt(emojiCodepointAsString, 16); 114 | } 115 | 116 | } 117 | 118 | @Test 119 | public void checkInverseParse() { 120 | assertEquals(emoji, EmojiParser.parseToUnicode(EmojiParser.parseToHtmlDecimal(emoji, EmojiParser.FitzpatrickAction.IGNORE))); 121 | 122 | assertEquals(emoji, EmojiParser.parseToUnicode(EmojiParser.parseToHtmlHexadecimal(emoji, EmojiParser.FitzpatrickAction.IGNORE))); 123 | 124 | assertEquals(emoji, EmojiParser.parseToUnicode(EmojiParser.parseToAliases(emoji, EmojiParser.FitzpatrickAction.IGNORE))); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v6.1i 4 | - Added support of complex emoji sequences of type **`{gender}{skin_kolor}?{basic_emoji}`** 5 | - Due to it `emojis.json` boolean parameter `supports_fitzpatrick` was changed to `sequence_type`.
6 | 1 — for supporting `{basic_emoji}{skin_color}?{gender}?`
7 | 2 — for supporting `{gender}{skin_kolor}?{basic_emoji}` (but there are only 3 emojis of such type: person, man and woman) 8 | - Emojis list was updated to Emoji Version 13.0. 9 |
**+150 emojis**, not counting combinations of skin color, hair color or gender 10 | 11 | ## v6.0i 12 | 13 | - Ivan Ivanov forked main repository 14 | - Added class `Gender` 15 | - Added support of complex emoji sequences of type `{basic_emoji}{skin_color}?{gender}?` 16 | - Added support of bugged VK.COM web-version emojis, where ending char `\uFE0F` is absent 17 | - `EmojiParser.UnicodeCandidate` renamed to `EmojiParser.EmojiResult`. And added new fields 18 | - `EmojiParser` algorithm improvement. `getEmojiEndPos` is replaced with `getNextEmoji`
19 | - `EmojiTrie.getBestEmoji` introduced to replace slow `EmojiTrie.getEmojiEndPos` method 20 | - Memory usage fixes by optimizing generation of new String objects. 21 | - As a result speed improvement reaches up to 5x times comparing to v.5.1.1 22 | 23 | ## v5.1.1 24 | 25 | - Bugfix: respect fitzpatrick modifier when extracting emojis (thanks @sullis) 26 | 27 | ## v5.1.0 28 | 29 | - Many performance improvements to the parsing of the emojis (thanks @freva) 30 | - Add a `containsEmoji` function (thanks @freva!) 31 | 32 | ## v5.0.0 33 | 34 | - Fix the HTML to Unicode parser to always parse the longer emojis (thanks @freva) 35 | - Add alias for "pumpkin" (thanks @sullis) 36 | - Add a lot of missing flag emojis (thanks @ankitkariryaa) 37 | - Support for all emojis from Unicode 11.0 38 | - Support for all emojis from Unicode 10.0 39 | - Add a `EmojiParser.replaceAllEmojis` function (thanks @cbedoy) 40 | 41 | ## v4.0.0 42 | 43 | - Add "source code" strings to emoji json database 44 | - Fix some missing/out-of-order code points (thanks @BillyGalbreath) 45 | - Upgrade `org.json:json` dependency (thanks @PhotonQyv) 46 | - Update README with new emojis (thanks @jtobard) 47 | 48 | ## v3.3.0 49 | 50 | - Add `family_man_woman_girl_boy` emoji (thanks @freva) 51 | - Fix `EmojiManager.isEmoji` to support fitzpatrick modifiers (thanks @freva) 52 | - Fixed several emojis that had the wrong `support_fitzpatrick` flag (thanks @Tapchicoma) 53 | - Add some tests to avoid duplicate aliases 54 | - Fixed several duplicated aliases in the emoji DB 55 | 56 | ## v3.2.0 57 | 58 | - Fixed Poland flag (thanks @Sheigutn) 59 | - Improvements to the smile emojis (thanks @miquelbeltran) 60 | - Add a bunch of emojis from Apple iOS 10.2 release 61 | - Fix some missing fitzpatrick modifiers 62 | - Add an `EmojiManager.isOnlyEmojis()` method 63 | 64 | ## v3.1.3 65 | 66 | - Removed all variance selectors from the JSON database. Thanks @roberterdin ! 67 | 68 | ## v3.1.2 69 | 70 | - Additions and updates to the emoji database (victory hand now supports fitzpatrick, adds Saint Vincent Grenadines' flag, add the regional indicator symbols). Thanks @lologist ! 71 | - Force the database to be loaded in UTF-8. 72 | - Enable the extension of the `EmojiParser` class. 73 | 74 | ## v3.1.1 75 | 76 | - Add the ability to provide a custom `EmojiTransformer` that will enable developers to add their custom emoji replacement methods. Thanks @freva ! 77 | 78 | ## v3.1.0 79 | 80 | - Add fitzpatrick support for 👃 ("nose") and 👂 ("ear") 81 | - Fix duplicated "sunglasses" alias 82 | - Performance improvements (using a Trie structure) 83 | - Parsing support for multiple emojis (such as "family_man_woman_boy") 84 | - Fix `EmojiManager.getAll()` that returned some duplicates 85 | - Use a BufferedReader to load the database 86 | 87 | ## v3.0.0 88 | 89 | Update the emoji database to support the additions of iOS 9.1 90 | 91 | ## v2.2.1 92 | 93 | Fix the `htmlDec` and `htmlHex` codes for the multiple emojis (such as `family (man, man, girl, boy)`) 94 | 95 | ## v2.2.0 96 | 97 | Rollback dependency org.json:json to 20140107 to keep the compatibility with Java 6 & 7 98 | 99 | ## v2.1.0 100 | 101 | - Add methods: 102 | - `EmojiParser#removeAllEmojis(String)` 103 | - `EmojiParser#removeAllEmojisExcept(String, Collection)` 104 | - `EmojiParser#removeEmojis(String, Collection)` 105 | - Upgrade dependency org.json:json 106 | 107 | ## v2.0.1 108 | 109 | Bug fix on the :-1: emoji 110 | 111 | ## v2.0.0 112 | 113 | - Update of the emoji database 114 | - Add 14 new family emojis (man_man_boy, woman_woman_girl, etc.) 115 | - Add 4 new couple emojis 116 | - Add the "vulcan_salute" and "middle_finger" emojis 117 | - Add 198 flags 118 | - Addition of the support for the diversity emojis (Fitzpatrick modifiers) 119 | - Removal of the deprecated methods `Emoji#getHtml` and `EmojiParser#parseToHtml` 120 | - Improvements in the javadoc 121 | 122 | ## v1.1.1 123 | 124 | Closing the stream used to read the emoji database in `EmojiManager.java` 125 | 126 | ## v1.1.0 127 | 128 | - Update of the emoji database 129 | - Adding support for HTML hexadecimal: 130 | - `Emoji#getHtmlHexadecimal` 131 | - `EmojiParser#parseToHtmlHexadecimal` 132 | - The old HTML support is now HTML decimal: 133 | - Deprecating `Emoji#getHtml` (replaced by `Emoji#getHtmlDecimal`) 134 | - Deprecating `EmojiParser#parseToHtml` (replaced by `EmojiParser#parseToHtmlDecimal`) 135 | 136 | ## v1.0.1 137 | 138 | Bug fix on the :+1: emoji 139 | 140 | ## v1.0.0 141 | 142 | First release. 143 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/EmojiManager.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.*; 6 | 7 | /** 8 | * Holds the loaded emojis and provides search functions. 9 | * 10 | * @author Improver: Ivan Ivanov [https://vk.com/irisism]
11 | * Creator: Vincent DURMONT [vdurmont@gmail.com] 12 | */ 13 | public class EmojiManager { 14 | private static final String PATH = "/emojis.json"; 15 | private static final Map EMOJIS_BY_ALIAS = new HashMap<>(); 16 | private static final Map> EMOJIS_BY_TAG = new HashMap<>(); 17 | private static final List ALL_EMOJIS; 18 | static final EmojiTrie EMOJI_TRIE; 19 | 20 | static { 21 | try { 22 | InputStream stream = EmojiLoader.class.getResourceAsStream(PATH); 23 | List emojis = EmojiLoader.loadEmojis(stream); 24 | ALL_EMOJIS = emojis; 25 | for (Emoji emoji : emojis) { 26 | for (String tag : emoji.getTags()) { 27 | var tagSet = EMOJIS_BY_TAG.computeIfAbsent(tag, k -> new HashSet<>()); 28 | tagSet.add(emoji); 29 | } 30 | for (String alias : emoji.getAliases()) { 31 | EMOJIS_BY_ALIAS.put(alias, emoji); 32 | } 33 | } 34 | 35 | EMOJI_TRIE = new EmojiTrie(emojis); 36 | ALL_EMOJIS.sort((e1, e2) -> e2.getUnicode().length() - e1.getUnicode().length()); 37 | stream.close(); 38 | } catch (IOException e) { 39 | throw new RuntimeException(e); 40 | } 41 | } 42 | 43 | /** 44 | * No need for a constructor, all the methods are static. 45 | */ 46 | private EmojiManager() {} 47 | 48 | /** 49 | * Returns all the {@link com.vdurmont.emoji.Emoji}s for a given tag. 50 | * 51 | * @param tag the tag 52 | * 53 | * @return the associated {@link com.vdurmont.emoji.Emoji}s, null if the tag 54 | * is unknown 55 | */ 56 | public static Set getForTag(String tag) { 57 | if (tag == null) { 58 | return null; 59 | } 60 | return EMOJIS_BY_TAG.get(tag); 61 | } 62 | 63 | /** 64 | * Returns the {@link com.vdurmont.emoji.Emoji} for a given alias. 65 | * 66 | * @param alias the alias 67 | * 68 | * @return the associated {@link com.vdurmont.emoji.Emoji}, null if the alias 69 | * is unknown 70 | */ 71 | public static Emoji getForAlias(String alias) { 72 | if (alias == null || alias.isEmpty()) { 73 | return null; 74 | } 75 | return EMOJIS_BY_ALIAS.get(trimAlias(alias)); 76 | } 77 | 78 | private static String trimAlias(String alias) { 79 | int len = alias.length(); 80 | return alias.substring( 81 | alias.charAt(0) == ':' ? 1 : 0, 82 | alias.charAt(len - 1) == ':' ? len - 1 : len); 83 | } 84 | 85 | 86 | /** 87 | * Returns the {@link com.vdurmont.emoji.Emoji} for a given unicode. 88 | * 89 | * @param unicode the the unicode 90 | * 91 | * @return the associated {@link com.vdurmont.emoji.Emoji}, null if the 92 | * unicode is unknown 93 | */ 94 | public static Emoji getByUnicode(String unicode) { 95 | if (unicode == null) { 96 | return null; 97 | } 98 | var res = EmojiParser.getEmojiInPosition(unicode.toCharArray(), 0); 99 | if (res == null) 100 | return null; 101 | return res.emoji; 102 | } 103 | 104 | /** 105 | * Returns all the {@link com.vdurmont.emoji.Emoji}s 106 | * 107 | * @return all the {@link com.vdurmont.emoji.Emoji}s 108 | */ 109 | public static Collection getAll() { 110 | return ALL_EMOJIS; 111 | } 112 | 113 | /** 114 | * Tests if a given String is an emoji. 115 | * 116 | * @param string the string to test 117 | * @return true if the string is an emoji's unicode, false else 118 | */ 119 | public static boolean isEmoji(String string) { 120 | if (string == null) return false; 121 | var chars = string.toCharArray(); 122 | EmojiParser.EmojiResult result = EmojiParser.getEmojiInPosition(chars, 0); 123 | return result != null && result.startIndex == 0 && result.endIndex == chars.length; 124 | } 125 | 126 | /** 127 | * Tests if a given String contains an emoji. 128 | * 129 | * @param string the string to test 130 | * @return true if the string contains an emoji's unicode, false otherwise 131 | */ 132 | public static boolean containsEmoji(String string) { 133 | if (string == null) return false; 134 | return EmojiParser.getNextEmoji(string.toCharArray(), 0) != null; 135 | } 136 | 137 | /** 138 | * Tests if a given String only contains emojis. 139 | * 140 | * @param string the string to test 141 | * @return true if the string only contains emojis, false else 142 | */ 143 | public static boolean isOnlyEmojis(String string) { 144 | return string != null && EmojiParser.removeAllEmojis(string).isEmpty(); 145 | } 146 | 147 | /** 148 | * Checks if sequence of chars contain an emoji. 149 | * 150 | * @param sequence Sequence of char that may contain emoji in full or 151 | * partially. 152 | * @return <li> 153 | * Matches.EXACTLY if char sequence in its entirety is an emoji 154 | * </li> 155 | * <li> 156 | * Matches.POSSIBLY if char sequence matches prefix of an emoji 157 | * </li> 158 | * <li> 159 | * Matches.IMPOSSIBLE if char sequence matches no emoji or prefix of an 160 | * emoji 161 | * </li> 162 | */ 163 | public static EmojiTrie.Matches isEmoji(char[] sequence) { 164 | return EMOJI_TRIE.isEmoji(sequence); 165 | } 166 | 167 | /** 168 | * Returns all the tags in the database 169 | * 170 | * @return the tags 171 | */ 172 | public static Collection getAllTags() { 173 | return EMOJIS_BY_TAG.keySet(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/test/java/com/vdurmont/emoji/EmojiLoaderTest.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.JUnit4; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.UnsupportedEncodingException; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.List; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | @RunWith(JUnit4.class) 19 | public class EmojiLoaderTest { 20 | @Test 21 | public void load_empty_database_returns_empty_list() throws IOException { 22 | // GIVEN 23 | byte[] bytes = new JSONArray().toString().getBytes(StandardCharsets.UTF_8); 24 | InputStream stream = new ByteArrayInputStream(bytes); 25 | 26 | // WHEN 27 | List emojis = EmojiLoader.loadEmojis(stream); 28 | 29 | // THEN 30 | assertEquals(0, emojis.size()); 31 | } 32 | 33 | @Test 34 | public void buildEmojiFromJSON() throws UnsupportedEncodingException { 35 | // GIVEN 36 | JSONObject json = new JSONObject("{" 37 | + "\"emoji\": \"😄\"," 38 | + "\"description\": \"smiling face with open mouth and smiling eyes\"," 39 | + "\"aliases\": [\"smile\"]," 40 | + "\"tags\": [\"happy\", \"joy\", \"pleased\"]" 41 | + "}"); 42 | 43 | // WHEN 44 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 45 | 46 | // THEN 47 | assertNotNull(emoji); 48 | assertEquals("😄", emoji.getUnicode()); 49 | assertEquals( 50 | "smiling face with open mouth and smiling eyes", 51 | emoji.getDescription() 52 | ); 53 | assertEquals(1, emoji.getAliases().size()); 54 | assertEquals("smile", emoji.getAliases().get(0)); 55 | assertEquals(3, emoji.getTags().size()); 56 | assertEquals("happy", emoji.getTags().get(0)); 57 | assertEquals("joy", emoji.getTags().get(1)); 58 | assertEquals("pleased", emoji.getTags().get(2)); 59 | } 60 | 61 | @Test 62 | public void buildEmojiFromJSON_without_description_sets_a_null_description() 63 | throws UnsupportedEncodingException { 64 | // GIVEN 65 | JSONObject json = new JSONObject("{" 66 | + "\"emoji\": \"😄\"," 67 | + "\"aliases\": [\"smile\"]," 68 | + "\"tags\": [\"happy\", \"joy\", \"pleased\"]" 69 | + "}"); 70 | 71 | // WHEN 72 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 73 | 74 | // THEN 75 | assertNotNull(emoji); 76 | assertNull(emoji.getDescription()); 77 | } 78 | 79 | @Test 80 | public void buildEmojiFromJSON_without_unicode_returns_null() 81 | throws UnsupportedEncodingException { 82 | // GIVEN 83 | JSONObject json = new JSONObject("{" 84 | + "\"aliases\": [\"smile\"]," 85 | + "\"tags\": [\"happy\", \"joy\", \"pleased\"]" 86 | + "}"); 87 | 88 | // WHEN 89 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 90 | 91 | // THEN 92 | assertNull(emoji); 93 | } 94 | 95 | @Test 96 | public void buildEmojiFromJSON_computes_the_html_codes() 97 | throws UnsupportedEncodingException { 98 | // GIVEN 99 | JSONObject json = new JSONObject("{" 100 | + "\"emoji\": \"😄\"," 101 | + "\"description\": \"smiling face with open mouth and smiling eyes\"," 102 | + "\"aliases\": [\"smile\"]," 103 | + "\"tags\": [\"happy\", \"joy\", \"pleased\"]" 104 | + "}"); 105 | 106 | // WHEN 107 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 108 | 109 | // THEN 110 | assertNotNull(emoji); 111 | assertEquals("😄", emoji.getUnicode()); 112 | assertEquals("😄", emoji.getHtmlDecimal()); 113 | assertEquals("😄", emoji.getHtmlHexadecimal()); 114 | } 115 | 116 | @Test 117 | public void buildEmojiFromJSON_with_support_for_fitzpatrick_true() 118 | throws UnsupportedEncodingException { 119 | // GIVEN 120 | JSONObject json = new JSONObject("{" 121 | + "\"emoji\": \"\uD83D\uDC66\"," 122 | + "\"description\": \"boy\"," 123 | + "\"supports_fitzpatrick\": true," 124 | + "\"aliases\": [\"boy\"]," 125 | + "\"tags\": [\"child\"]" 126 | + "}"); 127 | 128 | // WHEN 129 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 130 | 131 | // THEN 132 | assertNotNull(emoji); 133 | assertTrue(emoji.supportsFitzpatrick()); 134 | } 135 | 136 | @Test 137 | public void buildEmojiFromJSON_with_support_for_fitzpatrick_false() 138 | throws UnsupportedEncodingException { 139 | // GIVEN 140 | JSONObject json = new JSONObject("{" 141 | + "\"emoji\": \"\uD83D\uDE15\"," 142 | + "\"description\": \"confused face\"," 143 | + "\"supports_fitzpatrick\": false," 144 | + "\"aliases\": [\"confused\"]," 145 | + "\"tags\": []" 146 | + "}"); 147 | 148 | // WHEN 149 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 150 | 151 | // THEN 152 | assertNotNull(emoji); 153 | assertFalse(emoji.supportsFitzpatrick()); 154 | } 155 | 156 | @Test 157 | public void buildEmojiFromJSON_without_support_for_fitzpatrick() 158 | throws UnsupportedEncodingException { 159 | // GIVEN 160 | JSONObject json = new JSONObject("{" 161 | + "\"emoji\": \"\uD83D\uDE15\"," 162 | + "\"description\": \"confused face\"," 163 | + "\"aliases\": [\"confused\"]," 164 | + "\"tags\": []" 165 | + "}"); 166 | 167 | // WHEN 168 | Emoji emoji = EmojiLoader.buildEmojiFromJSON(json); 169 | 170 | // THEN 171 | assertNotNull(emoji); 172 | assertFalse(emoji.supportsFitzpatrick()); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /charConverter.js: -------------------------------------------------------------------------------- 1 | // Script to convert a JSON file with "character" emojis to a JSON file with "source code" emojis 2 | // This was probably a one-time thing but I'll commit the code in case it is useful in the future. 3 | // 4 | // Usage: `node charConverter.js` 5 | 6 | const fs = require('fs'); 7 | const path = './src/main/resources/emojis.json'; 8 | const f = fs.readFileSync(path, 'utf8'); 9 | const json = JSON.parse(f); 10 | 11 | const newJson = json.reduce((acc, e) => { 12 | acc.push({ 13 | emojiChar: e.emojiChar, 14 | emoji: convertCharStr2jEsc(e.emojiChar, ''), 15 | description: e.description, 16 | supports_fitzpatrick: e.supports_fitzpatrick || undefined, 17 | supports_gender: e.supports_gender || undefined, 18 | aliases: e.aliases, 19 | tags: e.tags, 20 | }); 21 | return acc; 22 | }, []); 23 | 24 | const newFile = JSON.stringify(newJson, (key, value) => value, 2); 25 | fs.writeFileSync(path, newFile); 26 | 27 | 28 | 29 | /////////////////////////////////////////////////////////////////// 30 | // Following functions copied from https://github.com/r12a/app-conversion/blob/gh-pages/conversionfunctions.js 31 | // http://rishida.net/ 32 | /////////////////////////////////////////////////////////////////// 33 | 34 | 35 | 36 | function convertCharStr2jEsc(str, parameters) { 37 | // Converts a string of characters to JavaScript escapes 38 | // str: sequence of Unicode characters 39 | // parameters: a semicolon separated string showing ids for checkboxes that are turned on 40 | var highsurrogate = 0; 41 | var suppCP; 42 | var pad; 43 | var n = 0; 44 | var pars = parameters.split(';'); 45 | var outputString = ''; 46 | for (var i = 0; i < str.length; i++) { 47 | var cc = str.charCodeAt(i); 48 | if (cc < 0 || cc > 0xffff) { 49 | outputString += 50 | '!Error in convertCharStr2UTF16: unexpected charCodeAt result, cc=' + 51 | cc + 52 | '!'; 53 | } 54 | if (highsurrogate != 0) { 55 | // this is a supp char, and cc contains the low surrogate 56 | if (0xdc00 <= cc && cc <= 0xdfff) { 57 | suppCP = 0x10000 + ((highsurrogate - 0xd800) << 10) + (cc - 0xdc00); 58 | if (parameters.match(/cstyleSC/)) { 59 | pad = suppCP.toString(16); 60 | while (pad.length < 8) { 61 | pad = '0' + pad; 62 | } 63 | outputString += '\\U' + pad; 64 | } else if (parameters.match(/es6styleSC/)) { 65 | pad = suppCP.toString(16).toUpperCase(); 66 | outputString += '\\u{' + pad + '}'; 67 | } else { 68 | suppCP -= 0x10000; 69 | outputString += 70 | '\\u' + 71 | dec2hex4(0xd800 | (suppCP >> 10)) + 72 | '\\u' + 73 | dec2hex4(0xdc00 | (suppCP & 0x3ff)); 74 | } 75 | highsurrogate = 0; 76 | continue; 77 | } else { 78 | outputString += 79 | 'Error in convertCharStr2UTF16: low surrogate expected, cc=' + 80 | cc + 81 | '!'; 82 | highsurrogate = 0; 83 | } 84 | } 85 | if (0xd800 <= cc && cc <= 0xdbff) { 86 | // start of supplementary character 87 | highsurrogate = cc; 88 | } else { 89 | // this is a BMP character 90 | //outputString += dec2hex(cc) + ' '; 91 | switch (cc) { 92 | case 0: 93 | outputString += '\\0'; 94 | break; 95 | case 8: 96 | outputString += '\\b'; 97 | break; 98 | case 9: 99 | if (parameters.match(/noCR/)) { 100 | outputString += '\\t'; 101 | } else { 102 | outputString += '\t'; 103 | } 104 | break; 105 | case 10: 106 | if (parameters.match(/noCR/)) { 107 | outputString += '\\n'; 108 | } else { 109 | outputString += '\n'; 110 | } 111 | break; 112 | case 13: 113 | if (parameters.match(/noCR/)) { 114 | outputString += '\\r'; 115 | } else { 116 | outputString += '\r'; 117 | } 118 | break; 119 | case 11: 120 | outputString += '\\v'; 121 | break; 122 | case 12: 123 | outputString += '\\f'; 124 | break; 125 | case 34: 126 | if (parameters.match(/noCR/)) { 127 | outputString += '\\"'; 128 | } else { 129 | outputString += '"'; 130 | } 131 | break; 132 | case 39: 133 | if (parameters.match(/noCR/)) { 134 | outputString += "\\'"; 135 | } else { 136 | outputString += "'"; 137 | } 138 | break; 139 | case 92: 140 | outputString += '\\\\'; 141 | break; 142 | default: 143 | if (cc > 0x1f && cc < 0x7f) { 144 | outputString += String.fromCharCode(cc); 145 | } else { 146 | pad = cc.toString(16).toUpperCase(); 147 | while (pad.length < 4) { 148 | pad = '0' + pad; 149 | } 150 | outputString += '\\u' + pad; 151 | } 152 | } 153 | } 154 | } 155 | return outputString; 156 | } 157 | 158 | function dec2hex4(textString) { 159 | var hexequiv = new Array( 160 | '0', 161 | '1', 162 | '2', 163 | '3', 164 | '4', 165 | '5', 166 | '6', 167 | '7', 168 | '8', 169 | '9', 170 | 'A', 171 | 'B', 172 | 'C', 173 | 'D', 174 | 'E', 175 | 'F' 176 | ); 177 | return ( 178 | hexequiv[(textString >> 12) & 0xf] + 179 | hexequiv[(textString >> 8) & 0xf] + 180 | hexequiv[(textString >> 4) & 0xf] + 181 | hexequiv[textString & 0xf] 182 | ); 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/Emoji.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * This class represents an emoji.
7 | *
8 | * This object is immutable so it can be used safely in a multithreaded context. 9 | * 10 | * @author Improver: Ivan Ivanov [https://vk.com/irisism]
11 | * Creator: Vincent DURMONT [vdurmont@gmail.com] 12 | */ 13 | public class Emoji { 14 | public final String description; 15 | public final boolean supportsFitzpatrick; 16 | public final int sequenceType; 17 | public final List aliases; 18 | public final List tags; 19 | public final String unicode; 20 | public final String htmlDec; 21 | public final String htmlHex; 22 | public final String emojiChar; 23 | 24 | public static final int SEQUENCE_BASE_SKIN_GENDER = 1; 25 | public static final int SEQUENCE_GENDER_SKIN_BASE = 2; 26 | 27 | /** 28 | * Constructor for the Emoji. 29 | * 30 | * @param description The description of the emoji 31 | * @param sequenceType Whether the emoji supports Fitzpatrick modifiers 32 | * @param aliases the aliases for this emoji 33 | * @param tags the tags associated with this emoji 34 | * @param unicode the bytes that represent the emoji 35 | */ 36 | protected Emoji( 37 | String description, 38 | int sequenceType, 39 | List aliases, 40 | List tags, 41 | String unicode, 42 | String emojiChar 43 | ) { 44 | this.description = description; 45 | this.sequenceType = sequenceType; 46 | this.supportsFitzpatrick = sequenceType != 0; 47 | this.aliases = aliases; 48 | this.tags = tags; 49 | 50 | int count = 0; 51 | this.unicode = unicode; 52 | this.emojiChar = emojiChar; 53 | int stringLength = unicode.length(); 54 | String[] pointCodes = new String[stringLength]; 55 | String[] pointCodesHex = new String[stringLength]; 56 | 57 | for (int offset = 0; offset < stringLength; ) { 58 | final int codePoint = getUnicode().codePointAt(offset); 59 | 60 | pointCodes[count] = String.format("&#%d;", codePoint); 61 | pointCodesHex[count++] = String.format("&#x%x;", codePoint); 62 | 63 | offset += Character.charCount(codePoint); 64 | } 65 | this.htmlDec = stringJoin(pointCodes, count); 66 | this.htmlHex = stringJoin(pointCodesHex, count); 67 | } 68 | 69 | /** 70 | * Method to replace String.join, since it was only introduced in java8 71 | * 72 | * @param array the array to be concatenated 73 | * @return concatenated String 74 | */ 75 | private String stringJoin(String[] array, int count) { 76 | StringBuilder joined = new StringBuilder(); 77 | for (int i = 0; i < count; i++) 78 | joined.append(array[i]); 79 | return joined.toString(); 80 | } 81 | 82 | /** 83 | * Returns the description of the emoji 84 | * 85 | * @return the description 86 | */ 87 | public String getDescription() { 88 | return this.description; 89 | } 90 | 91 | /** 92 | * Returns wether the emoji supports the Fitzpatrick modifiers or not 93 | * 94 | * @return true if the emoji supports the Fitzpatrick modifiers 95 | */ 96 | public boolean supportsFitzpatrick() { 97 | return this.supportsFitzpatrick; 98 | } 99 | 100 | /** 101 | * Returns the aliases of the emoji 102 | * 103 | * @return the aliases (unmodifiable) 104 | */ 105 | public List getAliases() { 106 | return this.aliases; 107 | } 108 | 109 | /** 110 | * Returns the tags of the emoji 111 | * 112 | * @return the tags (unmodifiable) 113 | */ 114 | public List getTags() { 115 | return this.tags; 116 | } 117 | 118 | /** 119 | * Returns the unicode representation of the emoji 120 | * 121 | * @return the unicode representation 122 | */ 123 | public String getUnicode() { 124 | return this.unicode; 125 | } 126 | 127 | public String getEmojiChar() {return this.emojiChar; } 128 | 129 | /** 130 | * Returns the unicode representation of the emoji associated with the 131 | * provided Fitzpatrick modifier.
132 | * If the modifier is null, then the result is similar to 133 | * {@link Emoji#getUnicode()} 134 | * 135 | * @param fitzpatrick the fitzpatrick modifier or null 136 | * @return the unicode representation 137 | * @throws UnsupportedOperationException if the emoji doesn't support the 138 | * Fitzpatrick modifiers 139 | */ 140 | public String getUnicode(Fitzpatrick fitzpatrick) { 141 | if (!this.supportsFitzpatrick()) { 142 | throw new UnsupportedOperationException( 143 | "Cannot get the unicode with a fitzpatrick modifier, " + 144 | "the emoji doesn't support fitzpatrick." 145 | ); 146 | } else if (fitzpatrick == null) { 147 | return this.getUnicode(); 148 | } 149 | return this.getUnicode() + fitzpatrick.unicode; 150 | } 151 | 152 | /** 153 | * Returns the HTML decimal representation of the emoji 154 | * 155 | * @return the HTML decimal representation 156 | */ 157 | public String getHtmlDecimal() { 158 | return this.htmlDec; 159 | } 160 | 161 | /** 162 | * Returns the HTML hexadecimal representation of the emoji 163 | * 164 | * @return the HTML hexadecimal representation 165 | */ 166 | public String getHtmlHexadecimal() { 167 | return this.htmlHex; 168 | } 169 | 170 | @Override 171 | public boolean equals(Object other) { 172 | return other instanceof Emoji && 173 | ((Emoji) other).getUnicode().equals(getUnicode()); 174 | } 175 | 176 | @Override 177 | public int hashCode() { 178 | return unicode.hashCode(); 179 | } 180 | 181 | /** 182 | * Returns the String representation of the Emoji object.
183 | *
184 | * Example:
185 | * Emoji { 186 | * description='smiling face with open mouth and smiling eyes', 187 | * supportsFitzpatrick=false, 188 | * aliases=[smile], 189 | * tags=[happy, joy, pleased], 190 | * unicode='😄', 191 | * htmlDec='&#128516;', 192 | * htmlHex='&#x1f604;' 193 | * } 194 | * 195 | * @return the string representation 196 | */ 197 | @Override 198 | public String toString() { 199 | return "Emoji{" + 200 | "description='" + description + '\'' + 201 | ", supportsFitzpatrick=" + supportsFitzpatrick + 202 | ", aliases=" + aliases + 203 | ", tags=" + tags + 204 | ", unicode='" + unicode + '\'' + 205 | ", htmlDec='" + htmlDec + '\'' + 206 | ", htmlHex='" + htmlHex + '\'' + 207 | '}'; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.vdurmont-iris 6 | emoji-java 7 | 6.0i 8 | jar 9 | 10 | emoji-java 11 | https://github.com/iris2iris/iris-emoji-java 12 | The missing emoji library for Java. 13 | 14 | 15 | scm:git:git@github.com:iris2iris/iris-emoji-java.git 16 | scm:git:git@github.com:iris2iris/iris-emoji-java.git 17 | git@github.com:iris2iris/iris-emoji-java.git 18 | 19 | 20 | 21 | 22 | Ivan Ivanov 23 | https://vk.com/irisism 24 | 25 | 26 | 27 | 28 | 29 | The MIT License 30 | http://www.opensource.org/licenses/mit-license.php 31 | repo 32 | 33 | 34 | 35 | 36 | UTF-8 37 | 38 | 39 | 40 | 41 | org.json 42 | json 43 | 20170516 44 | 45 | 46 | 47 | junit 48 | junit 49 | 4.13 50 | test 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.codehaus.mojo 59 | cobertura-maven-plugin 60 | 2.5.2 61 | 62 | xml 63 | 256m 64 | true 65 | 66 | 67 | 68 | org.eluder.coveralls 69 | coveralls-maven-plugin 70 | 2.2.0 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | release 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-source-plugin 84 | 2.3 85 | 86 | 87 | attach-sources 88 | 89 | jar-no-fork 90 | 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-javadoc-plugin 97 | 2.9.1 98 | 99 | UTF-8 100 | UTF-8 101 | public 102 | 103 | 104 | 105 | attach-javadocs 106 | 107 | jar 108 | 109 | 110 | 111 | 112 | 113 | org.apache.maven.plugins 114 | maven-gpg-plugin 115 | 1.6 116 | 117 | 118 | sign-artifacts 119 | verify 120 | 121 | sign 122 | 123 | 124 | 125 | 126 | 127 | org.sonatype.plugins 128 | nexus-staging-maven-plugin 129 | 1.6.7 130 | true 131 | 132 | ossrh 133 | https://oss.sonatype.org/ 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | ossrh 145 | Sonatype Nexus Snapshots 146 | https://oss.sonatype.org/content/repositories/snapshots/ 147 | 148 | 149 | ossrh 150 | Nexus Release Repository 151 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /src/test/java/com/vdurmont/emoji/EmojiManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.junit.runners.JUnit4; 6 | 7 | import java.util.Collection; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertFalse; 13 | import static org.junit.Assert.assertNull; 14 | import static org.junit.Assert.assertTrue; 15 | 16 | @RunWith(JUnit4.class) 17 | public class EmojiManagerTest { 18 | @Test 19 | public void getForTag_with_unknown_tag_returns_null() { 20 | // GIVEN 21 | 22 | // WHEN 23 | Set emojis = EmojiManager.getForTag("jkahsgdfjksghfjkshf"); 24 | 25 | // THEN 26 | assertNull(emojis); 27 | } 28 | 29 | @Test 30 | public void getForTag_returns_the_emojis_for_the_tag() { 31 | // GIVEN 32 | 33 | // WHEN 34 | Set emojis = EmojiManager.getForTag("happy"); 35 | 36 | // THEN 37 | assertEquals(4, emojis.size()); 38 | assertTrue(TestTools.containsEmojis( 39 | emojis, 40 | "smile", 41 | "smiley", 42 | "grinning", 43 | "satisfied" 44 | )); 45 | } 46 | 47 | @Test 48 | public void getForTag_returns_the_eu_emoji_for_same_tag() { 49 | // GIVEN 50 | 51 | // WHEN 52 | Set emojis = EmojiManager.getForTag("european union"); 53 | 54 | // THEN 55 | assertEquals(1, emojis.size()); 56 | assertTrue(TestTools.containsEmojis(emojis, "eu")); 57 | } 58 | 59 | @Test 60 | public void getForAlias_with_unknown_alias_returns_null() { 61 | // GIVEN 62 | 63 | // WHEN 64 | Emoji emoji = EmojiManager.getForAlias("jkahsgdfjksghfjkshf"); 65 | 66 | // THEN 67 | assertNull(emoji); 68 | } 69 | 70 | @Test 71 | public void getForAlias_returns_the_emoji_for_the_alias() { 72 | // GIVEN 73 | 74 | // WHEN 75 | Emoji emoji = EmojiManager.getForAlias("smile"); 76 | 77 | // THEN 78 | assertEquals( 79 | "smiling face with open mouth and smiling eyes", 80 | emoji.getDescription() 81 | ); 82 | } 83 | 84 | @Test 85 | public void getForAlias_with_colons_returns_the_emoji_for_the_alias() { 86 | // GIVEN 87 | 88 | // WHEN 89 | Emoji emoji = EmojiManager.getForAlias(":smile:"); 90 | 91 | // THEN 92 | assertEquals( 93 | "smiling face with open mouth and smiling eyes", 94 | emoji.getDescription() 95 | ); 96 | } 97 | 98 | @Test 99 | public void isEmoji_for_an_emoji_returns_true() { 100 | // GIVEN 101 | String emoji = "😀"; 102 | 103 | // WHEN 104 | boolean isEmoji = EmojiManager.isEmoji(emoji); 105 | 106 | // THEN 107 | assertTrue(isEmoji); 108 | } 109 | 110 | @Test 111 | public void isEmoji_with_fitzpatric_modifier_returns_true() { 112 | // GIVEN 113 | String emoji = "\uD83E\uDD30\uD83C\uDFFB"; 114 | 115 | // WHEN 116 | boolean isEmoji = EmojiManager.isEmoji(emoji); 117 | 118 | // THEN 119 | assertTrue(isEmoji); 120 | } 121 | 122 | @Test 123 | public void isEmoji_for_a_non_emoji_returns_false() { 124 | // GIVEN 125 | String str = "test"; 126 | 127 | // WHEN 128 | boolean isEmoji = EmojiManager.isEmoji(str); 129 | 130 | // THEN 131 | assertFalse(isEmoji); 132 | } 133 | 134 | @Test 135 | public void isEmoji_for_an_emoji_and_other_chars_returns_false() { 136 | // GIVEN 137 | String str = "😀 test"; 138 | 139 | // WHEN 140 | boolean isEmoji = EmojiManager.isEmoji(str); 141 | 142 | // THEN 143 | assertFalse(isEmoji); 144 | } 145 | 146 | @Test 147 | public void containsEmoji_with_fitzpatric_modifier_returns_true() { 148 | // GIVEN 149 | String emoji = "\uD83E\uDD30\uD83C\uDFFB"; 150 | 151 | // WHEN 152 | boolean containsEmoji = EmojiManager.containsEmoji(emoji); 153 | 154 | // THEN 155 | assertTrue(containsEmoji); 156 | } 157 | 158 | @Test 159 | public void containsEmoji_for_a_non_emoji_returns_false() { 160 | // GIVEN 161 | String str = "test"; 162 | 163 | // WHEN 164 | boolean containsEmoji = EmojiManager.containsEmoji(str); 165 | 166 | // THEN 167 | assertFalse(containsEmoji); 168 | } 169 | 170 | @Test 171 | public void containsEmoji_for_an_emoji_and_other_chars_returns_true() { 172 | // GIVEN 173 | String str = "😀 test"; 174 | 175 | // WHEN 176 | boolean containsEmoji = EmojiManager.containsEmoji(str); 177 | 178 | // THEN 179 | assertTrue(containsEmoji); 180 | } 181 | 182 | @Test 183 | public void isOnlyEmojis_for_an_emoji_returns_true() { 184 | // GIVEN 185 | String str = "😀"; 186 | 187 | // WHEN 188 | boolean isEmoji = EmojiManager.isOnlyEmojis(str); 189 | 190 | // THEN 191 | assertTrue(isEmoji); 192 | } 193 | 194 | @Test 195 | public void isOnlyEmojis_for_emojis_returns_true() { 196 | // GIVEN 197 | String str = "😀😀😀"; 198 | 199 | // WHEN 200 | boolean isEmoji = EmojiManager.isOnlyEmojis(str); 201 | 202 | // THEN 203 | assertTrue(isEmoji); 204 | } 205 | 206 | @Test 207 | public void isOnlyEmojis_for_random_string_returns_false() { 208 | // GIVEN 209 | String str = "😀a"; 210 | 211 | // WHEN 212 | boolean isEmoji = EmojiManager.isOnlyEmojis(str); 213 | 214 | // THEN 215 | assertFalse(isEmoji); 216 | } 217 | 218 | @Test 219 | public void getAllTags_returns_the_tags() { 220 | // GIVEN 221 | 222 | // WHEN 223 | Collection tags = EmojiManager.getAllTags(); 224 | 225 | // THEN 226 | // We know the number of distinct tags int the...! 227 | assertEquals(656, tags.size()); 228 | } 229 | 230 | @Test 231 | public void getAll_doesnt_return_duplicates() { 232 | // GIVEN 233 | 234 | // WHEN 235 | Collection emojis = EmojiManager.getAll(); 236 | 237 | // THEN 238 | Set unicodes = new HashSet(); 239 | for (Emoji emoji : emojis) { 240 | assertFalse( 241 | "Duplicate: " + emoji.getDescription(), 242 | unicodes.contains(emoji.getUnicode()) 243 | ); 244 | unicodes.add(emoji.getUnicode()); 245 | } 246 | assertEquals(unicodes.size(), emojis.size()); 247 | } 248 | 249 | @Test 250 | public void no_duplicate_alias() { 251 | // GIVEN 252 | 253 | // WHEN 254 | Collection emojis = EmojiManager.getAll(); 255 | 256 | // THEN 257 | Set aliases = new HashSet(); 258 | Set duplicates = new HashSet(); 259 | for (Emoji emoji : emojis) { 260 | for (String alias : emoji.getAliases()) { 261 | if (aliases.contains(alias)) { 262 | duplicates.add(alias); 263 | } 264 | aliases.add(alias); 265 | } 266 | } 267 | assertEquals("Duplicates: " + duplicates, duplicates.size(), 0); 268 | } 269 | 270 | @Test 271 | public void getByUnicode_returns_correct_emoji() { 272 | String wavingHand = "\uD83D\uDC4B"; 273 | Emoji e = EmojiManager.getByUnicode(wavingHand); 274 | assertEquals(wavingHand, e.getUnicode()); 275 | assertEquals("waving hand sign", e.getDescription()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iris:emoji-java 2 | 3 | [![License Info](http://img.shields.io/badge/license-The%20MIT%20License-brightgreen.svg)](https://github.com/vdurmont/emoji-java/blob/master/LICENSE.md) 4 | 5 | _The missing emoji library for java._ 6 | 7 | **[iris:emoji-java](https://github.com/iris2iris/iris-emoji-java)** is a lightweight java library that helps you use Emojis in your java applications. 8 | 9 | It was forked from **[emoji-java](https://github.com/vdurmont/emoji-java)** 10 | 11 | #### Reasons I forked it: 12 | 13 | - ❗️ The most important reason I forked it was I have found how to improve its __speed up to 5x__ times! 14 | - ✅ This tool supports complex emoji sequences of types `{basic_emoji}{skin_color}?{gender}?` and `{gender}{skin_color}?{basic_emoji}` 15 | - It supports of bugged VK.COM web-version emojis, where ending char `\uFE0F` is absent 16 | 17 | And of course, extend useful methods. 18 | 19 | ## How to get it? 20 | 21 | ##### Via Direct Download: 22 | 23 | - Use [releases](https://github.com/iris2iris/iris-emoji-java/releases) tab to download the jar directly. 24 | - Download JSON-java dependency from http://mvnrepository.com/artifact/org.json/json. 25 | 26 | ## How to use it? 27 | 28 | ### EmojiManager 29 | 30 | The `EmojiManager` provides several static methods to search through the emojis database: 31 | 32 | - `getForTag` returns all the emojis for a given tag 33 | - `getForAlias` returns the emoji for an alias 34 | - `getAll` returns all the emojis 35 | - `isEmoji` checks if a string is an emoji 36 | - `containsEmoji` checks if a string contains any emoji 37 | 38 | You can also query the metadata: 39 | 40 | - `getAllTags` returns the available tags 41 | 42 | Or get everything: 43 | 44 | - `getAll` returns all the emojis 45 | 46 | ### Emoji model 47 | 48 | An `Emoji` is a POJO (plain old java object), which provides the following methods: 49 | 50 | - `getUnicode` returns the unicode representation of the emoji 51 | - `getUnicode(Fitzpatrick)` returns the unicode representation of the emoji with the provided Fitzpatrick modifier. If the emoji doesn't support the Fitzpatrick modifiers, this method will throw an `UnsupportedOperationException`. If the provided Fitzpatrick is null, this method will return the unicode of the emoji. 52 | - `getDescription` returns the (optional) description of the emoji 53 | - `getAliases` returns a list of aliases for this emoji 54 | - `getTags` returns a list of tags for this emoji 55 | - `getHtmlDecimal` returns an html decimal representation of the emoji 56 | - `getHtmlHexadecimal` returns an html decimal representation of the emoji 57 | - `supportsFitzpatrick` returns true if the emoji supports the Fitzpatrick modifiers, else false 58 | 59 | ### Fitzpatrick modifiers 60 | 61 | Some emojis now support the use of Fitzpatrick modifiers that gives the choice between 5 shades of skin tones: 62 | 63 | | Modifier | Type | 64 | | :------: | -------- | 65 | | 🏻 | type_1_2 | 66 | | 🏼 | type_3 | 67 | | 🏽 | type_4 | 68 | | 🏾 | type_5 | 69 | | 🏿 | type_6 | 70 | 71 | We defined the format of the aliases including a Fitzpatrick modifier as: 72 | 73 | ``` 74 | :ALIAS|TYPE: 75 | ``` 76 | 77 | A few examples: 78 | 79 | ``` 80 | :boy|type_1_2: 81 | :swimmer|type_4: 82 | :santa|type_6: 83 | ``` 84 | 85 | ### EmojiParser 86 | 87 | #### To unicode 88 | 89 | To replace all the aliases and the html representations found in a string by their unicode, use `EmojiParser#parseToUnicode(String)`. 90 | 91 | For example: 92 | 93 | ```java 94 | String str = "An :grinning:awesome :smiley:string 😄with a few :wink:emojis!"; 95 | String result = EmojiParser.parseToUnicode(str); 96 | System.out.println(result); 97 | // Prints: 98 | // "An 😀awesome 😃string 😄with a few 😉emojis!" 99 | ``` 100 | 101 | #### To aliases 102 | 103 | To replace all the emoji's unicodes found in a string by their aliases, use `EmojiParser#parseToAliases(String)`. 104 | 105 | For example: 106 | 107 | ```java 108 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 109 | String result = EmojiParser.parseToAliases(str); 110 | System.out.println(result); 111 | // Prints: 112 | // "An :grinning:awesome :smiley:string with a few :wink:emojis!" 113 | ``` 114 | 115 | By default, the aliases will parse and include any Fitzpatrick modifier that would be provided. If you want to remove or ignore the Fitzpatrick modifiers, use `EmojiParser#parseToAliases(String, FitzpatrickAction)`. Examples: 116 | 117 | ```java 118 | String str = "Here is a boy: \uD83D\uDC66\uD83C\uDFFF!"; 119 | System.out.println(EmojiParser.parseToAliases(str)); 120 | System.out.println(EmojiParser.parseToAliases(str, FitzpatrickAction.PARSE)); 121 | // Prints twice: "Here is a boy: :boy|type_6:!" 122 | System.out.println(EmojiParser.parseToAliases(str, FitzpatrickAction.REMOVE)); 123 | // Prints: "Here is a boy: :boy:!" 124 | System.out.println(EmojiParser.parseToAliases(str, FitzpatrickAction.IGNORE)); 125 | // Prints: "Here is a boy: :boy:🏿!" 126 | ``` 127 | 128 | #### To html 129 | 130 | To replace all the emoji's unicodes found in a string by their html representation, use `EmojiParser#parseToHtmlDecimal(String)` or `EmojiParser#parseToHtmlHexadecimal(String)`. 131 | 132 | For example: 133 | 134 | ```java 135 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 136 | 137 | String resultDecimal = EmojiParser.parseToHtmlDecimal(str); 138 | System.out.println(resultDecimal); 139 | // Prints: 140 | // "An 😀awesome 😃string with a few 😉emojis!" 141 | 142 | String resultHexadecimal = EmojiParser.parseToHtmlHexadecimal(str); 143 | System.out.println(resultHexadecimal); 144 | // Prints: 145 | // "An 😀awesome 😃string with a few 😉emojis!" 146 | ``` 147 | 148 | By default, any Fitzpatrick modifier will be removed. If you want to ignore the Fitzpatrick modifiers, use `EmojiParser#parseToAliases(String, FitzpatrickAction)`. Examples: 149 | 150 | ```java 151 | String str = "Here is a boy: \uD83D\uDC66\uD83C\uDFFF!"; 152 | System.out.println(EmojiParser.parseToHtmlDecimal(str)); 153 | System.out.println(EmojiParser.parseToHtmlDecimal(str, FitzpatrickAction.PARSE)); 154 | System.out.println(EmojiParser.parseToHtmlDecimal(str, FitzpatrickAction.REMOVE)); 155 | // Print 3 times: "Here is a boy: 👦!" 156 | System.out.println(EmojiParser.parseToHtmlDecimal(str, FitzpatrickAction.IGNORE)); 157 | // Prints: "Here is a boy: 👦🏿!" 158 | ``` 159 | 160 | The same applies for the methods `EmojiParser#parseToHtmlHexadecimal(String)` and `EmojiParser#parseToHtmlHexadecimal(String, FitzpatrickAction)`. 161 | 162 | #### Remove emojis 163 | 164 | You can easily remove emojis from a string using one of the following methods: 165 | 166 | - `EmojiParser#removeAllEmojis(String)`: removes all the emojis from the String 167 | - `EmojiParser#removeAllEmojisExcept(String, Collection)`: removes all the emojis from the String, except the ones in the Collection 168 | - `EmojiParser#removeEmojis(String, Collection)`: removes the emojis in the Collection from the String 169 | 170 | For example: 171 | 172 | ```java 173 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 174 | Collection collection = new ArrayList(); 175 | collection.add(EmojiManager.getForAlias("wink")); // This is 😉 176 | 177 | System.out.println(EmojiParser.removeAllEmojis(str)); 178 | System.out.println(EmojiParser.removeAllEmojisExcept(str, collection)); 179 | System.out.println(EmojiParser.removeEmojis(str, collection)); 180 | 181 | // Prints: 182 | // "An awesome string with a few emojis!" 183 | // "An awesome string with a few 😉emojis!" 184 | // "An 😀awesome 😃string with a few emojis!" 185 | ``` 186 | 187 | #### Extract Emojis from a string 188 | 189 | You can search a string of mixed emoji/non-emoji characters and have all of the emoji characters returned as a Collection. 190 | 191 | - `EmojiParser#extractEmojis(String)`: returns all emojis as a Collection. This will include duplicates if emojis are present more than once. 192 | 193 | ## Credits 194 | 195 | **[iris-emoji-java](https://github.com/iris2iris/iris-emoji-java)** is based on [github/vdurmont/emoji-java](https://github.com/vdurmont/emoji-java). 196 | 197 | And in its turn **emoji-java** originally used the data provided by the [github/gemoji project](https://github.com/github/gemoji). It is still based on it but has evolved since. 198 | 199 | ## Don't forget to give stars ⭐ 200 | -------------------------------------------------------------------------------- /src/test/java/com/vdurmont/emoji/EmojiParserTest.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import com.vdurmont.emoji.EmojiParser.AliasCandidate; 4 | import com.vdurmont.emoji.EmojiParser.FitzpatrickAction; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.JUnit4; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | @RunWith(JUnit4.class) 15 | public class EmojiParserTest { 16 | @Test 17 | public void parseToAliases_replaces_the_emojis_by_one_of_their_aliases() { 18 | // GIVEN 19 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 20 | 21 | // WHEN 22 | String result = EmojiParser.parseToAliases(str); 23 | 24 | // THEN 25 | assertEquals( 26 | "An :grinning:awesome :smiley:string with a few :wink:emojis!", 27 | result 28 | ); 29 | } 30 | 31 | @Test 32 | public void replaceAllEmojis_replace_the_emojis_by_string() throws Exception { 33 | // GIVEN 34 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 35 | 36 | // WHEN 37 | String result = EmojiParser.replaceAllEmojis(str, ":)"); 38 | 39 | // THEN 40 | assertEquals( 41 | "An :)awesome :)string with a few :)emojis!", 42 | result 43 | ); 44 | } 45 | 46 | 47 | @Test 48 | public void parseToAliases_REPLACE_with_a_fitzpatrick_modifier() { 49 | // GIVEN 50 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 51 | 52 | // WHEN 53 | String result = EmojiParser.parseToAliases(str); 54 | 55 | // THEN 56 | assertEquals(":boy|TYPE_6:", result); 57 | } 58 | 59 | @Test 60 | public void parseToAliases_REMOVE_with_a_fitzpatrick_modifier() { 61 | // GIVEN 62 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 63 | 64 | // WHEN 65 | String result = EmojiParser.parseToAliases(str, FitzpatrickAction.REMOVE); 66 | 67 | // THEN 68 | assertEquals(":boy:", result); 69 | } 70 | 71 | @Test 72 | public void parseToAliases_REMOVE_without_a_fitzpatrick_modifier() { 73 | // GIVEN 74 | String str = "\uD83D\uDC66"; 75 | 76 | // WHEN 77 | String result = EmojiParser.parseToAliases(str, FitzpatrickAction.REMOVE); 78 | 79 | // THEN 80 | assertEquals(":boy:", result); 81 | } 82 | 83 | @Test 84 | public void parseToAliases_IGNORE_with_a_fitzpatrick_modifier() { 85 | // GIVEN 86 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 87 | 88 | // WHEN 89 | String result = EmojiParser.parseToAliases(str, FitzpatrickAction.IGNORE); 90 | 91 | // THEN 92 | assertEquals(":boy:\uD83C\uDFFF", result); 93 | } 94 | 95 | @Test 96 | public void parseToAliases_IGNORE_without_a_fitzpatrick_modifier() { 97 | // GIVEN 98 | String str = "\uD83D\uDC66"; 99 | 100 | // WHEN 101 | String result = EmojiParser.parseToAliases(str, FitzpatrickAction.IGNORE); 102 | 103 | // THEN 104 | assertEquals(":boy:", result); 105 | } 106 | 107 | @Test 108 | public void parseToAliases_with_long_overlapping_emoji() { 109 | // GIVEN 110 | String str = "\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66"; 111 | 112 | // WHEN 113 | String result = EmojiParser.parseToAliases(str); 114 | 115 | //With greedy parsing, this will give :man::woman::boy: 116 | //THEN 117 | assertEquals(":family_man_woman_boy:", result); 118 | } 119 | 120 | @Test 121 | public void parseToAliases_continuous_non_overlapping_emojis() { 122 | // GIVEN 123 | String str = "\uD83D\uDC69\uD83D\uDC68\uD83D\uDC66"; 124 | 125 | // WHEN 126 | String result = EmojiParser.parseToAliases(str); 127 | 128 | //THEN 129 | assertEquals(":woman::man::boy:", result); 130 | } 131 | 132 | @Test 133 | public void parseToHtmlDecimal_replaces_the_emojis_by_their_html_decimal_representation() { 134 | // GIVEN 135 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 136 | 137 | // WHEN 138 | String result = EmojiParser.parseToHtmlDecimal(str); 139 | 140 | // THEN 141 | assertEquals( 142 | "An 😀awesome 😃string with a few 😉emojis!", 143 | result 144 | ); 145 | } 146 | 147 | @Test 148 | public void parseToHtmlDecimal_PARSE_with_a_fitzpatrick_modifier() { 149 | // GIVEN 150 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 151 | 152 | // WHEN 153 | String result = EmojiParser.parseToHtmlDecimal( 154 | str, 155 | FitzpatrickAction.PARSE 156 | ); 157 | 158 | // THEN 159 | assertEquals("👦", result); 160 | } 161 | 162 | @Test 163 | public void parseToHtmlDecimal_REMOVE_with_a_fitzpatrick_modifier() { 164 | // GIVEN 165 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 166 | 167 | // WHEN 168 | String result = EmojiParser.parseToHtmlDecimal( 169 | str, 170 | FitzpatrickAction.REMOVE 171 | ); 172 | 173 | // THEN 174 | assertEquals("👦", result); 175 | } 176 | 177 | @Test 178 | public void parseToHtmlDecimal_IGNORE_with_a_fitzpatrick_modifier() { 179 | // GIVEN 180 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 181 | 182 | // WHEN 183 | String result = EmojiParser.parseToHtmlDecimal( 184 | str, 185 | FitzpatrickAction.IGNORE 186 | ); 187 | 188 | // THEN 189 | assertEquals("👦\uD83C\uDFFF", result); 190 | } 191 | 192 | @Test 193 | public void parseToHtmlHexadecimal_replaces_the_emojis_by_their_htm_hex_representation() { 194 | // GIVEN 195 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 196 | 197 | // WHEN 198 | String result = EmojiParser.parseToHtmlHexadecimal(str); 199 | 200 | // THEN 201 | assertEquals( 202 | "An 😀awesome 😃string with a few 😉emojis!", 203 | result 204 | ); 205 | } 206 | 207 | @Test 208 | public void parseToHtmlHexadecimal_PARSE_with_a_fitzpatrick_modifier() { 209 | // GIVEN 210 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 211 | 212 | // WHEN 213 | String result = EmojiParser.parseToHtmlHexadecimal( 214 | str, 215 | FitzpatrickAction.PARSE 216 | ); 217 | 218 | // THEN 219 | assertEquals("👦", result); 220 | } 221 | 222 | @Test 223 | public void parseToHtmlHexadecimal_REMOVE_with_a_fitzpatrick_modifier() { 224 | // GIVEN 225 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 226 | 227 | // WHEN 228 | String result = EmojiParser.parseToHtmlHexadecimal( 229 | str, 230 | FitzpatrickAction.REMOVE 231 | ); 232 | 233 | // THEN 234 | assertEquals("👦", result); 235 | } 236 | 237 | @Test 238 | public void parseToHtmlHexadecimal_IGNORE_with_a_fitzpatrick_modifier() { 239 | // GIVEN 240 | String str = "\uD83D\uDC66\uD83C\uDFFF"; 241 | 242 | // WHEN 243 | String result = EmojiParser.parseToHtmlHexadecimal( 244 | str, 245 | FitzpatrickAction.IGNORE 246 | ); 247 | 248 | // THEN 249 | assertEquals("👦\uD83C\uDFFF", result); 250 | } 251 | 252 | @Test 253 | public void parseToUnicode_replaces_the_aliases_and_the_html_by_their_emoji() { 254 | // GIVEN 255 | String str = "An :grinning:awesome :smiley:string " + 256 | "😄with a few 😉emojis!"; 257 | 258 | // WHEN 259 | String result = EmojiParser.parseToUnicode(str); 260 | 261 | // THEN 262 | assertEquals("An 😀awesome 😃string 😄with a few 😉emojis!", result); 263 | } 264 | 265 | @Test 266 | public void parseToUnicode_with_the_thumbsup_emoji_replaces_the_alias_by_the_emoji() { 267 | // GIVEN 268 | String str = "An :+1:awesome :smiley:string " + 269 | "😄with a few :wink:emojis!"; 270 | 271 | // WHEN 272 | String result = EmojiParser.parseToUnicode(str); 273 | 274 | // THEN 275 | assertEquals( 276 | "An \uD83D\uDC4Dawesome 😃string 😄with a few 😉emojis!", 277 | result 278 | ); 279 | } 280 | 281 | @Test 282 | public void parseToUnicode_with_the_thumbsdown_emoji_replaces_the_alias_by_the_emoji() { 283 | // GIVEN 284 | String str = "An :-1:awesome :smiley:string 😄" + 285 | "with a few :wink:emojis!"; 286 | 287 | // WHEN 288 | String result = EmojiParser.parseToUnicode(str); 289 | 290 | // THEN 291 | assertEquals( 292 | "An \uD83D\uDC4Eawesome 😃string 😄with a few 😉emojis!", 293 | result 294 | ); 295 | } 296 | 297 | @Test 298 | public void parseToUnicode_with_the_thumbsup_emoji_in_hex_replaces_the_alias_by_the_emoji() { 299 | // GIVEN 300 | String str = "An :+1:awesome :smiley:string 😄" + 301 | "with a few :wink:emojis!"; 302 | 303 | // WHEN 304 | String result = EmojiParser.parseToUnicode(str); 305 | 306 | // THEN 307 | assertEquals( 308 | "An \uD83D\uDC4Dawesome 😃string 😄with a few 😉emojis!", 309 | result 310 | ); 311 | } 312 | 313 | @Test 314 | public void parseToUnicode_with_a_fitzpatrick_modifier() { 315 | // GIVEN 316 | String str = ":boy|type_6:"; 317 | 318 | // WHEN 319 | String result = EmojiParser.parseToUnicode(str); 320 | 321 | // THEN 322 | assertEquals("\uD83D\uDC66\uD83C\uDFFF", result); 323 | } 324 | 325 | @Test 326 | public void parseToUnicode_with_an_unsupported_fitzpatrick_modifier_doesnt_replace() { 327 | // GIVEN 328 | String str = ":grinning|type_6:"; 329 | // WHEN 330 | String result = EmojiParser.parseToUnicode(str); 331 | 332 | // THEN 333 | assertEquals(str, result); 334 | } 335 | 336 | @Test 337 | public void getAliasAt_with_one_alias() { 338 | // GIVEN 339 | String str = "test :boy: test"; 340 | 341 | // WHEN 342 | AliasCandidate candidate = EmojiParser.getAliasAt(str, 5); 343 | 344 | // THEN 345 | assertTrue(candidate.emoji.getAliases().contains("boy")); 346 | assertNull(candidate.fitzpatrick); 347 | } 348 | 349 | @Test 350 | public void getAliasAt_with_one_alias_an_another_colon_after() { 351 | // GIVEN 352 | String str = "test :boy: test:"; 353 | 354 | // WHEN 355 | AliasCandidate candidate = EmojiParser.getAliasAt(str, 5); 356 | 357 | // THEN 358 | assertTrue(candidate.emoji.getAliases().contains("boy")); 359 | assertNull(candidate.fitzpatrick); 360 | } 361 | 362 | @Test 363 | public void getAliasAt_with_one_alias_an_another_colon_right_after() { 364 | // GIVEN 365 | String str = "test :boy::test"; 366 | 367 | // WHEN 368 | AliasCandidate candidate = EmojiParser.getAliasAt(str, 5); 369 | 370 | // THEN 371 | assertTrue(candidate.emoji.getAliases().contains("boy")); 372 | assertNull(candidate.fitzpatrick); 373 | } 374 | 375 | @Test 376 | public void getAliasAt_with_one_alias_an_another_colon_before_after() { 377 | // GIVEN 378 | String str = "test ::boy: test"; 379 | 380 | // WHEN 381 | AliasCandidate candidate = EmojiParser.getAliasAt(str, 5); 382 | assertNull(candidate); 383 | 384 | candidate = EmojiParser.getAliasAt(str, 6); 385 | 386 | // THEN 387 | assertTrue(candidate.emoji.getAliases().contains("boy")); 388 | assertNull(candidate.fitzpatrick); 389 | } 390 | 391 | @Test 392 | public void getAliasAt_with_a_fitzpatrick_modifier() { 393 | // GIVEN 394 | String str = "test :boy|type_3: test"; 395 | 396 | // WHEN 397 | AliasCandidate candidate = EmojiParser.getAliasAt(str, 5); 398 | 399 | // THEN 400 | assertTrue(candidate.emoji.getAliases().contains("boy")); 401 | assertEquals(Fitzpatrick.TYPE_3, candidate.fitzpatrick); 402 | } 403 | 404 | @Test 405 | public void test_with_a_new_flag() { 406 | String input = "Cuba has a new flag! :cu:"; 407 | String expected = "Cuba has a new flag! \uD83C\uDDE8\uD83C\uDDFA"; 408 | 409 | assertEquals(expected, EmojiParser.parseToUnicode(input)); 410 | assertEquals(input, EmojiParser.parseToAliases(expected)); 411 | } 412 | 413 | @Test 414 | public void removeAllEmojis_removes_all_the_emojis_from_the_string() { 415 | // GIVEN 416 | String input = "An 😀awesome 😃string 😄with " + 417 | "a \uD83D\uDC66\uD83C\uDFFFfew 😉emojis!"; 418 | 419 | // WHEN 420 | String result = EmojiParser.removeAllEmojis(input); 421 | 422 | // THEN 423 | String expected = "An awesome string with a few emojis!"; 424 | assertEquals(expected, result); 425 | } 426 | 427 | @Test 428 | public void removeEmojis_only_removes_the_emojis_in_the_iterable_from_the_string() { 429 | // GIVEN 430 | String input = "An\uD83D\uDE03 awesome\uD83D\uDE04 string" + 431 | "\uD83D\uDC4D\uD83C\uDFFF with\uD83D\uDCAA\uD83C\uDFFD a few emojis!"; 432 | 433 | List emojis = new ArrayList(); 434 | emojis.add(EmojiManager.getForAlias("smile")); 435 | emojis.add(EmojiManager.getForAlias("+1")); 436 | 437 | // WHEN 438 | String result = EmojiParser.removeEmojis(input, emojis); 439 | 440 | // THEN 441 | String expected = "An\uD83D\uDE03 awesome string with" + 442 | "\uD83D\uDCAA\uD83C\uDFFD a few emojis!"; 443 | assertEquals(expected, result); 444 | } 445 | 446 | @Test 447 | public void removeAllEmojisExcept_removes_all_the_emojis_from_the_string_except_those_in_the_iterable() { 448 | // GIVEN 449 | String input = "An\uD83D\uDE03 awesome\uD83D\uDE04 string" + 450 | "\uD83D\uDC4D\uD83C\uDFFF with\uD83D\uDCAA\uD83C\uDFFD a few emojis!"; 451 | 452 | List emojis = new ArrayList(); 453 | emojis.add(EmojiManager.getForAlias("smile")); 454 | emojis.add(EmojiManager.getForAlias("+1")); 455 | 456 | // WHEN 457 | String result = EmojiParser.removeAllEmojisExcept(input, emojis); 458 | 459 | // THEN 460 | String expected = "An awesome\uD83D\uDE04 string\uD83D\uDC4D\uD83C\uDFFF " + 461 | "with a few emojis!"; 462 | assertEquals(expected, result); 463 | } 464 | 465 | @Test 466 | public void parseToUnicode_with_the_keycap_asterisk_emoji_replaces_the_alias_by_the_emoji() { 467 | // GIVEN 468 | String str = "Let's test the :keycap_asterisk: emoji and " + 469 | "its other alias :star_keycap:"; 470 | 471 | // WHEN 472 | String result = EmojiParser.parseToUnicode(str); 473 | 474 | // THEN 475 | assertEquals("Let's test the *⃣ emoji and its other alias *⃣", result); 476 | } 477 | 478 | @Test 479 | public void parseToAliases_NG_and_nigeria() { 480 | // GIVEN 481 | String str = "Nigeria is 🇳🇬, NG is 🆖"; 482 | 483 | // WHEN 484 | String result = EmojiParser.parseToAliases(str); 485 | 486 | // THEN 487 | assertEquals("Nigeria is :ng:, NG is :squared_ng:", result); 488 | } 489 | 490 | @Test 491 | public void parseToAliases_couplekiss_woman_woman() { 492 | // GIVEN 493 | String str = "👩‍❤️‍💋‍👩"; 494 | 495 | // WHEN 496 | String result = EmojiParser.parseToAliases(str); 497 | 498 | // THEN 499 | assertEquals(":couplekiss_woman_woman:", result); 500 | } 501 | 502 | @Test 503 | public void extractEmojis() { 504 | // GIVEN 505 | String str = "An 😀awesome 😃string with a few 😉emojis!"; 506 | 507 | // WHEN 508 | List result = EmojiParser.extractEmojiStrings(str); 509 | 510 | // THEN 511 | assertEquals("😀", result.get(0)); 512 | assertEquals("😃", result.get(1)); 513 | assertEquals("😉", result.get(2)); 514 | 515 | } 516 | 517 | @Test 518 | public void extractEmojis_withFitzpatrickModifiers() { 519 | // GIVEN 520 | final String surfer = EmojiManager.getForAlias("surfer").getUnicode(); 521 | final String surfer3 = EmojiManager.getForAlias("surfer").getUnicode(Fitzpatrick.TYPE_3); 522 | final String surfer4 = EmojiManager.getForAlias("surfer").getUnicode(Fitzpatrick.TYPE_4); 523 | final String surfer5 = EmojiManager.getForAlias("surfer").getUnicode(Fitzpatrick.TYPE_5); 524 | final String surfer6 = EmojiManager.getForAlias("surfer").getUnicode(Fitzpatrick.TYPE_6); 525 | final String surfers = surfer + " " + surfer3 + " " + surfer4 + " " + surfer5 + " " + surfer6; 526 | 527 | // WHEN 528 | List result = EmojiParser.extractEmojiStrings(surfers); 529 | 530 | // THEN 531 | assertEquals(5, result.size()); 532 | assertEquals(surfer, result.get(0)); 533 | assertEquals(surfer3, result.get(1)); 534 | assertEquals(surfer4, result.get(2)); 535 | assertEquals(surfer5, result.get(3)); 536 | assertEquals(surfer6, result.get(4)); 537 | 538 | } 539 | 540 | @Test 541 | public void parseToAliases_with_first_medal() { 542 | // GIVEN 543 | String str = "🥇"; 544 | 545 | // WHEN 546 | String result = EmojiParser.parseToAliases(str); 547 | 548 | // THEN 549 | assertEquals(":first_place_medal:", result); 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /src/main/java/com/vdurmont/emoji/EmojiParser.java: -------------------------------------------------------------------------------- 1 | package com.vdurmont.emoji; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | /** 8 | * Provides methods to parse strings with emojis. 9 | * 10 | * @author Improver: Ivan Ivanov [https://vk.com/irisism]
11 | * Creator: Vincent DURMONT [vdurmont@gmail.com] 12 | */ 13 | public class EmojiParser { 14 | 15 | /** 16 | * See {@link #parseToAliases(String, FitzpatrickAction)} with the action 17 | * "PARSE" 18 | * 19 | * @param input the string to parse 20 | * @return the string with the emojis replaced by their alias. 21 | */ 22 | public static String parseToAliases(String input) { 23 | return parseToAliases(input, FitzpatrickAction.PARSE); 24 | } 25 | 26 | /** 27 | * Replaces the emoji's unicode occurrences by one of their alias 28 | * (between 2 ':').
29 | * Example: 😄 will be replaced by :smile:
30 | *
31 | * When a fitzpatrick modifier is present with a PARSE action, a "|" will be 32 | * appendend to the alias, with the fitzpatrick type.
33 | * Example: 👦🏿 will be replaced by 34 | * :boy|type_6:
35 | * The fitzpatrick types are: type_1_2, type_3, type_4, type_5, type_6
36 | *
37 | * When a fitzpatrick modifier is present with a REMOVE action, the modifier 38 | * will be deleted.
39 | * Example: 👦🏿 will be replaced by :boy:
40 | *
41 | * When a fitzpatrick modifier is present with a IGNORE action, the modifier 42 | * will be ignored.
43 | * Example: 👦🏿 will be replaced by :boy:🏿
44 | * 45 | * @param input the string to parse 46 | * @param fitzpatrickAction the action to apply for the fitzpatrick modifiers 47 | * @return the string with the emojis replaced by their alias. 48 | */ 49 | public static String parseToAliases(String input, final FitzpatrickAction fitzpatrickAction) { 50 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 51 | public String transform(EmojiResult unicodeCandidate) { 52 | switch (fitzpatrickAction) { 53 | default: 54 | case PARSE: 55 | if (unicodeCandidate.hasFitzpatrick()) { 56 | return ":" + 57 | unicodeCandidate.getEmoji().getAliases().get(0) + 58 | "|" + 59 | unicodeCandidate.getFitzpatrickType() + 60 | ":"; 61 | } 62 | case REMOVE: 63 | return ":" + 64 | unicodeCandidate.getEmoji().getAliases().get(0) + 65 | ":"; 66 | case IGNORE: 67 | return ":" + 68 | unicodeCandidate.getEmoji().getAliases().get(0) + 69 | ":" + 70 | unicodeCandidate.getFitzpatrickUnicode(); 71 | } 72 | } 73 | }; 74 | 75 | return parseFromUnicode(input, emojiTransformer); 76 | } 77 | 78 | /** 79 | * Replace all emojis with character 80 | * 81 | * @param str the string to process 82 | * @param replacementString replacement the string that will replace all the emojis 83 | * @return the string with replaced character 84 | */ 85 | public static String replaceAllEmojis(String str, final String replacementString) { 86 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 87 | public String transform(EmojiResult unicodeCandidate) { 88 | return replacementString; 89 | } 90 | }; 91 | 92 | return parseFromUnicode(str, emojiTransformer); 93 | } 94 | 95 | 96 | /** 97 | * Replaces the emoji's aliases (between 2 ':') occurrences and the html 98 | * representations by their unicode.
99 | * Examples:
100 | * :smile: will be replaced by 😄
101 | * &#128516; will be replaced by 😄
102 | * :boy|type_6: will be replaced by 👦🏿 103 | * 104 | * @param input the string to parse 105 | * @return the string with the aliases and html representations replaced by 106 | * their unicode. 107 | */ 108 | public static String parseToUnicode(String input) { 109 | StringBuilder sb = new StringBuilder(input.length()); 110 | 111 | for (int last = 0; last < input.length(); last++) { 112 | AliasCandidate alias = getAliasAt(input, last); 113 | if (alias == null) { 114 | alias = getHtmlEncodedEmojiAt(input, last); 115 | } 116 | 117 | if (alias != null) { 118 | sb.append(alias.emoji.getUnicode()); 119 | last = alias.endIndex; 120 | 121 | if (alias.fitzpatrick != null) { 122 | sb.append(alias.fitzpatrick.unicode); 123 | } 124 | } else { 125 | sb.append(input.charAt(last)); 126 | } 127 | } 128 | 129 | return sb.toString(); 130 | } 131 | 132 | /** 133 | * Finds the alias in the given string starting at the given point, null otherwise 134 | */ 135 | protected static AliasCandidate getAliasAt(String input, int start) { 136 | if (input.length() < start + 2 || input.charAt(start) != ':') return null; // Aliases start with : 137 | int aliasEnd = input.indexOf(':', start + 2); // Alias must be at least 1 char in length 138 | if (aliasEnd == -1) return null; // No alias end found 139 | 140 | int fitzpatrickStart = input.indexOf('|', start + 2); 141 | if (fitzpatrickStart != -1 && fitzpatrickStart < aliasEnd) { 142 | Emoji emoji = EmojiManager.getForAlias(input.substring(start, fitzpatrickStart)); 143 | if (emoji == null) return null; // Not a valid alias 144 | if (!emoji.supportsFitzpatrick()) return null; // Fitzpatrick was specified, but the emoji does not support it 145 | Fitzpatrick fitzpatrick = Fitzpatrick.fitzpatrickFromType(input.substring(fitzpatrickStart + 1, aliasEnd)); 146 | return new AliasCandidate(emoji, fitzpatrick, start, aliasEnd); 147 | } 148 | 149 | Emoji emoji = EmojiManager.getForAlias(input.substring(start, aliasEnd)); 150 | if (emoji == null) return null; // Not a valid alias 151 | return new AliasCandidate(emoji, null, start, aliasEnd); 152 | } 153 | 154 | /** 155 | * Finds the HTML encoded emoji in the given string starting at the given point, null otherwise 156 | */ 157 | protected static AliasCandidate getHtmlEncodedEmojiAt(String input, int start) { 158 | if (input.length() < start + 4 || input.charAt(start) != '&' || input.charAt(start + 1) != '#') return null; 159 | 160 | Emoji longestEmoji = null; 161 | int longestCodePointEnd = -1; 162 | char[] chars = new char[EmojiManager.EMOJI_TRIE.maxDepth]; 163 | int charsIndex = 0; 164 | int codePointStart = start; 165 | do { 166 | int codePointEnd = input.indexOf(';', codePointStart + 3); // Code point must be at least 1 char in length 167 | if (codePointEnd == -1) break; 168 | 169 | try { 170 | int radix = input.charAt(codePointStart + 2) == 'x' ? 16 : 10; 171 | int codePoint = Integer.parseInt(input.substring(codePointStart + 2 + radix / 16, codePointEnd), radix); 172 | charsIndex += Character.toChars(codePoint, chars, charsIndex); 173 | } catch (IllegalArgumentException e) { 174 | break; 175 | } 176 | Emoji foundEmoji = EmojiManager.EMOJI_TRIE.getEmoji(chars, 0, charsIndex); 177 | if (foundEmoji != null) { 178 | longestEmoji = foundEmoji; 179 | longestCodePointEnd = codePointEnd; 180 | } 181 | codePointStart = codePointEnd + 1; 182 | } while (input.length() > codePointStart + 4 && 183 | input.charAt(codePointStart) == '&' && 184 | input.charAt(codePointStart + 1) == '#' && 185 | charsIndex < chars.length && 186 | !EmojiManager.EMOJI_TRIE.isEmoji(chars, 0, charsIndex).impossibleMatch()); 187 | 188 | if (longestEmoji == null) return null; 189 | return new AliasCandidate(longestEmoji, null, start, longestCodePointEnd); 190 | } 191 | 192 | /** 193 | * See {@link #parseToHtmlDecimal(String, FitzpatrickAction)} with the action 194 | * "PARSE" 195 | * 196 | * @param input the string to parse 197 | * @return the string with the emojis replaced by their html decimal 198 | * representation. 199 | */ 200 | public static String parseToHtmlDecimal(String input) { 201 | return parseToHtmlDecimal(input, FitzpatrickAction.PARSE); 202 | } 203 | 204 | /** 205 | * Replaces the emoji's unicode occurrences by their html representation.
206 | * Example: 😄 will be replaced by &#128516;
207 | *
208 | * When a fitzpatrick modifier is present with a PARSE or REMOVE action, the 209 | * modifier will be deleted from the string.
210 | * Example: 👦🏿 will be replaced by 211 | * &#128102;
212 | *
213 | * When a fitzpatrick modifier is present with a IGNORE action, the modifier 214 | * will be ignored and will remain in the string.
215 | * Example: 👦🏿 will be replaced by 216 | * &#128102;🏿 217 | * 218 | * @param input the string to parse 219 | * @param fitzpatrickAction the action to apply for the fitzpatrick modifiers 220 | * @return the string with the emojis replaced by their html decimal 221 | * representation. 222 | */ 223 | public static String parseToHtmlDecimal( 224 | String input, 225 | final FitzpatrickAction fitzpatrickAction 226 | ) { 227 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 228 | public String transform(EmojiResult unicodeCandidate) { 229 | return switch (fitzpatrickAction) { 230 | case PARSE, REMOVE -> unicodeCandidate.getEmoji().getHtmlDecimal(); 231 | case IGNORE -> unicodeCandidate.getEmoji().getHtmlDecimal() + 232 | unicodeCandidate.getFitzpatrickUnicode(); 233 | }; 234 | } 235 | }; 236 | 237 | return parseFromUnicode(input, emojiTransformer); 238 | } 239 | 240 | /** 241 | * See {@link #parseToHtmlHexadecimal(String, FitzpatrickAction)} with the 242 | * action "PARSE" 243 | * 244 | * @param input the string to parse 245 | * @return the string with the emojis replaced by their html hex 246 | * representation. 247 | */ 248 | public static String parseToHtmlHexadecimal(String input) { 249 | return parseToHtmlHexadecimal(input, FitzpatrickAction.PARSE); 250 | } 251 | 252 | /** 253 | * Replaces the emoji's unicode occurrences by their html hex 254 | * representation.
255 | * Example: 👦 will be replaced by &#x1f466;
256 | *
257 | * When a fitzpatrick modifier is present with a PARSE or REMOVE action, the 258 | * modifier will be deleted.
259 | * Example: 👦🏿 will be replaced by 260 | * &#x1f466;
261 | *
262 | * When a fitzpatrick modifier is present with a IGNORE action, the modifier 263 | * will be ignored and will remain in the string.
264 | * Example: 👦🏿 will be replaced by 265 | * &#x1f466;🏿 266 | * 267 | * @param input the string to parse 268 | * @param fitzpatrickAction the action to apply for the fitzpatrick modifiers 269 | * @return the string with the emojis replaced by their html hex 270 | * representation. 271 | */ 272 | public static String parseToHtmlHexadecimal( 273 | String input, 274 | final FitzpatrickAction fitzpatrickAction 275 | ) { 276 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 277 | public String transform(EmojiResult unicodeCandidate) { 278 | return switch (fitzpatrickAction) { 279 | case PARSE, REMOVE -> unicodeCandidate.getEmoji().getHtmlHexadecimal(); 280 | case IGNORE -> unicodeCandidate.getEmoji().getHtmlHexadecimal() + 281 | unicodeCandidate.getFitzpatrickUnicode(); 282 | }; 283 | } 284 | }; 285 | 286 | return parseFromUnicode(input, emojiTransformer); 287 | } 288 | 289 | /** 290 | * Removes all emojis from a String 291 | * 292 | * @param str the string to process 293 | * @return the string without any emoji 294 | */ 295 | public static String removeAllEmojis(String str) { 296 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 297 | public String transform(EmojiResult unicodeCandidate) { 298 | return ""; 299 | } 300 | }; 301 | 302 | return parseFromUnicode(str, emojiTransformer); 303 | } 304 | 305 | 306 | /** 307 | * Removes a set of emojis from a String 308 | * 309 | * @param str the string to process 310 | * @param emojisToRemove the emojis to remove from this string 311 | * @return the string without the emojis that were removed 312 | */ 313 | public static String removeEmojis( 314 | String str, 315 | final Collection emojisToRemove 316 | ) { 317 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 318 | public String transform(EmojiResult unicodeCandidate) { 319 | if (!emojisToRemove.contains(unicodeCandidate.getEmoji())) { 320 | return unicodeCandidate.getEmoji().getUnicode() + 321 | unicodeCandidate.getFitzpatrickUnicode(); 322 | } 323 | return ""; 324 | } 325 | }; 326 | 327 | return parseFromUnicode(str, emojiTransformer); 328 | } 329 | 330 | /** 331 | * Removes all the emojis in a String except a provided set 332 | * 333 | * @param str the string to process 334 | * @param emojisToKeep the emojis to keep in this string 335 | * @return the string without the emojis that were removed 336 | */ 337 | public static String removeAllEmojisExcept( 338 | String str, 339 | final Collection emojisToKeep 340 | ) { 341 | EmojiTransformer emojiTransformer = new EmojiTransformer() { 342 | public String transform(EmojiResult unicodeCandidate) { 343 | if (emojisToKeep.contains(unicodeCandidate.getEmoji())) { 344 | return unicodeCandidate.getEmoji().getUnicode() + 345 | unicodeCandidate.getFitzpatrickUnicode(); 346 | } 347 | return ""; 348 | } 349 | }; 350 | 351 | return parseFromUnicode(str, emojiTransformer); 352 | } 353 | 354 | 355 | /** 356 | * Detects all unicode emojis in input string and replaces them with the 357 | * return value of transformer.transform() 358 | * 359 | * @param input the string to process 360 | * @param transformer emoji transformer to apply to each emoji 361 | * @return input string with all emojis transformed 362 | */ 363 | public static String parseFromUnicode(String input, EmojiTransformer transformer) { 364 | int prev = 0; 365 | StringBuilder sb = new StringBuilder(input.length()); 366 | List replacements = getEmojis(input); 367 | for (EmojiResult candidate : replacements) { 368 | sb.append(input, prev, candidate.startIndex); 369 | 370 | sb.append(transformer.transform(candidate)); 371 | prev = candidate.endIndex; 372 | } 373 | 374 | return sb.append(input.substring(prev)).toString(); 375 | } 376 | 377 | public static List extractEmojiStrings(String input) { 378 | return extractEmojiStrings(input, 0); 379 | } 380 | 381 | public static List extractEmojiStrings(String input, int limit) { 382 | var items = extractEmojis(input, limit); 383 | List result = new ArrayList<>(items.size()); 384 | for (EmojiResult i : items) { 385 | result.add(i.toString()); 386 | } 387 | return result; 388 | } 389 | 390 | public static List extractEmojis(String input) { 391 | return getEmojis(input, 0); 392 | } 393 | 394 | public static List extractEmojis(String input, int limit) { 395 | return getEmojis(input, limit); 396 | } 397 | 398 | /** 399 | * Generates a list UnicodeCandidates found in input string. A 400 | * UnicodeCandidate is created for every unicode emoticon found in input 401 | * string, additionally if Fitzpatrick modifier follows the emoji, it is 402 | * included in UnicodeCandidate. Finally, it contains start and end index of 403 | * unicode emoji itself (WITHOUT Fitzpatrick modifier whether it is there or 404 | * not!). 405 | * 406 | * @param input String to find all unicode emojis in 407 | * @return List of UnicodeCandidates for each unicode emote in text 408 | */ 409 | public static List getEmojis(String input, int limit) { 410 | char[] inputCharArray = input.toCharArray(); 411 | List candidates = new ArrayList<>(); 412 | EmojiResult next; 413 | for (int i = 0; (next = getNextEmoji(inputCharArray, i)) != null; i = next.endIndex) { 414 | candidates.add(next); 415 | if (limit != 0) { 416 | limit--; 417 | if (limit <= 0) 418 | break; 419 | } 420 | } 421 | 422 | return candidates; 423 | } 424 | 425 | public static List getEmojis(String input) { 426 | return getEmojis(input, 0); 427 | } 428 | 429 | /** 430 | * Finds the next UnicodeCandidate after a given starting index 431 | * 432 | * @param chars char array to find UnicodeCandidate in 433 | * @param start starting index for search 434 | * @return the next UnicodeCandidate or null if no UnicodeCandidate is found after start index 435 | */ 436 | public static EmojiResult getNextEmoji(char[] chars, int start) { 437 | for (int i = start; i < chars.length; i++) { 438 | /*var ch = chars[i]; 439 | if (Character.isAlphabetic(ch)) 440 | continue;*/ 441 | var emoji = getEmojiInPosition(chars, i); 442 | if (emoji != null) 443 | return emoji; 444 | } 445 | 446 | return null; 447 | } 448 | 449 | public static EmojiResult getEmojiInPosition(char[] chars, int start) { 450 | var emoji = getBestBaseEmoji(chars, start); 451 | if (emoji == null) 452 | return null; 453 | 454 | Fitzpatrick fitzpatrick = null; 455 | Gender gender = null; 456 | var endPos = start + emoji.unicode.length(); 457 | var sequenceType = emoji.sequenceType; 458 | if (sequenceType == Emoji.SEQUENCE_BASE_SKIN_GENDER) { 459 | fitzpatrick = Fitzpatrick.find(chars, endPos); 460 | if (fitzpatrick != null) { 461 | endPos += 2; 462 | } 463 | GenderMatch gg = findGender(chars, endPos); 464 | if (gg != null) { 465 | endPos = gg.endPos; 466 | gender = gg.gender; 467 | } 468 | } else if (sequenceType == Emoji.SEQUENCE_GENDER_SKIN_BASE) { 469 | 470 | Gender gg = findGender2(emoji.unicode); 471 | if (gg != null) { 472 | //endPos += emoji.unicode.length(); 473 | gender = gg; 474 | } 475 | 476 | fitzpatrick = Fitzpatrick.find(chars, endPos); 477 | if (fitzpatrick != null) { 478 | endPos += 2; 479 | } 480 | 481 | var baseEmoji = tryGetBestBaseEmoji(chars, endPos); 482 | if (baseEmoji != null) { 483 | endPos += baseEmoji.unicode.length() + 1; // +1 because \u200D 484 | emoji = baseEmoji; 485 | } 486 | } 487 | 488 | if (chars.length > endPos) { 489 | var ch = chars[endPos]; 490 | if (ch == '\uFE0F') 491 | endPos++; 492 | } 493 | return new EmojiResult(emoji, fitzpatrick, gender, chars, start, endPos); 494 | } 495 | 496 | private static Emoji tryGetBestBaseEmoji(char[] chars, int startPos) { 497 | var len = chars.length; 498 | if (startPos >= len) 499 | return null; 500 | if (chars[startPos] != '\u200D') 501 | return null; 502 | startPos++; 503 | return getBestBaseEmoji(chars, startPos); 504 | } 505 | 506 | private static GenderMatch findGender(char[] chars, int startPos) { 507 | var len = chars.length; 508 | if (len <= startPos) 509 | return null; 510 | var pos = startPos; 511 | var ch = chars[pos]; 512 | if (ch != '\u200D') 513 | return null; 514 | pos++; 515 | var gender = Gender.find(chars, pos); 516 | if (gender == null) 517 | return null; 518 | return new GenderMatch(gender, pos + 1); 519 | } 520 | 521 | private static Gender findGender2(String emoji) { 522 | return Gender.find2(emoji); 523 | } 524 | 525 | private static class GenderMatch { 526 | Gender gender; 527 | int endPos; 528 | 529 | public GenderMatch(Gender gender, int endPos) { 530 | this.gender = gender; 531 | this.endPos = endPos; 532 | } 533 | } 534 | 535 | 536 | /** 537 | * Returns end index of a unicode emoji if it is found in text starting at 538 | * index startPos, -1 if not found. 539 | * This returns the longest matching emoji, for example, in 540 | * "\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66" 541 | * it will find alias:family_man_woman_boy, NOT alias:man 542 | * 543 | * @param text the current text where we are looking for an emoji 544 | * @param startPos the position in the text where we should start looking for 545 | * an emoji end 546 | * @return the end index of the unicode emoji starting at startPos. -1 if not 547 | * found 548 | */ 549 | public static Emoji getBestBaseEmoji(char[] text, int startPos) { 550 | return EmojiManager.EMOJI_TRIE.getBestEmoji(text, startPos); 551 | } 552 | 553 | 554 | public static class EmojiResult { 555 | 556 | public final Emoji emoji; 557 | 558 | public final Fitzpatrick fitzpatrick; 559 | 560 | public final Gender gender; 561 | 562 | public final char[] source; 563 | 564 | public final int startIndex; 565 | 566 | public final int endIndex; 567 | 568 | public EmojiResult(Emoji emoji, Fitzpatrick fitzpatrick, Gender gender, char[] source, int startIndex, int endIndex) { 569 | this.emoji = emoji; 570 | this.fitzpatrick = fitzpatrick; 571 | this.gender = gender; 572 | this.source = source; 573 | this.startIndex = startIndex; 574 | this.endIndex = endIndex; 575 | } 576 | 577 | 578 | public Emoji getEmoji() { 579 | return emoji; 580 | } 581 | 582 | public boolean hasFitzpatrick() { 583 | return getFitzpatrick() != null; 584 | } 585 | 586 | public Fitzpatrick getFitzpatrick() { 587 | return fitzpatrick; 588 | } 589 | 590 | public String getFitzpatrickType() { 591 | return hasFitzpatrick() ? fitzpatrick.name() : ""; 592 | } 593 | 594 | public String getFitzpatrickUnicode() { 595 | return hasFitzpatrick() ? fitzpatrick.unicode : ""; 596 | } 597 | 598 | public int getEmojiStartIndex() { 599 | return startIndex; 600 | } 601 | 602 | public int getEmojiEndIndex() { 603 | return startIndex + emoji.getUnicode().length(); 604 | } 605 | 606 | public int getFitzpatrickEndIndex() { 607 | return getEmojiEndIndex() + (fitzpatrick != null ? 2 : 0); 608 | } 609 | 610 | private String sub = null; 611 | 612 | @Override 613 | public String toString() { 614 | if (sub != null) 615 | return sub; 616 | var len = endIndex - startIndex; 617 | char[] sub = new char[len]; 618 | 619 | System.arraycopy(source, startIndex, sub, 0, len); 620 | this.sub = new String(sub); 621 | return this.sub; 622 | } 623 | } 624 | 625 | 626 | protected static class AliasCandidate { 627 | public final Emoji emoji; 628 | public final Fitzpatrick fitzpatrick; 629 | public final int startIndex; 630 | public final int endIndex; 631 | 632 | private AliasCandidate(Emoji emoji, Fitzpatrick fitzpatrick, int startIndex, int endIndex) { 633 | this.emoji = emoji; 634 | this.fitzpatrick = fitzpatrick; 635 | this.startIndex = startIndex; 636 | this.endIndex = endIndex; 637 | } 638 | } 639 | 640 | /** 641 | * Enum used to indicate what should be done when a Fitzpatrick modifier is 642 | * found. 643 | */ 644 | public enum FitzpatrickAction { 645 | /** 646 | * Tries to match the Fitzpatrick modifier with the previous emoji 647 | */ 648 | PARSE, 649 | 650 | /** 651 | * Removes the Fitzpatrick modifier from the string 652 | */ 653 | REMOVE, 654 | 655 | /** 656 | * Ignores the Fitzpatrick modifier (it will stay in the string) 657 | */ 658 | IGNORE 659 | } 660 | 661 | public interface EmojiTransformer { 662 | String transform(EmojiResult unicodeCandidate); 663 | } 664 | 665 | public static void main(String[] args) { 666 | //val text = "ergerge\uD83D\uDC68\u200D\uD83C\uDFEB\uD83D\uDC69\u200D⚖\uD83D\uDC69\u200D✈\uD83D\uDC69\u200D\uD83C\uDFA8\uD83D\uDC68\u200D\uD83D\uDD27\uD83D\uDC68\u200D\uD83C\uDF93\uD83D\uDC69\u200D\uD83C\uDFEB\uD83D\uDC76\uD83D\uDC69\u200D❤\u200D\uD83D\uDC69\uD83D\uDC76\uD83D\uDC67\uD83D\uDE4D\u200D♀\uD83D\uDC41\u200D\uD83D\uDDE8\uD83D\uDC41\u200D\uD83D\uDDE8\uD83D\uDD0A\uD83D\uDCE3\uD83D\uDCE3\uD83E\uDDB9\u200D♂\uD83E\uDDB9\u200D♂\uD83E\uDDDE\u200D♂\uD83E\uDDDE\u200D♀\uD83E\uDDB9\u200D♂\uD83E\uDDDC\u200D♂\uD83D\uDC69\u200D\uD83C\uDF93\uD83D\uDC69\u200D\uD83C\uDF93\uD83D\uDD75\u200D♂\uD83E\uDD35\uD83E\uDD30\uD83E\uDD30\uD83D\uDC9F\uD83D\uDC9F\uD83D\uDC9B\uD83D\uDC99\uD83D\uDDA4\uD83E\uDD9A\uD83E\uDD95\uD83D\uDC32\uD83E\uDD8E\uD83E\uDD8E\uD83C\uDF1C⛈\uD83C\uDF20⛅⛅\uD83C\uDF02\uD83C\uDF25\uD83D\uDC75\uD83D\uDC75\uD83D\uDC69\u200D\uD83E\uDDB2\uD83D\uDC69\u200D\uD83E\uDDB2\uD83E\uDD2A\uD83D\uDE19\uD83D\uDE18\uD83D\uDE1A\uD83D\uDC74\uD83D\uDC69\u200D\uD83E\uDDB2\uD83D\uDC68\u200D\uD83E\uDDB3\uD83D\uDC68\u200D\uD83C\uDFED\uD83D\uDC69\u200D\uD83D\uDCBC\uD83D\uDC68\u200D\uD83D\uDCBC\uD83D\uDC69\u200D\uD83C\uDFEB\uD83D\uDC69\u200D\uD83D\uDCBC\uD83D\uDC69\u200D\uD83C\uDFA8\uD83D\uDC68\u200D\uD83C\uDFA4\uD83D\uDC6E\u200D♂\uD83D\uDD75\u200D♂\uD83D\uDD75\u200D♀\uD83D\uDC82\u200D♂\uD83D\uDC82\u200D♀\uD83D\uDC77\u200D♂\uD83D\uDC77\u200D♂\uD83D\uDC73\u200D♂\uD83D\uDC78" 667 | //var text = " " 668 | /*"ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 669 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 670 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 671 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 672 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп "*/ 673 | 674 | /*+ "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 675 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 676 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп " 677 | + "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп "*/ 678 | // ; 679 | //testNew(text); 680 | /*var text = "ergerge\uD83D\uDC68\u200D\uD83C\uDFEB\uD83D\uDC69\u200D⚖\uD83D\uDC69\u200D✈\uD83D\uDC69\u200D\uD83C\uDFA8\uD83D\uDC68\u200D\uD83D\uDD27\uD83D\uDC68\u200D\uD83C\uDF93\uD83D\uDC69\u200D\uD83C\uDFEB\uD83D\uDC76\uD83D\uDC69\u200D❤\u200D\uD83D\uDC69\uD83D\uDC76\uD83D\uDC67\uD83D\uDE4D\u200D♀\uD83D\uDC41\u200D\uD83D\uDDE8\uD83D\uDC41\u200D\uD83D\uDDE8\uD83D\uDD0A\uD83D\uDCE3\uD83D\uDCE3\uD83E\uDDB9\u200D♂\uD83E\uDDB9\u200D♂\uD83E\uDDDE\u200D♂\uD83E\uDDDE\u200D♀\uD83E\uDDB9\u200D♂\uD83E\uDDDC\u200D♂\uD83D\uDC69\u200D\uD83C\uDF93\uD83D\uDC69\u200D\uD83C\uDF93\uD83D\uDD75\u200D♂\uD83E\uDD35\uD83E\uDD30\uD83E\uDD30\uD83D\uDC9F\uD83D\uDC9F\uD83D\uDC9B\uD83D\uDC99\uD83D\uDDA4\uD83E\uDD9A\uD83E\uDD95\uD83D\uDC32\uD83E\uDD8E\uD83E\uDD8E\uD83C\uDF1C⛈\uD83C\uDF20⛅⛅\uD83C\uDF02\uD83C\uDF25\uD83D\uDC75\uD83D\uDC75\uD83D\uDC69\u200D\uD83E\uDDB2\uD83D\uDC69\u200D\uD83E\uDDB2\uD83E\uDD2A\uD83D\uDE19\uD83D\uDE18\uD83D\uDE1A\uD83D\uDC74\uD83D\uDC69\u200D\uD83E\uDDB2\uD83D\uDC68\u200D\uD83E\uDDB3\uD83D\uDC68\u200D\uD83C\uDFED\uD83D\uDC69\u200D\uD83D\uDCBC\uD83D\uDC68\u200D\uD83D\uDCBC\uD83D\uDC69\u200D\uD83C\uDFEB\uD83D\uDC69\u200D\uD83D\uDCBC\uD83D\uDC69\u200D\uD83C\uDFA8\uD83D\uDC68\u200D\uD83C\uDFA4\uD83D\uDC6E\u200D♂\uD83D\uDD75\u200D♂\uD83D\uDD75\u200D♀\uD83D\uDC82\u200D♂\uD83D\uDC82\u200D♀\uD83D\uDC77\u200D♂\uD83D\uDC77\u200D♂\uD83D\uDC73\u200D♂\uD83D\uDC78" 681 | + "ergerge\uD83D\uDC68\u200D\uD83C\uDFEB\uD83D\uDC69\u200D⚖\uD83D\uDC69\u200D✈\uD83D\uDC69\u200D\uD83C\uDFA8\uD83D\uDC68\u200D\uD83D\uDD27\uD83D\uDC68\u200D\uD83C\uDF93\uD83D\uDC69\u200D\uD83C\uDFEB\uD83D\uDC76\uD83D\uDC69\u200D❤\u200D\uD83D\uDC69\uD83D\uDC76\uD83D\uDC67\uD83D\uDE4D\u200D♀\uD83D\uDC41\u200D\uD83D\uDDE8\uD83D\uDC41\u200D\uD83D\uDDE8\uD83D\uDD0A\uD83D\uDCE3\uD83D\uDCE3\uD83E\uDDB9\u200D♂\uD83E\uDDB9\u200D♂\uD83E\uDDDE\u200D♂\uD83E\uDDDE\u200D♀\uD83E\uDDB9\u200D♂\uD83E\uDDDC\u200D♂\uD83D\uDC69\u200D\uD83C\uDF93\uD83D\uDC69\u200D\uD83C\uDF93\uD83D\uDD75\u200D♂\uD83E\uDD35\uD83E\uDD30\uD83E\uDD30\uD83D\uDC9F\uD83D\uDC9F\uD83D\uDC9B\uD83D\uDC99\uD83D\uDDA4\uD83E\uDD9A\uD83E\uDD95\uD83D\uDC32\uD83E\uDD8E\uD83E\uDD8E\uD83C\uDF1C⛈\uD83C\uDF20⛅⛅\uD83C\uDF02\uD83C\uDF25\uD83D\uDC75\uD83D\uDC75\uD83D\uDC69\u200D\uD83E\uDDB2\uD83D\uDC69\u200D\uD83E\uDDB2\uD83E\uDD2A\uD83D\uDE19\uD83D\uDE18\uD83D\uDE1A\uD83D\uDC74\uD83D\uDC69\u200D\uD83E\uDDB2\uD83D\uDC68\u200D\uD83E\uDDB3\uD83D\uDC68\u200D\uD83C\uDFED\uD83D\uDC69\u200D\uD83D\uDCBC\uD83D\uDC68\u200D\uD83D\uDCBC\uD83D\uDC69\u200D\uD83C\uDFEB\uD83D\uDC69\u200D\uD83D\uDCBC\uD83D\uDC69\u200D\uD83C\uDFA8\uD83D\uDC68\u200D\uD83C\uDFA4\uD83D\uDC6E\u200D♂\uD83D\uDD75\u200D♂\uD83D\uDD75\u200D♀\uD83D\uDC82\u200D♂\uD83D\uDC82\u200D♀\uD83D\uDC77\u200D♂\uD83D\uDC77\u200D♂\uD83D\uDC73\u200D♂\uD83D\uDC78";*/ 682 | //var text = "ук пуп уп у у пупыуп уы\uD83D\uDC73\u200D♀️пыу пыу\uD83D\uDC73\u200D♂️п кыуп уы пу у\uD83D\uDE0Bп \uD83E\uDD2Fуы\uD83E\uDD71пыукп купы ук ыу пыукп \uD83D\uDE2Cуп укп ук пукы пуы п\uD83D\uDE35укп ыук пуык пу пу\uD83D\uDE37ып уы пуы пуы пуы\uD83D\uDE38 у\uD83D\uDE3Eкп ыуп у пкуы пуы пыу\uD83E\uDD1E\uD83C\uDFFD \uD83D\uDC48уыпуып уып упк ыукп ук\uD83D\uDC7Fп \uD83D\uDC67\uD83C\uDFFCук пу уп \uD83D\uDC71\uD83C\uDFFE\u200D♂️уп уы укп уыкп у\uD83D\uDC74\uD83C\uDFFCп уыкп упу уы \uD83E\uDDD1\uD83C\uDFFD\u200D⚕️пу кпуы ук \uD83D\uDC68\uD83C\uDFFF\u200D\uD83C\uDFA4уы \uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF93ку пукп ыу уп ук \uD83D\uDC6E\u200D♀️ыу пуы уы пуы \uD83D\uDC6E\u200D♀️п уы\uD83D\uDC68\uD83C\uDFFC\u200D\uD83E\uDDB3пуы ппыу уы ыу \uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDDB2уп пу укп "; 683 | //testNew(text); 684 | /*var items = EmojiParser.extractEmojiStrings(text); 685 | System.out.println(items);*/ 686 | 687 | 688 | var text = "\uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDFA8\uD83D\uDC69\uD83C\uDFFB\u200D\uD83D\uDE92\uD83E\uDD34\uD83C\uDFFB\uD83D\uDC68\uD83C\uDFFB\u200D⚖\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83D\uDCBB\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83D\uDCBC\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83C\uDFA4\uD83D\uDC69\uD83C\uDFFB\u200D\uD83C\uDFED\uD83D\uDC68\uD83C\uDFFB\u200D\uD83C\uDF73\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83C\uDF3E\uD83D\uDC82\uD83C\uDFFB\u200D♂\uD83D\uDC82\uD83C\uDFFB\uD83D\uDC82\uD83C\uDFFB\u200D♀\uD83D\uDD75\uD83C\uDFFB\uD83D\uDC77\uD83C\uDFFB\u200D♂\uD83D\uDC77\uD83C\uDFFB\uD83D\uDC6E\uD83C\uDFFB\u200D♂\uD83E\uDDD5\uD83C\uDFFB\uD83E\uDDD5\uD83C\uDFFB\uD83D\uDC68\uD83C\uDFFF\u200D\uD83E\uDDB3\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDDB0"; 689 | 690 | System.out.println(EmojiParser.extractEmojiStrings(text)); 691 | 692 | } 693 | 694 | private static void testNew(String text) { 695 | for (int i = 0; i < 1000; i++) 696 | EmojiParser.extractEmojis(text); 697 | var start = System.currentTimeMillis(); 698 | for (int i = 0; i < 1_000_000; i++) 699 | EmojiParser.extractEmojis(text); 700 | var end = System.currentTimeMillis(); 701 | System.out.println("Diff = " + (end - start)); 702 | } 703 | } 704 | -------------------------------------------------------------------------------- /EMOJIS.md: -------------------------------------------------------------------------------- 1 | ## Available Emojis 2 | 3 | Here is a table of the available emojis and their aliases. 4 | 5 | | Emoji | Aliases | Emoji | Aliases | 6 | | :---: | ------- | :---: | ------- | 7 | | 🏴󠁧󠁢󠁥󠁮󠁧󠁿 | gbeng | 🏴󠁧󠁢󠁳󠁣󠁴󠁿 | gbsct | 8 | | 🏴󠁧󠁢󠁷󠁬󠁳󠁿 | gbwls | 🏴󠁵󠁳󠁴󠁸󠁿 | ustx | 9 | | 👨‍👩‍👦‍👦 | family_man_woman_boy_boy | 👨‍👩‍👧‍👧 | family_man_woman_girl_girl | 10 | | 👨‍👩‍👧‍👦 | family_man_woman_girl_boy | 👩‍👩‍👧‍👦 | family_woman_woman_girl_boy | 11 | | 👩‍👩‍👦‍👦 | family_woman_woman_boy_boy | 👩‍👩‍👧‍👧 | family_woman_woman_girl_girl | 12 | | 👨‍👨‍👧‍👦 | family_man_man_girl_boy | 👨‍👨‍👦‍👦 | family_man_man_boy_boy | 13 | | 👨‍👨‍👧‍👧 | family_man_man_girl_girl | 👩‍❤️‍💋‍👩 | couplekiss_woman_woman | 14 | | 👨‍❤️‍💋‍👨 | couplekiss_man_man | 👨‍👩‍👦 | family_man_woman_boy | 15 | | 👨‍👩‍👧 | family_man_woman_girl | 👩‍👩‍👦 | family_woman_woman_boy | 16 | | 👩‍👩‍👧 | family_woman_woman_girl | 👨‍👨‍👦 | family_man_man_boy | 17 | | 👨‍👨‍👧 | family_man_man_girl | 👩‍❤️‍👩 | couple_with_heart_woman_woman | 18 | | 👨‍❤️‍👨 | couple_with_heart_man_man | 🏳️‍🌈 | rainbow_flag, pride_flag | 19 | | 🏌️‍♂️ | man_golfer, male_golfer, man_golfing, male_golfing | 🏌️‍♀️ | woman_golfer, female_golfer, woman_golfing, female_golfing | 20 | | ♾🏴‍☠️ | pirate_flag, jolly_roger | 🙇‍♀️ | woman_bow, female_bow | 21 | | 🙇‍♂️ | man_bow, male_bow | 👁‍🗨 | eye_in_speech_bubble, i_am_a_witness | 22 | | 🤸‍♂️ | man_doing_cartwheel, male_doing_cartwheel | 🤸‍♀️ | woman_doing_cartwheel, female_doing_cartwheel | 23 | | 🤼‍♂️ | man_wrestlers, male_wrestlers | 🤼‍♀️ | woman_wrestlers, female_wrestlers | 24 | | 🤽‍♂️ | man_water_polo, male_water_polo | 🤽‍♀️ | woman_water_polo, female_water_polo | 25 | | 🤾‍♂️ | man_handball, male_handball | 🤾‍♀️ | woman_handball, female_handball | 26 | | 🤹‍♂️ | man_juggling, male_juggling | 🤹‍♀️ | woman_juggling, female_juggling | 27 | | 👨‍⚕️ | male_health_worker, man_health_worker | 👩‍⚕️ | female_health_worker, woman_health_worker | 28 | | 👨‍🎓 | male_student, man_student | 👩‍🎓 | female_student, woman_student | 29 | | 👨‍🏫 | male_teacher, man_teacher | 👩‍🏫 | female_teacher, woman_teacher | 30 | | 👨‍🌾 | male_farmer, man_farmer | 👩‍🌾 | female_farmer, woman_farmer | 31 | | 👨‍🍳 | male_cook, man_cook | 👩‍🍳 | female_cook, woman_cook | 32 | | 👨‍🔧 | male_mechanic, man_mechanic | 👩‍🔧 | female_mechanic, woman_mechanic | 33 | | 👨‍🏭 | male_factory_worker, man_factory_worker | 👩‍🏭 | female_factory_worker, woman_factory_worker | 34 | | 👨‍💼 | male_office_worker, man_office_worker | 👩‍💼 | female_office_worker, woman_office_worker | 35 | | 👨‍🔬 | male_scientist, man_scientist | 👩‍🔬 | female_scientist, woman_scientist | 36 | | 👨‍💻 | male_technologist, man_technologist | 👩‍💻 | female_technologist, woman_technologist | 37 | | 👨‍🎤 | male_singer, man_singer | 👩‍🎤 | female_singer, woman_singer | 38 | | 👨‍🎨 | male_artist, man_artist | 👩‍🎨 | female_artist, woman_artist | 39 | | 👨‍✈️ | male_pilot, man_pilot | 👩‍✈️ | female_pilot, woman_pilot | 40 | | 👨‍🚀 | male_astronaut, man_astronaut | 👩‍🚀 | female_astronaut, woman_astronaut | 41 | | 👨‍🚒 | male_firefighter, man_firefighter | 👩‍🚒 | female_firefighter, woman_firefighter | 42 | | 🤦‍♀️ | female_facepalm, woman_facepalm | 🤷‍♂️ | male_shrug, man_shrug | 43 | | 🤷‍♀️ | female_shrug, woman_shrug | 👨‍⚖️ | man_judge, male_judge | 44 | | 👩‍⚖️ | woman_judge, female_judge | 🧙‍♂️ | man_mage, wizard, sorcerer | 45 | | 🧙‍♀️ | woman_mage, witch, sorceress | 🧚‍♂️ | man_fairy | 46 | | 🧚‍♀️ | woman_fairy | 🧛‍♂️ | man_vampire, dracula | 47 | | 🧛‍♀️ | woman_vampire | 🧜‍♂️ | merman, merboy, man_merperson | 48 | | 🧜‍♀️ | mermaid, mergirl, woman_merperson | 🧝‍♂️ | man_elf, legolas | 49 | | 🧝‍♀️ | woman_elf | 🧞‍♂️ | man_genie | 50 | | 🧞‍♀️ | woman_genie | 🧟‍♂️ | man_zombie | 51 | | 🧟‍♀️ | woman_zombie | 🧖‍♂️ | man_in_steamy_room, man_in_sauna | 52 | | 🧖‍♀️ | woman_in_steamy_room, woman_in_sauna | 🧗‍♂️ | man_climbing, man_climber, man_rock_climbing | 53 | | 🧗‍♀️ | woman_climbing, woman_climber, woman_rock_climbing | 🧘‍♂️ | man_in_lotus_position, man_yoga, man_meditation | 54 | | 🧘‍♀️ | woman_in_lotus_position, woman_yoga, woman_meditation | 👨‍🦰 | man_with_red_hair, man_redhead, man_ginger | 55 | | 👩‍🦰 | woman_with_red_hair, woman_redhead, woman_ginger | 👨‍🦱 | man_with_curly_hair | 56 | | 👩‍🦱 | woman_with_curly_hair | 👨‍🦳 | man_with_white_hair, man_with_gray_hair, man_with_grey_hair | 57 | | 👩‍🦳 | woman_with_white_hair, woman_with_gray_hair, woman_with_grey_hair | 👨‍🦲 | man_with_no_hair, bald_man | 58 | | 👩‍🦲 | woman_with_no_hair, bald_woman | 🦸‍♂️ | man_superhero | 59 | | 🦸‍♀️ | woman_superhero | 🦹‍♂️ | man_supervillain | 60 | | 🦹‍♀️ | woman_supervillain | 🇦🇨 | ac | 61 | | 🇦🇩 | ad | 🇦🇪 | ae | 62 | | 🇦🇫 | af | 🇦🇬 | ag | 63 | | 🇦🇮 | ai | 🇦🇱 | al | 64 | | 🇦🇲 | am | 🇦🇴 | ao | 65 | | 🇦🇶 | aq | 🇦🇷 | ar | 66 | | 🇦🇸 | as | 🇦🇹 | at | 67 | | 🇦🇺 | au | 🇦🇼 | aw | 68 | | 🇦🇽 | ax | 🇦🇿 | az | 69 | | 🇧🇦 | ba | 🇧🇧 | bb | 70 | | 🇧🇩 | bd | 🇧🇪 | be | 71 | | 🇧🇫 | bf | 🇧🇬 | bg | 72 | | 🇧🇭 | bh | 🇧🇮 | bi | 73 | | 🇧🇯 | bj | 🇧🇱 | bl | 74 | | 🇧🇲 | bm | 🇧🇳 | bn | 75 | | 🇧🇴 | bo | 🇧🇶 | bq | 76 | | 🇧🇷 | br | 🇧🇸 | bs | 77 | | 🇧🇹 | bt | 🇧🇻 | bv | 78 | | 🇧🇼 | bw | 🇧🇾 | by | 79 | | 🇧🇿 | bz | 🇨🇦 | ca | 80 | | 🇨🇨 | cc | 🇨🇩 | cd_flag | 81 | | 🇨🇫 | cf | 🇨🇬 | cg | 82 | | 🇨🇭 | ch | 🇨🇮 | ci | 83 | | 🇨🇰 | ck | 🇨🇱 | cl_flag | 84 | | 🇨🇲 | cm | 🇨🇳 | cn | 85 | | 🇨🇴 | co | 🇨🇵 | cp | 86 | | 🇨🇷 | cr | 🇨🇺 | cu | 87 | | 🇨🇻 | cv | 🇨🇼 | cw | 88 | | 🇨🇽 | cx | 🇨🇾 | cy | 89 | | 🇨🇿 | cz | 🇩🇪 | de | 90 | | 🇩🇬 | dg | 🇩🇯 | dj | 91 | | 🇩🇰 | dk | 🇩🇲 | dm | 92 | | 🇩🇴 | do | 🇩🇿 | dz | 93 | | 🇪🇦 | ea | 🇪🇨 | ec | 94 | | 🇪🇪 | ee | 🇪🇬 | eg | 95 | | 🇪🇭 | eh | 🇪🇷 | er | 96 | | 🇪🇸 | es | 🇪🇹 | et | 97 | | 🇪🇺 | eu | 🇫🇮 | fi | 98 | | 🇫🇯 | fj | 🇫🇰 | fk | 99 | | 🇫🇲 | fm | 🇫🇴 | fo | 100 | | 🇫🇷 | fr | 🇬🇦 | ga | 101 | | 🇬🇧 | gb | 🇬🇩 | gd | 102 | | 🇬🇪 | ge | 🇬🇫 | gf | 103 | | 🇬🇬 | gg | 🇬🇭 | gh | 104 | | 🇬🇮 | gi | 🇬🇱 | gl | 105 | | 🇬🇲 | gm | 🇬🇳 | gn | 106 | | 🇬🇵 | gp | 🇬🇶 | gq | 107 | | 🇬🇷 | gr | 🇬🇸 | gs | 108 | | 🇬🇹 | gt | 🇬🇺 | gu | 109 | | 🇬🇼 | gw | 🇬🇾 | gy | 110 | | 🇭🇰 | hk | 🇭🇲 | hm | 111 | | 🇭🇳 | hn | 🇭🇷 | hr | 112 | | 🇭🇹 | ht | 🇭🇺 | hu | 113 | | 🇮🇨 | ic | 🇮🇩 | id_flag | 114 | | 🇮🇪 | ie | 🇮🇱 | il | 115 | | 🇮🇲 | im | 🇮🇳 | in | 116 | | 🇮🇴 | io | 🇮🇶 | iq | 117 | | 🇮🇷 | ir | 🇮🇸 | is | 118 | | 🇮🇹 | it | 🇯🇪 | je | 119 | | 🇯🇲 | jm | 🇯🇴 | jo | 120 | | 🇯🇵 | jp | 🇰🇪 | ke | 121 | | 🇰🇬 | kg | 🇰🇭 | kh | 122 | | 🇰🇮 | ki | 🇰🇲 | km | 123 | | 🇰🇳 | kn | 🇰🇵 | kp | 124 | | 🇰🇷 | kr | 🇰🇼 | kw | 125 | | 🇰🇾 | ky | 🇰🇿 | kz | 126 | | 🇱🇦 | la | 🇱🇧 | lb | 127 | | 🇱🇨 | lc | 🇱🇮 | li | 128 | | 🇱🇰 | lk | 🇱🇷 | lr | 129 | | 🇱🇸 | ls | 🇱🇹 | lt | 130 | | 🇱🇺 | lu | 🇱🇻 | lv | 131 | | 🇱🇾 | ly | 🇲🇦 | ma | 132 | | 🇲🇨 | mc | 🇲🇩 | md | 133 | | 🇲🇪 | me | 🇲🇫 | mf | 134 | | 🇲🇬 | mg | 🇲🇭 | mh | 135 | | 🇲🇰 | mk | 🇲🇱 | ml | 136 | | 🇲🇲 | mm | 🇲🇳 | mn | 137 | | 🇲🇴 | mo | 🇲🇵 | mp | 138 | | 🇲🇶 | mq | 🇲🇷 | mr | 139 | | 🇲🇸 | ms | 🇲🇹 | mt | 140 | | 🇲🇺 | mu | 🇲🇻 | mv | 141 | | 🇲🇼 | mw | 🇲🇽 | mx | 142 | | 🇲🇾 | my | 🇲🇿 | mz | 143 | | 🇳🇦 | na | 🇳🇨 | nc | 144 | | 🇳🇪 | ne | 🇳🇫 | nf | 145 | | 🇳🇬 | ng | 🇳🇮 | ni | 146 | | 🇳🇱 | nl | 🇳🇴 | no | 147 | | 🇳🇵 | np | 🇳🇷 | nr | 148 | | 🇳🇺 | nu | 🇳🇿 | nz | 149 | | 🇴🇲 | om | 🇵🇦 | pa | 150 | | 🇵🇪 | pe | 🇵🇫 | pf | 151 | | 🇵🇬 | pg | 🇵🇭 | ph | 152 | | 🇵🇰 | pk | 🇵🇱 | pl | 153 | | 🇵🇲 | pm | 🇵🇳 | pn | 154 | | 🇵🇷 | pr | 🇵🇸 | ps | 155 | | 🇵🇹 | pt | 🇵🇼 | pw | 156 | | 🇵🇾 | py | 🇶🇦 | qa | 157 | | 🇷🇪 | re | 🇷🇴 | ro | 158 | | 🇷🇸 | rs | 🇷🇺 | ru | 159 | | 🇷🇼 | rw | 🇸🇦 | sa_flag | 160 | | 🇸🇧 | sb | 🇸🇨 | sc | 161 | | 🇸🇩 | sd | 🇸🇪 | se | 162 | | 🇸🇬 | sg | 🇸🇭 | sh | 163 | | 🇸🇮 | si | 🇸🇯 | sj | 164 | | 🇸🇰 | sk | 🇸🇱 | sl | 165 | | 🇸🇲 | sm | 🇸🇳 | sn | 166 | | 🇸🇴 | so | 🇸🇷 | sr | 167 | | 🇸🇸 | ss | 🇸🇹 | st | 168 | | 🇸🇻 | sv | 🇸🇽 | sx | 169 | | 🇸🇾 | sy | 🇸🇿 | sz | 170 | | 🇹🇦 | ta | 🇹🇨 | tc | 171 | | 🇹🇩 | td | 🇹🇫 | tf | 172 | | 🇹🇬 | tg | 🇹🇭 | th | 173 | | 🇹🇯 | tj | 🇹🇰 | tk | 174 | | 🇹🇱 | tl | 🇹🇲 | tm_flag | 175 | | 🇹🇳 | tn | 🇹🇴 | to | 176 | | 🇹🇷 | tr | 🇹🇹 | tt | 177 | | 🇹🇻 | tv_flag | 🇹🇼 | tw | 178 | | 🇹🇿 | tz | 🇺🇦 | ua | 179 | | 🇺🇬 | ug | 🇺🇲 | um | 180 | | 🇺🇳 | un | 🇺🇸 | us | 181 | | 🇺🇾 | uy | 🇺🇿 | uz | 182 | | 🇻🇦 | va | 🇻🇨 | vc | 183 | | 🇻🇪 | ve | 🇻🇬 | vg | 184 | | 🇻🇮 | vi | 🇻🇳 | vn | 185 | | 🇻🇺 | vu | 🇼🇫 | wf | 186 | | 🇼🇸 | ws | 🇽🇰 | xk | 187 | | 🇾🇪 | ye | 🇾🇹 | yt | 188 | | 🇿🇦 | za | 🇿🇲 | zm | 189 | | 🇿🇼 | zw | 😄 | smile | 190 | | 😃 | smiley | 😀 | grinning | 191 | | 😊 | blush | 😉 | wink | 192 | | 😍 | heart_eyes | 😘 | kissing_heart | 193 | | 😚 | kissing_closed_eyes | 😗 | kissing | 194 | | 😙 | kissing_smiling_eyes | 😜 | stuck_out_tongue_winking_eye | 195 | | 😝 | stuck_out_tongue_closed_eyes | 😛 | stuck_out_tongue | 196 | | 😳 | flushed | 😁 | grin | 197 | | 😔 | pensive | 😌 | relieved | 198 | | 😒 | unamused | 😞 | disappointed | 199 | | 😣 | persevere | 😢 | cry | 200 | | 😂 | joy | 😭 | sob | 201 | | 😪 | sleepy | 😥 | disappointed_relieved | 202 | | 😰 | cold_sweat | 😅 | sweat_smile | 203 | | 😓 | sweat | 😩 | weary | 204 | | 😫 | tired_face | 😨 | fearful | 205 | | 😱 | scream | 😠 | angry | 206 | | 😡 | rage | 😤 | triumph | 207 | | 😖 | confounded | 😆 | laughing, satisfied | 208 | | 😋 | yum | 😷 | mask | 209 | | 😎 | sunglasses | 😴 | sleeping | 210 | | 😵 | dizzy_face | 😲 | astonished | 211 | | 😟 | worried | 😦 | frowning | 212 | | 😧 | anguished | 😈 | smiling_imp | 213 | | 👿 | imp | 😮 | open_mouth | 214 | | 😬 | grimacing | 😐 | neutral_face | 215 | | 😕 | confused | 😯 | hushed | 216 | | 😶 | no_mouth | 😇 | innocent | 217 | | 😏 | smirk | 😑 | expressionless | 218 | | 👲 | man_with_gua_pi_mao | 👳 | man_with_turban | 219 | | 👮 | cop | 👷 | construction_worker | 220 | | 💂 | guardsman | 👶 | baby | 221 | | 👦 | boy | 👧 | girl | 222 | | 👨 | man | 👩 | woman | 223 | | 👴 | older_man | 👵 | older_woman | 224 | | 👱 | person_with_blond_hair | 👼 | angel | 225 | | 👸 | princess | 😺 | smiley_cat | 226 | | 😸 | smile_cat | 😻 | heart_eyes_cat | 227 | | 😽 | kissing_cat | 😼 | smirk_cat | 228 | | 🙀 | scream_cat | 😿 | crying_cat_face | 229 | | 😹 | joy_cat | 😾 | pouting_cat | 230 | | 👹 | japanese_ogre | 👺 | japanese_goblin | 231 | | 🙈 | see_no_evil | 🙉 | hear_no_evil | 232 | | 🙊 | speak_no_evil | 💀 | skull | 233 | | 👽 | alien | 💩 | hankey, poop, shit | 234 | | 🔥 | fire | 🌟 | star2 | 235 | | 💫 | dizzy | 💥 | boom, collision | 236 | | 💢 | anger | 💦 | sweat_drops | 237 | | 💧 | droplet | 💤 | zzz | 238 | | 💨 | dash | 👂 | ear | 239 | | 👀 | eyes | 👃 | nose | 240 | | 👅 | tongue | 👄 | lips | 241 | | 👍 | +1, thumbsup | 👎 | -1, thumbsdown | 242 | | 👌 | ok_hand | 👊 | facepunch, punch | 243 | | 👋 | wave | 👐 | open_hands | 244 | | 👆 | point_up_2 | 👇 | point_down | 245 | | 👉 | point_right | 👈 | point_left | 246 | | 🙌 | raised_hands | 🙏 | pray | 247 | | 👏 | clap | 💪 | muscle | 248 | | 🚶 | walking | 🏃 | runner, running | 249 | | 💃 | dancer | 👫 | couple | 250 | | 👪 | family | 👬 | two_men_holding_hands | 251 | | 👭 | two_women_holding_hands | 💏 | couplekiss | 252 | | 💑 | couple_with_heart | 👯 | dancers | 253 | | 🙆 | ok_woman | 🙅 | no_good | 254 | | 💁 | information_desk_person | 🙋 | raising_hand | 255 | | 💆 | massage | 💇 | haircut | 256 | | 💅 | nail_care | 👰 | bride_with_veil | 257 | | 🙎 | person_with_pouting_face | 🙍 | person_frowning | 258 | | 🙇 | bow | 🎩 | tophat | 259 | | 👑 | crown | 👒 | womans_hat | 260 | | 👟 | athletic_shoe | 👞 | mans_shoe, shoe | 261 | | 👡 | sandal | 👠 | high_heel | 262 | | 👢 | boot | 👕 | shirt, tshirt | 263 | | 👔 | necktie | 👚 | womans_clothes | 264 | | 👗 | dress | 🎽 | running_shirt_with_sash | 265 | | 👖 | jeans | 👘 | kimono | 266 | | 👙 | bikini | 💼 | briefcase | 267 | | 👜 | handbag | 👝 | pouch | 268 | | 👛 | purse | 👓 | eyeglasses | 269 | | 🎀 | ribbon | 🌂 | closed_umbrella | 270 | | 💄 | lipstick | 💛 | yellow_heart | 271 | | 💙 | blue_heart | 💜 | purple_heart | 272 | | 💚 | green_heart | 💔 | broken_heart | 273 | | 💗 | heartpulse | 💓 | heartbeat | 274 | | 💕 | two_hearts | 💖 | sparkling_heart | 275 | | 💞 | revolving_hearts | 💘 | cupid | 276 | | 💌 | love_letter | 💋 | kiss | 277 | | 💍 | ring | 💎 | gem | 278 | | 👤 | bust_in_silhouette | 👥 | busts_in_silhouette | 279 | | 💬 | speech_balloon | 👣 | footprints | 280 | | 💭 | thought_balloon | 🐶 | dog | 281 | | 🐺 | wolf | 🐱 | cat | 282 | | 🐭 | mouse | 🐹 | hamster | 283 | | 🐰 | rabbit | 🐸 | frog | 284 | | 🐯 | tiger | 🐨 | koala | 285 | | 🐻 | bear | 🐷 | pig | 286 | | 🐽 | pig_nose | 🐮 | cow | 287 | | 🐗 | boar | 🐵 | monkey_face | 288 | | 🐒 | monkey | 🐴 | horse | 289 | | 🐑 | sheep | 🐘 | elephant | 290 | | 🐼 | panda_face | 🐧 | penguin | 291 | | 🐦 | bird | 🐤 | baby_chick | 292 | | 🐥 | hatched_chick | 🐣 | hatching_chick | 293 | | 🐔 | chicken | 🐍 | snake | 294 | | 🐢 | turtle | 🐛 | bug | 295 | | 🐝 | bee, honeybee | 🐜 | ant | 296 | | 🐞 | beetle | 🐌 | snail | 297 | | 🐙 | octopus | 🐚 | shell | 298 | | 🐠 | tropical_fish | 🐟 | fish | 299 | | 🐬 | dolphin, flipper | 🐳 | whale | 300 | | 🐋 | whale2 | 🐄 | cow2 | 301 | | 🐏 | ram | 🐀 | rat | 302 | | 🐃 | water_buffalo | 🐅 | tiger2 | 303 | | 🐇 | rabbit2 | 🐉 | dragon | 304 | | 🐎 | racehorse | 🐐 | goat | 305 | | 🐓 | rooster | 🐕 | dog2 | 306 | | 🐖 | pig2 | 🐁 | mouse2 | 307 | | 🐂 | ox | 🐲 | dragon_face | 308 | | 🐡 | blowfish | 🐊 | crocodile | 309 | | 🐫 | camel | 🐪 | dromedary_camel | 310 | | 🐆 | leopard | 🐈 | cat2 | 311 | | 🐩 | poodle | 🐾 | feet, paw_prints | 312 | | 💐 | bouquet | 🌸 | cherry_blossom | 313 | | 🌷 | tulip | 🍀 | four_leaf_clover | 314 | | 🌹 | rose | 🌻 | sunflower | 315 | | 🌺 | hibiscus | 🍁 | maple_leaf | 316 | | 🍃 | leaves | 🍂 | fallen_leaf | 317 | | 🌿 | herb | 🌾 | ear_of_rice | 318 | | 🍄 | mushroom | 🌵 | cactus | 319 | | 🌴 | palm_tree | 🌲 | evergreen_tree | 320 | | 🌳 | deciduous_tree | 🌰 | chestnut | 321 | | 🌱 | seedling | 🌼 | blossom | 322 | | 🌐 | globe_with_meridians | 🌞 | sun_with_face | 323 | | 🌝 | full_moon_with_face | 🌚 | new_moon_with_face | 324 | | 🌑 | new_moon | 🌒 | waxing_crescent_moon | 325 | | 🌓 | first_quarter_moon | 🌔 | moon, waxing_gibbous_moon | 326 | | 🌕 | full_moon | 🌖 | waning_gibbous_moon | 327 | | 🌗 | last_quarter_moon | 🌘 | waning_crescent_moon | 328 | | 🌜 | last_quarter_moon_with_face | 🌛 | first_quarter_moon_with_face | 329 | | 🌙 | crescent_moon | 🌍 | earth_africa | 330 | | 🌎 | earth_americas | 🌏 | earth_asia | 331 | | 🌋 | volcano | 🌌 | milky_way | 332 | | 🌠 | stars | 🌀 | cyclone | 333 | | 🌁 | foggy | 🌈 | rainbow | 334 | | 🌊 | ocean | 🎍 | bamboo | 335 | | 💝 | gift_heart | 🎎 | dolls | 336 | | 🎒 | school_satchel | 🎓 | mortar_board | 337 | | 🎏 | flags | 🎆 | fireworks | 338 | | 🎇 | sparkler | 🎐 | wind_chime | 339 | | 🎑 | rice_scene | 🎃 | jack_o_lantern | 340 | | 👻 | ghost | 🎅 | santa | 341 | | 🎄 | christmas_tree | 🎁 | gift | 342 | | 🎋 | tanabata_tree | 🎉 | tada | 343 | | 🎊 | confetti_ball | 🎈 | balloon | 344 | | 🎌 | crossed_flags | 🔮 | crystal_ball | 345 | | 🎥 | movie_camera | 📷 | camera | 346 | | 📹 | video_camera | 📼 | vhs | 347 | | 💿 | cd | 📀 | dvd | 348 | | 💽 | minidisc | 💾 | floppy_disk | 349 | | 💻 | computer | 📱 | iphone | 350 | | 📞 | telephone_receiver | 📟 | pager | 351 | | 📠 | fax | 📡 | satellite_antenna | 352 | | 📺 | tv | 📻 | radio | 353 | | 🔊 | loud_sound | 🔉 | sound | 354 | | 🔈 | speaker | 🔇 | mute | 355 | | 🔔 | bell | 🔕 | no_bell | 356 | | 📢 | loudspeaker | 📣 | mega | 357 | | 🔓 | unlock | 🔒 | lock | 358 | | 🔏 | lock_with_ink_pen | 🔐 | closed_lock_with_key | 359 | | 🔑 | key | 🔎 | mag_right | 360 | | 💡 | bulb | 🔦 | flashlight | 361 | | 🔆 | high_brightness | 🔅 | low_brightness | 362 | | 🔌 | electric_plug | 🔋 | battery | 363 | | 🔍 | mag | 🛁 | bathtub | 364 | | 🛀 | bath | 🚿 | shower | 365 | | 🚽 | toilet | 🔧 | wrench | 366 | | 🔩 | nut_and_bolt | 🔨 | hammer | 367 | | 🚪 | door | 🚬 | smoking | 368 | | 💣 | bomb | 🔫 | gun | 369 | | 🔪 | hocho, knife | 💊 | pill | 370 | | 💉 | syringe | 💰 | moneybag | 371 | | 💴 | yen | 💵 | dollar | 372 | | 💷 | pound | 💶 | euro | 373 | | 💳 | credit_card | 💸 | money_with_wings | 374 | | 📲 | calling | 📧 | e-mail | 375 | | 📥 | inbox_tray | 📤 | outbox_tray | 376 | | 📩 | envelope_with_arrow | 📨 | incoming_envelope | 377 | | 📯 | postal_horn | 📫 | mailbox | 378 | | 📪 | mailbox_closed | 📬 | mailbox_with_mail | 379 | | 📭 | mailbox_with_no_mail | 📮 | postbox | 380 | | 📦 | package | 📝 | memo, pencil | 381 | | 📄 | page_facing_up | 📃 | page_with_curl | 382 | | 📑 | bookmark_tabs | 📊 | bar_chart | 383 | | 📈 | chart_with_upwards_trend | 📉 | chart_with_downwards_trend | 384 | | 📜 | scroll | 📋 | clipboard | 385 | | 📅 | date | 📆 | calendar | 386 | | 📇 | card_index | 📁 | file_folder | 387 | | 📂 | open_file_folder | 📌 | pushpin | 388 | | 📎 | paperclip | 📏 | straight_ruler | 389 | | 📐 | triangular_ruler | 📕 | closed_book | 390 | | 📗 | green_book | 📘 | blue_book | 391 | | 📙 | orange_book | 📓 | notebook | 392 | | 📔 | notebook_with_decorative_cover | 📒 | ledger | 393 | | 📚 | books | 📖 | book, open_book | 394 | | 🔖 | bookmark | 📛 | name_badge | 395 | | 🔬 | microscope | 🔭 | telescope | 396 | | 📰 | newspaper | 🎨 | art | 397 | | 🎬 | clapper | 🎤 | microphone | 398 | | 🎧 | headphones | 🎼 | musical_score | 399 | | 🎵 | musical_note | 🎶 | notes | 400 | | 🎹 | musical_keyboard | 🎻 | violin | 401 | | 🎺 | trumpet | 🎷 | saxophone | 402 | | 🎸 | guitar | 👾 | space_invader | 403 | | 🎮 | video_game | 🃏 | black_joker | 404 | | 🎴 | flower_playing_cards | 🀄 | mahjong | 405 | | 🎲 | game_die | 🎯 | dart | 406 | | 🏈 | football | 🏀 | basketball | 407 | | 🎾 | tennis | 🎱 | 8ball | 408 | | 🏉 | rugby_football | 🎳 | bowling | 409 | | 🚵 | mountain_bicyclist | 🚴 | bicyclist | 410 | | 🏁 | checkered_flag | 🏇 | horse_racing | 411 | | 🏆 | trophy | 🎿 | ski | 412 | | 🏂 | snowboarder | 🏊 | swimmer | 413 | | 🏄 | surfer | 🎣 | fishing_pole_and_fish | 414 | | 🍵 | tea | 🍶 | sake | 415 | | 🍼 | baby_bottle | 🍺 | beer | 416 | | 🍻 | beers | 🍸 | cocktail | 417 | | 🍹 | tropical_drink | 🍷 | wine_glass | 418 | | 🍴 | fork_and_knife | 🍕 | pizza | 419 | | 🍔 | hamburger | 🍟 | fries | 420 | | 🍗 | poultry_leg | 🍖 | meat_on_bone | 421 | | 🍝 | spaghetti | 🍛 | curry | 422 | | 🍤 | fried_shrimp | 🍱 | bento | 423 | | 🍣 | sushi | 🍥 | fish_cake | 424 | | 🍙 | rice_ball | 🍘 | rice_cracker | 425 | | 🍚 | rice | 🍜 | ramen | 426 | | 🍲 | stew | 🍢 | oden | 427 | | 🍡 | dango | 🍳 | cooking | 428 | | 🍞 | bread | 🍩 | doughnut | 429 | | 🍮 | custard | 🍦 | icecream | 430 | | 🍨 | ice_cream | 🍧 | shaved_ice | 431 | | 🎂 | birthday | 🍰 | cake | 432 | | 🍪 | cookie | 🍫 | chocolate_bar | 433 | | 🍬 | candy | 🍭 | lollipop | 434 | | 🍯 | honey_pot | 🍎 | apple | 435 | | 🍏 | green_apple | 🍊 | tangerine | 436 | | 🍋 | lemon | 🍒 | cherries | 437 | | 🍇 | grapes | 🍉 | watermelon | 438 | | 🍓 | strawberry | 🍑 | peach | 439 | | 🍈 | melon | 🍌 | banana | 440 | | 🍐 | pear | 🍍 | pineapple | 441 | | 🍠 | sweet_potato | 🍆 | eggplant | 442 | | 🍅 | tomato | 🌽 | corn | 443 | | 🏠 | house | 🏡 | house_with_garden | 444 | | 🏫 | school | 🏢 | office | 445 | | 🏣 | post_office | 🏥 | hospital | 446 | | 🏦 | bank | 🏪 | convenience_store | 447 | | 🏩 | love_hotel | 🏨 | hotel | 448 | | 💒 | wedding | 🏬 | department_store | 449 | | 🏤 | european_post_office | 🌇 | city_sunrise | 450 | | 🌆 | city_sunset | 🏯 | japanese_castle | 451 | | 🏰 | european_castle | 🏭 | factory | 452 | | 🗼 | tokyo_tower | 🗾 | japan | 453 | | 🗻 | mount_fuji | 🌄 | sunrise_over_mountains | 454 | | 🌅 | sunrise | 🌃 | night_with_stars | 455 | | 🗽 | statue_of_liberty | 🌉 | bridge_at_night | 456 | | 🎠 | carousel_horse | 🎡 | ferris_wheel | 457 | | 🎢 | roller_coaster | 🚢 | ship | 458 | | 🚤 | speedboat | 🚣 | rowboat | 459 | | 🚀 | rocket | 💺 | seat | 460 | | 🚁 | helicopter | 🚂 | steam_locomotive | 461 | | 🚊 | tram | 🚉 | station | 462 | | 🚞 | mountain_railway | 🚆 | train2 | 463 | | 🚄 | bullettrain_side | 🚅 | bullettrain_front | 464 | | 🚈 | light_rail | 🚇 | metro | 465 | | 🚝 | monorail | 🚋 | train | 466 | | 🚃 | railway_car | 🚎 | trolleybus | 467 | | 🚌 | bus | 🚍 | oncoming_bus | 468 | | 🚙 | blue_car | 🚘 | oncoming_automobile | 469 | | 🚗 | car, red_car | 🚕 | taxi | 470 | | 🚖 | oncoming_taxi | 🚛 | articulated_lorry | 471 | | 🚚 | truck | 🚨 | rotating_light | 472 | | 🚓 | police_car | 🚔 | oncoming_police_car | 473 | | 🚒 | fire_engine | 🚑 | ambulance | 474 | | 🚐 | minibus | 🚲 | bike | 475 | | 🚡 | aerial_tramway | 🚟 | suspension_railway | 476 | | 🚠 | mountain_cableway | 🚜 | tractor | 477 | | 💈 | barber | 🚏 | busstop | 478 | | 🎫 | ticket | 🚦 | vertical_traffic_light | 479 | | 🚥 | traffic_light | 🚧 | construction | 480 | | 🔰 | beginner | 🏮 | izakaya_lantern, lantern | 481 | | 🎰 | slot_machine | 🗿 | moyai | 482 | | 🎪 | circus_tent | 🎭 | performing_arts | 483 | | 📍 | round_pushpin | 🚩 | triangular_flag_on_post | 484 | | 1⃣ | one | 2⃣ | two | 485 | | 3⃣ | three | 4⃣ | four | 486 | | 5⃣ | five | 6⃣ | six | 487 | | 7⃣ | seven | 8⃣ | eight | 488 | | 9⃣ | nine | 0⃣ | zero | 489 | | 🔟 | keycap_ten | 🔢 | 1234 | 490 | | #⃣ | hash | 🔣 | symbols | 491 | | 🔠 | capital_abcd | 🔡 | abcd | 492 | | 🔤 | abc | 🔄 | arrows_counterclockwise | 493 | | 🔼 | arrow_up_small | 🔽 | arrow_down_small | 494 | | 🆗 | ok | 🔀 | twisted_rightwards_arrows | 495 | | 🔁 | repeat | 🔂 | repeat_one | 496 | | 🆕 | new | 🆙 | up | 497 | | 🆒 | cool | 🆓 | free | 498 | | 🆖 | squared_ng | 📶 | signal_strength | 499 | | 🎦 | cinema | 🈁 | koko | 500 | | 🈯 | u6307 | 🈳 | u7a7a | 501 | | 🈵 | u6e80 | 🈴 | u5408 | 502 | | 🈲 | u7981 | 🉐 | ideograph_advantage | 503 | | 🈹 | u5272 | 🈺 | u55b6 | 504 | | 🈶 | u6709 | 🈚 | u7121 | 505 | | 🚻 | restroom | 🚹 | mens | 506 | | 🚺 | womens | 🚼 | baby_symbol | 507 | | 🚾 | wc | 🚰 | potable_water | 508 | | 🚮 | put_litter_in_its_place | 🅿 | parking | 509 | | 🚭 | no_smoking | 🈷 | u6708 | 510 | | 🈸 | u7533 | 🈂 | sa | 511 | | 🛂 | passport_control | 🛄 | baggage_claim | 512 | | 🛅 | left_luggage | 🛃 | customs | 513 | | 🉑 | accept | 🆑 | cl | 514 | | 🆘 | sos | 🆔 | id | 515 | | 🚫 | no_entry_sign | 🔞 | underage | 516 | | 📵 | no_mobile_phones | 🚯 | do_not_litter | 517 | | 🚱 | non-potable_water | 🚳 | no_bicycles | 518 | | 🚷 | no_pedestrians | 🚸 | children_crossing | 519 | | 💟 | heart_decoration | 🆚 | vs | 520 | | 📳 | vibration_mode | 📴 | mobile_phone_off | 521 | | 🅰 | a | 🅱 | b | 522 | | 🆎 | ab | 🅾 | o2 | 523 | | 💠 | diamond_shape_with_a_dot_inside | 🔯 | six_pointed_star | 524 | | 🏧 | atm | 💹 | chart | 525 | | 💲 | heavy_dollar_sign | 💱 | currency_exchange | 526 | | 🔝 | top | 🔚 | end | 527 | | 🔙 | back | 🔛 | on | 528 | | 🔜 | soon | 🔃 | arrows_clockwise | 529 | | 🕛 | clock12 | 🕧 | clock1230 | 530 | | 🕐 | clock1 | 🕜 | clock130 | 531 | | 🕑 | clock2 | 🕝 | clock230 | 532 | | 🕒 | clock3 | 🕞 | clock330 | 533 | | 🕓 | clock4 | 🕟 | clock430 | 534 | | 🕔 | clock5 | 🕠 | clock530 | 535 | | 🕕 | clock6 | 🕖 | clock7 | 536 | | 🕗 | clock8 | 🕘 | clock9 | 537 | | 🕙 | clock10 | 🕚 | clock11 | 538 | | 🕡 | clock630 | 🕢 | clock730 | 539 | | 🕣 | clock830 | 🕤 | clock930 | 540 | | 🕥 | clock1030 | 🕦 | clock1130 | 541 | | 💮 | white_flower | 💯 | 100 | 542 | | 🔘 | radio_button | 🔗 | link | 543 | | 🔱 | trident | 🔺 | small_red_triangle | 544 | | 🔲 | black_square_button | 🔳 | white_square_button | 545 | | 🔴 | red_circle | 🔵 | large_blue_circle | 546 | | 🔻 | small_red_triangle_down | 🔶 | large_orange_diamond | 547 | | 🔷 | large_blue_diamond | 🔸 | small_orange_diamond | 548 | | 🔹 | small_blue_diamond | 🇦 | regional_indicator_symbol_a | 549 | | 🇧 | regional_indicator_symbol_b | 🇨 | regional_indicator_symbol_c | 550 | | 🇩 | regional_indicator_symbol_d | 🇪 | regional_indicator_symbol_e | 551 | | 🇫 | regional_indicator_symbol_f | 🇬 | regional_indicator_symbol_g | 552 | | 🇭 | regional_indicator_symbol_h | 🇮 | regional_indicator_symbol_i | 553 | | 🇯 | regional_indicator_symbol_j | 🇰 | regional_indicator_symbol_k | 554 | | 🇱 | regional_indicator_symbol_l | 🇲 | regional_indicator_symbol_m | 555 | | 🇳 | regional_indicator_symbol_n | 🇴 | regional_indicator_symbol_o | 556 | | 🇵 | regional_indicator_symbol_p | 🇶 | regional_indicator_symbol_q | 557 | | 🇷 | regional_indicator_symbol_r | 🇸 | regional_indicator_symbol_s | 558 | | 🇹 | regional_indicator_symbol_t | 🇺 | regional_indicator_symbol_u | 559 | | 🇻 | regional_indicator_symbol_v | 🇼 | regional_indicator_symbol_w | 560 | | 🇽 | regional_indicator_symbol_x | 🇾 | regional_indicator_symbol_y | 561 | | 🇿 | regional_indicator_symbol_z | 🖖 | vulcan_salute | 562 | | 🖕 | middle_finger | 🙂 | slightly_smiling, slight_smile | 563 | | 🤗 | hugging, hug, hugs | 🤔 | thinking, think, thinker | 564 | | 🙄 | eye_roll, rolling_eyes | 🤐 | zipper_mouth, zip_it, sealed_lips, lips_sealed | 565 | | 🤓 | nerd, nerdy | 🙁 | slightly_frowning | 566 | | 🙃 | upside_down, flipped_face | 🤒 | sick, ill, thermometer_face | 567 | | 🤕 | injured, head_bandage, head_bandaged, bandaged | 🤑 | money_mouth, money_face | 568 | | 🕵 | detective, sleuth, private_eye, spy | 🗣 | speaking_head_in_silhouette | 569 | | 🕴 | hovering_man, levitating_man | 🤘 | horns_sign, rock_on, heavy_metal, devil_fingers | 570 | | 🖐 | raised_hand_with_fingers_splayed, splayed_hand | 👁 | eye | 571 | | 🕳 | hole | 🗯 | right_anger_bubble, zig_zag_bubble | 572 | | 🕶 | dark_sunglasses | 🛍 | shopping_bags | 573 | | 📿 | prayer_beads, dhikr_beads, rosary_beads | 🤖 | robot_face, bot_face | 574 | | 🦁 | lion_face, cute_lion, timid_lion | 🦄 | unicorn_face | 575 | | 🐿 | chipmunk, squirrel | 🦃 | turkey | 576 | | 🕊 | dove, dove_peace | 🦀 | crab | 577 | | 🕷 | spider | 🕸 | spider_web, cobweb | 578 | | 🦂 | scorpion | 🏵 | rosette | 579 | | 🌶 | hot_pepper, chili_pepper, spice, spicy | 🧀 | cheese | 580 | | 🌭 | hot_dog | 🌮 | taco | 581 | | 🌯 | burrito, wrap | 🍿 | popcorn | 582 | | 🍾 | champagne, sparkling_wine | 🍽 | fork_knife_plate | 583 | | 🏺 | amphora, jar, vase | 🗺 | world_map | 584 | | 🏔 | snow_capped_mountain, mont_fuji | 🏕 | camping, campsite | 585 | | 🏖 | breach | 🏜 | desert | 586 | | 🏝 | desert_island | 🏞 | national_park | 587 | | 🏟 | stadium | 🏛 | classical_building | 588 | | 🏗 | building_construction, crane | 🏘 | house_buildings, multiple_houses | 589 | | 🏙 | cityscape | 🏚 | derelict_house, old_house, abandoned_house | 590 | | 🛐 | worship_building, worship_place, religious_building, religious_place | 🕋 | kaaba, mecca | 591 | | 🕌 | mosque, minaret, domed_roof | 🕍 | synagogue, temple, jewish | 592 | | 🖼 | picture_frame, painting, gallery | 🛢 | oil_drum | 593 | | 🛣 | motorway, highway, road, interstate, freeway | 🛤 | railway_track | 594 | | 🛳 | passenger_ship | 🛥 | motor_boat | 595 | | 🛩 | small_airplane | 🛫 | airplane_departure, take_off | 596 | | 🛬 | airplane_arriving, airplane_arrival, landing | 🛰 | satellite | 597 | | 🛎 | bellhop_bell | 🛌 | sleeping_accommodation | 598 | | 🛏 | bed, bedroom | 🛋 | couch_lamp, couch, sofa, lounge | 599 | | 🕰 | mantelpiece_clock | 🌡 | thermometer, hot_weather, temperature | 600 | | 🌤 | white_sun_small_cloud | 🌥 | white_sun_behind_cloud | 601 | | 🌦 | white_sun_behind_cloud_rain | 🌧 | cloud_rain | 602 | | 🌨 | cloud_snow | 🌩 | cloud_lightning | 603 | | 🌪 | cloud_tornado | 🌫 | fog | 604 | | 🌬 | wind_blowing_face, mother_nature, blowing_wind | 🕎 | menorah, candelabrum, chanukiah | 605 | | 🎖 | military_medal, military_decoration | 🎗 | reminder_ribbon, awareness_ribbon | 606 | | 🎞 | film_frames | 🎟 | admission_ticket | 607 | | 🏷 | label | 🏌 | golfer, golf_club | 608 | | 🏋 | weight_lifter | 🏎 | racing_car, formula_one, f1 | 609 | | 🏍 | racing_motorcycle, motorcycle, motorbike | 🏅 | sports_medal, sports_decoration | 610 | | 🏏 | cricket_bat_and_ball, cricket_game | 🏐 | volleyball | 611 | | 🏑 | field_hockey | 🏒 | ice_hockey | 612 | | 🏓 | table_tennis, ping_pong | 🏸 | badminton | 613 | | 🕹 | joystick | 🎙 | studio_microphone | 614 | | 🎚 | level_slider | 🎛 | control_knobs | 615 | | *⃣ | keycap_asterisk, star_keycap | 🖥 | desktop_computer, pc_tower, imac | 616 | | 🖨 | printer | 🖱 | computer_mouse, three_button_mouse | 617 | | 🖲 | trackball | 📽 | film_projector | 618 | | 📸 | camera_flash | 🕯 | candle | 619 | | 🗞 | rolled_up_newspaper, newspaper_delivery | 🗳 | ballot, ballot_box | 620 | | 🖋 | lower_left_fountain_pen | 🖊 | lower_left_ballpoint_pen | 621 | | 🖌 | lower_left_paintbrush | 🖍 | lower_left_crayon | 622 | | 🗂 | card_index_dividers | 🗒 | spiral_note_pad | 623 | | 🗓 | spiral_calendar_pad | 🖇 | linked_paperclips | 624 | | 🗃 | card_file_box | 🗄 | file_cabinet | 625 | | 🗑 | wastebasket | 🗝 | old_key | 626 | | 🛠 | hammer_and_wrench | 🗜 | compression | 627 | | 🗡 | dagger, dagger_knife, knife_weapon | 🛡 | shield | 628 | | 🏹 | bow_and_arrow, bow_arrow, archery | 🏳 | waving_white_flag | 629 | | 🏴 | waving_black_flag | 🕉 | om_symbol, pranava, aumkara, omkara | 630 | | 🗨 | left_speech_bubble | 🤣 | rolling_on_the_floor_laughing, rofl | 631 | | 🤠 | face_with_cowboy_hat, cowboy | 🤡 | clown_face, clown | 632 | | 🤥 | lying_face | 🤤 | drooling_face | 633 | | 🤢 | nauseated_face | 🤧 | sneezing_face | 634 | | 🤴 | prince | 🤶 | mother_christmas | 635 | | 🤵 | man_in_tuxedo | 🤷 | shrug | 636 | | 🤦 | face_palm | 🤰 | pregnant_woman | 637 | | 🕺 | man_dancing | 🤳 | selfie | 638 | | 🤞 | hand_with_index_and_middle_fingers_crossed | 🤙 | call_me_hand | 639 | | 🤛 | left-facing_fist | 🤜 | right-facing_fist | 640 | | 🤚 | raised_back_of_hand | 🤝 | handshake | 641 | | 🖤 | black_heart | 🦍 | gorilla | 642 | | 🦊 | fox_face | 🦌 | deer | 643 | | 🦏 | rhinoceros | 🦇 | bat | 644 | | 🦅 | eagle | 🦆 | duck | 645 | | 🦉 | owl | 🦎 | lizard | 646 | | 🦈 | shark | 🦐 | shrimp | 647 | | 🦑 | squid | 🦋 | butterfly | 648 | | 🥀 | wilted_flower | 🥝 | kiwifruit | 649 | | 🥑 | avocado | 🥔 | potato | 650 | | 🥕 | carrot | 🥒 | cucumber | 651 | | 🥜 | peanuts | 🥐 | croissant | 652 | | 🥖 | baguette_bread | 🥞 | pancakes | 653 | | 🥓 | bacon | 🥙 | stuffed_flatbread | 654 | | 🥚 | egg | 🥘 | shallow_pan_of_food | 655 | | 🥗 | green_salad | 🥛 | glass_of_milk | 656 | | 🥂 | clinking_glasses | 🥃 | tumbler_glass | 657 | | 🥄 | spoon | 🛑 | octagonal_sign, stop_sign | 658 | | 🛴 | scooter | 🛵 | motor_scooter | 659 | | 🛶 | canoe | 🥇 | first_place_medal | 660 | | 🥈 | second_place_medal | 🥉 | third_place_medal | 661 | | 🥊 | boxing_glove | 🥋 | martial_arts_uniform | 662 | | 🤸 | person_doing_cartwheel | 🤼 | wrestlers | 663 | | 🤽 | water_polo | 🤾 | handball | 664 | | 🤺 | fencer | 🥅 | goal_net | 665 | | 🤹 | juggling | 🥁 | drum_with_drumsticks | 666 | | 🛒 | shopping_trolley, shopping_cart | 🤩 | star_struck, excited, star_eyes, starry_eyed, wow_face, face_with_starry_eyes, grinning_face_with_star_eyes | 667 | | 🤪 | zany_face, crazy_eyes, wild, goofy_face, grinning_face_with_one_large_and_one_small_eye | 🤭 | face_with_hand_over_mouth, blushing_face_with_hand_over_mouth, smiling_face_with_smiling_eyes_and_hand_covering_mouth | 668 | | 🤫 | shushing_face, hush, quiet, shh, face_with_finger_covering_closed_lips | 🤨 | face_with_raised_eyebrow, colbert, the_rock, face_with_one_eyebrow_raised | 669 | | 🤮 | face_vomiting, spew, throwing_up, vomit, vomiting_face, face_with_open_mouth_vomiting | 🤯 | exploding_head, mind_blown, shocked_face_with_exploding_head | 670 | | 🧐 | face_with_monocle | 🤬 | face_with_symbols_on_mouth, cursing, cussing, grawlix, swearing, face_with_symbols_over_mouth, serious_face_with_symbols_covering_mouth | 671 | | 🧡 | orange_heart | 🤟 | love_you_gesture, i_love_you_hand_sign | 672 | | 🤲 | palms_up_together, dua, palms_together_facing_up | 🧠 | brain | 673 | | 🧒 | child, gender_neutral_child | 🧑 | person, gender_neutral_adult, gender_neutral_person | 674 | | 🧔 | beard, bearded_man, bearded_person, man_with_beard, person_with_beard | 🧓 | older_person, gender_neutral_older_person, older_adult, gender_neutral_older_adult | 675 | | 🧕 | person_with_headscarf, woman_with_headscarf, hijab | 🤱 | breastfeeding | 676 | | 🧙 | mage, gender_neutral_mage | 🧚 | fairy, gender_neutral_fairy | 677 | | 🧛 | vampire, gender_neutral_vampire | 🧜 | merperson, gender_neutral_merperson | 678 | | 🧝 | elf, gender_neutral_elf | 🧞 | genie, gender_neutral_genie, djinni, jinni | 679 | | 🧟 | zombie, gender_neutral_zombie | 🧖 | person_in_steamy_room, sauna | 680 | | 🧗 | person_climbing, climber, rock_climbing | 🧘 | person_in_lotus_position, yoga, meditation | 681 | | 🦓 | zebra_face, zebra | 🦒 | giraffe_face, giraffe | 682 | | 🦔 | hedgehog | 🦕 | sauropod, brontosaurus, diplodocus, dinosaur | 683 | | 🦖 | trex, t_rex, tyrannosaurus_rex | 🦗 | cricket | 684 | | 🥥 | coconut, cocoanut | 🥦 | broccoli | 685 | | 🥨 | pretzel | 🥩 | cut_of_meat, meat, steak | 686 | | 🥪 | sandwich | 🥣 | bowl_with_spoon, cereal_bowl | 687 | | 🥫 | canned_food, tin_can, can_of_food, tinned_food | 🥟 | dumpling, jiaozi, gyoza, pierogi, empanada, xiaolongbao | 688 | | 🥠 | fortune_cookie | 🥡 | takeout_box, chinese_food_box, oyster_pail | 689 | | 🥧 | pie | 🥤 | cup_with_straw, milkshake, smoothie, soda_pop, soft_drink, to_go_cup | 690 | | 🥢 | chopsticks | 🛸 | flying_saucer, ufo, unidentified_flying_object | 691 | | 🛷 | sled | 🥌 | curling_stone | 692 | | 🧣 | scarf | 🧤 | gloves | 693 | | 🧥 | coat | 🧦 | socks | 694 | | 🧢 | billed_cap, baseball_cap, billed_hat | 🥰 | smiling_face_with_hearts, in_love_face, smiling_face_with_smiling_eyes_and_three_hearts | 695 | | 🥵 | hot_face, overheated_face | 🥶 | cold_face, freezing_face | 696 | | 🥴 | woozy_face, drunk_face, face_with_uneven_eyes_and_wavy_mouth | 🥳 | partying_face, party_face, face_with_party_horn_and_party_hat | 697 | | 🥺 | pleading_face, face_with_pleading_eyes | 🦵 | leg | 698 | | 🦶 | foot | 🦷 | tooth | 699 | | 🦴 | bone | 🦸 | superhero | 700 | | 🦹 | supervillain | 🦝 | raccoon | 701 | | 🦙 | llama, alpaca | 🦛 | hippo, hippopotamus | 702 | | 🦘 | roo, kangaroo | 🦡 | badger | 703 | | 🦢 | swan | 🦚 | peacock | 704 | | 🦜 | parrot | 🦟 | mosquito | 705 | | 🦠 | microbe | 🥭 | mango | 706 | | 🥬 | leafy_green, bok_choy, chinese_cabbage, cos_lettuce, romaine_lettuce, leafy_greens | 🥯 | bagel | 707 | | 🧂 | salt_shaker, salt | 🥮 | moon_cake, mooncake | 708 | | 🦞 | lobster | 🧁 | cupcake, fairy_cake | 709 | | 🧭 | compass | 🧱 | brick, bricks | 710 | | 🛹 | skateboard | 🧳 | lugagge, suitcase | 711 | | 🧨 | firecracker, dynamite | 🧧 | red_envelope, red_gift_envelope, ang_pao, hongbao, lai_see, red_packet | 712 | | 🥎 | softball | 🥏 | flying_disc | 713 | | 🥍 | lacrosse, lacrosse_stick_and_ball | 🧿 | nazar_amulet, evil_eye_talisman, nazar_boncugu | 714 | | 🧩 | jigsaw, puzzle_piece, jigsaw_puzzle_piece | 🧸 | teddy_bear | 715 | | ♟️ | black_chess_pawn, chess_pawn | 🧵 | spool_of_thread, thread | 716 | | 🧶 | ball_of_yarn, yarn | 🥽 | goggles | 717 | | 🥼 | lab_coat | 🥾 | hiking_boot | 718 | | 🥿 | flat_shoe | 🧮 | abacus | 719 | | 🧾 | receipt | 🧰 | toolbox | 720 | | 🧲 | magnet | 🧪 | test_tube | 721 | | 🧫 | petri_dish | 🧬 | dna, dna_double_helix | 722 | | 🧴 | lotion_bottle | 🧷 | safety_pin | 723 | | 🧹 | broom | 🧺 | basket | 724 | | 🧻 | roll_of_paper, toilet_paper | 🧼 | bar_of_soap, soap | 725 | | 🧽 | sponge | 🧯 | fire_extinguisher | 726 | | ♾️ | infinity, permanent_paper_sign | ☺ | relaxed | 727 | | ✨ | sparkles | ✊ | fist | 728 | | ✌ | v | ✋ | hand, raised_hand | 729 | | ☝ | point_up | ❤ | heart | 730 | | ⭐ | star | ☀ | sunny | 731 | | ⛅ | partly_sunny | ☁ | cloud | 732 | | ⚡ | zap | ☔ | umbrella | 733 | | ❄ | snowflake | ⛄ | snowman | 734 | | ☎ | phone, telephone | ⏳ | hourglass_flowing_sand | 735 | | ⌛ | hourglass | ⏰ | alarm_clock | 736 | | ⌚ | watch | ✉ | email, envelope | 737 | | ✂ | scissors | ✒ | black_nib | 738 | | ✏ | pencil2 | ⚽ | soccer | 739 | | ⚾ | baseball | ⛳ | golf | 740 | | ☕ | coffee | ⛪ | church | 741 | | ⛺ | tent | ⛲ | fountain | 742 | | ⛵ | boat, sailboat | ⚓ | anchor | 743 | | ✈ | airplane | ⚠ | warning | 744 | | ⛽ | fuelpump | ♨ | hotsprings | 745 | | ⬆ | arrow_up | ⬇ | arrow_down | 746 | | ⬅ | arrow_left | ➡ | arrow_right | 747 | | ↗ | arrow_upper_right | ↖ | arrow_upper_left | 748 | | ↘ | arrow_lower_right | ↙ | arrow_lower_left | 749 | | ↔ | left_right_arrow | ↕ | arrow_up_down | 750 | | ◀ | arrow_backward | ▶ | arrow_forward | 751 | | ↩ | leftwards_arrow_with_hook | ↪ | arrow_right_hook | 752 | | ℹ | information_source | ⏪ | rewind | 753 | | ⏩ | fast_forward | ⏫ | arrow_double_up | 754 | | ⏬ | arrow_double_down | ⤵ | arrow_heading_down | 755 | | ⤴ | arrow_heading_up | ♿ | wheelchair | 756 | | Ⓜ | m | ㊙ | secret | 757 | | ㊗ | congratulations | ⛔ | no_entry | 758 | | ✳ | eight_spoked_asterisk | ❇ | sparkle | 759 | | ❎ | negative_squared_cross_mark | ✅ | white_check_mark | 760 | | ✴ | eight_pointed_black_star | ➿ | loop | 761 | | ♻ | recycle | ♈ | aries | 762 | | ♉ | taurus | ♊ | gemini | 763 | | ♋ | cancer | ♌ | leo | 764 | | ♍ | virgo | ♎ | libra | 765 | | ♏ | scorpius | ♐ | sagittarius | 766 | | ♑ | capricorn | ♒ | aquarius | 767 | | ♓ | pisces | ⛎ | ophiuchus | 768 | | © | copyright | ® | registered | 769 | | ™ | tm | ❌ | x | 770 | | ‼ | bangbang | ⁉ | interrobang | 771 | | ❗ | exclamation, heavy_exclamation_mark | ❓ | question | 772 | | ❕ | grey_exclamation | ❔ | grey_question | 773 | | ⭕ | o | ✖ | heavy_multiplication_x | 774 | | ➕ | heavy_plus_sign | ➖ | heavy_minus_sign | 775 | | ➗ | heavy_division_sign | ♠ | spades | 776 | | ♥ | hearts | ♣ | clubs | 777 | | ♦ | diamonds | ✔ | heavy_check_mark | 778 | | ☑ | ballot_box_with_check | ➰ | curly_loop | 779 | | 〰 | wavy_dash | 〽 | part_alternation_mark | 780 | | ◼ | black_medium_square | ◻ | white_medium_square | 781 | | ◾ | black_medium_small_square | ◽ | white_medium_small_square | 782 | | ▪ | black_small_square | ▫ | white_small_square | 783 | | ⚫ | black_circle | ⚪ | white_circle | 784 | | ⬜ | white_large_square | ⬛ | black_large_square | 785 | | ☹ | frowning_face | ⛑ | helmet_white_cross | 786 | | ✍ | writing, writing_hand | ❣ | exclamation_heart | 787 | | ☠ | skull_crossbones | ☘ | shamrock, st_patrick | 788 | | ⛰ | mountain | ⛴ | ferry | 789 | | ⏱ | stopwatch | ⏲ | timer_clock | 790 | | ⛈ | thunder_cloud_rain | ☂ | open_umbrella | 791 | | ⛱ | planted_umbrella, umbrella_on_ground | ☃ | snowman_with_snow, snowing_snowman | 792 | | ☄ | comet, light_beam, blue_beam | ⛸ | ice_skate, ice_skating | 793 | | ⛷ | skier | ⛹ | person_with_ball | 794 | | ⏭ | black_right_pointing_double_triangle_with_vertical_bar | ⏯ | black_right_pointing_triangle_with_double_vertical_bar | 795 | | ⏮ | black_left_pointing_double_triangle_with_vertical_bar | ⏸ | double_vertical_bar | 796 | | ⏹ | black_square_for_stop | ⏺ | black_circle_for_record | 797 | | ⌨ | keyboard | ⛏ | pick | 798 | | ⚒ | hammer_and_pick | ⚙ | gear | 799 | | ⚗ | alembic | ⚖ | scales, scales_of_justice | 800 | | ⛓ | chains | ⚔ | crossed_swords | 801 | | ⚰ | coffin, funeral, casket | ⚱ | funeral_urn | 802 | | ⚜ | fleur_de_lis, scouts | ⚛ | atom, atom_symbol | 803 | | ✡ | star_of_david | ☸ | wheel_of_dharma | 804 | | ☯ | yin_yang | ✝ | latin_cross, christian_cross | 805 | | ☦ | orthodox_cross | ⛩ | shinto_shrine, kami_no_michi | 806 | | ☪ | star_and_crescent, star_crescent | ☮ | peace_symbol, peace_sign | 807 | | ☢ | radioactive, radioactive_symbol, radioactive_sign | ☣ | biohazard, biohazard_symbol, biohazard_sign | 808 | | ⚕ | medical_symbol, staff_of_aesculapius | 809 | 810 | --------------------------------------------------------------------------------