// 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) { font, 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 := font.Index(rune) if hasPrev { cursor += fUnitsToFloat64(int32(font.Kern(fixed.Int26_6(gc.Current.Scale), prev, index))) } if err := gc.glyphBuf.Load(gc.Current.Font, fixed.Int26_6(gc.Current.Scale), index, expfont.HintingNone); err != nil { log.Println(err) return 0, 0, 0, 0 } e0 := 0 for _, e1 := range gc.glyphBuf.End { ps := gc.glyphBuf.Point[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(int32(font.HMetric(fixed.Int26_6(gc.Current.Scale), index).AdvanceWidth)) prev, hasPrev = index, true } return left, top, right, bottom }
func (t *Text) getFontMetrics(scale int) FontMetrics { // Adapted from: https://code.google.com/p/plotinum/ // Converts truetype.FUnit to float64 fUnit2Float64 := float64(t.size) / float64(t.font.FUnitsPerEm()) width := 0 prev, hasPrev := truetype.Index(0), false for _, rune := range t.content { index := t.font.Index(rune) if hasPrev { width += int(t.font.Kerning(t.font.FUnitsPerEm(), prev, index)) } width += int(t.font.HMetric(t.font.FUnitsPerEm(), index).AdvanceWidth) prev, hasPrev = index, true } widthFloat := float64(width) * fUnit2Float64 * float64(scale) bounds := t.font.Bounds(t.font.FUnitsPerEm()) height := float64(bounds.YMax-bounds.YMin) * fUnit2Float64 * float64(scale) ascent := float64(bounds.YMax) * fUnit2Float64 * float64(scale) descent := float64(bounds.YMin) * fUnit2Float64 * float64(scale) return FontMetrics{widthFloat, height, ascent, descent} }
// DrawString draws s at p and returns p advanced by the text extent. The text // is placed so that the left edge of the em square of the first character of s // and the baseline intersect at p. 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. // p is a raster.Point and can therefore represent sub-pixel positions. func (c *Context) DrawString(s string, p raster.Point) (raster.Point, error) { if c.font == nil { return raster.Point{}, errors.New("freetype: DrawText called with a nil font") } prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := c.font.Index(rune) if hasPrev { kern := raster.Fix32(c.font.Kerning(c.scale, prev, index)) << 2 if c.hinting != NoHinting { kern = (kern + 128) &^ 255 } p.X += kern } advanceWidth, mask, offset, err := c.glyph(index, p) if err != nil { return raster.Point{}, err } p.X += advanceWidth glyphRect := mask.Bounds().Add(offset) dr := c.clip.Intersect(glyphRect) if !dr.Empty() { mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y} draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over) } prev, hasPrev = index, true } return p, nil }
// DrawString draws s at p and returns p advanced by the text extent. The text // is placed so that the left edge of the em square of the first character of s // and the baseline intersect at p. 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. // // p is a fixed.Point26_6 and can therefore represent sub-pixel positions. func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) { if c.f == nil { return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font") } prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := c.f.Index(rune) if hasPrev { kern := c.f.Kern(c.scale, prev, index) if c.hinting != font.HintingNone { kern = (kern + 32) &^ 63 } p.X += kern } advanceWidth, mask, offset, err := c.glyph(index, p) if err != nil { return fixed.Point26_6{}, err } p.X += advanceWidth glyphRect := mask.Bounds().Add(offset) dr := c.clip.Intersect(glyphRect) if !dr.Empty() { mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y} draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over) } prev, hasPrev = index, true } return p, nil }
// Width returns width of a string when drawn using the font. func (f *Font) Width(s string) Length { // scale converts truetype.FUnit to float64 scale := f.Size / Points(float64(f.font.FUnitsPerEm())) width := 0 prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := f.font.Index(rune) if hasPrev { width += int(f.font.Kerning(f.font.FUnitsPerEm(), prev, index)) } width += int(f.font.HMetric(f.font.FUnitsPerEm(), index).AdvanceWidth) prev, hasPrev = index, true } return Points(float64(width)) * 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 }
// 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 { font, 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 := font.Index(rune) if hasPrev { x += fUnitsToFloat64(int32(font.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(int32(font.HMetric(fixed.Int26_6(gc.Current.Scale), index).AdvanceWidth)) prev, hasPrev = index, true } return x - startx }