summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chart.go1240
-rw-r--r--chart_test.go24
-rw-r--r--drawing.go1209
3 files changed, 1287 insertions, 1186 deletions
diff --git a/chart.go b/chart.go
index 738ed4b..5f06c55 100644
--- a/chart.go
+++ b/chart.go
@@ -12,11 +12,9 @@ package excelize
import (
"bytes"
"encoding/json"
- "encoding/xml"
"errors"
+ "fmt"
"io"
- "log"
- "reflect"
"strconv"
"strings"
)
@@ -765,1205 +763,75 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
return err
}
-// countCharts provides a function to get chart files count storage in the
-// folder xl/charts.
-func (f *File) countCharts() int {
- count := 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/charts/chart") {
- count++
- }
- }
- return count
-}
-
-// prepareDrawing provides a function to prepare drawing ID and XML by given
-// drawingID, worksheet name and default drawingXML.
-func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {
- sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
- if xlsx.Drawing != nil {
- // The worksheet already has a picture or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
- sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
- drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
- drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
- } else {
- // Add first picture for given sheet.
- sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
- f.addSheetDrawing(sheet, rID)
+// DeleteChart provides a function to delete chart in XLSX by given worksheet
+// and cell name.
+func (f *File) DeleteChart(sheet, cell string) (err error) {
+ var wsDr *xlsxWsDr
+ col, row, err := CellNameToCoordinates(cell)
+ if err != nil {
+ return
}
- return drawingID, drawingXML
-}
-
-// addChart provides a function to create chart as xl/charts/chart%d.xml by
-// given format sets.
-func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
- count := f.countCharts()
- xlsxChartSpace := xlsxChartSpace{
- XMLNSc: NameSpaceDrawingMLChart,
- XMLNSa: NameSpaceDrawingML,
- XMLNSr: SourceRelationship,
- XMLNSc16r2: SourceRelationshipChart201506,
- Date1904: &attrValBool{Val: boolPtr(false)},
- Lang: &attrValString{Val: stringPtr("en-US")},
- RoundedCorners: &attrValBool{Val: boolPtr(false)},
- Chart: cChart{
- Title: &cTitle{
- 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: &aLatin{
- Typeface: "+mn-lt",
- },
- },
- },
- R: &aR{
- RPr: aRPr{
- Lang: "en-US",
- AltLang: "en-US",
- },
- T: formatSet.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{
- RotX: &attrValInt{Val: intPtr(chartView3DRotX[formatSet.Type])},
- RotY: &attrValInt{Val: intPtr(chartView3DRotY[formatSet.Type])},
- Perspective: &attrValInt{Val: intPtr(chartView3DPerspective[formatSet.Type])},
- RAngAx: &attrValInt{Val: intPtr(chartView3DRAngAx[formatSet.Type])},
- },
- Floor: &cThicknessSpPr{
- Thickness: &attrValInt{Val: intPtr(0)},
- },
- SideWall: &cThicknessSpPr{
- Thickness: &attrValInt{Val: intPtr(0)},
- },
- BackWall: &cThicknessSpPr{
- Thickness: &attrValInt{Val: intPtr(0)},
- },
- PlotArea: &cPlotArea{},
- Legend: &cLegend{
- LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[formatSet.Legend.Position])},
- Overlay: &attrValBool{Val: boolPtr(false)},
- },
-
- PlotVisOnly: &attrValBool{Val: boolPtr(false)},
- DispBlanksAs: &attrValString{Val: stringPtr(formatSet.ShowBlanksAs)},
- ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)},
- },
- SpPr: &cSpPr{
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{Val: "bg1"},
- },
- Ln: &aLn{
- W: 9525,
- Cap: "flat",
- Cmpd: "sng",
- Algn: "ctr",
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{Val: "tx1",
- LumMod: &attrValInt{
- Val: intPtr(15000),
- },
- LumOff: &attrValInt{
- Val: intPtr(85000),
- },
- },
- },
- },
- },
- PrintSettings: &cPrintSettings{
- PageMargins: &cPageMargins{
- B: 0.75,
- L: 0.7,
- R: 0.7,
- T: 0.7,
- Header: 0.3,
- Footer: 0.3,
- },
- },
+ col--
+ row--
+ ws, err := f.workSheetReader(sheet)
+ if err != nil {
+ return
}
- plotAreaFunc := map[string]func(*formatChart) *cPlotArea{
- Area: f.drawBaseChart,
- AreaStacked: f.drawBaseChart,
- AreaPercentStacked: f.drawBaseChart,
- Area3D: f.drawBaseChart,
- Area3DStacked: f.drawBaseChart,
- Area3DPercentStacked: f.drawBaseChart,
- Bar: f.drawBaseChart,
- BarStacked: f.drawBaseChart,
- BarPercentStacked: f.drawBaseChart,
- Bar3DClustered: f.drawBaseChart,
- Bar3DStacked: f.drawBaseChart,
- Bar3DPercentStacked: f.drawBaseChart,
- Bar3DConeClustered: f.drawBaseChart,
- Bar3DConeStacked: f.drawBaseChart,
- Bar3DConePercentStacked: f.drawBaseChart,
- Bar3DPyramidClustered: f.drawBaseChart,
- Bar3DPyramidStacked: f.drawBaseChart,
- Bar3DPyramidPercentStacked: f.drawBaseChart,
- Bar3DCylinderClustered: f.drawBaseChart,
- Bar3DCylinderStacked: f.drawBaseChart,
- Bar3DCylinderPercentStacked: f.drawBaseChart,
- Col: f.drawBaseChart,
- ColStacked: f.drawBaseChart,
- ColPercentStacked: f.drawBaseChart,
- Col3D: f.drawBaseChart,
- Col3DClustered: f.drawBaseChart,
- Col3DStacked: f.drawBaseChart,
- Col3DPercentStacked: f.drawBaseChart,
- Col3DCone: f.drawBaseChart,
- Col3DConeClustered: f.drawBaseChart,
- Col3DConeStacked: f.drawBaseChart,
- Col3DConePercentStacked: f.drawBaseChart,
- Col3DPyramid: f.drawBaseChart,
- Col3DPyramidClustered: f.drawBaseChart,
- Col3DPyramidStacked: f.drawBaseChart,
- Col3DPyramidPercentStacked: f.drawBaseChart,
- Col3DCylinder: f.drawBaseChart,
- Col3DCylinderClustered: f.drawBaseChart,
- Col3DCylinderStacked: f.drawBaseChart,
- Col3DCylinderPercentStacked: f.drawBaseChart,
- Doughnut: f.drawDoughnutChart,
- Line: f.drawLineChart,
- Pie3D: f.drawPie3DChart,
- Pie: f.drawPieChart,
- PieOfPieChart: f.drawPieOfPieChart,
- BarOfPieChart: f.drawBarOfPieChart,
- Radar: f.drawRadarChart,
- Scatter: f.drawScatterChart,
- Surface3D: f.drawSurface3DChart,
- WireframeSurface3D: f.drawSurface3DChart,
- Contour: f.drawSurfaceChart,
- WireframeContour: f.drawSurfaceChart,
- Bubble: f.drawBaseChart,
- Bubble3D: f.drawBaseChart,
+ if ws.Drawing == nil {
+ return
}
- addChart := func(c, p *cPlotArea) {
- immutable, mutable := reflect.ValueOf(c).Elem(), reflect.ValueOf(p).Elem()
- for i := 0; i < mutable.NumField(); i++ {
- field := mutable.Field(i)
- if field.IsNil() {
- continue
+ drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1)
+ wsDr, _ = f.drawingParser(drawingXML)
+ for idx, anchor := range wsDr.TwoCellAnchor {
+ if err = nil; anchor.From != nil && anchor.Pic == nil {
+ if anchor.From.Col == col && anchor.From.Row == row {
+ wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
}
- immutable.FieldByName(mutable.Type().Field(i).Name).Set(field)
- }
- }
- addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[formatSet.Type](formatSet))
- order := len(formatSet.Series)
- for idx := range comboCharts {
- comboCharts[idx].order = order
- addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](comboCharts[idx]))
- order += len(comboCharts[idx].Series)
- }
- chart, _ := xml.Marshal(xlsxChartSpace)
- media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
- f.saveFileList(media, chart)
-}
-
-// drawBaseChart provides a function to draw the c:plotArea element for bar,
-// and column series charts by given format sets.
-func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
- c := cCharts{
- BarDir: &attrValString{
- Val: stringPtr("col"),
- },
- Grouping: &attrValString{
- Val: stringPtr("clustered"),
- },
- VaryColors: &attrValBool{
- Val: boolPtr(true),
- },
- Ser: f.drawChartSeries(formatSet),
- Shape: f.drawChartShape(formatSet),
- DLbls: f.drawChartDLbls(formatSet),
- AxID: []*attrValInt{
- {Val: intPtr(754001152)},
- {Val: intPtr(753999904)},
- },
- Overlap: &attrValInt{Val: intPtr(100)},
- }
- var ok bool
- if *c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok {
- c.BarDir = nil
- }
- if *c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok {
- c.Grouping = nil
- }
- if *c.Overlap.Val, ok = plotAreaChartOverlap[formatSet.Type]; !ok {
- c.Overlap = nil
- }
- catAx := f.drawPlotAreaCatAx(formatSet)
- valAx := f.drawPlotAreaValAx(formatSet)
- charts := map[string]*cPlotArea{
- "area": {
- AreaChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "areaStacked": {
- AreaChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "areaPercentStacked": {
- AreaChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "area3D": {
- Area3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "area3DStacked": {
- Area3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "area3DPercentStacked": {
- Area3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar": {
- BarChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "barStacked": {
- BarChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "barPercentStacked": {
- BarChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DPercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DConeClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DConeStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DConePercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DPyramidClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DPyramidStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DPyramidPercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DCylinderClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DCylinderStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bar3DCylinderPercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col": {
- BarChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "colStacked": {
- BarChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "colPercentStacked": {
- BarChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3D": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DPercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DCone": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DConeClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DConeStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DConePercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DPyramid": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DPyramidClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DPyramidStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DPyramidPercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DCylinder": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DCylinderClustered": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DCylinderStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "col3DCylinderPercentStacked": {
- Bar3DChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bubble": {
- BubbleChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- "bubble3D": {
- BubbleChart: &c,
- CatAx: catAx,
- ValAx: valAx,
- },
- }
- return charts[formatSet.Type]
-}
-
-// drawDoughnutChart provides a function to draw the c:plotArea element for
-// doughnut chart by given format sets.
-func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- DoughnutChart: &cCharts{
- VaryColors: &attrValBool{
- Val: boolPtr(true),
- },
- Ser: f.drawChartSeries(formatSet),
- HoleSize: &attrValInt{Val: intPtr(75)},
- },
- }
-}
-
-// drawLineChart provides a function to draw the c:plotArea element for line
-// chart by given format sets.
-func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- LineChart: &cCharts{
- Grouping: &attrValString{
- Val: stringPtr(plotAreaChartGrouping[formatSet.Type]),
- },
- VaryColors: &attrValBool{
- Val: boolPtr(false),
- },
- Ser: f.drawChartSeries(formatSet),
- DLbls: f.drawChartDLbls(formatSet),
- Smooth: &attrValBool{
- Val: boolPtr(false),
- },
- AxID: []*attrValInt{
- {Val: intPtr(754001152)},
- {Val: intPtr(753999904)},
- },
- },
- CatAx: f.drawPlotAreaCatAx(formatSet),
- ValAx: f.drawPlotAreaValAx(formatSet),
- }
-}
-
-// drawPieChart provides a function to draw the c:plotArea element for pie
-// chart by given format sets.
-func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- PieChart: &cCharts{
- VaryColors: &attrValBool{
- Val: boolPtr(true),
- },
- Ser: f.drawChartSeries(formatSet),
- },
- }
-}
-
-// drawPie3DChart provides a function to draw the c:plotArea element for 3D
-// pie chart by given format sets.
-func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- Pie3DChart: &cCharts{
- VaryColors: &attrValBool{
- Val: boolPtr(true),
- },
- Ser: f.drawChartSeries(formatSet),
- },
- }
-}
-
-// drawPieOfPieChart provides a function to draw the c:plotArea element for
-// pie chart by given format sets.
-func (f *File) drawPieOfPieChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- PieChart: &cCharts{
- OfPieType: &attrValString{
- Val: stringPtr("pie"),
- },
- VaryColors: &attrValBool{
- Val: boolPtr(true),
- },
- Ser: f.drawChartSeries(formatSet),
- SerLines: &attrValString{},
- },
- }
-}
-
-// drawBarOfPieChart provides a function to draw the c:plotArea element for
-// pie chart by given format sets.
-func (f *File) drawBarOfPieChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- PieChart: &cCharts{
- OfPieType: &attrValString{
- Val: stringPtr("bar"),
- },
- VaryColors: &attrValBool{
- Val: boolPtr(true),
- },
- Ser: f.drawChartSeries(formatSet),
- SerLines: &attrValString{},
- },
- }
-}
-
-// drawRadarChart provides a function to draw the c:plotArea element for radar
-// chart by given format sets.
-func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- RadarChart: &cCharts{
- RadarStyle: &attrValString{
- Val: stringPtr("marker"),
- },
- VaryColors: &attrValBool{
- Val: boolPtr(false),
- },
- Ser: f.drawChartSeries(formatSet),
- DLbls: f.drawChartDLbls(formatSet),
- AxID: []*attrValInt{
- {Val: intPtr(754001152)},
- {Val: intPtr(753999904)},
- },
- },
- CatAx: f.drawPlotAreaCatAx(formatSet),
- ValAx: f.drawPlotAreaValAx(formatSet),
- }
-}
-
-// drawScatterChart provides a function to draw the c:plotArea element for
-// scatter chart by given format sets.
-func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
- return &cPlotArea{
- ScatterChart: &cCharts{
- ScatterStyle: &attrValString{
- Val: stringPtr("smoothMarker"), // line,lineMarker,marker,none,smooth,smoothMarker
- },
- VaryColors: &attrValBool{
- Val: boolPtr(false),
- },
- Ser: f.drawChartSeries(formatSet),
- DLbls: f.drawChartDLbls(formatSet),
- AxID: []*attrValInt{
- {Val: intPtr(754001152)},
- {Val: intPtr(753999904)},
- },
- },
- CatAx: f.drawPlotAreaCatAx(formatSet),
- ValAx: f.drawPlotAreaValAx(formatSet),
- }
-}
-
-// drawSurface3DChart provides a function to draw the c:surface3DChart element by
-// given format sets.
-func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea {
- plotArea := &cPlotArea{
- Surface3DChart: &cCharts{
- Ser: f.drawChartSeries(formatSet),
- AxID: []*attrValInt{
- {Val: intPtr(754001152)},
- {Val: intPtr(753999904)},
- {Val: intPtr(832256642)},
- },
- },
- CatAx: f.drawPlotAreaCatAx(formatSet),
- ValAx: f.drawPlotAreaValAx(formatSet),
- SerAx: f.drawPlotAreaSerAx(formatSet),
- }
- if formatSet.Type == WireframeSurface3D {
- plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)}
- }
- return plotArea
-}
-
-// drawSurfaceChart provides a function to draw the c:surfaceChart element by
-// given format sets.
-func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea {
- plotArea := &cPlotArea{
- SurfaceChart: &cCharts{
- Ser: f.drawChartSeries(formatSet),
- AxID: []*attrValInt{
- {Val: intPtr(754001152)},
- {Val: intPtr(753999904)},
- {Val: intPtr(832256642)},
- },
- },
- CatAx: f.drawPlotAreaCatAx(formatSet),
- ValAx: f.drawPlotAreaValAx(formatSet),
- SerAx: f.drawPlotAreaSerAx(formatSet),
- }
- if formatSet.Type == WireframeContour {
- plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)}
- }
- return plotArea
-}
-
-// drawChartShape provides a function to draw the c:shape element by given
-// format sets.
-func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
- shapes := map[string]string{
- Bar3DConeClustered: "cone",
- Bar3DConeStacked: "cone",
- Bar3DConePercentStacked: "cone",
- Bar3DPyramidClustered: "pyramid",
- Bar3DPyramidStacked: "pyramid",
- Bar3DPyramidPercentStacked: "pyramid",
- Bar3DCylinderClustered: "cylinder",
- Bar3DCylinderStacked: "cylinder",
- Bar3DCylinderPercentStacked: "cylinder",
- Col3DCone: "cone",
- Col3DConeClustered: "cone",
- Col3DConeStacked: "cone",
- Col3DConePercentStacked: "cone",
- Col3DPyramid: "pyramid",
- Col3DPyramidClustered: "pyramid",
- Col3DPyramidStacked: "pyramid",
- Col3DPyramidPercentStacked: "pyramid",
- Col3DCylinder: "cylinder",
- Col3DCylinderClustered: "cylinder",
- Col3DCylinderStacked: "cylinder",
- Col3DCylinderPercentStacked: "cylinder",
- }
- if shape, ok := shapes[formatSet.Type]; ok {
- return &attrValString{Val: stringPtr(shape)}
- }
- return nil
-}
-
-// drawChartSeries provides a function to draw the c:ser element by given
-// format sets.
-func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
- ser := []cSer{}
- for k := range formatSet.Series {
- ser = append(ser, cSer{
- IDx: &attrValInt{Val: intPtr(k + formatSet.order)},
- Order: &attrValInt{Val: intPtr(k + formatSet.order)},
- Tx: &cTx{
- StrRef: &cStrRef{
- F: formatSet.Series[k].Name,
- },
- },
- SpPr: f.drawChartSeriesSpPr(k, formatSet),
- Marker: f.drawChartSeriesMarker(k, formatSet),
- DPt: f.drawChartSeriesDPt(k, formatSet),
- DLbls: f.drawChartSeriesDLbls(formatSet),
- Cat: f.drawChartSeriesCat(formatSet.Series[k], formatSet),
- Val: f.drawChartSeriesVal(formatSet.Series[k], formatSet),
- XVal: f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
- YVal: f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
- BubbleSize: f.drawCharSeriesBubbleSize(formatSet.Series[k], formatSet),
- Bubble3D: f.drawCharSeriesBubble3D(formatSet),
- })
- }
- return &ser
-}
-
-// drawChartSeriesSpPr provides a function to draw the c:spPr element by given
-// format sets.
-func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
- spPrScatter := &cSpPr{
- Ln: &aLn{
- W: 25400,
- NoFill: " ",
- },
- }
- spPrLine := &cSpPr{
- Ln: &aLn{
- W: f.ptToEMUs(formatSet.Series[i].Line.Width),
- Cap: "rnd", // rnd, sq, flat
- },
- }
- if i+formatSet.order < 6 {
- spPrLine.Ln.SolidFill = &aSolidFill{
- SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+formatSet.order+1)},
- }
- }
- chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter}
- return chartSeriesSpPr[formatSet.Type]
-}
-
-// drawChartSeriesDPt provides a function to draw the c:dPt element by given
-// data index and format sets.
-func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
- dpt := []*cDPt{{
- IDx: &attrValInt{Val: intPtr(i)},
- Bubble3D: &attrValBool{Val: boolPtr(false)},
- SpPr: &cSpPr{
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+1)},
- },
- Ln: &aLn{
- W: 25400,
- Cap: "rnd",
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{Val: "lt" + strconv.Itoa(i+1)},
- },
- },
- Sp3D: &aSp3D{
- ContourW: 25400,
- ContourClr: &aContourClr{
- SchemeClr: &aSchemeClr{Val: "lt" + strconv.Itoa(i+1)},
- },
- },
- },
- }}
- chartSeriesDPt := map[string][]*cDPt{Pie: dpt, Pie3D: dpt}
- return chartSeriesDPt[formatSet.Type]
-}
-
-// drawChartSeriesCat provides a function to draw the c:cat element by given
-// chart series and format sets.
-func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *cCat {
- cat := &cCat{
- StrRef: &cStrRef{
- F: v.Categories,
- },
- }
- chartSeriesCat := map[string]*cCat{Scatter: nil, Bubble: nil, Bubble3D: nil}
- if _, ok := chartSeriesCat[formatSet.Type]; ok || v.Categories == "" {
- return nil
- }
- return cat
-}
-
-// drawChartSeriesVal provides a function to draw the c:val element by given
-// chart series and format sets.
-func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *cVal {
- val := &cVal{
- NumRef: &cNumRef{
- F: v.Values,
- },
- }
- chartSeriesVal := map[string]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil}
- if _, ok := chartSeriesVal[formatSet.Type]; ok {
- return nil
- }
- return val
-}
-
-// drawChartSeriesMarker provides a function to draw the c:marker element by
-// given data index and format sets.
-func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
- marker := &cMarker{
- Symbol: &attrValString{Val: stringPtr("circle")},
- Size: &attrValInt{Val: intPtr(5)},
- }
- if i < 6 {
- marker.SpPr = &cSpPr{
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{
- Val: "accent" + strconv.Itoa(i+1),
- },
- },
- Ln: &aLn{
- W: 9252,
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{
- Val: "accent" + strconv.Itoa(i+1),
- },
- },
- },
}
}
- chartSeriesMarker := map[string]*cMarker{Scatter: marker}
- return chartSeriesMarker[formatSet.Type]
+ return f.deleteChart(col, row, drawingXML, wsDr)
}
-// drawChartSeriesXVal provides a function to draw the c:xVal element by given
-// chart series and format sets.
-func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart) *cCat {
- cat := &cCat{
- StrRef: &cStrRef{
- F: v.Categories,
- },
- }
- chartSeriesXVal := map[string]*cCat{Scatter: cat}
- return chartSeriesXVal[formatSet.Type]
-}
-
-// drawChartSeriesYVal provides a function to draw the c:yVal element by given
-// chart series and format sets.
-func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart) *cVal {
- val := &cVal{
- NumRef: &cNumRef{
- F: v.Values,
- },
- }
- chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
- return chartSeriesYVal[formatSet.Type]
-}
-
-// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
-// element by given chart series and format sets.
-func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatChart) *cVal {
- if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[formatSet.Type]; !ok {
- return nil
- }
- return &cVal{
- NumRef: &cNumRef{
- F: v.Values,
- },
- }
-}
-
-// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
-// by given format sets.
-func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool {
- if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok {
- return nil
- }
- return &attrValBool{Val: boolPtr(true)}
-}
-
-// drawChartDLbls provides a function to draw the c:dLbls element by given
-// format sets.
-func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
- return &cDLbls{
- ShowLegendKey: &attrValBool{Val: boolPtr(formatSet.Legend.ShowLegendKey)},
- ShowVal: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowVal)},
- ShowCatName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowCatName)},
- ShowSerName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowSerName)},
- ShowBubbleSize: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowBubbleSize)},
- ShowPercent: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowPercent)},
- ShowLeaderLines: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowLeaderLines)},
- }
-}
-
-// drawChartSeriesDLbls provides a function to draw the c:dLbls element by
-// given format sets.
-func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
- dLbls := f.drawChartDLbls(formatSet)
- chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil}
- if _, ok := chartSeriesDLbls[formatSet.Type]; ok {
- return nil
- }
- return dLbls
-}
-
-// drawPlotAreaCatAx provides a function to draw the c:catAx element.
-func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
- min := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Minimum)}
- max := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Maximum)}
- if formatSet.XAxis.Minimum == 0 {
- min = nil
- }
- if formatSet.XAxis.Maximum == 0 {
- max = nil
- }
- axs := []*cAxs{
- {
- AxID: &attrValInt{Val: intPtr(754001152)},
- Scaling: &cScaling{
- Orientation: &attrValString{Val: stringPtr(orientation[formatSet.XAxis.ReverseOrder])},
- Max: max,
- Min: min,
- },
- Delete: &attrValBool{Val: boolPtr(false)},
- AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])},
- NumFmt: &cNumFmt{
- FormatCode: "General",
- SourceLinked: true,
- },
- MajorTickMark: &attrValString{Val: stringPtr("none")},
- MinorTickMark: &attrValString{Val: stringPtr("none")},
- TickLblPos: &attrValString{Val: stringPtr("nextTo")},
- SpPr: f.drawPlotAreaSpPr(),
- TxPr: f.drawPlotAreaTxPr(),
- CrossAx: &attrValInt{Val: intPtr(753999904)},
- Crosses: &attrValString{Val: stringPtr("autoZero")},
- Auto: &attrValBool{Val: boolPtr(true)},
- LblAlgn: &attrValString{Val: stringPtr("ctr")},
- LblOffset: &attrValInt{Val: intPtr(100)},
- NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},
- },
- }
- if formatSet.XAxis.MajorGridlines {
- axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
- }
- if formatSet.XAxis.MinorGridlines {
- axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
- }
- if formatSet.XAxis.TickLabelSkip != 0 {
- axs[0].TickLblSkip = &attrValInt{Val: intPtr(formatSet.XAxis.TickLabelSkip)}
- }
- return axs
-}
-
-// drawPlotAreaValAx provides a function to draw the c:valAx element.
-func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
- min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)}
- max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)}
- if formatSet.YAxis.Minimum == 0 {
- min = nil
- }
- if formatSet.YAxis.Maximum == 0 {
- max = nil
- }
- axs := []*cAxs{
- {
- AxID: &attrValInt{Val: intPtr(753999904)},
- Scaling: &cScaling{
- Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])},
- Max: max,
- Min: min,
- },
- Delete: &attrValBool{Val: boolPtr(false)},
- AxPos: &attrValString{Val: stringPtr(valAxPos[formatSet.YAxis.ReverseOrder])},
- NumFmt: &cNumFmt{
- FormatCode: chartValAxNumFmtFormatCode[formatSet.Type],
- SourceLinked: true,
- },
- MajorTickMark: &attrValString{Val: stringPtr("none")},
- MinorTickMark: &attrValString{Val: stringPtr("none")},
- TickLblPos: &attrValString{Val: stringPtr("nextTo")},
- SpPr: f.drawPlotAreaSpPr(),
- TxPr: f.drawPlotAreaTxPr(),
- CrossAx: &attrValInt{Val: intPtr(754001152)},
- Crosses: &attrValString{Val: stringPtr("autoZero")},
- CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[formatSet.Type])},
- },
- }
- if formatSet.YAxis.MajorGridlines {
- axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
- }
- if formatSet.YAxis.MinorGridlines {
- axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
- }
- if pos, ok := valTickLblPos[formatSet.Type]; ok {
- axs[0].TickLblPos.Val = stringPtr(pos)
- }
- if formatSet.YAxis.MajorUnit != 0 {
- axs[0].MajorUnit = &attrValFloat{Val: float64Ptr(formatSet.YAxis.MajorUnit)}
- }
- return axs
-}
-
-// drawPlotAreaSerAx provides a function to draw the c:serAx element.
-func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs {
- min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)}
- max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)}
- if formatSet.YAxis.Minimum == 0 {
- min = nil
- }
- if formatSet.YAxis.Maximum == 0 {
- max = nil
- }
- return []*cAxs{
- {
- AxID: &attrValInt{Val: intPtr(832256642)},
- Scaling: &cScaling{
- Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])},
- Max: max,
- Min: min,
- },
- Delete: &attrValBool{Val: boolPtr(false)},
- AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])},
- TickLblPos: &attrValString{Val: stringPtr("nextTo")},
- SpPr: f.drawPlotAreaSpPr(),
- TxPr: f.drawPlotAreaTxPr(),
- CrossAx: &attrValInt{Val: intPtr(753999904)},
- },
- }
-}
-
-// drawPlotAreaSpPr provides a function to draw the c:spPr element.
-func (f *File) drawPlotAreaSpPr() *cSpPr {
- return &cSpPr{
- Ln: &aLn{
- W: 9525,
- Cap: "flat",
- Cmpd: "sng",
- Algn: "ctr",
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{
- Val: "tx1",
- LumMod: &attrValInt{Val: intPtr(15000)},
- LumOff: &attrValInt{Val: intPtr(85000)},
- },
- },
- },
- }
-}
-
-// drawPlotAreaTxPr provides a function to draw the c:txPr element.
-func (f *File) drawPlotAreaTxPr() *cTxPr {
- return &cTxPr{
- BodyPr: aBodyPr{
- Rot: -60000000,
- SpcFirstLastPara: true,
- VertOverflow: "ellipsis",
- Vert: "horz",
- Wrap: "square",
- Anchor: "ctr",
- AnchorCtr: true,
- },
- P: aP{
- PPr: &aPPr{
- DefRPr: aRPr{
- Sz: 900,
- B: false,
- I: false,
- U: "none",
- Strike: "noStrike",
- Kern: 1200,
- Baseline: 0,
- SolidFill: &aSolidFill{
- SchemeClr: &aSchemeClr{
- Val: "tx1",
- LumMod: &attrValInt{Val: intPtr(15000)},
- LumOff: &attrValInt{Val: intPtr(85000)},
- },
- },
- Latin: &aLatin{Typeface: "+mn-lt"},
- Ea: &aEa{Typeface: "+mn-ea"},
- Cs: &aCs{Typeface: "+mn-cs"},
- },
- },
- EndParaRPr: &aEndParaRPr{Lang: "en-US"},
- },
- }
-}
-
-// drawingParser provides a function to parse drawingXML. In order to solve
-// the problem that the label structure is changed after serialization and
-// deserialization, two different structures: decodeWsDr and encodeWsDr are
-// defined.
-func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
+// deleteChart provides a function to delete chart graphic frame by given by
+// given coordinates.
+func (f *File) deleteChart(col, row int, drawingXML string, wsDr *xlsxWsDr) (err error) {
var (
- err error
- ok bool
+ deWsDr *decodeWsDr
+ deTwoCellAnchor *decodeTwoCellAnchor
)
-
- if f.Drawings[path] == nil {
- content := xlsxWsDr{}
- content.A = NameSpaceDrawingML
- content.Xdr = NameSpaceDrawingMLSpreadSheet
- if _, ok = f.XLSX[path]; ok { // Append Model
- decodeWsDr := decodeWsDr{}
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
- Decode(&decodeWsDr); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
- }
- content.R = decodeWsDr.R
- for _, v := range decodeWsDr.OneCellAnchor {
- content.OneCellAnchor = append(content.OneCellAnchor, &xdrCellAnchor{
- EditAs: v.EditAs,
- GraphicFrame: v.Content,
- })
- }
- for _, v := range decodeWsDr.TwoCellAnchor {
- content.TwoCellAnchor = append(content.TwoCellAnchor, &xdrCellAnchor{
- EditAs: v.EditAs,
- GraphicFrame: v.Content,
- })
+ deWsDr = new(decodeWsDr)
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
+ Decode(deWsDr); err != nil && err != io.EOF {
+ err = fmt.Errorf("xml decode error: %s", err)
+ return
+ }
+ for idx, anchor := range deWsDr.TwoCellAnchor {
+ deTwoCellAnchor = new(decodeTwoCellAnchor)
+ if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>"))).
+ Decode(deTwoCellAnchor); err != nil && err != io.EOF {
+ err = fmt.Errorf("xml decode error: %s", err)
+ return
+ }
+ if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic == nil {
+ if anchor.From.Col == col && anchor.From.Row == row {
+ wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
}
}
- f.Drawings[path] = &content
}
- wsDr := f.Drawings[path]
- return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2
+ f.Drawings[drawingXML] = wsDr
+ return err
}
-// addDrawingChart provides a function to add chart graphic frame by given
-// sheet, drawingXML, cell, width, height, relationship index and format sets.
-func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) error {
- col, row, err := CellNameToCoordinates(cell)
- if err != nil {
- return err
- }
- colIdx := col - 1
- rowIdx := row - 1
-
- width = int(float64(width) * formatSet.XScale)
- height = int(float64(height) * formatSet.YScale)
- colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 :=
- f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.OffsetX, formatSet.OffsetY, width, height)
- content, cNvPrID := f.drawingParser(drawingXML)
- twoCellAnchor := xdrCellAnchor{}
- twoCellAnchor.EditAs = formatSet.Positioning
- from := xlsxFrom{}
- from.Col = colStart
- from.ColOff = formatSet.OffsetX * EMU
- from.Row = rowStart
- from.RowOff = formatSet.OffsetY * EMU
- to := xlsxTo{}
- to.Col = colEnd
- to.ColOff = x2 * EMU
- to.Row = rowEnd
- to.RowOff = y2 * EMU
- twoCellAnchor.From = &from
- twoCellAnchor.To = &to
-
- graphicFrame := xlsxGraphicFrame{
- NvGraphicFramePr: xlsxNvGraphicFramePr{
- CNvPr: &xlsxCNvPr{
- ID: cNvPrID,
- Name: "Chart " + strconv.Itoa(cNvPrID),
- },
- },
- Graphic: &xlsxGraphic{
- GraphicData: &xlsxGraphicData{
- URI: NameSpaceDrawingMLChart,
- Chart: &xlsxChart{
- C: NameSpaceDrawingMLChart,
- R: SourceRelationship,
- RID: "rId" + strconv.Itoa(rID),
- },
- },
- },
- }
- graphic, _ := xml.Marshal(graphicFrame)
- twoCellAnchor.GraphicFrame = string(graphic)
- twoCellAnchor.ClientData = &xdrClientData{
- FLocksWithSheet: formatSet.FLocksWithSheet,
- FPrintsWithSheet: formatSet.FPrintsWithSheet,
+// countCharts provides a function to get chart files count storage in the
+// folder xl/charts.
+func (f *File) countCharts() int {
+ count := 0
+ for k := range f.XLSX {
+ if strings.Contains(k, "xl/charts/chart") {
+ count++
+ }
}
- content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
- f.Drawings[drawingXML] = content
- return err
+ return count
}
// ptToEMUs provides a function to convert pt to EMUs, 1 pt = 12700 EMUs. The
diff --git a/chart_test.go b/chart_test.go
index bb7d12c..d8d36d8 100644
--- a/chart_test.go
+++ b/chart_test.go
@@ -200,5 +200,29 @@ func TestAddChart(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
// Test with unsupported chart type
assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), "unsupported chart type unknown")
+ // Test add combo chart with invalid format set.
+ assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`, ""), "unexpected end of JSON input")
+ // Test add combo chart with unsupported chart type.
assert.EqualError(t, f.AddChart("Sheet2", "BD64", `{"type":"barOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`, `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`), "unsupported chart type unknown")
}
+
+func TestDeleteChart(t *testing.T) {
+ f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
+ assert.NoError(t, err)
+ assert.NoError(t, f.DeleteChart("Sheet1", "A1"))
+ assert.NoError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
+ assert.NoError(t, f.DeleteChart("Sheet1", "P1"))
+ assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteChart.xlsx")))
+ // Test delete chart on not exists worksheet.
+ assert.EqualError(t, f.DeleteChart("SheetN", "A1"), "sheet SheetN is not exist")
+ // Test delete chart with invalid coordinates.
+ assert.EqualError(t, f.DeleteChart("Sheet1", ""), `cannot convert cell "" to coordinates: invalid cell name ""`)
+ // Test delete chart with unsupport charset.
+ f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
+ assert.NoError(t, err)
+ delete(f.Sheet, "xl/drawings/drawing1.xml")
+ f.XLSX["xl/drawings/drawing1.xml"] = MacintoshCyrillicCharset
+ assert.EqualError(t, f.DeleteChart("Sheet1", "A1"), "xml decode error: XML syntax error on line 1: invalid UTF-8")
+ // Test delete chart on no chart worksheet.
+ assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1"))
+}
diff --git a/drawing.go b/drawing.go
new file mode 100644
index 0000000..49506a3
--- /dev/null
+++ b/drawing.go
@@ -0,0 +1,1209 @@
+// Copyright 2016 - 2020 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 XLSX files. Support reads and writes XLSX file generated by
+// Microsoft Excelâ„¢ 2007 and later. Support save file without losing original
+// charts of XLSX. This library needs Go version 1.10 or later.
+
+package excelize
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "log"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// prepareDrawing provides a function to prepare drawing ID and XML by given
+// drawingID, worksheet name and default drawingXML.
+func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {
+ sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
+ if xlsx.Drawing != nil {
+ // The worksheet already has a picture or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
+ sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
+ drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
+ drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
+ } else {
+ // Add first picture for given sheet.
+ sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
+ rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ f.addSheetDrawing(sheet, rID)
+ }
+ return drawingID, drawingXML
+}
+
+// addChart provides a function to create chart as xl/charts/chart%d.xml by
+// given format sets.
+func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
+ count := f.countCharts()
+ xlsxChartSpace := xlsxChartSpace{
+ XMLNSc: NameSpaceDrawingMLChart,
+ XMLNSa: NameSpaceDrawingML,
+ XMLNSr: SourceRelationship,
+ XMLNSc16r2: SourceRelationshipChart201506,
+ Date1904: &attrValBool{Val: boolPtr(false)},
+ Lang: &attrValString{Val: stringPtr("en-US")},
+ RoundedCorners: &attrValBool{Val: boolPtr(false)},
+ Chart: cChart{
+ Title: &cTitle{
+ 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: &aLatin{
+ Typeface: "+mn-lt",
+ },
+ },
+ },
+ R: &aR{
+ RPr: aRPr{
+ Lang: "en-US",
+ AltLang: "en-US",
+ },
+ T: formatSet.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{
+ RotX: &attrValInt{Val: intPtr(chartView3DRotX[formatSet.Type])},
+ RotY: &attrValInt{Val: intPtr(chartView3DRotY[formatSet.Type])},
+ Perspective: &attrValInt{Val: intPtr(chartView3DPerspective[formatSet.Type])},
+ RAngAx: &attrValInt{Val: intPtr(chartView3DRAngAx[formatSet.Type])},
+ },
+ Floor: &cThicknessSpPr{
+ Thickness: &attrValInt{Val: intPtr(0)},
+ },
+ SideWall: &cThicknessSpPr{
+ Thickness: &attrValInt{Val: intPtr(0)},
+ },
+ BackWall: &cThicknessSpPr{
+ Thickness: &attrValInt{Val: intPtr(0)},
+ },
+ PlotArea: &cPlotArea{},
+ Legend: &cLegend{
+ LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[formatSet.Legend.Position])},
+ Overlay: &attrValBool{Val: boolPtr(false)},
+ },
+
+ PlotVisOnly: &attrValBool{Val: boolPtr(false)},
+ DispBlanksAs: &attrValString{Val: stringPtr(formatSet.ShowBlanksAs)},
+ ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)},
+ },
+ SpPr: &cSpPr{
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{Val: "bg1"},
+ },
+ Ln: &aLn{
+ W: 9525,
+ Cap: "flat",
+ Cmpd: "sng",
+ Algn: "ctr",
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{Val: "tx1",
+ LumMod: &attrValInt{
+ Val: intPtr(15000),
+ },
+ LumOff: &attrValInt{
+ Val: intPtr(85000),
+ },
+ },
+ },
+ },
+ },
+ PrintSettings: &cPrintSettings{
+ PageMargins: &cPageMargins{
+ B: 0.75,
+ L: 0.7,
+ R: 0.7,
+ T: 0.7,
+ Header: 0.3,
+ Footer: 0.3,
+ },
+ },
+ }
+ plotAreaFunc := map[string]func(*formatChart) *cPlotArea{
+ Area: f.drawBaseChart,
+ AreaStacked: f.drawBaseChart,
+ AreaPercentStacked: f.drawBaseChart,
+ Area3D: f.drawBaseChart,
+ Area3DStacked: f.drawBaseChart,
+ Area3DPercentStacked: f.drawBaseChart,
+ Bar: f.drawBaseChart,
+ BarStacked: f.drawBaseChart,
+ BarPercentStacked: f.drawBaseChart,
+ Bar3DClustered: f.drawBaseChart,
+ Bar3DStacked: f.drawBaseChart,
+ Bar3DPercentStacked: f.drawBaseChart,
+ Bar3DConeClustered: f.drawBaseChart,
+ Bar3DConeStacked: f.drawBaseChart,
+ Bar3DConePercentStacked: f.drawBaseChart,
+ Bar3DPyramidClustered: f.drawBaseChart,
+ Bar3DPyramidStacked: f.drawBaseChart,
+ Bar3DPyramidPercentStacked: f.drawBaseChart,
+ Bar3DCylinderClustered: f.drawBaseChart,
+ Bar3DCylinderStacked: f.drawBaseChart,
+ Bar3DCylinderPercentStacked: f.drawBaseChart,
+ Col: f.drawBaseChart,
+ ColStacked: f.drawBaseChart,
+ ColPercentStacked: f.drawBaseChart,
+ Col3D: f.drawBaseChart,
+ Col3DClustered: f.drawBaseChart,
+ Col3DStacked: f.drawBaseChart,
+ Col3DPercentStacked: f.drawBaseChart,
+ Col3DCone: f.drawBaseChart,
+ Col3DConeClustered: f.drawBaseChart,
+ Col3DConeStacked: f.drawBaseChart,
+ Col3DConePercentStacked: f.drawBaseChart,
+ Col3DPyramid: f.drawBaseChart,
+ Col3DPyramidClustered: f.drawBaseChart,
+ Col3DPyramidStacked: f.drawBaseChart,
+ Col3DPyramidPercentStacked: f.drawBaseChart,
+ Col3DCylinder: f.drawBaseChart,
+ Col3DCylinderClustered: f.drawBaseChart,
+ Col3DCylinderStacked: f.drawBaseChart,
+ Col3DCylinderPercentStacked: f.drawBaseChart,
+ Doughnut: f.drawDoughnutChart,
+ Line: f.drawLineChart,
+ Pie3D: f.drawPie3DChart,
+ Pie: f.drawPieChart,
+ PieOfPieChart: f.drawPieOfPieChart,
+ BarOfPieChart: f.drawBarOfPieChart,
+ Radar: f.drawRadarChart,
+ Scatter: f.drawScatterChart,
+ Surface3D: f.drawSurface3DChart,
+ WireframeSurface3D: f.drawSurface3DChart,
+ Contour: f.drawSurfaceChart,
+ WireframeContour: f.drawSurfaceChart,
+ Bubble: f.drawBaseChart,
+ Bubble3D: f.drawBaseChart,
+ }
+ addChart := func(c, p *cPlotArea) {
+ immutable, mutable := reflect.ValueOf(c).Elem(), reflect.ValueOf(p).Elem()
+ for i := 0; i < mutable.NumField(); i++ {
+ field := mutable.Field(i)
+ if field.IsNil() {
+ continue
+ }
+ immutable.FieldByName(mutable.Type().Field(i).Name).Set(field)
+ }
+ }
+ addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[formatSet.Type](formatSet))
+ order := len(formatSet.Series)
+ for idx := range comboCharts {
+ comboCharts[idx].order = order
+ addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](comboCharts[idx]))
+ order += len(comboCharts[idx].Series)
+ }
+ chart, _ := xml.Marshal(xlsxChartSpace)
+ media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
+ f.saveFileList(media, chart)
+}
+
+// drawBaseChart provides a function to draw the c:plotArea element for bar,
+// and column series charts by given format sets.
+func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
+ c := cCharts{
+ BarDir: &attrValString{
+ Val: stringPtr("col"),
+ },
+ Grouping: &attrValString{
+ Val: stringPtr("clustered"),
+ },
+ VaryColors: &attrValBool{
+ Val: boolPtr(true),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ Shape: f.drawChartShape(formatSet),
+ DLbls: f.drawChartDLbls(formatSet),
+ AxID: []*attrValInt{
+ {Val: intPtr(754001152)},
+ {Val: intPtr(753999904)},
+ },
+ Overlap: &attrValInt{Val: intPtr(100)},
+ }
+ var ok bool
+ if *c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok {
+ c.BarDir = nil
+ }
+ if *c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok {
+ c.Grouping = nil
+ }
+ if *c.Overlap.Val, ok = plotAreaChartOverlap[formatSet.Type]; !ok {
+ c.Overlap = nil
+ }
+ catAx := f.drawPlotAreaCatAx(formatSet)
+ valAx := f.drawPlotAreaValAx(formatSet)
+ charts := map[string]*cPlotArea{
+ "area": {
+ AreaChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "areaStacked": {
+ AreaChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "areaPercentStacked": {
+ AreaChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "area3D": {
+ Area3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "area3DStacked": {
+ Area3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "area3DPercentStacked": {
+ Area3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar": {
+ BarChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "barStacked": {
+ BarChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "barPercentStacked": {
+ BarChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DPercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DConeClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DConeStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DConePercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DPyramidClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DPyramidStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DPyramidPercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DCylinderClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DCylinderStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bar3DCylinderPercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col": {
+ BarChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "colStacked": {
+ BarChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "colPercentStacked": {
+ BarChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3D": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DPercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DCone": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DConeClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DConeStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DConePercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DPyramid": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DPyramidClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DPyramidStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DPyramidPercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DCylinder": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DCylinderClustered": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DCylinderStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "col3DCylinderPercentStacked": {
+ Bar3DChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bubble": {
+ BubbleChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ "bubble3D": {
+ BubbleChart: &c,
+ CatAx: catAx,
+ ValAx: valAx,
+ },
+ }
+ return charts[formatSet.Type]
+}
+
+// drawDoughnutChart provides a function to draw the c:plotArea element for
+// doughnut chart by given format sets.
+func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ DoughnutChart: &cCharts{
+ VaryColors: &attrValBool{
+ Val: boolPtr(true),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ HoleSize: &attrValInt{Val: intPtr(75)},
+ },
+ }
+}
+
+// drawLineChart provides a function to draw the c:plotArea element for line
+// chart by given format sets.
+func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ LineChart: &cCharts{
+ Grouping: &attrValString{
+ Val: stringPtr(plotAreaChartGrouping[formatSet.Type]),
+ },
+ VaryColors: &attrValBool{
+ Val: boolPtr(false),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ DLbls: f.drawChartDLbls(formatSet),
+ Smooth: &attrValBool{
+ Val: boolPtr(false),
+ },
+ AxID: []*attrValInt{
+ {Val: intPtr(754001152)},
+ {Val: intPtr(753999904)},
+ },
+ },
+ CatAx: f.drawPlotAreaCatAx(formatSet),
+ ValAx: f.drawPlotAreaValAx(formatSet),
+ }
+}
+
+// drawPieChart provides a function to draw the c:plotArea element for pie
+// chart by given format sets.
+func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ PieChart: &cCharts{
+ VaryColors: &attrValBool{
+ Val: boolPtr(true),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ },
+ }
+}
+
+// drawPie3DChart provides a function to draw the c:plotArea element for 3D
+// pie chart by given format sets.
+func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ Pie3DChart: &cCharts{
+ VaryColors: &attrValBool{
+ Val: boolPtr(true),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ },
+ }
+}
+
+// drawPieOfPieChart provides a function to draw the c:plotArea element for
+// pie chart by given format sets.
+func (f *File) drawPieOfPieChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ PieChart: &cCharts{
+ OfPieType: &attrValString{
+ Val: stringPtr("pie"),
+ },
+ VaryColors: &attrValBool{
+ Val: boolPtr(true),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ SerLines: &attrValString{},
+ },
+ }
+}
+
+// drawBarOfPieChart provides a function to draw the c:plotArea element for
+// pie chart by given format sets.
+func (f *File) drawBarOfPieChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ PieChart: &cCharts{
+ OfPieType: &attrValString{
+ Val: stringPtr("bar"),
+ },
+ VaryColors: &attrValBool{
+ Val: boolPtr(true),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ SerLines: &attrValString{},
+ },
+ }
+}
+
+// drawRadarChart provides a function to draw the c:plotArea element for radar
+// chart by given format sets.
+func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ RadarChart: &cCharts{
+ RadarStyle: &attrValString{
+ Val: stringPtr("marker"),
+ },
+ VaryColors: &attrValBool{
+ Val: boolPtr(false),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ DLbls: f.drawChartDLbls(formatSet),
+ AxID: []*attrValInt{
+ {Val: intPtr(754001152)},
+ {Val: intPtr(753999904)},
+ },
+ },
+ CatAx: f.drawPlotAreaCatAx(formatSet),
+ ValAx: f.drawPlotAreaValAx(formatSet),
+ }
+}
+
+// drawScatterChart provides a function to draw the c:plotArea element for
+// scatter chart by given format sets.
+func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
+ return &cPlotArea{
+ ScatterChart: &cCharts{
+ ScatterStyle: &attrValString{
+ Val: stringPtr("smoothMarker"), // line,lineMarker,marker,none,smooth,smoothMarker
+ },
+ VaryColors: &attrValBool{
+ Val: boolPtr(false),
+ },
+ Ser: f.drawChartSeries(formatSet),
+ DLbls: f.drawChartDLbls(formatSet),
+ AxID: []*attrValInt{
+ {Val: intPtr(754001152)},
+ {Val: intPtr(753999904)},
+ },
+ },
+ CatAx: f.drawPlotAreaCatAx(formatSet),
+ ValAx: f.drawPlotAreaValAx(formatSet),
+ }
+}
+
+// drawSurface3DChart provides a function to draw the c:surface3DChart element by
+// given format sets.
+func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea {
+ plotArea := &cPlotArea{
+ Surface3DChart: &cCharts{
+ Ser: f.drawChartSeries(formatSet),
+ AxID: []*attrValInt{
+ {Val: intPtr(754001152)},
+ {Val: intPtr(753999904)},
+ {Val: intPtr(832256642)},
+ },
+ },
+ CatAx: f.drawPlotAreaCatAx(formatSet),
+ ValAx: f.drawPlotAreaValAx(formatSet),
+ SerAx: f.drawPlotAreaSerAx(formatSet),
+ }
+ if formatSet.Type == WireframeSurface3D {
+ plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)}
+ }
+ return plotArea
+}
+
+// drawSurfaceChart provides a function to draw the c:surfaceChart element by
+// given format sets.
+func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea {
+ plotArea := &cPlotArea{
+ SurfaceChart: &cCharts{
+ Ser: f.drawChartSeries(formatSet),
+ AxID: []*attrValInt{
+ {Val: intPtr(754001152)},
+ {Val: intPtr(753999904)},
+ {Val: intPtr(832256642)},
+ },
+ },
+ CatAx: f.drawPlotAreaCatAx(formatSet),
+ ValAx: f.drawPlotAreaValAx(formatSet),
+ SerAx: f.drawPlotAreaSerAx(formatSet),
+ }
+ if formatSet.Type == WireframeContour {
+ plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)}
+ }
+ return plotArea
+}
+
+// drawChartShape provides a function to draw the c:shape element by given
+// format sets.
+func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
+ shapes := map[string]string{
+ Bar3DConeClustered: "cone",
+ Bar3DConeStacked: "cone",
+ Bar3DConePercentStacked: "cone",
+ Bar3DPyramidClustered: "pyramid",
+ Bar3DPyramidStacked: "pyramid",
+ Bar3DPyramidPercentStacked: "pyramid",
+ Bar3DCylinderClustered: "cylinder",
+ Bar3DCylinderStacked: "cylinder",
+ Bar3DCylinderPercentStacked: "cylinder",
+ Col3DCone: "cone",
+ Col3DConeClustered: "cone",
+ Col3DConeStacked: "cone",
+ Col3DConePercentStacked: "cone",
+ Col3DPyramid: "pyramid",
+ Col3DPyramidClustered: "pyramid",
+ Col3DPyramidStacked: "pyramid",
+ Col3DPyramidPercentStacked: "pyramid",
+ Col3DCylinder: "cylinder",
+ Col3DCylinderClustered: "cylinder",
+ Col3DCylinderStacked: "cylinder",
+ Col3DCylinderPercentStacked: "cylinder",
+ }
+ if shape, ok := shapes[formatSet.Type]; ok {
+ return &attrValString{Val: stringPtr(shape)}
+ }
+ return nil
+}
+
+// drawChartSeries provides a function to draw the c:ser element by given
+// format sets.
+func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
+ ser := []cSer{}
+ for k := range formatSet.Series {
+ ser = append(ser, cSer{
+ IDx: &attrValInt{Val: intPtr(k + formatSet.order)},
+ Order: &attrValInt{Val: intPtr(k + formatSet.order)},
+ Tx: &cTx{
+ StrRef: &cStrRef{
+ F: formatSet.Series[k].Name,
+ },
+ },
+ SpPr: f.drawChartSeriesSpPr(k, formatSet),
+ Marker: f.drawChartSeriesMarker(k, formatSet),
+ DPt: f.drawChartSeriesDPt(k, formatSet),
+ DLbls: f.drawChartSeriesDLbls(formatSet),
+ Cat: f.drawChartSeriesCat(formatSet.Series[k], formatSet),
+ Val: f.drawChartSeriesVal(formatSet.Series[k], formatSet),
+ XVal: f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
+ YVal: f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
+ BubbleSize: f.drawCharSeriesBubbleSize(formatSet.Series[k], formatSet),
+ Bubble3D: f.drawCharSeriesBubble3D(formatSet),
+ })
+ }
+ return &ser
+}
+
+// drawChartSeriesSpPr provides a function to draw the c:spPr element by given
+// format sets.
+func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
+ spPrScatter := &cSpPr{
+ Ln: &aLn{
+ W: 25400,
+ NoFill: " ",
+ },
+ }
+ spPrLine := &cSpPr{
+ Ln: &aLn{
+ W: f.ptToEMUs(formatSet.Series[i].Line.Width),
+ Cap: "rnd", // rnd, sq, flat
+ },
+ }
+ if i+formatSet.order < 6 {
+ spPrLine.Ln.SolidFill = &aSolidFill{
+ SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+formatSet.order+1)},
+ }
+ }
+ chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter}
+ return chartSeriesSpPr[formatSet.Type]
+}
+
+// drawChartSeriesDPt provides a function to draw the c:dPt element by given
+// data index and format sets.
+func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
+ dpt := []*cDPt{{
+ IDx: &attrValInt{Val: intPtr(i)},
+ Bubble3D: &attrValBool{Val: boolPtr(false)},
+ SpPr: &cSpPr{
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+1)},
+ },
+ Ln: &aLn{
+ W: 25400,
+ Cap: "rnd",
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{Val: "lt" + strconv.Itoa(i+1)},
+ },
+ },
+ Sp3D: &aSp3D{
+ ContourW: 25400,
+ ContourClr: &aContourClr{
+ SchemeClr: &aSchemeClr{Val: "lt" + strconv.Itoa(i+1)},
+ },
+ },
+ },
+ }}
+ chartSeriesDPt := map[string][]*cDPt{Pie: dpt, Pie3D: dpt}
+ return chartSeriesDPt[formatSet.Type]
+}
+
+// drawChartSeriesCat provides a function to draw the c:cat element by given
+// chart series and format sets.
+func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *cCat {
+ cat := &cCat{
+ StrRef: &cStrRef{
+ F: v.Categories,
+ },
+ }
+ chartSeriesCat := map[string]*cCat{Scatter: nil, Bubble: nil, Bubble3D: nil}
+ if _, ok := chartSeriesCat[formatSet.Type]; ok || v.Categories == "" {
+ return nil
+ }
+ return cat
+}
+
+// drawChartSeriesVal provides a function to draw the c:val element by given
+// chart series and format sets.
+func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *cVal {
+ val := &cVal{
+ NumRef: &cNumRef{
+ F: v.Values,
+ },
+ }
+ chartSeriesVal := map[string]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil}
+ if _, ok := chartSeriesVal[formatSet.Type]; ok {
+ return nil
+ }
+ return val
+}
+
+// drawChartSeriesMarker provides a function to draw the c:marker element by
+// given data index and format sets.
+func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
+ marker := &cMarker{
+ Symbol: &attrValString{Val: stringPtr("circle")},
+ Size: &attrValInt{Val: intPtr(5)},
+ }
+ if i < 6 {
+ marker.SpPr = &cSpPr{
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{
+ Val: "accent" + strconv.Itoa(i+1),
+ },
+ },
+ Ln: &aLn{
+ W: 9252,
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{
+ Val: "accent" + strconv.Itoa(i+1),
+ },
+ },
+ },
+ }
+ }
+ chartSeriesMarker := map[string]*cMarker{Scatter: marker}
+ return chartSeriesMarker[formatSet.Type]
+}
+
+// drawChartSeriesXVal provides a function to draw the c:xVal element by given
+// chart series and format sets.
+func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart) *cCat {
+ cat := &cCat{
+ StrRef: &cStrRef{
+ F: v.Categories,
+ },
+ }
+ chartSeriesXVal := map[string]*cCat{Scatter: cat}
+ return chartSeriesXVal[formatSet.Type]
+}
+
+// drawChartSeriesYVal provides a function to draw the c:yVal element by given
+// chart series and format sets.
+func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart) *cVal {
+ val := &cVal{
+ NumRef: &cNumRef{
+ F: v.Values,
+ },
+ }
+ chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
+ return chartSeriesYVal[formatSet.Type]
+}
+
+// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
+// element by given chart series and format sets.
+func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatChart) *cVal {
+ if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[formatSet.Type]; !ok {
+ return nil
+ }
+ return &cVal{
+ NumRef: &cNumRef{
+ F: v.Values,
+ },
+ }
+}
+
+// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
+// by given format sets.
+func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool {
+ if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok {
+ return nil
+ }
+ return &attrValBool{Val: boolPtr(true)}
+}
+
+// drawChartDLbls provides a function to draw the c:dLbls element by given
+// format sets.
+func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
+ return &cDLbls{
+ ShowLegendKey: &attrValBool{Val: boolPtr(formatSet.Legend.ShowLegendKey)},
+ ShowVal: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowVal)},
+ ShowCatName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowCatName)},
+ ShowSerName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowSerName)},
+ ShowBubbleSize: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowBubbleSize)},
+ ShowPercent: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowPercent)},
+ ShowLeaderLines: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowLeaderLines)},
+ }
+}
+
+// drawChartSeriesDLbls provides a function to draw the c:dLbls element by
+// given format sets.
+func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
+ dLbls := f.drawChartDLbls(formatSet)
+ chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil}
+ if _, ok := chartSeriesDLbls[formatSet.Type]; ok {
+ return nil
+ }
+ return dLbls
+}
+
+// drawPlotAreaCatAx provides a function to draw the c:catAx element.
+func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
+ min := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Minimum)}
+ max := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Maximum)}
+ if formatSet.XAxis.Minimum == 0 {
+ min = nil
+ }
+ if formatSet.XAxis.Maximum == 0 {
+ max = nil
+ }
+ axs := []*cAxs{
+ {
+ AxID: &attrValInt{Val: intPtr(754001152)},
+ Scaling: &cScaling{
+ Orientation: &attrValString{Val: stringPtr(orientation[formatSet.XAxis.ReverseOrder])},
+ Max: max,
+ Min: min,
+ },
+ Delete: &attrValBool{Val: boolPtr(false)},
+ AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])},
+ NumFmt: &cNumFmt{
+ FormatCode: "General",
+ SourceLinked: true,
+ },
+ MajorTickMark: &attrValString{Val: stringPtr("none")},
+ MinorTickMark: &attrValString{Val: stringPtr("none")},
+ TickLblPos: &attrValString{Val: stringPtr("nextTo")},
+ SpPr: f.drawPlotAreaSpPr(),
+ TxPr: f.drawPlotAreaTxPr(),
+ CrossAx: &attrValInt{Val: intPtr(753999904)},
+ Crosses: &attrValString{Val: stringPtr("autoZero")},
+ Auto: &attrValBool{Val: boolPtr(true)},
+ LblAlgn: &attrValString{Val: stringPtr("ctr")},
+ LblOffset: &attrValInt{Val: intPtr(100)},
+ NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},
+ },
+ }
+ if formatSet.XAxis.MajorGridlines {
+ axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
+ }
+ if formatSet.XAxis.MinorGridlines {
+ axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
+ }
+ if formatSet.XAxis.TickLabelSkip != 0 {
+ axs[0].TickLblSkip = &attrValInt{Val: intPtr(formatSet.XAxis.TickLabelSkip)}
+ }
+ return axs
+}
+
+// drawPlotAreaValAx provides a function to draw the c:valAx element.
+func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
+ min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)}
+ max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)}
+ if formatSet.YAxis.Minimum == 0 {
+ min = nil
+ }
+ if formatSet.YAxis.Maximum == 0 {
+ max = nil
+ }
+ axs := []*cAxs{
+ {
+ AxID: &attrValInt{Val: intPtr(753999904)},
+ Scaling: &cScaling{
+ Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])},
+ Max: max,
+ Min: min,
+ },
+ Delete: &attrValBool{Val: boolPtr(false)},
+ AxPos: &attrValString{Val: stringPtr(valAxPos[formatSet.YAxis.ReverseOrder])},
+ NumFmt: &cNumFmt{
+ FormatCode: chartValAxNumFmtFormatCode[formatSet.Type],
+ SourceLinked: true,
+ },
+ MajorTickMark: &attrValString{Val: stringPtr("none")},
+ MinorTickMark: &attrValString{Val: stringPtr("none")},
+ TickLblPos: &attrValString{Val: stringPtr("nextTo")},
+ SpPr: f.drawPlotAreaSpPr(),
+ TxPr: f.drawPlotAreaTxPr(),
+ CrossAx: &attrValInt{Val: intPtr(754001152)},
+ Crosses: &attrValString{Val: stringPtr("autoZero")},
+ CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[formatSet.Type])},
+ },
+ }
+ if formatSet.YAxis.MajorGridlines {
+ axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
+ }
+ if formatSet.YAxis.MinorGridlines {
+ axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
+ }
+ if pos, ok := valTickLblPos[formatSet.Type]; ok {
+ axs[0].TickLblPos.Val = stringPtr(pos)
+ }
+ if formatSet.YAxis.MajorUnit != 0 {
+ axs[0].MajorUnit = &attrValFloat{Val: float64Ptr(formatSet.YAxis.MajorUnit)}
+ }
+ return axs
+}
+
+// drawPlotAreaSerAx provides a function to draw the c:serAx element.
+func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs {
+ min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)}
+ max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)}
+ if formatSet.YAxis.Minimum == 0 {
+ min = nil
+ }
+ if formatSet.YAxis.Maximum == 0 {
+ max = nil
+ }
+ return []*cAxs{
+ {
+ AxID: &attrValInt{Val: intPtr(832256642)},
+ Scaling: &cScaling{
+ Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])},
+ Max: max,
+ Min: min,
+ },
+ Delete: &attrValBool{Val: boolPtr(false)},
+ AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])},
+ TickLblPos: &attrValString{Val: stringPtr("nextTo")},
+ SpPr: f.drawPlotAreaSpPr(),
+ TxPr: f.drawPlotAreaTxPr(),
+ CrossAx: &attrValInt{Val: intPtr(753999904)},
+ },
+ }
+}
+
+// drawPlotAreaSpPr provides a function to draw the c:spPr element.
+func (f *File) drawPlotAreaSpPr() *cSpPr {
+ return &cSpPr{
+ Ln: &aLn{
+ W: 9525,
+ Cap: "flat",
+ Cmpd: "sng",
+ Algn: "ctr",
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{
+ Val: "tx1",
+ LumMod: &attrValInt{Val: intPtr(15000)},
+ LumOff: &attrValInt{Val: intPtr(85000)},
+ },
+ },
+ },
+ }
+}
+
+// drawPlotAreaTxPr provides a function to draw the c:txPr element.
+func (f *File) drawPlotAreaTxPr() *cTxPr {
+ return &cTxPr{
+ BodyPr: aBodyPr{
+ Rot: -60000000,
+ SpcFirstLastPara: true,
+ VertOverflow: "ellipsis",
+ Vert: "horz",
+ Wrap: "square",
+ Anchor: "ctr",
+ AnchorCtr: true,
+ },
+ P: aP{
+ PPr: &aPPr{
+ DefRPr: aRPr{
+ Sz: 900,
+ B: false,
+ I: false,
+ U: "none",
+ Strike: "noStrike",
+ Kern: 1200,
+ Baseline: 0,
+ SolidFill: &aSolidFill{
+ SchemeClr: &aSchemeClr{
+ Val: "tx1",
+ LumMod: &attrValInt{Val: intPtr(15000)},
+ LumOff: &attrValInt{Val: intPtr(85000)},
+ },
+ },
+ Latin: &aLatin{Typeface: "+mn-lt"},
+ Ea: &aEa{Typeface: "+mn-ea"},
+ Cs: &aCs{Typeface: "+mn-cs"},
+ },
+ },
+ EndParaRPr: &aEndParaRPr{Lang: "en-US"},
+ },
+ }
+}
+
+// drawingParser provides a function to parse drawingXML. In order to solve
+// the problem that the label structure is changed after serialization and
+// deserialization, two different structures: decodeWsDr and encodeWsDr are
+// defined.
+func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
+ var (
+ err error
+ ok bool
+ )
+
+ if f.Drawings[path] == nil {
+ content := xlsxWsDr{}
+ content.A = NameSpaceDrawingML
+ content.Xdr = NameSpaceDrawingMLSpreadSheet
+ if _, ok = f.XLSX[path]; ok { // Append Model
+ decodeWsDr := decodeWsDr{}
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
+ Decode(&decodeWsDr); err != nil && err != io.EOF {
+ log.Printf("xml decode error: %s", err)
+ }
+ content.R = decodeWsDr.R
+ for _, v := range decodeWsDr.OneCellAnchor {
+ content.OneCellAnchor = append(content.OneCellAnchor, &xdrCellAnchor{
+ EditAs: v.EditAs,
+ GraphicFrame: v.Content,
+ })
+ }
+ for _, v := range decodeWsDr.TwoCellAnchor {
+ content.TwoCellAnchor = append(content.TwoCellAnchor, &xdrCellAnchor{
+ EditAs: v.EditAs,
+ GraphicFrame: v.Content,
+ })
+ }
+ }
+ f.Drawings[path] = &content
+ }
+ wsDr := f.Drawings[path]
+ return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2
+}
+
+// addDrawingChart provides a function to add chart graphic frame by given
+// sheet, drawingXML, cell, width, height, relationship index and format sets.
+func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) error {
+ col, row, err := CellNameToCoordinates(cell)
+ if err != nil {
+ return err
+ }
+ colIdx := col - 1
+ rowIdx := row - 1
+
+ width = int(float64(width) * formatSet.XScale)
+ height = int(float64(height) * formatSet.YScale)
+ colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 :=
+ f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.OffsetX, formatSet.OffsetY, width, height)
+ content, cNvPrID := f.drawingParser(drawingXML)
+ twoCellAnchor := xdrCellAnchor{}
+ twoCellAnchor.EditAs = formatSet.Positioning
+ from := xlsxFrom{}
+ from.Col = colStart
+ from.ColOff = formatSet.OffsetX * EMU
+ from.Row = rowStart
+ from.RowOff = formatSet.OffsetY * EMU
+ to := xlsxTo{}
+ to.Col = colEnd
+ to.ColOff = x2 * EMU
+ to.Row = rowEnd
+ to.RowOff = y2 * EMU
+ twoCellAnchor.From = &from
+ twoCellAnchor.To = &to
+
+ graphicFrame := xlsxGraphicFrame{
+ NvGraphicFramePr: xlsxNvGraphicFramePr{
+ CNvPr: &xlsxCNvPr{
+ ID: cNvPrID,
+ Name: "Chart " + strconv.Itoa(cNvPrID),
+ },
+ },
+ Graphic: &xlsxGraphic{
+ GraphicData: &xlsxGraphicData{
+ URI: NameSpaceDrawingMLChart,
+ Chart: &xlsxChart{
+ C: NameSpaceDrawingMLChart,
+ R: SourceRelationship,
+ RID: "rId" + strconv.Itoa(rID),
+ },
+ },
+ },
+ }
+ graphic, _ := xml.Marshal(graphicFrame)
+ twoCellAnchor.GraphicFrame = string(graphic)
+ twoCellAnchor.ClientData = &xdrClientData{
+ FLocksWithSheet: formatSet.FLocksWithSheet,
+ FPrintsWithSheet: formatSet.FPrintsWithSheet,
+ }
+ content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
+ f.Drawings[drawingXML] = content
+ return err
+}