font-show.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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 StripANSIColors(text string) string {
  31. var re *regexp.Regexp
  32. re, _ = regexp.Compile("\x1b\\[[0-9;]*m")
  33. return re.ReplaceAllString(text, "")
  34. }
  35. func Show(parts []string) {
  36. var reset string = "\x1b[0m"
  37. if len(parts) > 0 {
  38. for _, line := range parts {
  39. fmt.Printf("%s%s\n", door.CP437_to_Unicode(string(line)), reset)
  40. // fmt.Printf("%s%s\n", line, reset)
  41. }
  42. fmt.Println("")
  43. }
  44. }
  45. // Examine character indexes, are there lower case letter? are they unique?
  46. // What are the available characters in this font?
  47. func FontInfo(characters []int) (lower bool, lowerUnique bool, available string) {
  48. // Does it have lowercase?
  49. // Is lowercase unique?
  50. // Available characters in font
  51. // 33 -126
  52. lower = characters[int(rune('a')-33)] != -1
  53. lowerUnique = characters[int(rune('A')-33)] != characters[int(rune('a')-33)]
  54. for idx, offset := range characters {
  55. char := idx + 33
  56. if offset != -1 {
  57. available += string(char)
  58. }
  59. }
  60. return
  61. }
  62. func ShowBlockFontSize(bf *door.BlockFont, text string, width int) {
  63. var output []string
  64. output, _ = bf.Output(text)
  65. if width == 0 || len(output[0]) < width {
  66. Show(output)
  67. return
  68. }
  69. var pos int = 1
  70. for text != "" {
  71. nextPart:
  72. for pos = 1; pos < len(text); pos++ {
  73. output, _ = bf.Output(text[:pos])
  74. if len(output[0]) >= width {
  75. // use previous
  76. output, _ = bf.Output(text[:pos-1])
  77. Show(output)
  78. text = text[pos-1:]
  79. goto nextPart
  80. }
  81. }
  82. output, _ = bf.Output(text)
  83. Show(output)
  84. text = ""
  85. }
  86. }
  87. func ShowBlockFont(name string, bf *door.BlockFont, width int) {
  88. var low, uniq bool
  89. var avail string
  90. low, uniq, avail = FontInfo(bf.Characters)
  91. fmt.Printf("Font: %s (LowerCase %t, Unique Lower %t, [%s]\n", name, low, uniq, avail)
  92. ShowBlockFontSize(bf, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", width)
  93. /*
  94. var output []string
  95. output, _ = bf.Output("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  96. Show(output)
  97. */
  98. if low && uniq {
  99. ShowBlockFontSize(bf, "abcdefghijklmnopqrstuvwxyz", width)
  100. /*
  101. output, _ = bf.Output("abcdefghijklmnopqrstuvwxyz")
  102. Show(output)
  103. */
  104. }
  105. var leftovers string = avail
  106. var reg *regexp.Regexp
  107. reg, _ = regexp.Compile("[a-zA-Z]+")
  108. var left string = reg.ReplaceAllString(leftovers, "")
  109. // output, _ = bf.Output("abcdef")
  110. // Show(output)
  111. if len(left) > 0 {
  112. ShowBlockFontSize(bf, left, width)
  113. /*
  114. output, _ = bf.Output(left)
  115. Show(output)
  116. */
  117. }
  118. }
  119. func ShowColorFontSize(bf *door.ColorFont, text string, width int) {
  120. var output []string
  121. output, _ = bf.Output(text)
  122. if width == 0 || len(output[0]) < width {
  123. Show(output)
  124. return
  125. }
  126. // Guessing doesn't work very well
  127. var pos int = 1
  128. for text != "" {
  129. nextPart:
  130. // fmt.Printf("TEXT: [%s]\n", text)
  131. for pos = 1; pos < len(text); pos++ {
  132. output, _ = bf.Output(text[:pos])
  133. // fmt.Println(pos, len(StripANSIColors(output[0])))
  134. if len(StripANSIColors(output[0])) >= width {
  135. // use previous
  136. output, _ = bf.Output(text[:pos-1])
  137. // fmt.Println("OUT:", text[:pos-1])
  138. Show(output)
  139. text = text[pos-1:]
  140. // fmt.Println("TEXT:", text)
  141. goto nextPart
  142. }
  143. }
  144. output, _ = bf.Output(text)
  145. Show(output)
  146. text = ""
  147. }
  148. /*
  149. var guess int = len(StripANSIColors(output[0])) / len(text)
  150. guess = (width / guess)
  151. for text != "" {
  152. var part string
  153. if guess >= len(text) {
  154. part = text
  155. } else {
  156. part = text[0:guess]
  157. }
  158. output, _ = bf.Output(part)
  159. Show(output)
  160. if guess >= len(text) {
  161. text = ""
  162. } else {
  163. text = text[guess:]
  164. }
  165. }
  166. */
  167. }
  168. func ShowColorFont(name string, cf *door.ColorFont, width int) {
  169. var low, uniq bool
  170. var avail string
  171. low, uniq, avail = FontInfo(cf.Characters)
  172. fmt.Printf("Font: %s (LowerCase %t, Unique Lower %t, [%s]\n", name, low, uniq, avail)
  173. ShowColorFontSize(cf, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", width)
  174. /*
  175. // fmt.Printf("Font: %s\n", name)
  176. var output []string
  177. output, _ = cf.Output("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  178. Show(output)
  179. */
  180. if low && uniq {
  181. ShowColorFontSize(cf, "abcdefghijklmnopqrstuvwxyz", width)
  182. /*
  183. output, _ = cf.Output("abcdefghijklmnopqrstuvwxyz")
  184. Show(output)
  185. */
  186. }
  187. var leftovers string = avail
  188. var reg *regexp.Regexp
  189. reg, _ = regexp.Compile("[a-zA-Z]+")
  190. var left string = reg.ReplaceAllString(leftovers, "")
  191. // output, _ = bf.Output("abcdef")
  192. // Show(output)
  193. if len(left) > 0 {
  194. ShowColorFontSize(cf, left, width)
  195. /*
  196. output, _ = cf.Output(left)
  197. Show(output)
  198. */
  199. }
  200. }
  201. // Example using FontOutput interface
  202. /*
  203. func ShowFont(name string, f FontOutput) {
  204. var low, uniq bool
  205. var avail string
  206. v, ok := f.(BlockFont)
  207. if ok {
  208. low, uniq, avail := FontInfo(v.characters)
  209. }
  210. v, ok = f.(ColorFont)
  211. if ok {
  212. low, uniq, avail := FontInfo(v.characters)
  213. }
  214. // low, uniq, avail := FontInfo((*f).characters)
  215. fmt.Printf("Font: %s (LowerCase %t, Unique Lower %t, [%s]\n", name, low, uniq, avail)
  216. // fmt.Printf("Font: %s\n", name)
  217. output, _ := f.Output("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  218. Show(output)
  219. if low && uniq {
  220. output, _ = f.Output("abcdefghijklmnopqrstuvwxyz")
  221. Show(output)
  222. }
  223. leftovers := avail
  224. reg, _ := regexp.Compile("[a-zA-Z]+")
  225. left := reg.ReplaceAllString(leftovers, "")
  226. // output, _ = bf.Output("abcdef")
  227. // Show(output)
  228. // left := "0123456789!@#$%^&*()-=_+[]{}~"
  229. if len(left) > 0 {
  230. output, _ = f.Output(left)
  231. Show(output)
  232. }
  233. }
  234. */
  235. // displays fonts
  236. func DisplayFonts(filename string, fonts []string, width int) {
  237. f, err := os.Open(filename)
  238. if err != nil {
  239. fmt.Printf("Open(%s): %s\n", filename, err)
  240. panic(err)
  241. }
  242. defer f.Close()
  243. tdfonts := make([]byte, 20)
  244. f.Read(tdfonts)
  245. for true {
  246. fontdef := make([]byte, 4)
  247. read, _ := f.Read(fontdef)
  248. if read != 4 {
  249. break
  250. }
  251. fontname := make([]byte, 13)
  252. f.Read(fontname)
  253. Name := strings.Trim(string(fontname[1:]), "\x00")
  254. // fmt.Printf("Font: %s\n", Name)
  255. f.Read(fontdef)
  256. single := make([]byte, 1)
  257. var FontType int8
  258. binary.Read(f, binary.LittleEndian, &FontType)
  259. // fmt.Printf("Font: %s (type %d)\n", Name, FontType)
  260. f.Read(single) // Spacing
  261. var BlockSize int16
  262. binary.Read(f, binary.LittleEndian, &BlockSize)
  263. letterOffsets := make([]uint16, 94)
  264. binary.Read(f, binary.LittleEndian, &letterOffsets)
  265. if false {
  266. for idx, i := range letterOffsets {
  267. fmt.Printf(" %04X", i)
  268. if (idx+1)%10 == 0 {
  269. fmt.Println("")
  270. }
  271. }
  272. fmt.Println("")
  273. }
  274. data := make([]byte, BlockSize)
  275. binary.Read(f, binary.LittleEndian, &data)
  276. // The problem isn't that the offsets are > BlockSize
  277. // Detect "truncated" fonts...
  278. broken := false
  279. for idx, i := range letterOffsets {
  280. if i != 65535 {
  281. if i >= uint16(BlockSize) {
  282. broken = true
  283. // Mark character offset as not used
  284. letterOffsets[idx] = 65535
  285. // fmt.Printf("offset %d / %x is out of range %d / %x\n", i, i, BlockSize, BlockSize)
  286. }
  287. }
  288. }
  289. if broken {
  290. fmt.Println("FONT is corrupted/truncated. FIX attempted.")
  291. }
  292. if FontFixup(letterOffsets, &data) {
  293. fmt.Printf("Attempting to *FIX* Font %s\n", Name)
  294. }
  295. // Special case where they are asking for all fonts
  296. if len(fonts) == 1 && fonts[0] == "*" {
  297. switch FontType {
  298. case 1:
  299. bf := ExtractBlock(Name, letterOffsets, data)
  300. if len(bf.Characters) == 0 {
  301. fmt.Printf("%s : BLOCK FONT FAIL\n", Name)
  302. } else {
  303. // ShowFont(Name, &bf)
  304. ShowBlockFont(Name, &bf, width)
  305. }
  306. case 2:
  307. cf := ExtractColor(Name, letterOffsets, data)
  308. if len(cf.Characters) == 0 {
  309. fmt.Printf("%s : COLOR FONT FAIL\n", Name)
  310. } else {
  311. // ShowFont(Name, &cf)
  312. ShowColorFont(Name, &cf, width)
  313. }
  314. default:
  315. fmt.Printf("Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
  316. }
  317. } else {
  318. for _, f := range fonts {
  319. if Name == f {
  320. switch FontType {
  321. case 1:
  322. bf := ExtractBlock(Name, letterOffsets, data)
  323. if len(bf.Characters) == 0 {
  324. fmt.Printf("%s : BLOCK FONT FAIL\n", Name)
  325. } else {
  326. ShowBlockFont(Name, &bf, width)
  327. }
  328. case 2:
  329. cf := ExtractColor(Name, letterOffsets, data)
  330. if len(cf.Characters) == 0 {
  331. fmt.Printf("%s : COLOR FONT FAIL\n", Name)
  332. } else {
  333. ShowColorFont(Name, &cf, width)
  334. }
  335. default:
  336. fmt.Printf("Sorry, I can't handle Font: %s Type %d!\n", Name, FontType)
  337. }
  338. break
  339. }
  340. }
  341. }
  342. }
  343. }
  344. /*
  345. func main() {
  346. fmt.Println("Font-Show - A TDF (TheDraw Font) Viewer.")
  347. var fonts string
  348. var listFonts bool
  349. var allFonts bool
  350. door.Unicode = true
  351. flag.StringVar(&fonts, "f", "", "Font(s) to show")
  352. flag.BoolVar(&allFonts, "a", false, "Show All Fonts")
  353. flag.BoolVar(&listFonts, "l", false, "List Fonts")
  354. flag.Parse()
  355. if flag.NArg() == 0 {
  356. fmt.Println("I need a TDF filename.")
  357. flag.PrintDefaults()
  358. os.Exit(2)
  359. }
  360. var fontList []string
  361. if len(fonts) > 0 {
  362. fontList = strings.Split(fonts, ",")
  363. }
  364. if allFonts {
  365. fontList = make([]string, 0)
  366. fontList = append(fontList, "*")
  367. }
  368. for _, fontfile := range flag.Args() {
  369. if listFonts {
  370. ListFonts(fontfile)
  371. }
  372. if len(fontList) > 0 {
  373. DisplayFonts(fontfile, fontList)
  374. }
  375. }
  376. }
  377. */