Remove internal error log print, throw XML deserialize error
This commit is contained in:
parent
bd5dd17673
commit
ac564afa56
|
@ -54,7 +54,10 @@ func (f *File) deleteCalcChain(index int, cell string) error {
|
||||||
if len(calc.C) == 0 {
|
if len(calc.C) == 0 {
|
||||||
f.CalcChain = nil
|
f.CalcChain = nil
|
||||||
f.Pkg.Delete(defaultXMLPathCalcChain)
|
f.Pkg.Delete(defaultXMLPathCalcChain)
|
||||||
content := f.contentTypesReader()
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
for k, v := range content.Overrides {
|
for k, v := range content.Overrides {
|
||||||
|
|
|
@ -33,7 +33,14 @@ func TestDeleteCalcChain(t *testing.T) {
|
||||||
|
|
||||||
formulaType, ref := STCellFormulaTypeShared, "C1:C5"
|
formulaType, ref := STCellFormulaTypeShared, "C1:C5"
|
||||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", "=A1+B1", FormulaOpts{Ref: &ref, Type: &formulaType}))
|
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", "=A1+B1", FormulaOpts{Ref: &ref, Type: &formulaType}))
|
||||||
|
|
||||||
|
// Test delete calculation chain with unsupported charset calculation chain.
|
||||||
f.CalcChain = nil
|
f.CalcChain = nil
|
||||||
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.SetCellValue("Sheet1", "C1", true), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.SetCellValue("Sheet1", "C1", true), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
||||||
|
// Test delete calculation chain with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.deleteCalcChain(1, "A1"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
13
cell.go
13
cell.go
|
@ -241,11 +241,14 @@ func (f *File) setCellTimeFunc(sheet, cell string, value time.Time) error {
|
||||||
ws.Lock()
|
ws.Lock()
|
||||||
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
c.S = f.prepareCellStyle(ws, col, row, c.S)
|
||||||
ws.Unlock()
|
ws.Unlock()
|
||||||
date1904, wb := false, f.workbookReader()
|
var date1904, isNum bool
|
||||||
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if wb != nil && wb.WorkbookPr != nil {
|
if wb != nil && wb.WorkbookPr != nil {
|
||||||
date1904 = wb.WorkbookPr.Date1904
|
date1904 = wb.WorkbookPr.Date1904
|
||||||
}
|
}
|
||||||
var isNum bool
|
|
||||||
if isNum, err = c.setCellTime(value, date1904); err != nil {
|
if isNum, err = c.setCellTime(value, date1904); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1320,7 +1323,11 @@ func (f *File) formattedValue(s int, v string, raw bool) (string, error) {
|
||||||
if styleSheet.CellXfs.Xf[s].NumFmtID != nil {
|
if styleSheet.CellXfs.Xf[s].NumFmtID != nil {
|
||||||
numFmtID = *styleSheet.CellXfs.Xf[s].NumFmtID
|
numFmtID = *styleSheet.CellXfs.Xf[s].NumFmtID
|
||||||
}
|
}
|
||||||
date1904, wb := false, f.workbookReader()
|
date1904 := false
|
||||||
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
if wb != nil && wb.WorkbookPr != nil {
|
if wb != nil && wb.WorkbookPr != nil {
|
||||||
date1904 = wb.WorkbookPr.Date1904
|
date1904 = wb.WorkbookPr.Date1904
|
||||||
}
|
}
|
||||||
|
|
16
cell_test.go
16
cell_test.go
|
@ -173,7 +173,7 @@ func TestSetCellValue(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Now().UTC()), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Now().UTC()), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Duration(1e13)), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Duration(1e13)), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
// Test set cell value with column and row style inherit
|
// Test set cell value with column and row style inherit.
|
||||||
style1, err := f.NewStyle(&Style{NumFmt: 2})
|
style1, err := f.NewStyle(&Style{NumFmt: 2})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
style2, err := f.NewStyle(&Style{NumFmt: 9})
|
style2, err := f.NewStyle(&Style{NumFmt: 9})
|
||||||
|
@ -189,10 +189,14 @@ func TestSetCellValue(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "0.50", B2)
|
assert.Equal(t, "0.50", B2)
|
||||||
|
|
||||||
// Test set cell value with unsupported charset shared strings table
|
// Test set cell value with unsupported charset shared strings table.
|
||||||
f.SharedStrings = nil
|
f.SharedStrings = nil
|
||||||
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", "A1"), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", "A1"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test set cell value with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", time.Now().UTC()), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetCellValues(t *testing.T) {
|
func TestSetCellValues(t *testing.T) {
|
||||||
|
@ -204,7 +208,7 @@ func TestSetCellValues(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, v, "12/31/10 00:00")
|
assert.Equal(t, v, "12/31/10 00:00")
|
||||||
|
|
||||||
// Test date value lower than min date supported by Excel
|
// Test date value lower than min date supported by Excel.
|
||||||
err = f.SetCellValue("Sheet1", "A1", time.Date(1600, time.December, 31, 0, 0, 0, 0, time.UTC))
|
err = f.SetCellValue("Sheet1", "A1", time.Date(1600, time.December, 31, 0, 0, 0, 0, time.UTC))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -782,6 +786,12 @@ func TestFormattedValue(t *testing.T) {
|
||||||
assert.Equal(t, "0_0", fn("0_0", "", false))
|
assert.Equal(t, "0_0", fn("0_0", "", false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test format value with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
_, err = f.formattedValue(1, "43528", false)
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
||||||
// Test format value with unsupported charset style sheet.
|
// Test format value with unsupported charset style sheet.
|
||||||
f.Styles = nil
|
f.Styles = nil
|
||||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||||
|
|
20
chart.go
20
chart.go
|
@ -927,8 +927,10 @@ func (f *File) AddChart(sheet, cell, opts string, combo ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addChart(options, comboCharts)
|
f.addChart(options, comboCharts)
|
||||||
f.addContentTypePart(chartID, "chart")
|
if err = f.addContentTypePart(chartID, "chart"); err != nil {
|
||||||
f.addContentTypePart(drawingID, "drawings")
|
return err
|
||||||
|
}
|
||||||
|
_ = f.addContentTypePart(drawingID, "drawings")
|
||||||
f.addSheetNameSpace(sheet, SourceRelationship)
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -952,7 +954,7 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f.SheetCount++
|
f.SheetCount++
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
sheetID := 0
|
sheetID := 0
|
||||||
for _, v := range wb.Sheets.Sheet {
|
for _, v := range wb.Sheets.Sheet {
|
||||||
if v.SheetID > sheetID {
|
if v.SheetID > sheetID {
|
||||||
|
@ -969,11 +971,15 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
|
||||||
f.prepareChartSheetDrawing(&cs, drawingID, sheet)
|
f.prepareChartSheetDrawing(&cs, drawingID, sheet)
|
||||||
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
||||||
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
|
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
|
||||||
f.addSheetDrawingChart(drawingXML, drawingRID, &options.Format)
|
if err = f.addSheetDrawingChart(drawingXML, drawingRID, &options.Format); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
f.addChart(options, comboCharts)
|
f.addChart(options, comboCharts)
|
||||||
f.addContentTypePart(chartID, "chart")
|
if err = f.addContentTypePart(chartID, "chart"); err != nil {
|
||||||
f.addContentTypePart(sheetID, "chartsheet")
|
return err
|
||||||
f.addContentTypePart(drawingID, "drawings")
|
}
|
||||||
|
_ = f.addContentTypePart(sheetID, "chartsheet")
|
||||||
|
_ = f.addContentTypePart(drawingID, "drawings")
|
||||||
// Update workbook.xml.rels
|
// Update workbook.xml.rels
|
||||||
rID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipChartsheet, fmt.Sprintf("/xl/chartsheets/sheet%d.xml", sheetID), "")
|
rID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipChartsheet, fmt.Sprintf("/xl/chartsheets/sheet%d.xml", sheetID), "")
|
||||||
// Update workbook.xml
|
// Update workbook.xml
|
||||||
|
|
|
@ -226,6 +226,11 @@ func TestAddChart(t *testing.T) {
|
||||||
// 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")
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test add chart with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}],"title":{"name":"2D Column Chart"}}`), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddChartSheet(t *testing.T) {
|
func TestAddChartSheet(t *testing.T) {
|
||||||
|
@ -259,6 +264,14 @@ func TestAddChartSheet(t *testing.T) {
|
||||||
assert.NoError(t, f.UpdateLinkedValue())
|
assert.NoError(t, f.UpdateLinkedValue())
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChartSheet.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChartSheet.xlsx")))
|
||||||
|
// Test add chart sheet with unsupported charset drawing XML.
|
||||||
|
f.Pkg.Store("xl/drawings/drawing4.xml", MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddChartSheet("Chart3", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}],"title":{"name":"2D Column Chart"}}`), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test add chart sheet with unsupported charset content types.
|
||||||
|
f = NewFile()
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddChartSheet("Chart4", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}],"title":{"name":"2D Column Chart"}}`), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteChart(t *testing.T) {
|
func TestDeleteChart(t *testing.T) {
|
||||||
|
|
|
@ -69,8 +69,8 @@ func (f *File) GetComments() (map[string][]Comment, error) {
|
||||||
// getSheetComments provides the method to get the target comment reference by
|
// getSheetComments provides the method to get the target comment reference by
|
||||||
// given worksheet file path.
|
// given worksheet file path.
|
||||||
func (f *File) getSheetComments(sheetFile string) string {
|
func (f *File) getSheetComments(sheetFile string) string {
|
||||||
rels := "xl/worksheets/_rels/" + sheetFile + ".rels"
|
rels, _ := f.relsReader("xl/worksheets/_rels/" + sheetFile + ".rels")
|
||||||
if sheetRels := f.relsReader(rels); sheetRels != nil {
|
if sheetRels := rels; sheetRels != nil {
|
||||||
sheetRels.Lock()
|
sheetRels.Lock()
|
||||||
defer sheetRels.Unlock()
|
defer sheetRels.Unlock()
|
||||||
for _, v := range sheetRels.Relationships {
|
for _, v := range sheetRels.Relationships {
|
||||||
|
@ -135,8 +135,7 @@ func (f *File) AddComment(sheet string, comment Comment) error {
|
||||||
if err = f.addComment(commentsXML, comment); err != nil {
|
if err = f.addComment(commentsXML, comment); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addContentTypePart(commentID, "comments")
|
return f.addContentTypePart(commentID, "comments")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteComment provides the method to delete comment in a sheet by given
|
// DeleteComment provides the method to delete comment in a sheet by given
|
||||||
|
|
|
@ -112,7 +112,8 @@ func TestDecodeVMLDrawingReader(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
path := "xl/drawings/vmlDrawing1.xml"
|
path := "xl/drawings/vmlDrawing1.xml"
|
||||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||||
f.decodeVMLDrawingReader(path)
|
_, err := f.decodeVMLDrawingReader(path)
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommentsReader(t *testing.T) {
|
func TestCommentsReader(t *testing.T) {
|
||||||
|
|
36
excelize.go
36
excelize.go
|
@ -181,8 +181,10 @@ func OpenReader(r io.Reader, opts ...Options) (*File, error) {
|
||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
f.sheetMap = f.getSheetMap()
|
f.sheetMap = f.getSheetMap()
|
||||||
f.Styles, err = f.stylesReader()
|
if f.Styles, err = f.stylesReader(); err != nil {
|
||||||
f.Theme = f.themeReader()
|
return f, err
|
||||||
|
}
|
||||||
|
f.Theme, err = f.themeReader()
|
||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +337,7 @@ func checkSheetR0(ws *xlsxWorksheet, sheetData *xlsxSheetData, r0 *xlsxRow) {
|
||||||
// setRels provides a function to set relationships by given relationship ID,
|
// setRels provides a function to set relationships by given relationship ID,
|
||||||
// XML path, relationship type, target and target mode.
|
// XML path, relationship type, target and target mode.
|
||||||
func (f *File) setRels(rID, relPath, relType, target, targetMode string) int {
|
func (f *File) setRels(rID, relPath, relType, target, targetMode string) int {
|
||||||
rels := f.relsReader(relPath)
|
rels, _ := f.relsReader(relPath)
|
||||||
if rels == nil || rID == "" {
|
if rels == nil || rID == "" {
|
||||||
return f.addRels(relPath, relType, target, targetMode)
|
return f.addRels(relPath, relType, target, targetMode)
|
||||||
}
|
}
|
||||||
|
@ -360,7 +362,7 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
|
||||||
uniqPart := map[string]string{
|
uniqPart := map[string]string{
|
||||||
SourceRelationshipSharedStrings: "/xl/sharedStrings.xml",
|
SourceRelationshipSharedStrings: "/xl/sharedStrings.xml",
|
||||||
}
|
}
|
||||||
rels := f.relsReader(relPath)
|
rels, _ := f.relsReader(relPath)
|
||||||
if rels == nil {
|
if rels == nil {
|
||||||
rels = &xlsxRelationships{}
|
rels = &xlsxRelationships{}
|
||||||
}
|
}
|
||||||
|
@ -418,7 +420,10 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
|
||||||
// </c>
|
// </c>
|
||||||
// </row>
|
// </row>
|
||||||
func (f *File) UpdateLinkedValue() error {
|
func (f *File) UpdateLinkedValue() error {
|
||||||
wb := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// recalculate formulas
|
// recalculate formulas
|
||||||
wb.CalcPr = nil
|
wb.CalcPr = nil
|
||||||
for _, name := range f.GetSheetList() {
|
for _, name := range f.GetSheetList() {
|
||||||
|
@ -465,12 +470,15 @@ func (f *File) AddVBAProject(bin string) error {
|
||||||
if path.Ext(bin) != ".bin" {
|
if path.Ext(bin) != ".bin" {
|
||||||
return ErrAddVBAProject
|
return ErrAddVBAProject
|
||||||
}
|
}
|
||||||
wb := f.relsReader(f.getWorkbookRelsPath())
|
rels, err := f.relsReader(f.getWorkbookRelsPath())
|
||||||
wb.Lock()
|
if err != nil {
|
||||||
defer wb.Unlock()
|
return err
|
||||||
|
}
|
||||||
|
rels.Lock()
|
||||||
|
defer rels.Unlock()
|
||||||
var rID int
|
var rID int
|
||||||
var ok bool
|
var ok bool
|
||||||
for _, rel := range wb.Relationships {
|
for _, rel := range rels.Relationships {
|
||||||
if rel.Target == "vbaProject.bin" && rel.Type == SourceRelationshipVBAProject {
|
if rel.Target == "vbaProject.bin" && rel.Type == SourceRelationshipVBAProject {
|
||||||
ok = true
|
ok = true
|
||||||
continue
|
continue
|
||||||
|
@ -482,7 +490,7 @@ func (f *File) AddVBAProject(bin string) error {
|
||||||
}
|
}
|
||||||
rID++
|
rID++
|
||||||
if !ok {
|
if !ok {
|
||||||
wb.Relationships = append(wb.Relationships, xlsxRelationship{
|
rels.Relationships = append(rels.Relationships, xlsxRelationship{
|
||||||
ID: "rId" + strconv.Itoa(rID),
|
ID: "rId" + strconv.Itoa(rID),
|
||||||
Target: "vbaProject.bin",
|
Target: "vbaProject.bin",
|
||||||
Type: SourceRelationshipVBAProject,
|
Type: SourceRelationshipVBAProject,
|
||||||
|
@ -495,9 +503,12 @@ func (f *File) AddVBAProject(bin string) error {
|
||||||
|
|
||||||
// setContentTypePartProjectExtensions provides a function to set the content
|
// setContentTypePartProjectExtensions provides a function to set the content
|
||||||
// type for relationship parts and the main document part.
|
// type for relationship parts and the main document part.
|
||||||
func (f *File) setContentTypePartProjectExtensions(contentType string) {
|
func (f *File) setContentTypePartProjectExtensions(contentType string) error {
|
||||||
var ok bool
|
var ok bool
|
||||||
content := f.contentTypesReader()
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
for _, v := range content.Defaults {
|
for _, v := range content.Defaults {
|
||||||
|
@ -516,4 +527,5 @@ func (f *File) setContentTypePartProjectExtensions(contentType string) {
|
||||||
ContentType: ContentTypeVBA,
|
ContentType: ContentTypeVBA,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,26 +219,31 @@ func TestOpenReader(t *testing.T) {
|
||||||
assert.EqualError(t, err, ErrWorkbookFileFormat.Error())
|
assert.EqualError(t, err, ErrWorkbookFileFormat.Error())
|
||||||
|
|
||||||
// Test open workbook with unsupported charset internal calculation chain.
|
// Test open workbook with unsupported charset internal calculation chain.
|
||||||
source, err := zip.OpenReader(filepath.Join("test", "Book1.xlsx"))
|
preset := func(filePath string) *bytes.Buffer {
|
||||||
assert.NoError(t, err)
|
source, err := zip.OpenReader(filepath.Join("test", "Book1.xlsx"))
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
zw := zip.NewWriter(buf)
|
|
||||||
for _, item := range source.File {
|
|
||||||
// The following statements can be simplified as zw.Copy(item) in go1.17
|
|
||||||
writer, err := zw.Create(item.Name)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
readerCloser, err := item.Open()
|
buf := new(bytes.Buffer)
|
||||||
|
zw := zip.NewWriter(buf)
|
||||||
|
for _, item := range source.File {
|
||||||
|
// The following statements can be simplified as zw.Copy(item) in go1.17
|
||||||
|
writer, err := zw.Create(item.Name)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
readerCloser, err := item.Open()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = io.Copy(writer, readerCloser)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
fi, err := zw.Create(filePath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = io.Copy(writer, readerCloser)
|
_, err = fi.Write(MacintoshCyrillicCharset)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, zw.Close())
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
for _, defaultXMLPath := range []string{defaultXMLPathCalcChain, defaultXMLPathStyles} {
|
||||||
|
_, err = OpenReader(preset(defaultXMLPath))
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
fi, err := zw.Create(defaultXMLPathCalcChain)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = fi.Write(MacintoshCyrillicCharset)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NoError(t, zw.Close())
|
|
||||||
_, err = OpenReader(buf)
|
|
||||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
|
||||||
|
|
||||||
// Test open spreadsheet with unzip size limit.
|
// Test open spreadsheet with unzip size limit.
|
||||||
_, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipSizeLimit: 100})
|
_, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipSizeLimit: 100})
|
||||||
|
@ -466,29 +471,16 @@ func TestGetCellHyperLink(t *testing.T) {
|
||||||
|
|
||||||
func TestSetSheetBackground(t *testing.T) {
|
func TestSetSheetBackground(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
assert.NoError(t, f.SetSheetBackground("Sheet2", filepath.Join("test", "images", "background.jpg")))
|
||||||
}
|
assert.NoError(t, f.SetSheetBackground("Sheet2", filepath.Join("test", "images", "background.jpg")))
|
||||||
|
|
||||||
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "images", "background.jpg"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "images", "background.jpg"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetBackground.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetBackground.xlsx")))
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetSheetBackgroundErrors(t *testing.T) {
|
func TestSetSheetBackgroundErrors(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "not_exists", "not_exists.png"))
|
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "not_exists", "not_exists.png"))
|
||||||
if assert.Error(t, err) {
|
if assert.Error(t, err) {
|
||||||
|
@ -497,7 +489,16 @@ func TestSetSheetBackgroundErrors(t *testing.T) {
|
||||||
|
|
||||||
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "Book1.xlsx"))
|
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "Book1.xlsx"))
|
||||||
assert.EqualError(t, err, ErrImgExt.Error())
|
assert.EqualError(t, err, ErrImgExt.Error())
|
||||||
|
// Test set sheet background on not exist worksheet.
|
||||||
|
err = f.SetSheetBackground("SheetN", filepath.Join("test", "images", "background.jpg"))
|
||||||
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test set sheet background with unsupported charset content types.
|
||||||
|
f = NewFile()
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.SetSheetBackground("Sheet1", filepath.Join("test", "images", "background.jpg")), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestWriteArrayFormula tests the extended options of SetCellFormula by writing an array function
|
// TestWriteArrayFormula tests the extended options of SetCellFormula by writing an array function
|
||||||
|
@ -1027,12 +1028,6 @@ func TestGetSheetComments(t *testing.T) {
|
||||||
assert.Equal(t, "", f.getSheetComments("sheet0"))
|
assert.Equal(t, "", f.getSheetComments("sheet0"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetSheetVisible(t *testing.T) {
|
|
||||||
f := NewFile()
|
|
||||||
f.WorkBook.Sheets.Sheet[0].Name = "SheetN"
|
|
||||||
assert.EqualError(t, f.SetSheetVisible("Sheet1", false), "sheet SheetN does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetActiveSheetIndex(t *testing.T) {
|
func TestGetActiveSheetIndex(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.WorkBook.BookViews = nil
|
f.WorkBook.BookViews = nil
|
||||||
|
@ -1334,6 +1329,10 @@ func TestAddVBAProject(t *testing.T) {
|
||||||
// Test add VBA project twice.
|
// Test add VBA project twice.
|
||||||
assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")))
|
assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddVBAProject.xlsm")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddVBAProject.xlsm")))
|
||||||
|
// Test add VBs with unsupported charset workbook relationships.
|
||||||
|
f.Relationships.Delete(defaultXMLPathWorkbookRels)
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContentTypesReader(t *testing.T) {
|
func TestContentTypesReader(t *testing.T) {
|
||||||
|
@ -1341,7 +1340,8 @@ func TestContentTypesReader(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
f.contentTypesReader()
|
_, err := f.contentTypesReader()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkbookReader(t *testing.T) {
|
func TestWorkbookReader(t *testing.T) {
|
||||||
|
@ -1349,7 +1349,8 @@ func TestWorkbookReader(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
f.workbookReader()
|
_, err := f.workbookReader()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkSheetReader(t *testing.T) {
|
func TestWorkSheetReader(t *testing.T) {
|
||||||
|
@ -1373,19 +1374,28 @@ func TestWorkSheetReader(t *testing.T) {
|
||||||
func TestRelsReader(t *testing.T) {
|
func TestRelsReader(t *testing.T) {
|
||||||
// Test unsupported charset.
|
// Test unsupported charset.
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
rels := "xl/_rels/workbook.xml.rels"
|
rels := defaultXMLPathWorkbookRels
|
||||||
f.Relationships.Store(rels, nil)
|
f.Relationships.Store(rels, nil)
|
||||||
f.Pkg.Store(rels, MacintoshCyrillicCharset)
|
f.Pkg.Store(rels, MacintoshCyrillicCharset)
|
||||||
f.relsReader(rels)
|
_, err := f.relsReader(rels)
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteSheetFromWorkbookRels(t *testing.T) {
|
func TestDeleteSheetFromWorkbookRels(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
rels := "xl/_rels/workbook.xml.rels"
|
rels := defaultXMLPathWorkbookRels
|
||||||
f.Relationships.Store(rels, nil)
|
f.Relationships.Store(rels, nil)
|
||||||
assert.Equal(t, f.deleteSheetFromWorkbookRels("rID"), "")
|
assert.Equal(t, f.deleteSheetFromWorkbookRels("rID"), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateLinkedValue(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test update lined value with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.UpdateLinkedValue(), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
func TestAttrValToInt(t *testing.T) {
|
func TestAttrValToInt(t *testing.T) {
|
||||||
_, err := attrValToInt("r", []xml.Attr{
|
_, err := attrValToInt("r", []xml.Attr{
|
||||||
{Name: xml.Name{Local: "r"}, Value: "s"},
|
{Name: xml.Name{Local: "r"}, Value: "s"},
|
||||||
|
|
15
file.go
15
file.go
|
@ -30,7 +30,7 @@ func NewFile() *File {
|
||||||
f.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
|
f.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
|
||||||
f.Pkg.Store(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
|
f.Pkg.Store(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
|
||||||
f.Pkg.Store(defaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))
|
f.Pkg.Store(defaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))
|
||||||
f.Pkg.Store("xl/_rels/workbook.xml.rels", []byte(xml.Header+templateWorkbookRels))
|
f.Pkg.Store(defaultXMLPathWorkbookRels, []byte(xml.Header+templateWorkbookRels))
|
||||||
f.Pkg.Store("xl/theme/theme1.xml", []byte(xml.Header+templateTheme))
|
f.Pkg.Store("xl/theme/theme1.xml", []byte(xml.Header+templateTheme))
|
||||||
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+templateSheet))
|
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+templateSheet))
|
||||||
f.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))
|
f.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))
|
||||||
|
@ -39,18 +39,19 @@ func NewFile() *File {
|
||||||
f.SheetCount = 1
|
f.SheetCount = 1
|
||||||
f.CalcChain, _ = f.calcChainReader()
|
f.CalcChain, _ = f.calcChainReader()
|
||||||
f.Comments = make(map[string]*xlsxComments)
|
f.Comments = make(map[string]*xlsxComments)
|
||||||
f.ContentTypes = f.contentTypesReader()
|
f.ContentTypes, _ = f.contentTypesReader()
|
||||||
f.Drawings = sync.Map{}
|
f.Drawings = sync.Map{}
|
||||||
f.Styles, _ = f.stylesReader()
|
f.Styles, _ = f.stylesReader()
|
||||||
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
|
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
|
||||||
f.VMLDrawing = make(map[string]*vmlDrawing)
|
f.VMLDrawing = make(map[string]*vmlDrawing)
|
||||||
f.WorkBook = f.workbookReader()
|
f.WorkBook, _ = f.workbookReader()
|
||||||
f.Relationships = sync.Map{}
|
f.Relationships = sync.Map{}
|
||||||
f.Relationships.Store("xl/_rels/workbook.xml.rels", f.relsReader("xl/_rels/workbook.xml.rels"))
|
rels, _ := f.relsReader(defaultXMLPathWorkbookRels)
|
||||||
|
f.Relationships.Store(defaultXMLPathWorkbookRels, rels)
|
||||||
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
|
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
|
||||||
ws, _ := f.workSheetReader("Sheet1")
|
ws, _ := f.workSheetReader("Sheet1")
|
||||||
f.Sheet.Store("xl/worksheets/sheet1.xml", ws)
|
f.Sheet.Store("xl/worksheets/sheet1.xml", ws)
|
||||||
f.Theme = f.themeReader()
|
f.Theme, _ = f.themeReader()
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +120,9 @@ func (f *File) WriteTo(w io.Writer, opts ...Options) (int64, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, ErrWorkbookFileFormat
|
return 0, ErrWorkbookFileFormat
|
||||||
}
|
}
|
||||||
f.setContentTypePartProjectExtensions(contentType)
|
if err := f.setContentTypePartProjectExtensions(contentType); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if f.options != nil && f.options.Password != "" {
|
if f.options != nil && f.options.Password != "" {
|
||||||
buf, err := f.WriteToBuffer()
|
buf, err := f.WriteToBuffer()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -79,6 +80,14 @@ func TestWriteTo(t *testing.T) {
|
||||||
_, err := f.WriteTo(bufio.NewWriter(&buf))
|
_, err := f.WriteTo(bufio.NewWriter(&buf))
|
||||||
assert.EqualError(t, err, ErrWorkbookFileFormat.Error())
|
assert.EqualError(t, err, ErrWorkbookFileFormat.Error())
|
||||||
}
|
}
|
||||||
|
// Test write with unsupported charset content types.
|
||||||
|
{
|
||||||
|
f, buf := NewFile(), bytes.Buffer{}
|
||||||
|
f.ContentTypes, f.Path = nil, filepath.Join("test", "TestWriteTo.xlsx")
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
_, err := f.WriteTo(bufio.NewWriter(&buf))
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClose(t *testing.T) {
|
func TestClose(t *testing.T) {
|
||||||
|
|
52
picture.go
52
picture.go
|
@ -187,7 +187,9 @@ func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, fi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addContentTypePart(drawingID, "drawings")
|
if err = f.addContentTypePart(drawingID, "drawings"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
f.addSheetNameSpace(sheet, SourceRelationship)
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -201,7 +203,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
|
||||||
name = strings.ToLower(sheet) + ".xml"
|
name = strings.ToLower(sheet) + ".xml"
|
||||||
}
|
}
|
||||||
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
||||||
sheetRels := f.relsReader(rels)
|
sheetRels, _ := f.relsReader(rels)
|
||||||
if sheetRels == nil {
|
if sheetRels == nil {
|
||||||
sheetRels = &xlsxRelationships{}
|
sheetRels = &xlsxRelationships{}
|
||||||
}
|
}
|
||||||
|
@ -235,11 +237,15 @@ func (f *File) addSheetDrawing(sheet string, rID int) {
|
||||||
|
|
||||||
// addSheetPicture provides a function to add picture element to
|
// addSheetPicture provides a function to add picture element to
|
||||||
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||||
func (f *File) addSheetPicture(sheet string, rID int) {
|
func (f *File) addSheetPicture(sheet string, rID int) error {
|
||||||
ws, _ := f.workSheetReader(sheet)
|
ws, err := f.workSheetReader(sheet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
ws.Picture = &xlsxPicture{
|
ws.Picture = &xlsxPicture{
|
||||||
RID: "rId" + strconv.Itoa(rID),
|
RID: "rId" + strconv.Itoa(rID),
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// countDrawings provides a function to get drawing files count storage in the
|
// countDrawings provides a function to get drawing files count storage in the
|
||||||
|
@ -378,12 +384,15 @@ func (f *File) addMedia(file []byte, ext string) string {
|
||||||
|
|
||||||
// setContentTypePartImageExtensions provides a function to set the content
|
// setContentTypePartImageExtensions provides a function to set the content
|
||||||
// type for relationship parts and the Main Document part.
|
// type for relationship parts and the Main Document part.
|
||||||
func (f *File) setContentTypePartImageExtensions() {
|
func (f *File) setContentTypePartImageExtensions() error {
|
||||||
imageTypes := map[string]string{
|
imageTypes := map[string]string{
|
||||||
"jpeg": "image/", "png": "image/", "gif": "image/", "svg": "image/", "tiff": "image/",
|
"jpeg": "image/", "png": "image/", "gif": "image/", "svg": "image/", "tiff": "image/",
|
||||||
"emf": "image/x-", "wmf": "image/x-", "emz": "image/x-", "wmz": "image/x-",
|
"emf": "image/x-", "wmf": "image/x-", "emz": "image/x-", "wmz": "image/x-",
|
||||||
}
|
}
|
||||||
content := f.contentTypesReader()
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
for _, file := range content.Defaults {
|
for _, file := range content.Defaults {
|
||||||
|
@ -395,13 +404,17 @@ func (f *File) setContentTypePartImageExtensions() {
|
||||||
ContentType: prefix + extension,
|
ContentType: prefix + extension,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// setContentTypePartVMLExtensions provides a function to set the content type
|
// setContentTypePartVMLExtensions provides a function to set the content type
|
||||||
// for relationship parts and the Main Document part.
|
// for relationship parts and the Main Document part.
|
||||||
func (f *File) setContentTypePartVMLExtensions() {
|
func (f *File) setContentTypePartVMLExtensions() error {
|
||||||
vml := false
|
var vml bool
|
||||||
content := f.contentTypesReader()
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
for _, v := range content.Defaults {
|
for _, v := range content.Defaults {
|
||||||
|
@ -415,12 +428,13 @@ func (f *File) setContentTypePartVMLExtensions() {
|
||||||
ContentType: ContentTypeVML,
|
ContentType: ContentTypeVML,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// addContentTypePart provides a function to add content type part
|
// addContentTypePart provides a function to add content type part
|
||||||
// relationships in the file [Content_Types].xml by given index.
|
// relationships in the file [Content_Types].xml by given index.
|
||||||
func (f *File) addContentTypePart(index int, contentType string) {
|
func (f *File) addContentTypePart(index int, contentType string) error {
|
||||||
setContentType := map[string]func(){
|
setContentType := map[string]func() error{
|
||||||
"comments": f.setContentTypePartVMLExtensions,
|
"comments": f.setContentTypePartVMLExtensions,
|
||||||
"drawings": f.setContentTypePartImageExtensions,
|
"drawings": f.setContentTypePartImageExtensions,
|
||||||
}
|
}
|
||||||
|
@ -446,20 +460,26 @@ func (f *File) addContentTypePart(index int, contentType string) {
|
||||||
}
|
}
|
||||||
s, ok := setContentType[contentType]
|
s, ok := setContentType[contentType]
|
||||||
if ok {
|
if ok {
|
||||||
s()
|
if err := s(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
content := f.contentTypesReader()
|
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
for _, v := range content.Overrides {
|
for _, v := range content.Overrides {
|
||||||
if v.PartName == partNames[contentType] {
|
if v.PartName == partNames[contentType] {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content.Overrides = append(content.Overrides, xlsxOverride{
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
||||||
PartName: partNames[contentType],
|
PartName: partNames[contentType],
|
||||||
ContentType: contentTypes[contentType],
|
ContentType: contentTypes[contentType],
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSheetRelationshipsTargetByID provides a function to get Target attribute
|
// getSheetRelationshipsTargetByID provides a function to get Target attribute
|
||||||
|
@ -471,7 +491,7 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
|
||||||
name = strings.ToLower(sheet) + ".xml"
|
name = strings.ToLower(sheet) + ".xml"
|
||||||
}
|
}
|
||||||
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
||||||
sheetRels := f.relsReader(rels)
|
sheetRels, _ := f.relsReader(rels)
|
||||||
if sheetRels == nil {
|
if sheetRels == nil {
|
||||||
sheetRels = &xlsxRelationships{}
|
sheetRels = &xlsxRelationships{}
|
||||||
}
|
}
|
||||||
|
@ -630,7 +650,7 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD
|
||||||
// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
|
// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
|
||||||
// relationship ID.
|
// relationship ID.
|
||||||
func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
|
func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
|
||||||
if drawingRels := f.relsReader(rels); drawingRels != nil {
|
if drawingRels, _ := f.relsReader(rels); drawingRels != nil {
|
||||||
drawingRels.Lock()
|
drawingRels.Lock()
|
||||||
defer drawingRels.Unlock()
|
defer drawingRels.Unlock()
|
||||||
for _, v := range drawingRels.Relationships {
|
for _, v := range drawingRels.Relationships {
|
||||||
|
|
|
@ -67,6 +67,12 @@ func TestAddPicture(t *testing.T) {
|
||||||
// Test write file to given path.
|
// Test write file to given path.
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture1.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture1.xlsx")))
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test add picture with unsupported charset content types.
|
||||||
|
f = NewFile()
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "Q1", "", "Excel Logo", ".png", file), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPictureErrors(t *testing.T) {
|
func TestAddPictureErrors(t *testing.T) {
|
||||||
|
@ -236,3 +242,27 @@ func TestDrawingResize(t *testing.T) {
|
||||||
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
|
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
|
||||||
assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetContentTypePartImageExtensions(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test set content type part image extensions with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.setContentTypePartImageExtensions(), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetContentTypePartVMLExtensions(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test set content type part VML extensions with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.setContentTypePartVMLExtensions(), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddContentTypePart(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test add content type part with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.addContentTypePart(0, "unknown"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
|
@ -160,10 +160,10 @@ func (f *File) AddPivotTable(opts *PivotTableOptions) error {
|
||||||
}
|
}
|
||||||
pivotTableSheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(pivotTableSheetPath, "xl/worksheets/") + ".rels"
|
pivotTableSheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(pivotTableSheetPath, "xl/worksheets/") + ".rels"
|
||||||
f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
|
f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
|
||||||
f.addContentTypePart(pivotTableID, "pivotTable")
|
if err = f.addContentTypePart(pivotTableID, "pivotTable"); err != nil {
|
||||||
f.addContentTypePart(pivotCacheID, "pivotCache")
|
return err
|
||||||
|
}
|
||||||
return nil
|
return f.addContentTypePart(pivotCacheID, "pivotCache")
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseFormatPivotTableSet provides a function to validate pivot table
|
// parseFormatPivotTableSet provides a function to validate pivot table
|
||||||
|
@ -697,7 +697,7 @@ func (f *File) getPivotTableFieldOptions(name string, fields []PivotTableField)
|
||||||
|
|
||||||
// addWorkbookPivotCache add the association ID of the pivot cache in workbook.xml.
|
// addWorkbookPivotCache add the association ID of the pivot cache in workbook.xml.
|
||||||
func (f *File) addWorkbookPivotCache(RID int) int {
|
func (f *File) addWorkbookPivotCache(RID int) int {
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
if wb.PivotCaches == nil {
|
if wb.PivotCaches == nil {
|
||||||
wb.PivotCaches = &xlsxPivotCaches{}
|
wb.PivotCaches = &xlsxPivotCaches{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,6 +259,15 @@ func TestAddPivotTable(t *testing.T) {
|
||||||
// Test get pivot fields index with empty data range
|
// Test get pivot fields index with empty data range
|
||||||
_, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOptions{})
|
_, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOptions{})
|
||||||
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
|
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
|
||||||
|
// Test add pivot table with unsupported charset content types.
|
||||||
|
f = NewFile()
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
|
||||||
|
DataRange: "Sheet1!$A$1:$E$31",
|
||||||
|
PivotTableRange: "Sheet1!$G$2:$M$34",
|
||||||
|
Rows: []PivotTableField{{Data: "Year"}},
|
||||||
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPivotRowFields(t *testing.T) {
|
func TestAddPivotRowFields(t *testing.T) {
|
||||||
|
|
9
rows.go
9
rows.go
|
@ -435,8 +435,13 @@ func (f *File) sharedStringsReader() (*xlsxSST, error) {
|
||||||
f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
|
f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.addContentTypePart(0, "sharedStrings")
|
if err = f.addContentTypePart(0, "sharedStrings"); err != nil {
|
||||||
rels := f.relsReader(relPath)
|
return f.SharedStrings, err
|
||||||
|
}
|
||||||
|
rels, err := f.relsReader(relPath)
|
||||||
|
if err != nil {
|
||||||
|
return f.SharedStrings, err
|
||||||
|
}
|
||||||
for _, rel := range rels.Relationships {
|
for _, rel := range rels.Relationships {
|
||||||
if rel.Target == "/xl/sharedStrings.xml" {
|
if rel.Target == "/xl/sharedStrings.xml" {
|
||||||
return f.SharedStrings, nil
|
return f.SharedStrings, nil
|
||||||
|
|
17
rows_test.go
17
rows_test.go
|
@ -235,9 +235,20 @@ func TestSharedStringsReader(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test read shared string with unsupported charset.
|
// Test read shared string with unsupported charset.
|
||||||
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
||||||
f.sharedStringsReader()
|
_, err := f.sharedStringsReader()
|
||||||
si := xlsxSI{}
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
assert.EqualValues(t, "", si.String())
|
// Test read shared strings with unsupported charset content types.
|
||||||
|
f = NewFile()
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
_, err = f.sharedStringsReader()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test read shared strings with unsupported charset workbook relationships.
|
||||||
|
f = NewFile()
|
||||||
|
f.Relationships.Delete(defaultXMLPathWorkbookRels)
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset)
|
||||||
|
_, err = f.sharedStringsReader()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRowVisibility(t *testing.T) {
|
func TestRowVisibility(t *testing.T) {
|
||||||
|
|
3
shape.go
3
shape.go
|
@ -308,8 +308,7 @@ func (f *File) AddShape(sheet, cell, opts string) error {
|
||||||
if err = f.addDrawingShape(sheet, drawingXML, cell, options); err != nil {
|
if err = f.addDrawingShape(sheet, drawingXML, cell, options); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addContentTypePart(drawingID, "drawings")
|
return f.addContentTypePart(drawingID, "drawings")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addDrawingShape provides a function to add preset geometry by given sheet,
|
// addDrawingShape provides a function to add preset geometry by given sheet,
|
||||||
|
|
|
@ -87,10 +87,15 @@ func TestAddShape(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`))
|
}`))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx")))
|
||||||
// Test set row style with unsupported charset style sheet.
|
// Test add shape with unsupported charset style sheet.
|
||||||
f.Styles = nil
|
f.Styles = nil
|
||||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.AddShape("Sheet1", "B30", `{"type":"rect","paragraph":[{"text":"Rectangle"},{}]}`), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AddShape("Sheet1", "B30", `{"type":"rect","paragraph":[{"text":"Rectangle"},{}]}`), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test add shape with unsupported charset content types.
|
||||||
|
f = NewFile()
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AddShape("Sheet1", "B30", `{"type":"rect","paragraph":[{"text":"Rectangle"},{}]}`), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddDrawingShape(t *testing.T) {
|
func TestAddDrawingShape(t *testing.T) {
|
||||||
|
|
129
sheet.go
129
sheet.go
|
@ -17,7 +17,6 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -47,7 +46,7 @@ func (f *File) NewSheet(sheet string) int {
|
||||||
}
|
}
|
||||||
f.DeleteSheet(sheet)
|
f.DeleteSheet(sheet)
|
||||||
f.SheetCount++
|
f.SheetCount++
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
sheetID := 0
|
sheetID := 0
|
||||||
for _, v := range wb.Sheets.Sheet {
|
for _, v := range wb.Sheets.Sheet {
|
||||||
if v.SheetID > sheetID {
|
if v.SheetID > sheetID {
|
||||||
|
@ -56,7 +55,7 @@ func (f *File) NewSheet(sheet string) int {
|
||||||
}
|
}
|
||||||
sheetID++
|
sheetID++
|
||||||
// Update [Content_Types].xml
|
// Update [Content_Types].xml
|
||||||
f.setContentTypes("/xl/worksheets/sheet"+strconv.Itoa(sheetID)+".xml", ContentTypeSpreadSheetMLWorksheet)
|
_ = 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, sheet)
|
f.setSheet(sheetID, sheet)
|
||||||
// Update workbook.xml.rels
|
// Update workbook.xml.rels
|
||||||
|
@ -68,19 +67,17 @@ func (f *File) NewSheet(sheet string) int {
|
||||||
|
|
||||||
// contentTypesReader provides a function to get the pointer to the
|
// contentTypesReader provides a function to get the pointer to the
|
||||||
// [Content_Types].xml structure after deserialization.
|
// [Content_Types].xml structure after deserialization.
|
||||||
func (f *File) contentTypesReader() *xlsxTypes {
|
func (f *File) contentTypesReader() (*xlsxTypes, error) {
|
||||||
var err error
|
|
||||||
|
|
||||||
if f.ContentTypes == nil {
|
if f.ContentTypes == nil {
|
||||||
f.ContentTypes = new(xlsxTypes)
|
f.ContentTypes = new(xlsxTypes)
|
||||||
f.ContentTypes.Lock()
|
f.ContentTypes.Lock()
|
||||||
defer f.ContentTypes.Unlock()
|
defer f.ContentTypes.Unlock()
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).
|
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).
|
||||||
Decode(f.ContentTypes); err != nil && err != io.EOF {
|
Decode(f.ContentTypes); err != nil && err != io.EOF {
|
||||||
log.Printf("xml decode error: %s", err)
|
return f.ContentTypes, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f.ContentTypes
|
return f.ContentTypes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contentTypesWriter provides a function to save [Content_Types].xml after
|
// contentTypesWriter provides a function to save [Content_Types].xml after
|
||||||
|
@ -215,14 +212,18 @@ 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 the spreadsheet.
|
// type of the spreadsheet.
|
||||||
func (f *File) setContentTypes(partName, contentType string) {
|
func (f *File) setContentTypes(partName, contentType string) error {
|
||||||
content := f.contentTypesReader()
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
content.Overrides = append(content.Overrides, xlsxOverride{
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
||||||
PartName: partName,
|
PartName: partName,
|
||||||
ContentType: contentType,
|
ContentType: contentType,
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// setSheet provides a function to update sheet property by given index.
|
// setSheet provides a function to update sheet property by given index.
|
||||||
|
@ -271,7 +272,7 @@ func (f *File) SetActiveSheet(index int) {
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
index = 0
|
index = 0
|
||||||
}
|
}
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
for activeTab := range wb.Sheets.Sheet {
|
for activeTab := range wb.Sheets.Sheet {
|
||||||
if activeTab == index {
|
if activeTab == index {
|
||||||
if wb.BookViews == nil {
|
if wb.BookViews == nil {
|
||||||
|
@ -316,7 +317,7 @@ func (f *File) SetActiveSheet(index int) {
|
||||||
// spreadsheet. If not found the active sheet will be return integer 0.
|
// spreadsheet. If not found the active sheet will be return integer 0.
|
||||||
func (f *File) GetActiveSheetIndex() (index int) {
|
func (f *File) GetActiveSheetIndex() (index int) {
|
||||||
sheetID := f.getActiveSheetID()
|
sheetID := f.getActiveSheetID()
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
if wb != nil {
|
if wb != nil {
|
||||||
for idx, sheet := range wb.Sheets.Sheet {
|
for idx, sheet := range wb.Sheets.Sheet {
|
||||||
if sheet.SheetID == sheetID {
|
if sheet.SheetID == sheetID {
|
||||||
|
@ -331,7 +332,7 @@ func (f *File) GetActiveSheetIndex() (index int) {
|
||||||
// getActiveSheetID provides a function to get active sheet ID of the
|
// getActiveSheetID provides a function to get active sheet ID of the
|
||||||
// spreadsheet. If not found the active sheet will be return integer 0.
|
// spreadsheet. If not found the active sheet will be return integer 0.
|
||||||
func (f *File) getActiveSheetID() int {
|
func (f *File) getActiveSheetID() int {
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
if wb != nil {
|
if wb != nil {
|
||||||
if wb.BookViews != nil && len(wb.BookViews.WorkBookView) > 0 {
|
if wb.BookViews != nil && len(wb.BookViews.WorkBookView) > 0 {
|
||||||
activeTab := wb.BookViews.WorkBookView[0].ActiveTab
|
activeTab := wb.BookViews.WorkBookView[0].ActiveTab
|
||||||
|
@ -357,10 +358,10 @@ func (f *File) SetSheetName(source, target string) {
|
||||||
if strings.EqualFold(target, source) {
|
if strings.EqualFold(target, source) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
content := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
for k, v := range content.Sheets.Sheet {
|
for k, v := range wb.Sheets.Sheet {
|
||||||
if v.Name == source {
|
if v.Name == source {
|
||||||
content.Sheets.Sheet[k].Name = target
|
wb.Sheets.Sheet[k].Name = target
|
||||||
f.sheetMap[target] = f.sheetMap[source]
|
f.sheetMap[target] = f.sheetMap[source]
|
||||||
delete(f.sheetMap, source)
|
delete(f.sheetMap, source)
|
||||||
}
|
}
|
||||||
|
@ -422,7 +423,7 @@ func (f *File) GetSheetIndex(sheet string) int {
|
||||||
// fmt.Println(index, name)
|
// fmt.Println(index, name)
|
||||||
// }
|
// }
|
||||||
func (f *File) GetSheetMap() map[int]string {
|
func (f *File) GetSheetMap() map[int]string {
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
sheetMap := map[int]string{}
|
sheetMap := map[int]string{}
|
||||||
if wb != nil {
|
if wb != nil {
|
||||||
for _, sheet := range wb.Sheets.Sheet {
|
for _, sheet := range wb.Sheets.Sheet {
|
||||||
|
@ -435,7 +436,7 @@ func (f *File) GetSheetMap() map[int]string {
|
||||||
// GetSheetList provides a function to get worksheets, chart sheets, and
|
// GetSheetList provides a function to get worksheets, chart sheets, and
|
||||||
// dialog sheets name list of the workbook.
|
// dialog sheets name list of the workbook.
|
||||||
func (f *File) GetSheetList() (list []string) {
|
func (f *File) GetSheetList() (list []string) {
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
if wb != nil {
|
if wb != nil {
|
||||||
for _, sheet := range wb.Sheets.Sheet {
|
for _, sheet := range wb.Sheets.Sheet {
|
||||||
list = append(list, sheet.Name)
|
list = append(list, sheet.Name)
|
||||||
|
@ -448,8 +449,10 @@ func (f *File) GetSheetList() (list []string) {
|
||||||
// of the spreadsheet.
|
// of the spreadsheet.
|
||||||
func (f *File) getSheetMap() map[string]string {
|
func (f *File) getSheetMap() map[string]string {
|
||||||
maps := map[string]string{}
|
maps := map[string]string{}
|
||||||
for _, v := range f.workbookReader().Sheets.Sheet {
|
wb, _ := f.workbookReader()
|
||||||
for _, rel := range f.relsReader(f.getWorkbookRelsPath()).Relationships {
|
rels, _ := f.relsReader(f.getWorkbookRelsPath())
|
||||||
|
for _, v := range wb.Sheets.Sheet {
|
||||||
|
for _, rel := range rels.Relationships {
|
||||||
if rel.ID == v.ID {
|
if rel.ID == v.ID {
|
||||||
sheetXMLPath := f.getWorksheetPath(rel.Target)
|
sheetXMLPath := f.getWorksheetPath(rel.Target)
|
||||||
if _, ok := f.Pkg.Load(sheetXMLPath); ok {
|
if _, ok := f.Pkg.Load(sheetXMLPath); ok {
|
||||||
|
@ -498,10 +501,11 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
|
||||||
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
|
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
|
||||||
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
|
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
|
||||||
rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
|
rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
|
||||||
f.addSheetPicture(sheet, rID)
|
if err = f.addSheetPicture(sheet, rID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
f.addSheetNameSpace(sheet, SourceRelationship)
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
||||||
f.setContentTypePartImageExtensions()
|
return f.setContentTypePartImageExtensions()
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSheet provides a function to delete worksheet in a workbook by given
|
// DeleteSheet provides a function to delete worksheet in a workbook by given
|
||||||
|
@ -514,8 +518,8 @@ func (f *File) DeleteSheet(sheet string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sheetName := trimSheetName(sheet)
|
sheetName := trimSheetName(sheet)
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
wbRels := f.relsReader(f.getWorkbookRelsPath())
|
wbRels, _ := f.relsReader(f.getWorkbookRelsPath())
|
||||||
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
|
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
|
||||||
deleteLocalSheetID := f.GetSheetIndex(sheet)
|
deleteLocalSheetID := f.GetSheetIndex(sheet)
|
||||||
deleteAndAdjustDefinedNames(wb, deleteLocalSheetID)
|
deleteAndAdjustDefinedNames(wb, deleteLocalSheetID)
|
||||||
|
@ -537,8 +541,8 @@ func (f *File) DeleteSheet(sheet string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target := f.deleteSheetFromWorkbookRels(v.ID)
|
target := f.deleteSheetFromWorkbookRels(v.ID)
|
||||||
f.deleteSheetFromContentTypes(target)
|
_ = f.deleteSheetFromContentTypes(target)
|
||||||
f.deleteCalcChain(f.getSheetID(sheet), "")
|
_ = f.deleteCalcChain(f.getSheetID(sheet), "")
|
||||||
delete(f.sheetMap, v.Name)
|
delete(f.sheetMap, v.Name)
|
||||||
f.Pkg.Delete(sheetXML)
|
f.Pkg.Delete(sheetXML)
|
||||||
f.Pkg.Delete(rels)
|
f.Pkg.Delete(rels)
|
||||||
|
@ -573,12 +577,12 @@ func deleteAndAdjustDefinedNames(wb *xlsxWorkbook, deleteLocalSheetID int) {
|
||||||
// deleteSheetFromWorkbookRels provides a function to remove worksheet
|
// deleteSheetFromWorkbookRels provides a function to remove worksheet
|
||||||
// relationships by given relationships ID in the file workbook.xml.rels.
|
// relationships by given relationships ID in the file workbook.xml.rels.
|
||||||
func (f *File) deleteSheetFromWorkbookRels(rID string) string {
|
func (f *File) deleteSheetFromWorkbookRels(rID string) string {
|
||||||
content := f.relsReader(f.getWorkbookRelsPath())
|
rels, _ := f.relsReader(f.getWorkbookRelsPath())
|
||||||
content.Lock()
|
rels.Lock()
|
||||||
defer content.Unlock()
|
defer rels.Unlock()
|
||||||
for k, v := range content.Relationships {
|
for k, v := range rels.Relationships {
|
||||||
if v.ID == rID {
|
if v.ID == rID {
|
||||||
content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...)
|
rels.Relationships = append(rels.Relationships[:k], rels.Relationships[k+1:]...)
|
||||||
return v.Target
|
return v.Target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -587,11 +591,14 @@ func (f *File) deleteSheetFromWorkbookRels(rID string) string {
|
||||||
|
|
||||||
// deleteSheetFromContentTypes provides a function to remove worksheet
|
// deleteSheetFromContentTypes provides a function to remove worksheet
|
||||||
// relationships by given target name in the file [Content_Types].xml.
|
// relationships by given target name in the file [Content_Types].xml.
|
||||||
func (f *File) deleteSheetFromContentTypes(target string) {
|
func (f *File) deleteSheetFromContentTypes(target string) error {
|
||||||
if !strings.HasPrefix(target, "/") {
|
if !strings.HasPrefix(target, "/") {
|
||||||
target = "/xl/" + target
|
target = "/xl/" + target
|
||||||
}
|
}
|
||||||
content := f.contentTypesReader()
|
content, err := f.contentTypesReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
for k, v := range content.Overrides {
|
for k, v := range content.Overrides {
|
||||||
|
@ -599,6 +606,7 @@ func (f *File) deleteSheetFromContentTypes(target string) {
|
||||||
content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
|
content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopySheet provides a function to duplicate a worksheet by gave source and
|
// CopySheet provides a function to duplicate a worksheet by gave source and
|
||||||
|
@ -659,22 +667,25 @@ func (f *File) copySheet(from, to int) error {
|
||||||
// err := f.SetSheetVisible("Sheet1", false)
|
// err := f.SetSheetVisible("Sheet1", false)
|
||||||
func (f *File) SetSheetVisible(sheet string, visible bool) error {
|
func (f *File) SetSheetVisible(sheet string, visible bool) error {
|
||||||
sheet = trimSheetName(sheet)
|
sheet = trimSheetName(sheet)
|
||||||
content := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if visible {
|
if visible {
|
||||||
for k, v := range content.Sheets.Sheet {
|
for k, v := range wb.Sheets.Sheet {
|
||||||
if strings.EqualFold(v.Name, sheet) {
|
if strings.EqualFold(v.Name, sheet) {
|
||||||
content.Sheets.Sheet[k].State = ""
|
wb.Sheets.Sheet[k].State = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
count := 0
|
count := 0
|
||||||
for _, v := range content.Sheets.Sheet {
|
for _, v := range wb.Sheets.Sheet {
|
||||||
if v.State != "hidden" {
|
if v.State != "hidden" {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, v := range content.Sheets.Sheet {
|
for k, v := range wb.Sheets.Sheet {
|
||||||
ws, err := f.workSheetReader(v.Name)
|
ws, err := f.workSheetReader(v.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -684,10 +695,10 @@ func (f *File) SetSheetVisible(sheet string, visible bool) error {
|
||||||
tabSelected = ws.SheetViews.SheetView[0].TabSelected
|
tabSelected = ws.SheetViews.SheetView[0].TabSelected
|
||||||
}
|
}
|
||||||
if strings.EqualFold(v.Name, sheet) && count > 1 && !tabSelected {
|
if strings.EqualFold(v.Name, sheet) && count > 1 && !tabSelected {
|
||||||
content.Sheets.Sheet[k].State = "hidden"
|
wb.Sheets.Sheet[k].State = "hidden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePanesOptions provides a function to parse the panes settings.
|
// parsePanesOptions provides a function to parse the panes settings.
|
||||||
|
@ -830,10 +841,11 @@ func (f *File) SetPanes(sheet, panes string) error {
|
||||||
//
|
//
|
||||||
// f.GetSheetVisible("Sheet1")
|
// f.GetSheetVisible("Sheet1")
|
||||||
func (f *File) GetSheetVisible(sheet string) bool {
|
func (f *File) GetSheetVisible(sheet string) bool {
|
||||||
content, name, visible := f.workbookReader(), trimSheetName(sheet), false
|
name, visible := trimSheetName(sheet), false
|
||||||
for k, v := range content.Sheets.Sheet {
|
wb, _ := f.workbookReader()
|
||||||
|
for k, v := range wb.Sheets.Sheet {
|
||||||
if strings.EqualFold(v.Name, name) {
|
if strings.EqualFold(v.Name, name) {
|
||||||
if content.Sheets.Sheet[k].State == "" || content.Sheets.Sheet[k].State == "visible" {
|
if wb.Sheets.Sheet[k].State == "" || wb.Sheets.Sheet[k].State == "visible" {
|
||||||
visible = true
|
visible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1460,7 +1472,10 @@ func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
|
||||||
// Scope: "Sheet2",
|
// Scope: "Sheet2",
|
||||||
// })
|
// })
|
||||||
func (f *File) SetDefinedName(definedName *DefinedName) error {
|
func (f *File) SetDefinedName(definedName *DefinedName) error {
|
||||||
wb := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
d := xlsxDefinedName{
|
d := xlsxDefinedName{
|
||||||
Name: definedName.Name,
|
Name: definedName.Name,
|
||||||
Comment: definedName.Comment,
|
Comment: definedName.Comment,
|
||||||
|
@ -1499,7 +1514,10 @@ func (f *File) SetDefinedName(definedName *DefinedName) error {
|
||||||
// Scope: "Sheet2",
|
// Scope: "Sheet2",
|
||||||
// })
|
// })
|
||||||
func (f *File) DeleteDefinedName(definedName *DefinedName) error {
|
func (f *File) DeleteDefinedName(definedName *DefinedName) error {
|
||||||
wb := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if wb.DefinedNames != nil {
|
if wb.DefinedNames != nil {
|
||||||
for idx, dn := range wb.DefinedNames.DefinedName {
|
for idx, dn := range wb.DefinedNames.DefinedName {
|
||||||
scope := "Workbook"
|
scope := "Workbook"
|
||||||
|
@ -1512,7 +1530,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
|
||||||
}
|
}
|
||||||
if scope == deleteScope && dn.Name == definedName.Name {
|
if scope == deleteScope && dn.Name == definedName.Name {
|
||||||
wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...)
|
wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1523,7 +1541,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
|
||||||
// or worksheet.
|
// or worksheet.
|
||||||
func (f *File) GetDefinedName() []DefinedName {
|
func (f *File) GetDefinedName() []DefinedName {
|
||||||
var definedNames []DefinedName
|
var definedNames []DefinedName
|
||||||
wb := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
if wb.DefinedNames != nil {
|
if wb.DefinedNames != nil {
|
||||||
for _, dn := range wb.DefinedNames.DefinedName {
|
for _, dn := range wb.DefinedNames.DefinedName {
|
||||||
definedName := DefinedName{
|
definedName := DefinedName{
|
||||||
|
@ -1715,23 +1733,22 @@ func (f *File) RemovePageBreak(sheet, cell string) error {
|
||||||
|
|
||||||
// relsReader provides a function to get the pointer to the structure
|
// relsReader provides a function to get the pointer to the structure
|
||||||
// after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
|
// after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
|
||||||
func (f *File) relsReader(path string) *xlsxRelationships {
|
func (f *File) relsReader(path string) (*xlsxRelationships, error) {
|
||||||
var err error
|
|
||||||
rels, _ := f.Relationships.Load(path)
|
rels, _ := f.Relationships.Load(path)
|
||||||
if rels == nil {
|
if rels == nil {
|
||||||
if _, ok := f.Pkg.Load(path); ok {
|
if _, ok := f.Pkg.Load(path); ok {
|
||||||
c := xlsxRelationships{}
|
c := xlsxRelationships{}
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
|
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
|
||||||
Decode(&c); err != nil && err != io.EOF {
|
Decode(&c); err != nil && err != io.EOF {
|
||||||
log.Printf("xml decode error: %s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
f.Relationships.Store(path, &c)
|
f.Relationships.Store(path, &c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rels, _ = f.Relationships.Load(path); rels != nil {
|
if rels, _ = f.Relationships.Load(path); rels != nil {
|
||||||
return rels.(*xlsxRelationships)
|
return rels.(*xlsxRelationships), nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fillSheetData ensures there are enough rows, and columns in the chosen
|
// fillSheetData ensures there are enough rows, and columns in the chosen
|
||||||
|
|
|
@ -188,6 +188,17 @@ func TestDefinedName(t *testing.T) {
|
||||||
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[0].RefersTo)
|
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[0].RefersTo)
|
||||||
assert.Exactly(t, 1, len(f.GetDefinedName()))
|
assert.Exactly(t, 1, len(f.GetDefinedName()))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDefinedName.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDefinedName.xlsx")))
|
||||||
|
// Test set defined name with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.SetDefinedName(&DefinedName{
|
||||||
|
Name: "Amount", RefersTo: "Sheet1!$A$2:$D$5",
|
||||||
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test delete defined name with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.DeleteDefinedName(&DefinedName{Name: "Amount"}),
|
||||||
|
"XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGroupSheets(t *testing.T) {
|
func TestGroupSheets(t *testing.T) {
|
||||||
|
@ -367,6 +378,32 @@ func TestGetSheetID(t *testing.T) {
|
||||||
assert.NotEqual(t, -1, id)
|
assert.NotEqual(t, -1, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetSheetVisible(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
f.WorkBook.Sheets.Sheet[0].Name = "SheetN"
|
||||||
|
assert.EqualError(t, f.SetSheetVisible("Sheet1", false), "sheet SheetN does not exist")
|
||||||
|
// Test set sheet visible with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.SetSheetVisible("Sheet1", false), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetContentTypes(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test set content type with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.setContentTypes("/xl/worksheets/sheet1.xml", ContentTypeSpreadSheetMLWorksheet), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteSheetFromContentTypes(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test delete sheet from content types with unsupported charset content types.
|
||||||
|
f.ContentTypes = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.deleteSheetFromContentTypes("/xl/worksheets/sheet1.xml"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkNewSheet(b *testing.B) {
|
func BenchmarkNewSheet(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
|
|
34
stream.go
34
stream.go
|
@ -226,11 +226,12 @@ func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error {
|
||||||
|
|
||||||
sw.tableParts = fmt.Sprintf(`<tableParts count="1"><tablePart r:id="rId%d"></tablePart></tableParts>`, rID)
|
sw.tableParts = fmt.Sprintf(`<tableParts count="1"><tablePart r:id="rId%d"></tablePart></tableParts>`, rID)
|
||||||
|
|
||||||
sw.File.addContentTypePart(tableID, "table")
|
if err = sw.File.addContentTypePart(tableID, "table"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
b, _ := xml.Marshal(table)
|
b, _ := xml.Marshal(table)
|
||||||
sw.File.saveFileList(tableXML, b)
|
sw.File.saveFileList(tableXML, b)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract values from a row in the StreamWriter.
|
// Extract values from a row in the StreamWriter.
|
||||||
|
@ -471,6 +472,23 @@ func setCellFormula(c *xlsxC, formula string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setCellTime provides a function to set number of a cell with a time.
|
||||||
|
func (sw *StreamWriter) setCellTime(c *xlsxC, val time.Time) error {
|
||||||
|
var date1904, isNum bool
|
||||||
|
wb, err := sw.File.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if wb != nil && wb.WorkbookPr != nil {
|
||||||
|
date1904 = wb.WorkbookPr.Date1904
|
||||||
|
}
|
||||||
|
if isNum, err = c.setCellTime(val, date1904); err == nil && isNum && c.S == 0 {
|
||||||
|
style, _ := sw.File.NewStyle(&Style{NumFmt: 22})
|
||||||
|
c.S = style
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// setCellValFunc provides a function to set value of a cell.
|
// setCellValFunc provides a function to set value of a cell.
|
||||||
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
|
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -488,15 +506,7 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
|
||||||
case time.Duration:
|
case time.Duration:
|
||||||
c.T, c.V = setCellDuration(val)
|
c.T, c.V = setCellDuration(val)
|
||||||
case time.Time:
|
case time.Time:
|
||||||
var isNum bool
|
err = sw.setCellTime(c, val)
|
||||||
date1904, wb := false, sw.File.workbookReader()
|
|
||||||
if wb != nil && wb.WorkbookPr != nil {
|
|
||||||
date1904 = wb.WorkbookPr.Date1904
|
|
||||||
}
|
|
||||||
if isNum, err = c.setCellTime(val, date1904); isNum && c.S == 0 {
|
|
||||||
style, _ := sw.File.NewStyle(&Style{NumFmt: 22})
|
|
||||||
c.S = style
|
|
||||||
}
|
|
||||||
case bool:
|
case bool:
|
||||||
c.T, c.V = setCellBool(val)
|
c.T, c.V = setCellBool(val)
|
||||||
case nil:
|
case nil:
|
||||||
|
|
|
@ -186,7 +186,7 @@ func TestStreamTable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a table.
|
// Write a table.
|
||||||
assert.NoError(t, streamWriter.AddTable("A1", "C2", ``))
|
assert.NoError(t, streamWriter.AddTable("A1", "C2", ""))
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
|
|
||||||
// Verify the table has names.
|
// Verify the table has names.
|
||||||
|
@ -198,13 +198,17 @@ func TestStreamTable(t *testing.T) {
|
||||||
assert.Equal(t, "B", table.TableColumns.TableColumn[1].Name)
|
assert.Equal(t, "B", table.TableColumns.TableColumn[1].Name)
|
||||||
assert.Equal(t, "C", table.TableColumns.TableColumn[2].Name)
|
assert.Equal(t, "C", table.TableColumns.TableColumn[2].Name)
|
||||||
|
|
||||||
assert.NoError(t, streamWriter.AddTable("A1", "C1", ``))
|
assert.NoError(t, streamWriter.AddTable("A1", "C1", ""))
|
||||||
|
|
||||||
// Test add table with illegal options.
|
// Test add table with illegal options.
|
||||||
assert.EqualError(t, streamWriter.AddTable("B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string")
|
assert.EqualError(t, streamWriter.AddTable("B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string")
|
||||||
// Test add table with illegal cell reference.
|
// Test add table with illegal cell reference.
|
||||||
assert.EqualError(t, streamWriter.AddTable("A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, streamWriter.AddTable("A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.EqualError(t, streamWriter.AddTable("A1", "B", `{}`), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.EqualError(t, streamWriter.AddTable("A1", "B", `{}`), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||||
|
// Test add table with unsupported charset content types.
|
||||||
|
file.ContentTypes = nil
|
||||||
|
file.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, streamWriter.AddTable("A1", "C2", ""), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamMergeCells(t *testing.T) {
|
func TestStreamMergeCells(t *testing.T) {
|
||||||
|
@ -242,7 +246,7 @@ func TestStreamMarshalAttrs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamSetRow(t *testing.T) {
|
func TestStreamSetRow(t *testing.T) {
|
||||||
// Test error exceptions
|
// Test error exceptions.
|
||||||
file := NewFile()
|
file := NewFile()
|
||||||
defer func() {
|
defer func() {
|
||||||
assert.NoError(t, file.Close())
|
assert.NoError(t, file.Close())
|
||||||
|
@ -250,9 +254,13 @@ func TestStreamSetRow(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
// Test set row with non-ascending row number
|
// Test set row with non-ascending row number.
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{}))
|
||||||
assert.EqualError(t, streamWriter.SetRow("A1", []interface{}{}), newStreamSetRowError(1).Error())
|
assert.EqualError(t, streamWriter.SetRow("A1", []interface{}{}), newStreamSetRowError(1).Error())
|
||||||
|
// Test set row with unsupported charset workbook.
|
||||||
|
file.WorkBook = nil
|
||||||
|
file.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, streamWriter.SetRow("A2", []interface{}{time.Now()}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamSetRowNilValues(t *testing.T) {
|
func TestStreamSetRowNilValues(t *testing.T) {
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -3357,16 +3356,16 @@ func getPaletteColor(color string) string {
|
||||||
|
|
||||||
// themeReader provides a function to get the pointer to the xl/theme/theme1.xml
|
// themeReader provides a function to get the pointer to the xl/theme/theme1.xml
|
||||||
// structure after deserialization.
|
// structure after deserialization.
|
||||||
func (f *File) themeReader() *xlsxTheme {
|
func (f *File) themeReader() (*xlsxTheme, error) {
|
||||||
if _, ok := f.Pkg.Load(defaultXMLPathTheme); !ok {
|
if _, ok := f.Pkg.Load(defaultXMLPathTheme); !ok {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
theme := xlsxTheme{XMLNSa: NameSpaceDrawingML.Value, XMLNSr: SourceRelationship.Value}
|
theme := xlsxTheme{XMLNSa: NameSpaceDrawingML.Value, XMLNSr: SourceRelationship.Value}
|
||||||
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathTheme)))).
|
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathTheme)))).
|
||||||
Decode(&theme); err != nil && err != io.EOF {
|
Decode(&theme); err != nil && err != io.EOF {
|
||||||
log.Printf("xml decoder error: %s", err)
|
return &theme, err
|
||||||
}
|
}
|
||||||
return &theme
|
return &theme, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThemeColor applied the color with tint value.
|
// ThemeColor applied the color with tint value.
|
||||||
|
|
|
@ -366,7 +366,9 @@ func TestThemeReader(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test read theme with unsupported charset.
|
// Test read theme with unsupported charset.
|
||||||
f.Pkg.Store(defaultXMLPathTheme, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathTheme, MacintoshCyrillicCharset)
|
||||||
assert.EqualValues(t, &xlsxTheme{XMLNSa: NameSpaceDrawingML.Value, XMLNSr: SourceRelationship.Value}, f.themeReader())
|
theme, err := f.themeReader()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
assert.EqualValues(t, &xlsxTheme{XMLNSa: NameSpaceDrawingML.Value, XMLNSr: SourceRelationship.Value}, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetCellStyle(t *testing.T) {
|
func TestSetCellStyle(t *testing.T) {
|
||||||
|
|
8
table.go
8
table.go
|
@ -94,8 +94,7 @@ func (f *File) AddTable(sheet, hCell, vCell, opts string) error {
|
||||||
if err = f.addTable(sheet, tableXML, hCol, hRow, vCol, vRow, tableID, options); err != nil {
|
if err = f.addTable(sheet, tableXML, hCol, hRow, vCol, vRow, tableID, options); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addContentTypePart(tableID, "table")
|
return f.addContentTypePart(tableID, "table")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// countTables provides a function to get table files count storage in the
|
// countTables provides a function to get table files count storage in the
|
||||||
|
@ -301,7 +300,10 @@ func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error {
|
||||||
cellStart, _ := CoordinatesToCellName(hCol, hRow, true)
|
cellStart, _ := CoordinatesToCellName(hCol, hRow, true)
|
||||||
cellEnd, _ := CoordinatesToCellName(vCol, vRow, true)
|
cellEnd, _ := CoordinatesToCellName(vCol, vRow, true)
|
||||||
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
|
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
|
||||||
wb := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
sheetID := f.GetSheetIndex(sheet)
|
sheetID := f.GetSheetIndex(sheet)
|
||||||
filterRange := fmt.Sprintf("'%s'!%s", sheet, ref)
|
filterRange := fmt.Sprintf("'%s'!%s", sheet, ref)
|
||||||
d := xlsxDefinedName{
|
d := xlsxDefinedName{
|
||||||
|
|
|
@ -78,9 +78,13 @@ func TestAutoFilter(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test AutoFilter with illegal cell reference.
|
// Test add auto filter with illegal cell reference.
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "A", "B1", ""), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AutoFilter("Sheet1", "A", "B1", ""), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "A1", "B", ""), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.EqualError(t, f.AutoFilter("Sheet1", "A1", "B", ""), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||||
|
// Test add auto filter with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.AutoFilter("Sheet1", "D4", "B1", formats[0]), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoFilterError(t *testing.T) {
|
func TestAutoFilterError(t *testing.T) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ const (
|
||||||
defaultXMLPathStyles = "xl/styles.xml"
|
defaultXMLPathStyles = "xl/styles.xml"
|
||||||
defaultXMLPathTheme = "xl/theme/theme1.xml"
|
defaultXMLPathTheme = "xl/theme/theme1.xml"
|
||||||
defaultXMLPathWorkbook = "xl/workbook.xml"
|
defaultXMLPathWorkbook = "xl/workbook.xml"
|
||||||
|
defaultXMLPathWorkbookRels = "xl/_rels/workbook.xml.rels"
|
||||||
defaultTempFileSST = "sharedStrings"
|
defaultTempFileSST = "sharedStrings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
26
workbook.go
26
workbook.go
|
@ -15,7 +15,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -23,7 +22,10 @@ import (
|
||||||
|
|
||||||
// SetWorkbookProps provides a function to sets workbook properties.
|
// SetWorkbookProps provides a function to sets workbook properties.
|
||||||
func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
|
func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
|
||||||
wb := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if wb.WorkbookPr == nil {
|
if wb.WorkbookPr == nil {
|
||||||
wb.WorkbookPr = new(xlsxWorkbookPr)
|
wb.WorkbookPr = new(xlsxWorkbookPr)
|
||||||
}
|
}
|
||||||
|
@ -44,20 +46,24 @@ func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
|
||||||
|
|
||||||
// GetWorkbookProps provides a function to gets workbook properties.
|
// GetWorkbookProps provides a function to gets workbook properties.
|
||||||
func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
|
func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
|
||||||
wb, opts := f.workbookReader(), WorkbookPropsOptions{}
|
var opts WorkbookPropsOptions
|
||||||
|
wb, err := f.workbookReader()
|
||||||
|
if err != nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
if wb.WorkbookPr != nil {
|
if wb.WorkbookPr != nil {
|
||||||
opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
|
opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
|
||||||
opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
|
opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
|
||||||
opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
|
opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
|
||||||
}
|
}
|
||||||
return opts, nil
|
return opts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// setWorkbook update workbook property of the spreadsheet. Maximum 31
|
// setWorkbook update workbook property of the spreadsheet. Maximum 31
|
||||||
// characters are allowed in sheet title.
|
// characters are allowed in sheet title.
|
||||||
func (f *File) setWorkbook(name string, sheetID, rid int) {
|
func (f *File) setWorkbook(name string, sheetID, rid int) {
|
||||||
content := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
|
wb.Sheets.Sheet = append(wb.Sheets.Sheet, xlsxSheet{
|
||||||
Name: trimSheetName(name),
|
Name: trimSheetName(name),
|
||||||
SheetID: sheetID,
|
SheetID: sheetID,
|
||||||
ID: "rId" + strconv.Itoa(rid),
|
ID: "rId" + strconv.Itoa(rid),
|
||||||
|
@ -67,7 +73,7 @@ func (f *File) setWorkbook(name string, sheetID, rid int) {
|
||||||
// getWorkbookPath provides a function to get the path of the workbook.xml in
|
// getWorkbookPath provides a function to get the path of the workbook.xml in
|
||||||
// the spreadsheet.
|
// the spreadsheet.
|
||||||
func (f *File) getWorkbookPath() (path string) {
|
func (f *File) getWorkbookPath() (path string) {
|
||||||
if rels := f.relsReader("_rels/.rels"); rels != nil {
|
if rels, _ := f.relsReader("_rels/.rels"); rels != nil {
|
||||||
rels.Lock()
|
rels.Lock()
|
||||||
defer rels.Unlock()
|
defer rels.Unlock()
|
||||||
for _, rel := range rels.Relationships {
|
for _, rel := range rels.Relationships {
|
||||||
|
@ -95,7 +101,7 @@ func (f *File) getWorkbookRelsPath() (path string) {
|
||||||
|
|
||||||
// workbookReader provides a function to get the pointer to the workbook.xml
|
// workbookReader provides a function to get the pointer to the workbook.xml
|
||||||
// structure after deserialization.
|
// structure after deserialization.
|
||||||
func (f *File) workbookReader() *xlsxWorkbook {
|
func (f *File) workbookReader() (*xlsxWorkbook, error) {
|
||||||
var err error
|
var err error
|
||||||
if f.WorkBook == nil {
|
if f.WorkBook == nil {
|
||||||
wbPath := f.getWorkbookPath()
|
wbPath := f.getWorkbookPath()
|
||||||
|
@ -107,10 +113,10 @@ func (f *File) workbookReader() *xlsxWorkbook {
|
||||||
}
|
}
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
|
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
|
||||||
Decode(f.WorkBook); err != nil && err != io.EOF {
|
Decode(f.WorkBook); err != nil && err != io.EOF {
|
||||||
log.Printf("xml decode error: %s", err)
|
return f.WorkBook, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f.WorkBook
|
return f.WorkBook, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// workBookWriter provides a function to save workbook.xml after serialize
|
// workBookWriter provides a function to save workbook.xml after serialize
|
||||||
|
|
|
@ -9,7 +9,8 @@ import (
|
||||||
func TestWorkbookProps(t *testing.T) {
|
func TestWorkbookProps(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
assert.NoError(t, f.SetWorkbookProps(nil))
|
assert.NoError(t, f.SetWorkbookProps(nil))
|
||||||
wb := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
|
assert.NoError(t, err)
|
||||||
wb.WorkbookPr = nil
|
wb.WorkbookPr = nil
|
||||||
expected := WorkbookPropsOptions{
|
expected := WorkbookPropsOptions{
|
||||||
Date1904: boolPtr(true),
|
Date1904: boolPtr(true),
|
||||||
|
@ -20,4 +21,13 @@ func TestWorkbookProps(t *testing.T) {
|
||||||
opts, err := f.GetWorkbookProps()
|
opts, err := f.GetWorkbookProps()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expected, opts)
|
assert.Equal(t, expected, opts)
|
||||||
|
// Test set workbook properties with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.SetWorkbookProps(&expected), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test get workbook properties with unsupported charset workbook.
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
_, err = f.GetWorkbookProps()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue