From 95fdc10f68f8eabb158a85f9a37baf040cbf2443 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 28 Mar 2020 23:47:26 +0800 Subject: [PATCH] Resolve #451, support create chart sheet --- LICENSE | 1 - chart.go | 68 +++++++++++++++++++++++++++++++ chart_test.go | 23 ++++++++++- drawing.go | 63 +++++++++++++++++++++++++++++ excelize.go | 23 +++-------- picture.go | 16 ++++---- sheet.go | 16 ++++---- styles.go | 2 +- xmlChartSheet.go | 2 +- xmlDrawing.go | 101 ++++++++++++++++++++++++++++------------------- xmlWorksheet.go | 3 +- 11 files changed, 239 insertions(+), 79 deletions(-) diff --git a/LICENSE b/LICENSE index 51ec1fbebb..fe738b9be1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,6 @@ BSD 3-Clause License Copyright (c) 2016-2020, 360 Enterprise Security Group, Endpoint Security, Inc. -Copyright (c) 2011-2017, Geoffrey J. Teale All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/chart.go b/chart.go index 69c2c950bb..df196e9a8e 100644 --- a/chart.go +++ b/chart.go @@ -11,7 +11,9 @@ package excelize import ( "encoding/json" + "encoding/xml" "errors" + "fmt" "strconv" "strings" ) @@ -768,6 +770,72 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error { 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 // and cell name. func (f *File) DeleteChart(sheet, cell string) (err error) { diff --git a/chart_test.go b/chart_test.go index 98f3555ad7..351e6638a5 100644 --- a/chart_test.go +++ b/chart_test.go @@ -200,12 +200,31 @@ 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. + // 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. + // 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 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) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) diff --git a/drawing.go b/drawing.go index e51b6afcc9..8ca1f49e5b 100644 --- a/drawing.go +++ b/drawing.go @@ -38,6 +38,26 @@ func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawing 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 // given format sets. 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 } +// 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 // given coordinates and graphic type. func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err error) { diff --git a/excelize.go b/excelize.go index 795120d8ba..3dd43119ab 100644 --- a/excelize.go +++ b/excelize.go @@ -228,21 +228,10 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int { } // 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(``) - var newXmlns = []byte(``) - var newXmlns = []byte(``) + var newXmlns = []byte(templateNamespaceIDMap) contentMarshal = bytes.Replace(contentMarshal, oldXmlns, newXmlns, -1) return contentMarshal } @@ -354,13 +343,13 @@ func (f *File) setContentTypePartVBAProjectExtensions() { } for idx, o := range content.Overrides { 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 { content.Defaults = append(content.Defaults, xlsxDefault{ Extension: "bin", - ContentType: "application/vnd.ms-office.vbaProject", + ContentType: ContentTypeVBA, }) } } diff --git a/picture.go b/picture.go index 3e24ce3a8a..ddc048065b 100644 --- a/picture.go +++ b/picture.go @@ -354,7 +354,7 @@ func (f *File) setContentTypePartVMLExtensions() { if !vml { content.Defaults = append(content.Defaults, xlsxDefault{ 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{ "chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml", + "chartsheet": "/xl/chartsheets/sheet" + strconv.Itoa(index) + ".xml", "comments": "/xl/comments" + strconv.Itoa(index) + ".xml", "drawings": "/xl/drawings/drawing" + 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", } 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] if ok { diff --git a/sheet.go b/sheet.go index 08b0e96f3d..11f56d9103 100644 --- a/sheet.go +++ b/sheet.go @@ -50,7 +50,7 @@ func (f *File) NewSheet(name string) int { // Update docProps/app.xml f.setAppXML() // 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 f.setSheet(sheetID, name) // 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 // type of XLSX. -func (f *File) setContentTypes(index int) { +func (f *File) setContentTypes(partName, contentType string) { content := f.contentTypesReader() 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 } -// 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") // if err != nil { @@ -358,8 +358,8 @@ func (f *File) GetSheetMap() map[int]string { 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 { content := f.workbookReader() rels := f.relsReader("xl/_rels/workbook.xml.rels") diff --git a/styles.go b/styles.go index f2171bbc15..8d8b464293 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", replaceStyleRelationshipsNameSpaceBytes(output)) + f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpaceBytes(output)) } } diff --git a/xmlChartSheet.go b/xmlChartSheet.go index 3417eac0dd..fae5a16ea8 100644 --- a/xmlChartSheet.go +++ b/xmlChartSheet.go @@ -24,7 +24,7 @@ type xlsxChartsheet struct { PageMargins *xlsxPageMargins `xml:"pageMargins"` PageSetup []*xlsxPageSetUp `xml:"pageSetup"` HeaderFooter *xlsxHeaderFooter `xml:"headerFooter"` - Drawing []*xlsxDrawing `xml:"drawing"` + Drawing *xlsxDrawing `xml:"drawing"` DrawingHF []*xlsxDrawingHF `xml:"drawingHF"` Picture []*xlsxPicture `xml:"picture"` WebPublishItems []*xlsxInnerXML `xml:"webPublishItems"` diff --git a/xmlDrawing.go b/xmlDrawing.go index 2bad16a0bf..142121d213 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -13,40 +13,52 @@ import "encoding/xml" // Source relationship and namespace. 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 // ([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 @@ -240,6 +252,7 @@ type xdrClientData struct { // with cells and its extents are in EMU units. type xdrCellAnchor struct { EditAs string `xml:"editAs,attr,omitempty"` + Pos *xlsxPoint2D `xml:"xdr:pos"` From *xlsxFrom `xml:"xdr:from"` To *xlsxTo `xml:"xdr:to"` Ext *xlsxExt `xml:"xdr:ext"` @@ -249,15 +262,23 @@ type xdrCellAnchor struct { 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 // wsDr. 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. diff --git a/xmlWorksheet.go b/xmlWorksheet.go index aa33819c1f..316ffd783f 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -12,8 +12,7 @@ package excelize import "encoding/xml" // 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 { XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"` SheetPr *xlsxSheetPr `xml:"sheetPr"`