Skip to content

Commit e8939ae

Browse files
committed
This simplify code and add support set font for each series of charts
1 parent 3580fc4 commit e8939ae

File tree

3 files changed

+55
-88
lines changed

3 files changed

+55
-88
lines changed

chart_test.go

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func TestAddChart(t *testing.T) {
171171
Width: 1,
172172
},
173173
},
174-
},
174+
Legend: ChartLegend{Font: &Font{Family: "Arial", Size: 11, Strike: true, Color: "777777"}}},
175175
}
176176
series2 := []ChartSeries{
177177
{
@@ -225,7 +225,7 @@ func TestAddChart(t *testing.T) {
225225
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: []RichTextRun{{Text: "2D Column Chart", Font: &Font{Size: 11, Family: "Calibri"}}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineNone}, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Family: "Times New Roman", Size: 15, Strike: true, 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}}}}}},
226226
{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", GapWidth: uintPtr(10), Overlap: intPtr(100)}},
227227
{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", XAxis: ChartAxis{Alignment: Alignment{Vertical: "wordArtVertRtl", TextRotation: 0}}}},
228-
{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"}},
228+
{sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false, Font: &Font{Size: 10}}, Title: []RichTextRun{{Text: "3D Clustered Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
229229
{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", XAxis: ChartAxis{Alignment: Alignment{Vertical: "vert", TextRotation: 0}}}},
230230
{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"}},
231231
{sheetName: "Sheet1", cell: "X45", opts: &Chart{Type: Radar, Series: series, Format: format, Legend: ChartLegend{Position: "top_right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Radar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "span"}},
@@ -324,57 +324,6 @@ func TestAddChart(t *testing.T) {
324324
assert.EqualError(t, f.AddChart("Sheet1", "P1", &Chart{Type: Col, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"}}, Title: []RichTextRun{{Text: "2D Column Chart"}}}), "XML syntax error on line 1: invalid UTF-8")
325325
}
326326

327-
func TestChartLegendFont(t *testing.T) {
328-
f := NewFile()
329-
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
330-
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
331-
332-
for k, v := range categories {
333-
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
334-
}
335-
for k, v := range values {
336-
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
337-
}
338-
339-
series := []ChartSeries{
340-
{Name: "Sheet1!$B$1", Categories: "Sheet1!$A$2:$A$4", Values: "Sheet1!$B$2:$B$4"},
341-
{Name: "Sheet1!$C$1", Categories: "Sheet1!$A$2:$A$4", Values: "Sheet1!$C$2:$C$4"},
342-
{Name: "Sheet1!$D$1", Categories: "Sheet1!$A$2:$A$4", Values: "Sheet1!$D$2:$D$4"},
343-
}
344-
345-
// Test chart with legend font settings
346-
assert.NoError(t, f.AddChart("Sheet1", "E1", &Chart{
347-
Type: Col,
348-
Series: series,
349-
Title: []RichTextRun{{Text: "Chart with Legend Font"}},
350-
Legend: ChartLegend{
351-
Position: "bottom",
352-
Font: Font{
353-
Family: "Aptos",
354-
Color: "#3E3E3E",
355-
Size: 10,
356-
Bold: true,
357-
},
358-
},
359-
}))
360-
361-
// Test chart with none position (should not cause errors)
362-
assert.NoError(t, f.AddChart("Sheet1", "E16", &Chart{
363-
Type: Col,
364-
Series: series,
365-
Title: []RichTextRun{{Text: "Chart without Legend"}},
366-
Legend: ChartLegend{
367-
Position: "none",
368-
Font: Font{
369-
Family: "Calibri",
370-
Size: 12,
371-
},
372-
},
373-
}))
374-
375-
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestChartLegendFont.xlsx")))
376-
}
377-
378327
func TestAddChartSheet(t *testing.T) {
379328
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
380329
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}

drawing.go

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -80,26 +80,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
8080
BackWall: &cThicknessSpPr{
8181
Thickness: &attrValInt{Val: intPtr(0)},
8282
},
83-
PlotArea: &cPlotArea{},
84-
Legend: &cLegend{
85-
LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[opts.Legend.Position])},
86-
Overlay: &attrValBool{Val: boolPtr(false)},
87-
TxPr: &cTxPr{
88-
BodyPr: aBodyPr{
89-
Rot: 0,
90-
SpcFirstLastPara: true,
91-
VertOverflow: "ellipsis",
92-
Vert: "horz",
93-
Wrap: "square",
94-
Anchor: "ctr",
95-
AnchorCtr: true,
96-
},
97-
P: aP{
98-
PPr: &aPPr{DefRPr: aRPr{}},
99-
},
100-
},
101-
},
102-
83+
PlotArea: &cPlotArea{},
10384
PlotVisOnly: &attrValBool{Val: boolPtr(false)},
10485
DispBlanksAs: &attrValString{Val: stringPtr(opts.ShowBlanksAs)},
10586
ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)},
@@ -179,12 +160,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
179160
Bubble: f.drawBubbleChart,
180161
Bubble3D: f.drawBubbleChart,
181162
}
182-
if opts.Legend.Position == "none" {
183-
xlsxChartSpace.Chart.Legend = nil
184-
} else if xlsxChartSpace.Chart.Legend != nil && xlsxChartSpace.Chart.Legend.TxPr != nil {
185-
// Apply font settings to legend
186-
drawChartFont(&opts.Legend.Font, &xlsxChartSpace.Chart.Legend.TxPr.P.PPr.DefRPr)
187-
}
163+
xlsxChartSpace.Chart.drawChartLegend(opts)
188164
xlsxChartSpace.Chart.PlotArea.SpPr = f.drawShapeFill(opts.PlotArea.Fill, xlsxChartSpace.Chart.PlotArea.SpPr)
189165
xlsxChartSpace.Chart.PlotArea.DTable = f.drawPlotAreaDTable(opts)
190166
addChart := func(c, p *cPlotArea) {
@@ -898,7 +874,8 @@ func (f *File) drawChartSeriesCat(v ChartSeries, opts *Chart) *cCat {
898874
func (f *File) drawChartSeriesVal(v ChartSeries, opts *Chart) *cVal {
899875
val := &cVal{
900876
NumRef: &cNumRef{
901-
F: v.Values,
877+
F: v.Values,
878+
NumCache: &cNumCache{},
902879
},
903880
}
904881
chartSeriesVal := map[ChartType]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil}
@@ -952,7 +929,8 @@ func (f *File) drawChartSeriesXVal(v ChartSeries, opts *Chart) *cCat {
952929
func (f *File) drawChartSeriesYVal(v ChartSeries, opts *Chart) *cVal {
953930
val := &cVal{
954931
NumRef: &cNumRef{
955-
F: v.Values,
932+
F: v.Values,
933+
NumCache: &cNumCache{},
956934
},
957935
}
958936
chartSeriesYVal := map[ChartType]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
@@ -971,7 +949,8 @@ func (f *File) drawCharSeriesBubbleSize(v ChartSeries, opts *Chart) *cVal {
971949
}
972950
return &cVal{
973951
NumRef: &cNumRef{
974-
F: fVal,
952+
F: fVal,
953+
NumCache: &cNumCache{},
975954
},
976955
}
977956
}
@@ -1360,6 +1339,36 @@ func (f *File) drawChartLn(opts *ChartLine) *aLn {
13601339
}
13611340
}
13621341

1342+
// drawChartLegend provides a function to draw the c:legend element.
1343+
func (c *cChart) drawChartLegend(opts *Chart) {
1344+
if opts.Legend.Position == "none" {
1345+
c.Legend = nil
1346+
return
1347+
}
1348+
if c.Legend == nil {
1349+
c.Legend = &cLegend{
1350+
LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[opts.Legend.Position])},
1351+
Overlay: &attrValBool{Val: boolPtr(false)},
1352+
}
1353+
}
1354+
if opts.Legend.Font != nil {
1355+
c.Legend.TxPr = &cTxPr{P: aP{PPr: &aPPr{}}}
1356+
drawChartFont(opts.Legend.Font, &c.Legend.TxPr.P.PPr.DefRPr)
1357+
}
1358+
for k := range opts.Series {
1359+
font := opts.Series[k].Legend.Font
1360+
if font == nil {
1361+
continue
1362+
}
1363+
legendEntry := cLegendEntry{
1364+
IDx: &attrValInt{Val: intPtr(k + opts.order)},
1365+
TxPr: &cTxPr{P: aP{PPr: &aPPr{}}},
1366+
}
1367+
drawChartFont(font, &legendEntry.TxPr.P.PPr.DefRPr)
1368+
c.Legend.LegendEntry = append(c.Legend.LegendEntry, legendEntry)
1369+
}
1370+
}
1371+
13631372
// drawingParser provides a function to parse drawingXML. In order to solve
13641373
// the problem that the label structure is changed after serialization and
13651374
// deserialization, two different structures: decodeWsDr and encodeWsDr are

xmlChart.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,22 @@ type cDLbls struct {
509509
ShowLeaderLines *attrValBool `xml:"showLeaderLines"`
510510
}
511511

512+
// cLegendEntry (Legend Entry) directly maps the legendEntry element. This
513+
// element specifies the legend entry.
514+
type cLegendEntry struct {
515+
IDx *attrValInt `xml:"idx"`
516+
TxPr *cTxPr `xml:"txPr"`
517+
}
518+
512519
// cLegend (Legend) directly maps the legend element. This element specifies
513520
// the legend.
514521
type cLegend struct {
515-
Layout *string `xml:"layout"`
516-
LegendPos *attrValString `xml:"legendPos"`
517-
Overlay *attrValBool `xml:"overlay"`
518-
SpPr *cSpPr `xml:"spPr"`
519-
TxPr *cTxPr `xml:"txPr"`
522+
Layout *string `xml:"layout"`
523+
LegendPos *attrValString `xml:"legendPos"`
524+
LegendEntry []cLegendEntry `xml:"legendEntry"`
525+
Overlay *attrValBool `xml:"overlay"`
526+
SpPr *cSpPr `xml:"spPr"`
527+
TxPr *cTxPr `xml:"txPr"`
520528
}
521529

522530
// cPrintSettings directly maps the printSettings element. This element
@@ -611,7 +619,7 @@ type Chart struct {
611619
type ChartLegend struct {
612620
Position string
613621
ShowLegendKey bool
614-
Font Font
622+
Font *Font
615623
}
616624

617625
// ChartMarker directly maps the format settings of the chart marker.
@@ -645,6 +653,7 @@ type ChartSeries struct {
645653
Values string
646654
Sizes string
647655
Fill Fill
656+
Legend ChartLegend
648657
Line ChartLine
649658
Marker ChartMarker
650659
DataLabel ChartDataLabel

0 commit comments

Comments
 (0)