|
@@ -0,0 +1,580 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/binary"
|
|
|
+ "flag"
|
|
|
+ "fmt"
|
|
|
+ "os"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+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))
|
|
|
+ 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 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:
|
|
|
+ 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() {
|
|
|
+ if len(os.Args) == 1 {
|
|
|
+ fmt.Println("Usage: font-out <command> [<args>] fontfile.tdf...")
|
|
|
+ fmt.Println(" list - List available fonts")
|
|
|
+ fmt.Println(" show - Show fonts")
|
|
|
+ fmt.Println(" extract - Extract to source.go file")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var fonts string
|
|
|
+ var defaultPackage string = "main"
|
|
|
+ var allFonts bool
|
|
|
+ var convert arrayFlags
|
|
|
+ var output string
|
|
|
+
|
|
|
+ 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")
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ 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:
|
|
|
+ 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
|
|
|
+ if len(fontList) == 0 {
|
|
|
+ fmt.Println("No Fonts selected.")
|
|
|
+ showCommand.Usage()
|
|
|
+ os.Exit(2)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(showCommand.Args()) == 0 {
|
|
|
+ fmt.Println("No TDF Fonts files given.")
|
|
|
+ showCommand.Usage()
|
|
|
+ os.Exit(2)
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, fontfile := range showCommand.Args() {
|
|
|
+ fmt.Println("FILE:", fontfile)
|
|
|
+ DisplayFonts(fontfile, fontList)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if extractCommand.Parsed() {
|
|
|
+ // Extract Fonts
|
|
|
+ if len(fontList) == 0 {
|
|
|
+ fmt.Println("No Fonts selected.")
|
|
|
+ extractCommand.Usage()
|
|
|
+ os.Exit(2)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(extractCommand.Args()) == 0 {
|
|
|
+ fmt.Println("No TDF Fonts files given.")
|
|
|
+ extractCommand.Usage()
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|