package main import ( "bufio" "encoding/binary" "flag" "fmt" "os" "strconv" "strings" ) // TODO: // Add ability to load font into struct and render func ListFonts(filename 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 true { fontdef := make([]byte, 4) read, _ := f.Read(fontdef) if read != 4 { fmt.Println("*END*") 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) // f.Read(single) // FontType // FontType := int(single[0]) fmt.Printf("Font: %s (type %d)\n", Name, FontType) f.Read(single) // Spacing // blocksize := make([]byte, 2) // f.Read(blocksize) var BlockSize int16 binary.Read(f, binary.LittleEndian, &BlockSize) // fmt.Printf("Size: %d / %x\n", BlockSize, BlockSize) letterOffsets := make([]int16, 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) } } func byte_to_text(line []byte) string { var output string for _, ch := range line { output += fmt.Sprintf("0x%02x,", ch) } if len(output) > 0 { output = output[:len(output)-1] } return output } func text_to_hextext(line string) string { var output string // output = "\"" for _, ch := range []byte(line) { // output += fmt.Sprintf("\\x%02x", ch) output += fmt.Sprintf("0x%02x,", ch) } if len(output) > 1 { output = output[:len(output)-1] } // output += "\"" return output } func ExtractColor(name string, offsets []uint16, data []byte) { fmt.Printf("Extract Color Font: %s\n", name) var indexes []int var blocks [][][]byte var current [][]byte // w := -1 // h := -1 // ch := -1 // color := -1 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) } } // the old, sloppy way /* for idx, b := range data { if w == -1 { indexes = append(indexes, idx) current = make([][]byte, 0) line = make([]byte, 0) w = int(b) continue } if h == -1 { h = int(b) continue } if ch == -1 { ch = int(b) if ch == 0x0d { ch = -1 current = append(current, line) line = make([]byte, 0) continue } if ch == 0 { current = append(current, line) blocks = append(blocks, current) current = make([][]byte, 0) line = make([]byte, 0) w = -1 h = -1 ch = -1 continue } if ch == 0x26 { // & descender mark ch = -1 continue } } else { color = int(b) line = append(line, byte(ch)) line = append(line, byte(color)) ch = -1 } } */ // 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.WriteString("package main\n") writer.WriteString("// " + name + "\n\n") // Name := strings.ToUpper(name) Name := strings.Replace(name, " ", "", -1) writer.WriteString("func Font" + Name + "() ColorFont {\n") var output string output = " return ColorFont{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 += text_to_hextext(b) + "," output += "{" + byte_to_text(inner) + "}," } output = output[:len(output)-1] } output += "},\n" writer.WriteString(output) output = " " } writer.WriteString(" }}\n") writer.WriteString("}\n") writer.Flush() } func ExtractBlock(name string, offsets []uint16, data []byte) { fmt.Printf("Extract Block Font: %s\n", name) var indexes []int var blocks [][][]byte var current [][]byte // w := -1 // h := -1 // ch := -1 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) } } /* for idx, b := range data { if w == -1 { indexes = append(indexes, idx) current = make([][]byte, 0) line = make([]byte, 0) w = int(b) continue } if h == -1 { h = int(b) continue } if ch == -1 { ch = int(b) if ch == 0x0d { ch = -1 current = append(current, line) line = make([]byte, 0) continue } if ch == 0 { current = append(current, line) blocks = append(blocks, current) current = make([][]byte, 0) line = make([]byte, 0) w = -1 h = -1 ch = -1 continue } if ch == 0x26 { // & descender mark ch = -1 continue } line = append(line, byte(ch)) ch = -1 } } */ // 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) // 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 + "() BlockFont {\n") var output string output = " return 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() } func ExtractFonts(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 true { 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: ExtractBlock(Name, letterOffsets, data) case 2: ExtractColor(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: ExtractBlock(Name, letterOffsets, data) case 2: ExtractColor(Name, letterOffsets, data) default: fmt.Printf("Sorry, I can't handle Font: %s Type %d!\n", Name, FontType) } break } } } } } func main() { fmt.Println("Font-Out - A TDF (TheDraw Font) file processor.") var fontfile string var fonts string var defaultPackage string = "main" var listFonts bool var allFonts bool flag.StringVar(&fontfile, "tdf", "", "TheDraw Font File") flag.StringVar(&fonts, "f", "", "Font(s) to extract") flag.BoolVar(&allFonts, "a", false, "Extract All Fonts") flag.StringVar(&defaultPackage, "p", "main", "Package name to use") flag.BoolVar(&listFonts, "l", false, "List Fonts") flag.Parse() if len(fontfile) == 0 { fmt.Println("I need a TDF filename.") flag.PrintDefaults() os.Exit(2) } if listFonts { ListFonts(fontfile) } var fontList []string if len(fonts) > 0 { fontList = strings.Split(fonts, ",") } if allFonts { fontList = make([]string, 0) fontList = append(fontList, "*") } if len(fontList) > 0 { ExtractFonts(fontfile, fontList) } }