font.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. // Copyright ©2021 The star-tex Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE-STAR-TEX file.
  4. package tfm
  5. import (
  6. "bytes"
  7. "fmt"
  8. "image"
  9. "io"
  10. "math"
  11. "golang.org/x/image/font"
  12. xfix "golang.org/x/image/math/fixed"
  13. "modernc.org/knuth/font/fixed"
  14. "modernc.org/knuth/internal/iobuf"
  15. )
  16. type fontFamily string
  17. const (
  18. fontTypeVanilla fontFamily = "TEX TEXT"
  19. fontTypeMathSym fontFamily = "TEX MATH SYMBOLS"
  20. fontTypeMathExt fontFamily = "TEX MATH EXTENSION"
  21. )
  22. // Font is a TeX Font metrics.
  23. type Font struct {
  24. hdr fileHeader
  25. body fileBody
  26. metSet bool // metSet tells whether the font metrics have been computed.
  27. metrics font.Metrics
  28. }
  29. type fileHeader struct {
  30. lf uint16 // length of the entire file, in words
  31. lh uint16 // length of the header data, in words
  32. bc uint16 // smallest character code in the font
  33. ec uint16 // largest character code in the font
  34. nw uint16 // number of words in the width table
  35. nh uint16 // number of words in the height table
  36. nd uint16 // number of words in the depth table
  37. ni uint16 // number of words in the italic correction table
  38. nl uint16 // number of words in the lig/kern table
  39. nk uint16 // number of words in the kern table
  40. ne uint16 // number of words in the extensible character table
  41. np uint16 // number of font parameter words
  42. }
  43. // Parse parses a TFM file.
  44. func Parse(r io.Reader) (Font, error) {
  45. var (
  46. fnt Font
  47. rr, err = newReader(r)
  48. )
  49. if err != nil {
  50. return fnt, fmt.Errorf("could not read TFM file: %w", err)
  51. }
  52. err = fnt.readHeader(rr)
  53. if err != nil {
  54. return fnt, fmt.Errorf("could not parse TFM file header: %w", err)
  55. }
  56. err = fnt.readBody(rr)
  57. if err != nil {
  58. return fnt, fmt.Errorf("could not parse TFM file body: %w", err)
  59. }
  60. return fnt, nil
  61. }
  62. func (fnt *Font) DesignSize() fixed.Int12_20 {
  63. return fnt.body.header.designSize
  64. }
  65. func (fnt *Font) CodingScheme() string {
  66. return fnt.body.header.codingScheme
  67. }
  68. func (fnt *Font) Name() string {
  69. return fnt.body.header.fontID
  70. }
  71. // Checksum returns the checksum of the TeX font metrics file.
  72. func (fnt *Font) Checksum() uint32 {
  73. return fnt.body.header.chksum
  74. }
  75. // NumGlyphs returns the number of glyphs in this font.
  76. func (fnt *Font) NumGlyphs() int {
  77. return int(fnt.hdr.ec) + 1 - int(fnt.hdr.bc)
  78. }
  79. // index returns the glyphIndex for the given rune.
  80. //
  81. // index returns -1 if there is no such rune.
  82. func (fnt *Font) index(r rune) glyphIndex {
  83. i := int(r)
  84. if !(int(fnt.hdr.bc) <= i && i <= int(fnt.hdr.ec)) {
  85. panic(fmt.Errorf("glyph out of range"))
  86. }
  87. i -= int(fnt.hdr.bc)
  88. return glyphIndex(i)
  89. }
  90. func (fnt *Font) glyph(x glyphIndex) glyphInfo {
  91. return fnt.body.glyphs[x]
  92. }
  93. // Box returns the width, height and depth of r's glyph.
  94. //
  95. // It returns !ok if the face does not contain a glyph for r.
  96. func (fnt *Font) Box(r rune) (w, h, d fixed.Int12_20, ok bool) {
  97. i := int(r)
  98. if !(int(fnt.hdr.bc) <= i && i <= int(fnt.hdr.ec)) {
  99. return 0, 0, 0, false
  100. }
  101. i -= int(fnt.hdr.bc)
  102. g := fnt.body.glyphs[i]
  103. w = fnt.body.width[g.wd()] // FIXME(sbinet): apply italic correction?
  104. h = fnt.body.height[g.ht()]
  105. d = fnt.body.depth[g.dp()]
  106. return w, h, d, true
  107. }
  108. // GlyphAdvance returns the advance width of r's glyph.
  109. //
  110. // It returns !ok if the face does not contain a glyph for r.
  111. func (fnt *Font) GlyphAdvance(r rune) (xfix.Int26_6, bool) {
  112. i := int(r)
  113. if !(int(fnt.hdr.bc) <= i && i <= int(fnt.hdr.ec)) {
  114. return 0, false
  115. }
  116. i -= int(fnt.hdr.bc)
  117. var (
  118. g = fnt.body.glyphs[i]
  119. ic = fnt.body.italic[g.ic()]
  120. w = fnt.body.width[g.wd()] + ic
  121. )
  122. return w.ToInt26_6(), true
  123. }
  124. // GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
  125. // to the origin, and that glyph's advance width.
  126. //
  127. // It returns !ok if the face does not contain a glyph for r.
  128. //
  129. // The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y. A
  130. // visual depiction of what these metrics are is at
  131. // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png
  132. func (fnt *Font) GlyphBounds(r rune) (bounds xfix.Rectangle26_6, advance xfix.Int26_6, ok bool) {
  133. i := fnt.index(r)
  134. if i < 0 {
  135. return
  136. }
  137. ok = true
  138. g := fnt.body.glyphs[i]
  139. var (
  140. ic = fnt.body.italic[g.ic()]
  141. w = fnt.body.width[g.wd()] + ic
  142. h = fnt.body.height[g.ht()]
  143. d = fnt.body.depth[g.dp()]
  144. )
  145. bounds = xfix.Rectangle26_6{
  146. Min: xfix.Point26_6{
  147. X: 0, // bearing?
  148. Y: -h.ToInt26_6(),
  149. },
  150. Max: xfix.Point26_6{
  151. X: w.ToInt26_6(),
  152. Y: d.ToInt26_6(),
  153. },
  154. }
  155. advance = w.ToInt26_6()
  156. return
  157. }
  158. // Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
  159. // positive kern means to move the glyphs further apart.
  160. func (fnt *Font) Kern(r0, r1 rune) xfix.Int26_6 {
  161. i0 := fnt.index(r0)
  162. if i0 < 0 {
  163. return 0
  164. }
  165. i1 := fnt.index(r1)
  166. if i1 < 0 {
  167. return 0
  168. }
  169. g0 := fnt.glyph(i0)
  170. c1 := int(r1)
  171. if g0.raw[2]&3 == 1 {
  172. ii := int(g0.raw[3])
  173. rr := fnt.body.ligKern[ii]
  174. if rr.raw[0] > 128 {
  175. ii = int(rr.raw[2])*256 + int(rr.raw[3])
  176. }
  177. for {
  178. lk := fnt.body.ligKern[ii]
  179. switch {
  180. case lk.raw[2] >= 128:
  181. if int(lk.raw[1]) == c1 {
  182. rr := lk.nextIndex()
  183. kern := fnt.body.kern[rr]
  184. return kern.ToInt26_6()
  185. }
  186. default:
  187. // ok.
  188. }
  189. switch {
  190. case lk.raw[0] >= 128:
  191. ii = len(fnt.body.ligKern)
  192. default:
  193. ii += int(lk.raw[0]) + 1
  194. }
  195. if ii >= len(fnt.body.ligKern) {
  196. return 0
  197. }
  198. }
  199. }
  200. // FIXME(sbinet): implement it.
  201. return 0
  202. }
  203. // Metrics returns the metrics for this Face.
  204. func (fnt *Font) Metrics() font.Metrics {
  205. if !fnt.metSet {
  206. fnt.metSet = true
  207. fnt.computeMetrics()
  208. }
  209. return fnt.metrics
  210. }
  211. func (fnt *Font) computeMetrics() {
  212. slant := fnt.body.param[0].Float64()
  213. slope := slopeFrom(slant)
  214. var (
  215. caph fixed.Int12_20
  216. asc fixed.Int12_20
  217. desc fixed.Int12_20
  218. )
  219. for _, r := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
  220. idx := fnt.index(r)
  221. if idx < 0 {
  222. continue
  223. }
  224. g := fnt.glyph(idx)
  225. h := fnt.body.height[g.ht()]
  226. if h >= caph {
  227. caph = h
  228. }
  229. }
  230. // FIXME(sbinet): ascender and descender do not seem to be properly inferred
  231. // when using these heuristics.
  232. // for _, r := range "abcdefghijklmnopqrstuvwxyz" {
  233. // idx := fnt.index(r)
  234. // if idx < 0 {
  235. // continue
  236. // }
  237. // g := fnt.glyph(idx)
  238. // h := fnt.body.height[g.ht()]
  239. // d := fnt.body.depth[g.dp()]
  240. // if h > asc {
  241. // asc = h
  242. // }
  243. // if d > desc {
  244. // desc = d
  245. // }
  246. // }
  247. fnt.metrics = font.Metrics{
  248. Ascent: asc.ToInt26_6(),
  249. Descent: desc.ToInt26_6(),
  250. XHeight: fnt.body.param[4].ToInt26_6(),
  251. CapHeight: caph.ToInt26_6(),
  252. CaretSlope: slope,
  253. }
  254. }
  255. func slopeFrom(slant float64) image.Point {
  256. if slant == 0 {
  257. return image.Pt(0, 1)
  258. }
  259. const epsilon = 1e-6
  260. var (
  261. v = math.Abs(slant)
  262. f = 1.0
  263. )
  264. for i := 0; i < 10; i++ {
  265. f = math.Pow10(i)
  266. r := math.Trunc(v * f)
  267. if math.Abs(r-v*f) < epsilon {
  268. break
  269. }
  270. }
  271. return image.Pt(int(f*slant), int(f))
  272. }
  273. func (fnt *Font) readHeader(r *iobuf.Reader) error {
  274. hdr := &fnt.hdr
  275. err := readHeader(r, hdr)
  276. if err != nil {
  277. return fmt.Errorf("could not parse TFM file header: %w", err)
  278. }
  279. if !(int32(hdr.bc)-1 <= int32(hdr.ec) && hdr.ec <= 255) {
  280. return fmt.Errorf("invalid TFM file header glyph code range")
  281. }
  282. if !(hdr.ne <= 256) {
  283. return fmt.Errorf("invalid TFM file header extensible character table")
  284. }
  285. sum := 6 + hdr.lh + (hdr.ec - hdr.bc + 1) + hdr.nw + hdr.nh + hdr.nd + hdr.ni + hdr.nl + hdr.nk + hdr.ne + hdr.np
  286. if hdr.lf != sum {
  287. return fmt.Errorf("invalid TFM file length")
  288. }
  289. return nil
  290. }
  291. type fileBody struct {
  292. header header
  293. glyphs []glyphInfo
  294. width []fixed.Int12_20
  295. height []fixed.Int12_20
  296. depth []fixed.Int12_20
  297. italic []fixed.Int12_20
  298. ligKern []ligKernCmd
  299. kern []fixed.Int12_20
  300. exten []extensible
  301. param []fixed.Int12_20
  302. }
  303. type header struct {
  304. chksum uint32
  305. designSize fixed.Int12_20
  306. codingScheme string
  307. fontID string
  308. sevenBitSafe bool
  309. face byte
  310. extra []fixed.Int12_20
  311. }
  312. func (fnt *Font) readBody(r *iobuf.Reader) error {
  313. fnt.body.header.chksum = r.ReadU32()
  314. fnt.body.header.designSize = fixed.Int12_20(r.ReadU32())
  315. if fnt.hdr.lh > 2 {
  316. fnt.body.header.codingScheme = readStr(r, 40)
  317. fnt.body.header.fontID = readStr(r, 20)
  318. fnt.body.header.sevenBitSafe = r.ReadU8() == 0b1000_0000
  319. _ = r.ReadU16() // unused
  320. fnt.body.header.face = r.ReadU8()
  321. }
  322. if lh := int(fnt.hdr.lh); lh > 18 {
  323. n := lh - 18
  324. fnt.body.header.extra = readFWs(r, n)
  325. }
  326. fnt.body.glyphs = readCharInfos(r, int(fnt.hdr.ec-fnt.hdr.bc+1))
  327. fnt.body.width = readFWs(r, int(fnt.hdr.nw))
  328. fnt.body.height = readFWs(r, int(fnt.hdr.nh))
  329. fnt.body.depth = readFWs(r, int(fnt.hdr.nd))
  330. fnt.body.italic = readFWs(r, int(fnt.hdr.ni))
  331. fnt.body.ligKern = readLigKerns(r, int(fnt.hdr.nl))
  332. fnt.body.kern = readFWs(r, int(fnt.hdr.nk))
  333. fnt.body.exten = readExtens(r, int(fnt.hdr.ne))
  334. fnt.body.param = readFWs(r, int(fnt.hdr.np))
  335. return nil
  336. }
  337. func (fnt *Font) MarshalText() ([]byte, error) {
  338. var (
  339. buf = new(bytes.Buffer)
  340. err = newTextEncoder(buf).encode(fnt)
  341. )
  342. return buf.Bytes(), err
  343. }