diff --git a/README.md b/README.md index 7f424ff..41fd978 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ These features *should* work just like their HTML5 counterparts, but there are l - fillText - measureText - textAlign +- textBaseline - fillStyle - strokeText - strokeStyle @@ -124,6 +125,5 @@ These features *should* work just like their HTML5 counterparts, but there are l # Missing features - globalCompositeOperation -- textBaseline - isPointInPath - isPointInStroke diff --git a/canvas.go b/canvas.go index d4bd065..7b05c8c 100644 --- a/canvas.go +++ b/canvas.go @@ -7,6 +7,7 @@ import ( "os" "github.com/golang/freetype/truetype" + "golang.org/x/image/font" ) //go:generate go run make_shaders.go @@ -36,7 +37,9 @@ type drawState struct { stroke drawStyle font *Font fontSize float64 + fontMetrics font.Metrics textAlign textAlign + textBaseline textBaseline lineAlpha float64 lineWidth float64 lineJoin lineJoin @@ -102,6 +105,18 @@ const ( End ) +type textBaseline uint8 + +// Text baseline constants for SetTextBaseline +const ( + Alphabetic = iota + Top + Hanging + Middle + Ideographic + Bottom +) + // New creates a new canvas with the given viewport coordinates. // While all functions on the canvas use the top left point as // the origin, since GL uses the bottom left coordinate, the @@ -687,6 +702,9 @@ func (cv *Canvas) SetFont(src interface{}, size float64) { } } cv.state.fontSize = size + + fontFace := truetype.NewFace(cv.state.font.font, &truetype.Options{Size: size}) + cv.state.fontMetrics = fontFace.Metrics() } // SetTextAlign sets the text align for any text drawing calls. @@ -695,6 +713,13 @@ func (cv *Canvas) SetTextAlign(align textAlign) { cv.state.textAlign = align } +// SetTextBaseline sets the text baseline for any text drawing calls. +// The value can be Alphabetic (default), Top, Hanging, Middle, +// Ideographic, or Bottom +func (cv *Canvas) SetTextBaseline(baseline textBaseline) { + cv.state.textBaseline = baseline +} + // SetLineJoin sets the style of line joints for rendering a path with Stroke. // The value can be Miter, Bevel, or Round func (cv *Canvas) SetLineJoin(join lineJoin) { diff --git a/text.go b/text.go index e28544f..54112bd 100644 --- a/text.go +++ b/text.go @@ -73,7 +73,7 @@ func (cv *Canvas) FillText(str string, x, y float64) { frc := fontRenderingContext frc.setFont(cv.state.font.font) - frc.setFontSize(float64(cv.state.fontSize)) + frc.setFontSize(cv.state.fontSize) fnt := cv.state.font.font curX := x @@ -197,6 +197,19 @@ func (cv *Canvas) FillText(str string, x, y float64) { x -= float64(strWidth) } + var yOff float64 + metrics := cv.state.fontMetrics + switch cv.state.textBaseline { + case Alphabetic: + yOff = 0 + case Middle: + yOff = -float64(metrics.Descent)/64 + float64(metrics.Height)*0.5/64 + case Top, Hanging: + yOff = -float64(metrics.Descent)/64 + float64(metrics.Height)/64 + case Bottom, Ideographic: + yOff = -float64(metrics.Descent) / 64 + } + gli.ActiveTexture(gl_TEXTURE1) gli.BindTexture(gl_TEXTURE_2D, alphaTex) for y := 0; y < strHeight; y++ { @@ -215,10 +228,10 @@ func (cv *Canvas) FillText(str string, x, y float64) { gli.EnableVertexAttribArray(vertex) gli.EnableVertexAttribArray(alphaTexCoord) - p0 := cv.tf(vec{float64(textOffset.X) + x, float64(textOffset.Y) + y}) - p1 := cv.tf(vec{float64(textOffset.X) + x, float64(textOffset.Y+strHeight) + y}) - p2 := cv.tf(vec{float64(textOffset.X+strWidth) + x, float64(textOffset.Y+strHeight) + y}) - p3 := cv.tf(vec{float64(textOffset.X+strWidth) + x, float64(textOffset.Y) + y}) + p0 := cv.tf(vec{float64(textOffset.X) + x, float64(textOffset.Y) + y + yOff}) + p1 := cv.tf(vec{float64(textOffset.X) + x, float64(textOffset.Y+strHeight) + y + yOff}) + p2 := cv.tf(vec{float64(textOffset.X+strWidth) + x, float64(textOffset.Y+strHeight) + y + yOff}) + p3 := cv.tf(vec{float64(textOffset.X+strWidth) + x, float64(textOffset.Y) + y + yOff}) tw := float64(strWidth) / alphaTexSize th := float64(strHeight) / alphaTexSize