From 0e657c887bf505d62ce3bf685c518cd0ed7bc558 Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 12 Oct 2022 00:06:09 +0800 Subject: [PATCH] This closes #1368, fixes number parsing issue, adds support for create a 3D line chart --- cell_test.go | 27 ++++++++++++--------------- chart.go | 10 ++++++++++ chart_test.go | 3 ++- drawing.go | 26 +++++++++++++++++++++++++- lib.go | 3 +++ styles.go | 21 +++++++++++++++++++++ xmlChart.go | 1 + 7 files changed, 74 insertions(+), 17 deletions(-) diff --git a/cell_test.go b/cell_test.go index 02057051..75980583 100644 --- a/cell_test.go +++ b/cell_test.go @@ -683,51 +683,48 @@ func TestSetCellRichText(t *testing.T) { func TestFormattedValue2(t *testing.T) { f := NewFile() - v := f.formattedValue(0, "43528", false) - assert.Equal(t, "43528", v) + assert.Equal(t, "43528", f.formattedValue(0, "43528", false)) - v = f.formattedValue(15, "43528", false) - assert.Equal(t, "43528", v) + assert.Equal(t, "43528", f.formattedValue(15, "43528", false)) - v = f.formattedValue(1, "43528", false) - assert.Equal(t, "43528", v) + assert.Equal(t, "43528", f.formattedValue(1, "43528", false)) customNumFmt := "[$-409]MM/DD/YYYY" _, err := f.NewStyle(&Style{ CustomNumFmt: &customNumFmt, }) assert.NoError(t, err) - v = f.formattedValue(1, "43528", false) - assert.Equal(t, "03/04/2019", v) + assert.Equal(t, "03/04/2019", f.formattedValue(1, "43528", false)) // formatted value with no built-in number format ID numFmtID := 5 f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ NumFmtID: &numFmtID, }) - v = f.formattedValue(2, "43528", false) - assert.Equal(t, "43528", v) + assert.Equal(t, "43528", f.formattedValue(2, "43528", false)) // formatted value with invalid number format ID f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ NumFmtID: nil, }) - _ = f.formattedValue(3, "43528", false) + assert.Equal(t, "43528", f.formattedValue(3, "43528", false)) // formatted value with empty number format f.Styles.NumFmts = nil f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ NumFmtID: &numFmtID, }) - v = f.formattedValue(1, "43528", false) - assert.Equal(t, "43528", v) + assert.Equal(t, "43528", f.formattedValue(1, "43528", false)) // formatted decimal value with build-in number format ID styleID, err := f.NewStyle(&Style{ NumFmt: 1, }) assert.NoError(t, err) - v = f.formattedValue(styleID, "310.56", false) - assert.Equal(t, "311", v) + assert.Equal(t, "311", f.formattedValue(styleID, "310.56", false)) + + for _, fn := range builtInNumFmtFunc { + assert.Equal(t, "0_0", fn("0_0", "", false)) + } } func TestSharedStringsError(t *testing.T) { diff --git a/chart.go b/chart.go index 24ad2076..0caa5054 100644 --- a/chart.go +++ b/chart.go @@ -63,6 +63,7 @@ const ( Col3DCylinderPercentStacked = "col3DCylinderPercentStacked" Doughnut = "doughnut" Line = "line" + Line3D = "line3D" Pie = "pie" Pie3D = "pie3D" PieOfPieChart = "pieOfPie" @@ -122,6 +123,7 @@ var ( Col3DCylinderPercentStacked: 15, Doughnut: 0, Line: 0, + Line3D: 20, Pie: 0, Pie3D: 30, PieOfPieChart: 0, @@ -176,6 +178,7 @@ var ( Col3DCylinderPercentStacked: 20, Doughnut: 0, Line: 0, + Line3D: 15, Pie: 0, Pie3D: 0, PieOfPieChart: 0, @@ -194,6 +197,7 @@ var ( ColPercentStacked: 100, } chartView3DPerspective = map[string]int{ + Line3D: 30, Contour: 0, WireframeContour: 0, } @@ -240,6 +244,7 @@ var ( Col3DCylinderPercentStacked: 1, Doughnut: 0, Line: 0, + Line3D: 0, Pie: 0, Pie3D: 0, PieOfPieChart: 0, @@ -302,6 +307,7 @@ var ( Col3DCylinderPercentStacked: "0%", Doughnut: "General", Line: "General", + Line3D: "General", Pie: "General", Pie3D: "General", PieOfPieChart: "General", @@ -358,6 +364,7 @@ var ( Col3DCylinderPercentStacked: "between", Doughnut: "between", Line: "between", + Line3D: "between", Pie: "between", Pie3D: "between", PieOfPieChart: "between", @@ -413,6 +420,7 @@ var ( Col3DCylinderStacked: "stacked", Col3DCylinderPercentStacked: "percentStacked", Line: "standard", + Line3D: "standard", } plotAreaChartBarDir = map[string]string{ Bar: "bar", @@ -450,6 +458,7 @@ var ( Col3DCylinderStacked: "col", Col3DCylinderPercentStacked: "col", Line: "standard", + Line3D: "standard", } orientation = map[bool]string{ true: "maxMin", @@ -624,6 +633,7 @@ func parseChartOptions(opts string) (*chartOptions, error) { // col3DCylinderPercentStacked | 3D cylinder percent stacked column chart // doughnut | doughnut chart // line | line chart +// line3D | 3D line chart // pie | pie chart // pie3D | 3D pie chart // pieOfPie | pie of pie chart diff --git a/chart_test.go b/chart_test.go index bd633761..3c80b390 100644 --- a/chart_test.go +++ b/chart_test.go @@ -122,6 +122,7 @@ func TestAddChart(t *testing.T) { assert.NoError(t, f.AddChart("Sheet1", "X16", `{"type":"col3DClustered","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":"bottom","show_legend_key":false},"title":{"name":"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"}`)) assert.NoError(t, f.AddChart("Sheet1", "P30", `{"type":"col3DStacked","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":"3D Stacked 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"}`)) assert.NoError(t, f.AddChart("Sheet1", "X30", `{"type":"col3DPercentStacked","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":"3D 100% Stacked 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"}`)) + assert.NoError(t, f.AddChart("Sheet1", "X45", `{"type":"radar","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":"top_right","show_legend_key":false},"title":{"name":"Radar 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":"span"}`)) assert.NoError(t, f.AddChart("Sheet1", "AF1", `{"type":"col3DConeStacked","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":"3D Column Cone Stacked 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("Sheet1", "AF16", `{"type":"col3DConeClustered","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":"3D Column Cone Clustered 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("Sheet1", "AF30", `{"type":"col3DConePercentStacked","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":"3D Column Cone Percent Stacked 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"}`)) @@ -135,7 +136,7 @@ func TestAddChart(t *testing.T) { assert.NoError(t, f.AddChart("Sheet1", "AV30", `{"type":"col3DCylinderPercentStacked","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":"3D Column Cylinder Percent Stacked 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("Sheet1", "AV45", `{"type":"col3DCylinder","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":"3D Column Cylinder 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("Sheet1", "P45", `{"type":"col3D","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":"3D 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"}`)) - assert.NoError(t, f.AddChart("Sheet2", "P1", `{"type":"radar","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":"top_right","show_legend_key":false},"title":{"name":"Radar 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":"span"}`)) + assert.NoError(t, f.AddChart("Sheet2", "P1", `{"type":"line3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30","marker":{"symbol":"none","size":10}, "line":{"color":"#000000"}},{"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","line":{"width":0.25}}],"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":"top","show_legend_key":false},"title":{"name":"3D 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},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true,"minor_grid_lines":true,"tick_label_skip":1},"y_axis":{"major_grid_lines":true,"minor_grid_lines":true,"major_unit":1}}`)) assert.NoError(t, f.AddChart("Sheet2", "X1", `{"type":"scatter","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":"bottom","show_legend_key":false},"title":{"name":"Scatter 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", "P16", `{"type":"doughnut","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":"right","show_legend_key":false},"title":{"name":"Doughnut Chart"},"plotarea":{"show_bubble_size":false,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":false,"show_val":false},"show_blanks_as":"zero","hole_size":30}`)) assert.NoError(t, f.AddChart("Sheet2", "X16", `{"type":"line","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30","marker":{"symbol":"none","size":10}, "line":{"color":"#000000"}},{"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","line":{"width":0.25}}],"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":"top","show_legend_key":false},"title":{"name":"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},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true,"minor_grid_lines":true,"tick_label_skip":1},"y_axis":{"major_grid_lines":true,"minor_grid_lines":true,"major_unit":1}}`)) diff --git a/drawing.go b/drawing.go index 3ef58212..2da2573b 100644 --- a/drawing.go +++ b/drawing.go @@ -224,8 +224,9 @@ func (f *File) addChart(opts *chartOptions, comboCharts []*chartOptions) { Col3DCylinderPercentStacked: f.drawBaseChart, Doughnut: f.drawDoughnutChart, Line: f.drawLineChart, - Pie3D: f.drawPie3DChart, + Line3D: f.drawLine3DChart, Pie: f.drawPieChart, + Pie3D: f.drawPie3DChart, PieOfPieChart: f.drawPieOfPieChart, BarOfPieChart: f.drawBarOfPieChart, Radar: f.drawRadarChart, @@ -553,6 +554,29 @@ func (f *File) drawLineChart(opts *chartOptions) *cPlotArea { } } +// drawLine3DChart provides a function to draw the c:plotArea element for line +// chart by given format sets. +func (f *File) drawLine3DChart(opts *chartOptions) *cPlotArea { + return &cPlotArea{ + Line3DChart: &cCharts{ + Grouping: &attrValString{ + Val: stringPtr(plotAreaChartGrouping[opts.Type]), + }, + VaryColors: &attrValBool{ + Val: boolPtr(false), + }, + Ser: f.drawChartSeries(opts), + DLbls: f.drawChartDLbls(opts), + AxID: []*attrValInt{ + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, + }, + }, + CatAx: f.drawPlotAreaCatAx(opts), + ValAx: f.drawPlotAreaValAx(opts), + } +} + // drawPieChart provides a function to draw the c:plotArea element for pie // chart by given format sets. func (f *File) drawPieChart(opts *chartOptions) *cPlotArea { diff --git a/lib.go b/lib.go index 945c6f0f..7f388e01 100644 --- a/lib.go +++ b/lib.go @@ -688,6 +688,9 @@ func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) { // isNumeric determines whether an expression is a valid numeric type and get // the precision for the numeric. func isNumeric(s string) (bool, int, float64) { + if strings.Contains(s, "_") { + return false, 0, 0 + } var decimal big.Float _, ok := decimal.SetString(s) if !ok { diff --git a/styles.go b/styles.go index 6d90a9ec..5299fbd5 100644 --- a/styles.go +++ b/styles.go @@ -873,6 +873,9 @@ var operatorType = map[string]string{ // format as string type by given built-in number formats code and cell // string. func formatToInt(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v @@ -884,6 +887,9 @@ func formatToInt(v, format string, date1904 bool) string { // format as string type by given built-in number formats code and cell // string. func formatToFloat(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v @@ -894,6 +900,9 @@ func formatToFloat(v, format string, date1904 bool) string { // formatToA provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToA(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v @@ -907,6 +916,9 @@ func formatToA(v, format string, date1904 bool) string { // formatToB provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToB(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v @@ -920,6 +932,9 @@ func formatToB(v, format string, date1904 bool) string { // formatToC provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToC(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v @@ -930,6 +945,9 @@ func formatToC(v, format string, date1904 bool) string { // formatToD provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToD(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v @@ -940,6 +958,9 @@ func formatToD(v, format string, date1904 bool) string { // formatToE provides a function to convert original string to special format // as string type by given built-in number formats code and cell string. func formatToE(v, format string, date1904 bool) string { + if strings.Contains(v, "_") { + return v + } f, err := strconv.ParseFloat(v, 64) if err != nil { return v diff --git a/xmlChart.go b/xmlChart.go index 53755f37..9024770b 100644 --- a/xmlChart.go +++ b/xmlChart.go @@ -309,6 +309,7 @@ type cPlotArea struct { BubbleChart *cCharts `xml:"bubbleChart"` DoughnutChart *cCharts `xml:"doughnutChart"` LineChart *cCharts `xml:"lineChart"` + Line3DChart *cCharts `xml:"line3DChart"` PieChart *cCharts `xml:"pieChart"` Pie3DChart *cCharts `xml:"pie3DChart"` OfPieChart *cCharts `xml:"ofPieChart"`