diff --git a/chart.go b/chart.go index 839674c..a58eac4 100644 --- a/chart.go +++ b/chart.go @@ -657,7 +657,9 @@ func parseChartOptions(opts *Chart) (*Chart, error) { // // Name // Categories +// Sizes // Values +// Fill // Line // Marker // @@ -670,16 +672,18 @@ func parseChartOptions(opts *Chart) (*Chart, error) { // the same as the X axis. In most chart types the 'Categories' property is // optional and the chart will just assume a sequential series from 1..n. // +// Sizes: This sets the bubble size in a data series. +// // Values: This is the most important property of a series and is the only // mandatory option for every chart object. This option links the chart with // the worksheet data that it displays. // +// Fill: This set the format for the data series fill. +// // Line: This sets the line format of the line chart. The 'Line' property is // optional and if it isn't supplied it will default style. The options that // can be set are width and color. The range of width is 0.25pt - 999pt. If the // value of width is outside the range, the default width of the line is 2pt. -// The value for color should be represented in hex format -// (e.g., #000000 - #FFFFFF) // // Marker: This sets the marker of the line chart and scatter chart. The range // of optional field 'Size' is 2-72 (default value is 5). The enumeration value diff --git a/chart_test.go b/chart_test.go index 2c740ef..cc78944 100644 --- a/chart_test.go +++ b/chart_test.go @@ -153,7 +153,9 @@ func TestAddChart(t *testing.T) { {Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"}, } series2 := []ChartSeries{ - {Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Marker: ChartMarker{Symbol: "none", Size: 10}, Line: ChartLine{Color: "#000000"}}, + {Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", + Fill: Fill{Type: "pattern", Color: []string{"000000"}, Pattern: 1}, + Marker: ChartMarker{Symbol: "none", Size: 10}}, {Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31"}, {Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32"}, {Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33"}, @@ -163,6 +165,16 @@ func TestAddChart(t *testing.T) { {Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Line: ChartLine{Width: 0.25}}, } series3 := []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}} + series4 := []ChartSeries{ + {Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Sizes: "Sheet1!$B$30:$D$30"}, + {Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31", Sizes: "Sheet1!$B$31:$D$31"}, + {Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32", Sizes: "Sheet1!$B$32:$D$32"}, + {Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33", Sizes: "Sheet1!$B$33:$D$33"}, + {Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34", Sizes: "Sheet1!$B$34:$D$34"}, + {Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35", Sizes: "Sheet1!$B$35:$D$35"}, + {Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36", Sizes: "Sheet1!$B$36:$D$36"}, + {Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Sizes: "Sheet1!$B$37:$D$37"}, + } format := GraphicOptions{ ScaleX: defaultPictureScale, ScaleY: defaultPictureScale, @@ -242,8 +254,8 @@ func TestAddChart(t *testing.T) { {sheetName: "Sheet2", cell: "AV32", opts: &Chart{Type: "contour", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "BD1", opts: &Chart{Type: "wireframeContour", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Wireframe Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, // bubble chart - {sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: "bubble", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, - {sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: "bubble3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}}, + {sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: "bubble", Series: series4, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, + {sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: "bubble3D", Series: series4, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}}, // pie of pie chart {sheetName: "Sheet2", cell: "BD48", opts: &Chart{Type: "pieOfPie", Series: series3, Format: format, Legend: legend, Title: ChartTitle{Name: "Pie of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}}, // bar of pie chart @@ -256,18 +268,14 @@ func TestAddChart(t *testing.T) { assert.NoError(t, err) clusteredColumnCombo := [][]string{ {"A1", "line", "Clustered Column - Line Chart"}, - {"I1", "bubble", "Clustered Column - Bubble Chart"}, - {"Q1", "bubble3D", "Clustered Column - Bubble 3D Chart"}, - {"Y1", "doughnut", "Clustered Column - Doughnut Chart"}, + {"I1", "doughnut", "Clustered Column - Doughnut Chart"}, } for _, props := range clusteredColumnCombo { assert.NoError(t, f.AddChart("Combo Charts", props[0], &Chart{Type: "col", Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[2]}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[1], Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}})) } stackedAreaCombo := map[string][]string{ "A16": {"line", "Stacked Area - Line Chart"}, - "I16": {"bubble", "Stacked Area - Bubble Chart"}, - "Q16": {"bubble3D", "Stacked Area - Bubble 3D Chart"}, - "Y16": {"doughnut", "Stacked Area - Doughnut Chart"}, + "I16": {"doughnut", "Stacked Area - Doughnut Chart"}, } for axis, props := range stackedAreaCombo { assert.NoError(t, f.AddChart("Combo Charts", axis, &Chart{Type: "areaStacked", Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[1]}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[0], Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}})) diff --git a/drawing.go b/drawing.go index 88aaab8..7ce4b9e 100644 --- a/drawing.go +++ b/drawing.go @@ -234,8 +234,8 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) { WireframeSurface3D: f.drawSurface3DChart, Contour: f.drawSurfaceChart, WireframeContour: f.drawSurfaceChart, - Bubble: f.drawBaseChart, - Bubble3D: f.drawBaseChart, + Bubble: f.drawBubbleChart, + Bubble3D: f.drawBubbleChart, } if opts.Legend.Position == "none" { xlsxChartSpace.Chart.Legend = nil @@ -270,7 +270,7 @@ func (f *File) drawBaseChart(opts *Chart) *cPlotArea { Val: stringPtr("col"), }, Grouping: &attrValString{ - Val: stringPtr("clustered"), + Val: stringPtr(plotAreaChartGrouping[opts.Type]), }, VaryColors: &attrValBool{ Val: opts.VaryColors, @@ -288,9 +288,6 @@ func (f *File) drawBaseChart(opts *Chart) *cPlotArea { if *c.BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok { c.BarDir = nil } - if *c.Grouping.Val, ok = plotAreaChartGrouping[opts.Type]; !ok { - c.Grouping = nil - } if *c.Overlap.Val, ok = plotAreaChartOverlap[opts.Type]; !ok { c.Overlap = nil } @@ -726,6 +723,26 @@ func (f *File) drawSurfaceChart(opts *Chart) *cPlotArea { return plotArea } +// drawBubbleChart provides a function to draw the c:bubbleChart element by +// given format sets. +func (f *File) drawBubbleChart(opts *Chart) *cPlotArea { + plotArea := &cPlotArea{ + BubbleChart: &cCharts{ + VaryColors: &attrValBool{ + Val: opts.VaryColors, + }, + Ser: f.drawChartSeries(opts), + DLbls: f.drawChartDLbls(opts), + AxID: []*attrValInt{ + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, + }, + }, + ValAx: []*cAxs{f.drawPlotAreaCatAx(opts)[0], f.drawPlotAreaValAx(opts)[0]}, + } + return plotArea +} + // drawChartShape provides a function to draw the c:shape element by given // format sets. func (f *File) drawChartShape(opts *Chart) *attrValString { @@ -794,13 +811,13 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr { var srgbClr *attrValString var schemeClr *aSchemeClr - if color := stringPtr(opts.Series[i].Line.Color); *color != "" { - *color = strings.TrimPrefix(*color, "#") - srgbClr = &attrValString{Val: color} + if color := opts.Series[i].Fill.Color; len(color) == 1 { + srgbClr = &attrValString{Val: stringPtr(strings.TrimPrefix(color[0], "#"))} } else { schemeClr = &aSchemeClr{Val: "accent" + strconv.Itoa((opts.order+i)%6+1)} } + spPr := &cSpPr{SolidFill: &aSolidFill{SchemeClr: schemeClr, SrgbClr: srgbClr}} spPrScatter := &cSpPr{ Ln: &aLn{ W: 25400, @@ -817,8 +834,15 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr { }, }, } - chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter} - return chartSeriesSpPr[opts.Type] + if chartSeriesSpPr, ok := map[string]*cSpPr{ + Line: spPrLine, Scatter: spPrScatter, + }[opts.Type]; ok { + return chartSeriesSpPr + } + if srgbClr != nil { + return spPr + } + return nil } // drawChartSeriesDPt provides a function to draw the c:dPt element by given @@ -923,7 +947,7 @@ func (f *File) drawChartSeriesXVal(v ChartSeries, opts *Chart) *cCat { F: v.Categories, }, } - chartSeriesXVal := map[string]*cCat{Scatter: cat} + chartSeriesXVal := map[string]*cCat{Scatter: cat, Bubble: cat, Bubble3D: cat} return chartSeriesXVal[opts.Type] } @@ -942,12 +966,12 @@ func (f *File) drawChartSeriesYVal(v ChartSeries, opts *Chart) *cVal { // drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize // element by given chart series and format sets. func (f *File) drawCharSeriesBubbleSize(v ChartSeries, opts *Chart) *cVal { - if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok { + if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok || v.Sizes == "" { return nil } return &cVal{ NumRef: &cNumRef{ - F: v.Values, + F: v.Sizes, }, } } diff --git a/sheet.go b/sheet.go index 4f9b957..0bc6815 100644 --- a/sheet.go +++ b/sheet.go @@ -1920,7 +1920,7 @@ func (f *File) SetSheetDimension(sheet string, rangeRef string) error { return err } -// SetSheetDimension provides the method to get the used range of the worksheet. +// GetSheetDimension provides the method to get the used range of the worksheet. func (f *File) GetSheetDimension(sheet string) (string, error) { var ref string ws, err := f.workSheetReader(sheet) diff --git a/xmlChart.go b/xmlChart.go index 9818ca1..6688ed1 100644 --- a/xmlChart.go +++ b/xmlChart.go @@ -579,7 +579,6 @@ type ChartMarker struct { // ChartLine directly maps the format settings of the chart line. type ChartLine struct { - Color string Smooth bool Width float64 } @@ -588,7 +587,9 @@ type ChartLine struct { type ChartSeries struct { Name string Categories string + Sizes string Values string + Fill Fill Line ChartLine Marker ChartMarker }