diff --git a/_examples/css_classes/main.go b/_examples/css_classes/main.go new file mode 100644 index 0000000..d650e96 --- /dev/null +++ b/_examples/css_classes/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "github.com/wcharczuk/go-chart" + "log" + "net/http" +) + +func inlineSVGWithClasses(res http.ResponseWriter, req *http.Request) { + res.Write([]byte( + "
" + + "" + + "" + + "")) + + pie := chart.PieChart{ + // Note that setting ClassName will cause all other inline styles to be dropped! + Background: chart.Style{ClassName: "background"}, + Canvas: chart.Style{ + ClassName: "canvas", + }, + Width: 512, + Height: 512, + Values: []chart.Value{ + {Value: 5, Label: "Blue", Style: chart.Style{ClassName: "blue"}}, + {Value: 5, Label: "Green", Style: chart.Style{ClassName: "green"}}, + {Value: 4, Label: "Gray", Style: chart.Style{ClassName: "gray"}}, + }, + } + + err := pie.Render(chart.SVG, res) + if err != nil { + fmt.Printf("Error rendering pie chart: %v\n", err) + } + res.Write([]byte("")) +} + +func css(res http.ResponseWriter, req *http.Request) { + res.Header().Set("Content-Type", "text/css") + res.Write([]byte("svg .background { fill: white; }" + + "svg .canvas { fill: white; }" + + "svg path.blue { fill: blue; stroke: lightblue; }" + + "svg path.green { fill: green; stroke: lightgreen; }" + + "svg path.gray { fill: gray; stroke: lightgray; }" + + "svg text.blue { fill: white; }" + + "svg text.green { fill: white; }" + + "svg text.gray { fill: white; }")) +} + +func main() { + http.HandleFunc("/", inlineSVGWithClasses) + http.HandleFunc("/main.css", css) + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/raster_renderer.go b/raster_renderer.go index 1f18309..18b4fef 100644 --- a/raster_renderer.go +++ b/raster_renderer.go @@ -49,6 +49,10 @@ func (rr *rasterRenderer) SetDPI(dpi float64) { rr.gc.SetDPI(dpi) } +// SetClassName implements the interface method. However, PNGs have no classes. +func (vr *rasterRenderer) SetClassName(_ string) { +} + // SetStrokeColor implements the interface method. func (rr *rasterRenderer) SetStrokeColor(c drawing.Color) { rr.s.StrokeColor = c diff --git a/renderer.go b/renderer.go index 7eb06bb..68a668b 100644 --- a/renderer.go +++ b/renderer.go @@ -18,6 +18,9 @@ type Renderer interface { // SetDPI sets the DPI for the renderer. SetDPI(dpi float64) + // SetClassName sets the current class name. + SetClassName(string) + // SetStrokeColor sets the current stroke color. SetStrokeColor(drawing.Color) diff --git a/style.go b/style.go index 9d1b268..eafc552 100644 --- a/style.go +++ b/style.go @@ -39,6 +39,8 @@ type Style struct { Show bool Padding Box + ClassName string + StrokeWidth float64 StrokeColor drawing.Color StrokeDashArray []float64 @@ -71,7 +73,8 @@ func (s Style) IsZero() bool { s.FillColor.IsZero() && s.FontColor.IsZero() && s.FontSize == 0 && - s.Font == nil + s.Font == nil && + s.ClassName == "" } // String returns a text representation of the style. @@ -87,6 +90,12 @@ func (s Style) String() string { output = []string{"\"show\": false"} } + if s.ClassName != "" { + output = append(output, fmt.Sprintf("\"class_name\": %s", s.ClassName)) + } else { + output = append(output, "\"class_name\": null") + } + if !s.Padding.IsZero() { output = append(output, fmt.Sprintf("\"padding\": %s", s.Padding.String())) } else { @@ -155,6 +164,16 @@ func (s Style) String() string { return "{" + strings.Join(output, ", ") + "}" } +func (s Style) GetClassName(defaults ...string) string { + if s.ClassName == "" { + if len(defaults) > 0 { + return defaults[0] + } + return "" + } + return s.ClassName +} + // GetStrokeColor returns the stroke color. func (s Style) GetStrokeColor(defaults ...drawing.Color) drawing.Color { if s.StrokeColor.IsZero() { @@ -321,6 +340,7 @@ func (s Style) GetTextRotationDegrees(defaults ...float64) float64 { // WriteToRenderer passes the style's options to a renderer. func (s Style) WriteToRenderer(r Renderer) { + r.SetClassName(s.GetClassName()) r.SetStrokeColor(s.GetStrokeColor()) r.SetStrokeWidth(s.GetStrokeWidth()) r.SetStrokeDashArray(s.GetStrokeDashArray()) @@ -337,6 +357,7 @@ func (s Style) WriteToRenderer(r Renderer) { // WriteDrawingOptionsToRenderer passes just the drawing style options to a renderer. func (s Style) WriteDrawingOptionsToRenderer(r Renderer) { + r.SetClassName(s.GetClassName()) r.SetStrokeColor(s.GetStrokeColor()) r.SetStrokeWidth(s.GetStrokeWidth()) r.SetStrokeDashArray(s.GetStrokeDashArray()) @@ -345,6 +366,7 @@ func (s Style) WriteDrawingOptionsToRenderer(r Renderer) { // WriteTextOptionsToRenderer passes just the text style options to a renderer. func (s Style) WriteTextOptionsToRenderer(r Renderer) { + r.SetClassName(s.GetClassName()) r.SetFont(s.GetFont()) r.SetFontColor(s.GetFontColor()) r.SetFontSize(s.GetFontSize()) @@ -352,6 +374,8 @@ func (s Style) WriteTextOptionsToRenderer(r Renderer) { // InheritFrom coalesces two styles into a new style. func (s Style) InheritFrom(defaults Style) (final Style) { + final.ClassName = s.GetClassName(defaults.ClassName) + final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor) final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth) final.StrokeDashArray = s.GetStrokeDashArray(defaults.StrokeDashArray) @@ -379,6 +403,7 @@ func (s Style) InheritFrom(defaults Style) (final Style) { // GetStrokeOptions returns the stroke components. func (s Style) GetStrokeOptions() Style { return Style{ + ClassName: s.ClassName, StrokeDashArray: s.StrokeDashArray, StrokeColor: s.StrokeColor, StrokeWidth: s.StrokeWidth, @@ -388,6 +413,7 @@ func (s Style) GetStrokeOptions() Style { // GetFillOptions returns the fill components. func (s Style) GetFillOptions() Style { return Style{ + ClassName: s.ClassName, FillColor: s.FillColor, } } @@ -395,6 +421,7 @@ func (s Style) GetFillOptions() Style { // GetDotOptions returns the dot components. func (s Style) GetDotOptions() Style { return Style{ + ClassName: s.ClassName, StrokeDashArray: nil, FillColor: s.DotColor, StrokeColor: s.DotColor, @@ -405,6 +432,7 @@ func (s Style) GetDotOptions() Style { // GetFillAndStrokeOptions returns the fill and stroke components. func (s Style) GetFillAndStrokeOptions() Style { return Style{ + ClassName: s.ClassName, StrokeDashArray: s.StrokeDashArray, FillColor: s.FillColor, StrokeColor: s.StrokeColor, @@ -415,6 +443,7 @@ func (s Style) GetFillAndStrokeOptions() Style { // GetTextOptions returns just the text components of the style. func (s Style) GetTextOptions() Style { return Style{ + ClassName: s.ClassName, FontColor: s.FontColor, FontSize: s.FontSize, Font: s.Font, diff --git a/vector_renderer.go b/vector_renderer.go index 6f9b6f4..c154424 100644 --- a/vector_renderer.go +++ b/vector_renderer.go @@ -54,6 +54,11 @@ func (vr *vectorRenderer) SetDPI(dpi float64) { vr.c.dpi = dpi } +// SetClassName implements the interface method. +func (vr *vectorRenderer) SetClassName(classname string) { + vr.s.ClassName = classname +} + // SetStrokeColor implements the interface method. func (vr *vectorRenderer) SetStrokeColor(c drawing.Color) { vr.s.StrokeColor = c @@ -230,20 +235,20 @@ func (c *canvas) Path(d string, style Style) { if len(style.StrokeDashArray) > 0 { strokeDashArrayProperty = c.getStrokeDashArray(style) } - c.w.Write([]byte(fmt.Sprintf(`