SVG!
This commit is contained in:
parent
3d68b7e51d
commit
f843d124d6
7 changed files with 205 additions and 14 deletions
|
|
@ -1 +1,153 @@
|
|||
package chart
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
|
||||
"github.com/ajstarks/svgo"
|
||||
"github.com/golang/freetype/truetype"
|
||||
)
|
||||
|
||||
// SVG returns a new png/raster renderer.
|
||||
func SVG(width, height int) Renderer {
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
canvas := svg.New(buffer)
|
||||
canvas.Start(width, height)
|
||||
return &vectorRenderer{
|
||||
b: buffer,
|
||||
c: canvas,
|
||||
s: &Style{},
|
||||
p: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
// vectorRenderer renders chart commands to a bitmap.
|
||||
type vectorRenderer struct {
|
||||
b *bytes.Buffer
|
||||
c *svg.SVG
|
||||
s *Style
|
||||
f *truetype.Font
|
||||
p []string
|
||||
fc *font.Drawer
|
||||
}
|
||||
|
||||
func (vr *vectorRenderer) SetStrokeColor(c color.RGBA) {
|
||||
vr.s.StrokeColor = c
|
||||
}
|
||||
|
||||
// SetFillColor implements the interface method.
|
||||
func (vr *vectorRenderer) SetFillColor(c color.RGBA) {
|
||||
vr.s.FillColor = c
|
||||
}
|
||||
|
||||
// SetLineWidth implements the interface method.
|
||||
func (vr *vectorRenderer) SetStrokeWidth(width float64) {
|
||||
vr.s.StrokeWidth = width
|
||||
}
|
||||
|
||||
// MoveTo implements the interface method.
|
||||
func (vr *vectorRenderer) MoveTo(x, y int) {
|
||||
vr.p = append(vr.p, fmt.Sprintf("M %d %d", x, y))
|
||||
}
|
||||
|
||||
// LineTo implements the interface method.
|
||||
func (vr *vectorRenderer) LineTo(x, y int) {
|
||||
vr.p = append(vr.p, fmt.Sprintf("L %d %d", x, y))
|
||||
}
|
||||
|
||||
func (vr *vectorRenderer) Close() {
|
||||
vr.p = append(vr.p, fmt.Sprintf("Z"))
|
||||
}
|
||||
|
||||
// Stroke draws the path with no fill.
|
||||
func (vr *vectorRenderer) Stroke() {
|
||||
vr.s.FillColor = color.RGBA{}
|
||||
vr.s.FontColor = color.RGBA{}
|
||||
vr.drawPath()
|
||||
}
|
||||
|
||||
// Fill draws the path with no stroke.
|
||||
func (vr *vectorRenderer) Fill() {
|
||||
vr.s.StrokeColor = color.RGBA{}
|
||||
vr.s.StrokeWidth = 0
|
||||
vr.s.FontColor = color.RGBA{}
|
||||
vr.drawPath()
|
||||
}
|
||||
|
||||
// FillStroke draws the path with both fill and stroke.
|
||||
func (vr *vectorRenderer) FillStroke() {
|
||||
vr.s.FontColor = color.RGBA{}
|
||||
vr.drawPath()
|
||||
}
|
||||
|
||||
func (vr *vectorRenderer) drawPath() {
|
||||
vr.c.Path(strings.Join(vr.p, "\n"), vr.s.SVG())
|
||||
vr.p = []string{}
|
||||
}
|
||||
|
||||
// Circle implements the interface method.
|
||||
func (vr *vectorRenderer) Circle(radius float64, x, y int) {
|
||||
vr.c.Circle(x, y, int(radius), vr.s.SVG())
|
||||
}
|
||||
|
||||
// SetFont implements the interface method.
|
||||
func (vr *vectorRenderer) SetFont(f *truetype.Font) {
|
||||
vr.f = f
|
||||
}
|
||||
|
||||
// SetFontColor implements the interface method.
|
||||
func (vr *vectorRenderer) SetFontColor(c color.RGBA) {
|
||||
vr.s.FontColor = c
|
||||
}
|
||||
|
||||
// SetFontSize implements the interface method.
|
||||
func (vr *vectorRenderer) SetFontSize(size float64) {
|
||||
vr.s.FontSize = size
|
||||
}
|
||||
|
||||
func (vr *vectorRenderer) svgFontFace() string {
|
||||
family := "sans-serif"
|
||||
if vr.f != nil {
|
||||
name := vr.f.Name(truetype.NameIDFontFamily)
|
||||
if len(name) != 0 {
|
||||
family = fmt.Sprintf(`'%s',%s`, name, family)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("font-family:%s", family)
|
||||
}
|
||||
|
||||
// Text draws a text blob.
|
||||
func (vr *vectorRenderer) Text(body string, x, y int) {
|
||||
vr.s.FillColor = color.RGBA{}
|
||||
vr.s.StrokeColor = color.RGBA{}
|
||||
vr.s.StrokeWidth = 0
|
||||
vr.c.Text(x, y, body, vr.s.SVG()+";"+vr.svgFontFace())
|
||||
}
|
||||
|
||||
// MeasureText uses the truetype font drawer to measure the width of text.
|
||||
func (vr *vectorRenderer) MeasureText(body string) int {
|
||||
if vr.fc == nil && vr.f != nil {
|
||||
vr.fc = &font.Drawer{
|
||||
Face: truetype.NewFace(vr.f, &truetype.Options{
|
||||
DPI: DefaultDPI,
|
||||
Size: vr.s.FontSize,
|
||||
}),
|
||||
}
|
||||
}
|
||||
if vr.fc != nil {
|
||||
dimensions := vr.fc.MeasureString(body)
|
||||
return dimensions.Floor()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (vr *vectorRenderer) Save(w io.Writer) error {
|
||||
vr.c.End()
|
||||
_, err := w.Write(vr.b.Bytes())
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue