Implementation of UTS #51 (Unicode Emoji) for complete emoji support in terminals, text editors, and layout engines.
This package provides 100% conformance (5,223/5,223 test cases passing) for UTS #51, with complete emoji property detection, sequence validation, and terminal rendering support.
Essential for:
- Terminal emulators calculating emoji display widths
- Text editors with emoji input support
- Layout engines handling emoji sequences
- Grapheme cluster segmentation around emoji
go get github.com/SCKelemen/unicode/v6/uts51package main
import (
"fmt"
"github.com/SCKelemen/unicode/v6/uts51"
)
func main() {
// Check if character is emoji
if uts51.IsEmoji('😀') {
fmt.Println("Is emoji!")
}
// Check default presentation
if uts51.HasEmojiPresentation('😀') {
fmt.Println("Displays as colorful emoji by default")
}
// Calculate width for terminal rendering
width := uts51.EmojiWidth('😀') // Returns 2 (like CJK characters)
}UTS #51 defines six core properties (§1.4):
Characters recommended for emoji use.
uts51.IsEmoji('😀') // true
uts51.IsEmoji('#') // true - can be used in keycap sequences
uts51.IsEmoji('A') // falseCharacters that display as emoji (colorful) by default.
uts51.HasEmojiPresentation('😀') // true - emoji by default
uts51.HasEmojiPresentation('☺') // false - text by default (needs U+FE0F)Skin tone modifiers (U+1F3FB..U+1F3FF).
uts51.IsEmojiModifier('\U0001F3FB') // true - light skin toneCharacters that accept skin tone modifiers.
uts51.IsEmojiModifierBase('👋') // true - waving hand can have skin tone
uts51.IsEmojiModifierBase('😀') // false - faces don't have skin tonesCharacters used in emoji sequences but not standalone.
uts51.IsEmojiComponent('\U0001F3FB') // true - skin toneAll emoji and pictographic characters for segmentation.
uts51.IsExtendedPictographic('😀') // truePer UTS #51 §4, emoji have the same advance width as CJK ideographs (2 columns).
uts51.EmojiWidth('😀') // 2 - emoji presentation
uts51.EmojiWidth('☺') // 1 - text presentation
uts51.EmojiWidth('\U0001F3FB') // 0 - skin tone modifierThis integrates with UAX #11 (East Asian Width) for complete width calculation.
This package works seamlessly with:
- UAX #11 (East Asian Width): Emoji width calculation
- UAX #14 (Line Breaking): Break opportunities around emoji
- UAX #29 (Text Segmentation): Grapheme cluster boundaries
- UAX #50 (Vertical Text Layout): Emoji orientation in vertical text
IsEmoji(r rune) bool- Has Emoji propertyHasEmojiPresentation(r rune) bool- Displays as emoji by defaultIsEmojiModifier(r rune) bool- Is a skin tone modifierIsEmojiModifierBase(r rune) bool- Accepts modifiersIsEmojiComponent(r rune) bool- Used in sequencesIsExtendedPictographic(r rune) bool- For segmentation
DefaultPresentation(r rune) rune- Returns 'E' or 'T'EmojiWidth(r rune) int- Display width in columns
IsRegionalIndicator(r rune) bool- For flag sequencesIsTagCharacter(r rune) bool- For subdivision flags
IsValidKeycapSequence(runes []rune) bool- Validates keycap sequences ([0-9#*] + U+FE0F + U+20E3)IsValidTagSequence(runes []rune) bool- Validates tag sequences (subdivision flags)IsValidEmojiSequence(runes []rune) bool- Validates any emoji sequence type
VariationSelector15 // U+FE0E - text presentation
VariationSelector16 // U+FE0F - emoji presentation
ZeroWidthJoiner // U+200D - joins emoji
CombiningEnclosingKeycap // U+20E3 - keycap sequences100% conformance with UTS #51 Version 17.0
- 5,223/5,223 test cases passing from emoji-test.txt
- All 6 emoji properties correctly implemented
- Complete sequence validation (keycap, tag, modifier, flag, ZWJ sequences)
Per UTS #51 §5:
- ✅ C1: Version 17.0 identification
- ✅ C2: Display capability for basic emoji set
- ✅ C3: Rejection of invalid sequences
- emoji-data.txt Version 17.0 (2025-07-25)
- emoji-test.txt with 5,223 test cases
- Downloaded from: https://www.unicode.org/Public/emoji/latest/
- Binary search for O(log n) property lookups
- Efficient range-based data structure
- Zero external dependencies beyond Go standard library
cd uts51
go run generate_emoji_data.gofunc renderEmoji(text string) {
for _, r := range text {
width := uts51.EmojiWidth(r)
if width == 2 {
// Emoji occupies 2 columns
renderWideChar(r)
} else if width == 1 {
// Text presentation
renderNarrowChar(r)
}
// width == 0: invisible component
}
}// Force emoji presentation
text := "☺" + string(uts51.VariationSelector16) // ☺️
// Force text presentation
text := "😀" + string(uts51.VariationSelector15) // Text versionfunc analyzeEmoji(r rune) {
if !uts51.IsEmoji(r) {
return
}
if uts51.HasEmojiPresentation(r) {
fmt.Println("Colorful emoji by default")
}
if uts51.IsEmojiModifierBase(r) {
fmt.Println("Can have skin tone")
}
}// Validate a keycap sequence
keycap := []rune{'9', '\uFE0F', '\u20E3'} // 9⃣
if uts51.IsValidKeycapSequence(keycap) {
fmt.Println("Valid keycap sequence")
}
// Validate a tag sequence (subdivision flag)
englandFlag := []rune{0x1F3F4, 0xE0067, 0xE0062, 0xE0065, 0xE006E, 0xE0067, 0xE007F}
if uts51.IsValidTagSequence(englandFlag) {
fmt.Println("Valid subdivision flag")
}
// Validate any emoji sequence
sequence := []rune{0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F467} // Family ZWJ sequence
if uts51.IsValidEmojiSequence(sequence) {
fmt.Println("Valid emoji sequence")
}- Full grapheme cluster segmentation (UAX #29 integration)
- Emoji version detection per character
- RGI (Recommended for General Interchange) emoji set identification
- Sequence width calculation for multi-codepoint emoji
- UTS #51: Unicode Emoji
- §1.4 Emoji Properties
- §2 Emoji Sequences
- §4 Display
- §5 Conformance
- emoji-test.txt
MIT