github.com/jmigpin/editor@v1.6.0/util/fontutil/fontsmanager.go (about) 1 package fontutil 2 3 import ( 4 "github.com/golang/freetype/truetype" 5 "golang.org/x/image/font" 6 "golang.org/x/image/font/gofont/goregular" 7 "golang.org/x/image/math/fixed" 8 ) 9 10 //---------- 11 12 var DPI float64 // default: github.com/golang/freetype/truetype/face.go:31:3 13 var FontsMan = NewFontsManager() 14 15 func DefaultFont() *Font { 16 f, err := FontsMan.Font(goregular.TTF) 17 if err != nil { 18 panic(err) 19 } 20 return f 21 } 22 23 func DefaultFontFace() *FontFace { 24 f := DefaultFont() 25 opt := truetype.Options{} // defaults: size=12, dpi=72, ~14px 26 return f.FontFace(opt) 27 } 28 29 //---------- 30 31 type FontsManager struct { 32 fontsCache map[string]*Font 33 } 34 35 func NewFontsManager() *FontsManager { 36 fm := &FontsManager{} 37 fm.ClearFontsCache() 38 return fm 39 } 40 41 func (fm *FontsManager) ClearFontsCache() { 42 fm.fontsCache = map[string]*Font{} 43 } 44 45 func (fm *FontsManager) Font(ttf []byte) (*Font, error) { 46 f, ok := fm.fontsCache[string(ttf)] 47 if ok { 48 return f, nil 49 } 50 f, err := NewFont(ttf) 51 if err != nil { 52 return nil, err 53 } 54 fm.fontsCache[string(ttf)] = f 55 return f, nil 56 } 57 58 //---------- 59 60 type Font struct { 61 Font *truetype.Font 62 facesCache map[truetype.Options]*FontFace 63 } 64 65 func NewFont(ttf []byte) (*Font, error) { 66 font, err := truetype.Parse(ttf) 67 if err != nil { 68 return nil, err 69 } 70 f := &Font{Font: font} 71 f.ClearFacesCache() 72 return f, nil 73 } 74 75 func (f *Font) ClearFacesCache() { 76 f.facesCache = map[truetype.Options]*FontFace{} 77 } 78 79 func (f *Font) FontFace(opt truetype.Options) *FontFace { 80 if opt.DPI == 0 { 81 opt.DPI = DPI 82 } 83 ff, ok := f.facesCache[opt] 84 if ok { 85 return ff 86 } 87 ff = NewFontFace(f, opt) 88 f.facesCache[opt] = ff 89 return ff 90 } 91 92 func (f *Font) FontFace2(size float64) *FontFace { 93 opt := truetype.Options{Size: size} 94 return f.FontFace(opt) 95 } 96 97 //---------- 98 99 type FontFace struct { 100 Font *Font 101 Face font.Face 102 Size float64 // in points, readonly 103 Metrics *font.Metrics 104 lineHeight fixed.Int26_6 105 } 106 107 func NewFontFace(font *Font, opt truetype.Options) *FontFace { 108 face := truetype.NewFace(font.Font, &opt) 109 face = NewFaceRunes(face) 110 // TODO: allow cache choice 111 //face = NewFaceCache(face) // can safely be used only in ui loop (read) 112 face = NewFaceCacheL(face) // safe for concurrent calls 113 //face = NewFaceCacheL2(face) 114 115 ff := &FontFace{Font: font, Face: face} 116 m := face.Metrics() 117 ff.Metrics = &m 118 ff.lineHeight = ff.calcLineHeight() 119 120 ff.Size = opt.Size 121 if ff.Size == 0 { 122 // github.com/golang/freetype/truetype/face.go:26:2 123 ff.Size = 12 124 } 125 126 return ff 127 } 128 129 func (ff *FontFace) calcLineHeight() fixed.Int26_6 { 130 // TODO: failing: m.Height 131 m := ff.Metrics 132 lh := m.Ascent + m.Descent 133 return fixed.I(lh.Ceil()) // ceil for stable lines 134 } 135 136 func (ff *FontFace) LineHeight() fixed.Int26_6 { 137 return ff.lineHeight 138 } 139 func (ff *FontFace) LineHeightInt() int { 140 return ff.LineHeight().Floor() 141 } 142 func (ff *FontFace) LineHeightFloat() float64 { 143 return Fixed266ToFloat64(ff.LineHeight()) 144 } 145 146 func (ff *FontFace) BaseLine() fixed.Point26_6 { 147 return fixed.Point26_6{0, ff.Metrics.Ascent} 148 }