func NewFontFace(path string, size float64, fg, bg color.Color) (fontface *FontFace, err error) { var ( font *truetype.Font fontbytes []byte bounds truetype.Bounds context *freetype.Context scale float32 ) if fontbytes, err = ioutil.ReadFile(path); err != nil { return } if font, err = freetype.ParseFont(fontbytes); err != nil { return } bounds = font.Bounds(1) fmt.Printf("bounds %v\n", bounds) context = freetype.NewContext() context.SetFont(font) context.SetFontSize(size) context.SetDPI(72) scale = float32(context.PointToFixed(size) / 64) fontface = &FontFace{ font: font, charw: scale * float32(bounds.XMax-bounds.XMin), charh: scale * float32(bounds.YMax-bounds.YMin), fg: fg, bg: bg, context: context, } return }
func NewFontFace(path string, pixels float32, fg, bg color.Color) (fontface *FontFace, err error) { var ( font *truetype.Font fontbytes []byte bounds fixed.Rectangle26_6 context *freetype.Context points float32 dpi float32 = 96 ) if fontbytes, err = ioutil.ReadFile(path); err != nil { return } if font, err = freetype.ParseFont(fontbytes); err != nil { return } points = pixels * 72 / dpi bounds = font.Bounds(fixed.I(int(pixels))) context = freetype.NewContext() context.SetFont(font) context.SetFontSize(float64(points)) context.SetDPI(float64(dpi)) fontface = &FontFace{ font: font, charw: float32(bounds.Max.X-bounds.Min.X) / 64, charh: float32(bounds.Max.Y-bounds.Min.Y) / 64, offy: float32(bounds.Min.Y) / 64, fg: fg, bg: bg, context: context, } return }
// enumerate returns all glyphs with a valid index. func enumerate(f *truetype.Font, fc font.Face) []*glyph { var gs []*glyph for r := rune(1); r < (1<<16)-1; r++ { if r == '\uFEFF' { continue // ignore BOM } if f.Index(r) != 0 { b, a, _ := fc.GlyphBounds(r) gs = append(gs, &glyph{r: r, b: b, a: a}) } } return gs }
func createTextImage(text string, fontSize int, color color.Color, f *truetype.Font) (*image.RGBA, float32, float32, error) { // 1 pt = 1/72 in, 72 dpi = 1 in const dpi = 72 fg, bg := image.NewUniform(color), image.Transparent c := freetype.NewContext() c.SetFont(f) c.SetDPI(dpi) c.SetFontSize(float64(fontSize)) // points c.SetSrc(fg) c.SetHinting(font.HintingFull) // 1. Figure out maximum height so all text lines are the same height. // 2. Draw within small bounds to figure out bounds. // 3. Draw within final bounds. scale := c.PointToFixed(float64(fontSize)) // point to pixels rect := f.Bounds(scale) // scale is pixels of 1 em maxHeight := int(rect.Max.Y>>6) - int(rect.Min.Y>>6) var rgba *image.RGBA w, h := 10, maxHeight for i := 0; i < 2; i++ { rgba = image.NewRGBA(image.Rect(0, 0, w, h)) draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) c.SetClip(rgba.Bounds()) c.SetDst(rgba) pt := freetype.Pt(0, int(scale>>6)) end, err := c.DrawString(text, pt) if err != nil { return nil, 0, 0, err } w = int(end.X >> 6) } return rgba, float32(w), float32(h), nil }
func loadRanges(f *truetype.Font) (ret [][2]rune) { rr := [2]rune{-1, -1} for r := rune(0); r <= unicode.MaxRune; r++ { if privateUseArea(r) { continue } if f.Index(r) == 0 { continue } if rr[1] == r { rr[1] = r + 1 continue } if rr[0] != -1 { ret = append(ret, rr) } rr = [2]rune{r, r + 1} } if rr[0] != -1 { ret = append(ret, rr) } return ret }
// Extents returns the FontExtents for a font. // TODO needs to read this https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#intro func Extents(font *truetype.Font, size float64) FontExtents { bounds := font.Bounds(fixed.Int26_6(font.FUnitsPerEm())) scale := size / float64(font.FUnitsPerEm()) return FontExtents{ Ascent: float64(bounds.YMax) * scale, Descent: float64(bounds.YMin) * scale, Height: float64(bounds.YMax-bounds.YMin) * scale, } }
// https://code.google.com/p/plotinum/source/browse/vg/font.go#160 func widthOfString(font *truetype.Font, size float64, s string) float64 { // scale converts truetype.FUnit to float64 scale := size / float64(font.FUnitsPerEm()) width := 0 prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := font.Index(rune) if hasPrev { width += int(font.Kern(fixed.Int26_6(font.FUnitsPerEm()), prev, index)) } width += int(font.HMetric(fixed.Int26_6(font.FUnitsPerEm()), index).AdvanceWidth) prev, hasPrev = index, true } return float64(width) * scale }