font-out.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. package main
  2. import (
  3. "encoding/binary"
  4. "flag"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. "strings"
  9. )
  10. var Conversion [][2]int
  11. // copied from tdfont.go, and modified
  12. func thedraw_to_ansi(c int) int {
  13. trans := []int{0, 4, 2, 6, 1, 5, 3, 7}
  14. // 0, 1, 2, 3, 4, 5, 6, 7
  15. return trans[c]
  16. }
  17. func MatchStyle(color byte, look byte) int {
  18. var match int = 0
  19. if ((color >> 4) & 0x07) == look {
  20. // Top
  21. match |= 1
  22. }
  23. if (color & 0x07) == look {
  24. // Bottom
  25. match |= 2
  26. }
  27. return match
  28. }
  29. func PatchColor(color byte, new_color byte, style int) byte {
  30. var c byte = color
  31. if style&1 == 1 {
  32. c = (c & 0x8f) | new_color<<4
  33. }
  34. if style&2 == 2 {
  35. c = (c & 0xf8) | new_color
  36. }
  37. return c
  38. }
  39. type ColorMap map[[2]int][][2]int
  40. func Scan(block [][][]byte, find_color int) ColorMap {
  41. var Targets ColorMap = make(ColorMap, 0)
  42. // Scan the font looking for the given color FG/BG
  43. // Covert color code to TheDraw Color
  44. actual := byte(thedraw_to_ansi(find_color))
  45. for charIndex := range block {
  46. for lineIndex := range block[charIndex] {
  47. var found bool = false
  48. var patches [][2]int = make([][2]int, 0)
  49. for offset := 1; offset < len(block[charIndex][lineIndex]); offset += 2 {
  50. color := block[charIndex][lineIndex][offset]
  51. style := MatchStyle(color, actual)
  52. if style != 0 {
  53. // log.Printf("color: %x actual %x style: %d\n", color, actual, style)
  54. patches = append(patches, [2]int{offset, style})
  55. found = true
  56. }
  57. }
  58. if found {
  59. pos := [2]int{charIndex, lineIndex}
  60. Targets[pos] = make([][2]int, len(patches))
  61. for i := range patches {
  62. Targets[pos][i] = patches[i]
  63. }
  64. // Targets[pos] = patches
  65. }
  66. }
  67. }
  68. return Targets
  69. }
  70. func Modify(block [][][]byte, new_color int, Targets ColorMap) {
  71. // Covert color code to TheDraw Color
  72. actual := byte(thedraw_to_ansi(new_color))
  73. for pos, patch := range Targets {
  74. for _, p := range patch {
  75. block[pos[0]][pos[1]][p[0]] = PatchColor(block[pos[0]][pos[1]][p[0]], actual, p[1])
  76. }
  77. }
  78. }
  79. // Use ExtractColor to get the font -- then output that?
  80. func OutputColor(writer *os.File, name string, offsets []uint16, data []byte) {
  81. // fmt.Printf("Extract Color Font: %s\n", name)
  82. var indexes []int
  83. var blocks [][][]byte
  84. var current [][]byte
  85. var line []byte
  86. pos := 0
  87. for pos < len(data) {
  88. indexes = append(indexes, pos)
  89. current = make([][]byte, 0)
  90. line = make([]byte, 0)
  91. // We don't use these.
  92. // w = data[pos]
  93. // h = data[pos+1]
  94. pos += 2
  95. // process this character
  96. for pos < len(data) {
  97. ch := data[pos]
  98. pos++
  99. if ch == 0x00 {
  100. // end of character
  101. current = append(current, line)
  102. blocks = append(blocks, current)
  103. current = make([][]byte, 0)
  104. line = make([]byte, 0)
  105. break
  106. }
  107. if ch == 0x0d {
  108. // end of this character line
  109. current = append(current, line)
  110. line = make([]byte, 0)
  111. continue
  112. }
  113. if ch == 0x26 {
  114. // & descender mark
  115. continue
  116. }
  117. line = append(line, ch)
  118. color := data[pos]
  119. pos++
  120. line = append(line, color)
  121. }
  122. }
  123. // offset optimization:
  124. var single []int
  125. for _, o := range offsets {
  126. if o == 65535 {
  127. single = append(single, -1)
  128. continue
  129. }
  130. for idx, i := range indexes {
  131. if o == uint16(i) {
  132. single = append(single, idx)
  133. break
  134. }
  135. }
  136. }
  137. /*
  138. // Handle Names with spaces
  139. filename := fmt.Sprintf("%s_font.go", strings.Replace(name, " ", "", -1))
  140. fp, err := os.Create(filename)
  141. if err != nil {
  142. panic(err)
  143. }
  144. fmt.Printf("Writing: %s\n", filename)
  145. defer fp.Close()
  146. writer := bufio.NewWriter(fp)
  147. */
  148. // writer := bufio.NewWriter(os.Stdout)
  149. writer.WriteString("\n// " + name + "\n\n")
  150. // Name := strings.ToUpper(name)
  151. Name := strings.Replace(name, " ", "", -1)
  152. writer.WriteString("func Font" + Name + "() door.ColorFont {\n")
  153. var output string
  154. output = "\treturn door.ColorFont{Characters: []int{"
  155. for _, s := range single {
  156. output += strconv.Itoa(s) + ", "
  157. }
  158. output = output[:len(output)-2] + "},\n"
  159. writer.WriteString(output)
  160. // writer.Flush()
  161. if len(Conversion) > 0 {
  162. // Color Convert time!
  163. var Maps []map[[2]int][][2]int = make([]map[[2]int][][2]int, len(Conversion))
  164. for idx, codes := range Conversion {
  165. Maps[idx] = Scan(blocks, codes[0])
  166. }
  167. for idx, codes := range Conversion {
  168. Modify(blocks, codes[1], Maps[idx])
  169. }
  170. }
  171. output = "\t\tData: [][][]byte{"
  172. for _, blk := range blocks {
  173. output += "{"
  174. if len(blk) == 0 {
  175. output += "{},"
  176. } else {
  177. for _, inner := range blk {
  178. // output += text_to_hextext(b) + ","
  179. output += "{" + byte_to_text(inner) + "}, "
  180. }
  181. output = output[:len(output)-2]
  182. }
  183. output += "},\n"
  184. writer.WriteString(output)
  185. output = "\t\t\t"
  186. }
  187. writer.WriteString("\t\t}}\n")
  188. writer.WriteString("}\n")
  189. // writer.Flush()
  190. }
  191. // Use ExtractBlock to get the Font, and then output that?
  192. func OutputBlock(writer *os.File, name string, offsets []uint16, data []byte) {
  193. // fmt.Printf("Extract Block Font: %s\n", name)
  194. var indexes []int
  195. var blocks [][][]byte
  196. var current [][]byte
  197. var line []byte
  198. pos := 0
  199. for pos < len(data) {
  200. indexes = append(indexes, pos)
  201. current = make([][]byte, 0)
  202. line = make([]byte, 0)
  203. // We don't use these
  204. // w = data[pos]
  205. // h = data[pos+1]
  206. pos += 2
  207. // process this character
  208. for pos < len(data) {
  209. ch := data[pos]
  210. pos++
  211. if ch == 0x00 {
  212. // end of character
  213. current = append(current, line)
  214. blocks = append(blocks, current)
  215. current = make([][]byte, 0)
  216. line = make([]byte, 0)
  217. break
  218. }
  219. if ch == 0x0d {
  220. // end of this character line
  221. current = append(current, line)
  222. line = make([]byte, 0)
  223. continue
  224. }
  225. if ch == 0x26 {
  226. // & descender mark
  227. continue
  228. }
  229. line = append(line, ch)
  230. }
  231. }
  232. // offset optimization:
  233. var single []int
  234. for _, o := range offsets {
  235. if o == 65535 {
  236. single = append(single, -1)
  237. continue
  238. }
  239. for idx, i := range indexes {
  240. if o == uint16(i) {
  241. single = append(single, idx)
  242. break
  243. }
  244. }
  245. }
  246. /*
  247. // Handle Names with spaces
  248. filename := fmt.Sprintf("%s_font.go", strings.Replace(name, " ", "", -1))
  249. fp, err := os.Create(filename)
  250. if err != nil {
  251. panic(err)
  252. }
  253. fmt.Printf("Writing: %s\n", filename)
  254. defer fp.Close()
  255. writer := bufio.NewWriter(fp)
  256. */
  257. // writer := bufio.NewWriter(os.Stdout)
  258. // Should this output routine be part of the BlockFont?
  259. // I think so!
  260. // writer.WriteString("package main\n")
  261. writer.WriteString("// " + name + "\n\n")
  262. // Name := strings.ToUpper(name)
  263. Name := strings.Replace(name, " ", "", -1)
  264. writer.WriteString("func Font" + Name + "() door.BlockFont {\n")
  265. var output string
  266. output = " return door.BlockFont{Characters: []int{"
  267. for _, s := range single {
  268. output += strconv.Itoa(s) + ", "
  269. }
  270. output = output[:len(output)-2] + "},\n"
  271. writer.WriteString(output)
  272. // writer.Flush()
  273. output = " Data: [][][]byte{"
  274. for _, blk := range blocks {
  275. output += "{"
  276. if len(blk) == 0 {
  277. output += "{},"
  278. } else {
  279. for _, inner := range blk {
  280. output += "{" + byte_to_text(inner) + "},"
  281. }
  282. output = output[:len(output)-1]
  283. }
  284. output += "},\n"
  285. // output = output[:len(output)-1]
  286. // output += "},\n"
  287. writer.WriteString(output)
  288. output = " "
  289. }
  290. writer.WriteString(" }}\n")
  291. writer.WriteString("}\n")
  292. // writer.Flush()
  293. }
  294. // outputs fonts
  295. func OutputFonts(writer *os.File, filename string, fonts []string) {
  296. f, err := os.Open(filename)
  297. if err != nil {
  298. fmt.Printf("Open(%s): %s\n", filename, err)
  299. panic(err)
  300. }
  301. defer f.Close()
  302. tdfonts := make([]byte, 20)
  303. f.Read(tdfonts)
  304. for true {
  305. fontdef := make([]byte, 4)
  306. read, _ := f.Read(fontdef)
  307. if read != 4 {
  308. break
  309. }
  310. fontname := make([]byte, 13)
  311. f.Read(fontname)
  312. Name := strings.Trim(string(fontname[1:]), "\x00")
  313. // fmt.Printf("Font: %s\n", Name)
  314. f.Read(fontdef)
  315. single := make([]byte, 1)
  316. var FontType int8
  317. binary.Read(f, binary.LittleEndian, &FontType)
  318. // fmt.Printf("Font: %s (type %d)\n", Name, FontType)
  319. f.Read(single) // Spacing
  320. var BlockSize int16
  321. binary.Read(f, binary.LittleEndian, &BlockSize)
  322. letterOffsets := make([]uint16, 94)
  323. binary.Read(f, binary.LittleEndian, &letterOffsets)
  324. if false {
  325. for idx, i := range letterOffsets {
  326. fmt.Printf(" %04X", i)
  327. if (idx+1)%10 == 0 {
  328. fmt.Println("")
  329. }
  330. }
  331. fmt.Println("")
  332. }
  333. data := make([]byte, BlockSize)
  334. binary.Read(f, binary.LittleEndian, &data)
  335. // Special case where they are asking for all fonts
  336. if len(fonts) == 1 && fonts[0] == "*" {
  337. switch FontType {
  338. case 1:
  339. OutputBlock(writer, Name, letterOffsets, data)
  340. case 2:
  341. OutputColor(writer, Name, letterOffsets, data)
  342. default:
  343. fmt.Printf("// Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
  344. }
  345. } else {
  346. for _, f := range fonts {
  347. if Name == f {
  348. switch FontType {
  349. case 1:
  350. OutputBlock(writer, Name, letterOffsets, data)
  351. case 2:
  352. OutputColor(writer, Name, letterOffsets, data)
  353. default:
  354. fmt.Printf("// Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
  355. }
  356. break
  357. }
  358. }
  359. }
  360. }
  361. }
  362. // Created so that multiple inputs can be accepted
  363. type arrayFlags []string
  364. func (i *arrayFlags) String() string {
  365. // change this, this example is just to satisfy the interface
  366. result := ""
  367. for _, str := range *i {
  368. if result != "" {
  369. result += ", "
  370. }
  371. result += str
  372. }
  373. return result
  374. }
  375. func (i *arrayFlags) Set(value string) error {
  376. *i = append(*i, strings.TrimSpace(value))
  377. return nil
  378. }
  379. func ParseColorConvert(convert arrayFlags) {
  380. Conversion = make([][2]int, 0)
  381. if len(convert) > 0 {
  382. // Something to do
  383. for _, color := range convert {
  384. split := strings.Split(color, ",")
  385. v1, _ := strconv.Atoi(split[0])
  386. v2, _ := strconv.Atoi(split[1])
  387. Conversion = append(Conversion, [2]int{v1, v2})
  388. }
  389. }
  390. }
  391. /*
  392. I could envision something like this:
  393. https://blog.ralch.com/articles/golang-subcommands/
  394. https://stackoverflow.com/questions/23725924/can-gos-flag-package-print-usage
  395. list (font files)
  396. show -a -f (font files)
  397. extract -all -font -color -package -output (font files)
  398. show - under windows ? :cat_scream:
  399. */
  400. func main() {
  401. var usage func() = func() {
  402. fmt.Println("Usage: font-util <command> [<args>] fontfile.tdf...")
  403. fmt.Println(" list - List available fonts")
  404. fmt.Println(" show - Show fonts")
  405. fmt.Println(" extract - Extract to source.go file")
  406. }
  407. if len(os.Args) == 1 {
  408. usage()
  409. return
  410. }
  411. var fonts string
  412. var defaultPackage string = "main"
  413. var allFonts bool
  414. var convert arrayFlags
  415. var output string
  416. var width int
  417. var listCommand *flag.FlagSet = flag.NewFlagSet("list", flag.ExitOnError)
  418. var showCommand *flag.FlagSet = flag.NewFlagSet("show", flag.ExitOnError)
  419. showCommand.BoolVar(&allFonts, "a", false, "Show All Fonts")
  420. showCommand.StringVar(&fonts, "f", "", "Fonts to Show font1,font2,font3")
  421. showCommand.IntVar(&width, "w", 0, "Width to Show fonts")
  422. var extractCommand *flag.FlagSet = flag.NewFlagSet("extract", flag.ExitOnError)
  423. extractCommand.BoolVar(&allFonts, "a", false, "Extract All Fonts")
  424. extractCommand.StringVar(&fonts, "f", "", "Fonts to Extract font1,font2,font3")
  425. extractCommand.StringVar(&defaultPackage, "p", "main", "Package name to use")
  426. extractCommand.Var(&convert, "c", "Convert Color to Color n,n")
  427. extractCommand.StringVar(&output, "o", "", "Output to file")
  428. switch os.Args[1] {
  429. case "list":
  430. listCommand.Parse(os.Args[2:])
  431. if listCommand.Parsed() {
  432. if len(listCommand.Args()) == 0 {
  433. listCommand.Usage()
  434. fmt.Println("No TDF Font files given.")
  435. os.Exit(2)
  436. }
  437. for _, fontfile := range listCommand.Args() {
  438. ListFonts(fontfile)
  439. }
  440. }
  441. os.Exit(0)
  442. case "show":
  443. showCommand.Parse(os.Args[2:])
  444. case "extract":
  445. extractCommand.Parse(os.Args[2:])
  446. default:
  447. usage()
  448. fmt.Printf("%q is not a valid command.\n", os.Args[1])
  449. os.Exit(2)
  450. }
  451. var fontList []string
  452. if len(fonts) > 0 {
  453. fontList = strings.Split(fonts, ",")
  454. }
  455. if allFonts {
  456. fontList = make([]string, 0)
  457. fontList = append(fontList, "*")
  458. }
  459. var err error
  460. if showCommand.Parsed() {
  461. // Show Fonts
  462. var exit bool
  463. if len(fontList) == 0 {
  464. showCommand.Usage()
  465. fmt.Println("No Fonts selected.")
  466. exit = true
  467. }
  468. if len(showCommand.Args()) == 0 {
  469. if !exit {
  470. showCommand.Usage()
  471. }
  472. fmt.Println("No TDF Fonts files given.")
  473. exit = true
  474. }
  475. if exit {
  476. os.Exit(2)
  477. }
  478. for _, fontfile := range showCommand.Args() {
  479. fmt.Println("FILE:", fontfile)
  480. DisplayFonts(fontfile, fontList, width)
  481. }
  482. }
  483. if extractCommand.Parsed() {
  484. // Extract Fonts
  485. var exit bool
  486. if len(fontList) == 0 {
  487. extractCommand.Usage()
  488. fmt.Println("No Fonts selected.")
  489. exit = true
  490. }
  491. if len(extractCommand.Args()) == 0 {
  492. if !exit {
  493. extractCommand.Usage()
  494. }
  495. fmt.Println("No TDF Fonts files given.")
  496. exit = true
  497. }
  498. if exit {
  499. os.Exit(2)
  500. }
  501. var saveTo *os.File
  502. ParseColorConvert(convert)
  503. // Setup saveTo
  504. if output == "" {
  505. saveTo = os.Stdout
  506. } else {
  507. saveTo, err = os.Create(output)
  508. if err != nil {
  509. fmt.Println("Create", output, "error:", err)
  510. os.Exit(2)
  511. }
  512. }
  513. fmt.Fprintf(saveTo, "package %s\n\n", defaultPackage)
  514. fmt.Fprintf(saveTo, "import (\n\t\"red-green/door\"\n)\n")
  515. for _, fontfile := range extractCommand.Args() {
  516. OutputFonts(saveTo, fontfile, fontList)
  517. }
  518. }
  519. }