diff --git a/example/freetype/main.go b/example/freetype/main.go index 21657a3..af1049b 100644 --- a/example/freetype/main.go +++ b/example/freetype/main.go @@ -33,6 +33,7 @@ var ( size = flag.Float64("size", 12, "font size in points") spacing = flag.Float64("spacing", 1.5, "line spacing (e.g. 2 means double spaced)") wonb = flag.Bool("whiteonblack", false, "white text on a black background") + bound = flag.Bool("bound", false, "generates image with minimum size for the text") ) var text = []string{ @@ -88,21 +89,10 @@ func main() { } // Initialize the context. - fg, bg := image.Black, image.White - ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff} - if *wonb { - fg, bg = image.White, image.Black - ruler = color.RGBA{0x22, 0x22, 0x22, 0xff} - } - rgba := image.NewRGBA(image.Rect(0, 0, 640, 480)) - draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) c := freetype.NewContext() c.SetDPI(*dpi) c.SetFont(f) c.SetFontSize(*size) - c.SetClip(rgba.Bounds()) - c.SetDst(rgba) - c.SetSrc(fg) switch *hinting { default: c.SetHinting(font.HintingNone) @@ -110,6 +100,44 @@ func main() { c.SetHinting(font.HintingFull) } + var width, height int + // Measure the text to calculate the minimum size of the image + if *bound { + x := 0 + pt := freetype.Pt(10, 10+int(c.PointToFixed(*size)>>6)) + for _, s := range text { + ptr, err := c.MeasureString(s, pt) + if err != nil { + log.Println(err) + return + } + pt.Y += c.PointToFixed(*size * *spacing) + x = int(ptr.X >> 6) + if x > width { + width = x + } + } + width += 10 + height = int(pt.Y)>>6 - int(c.PointToFixed(*size)>>6) + // Use default size for the image + } else { + width = 640 + height = 480 + } + + // Creates image with the specified size + fg, bg := image.Black, image.White + ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff} + if *wonb { + fg, bg = image.White, image.Black + ruler = color.RGBA{0x22, 0x22, 0x22, 0xff} + } + rgba := image.NewRGBA(image.Rect(0, 0, width, height)) + draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) + c.SetClip(rgba.Bounds()) + c.SetDst(rgba) + c.SetSrc(fg) + // Draw the guidelines. for i := 0; i < 200; i++ { rgba.Set(10, 10+i, ruler) diff --git a/freetype.go b/freetype.go index 9603586..5e3255b 100644 --- a/freetype.go +++ b/freetype.go @@ -229,7 +229,7 @@ func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) ( // 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") + return fixed.Point26_6{}, errors.New("freetype: DrawString called with a nil font") } prev, hasPrev := truetype.Index(0), false for _, rune := range s { @@ -257,6 +257,31 @@ func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, erro return p, nil } +// MeasureString is identical to DrawString but only measure the text. +func (c *Context) MeasureString(s string, p fixed.Point26_6) (fixed.Point26_6, error) { + if c.f == nil { + return fixed.Point26_6{}, errors.New("freetype: MeasureString 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, _, _, err := c.glyph(index, p) + if err != nil { + return fixed.Point26_6{}, err + } + p.X += advanceWidth + prev, hasPrev = index, true + } + return p, nil +} + // recalc recalculates scale and bounds values from the font size, screen // resolution and font metrics, and invalidates the glyph cache. func (c *Context) recalc() {