Explorar el Código

Resolve #451, support create chart sheet

xuri hace 5 años
padre
commit
6afc468a02
Se han modificado 11 ficheros con 239 adiciones y 79 borrados
  1. 0 1
      LICENSE
  2. 68 0
      chart.go
  3. 21 2
      chart_test.go
  4. 63 0
      drawing.go
  5. 6 17
      excelize.go
  6. 9 7
      picture.go
  7. 8 8
      sheet.go
  8. 1 1
      styles.go
  9. 1 1
      xmlChartSheet.go
  10. 61 40
      xmlDrawing.go
  11. 1 2
      xmlWorksheet.go

+ 0 - 1
LICENSE

@@ -1,7 +1,6 @@
 BSD 3-Clause License
 BSD 3-Clause License
 
 
 Copyright (c) 2016-2020, 360 Enterprise Security Group, Endpoint Security, Inc.
 Copyright (c) 2016-2020, 360 Enterprise Security Group, Endpoint Security, Inc.
-Copyright (c) 2011-2017, Geoffrey J. Teale
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use in source and binary forms, with or without
 Redistribution and use in source and binary forms, with or without

+ 68 - 0
chart.go

@@ -11,7 +11,9 @@ package excelize
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"encoding/xml"
 	"errors"
 	"errors"
+	"fmt"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
@@ -768,6 +770,72 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
 	return err
 	return err
 }
 }
 
 
+// AddChartSheet provides the method to create a chartsheet by given chart
+// format set (such as offset, scale, aspect ratio setting and print settings)
+// and properties set. In Excel a chartsheet is a worksheet that only contains
+// a chart.
+func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
+	// Check if the worksheet already exists
+	if f.GetSheetIndex(sheet) != 0 {
+		return errors.New("already existing name worksheet")
+	}
+	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)
+	}
+	if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
+		return errors.New("unsupported chart type " + formatSet.Type)
+	}
+	cs := xlsxChartsheet{
+		SheetViews: []*xlsxChartsheetViews{{
+			SheetView: []*xlsxChartsheetView{{ZoomScaleAttr: 100, ZoomToFitAttr: true}}},
+		},
+	}
+	wb := f.workbookReader()
+	sheetID := 0
+	for _, v := range wb.Sheets.Sheet {
+		if v.SheetID > sheetID {
+			sheetID = v.SheetID
+		}
+	}
+	sheetID++
+	path := "xl/chartsheets/sheet" + strconv.Itoa(sheetID) + ".xml"
+	f.sheetMap[trimSheetName(sheet)] = path
+	f.Sheet[path] = nil
+	drawingID := f.countDrawings() + 1
+	chartID := f.countCharts() + 1
+	drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
+	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.addChart(formatSet, comboCharts)
+	f.addContentTypePart(chartID, "chart")
+	f.addContentTypePart(sheetID, "chartsheet")
+	f.addContentTypePart(drawingID, "drawings")
+	// Update xl/_rels/workbook.xml.rels
+	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)))
+	return err
+}
+
 // DeleteChart provides a function to delete chart in XLSX by given worksheet
 // DeleteChart provides a function to delete chart in XLSX by given worksheet
 // and cell name.
 // and cell name.
 func (f *File) DeleteChart(sheet, cell string) (err error) {
 func (f *File) DeleteChart(sheet, cell string) (err error) {

+ 21 - 2
chart_test.go

@@ -200,12 +200,31 @@ func TestAddChart(t *testing.T) {
 	assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
 	assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
 	// Test with unsupported chart type
 	// Test with unsupported chart type
 	assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), "unsupported chart type unknown")
 	assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), "unsupported chart type unknown")
-	// Test add combo chart with invalid format set.
+	// 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")
 	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.
+	// 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")
 	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 TestAddChartSheet(t *testing.T) {
+	categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
+	values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
+	f := NewFile()
+	for k, v := range categories {
+		assert.NoError(t, f.SetCellValue("Sheet1", k, v))
+	}
+	for k, v := range values {
+		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 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")
+
+	assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChartSheet.xlsx")))
+}
+
 func TestDeleteChart(t *testing.T) {
 func TestDeleteChart(t *testing.T) {
 	f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
 	f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
 	assert.NoError(t, err)
 	assert.NoError(t, err)

+ 63 - 0
drawing.go

@@ -38,6 +38,26 @@ func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawing
 	return drawingID, drawingXML
 	return drawingID, drawingXML
 }
 }
 
 
+// prepareChartSheetDrawing provides a function to prepare drawing ID and XML
+// by given drawingID, worksheet name and default drawingXML.
+func (f *File) prepareChartSheetDrawing(xlsx *xlsxChartsheet, 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/chartsheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/chartsheets/") + ".rels"
+		rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+		xlsx.Drawing = &xlsxDrawing{
+			RID: "rId" + strconv.Itoa(rID),
+		}
+	}
+	return drawingID, drawingXML
+}
+
 // addChart provides a function to create chart as xl/charts/chart%d.xml by
 // addChart provides a function to create chart as xl/charts/chart%d.xml by
 // given format sets.
 // given format sets.
 func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
 func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
@@ -1209,6 +1229,49 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
 	return err
 	return err
 }
 }
 
 
+// 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)
+
+	content, cNvPrID := f.drawingParser(drawingXML)
+	absoluteAnchor := xdrCellAnchor{
+		EditAs: formatSet.Positioning,
+		Pos:    &xlsxPoint2D{},
+		Ext:    &xlsxExt{},
+	}
+
+	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)
+	absoluteAnchor.GraphicFrame = string(graphic)
+	absoluteAnchor.ClientData = &xdrClientData{
+		FLocksWithSheet:  formatSet.FLocksWithSheet,
+		FPrintsWithSheet: formatSet.FPrintsWithSheet,
+	}
+	content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
+	f.Drawings[drawingXML] = content
+	return err
+}
+
 // deleteDrawing provides a function to delete chart graphic frame by given by
 // deleteDrawing provides a function to delete chart graphic frame by given by
 // given coordinates and graphic type.
 // given coordinates and graphic type.
 func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err error) {
 func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err error) {

+ 6 - 17
excelize.go

@@ -228,21 +228,10 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
 }
 }
 
 
 // replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
 // replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
-// xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft
-// Office Excel 2007.
-func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
-	var oldXmlns = []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
-	var newXmlns = []byte(`<worksheet` + templateNamespaceIDMap)
-	workbookMarshal = bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
-	return workbookMarshal
-}
-
-// replaceStyleRelationshipsNameSpaceBytes provides a function to replace
-// xl/styles.xml XML tags to self-closing for compatible Microsoft Office
-// Excel 2007.
-func replaceStyleRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
-	var oldXmlns = []byte(`<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
-	var newXmlns = []byte(`<styleSheet` + templateNamespaceIDMap)
+// XML tags to self-closing for compatible Microsoft Office Excel 2007.
+func replaceWorkSheetsRelationshipsNameSpaceBytes(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)
 	contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1)
 	return contentMarshal
 	return contentMarshal
 }
 }
@@ -354,13 +343,13 @@ func (f *File) setContentTypePartVBAProjectExtensions() {
 	}
 	}
 	for idx, o := range content.Overrides {
 	for idx, o := range content.Overrides {
 		if o.PartName == "/xl/workbook.xml" {
 		if o.PartName == "/xl/workbook.xml" {
-			content.Overrides[idx].ContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"
+			content.Overrides[idx].ContentType = ContentTypeMacro
 		}
 		}
 	}
 	}
 	if !ok {
 	if !ok {
 		content.Defaults = append(content.Defaults, xlsxDefault{
 		content.Defaults = append(content.Defaults, xlsxDefault{
 			Extension:   "bin",
 			Extension:   "bin",
-			ContentType: "application/vnd.ms-office.vbaProject",
+			ContentType: ContentTypeVBA,
 		})
 		})
 	}
 	}
 }
 }

+ 9 - 7
picture.go

@@ -354,7 +354,7 @@ func (f *File) setContentTypePartVMLExtensions() {
 	if !vml {
 	if !vml {
 		content.Defaults = append(content.Defaults, xlsxDefault{
 		content.Defaults = append(content.Defaults, xlsxDefault{
 			Extension:   "vml",
 			Extension:   "vml",
-			ContentType: "application/vnd.openxmlformats-officedocument.vmlDrawing",
+			ContentType: ContentTypeVML,
 		})
 		})
 	}
 	}
 }
 }
@@ -368,6 +368,7 @@ func (f *File) addContentTypePart(index int, contentType string) {
 	}
 	}
 	partNames := map[string]string{
 	partNames := map[string]string{
 		"chart":      "/xl/charts/chart" + strconv.Itoa(index) + ".xml",
 		"chart":      "/xl/charts/chart" + strconv.Itoa(index) + ".xml",
+		"chartsheet": "/xl/chartsheets/sheet" + strconv.Itoa(index) + ".xml",
 		"comments":   "/xl/comments" + strconv.Itoa(index) + ".xml",
 		"comments":   "/xl/comments" + strconv.Itoa(index) + ".xml",
 		"drawings":   "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml",
 		"drawings":   "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml",
 		"table":      "/xl/tables/table" + strconv.Itoa(index) + ".xml",
 		"table":      "/xl/tables/table" + strconv.Itoa(index) + ".xml",
@@ -375,12 +376,13 @@ func (f *File) addContentTypePart(index int, contentType string) {
 		"pivotCache": "/xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(index) + ".xml",
 		"pivotCache": "/xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(index) + ".xml",
 	}
 	}
 	contentTypes := map[string]string{
 	contentTypes := map[string]string{
-		"chart":      "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
-		"comments":   "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
-		"drawings":   "application/vnd.openxmlformats-officedocument.drawing+xml",
-		"table":      "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml",
-		"pivotTable": "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml",
-		"pivotCache": "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml",
+		"chart":      ContentTypeDrawingML,
+		"chartsheet": ContentTypeSpreadSheetMLChartsheet,
+		"comments":   ContentTypeSpreadSheetMLComments,
+		"drawings":   ContentTypeDrawing,
+		"table":      ContentTypeSpreadSheetMLTable,
+		"pivotTable": ContentTypeSpreadSheetMLPivotTable,
+		"pivotCache": ContentTypeSpreadSheetMLPivotCacheDefinition,
 	}
 	}
 	s, ok := setContentType[contentType]
 	s, ok := setContentType[contentType]
 	if ok {
 	if ok {

+ 8 - 8
sheet.go

@@ -50,7 +50,7 @@ func (f *File) NewSheet(name string) int {
 	// Update docProps/app.xml
 	// Update docProps/app.xml
 	f.setAppXML()
 	f.setAppXML()
 	// Update [Content_Types].xml
 	// Update [Content_Types].xml
-	f.setContentTypes(sheetID)
+	f.setContentTypes("/xl/worksheets/sheet"+strconv.Itoa(sheetID)+".xml", ContentTypeSpreadSheetMLWorksheet)
 	// Create new sheet /xl/worksheets/sheet%d.xml
 	// Create new sheet /xl/worksheets/sheet%d.xml
 	f.setSheet(sheetID, name)
 	f.setSheet(sheetID, name)
 	// Update xl/_rels/workbook.xml.rels
 	// Update xl/_rels/workbook.xml.rels
@@ -151,11 +151,11 @@ func trimCell(column []xlsxC) []xlsxC {
 
 
 // setContentTypes provides a function to read and update property of contents
 // setContentTypes provides a function to read and update property of contents
 // type of XLSX.
 // type of XLSX.
-func (f *File) setContentTypes(index int) {
+func (f *File) setContentTypes(partName, contentType string) {
 	content := f.contentTypesReader()
 	content := f.contentTypesReader()
 	content.Overrides = append(content.Overrides, xlsxOverride{
 	content.Overrides = append(content.Overrides, xlsxOverride{
-		PartName:    "/xl/worksheets/sheet" + strconv.Itoa(index) + ".xml",
-		ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
+		PartName:    partName,
+		ContentType: contentType,
 	})
 	})
 }
 }
 
 
@@ -336,8 +336,8 @@ func (f *File) GetSheetIndex(name string) int {
 	return 0
 	return 0
 }
 }
 
 
-// GetSheetMap provides a function to get worksheet name and index map of XLSX.
-// For example:
+// GetSheetMap provides a function to get worksheet and chartsheet name and
+// index map of XLSX. For example:
 //
 //
 //    f, err := excelize.OpenFile("Book1.xlsx")
 //    f, err := excelize.OpenFile("Book1.xlsx")
 //    if err != nil {
 //    if err != nil {
@@ -358,8 +358,8 @@ func (f *File) GetSheetMap() map[int]string {
 	return sheetMap
 	return sheetMap
 }
 }
 
 
-// getSheetMap provides a function to get worksheet name and XML file path map
-// of XLSX.
+// getSheetMap provides a function to get worksheet and chartsheet name and
+// XML file path map of XLSX.
 func (f *File) getSheetMap() map[string]string {
 func (f *File) getSheetMap() map[string]string {
 	content := f.workbookReader()
 	content := f.workbookReader()
 	rels := f.relsReader("xl/_rels/workbook.xml.rels")
 	rels := f.relsReader("xl/_rels/workbook.xml.rels")

+ 1 - 1
styles.go

@@ -1018,7 +1018,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
 func (f *File) styleSheetWriter() {
 func (f *File) styleSheetWriter() {
 	if f.Styles != nil {
 	if f.Styles != nil {
 		output, _ := xml.Marshal(f.Styles)
 		output, _ := xml.Marshal(f.Styles)
-		f.saveFileList("xl/styles.xml", replaceStyleRelationshipsNameSpaceBytes(output))
+		f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpaceBytes(output))
 	}
 	}
 }
 }
 
 

+ 1 - 1
xmlChartSheet.go

@@ -24,7 +24,7 @@ type xlsxChartsheet struct {
 	PageMargins      *xlsxPageMargins             `xml:"pageMargins"`
 	PageMargins      *xlsxPageMargins             `xml:"pageMargins"`
 	PageSetup        []*xlsxPageSetUp             `xml:"pageSetup"`
 	PageSetup        []*xlsxPageSetUp             `xml:"pageSetup"`
 	HeaderFooter     *xlsxHeaderFooter            `xml:"headerFooter"`
 	HeaderFooter     *xlsxHeaderFooter            `xml:"headerFooter"`
-	Drawing          []*xlsxDrawing               `xml:"drawing"`
+	Drawing          *xlsxDrawing                 `xml:"drawing"`
 	DrawingHF        []*xlsxDrawingHF             `xml:"drawingHF"`
 	DrawingHF        []*xlsxDrawingHF             `xml:"drawingHF"`
 	Picture          []*xlsxPicture               `xml:"picture"`
 	Picture          []*xlsxPicture               `xml:"picture"`
 	WebPublishItems  []*xlsxInnerXML              `xml:"webPublishItems"`
 	WebPublishItems  []*xlsxInnerXML              `xml:"webPublishItems"`

+ 61 - 40
xmlDrawing.go

@@ -13,40 +13,52 @@ import "encoding/xml"
 
 
 // Source relationship and namespace.
 // Source relationship and namespace.
 const (
 const (
-	SourceRelationship                   = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
-	SourceRelationshipChart              = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
-	SourceRelationshipComments           = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
-	SourceRelationshipImage              = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
-	SourceRelationshipTable              = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
-	SourceRelationshipDrawingML          = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
-	SourceRelationshipDrawingVML         = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
-	SourceRelationshipHyperLink          = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
-	SourceRelationshipWorkSheet          = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
-	SourceRelationshipPivotTable         = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
-	SourceRelationshipPivotCache         = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
-	SourceRelationshipVBAProject         = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
-	SourceRelationshipChart201506        = "http://schemas.microsoft.com/office/drawing/2015/06/chart"
-	SourceRelationshipChart20070802      = "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"
-	SourceRelationshipChart2014          = "http://schemas.microsoft.com/office/drawing/2014/chart"
-	SourceRelationshipCompatibility      = "http://schemas.openxmlformats.org/markup-compatibility/2006"
-	NameSpaceDrawingML                   = "http://schemas.openxmlformats.org/drawingml/2006/main"
-	NameSpaceDrawingMLChart              = "http://schemas.openxmlformats.org/drawingml/2006/chart"
-	NameSpaceDrawingMLSpreadSheet        = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
-	NameSpaceSpreadSheet                 = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
-	NameSpaceSpreadSheetX14              = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
-	NameSpaceSpreadSheetX15              = "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"
-	NameSpaceSpreadSheetExcel2006Main    = "http://schemas.microsoft.com/office/excel/2006/main"
-	NameSpaceMacExcel2008Main            = "http://schemas.microsoft.com/office/mac/excel/2008/main"
-	NameSpaceXML                         = "http://www.w3.org/XML/1998/namespace"
-	NameSpaceXMLSchemaInstance           = "http://www.w3.org/2001/XMLSchema-instance"
-	StrictSourceRelationship             = "http://purl.oclc.org/ooxml/officeDocument/relationships"
-	StrictSourceRelationshipChart        = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart"
-	StrictSourceRelationshipComments     = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments"
-	StrictSourceRelationshipImage        = "http://purl.oclc.org/ooxml/officeDocument/relationships/image"
-	StrictNameSpaceSpreadSheet           = "http://purl.oclc.org/ooxml/spreadsheetml/main"
-	NameSpaceDublinCore                  = "http://purl.org/dc/elements/1.1/"
-	NameSpaceDublinCoreTerms             = "http://purl.org/dc/terms/"
-	NameSpaceDublinCoreMetadataIntiative = "http://purl.org/dc/dcmitype/"
+	SourceRelationship                           = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
+	SourceRelationshipChart                      = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
+	SourceRelationshipComments                   = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
+	SourceRelationshipImage                      = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
+	SourceRelationshipTable                      = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
+	SourceRelationshipDrawingML                  = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
+	SourceRelationshipDrawingVML                 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
+	SourceRelationshipHyperLink                  = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
+	SourceRelationshipWorkSheet                  = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
+	SourceRelationshipChartsheet                 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"
+	SourceRelationshipPivotTable                 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
+	SourceRelationshipPivotCache                 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
+	SourceRelationshipVBAProject                 = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
+	SourceRelationshipChart201506                = "http://schemas.microsoft.com/office/drawing/2015/06/chart"
+	SourceRelationshipChart20070802              = "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"
+	SourceRelationshipChart2014                  = "http://schemas.microsoft.com/office/drawing/2014/chart"
+	SourceRelationshipCompatibility              = "http://schemas.openxmlformats.org/markup-compatibility/2006"
+	NameSpaceDrawingML                           = "http://schemas.openxmlformats.org/drawingml/2006/main"
+	NameSpaceDrawingMLChart                      = "http://schemas.openxmlformats.org/drawingml/2006/chart"
+	NameSpaceDrawingMLSpreadSheet                = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
+	NameSpaceSpreadSheet                         = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+	NameSpaceSpreadSheetX14                      = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
+	NameSpaceSpreadSheetX15                      = "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"
+	NameSpaceSpreadSheetExcel2006Main            = "http://schemas.microsoft.com/office/excel/2006/main"
+	NameSpaceMacExcel2008Main                    = "http://schemas.microsoft.com/office/mac/excel/2008/main"
+	NameSpaceXML                                 = "http://www.w3.org/XML/1998/namespace"
+	NameSpaceXMLSchemaInstance                   = "http://www.w3.org/2001/XMLSchema-instance"
+	StrictSourceRelationship                     = "http://purl.oclc.org/ooxml/officeDocument/relationships"
+	StrictSourceRelationshipChart                = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart"
+	StrictSourceRelationshipComments             = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments"
+	StrictSourceRelationshipImage                = "http://purl.oclc.org/ooxml/officeDocument/relationships/image"
+	StrictNameSpaceSpreadSheet                   = "http://purl.oclc.org/ooxml/spreadsheetml/main"
+	NameSpaceDublinCore                          = "http://purl.org/dc/elements/1.1/"
+	NameSpaceDublinCoreTerms                     = "http://purl.org/dc/terms/"
+	NameSpaceDublinCoreMetadataIntiative         = "http://purl.org/dc/dcmitype/"
+	ContentTypeDrawing                           = "application/vnd.openxmlformats-officedocument.drawing+xml"
+	ContentTypeDrawingML                         = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
+	ContentTypeMacro                             = "application/vnd.ms-excel.sheet.macroEnabled.main+xml"
+	ContentTypeSpreadSheetMLChartsheet           = "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"
+	ContentTypeSpreadSheetMLComments             = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
+	ContentTypeSpreadSheetMLPivotCacheDefinition = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"
+	ContentTypeSpreadSheetMLPivotTable           = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"
+	ContentTypeSpreadSheetMLTable                = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
+	ContentTypeSpreadSheetMLWorksheet            = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
+	ContentTypeVBA                               = "application/vnd.ms-office.vbaProject"
+	ContentTypeVML                               = "application/vnd.openxmlformats-officedocument.vmlDrawing"
 	// ExtURIConditionalFormattings is the extLst child element
 	// ExtURIConditionalFormattings is the extLst child element
 	// ([ISO/IEC29500-1:2016] section 18.2.10) of the worksheet element
 	// ([ISO/IEC29500-1:2016] section 18.2.10) of the worksheet element
 	// ([ISO/IEC29500-1:2016] section 18.3.1.99) is extended by the addition of
 	// ([ISO/IEC29500-1:2016] section 18.3.1.99) is extended by the addition of
@@ -240,6 +252,7 @@ type xdrClientData struct {
 // with cells and its extents are in EMU units.
 // with cells and its extents are in EMU units.
 type xdrCellAnchor struct {
 type xdrCellAnchor struct {
 	EditAs       string         `xml:"editAs,attr,omitempty"`
 	EditAs       string         `xml:"editAs,attr,omitempty"`
+	Pos          *xlsxPoint2D   `xml:"xdr:pos"`
 	From         *xlsxFrom      `xml:"xdr:from"`
 	From         *xlsxFrom      `xml:"xdr:from"`
 	To           *xlsxTo        `xml:"xdr:to"`
 	To           *xlsxTo        `xml:"xdr:to"`
 	Ext          *xlsxExt       `xml:"xdr:ext"`
 	Ext          *xlsxExt       `xml:"xdr:ext"`
@@ -249,15 +262,23 @@ type xdrCellAnchor struct {
 	ClientData   *xdrClientData `xml:"xdr:clientData"`
 	ClientData   *xdrClientData `xml:"xdr:clientData"`
 }
 }
 
 
+// xlsxPoint2D describes the position of a drawing element within a spreadsheet.
+type xlsxPoint2D struct {
+	XMLName xml.Name `xml:"xdr:pos"`
+	X       int      `xml:"x,attr"`
+	Y       int      `xml:"y,attr"`
+}
+
 // xlsxWsDr directly maps the root element for a part of this content type shall
 // xlsxWsDr directly maps the root element for a part of this content type shall
 // wsDr.
 // wsDr.
 type xlsxWsDr struct {
 type xlsxWsDr struct {
-	XMLName       xml.Name         `xml:"xdr:wsDr"`
-	OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
-	TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
-	A             string           `xml:"xmlns:a,attr,omitempty"`
-	Xdr           string           `xml:"xmlns:xdr,attr,omitempty"`
-	R             string           `xml:"xmlns:r,attr,omitempty"`
+	XMLName        xml.Name         `xml:"xdr:wsDr"`
+	AbsoluteAnchor []*xdrCellAnchor `xml:"xdr:absoluteAnchor"`
+	OneCellAnchor  []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
+	TwoCellAnchor  []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
+	A              string           `xml:"xmlns:a,attr,omitempty"`
+	Xdr            string           `xml:"xmlns:xdr,attr,omitempty"`
+	R              string           `xml:"xmlns:r,attr,omitempty"`
 }
 }
 
 
 // xlsxGraphicFrame (Graphic Frame) directly maps the xdr:graphicFrame element.
 // xlsxGraphicFrame (Graphic Frame) directly maps the xdr:graphicFrame element.

+ 1 - 2
xmlWorksheet.go

@@ -12,8 +12,7 @@ package excelize
 import "encoding/xml"
 import "encoding/xml"
 
 
 // xlsxWorksheet directly maps the worksheet element in the namespace
 // xlsxWorksheet directly maps the worksheet element in the namespace
-// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
-// not checked it for completeness - it does as much as I need.
+// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
 type xlsxWorksheet struct {
 type xlsxWorksheet struct {
 	XMLName               xml.Name                     `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
 	XMLName               xml.Name                     `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
 	SheetPr               *xlsxSheetPr                 `xml:"sheetPr"`
 	SheetPr               *xlsxSheetPr                 `xml:"sheetPr"`