Remove internal error log print, throw XML deserialize error

This commit is contained in:
xuri 2022-11-13 00:40:04 +08:00
parent bd5dd17673
commit ac564afa56
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
31 changed files with 458 additions and 203 deletions

View File

@ -54,7 +54,10 @@ func (f *File) deleteCalcChain(index int, cell string) error {
if len(calc.C) == 0 {
f.CalcChain = nil
f.Pkg.Delete(defaultXMLPathCalcChain)
content := f.contentTypesReader()
content, err := f.contentTypesReader()
if err != nil {
return err
}
content.Lock()
defer content.Unlock()
for k, v := range content.Overrides {

View File

@ -33,7 +33,14 @@ func TestDeleteCalcChain(t *testing.T) {
formulaType, ref := STCellFormulaTypeShared, "C1:C5"
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.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
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
View File

@ -241,11 +241,14 @@ func (f *File) setCellTimeFunc(sheet, cell string, value time.Time) error {
ws.Lock()
c.S = f.prepareCellStyle(ws, col, row, c.S)
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 {
date1904 = wb.WorkbookPr.Date1904
}
var isNum bool
if isNum, err = c.setCellTime(value, date1904); err != nil {
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 {
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 {
date1904 = wb.WorkbookPr.Date1904
}

View File

@ -173,7 +173,7 @@ func TestSetCellValue(t *testing.T) {
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.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})
assert.NoError(t, err)
style2, err := f.NewStyle(&Style{NumFmt: 9})
@ -189,10 +189,14 @@ func TestSetCellValue(t *testing.T) {
assert.NoError(t, err)
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.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
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) {
@ -204,7 +208,7 @@ func TestSetCellValues(t *testing.T) {
assert.NoError(t, err)
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))
assert.NoError(t, err)
@ -782,6 +786,12 @@ func TestFormattedValue(t *testing.T) {
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.
f.Styles = nil
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)

View File

@ -927,8 +927,10 @@ func (f *File) AddChart(sheet, cell, opts string, combo ...string) error {
return err
}
f.addChart(options, comboCharts)
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(drawingID, "drawings")
if err = f.addContentTypePart(chartID, "chart"); err != nil {
return err
}
_ = f.addContentTypePart(drawingID, "drawings")
f.addSheetNameSpace(sheet, SourceRelationship)
return err
}
@ -952,7 +954,7 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
},
}
f.SheetCount++
wb := f.workbookReader()
wb, _ := f.workbookReader()
sheetID := 0
for _, v := range wb.Sheets.Sheet {
if v.SheetID > sheetID {
@ -969,11 +971,15 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
f.prepareChartSheetDrawing(&cs, drawingID, sheet)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
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.addContentTypePart(chartID, "chart")
f.addContentTypePart(sheetID, "chartsheet")
f.addContentTypePart(drawingID, "drawings")
if err = f.addContentTypePart(chartID, "chart"); err != nil {
return err
}
_ = f.addContentTypePart(sheetID, "chartsheet")
_ = f.addContentTypePart(drawingID, "drawings")
// Update workbook.xml.rels
rID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipChartsheet, fmt.Sprintf("/xl/chartsheets/sheet%d.xml", sheetID), "")
// Update workbook.xml

View File

@ -226,6 +226,11 @@ func TestAddChart(t *testing.T) {
// 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.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) {
@ -259,6 +264,14 @@ func TestAddChartSheet(t *testing.T) {
assert.NoError(t, f.UpdateLinkedValue())
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) {

View File

@ -69,8 +69,8 @@ func (f *File) GetComments() (map[string][]Comment, error) {
// getSheetComments provides the method to get the target comment reference by
// given worksheet file path.
func (f *File) getSheetComments(sheetFile string) string {
rels := "xl/worksheets/_rels/" + sheetFile + ".rels"
if sheetRels := f.relsReader(rels); sheetRels != nil {
rels, _ := f.relsReader("xl/worksheets/_rels/" + sheetFile + ".rels")
if sheetRels := rels; sheetRels != nil {
sheetRels.Lock()
defer sheetRels.Unlock()
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 {
return err
}
f.addContentTypePart(commentID, "comments")
return err
return f.addContentTypePart(commentID, "comments")
}
// DeleteComment provides the method to delete comment in a sheet by given

View File

@ -112,7 +112,8 @@ func TestDecodeVMLDrawingReader(t *testing.T) {
f := NewFile()
path := "xl/drawings/vmlDrawing1.xml"
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) {

View File

@ -181,8 +181,10 @@ func OpenReader(r io.Reader, opts ...Options) (*File, error) {
return f, err
}
f.sheetMap = f.getSheetMap()
f.Styles, err = f.stylesReader()
f.Theme = f.themeReader()
if f.Styles, err = f.stylesReader(); err != nil {
return f, err
}
f.Theme, err = f.themeReader()
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,
// XML path, relationship type, target and target mode.
func (f *File) setRels(rID, relPath, relType, target, targetMode string) int {
rels := f.relsReader(relPath)
rels, _ := f.relsReader(relPath)
if rels == nil || rID == "" {
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{
SourceRelationshipSharedStrings: "/xl/sharedStrings.xml",
}
rels := f.relsReader(relPath)
rels, _ := f.relsReader(relPath)
if rels == nil {
rels = &xlsxRelationships{}
}
@ -418,7 +420,10 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
// </c>
// </row>
func (f *File) UpdateLinkedValue() error {
wb := f.workbookReader()
wb, err := f.workbookReader()
if err != nil {
return err
}
// recalculate formulas
wb.CalcPr = nil
for _, name := range f.GetSheetList() {
@ -465,12 +470,15 @@ func (f *File) AddVBAProject(bin string) error {
if path.Ext(bin) != ".bin" {
return ErrAddVBAProject
}
wb := f.relsReader(f.getWorkbookRelsPath())
wb.Lock()
defer wb.Unlock()
rels, err := f.relsReader(f.getWorkbookRelsPath())
if err != nil {
return err
}
rels.Lock()
defer rels.Unlock()
var rID int
var ok bool
for _, rel := range wb.Relationships {
for _, rel := range rels.Relationships {
if rel.Target == "vbaProject.bin" && rel.Type == SourceRelationshipVBAProject {
ok = true
continue
@ -482,7 +490,7 @@ func (f *File) AddVBAProject(bin string) error {
}
rID++
if !ok {
wb.Relationships = append(wb.Relationships, xlsxRelationship{
rels.Relationships = append(rels.Relationships, xlsxRelationship{
ID: "rId" + strconv.Itoa(rID),
Target: "vbaProject.bin",
Type: SourceRelationshipVBAProject,
@ -495,9 +503,12 @@ func (f *File) AddVBAProject(bin string) error {
// setContentTypePartProjectExtensions provides a function to set the content
// 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
content := f.contentTypesReader()
content, err := f.contentTypesReader()
if err != nil {
return err
}
content.Lock()
defer content.Unlock()
for _, v := range content.Defaults {
@ -516,4 +527,5 @@ func (f *File) setContentTypePartProjectExtensions(contentType string) {
ContentType: ContentTypeVBA,
})
}
return err
}

View File

@ -219,6 +219,7 @@ func TestOpenReader(t *testing.T) {
assert.EqualError(t, err, ErrWorkbookFileFormat.Error())
// Test open workbook with unsupported charset internal calculation chain.
preset := func(filePath string) *bytes.Buffer {
source, err := zip.OpenReader(filepath.Join("test", "Book1.xlsx"))
assert.NoError(t, err)
buf := new(bytes.Buffer)
@ -232,13 +233,17 @@ func TestOpenReader(t *testing.T) {
_, err = io.Copy(writer, readerCloser)
assert.NoError(t, err)
}
fi, err := zw.Create(defaultXMLPathCalcChain)
fi, err := zw.Create(filePath)
assert.NoError(t, err)
_, err = fi.Write(MacintoshCyrillicCharset)
assert.NoError(t, err)
assert.NoError(t, zw.Close())
_, err = OpenReader(buf)
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")
}
// Test open spreadsheet with unzip size limit.
_, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipSizeLimit: 100})
@ -466,29 +471,16 @@ func TestGetCellHyperLink(t *testing.T) {
func TestSetSheetBackground(t *testing.T) {
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
}
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, err)
assert.NoError(t, f.SetSheetBackground("Sheet2", filepath.Join("test", "images", "background.jpg")))
assert.NoError(t, f.SetSheetBackground("Sheet2", filepath.Join("test", "images", "background.jpg")))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetBackground.xlsx")))
assert.NoError(t, f.Close())
}
func TestSetSheetBackgroundErrors(t *testing.T) {
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
}
assert.NoError(t, err)
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "not_exists", "not_exists.png"))
if assert.Error(t, err) {
@ -497,7 +489,16 @@ func TestSetSheetBackgroundErrors(t *testing.T) {
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "Book1.xlsx"))
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())
// 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
@ -1027,12 +1028,6 @@ func TestGetSheetComments(t *testing.T) {
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) {
f := NewFile()
f.WorkBook.BookViews = nil
@ -1334,6 +1329,10 @@ func TestAddVBAProject(t *testing.T) {
// Test add VBA project twice.
assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")))
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) {
@ -1341,7 +1340,8 @@ func TestContentTypesReader(t *testing.T) {
f := NewFile()
f.ContentTypes = nil
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) {
@ -1349,7 +1349,8 @@ func TestWorkbookReader(t *testing.T) {
f := NewFile()
f.WorkBook = nil
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) {
@ -1373,19 +1374,28 @@ func TestWorkSheetReader(t *testing.T) {
func TestRelsReader(t *testing.T) {
// Test unsupported charset.
f := NewFile()
rels := "xl/_rels/workbook.xml.rels"
rels := defaultXMLPathWorkbookRels
f.Relationships.Store(rels, nil)
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) {
f := NewFile()
rels := "xl/_rels/workbook.xml.rels"
rels := defaultXMLPathWorkbookRels
f.Relationships.Store(rels, nil)
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) {
_, err := attrValToInt("r", []xml.Attr{
{Name: xml.Name{Local: "r"}, Value: "s"},

15
file.go
View File

@ -30,7 +30,7 @@ func NewFile() *File {
f.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
f.Pkg.Store(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
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/worksheets/sheet1.xml", []byte(xml.Header+templateSheet))
f.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))
@ -39,18 +39,19 @@ func NewFile() *File {
f.SheetCount = 1
f.CalcChain, _ = f.calcChainReader()
f.Comments = make(map[string]*xlsxComments)
f.ContentTypes = f.contentTypesReader()
f.ContentTypes, _ = f.contentTypesReader()
f.Drawings = sync.Map{}
f.Styles, _ = f.stylesReader()
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
f.VMLDrawing = make(map[string]*vmlDrawing)
f.WorkBook = f.workbookReader()
f.WorkBook, _ = f.workbookReader()
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"
ws, _ := f.workSheetReader("Sheet1")
f.Sheet.Store("xl/worksheets/sheet1.xml", ws)
f.Theme = f.themeReader()
f.Theme, _ = f.themeReader()
return f
}
@ -119,7 +120,9 @@ func (f *File) WriteTo(w io.Writer, opts ...Options) (int64, error) {
if !ok {
return 0, ErrWorkbookFileFormat
}
f.setContentTypePartProjectExtensions(contentType)
if err := f.setContentTypePartProjectExtensions(contentType); err != nil {
return 0, err
}
}
if f.options != nil && f.options.Password != "" {
buf, err := f.WriteToBuffer()

View File

@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"os"
"path/filepath"
"strings"
"sync"
"testing"
@ -79,6 +80,14 @@ func TestWriteTo(t *testing.T) {
_, err := f.WriteTo(bufio.NewWriter(&buf))
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) {

View File

@ -187,7 +187,9 @@ func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, fi
if err != nil {
return err
}
f.addContentTypePart(drawingID, "drawings")
if err = f.addContentTypePart(drawingID, "drawings"); err != nil {
return err
}
f.addSheetNameSpace(sheet, SourceRelationship)
return err
}
@ -201,7 +203,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
name = strings.ToLower(sheet) + ".xml"
}
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
sheetRels := f.relsReader(rels)
sheetRels, _ := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
}
@ -235,11 +237,15 @@ func (f *File) addSheetDrawing(sheet string, rID int) {
// addSheetPicture provides a function to add picture element to
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
func (f *File) addSheetPicture(sheet string, rID int) {
ws, _ := f.workSheetReader(sheet)
func (f *File) addSheetPicture(sheet string, rID int) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
ws.Picture = &xlsxPicture{
RID: "rId" + strconv.Itoa(rID),
}
return err
}
// 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
// type for relationship parts and the Main Document part.
func (f *File) setContentTypePartImageExtensions() {
func (f *File) setContentTypePartImageExtensions() error {
imageTypes := map[string]string{
"jpeg": "image/", "png": "image/", "gif": "image/", "svg": "image/", "tiff": "image/",
"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()
defer content.Unlock()
for _, file := range content.Defaults {
@ -395,13 +404,17 @@ func (f *File) setContentTypePartImageExtensions() {
ContentType: prefix + extension,
})
}
return err
}
// setContentTypePartVMLExtensions provides a function to set the content type
// for relationship parts and the Main Document part.
func (f *File) setContentTypePartVMLExtensions() {
vml := false
content := f.contentTypesReader()
func (f *File) setContentTypePartVMLExtensions() error {
var vml bool
content, err := f.contentTypesReader()
if err != nil {
return err
}
content.Lock()
defer content.Unlock()
for _, v := range content.Defaults {
@ -415,12 +428,13 @@ func (f *File) setContentTypePartVMLExtensions() {
ContentType: ContentTypeVML,
})
}
return err
}
// addContentTypePart provides a function to add content type part
// relationships in the file [Content_Types].xml by given index.
func (f *File) addContentTypePart(index int, contentType string) {
setContentType := map[string]func(){
func (f *File) addContentTypePart(index int, contentType string) error {
setContentType := map[string]func() error{
"comments": f.setContentTypePartVMLExtensions,
"drawings": f.setContentTypePartImageExtensions,
}
@ -446,20 +460,26 @@ func (f *File) addContentTypePart(index int, contentType string) {
}
s, ok := setContentType[contentType]
if ok {
s()
if err := s(); err != nil {
return err
}
}
content, err := f.contentTypesReader()
if err != nil {
return err
}
content := f.contentTypesReader()
content.Lock()
defer content.Unlock()
for _, v := range content.Overrides {
if v.PartName == partNames[contentType] {
return
return err
}
}
content.Overrides = append(content.Overrides, xlsxOverride{
PartName: partNames[contentType],
ContentType: contentTypes[contentType],
})
return err
}
// 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"
}
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
sheetRels := f.relsReader(rels)
sheetRels, _ := f.relsReader(rels)
if sheetRels == nil {
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
// relationship ID.
func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
if drawingRels := f.relsReader(rels); drawingRels != nil {
if drawingRels, _ := f.relsReader(rels); drawingRels != nil {
drawingRels.Lock()
defer drawingRels.Unlock()
for _, v := range drawingRels.Relationships {

View File

@ -67,6 +67,12 @@ func TestAddPicture(t *testing.T) {
// Test write file to given path.
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture1.xlsx")))
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) {
@ -236,3 +242,27 @@ func TestDrawingResize(t *testing.T) {
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())
}
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")
}

View File

@ -160,10 +160,10 @@ func (f *File) AddPivotTable(opts *PivotTableOptions) error {
}
pivotTableSheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(pivotTableSheetPath, "xl/worksheets/") + ".rels"
f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
f.addContentTypePart(pivotTableID, "pivotTable")
f.addContentTypePart(pivotCacheID, "pivotCache")
return nil
if err = f.addContentTypePart(pivotTableID, "pivotTable"); err != nil {
return err
}
return f.addContentTypePart(pivotCacheID, "pivotCache")
}
// 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.
func (f *File) addWorkbookPivotCache(RID int) int {
wb := f.workbookReader()
wb, _ := f.workbookReader()
if wb.PivotCaches == nil {
wb.PivotCaches = &xlsxPivotCaches{}
}

View File

@ -259,6 +259,15 @@ func TestAddPivotTable(t *testing.T) {
// Test get pivot fields index with empty data range
_, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOptions{})
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) {

View File

@ -435,8 +435,13 @@ func (f *File) sharedStringsReader() (*xlsxSST, error) {
f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
}
}
f.addContentTypePart(0, "sharedStrings")
rels := f.relsReader(relPath)
if err = f.addContentTypePart(0, "sharedStrings"); err != nil {
return f.SharedStrings, err
}
rels, err := f.relsReader(relPath)
if err != nil {
return f.SharedStrings, err
}
for _, rel := range rels.Relationships {
if rel.Target == "/xl/sharedStrings.xml" {
return f.SharedStrings, nil

View File

@ -235,9 +235,20 @@ func TestSharedStringsReader(t *testing.T) {
f := NewFile()
// Test read shared string with unsupported charset.
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
f.sharedStringsReader()
si := xlsxSI{}
assert.EqualValues(t, "", si.String())
_, err := f.sharedStringsReader()
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
// 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) {

View File

@ -308,8 +308,7 @@ func (f *File) AddShape(sheet, cell, opts string) error {
if err = f.addDrawingShape(sheet, drawingXML, cell, options); err != nil {
return err
}
f.addContentTypePart(drawingID, "drawings")
return err
return f.addContentTypePart(drawingID, "drawings")
}
// addDrawingShape provides a function to add preset geometry by given sheet,

View File

@ -87,10 +87,15 @@ func TestAddShape(t *testing.T) {
}
}`))
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.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")
// 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) {

129
sheet.go
View File

@ -17,7 +17,6 @@ import (
"encoding/xml"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
@ -47,7 +46,7 @@ func (f *File) NewSheet(sheet string) int {
}
f.DeleteSheet(sheet)
f.SheetCount++
wb := f.workbookReader()
wb, _ := f.workbookReader()
sheetID := 0
for _, v := range wb.Sheets.Sheet {
if v.SheetID > sheetID {
@ -56,7 +55,7 @@ func (f *File) NewSheet(sheet string) int {
}
sheetID++
// 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
f.setSheet(sheetID, sheet)
// 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
// [Content_Types].xml structure after deserialization.
func (f *File) contentTypesReader() *xlsxTypes {
var err error
func (f *File) contentTypesReader() (*xlsxTypes, error) {
if f.ContentTypes == nil {
f.ContentTypes = new(xlsxTypes)
f.ContentTypes.Lock()
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 {
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
@ -215,14 +212,18 @@ func trimCell(column []xlsxC) []xlsxC {
// setContentTypes provides a function to read and update property of contents
// type of the spreadsheet.
func (f *File) setContentTypes(partName, contentType string) {
content := f.contentTypesReader()
func (f *File) setContentTypes(partName, contentType string) error {
content, err := f.contentTypesReader()
if err != nil {
return err
}
content.Lock()
defer content.Unlock()
content.Overrides = append(content.Overrides, xlsxOverride{
PartName: partName,
ContentType: contentType,
})
return err
}
// setSheet provides a function to update sheet property by given index.
@ -271,7 +272,7 @@ func (f *File) SetActiveSheet(index int) {
if index < 0 {
index = 0
}
wb := f.workbookReader()
wb, _ := f.workbookReader()
for activeTab := range wb.Sheets.Sheet {
if activeTab == index {
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.
func (f *File) GetActiveSheetIndex() (index int) {
sheetID := f.getActiveSheetID()
wb := f.workbookReader()
wb, _ := f.workbookReader()
if wb != nil {
for idx, sheet := range wb.Sheets.Sheet {
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
// spreadsheet. If not found the active sheet will be return integer 0.
func (f *File) getActiveSheetID() int {
wb := f.workbookReader()
wb, _ := f.workbookReader()
if wb != nil {
if wb.BookViews != nil && len(wb.BookViews.WorkBookView) > 0 {
activeTab := wb.BookViews.WorkBookView[0].ActiveTab
@ -357,10 +358,10 @@ func (f *File) SetSheetName(source, target string) {
if strings.EqualFold(target, source) {
return
}
content := f.workbookReader()
for k, v := range content.Sheets.Sheet {
wb, _ := f.workbookReader()
for k, v := range wb.Sheets.Sheet {
if v.Name == source {
content.Sheets.Sheet[k].Name = target
wb.Sheets.Sheet[k].Name = target
f.sheetMap[target] = f.sheetMap[source]
delete(f.sheetMap, source)
}
@ -422,7 +423,7 @@ func (f *File) GetSheetIndex(sheet string) int {
// fmt.Println(index, name)
// }
func (f *File) GetSheetMap() map[int]string {
wb := f.workbookReader()
wb, _ := f.workbookReader()
sheetMap := map[int]string{}
if wb != nil {
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
// dialog sheets name list of the workbook.
func (f *File) GetSheetList() (list []string) {
wb := f.workbookReader()
wb, _ := f.workbookReader()
if wb != nil {
for _, sheet := range wb.Sheets.Sheet {
list = append(list, sheet.Name)
@ -448,8 +449,10 @@ func (f *File) GetSheetList() (list []string) {
// of the spreadsheet.
func (f *File) getSheetMap() map[string]string {
maps := map[string]string{}
for _, v := range f.workbookReader().Sheets.Sheet {
for _, rel := range f.relsReader(f.getWorkbookRelsPath()).Relationships {
wb, _ := f.workbookReader()
rels, _ := f.relsReader(f.getWorkbookRelsPath())
for _, v := range wb.Sheets.Sheet {
for _, rel := range rels.Relationships {
if rel.ID == v.ID {
sheetXMLPath := f.getWorksheetPath(rel.Target)
if _, ok := f.Pkg.Load(sheetXMLPath); ok {
@ -498,10 +501,11 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
f.addSheetPicture(sheet, rID)
f.addSheetNameSpace(sheet, SourceRelationship)
f.setContentTypePartImageExtensions()
if err = f.addSheetPicture(sheet, rID); err != nil {
return err
}
f.addSheetNameSpace(sheet, SourceRelationship)
return f.setContentTypePartImageExtensions()
}
// DeleteSheet provides a function to delete worksheet in a workbook by given
@ -514,8 +518,8 @@ func (f *File) DeleteSheet(sheet string) {
return
}
sheetName := trimSheetName(sheet)
wb := f.workbookReader()
wbRels := f.relsReader(f.getWorkbookRelsPath())
wb, _ := f.workbookReader()
wbRels, _ := f.relsReader(f.getWorkbookRelsPath())
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
deleteLocalSheetID := f.GetSheetIndex(sheet)
deleteAndAdjustDefinedNames(wb, deleteLocalSheetID)
@ -537,8 +541,8 @@ func (f *File) DeleteSheet(sheet string) {
}
}
target := f.deleteSheetFromWorkbookRels(v.ID)
f.deleteSheetFromContentTypes(target)
f.deleteCalcChain(f.getSheetID(sheet), "")
_ = f.deleteSheetFromContentTypes(target)
_ = f.deleteCalcChain(f.getSheetID(sheet), "")
delete(f.sheetMap, v.Name)
f.Pkg.Delete(sheetXML)
f.Pkg.Delete(rels)
@ -573,12 +577,12 @@ func deleteAndAdjustDefinedNames(wb *xlsxWorkbook, deleteLocalSheetID int) {
// deleteSheetFromWorkbookRels provides a function to remove worksheet
// relationships by given relationships ID in the file workbook.xml.rels.
func (f *File) deleteSheetFromWorkbookRels(rID string) string {
content := f.relsReader(f.getWorkbookRelsPath())
content.Lock()
defer content.Unlock()
for k, v := range content.Relationships {
rels, _ := f.relsReader(f.getWorkbookRelsPath())
rels.Lock()
defer rels.Unlock()
for k, v := range rels.Relationships {
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
}
}
@ -587,11 +591,14 @@ func (f *File) deleteSheetFromWorkbookRels(rID string) string {
// deleteSheetFromContentTypes provides a function to remove worksheet
// 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, "/") {
target = "/xl/" + target
}
content := f.contentTypesReader()
content, err := f.contentTypesReader()
if err != nil {
return err
}
content.Lock()
defer content.Unlock()
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:]...)
}
}
return err
}
// 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)
func (f *File) SetSheetVisible(sheet string, visible bool) error {
sheet = trimSheetName(sheet)
content := f.workbookReader()
wb, err := f.workbookReader()
if err != nil {
return err
}
if visible {
for k, v := range content.Sheets.Sheet {
for k, v := range wb.Sheets.Sheet {
if strings.EqualFold(v.Name, sheet) {
content.Sheets.Sheet[k].State = ""
wb.Sheets.Sheet[k].State = ""
}
}
return nil
return err
}
count := 0
for _, v := range content.Sheets.Sheet {
for _, v := range wb.Sheets.Sheet {
if v.State != "hidden" {
count++
}
}
for k, v := range content.Sheets.Sheet {
for k, v := range wb.Sheets.Sheet {
ws, err := f.workSheetReader(v.Name)
if err != nil {
return err
@ -684,10 +695,10 @@ func (f *File) SetSheetVisible(sheet string, visible bool) error {
tabSelected = ws.SheetViews.SheetView[0].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.
@ -830,10 +841,11 @@ func (f *File) SetPanes(sheet, panes string) error {
//
// f.GetSheetVisible("Sheet1")
func (f *File) GetSheetVisible(sheet string) bool {
content, name, visible := f.workbookReader(), trimSheetName(sheet), false
for k, v := range content.Sheets.Sheet {
name, visible := trimSheetName(sheet), false
wb, _ := f.workbookReader()
for k, v := range wb.Sheets.Sheet {
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
}
}
@ -1460,7 +1472,10 @@ func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
// Scope: "Sheet2",
// })
func (f *File) SetDefinedName(definedName *DefinedName) error {
wb := f.workbookReader()
wb, err := f.workbookReader()
if err != nil {
return err
}
d := xlsxDefinedName{
Name: definedName.Name,
Comment: definedName.Comment,
@ -1499,7 +1514,10 @@ func (f *File) SetDefinedName(definedName *DefinedName) error {
// Scope: "Sheet2",
// })
func (f *File) DeleteDefinedName(definedName *DefinedName) error {
wb := f.workbookReader()
wb, err := f.workbookReader()
if err != nil {
return err
}
if wb.DefinedNames != nil {
for idx, dn := range wb.DefinedNames.DefinedName {
scope := "Workbook"
@ -1512,7 +1530,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
}
if scope == deleteScope && dn.Name == definedName.Name {
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.
func (f *File) GetDefinedName() []DefinedName {
var definedNames []DefinedName
wb := f.workbookReader()
wb, _ := f.workbookReader()
if wb.DefinedNames != nil {
for _, dn := range wb.DefinedNames.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
// after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
func (f *File) relsReader(path string) *xlsxRelationships {
var err error
func (f *File) relsReader(path string) (*xlsxRelationships, error) {
rels, _ := f.Relationships.Load(path)
if rels == nil {
if _, ok := f.Pkg.Load(path); ok {
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 {
log.Printf("xml decode error: %s", err)
return nil, err
}
f.Relationships.Store(path, &c)
}
}
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

View File

@ -188,6 +188,17 @@ func TestDefinedName(t *testing.T) {
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[0].RefersTo)
assert.Exactly(t, 1, len(f.GetDefinedName()))
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) {
@ -367,6 +378,32 @@ func TestGetSheetID(t *testing.T) {
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) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {

View File

@ -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.File.addContentTypePart(tableID, "table")
if err = sw.File.addContentTypePart(tableID, "table"); err != nil {
return err
}
b, _ := xml.Marshal(table)
sw.File.saveFileList(tableXML, b)
return nil
return err
}
// 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.
func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
var err error
@ -488,15 +506,7 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
case time.Duration:
c.T, c.V = setCellDuration(val)
case time.Time:
var isNum bool
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
}
err = sw.setCellTime(c, val)
case bool:
c.T, c.V = setCellBool(val)
case nil:

View File

@ -186,7 +186,7 @@ func TestStreamTable(t *testing.T) {
}
// Write a table.
assert.NoError(t, streamWriter.AddTable("A1", "C2", ``))
assert.NoError(t, streamWriter.AddTable("A1", "C2", ""))
assert.NoError(t, streamWriter.Flush())
// 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, "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.
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.
assert.EqualError(t, streamWriter.AddTable("A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).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) {
@ -242,7 +246,7 @@ func TestStreamMarshalAttrs(t *testing.T) {
}
func TestStreamSetRow(t *testing.T) {
// Test error exceptions
// Test error exceptions.
file := NewFile()
defer func() {
assert.NoError(t, file.Close())
@ -250,9 +254,13 @@ func TestStreamSetRow(t *testing.T) {
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
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.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) {

View File

@ -17,7 +17,6 @@ import (
"encoding/xml"
"fmt"
"io"
"log"
"math"
"reflect"
"strconv"
@ -3357,16 +3356,16 @@ func getPaletteColor(color string) string {
// themeReader provides a function to get the pointer to the xl/theme/theme1.xml
// structure after deserialization.
func (f *File) themeReader() *xlsxTheme {
func (f *File) themeReader() (*xlsxTheme, error) {
if _, ok := f.Pkg.Load(defaultXMLPathTheme); !ok {
return nil
return nil, nil
}
theme := xlsxTheme{XMLNSa: NameSpaceDrawingML.Value, XMLNSr: SourceRelationship.Value}
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathTheme)))).
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.

View File

@ -366,7 +366,9 @@ func TestThemeReader(t *testing.T) {
f := NewFile()
// Test read theme with unsupported charset.
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) {

View File

@ -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 {
return err
}
f.addContentTypePart(tableID, "table")
return err
return f.addContentTypePart(tableID, "table")
}
// 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)
cellEnd, _ := CoordinatesToCellName(vCol, vRow, true)
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
wb := f.workbookReader()
wb, err := f.workbookReader()
if err != nil {
return err
}
sheetID := f.GetSheetIndex(sheet)
filterRange := fmt.Sprintf("'%s'!%s", sheet, ref)
d := xlsxDefinedName{

View File

@ -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", "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) {

View File

@ -23,6 +23,7 @@ const (
defaultXMLPathStyles = "xl/styles.xml"
defaultXMLPathTheme = "xl/theme/theme1.xml"
defaultXMLPathWorkbook = "xl/workbook.xml"
defaultXMLPathWorkbookRels = "xl/_rels/workbook.xml.rels"
defaultTempFileSST = "sharedStrings"
)

View File

@ -15,7 +15,6 @@ import (
"bytes"
"encoding/xml"
"io"
"log"
"path/filepath"
"strconv"
"strings"
@ -23,7 +22,10 @@ import (
// SetWorkbookProps provides a function to sets workbook properties.
func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
wb := f.workbookReader()
wb, err := f.workbookReader()
if err != nil {
return err
}
if wb.WorkbookPr == nil {
wb.WorkbookPr = new(xlsxWorkbookPr)
}
@ -44,20 +46,24 @@ func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
// GetWorkbookProps provides a function to gets workbook properties.
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 {
opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
}
return opts, nil
return opts, err
}
// setWorkbook update workbook property of the spreadsheet. Maximum 31
// characters are allowed in sheet title.
func (f *File) setWorkbook(name string, sheetID, rid int) {
content := f.workbookReader()
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
wb, _ := f.workbookReader()
wb.Sheets.Sheet = append(wb.Sheets.Sheet, xlsxSheet{
Name: trimSheetName(name),
SheetID: sheetID,
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
// the spreadsheet.
func (f *File) getWorkbookPath() (path string) {
if rels := f.relsReader("_rels/.rels"); rels != nil {
if rels, _ := f.relsReader("_rels/.rels"); rels != nil {
rels.Lock()
defer rels.Unlock()
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
// structure after deserialization.
func (f *File) workbookReader() *xlsxWorkbook {
func (f *File) workbookReader() (*xlsxWorkbook, error) {
var err error
if f.WorkBook == nil {
wbPath := f.getWorkbookPath()
@ -107,10 +113,10 @@ func (f *File) workbookReader() *xlsxWorkbook {
}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
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

View File

@ -9,7 +9,8 @@ import (
func TestWorkbookProps(t *testing.T) {
f := NewFile()
assert.NoError(t, f.SetWorkbookProps(nil))
wb := f.workbookReader()
wb, err := f.workbookReader()
assert.NoError(t, err)
wb.WorkbookPr = nil
expected := WorkbookPropsOptions{
Date1904: boolPtr(true),
@ -20,4 +21,13 @@ func TestWorkbookProps(t *testing.T) {
opts, err := f.GetWorkbookProps()
assert.NoError(t, err)
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")
}