package freetype import ( "fmt" "os" "path/filepath" "strconv" "strings" ) type emoji struct { Codepoint []rune IsEmoji bool Path string Sub emojiTable } func (e emoji) String() (str string) { str = "Emoji(" for _, cprune := range e.Codepoint { str += fmt.Sprintf("%U ", cprune) } str = strings.TrimRight(str, " ") + ")" if e.Sub != nil { str += "+" } if e.IsEmoji { str += fmt.Sprintf("\n └ Path: %s", e.Path) } return str + "\n" } type emojiTable map[rune]emoji func (em emojiTable) Find(str string) (emj emoji, length int) { length = 0 for i, r := range str { e, ok := em[r] if !ok { break } // Check if there are more bytes to check if len(str) > i && e.Sub != nil { newemj, len := e.Sub.Find(str[i:]) if len > 0 { length += len emj = newemj return } } if e.IsEmoji { emj = e } length++ } return } func (em emojiTable) IsEmoji(cp rune) bool { _, ok := em[cp] return ok } func (em emojiTable) tostring(indent string, nomarker bool) (str string) { counter := len(em) marker := "│ " if nomarker { marker = " " } for r, emo := range em { listr := "├" if counter == 1 { listr = "└" } str += fmt.Sprintf(indent+"%s%s %U", marker, listr, r) if emo.IsEmoji { str += fmt.Sprintf(": %s", emo.Path) } str += "\n" if emo.Sub != nil { str += emo.Sub.tostring(indent+marker, counter == 1) } counter-- } return } func (em emojiTable) String() string { return "Emoji table\n" + em.tostring("", true) } func scanEmojiDirectory(emojipath string) (tab emojiTable, err error) { tab = make(emojiTable) filepath.Walk(emojipath, func(path string, info os.FileInfo, err error) error { // Ignore non-images if !strings.HasSuffix(strings.ToLower(path), ".png") { return nil } // Get icon filename emojiname := filepath.Base(path) // Strip prefix and suffix extsep := strings.LastIndexByte(emojiname, '.') basesep := strings.IndexByte(emojiname, '_') codepointstr := emojiname[basesep+2 : extsep] // Split codepoints by separator (_) codepointlist := strings.Split(codepointstr, "_") // Parse codepoints to runes curtab := &tab codepoints := []rune{} for cpi, cpstr := range codepointlist { num, err := strconv.ParseInt(cpstr, 16, 32) if err != nil { return fmt.Errorf("malformed icon filename: %s (codepoints are not valid int32)", emojiname) } cprune := rune(num) newemo := (*curtab)[cprune] codepoints = append(codepoints, cprune) newemo.Codepoint = codepoints[:] if len(codepointlist) < cpi+2 { // Set as emoji newemo.IsEmoji = true newemo.Path = path } else { // Add sub-entry newemo.Sub = make(emojiTable) } (*curtab)[cprune] = newemo curtab = &newemo.Sub } fmt.Println(codepointstr) return nil }) return tab, err }