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  }