Browse Source

Fix #554, init combo chart support, new chart pie of pie, bar of pie chart support

xuri 5 years ago
parent
commit
9ddb52eac4
3 changed files with 136 additions and 12 deletions
  1. 102 7
      chart.go
  2. 26 1
      chart_test.go
  3. 8 4
      xmlChart.go

+ 102 - 7
chart.go

@@ -16,6 +16,7 @@ import (
 	"errors"
 	"errors"
 	"io"
 	"io"
 	"log"
 	"log"
+	"reflect"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
@@ -66,6 +67,8 @@ const (
 	Line                        = "line"
 	Line                        = "line"
 	Pie                         = "pie"
 	Pie                         = "pie"
 	Pie3D                       = "pie3D"
 	Pie3D                       = "pie3D"
+	PieOfPieChart               = "pieOfPie"
+	BarOfPieChart               = "barOfPie"
 	Radar                       = "radar"
 	Radar                       = "radar"
 	Scatter                     = "scatter"
 	Scatter                     = "scatter"
 	Surface3D                   = "surface3D"
 	Surface3D                   = "surface3D"
@@ -123,6 +126,8 @@ var (
 		Line:                        0,
 		Line:                        0,
 		Pie:                         0,
 		Pie:                         0,
 		Pie3D:                       30,
 		Pie3D:                       30,
+		PieOfPieChart:               0,
+		BarOfPieChart:               0,
 		Radar:                       0,
 		Radar:                       0,
 		Scatter:                     0,
 		Scatter:                     0,
 		Surface3D:                   15,
 		Surface3D:                   15,
@@ -175,6 +180,8 @@ var (
 		Line:                        0,
 		Line:                        0,
 		Pie:                         0,
 		Pie:                         0,
 		Pie3D:                       0,
 		Pie3D:                       0,
+		PieOfPieChart:               0,
+		BarOfPieChart:               0,
 		Radar:                       0,
 		Radar:                       0,
 		Scatter:                     0,
 		Scatter:                     0,
 		Surface3D:                   20,
 		Surface3D:                   20,
@@ -237,6 +244,8 @@ var (
 		Line:                        0,
 		Line:                        0,
 		Pie:                         0,
 		Pie:                         0,
 		Pie3D:                       0,
 		Pie3D:                       0,
+		PieOfPieChart:               0,
+		BarOfPieChart:               0,
 		Radar:                       0,
 		Radar:                       0,
 		Scatter:                     0,
 		Scatter:                     0,
 		Surface3D:                   0,
 		Surface3D:                   0,
@@ -297,6 +306,8 @@ var (
 		Line:                        "General",
 		Line:                        "General",
 		Pie:                         "General",
 		Pie:                         "General",
 		Pie3D:                       "General",
 		Pie3D:                       "General",
+		PieOfPieChart:               "General",
+		BarOfPieChart:               "General",
 		Radar:                       "General",
 		Radar:                       "General",
 		Scatter:                     "General",
 		Scatter:                     "General",
 		Surface3D:                   "General",
 		Surface3D:                   "General",
@@ -351,6 +362,8 @@ var (
 		Line:                        "between",
 		Line:                        "between",
 		Pie:                         "between",
 		Pie:                         "between",
 		Pie3D:                       "between",
 		Pie3D:                       "between",
+		PieOfPieChart:               "between",
+		BarOfPieChart:               "between",
 		Radar:                       "between",
 		Radar:                       "between",
 		Scatter:                     "between",
 		Scatter:                     "between",
 		Surface3D:                   "midCat",
 		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
 // 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
 // set (such as offset, scale, aspect ratio setting and print settings) and
 // properties set. For example, create 3D clustered column chart with data
 // properties set. For example, create 3D clustered column chart with data
-// Sheet1!$A$29:$D$32:
+// Sheet1!$E$1:$L$15:
 //
 //
 //    package main
 //    package main
 //
 //
@@ -507,12 +520,12 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
 //        for k, v := range values {
 //        for k, v := range values {
 //            f.SetCellValue("Sheet1", k, v)
 //            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())
 //            println(err.Error())
 //            return
 //            return
 //        }
 //        }
 //        // Save xlsx file by the given path.
 //        // 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())
 //            println(err.Error())
 //        }
 //        }
 //    }
 //    }
@@ -565,6 +578,8 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
 //     line                        | line chart
 //     line                        | line chart
 //     pie                         | pie chart
 //     pie                         | pie chart
 //     pie3D                       | 3D pie chart
 //     pie3D                       | 3D pie chart
+//     pieOfPie                    | pie of pie chart
+//     barOfPie                    | bar of pie chart
 //     radar                       | radar chart
 //     radar                       | radar chart
 //     scatter                     | scatter chart
 //     scatter                     | scatter chart
 //     surface3D                   | 3D surface 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.
 // 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 {
 func (f *File) AddChart(sheet, cell, format string) error {
 	formatSet, err := parseFormatChartSet(format)
 	formatSet, err := parseFormatChartSet(format)
 	if err != nil {
 	if err != nil {
@@ -915,6 +958,8 @@ func (f *File) addChart(formatSet *formatChart) {
 		Line:                        f.drawLineChart,
 		Line:                        f.drawLineChart,
 		Pie3D:                       f.drawPie3DChart,
 		Pie3D:                       f.drawPie3DChart,
 		Pie:                         f.drawPieChart,
 		Pie:                         f.drawPieChart,
+		PieOfPieChart:               f.drawPieOfPieChart,
+		BarOfPieChart:               f.drawBarOfPieChart,
 		Radar:                       f.drawRadarChart,
 		Radar:                       f.drawRadarChart,
 		Scatter:                     f.drawScatterChart,
 		Scatter:                     f.drawScatterChart,
 		Surface3D:                   f.drawSurface3DChart,
 		Surface3D:                   f.drawSurface3DChart,
@@ -924,8 +969,20 @@ func (f *File) addChart(formatSet *formatChart) {
 		Bubble:                      f.drawBaseChart,
 		Bubble:                      f.drawBaseChart,
 		Bubble3D:                    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)
 	chart, _ := xml.Marshal(xlsxChartSpace)
 	media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
 	media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
 	f.saveFileList(media, chart)
 	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
 // drawRadarChart provides a function to draw the c:plotArea element for radar
 // chart by given format sets.
 // chart by given format sets.
 func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
 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
 // drawChartSeries provides a function to draw the c:ser element by given
 // format sets.
 // format sets.
 func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
 func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
+	var baseIdx int
+	if formatSet.Combo != nil {
+		baseIdx = len(formatSet.Combo.Series)
+	}
 	ser := []cSer{}
 	ser := []cSer{}
 	for k := range formatSet.Series {
 	for k := range formatSet.Series {
 		ser = append(ser, cSer{
 		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{
 			Tx: &cTx{
 				StrRef: &cStrRef{
 				StrRef: &cStrRef{
 					F: formatSet.Series[k].Name,
 					F: formatSet.Series[k].Name,

+ 26 - 1
chart_test.go

@@ -3,6 +3,7 @@ package excelize
 import (
 import (
 	"bytes"
 	"bytes"
 	"encoding/xml"
 	"encoding/xml"
+	"fmt"
 	"path/filepath"
 	"path/filepath"
 	"testing"
 	"testing"
 
 
@@ -172,7 +173,31 @@ func TestAddChart(t *testing.T) {
 	// bubble chart
 	// 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", "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}}`))
 	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")))
 	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")
 	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")
 }
 }

+ 8 - 4
xmlChart.go

@@ -312,6 +312,7 @@ type cPlotArea struct {
 	LineChart      *cCharts `xml:"lineChart"`
 	LineChart      *cCharts `xml:"lineChart"`
 	PieChart       *cCharts `xml:"pieChart"`
 	PieChart       *cCharts `xml:"pieChart"`
 	Pie3DChart     *cCharts `xml:"pie3DChart"`
 	Pie3DChart     *cCharts `xml:"pie3DChart"`
+	OfPieChart     *cCharts `xml:"ofPieChart"`
 	RadarChart     *cCharts `xml:"radarChart"`
 	RadarChart     *cCharts `xml:"radarChart"`
 	ScatterChart   *cCharts `xml:"scatterChart"`
 	ScatterChart   *cCharts `xml:"scatterChart"`
 	Surface3DChart *cCharts `xml:"surface3DChart"`
 	Surface3DChart *cCharts `xml:"surface3DChart"`
@@ -329,6 +330,8 @@ type cCharts struct {
 	Grouping     *attrValString `xml:"grouping"`
 	Grouping     *attrValString `xml:"grouping"`
 	RadarStyle   *attrValString `xml:"radarStyle"`
 	RadarStyle   *attrValString `xml:"radarStyle"`
 	ScatterStyle *attrValString `xml:"scatterStyle"`
 	ScatterStyle *attrValString `xml:"scatterStyle"`
+	OfPieType    *attrValString `xml:"ofPieType"`
+	SerLines     *attrValString `xml:"serLines"`
 	VaryColors   *attrValBool   `xml:"varyColors"`
 	VaryColors   *attrValBool   `xml:"varyColors"`
 	Wireframe    *attrValBool   `xml:"wireframe"`
 	Wireframe    *attrValBool   `xml:"wireframe"`
 	Ser          *[]cSer        `xml:"ser"`
 	Ser          *[]cSer        `xml:"ser"`
@@ -590,10 +593,11 @@ type formatChart struct {
 		} `json:"fill"`
 		} `json:"fill"`
 		Layout formatLayout `json:"layout"`
 		Layout formatLayout `json:"layout"`
 	} `json:"plotarea"`
 	} `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.
 // formatChartLegend directly maps the format settings of the chart legend.