font-show.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package main
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "os"
  6. "red-green/door"
  7. "regexp"
  8. "strings"
  9. )
  10. // Best used with | less -RS
  11. // Attempt to fix broken fonts.
  12. // This verifies that the character offsets are proceeded
  13. // by a null character.
  14. func FontFixup(offsets []uint16, data *[]byte) bool {
  15. fixed := false
  16. for _, offset := range offsets {
  17. if offset == 65535 {
  18. continue
  19. }
  20. if offset == 0 {
  21. continue
  22. }
  23. if (*data)[offset-1] != 0 {
  24. (*data)[offset-1] = 0
  25. fixed = true
  26. }
  27. }
  28. return fixed
  29. }
  30. func Show(parts []string) {
  31. reset := "\x1b[0m"
  32. if len(parts) > 0 {
  33. for _, line := range parts {
  34. fmt.Printf("%s%s\n", door.CP437_to_Unicode(string(line)), reset)
  35. // fmt.Printf("%s%s\n", line, reset)
  36. }
  37. fmt.Println("")
  38. }
  39. }
  40. // Examine character indexes, are there lower case letter? are they unique?
  41. // What are the available characters in this font?
  42. func FontInfo(characters []int) (lower bool, lowerUnique bool, available string) {
  43. // Does it have lowercase?
  44. // Is lowercase unique?
  45. // Available characters in font
  46. // 33 -126
  47. lower = characters[int(rune('a')-33)] != -1
  48. lowerUnique = characters[int(rune('A')-33)] != characters[int(rune('a')-33)]
  49. for idx, offset := range characters {
  50. char := idx + 33
  51. if offset != -1 {
  52. available += string(char)
  53. }
  54. }
  55. return
  56. }
  57. func ShowBlockFont(name string, bf *door.BlockFont) {
  58. low, uniq, avail := FontInfo(bf.Characters)
  59. fmt.Printf("Font: %s (LowerCase %t, Unique Lower %t, [%s]\n", name, low, uniq, avail)
  60. output, _ := bf.Output("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  61. Show(output)
  62. if low && uniq {
  63. output, _ := bf.Output("abcdefghijklmnopqrstuvwxyz")
  64. Show(output)
  65. }
  66. leftovers := avail
  67. reg, _ := regexp.Compile("[a-zA-Z]+")
  68. left := reg.ReplaceAllString(leftovers, "")
  69. // output, _ = bf.Output("abcdef")
  70. // Show(output)
  71. if len(left) > 0 {
  72. output, _ = bf.Output(left)
  73. Show(output)
  74. }
  75. }
  76. func ShowColorFont(name string, cf *door.ColorFont) {
  77. low, uniq, avail := FontInfo(cf.Characters)
  78. fmt.Printf("Font: %s (LowerCase %t, Unique Lower %t, [%s]\n", name, low, uniq, avail)
  79. // fmt.Printf("Font: %s\n", name)
  80. output, _ := cf.Output("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  81. Show(output)
  82. if low && uniq {
  83. output, _ := cf.Output("abcdefghijklmnopqrstuvwxyz")
  84. Show(output)
  85. }
  86. leftovers := avail
  87. reg, _ := regexp.Compile("[a-zA-Z]+")
  88. left := reg.ReplaceAllString(leftovers, "")
  89. // output, _ = bf.Output("abcdef")
  90. // Show(output)
  91. if len(left) > 0 {
  92. output, _ = cf.Output(left)
  93. Show(output)
  94. }
  95. }
  96. // Example using FontOutput interface
  97. /*
  98. func ShowFont(name string, f FontOutput) {
  99. var low, uniq bool
  100. var avail string
  101. v, ok := f.(BlockFont)
  102. if ok {
  103. low, uniq, avail := FontInfo(v.characters)
  104. }
  105. v, ok = f.(ColorFont)
  106. if ok {
  107. low, uniq, avail := FontInfo(v.characters)
  108. }
  109. // low, uniq, avail := FontInfo((*f).characters)
  110. fmt.Printf("Font: %s (LowerCase %t, Unique Lower %t, [%s]\n", name, low, uniq, avail)
  111. // fmt.Printf("Font: %s\n", name)
  112. output, _ := f.Output("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  113. Show(output)
  114. if low && uniq {
  115. output, _ = f.Output("abcdefghijklmnopqrstuvwxyz")
  116. Show(output)
  117. }
  118. leftovers := avail
  119. reg, _ := regexp.Compile("[a-zA-Z]+")
  120. left := reg.ReplaceAllString(leftovers, "")
  121. // output, _ = bf.Output("abcdef")
  122. // Show(output)
  123. // left := "0123456789!@#$%^&*()-=_+[]{}~"
  124. if len(left) > 0 {
  125. output, _ = f.Output(left)
  126. Show(output)
  127. }
  128. }
  129. */
  130. // displays fonts
  131. func DisplayFonts(filename string, fonts []string) {
  132. f, err := os.Open(filename)
  133. if err != nil {
  134. fmt.Printf("Open(%s): %s\n", filename, err)
  135. panic(err)
  136. }
  137. defer f.Close()
  138. tdfonts := make([]byte, 20)
  139. f.Read(tdfonts)
  140. for true {
  141. fontdef := make([]byte, 4)
  142. read, _ := f.Read(fontdef)
  143. if read != 4 {
  144. break
  145. }
  146. fontname := make([]byte, 13)
  147. f.Read(fontname)
  148. Name := strings.Trim(string(fontname[1:]), "\x00")
  149. // fmt.Printf("Font: %s\n", Name)
  150. f.Read(fontdef)
  151. single := make([]byte, 1)
  152. var FontType int8
  153. binary.Read(f, binary.LittleEndian, &FontType)
  154. // fmt.Printf("Font: %s (type %d)\n", Name, FontType)
  155. f.Read(single) // Spacing
  156. var BlockSize int16
  157. binary.Read(f, binary.LittleEndian, &BlockSize)
  158. letterOffsets := make([]uint16, 94)
  159. binary.Read(f, binary.LittleEndian, &letterOffsets)
  160. if false {
  161. for idx, i := range letterOffsets {
  162. fmt.Printf(" %04X", i)
  163. if (idx+1)%10 == 0 {
  164. fmt.Println("")
  165. }
  166. }
  167. fmt.Println("")
  168. }
  169. data := make([]byte, BlockSize)
  170. binary.Read(f, binary.LittleEndian, &data)
  171. // The problem isn't that the offsets are > BlockSize
  172. // Detect "truncated" fonts...
  173. broken := false
  174. for idx, i := range letterOffsets {
  175. if i != 65535 {
  176. if i >= uint16(BlockSize) {
  177. broken = true
  178. // Mark character offset as not used
  179. letterOffsets[idx] = 65535
  180. // fmt.Printf("offset %d / %x is out of range %d / %x\n", i, i, BlockSize, BlockSize)
  181. }
  182. }
  183. }
  184. if broken {
  185. fmt.Println("FONT is corrupted/truncated. FIX attempted.")
  186. }
  187. if FontFixup(letterOffsets, &data) {
  188. fmt.Printf("Attempting to *FIX* Font %s\n", Name)
  189. }
  190. // Special case where they are asking for all fonts
  191. if len(fonts) == 1 && fonts[0] == "*" {
  192. switch FontType {
  193. case 1:
  194. bf := ExtractBlock(Name, letterOffsets, data)
  195. if len(bf.Characters) == 0 {
  196. fmt.Printf("%s : BLOCK FONT FAIL\n", Name)
  197. } else {
  198. // ShowFont(Name, &bf)
  199. ShowBlockFont(Name, &bf)
  200. }
  201. case 2:
  202. cf := ExtractColor(Name, letterOffsets, data)
  203. if len(cf.Characters) == 0 {
  204. fmt.Printf("%s : COLOR FONT FAIL\n", Name)
  205. } else {
  206. // ShowFont(Name, &cf)
  207. ShowColorFont(Name, &cf)
  208. }
  209. default:
  210. fmt.Printf("Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
  211. }
  212. } else {
  213. for _, f := range fonts {
  214. if Name == f {
  215. switch FontType {
  216. case 1:
  217. bf := ExtractBlock(Name, letterOffsets, data)
  218. if len(bf.Characters) == 0 {
  219. fmt.Printf("%s : BLOCK FONT FAIL\n", Name)
  220. } else {
  221. ShowBlockFont(Name, &bf)
  222. }
  223. case 2:
  224. cf := ExtractColor(Name, letterOffsets, data)
  225. if len(cf.Characters) == 0 {
  226. fmt.Printf("%s : COLOR FONT FAIL\n", Name)
  227. } else {
  228. ShowColorFont(Name, &cf)
  229. }
  230. default:
  231. fmt.Printf("Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
  232. }
  233. break
  234. }
  235. }
  236. }
  237. }
  238. }
  239. /*
  240. func main() {
  241. fmt.Println("Font-Show - A TDF (TheDraw Font) Viewer.")
  242. var fonts string
  243. var listFonts bool
  244. var allFonts bool
  245. door.Unicode = true
  246. flag.StringVar(&fonts, "f", "", "Font(s) to show")
  247. flag.BoolVar(&allFonts, "a", false, "Show All Fonts")
  248. flag.BoolVar(&listFonts, "l", false, "List Fonts")
  249. flag.Parse()
  250. if flag.NArg() == 0 {
  251. fmt.Println("I need a TDF filename.")
  252. flag.PrintDefaults()
  253. os.Exit(2)
  254. }
  255. var fontList []string
  256. if len(fonts) > 0 {
  257. fontList = strings.Split(fonts, ",")
  258. }
  259. if allFonts {
  260. fontList = make([]string, 0)
  261. fontList = append(fontList, "*")
  262. }
  263. for _, fontfile := range flag.Args() {
  264. if listFonts {
  265. ListFonts(fontfile)
  266. }
  267. if len(fontList) > 0 {
  268. DisplayFonts(fontfile, fontList)
  269. }
  270. }
  271. }
  272. */