From 9ddb52eac4e451f676dabe4eed45ee95fce38eef Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 14 Jan 2020 00:33:36 +0800 Subject: Fix #554, init combo chart support, new chart pie of pie, bar of pie chart support --- chart.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- chart_test.go | 27 ++++++++++++++- xmlChart.go | 12 ++++--- 3 files changed, 136 insertions(+), 12 deletions(-) diff --git a/chart.go b/chart.go index a8fcaf5..f3b1cd8 100644 --- a/chart.go +++ b/chart.go @@ -16,6 +16,7 @@ import ( "errors" "io" "log" + "reflect" "strconv" "strings" ) @@ -66,6 +67,8 @@ const ( Line = "line" Pie = "pie" Pie3D = "pie3D" + PieOfPieChart = "pieOfPie" + BarOfPieChart = "barOfPie" Radar = "radar" Scatter = "scatter" Surface3D = "surface3D" @@ -123,6 +126,8 @@ var ( Line: 0, Pie: 0, Pie3D: 30, + PieOfPieChart: 0, + BarOfPieChart: 0, Radar: 0, Scatter: 0, Surface3D: 15, @@ -175,6 +180,8 @@ var ( Line: 0, Pie: 0, Pie3D: 0, + PieOfPieChart: 0, + BarOfPieChart: 0, Radar: 0, Scatter: 0, Surface3D: 20, @@ -237,6 +244,8 @@ var ( Line: 0, Pie: 0, Pie3D: 0, + PieOfPieChart: 0, + BarOfPieChart: 0, Radar: 0, Scatter: 0, Surface3D: 0, @@ -297,6 +306,8 @@ var ( Line: "General", Pie: "General", Pie3D: "General", + PieOfPieChart: "General", + BarOfPieChart: "General", Radar: "General", Scatter: "General", Surface3D: "General", @@ -351,6 +362,8 @@ var ( Line: "between", Pie: "between", Pie3D: "between", + PieOfPieChart: "between", + BarOfPieChart: "between", Radar: "between", Scatter: "between", Surface3D: "midCat", @@ -491,7 +504,7 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) { // AddChart provides the method to add chart in a sheet by given chart format // set (such as offset, scale, aspect ratio setting and print settings) and // properties set. For example, create 3D clustered column chart with data -// Sheet1!$A$29:$D$32: +// Sheet1!$E$1:$L$15: // // package main // @@ -507,12 +520,12 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) { // for k, v := range values { // f.SetCellValue("Sheet1", k, v) // } -// if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","dimension":{"width":640,"height":480},"series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"bottom","show_legend_key":false},"title":{"name":"Fruit 3D Clustered Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"reverse_order":true},"y_axis":{"maximum":7.5,"minimum":0.5}}`); err != nil { +// if err := f.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"reverse_order":true},"y_axis":{"maximum":7.5,"minimum":0.5}}`); err != nil { // println(err.Error()) // return // } // // Save xlsx file by the given path. -// if err := xlsx.SaveAs("Book1.xlsx"); err != nil { +// if err := f.SaveAs("Book1.xlsx"); err != nil { // println(err.Error()) // } // } @@ -565,6 +578,8 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) { // line | line chart // pie | pie chart // pie3D | 3D pie chart +// pieOfPie | pie of pie chart +// barOfPie | bar of pie chart // radar | radar chart // scatter | scatter chart // surface3D | 3D surface chart @@ -681,6 +696,34 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) { // // Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290. // +// combo: Specifies tha create a chart that combines two art types in a single +// chart. For example, create a clustered column - line chart with data +// Sheet1!$E$1:$L$15: +// +// package main +// +// import "github.com/360EntSecGroup-Skylar/excelize" +// +// func main() { +// categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"} +// values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8} +// f := excelize.NewFile() +// for k, v := range categories { +// f.SetCellValue("Sheet1", k, v) +// } +// for k, v := range values { +// f.SetCellValue("Sheet1", k, v) +// } +// if err := f.AddChart("Sheet1", "E1", `{"type":"col","series":[{"name":"Sheet1!$A$2","categories":"","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Clustered Column - Line Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"combo":{"type":"line","series":[{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}}`); err != nil { +// println(err.Error()) +// return +// } +// // Save xlsx file by the given path. +// if err := f.SaveAs("Book1.xlsx"); err != nil { +// println(err.Error()) +// } +// } +// func (f *File) AddChart(sheet, cell, format string) error { formatSet, err := parseFormatChartSet(format) if err != nil { @@ -915,6 +958,8 @@ func (f *File) addChart(formatSet *formatChart) { Line: f.drawLineChart, Pie3D: f.drawPie3DChart, Pie: f.drawPieChart, + PieOfPieChart: f.drawPieOfPieChart, + BarOfPieChart: f.drawBarOfPieChart, Radar: f.drawRadarChart, Scatter: f.drawScatterChart, Surface3D: f.drawSurface3DChart, @@ -924,8 +969,20 @@ func (f *File) addChart(formatSet *formatChart) { Bubble: f.drawBaseChart, Bubble3D: f.drawBaseChart, } - xlsxChartSpace.Chart.PlotArea = plotAreaFunc[formatSet.Type](formatSet) - + addChart := func(c, p *cPlotArea) { + immutable, mutable := reflect.ValueOf(c).Elem(), reflect.ValueOf(p).Elem() + for i := 0; i < mutable.NumField(); i++ { + field := mutable.Field(i) + if field.IsNil() { + continue + } + immutable.FieldByName(mutable.Type().Field(i).Name).Set(field) + } + } + addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[formatSet.Type](formatSet)) + if formatSet.Combo != nil { + addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[formatSet.Combo.Type](formatSet.Combo)) + } chart, _ := xml.Marshal(xlsxChartSpace) media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml" f.saveFileList(media, chart) @@ -1246,6 +1303,40 @@ func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea { } } +// drawPieOfPieChart provides a function to draw the c:plotArea element for +// pie chart by given format sets. +func (f *File) drawPieOfPieChart(formatSet *formatChart) *cPlotArea { + return &cPlotArea{ + PieChart: &cCharts{ + OfPieType: &attrValString{ + Val: stringPtr("pie"), + }, + VaryColors: &attrValBool{ + Val: boolPtr(true), + }, + Ser: f.drawChartSeries(formatSet), + SerLines: &attrValString{}, + }, + } +} + +// drawBarOfPieChart provides a function to draw the c:plotArea element for +// pie chart by given format sets. +func (f *File) drawBarOfPieChart(formatSet *formatChart) *cPlotArea { + return &cPlotArea{ + PieChart: &cCharts{ + OfPieType: &attrValString{ + Val: stringPtr("bar"), + }, + VaryColors: &attrValBool{ + Val: boolPtr(true), + }, + Ser: f.drawChartSeries(formatSet), + SerLines: &attrValString{}, + }, + } +} + // drawRadarChart provides a function to draw the c:plotArea element for radar // chart by given format sets. func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea { @@ -1371,11 +1462,15 @@ func (f *File) drawChartShape(formatSet *formatChart) *attrValString { // drawChartSeries provides a function to draw the c:ser element by given // format sets. func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer { + var baseIdx int + if formatSet.Combo != nil { + baseIdx = len(formatSet.Combo.Series) + } ser := []cSer{} for k := range formatSet.Series { ser = append(ser, cSer{ - IDx: &attrValInt{Val: intPtr(k)}, - Order: &attrValInt{Val: intPtr(k)}, + IDx: &attrValInt{Val: intPtr(k + baseIdx)}, + Order: &attrValInt{Val: intPtr(k + baseIdx)}, Tx: &cTx{ StrRef: &cStrRef{ F: formatSet.Series[k].Name, diff --git a/chart_test.go b/chart_test.go index 2ed7944..e350657 100644 --- a/chart_test.go +++ b/chart_test.go @@ -3,6 +3,7 @@ package excelize import ( "bytes" "encoding/xml" + "fmt" "path/filepath" "testing" @@ -172,7 +173,31 @@ func TestAddChart(t *testing.T) { // bubble chart assert.NoError(t, f.AddChart("Sheet2", "BD16", `{"type":"bubble","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`)) assert.NoError(t, f.AddChart("Sheet2", "BD32", `{"type":"bubble3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`)) + // pie of pie chart + assert.NoError(t, f.AddChart("Sheet2", "BD48", `{"type":"pieOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Pie of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`)) + // bar of pie chart + assert.NoError(t, f.AddChart("Sheet2", "BD64", `{"type":"barOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`)) + // combo chart + f.NewSheet("Combo Charts") + clusteredColumnCombo := map[string][]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"}, + } + for axis, props := range clusteredColumnCombo { + assert.NoError(t, f.AddChart("Combo Charts", axis, fmt.Sprintf(`{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"%s"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"combo":{"type":"%s","series":[{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}}`, props[1], props[0]))) + } + 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"}, + } + for axis, props := range stackedAreaCombo { + assert.NoError(t, f.AddChart("Combo Charts", axis, fmt.Sprintf(`{"type":"areaStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"%s"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"combo":{"type":"%s","series":[{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}}`, props[1], props[0]))) + } assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx"))) - + // Test with unsupported chart type assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"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"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), "unsupported chart type unknown") } diff --git a/xmlChart.go b/xmlChart.go index b6d041e..9d6263f 100644 --- a/xmlChart.go +++ b/xmlChart.go @@ -312,6 +312,7 @@ type cPlotArea struct { LineChart *cCharts `xml:"lineChart"` PieChart *cCharts `xml:"pieChart"` Pie3DChart *cCharts `xml:"pie3DChart"` + OfPieChart *cCharts `xml:"ofPieChart"` RadarChart *cCharts `xml:"radarChart"` ScatterChart *cCharts `xml:"scatterChart"` Surface3DChart *cCharts `xml:"surface3DChart"` @@ -329,6 +330,8 @@ type cCharts struct { Grouping *attrValString `xml:"grouping"` RadarStyle *attrValString `xml:"radarStyle"` ScatterStyle *attrValString `xml:"scatterStyle"` + OfPieType *attrValString `xml:"ofPieType"` + SerLines *attrValString `xml:"serLines"` VaryColors *attrValBool `xml:"varyColors"` Wireframe *attrValBool `xml:"wireframe"` Ser *[]cSer `xml:"ser"` @@ -590,10 +593,11 @@ type formatChart struct { } `json:"fill"` Layout formatLayout `json:"layout"` } `json:"plotarea"` - ShowBlanksAs string `json:"show_blanks_as"` - ShowHiddenData bool `json:"show_hidden_data"` - SetRotation int `json:"set_rotation"` - SetHoleSize int `json:"set_hole_size"` + ShowBlanksAs string `json:"show_blanks_as"` + ShowHiddenData bool `json:"show_hidden_data"` + SetRotation int `json:"set_rotation"` + SetHoleSize int `json:"set_hole_size"` + Combo *formatChart `json:"combo"` } // formatChartLegend directly maps the format settings of the chart legend. -- cgit v1.2.1