123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- package main
- import (
- "encoding/binary"
- "flag"
- "fmt"
- "os"
- "strconv"
- "strings"
- )
- // http://www.roysac.com/blog/2014/04/thedraw-fonts-file-tdf-specifications/
- var Conversion [][2]int
- // copied from tdfont.go, and modified
- func thedraw_to_ansi(c int) int {
- trans := []int{0, 4, 2, 6, 1, 5, 3, 7}
- // 0, 1, 2, 3, 4, 5, 6, 7
- return trans[c]
- }
- func MatchStyle(color byte, look byte) int {
- var match int = 0
- if ((color >> 4) & 0x07) == look {
- // Top
- match |= 1
- }
- if (color & 0x07) == look {
- // Bottom
- match |= 2
- }
- return match
- }
- func PatchColor(color byte, new_color byte, style int) byte {
- var c byte = color
- if style&1 == 1 {
- c = (c & 0x8f) | new_color<<4
- }
- if style&2 == 2 {
- c = (c & 0xf8) | new_color
- }
- return c
- }
- type ColorMap map[[2]int][][2]int
- func Scan(block [][][]byte, find_color int) ColorMap {
- var Targets ColorMap = make(ColorMap, 0)
- // Scan the font looking for the given color FG/BG
- // Covert color code to TheDraw Color
- actual := byte(thedraw_to_ansi(find_color))
- for charIndex := range block {
- for lineIndex := range block[charIndex] {
- var found bool = false
- var patches [][2]int = make([][2]int, 0)
- for offset := 1; offset < len(block[charIndex][lineIndex]); offset += 2 {
- color := block[charIndex][lineIndex][offset]
- style := MatchStyle(color, actual)
- if style != 0 {
- // log.Printf("color: %x actual %x style: %d\n", color, actual, style)
- patches = append(patches, [2]int{offset, style})
- found = true
- }
- }
- if found {
- pos := [2]int{charIndex, lineIndex}
- Targets[pos] = make([][2]int, len(patches))
- copy(Targets[pos], patches)
- /*
- for i := range patches {
- Targets[pos][i] = patches[i]
- }
- */
- // Targets[pos] = patches
- }
- }
- }
- return Targets
- }
- func Modify(block [][][]byte, new_color int, Targets ColorMap) {
- // Covert color code to TheDraw Color
- actual := byte(thedraw_to_ansi(new_color))
- for pos, patch := range Targets {
- for _, p := range patch {
- block[pos[0]][pos[1]][p[0]] = PatchColor(block[pos[0]][pos[1]][p[0]], actual, p[1])
- }
- }
- }
- // Use ExtractColor to get the font -- then output that?
- func OutputColor(writer *os.File, name string, offsets []uint16, data []byte) {
- // fmt.Printf("Extract Color Font: %s\n", name)
- var indexes []int
- var blocks [][][]byte
- var current [][]byte
- var line []byte
- pos := 0
- for pos < len(data) {
- indexes = append(indexes, pos)
- current = make([][]byte, 0)
- line = make([]byte, 0)
- // We don't use these.
- // w = data[pos]
- // h = data[pos+1]
- pos += 2
- // process this character
- for pos < len(data) {
- ch := data[pos]
- pos++
- if ch == 0x00 {
- // end of character
- current = append(current, line)
- blocks = append(blocks, current)
- // current = make([][]byte, 0)
- // line = make([]byte, 0)
- break
- }
- if ch == 0x0d {
- // end of this character line
- current = append(current, line)
- line = make([]byte, 0)
- continue
- }
- if ch == 0x26 {
- // & descender mark
- continue
- }
- line = append(line, ch)
- color := data[pos]
- pos++
- line = append(line, color)
- }
- }
- // offset optimization:
- var single []int
- for _, o := range offsets {
- if o == 65535 {
- single = append(single, -1)
- continue
- }
- for idx, i := range indexes {
- if o == uint16(i) {
- single = append(single, idx)
- break
- }
- }
- }
- /*
- // Handle Names with spaces
- filename := fmt.Sprintf("%s_font.go", strings.Replace(name, " ", "", -1))
- fp, err := os.Create(filename)
- if err != nil {
- panic(err)
- }
- fmt.Printf("Writing: %s\n", filename)
- defer fp.Close()
- writer := bufio.NewWriter(fp)
- */
- // writer := bufio.NewWriter(os.Stdout)
- writer.WriteString("\n// " + name + "\n\n")
- // Name := strings.ToUpper(name)
- Name := strings.Replace(name, " ", "", -1)
- writer.WriteString("func Font" + Name + "() door.ColorFont {\n")
- var output string
- output = "\treturn door.ColorFont{Characters: []int{"
- for _, s := range single {
- output += strconv.Itoa(s) + ", "
- }
- output = output[:len(output)-2] + "},\n"
- writer.WriteString(output)
- // writer.Flush()
- if len(Conversion) > 0 {
- // Color Convert time!
- var Maps []map[[2]int][][2]int = make([]map[[2]int][][2]int, len(Conversion))
- for idx, codes := range Conversion {
- Maps[idx] = Scan(blocks, codes[0])
- }
- for idx, codes := range Conversion {
- Modify(blocks, codes[1], Maps[idx])
- }
- }
- output = "\t\tData: [][][]byte{"
- for _, blk := range blocks {
- output += "{"
- if len(blk) == 0 {
- output += "{},"
- } else {
- for _, inner := range blk {
- // output += text_to_hextext(b) + ","
- output += "{" + byte_to_text(inner) + "}, "
- }
- output = output[:len(output)-2]
- }
- output += "},\n"
- writer.WriteString(output)
- output = "\t\t\t"
- }
- writer.WriteString("\t\t}}\n")
- writer.WriteString("}\n")
- // writer.Flush()
- }
- // Use ExtractBlock to get the Font, and then output that?
- func OutputBlock(writer *os.File, name string, offsets []uint16, data []byte) {
- // fmt.Printf("Extract Block Font: %s\n", name)
- var indexes []int
- var blocks [][][]byte
- var current [][]byte
- var line []byte
- pos := 0
- for pos < len(data) {
- indexes = append(indexes, pos)
- current = make([][]byte, 0)
- line = make([]byte, 0)
- // We don't use these
- // w = data[pos]
- // h = data[pos+1]
- pos += 2
- // process this character
- for pos < len(data) {
- ch := data[pos]
- pos++
- if ch == 0x00 {
- // end of character
- current = append(current, line)
- blocks = append(blocks, current)
- // current = make([][]byte, 0)
- // line = make([]byte, 0)
- break
- }
- if ch == 0x0d {
- // end of this character line
- current = append(current, line)
- line = make([]byte, 0)
- continue
- }
- if ch == 0x26 {
- // & descender mark
- continue
- }
- line = append(line, ch)
- }
- }
- // offset optimization:
- var single []int
- for _, o := range offsets {
- if o == 65535 {
- single = append(single, -1)
- continue
- }
- for idx, i := range indexes {
- if o == uint16(i) {
- single = append(single, idx)
- break
- }
- }
- }
- /*
- // Handle Names with spaces
- filename := fmt.Sprintf("%s_font.go", strings.Replace(name, " ", "", -1))
- fp, err := os.Create(filename)
- if err != nil {
- panic(err)
- }
- fmt.Printf("Writing: %s\n", filename)
- defer fp.Close()
- writer := bufio.NewWriter(fp)
- */
- // writer := bufio.NewWriter(os.Stdout)
- // Should this output routine be part of the BlockFont?
- // I think so!
- // writer.WriteString("package main\n")
- writer.WriteString("// " + name + "\n\n")
- // Name := strings.ToUpper(name)
- Name := strings.Replace(name, " ", "", -1)
- writer.WriteString("func Font" + Name + "() door.BlockFont {\n")
- var output string
- output = " return door.BlockFont{Characters: []int{"
- for _, s := range single {
- output += strconv.Itoa(s) + ", "
- }
- output = output[:len(output)-2] + "},\n"
- writer.WriteString(output)
- // writer.Flush()
- output = " Data: [][][]byte{"
- for _, blk := range blocks {
- output += "{"
- if len(blk) == 0 {
- output += "{},"
- } else {
- for _, inner := range blk {
- output += "{" + byte_to_text(inner) + "},"
- }
- output = output[:len(output)-1]
- }
- output += "},\n"
- // output = output[:len(output)-1]
- // output += "},\n"
- writer.WriteString(output)
- output = " "
- }
- writer.WriteString(" }}\n")
- writer.WriteString("}\n")
- // writer.Flush()
- }
- // outputs fonts
- func OutputFonts(writer *os.File, filename string, fonts []string) {
- f, err := os.Open(filename)
- if err != nil {
- fmt.Printf("Open(%s): %s\n", filename, err)
- panic(err)
- }
- defer f.Close()
- tdfonts := make([]byte, 20)
- f.Read(tdfonts)
- for {
- fontdef := make([]byte, 4)
- read, _ := f.Read(fontdef)
- if read != 4 {
- break
- }
- fontname := make([]byte, 13)
- f.Read(fontname)
- Name := strings.Trim(string(fontname[1:]), "\x00")
- // fmt.Printf("Font: %s\n", Name)
- f.Read(fontdef)
- single := make([]byte, 1)
- var FontType int8
- binary.Read(f, binary.LittleEndian, &FontType)
- // fmt.Printf("Font: %s (type %d)\n", Name, FontType)
- f.Read(single) // Spacing
- var BlockSize int16
- binary.Read(f, binary.LittleEndian, &BlockSize)
- letterOffsets := make([]uint16, 94)
- binary.Read(f, binary.LittleEndian, &letterOffsets)
- if false {
- for idx, i := range letterOffsets {
- fmt.Printf(" %04X", i)
- if (idx+1)%10 == 0 {
- fmt.Println("")
- }
- }
- fmt.Println("")
- }
- data := make([]byte, BlockSize)
- binary.Read(f, binary.LittleEndian, &data)
- // Special case where they are asking for all fonts
- if len(fonts) == 1 && fonts[0] == "*" {
- switch FontType {
- case 1:
- OutputBlock(writer, Name, letterOffsets, data)
- case 2:
- OutputColor(writer, Name, letterOffsets, data)
- default:
- fmt.Printf("// Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
- }
- } else {
- for _, f := range fonts {
- if Name == f {
- switch FontType {
- case 1:
- OutputBlock(writer, Name, letterOffsets, data)
- case 2:
- OutputColor(writer, Name, letterOffsets, data)
- default:
- fmt.Printf("// Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
- }
- break
- }
- }
- }
- }
- }
- // Created so that multiple inputs can be accepted
- type arrayFlags []string
- func (i *arrayFlags) String() string {
- // change this, this example is just to satisfy the interface
- result := ""
- for _, str := range *i {
- if result != "" {
- result += ", "
- }
- result += str
- }
- return result
- }
- func (i *arrayFlags) Set(value string) error {
- *i = append(*i, strings.TrimSpace(value))
- return nil
- }
- func ParseColorConvert(convert arrayFlags) {
- Conversion = make([][2]int, 0)
- if len(convert) > 0 {
- // Something to do
- for _, color := range convert {
- split := strings.Split(color, ",")
- v1, _ := strconv.Atoi(split[0])
- v2, _ := strconv.Atoi(split[1])
- Conversion = append(Conversion, [2]int{v1, v2})
- }
- }
- }
- /*
- I could envision something like this:
- https://blog.ralch.com/articles/golang-subcommands/
- https://stackoverflow.com/questions/23725924/can-gos-flag-package-print-usage
- list (font files)
- show -a -f (font files)
- extract -all -font -color -package -output (font files)
- show - under windows ? :cat_scream:
- */
- func main() {
- var usage func() = func() {
- fmt.Println("Usage: font-util <command> [<args>] fontfile.tdf...")
- fmt.Println(" list - List available fonts")
- fmt.Println(" show - Show fonts")
- fmt.Println(" extract - Extract to source.go file")
- }
- if len(os.Args) == 1 {
- usage()
- return
- }
- var fonts string
- var defaultPackage string = "main"
- var allFonts bool
- var convert arrayFlags
- var output string
- var width int
- var listCommand *flag.FlagSet = flag.NewFlagSet("list", flag.ExitOnError)
- var showCommand *flag.FlagSet = flag.NewFlagSet("show", flag.ExitOnError)
- showCommand.BoolVar(&allFonts, "a", false, "Show All Fonts")
- showCommand.StringVar(&fonts, "f", "", "Fonts to Show font1,font2,font3")
- showCommand.IntVar(&width, "w", 0, "Width to Show fonts")
- var extractCommand *flag.FlagSet = flag.NewFlagSet("extract", flag.ExitOnError)
- extractCommand.BoolVar(&allFonts, "a", false, "Extract All Fonts")
- extractCommand.StringVar(&fonts, "f", "", "Fonts to Extract font1,font2,font3")
- extractCommand.StringVar(&defaultPackage, "p", "main", "Package name to use")
- extractCommand.Var(&convert, "c", "Convert Color to Color n,n")
- extractCommand.StringVar(&output, "o", "", "Output to file")
- switch os.Args[1] {
- case "list":
- listCommand.Parse(os.Args[2:])
- if listCommand.Parsed() {
- if len(listCommand.Args()) == 0 {
- listCommand.Usage()
- fmt.Println("No TDF Font files given.")
- os.Exit(2)
- }
- for _, fontfile := range listCommand.Args() {
- ListFonts(fontfile)
- }
- }
- os.Exit(0)
- case "show":
- showCommand.Parse(os.Args[2:])
- case "extract":
- extractCommand.Parse(os.Args[2:])
- default:
- usage()
- fmt.Printf("%q is not a valid command.\n", os.Args[1])
- os.Exit(2)
- }
- var fontList []string
- if len(fonts) > 0 {
- fontList = strings.Split(fonts, ",")
- }
- if allFonts {
- fontList = make([]string, 0)
- fontList = append(fontList, "*")
- }
- var err error
- if showCommand.Parsed() {
- // Show Fonts
- var exit bool
- if len(fontList) == 0 {
- showCommand.Usage()
- fmt.Println("No Fonts selected.")
- exit = true
- }
- if len(showCommand.Args()) == 0 {
- if !exit {
- showCommand.Usage()
- }
- fmt.Println("No TDF Fonts files given.")
- exit = true
- }
- if exit {
- os.Exit(2)
- }
- for _, fontfile := range showCommand.Args() {
- fmt.Println("FILE:", fontfile)
- DisplayFonts(fontfile, fontList, width)
- }
- }
- if extractCommand.Parsed() {
- // Extract Fonts
- var exit bool
- if len(fontList) == 0 {
- extractCommand.Usage()
- fmt.Println("No Fonts selected.")
- exit = true
- }
- if len(extractCommand.Args()) == 0 {
- if !exit {
- extractCommand.Usage()
- }
- fmt.Println("No TDF Fonts files given.")
- exit = true
- }
- if exit {
- os.Exit(2)
- }
- var saveTo *os.File
- ParseColorConvert(convert)
- // Setup saveTo
- if output == "" {
- saveTo = os.Stdout
- } else {
- saveTo, err = os.Create(output)
- if err != nil {
- fmt.Println("Create", output, "error:", err)
- os.Exit(2)
- }
- }
- fmt.Fprintf(saveTo, "package %s\n\n", defaultPackage)
- fmt.Fprintf(saveTo, "import (\n\t\"red-green/door\"\n)\n")
- for _, fontfile := range extractCommand.Args() {
- OutputFonts(saveTo, fontfile, fontList)
- }
- }
- }
|