github.com/racerxdl/gonx@v0.0.0-20210103083128-c5afc43bcbd2/font/generate.go (about) 1 // +build ignore 2 3 // This program generates fontname.go. It can be invoked by running 4 // go generate 5 package main 6 7 import ( 8 "fmt" 9 "go/format" 10 "io/ioutil" 11 "os" 12 "path" 13 "path/filepath" 14 "regexp" 15 "strconv" 16 "strings" 17 ) 18 19 var ( 20 charRegex = regexp.MustCompile(`STARTCHAR([\S\s]*?)ENDCHAR`) 21 boundBoxRegex = regexp.MustCompile(`FONTBOUNDINGBOX (.*)`) 22 ) 23 24 func hexByteToBytes(hexByte string) ([]byte, error) { 25 numBits := len(hexByte) * 4 // Hex string chars = nibbles 26 v, err := strconv.ParseUint(hexByte, 16, numBits) 27 if err != nil { 28 fmt.Printf("Error parsing %q as number: %s\n", hexByte, err) 29 return nil, err 30 } 31 bits := make([]byte, 0) 32 33 for i := numBits - 1; i >= 0; i-- { 34 mask := 1 << uint(i) 35 if v&uint64(mask) > 0 { 36 bits = append(bits, 1) 37 } else { 38 bits = append(bits, 0) 39 } 40 } 41 42 return bits, nil 43 } 44 45 func buildGlyph(glyphInfo string) (uint32, string) { 46 glyphFragment := "" 47 lines := strings.Split(glyphInfo, "\n") 48 49 var data [][]byte 50 name := "" 51 encoding := uint32(0) 52 53 for _, v := range lines { 54 switch { 55 case strings.Contains(v, "STARTCHAR"): 56 name = strings.Trim(v[len("STARTCHAR"):], " \r\n") 57 //fmt.Println("GLYPH NAME", name) 58 case strings.Contains(v, "ENCODING"): 59 enc := strings.Trim(v[len("ENCODING"):], " \r\n") 60 enc64, err := strconv.ParseUint(enc, 10, 32) 61 if err != nil { 62 panic(err) 63 } 64 encoding = uint32(enc64) 65 //fmt.Println("ENCODING", encoding) 66 case strings.Contains(v, "BITMAP"): 67 case strings.Contains(v, "SWIDTH"): 68 case strings.Contains(v, "DWIDTH"): 69 case strings.Contains(v, "BBX"): 70 case strings.Contains(v, "BITMAP"): 71 case strings.Contains(v, "ENDCHAR"): 72 default: 73 rowData, err := hexByteToBytes(v) 74 if err != nil { 75 continue 76 } 77 data = append(data, rowData) 78 } 79 } 80 81 //fmt.Println(name, encoding, data) 82 83 contentFragment := "" 84 85 for _, v := range data { 86 frag := "{" 87 for z, b := range v { 88 frag += fmt.Sprintf(" %d", b) 89 if z != len(v)-1 { 90 frag += "," 91 } 92 } 93 frag += " },\n" 94 contentFragment += frag 95 } 96 97 glyphFragment = fmt.Sprintf(`&Glyph{ 98 Name: "%s", 99 Data: [][]byte{ 100 %s 101 }, 102 }`, name, contentFragment) 103 104 z, err := format.Source([]byte(glyphFragment)) 105 106 if err != nil { 107 panic(err) 108 } 109 110 glyphFragment = string(z) 111 112 return encoding, glyphFragment 113 } 114 115 func buildFont(tpl, name, bdf string) string { 116 name = strings.Replace(name, " ", "_", -1) 117 name = strings.Replace(name, "-", "_", -1) 118 name = strings.Replace(name, ".", "_", -1) 119 tpl = strings.Replace(tpl, "_FONTNAME_", name, -1) 120 121 boundBox := strings.Split(boundBoxRegex.FindString(bdf), " ")[1:] 122 tpl = strings.Replace(tpl, "_FONTWIDTH_", boundBox[0], -1) 123 tpl = strings.Replace(tpl, "_FONTHEIGHT_", boundBox[1], -1) 124 tpl = strings.Replace(tpl, "_FONTXOFF_", boundBox[2], -1) 125 tpl = strings.Replace(tpl, "_FONTYOFF_", boundBox[3], -1) 126 127 chars := charRegex.FindAllStringSubmatch(bdf, -1) 128 129 glyphList := "" 130 for _, v := range chars { 131 encoding, frag := buildGlyph(v[0]) 132 glyphList += fmt.Sprintf("%d: %s,\n", encoding, frag) 133 } 134 135 tpl = strings.Replace(tpl, "_GLYPHS_", "\n"+glyphList, -1) 136 137 z, err := format.Source([]byte(tpl)) 138 139 if err != nil { 140 fmt.Printf("Error formatting generated code: %s\n%s\n", err, string(tpl)) 141 panic(err) 142 } 143 144 return string(z) 145 } 146 147 func main() { 148 var files []string 149 150 root := "." 151 err := filepath.Walk(root, func(f string, info os.FileInfo, err error) error { 152 if strings.Contains(f, ".bdf") { 153 files = append(files, f) 154 } 155 return nil 156 }) 157 if err != nil { 158 panic(err) 159 } 160 161 tplB, err := ioutil.ReadFile("font.templ.go") 162 if err != nil { 163 panic(err) 164 } 165 166 tpl := string(tplB) 167 168 tpl = strings.Replace(tpl, "//+build ignore\n\n", "", -1) // Remove build ignore 169 170 for _, file := range files { 171 fmt.Printf("Building font %s\n", file) 172 fontData, err := ioutil.ReadFile(file) 173 if err != nil { 174 panic(err) 175 } 176 177 fontName := strings.Replace(strings.ToLower(path.Base(file)), ".bdf", "", -1) 178 bdf := string(fontData) 179 resultFile := buildFont(tpl, fontName, bdf) 180 filename := "font_gen_" + fontName + ".go" 181 err = ioutil.WriteFile(filename, []byte(resultFile), 0660) 182 if err != nil { 183 panic(err) 184 } 185 } 186 }