// Copyright 2016 - 2023 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // // Package excelize providing a set of functions that allow you to write to and // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and // writing spreadsheet documents generated by Microsoft Excelâ„¢ 2007 and later. // Supports complex components by high compatibility, and provided streaming // API for generating or reading data from a worksheet with huge amounts of // data. This library needs Go version 1.16 or later. package excelize import ( "encoding/xml" "fmt" "strconv" "strings" ) // This section defines the currently supported chart types. const ( Area = "area" AreaStacked = "areaStacked" AreaPercentStacked = "areaPercentStacked" Area3D = "area3D" Area3DStacked = "area3DStacked" Area3DPercentStacked = "area3DPercentStacked" Bar = "bar" BarStacked = "barStacked" BarPercentStacked = "barPercentStacked" Bar3DClustered = "bar3DClustered" Bar3DStacked = "bar3DStacked" Bar3DPercentStacked = "bar3DPercentStacked" Bar3DConeClustered = "bar3DConeClustered" Bar3DConeStacked = "bar3DConeStacked" Bar3DConePercentStacked = "bar3DConePercentStacked" Bar3DPyramidClustered = "bar3DPyramidClustered" Bar3DPyramidStacked = "bar3DPyramidStacked" Bar3DPyramidPercentStacked = "bar3DPyramidPercentStacked" Bar3DCylinderClustered = "bar3DCylinderClustered" Bar3DCylinderStacked = "bar3DCylinderStacked" Bar3DCylinderPercentStacked = "bar3DCylinderPercentStacked" Col = "col" ColStacked = "colStacked" ColPercentStacked = "colPercentStacked" Col3D = "col3D" Col3DClustered = "col3DClustered" Col3DStacked = "col3DStacked" Col3DPercentStacked = "col3DPercentStacked" Col3DCone = "col3DCone" Col3DConeClustered = "col3DConeClustered" Col3DConeStacked = "col3DConeStacked" Col3DConePercentStacked = "col3DConePercentStacked" Col3DPyramid = "col3DPyramid" Col3DPyramidClustered = "col3DPyramidClustered" Col3DPyramidStacked = "col3DPyramidStacked" Col3DPyramidPercentStacked = "col3DPyramidPercentStacked" Col3DCylinder = "col3DCylinder" Col3DCylinderClustered = "col3DCylinderClustered" Col3DCylinderStacked = "col3DCylinderStacked" Col3DCylinderPercentStacked = "col3DCylinderPercentStacked" Doughnut = "doughnut" Line = "line" Line3D = "line3D" Pie = "pie" Pie3D = "pie3D" PieOfPieChart = "pieOfPie" BarOfPieChart = "barOfPie" Radar = "radar" Scatter = "scatter" Surface3D = "surface3D" WireframeSurface3D = "wireframeSurface3D" Contour = "contour" WireframeContour = "wireframeContour" Bubble = "bubble" Bubble3D = "bubble3D" ) // This section defines the default value of chart properties. var ( chartView3DRotX = map[string]int{ Area: 0, AreaStacked: 0, AreaPercentStacked: 0, Area3D: 15, Area3DStacked: 15, Area3DPercentStacked: 15, Bar: 0, BarStacked: 0, BarPercentStacked: 0, Bar3DClustered: 15, Bar3DStacked: 15, Bar3DPercentStacked: 15, Bar3DConeClustered: 15, Bar3DConeStacked: 15, Bar3DConePercentStacked: 15, Bar3DPyramidClustered: 15, Bar3DPyramidStacked: 15, Bar3DPyramidPercentStacked: 15, Bar3DCylinderClustered: 15, Bar3DCylinderStacked: 15, Bar3DCylinderPercentStacked: 15, Col: 0, ColStacked: 0, ColPercentStacked: 0, Col3D: 15, Col3DClustered: 15, Col3DStacked: 15, Col3DPercentStacked: 15, Col3DCone: 15, Col3DConeClustered: 15, Col3DConeStacked: 15, Col3DConePercentStacked: 15, Col3DPyramid: 15, Col3DPyramidClustered: 15, Col3DPyramidStacked: 15, Col3DPyramidPercentStacked: 15, Col3DCylinder: 15, Col3DCylinderClustered: 15, Col3DCylinderStacked: 15, Col3DCylinderPercentStacked: 15, Doughnut: 0, Line: 0, Line3D: 20, Pie: 0, Pie3D: 30, PieOfPieChart: 0, BarOfPieChart: 0, Radar: 0, Scatter: 0, Surface3D: 15, WireframeSurface3D: 15, Contour: 90, WireframeContour: 90, } chartView3DRotY = map[string]int{ Area: 0, AreaStacked: 0, AreaPercentStacked: 0, Area3D: 20, Area3DStacked: 20, Area3DPercentStacked: 20, Bar: 0, BarStacked: 0, BarPercentStacked: 0, Bar3DClustered: 20, Bar3DStacked: 20, Bar3DPercentStacked: 20, Bar3DConeClustered: 20, Bar3DConeStacked: 20, Bar3DConePercentStacked: 20, Bar3DPyramidClustered: 20, Bar3DPyramidStacked: 20, Bar3DPyramidPercentStacked: 20, Bar3DCylinderClustered: 20, Bar3DCylinderStacked: 20, Bar3DCylinderPercentStacked: 20, Col: 0, ColStacked: 0, ColPercentStacked: 0, Col3D: 20, Col3DClustered: 20, Col3DStacked: 20, Col3DPercentStacked: 20, Col3DCone: 20, Col3DConeClustered: 20, Col3DConeStacked: 20, Col3DConePercentStacked: 20, Col3DPyramid: 20, Col3DPyramidClustered: 20, Col3DPyramidStacked: 20, Col3DPyramidPercentStacked: 20, Col3DCylinder: 20, Col3DCylinderClustered: 20, Col3DCylinderStacked: 20, Col3DCylinderPercentStacked: 20, Doughnut: 0, Line: 0, Line3D: 15, Pie: 0, Pie3D: 0, PieOfPieChart: 0, BarOfPieChart: 0, Radar: 0, Scatter: 0, Surface3D: 20, WireframeSurface3D: 20, Contour: 0, WireframeContour: 0, } plotAreaChartOverlap = map[string]int{ BarStacked: 100, BarPercentStacked: 100, ColStacked: 100, ColPercentStacked: 100, } chartView3DPerspective = map[string]int{ Line3D: 30, Contour: 0, WireframeContour: 0, } chartView3DRAngAx = map[string]int{ Area: 0, AreaStacked: 0, AreaPercentStacked: 0, Area3D: 1, Area3DStacked: 1, Area3DPercentStacked: 1, Bar: 0, BarStacked: 0, BarPercentStacked: 0, Bar3DClustered: 1, Bar3DStacked: 1, Bar3DPercentStacked: 1, Bar3DConeClustered: 1, Bar3DConeStacked: 1, Bar3DConePercentStacked: 1, Bar3DPyramidClustered: 1, Bar3DPyramidStacked: 1, Bar3DPyramidPercentStacked: 1, Bar3DCylinderClustered: 1, Bar3DCylinderStacked: 1, Bar3DCylinderPercentStacked: 1, Col: 0, ColStacked: 0, ColPercentStacked: 0, Col3D: 1, Col3DClustered: 1, Col3DStacked: 1, Col3DPercentStacked: 1, Col3DCone: 1, Col3DConeClustered: 1, Col3DConeStacked: 1, Col3DConePercentStacked: 1, Col3DPyramid: 1, Col3DPyramidClustered: 1, Col3DPyramidStacked: 1, Col3DPyramidPercentStacked: 1, Col3DCylinder: 1, Col3DCylinderClustered: 1, Col3DCylinderStacked: 1, Col3DCylinderPercentStacked: 1, Doughnut: 0, Line: 0, Line3D: 0, Pie: 0, Pie3D: 0, PieOfPieChart: 0, BarOfPieChart: 0, Radar: 0, Scatter: 0, Surface3D: 0, WireframeSurface3D: 0, Contour: 0, Bubble: 0, Bubble3D: 0, } chartLegendPosition = map[string]string{ "bottom": "b", "left": "l", "right": "r", "top": "t", "top_right": "tr", } chartValAxNumFmtFormatCode = map[string]string{ Area: "General", AreaStacked: "General", AreaPercentStacked: "0%", Area3D: "General", Area3DStacked: "General", Area3DPercentStacked: "0%", Bar: "General", BarStacked: "General", BarPercentStacked: "0%", Bar3DClustered: "General", Bar3DStacked: "General", Bar3DPercentStacked: "0%", Bar3DConeClustered: "General", Bar3DConeStacked: "General", Bar3DConePercentStacked: "0%", Bar3DPyramidClustered: "General", Bar3DPyramidStacked: "General", Bar3DPyramidPercentStacked: "0%", Bar3DCylinderClustered: "General", Bar3DCylinderStacked: "General", Bar3DCylinderPercentStacked: "0%", Col: "General", ColStacked: "General", ColPercentStacked: "0%", Col3D: "General", Col3DClustered: "General", Col3DStacked: "General", Col3DPercentStacked: "0%", Col3DCone: "General", Col3DConeClustered: "General", Col3DConeStacked: "General", Col3DConePercentStacked: "0%", Col3DPyramid: "General", Col3DPyramidClustered: "General", Col3DPyramidStacked: "General", Col3DPyramidPercentStacked: "0%", Col3DCylinder: "General", Col3DCylinderClustered: "General", Col3DCylinderStacked: "General", Col3DCylinderPercentStacked: "0%", Doughnut: "General", Line: "General", Line3D: "General", Pie: "General", Pie3D: "General", PieOfPieChart: "General", BarOfPieChart: "General", Radar: "General", Scatter: "General", Surface3D: "General", WireframeSurface3D: "General", Contour: "General", WireframeContour: "General", Bubble: "General", Bubble3D: "General", } chartValAxCrossBetween = map[string]string{ Area: "midCat", AreaStacked: "midCat", AreaPercentStacked: "midCat", Area3D: "midCat", Area3DStacked: "midCat", Area3DPercentStacked: "midCat", Bar: "between", BarStacked: "between", BarPercentStacked: "between", Bar3DClustered: "between", Bar3DStacked: "between", Bar3DPercentStacked: "between", Bar3DConeClustered: "between", Bar3DConeStacked: "between", Bar3DConePercentStacked: "between", Bar3DPyramidClustered: "between", Bar3DPyramidStacked: "between", Bar3DPyramidPercentStacked: "between", Bar3DCylinderClustered: "between", Bar3DCylinderStacked: "between", Bar3DCylinderPercentStacked: "between", Col: "between", ColStacked: "between", ColPercentStacked: "between", Col3D: "between", Col3DClustered: "between", Col3DStacked: "between", Col3DPercentStacked: "between", Col3DCone: "between", Col3DConeClustered: "between", Col3DConeStacked: "between", Col3DConePercentStacked: "between", Col3DPyramid: "between", Col3DPyramidClustered: "between", Col3DPyramidStacked: "between", Col3DPyramidPercentStacked: "between", Col3DCylinder: "between", Col3DCylinderClustered: "between", Col3DCylinderStacked: "between", Col3DCylinderPercentStacked: "between", Doughnut: "between", Line: "between", Line3D: "between", Pie: "between", Pie3D: "between", PieOfPieChart: "between", BarOfPieChart: "between", Radar: "between", Scatter: "between", Surface3D: "midCat", WireframeSurface3D: "midCat", Contour: "midCat", WireframeContour: "midCat", Bubble: "midCat", Bubble3D: "midCat", } plotAreaChartGrouping = map[string]string{ Area: "standard", AreaStacked: "stacked", AreaPercentStacked: "percentStacked", Area3D: "standard", Area3DStacked: "stacked", Area3DPercentStacked: "percentStacked", Bar: "clustered", BarStacked: "stacked", BarPercentStacked: "percentStacked", Bar3DClustered: "clustered", Bar3DStacked: "stacked", Bar3DPercentStacked: "percentStacked", Bar3DConeClustered: "clustered", Bar3DConeStacked: "stacked", Bar3DConePercentStacked: "percentStacked", Bar3DPyramidClustered: "clustered", Bar3DPyramidStacked: "stacked", Bar3DPyramidPercentStacked: "percentStacked", Bar3DCylinderClustered: "clustered", Bar3DCylinderStacked: "stacked", Bar3DCylinderPercentStacked: "percentStacked", Col: "clustered", ColStacked: "stacked", ColPercentStacked: "percentStacked", Col3D: "standard", Col3DClustered: "clustered", Col3DStacked: "stacked", Col3DPercentStacked: "percentStacked", Col3DCone: "standard", Col3DConeClustered: "clustered", Col3DConeStacked: "stacked", Col3DConePercentStacked: "percentStacked", Col3DPyramid: "standard", Col3DPyramidClustered: "clustered", Col3DPyramidStacked: "stacked", Col3DPyramidPercentStacked: "percentStacked", Col3DCylinder: "standard", Col3DCylinderClustered: "clustered", Col3DCylinderStacked: "stacked", Col3DCylinderPercentStacked: "percentStacked", Line: "standard", Line3D: "standard", } plotAreaChartBarDir = map[string]string{ Bar: "bar", BarStacked: "bar", BarPercentStacked: "bar", Bar3DClustered: "bar", Bar3DStacked: "bar", Bar3DPercentStacked: "bar", Bar3DConeClustered: "bar", Bar3DConeStacked: "bar", Bar3DConePercentStacked: "bar", Bar3DPyramidClustered: "bar", Bar3DPyramidStacked: "bar", Bar3DPyramidPercentStacked: "bar", Bar3DCylinderClustered: "bar", Bar3DCylinderStacked: "bar", Bar3DCylinderPercentStacked: "bar", Col: "col", ColStacked: "col", ColPercentStacked: "col", Col3D: "col", Col3DClustered: "col", Col3DStacked: "col", Col3DPercentStacked: "col", Col3DCone: "col", Col3DConeStacked: "col", Col3DConeClustered: "col", Col3DConePercentStacked: "col", Col3DPyramid: "col", Col3DPyramidClustered: "col", Col3DPyramidStacked: "col", Col3DPyramidPercentStacked: "col", Col3DCylinder: "col", Col3DCylinderClustered: "col", Col3DCylinderStacked: "col", Col3DCylinderPercentStacked: "col", Line: "standard", Line3D: "standard", } orientation = map[bool]string{ true: "maxMin", false: "minMax", } catAxPos = map[bool]string{ true: "t", false: "b", } valAxPos = map[bool]string{ true: "r", false: "l", } valTickLblPos = map[string]string{ Contour: "none", WireframeContour: "none", } ) // parseChartOptions provides a function to parse the format settings of the // chart with default value. func parseChartOptions(opts *Chart) (*Chart, error) { if opts == nil { return nil, ErrParameterInvalid } if opts.Dimension.Width == 0 { opts.Dimension.Width = defaultChartDimensionWidth } if opts.Dimension.Height == 0 { opts.Dimension.Height = defaultChartDimensionHeight } if opts.Format.PrintObject == nil { opts.Format.PrintObject = boolPtr(true) } if opts.Format.Locked == nil { opts.Format.Locked = boolPtr(false) } if opts.Format.ScaleX == 0 { opts.Format.ScaleX = defaultPictureScale } if opts.Format.ScaleY == 0 { opts.Format.ScaleY = defaultPictureScale } if opts.Legend.Position == "" { opts.Legend.Position = defaultChartLegendPosition } if opts.Title.Name == "" { opts.Title.Name = " " } if opts.VaryColors == nil { opts.VaryColors = boolPtr(true) } if opts.ShowBlanksAs == "" { opts.ShowBlanksAs = defaultChartShowBlanksAs } return opts, nil } // AddChart provides the method to add chart in a sheet by given chart format // set (such as offset, scale, aspect ratio setting and print settings) and // properties set. For example, create 3D clustered column chart with data // Sheet1!$E$1:$L$15: // // package main // // import ( // "fmt" // // "github.com/xuri/excelize/v2" // ) // // func main() { // f := excelize.NewFile() // defer func() { // if err := f.Close(); err != nil { // fmt.Println(err) // } // }() // for idx, row := range [][]interface{}{ // {nil, "Apple", "Orange", "Pear"}, {"Small", 2, 3, 3}, // {"Normal", 5, 2, 4}, {"Large", 6, 7, 8}, // } { // cell, err := excelize.CoordinatesToCellName(1, idx+1) // if err != nil { // fmt.Println(err) // return // } // f.SetSheetRow("Sheet1", cell, &row) // } // if err := f.AddChart("Sheet1", "E1", &excelize.Chart{ // Type: "col3DClustered", // Series: []excelize.ChartSeries{ // { // 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: excelize.ChartTitle{ // Name: "Fruit 3D Clustered Column Chart", // }, // Legend: excelize.ChartLegend{ // ShowLegendKey: false, // }, // PlotArea: excelize.ChartPlotArea{ // ShowBubbleSize: true, // ShowCatName: false, // ShowLeaderLines: false, // ShowPercent: true, // ShowSerName: true, // ShowVal: true, // }, // }); err != nil { // fmt.Println(err) // return // } // // Save spreadsheet by the given path. // if err := f.SaveAs("Book1.xlsx"); err != nil { // fmt.Println(err) // } // } // // The following shows the type of chart supported by excelize: // // Type | Chart // -----------------------------+------------------------------ // area | 2D area chart // areaStacked | 2D stacked area chart // areaPercentStacked | 2D 100% stacked area chart // area3D | 3D area chart // area3DStacked | 3D stacked area chart // area3DPercentStacked | 3D 100% stacked area chart // bar | 2D clustered bar chart // barStacked | 2D stacked bar chart // barPercentStacked | 2D 100% stacked bar chart // bar3DClustered | 3D clustered bar chart // bar3DStacked | 3D stacked bar chart // bar3DPercentStacked | 3D 100% stacked bar chart // bar3DConeClustered | 3D cone clustered bar chart // bar3DConeStacked | 3D cone stacked bar chart // bar3DConePercentStacked | 3D cone percent bar chart // bar3DPyramidClustered | 3D pyramid clustered bar chart // bar3DPyramidStacked | 3D pyramid stacked bar chart // bar3DPyramidPercentStacked | 3D pyramid percent stacked bar chart // bar3DCylinderClustered | 3D cylinder clustered bar chart // bar3DCylinderStacked | 3D cylinder stacked bar chart // bar3DCylinderPercentStacked | 3D cylinder percent stacked bar chart // col | 2D clustered column chart // colStacked | 2D stacked column chart // colPercentStacked | 2D 100% stacked column chart // col3DClustered | 3D clustered column chart // col3D | 3D column chart // col3DStacked | 3D stacked column chart // col3DPercentStacked | 3D 100% stacked column chart // col3DCone | 3D cone column chart // col3DConeClustered | 3D cone clustered column chart // col3DConeStacked | 3D cone stacked column chart // col3DConePercentStacked | 3D cone percent stacked column chart // col3DPyramid | 3D pyramid column chart // col3DPyramidClustered | 3D pyramid clustered column chart // col3DPyramidStacked | 3D pyramid stacked column chart // col3DPyramidPercentStacked | 3D pyramid percent stacked column chart // col3DCylinder | 3D cylinder column chart // col3DCylinderClustered | 3D cylinder clustered column chart // col3DCylinderStacked | 3D cylinder stacked column chart // col3DCylinderPercentStacked | 3D cylinder percent stacked column chart // doughnut | doughnut chart // line | line chart // line3D | 3D line chart // pie | pie chart // pie3D | 3D pie chart // pieOfPie | pie of pie chart // barOfPie | bar of pie chart // radar | radar chart // scatter | scatter chart // surface3D | 3D surface chart // wireframeSurface3D | 3D wireframe surface chart // contour | contour chart // wireframeContour | wireframe contour chart // bubble | bubble chart // bubble3D | 3D bubble chart // // In Excel a chart series is a collection of information that defines which // data is plotted such as values, axis labels and formatting. // // The series options that can be set are: // // Name // Categories // Values // Line // Marker // // Name: Set the name for the series. The name is displayed in the chart legend // and in the formula bar. The 'Name' property is optional and if it isn't // supplied it will default to Series 1..n. The name can also be a formula such // as Sheet1!$A$1 // // Categories: This sets the chart category labels. The category is more or less // the same as the X axis. In most chart types the 'Categories' property is // optional and the chart will just assume a sequential series from 1..n. // // Values: This is the most important property of a series and is the only // mandatory option for every chart object. This option links the chart with // the worksheet data that it displays. // // Line: This sets the line format of the line chart. The 'Line' property is // optional and if it isn't supplied it will default style. The options that // can be set are width and color. The range of width is 0.25pt - 999pt. If the // value of width is outside the range, the default width of the line is 2pt. // The value for color should be represented in hex format // (e.g., #000000 - #FFFFFF) // // Marker: This sets the marker of the line chart and scatter chart. The range // of optional field 'Size' is 2-72 (default value is 5). The enumeration value // of optional field 'Symbol' are (default value is 'auto'): // // circle // dash // diamond // dot // none // picture // plus // square // star // triangle // x // auto // // Set properties of the chart legend. The options that can be set are: // // Position // ShowLegendKey // // Position: Set the position of the chart legend. The default legend position // is bottom. The available positions are: // // none // top // bottom // left // right // top_right // // ShowLegendKey: Set the legend keys shall be shown in data labels. The default // value is false. // // Set properties of the chart title. The properties that can be set are: // // Title // // Name: 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 // sheet name. The name property is optional. The default is to have no chart // title. // // Specifies how blank cells are plotted on the chart by 'ShowBlanksAs'. The // default value is gap. The options that can be set are: // // gap // span // zero // // gap: Specifies that blank values shall be left as a gap. // // span: Specifies that blank values shall be spanned with a line. // // zero: Specifies that blank values shall be treated as zero. // // Specifies that each data marker in the series has a different color by // 'VaryColors'. The default value is true. // // Set chart offset, scale, aspect ratio setting and print settings by format, // same as function 'AddPicture'. // // Set the position of the chart plot area by PlotArea. The properties that can // be set are: // // ShowBubbleSize // ShowCatName // ShowLeaderLines // ShowPercent // ShowSerName // ShowVal // // ShowBubbleSize: Specifies the bubble size shall be shown in a data label. The // 'ShowBubbleSize' property is optional. The default value is false. // // ShowCatName: Specifies that the category name shall be shown in the data // label. The 'ShowCatName' property is optional. The default value is true. // // ShowLeaderLines: Specifies leader lines shall be shown for data labels. The // 'ShowLeaderLines' property is optional. The default value is false. // // ShowPercent: Specifies that the percentage shall be shown in a data label. // The 'ShowPercent' property is optional. The default value is false. // // ShowSerName: Specifies that the series name shall be shown in a data label. // The 'ShowSerName' property is optional. The default value is false. // // ShowVal: Specifies that the value shall be shown in a data label. // The 'ShowVal' property is optional. The default value is false. // // Set the primary horizontal and vertical axis options by 'XAxis' and 'YAxis'. // The properties of XAxis that can be set are: // // None // MajorGridLines // MinorGridLines // TickLabelSkip // ReverseOrder // Maximum // Minimum // Font // // The properties of 'YAxis' that can be set are: // // None // MajorGridLines // MinorGridLines // MajorUnit // TickLabelSkip // ReverseOrder // Maximum // Minimum // Font // // none: Disable axes. // // MajorGridLines: Specifies major grid lines. // // MinorGridLines: Specifies minor grid lines. // // MajorUnit: Specifies the distance between major ticks. Shall contain a // positive floating-point number. The MajorUnit property is optional. The // default value is auto. // // TickLabelSkip: Specifies how many tick labels to skip between label that is // drawn. The 'TickLabelSkip' property is optional. The default value is auto. // // ReverseOrder: Specifies that the categories or values on reverse order // (orientation of the chart). The ReverseOrder property is optional. The // default value is false. // // Maximum: Specifies that the fixed maximum, 0 is auto. The 'Maximum' property // is optional. The default value is auto. // // Minimum: Specifies that the fixed minimum, 0 is auto. The 'Minimum' property // is optional. The default value is auto. // // Font: Specifies that the font of the horizontal and vertical axis. The // properties of font that can be set are: // // Bold // Italic // Underline // Family // Size // Strike // Color // VertAlign // // Set chart size by 'Dimension' property. The 'Dimension' property is optional. // The default width is 480, and height is 290. // // combo: Specifies the create a chart that combines two or more chart types in // a single chart. For example, create a clustered column - line chart with // data Sheet1!$E$1:$L$15: // // package main // // import ( // "fmt" // // "github.com/xuri/excelize/v2" // ) // // func main() { // f := excelize.NewFile() // defer func() { // if err := f.Close(); err != nil { // fmt.Println(err) // } // }() // for idx, row := range [][]interface{}{ // {nil, "Apple", "Orange", "Pear"}, {"Small", 2, 3, 3}, // {"Normal", 5, 2, 4}, {"Large", 6, 7, 8}, // } { // cell, err := excelize.CoordinatesToCellName(1, idx+1) // if err != nil { // fmt.Println(err) // return // } // f.SetSheetRow("Sheet1", cell, &row) // } // enable, disable := true, false // if err := f.AddChart("Sheet1", "E1", &excelize.Chart{ // Type: "col", // Series: []excelize.ChartSeries{ // { // Name: "Sheet1!$A$2", // Categories: "Sheet1!$B$1:$D$1", // Values: "Sheet1!$B$2:$D$2", // }, // }, // Format: excelize.GraphicOptions{ // ScaleX: 1, // ScaleY: 1, // OffsetX: 15, // OffsetY: 10, // PrintObject: &enable, // LockAspectRatio: false, // Locked: &disable, // }, // Title: excelize.ChartTitle{ // Name: "Clustered Column - Line Chart", // }, // Legend: excelize.ChartLegend{ // Position: "left", // ShowLegendKey: false, // }, // PlotArea: excelize.ChartPlotArea{ // ShowCatName: false, // ShowLeaderLines: false, // ShowPercent: true, // ShowSerName: true, // ShowVal: true, // }, // }, &excelize.Chart{ // Type: "line", // Series: []excelize.ChartSeries{ // { // Name: "Sheet1!$A$4", // Categories: "Sheet1!$B$1:$D$1", // Values: "Sheet1!$B$4:$D$4", // Marker: excelize.ChartMarker{ // Symbol: "none", Size: 10, // }, // }, // }, // Format: excelize.GraphicOptions{ // ScaleX: 1, // ScaleY: 1, // OffsetX: 15, // OffsetY: 10, // PrintObject: &enable, // LockAspectRatio: false, // Locked: &disable, // }, // Legend: excelize.ChartLegend{ // Position: "right", // ShowLegendKey: false, // }, // PlotArea: excelize.ChartPlotArea{ // ShowCatName: false, // ShowLeaderLines: false, // ShowPercent: true, // ShowSerName: true, // ShowVal: true, // }, // }); err != nil { // fmt.Println(err) // return // } // // Save spreadsheet by the given path. // if err := f.SaveAs("Book1.xlsx"); err != nil { // fmt.Println(err) // } // } func (f *File) AddChart(sheet, cell string, chart *Chart, combo ...*Chart) error { // Read worksheet data ws, err := f.workSheetReader(sheet) if err != nil { return err } opts, comboCharts, err := f.getChartOptions(chart, combo) if err != nil { return err } // Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder. drawingID := f.countDrawings() + 1 chartID := f.countCharts() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML) drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") err = f.addDrawingChart(sheet, drawingXML, cell, int(opts.Dimension.Width), int(opts.Dimension.Height), drawingRID, &opts.Format) if err != nil { return err } f.addChart(opts, comboCharts) if err = f.addContentTypePart(chartID, "chart"); err != nil { return err } _ = f.addContentTypePart(drawingID, "drawings") f.addSheetNameSpace(sheet, SourceRelationship) return err } // AddChartSheet provides the method to create a chartsheet by given chart // format set (such as offset, scale, aspect ratio setting and print settings) // and properties set. In Excel a chartsheet is a worksheet that only contains // a chart. func (f *File) AddChartSheet(sheet string, chart *Chart, combo ...*Chart) error { // Check if the worksheet already exists idx, err := f.GetSheetIndex(sheet) if err != nil { return err } if idx != -1 { return ErrExistsSheet } opts, comboCharts, err := f.getChartOptions(chart, combo) if err != nil { return err } cs := xlsxChartsheet{ SheetViews: &xlsxChartsheetViews{ SheetView: []*xlsxChartsheetView{{ZoomScaleAttr: 100, ZoomToFitAttr: true}}, }, } f.SheetCount++ wb, _ := f.workbookReader() sheetID := 0 for _, v := range wb.Sheets.Sheet { if v.SheetID > sheetID { sheetID = v.SheetID } } sheetID++ path := "xl/chartsheets/sheet" + strconv.Itoa(sheetID) + ".xml" f.sheetMap[sheet] = path f.Sheet.Store(path, nil) drawingID := f.countDrawings() + 1 chartID := f.countCharts() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" f.prepareChartSheetDrawing(&cs, drawingID, sheet) drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") if err = f.addSheetDrawingChart(drawingXML, drawingRID, &opts.Format); err != nil { return err } f.addChart(opts, comboCharts) if err = f.addContentTypePart(chartID, "chart"); err != nil { return err } _ = f.addContentTypePart(sheetID, "chartsheet") _ = f.addContentTypePart(drawingID, "drawings") // Update workbook.xml.rels rID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipChartsheet, fmt.Sprintf("/xl/chartsheets/sheet%d.xml", sheetID), "") // Update workbook.xml f.setWorkbook(sheet, sheetID, rID) chartsheet, _ := xml.Marshal(cs) f.addSheetNameSpace(sheet, NameSpaceSpreadSheet) f.saveFileList(path, replaceRelationshipsBytes(f.replaceNameSpaceBytes(path, chartsheet))) return err } // getChartOptions provides a function to check format set of the chart and // create chart format. func (f *File) getChartOptions(opts *Chart, combo []*Chart) (*Chart, []*Chart, error) { var comboCharts []*Chart options, err := parseChartOptions(opts) if err != nil { return options, comboCharts, err } for _, comboFormat := range combo { comboChart, err := parseChartOptions(comboFormat) if err != nil { return options, comboCharts, err } if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok { return options, comboCharts, newUnsupportedChartType(comboChart.Type) } comboCharts = append(comboCharts, comboChart) } if _, ok := chartValAxNumFmtFormatCode[options.Type]; !ok { return options, comboCharts, newUnsupportedChartType(options.Type) } return options, comboCharts, err } // DeleteChart provides a function to delete chart in spreadsheet by given // worksheet name and cell reference. func (f *File) DeleteChart(sheet, cell string) error { col, row, err := CellNameToCoordinates(cell) if err != nil { return err } col-- row-- ws, err := f.workSheetReader(sheet) if err != nil { return err } if ws.Drawing == nil { return err } drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl") return f.deleteDrawing(col, row, drawingXML, "Chart") } // countCharts provides a function to get chart files count storage in the // folder xl/charts. func (f *File) countCharts() int { count := 0 f.Pkg.Range(func(k, v interface{}) bool { if strings.Contains(k.(string), "xl/charts/chart") { count++ } return true }) return count } // ptToEMUs provides a function to convert pt to EMUs, 1 pt = 12700 EMUs. The // range of pt is 0.25pt - 999pt. If the value of pt is outside the range, the // default EMUs will be returned. func (f *File) ptToEMUs(pt float64) int { if 0.25 > pt || pt > 999 { return 25400 } return int(12700 * pt) }