This closes #1588, closes #1591, breaking changes for the `AddChart` function

- Removed exported `ChartTitle` data type
- The `AddChart` function now supports formatting and setting rich text titles for the chart
- New exported function `GetFormControl` for getting form control
- Made case in-sensitive for internal worksheet XML path to improve compatibility
- Update the unit tests
- Update the documentation and internal comments on the codes
This commit is contained in:
xuri 2023-07-28 00:24:08 +08:00
parent 2e9c2904f2
commit a07c8cd0b4
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
12 changed files with 352 additions and 212 deletions

View File

@ -165,8 +165,10 @@ func main() {
Categories: "Sheet1!$B$1:$D$1", Categories: "Sheet1!$B$1:$D$1",
Values: "Sheet1!$B$4:$D$4", Values: "Sheet1!$B$4:$D$4",
}}, }},
Title: excelize.ChartTitle{ Title: []excelize.RichTextRun{
Name: "Fruit 3D Clustered Column Chart", {
Text: "Fruit 3D Clustered Column Chart",
},
}, },
}); err != nil { }); err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -165,8 +165,10 @@ func main() {
Categories: "Sheet1!$B$1:$D$1", Categories: "Sheet1!$B$1:$D$1",
Values: "Sheet1!$B$4:$D$4", Values: "Sheet1!$B$4:$D$4",
}}, }},
Title: excelize.ChartTitle{ Title: []excelize.RichTextRun{
Name: "Fruit 3D Clustered Column Chart", {
Text: "Fruit 3D Clustered Column Chart",
},
}, },
}); err != nil { }); err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -507,8 +507,16 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
if opts.Legend.Position == "" { if opts.Legend.Position == "" {
opts.Legend.Position = defaultChartLegendPosition opts.Legend.Position = defaultChartLegendPosition
} }
if opts.Title.Name == "" { for i := range opts.Title {
opts.Title.Name = " " if opts.Title[i].Font == nil {
opts.Title[i].Font = &Font{}
}
if opts.Title[i].Font.Color == "" {
opts.Title[i].Font.Color = "595959"
}
if opts.Title[i].Font.Size == 0 {
opts.Title[i].Font.Size = 14
}
} }
if opts.VaryColors == nil { if opts.VaryColors == nil {
opts.VaryColors = boolPtr(true) opts.VaryColors = boolPtr(true)
@ -569,8 +577,10 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
// Values: "Sheet1!$B$4:$D$4", // Values: "Sheet1!$B$4:$D$4",
// }, // },
// }, // },
// Title: excelize.ChartTitle{ // Title: []excelize.RichTextRun{
// Name: "Fruit 3D Clustered Column Chart", // {
// Text: "Fruit 3D Clustered Column Chart",
// },
// }, // },
// Legend: excelize.ChartLegend{ // Legend: excelize.ChartLegend{
// ShowLegendKey: false, // ShowLegendKey: false,
@ -727,7 +737,7 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
// //
// Title // Title
// //
// Name: Set the name (title) for the chart. The name is displayed above the // Title: Set the name (title) for the chart. The name is displayed above the
// chart. The name can also be a formula such as Sheet1!$A$1 or a list with a // chart. The name can also be a formula such as Sheet1!$A$1 or a list with a
// sheet name. The name property is optional. The default is to have no chart // sheet name. The name property is optional. The default is to have no chart
// title. // title.
@ -912,8 +922,10 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
// LockAspectRatio: false, // LockAspectRatio: false,
// Locked: &disable, // Locked: &disable,
// }, // },
// Title: excelize.ChartTitle{ // Title: []excelize.RichTextRun{
// Name: "Clustered Column - Line Chart", // {
// Text: "Clustered Column - Line Chart",
// },
// }, // },
// Legend: excelize.ChartLegend{ // Legend: excelize.ChartLegend{
// Position: "left", // Position: "left",

View File

@ -52,7 +52,7 @@ func TestChartSize(t *testing.T) {
{Name: "Sheet1!$A$3", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$3:$D$3"}, {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"}, {Name: "Sheet1!$A$4", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$4:$D$4"},
}, },
Title: ChartTitle{Name: "3D Clustered Column Chart"}, Title: []RichTextRun{{Text: "3D Clustered Column Chart"}},
})) }))
var buffer bytes.Buffer var buffer bytes.Buffer
@ -206,69 +206,69 @@ func TestAddChart(t *testing.T) {
sheetName, cell string sheetName, cell string
opts *Chart opts *Chart
}{ }{
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "000000"}, Title: []RichTextRun{{Text: "Primary Horizontal Axis Title"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}, Title: []RichTextRun{{Text: "Primary Vertical Axis Title", Font: &Font{Color: "777777", Bold: true, Italic: true, Size: 12}}}}}}, {sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "000000"}, Title: []RichTextRun{{Text: "Primary Horizontal Axis Title"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}, Title: []RichTextRun{{Text: "Primary Vertical Axis Title", Font: &Font{Color: "777777", Bold: true, Italic: true, Size: 12}}}}}},
{sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "100% Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "100% Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "3D Clustered Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Clustered Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "P30", opts: &Chart{Type: Col3DStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P30", opts: &Chart{Type: Col3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "X30", opts: &Chart{Type: Col3DPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D 100% Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X30", opts: &Chart{Type: Col3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D 100% Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "X45", opts: &Chart{Type: Radar, Series: series, Format: format, Legend: ChartLegend{Position: "top_right", ShowLegendKey: false}, Title: ChartTitle{Name: "Radar Chart"}, PlotArea: plotArea, ShowBlanksAs: "span"}}, {sheetName: "Sheet1", cell: "X45", opts: &Chart{Type: Radar, Series: series, Format: format, Legend: ChartLegend{Position: "top_right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Radar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "span"}},
{sheetName: "Sheet1", cell: "AF1", opts: &Chart{Type: Col3DConeStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AF1", opts: &Chart{Type: Col3DConeStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cone Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AF16", opts: &Chart{Type: Col3DConeClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AF16", opts: &Chart{Type: Col3DConeClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cone Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AF30", opts: &Chart{Type: Col3DConePercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AF30", opts: &Chart{Type: Col3DConePercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cone Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AF45", opts: &Chart{Type: Col3DCone, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AF45", opts: &Chart{Type: Col3DCone, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cone Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AN1", opts: &Chart{Type: Col3DPyramidStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AN1", opts: &Chart{Type: Col3DPyramidStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Pyramid Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AN16", opts: &Chart{Type: Col3DPyramidClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AN16", opts: &Chart{Type: Col3DPyramidClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Pyramid Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AN30", opts: &Chart{Type: Col3DPyramidPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AN30", opts: &Chart{Type: Col3DPyramidPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Pyramid Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AN45", opts: &Chart{Type: Col3DPyramid, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AN45", opts: &Chart{Type: Col3DPyramid, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Pyramid Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AV1", opts: &Chart{Type: Col3DCylinderStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV1", opts: &Chart{Type: Col3DCylinderStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AV16", opts: &Chart{Type: Col3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV16", opts: &Chart{Type: Col3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AV30", opts: &Chart{Type: Col3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV30", opts: &Chart{Type: Col3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AV45", opts: &Chart{Type: Col3DCylinder, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV45", opts: &Chart{Type: Col3DCylinder, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "P45", opts: &Chart{Type: Col3D, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P45", opts: &Chart{Type: Col3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "P1", opts: &Chart{Type: Line3D, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: ChartTitle{Name: "3D Line Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}}}, {sheetName: "Sheet2", cell: "P1", opts: &Chart{Type: Line3D, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}}},
{sheetName: "Sheet2", cell: "X1", opts: &Chart{Type: Scatter, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "Scatter Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "X1", opts: &Chart{Type: Scatter, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Scatter Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "P16", opts: &Chart{Type: Doughnut, Series: series3, Format: format, Legend: ChartLegend{Position: "right", ShowLegendKey: false}, Title: ChartTitle{Name: "Doughnut Chart"}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "zero", HoleSize: 30}}, {sheetName: "Sheet2", cell: "P16", opts: &Chart{Type: Doughnut, Series: series3, Format: format, Legend: ChartLegend{Position: "right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Doughnut Chart"}}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "zero", HoleSize: 30}},
{sheetName: "Sheet2", cell: "X16", opts: &Chart{Type: Line, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: ChartTitle{Name: "Line Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}}, {sheetName: "Sheet2", cell: "X16", opts: &Chart{Type: Line, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}},
{sheetName: "Sheet2", cell: "P32", opts: &Chart{Type: Pie3D, Series: series3, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "3D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "P32", opts: &Chart{Type: Pie3D, Series: series3, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "X32", opts: &Chart{Type: Pie, Series: series3, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "Pie Chart"}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false, NumFmt: ChartNumFmt{CustomNumFmt: "0.00%;0;;"}}, ShowBlanksAs: "gap"}}, {sheetName: "Sheet2", cell: "X32", opts: &Chart{Type: Pie, Series: series3, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Pie Chart"}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false, NumFmt: ChartNumFmt{CustomNumFmt: "0.00%;0;;"}}, ShowBlanksAs: "gap"}},
// bar series chart // bar series chart
{sheetName: "Sheet2", cell: "P48", opts: &Chart{Type: Bar, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Clustered Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "P48", opts: &Chart{Type: Bar, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Clustered Bar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "X48", opts: &Chart{Type: BarStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "X48", opts: &Chart{Type: BarStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Bar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "P64", opts: &Chart{Type: BarPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked 100% Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "P64", opts: &Chart{Type: BarPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked 100% Bar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "X64", opts: &Chart{Type: Bar3DClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Clustered Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "X64", opts: &Chart{Type: Bar3DClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Clustered Bar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "P80", opts: &Chart{Type: Bar3DStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Stacked Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{Maximum: &maximum, Minimum: &minimum}}}, {sheetName: "Sheet2", cell: "P80", opts: &Chart{Type: Bar3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Stacked Bar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{Maximum: &maximum, Minimum: &minimum}}},
{sheetName: "Sheet2", cell: "X80", opts: &Chart{Type: Bar3DPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D 100% Stacked Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{ReverseOrder: true, Secondary: true, Minimum: &zero}, YAxis: ChartAxis{ReverseOrder: true, Minimum: &zero}}}, {sheetName: "Sheet2", cell: "X80", opts: &Chart{Type: Bar3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D 100% Stacked Bar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{ReverseOrder: true, Secondary: true, Minimum: &zero}, YAxis: ChartAxis{ReverseOrder: true, Minimum: &zero}}},
// area series chart // area series chart
{sheetName: "Sheet2", cell: "AF1", opts: &Chart{Type: Area, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AF1", opts: &Chart{Type: Area, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Area Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AN1", opts: &Chart{Type: AreaStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AN1", opts: &Chart{Type: AreaStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Area Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AF16", opts: &Chart{Type: AreaPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D 100% Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AF16", opts: &Chart{Type: AreaPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D 100% Stacked Area Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AN16", opts: &Chart{Type: Area3D, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AN16", opts: &Chart{Type: Area3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Area Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AF32", opts: &Chart{Type: Area3DStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AF32", opts: &Chart{Type: Area3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Stacked Area Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AN32", opts: &Chart{Type: Area3DPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D 100% Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AN32", opts: &Chart{Type: Area3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D 100% Stacked Area Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
// cylinder series chart // cylinder series chart
{sheetName: "Sheet2", cell: "AF48", opts: &Chart{Type: Bar3DCylinderStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cylinder Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AF48", opts: &Chart{Type: Bar3DCylinderStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Cylinder Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AF64", opts: &Chart{Type: Bar3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cylinder Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AF64", opts: &Chart{Type: Bar3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Cylinder Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AF80", opts: &Chart{Type: Bar3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cylinder Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AF80", opts: &Chart{Type: Bar3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Cylinder Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
// cone series chart // cone series chart
{sheetName: "Sheet2", cell: "AN48", opts: &Chart{Type: Bar3DConeStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cone Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AN48", opts: &Chart{Type: Bar3DConeStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Cone Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AN64", opts: &Chart{Type: Bar3DConeClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cone Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AN64", opts: &Chart{Type: Bar3DConeClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Cone Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AN80", opts: &Chart{Type: Bar3DConePercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cone Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AN80", opts: &Chart{Type: Bar3DConePercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Cone Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AV48", opts: &Chart{Type: Bar3DPyramidStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Pyramid Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AV48", opts: &Chart{Type: Bar3DPyramidStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Pyramid Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AV64", opts: &Chart{Type: Bar3DPyramidClustered, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Pyramid Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AV64", opts: &Chart{Type: Bar3DPyramidClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Pyramid Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "AV80", opts: &Chart{Type: Bar3DPyramidPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Pyramid Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AV80", opts: &Chart{Type: Bar3DPyramidPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Bar Pyramid Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
// surface series chart // surface series chart
{sheetName: "Sheet2", cell: "AV1", opts: &Chart{Type: Surface3D, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Surface Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{MajorGridLines: true}}}, {sheetName: "Sheet2", cell: "AV1", opts: &Chart{Type: Surface3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Surface Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{MajorGridLines: true}}},
{sheetName: "Sheet2", cell: "AV16", opts: &Chart{Type: WireframeSurface3D, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Wireframe Surface Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{MajorGridLines: true}}}, {sheetName: "Sheet2", cell: "AV16", opts: &Chart{Type: WireframeSurface3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Wireframe Surface Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{MajorGridLines: true}}},
{sheetName: "Sheet2", cell: "AV32", opts: &Chart{Type: Contour, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "AV32", opts: &Chart{Type: Contour, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Contour Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "BD1", opts: &Chart{Type: WireframeContour, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Wireframe Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "BD1", opts: &Chart{Type: WireframeContour, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Wireframe Contour Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
// bubble chart // bubble chart
{sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: Bubble, Series: series4, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: Bubble, Series: series4, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: Bubble3D, Series: series4, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}}, {sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: Bubble3D, Series: series4, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble 3D Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
// pie of pie chart // pie of pie chart
{sheetName: "Sheet2", cell: "BD48", opts: &Chart{Type: PieOfPie, Series: series3, Format: format, Legend: legend, Title: ChartTitle{Name: "Pie of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}}, {sheetName: "Sheet2", cell: "BD48", opts: &Chart{Type: PieOfPie, Series: series3, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Pie of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
// bar of pie chart // bar of pie chart
{sheetName: "Sheet2", cell: "BD64", opts: &Chart{Type: BarOfPie, Series: series3, Format: format, Legend: legend, Title: ChartTitle{Name: "Bar of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}}, {sheetName: "Sheet2", cell: "BD64", opts: &Chart{Type: BarOfPie, Series: series3, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bar of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
} { } {
assert.NoError(t, f.AddChart(c.sheetName, c.cell, c.opts)) assert.NoError(t, f.AddChart(c.sheetName, c.cell, c.opts))
} }
@ -280,32 +280,32 @@ func TestAddChart(t *testing.T) {
{"I1", Doughnut, "Clustered Column - Doughnut Chart"}, {"I1", Doughnut, "Clustered Column - Doughnut Chart"},
} }
for _, props := range clusteredColumnCombo { for _, props := range clusteredColumnCombo {
assert.NoError(t, f.AddChart("Combo Charts", props[0].(string), &Chart{Type: Col, Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[2].(string)}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[1].(ChartType), Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}, YAxis: ChartAxis{Secondary: true}})) assert.NoError(t, f.AddChart("Combo Charts", props[0].(string), &Chart{Type: Col, Series: series[:4], Format: format, Legend: legend, Title: []RichTextRun{{Text: props[2].(string)}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[1].(ChartType), Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}, YAxis: ChartAxis{Secondary: true}}))
} }
stackedAreaCombo := map[string][]interface{}{ stackedAreaCombo := map[string][]interface{}{
"A16": {Line, "Stacked Area - Line Chart"}, "A16": {Line, "Stacked Area - Line Chart"},
"I16": {Doughnut, "Stacked Area - Doughnut Chart"}, "I16": {Doughnut, "Stacked Area - Doughnut Chart"},
} }
for axis, props := range stackedAreaCombo { for axis, props := range stackedAreaCombo {
assert.NoError(t, f.AddChart("Combo Charts", axis, &Chart{Type: AreaStacked, Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[1].(string)}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[0].(ChartType), Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}})) assert.NoError(t, f.AddChart("Combo Charts", axis, &Chart{Type: AreaStacked, Series: series[:4], Format: format, Legend: legend, Title: []RichTextRun{{Text: props[1].(string)}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[0].(ChartType), Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}))
} }
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
// Test with invalid sheet name // Test with invalid sheet name
assert.EqualError(t, f.AddChart("Sheet:1", "A1", &Chart{Type: Col, Series: series[:1]}), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.AddChart("Sheet:1", "A1", &Chart{Type: Col, Series: series[:1]}), ErrSheetNameInvalid.Error())
// Test with illegal cell reference // Test with illegal cell reference
assert.EqualError(t, f.AddChart("Sheet2", "A", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) 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())
// Test with unsupported chart type // Test with unsupported chart type
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: 0x37, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}), newUnsupportedChartType(0x37).Error()) 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())
// Test add combo chart with invalid format set // Test add combo chart with invalid format set
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}, nil), ErrParameterInvalid.Error()) 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())
// Test add combo chart with unsupported chart type // Test add combo chart with unsupported chart type
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: ChartTitle{Name: "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: ChartTitle{Name: "Bar of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}), newUnsupportedChartType(0x37).Error()) 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())
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
// Test add chart with unsupported charset content types. // Test add chart with unsupported charset content types.
f.ContentTypes = nil f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
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: ChartTitle{Name: "2D Column Chart"}}), "XML syntax error on line 1: invalid UTF-8") 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")
} }
func TestAddChartSheet(t *testing.T) { func TestAddChartSheet(t *testing.T) {
@ -323,7 +323,7 @@ func TestAddChartSheet(t *testing.T) {
{Name: "Sheet1!$A$3", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$3:$D$3"}, {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"}, {Name: "Sheet1!$A$4", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$4:$D$4"},
} }
assert.NoError(t, f.AddChartSheet("Chart1", &Chart{Type: Col3DClustered, Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}})) assert.NoError(t, f.AddChartSheet("Chart1", &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}))
// Test set the chartsheet as active sheet // Test set the chartsheet as active sheet
var sheetIdx int var sheetIdx int
for idx, sheetName := range f.GetSheetList() { for idx, sheetName := range f.GetSheetList() {
@ -338,11 +338,11 @@ func TestAddChartSheet(t *testing.T) {
assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is not a worksheet") assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is not a worksheet")
// Test add chartsheet on already existing name sheet // Test add chartsheet on already existing name sheet
assert.EqualError(t, f.AddChartSheet("Sheet1", &Chart{Type: Col3DClustered, Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}), ErrExistsSheet.Error()) assert.EqualError(t, f.AddChartSheet("Sheet1", &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}), ErrExistsSheet.Error())
// Test add chartsheet with invalid sheet name // Test add chartsheet with invalid sheet name
assert.EqualError(t, f.AddChartSheet("Sheet:1", nil, &Chart{Type: Col3DClustered, Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.AddChartSheet("Sheet:1", nil, &Chart{Type: Col3DClustered, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}), ErrSheetNameInvalid.Error())
// Test with unsupported chart type // Test with unsupported chart type
assert.EqualError(t, f.AddChartSheet("Chart2", &Chart{Type: 0x37, Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}), newUnsupportedChartType(0x37).Error()) assert.EqualError(t, f.AddChartSheet("Chart2", &Chart{Type: 0x37, Series: series, Title: []RichTextRun{{Text: "Fruit 3D Clustered Column Chart"}}}), newUnsupportedChartType(0x37).Error())
assert.NoError(t, f.UpdateLinkedValue()) assert.NoError(t, f.UpdateLinkedValue())
@ -351,7 +351,7 @@ func TestAddChartSheet(t *testing.T) {
f = NewFile() f = NewFile()
f.ContentTypes = nil f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
assert.EqualError(t, f.AddChartSheet("Chart4", &Chart{Type: Col, Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"}}, Title: ChartTitle{Name: "2D Column Chart"}}), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.AddChartSheet("Chart4", &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")
} }
func TestDeleteChart(t *testing.T) { func TestDeleteChart(t *testing.T) {
@ -386,7 +386,7 @@ func TestDeleteChart(t *testing.T) {
ShowSerName: true, ShowSerName: true,
ShowVal: true, ShowVal: true,
} }
assert.NoError(t, f.AddChart("Sheet1", "P1", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"})) assert.NoError(t, f.AddChart("Sheet1", "P1", &Chart{Type: Col, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}))
assert.NoError(t, f.DeleteChart("Sheet1", "P1")) assert.NoError(t, f.DeleteChart("Sheet1", "P1"))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteChart.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteChart.xlsx")))
// Test delete chart with invalid sheet name // Test delete chart with invalid sheet name
@ -435,12 +435,12 @@ func TestChartWithLogarithmicBase(t *testing.T) {
cell string cell string
opts *Chart opts *Chart
}{ }{
{cell: "C1", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[0], Height: dimension[1]}, Series: series, Title: ChartTitle{Name: "Line chart without log scaling"}}}, {cell: "C1", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[0], Height: dimension[1]}, Series: series, Title: []RichTextRun{{Text: "Line chart without log scaling"}}}},
{cell: "M1", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[0], Height: dimension[1]}, Series: series, Title: ChartTitle{Name: "Line chart with log 10.5 scaling"}, YAxis: ChartAxis{LogBase: 10.5}}}, {cell: "M1", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[0], Height: dimension[1]}, Series: series, Title: []RichTextRun{{Text: "Line chart with log 10.5 scaling"}}, YAxis: ChartAxis{LogBase: 10.5}}},
{cell: "A25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 1.9 scaling"}, YAxis: ChartAxis{LogBase: 1.9}}}, {cell: "A25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: "Line chart with log 1.9 scaling"}}, YAxis: ChartAxis{LogBase: 1.9}}},
{cell: "F25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 2 scaling"}, YAxis: ChartAxis{LogBase: 2}}}, {cell: "F25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: "Line chart with log 2 scaling"}}, YAxis: ChartAxis{LogBase: 2}}},
{cell: "K25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 1000.1 scaling"}, YAxis: ChartAxis{LogBase: 1000.1}}}, {cell: "K25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: "Line chart with log 1000.1 scaling"}}, YAxis: ChartAxis{LogBase: 1000.1}}},
{cell: "P25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 1000 scaling"}, YAxis: ChartAxis{LogBase: 1000}}}, {cell: "P25", opts: &Chart{Type: Line, Dimension: ChartDimension{Width: dimension[2], Height: dimension[3]}, Series: series, Title: []RichTextRun{{Text: "Line chart with log 1000 scaling"}}, YAxis: ChartAxis{LogBase: 1000}}},
} { } {
// Add two chart, one without and one with log scaling // Add two chart, one without and one with log scaling
assert.NoError(t, f.AddChart(sheet1, c.cell, c.opts)) assert.NoError(t, f.AddChart(sheet1, c.cell, c.opts))

View File

@ -63,67 +63,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
Lang: &attrValString{Val: stringPtr("en-US")}, Lang: &attrValString{Val: stringPtr("en-US")},
RoundedCorners: &attrValBool{Val: boolPtr(false)}, RoundedCorners: &attrValBool{Val: boolPtr(false)},
Chart: cChart{ Chart: cChart{
Title: &cTitle{ Title: f.drawPlotAreaTitles(opts.Title, ""),
Tx: cTx{
Rich: &cRich{
P: []aP{
{
PPr: &aPPr{
DefRPr: aRPr{
Kern: 1200,
Strike: "noStrike",
U: "none",
Sz: 1400,
SolidFill: &aSolidFill{
SchemeClr: &aSchemeClr{
Val: "tx1",
LumMod: &attrValInt{
Val: intPtr(65000),
},
LumOff: &attrValInt{
Val: intPtr(35000),
},
},
},
Ea: &aEa{
Typeface: "+mn-ea",
},
Cs: &aCs{
Typeface: "+mn-cs",
},
Latin: &xlsxCTTextFont{
Typeface: "+mn-lt",
},
},
},
R: &aR{
RPr: aRPr{
Lang: "en-US",
AltLang: "en-US",
},
T: opts.Title.Name,
},
},
},
},
},
TxPr: cTxPr{
P: aP{
PPr: &aPPr{
DefRPr: aRPr{
Kern: 1200,
U: "none",
Sz: 14000,
Strike: "noStrike",
},
},
EndParaRPr: &aEndParaRPr{
Lang: "en-US",
},
},
},
Overlay: &attrValBool{Val: boolPtr(false)},
},
View3D: &cView3D{ View3D: &cView3D{
RotX: &attrValInt{Val: intPtr(chartView3DRotX[opts.Type])}, RotX: &attrValInt{Val: intPtr(chartView3DRotX[opts.Type])},
RotY: &attrValInt{Val: intPtr(chartView3DRotY[opts.Type])}, RotY: &attrValInt{Val: intPtr(chartView3DRotY[opts.Type])},

2
lib.go
View File

@ -53,7 +53,7 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
continue continue
} }
} }
if strings.HasPrefix(fileName, "xl/worksheets/sheet") { if strings.HasPrefix(strings.ToLower(fileName), "xl/worksheets/sheet") {
worksheets++ worksheets++
if fileSize > f.options.UnzipXMLSizeLimit && !v.FileInfo().IsDir() { if fileSize > f.options.UnzipXMLSizeLimit && !v.FileInfo().IsDir() {
if tempFile, err := f.unzipToTemp(v); err == nil { if tempFile, err := f.unzipToTemp(v); err == nil {

View File

@ -216,7 +216,7 @@ func (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error {
if err != nil { if err != nil {
return err return err
} }
// Read sheet data. // Read sheet data
f.mu.Lock() f.mu.Lock()
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {

View File

@ -293,7 +293,7 @@ func (f *File) AddShape(sheet string, opts *Shape) error {
if err != nil { if err != nil {
return err return err
} }
// Read sheet data. // Read sheet data
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err

90
vml.go
View File

@ -390,7 +390,7 @@ func (f *File) DeleteFormControl(sheet, cell string) error {
VPath: &vPath{GradientShapeOK: "t", ConnectType: "rect"}, VPath: &vPath{GradientShapeOK: "t", ConnectType: "rect"},
}, },
} }
// load exist VML shapes from xl/drawings/vmlDrawing%d.vml // Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
d, err := f.decodeVMLDrawingReader(drawingVML) d, err := f.decodeVMLDrawingReader(drawingVML)
if err != nil { if err != nil {
return err return err
@ -477,7 +477,7 @@ func (f *File) vmlDrawingWriter() {
// addVMLObject provides a function to create VML drawing parts and // addVMLObject provides a function to create VML drawing parts and
// relationships for comments and form controls. // relationships for comments and form controls.
func (f *File) addVMLObject(opts vmlOptions) error { func (f *File) addVMLObject(opts vmlOptions) error {
// Read sheet data. // Read sheet data
ws, err := f.workSheetReader(opts.sheet) ws, err := f.workSheetReader(opts.sheet)
if err != nil { if err != nil {
return err return err
@ -836,7 +836,7 @@ func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) er
VPath: &vPath{GradientShapeOK: "t", ConnectType: "rect"}, VPath: &vPath{GradientShapeOK: "t", ConnectType: "rect"},
}, },
} }
// load exist VML shapes from xl/drawings/vmlDrawing%d.vml // Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
d, err := f.decodeVMLDrawingReader(drawingVML) d, err := f.decodeVMLDrawingReader(drawingVML)
if err != nil { if err != nil {
return err return err
@ -883,3 +883,87 @@ func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) er
f.VMLDrawing[drawingVML] = vml f.VMLDrawing[drawingVML] = vml
return err return err
} }
// GetFormControls retrieves all form controls in a worksheet by a given
// worksheet name. Note that, this function does not support getting the width,
// height, text, rich text, and format currently.
func (f *File) GetFormControls(sheet string) ([]FormControl, error) {
var formControls []FormControl
// Read sheet data
ws, err := f.workSheetReader(sheet)
if err != nil {
return formControls, err
}
if ws.LegacyDrawing == nil {
return formControls, err
}
target := f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawing.RID)
drawingVML := strings.ReplaceAll(target, "..", "xl")
vml := f.VMLDrawing[drawingVML]
if vml == nil {
// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
d, err := f.decodeVMLDrawingReader(drawingVML)
if err != nil {
return formControls, err
}
for _, sp := range d.Shape {
if sp.Type != "#_x0000_t201" {
continue
}
formControl, err := extractFormControl(sp.Val)
if err != nil {
return formControls, err
}
if formControl.Type == FormControlNote || formControl.Cell == "" {
continue
}
formControls = append(formControls, formControl)
}
return formControls, err
}
for _, sp := range vml.Shape {
if sp.Type != "#_x0000_t201" {
continue
}
formControl, err := extractFormControl(sp.Val)
if err != nil {
return formControls, err
}
if formControl.Type == FormControlNote || formControl.Cell == "" {
continue
}
formControls = append(formControls, formControl)
}
return formControls, err
}
// extractFormControl provides a function to extract form controls for a
// worksheets by given client data.
func extractFormControl(clientData string) (FormControl, error) {
var (
err error
formControl FormControl
shapeVal decodeShapeVal
)
if err = xml.Unmarshal([]byte(fmt.Sprintf("<shape>%s</shape>", clientData)), &shapeVal); err != nil {
return formControl, err
}
for formCtrlType, preset := range formCtrlPresets {
if shapeVal.ClientData.ObjectType == preset.objectType {
formControl.Type = formCtrlType
if formControl.Cell, err = CoordinatesToCellName(shapeVal.ClientData.Column+1, shapeVal.ClientData.Row+1); err != nil {
return formControl, err
}
formControl.Macro = shapeVal.ClientData.FmlaMacro
formControl.Checked = shapeVal.ClientData.Checked != 0
formControl.CellLink = shapeVal.ClientData.FmlaLink
formControl.CurrentVal = shapeVal.ClientData.Val
formControl.MinVal = shapeVal.ClientData.Min
formControl.MaxVal = shapeVal.ClientData.Max
formControl.IncChange = shapeVal.ClientData.Inc
formControl.PageChange = shapeVal.ClientData.Page
formControl.Horizontally = shapeVal.ClientData.Horiz != nil
}
}
return formControl, err
}

View File

@ -192,8 +192,17 @@ type decodeShapeVal struct {
// element in the file xl/drawings/vmlDrawing%d.vml. // element in the file xl/drawings/vmlDrawing%d.vml.
type decodeVMLClientData struct { type decodeVMLClientData struct {
ObjectType string `xml:"ObjectType,attr"` ObjectType string `xml:"ObjectType,attr"`
FmlaMacro string
Column int Column int
Row int Row int
Checked int
FmlaLink string
Val uint
Min uint
Max uint
Inc uint
Page uint
Horiz *string
} }
// encodeShape defines the structure used to re-serialization shape element. // encodeShape defines the structure used to re-serialization shape element.

View File

@ -13,6 +13,7 @@ package excelize
import ( import (
"encoding/xml" "encoding/xml"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -157,62 +158,83 @@ func TestAddDrawingVML(t *testing.T) {
func TestFormControl(t *testing.T) { func TestFormControl(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ formControls := []FormControl{
Cell: "D1", Type: FormControlButton, Macro: "Button1_Click", {
})) Cell: "D1", Type: FormControlButton, Macro: "Button1_Click",
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ },
Cell: "A1", Type: FormControlButton, Macro: "Button1_Click", {
Width: 140, Height: 60, Text: "Button 1\r\n", Cell: "A1", Type: FormControlButton, Macro: "Button1_Click",
Paragraph: []RichTextRun{ Width: 140, Height: 60, Text: "Button 1\r\n",
{ Paragraph: []RichTextRun{
Font: &Font{ {
Bold: true, Font: &Font{
Italic: true, Bold: true,
Underline: "single", Italic: true,
Family: "Times New Roman", Underline: "single",
Size: 14, Family: "Times New Roman",
Color: "777777", Size: 14,
Color: "777777",
},
Text: "C1=A1+B1",
}, },
Text: "C1=A1+B1", },
Format: GraphicOptions{PrintObject: boolPtr(true), Positioning: "absolute"},
},
{
Cell: "A5", Type: FormControlCheckBox, Text: "Check Box 1",
Checked: true, Format: GraphicOptions{
PrintObject: boolPtr(false), Positioning: "oneCell",
}, },
}, },
Format: GraphicOptions{PrintObject: boolPtr(true), Positioning: "absolute"}, {
})) Cell: "A6", Type: FormControlCheckBox, Text: "Check Box 2",
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ Format: GraphicOptions{Positioning: "twoCell"},
Cell: "A5", Type: FormControlCheckBox, Text: "Check Box 1",
Checked: true, Format: GraphicOptions{
PrintObject: boolPtr(false), Positioning: "oneCell",
}, },
})) {
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ Cell: "A7", Type: FormControlOptionButton, Text: "Option Button 1", Checked: true,
Cell: "A6", Type: FormControlCheckBox, Text: "Check Box 2", },
Format: GraphicOptions{Positioning: "twoCell"}, {
})) Cell: "A8", Type: FormControlOptionButton, Text: "Option Button 2",
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ },
Cell: "A7", Type: FormControlOptionButton, Text: "Option Button 1", Checked: true, {
})) Cell: "D3", Type: FormControlGroupBox, Text: "Group Box 1",
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ Width: 140, Height: 60,
Cell: "A8", Type: FormControlOptionButton, Text: "Option Button 2", },
})) {
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ Cell: "A9", Type: FormControlLabel, Text: "Label 1", Width: 140,
Cell: "D3", Type: FormControlGroupBox, Text: "Group Box 1", },
Width: 140, Height: 60, {
})) Cell: "C5", Type: FormControlSpinButton, Width: 40, Height: 60,
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ CurrentVal: 7, MinVal: 5, MaxVal: 10, IncChange: 1, CellLink: "C2",
Cell: "A9", Type: FormControlLabel, Text: "Label 1", Width: 140, },
})) {
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ Cell: "D7", Type: FormControlScrollBar, Width: 140, Height: 20,
Cell: "C5", Type: FormControlSpinButton, Width: 40, Height: 60, CurrentVal: 50, MinVal: 10, MaxVal: 100, IncChange: 1, PageChange: 1, Horizontally: true, CellLink: "C3",
CurrentVal: 7, MinVal: 5, MaxVal: 10, IncChange: 1, CellLink: "C2", },
})) {
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ Cell: "G1", Type: FormControlScrollBar, Width: 20, Height: 140,
Cell: "D7", Type: FormControlScrollBar, Width: 140, Height: 20, CurrentVal: 50, MinVal: 1000, MaxVal: 100, IncChange: 1, PageChange: 1, CellLink: "C4",
CurrentVal: 50, MinVal: 10, MaxVal: 100, IncChange: 1, PageChange: 1, Horizontally: true, CellLink: "C3", },
})) }
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ for _, formCtrl := range formControls {
Cell: "G1", Type: FormControlScrollBar, Width: 20, Height: 140, assert.NoError(t, f.AddFormControl("Sheet1", formCtrl))
CurrentVal: 50, MinVal: 1000, MaxVal: 100, IncChange: 1, PageChange: 1, CellLink: "C4", }
})) // Test get from controls
result, err := f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, result, 11)
for i, formCtrl := range formControls {
assert.Equal(t, formCtrl.Type, result[i].Type)
assert.Equal(t, formCtrl.Cell, result[i].Cell)
assert.Equal(t, formCtrl.Macro, result[i].Macro)
assert.Equal(t, formCtrl.Checked, result[i].Checked)
assert.Equal(t, formCtrl.CurrentVal, result[i].CurrentVal)
assert.Equal(t, formCtrl.MinVal, result[i].MinVal)
assert.Equal(t, formCtrl.MaxVal, result[i].MaxVal)
assert.Equal(t, formCtrl.IncChange, result[i].IncChange)
assert.Equal(t, formCtrl.Horizontally, result[i].Horizontally)
assert.Equal(t, formCtrl.CellLink, result[i].CellLink)
}
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{CodeName: stringPtr("Sheet1")})) assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{CodeName: stringPtr("Sheet1")}))
file, err := os.ReadFile(filepath.Join("test", "vbaProject.bin")) file, err := os.ReadFile(filepath.Join("test", "vbaProject.bin"))
assert.NoError(t, err) assert.NoError(t, err)
@ -221,9 +243,18 @@ func TestFormControl(t *testing.T) {
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
f, err = OpenFile(filepath.Join("test", "TestAddFormControl.xlsm")) f, err = OpenFile(filepath.Join("test", "TestAddFormControl.xlsm"))
assert.NoError(t, err) assert.NoError(t, err)
// Test get from controls before add form controls
result, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, result, 11)
// Test add from control to a worksheet which already contains form controls
assert.NoError(t, f.AddFormControl("Sheet1", FormControl{ assert.NoError(t, f.AddFormControl("Sheet1", FormControl{
Cell: "D4", Type: FormControlButton, Macro: "Button1_Click", Text: "Button 2", Cell: "D4", Type: FormControlButton, Macro: "Button1_Click", Text: "Button 2",
})) }))
// Test get from controls after add form controls
result, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, result, 12)
// Test add unsupported form control // Test add unsupported form control
assert.Equal(t, f.AddFormControl("Sheet1", FormControl{ assert.Equal(t, f.AddFormControl("Sheet1", FormControl{
Cell: "A1", Type: 0x37, Macro: "Button1_Click", Cell: "A1", Type: 0x37, Macro: "Button1_Click",
@ -251,9 +282,13 @@ func TestFormControl(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.DeleteFormControl("Sheet1", "D1")) assert.NoError(t, f.DeleteFormControl("Sheet1", "D1"))
assert.NoError(t, f.DeleteFormControl("Sheet1", "A1")) assert.NoError(t, f.DeleteFormControl("Sheet1", "A1"))
// Test get from controls after delete form controls
result, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, result, 9)
// Test delete form control on not exists worksheet // Test delete form control on not exists worksheet
assert.Equal(t, f.DeleteFormControl("SheetN", "A1"), newNoExistSheetError("SheetN")) assert.Equal(t, f.DeleteFormControl("SheetN", "A1"), newNoExistSheetError("SheetN"))
// Test delete form control on not exists worksheet // Test delete form control with illegal cell link reference
assert.Equal(t, f.DeleteFormControl("Sheet1", "A"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A"))) assert.Equal(t, f.DeleteFormControl("Sheet1", "A"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteFormControl.xlsm"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteFormControl.xlsm")))
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
@ -266,4 +301,65 @@ func TestFormControl(t *testing.T) {
// Test delete form control on a worksheet without form control // Test delete form control on a worksheet without form control
f = NewFile() f = NewFile()
assert.NoError(t, f.DeleteFormControl("Sheet1", "A1")) assert.NoError(t, f.DeleteFormControl("Sheet1", "A1"))
// Test get form controls on a worksheet without form control
_, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
// Test get form controls on not exists worksheet
_, err = f.GetFormControls("SheetN")
assert.Equal(t, err, newNoExistSheetError("SheetN"))
// Test get form controls with unsupported charset VML drawing
f, err = OpenFile(filepath.Join("test", "TestAddFormControl.xlsm"))
assert.NoError(t, err)
f.Pkg.Store("xl/drawings/vmlDrawing1.vml", MacintoshCyrillicCharset)
_, err = f.GetFormControls("Sheet1")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
// Test get form controls with unsupported shape type
f.DecodeVMLDrawing["xl/drawings/vmlDrawing1.vml"] = &decodeVmlDrawing{
Shape: []decodeShape{{Type: "_x0000_t202"}},
}
formControls, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, formControls, 0)
// Test get form controls with invalid column number
f.DecodeVMLDrawing["xl/drawings/vmlDrawing1.vml"] = &decodeVmlDrawing{
Shape: []decodeShape{{Type: "#_x0000_t201", Val: fmt.Sprintf("<x:ClientData ObjectType=\"Scroll\"><x:Column>%d</x:Column></x:ClientData>", MaxColumns)}},
}
formControls, err = f.GetFormControls("Sheet1")
assert.Equal(t, err, ErrColumnNumber)
assert.Len(t, formControls, 0)
// Test get form controls with comment (Note) shape type
f.DecodeVMLDrawing["xl/drawings/vmlDrawing1.vml"] = &decodeVmlDrawing{
Shape: []decodeShape{{Type: "#_x0000_t201", Val: "<x:ClientData ObjectType=\"Note\"></x:ClientData>"}},
}
formControls, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, formControls, 0)
// Test get form controls with unsupported shape type
f.VMLDrawing["xl/drawings/vmlDrawing1.vml"] = &vmlDrawing{
Shape: []xlsxShape{{Type: "_x0000_t202"}},
}
formControls, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, formControls, 0)
// Test get form controls with invalid column number
f.VMLDrawing["xl/drawings/vmlDrawing1.vml"] = &vmlDrawing{
Shape: []xlsxShape{{Type: "#_x0000_t201", Val: fmt.Sprintf("<x:ClientData ObjectType=\"Scroll\"><x:Column>%d</x:Column></x:ClientData>", MaxColumns)}},
}
formControls, err = f.GetFormControls("Sheet1")
assert.Equal(t, err, ErrColumnNumber)
assert.Len(t, formControls, 0)
// Test get form controls with comment (Note) shape type
f.VMLDrawing["xl/drawings/vmlDrawing1.vml"] = &vmlDrawing{
Shape: []xlsxShape{{Type: "#_x0000_t201", Val: "<x:ClientData ObjectType=\"Note\"></x:ClientData>"}},
}
formControls, err = f.GetFormControls("Sheet1")
assert.NoError(t, err)
assert.Len(t, formControls, 0)
assert.NoError(t, f.Close())
}
func TestExtractFormControl(t *testing.T) {
// Test extract form control with unsupported charset
_, err := extractFormControl(string(MacintoshCyrillicCharset))
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
} }

View File

@ -570,7 +570,7 @@ type Chart struct {
Format GraphicOptions Format GraphicOptions
Dimension ChartDimension Dimension ChartDimension
Legend ChartLegend Legend ChartLegend
Title ChartTitle Title []RichTextRun
VaryColors *bool VaryColors *bool
XAxis ChartAxis XAxis ChartAxis
YAxis ChartAxis YAxis ChartAxis
@ -608,8 +608,3 @@ type ChartSeries struct {
Line ChartLine Line ChartLine
Marker ChartMarker Marker ChartMarker
} }
// ChartTitle directly maps the format settings of the chart title.
type ChartTitle struct {
Name string
}