Open dmjones opened 3 years ago
The issue is more significant when there are descenders:
I notice the line height is set differently depending if one calls LoadFontFace
or SetFontFace
. If we change LoadFontFace
to look like this:
func (dc *Context) LoadFontFace(path string, points float64) error {
face, err := LoadFontFace(path, points)
if err == nil {
dc.SetFontFace(face)
}
return err
}
Then at least the red box becomes the correct size for the text. By estimating the offset (in this case, 8
), you can then draw a perfect bounding box:
// Modify this line (from opening example)
dc.DrawString(s, offset, offset+h-8)
The magic value of 8 could be calculated by inspecting the font metrics, but unfortunately the Height
value does not get set correctly (see https://github.com/golang/freetype/issues/59). We ought to be able to do Height - Ascent
to calculate the value, but alas not.
Not really sure if there is a solution here.
I worked around this in a slightly clunky way for where I really need to know the height of a string, and using the font Size is too coarse.
func getStringHeight(s string, style TextStyleType) float64 {
ctx := gg.NewContext(int(style.Size), int(style.Size)*len(s))
ctx.SetFontFace(style.getFace())
ctx.SetColor(style.getColour())
ctx.DrawString(s, 0, style.Size)
stringImage := ctx.Image()
var stringHeight float64
for y := 0; y < stringImage.Bounds().Dy(); y++ {
for x := 0; x < stringImage.Bounds().Dx(); x++ {
if _, _, _, alpha := stringImage.At(x, y).RGBA(); alpha > 0 {
stringHeight = style.Size - float64(y)
break
}
}
if stringHeight > 0 {
break
}
}
return stringHeight
}
Haven't tidied it up, so there are references to my own functions in there, but you get the idea. It's a bit of a kludge, but it draws the string in a context, and then scans down the image to find the first pixel that's been drawn.
I had to do something similar for dealing with font descenders:
func getLengthOfStringDescender(style TextStyleType) float64 {
var checkString string = "ygpqf"
ctx := gg.NewContext(int(style.Size)*2, int(style.Size)*len(checkString))
ctx.SetFontFace(style.getFace())
ctx.SetColor(style.getColour())
ctx.DrawString(checkString, 0, style.Size)
stringImage := ctx.Image()
var descenderLength float64
for y := stringImage.Bounds().Dy(); y > 0; y-- {
for x := stringImage.Bounds().Dx(); x > 0; x-- {
if _, _, _, alpha := stringImage.At(x, y).RGBA(); alpha > 0 {
descenderLength = float64(y) - style.Size
break
}
}
if descenderLength > 0 {
break
}
}
return descenderLength
}
I've noticed
MeasureString
slightly underestimates the size of a string. Here is the code that I used to test this:The net result is this image:
Note how small aspects of the text go outside the bounding box. I'm far from a font expert, so perhaps this is to be expected.