face.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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 pkf
  5. import (
  6. "image"
  7. "golang.org/x/image/font"
  8. "golang.org/x/image/math/fixed"
  9. tfix "modernc.org/knuth/font/fixed"
  10. "modernc.org/knuth/font/tfm"
  11. )
  12. // Face implements the font.Face interface for PK fonts.
  13. type Face struct {
  14. font *Font
  15. tfm *tfm.Font
  16. scale fixed.Int26_6
  17. glyphs map[rune]int
  18. }
  19. // FaceOptions describes the possible options given to NewFace when
  20. // creating a new Face from a Font.
  21. type FaceOptions struct {
  22. Size float64 // Size is the font size in DVI points.
  23. DPI float64 // DPI is the dots per inch resolution
  24. }
  25. func defaultFaceOptions(font *tfm.Font) *FaceOptions {
  26. return &FaceOptions{
  27. Size: font.DesignSize().Float64(),
  28. DPI: 72,
  29. }
  30. }
  31. func NewFace(font *Font, metrics *tfm.Font, opts *FaceOptions) *Face {
  32. if opts == nil {
  33. opts = defaultFaceOptions(metrics)
  34. }
  35. return &Face{
  36. font: font,
  37. tfm: metrics,
  38. scale: fixed.Int26_6(0.5 + (opts.Size * opts.DPI * 64 / 72)),
  39. glyphs: make(map[rune]int, len(font.glyphs)/4),
  40. }
  41. }
  42. // xscale returns x divided by unitsPerEm, rounded to the nearest fixed.Int26_6
  43. // value (1/64th of a pixel).
  44. func xscale(x fixed.Int26_6, unitsPerEm Units) fixed.Int26_6 {
  45. u := fixed.Int26_6(unitsPerEm)
  46. v := u / 2
  47. switch {
  48. case x >= 0:
  49. x += v
  50. default:
  51. x -= v
  52. }
  53. return x / u
  54. }
  55. // Close satisfies the font.Face interface.
  56. func (*Face) Close() error {
  57. return nil
  58. }
  59. // Name returns the name of the font face.
  60. func (face *Face) Name() string {
  61. return face.tfm.Name()
  62. }
  63. // Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's
  64. // glyph at the sub-pixel destination location dot, and that glyph's
  65. // advance width.
  66. //
  67. // It returns !ok if the face does not contain a glyph for r.
  68. //
  69. // The contents of the mask image returned by one Glyph call may change
  70. // after the next Glyph call. Callers that want to cache the mask must make
  71. // a copy.
  72. func (face *Face) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
  73. g, ok := face.glyph(r)
  74. if !ok {
  75. return
  76. }
  77. g.unpack()
  78. advance, ok = face.glyphAdvance(r, g)
  79. if !ok {
  80. return
  81. }
  82. dr = image.Rect(
  83. -int(g.xoff),
  84. -int(g.yoff),
  85. -int(g.xoff)+int(g.width),
  86. -int(g.yoff)+int(g.height),
  87. ).Add(image.Pt(dot.X.Floor(), dot.Y.Floor()))
  88. msk := g.Mask()
  89. mask = &msk
  90. ok = true
  91. return
  92. }
  93. // GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal
  94. // to the origin, and that glyph's advance width.
  95. //
  96. // It returns !ok if the face does not contain a glyph for r.
  97. //
  98. // The glyph's ascent and descent are equal to -bounds.Min.Y and
  99. // +bounds.Max.Y. The glyph's left-side and right-side bearings are equal
  100. // to bounds.Min.X and advance-bounds.Max.X. A visual depiction of what
  101. // these metrics are is at
  102. // https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png
  103. func (face *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
  104. g, ok := face.glyph(r)
  105. if !ok {
  106. return
  107. }
  108. return face.glyphBounds(r, g)
  109. }
  110. func (face *Face) glyphBounds(r rune, g *Glyph) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
  111. bounds, _, ok = face.tfm.GlyphBounds(r)
  112. if !ok {
  113. return
  114. }
  115. advance, ok = face.glyphAdvance(r, g)
  116. if !ok {
  117. return
  118. }
  119. em := face.font.UnitsPerEm()
  120. rescale := func(v fixed.Int26_6) fixed.Int26_6 {
  121. v *= fixed.Int26_6(em)
  122. v /= 1 << 6
  123. return v
  124. }
  125. bounds.Min.X = rescale(bounds.Min.X)
  126. bounds.Min.Y = rescale(bounds.Min.Y)
  127. bounds.Max.X = rescale(bounds.Max.X)
  128. bounds.Max.Y = rescale(bounds.Max.Y)
  129. bounds.Min.X = xscale(bounds.Min.X*face.scale, em)
  130. bounds.Min.Y = xscale(bounds.Min.Y*face.scale, em)
  131. bounds.Max.X = xscale(bounds.Max.X*face.scale, em)
  132. bounds.Max.Y = xscale(bounds.Max.Y*face.scale, em)
  133. dx := tfix.Int12_20(g.dx).ToInt26_6()
  134. dy := tfix.Int12_20(g.dy).ToInt26_6()
  135. bounds.Min.X += dx
  136. bounds.Max.X -= dx
  137. bounds.Min.Y += dy // FIXME(sbinet): check sign of vertical displacement
  138. bounds.Max.Y -= dy // FIXME(sbinet): check sign of vertical displacement
  139. return bounds, advance, ok
  140. }
  141. // GlyphAdvance returns the advance width of r's glyph.
  142. //
  143. // It returns !ok if the face does not contain a glyph for r.
  144. func (face *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
  145. g, ok := face.glyph(r)
  146. if !ok {
  147. return
  148. }
  149. return face.glyphAdvance(r, g)
  150. }
  151. func (face *Face) glyphAdvance(r rune, g *Glyph) (advance fixed.Int26_6, ok bool) {
  152. advance, ok = face.tfm.GlyphAdvance(r)
  153. if !ok {
  154. return 0, false
  155. }
  156. em := face.font.UnitsPerEm()
  157. advance *= fixed.Int26_6(em) // FIXME(sbinet): by trial and error.
  158. advance /= 1 << 6 // figure out why we need this.
  159. advance = xscale(advance*face.scale, em)
  160. return advance, true
  161. }
  162. // Kern returns the horizontal adjustment for the kerning pair (r0, r1). A
  163. // positive kern means to move the glyphs further apart.
  164. func (face *Face) Kern(r0, r1 rune) fixed.Int26_6 {
  165. k := face.tfm.Kern(r0, r1)
  166. return xscale(k*face.scale, face.font.UnitsPerEm())
  167. }
  168. // Metrics returns the metrics for this Face.
  169. func (face *Face) Metrics() font.Metrics {
  170. em := face.font.UnitsPerEm()
  171. met := face.tfm.Metrics()
  172. rescale := func(v fixed.Int26_6) fixed.Int26_6 {
  173. v *= fixed.Int26_6(em)
  174. v /= 1 << 6
  175. return v
  176. }
  177. met.Height = rescale(met.Height)
  178. met.Ascent = rescale(met.Ascent)
  179. met.Descent = rescale(met.Descent)
  180. met.XHeight = rescale(met.XHeight)
  181. met.CapHeight = rescale(met.CapHeight)
  182. met.Height = xscale(met.Height*face.scale, em)
  183. met.Ascent = xscale(met.Ascent*face.scale, em)
  184. met.Descent = xscale(met.Descent*face.scale, em)
  185. met.XHeight = xscale(met.XHeight*face.scale, em)
  186. met.CapHeight = xscale(met.CapHeight*face.scale, em)
  187. return met
  188. }
  189. func (face *Face) glyph(r rune) (*Glyph, bool) {
  190. if i, ok := face.glyphs[r]; ok {
  191. return &face.font.glyphs[i], true
  192. }
  193. i := face.font.index(r)
  194. if i < 0 {
  195. return nil, false
  196. }
  197. face.glyphs[r] = i
  198. return &face.font.glyphs[i], true
  199. }
  200. var (
  201. _ font.Face = (*Face)(nil)
  202. )