// GetStringBounds returns the approximate pixel bounds of the string s at x, y. // The the left edge of the em square of the first character of s // and the baseline intersect at 0, 0 in the returned coordinates. // Therefore the top and left coordinates may well be negative. func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) { f, err := gc.loadCurrentFont() if err != nil { log.Println(err) return 0, 0, 0, 0 } top, left, bottom, right = 10e6, 10e6, -10e6, -10e6 cursor := 0.0 prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := f.Index(rune) if hasPrev { cursor += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index)) } if err := gc.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), index, font.HintingNone); err != nil { log.Println(err) return 0, 0, 0, 0 } e0 := 0 for _, e1 := range gc.glyphBuf.Ends { ps := gc.glyphBuf.Points[e0:e1] for _, p := range ps { x, y := pointToF64Point(p) top = math.Min(top, y) bottom = math.Max(bottom, y) left = math.Min(left, x+cursor) right = math.Max(right, x+cursor) } } cursor += fUnitsToFloat64(f.HMetric(fixed.Int26_6(gc.Current.Scale), index).AdvanceWidth) prev, hasPrev = index, true } return left, top, right, bottom }
// 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.Max.Y) * scale, Descent: float64(bounds.Min.Y) * scale, Height: float64(bounds.Max.Y-bounds.Min.Y) * scale, } }
func (gc *GraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error { if err := gc.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), glyph, font.HintingNone); err != nil { return err } e0 := 0 for _, e1 := range gc.glyphBuf.Ends { DrawContour(gc, gc.glyphBuf.Points[e0:e1], dx, dy) e0 = e1 } return nil }
// CreateStringPath creates a path from the string s at x, y, and returns the string width. // The text is placed so that the left edge of the em square of the first character of s // and the baseline intersect at x, y. The majority of the affected pixels will be // above and to the right of the point, but some may be below or to the left. // For example, drawing a string that starts with a 'J' in an italic font may // affect pixels below and left of the point. func (gc *GraphicContext) CreateStringPath(s string, x, y float64) float64 { f, err := gc.loadCurrentFont() if err != nil { log.Println(err) return 0.0 } startx := x prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := f.Index(rune) if hasPrev { x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index)) } err := gc.drawGlyph(index, x, y) if err != nil { log.Println(err) return startx - x } x += fUnitsToFloat64(f.HMetric(fixed.Int26_6(gc.Current.Scale), index).AdvanceWidth) prev, hasPrev = index, true } return x - startx }