package freetype import ( "errors" "fmt" "strings" "unicode/utf8" ) type Emoji struct { Codepoints []rune Bytes int } var ( errNotAnEmoji = errors.New("not an emoji") ) func inrange(val, min, max rune) bool { return val >= min && val < max } // https://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji/39425959 func isEmoji(chr rune) bool { switch { case inrange(chr, 0x1f600, 0x1f64f), // Emoticons inrange(chr, 0x1f300, 0x1f5ff), // Misc Symbols and Pictographs inrange(chr, 0x1f680, 0x1f6ff), // Transport and Map inrange(chr, 0x1f1e6, 0x1f1ff), // Regional country flags inrange(chr, 0x2600, 0x26ff), // Misc symbols inrange(chr, 0x2700, 0x27bf), // Dingbats inrange(chr, 0xfe00, 0xfe0f), // Variation Selectors inrange(chr, 0x1f900, 0x1f9ff), // Supplemental Symbols and Pictographs inrange(chr, 127000, 127600), // Various asian characters inrange(chr, 65024, 65039), // Variation selector inrange(chr, 9100, 9300), // Misc items inrange(chr, 8400, 8447): // Combining Diacritical Marks for Symbols return true } return false } func isZWJ(chr rune) bool { return chr == 8205 } func parseEmoji(str string) (Emoji, error) { if len(str) < 1 { return Emoji{}, errNotAnEmoji } emoji := Emoji{} for _, r := range str { // Check if rune is emoji if !isEmoji(r) && !isZWJ(r) { if len(emoji.Codepoints) < 1 { return emoji, errNotAnEmoji } break } emoji.Codepoints = append(emoji.Codepoints, r) emoji.Bytes += utf8.RuneLen(r) } return emoji, nil } func (e Emoji) String() string { codepoints := []string{} for _, cp := range e.Codepoints { codepoints = append(codepoints, fmt.Sprintf("%U", cp)) } return "Emoji(" + strings.Join(codepoints, ", ") + ")" }