Skip to content

Commit 9e0c94e

Browse files
authored
Add 4 stock chart support for AddChart and AddChartSheet function (#2177)
- Support create 4 kinds of box and whisker stock charts: "High-Low-Close", "Open-High-Low-Close", "Volume-High-Low-Close", "Volume-Open-High-Low-Close" - Add new ChartUpDownBar data type - Add 2 new fields UpBars and DownBars in the ChartPlotArea type - Update unit tests
1 parent 2e1d05f commit 9e0c94e

File tree

4 files changed

+195
-8
lines changed

4 files changed

+195
-8
lines changed

chart.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ const (
7878
WireframeContour
7979
Bubble
8080
Bubble3D
81+
StockHighLowClose
82+
StockOpenHighLowClose
8183
)
8284

8385
// ChartDashType is the type of supported chart dash types.
@@ -365,6 +367,8 @@ var (
365367
WireframeContour: "General",
366368
Bubble: "General",
367369
Bubble3D: "General",
370+
StockHighLowClose: "General",
371+
StockOpenHighLowClose: "General",
368372
}
369373
chartValAxCrossBetween = map[ChartType]string{
370374
Area: "midCat",
@@ -422,6 +426,8 @@ var (
422426
WireframeContour: "midCat",
423427
Bubble: "midCat",
424428
Bubble3D: "midCat",
429+
StockHighLowClose: "between",
430+
StockOpenHighLowClose: "between",
425431
}
426432
plotAreaChartGrouping = map[ChartType]string{
427433
Area: "standard",
@@ -768,6 +774,8 @@ func (opts *Chart) parseTitle() {
768774
// 52 | WireframeContour | wireframe contour chart
769775
// 53 | Bubble | bubble chart
770776
// 54 | Bubble3D | 3D bubble chart
777+
// 55 | StockHighLowClose | High-Low-Close stock chart
778+
// 56 | StockOpenHighLowClose | Open-High-Low-Close stock chart
771779
//
772780
// In Excel a chart series is a collection of information that defines which
773781
// data is plotted such as values, axis labels and formatting.

chart_test.go

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,11 @@ func TestAddChart(t *testing.T) {
312312
// Test with illegal cell reference
313313
assert.EqualError(t, f.AddChart("Sheet2", "A", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
314314
// Test with unsupported chart type
315-
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: 0x37, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble 3D Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}), newUnsupportedChartType(0x37).Error())
315+
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: 0x39, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble 3D Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}), newUnsupportedChartType(0x39).Error())
316316
// Test add combo chart with invalid format set
317317
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}, nil), ErrParameterInvalid.Error())
318318
// Test add combo chart with unsupported chart type
319-
assert.EqualError(t, f.AddChart("Sheet2", "BD64", &Chart{Type: BarOfPie, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bar of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}, &Chart{Type: 0x37, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bar of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}), newUnsupportedChartType(0x37).Error())
319+
assert.EqualError(t, f.AddChart("Sheet2", "BD64", &Chart{Type: BarOfPie, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bar of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}, &Chart{Type: 0x39, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bar of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}), newUnsupportedChartType(0x39).Error())
320320
// Test add chart with series transparency value exceeds limit
321321
assert.Equal(t, ErrTransparency, f.AddChart("Sheet1", "BD64", &Chart{Type: Col, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Fill: Fill{Transparency: 110}}}}))
322322
// Test add chart with transparency value exceeds limit
@@ -327,6 +327,113 @@ func TestAddChart(t *testing.T) {
327327
f.ContentTypes = nil
328328
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
329329
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")
330+
331+
t.Run("for_create_stock_chart", func(t *testing.T) {
332+
f := NewFile()
333+
for i, row := range [][]interface{}{
334+
{"Date", "Volume", "Open", "High", "Low", "Close"},
335+
{45593, 14864000, 431.66, 431.94, 426.3, 426.59},
336+
{45590, 16899100, 426.76, 432.52, 426.57, 428.15},
337+
{45589, 13581600, 425.33, 425.98, 422.4, 424.73},
338+
{45588, 19654400, 430.86, 431.08, 422.53, 424.6},
339+
{45587, 25482200, 418.49, 430.58, 418.04, 427.51},
340+
{45586, 14206100, 416.12, 418.96, 413.75, 418.78},
341+
{45583, 17145300, 417.14, 419.65, 416.26, 418.16},
342+
{45582, 14820000, 422.36, 422.5, 415.59, 416.72},
343+
{45581, 15508900, 415.17, 416.36, 410.48, 416.12},
344+
{45580, 18900200, 422.18, 422.48, 415.26, 418.74},
345+
{45579, 16653100, 417.77, 424.04, 417.52, 419.14},
346+
{45576, 14144900, 416.14, 417.13, 413.25, 416.32},
347+
{45575, 13848400, 415.23, 417.35, 413.15, 415.84},
348+
{45574, 14974300, 415.86, 420.38, 414.3, 417.46},
349+
{45573, 19229300, 410.9, 415.66, 408.17, 414.71},
350+
{45572, 20919800, 416, 417.11, 409, 409.54},
351+
{45569, 19169700, 418.24, 419.75, 414.97, 416.06},
352+
{45568, 13686400, 417.63, 419.55, 414.29, 416.54},
353+
{45567, 16582300, 422.58, 422.82, 416.71, 417.13},
354+
{45566, 19092900, 428.45, 428.48, 418.81, 420.69},
355+
{45565, 16807300, 428.21, 430.42, 425.37, 430.3},
356+
} {
357+
cell, err := CoordinatesToCellName(1, i+1)
358+
assert.NoError(t, err)
359+
assert.NoError(t, f.SetSheetRow("Sheet1", cell, &row))
360+
}
361+
style, err := f.NewStyle(&Style{NumFmt: 15})
362+
assert.NoError(t, err)
363+
assert.NoError(t, f.SetColStyle("Sheet1", "A", style))
364+
365+
assert.NoError(t, f.AddChart("Sheet1", "G1", &Chart{
366+
Type: StockHighLowClose,
367+
Series: []ChartSeries{
368+
{Name: "Sheet1!$D$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$D$2:$D$22"},
369+
{Name: "Sheet1!$E$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$E$2:$E$22"},
370+
{Name: "Sheet1!$F$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$F$2:$F$22"},
371+
},
372+
Legend: ChartLegend{Position: "none"},
373+
Title: []RichTextRun{{Text: "High-Low-Close Stock Chart"}},
374+
XAxis: ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: "d-mmm-yy"}},
375+
}))
376+
assert.NoError(t, f.AddChart("Sheet1", "G16", &Chart{
377+
Type: StockOpenHighLowClose,
378+
Series: []ChartSeries{
379+
{Name: "Sheet1!$C$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$C$2:$C$22"},
380+
{Name: "Sheet1!$D$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$D$2:$D$22"},
381+
{Name: "Sheet1!$E$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$E$2:$E$22"},
382+
{Name: "Sheet1!$F$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$F$2:$F$22"},
383+
},
384+
Legend: ChartLegend{Position: "none"},
385+
Title: []RichTextRun{{Text: "Open-High-Low-Close Stock Chart"}},
386+
XAxis: ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: "d-mmm-yy"}},
387+
PlotArea: ChartPlotArea{
388+
UpBars: ChartUpDownBar{
389+
Border: ChartLine{Type: ChartLineNone},
390+
Fill: Fill{Type: "pattern", Color: []string{"00B050"}, Pattern: 1},
391+
},
392+
DownBars: ChartUpDownBar{
393+
Border: ChartLine{Type: ChartLineNone},
394+
Fill: Fill{Type: "pattern", Color: []string{"FF0000"}, Pattern: 1},
395+
},
396+
},
397+
}))
398+
assert.NoError(t, f.AddChart("Sheet1", "O1", &Chart{
399+
Type: Col,
400+
Series: []ChartSeries{
401+
{Name: "Sheet1!$B$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$B$2:$B$22"},
402+
},
403+
VaryColors: boolPtr(false),
404+
XAxis: ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: "d-mmm-yy"}},
405+
YAxis: ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: "#,##0"}},
406+
Title: []RichTextRun{{Text: "Volume-High-Low-Close Stock Chart"}},
407+
}, &Chart{
408+
Type: StockHighLowClose,
409+
Series: []ChartSeries{
410+
{Name: "Sheet1!$D$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$D$2:$D$22"},
411+
{Name: "Sheet1!$E$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$E$2:$E$22"},
412+
{Name: "Sheet1!$F$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$F$2:$F$22"},
413+
},
414+
YAxis: ChartAxis{Secondary: true},
415+
}))
416+
assert.NoError(t, f.AddChart("Sheet1", "O16", &Chart{
417+
Type: Col,
418+
Series: []ChartSeries{
419+
{Name: "Sheet1!$B$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$B$2:$B$22"},
420+
},
421+
VaryColors: boolPtr(false),
422+
XAxis: ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: "d-mmm-yy"}},
423+
YAxis: ChartAxis{NumFmt: ChartNumFmt{CustomNumFmt: "#,##0"}},
424+
Title: []RichTextRun{{Text: "Volume-Open-High-Low-Close Stock Chart"}},
425+
}, &Chart{
426+
Type: StockOpenHighLowClose,
427+
Series: []ChartSeries{
428+
{Name: "Sheet1!$C$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$C$2:$C$22"},
429+
{Name: "Sheet1!$D$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$D$2:$D$22"},
430+
{Name: "Sheet1!$E$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$E$2:$E$22"},
431+
{Name: "Sheet1!$F$1", Categories: "Sheet1!$A$2:$A$22", Values: "Sheet1!$F$2:$F$22"},
432+
},
433+
YAxis: ChartAxis{Secondary: true},
434+
}))
435+
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChartStock.xlsx")))
436+
})
330437
}
331438

332439
func TestAddChartSheet(t *testing.T) {
@@ -363,7 +470,7 @@ func TestAddChartSheet(t *testing.T) {
363470
// Test add chartsheet with invalid sheet name
364471
assert.EqualError(t, f.AddChartSheet("Sheet:1", nil, &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}), ErrSheetNameInvalid.Error())
365472
// Test with unsupported chart type
366-
assert.EqualError(t, f.AddChartSheet("Chart2", &Chart{Type: 0x37, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}), newUnsupportedChartType(0x37).Error())
473+
assert.EqualError(t, f.AddChartSheet("Chart2", &Chart{Type: 0x39, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}), newUnsupportedChartType(0x39).Error())
367474

368475
assert.NoError(t, f.UpdateLinkedValue())
369476

drawing.go

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
159159
WireframeContour: f.drawSurfaceChart,
160160
Bubble: f.drawBubbleChart,
161161
Bubble3D: f.drawBubbleChart,
162+
StockHighLowClose: f.drawStockChart,
163+
StockOpenHighLowClose: f.drawStockChart,
162164
}
163165
xlsxChartSpace.Chart.drawChartLegend(opts)
164166
xlsxChartSpace.Chart.PlotArea.SpPr = f.drawShapeFill(opts.PlotArea.Fill, xlsxChartSpace.Chart.PlotArea.SpPr)
@@ -171,7 +173,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
171173
continue
172174
}
173175
fld := immutable.FieldByName(mutable.Type().Field(i).Name)
174-
if field.Kind() == reflect.Slice && i < 16 { // All []*cCharts type fields
176+
if field.Kind() == reflect.Slice && i < 17 { // All []*cCharts type fields
175177
fld.Set(reflect.Append(fld, field.Index(0)))
176178
continue
177179
}
@@ -185,6 +187,10 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
185187
addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](xlsxChartSpace.Chart.PlotArea, comboCharts[idx]))
186188
order += len(comboCharts[idx].Series)
187189
}
190+
// If the dateAx field exists, valAx field should be nil.
191+
if xlsxChartSpace.Chart.PlotArea != nil && xlsxChartSpace.Chart.PlotArea.DateAx != nil {
192+
xlsxChartSpace.Chart.PlotArea.CatAx = nil
193+
}
188194
chart, _ := xml.Marshal(xlsxChartSpace)
189195
media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
190196
f.saveFileList(media, chart)
@@ -689,6 +695,39 @@ func (f *File) drawBubbleChart(pa *cPlotArea, opts *Chart) *cPlotArea {
689695
return plotArea
690696
}
691697

698+
// drawStockChart provides a function to draw the c:stockChart element by
699+
// given format sets.
700+
func (f *File) drawStockChart(pa *cPlotArea, opts *Chart) *cPlotArea {
701+
plotArea := &cPlotArea{
702+
StockChart: []*cCharts{
703+
{
704+
VaryColors: &attrValBool{
705+
Val: opts.VaryColors,
706+
},
707+
Ser: f.drawChartSeries(opts),
708+
DLbls: f.drawChartDLbls(opts),
709+
AxID: f.genAxID(opts),
710+
},
711+
},
712+
ValAx: f.drawPlotAreaValAx(pa, opts),
713+
DateAx: f.drawPlotAreaCatAx(pa, opts),
714+
}
715+
if opts.Type == StockHighLowClose {
716+
plotArea.StockChart[0].HiLowLines = &cChartLines{}
717+
}
718+
if opts.Type == StockOpenHighLowClose {
719+
plotArea.StockChart[0].HiLowLines = &cChartLines{}
720+
plotArea.StockChart[0].UpDownBars = &cUpDownBars{
721+
GapWidth: &attrValString{Val: stringPtr("150")},
722+
UpBars: &cChartLines{f.drawShapeFill(opts.PlotArea.UpBars.Fill, &cSpPr{Ln: f.drawChartLn(&opts.PlotArea.UpBars.Border)})},
723+
DownBars: &cChartLines{f.drawShapeFill(opts.PlotArea.DownBars.Fill, &cSpPr{Ln: f.drawChartLn(&opts.PlotArea.UpBars.Border)})},
724+
}
725+
}
726+
ser := *plotArea.StockChart[0].Ser
727+
ser[0].Val.NumRef.NumCache = &cNumCache{}
728+
return plotArea
729+
}
730+
692731
// drawChartGapWidth provides a function to draw the c:gapWidth element by given
693732
// format sets.
694733
func (f *File) drawChartGapWidth(opts *Chart) *attrValInt {
@@ -818,8 +857,10 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr {
818857
}
819858
noLn := &cSpPr{Ln: &aLn{NoFill: &attrValString{}}}
820859
if chartSeriesSpPr, ok := map[ChartType]map[ChartLineType]*cSpPr{
821-
Line: {ChartLineUnset: solid, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: solid},
822-
Scatter: {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},
860+
Line: {ChartLineUnset: solid, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: solid},
861+
Scatter: {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},
862+
StockHighLowClose: {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},
863+
StockOpenHighLowClose: {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},
823864
}[opts.Type]; ok {
824865
return chartSeriesSpPr[opts.Series[i].Line.Type]
825866
}
@@ -892,7 +933,11 @@ func (f *File) drawChartSeriesVal(v ChartSeries, opts *Chart) *cVal {
892933
// drawChartSeriesMarker provides a function to draw the c:marker element by
893934
// given data index and format sets.
894935
func (f *File) drawChartSeriesMarker(i int, opts *Chart) *cMarker {
895-
defaultSymbol := map[ChartType]*attrValString{Scatter: {Val: stringPtr("circle")}}
936+
defaultSymbol := map[ChartType]*attrValString{
937+
Scatter: {Val: stringPtr("circle")},
938+
StockHighLowClose: {Val: stringPtr("dot")},
939+
StockOpenHighLowClose: {Val: stringPtr("none")},
940+
}
896941
marker := &cMarker{
897942
Symbol: defaultSymbol[opts.Type],
898943
Size: &attrValInt{Val: intPtr(5)},
@@ -912,7 +957,12 @@ func (f *File) drawChartSeriesMarker(i int, opts *Chart) *cMarker {
912957
if marker.SpPr != nil && marker.SpPr.Ln != nil {
913958
marker.SpPr.Ln = f.drawChartLn(&opts.Series[i].Marker.Border)
914959
}
915-
chartSeriesMarker := map[ChartType]*cMarker{Scatter: marker, Line: marker}
960+
chartSeriesMarker := map[ChartType]*cMarker{
961+
Scatter: marker,
962+
Line: marker,
963+
StockHighLowClose: marker,
964+
StockOpenHighLowClose: marker,
965+
}
916966
return chartSeriesMarker[opts.Type]
917967
}
918968

xmlChart.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ type cPlotArea struct {
356356
DoughnutChart []*cCharts `xml:"doughnutChart"`
357357
LineChart []*cCharts `xml:"lineChart"`
358358
Line3DChart []*cCharts `xml:"line3DChart"`
359+
StockChart []*cCharts `xml:"stockChart"`
359360
PieChart []*cCharts `xml:"pieChart"`
360361
Pie3DChart []*cCharts `xml:"pie3DChart"`
361362
OfPieChart []*cCharts `xml:"ofPieChart"`
@@ -365,6 +366,7 @@ type cPlotArea struct {
365366
SurfaceChart []*cCharts `xml:"surfaceChart"`
366367
CatAx []*cAxs `xml:"catAx"`
367368
ValAx []*cAxs `xml:"valAx"`
369+
DateAx []*cAxs `xml:"dateAx"`
368370
SerAx []*cAxs `xml:"serAx"`
369371
DTable *cDTable `xml:"dTable"`
370372
SpPr *cSpPr `xml:"spPr"`
@@ -384,6 +386,8 @@ type cCharts struct {
384386
SplitPos *attrValInt `xml:"splitPos"`
385387
SerLines *attrValString `xml:"serLines"`
386388
DLbls *cDLbls `xml:"dLbls"`
389+
HiLowLines *cChartLines `xml:"hiLowLines"`
390+
UpDownBars *cUpDownBars `xml:"upDownBars"`
387391
GapWidth *attrValInt `xml:"gapWidth"`
388392
Shape *attrValString `xml:"shape"`
389393
HoleSize *attrValInt `xml:"holeSize"`
@@ -420,6 +424,15 @@ type cAxs struct {
420424
NoMultiLvlLbl *attrValBool `xml:"noMultiLvlLbl"`
421425
}
422426

427+
// cUpDownBars directly maps the upDownBars lement. This element specifies
428+
// the up and down bars.
429+
type cUpDownBars struct {
430+
GapWidth *attrValString `xml:"gapWidth"`
431+
UpBars *cChartLines `xml:"upBars"`
432+
DownBars *cChartLines `xml:"downBars"`
433+
ExtLst *xlsxExtLst `xml:"extLst"`
434+
}
435+
423436
// cChartLines directly maps the chart lines content model.
424437
type cChartLines struct {
425438
SpPr *cSpPr `xml:"spPr"`
@@ -613,6 +626,13 @@ type ChartDimension struct {
613626
Height uint
614627
}
615628

629+
// ChartUpDownBar directly maps the format settings of the stock chart up bars
630+
// and down bars.
631+
type ChartUpDownBar struct {
632+
Fill Fill
633+
Border ChartLine
634+
}
635+
616636
// ChartPlotArea directly maps the format settings of the plot area.
617637
type ChartPlotArea struct {
618638
SecondPlotValues int
@@ -625,6 +645,8 @@ type ChartPlotArea struct {
625645
ShowSerName bool
626646
ShowVal bool
627647
Fill Fill
648+
UpBars ChartUpDownBar
649+
DownBars ChartUpDownBar
628650
NumFmt ChartNumFmt
629651
}
630652

0 commit comments

Comments
 (0)