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  }