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} }
// GetStringBounds returns the approximate pixel bounds of the string s at x, y. // 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 *ImageGraphicContext) 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(font.Kerning(int32(gc.Current.Scale), prev, index)) } if err := gc.glyphBuf.Load(gc.Current.Font, int32(gc.Current.Scale), index, truetype.NoHinting); 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(font.HMetric(int32(gc.Current.Scale), index).AdvanceWidth) prev, hasPrev = index, true } return left, top, right, bottom }
// Implements the Font interface. func (f *TruetypeFont) Measure(i FontIndex) (*GlyphMetrics, error) { // Load into glyph buffer. if f.buf == nil { f.buf = truetype.NewGlyphBuf() } index := truetype.Index(i) scale := f.Font.FUnitsPerEm() err := f.buf.Load(f.Font, scale, index, f.Hinting) if err != nil { return nil, err } hMetric := f.Font.HMetric(scale, index) vMetric := f.Font.VMetric(scale, index) b := f.buf.B return &GlyphMetrics{ Bounds: GlyphBounds{ Min: Point{X: int(b.XMin), Y: int(b.YMin)}, Max: Point{X: int(b.XMax), Y: int(b.YMax)}, }, BearingX: int(hMetric.LeftSideBearing), AdvanceX: int(hMetric.AdvanceWidth), BearingY: int(vMetric.TopSideBearing), AdvanceY: int(vMetric.AdvanceHeight), }, 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 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 { p.X += raster.Fix32(c.font.Kerning(c.scale, prev, index)) << 2 } mask, offset, err := c.glyph(index, p) if err != nil { return raster.Point{}, err } p.X += raster.Fix32(c.font.HMetric(c.scale, index).AdvanceWidth) << 2 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 }
// MeasureString returns the width and height of the string in s, in terms of // raster.Fix32 units. // // BUG(burntsushi): I don't think negative x-coordinates are handled at all, so // that the bounding box could be smaller than what it actually is. (i.e., the // first letter is an italic 'J'.) func (c *Context) MeasureString(s string) (raster.Fix32, raster.Fix32, error) { if c.font == nil { return 0, 0, errors.New("freetype: DrawText called with a nil font") } var width, height, heightMax raster.Fix32 oneLine := c.PointToFix32(c.fontSize) & 0xff height = c.PointToFix32(c.fontSize) prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := c.font.Index(rune) if hasPrev { width += raster.Fix32(c.font.Kerning(c.scale, prev, index)) << 2 } if err := c.glyphBuf.Load(c.font, c.scale, index, truetype.NoHinting); err != nil { return 0, 0, err } ymax := oneLine - raster.Fix32(c.glyphBuf.B.YMin<<2) + 0xff heightMax = max(heightMax, ymax) width += raster.Fix32(c.font.HMetric(c.scale, index).AdvanceWidth) << 2 prev, hasPrev = index, true } if heightMax > 0 { height += heightMax } return width, height, nil }
func (f *font) width(s string) int { em := f.FUnitsPerEm() var width int32 prev, hasPrev := truetype.Index(0), false for _, r := range s { index := f.Index(r) if hasPrev { width += f.Kerning(em, prev, index) } width += f.HMetric(em, index).AdvanceWidth prev, hasPrev = index, true } return int(float64(width)*f.scale() + 0.5) }
// 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 }
func (ig *ImageGraphics) TextLen(s string, font chart.Font) int { c := freetype.NewContext() c.SetDPI(dpi) c.SetFont(ig.font) c.SetFontSize(ig.relFontsizeToPixel(font.Size)) var p raster.Point prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := ig.font.Index(rune) if hasPrev { p.X += c.FUnitToFix32(int(ig.font.Kerning(prev, index))) } p.X += c.FUnitToFix32(int(ig.font.HMetric(index).AdvanceWidth)) prev, hasPrev = index, true } return int((p.X + 127) / 256) }
func (ig *ImageGraphics) TextLen(s string, font chart.Font) int { c := freetype.NewContext() c.SetDPI(dpi) c.SetFont(ig.font) fontsize := ig.relFontsizeToPixel(font.Size) c.SetFontSize(fontsize) scale := int32(fontsize * dpi * (64.0 / 72.0)) var p raster.Point prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := ig.font.Index(rune) if hasPrev { p.X += raster.Fix32(ig.font.Kerning(scale, prev, index)) << 2 } p.X += raster.Fix32(ig.font.HMetric(scale, index).AdvanceWidth) << 2 prev, hasPrev = index, true } return int((p.X + 127) / 256) }
func (f Font) textSize(s string) (int, int, int) { // scale converts truetype.FUnit to float64 scale := f.sz / float64(f.fnt.FUnitsPerEm()) * (dpi / ptInch) width := 0 prev, hasPrev := truetype.Index(0), false for _, rune := range s { index := f.fnt.Index(rune) if hasPrev { width += int(f.fnt.Kerning(f.fnt.FUnitsPerEm(), prev, index)) } width += int(f.fnt.HMetric(f.fnt.FUnitsPerEm(), index).AdvanceWidth) prev, hasPrev = index, true } width = int(float64(width)*scale + 0.5) b := f.fnt.Bounds(f.fnt.FUnitsPerEm()) height := int(float64(b.YMax-b.YMin)*scale + 0.5) descent := int(float64(b.YMin)*scale + 0.5) return width, height, descent }
// 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 *ImageGraphicContext) 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(font.Kerning(int32(gc.Current.Scale), prev, index)) } err := gc.drawGlyph(index, x, y) if err != nil { log.Println(err) return startx - x } x += fUnitsToFloat64(font.HMetric(int32(gc.Current.Scale), index).AdvanceWidth) prev, hasPrev = index, true } return x - startx }
// Implements the Font interface. func (f *TruetypeFont) Lookup(i FontIndex) (glyphData interface{}, err error) { // Load into glyph buffer. if f.buf == nil { f.buf = truetype.NewGlyphBuf() } index := truetype.Index(i) scale := f.Font.FUnitsPerEm() err = f.buf.Load(f.Font, scale, index, f.Hinting) if err != nil { return nil, err } // Expand each contour. var ( glyph QuadGlyph e0 int ) for _, e1 := range f.buf.End { f.expandContour(&glyph, f.buf.Point[e0:e1]) e0 = e1 } return glyph, nil }
// Implements the Font interface. func (f *TruetypeFont) Kerning(a, b FontIndex) (x, y int) { x = int(f.Font.Kerning(f.Font.FUnitsPerEm(), truetype.Index(a), truetype.Index(b))) y = -1 return }