forked from p30928647/excelize
Resolve #451, support create chart sheet
This commit is contained in:
parent
a75c6f63be
commit
6afc468a02
1
LICENSE
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
chart.go
68
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) {
|
||||||
|
|
|
@ -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
drawing.go
63
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) {
|
||||||
|
|
23
excelize.go
23
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
|
// XML tags to self-closing for compatible Microsoft Office Excel 2007.
|
||||||
// Office Excel 2007.
|
func replaceWorkSheetsRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
|
||||||
func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
|
var oldXmlns = []byte(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||||
var oldXmlns = []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
var newXmlns = []byte(templateNamespaceIDMap)
|
||||||
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)
|
|
||||||
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
picture.go
16
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",
|
"chart": ContentTypeDrawingML,
|
||||||
"comments": "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
|
"chartsheet": ContentTypeSpreadSheetMLChartsheet,
|
||||||
"drawings": "application/vnd.openxmlformats-officedocument.drawing+xml",
|
"comments": ContentTypeSpreadSheetMLComments,
|
||||||
"table": "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml",
|
"drawings": ContentTypeDrawing,
|
||||||
"pivotTable": "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml",
|
"table": ContentTypeSpreadSheetMLTable,
|
||||||
"pivotCache": "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml",
|
"pivotTable": ContentTypeSpreadSheetMLPivotTable,
|
||||||
|
"pivotCache": ContentTypeSpreadSheetMLPivotCacheDefinition,
|
||||||
}
|
}
|
||||||
s, ok := setContentType[contentType]
|
s, ok := setContentType[contentType]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
16
sheet.go
16
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",
|
PartName: partName,
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
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.
|
// GetSheetMap provides a function to get worksheet and chartsheet name and
|
||||||
// For example:
|
// 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
|
// getSheetMap provides a function to get worksheet and chartsheet name and
|
||||||
// of XLSX.
|
// 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")
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -22,6 +22,7 @@ const (
|
||||||
SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
||||||
SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
||||||
SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
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"
|
SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
|
||||||
SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
|
SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
|
||||||
SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
|
SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
|
||||||
|
@ -47,6 +48,17 @@ const (
|
||||||
NameSpaceDublinCore = "http://purl.org/dc/elements/1.1/"
|
NameSpaceDublinCore = "http://purl.org/dc/elements/1.1/"
|
||||||
NameSpaceDublinCoreTerms = "http://purl.org/dc/terms/"
|
NameSpaceDublinCoreTerms = "http://purl.org/dc/terms/"
|
||||||
NameSpaceDublinCoreMetadataIntiative = "http://purl.org/dc/dcmitype/"
|
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,10 +262,18 @@ 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"`
|
XMLName xml.Name `xml:"xdr:wsDr"`
|
||||||
|
AbsoluteAnchor []*xdrCellAnchor `xml:"xdr:absoluteAnchor"`
|
||||||
OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
|
OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
|
||||||
TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
|
TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
|
||||||
A string `xml:"xmlns:a,attr,omitempty"`
|
A string `xml:"xmlns:a,attr,omitempty"`
|
||||||
|
|
|
@ -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
|
// http://schemas.openxmlformats.org/spreadsheetml/2006/main.
|
||||||
// not checked it for completeness - it does as much as I need.
|
|
||||||
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"`
|
||||||
|
|
Loading…
Reference in New Issue