From 5399572353aa8da7cb37603b6bcdd09ec020ede1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MELF=E6=99=93=E5=AE=87?= Date: Mon, 22 Jan 2024 09:41:57 +0800 Subject: [PATCH] This closes #1786, support set fill color of the chart (#1788) - Add a new field Fill in Chart, ChartPlotArea, and ChartMarker struct - Support set solid color or transparent fill for chart area, plot area, and maker --- chart_test.go | 12 +++++++++--- drawing.go | 43 ++++++++++++++++++++++++++----------------- xmlChart.go | 3 +++ 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/chart_test.go b/chart_test.go index 79501eeb7f..e70f06aa6a 100644 --- a/chart_test.go +++ b/chart_test.go @@ -158,7 +158,12 @@ func TestAddChart(t *testing.T) { {Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34"}, {Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35"}, {Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36"}, - {Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"}, + { + Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", + Marker: ChartMarker{ + Fill: Fill{Type: "pattern", Color: []string{"FFFF00"}, Pattern: 1}, + }, + }, } series2 := []ChartSeries{ { @@ -203,14 +208,15 @@ func TestAddChart(t *testing.T) { ShowPercent: true, ShowSerName: true, ShowVal: true, + Fill: Fill{Type: "pattern", Pattern: 1}, } for _, c := range []struct { sheetName, cell string opts *Chart }{ {sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineNone}, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "000000"}, Title: []RichTextRun{{Text: "Primary Horizontal Axis Title"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}, Title: []RichTextRun{{Text: "Primary Vertical Axis Title", Font: &Font{Color: "777777", Bold: true, Italic: true, Size: 12}}}}}}, - {sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Column Chart"}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineAutomatic}, ShowBlanksAs: "zero"}}, - {sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "100% Stacked Column Chart"}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineSolid, Width: 2}, ShowBlanksAs: "zero"}}, + {sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Column Chart"}}, PlotArea: plotArea, Fill: Fill{Type: "pattern", Pattern: 1}, Border: ChartLine{Type: ChartLineAutomatic}, ShowBlanksAs: "zero"}}, + {sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "100% Stacked Column Chart"}}, PlotArea: plotArea, Fill: Fill{Type: "pattern", Color: []string{"EEEEEE"}, Pattern: 1}, Border: ChartLine{Type: ChartLineSolid, Width: 2}, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Clustered Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P30", opts: &Chart{Type: Col3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X30", opts: &Chart{Type: Col3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D 100% Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, diff --git a/drawing.go b/drawing.go index 92481bf417..035ca4b072 100644 --- a/drawing.go +++ b/drawing.go @@ -107,6 +107,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) { }, }, } + xlsxChartSpace.SpPr = f.drawShapeFill(opts.Fill, xlsxChartSpace.SpPr) plotAreaFunc := map[ChartType]func(*Chart) *cPlotArea{ Area: f.drawBaseChart, AreaStacked: f.drawBaseChart, @@ -167,6 +168,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) { if opts.Legend.Position == "none" { xlsxChartSpace.Chart.Legend = nil } + xlsxChartSpace.Chart.PlotArea.SpPr = f.drawShapeFill(opts.PlotArea.Fill, xlsxChartSpace.Chart.PlotArea.SpPr) addChart := func(c, p *cPlotArea) { immutable, mutable := reflect.ValueOf(c).Elem(), reflect.ValueOf(p).Elem() for i := 0; i < mutable.NumField(); i++ { @@ -727,19 +729,28 @@ func (f *File) drawChartSeries(opts *Chart) *[]cSer { return &ser } +// drawShapeFill provides a function to draw the a:solidFill element by given +// fill format sets. +func (f *File) drawShapeFill(fill Fill, spPr *cSpPr) *cSpPr { + if fill.Type == "pattern" && fill.Pattern == 1 { + if spPr == nil { + spPr = &cSpPr{} + } + if len(fill.Color) == 1 { + spPr.SolidFill = &aSolidFill{SrgbClr: &attrValString{Val: stringPtr(strings.TrimPrefix(fill.Color[0], "#"))}} + return spPr + } + spPr.SolidFill = nil + spPr.NoFill = stringPtr("") + } + return spPr +} + // drawChartSeriesSpPr provides a function to draw the c:spPr element by given // format sets. func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr { - var srgbClr *attrValString - var schemeClr *aSchemeClr - - 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}} + spPr := &cSpPr{SolidFill: &aSolidFill{SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa((opts.order+i)%6+1)}}} + spPr = f.drawShapeFill(opts.Series[i].Fill, spPr) spPrScatter := &cSpPr{ Ln: &aLn{ W: 25400, @@ -748,12 +759,9 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr { } spPrLine := &cSpPr{ Ln: &aLn{ - W: f.ptToEMUs(opts.Series[i].Line.Width), - Cap: "rnd", // rnd, sq, flat - SolidFill: &aSolidFill{ - SchemeClr: schemeClr, - SrgbClr: srgbClr, - }, + W: f.ptToEMUs(opts.Series[i].Line.Width), + Cap: "rnd", // rnd, sq, flat + SolidFill: spPr.SolidFill, }, } if chartSeriesSpPr, ok := map[ChartType]*cSpPr{ @@ -761,7 +769,7 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr { }[opts.Type]; ok { return chartSeriesSpPr } - if srgbClr != nil { + if spPr.SolidFill.SrgbClr != nil { return spPr } return nil @@ -857,6 +865,7 @@ func (f *File) drawChartSeriesMarker(i int, opts *Chart) *cMarker { }, } } + marker.SpPr = f.drawShapeFill(opts.Series[i].Marker.Fill, marker.SpPr) chartSeriesMarker := map[ChartType]*cMarker{Scatter: marker, Line: marker} return chartSeriesMarker[opts.Type] } diff --git a/xmlChart.go b/xmlChart.go index ee97a69765..c5d601e0eb 100644 --- a/xmlChart.go +++ b/xmlChart.go @@ -561,6 +561,7 @@ type ChartPlotArea struct { ShowPercent bool ShowSerName bool ShowVal bool + Fill Fill NumFmt ChartNumFmt } @@ -576,6 +577,7 @@ type Chart struct { XAxis ChartAxis YAxis ChartAxis PlotArea ChartPlotArea + Fill Fill Border ChartLine ShowBlanksAs string BubbleSize int @@ -591,6 +593,7 @@ type ChartLegend struct { // ChartMarker directly maps the format settings of the chart marker. type ChartMarker struct { + Fill Fill Symbol string Size int }