summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--chart.go72
-rw-r--r--chart_test.go18
-rw-r--r--drawing.go7
-rw-r--r--excelize.go8
-rw-r--r--rows.go2
-rw-r--r--sheet.go35
-rw-r--r--styles.go2
8 files changed, 75 insertions, 71 deletions
diff --git a/LICENSE b/LICENSE
index fe738b9..e0f34bb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2016-2020, 360 Enterprise Security Group, Endpoint Security, Inc.
+Copyright (c) 2016-2020 The excelize Authors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/chart.go b/chart.go
index df196e9..cae833d 100644
--- a/chart.go
+++ b/chart.go
@@ -730,28 +730,14 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
// }
//
func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
- formatSet, err := parseFormatChartSet(format)
- if err != nil {
- return err
- }
- comboCharts := []*formatChart{}
- for _, comboFormat := range combo {
- comboChart, err := parseFormatChartSet(comboFormat)
- if err != nil {
- return err
- }
- if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
- return errors.New("unsupported chart type " + comboChart.Type)
- }
- comboCharts = append(comboCharts, comboChart)
- }
// Read sheet data.
xlsx, err := f.workSheetReader(sheet)
if err != nil {
return err
}
- if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
- return errors.New("unsupported chart type " + formatSet.Type)
+ formatSet, comboCharts, err := f.getFormatChart(format, combo)
+ if err != nil {
+ return err
}
// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
drawingID := f.countDrawings() + 1
@@ -777,31 +763,18 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
// Check if the worksheet already exists
if f.GetSheetIndex(sheet) != 0 {
- return errors.New("already existing name worksheet")
+ return errors.New("the same name worksheet already exists")
}
- formatSet, err := parseFormatChartSet(format)
+ formatSet, comboCharts, err := f.getFormatChart(format, combo)
if err != nil {
return err
}
- comboCharts := []*formatChart{}
- for _, comboFormat := range combo {
- comboChart, err := parseFormatChartSet(comboFormat)
- if err != nil {
- return err
- }
- if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
- return errors.New("unsupported chart type " + comboChart.Type)
- }
- comboCharts = append(comboCharts, comboChart)
- }
- if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
- return errors.New("unsupported chart type " + formatSet.Type)
- }
cs := xlsxChartsheet{
SheetViews: []*xlsxChartsheetViews{{
SheetView: []*xlsxChartsheetView{{ZoomScaleAttr: 100, ZoomToFitAttr: true}}},
},
}
+ f.SheetCount++
wb := f.workbookReader()
sheetID := 0
for _, v := range wb.Sheets.Sheet {
@@ -819,10 +792,7 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
drawingID, drawingXML = f.prepareChartSheetDrawing(&cs, 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.addSheetDrawingChart(sheet, drawingXML, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format)
- if err != nil {
- return err
- }
+ f.addSheetDrawingChart(drawingXML, drawingRID, &formatSet.Format)
f.addChart(formatSet, comboCharts)
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(sheetID, "chartsheet")
@@ -831,11 +801,35 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
rID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipChartsheet, fmt.Sprintf("chartsheets/sheet%d.xml", sheetID), "")
// Update xl/workbook.xml
f.setWorkbook(sheet, sheetID, rID)
- v, _ := xml.Marshal(cs)
- f.saveFileList(path, replaceRelationshipsBytes(replaceWorkSheetsRelationshipsNameSpaceBytes(v)))
+ chartsheet, _ := xml.Marshal(cs)
+ f.saveFileList(path, replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(chartsheet)))
return err
}
+// getFormatChart provides a function to check format set of the chart and
+// create chart format.
+func (f *File) getFormatChart(format string, combo []string) (*formatChart, []*formatChart, error) {
+ comboCharts := []*formatChart{}
+ formatSet, err := parseFormatChartSet(format)
+ if err != nil {
+ return formatSet, comboCharts, err
+ }
+ for _, comboFormat := range combo {
+ comboChart, err := parseFormatChartSet(comboFormat)
+ if err != nil {
+ return formatSet, comboCharts, err
+ }
+ if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
+ return formatSet, comboCharts, errors.New("unsupported chart type " + comboChart.Type)
+ }
+ comboCharts = append(comboCharts, comboChart)
+ }
+ if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
+ return formatSet, comboCharts, errors.New("unsupported chart type " + formatSet.Type)
+ }
+ return formatSet, comboCharts, err
+}
+
// DeleteChart provides a function to delete chart in XLSX by given worksheet
// and cell name.
func (f *File) DeleteChart(sheet, cell string) (err error) {
diff --git a/chart_test.go b/chart_test.go
index 351e663..3b419f0 100644
--- a/chart_test.go
+++ b/chart_test.go
@@ -198,6 +198,8 @@ func TestAddChart(t *testing.T) {
assert.NoError(t, f.AddChart("Combo Charts", axis, fmt.Sprintf(`{"type":"areaStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"%s"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[1]), fmt.Sprintf(`{"type":"%s","series":[{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[0])))
}
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
+ // Test with illegal cell coordinates
+ assert.EqualError(t, f.AddChart("Sheet2", "A", `{"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"}`), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
// 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
@@ -217,8 +219,20 @@ func TestAddChartSheet(t *testing.T) {
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
}
assert.NoError(t, f.AddChartSheet("Chart1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`))
-
- assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "already existing name worksheet")
+ // Test set the chartsheet as active sheet
+ var sheetID int
+ for idx, sheetName := range f.GetSheetMap() {
+ if sheetName != "Chart1" {
+ continue
+ }
+ sheetID = idx
+ }
+ f.SetActiveSheet(sheetID)
+
+ // Test cell value on chartsheet
+ assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is chart sheet")
+ // Test add chartsheet on already existing name sheet
+ assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "the same name worksheet already exists")
// Test with unsupported chart type
assert.EqualError(t, f.AddChartSheet("Chart2", `{"type":"unknown","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "unsupported chart type unknown")
diff --git a/drawing.go b/drawing.go
index 8ca1f49..b291d98 100644
--- a/drawing.go
+++ b/drawing.go
@@ -1232,10 +1232,7 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
// addSheetDrawingChart provides a function to add chart graphic frame for
// chartsheet by given sheet, drawingXML, width, height, relationship index
// and format sets.
-func (f *File) addSheetDrawingChart(sheet, drawingXML string, width, height, rID int, formatSet *formatPicture) (err error) {
- width = int(float64(width) * formatSet.XScale)
- height = int(float64(height) * formatSet.YScale)
-
+func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *formatPicture) {
content, cNvPrID := f.drawingParser(drawingXML)
absoluteAnchor := xdrCellAnchor{
EditAs: formatSet.Positioning,
@@ -1269,7 +1266,7 @@ func (f *File) addSheetDrawingChart(sheet, drawingXML string, width, height, rID
}
content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
f.Drawings[drawingXML] = content
- return err
+ return
}
// deleteDrawing provides a function to delete chart graphic frame by given by
diff --git a/excelize.go b/excelize.go
index 3dd4311..520cbb7 100644
--- a/excelize.go
+++ b/excelize.go
@@ -156,6 +156,10 @@ func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) {
return
}
if xlsx = f.Sheet[name]; f.Sheet[name] == nil {
+ if strings.HasPrefix(name, "xl/chartsheets") {
+ err = fmt.Errorf("sheet %s is chart sheet", sheet)
+ return
+ }
xlsx = new(xlsxWorksheet)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
Decode(xlsx); err != nil && err != io.EOF {
@@ -227,9 +231,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
return rID
}
-// replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
+// replaceRelationshipsNameSpaceBytes provides a function to replace
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
-func replaceWorkSheetsRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
+func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
var oldXmlns = []byte(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
var newXmlns = []byte(templateNamespaceIDMap)
contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1)
diff --git a/rows.go b/rows.go
index e00a627..76dd0f0 100644
--- a/rows.go
+++ b/rows.go
@@ -174,7 +174,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
if f.Sheet[name] != nil {
// flush data
output, _ := xml.Marshal(f.Sheet[name])
- f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
+ f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
}
var (
err error
diff --git a/sheet.go b/sheet.go
index 11f56d9..6ddd629 100644
--- a/sheet.go
+++ b/sheet.go
@@ -119,7 +119,7 @@ func (f *File) workSheetWriter() {
f.Sheet[p].SheetData.Row[k].C = trimCell(v.C)
}
output, _ := xml.Marshal(sheet)
- f.saveFileList(p, replaceRelationshipsBytes(replaceWorkSheetsRelationshipsNameSpaceBytes(output)))
+ f.saveFileList(p, replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(output)))
ok := f.checked[p]
if ok {
delete(f.Sheet, p)
@@ -190,7 +190,7 @@ func (f *File) relsWriter() {
if rel != nil {
output, _ := xml.Marshal(rel)
if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") {
- output = replaceWorkSheetsRelationshipsNameSpaceBytes(output)
+ output = replaceRelationshipsNameSpaceBytes(output)
}
f.saveFileList(path, replaceRelationshipsBytes(output))
}
@@ -211,19 +211,6 @@ func replaceRelationshipsBytes(content []byte) []byte {
return bytes.Replace(content, oldXmlns, newXmlns, -1)
}
-// replaceRelationshipsNameSpaceBytes; Some tools that read XLSX files have
-// very strict requirements about the structure of the input XML. In
-// particular both Numbers on the Mac and SAS dislike inline XML namespace
-// declarations, or namespace prefixes that don't match the ones that Excel
-// itself uses. This is a problem because the Go XML library doesn't multiple
-// namespace declarations in a single element of a document. This function is
-// a horrible hack to fix that after the XML marshalling is completed.
-func replaceRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
- oldXmlns := []byte(`<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
- newXmlns := []byte(`<workbook` + templateNamespaceIDMap)
- return bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
-}
-
// SetActiveSheet provides function to set default active worksheet of XLSX by
// given index. Note that active index is different from the index returned by
// function GetSheetMap(). It should be greater than 0 and less than total
@@ -248,7 +235,11 @@ func (f *File) SetActiveSheet(index int) {
}
}
for idx, name := range f.GetSheetMap() {
- xlsx, _ := f.workSheetReader(name)
+ xlsx, err := f.workSheetReader(name)
+ if err != nil {
+ // Chartsheet
+ return
+ }
if xlsx.SheetViews == nil {
xlsx.SheetViews = &xlsxSheetViews{
SheetView: []xlsxSheetView{{WorkbookViewID: 0}},
@@ -370,8 +361,8 @@ func (f *File) getSheetMap() map[string]string {
// Construct a target XML as xl/worksheets/sheet%d by split path, compatible with different types of relative paths in workbook.xml.rels, for example: worksheets/sheet%d.xml and /xl/worksheets/sheet%d.xml
pathInfo := strings.Split(rel.Target, "/")
pathInfoLen := len(pathInfo)
- if pathInfoLen > 0 {
- maps[v.Name] = fmt.Sprintf("xl/worksheets/%s", pathInfo[pathInfoLen-1])
+ if pathInfoLen > 1 {
+ maps[v.Name] = fmt.Sprintf("xl/%s", strings.Join(pathInfo[pathInfoLen-2:], "/"))
}
}
}
@@ -420,7 +411,10 @@ func (f *File) DeleteSheet(name string) {
for _, rel := range wbRels.Relationships {
if rel.ID == sheet.ID {
sheetXML = fmt.Sprintf("xl/%s", rel.Target)
- rels = strings.Replace(fmt.Sprintf("xl/%s.rels", rel.Target), "xl/worksheets/", "xl/worksheets/_rels/", -1)
+ pathInfo := strings.Split(rel.Target, "/")
+ if len(pathInfo) == 2 {
+ rels = fmt.Sprintf("xl/%s/_rels/%s.rels", pathInfo[0], pathInfo[1])
+ }
}
}
}
@@ -430,6 +424,7 @@ func (f *File) DeleteSheet(name string) {
delete(f.sheetMap, sheetName)
delete(f.XLSX, sheetXML)
delete(f.XLSX, rels)
+ delete(f.Relationships, rels)
delete(f.Sheet, sheetXML)
f.SheetCount--
}
@@ -729,7 +724,7 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
if f.Sheet[name] != nil {
// flush data
output, _ := xml.Marshal(f.Sheet[name])
- f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
+ f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
}
return f.searchSheet(name, value, regSearch)
}
diff --git a/styles.go b/styles.go
index 8d8b464..9cf974e 100644
--- a/styles.go
+++ b/styles.go
@@ -1018,7 +1018,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
func (f *File) styleSheetWriter() {
if f.Styles != nil {
output, _ := xml.Marshal(f.Styles)
- f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpaceBytes(output))
+ f.saveFileList("xl/styles.xml", replaceRelationshipsNameSpaceBytes(output))
}
}