| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 | package mainimport (	"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 modifiedfunc 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]intfunc 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 fontsfunc 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 acceptedtype arrayFlags []stringfunc (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-usagelist   (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)		}	}}
 |