From 6a5ee811ba200ce9524720822507477131401f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=AD=E4=BC=9F=E5=8C=A1?= <673411814@qq.com> Date: Fri, 23 Dec 2022 00:54:40 +0800 Subject: [PATCH] This closes #1425, breaking changes for sheet name (#1426) - Checking and return error for invalid sheet name instead of trim invalid characters - Add error return for the 4 functions: `DeleteSheet`, `GetSheetIndex`, `GetSheetVisible` and `SetSheetName` - Export new error 4 constants: `ErrSheetNameBlank`, `ErrSheetNameInvalid`, `ErrSheetNameLength` and `ErrSheetNameSingleQuote` - Rename exported error constant `ErrExistsWorksheet` to `ErrExistsSheet` - Update unit tests for 90 functions: `AddChart`, `AddChartSheet`, `AddComment`, `AddDataValidation`, `AddPicture`, `AddPictureFromBytes`, `AddPivotTable`, `AddShape`, `AddSparkline`, `AddTable`, `AutoFilter`, `CalcCellValue`, `Cols`, `DeleteChart`, `DeleteComment`, `DeleteDataValidation`, `DeletePicture`, `DeleteSheet`, `DuplicateRow`, `DuplicateRowTo`, `GetCellFormula`, `GetCellHyperLink`, `GetCellRichText`, `GetCellStyle`, `GetCellType`, `GetCellValue`, `GetColOutlineLevel`, `GetCols`, `GetColStyle`, `GetColVisible`, `GetColWidth`, `GetConditionalFormats`, `GetDataValidations`, `GetMergeCells`, `GetPageLayout`, `GetPageMargins`, `GetPicture`, `GetRowHeight`, `GetRowOutlineLevel`, `GetRows`, `GetRowVisible`, `GetSheetIndex`, `GetSheetProps`, `GetSheetVisible`, `GroupSheets`, `InsertCol`, `InsertPageBreak`, `InsertRows`, `MergeCell`, `NewSheet`, `NewStreamWriter`, `ProtectSheet`, `RemoveCol`, `RemovePageBreak`, `RemoveRow`, `Rows`, `SearchSheet`, `SetCellBool`, `SetCellDefault`, `SetCellFloat`, `SetCellFormula`, `SetCellHyperLink`, `SetCellInt`, `SetCellRichText`, `SetCellStr`, `SetCellStyle`, `SetCellValue`, `SetColOutlineLevel`, `SetColStyle`, `SetColVisible`, `SetColWidth`, `SetConditionalFormat`, `SetHeaderFooter`, `SetPageLayout`, `SetPageMargins`, `SetPanes`, `SetRowHeight`, `SetRowOutlineLevel`, `SetRowStyle`, `SetRowVisible`, `SetSheetBackground`, `SetSheetBackgroundFromBytes`, `SetSheetCol`, `SetSheetName`, `SetSheetProps`, `SetSheetRow`, `SetSheetVisible`, `UnmergeCell`, `UnprotectSheet` and `UnsetConditionalFormat` - Update documentation of the set style functions Co-authored-by: guoweikuang --- calc.go | 11 +-- calc_test.go | 9 ++- cell_test.go | 92 +++++++++++++-------- chart.go | 10 ++- chart_test.go | 16 ++-- col.go | 5 +- col_test.go | 81 +++++++++++++------ comment.go | 3 + comment_test.go | 17 ++-- datavalidation_test.go | 18 +++-- errors.go | 17 +++- excelize.go | 3 + excelize_test.go | 176 +++++++++++++++++++++++------------------ merge_test.go | 21 +++-- picture_test.go | 63 +++++++++------ pivotTable_test.go | 6 ++ rows.go | 5 +- rows_test.go | 75 ++++++++++++------ shape_test.go | 8 +- sheet.go | 170 +++++++++++++++++++++------------------ sheet_test.go | 124 +++++++++++++++++++++++------ sheetpr_test.go | 18 ++++- sparkline_test.go | 10 ++- stream.go | 9 ++- stream_test.go | 3 + styles.go | 40 +++++++--- styles_test.go | 29 ++++--- table.go | 5 +- table_test.go | 49 ++++-------- workbook.go | 2 +- xmlComments.go | 2 +- xmlDrawing.go | 119 ++++++++++++++-------------- 32 files changed, 761 insertions(+), 455 deletions(-) diff --git a/calc.go b/calc.go index 9c360c1..9ab5eeb 100644 --- a/calc.go +++ b/calc.go @@ -11460,19 +11460,20 @@ func (fn *formulaFuncs) SHEET(argsList *list.List) formulaArg { return newErrorFormulaArg(formulaErrorVALUE, "SHEET accepts at most 1 argument") } if argsList.Len() == 0 { - return newNumberFormulaArg(float64(fn.f.GetSheetIndex(fn.sheet) + 1)) + idx, _ := fn.f.GetSheetIndex(fn.sheet) + return newNumberFormulaArg(float64(idx + 1)) } arg := argsList.Front().Value.(formulaArg) - if sheetIdx := fn.f.GetSheetIndex(arg.Value()); sheetIdx != -1 { + if sheetIdx, _ := fn.f.GetSheetIndex(arg.Value()); sheetIdx != -1 { return newNumberFormulaArg(float64(sheetIdx + 1)) } if arg.cellRanges != nil && arg.cellRanges.Len() > 0 { - if sheetIdx := fn.f.GetSheetIndex(arg.cellRanges.Front().Value.(cellRange).From.Sheet); sheetIdx != -1 { + if sheetIdx, _ := fn.f.GetSheetIndex(arg.cellRanges.Front().Value.(cellRange).From.Sheet); sheetIdx != -1 { return newNumberFormulaArg(float64(sheetIdx + 1)) } } if arg.cellRefs != nil && arg.cellRefs.Len() > 0 { - if sheetIdx := fn.f.GetSheetIndex(arg.cellRefs.Front().Value.(cellRef).Sheet); sheetIdx != -1 { + if sheetIdx, _ := fn.f.GetSheetIndex(arg.cellRefs.Front().Value.(cellRef).Sheet); sheetIdx != -1 { return newNumberFormulaArg(float64(sheetIdx + 1)) } } @@ -13960,7 +13961,7 @@ func (fn *formulaFuncs) ADDRESS(argsList *list.List) formulaArg { } var sheetText string if argsList.Len() == 5 { - sheetText = fmt.Sprintf("%s!", trimSheetName(argsList.Back().Value.(formulaArg).Value())) + sheetText = fmt.Sprintf("%s!", argsList.Back().Value.(formulaArg).Value()) } formatter := addressFmtMaps[fmt.Sprintf("%d_%s", int(absNum.Number), a1.Value())] addr, err := formatter(int(colNum.Number), int(rowNum.Number)) diff --git a/calc_test.go b/calc_test.go index 1376c00..1c1f4d5 100644 --- a/calc_test.go +++ b/calc_test.go @@ -4389,16 +4389,19 @@ func TestCalcCellValue(t *testing.T) { assert.NoError(t, err) } - // Test get calculated cell value on not formula cell. + // Test get calculated cell value on not formula cell f := prepareCalcData(cellData) result, err := f.CalcCellValue("Sheet1", "A1") assert.NoError(t, err) assert.Equal(t, "", result) - // Test get calculated cell value on not exists worksheet. + // Test get calculated cell value on not exists worksheet f = prepareCalcData(cellData) _, err = f.CalcCellValue("SheetN", "A1") assert.EqualError(t, err, "sheet SheetN does not exist") - // Test get calculated cell value with not support formula. + // Test get calculated cell value with invalid sheet name + _, err = f.CalcCellValue("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test get calculated cell value with not support formula f = prepareCalcData(cellData) assert.NoError(t, f.SetCellFormula("Sheet1", "A1", "=UNSUPPORT(A1)")) _, err = f.CalcCellValue("Sheet1", "A1") diff --git a/cell_test.go b/cell_test.go index 40bab9b..69a3f81 100644 --- a/cell_test.go +++ b/cell_test.go @@ -167,13 +167,15 @@ func TestSetCellFloat(t *testing.T) { }) f := NewFile() assert.EqualError(t, f.SetCellFloat(sheet, "A", 123.42, -1, 64), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + // Test set cell float data type value with invalid sheet name + assert.EqualError(t, f.SetCellFloat("Sheet:1", "A1", 123.42, -1, 64), ErrSheetNameInvalid.Error()) } 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,11 +191,13 @@ 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 invalid sheet name + assert.EqualError(t, f.SetCellValue("Sheet:1", "A1", "A1"), ErrSheetNameInvalid.Error()) + // 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. + // 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") @@ -208,7 +212,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) @@ -220,6 +224,8 @@ func TestSetCellValues(t *testing.T) { func TestSetCellBool(t *testing.T) { f := NewFile() assert.EqualError(t, f.SetCellBool("Sheet1", "A", true), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + // Test set cell boolean data type value with invalid sheet name + assert.EqualError(t, f.SetCellBool("Sheet:1", "A1", true), ErrSheetNameInvalid.Error()) } func TestSetCellTime(t *testing.T) { @@ -242,7 +248,7 @@ func TestSetCellTime(t *testing.T) { } func TestGetCellValue(t *testing.T) { - // Test get cell value without r attribute of the row. + // Test get cell value without r attribute of the row f := NewFile() sheetData := `%s` @@ -387,11 +393,14 @@ func TestGetCellValue(t *testing.T) { }, rows[0]) assert.NoError(t, err) - // Test get cell value with unsupported charset shared strings table. + // Test get cell value with unsupported charset shared strings table f.SharedStrings = nil f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset) _, value := f.GetCellValue("Sheet1", "A1") assert.EqualError(t, value, "XML syntax error on line 1: invalid UTF-8") + // Test get cell value with invalid sheet name + _, err = f.GetCellValue("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestGetCellType(t *testing.T) { @@ -405,6 +414,9 @@ func TestGetCellType(t *testing.T) { assert.Equal(t, CellTypeSharedString, cellType) _, err = f.GetCellType("Sheet1", "A") assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + // Test get cell type with invalid sheet name + _, err = f.GetCellType("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestGetValueFrom(t *testing.T) { @@ -418,12 +430,16 @@ func TestGetValueFrom(t *testing.T) { } func TestGetCellFormula(t *testing.T) { - // Test get cell formula on not exist worksheet. + // Test get cell formula on not exist worksheet f := NewFile() _, err := f.GetCellFormula("SheetN", "A1") assert.EqualError(t, err, "sheet SheetN does not exist") - // Test get cell formula on no formula cell. + // Test get cell formula with invalid sheet name + _, err = f.GetCellFormula("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + + // Test get cell formula on no formula cell assert.NoError(t, f.SetCellValue("Sheet1", "A1", true)) _, err = f.GetCellFormula("Sheet1", "A1") assert.NoError(t, err) @@ -497,7 +513,10 @@ func TestSetCellFormula(t *testing.T) { assert.NoError(t, f.SetCellFormula("Sheet1", "B19", "SUM(Sheet2!D2,Sheet2!D11)")) assert.NoError(t, f.SetCellFormula("Sheet1", "C19", "SUM(Sheet2!D2,Sheet2!D9)")) - // Test set cell formula with illegal rows number. + // Test set cell formula with invalid sheet name + assert.EqualError(t, f.SetCellFormula("Sheet:1", "A1", "SUM(1,2)"), ErrSheetNameInvalid.Error()) + + // Test set cell formula with illegal rows number assert.EqualError(t, f.SetCellFormula("Sheet1", "C", "SUM(Sheet2!D2,Sheet2!D9)"), newCellNameToCoordinatesError("C", newInvalidCellNameError("C")).Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula1.xlsx"))) @@ -507,15 +526,15 @@ func TestSetCellFormula(t *testing.T) { if !assert.NoError(t, err) { t.FailNow() } - // Test remove cell formula. + // Test remove cell formula assert.NoError(t, f.SetCellFormula("Sheet1", "A1", "")) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula2.xlsx"))) - // Test remove all cell formula. + // Test remove all cell formula assert.NoError(t, f.SetCellFormula("Sheet1", "B1", "")) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula3.xlsx"))) assert.NoError(t, f.Close()) - // Test set shared formula for the cells. + // Test set shared formula for the cells f = NewFile() for r := 1; r <= 5; r++ { assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", r), &[]interface{}{r, r + 1})) @@ -533,7 +552,7 @@ func TestSetCellFormula(t *testing.T) { assert.EqualError(t, f.SetCellFormula("Sheet1", "D1", "=A1+C1", FormulaOpts{Ref: &ref, Type: &formulaType}), ErrParameterInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula5.xlsx"))) - // Test set table formula for the cells. + // Test set table formula for the cells f = NewFile() for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} { assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row)) @@ -583,46 +602,49 @@ func TestGetCellRichText(t *testing.T) { runsSource[1].Font.Color = strings.ToUpper(runsSource[1].Font.Color) assert.True(t, reflect.DeepEqual(runsSource[1].Font, runs[1].Font), "should get the same font") - // Test get cell rich text when string item index overflow. + // Test get cell rich text when string item index overflow ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "2" runs, err = f.GetCellRichText("Sheet1", "A1") assert.NoError(t, err) assert.Equal(t, 0, len(runs)) - // Test get cell rich text when string item index is negative. + // Test get cell rich text when string item index is negative ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "-1" runs, err = f.GetCellRichText("Sheet1", "A1") assert.NoError(t, err) assert.Equal(t, 0, len(runs)) - // Test get cell rich text on invalid string item index. + // Test get cell rich text on invalid string item index ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "x" _, err = f.GetCellRichText("Sheet1", "A1") assert.EqualError(t, err, "strconv.Atoi: parsing \"x\": invalid syntax") - // Test set cell rich text on not exists worksheet. + // Test set cell rich text on not exists worksheet _, err = f.GetCellRichText("SheetN", "A1") assert.EqualError(t, err, "sheet SheetN does not exist") - // Test set cell rich text with illegal cell reference. + // Test set cell rich text with illegal cell reference _, err = f.GetCellRichText("Sheet1", "A") assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) - // Test set rich text color theme without tint. + // Test set rich text color theme without tint assert.NoError(t, f.SetCellRichText("Sheet1", "A1", []RichTextRun{{Font: &Font{ColorTheme: &theme}}})) - // Test set rich text color tint without theme. + // Test set rich text color tint without theme assert.NoError(t, f.SetCellRichText("Sheet1", "A1", []RichTextRun{{Font: &Font{ColorTint: 0.5}}})) - // Test set cell rich text with unsupported charset shared strings table. + // Test set cell rich text with unsupported charset shared strings table f.SharedStrings = nil f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset) assert.EqualError(t, f.SetCellRichText("Sheet1", "A1", runsSource), "XML syntax error on line 1: invalid UTF-8") - // Test get cell rich text with unsupported charset shared strings table. + // Test get cell rich text with unsupported charset shared strings table f.SharedStrings = nil f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset) _, err = f.GetCellRichText("Sheet1", "A1") assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") + // Test get cell rich text with invalid sheet name + _, err = f.GetCellRichText("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestSetCellRichText(t *testing.T) { @@ -716,12 +738,14 @@ func TestSetCellRichText(t *testing.T) { assert.NoError(t, err) assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style)) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellRichText.xlsx"))) - // Test set cell rich text on not exists worksheet. + // Test set cell rich text on not exists worksheet assert.EqualError(t, f.SetCellRichText("SheetN", "A1", richTextRun), "sheet SheetN does not exist") - // Test set cell rich text with illegal cell reference. + // Test set cell rich text with invalid sheet name + assert.EqualError(t, f.SetCellRichText("Sheet:1", "A1", richTextRun), ErrSheetNameInvalid.Error()) + // Test set cell rich text with illegal cell reference assert.EqualError(t, f.SetCellRichText("Sheet1", "A", richTextRun), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) richTextRun = []RichTextRun{{Text: strings.Repeat("s", TotalCellChars+1)}} - // Test set cell rich text with characters over the maximum limit. + // Test set cell rich text with characters over the maximum limit assert.EqualError(t, f.SetCellRichText("Sheet1", "A1", richTextRun), ErrCellCharsLength.Error()) } @@ -747,7 +771,7 @@ func TestFormattedValue(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "03/04/2019", result) - // Test format value with no built-in number format ID. + // Test format value with no built-in number format ID numFmtID := 5 f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ NumFmtID: &numFmtID, @@ -756,7 +780,7 @@ func TestFormattedValue(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "43528", result) - // Test format value with invalid number format ID. + // Test format value with invalid number format ID f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ NumFmtID: nil, }) @@ -764,7 +788,7 @@ func TestFormattedValue(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "43528", result) - // Test format value with empty number format. + // Test format value with empty number format f.Styles.NumFmts = nil f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ NumFmtID: &numFmtID, @@ -773,7 +797,7 @@ func TestFormattedValue(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "43528", result) - // Test format decimal value with build-in number format ID. + // Test format decimal value with build-in number format ID styleID, err := f.NewStyle(&Style{ NumFmt: 1, }) @@ -786,13 +810,13 @@ func TestFormattedValue(t *testing.T) { assert.Equal(t, "0_0", fn("0_0", "", false)) } - // Test format value with unsupported charset workbook. + // 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.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.formattedValue(1, "43528", false) @@ -800,7 +824,7 @@ func TestFormattedValue(t *testing.T) { } func TestFormattedValueNilXfs(t *testing.T) { - // Set the CellXfs to nil and verify that the formattedValue function does not crash. + // Set the CellXfs to nil and verify that the formattedValue function does not crash f := NewFile() f.Styles.CellXfs = nil result, err := f.formattedValue(3, "43528", false) @@ -809,7 +833,7 @@ func TestFormattedValueNilXfs(t *testing.T) { } func TestFormattedValueNilNumFmts(t *testing.T) { - // Set the NumFmts value to nil and verify that the formattedValue function does not crash. + // Set the NumFmts value to nil and verify that the formattedValue function does not crash f := NewFile() f.Styles.NumFmts = nil result, err := f.formattedValue(3, "43528", false) @@ -818,7 +842,7 @@ func TestFormattedValueNilNumFmts(t *testing.T) { } func TestFormattedValueNilWorkbook(t *testing.T) { - // Set the Workbook value to nil and verify that the formattedValue function does not crash. + // Set the Workbook value to nil and verify that the formattedValue function does not crash f := NewFile() f.WorkBook = nil result, err := f.formattedValue(3, "43528", false) diff --git a/chart.go b/chart.go index ec5e468..d4ea8d9 100644 --- a/chart.go +++ b/chart.go @@ -941,8 +941,12 @@ func (f *File) AddChart(sheet, cell, opts string, combo ...string) error { // a chart. func (f *File) AddChartSheet(sheet, opts string, combo ...string) error { // Check if the worksheet already exists - if f.GetSheetIndex(sheet) != -1 { - return ErrExistsWorksheet + idx, err := f.GetSheetIndex(sheet) + if err != nil { + return err + } + if idx != -1 { + return ErrExistsSheet } options, comboCharts, err := f.getChartOptions(opts, combo) if err != nil { @@ -963,7 +967,7 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error { } sheetID++ path := "xl/chartsheets/sheet" + strconv.Itoa(sheetID) + ".xml" - f.sheetMap[trimSheetName(sheet)] = path + f.sheetMap[sheet] = path f.Sheet.Store(path, nil) drawingID := f.countDrawings() + 1 chartID := f.countCharts() + 1 diff --git a/chart_test.go b/chart_test.go index 61e7c15..a61f1d7 100644 --- a/chart_test.go +++ b/chart_test.go @@ -217,6 +217,8 @@ func TestAddChart(t *testing.T) { assert.NoError(t, f.AddChart("Combo Charts", axis, fmt.Sprintf(`{"type":"areaStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"}],"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":"%s"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[1]), fmt.Sprintf(`{"type":"%s","series":[{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[0]))) } assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx"))) + // Test with invalid sheet name + assert.EqualError(t, f.AddChart("Sheet:1", "A1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}]}`), ErrSheetNameInvalid.Error()) // Test with illegal cell reference assert.EqualError(t, f.AddChart("Sheet2", "A", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) // Test with unsupported chart type @@ -257,14 +259,16 @@ func TestAddChartSheet(t *testing.T) { // Test cell value on chartsheet assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is not a worksheet") // Test add chartsheet on already existing name sheet - assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), ErrExistsWorksheet.Error()) + assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), ErrExistsSheet.Error()) + // Test add chartsheet with invalid sheet name + assert.EqualError(t, f.AddChartSheet("Sheet:1", "A1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), ErrSheetNameInvalid.Error()) // Test with unsupported chart type assert.EqualError(t, f.AddChartSheet("Chart2", `{"type":"unknown","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "unsupported chart type unknown") assert.NoError(t, f.UpdateLinkedValue()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChartSheet.xlsx"))) - // Test add chart sheet with unsupported charset content types. + // Test add chart sheet with unsupported charset content types f = NewFile() f.ContentTypes = nil f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) @@ -278,11 +282,13 @@ func TestDeleteChart(t *testing.T) { assert.NoError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`)) assert.NoError(t, f.DeleteChart("Sheet1", "P1")) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteChart.xlsx"))) - // Test delete chart on not exists worksheet. + // Test delete chart with invalid sheet name + assert.EqualError(t, f.DeleteChart("Sheet:1", "P1"), ErrSheetNameInvalid.Error()) + // Test delete chart on not exists worksheet assert.EqualError(t, f.DeleteChart("SheetN", "A1"), "sheet SheetN does not exist") - // Test delete chart with invalid coordinates. + // Test delete chart with invalid coordinates assert.EqualError(t, f.DeleteChart("Sheet1", ""), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error()) - // Test delete chart on no chart worksheet. + // Test delete chart on no chart worksheet assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1")) assert.NoError(t, f.Close()) } diff --git a/col.go b/col.go index b930877..f890610 100644 --- a/col.go +++ b/col.go @@ -208,6 +208,9 @@ func (cols *Cols) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.Sta // fmt.Println() // } func (f *File) Cols(sheet string) (*Cols, error) { + if err := checkSheetName(sheet); err != nil { + return nil, err + } name, ok := f.getSheetXMLPath(sheet) if !ok { return nil, ErrSheetNotExist{sheet} @@ -236,7 +239,7 @@ func (f *File) Cols(sheet string) (*Cols, error) { case xml.EndElement: if xmlElement.Name.Local == "sheetData" { colIterator.cols.f = f - colIterator.cols.sheet = trimSheetName(sheet) + colIterator.cols.sheet = sheet return &colIterator.cols, nil } } diff --git a/col_test.go b/col_test.go index 0ed1906..4c5961f 100644 --- a/col_test.go +++ b/col_test.go @@ -57,7 +57,13 @@ func TestCols(t *testing.T) { _, err = f.Rows("Sheet1") assert.NoError(t, err) - // Test columns iterator with unsupported charset shared strings table. + // Test columns iterator with invalid sheet name + _, err = f.Cols("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test get columns cells with invalid sheet name + _, err = f.GetCols("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test columns iterator with unsupported charset shared strings table f.SharedStrings = nil f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset) cols, err = f.Cols("Sheet1") @@ -212,14 +218,18 @@ func TestColumnVisibility(t *testing.T) { assert.Equal(t, true, visible) assert.NoError(t, err) - // Test get column visible on an inexistent worksheet. + // Test get column visible on an inexistent worksheet _, err = f.GetColVisible("SheetN", "F") assert.EqualError(t, err, "sheet SheetN does not exist") - - // Test get column visible with illegal cell reference. + // Test get column visible with invalid sheet name + _, err = f.GetColVisible("Sheet:1", "F") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test get column visible with illegal cell reference _, err = f.GetColVisible("Sheet1", "*") assert.EqualError(t, err, newInvalidColumnNameError("*").Error()) assert.EqualError(t, f.SetColVisible("Sheet1", "*", false), newInvalidColumnNameError("*").Error()) + // Test set column visible with invalid sheet name + assert.EqualError(t, f.SetColVisible("Sheet:1", "A", false), ErrSheetNameInvalid.Error()) f.NewSheet("Sheet3") assert.NoError(t, f.SetColVisible("Sheet3", "E", false)) @@ -254,25 +264,35 @@ func TestOutlineLevel(t *testing.T) { assert.Equal(t, uint8(0), level) assert.EqualError(t, err, "sheet SheetN does not exist") + // Test column outline level with invalid sheet name + _, err = f.GetColOutlineLevel("Sheet:1", "A") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + assert.NoError(t, f.SetColWidth("Sheet2", "A", "D", 13)) assert.EqualError(t, f.SetColWidth("Sheet2", "A", "D", MaxColumnWidth+1), ErrColumnWidth.Error()) + // Test set column width with invalid sheet name + assert.EqualError(t, f.SetColWidth("Sheet:1", "A", "D", 13), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SetColOutlineLevel("Sheet2", "B", 2)) assert.NoError(t, f.SetRowOutlineLevel("Sheet1", 2, 7)) assert.EqualError(t, f.SetColOutlineLevel("Sheet1", "D", 8), ErrOutlineLevel.Error()) assert.EqualError(t, f.SetRowOutlineLevel("Sheet1", 2, 8), ErrOutlineLevel.Error()) - // Test set row outline level on not exists worksheet. + // Test set row outline level on not exists worksheet assert.EqualError(t, f.SetRowOutlineLevel("SheetN", 1, 4), "sheet SheetN does not exist") - // Test get row outline level on not exists worksheet. + // Test set row outline level with invalid sheet name + assert.EqualError(t, f.SetRowOutlineLevel("Sheet:1", 1, 4), ErrSheetNameInvalid.Error()) + // Test get row outline level on not exists worksheet _, err = f.GetRowOutlineLevel("SheetN", 1) assert.EqualError(t, err, "sheet SheetN does not exist") - - // Test set and get column outline level with illegal cell reference. + // Test get row outline level with invalid sheet name + _, err = f.GetRowOutlineLevel("Sheet:1", 1) + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test set and get column outline level with illegal cell reference assert.EqualError(t, f.SetColOutlineLevel("Sheet1", "*", 1), newInvalidColumnNameError("*").Error()) _, err = f.GetColOutlineLevel("Sheet1", "*") assert.EqualError(t, err, newInvalidColumnNameError("*").Error()) - // Test set column outline level on not exists worksheet. + // Test set column outline level on not exists worksheet assert.EqualError(t, f.SetColOutlineLevel("SheetN", "E", 2), "sheet SheetN does not exist") assert.EqualError(t, f.SetRowOutlineLevel("Sheet1", 0, 1), newInvalidRowNumberError(0).Error()) @@ -300,22 +320,24 @@ func TestSetColStyle(t *testing.T) { assert.NoError(t, f.SetCellValue("Sheet1", "B2", "Hello")) styleID, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#94d3a2"],"pattern":1}}`) assert.NoError(t, err) - // Test set column style on not exists worksheet. + // Test set column style on not exists worksheet assert.EqualError(t, f.SetColStyle("SheetN", "E", styleID), "sheet SheetN does not exist") - // Test set column style with illegal column name. + // Test set column style with illegal column name assert.EqualError(t, f.SetColStyle("Sheet1", "*", styleID), newInvalidColumnNameError("*").Error()) assert.EqualError(t, f.SetColStyle("Sheet1", "A:*", styleID), newInvalidColumnNameError("*").Error()) - // Test set column style with invalid style ID. + // Test set column style with invalid style ID assert.EqualError(t, f.SetColStyle("Sheet1", "B", -1), newInvalidStyleID(-1).Error()) - // Test set column style with not exists style ID. + // Test set column style with not exists style ID assert.EqualError(t, f.SetColStyle("Sheet1", "B", 10), newInvalidStyleID(10).Error()) + // Test set column style with invalid sheet name + assert.EqualError(t, f.SetColStyle("Sheet:1", "A", 0), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SetColStyle("Sheet1", "B", styleID)) style, err := f.GetColStyle("Sheet1", "B") assert.NoError(t, err) assert.Equal(t, styleID, style) - // Test set column style with already exists column with style. + // Test set column style with already exists column with style assert.NoError(t, f.SetColStyle("Sheet1", "B", styleID)) assert.NoError(t, f.SetColStyle("Sheet1", "D:C", styleID)) ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") @@ -325,7 +347,7 @@ func TestSetColStyle(t *testing.T) { assert.NoError(t, err) assert.Equal(t, styleID, cellStyleID) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetColStyle.xlsx"))) - // Test set column style with unsupported charset style sheet. + // Test set column style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) assert.EqualError(t, f.SetColStyle("Sheet1", "C:F", styleID), "XML syntax error on line 1: invalid UTF-8") @@ -342,19 +364,21 @@ func TestColWidth(t *testing.T) { assert.Equal(t, defaultColWidth, width) assert.NoError(t, err) - // Test set and get column width with illegal cell reference. + // Test set and get column width with illegal cell reference width, err = f.GetColWidth("Sheet1", "*") assert.Equal(t, defaultColWidth, width) assert.EqualError(t, err, newInvalidColumnNameError("*").Error()) assert.EqualError(t, f.SetColWidth("Sheet1", "*", "B", 1), newInvalidColumnNameError("*").Error()) assert.EqualError(t, f.SetColWidth("Sheet1", "A", "*", 1), newInvalidColumnNameError("*").Error()) - // Test set column width on not exists worksheet. + // Test set column width on not exists worksheet assert.EqualError(t, f.SetColWidth("SheetN", "B", "A", 12), "sheet SheetN does not exist") - - // Test get column width on not exists worksheet. + // Test get column width on not exists worksheet _, err = f.GetColWidth("SheetN", "A") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test get column width invalid sheet name + _, err = f.GetColWidth("Sheet:1", "A") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestColWidth.xlsx"))) convertRowHeightToPixels(0) @@ -366,12 +390,15 @@ func TestGetColStyle(t *testing.T) { assert.NoError(t, err) assert.Equal(t, styleID, 0) - // Test set column style on not exists worksheet. + // Test get column style on not exists worksheet _, err = f.GetColStyle("SheetN", "A") assert.EqualError(t, err, "sheet SheetN does not exist") - // Test set column style with illegal column name. + // Test get column style with illegal column name _, err = f.GetColStyle("Sheet1", "*") assert.EqualError(t, err, newInvalidColumnNameError("*").Error()) + // Test get column style with invalid sheet name + _, err = f.GetColStyle("Sheet:1", "A") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestInsertCols(t *testing.T) { @@ -386,9 +413,10 @@ func TestInsertCols(t *testing.T) { assert.NoError(t, f.AutoFilter(sheet1, "A2", "B2", `{"column":"B","expression":"x != blanks"}`)) assert.NoError(t, f.InsertCols(sheet1, "A", 1)) - // Test insert column with illegal cell reference. + // Test insert column with illegal cell reference assert.EqualError(t, f.InsertCols(sheet1, "*", 1), newInvalidColumnNameError("*").Error()) - + // Test insert column with invalid sheet name + assert.EqualError(t, f.InsertCols("Sheet:1", "A", 1), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.InsertCols(sheet1, "A", 0), ErrColumnNumber.Error()) assert.EqualError(t, f.InsertCols(sheet1, "A", MaxColumns), ErrColumnNumber.Error()) assert.EqualError(t, f.InsertCols(sheet1, "A", MaxColumns-10), ErrColumnNumber.Error()) @@ -411,11 +439,12 @@ func TestRemoveCol(t *testing.T) { assert.NoError(t, f.RemoveCol(sheet1, "A")) assert.NoError(t, f.RemoveCol(sheet1, "A")) - // Test remove column with illegal cell reference. + // Test remove column with illegal cell reference assert.EqualError(t, f.RemoveCol("Sheet1", "*"), newInvalidColumnNameError("*").Error()) - - // Test remove column on not exists worksheet. + // Test remove column on not exists worksheet assert.EqualError(t, f.RemoveCol("SheetN", "B"), "sheet SheetN does not exist") + // Test remove column with invalid sheet name + assert.EqualError(t, f.RemoveCol("Sheet:1", "A"), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestRemoveCol.xlsx"))) } diff --git a/comment.go b/comment.go index ae62c37..395d7c1 100644 --- a/comment.go +++ b/comment.go @@ -143,6 +143,9 @@ func (f *File) AddComment(sheet string, comment Comment) error { // // err := f.DeleteComment("Sheet1", "A30") func (f *File) DeleteComment(sheet, cell string) error { + if err := checkSheetName(sheet); err != nil { + return err + } sheetXMLPath, ok := f.getSheetXMLPath(sheet) if !ok { return newNoExistSheetError(sheet) diff --git a/comment_test.go b/comment_test.go index ead3939..0f668f1 100644 --- a/comment_test.go +++ b/comment_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestAddComments(t *testing.T) { +func TestAddComment(t *testing.T) { f, err := prepareTestBook1() if !assert.NoError(t, err) { t.FailNow() @@ -30,7 +30,7 @@ func TestAddComments(t *testing.T) { assert.NoError(t, f.AddComment("Sheet1", Comment{Cell: "A30", Author: s, Text: s, Runs: []RichTextRun{{Text: s}, {Text: s}}})) assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "B7", Author: "Excelize", Text: s[:TotalCellChars-1], Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}})) - // Test add comment on not exists worksheet. + // Test add comment on not exists worksheet assert.EqualError(t, f.AddComment("SheetN", Comment{Cell: "B7", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), "sheet SheetN does not exist") // Test add comment on with illegal cell reference assert.EqualError(t, f.AddComment("Sheet1", Comment{Cell: "A", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) @@ -50,18 +50,21 @@ func TestAddComments(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, len(comments), 0) - // Test add comments with unsupported charset. + // Test add comments with invalid sheet name + assert.EqualError(t, f.AddComment("Sheet:1", Comment{Cell: "A1", Author: "Excelize", Text: "This is a comment."}), ErrSheetNameInvalid.Error()) + + // Test add comments with unsupported charset f.Comments["xl/comments2.xml"] = nil f.Pkg.Store("xl/comments2.xml", MacintoshCyrillicCharset) _, err = f.GetComments() assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") - // Test add comments with unsupported charset. + // Test add comments with unsupported charset f.Comments["xl/comments2.xml"] = nil f.Pkg.Store("xl/comments2.xml", MacintoshCyrillicCharset) assert.EqualError(t, f.AddComment("Sheet2", Comment{Cell: "A30", Text: "Comment"}), "XML syntax error on line 1: invalid UTF-8") - // Test add comments with unsupported charset style sheet. + // Test add comments with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) assert.EqualError(t, f.AddComment("Sheet2", Comment{Cell: "A30", Text: "Comment"}), "XML syntax error on line 1: invalid UTF-8") @@ -90,6 +93,8 @@ func TestDeleteComment(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, len(comments), 0) + // Test delete comment with invalid sheet name + assert.EqualError(t, f.DeleteComment("Sheet:1", "A1"), ErrSheetNameInvalid.Error()) // Test delete all comments in a worksheet assert.NoError(t, f.DeleteComment("Sheet2", "A41")) assert.NoError(t, f.DeleteComment("Sheet2", "C41")) @@ -118,7 +123,7 @@ func TestDecodeVMLDrawingReader(t *testing.T) { func TestCommentsReader(t *testing.T) { f := NewFile() - // Test read comments with unsupported charset. + // Test read comments with unsupported charset path := "xl/comments1.xml" f.Pkg.Store(path, MacintoshCyrillicCharset) _, err := f.commentsReader(path) diff --git a/datavalidation_test.go b/datavalidation_test.go index c0d9117..c307d20 100644 --- a/datavalidation_test.go +++ b/datavalidation_test.go @@ -91,6 +91,9 @@ func TestDataValidation(t *testing.T) { // Test get data validation on no exists worksheet _, err = f.GetDataValidations("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test get data validation with invalid sheet name + _, err = f.GetDataValidations("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(resultFile)) @@ -130,7 +133,7 @@ func TestDataValidationError(t *testing.T) { assert.NoError(t, f.AddDataValidation("Sheet1", dvRange)) - // Test width invalid data validation formula. + // Test width invalid data validation formula prevFormula1 := dvRange.Formula1 for _, keys := range [][]string{ make([]string, 257), @@ -156,9 +159,13 @@ func TestDataValidationError(t *testing.T) { DataValidationTypeWhole, DataValidationOperatorGreaterThan), ErrDataValidationRange.Error()) assert.NoError(t, f.SaveAs(resultFile)) - // Test add data validation on no exists worksheet. + // Test add data validation on no exists worksheet f = NewFile() assert.EqualError(t, f.AddDataValidation("SheetN", nil), "sheet SheetN does not exist") + + // Test add data validation with invalid sheet name + f = NewFile() + assert.EqualError(t, f.AddDataValidation("Sheet:1", nil), ErrSheetNameInvalid.Error()) } func TestDeleteDataValidation(t *testing.T) { @@ -200,10 +207,11 @@ func TestDeleteDataValidation(t *testing.T) { ws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = "A1:A" assert.EqualError(t, f.DeleteDataValidation("Sheet1", "A1:B2"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) - // Test delete data validation on no exists worksheet. + // Test delete data validation on no exists worksheet assert.EqualError(t, f.DeleteDataValidation("SheetN", "A1:B2"), "sheet SheetN does not exist") - - // Test delete all data validations in the worksheet. + // Test delete all data validation with invalid sheet name + assert.EqualError(t, f.DeleteDataValidation("Sheet:1"), ErrSheetNameInvalid.Error()) + // Test delete all data validations in the worksheet assert.NoError(t, f.DeleteDataValidation("Sheet1")) assert.Nil(t, ws.(*xlsxWorksheet).DataValidations) } diff --git a/errors.go b/errors.go index 1f7c6f8..7a31a4c 100644 --- a/errors.go +++ b/errors.go @@ -113,9 +113,8 @@ var ( // ErrCoordinates defined the error message on invalid coordinates tuples // length. ErrCoordinates = errors.New("coordinates length must be 4") - // ErrExistsWorksheet defined the error message on given worksheet already - // exists. - ErrExistsWorksheet = errors.New("the same name worksheet already exists") + // ErrExistsSheet defined the error message on given sheet already exists. + ErrExistsSheet = errors.New("the same name sheet already exists") // ErrTotalSheetHyperlinks defined the error message on hyperlinks count // overflow. ErrTotalSheetHyperlinks = errors.New("over maximum limit hyperlinks in a worksheet") @@ -219,4 +218,16 @@ var ( // ErrWorkbookPassword defined the error message on receiving the incorrect // workbook password. ErrWorkbookPassword = errors.New("the supplied open workbook password is not correct") + // ErrSheetNameInvalid defined the error message on receive the sheet name + // contains invalid characters. + ErrSheetNameInvalid = errors.New("the sheet can not contain any of the characters :\\/?*[or]") + // ErrSheetNameSingleQuote defined the error message on the first or last + // character of the sheet name was a single quote. + ErrSheetNameSingleQuote = errors.New("the first or last character of the sheet name can not be a single quote") + // ErrSheetNameBlank defined the error message on receive the blank sheet + // name. + ErrSheetNameBlank = errors.New("the sheet name can not be blank") + // ErrSheetNameLength defined the error message on receiving the sheet + // name length exceeds the limit. + ErrSheetNameLength = fmt.Errorf("the sheet name length exceeds the %d characters limit", MaxSheetNameLength) ) diff --git a/excelize.go b/excelize.go index f4c7a25..f84afd6 100644 --- a/excelize.go +++ b/excelize.go @@ -233,6 +233,9 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) { name string ok bool ) + if err = checkSheetName(sheet); err != nil { + return + } if name, ok = f.getSheetXMLPath(sheet); !ok { err = newNoExistSheetError(sheet) return diff --git a/excelize_test.go b/excelize_test.go index ece74b2..1dad0ff 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -23,14 +23,17 @@ import ( ) func TestOpenFile(t *testing.T) { - // Test update the spreadsheet file. + // Test update the spreadsheet file f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) - // Test get all the rows in a not exists worksheet. + // Test get all the rows in a not exists worksheet _, err = f.GetRows("Sheet4") assert.EqualError(t, err, "sheet Sheet4 does not exist") - // Test get all the rows in a worksheet. + // Test get all the rows with invalid sheet name + _, err = f.GetRows("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test get all the rows in a worksheet rows, err := f.GetRows("Sheet2") expected := [][]string{ {"Monitor", "", "Brand", "", "inlineStr"}, @@ -52,40 +55,44 @@ func TestOpenFile(t *testing.T) { assert.NoError(t, f.SetCellDefault("Sheet2", "A1", strconv.FormatFloat(100.1588, 'f', -1, 32))) assert.NoError(t, f.SetCellDefault("Sheet2", "A1", strconv.FormatFloat(-100.1588, 'f', -1, 64))) - - // Test set cell value with illegal row number. + // Test set cell value with invalid sheet name + assert.EqualError(t, f.SetCellDefault("Sheet:1", "A1", ""), ErrSheetNameInvalid.Error()) + // Test set cell value with illegal row number assert.EqualError(t, f.SetCellDefault("Sheet2", "A", strconv.FormatFloat(-100.1588, 'f', -1, 64)), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.NoError(t, f.SetCellInt("Sheet2", "A1", 100)) - // Test set cell integer value with illegal row number. + // Test set cell integer value with illegal row number assert.EqualError(t, f.SetCellInt("Sheet2", "A", 100), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + // Test set cell integer value with invalid sheet name + assert.EqualError(t, f.SetCellInt("Sheet:1", "A1", 100), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SetCellStr("Sheet2", "C11", "Knowns")) - // Test max characters in a cell. + // Test max characters in a cell assert.NoError(t, f.SetCellStr("Sheet2", "D11", strings.Repeat("c", TotalCellChars+2))) f.NewSheet(":\\/?*[]Maximum 31 characters allowed in sheet title.") - // Test set worksheet name with illegal name. + // Test set worksheet name with illegal name f.SetSheetName("Maximum 31 characters allowed i", "[Rename]:\\/?* Maximum 31 characters allowed in sheet title.") assert.EqualError(t, f.SetCellInt("Sheet3", "A23", 10), "sheet Sheet3 does not exist") assert.EqualError(t, f.SetCellStr("Sheet3", "b230", "10"), "sheet Sheet3 does not exist") assert.EqualError(t, f.SetCellStr("Sheet10", "b230", "10"), "sheet Sheet10 does not exist") - - // Test set cell string value with illegal row number. + // Test set cell string data type value with invalid sheet name + assert.EqualError(t, f.SetCellStr("Sheet:1", "A1", "1"), ErrSheetNameInvalid.Error()) + // Test set cell string value with illegal row number assert.EqualError(t, f.SetCellStr("Sheet1", "A", "10"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) f.SetActiveSheet(2) - // Test get cell formula with given rows number. + // Test get cell formula with given rows number _, err = f.GetCellFormula("Sheet1", "B19") assert.NoError(t, err) - // Test get cell formula with illegal worksheet name. + // Test get cell formula with illegal worksheet name _, err = f.GetCellFormula("Sheet2", "B20") assert.NoError(t, err) _, err = f.GetCellFormula("Sheet1", "B20") assert.NoError(t, err) - // Test get cell formula with illegal rows number. + // Test get cell formula with illegal rows number _, err = f.GetCellFormula("Sheet1", "B") assert.EqualError(t, err, newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error()) // Test get shared cell formula @@ -110,7 +117,7 @@ func TestOpenFile(t *testing.T) { assert.NoError(t, err) _, err = f.GetCellValue("Sheet2", "D12") assert.NoError(t, err) - // Test SetCellValue function. + // Test SetCellValue function assert.NoError(t, f.SetCellValue("Sheet2", "F1", " Hello")) assert.NoError(t, f.SetCellValue("Sheet2", "G1", []byte("World"))) assert.NoError(t, f.SetCellValue("Sheet2", "F2", 42)) @@ -130,7 +137,7 @@ func TestOpenFile(t *testing.T) { assert.NoError(t, f.SetCellValue("Sheet2", "F16", true)) assert.NoError(t, f.SetCellValue("Sheet2", "F17", complex64(5+10i))) - // Test on not exists worksheet. + // Test on not exists worksheet assert.EqualError(t, f.SetCellDefault("SheetN", "A1", ""), "sheet SheetN does not exist") assert.EqualError(t, f.SetCellFloat("SheetN", "A1", 42.65418, 2, 32), "sheet SheetN does not exist") assert.EqualError(t, f.SetCellBool("SheetN", "A1", true), "sheet SheetN does not exist") @@ -163,18 +170,18 @@ func TestOpenFile(t *testing.T) { assert.EqualError(t, f.SetCellValue("SheetN", "A1", time.Now()), "sheet SheetN does not exist") // 02:46:40 assert.NoError(t, f.SetCellValue("Sheet2", "G5", time.Duration(1e13))) - // Test completion column. + // Test completion column assert.NoError(t, f.SetCellValue("Sheet2", "M2", nil)) - // Test read cell value with given cell reference large than exists row. + // Test read cell value with given cell reference large than exists row _, err = f.GetCellValue("Sheet2", "E231") assert.NoError(t, err) - // Test get active worksheet of spreadsheet and get worksheet name of spreadsheet by given worksheet index. + // Test get active worksheet of spreadsheet and get worksheet name of spreadsheet by given worksheet index f.GetSheetName(f.GetActiveSheetIndex()) - // Test get worksheet index of spreadsheet by given worksheet name. + // Test get worksheet index of spreadsheet by given worksheet name f.GetSheetIndex("Sheet1") - // Test get worksheet name of spreadsheet by given invalid worksheet index. + // Test get worksheet name of spreadsheet by given invalid worksheet index f.GetSheetName(4) - // Test get worksheet map of workbook. + // Test get worksheet map of workbook f.GetSheetMap() for i := 1; i <= 300; i++ { assert.NoError(t, f.SetCellStr("Sheet2", "c"+strconv.Itoa(i), strconv.Itoa(i))) @@ -202,7 +209,7 @@ func TestSaveFile(t *testing.T) { func TestSaveAsWrongPath(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) - // Test write file to not exist directory. + // Test write file to not exist directory assert.Error(t, f.SaveAs(filepath.Join("x", "Book1.xlsx"))) assert.NoError(t, f.Close()) } @@ -218,7 +225,7 @@ func TestOpenReader(t *testing.T) { _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password", UnzipXMLSizeLimit: UnzipSizeLimit + 1}) assert.EqualError(t, err, ErrWorkbookFileFormat.Error()) - // Test open workbook with unsupported charset internal calculation chain. + // 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) @@ -245,11 +252,11 @@ func TestOpenReader(t *testing.T) { 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}) assert.EqualError(t, err, newUnzipSizeLimitError(100).Error()) - // Test open password protected spreadsheet created by Microsoft Office Excel 2010. + // Test open password protected spreadsheet created by Microsoft Office Excel 2010 f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"}) assert.NoError(t, err) val, err := f.GetCellValue("Sheet1", "A1") @@ -257,7 +264,7 @@ func TestOpenReader(t *testing.T) { assert.Equal(t, "SECRET", val) assert.NoError(t, f.Close()) - // Test open password protected spreadsheet created by LibreOffice 7.0.0.3. + // Test open password protected spreadsheet created by LibreOffice 7.0.0.3 f, err = OpenFile(filepath.Join("test", "encryptAES.xlsx"), Options{Password: "password"}) assert.NoError(t, err) val, err = f.GetCellValue("Sheet1", "A1") @@ -265,11 +272,11 @@ func TestOpenReader(t *testing.T) { assert.Equal(t, "SECRET", val) assert.NoError(t, f.Close()) - // Test open spreadsheet with invalid options. + // Test open spreadsheet with invalid options _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, UnzipXMLSizeLimit: 2}) assert.EqualError(t, err, ErrOptionsUnzipSizeLimit.Error()) - // Test unexpected EOF. + // Test unexpected EOF var b bytes.Buffer w := gzip.NewWriter(&b) defer w.Close() @@ -299,7 +306,7 @@ func TestOpenReader(t *testing.T) { } func TestBrokenFile(t *testing.T) { - // Test write file with broken file struct. + // Test write file with broken file struct f := File{} t.Run("SaveWithoutName", func(t *testing.T) { @@ -307,12 +314,12 @@ func TestBrokenFile(t *testing.T) { }) t.Run("SaveAsEmptyStruct", func(t *testing.T) { - // Test write file with broken file struct with given path. + // Test write file with broken file struct with given path assert.NoError(t, f.SaveAs(filepath.Join("test", "BadWorkbook.SaveAsEmptyStruct.xlsx"))) }) t.Run("OpenBadWorkbook", func(t *testing.T) { - // Test set active sheet without BookViews and Sheets maps in xl/workbook.xml. + // Test set active sheet without BookViews and Sheets maps in xl/workbook.xml f3, err := OpenFile(filepath.Join("test", "BadWorkbook.xlsx")) f3.GetActiveSheetIndex() f3.SetActiveSheet(1) @@ -321,7 +328,7 @@ func TestBrokenFile(t *testing.T) { }) t.Run("OpenNotExistsFile", func(t *testing.T) { - // Test open a spreadsheet file with given illegal path. + // Test open a spreadsheet file with given illegal path _, err := OpenFile(filepath.Join("test", "NotExistsFile.xlsx")) if assert.Error(t, err) { assert.True(t, os.IsNotExist(err), "Expected os.IsNotExists(err) == true") @@ -330,7 +337,7 @@ func TestBrokenFile(t *testing.T) { } func TestNewFile(t *testing.T) { - // Test create a spreadsheet file. + // Test create a spreadsheet file f := NewFile() f.NewSheet("Sheet1") f.NewSheet("XLSXSheet2") @@ -339,20 +346,20 @@ func TestNewFile(t *testing.T) { assert.NoError(t, f.SetCellStr("Sheet1", "B20", "42")) f.SetActiveSheet(0) - // Test add picture to sheet with scaling and positioning. + // Test add picture to sheet with scaling and positioning err := f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"), `{"x_scale": 0.5, "y_scale": 0.5, "positioning": "absolute"}`) if !assert.NoError(t, err) { t.FailNow() } - // Test add picture to worksheet without options. + // Test add picture to worksheet without options err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), "") if !assert.NoError(t, err) { t.FailNow() } - // Test add picture to worksheet with invalid options. + // Test add picture to worksheet with invalid options err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), `{`) if !assert.Error(t, err) { t.FailNow() @@ -363,7 +370,7 @@ func TestNewFile(t *testing.T) { } func TestAddDrawingVML(t *testing.T) { - // Test addDrawingVML with illegal cell reference. + // Test addDrawingVML with illegal cell reference f := NewFile() assert.EqualError(t, f.addDrawingVML(0, "", "*", 0, 0), newCellNameToCoordinatesError("*", newInvalidCellNameError("*")).Error()) @@ -374,23 +381,22 @@ func TestAddDrawingVML(t *testing.T) { func TestSetCellHyperLink(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) - // Test set cell hyperlink in a work sheet already have hyperlinks. + // Test set cell hyperlink in a work sheet already have hyperlinks assert.NoError(t, f.SetCellHyperLink("Sheet1", "B19", "https://github.com/xuri/excelize", "External")) - // Test add first hyperlink in a work sheet. + // Test add first hyperlink in a work sheet assert.NoError(t, f.SetCellHyperLink("Sheet2", "C1", "https://github.com/xuri/excelize", "External")) // Test add Location hyperlink in a work sheet. assert.NoError(t, f.SetCellHyperLink("Sheet2", "D6", "Sheet1!D8", "Location")) - // Test add Location hyperlink with display & tooltip in a work sheet. + // Test add Location hyperlink with display & tooltip in a work sheet display, tooltip := "Display value", "Hover text" assert.NoError(t, f.SetCellHyperLink("Sheet2", "D7", "Sheet1!D9", "Location", HyperlinkOpts{ Display: &display, Tooltip: &tooltip, })) - + // Test set cell hyperlink with invalid sheet name + assert.EqualError(t, f.SetCellHyperLink("Sheet:1", "A1", "Sheet1!D60", "Location"), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.SetCellHyperLink("Sheet2", "C3", "Sheet1!D8", ""), `invalid link type ""`) - assert.EqualError(t, f.SetCellHyperLink("Sheet2", "", "Sheet1!D60", "Location"), `invalid cell name ""`) - assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellHyperLink.xlsx"))) assert.NoError(t, f.Close()) @@ -467,6 +473,10 @@ func TestGetCellHyperLink(t *testing.T) { assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.Equal(t, link, false) assert.Equal(t, target, "") + + // Test get cell hyperlink with invalid sheet name + _, _, err = f.GetCellHyperLink("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestSetSheetBackground(t *testing.T) { @@ -489,12 +499,14 @@ 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. + // 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") + // Test set sheet background with invalid sheet name + assert.EqualError(t, f.SetSheetBackground("Sheet:1", filepath.Join("test", "images", "background.jpg")), ErrSheetNameInvalid.Error()) assert.NoError(t, f.Close()) - // Test set sheet background with unsupported charset content types. + // Test set sheet background with unsupported charset content types f = NewFile() f.ContentTypes = nil f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) @@ -510,7 +522,6 @@ func TestWriteArrayFormula(t *testing.T) { if err != nil { t.Fatal(err) } - return c } @@ -621,15 +632,20 @@ func TestSetCellStyleAlignment(t *testing.T) { assert.NoError(t, f.SetCellStyle("Sheet1", "A22", "A22", style)) - // Test set cell style with given illegal rows number. + // Test set cell style with given illegal rows number assert.EqualError(t, f.SetCellStyle("Sheet1", "A", "A22", style), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.SetCellStyle("Sheet1", "A22", "A", style), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) - - // Test get cell style with given illegal rows number. + // Test set cell style with invalid sheet name + assert.EqualError(t, f.SetCellStyle("Sheet:1", "A1", "A2", style), ErrSheetNameInvalid.Error()) + // Test get cell style with given illegal rows number index, err := f.GetCellStyle("Sheet1", "A") assert.Equal(t, 0, index) assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + // Test get cell style with invalid sheet name + _, err = f.GetCellStyle("Sheet:1", "A1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleAlignment.xlsx"))) } @@ -987,18 +1003,18 @@ func TestSheetVisibility(t *testing.T) { assert.NoError(t, f.SetSheetVisible("Sheet2", false)) assert.NoError(t, f.SetSheetVisible("Sheet1", false)) assert.NoError(t, f.SetSheetVisible("Sheet1", true)) - assert.Equal(t, true, f.GetSheetVisible("Sheet1")) - + visible, err := f.GetSheetVisible("Sheet1") + assert.Equal(t, true, visible) + assert.NoError(t, err) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSheetVisibility.xlsx"))) } func TestCopySheet(t *testing.T) { f, err := prepareTestBook1() - if !assert.NoError(t, err) { - t.FailNow() - } + assert.NoError(t, err) - idx := f.NewSheet("CopySheet") + idx, err := f.NewSheet("CopySheet") + assert.NoError(t, err) assert.NoError(t, f.CopySheet(0, idx)) assert.NoError(t, f.SetCellValue("CopySheet", "F1", "Hello")) @@ -1011,15 +1027,9 @@ func TestCopySheet(t *testing.T) { func TestCopySheetError(t *testing.T) { f, err := prepareTestBook1() - if !assert.NoError(t, err) { - t.FailNow() - } - - assert.EqualError(t, f.copySheet(-1, -2), "sheet does not exist") - if !assert.EqualError(t, f.CopySheet(-1, -2), "invalid worksheet index") { - t.FailNow() - } - + assert.NoError(t, err) + assert.EqualError(t, f.copySheet(-1, -2), ErrSheetNameBlank.Error()) + assert.EqualError(t, f.CopySheet(-1, -2), ErrSheetIdx.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestCopySheetError.xlsx"))) } @@ -1072,9 +1082,9 @@ func TestConditionalFormat(t *testing.T) { t.FailNow() } - // Color scales: 2 color. + // Color scales: 2 color assert.NoError(t, f.SetConditionalFormat(sheet1, "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`)) - // Color scales: 3 color. + // Color scales: 3 color assert.NoError(t, f.SetConditionalFormat(sheet1, "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)) // Highlight cells rules: between... assert.NoError(t, f.SetConditionalFormat(sheet1, "C1:C10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format1))) @@ -1092,29 +1102,31 @@ func TestConditionalFormat(t *testing.T) { assert.NoError(t, f.SetConditionalFormat(sheet1, "I1:I10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": true}]`, format3))) // Top/Bottom rules: Below Average... assert.NoError(t, f.SetConditionalFormat(sheet1, "J1:J10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": false}]`, format1))) - // Data Bars: Gradient Fill. + // Data Bars: Gradient Fill assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10", `[{"type":"data_bar", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)) - // Use a formula to determine which cells to format. + // Use a formula to determine which cells to format assert.NoError(t, f.SetConditionalFormat(sheet1, "L1:L10", fmt.Sprintf(`[{"type":"formula", "criteria":"L2<3", "format":%d}]`, format1))) - // Alignment/Border cells rules. + // Alignment/Border cells rules assert.NoError(t, f.SetConditionalFormat(sheet1, "M1:M10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"0"}]`, format4))) - // Test set invalid format set in conditional format. + // Test set invalid format set in conditional format assert.EqualError(t, f.SetConditionalFormat(sheet1, "L1:L10", ""), "unexpected end of JSON input") - // Set conditional format on not exists worksheet. + // Test set conditional format on not exists worksheet assert.EqualError(t, f.SetConditionalFormat("SheetN", "L1:L10", "[]"), "sheet SheetN does not exist") + // Test set conditional format with invalid sheet name + assert.EqualError(t, f.SetConditionalFormat("Sheet:1", "L1:L10", "[]"), ErrSheetNameInvalid.Error()) err = f.SaveAs(filepath.Join("test", "TestConditionalFormat.xlsx")) if !assert.NoError(t, err) { t.FailNow() } - // Set conditional format with illegal valid type. + // Set conditional format with illegal valid type assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10", `[{"type":"", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)) - // Set conditional format with illegal criteria type. + // Set conditional format with illegal criteria type assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10", `[{"type":"data_bar", "criteria":"", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)) - // Set conditional format with file without dxfs element should not return error. + // Set conditional format with file without dxfs element should not return error f, err = OpenFile(filepath.Join("test", "Book1.xlsx")) if !assert.NoError(t, err) { t.FailNow() @@ -1168,7 +1180,8 @@ func TestSetSheetCol(t *testing.T) { assert.EqualError(t, f.SetSheetCol("Sheet1", "", &[]interface{}{"cell", nil, 2}), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error()) - + // Test set worksheet column values with invalid sheet name + assert.EqualError(t, f.SetSheetCol("Sheet:1", "A1", &[]interface{}{nil}), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.SetSheetCol("Sheet1", "B27", []interface{}{}), ErrParameterInvalid.Error()) assert.EqualError(t, f.SetSheetCol("Sheet1", "B27", &f), ErrParameterInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetCol.xlsx"))) @@ -1185,7 +1198,8 @@ func TestSetSheetRow(t *testing.T) { assert.EqualError(t, f.SetSheetRow("Sheet1", "", &[]interface{}{"cell", nil, 2}), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error()) - + // Test set worksheet row with invalid sheet name + assert.EqualError(t, f.SetSheetRow("Sheet:1", "A1", &[]interface{}{1}), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", []interface{}{}), ErrParameterInvalid.Error()) assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", &f), ErrParameterInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetRow.xlsx"))) @@ -1261,6 +1275,8 @@ func TestProtectSheet(t *testing.T) { assert.Equal(t, int(sheetProtectionSpinCount), ws.SheetProtection.SpinCount) // Test remove sheet protection with an incorrect password assert.EqualError(t, f.UnprotectSheet(sheetName, "wrongPassword"), ErrUnprotectSheetPassword.Error()) + // Test remove sheet protection with invalid sheet name + assert.EqualError(t, f.UnprotectSheet("Sheet:1", "wrongPassword"), ErrSheetNameInvalid.Error()) // Test remove sheet protection with password verification assert.NoError(t, f.UnprotectSheet(sheetName, "password")) // Test protect worksheet with empty password @@ -1276,8 +1292,10 @@ func TestProtectSheet(t *testing.T) { AlgorithmName: "RIPEMD-160", Password: "password", }), ErrUnsupportedHashAlgorithm.Error()) - // Test protect not exists worksheet. + // Test protect not exists worksheet assert.EqualError(t, f.ProtectSheet("SheetN", nil), "sheet SheetN does not exist") + // Test protect sheet with invalid sheet name + assert.EqualError(t, f.ProtectSheet("Sheet:1", nil), ErrSheetNameInvalid.Error()) } func TestUnprotectSheet(t *testing.T) { @@ -1326,10 +1344,10 @@ func TestAddVBAProject(t *testing.T) { assert.EqualError(t, f.AddVBAProject("macros.bin"), "stat macros.bin: no such file or directory") assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "Book1.xlsx")), ErrAddVBAProject.Error()) assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin"))) - // Test add VBA project twice. + // 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. + // Test add VBA 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") diff --git a/merge_test.go b/merge_test.go index 31f2cf4..40055c9 100644 --- a/merge_test.go +++ b/merge_test.go @@ -29,7 +29,8 @@ func TestMergeCell(t *testing.T) { value, err := f.GetCellValue("Sheet1", "H11") assert.Equal(t, "100", value) assert.NoError(t, err) - value, err = f.GetCellValue("Sheet2", "A6") // Merged cell ref is single coordinate. + // Merged cell ref is single coordinate + value, err = f.GetCellValue("Sheet2", "A6") assert.Equal(t, "", value) assert.NoError(t, err) value, err = f.GetCellFormula("Sheet1", "G12") @@ -64,9 +65,10 @@ func TestMergeCell(t *testing.T) { assert.NoError(t, f.MergeCell("Sheet3", "M8", "Q13")) assert.NoError(t, f.MergeCell("Sheet3", "N10", "O11")) - // Test get merged cells on not exists worksheet. + // Test merge cells on not exists worksheet assert.EqualError(t, f.MergeCell("SheetN", "N10", "O11"), "sheet SheetN does not exist") - + // Test merged cells with invalid sheet name + assert.EqualError(t, f.MergeCell("Sheet:1", "N10", "O11"), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestMergeCell.xlsx"))) assert.NoError(t, f.Close()) @@ -137,8 +139,10 @@ func TestGetMergeCells(t *testing.T) { assert.Equal(t, wants[i].start, m.GetStartAxis()) assert.Equal(t, wants[i].end, m.GetEndAxis()) } - - // Test get merged cells on not exists worksheet. + // Test get merged cells with invalid sheet name + _, err = f.GetMergeCells("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + // Test get merged cells on not exists worksheet _, err = f.GetMergeCells("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") assert.NoError(t, f.Close()) @@ -158,7 +162,7 @@ func TestUnmergeCell(t *testing.T) { assert.EqualError(t, f.UnmergeCell("Sheet1", "A", "A"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) - // unmerge the mergecell that contains A1 + // Test unmerge the merged cells that contains A1 assert.NoError(t, f.UnmergeCell(sheet1, "A1", "A1")) if len(sheet.MergeCells.Cells) != mergeCellNum-1 { t.FailNow() @@ -169,9 +173,12 @@ func TestUnmergeCell(t *testing.T) { f = NewFile() assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3")) - // Test unmerged range reference on not exists worksheet. + // Test unmerged range reference on not exists worksheet assert.EqualError(t, f.UnmergeCell("SheetN", "A1", "A1"), "sheet SheetN does not exist") + // Test unmerge the merged cells with invalid sheet name + assert.EqualError(t, f.UnmergeCell("Sheet:1", "A1", "A1"), ErrSheetNameInvalid.Error()) + ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).MergeCells = nil diff --git a/picture_test.go b/picture_test.go index 11196c6..2392314 100644 --- a/picture_test.go +++ b/picture_test.go @@ -35,17 +35,17 @@ func TestAddPicture(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) - // Test add picture to worksheet with offset and location hyperlink. + // Test add picture to worksheet with offset and location hyperlink assert.NoError(t, f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"), `{"x_offset": 140, "y_offset": 120, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`)) - // Test add picture to worksheet with offset, external hyperlink and positioning. + // Test add picture to worksheet with offset, external hyperlink and positioning assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"), `{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`)) file, err := os.ReadFile(filepath.Join("test", "images", "excel.png")) assert.NoError(t, err) - // Test add picture to worksheet with autofit. + // Test add picture to worksheet with autofit assert.NoError(t, f.AddPicture("Sheet1", "A30", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`)) assert.NoError(t, f.AddPicture("Sheet1", "B30", filepath.Join("test", "images", "excel.jpg"), `{"x_offset": 10, "y_offset": 10, "autofit": true}`)) f.NewSheet("AddPicture") @@ -55,41 +55,44 @@ func TestAddPicture(t *testing.T) { assert.NoError(t, f.AddPicture("AddPicture", "C6", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`)) assert.NoError(t, f.AddPicture("AddPicture", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`)) - // Test add picture to worksheet from bytes. + // Test add picture to worksheet from bytes assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", "", "Excel Logo", ".png", file)) - // Test add picture to worksheet from bytes with illegal cell reference. + // Test add picture to worksheet from bytes with illegal cell reference assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "A", "", "Excel Logo", ".png", file), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.NoError(t, f.AddPicture("Sheet1", "Q8", filepath.Join("test", "images", "excel.gif"), "")) assert.NoError(t, f.AddPicture("Sheet1", "Q15", filepath.Join("test", "images", "excel.jpg"), "")) assert.NoError(t, f.AddPicture("Sheet1", "Q22", filepath.Join("test", "images", "excel.tif"), "")) - // 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.Close()) - // Test add picture with unsupported charset content types. + // 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") + + // Test add picture with invalid sheet name + assert.EqualError(t, f.AddPicture("Sheet:1", "A1", filepath.Join("test", "images", "excel.jpg"), ""), ErrSheetNameInvalid.Error()) } func TestAddPictureErrors(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) assert.NoError(t, err) - // Test add picture to worksheet with invalid file path. + // Test add picture to worksheet with invalid file path assert.Error(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), "")) - // Test add picture to worksheet with unsupported file type. + // Test add picture to worksheet with unsupported file type assert.EqualError(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), ""), ErrImgExt.Error()) assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1)), ErrImgExt.Error()) - // Test add picture to worksheet with invalid file data. + // Test add picture to worksheet with invalid file data assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1)), image.ErrFormat.Error()) - // Test add picture with custom image decoder and encoder. + // Test add picture with custom image decoder and encoder decode := func(r io.Reader) (image.Image, error) { return nil, nil } decodeConfig := func(r io.Reader) (image.Config, error) { return image.Config{Height: 100, Width: 90}, nil } image.RegisterFormat("emf", "", decode, decodeConfig) @@ -126,22 +129,26 @@ func TestGetPicture(t *testing.T) { t.FailNow() } - // Try to get picture from a worksheet with illegal cell reference. + // Try to get picture from a worksheet with illegal cell reference _, _, err = f.GetPicture("Sheet1", "A") assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) - // Try to get picture from a worksheet that doesn't contain any images. + // Try to get picture from a worksheet that doesn't contain any images file, raw, err = f.GetPicture("Sheet3", "I9") assert.EqualError(t, err, "sheet Sheet3 does not exist") assert.Empty(t, file) assert.Empty(t, raw) - // Try to get picture from a cell that doesn't contain an image. + // Try to get picture from a cell that doesn't contain an image file, raw, err = f.GetPicture("Sheet2", "A2") assert.NoError(t, err) assert.Empty(t, file) assert.Empty(t, raw) + // Test get picture with invalid sheet name + _, _, err = f.GetPicture("Sheet:1", "A2") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + f.getDrawingRelationships("xl/worksheets/_rels/sheet1.xml.rels", "rId8") f.getDrawingRelationships("", "") f.getSheetRelationshipsTargetByID("", "") @@ -160,14 +167,14 @@ func TestGetPicture(t *testing.T) { t.FailNow() } - // Try to get picture from a local storage file that doesn't contain an image. + // Try to get picture from a local storage file that doesn't contain an image file, raw, err = f.GetPicture("Sheet1", "F22") assert.NoError(t, err) assert.Empty(t, file) assert.Empty(t, raw) assert.NoError(t, f.Close()) - // Test get picture from none drawing worksheet. + // Test get picture from none drawing worksheet f = NewFile() file, raw, err = f.GetPicture("Sheet1", "F22") assert.NoError(t, err) @@ -176,7 +183,7 @@ func TestGetPicture(t *testing.T) { f, err = prepareTestBook1() assert.NoError(t, err) - // Test get pictures with unsupported charset. + // Test get pictures with unsupported charset path := "xl/drawings/drawing1.xml" f.Pkg.Store(path, MacintoshCyrillicCharset) _, _, err = f.getPicture(20, 5, path, "xl/drawings/_rels/drawing2.xml.rels") @@ -187,7 +194,7 @@ func TestGetPicture(t *testing.T) { } func TestAddDrawingPicture(t *testing.T) { - // Test addDrawingPicture with illegal cell reference. + // Test addDrawingPicture with illegal cell reference f := NewFile() assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", "", 0, 0, image.Config{}, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) @@ -211,6 +218,8 @@ func TestAddPictureFromBytes(t *testing.T) { }) assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.") assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), "", "logo", ".png", imgFile), "sheet SheetN does not exist") + // Test add picture from bytes with invalid sheet name + assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", fmt.Sprint("A", 1), "", "logo", ".png", imgFile), ErrSheetNameInvalid.Error()) } func TestDeletePicture(t *testing.T) { @@ -220,21 +229,23 @@ func TestDeletePicture(t *testing.T) { assert.NoError(t, f.AddPicture("Sheet1", "P1", filepath.Join("test", "images", "excel.jpg"), "")) assert.NoError(t, f.DeletePicture("Sheet1", "P1")) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture.xlsx"))) - // Test delete picture on not exists worksheet. + // Test delete picture on not exists worksheet assert.EqualError(t, f.DeletePicture("SheetN", "A1"), "sheet SheetN does not exist") - // Test delete picture with invalid coordinates. + // Test delete picture with invalid sheet name + assert.EqualError(t, f.DeletePicture("Sheet:1", "A1"), ErrSheetNameInvalid.Error()) + // Test delete picture with invalid coordinates assert.EqualError(t, f.DeletePicture("Sheet1", ""), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error()) assert.NoError(t, f.Close()) - // Test delete picture on no chart worksheet. + // Test delete picture on no chart worksheet assert.NoError(t, NewFile().DeletePicture("Sheet1", "A1")) } func TestDrawingResize(t *testing.T) { f := NewFile() - // Test calculate drawing resize on not exists worksheet. + // Test calculate drawing resize on not exists worksheet _, _, _, _, err := f.drawingResize("SheetN", "A1", 1, 1, nil) assert.EqualError(t, err, "sheet SheetN does not exist") - // Test calculate drawing resize with invalid coordinates. + // Test calculate drawing resize with invalid coordinates _, _, _, _, err = f.drawingResize("Sheet1", "", 1, 1, nil) assert.EqualError(t, err, newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error()) ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") @@ -245,7 +256,7 @@ func TestDrawingResize(t *testing.T) { func TestSetContentTypePartImageExtensions(t *testing.T) { f := NewFile() - // Test set content type part image extensions with unsupported charset content types. + // 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") @@ -253,7 +264,7 @@ func TestSetContentTypePartImageExtensions(t *testing.T) { func TestSetContentTypePartVMLExtensions(t *testing.T) { f := NewFile() - // Test set content type part VML extensions with unsupported charset content types. + // 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") @@ -261,7 +272,7 @@ func TestSetContentTypePartVMLExtensions(t *testing.T) { func TestAddContentTypePart(t *testing.T) { f := NewFile() - // Test add content type part with unsupported charset content types. + // 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") diff --git a/pivotTable_test.go b/pivotTable_test.go index fc9e090..206388c 100644 --- a/pivotTable_test.go +++ b/pivotTable_test.go @@ -225,6 +225,12 @@ func TestAddPivotTable(t *testing.T) { Data: []PivotTableField{{Data: "Sales", Subtotal: "-", Name: strings.Repeat("s", MaxFieldLength+1)}}, })) + // Test add pivot table with invalid sheet name + assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{ + DataRange: "Sheet:1!$A$1:$E$31", + PivotTableRange: "Sheet:1!$G$2:$M$34", + Rows: []PivotTableField{{Data: "Year"}}, + }), ErrSheetNameInvalid.Error()) // Test adjust range with invalid range _, _, err := f.adjustRange("") assert.EqualError(t, err, ErrParameterRequired.Error()) diff --git a/rows.go b/rows.go index 9f1ac73..8e0bb63 100644 --- a/rows.go +++ b/rows.go @@ -260,6 +260,9 @@ func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.Sta // fmt.Println(err) // } func (f *File) Rows(sheet string) (*Rows, error) { + if err := checkSheetName(sheet); err != nil { + return nil, err + } name, ok := f.getSheetXMLPath(sheet) if !ok { return nil, ErrSheetNotExist{sheet} @@ -268,7 +271,7 @@ func (f *File) Rows(sheet string) (*Rows, error) { worksheet := ws.(*xlsxWorksheet) worksheet.Lock() defer worksheet.Unlock() - // flush data + // Flush data output, _ := xml.Marshal(worksheet) f.saveFileList(name, f.replaceNameSpaceBytes(name, output)) } diff --git a/rows_test.go b/rows_test.go index 2e49c28..70ad48b 100644 --- a/rows_test.go +++ b/rows_test.go @@ -13,17 +13,15 @@ import ( func TestRows(t *testing.T) { const sheet2 = "Sheet2" - f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) - if !assert.NoError(t, err) { - t.FailNow() - } + assert.NoError(t, err) + + // Test get rows with invalid sheet name + _, err = f.Rows("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) rows, err := f.Rows(sheet2) - if !assert.NoError(t, err) { - t.FailNow() - } - + assert.NoError(t, err) var collectedRows [][]string for rows.Next() { columns, err := rows.Columns() @@ -49,13 +47,13 @@ func TestRows(t *testing.T) { _, err = f.Rows("Sheet1") assert.NoError(t, err) - // Test reload the file to memory from system temporary directory. + // Test reload the file to memory from system temporary directory f, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipXMLSizeLimit: 128}) assert.NoError(t, err) value, err := f.GetCellValue("Sheet1", "A19") assert.NoError(t, err) assert.Equal(t, "Total:", value) - // Test load shared string table to memory. + // Test load shared string table to memory err = f.SetCellValue("Sheet1", "A19", "A19") assert.NoError(t, err) value, err = f.GetCellValue("Sheet1", "A19") @@ -64,7 +62,7 @@ func TestRows(t *testing.T) { assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRow.xlsx"))) assert.NoError(t, f.Close()) - // Test rows iterator with unsupported charset shared strings table. + // Test rows iterator with unsupported charset shared strings table f.SharedStrings = nil f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset) rows, err = f.Rows(sheet2) @@ -154,25 +152,32 @@ func TestRowHeight(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 111.0, height) - // Test set row height overflow max row height limit. + // Test set row height overflow max row height limit assert.EqualError(t, f.SetRowHeight(sheet1, 4, MaxRowHeight+1), ErrMaxRowHeight.Error()) - // Test get row height that rows index over exists rows. + // Test get row height that rows index over exists rows height, err = f.GetRowHeight(sheet1, 5) assert.NoError(t, err) assert.Equal(t, defaultRowHeight, height) - // Test get row height that rows heights haven't changed. + // Test get row height that rows heights haven't changed height, err = f.GetRowHeight(sheet1, 3) assert.NoError(t, err) assert.Equal(t, defaultRowHeight, height) - // Test set and get row height on not exists worksheet. + // Test set and get row height on not exists worksheet assert.EqualError(t, f.SetRowHeight("SheetN", 1, 111.0), "sheet SheetN does not exist") _, err = f.GetRowHeight("SheetN", 3) assert.EqualError(t, err, "sheet SheetN does not exist") - // Test get row height with custom default row height. + // Test set row height with invalid sheet name + assert.EqualError(t, f.SetRowHeight("Sheet:1", 1, 10.0), ErrSheetNameInvalid.Error()) + + // Test get row height with invalid sheet name + _, err = f.GetRowHeight("Sheet:1", 3) + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + + // Test get row height with custom default row height assert.NoError(t, f.SetSheetProps(sheet1, &SheetPropsOptions{ DefaultRowHeight: float64Ptr(30.0), CustomHeight: boolPtr(true), @@ -181,7 +186,7 @@ func TestRowHeight(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 30.0, height) - // Test set row height with custom default row height with prepare XML. + // Test set row height with custom default row height with prepare XML assert.NoError(t, f.SetCellValue(sheet1, "A10", "A10")) f.NewSheet("Sheet2") @@ -233,17 +238,17 @@ func TestColumns(t *testing.T) { func TestSharedStringsReader(t *testing.T) { f := NewFile() - // Test read shared string with unsupported charset. + // Test read shared string with unsupported charset f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset) _, 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. + // 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. + // Test read shared strings with unsupported charset workbook relationships f = NewFile() f.Relationships.Delete(defaultXMLPathWorkbookRels) f.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset) @@ -267,13 +272,17 @@ func TestRowVisibility(t *testing.T) { assert.NoError(t, err) assert.EqualError(t, f.SetRowVisible("Sheet3", 0, true), newInvalidRowNumberError(0).Error()) assert.EqualError(t, f.SetRowVisible("SheetN", 2, false), "sheet SheetN does not exist") + // Test set row visibility with invalid sheet name + assert.EqualError(t, f.SetRowVisible("Sheet:1", 1, false), ErrSheetNameInvalid.Error()) visible, err = f.GetRowVisible("Sheet3", 0) assert.Equal(t, false, visible) assert.EqualError(t, err, newInvalidRowNumberError(0).Error()) _, err = f.GetRowVisible("SheetN", 1) assert.EqualError(t, err, "sheet SheetN does not exist") - + // Test get row visibility with invalid sheet name + _, err = f.GetRowVisible("Sheet:1", 1) + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestRowVisibility.xlsx"))) } @@ -335,7 +344,9 @@ func TestRemoveRow(t *testing.T) { assert.NoError(t, f.SaveAs(filepath.Join("test", "TestRemoveRow.xlsx"))) // Test remove row on not exist worksheet - assert.EqualError(t, f.RemoveRow("SheetN", 1), `sheet SheetN does not exist`) + assert.EqualError(t, f.RemoveRow("SheetN", 1), "sheet SheetN does not exist") + // Test remove row with invalid sheet name + assert.EqualError(t, f.RemoveRow("Sheet:1", 1), ErrSheetNameInvalid.Error()) } func TestInsertRows(t *testing.T) { @@ -365,6 +376,8 @@ func TestInsertRows(t *testing.T) { if !assert.Len(t, r.SheetData.Row, rowCount+4) { t.FailNow() } + // Test insert rows with invalid sheet name + assert.EqualError(t, f.InsertRows("Sheet:1", 1, 1), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.InsertRows(sheet1, -1, 1), newInvalidRowNumberError(-1).Error()) assert.EqualError(t, f.InsertRows(sheet1, 0, 1), newInvalidRowNumberError(0).Error()) @@ -892,6 +905,12 @@ func TestDuplicateRowInvalidRowNum(t *testing.T) { } } +func TestDuplicateRow(t *testing.T) { + f := NewFile() + // Test duplicate row with invalid sheet name + assert.EqualError(t, f.DuplicateRowTo("Sheet:1", 1, 2), ErrSheetNameInvalid.Error()) +} + func TestDuplicateRowTo(t *testing.T) { f, sheetName := NewFile(), "Sheet1" // Test duplicate row with invalid target row number @@ -907,6 +926,8 @@ func TestDuplicateRowTo(t *testing.T) { assert.EqualError(t, f.DuplicateRowTo(sheetName, 1, 2), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) // Test duplicate row on not exists worksheet assert.EqualError(t, f.DuplicateRowTo("SheetN", 1, 2), "sheet SheetN does not exist") + // Test duplicate row with invalid sheet name + assert.EqualError(t, f.DuplicateRowTo("Sheet:1", 1, 2), ErrSheetNameInvalid.Error()) } func TestDuplicateMergeCells(t *testing.T) { @@ -976,22 +997,24 @@ func TestSetRowStyle(t *testing.T) { assert.NoError(t, f.SetCellStyle("Sheet1", "B2", "B2", style1)) assert.EqualError(t, f.SetRowStyle("Sheet1", 5, -1, style2), newInvalidRowNumberError(-1).Error()) assert.EqualError(t, f.SetRowStyle("Sheet1", 1, TotalRows+1, style2), ErrMaxRows.Error()) - // Test set row style with invalid style ID. + // Test set row style with invalid style ID assert.EqualError(t, f.SetRowStyle("Sheet1", 1, 1, -1), newInvalidStyleID(-1).Error()) - // Test set row style with not exists style ID. + // Test set row style with not exists style ID assert.EqualError(t, f.SetRowStyle("Sheet1", 1, 1, 10), newInvalidStyleID(10).Error()) assert.EqualError(t, f.SetRowStyle("SheetN", 1, 1, style2), "sheet SheetN does not exist") + // Test set row style with invalid sheet name + assert.EqualError(t, f.SetRowStyle("Sheet:1", 1, 1, 0), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SetRowStyle("Sheet1", 5, 1, style2)) cellStyleID, err := f.GetCellStyle("Sheet1", "B2") assert.NoError(t, err) assert.Equal(t, style2, cellStyleID) - // Test cell inheritance rows style. + // Test cell inheritance rows style assert.NoError(t, f.SetCellValue("Sheet1", "C1", nil)) cellStyleID, err = f.GetCellStyle("Sheet1", "C1") assert.NoError(t, err) assert.Equal(t, style2, cellStyleID) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRowStyle.xlsx"))) - // Test set row style with unsupported charset style sheet. + // Test set row style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) assert.EqualError(t, f.SetRowStyle("Sheet1", 1, 1, cellStyleID), "XML syntax error on line 1: invalid UTF-8") diff --git a/shape_test.go b/shape_test.go index 2b2e87c..4c47d58 100644 --- a/shape_test.go +++ b/shape_test.go @@ -59,7 +59,7 @@ func TestAddShape(t *testing.T) { }`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape1.xlsx"))) - // Test add first shape for given sheet. + // Test add first shape for given sheet f = NewFile() assert.NoError(t, f.AddShape("Sheet1", "A1", `{ "type": "ellipseRibbon", @@ -87,11 +87,13 @@ func TestAddShape(t *testing.T) { } }`)) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx"))) - // Test add shape with unsupported charset style sheet. + // Test add shape with invalid sheet name + assert.EqualError(t, f.AddShape("Sheet:1", "A30", `{"type":"rect","paragraph":[{"text":"Rectangle","font":{"color":"CD5C5C"}},{"text":"Shape","font":{"bold":true,"color":"2980B9"}}]}`), ErrSheetNameInvalid.Error()) + // 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. + // Test add shape with unsupported charset content types f = NewFile() f.ContentTypes = nil f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) diff --git a/sheet.go b/sheet.go index bbf529a..16c4c16 100644 --- a/sheet.go +++ b/sheet.go @@ -35,14 +35,15 @@ import ( // name and returns the index of the sheets in the workbook after it appended. // Note that when creating a new workbook, the default worksheet named // `Sheet1` will be created. -func (f *File) NewSheet(sheet string) int { - if trimSheetName(sheet) == "" { - return -1 +func (f *File) NewSheet(sheet string) (int, error) { + var err error + if err = checkSheetName(sheet); err != nil { + return -1, err } // Check if the worksheet already exists - index := f.GetSheetIndex(sheet) + index, err := f.GetSheetIndex(sheet) if index != -1 { - return index + return index, err } f.DeleteSheet(sheet) f.SheetCount++ @@ -235,7 +236,7 @@ func (f *File) setSheet(index int, name string) { }, } sheetXMLPath := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml" - f.sheetMap[trimSheetName(name)] = sheetXMLPath + f.sheetMap[name] = sheetXMLPath f.Sheet.Store(sheetXMLPath, &ws) f.xmlAttr[sheetXMLPath] = []xml.Attr{NameSpaceSpreadSheet} } @@ -352,11 +353,16 @@ func (f *File) getActiveSheetID() int { // this function only changes the name of the sheet and will not update the // sheet name in the formula or reference associated with the cell. So there // may be problem formula error or reference missing. -func (f *File) SetSheetName(source, target string) { - source = trimSheetName(source) - target = trimSheetName(target) +func (f *File) SetSheetName(source, target string) error { + var err error + if err = checkSheetName(source); err != nil { + return err + } + if err = checkSheetName(target); err != nil { + return err + } if strings.EqualFold(target, source) { - return + return err } wb, _ := f.workbookReader() for k, v := range wb.Sheets.Sheet { @@ -366,6 +372,7 @@ func (f *File) SetSheetName(source, target string) { delete(f.sheetMap, source) } } + return err } // GetSheetName provides a function to get the sheet name of the workbook by @@ -385,9 +392,8 @@ func (f *File) GetSheetName(index int) (name string) { // given sheet name. If given worksheet name is invalid, will return an // integer type value -1. func (f *File) getSheetID(sheet string) int { - sheetName := trimSheetName(sheet) for sheetID, name := range f.GetSheetMap() { - if strings.EqualFold(name, sheetName) { + if strings.EqualFold(name, sheet) { return sheetID } } @@ -397,14 +403,16 @@ func (f *File) getSheetID(sheet string) int { // GetSheetIndex provides a function to get a sheet index of the workbook by // the given sheet name. If the given sheet name is invalid or sheet doesn't // exist, it will return an integer type value -1. -func (f *File) GetSheetIndex(sheet string) int { - sheetName := trimSheetName(sheet) +func (f *File) GetSheetIndex(sheet string) (int, error) { + if err := checkSheetName(sheet); err != nil { + return -1, err + } for index, name := range f.GetSheetList() { - if strings.EqualFold(name, sheetName) { - return index + if strings.EqualFold(name, sheet) { + return index, nil } } - return -1 + return -1, nil } // GetSheetMap provides a function to get worksheets, chart sheets, dialog @@ -474,7 +482,6 @@ func (f *File) getSheetXMLPath(sheet string) (string, bool) { name string ok bool ) - sheet = trimSheetName(sheet) for sheetName, filePath := range f.sheetMap { if strings.EqualFold(sheetName, sheet) { name, ok = filePath, true @@ -530,19 +537,22 @@ func (f *File) setSheetBackground(sheet, extension string, file []byte) error { // references such as formulas, charts, and so on. If there is any referenced // value of the deleted worksheet, it will cause a file error when you open // it. This function will be invalid when only one worksheet is left. -func (f *File) DeleteSheet(sheet string) { - if f.SheetCount == 1 || f.GetSheetIndex(sheet) == -1 { - return +func (f *File) DeleteSheet(sheet string) error { + if err := checkSheetName(sheet); err != nil { + return err } - sheetName := trimSheetName(sheet) + if idx, _ := f.GetSheetIndex(sheet); f.SheetCount == 1 || idx == -1 { + return nil + } + wb, _ := f.workbookReader() wbRels, _ := f.relsReader(f.getWorkbookRelsPath()) activeSheetName := f.GetSheetName(f.GetActiveSheetIndex()) - deleteLocalSheetID := f.GetSheetIndex(sheet) + deleteLocalSheetID, _ := f.GetSheetIndex(sheet) deleteAndAdjustDefinedNames(wb, deleteLocalSheetID) for idx, v := range wb.Sheets.Sheet { - if !strings.EqualFold(v.Name, sheetName) { + if !strings.EqualFold(v.Name, sheet) { continue } @@ -568,7 +578,9 @@ func (f *File) DeleteSheet(sheet string) { delete(f.xmlAttr, sheetXML) f.SheetCount-- } - f.SetActiveSheet(f.GetSheetIndex(activeSheetName)) + index, err := f.GetSheetIndex(activeSheetName) + f.SetActiveSheet(index) + return err } // deleteAndAdjustDefinedNames delete and adjust defined name in the workbook @@ -683,7 +695,9 @@ 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) + if err := checkSheetName(sheet); err != nil { + return err + } wb, err := f.workbookReader() if err != nil { return err @@ -857,17 +871,20 @@ func (f *File) SetPanes(sheet, panes string) error { // name. For example, get visible state of Sheet1: // // f.GetSheetVisible("Sheet1") -func (f *File) GetSheetVisible(sheet string) bool { - name, visible := trimSheetName(sheet), false +func (f *File) GetSheetVisible(sheet string) (bool, error) { + var visible bool + if err := checkSheetName(sheet); err != nil { + return visible, err + } wb, _ := f.workbookReader() for k, v := range wb.Sheets.Sheet { - if strings.EqualFold(v.Name, name) { + if strings.EqualFold(v.Name, sheet) { if wb.Sheets.Sheet[k].State == "" || wb.Sheets.Sheet[k].State == "visible" { visible = true } } } - return visible + return visible, nil } // SearchSheet provides a function to get cell reference by given worksheet name, @@ -889,6 +906,9 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { regSearch bool result []string ) + if err := checkSheetName(sheet); err != nil { + return result, err + } for _, r := range reg { regSearch = r } @@ -897,7 +917,7 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { return result, ErrSheetNotExist{sheet} } if ws, ok := f.Sheet.Load(name); ok && ws != nil { - // flush data + // Flush data output, _ := xml.Marshal(ws.(*xlsxWorksheet)) f.saveFileList(name, f.replaceNameSpaceBytes(name, output)) } @@ -1051,7 +1071,7 @@ func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) { // | // &F | Current workbook's file name // | -// &G | Drawing object as background +// &G | Drawing object as background (Not support currently) // | // &H | Shadow text format // | @@ -1167,47 +1187,47 @@ func (f *File) SetHeaderFooter(sheet string, settings *HeaderFooterOptions) erro // Password: "password", // EditScenarios: false, // }) -func (f *File) ProtectSheet(sheet string, settings *SheetProtectionOptions) error { +func (f *File) ProtectSheet(sheet string, opts *SheetProtectionOptions) error { ws, err := f.workSheetReader(sheet) if err != nil { return err } - if settings == nil { - settings = &SheetProtectionOptions{ + if opts == nil { + opts = &SheetProtectionOptions{ EditObjects: true, EditScenarios: true, SelectLockedCells: true, } } ws.SheetProtection = &xlsxSheetProtection{ - AutoFilter: settings.AutoFilter, - DeleteColumns: settings.DeleteColumns, - DeleteRows: settings.DeleteRows, - FormatCells: settings.FormatCells, - FormatColumns: settings.FormatColumns, - FormatRows: settings.FormatRows, - InsertColumns: settings.InsertColumns, - InsertHyperlinks: settings.InsertHyperlinks, - InsertRows: settings.InsertRows, - Objects: settings.EditObjects, - PivotTables: settings.PivotTables, - Scenarios: settings.EditScenarios, - SelectLockedCells: settings.SelectLockedCells, - SelectUnlockedCells: settings.SelectUnlockedCells, + AutoFilter: opts.AutoFilter, + DeleteColumns: opts.DeleteColumns, + DeleteRows: opts.DeleteRows, + FormatCells: opts.FormatCells, + FormatColumns: opts.FormatColumns, + FormatRows: opts.FormatRows, + InsertColumns: opts.InsertColumns, + InsertHyperlinks: opts.InsertHyperlinks, + InsertRows: opts.InsertRows, + Objects: opts.EditObjects, + PivotTables: opts.PivotTables, + Scenarios: opts.EditScenarios, + SelectLockedCells: opts.SelectLockedCells, + SelectUnlockedCells: opts.SelectUnlockedCells, Sheet: true, - Sort: settings.Sort, + Sort: opts.Sort, } - if settings.Password != "" { - if settings.AlgorithmName == "" { - ws.SheetProtection.Password = genSheetPasswd(settings.Password) + if opts.Password != "" { + if opts.AlgorithmName == "" { + ws.SheetProtection.Password = genSheetPasswd(opts.Password) return err } - hashValue, saltValue, err := genISOPasswdHash(settings.Password, settings.AlgorithmName, "", int(sheetProtectionSpinCount)) + hashValue, saltValue, err := genISOPasswdHash(opts.Password, opts.AlgorithmName, "", int(sheetProtectionSpinCount)) if err != nil { return err } ws.SheetProtection.Password = "" - ws.SheetProtection.AlgorithmName = settings.AlgorithmName + ws.SheetProtection.AlgorithmName = opts.AlgorithmName ws.SheetProtection.SaltValue = saltValue ws.SheetProtection.HashValue = hashValue ws.SheetProtection.SpinCount = int(sheetProtectionSpinCount) @@ -1246,25 +1266,25 @@ func (f *File) UnprotectSheet(sheet string, password ...string) error { return err } -// trimSheetName provides a function to trim invalid characters by given worksheet -// name. -func trimSheetName(name string) string { - if strings.ContainsAny(name, ":\\/?*[]") || utf8.RuneCountInString(name) > 31 { - r := make([]rune, 0, 31) - for _, v := range name { - switch v { - case 58, 92, 47, 63, 42, 91, 93: // replace :\/?*[] - continue - default: - r = append(r, v) - } - if len(r) == 31 { - break - } - } - name = string(r) +// checkSheetName check whether there are illegal characters in the sheet name. +// 1. Confirm that the sheet name is not empty +// 2. Make sure to enter a name with no more than 31 characters +// 3. Make sure the first or last character of the name cannot be a single quote +// 4. Verify that the following characters are not included in the name :\/?*[] +func checkSheetName(name string) error { + if name == "" { + return ErrSheetNameBlank } - return name + if utf8.RuneCountInString(name) > MaxSheetNameLength { + return ErrSheetNameLength + } + if strings.HasPrefix(name, "'") || strings.HasSuffix(name, "'") { + return ErrSheetNameSingleQuote + } + if strings.ContainsAny(name, ":\\/?*[]") { + return ErrSheetNameInvalid + } + return nil } // SetPageLayout provides a function to sets worksheet page layout. @@ -1499,7 +1519,7 @@ func (f *File) SetDefinedName(definedName *DefinedName) error { Data: definedName.RefersTo, } if definedName.Scope != "" { - if sheetIndex := f.GetSheetIndex(definedName.Scope); sheetIndex >= 0 { + if sheetIndex, _ := f.GetSheetIndex(definedName.Scope); sheetIndex >= 0 { d.LocalSheetID = &sheetIndex } } @@ -1579,7 +1599,7 @@ func (f *File) GetDefinedName() []DefinedName { // GroupSheets provides a function to group worksheets by given worksheets // name. Group worksheets must contain an active worksheet. func (f *File) GroupSheets(sheets []string) error { - // check an active worksheet in group worksheets + // Check an active worksheet in group worksheets var inActiveSheet bool activeSheet := f.GetActiveSheetIndex() sheetMap := f.GetSheetList() diff --git a/sheet_test.go b/sheet_test.go index 2494cfb..ed85c26 100644 --- a/sheet_test.go +++ b/sheet_test.go @@ -16,18 +16,27 @@ import ( func TestNewSheet(t *testing.T) { f := NewFile() f.NewSheet("Sheet2") - sheetID := f.NewSheet("sheet2") + sheetID, err := f.NewSheet("sheet2") + assert.NoError(t, err) f.SetActiveSheet(sheetID) - // delete original sheet - f.DeleteSheet(f.GetSheetName(f.GetSheetIndex("Sheet1"))) + // Test delete original sheet + idx, err := f.GetSheetIndex("Sheet1") + assert.NoError(t, err) + f.DeleteSheet(f.GetSheetName(idx)) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx"))) - // create new worksheet with already exists name - assert.Equal(t, f.GetSheetIndex("Sheet2"), f.NewSheet("Sheet2")) - // create new worksheet with empty sheet name - assert.Equal(t, -1, f.NewSheet(":\\/?*[]")) + // Test create new worksheet with already exists name + sheetID, err = f.NewSheet("Sheet2") + assert.NoError(t, err) + idx, err = f.GetSheetIndex("Sheet2") + assert.NoError(t, err) + assert.Equal(t, idx, sheetID) + // Test create new worksheet with empty sheet name + sheetID, err = f.NewSheet(":\\/?*[]") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) + assert.Equal(t, -1, sheetID) } -func TestSetPane(t *testing.T) { +func TestSetPanes(t *testing.T) { f := NewFile() assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":false,"split":false}`)) f.NewSheet("Panes 2") @@ -38,6 +47,8 @@ func TestSetPane(t *testing.T) { assert.NoError(t, f.SetPanes("Panes 4", `{"freeze":true,"split":false,"x_split":0,"y_split":9,"top_left_cell":"A34","active_pane":"bottomLeft","panes":[{"sqref":"A11:XFD11","active_cell":"A11","pane":"bottomLeft"}]}`)) assert.EqualError(t, f.SetPanes("Panes 4", ""), "unexpected end of JSON input") assert.EqualError(t, f.SetPanes("SheetN", ""), "sheet SheetN does not exist") + // Test set panes with invalid sheet name + assert.EqualError(t, f.SetPanes("Sheet:1", `{"freeze":false,"split":false}`), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx"))) // Test add pane on empty sheet views worksheet f = NewFile() @@ -52,11 +63,14 @@ func TestSearchSheet(t *testing.T) { if !assert.NoError(t, err) { t.FailNow() } - // Test search in a not exists worksheet. + // Test search in a not exists worksheet _, err = f.SearchSheet("Sheet4", "") assert.EqualError(t, err, "sheet Sheet4 does not exist") + // Test search sheet with invalid sheet name + _, err = f.SearchSheet("Sheet:1", "") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) var expected []string - // Test search a not exists value. + // Test search a not exists value result, err := f.SearchSheet("Sheet1", "X") assert.NoError(t, err) assert.EqualValues(t, expected, result) @@ -120,23 +134,30 @@ func TestSetPageLayout(t *testing.T) { opts, err := f.GetPageLayout("Sheet1") assert.NoError(t, err) assert.Equal(t, expected, opts) - // Test set page layout on not exists worksheet. + // Test set page layout on not exists worksheet assert.EqualError(t, f.SetPageLayout("SheetN", nil), "sheet SheetN does not exist") + // Test set page layout with invalid sheet name + assert.EqualError(t, f.SetPageLayout("Sheet:1", nil), ErrSheetNameInvalid.Error()) } func TestGetPageLayout(t *testing.T) { f := NewFile() - // Test get page layout on not exists worksheet. + // Test get page layout on not exists worksheet _, err := f.GetPageLayout("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test get page layout with invalid sheet name + _, err = f.GetPageLayout("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestSetHeaderFooter(t *testing.T) { f := NewFile() assert.NoError(t, f.SetCellStr("Sheet1", "A1", "Test SetHeaderFooter")) - // Test set header and footer on not exists worksheet. + // Test set header and footer on not exists worksheet assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN does not exist") - // Test set header and footer with illegal setting. + // Test Sheet:1 with invalid sheet name + assert.EqualError(t, f.SetHeaderFooter("Sheet:1", nil), ErrSheetNameInvalid.Error()) + // Test set header and footer with illegal setting assert.EqualError(t, f.SetHeaderFooter("Sheet1", &HeaderFooterOptions{ OddHeader: strings.Repeat("c", MaxFieldLength+1), }), newFieldLengthError("OddHeader").Error()) @@ -190,13 +211,13 @@ 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. + // 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. + // Test delete defined name with unsupported charset workbook f.WorkBook = nil f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset) assert.EqualError(t, f.DeleteDefinedName(&DefinedName{Name: "Amount"}), @@ -211,6 +232,8 @@ func TestGroupSheets(t *testing.T) { } assert.EqualError(t, f.GroupSheets([]string{"Sheet1", "SheetN"}), "sheet SheetN does not exist") assert.EqualError(t, f.GroupSheets([]string{"Sheet2", "Sheet3"}), "group worksheet must contain an active worksheet") + // Test group sheets with invalid sheet name + assert.EqualError(t, f.GroupSheets([]string{"Sheet:1", "Sheet1"}), ErrSheetNameInvalid.Error()) assert.NoError(t, f.GroupSheets([]string{"Sheet1", "Sheet2"})) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestGroupSheets.xlsx"))) } @@ -232,6 +255,8 @@ func TestInsertPageBreak(t *testing.T) { assert.NoError(t, f.InsertPageBreak("Sheet1", "C3")) assert.EqualError(t, f.InsertPageBreak("Sheet1", "A"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.InsertPageBreak("SheetN", "C3"), "sheet SheetN does not exist") + // Test insert page break with invalid sheet name + assert.EqualError(t, f.InsertPageBreak("Sheet:1", "C3"), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestInsertPageBreak.xlsx"))) } @@ -258,6 +283,8 @@ func TestRemovePageBreak(t *testing.T) { assert.EqualError(t, f.RemovePageBreak("Sheet1", "A"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.RemovePageBreak("SheetN", "C3"), "sheet SheetN does not exist") + // Test remove page break with invalid sheet name + assert.EqualError(t, f.RemovePageBreak("Sheet:1", "A3"), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestRemovePageBreak.xlsx"))) } @@ -305,7 +332,8 @@ func TestSetActiveSheet(t *testing.T) { f = NewFile() f.WorkBook.BookViews = nil - idx := f.NewSheet("Sheet2") + idx, err := f.NewSheet("Sheet2") + assert.NoError(t, err) ws, ok = f.Sheet.Load("xl/worksheets/sheet2.xml") assert.True(t, ok) ws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{}} @@ -314,9 +342,11 @@ func TestSetActiveSheet(t *testing.T) { func TestSetSheetName(t *testing.T) { f := NewFile() - // Test set worksheet with the same name. - f.SetSheetName("Sheet1", "Sheet1") + // Test set worksheet with the same name + assert.NoError(t, f.SetSheetName("Sheet1", "Sheet1")) assert.Equal(t, "Sheet1", f.GetSheetName(0)) + // Test set sheet name with invalid sheet name + assert.EqualError(t, f.SetSheetName("Sheet:1", "Sheet1"), ErrSheetNameInvalid.Error()) } func TestWorksheetWriter(t *testing.T) { @@ -348,7 +378,9 @@ func TestGetWorkbookRelsPath(t *testing.T) { func TestDeleteSheet(t *testing.T) { f := NewFile() - f.SetActiveSheet(f.NewSheet("Sheet2")) + idx, err := f.NewSheet("Sheet2") + assert.NoError(t, err) + f.SetActiveSheet(idx) f.NewSheet("Sheet3") f.DeleteSheet("Sheet1") assert.Equal(t, "Sheet2", f.GetSheetName(f.GetActiveSheetIndex())) @@ -363,8 +395,10 @@ func TestDeleteSheet(t *testing.T) { assert.NoError(t, f.AutoFilter("Sheet1", "A1", "A1", "")) assert.NoError(t, f.AutoFilter("Sheet2", "A1", "A1", "")) assert.NoError(t, f.AutoFilter("Sheet3", "A1", "A1", "")) - f.DeleteSheet("Sheet2") - f.DeleteSheet("Sheet1") + assert.NoError(t, f.DeleteSheet("Sheet2")) + assert.NoError(t, f.DeleteSheet("Sheet1")) + // Test delete sheet with invalid sheet name + assert.EqualError(t, f.DeleteSheet("Sheet:1"), ErrSheetNameInvalid.Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet2.xlsx"))) } @@ -382,14 +416,32 @@ func TestGetSheetID(t *testing.T) { func TestSetSheetVisible(t *testing.T) { f := NewFile() + // Test set sheet visible with invalid sheet name + assert.EqualError(t, f.SetSheetVisible("Sheet:1", false), ErrSheetNameInvalid.Error()) 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. + // 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 TestGetSheetVisible(t *testing.T) { + f := NewFile() + // Test get sheet visible with invalid sheet name + visible, err := f.GetSheetVisible("Sheet:1") + assert.Equal(t, false, visible) + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) +} + +func TestGetSheetIndex(t *testing.T) { + f := NewFile() + // Test get sheet index with invalid sheet name + idx, err := f.GetSheetIndex("Sheet:1") + assert.Equal(t, -1, idx) + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) +} + func TestSetContentTypes(t *testing.T) { f := NewFile() // Test set content type with unsupported charset content types. @@ -482,8 +534,34 @@ func TestSetSheetBackgroundFromBytes(t *testing.T) { assert.NoError(t, img.Close()) assert.NoError(t, f.SetSheetBackgroundFromBytes(imageTypes, imageTypes, content)) } + // Test set worksheet background with invalid sheet name + img, err := os.Open(filepath.Join("test", "images", "excel.png")) + assert.NoError(t, err) + content, err := io.ReadAll(img) + assert.NoError(t, err) + assert.EqualError(t, f.SetSheetBackgroundFromBytes("Sheet:1", ".png", content), ErrSheetNameInvalid.Error()) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetBackgroundFromBytes.xlsx"))) assert.NoError(t, f.Close()) assert.EqualError(t, f.SetSheetBackgroundFromBytes("Sheet1", ".svg", nil), ErrParameterInvalid.Error()) } + +func TestCheckSheetName(t *testing.T) { + // Test valid sheet name + assert.NoError(t, checkSheetName("Sheet1")) + assert.NoError(t, checkSheetName("She'et1")) + // Test invalid sheet name, empty name + assert.EqualError(t, checkSheetName(""), ErrSheetNameBlank.Error()) + // Test invalid sheet name, include :\/?*[] + assert.EqualError(t, checkSheetName("Sheet:"), ErrSheetNameInvalid.Error()) + assert.EqualError(t, checkSheetName(`Sheet\`), ErrSheetNameInvalid.Error()) + assert.EqualError(t, checkSheetName("Sheet/"), ErrSheetNameInvalid.Error()) + assert.EqualError(t, checkSheetName("Sheet?"), ErrSheetNameInvalid.Error()) + assert.EqualError(t, checkSheetName("Sheet*"), ErrSheetNameInvalid.Error()) + assert.EqualError(t, checkSheetName("Sheet["), ErrSheetNameInvalid.Error()) + assert.EqualError(t, checkSheetName("Sheet]"), ErrSheetNameInvalid.Error()) + // Test invalid sheet name, single quotes at the front or at the end + assert.EqualError(t, checkSheetName("'Sheet"), ErrSheetNameSingleQuote.Error()) + assert.EqualError(t, checkSheetName("Sheet'"), ErrSheetNameSingleQuote.Error()) +} diff --git a/sheetpr_test.go b/sheetpr_test.go index d422e3f..daf6c19 100644 --- a/sheetpr_test.go +++ b/sheetpr_test.go @@ -27,15 +27,20 @@ func TestSetPageMargins(t *testing.T) { opts, err := f.GetPageMargins("Sheet1") assert.NoError(t, err) assert.Equal(t, expected, opts) - // Test set page margins on not exists worksheet. + // Test set page margins on not exists worksheet assert.EqualError(t, f.SetPageMargins("SheetN", nil), "sheet SheetN does not exist") + // Test set page margins with invalid sheet name + assert.EqualError(t, f.SetPageMargins("Sheet:1", nil), ErrSheetNameInvalid.Error()) } func TestGetPageMargins(t *testing.T) { f := NewFile() - // Test get page margins on not exists worksheet. + // Test get page margins on not exists worksheet _, err := f.GetPageMargins("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test get page margins with invalid sheet name + _, err = f.GetPageMargins("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestSetSheetProps(t *testing.T) { @@ -80,13 +85,18 @@ func TestSetSheetProps(t *testing.T) { ws.(*xlsxWorksheet).SheetPr = nil assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorTint: float64Ptr(1)})) - // Test SetSheetProps on not exists worksheet. + // Test set worksheet properties on not exists worksheet assert.EqualError(t, f.SetSheetProps("SheetN", nil), "sheet SheetN does not exist") + // Test set worksheet properties with invalid sheet name + assert.EqualError(t, f.SetSheetProps("Sheet:1", nil), ErrSheetNameInvalid.Error()) } func TestGetSheetProps(t *testing.T) { f := NewFile() - // Test GetSheetProps on not exists worksheet. + // Test get worksheet properties on not exists worksheet _, err := f.GetSheetProps("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test get worksheet properties with invalid sheet name + _, err = f.GetSheetProps("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } diff --git a/sparkline_test.go b/sparkline_test.go index e20dfdc..c2c1c41 100644 --- a/sparkline_test.go +++ b/sparkline_test.go @@ -214,7 +214,7 @@ func TestAddSparkline(t *testing.T) { Negative: true, })) - // Save spreadsheet by the given path. + // Save spreadsheet by the given path assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddSparkline.xlsx"))) // Test error exceptions @@ -225,6 +225,14 @@ func TestAddSparkline(t *testing.T) { assert.EqualError(t, f.AddSparkline("Sheet1", nil), ErrParameterRequired.Error()) + // Test add sparkline with invalid sheet name + assert.EqualError(t, f.AddSparkline("Sheet:1", &SparklineOptions{ + Location: []string{"F3"}, + Range: []string{"Sheet2!A3:E3"}, + Type: "win_loss", + Negative: true, + }), ErrSheetNameInvalid.Error()) + assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{ Range: []string{"Sheet2!A3:E3"}, }), ErrSparklineLocation.Error()) diff --git a/stream.go b/stream.go index a575e76..0209e22 100644 --- a/stream.go +++ b/stream.go @@ -102,6 +102,9 @@ type StreamWriter struct { // excelize.Cell{Value: 1}}, // excelize.RowOpts{StyleID: styleID, Height: 20, Hidden: false}); func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) { + if err := checkSheetName(sheet); err != nil { + return nil, err + } sheetID := f.getSheetID(sheet) if sheetID == -1 { return nil, newNoExistSheetError(sheet) @@ -219,8 +222,8 @@ func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error { sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml" tableXML := strings.ReplaceAll(sheetRelationshipsTableXML, "..", "xl") - // Add first table for given sheet. - sheetPath := sw.file.sheetMap[trimSheetName(sw.Sheet)] + // Add first table for given sheet + sheetPath := sw.file.sheetMap[sw.Sheet] sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" rID := sw.file.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "") @@ -661,7 +664,7 @@ func (sw *StreamWriter) Flush() error { return err } - sheetPath := sw.file.sheetMap[trimSheetName(sw.Sheet)] + sheetPath := sw.file.sheetMap[sw.Sheet] sw.file.Sheet.Delete(sheetPath) delete(sw.file.checked, sheetPath) sw.file.Pkg.Delete(sheetPath) diff --git a/stream_test.go b/stream_test.go index 41f5415..1a63e35 100644 --- a/stream_test.go +++ b/stream_test.go @@ -257,6 +257,9 @@ func TestNewStreamWriter(t *testing.T) { assert.NoError(t, err) _, err = file.NewStreamWriter("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test new stream write with invalid sheet name + _, err = file.NewStreamWriter("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestStreamMarshalAttrs(t *testing.T) { diff --git a/styles.go b/styles.go index 79bd6d3..0f0b560 100644 --- a/styles.go +++ b/styles.go @@ -1101,11 +1101,25 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { return &fs, err } -// NewStyle provides a function to create the style for cells by given -// structure pointer or JSON. This function is concurrency safe. Note that the -// color field uses RGB color code. +// NewStyle provides a function to create the style for cells by given structure +// pointer or JSON. This function is concurrency safe. Note that +// the 'Font.Color' field uses an RGB color represented in 'RRGGBB' hexadecimal +// notation. // -// The following shows the border styles sorted by excelize index number: +// The following table shows the border types used in 'Border.Type' supported by +// excelize: +// +// Type | Description +// --------------+------------------ +// left | Left border +// top | Top border +// right | Right border +// bottom | Bottom border +// diagonalDown | Diagonal down border +// diagonalUp | Diagonal up border +// +// The following table shows the border styles used in 'Border.Style' supported +// by excelize index number: // // Index | Name | Weight | Style // -------+---------------+--------+------------- @@ -1124,7 +1138,8 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { // 12 | Dash Dot Dot | 2 | - . . - . . // 13 | SlantDash Dot | 2 | / - . / - . // -// The following shows the borders in the order shown in the Excel dialog: +// The following table shows the border styles used in 'Border.Style' in the +// order shown in the Excel dialog: // // Index | Style | Index | Style // -------+-------------+-------+------------- @@ -1136,7 +1151,8 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { // 3 | - - - - - - | 5 | ----------- // 1 | ----------- | 6 | =========== // -// The following shows the shading styles sorted by excelize index number: +// The following table shows the shading styles used in 'Fill.Shading' supported +// by excelize index number: // // Index | Style | Index | Style // -------+-----------------+-------+----------------- @@ -1144,7 +1160,8 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { // 1 | Vertical | 4 | From corner // 2 | Diagonal Up | 5 | From center // -// The following shows the patterns styles sorted by excelize index number: +// The following table shows the pattern styles used in 'Fill.Pattern' supported +// by excelize index number: // // Index | Style | Index | Style // -------+-----------------+-------+----------------- @@ -1159,7 +1176,8 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { // 8 | darkUp | 18 | gray0625 // 9 | darkGrid | | // -// The following the type of horizontal alignment in cells: +// The following table shows the type of cells' horizontal alignment used +// in 'Alignment.Horizontal': // // Style // ------------------ @@ -1171,7 +1189,8 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { // centerContinuous // distributed // -// The following the type of vertical alignment in cells: +// The following table shows the type of cells' vertical alignment used in +// 'Alignment.Vertical': // // Style // ------------------ @@ -1180,7 +1199,8 @@ func parseFormatStyleSet(style interface{}) (*Style, error) { // justify // distributed // -// The following the type of font underline style: +// The following table shows the type of font underline style used in +// 'Font.Underline': // // Style // ------------------ diff --git a/styles_test.go b/styles_test.go index 9001d5b..0d216b0 100644 --- a/styles_test.go +++ b/styles_test.go @@ -201,6 +201,9 @@ func TestGetConditionalFormats(t *testing.T) { f := NewFile() _, err := f.GetConditionalFormats("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") + // Test get conditional formats with invalid sheet name + _, err = f.GetConditionalFormats("Sheet:1") + assert.EqualError(t, err, ErrSheetNameInvalid.Error()) } func TestUnsetConditionalFormat(t *testing.T) { @@ -211,9 +214,11 @@ func TestUnsetConditionalFormat(t *testing.T) { assert.NoError(t, err) assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format))) assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10")) - // Test unset conditional format on not exists worksheet. + // Test unset conditional format on not exists worksheet assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN does not exist") - // Save spreadsheet by the given path. + // Test unset conditional format with invalid sheet name + assert.EqualError(t, f.UnsetConditionalFormat("Sheet:1", "A1:A10"), ErrSheetNameInvalid.Error()) + // Save spreadsheet by the given path assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnsetConditionalFormat.xlsx"))) } @@ -240,7 +245,7 @@ func TestNewStyle(t *testing.T) { _, err = f.NewStyle(&Style{Font: &Font{Size: MaxFontSize + 1}}) assert.EqualError(t, err, ErrFontSize.Error()) - // Test create numeric custom style. + // Test create numeric custom style numFmt := "####;####" f.Styles.NumFmts = nil styleID, err = f.NewStyle(&Style{ @@ -256,7 +261,7 @@ func TestNewStyle(t *testing.T) { nf := f.Styles.CellXfs.Xf[styleID] assert.Equal(t, 164, *nf.NumFmtID) - // Test create currency custom style. + // Test create currency custom style f.Styles.NumFmts = nil styleID, err = f.NewStyle(&Style{ Lang: "ko-kr", @@ -273,7 +278,7 @@ func TestNewStyle(t *testing.T) { nf = f.Styles.CellXfs.Xf[styleID] assert.Equal(t, 32, *nf.NumFmtID) - // Test set build-in scientific number format. + // Test set build-in scientific number format styleID, err = f.NewStyle(&Style{NumFmt: 11}) assert.NoError(t, err) assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "B1", styleID)) @@ -283,7 +288,7 @@ func TestNewStyle(t *testing.T) { assert.Equal(t, [][]string{{"1.23E+00", "1.23E+00"}}, rows) f = NewFile() - // Test currency number format. + // Test currency number format customNumFmt := "[$$-409]#,##0.00" style1, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt}) assert.NoError(t, err) @@ -309,7 +314,7 @@ func TestNewStyle(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 0, style5) - // Test create style with unsupported charset style sheet. + // Test create style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.NewStyle(&Style{NumFmt: 165}) @@ -318,7 +323,7 @@ func TestNewStyle(t *testing.T) { func TestNewConditionalStyle(t *testing.T) { f := NewFile() - // Test create conditional style with unsupported charset style sheet. + // Test create conditional style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err := f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`) @@ -330,7 +335,7 @@ func TestGetDefaultFont(t *testing.T) { s, err := f.GetDefaultFont() assert.NoError(t, err) assert.Equal(t, s, "Calibri", "Default font should be Calibri") - // Test get default font with unsupported charset style sheet. + // Test get default font with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.GetDefaultFont() @@ -346,7 +351,7 @@ func TestSetDefaultFont(t *testing.T) { assert.NoError(t, err) assert.Equal(t, s, "Arial", "Default font should change to Arial") assert.Equal(t, *styles.CellStyles.CellStyle[0].CustomBuiltIn, true) - // Test set default font with unsupported charset style sheet. + // Test set default font with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) assert.EqualError(t, f.SetDefaultFont("Arial"), "XML syntax error on line 1: invalid UTF-8") @@ -354,7 +359,7 @@ func TestSetDefaultFont(t *testing.T) { func TestStylesReader(t *testing.T) { f := NewFile() - // Test read styles with unsupported charset. + // Test read styles with unsupported charset f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) styles, err := f.stylesReader() @@ -364,7 +369,7 @@ func TestStylesReader(t *testing.T) { func TestThemeReader(t *testing.T) { f := NewFile() - // Test read theme with unsupported charset. + // Test read theme with unsupported charset f.Pkg.Store(defaultXMLPathTheme, MacintoshCyrillicCharset) theme, err := f.themeReader() assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") diff --git a/table.go b/table.go index 06336ff..90cc97f 100644 --- a/table.go +++ b/table.go @@ -304,7 +304,10 @@ func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error { if err != nil { return err } - sheetID := f.GetSheetIndex(sheet) + sheetID, err := f.GetSheetIndex(sheet) + if err != nil { + return err + } filterRange := fmt.Sprintf("'%s'!%s", sheet, ref) d := xlsxDefinedName{ Name: filterDB, diff --git a/table_test.go b/table_test.go index 5ac464b..d26d20c 100644 --- a/table_test.go +++ b/table_test.go @@ -10,36 +10,24 @@ import ( func TestAddTable(t *testing.T) { f, err := prepareTestBook1() - if !assert.NoError(t, err) { - t.FailNow() - } + assert.NoError(t, err) + assert.NoError(t, f.AddTable("Sheet1", "B26", "A21", `{}`)) + assert.NoError(t, f.AddTable("Sheet2", "A2", "B5", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)) + assert.NoError(t, f.AddTable("Sheet2", "F1", "F1", `{"table_style":"TableStyleMedium8"}`)) - err = f.AddTable("Sheet1", "B26", "A21", `{}`) - if !assert.NoError(t, err) { - t.FailNow() - } - - err = f.AddTable("Sheet2", "A2", "B5", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`) - if !assert.NoError(t, err) { - t.FailNow() - } - - err = f.AddTable("Sheet2", "F1", "F1", `{"table_style":"TableStyleMedium8"}`) - if !assert.NoError(t, err) { - t.FailNow() - } - - // Test add table in not exist worksheet. + // Test add table in not exist worksheet assert.EqualError(t, f.AddTable("SheetN", "B26", "A21", `{}`), "sheet SheetN does not exist") - // Test add table with illegal options. + // Test add table with illegal options assert.EqualError(t, f.AddTable("Sheet1", "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, f.AddTable("Sheet1", "A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.AddTable("Sheet1", "A1", "B", `{}`), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error()) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx"))) - // Test addTable with illegal cell reference. + // Test add table with invalid sheet name + assert.EqualError(t, f.AddTable("Sheet:1", "B26", "A21", `{}`), ErrSheetNameInvalid.Error()) + // Test addTable with illegal cell reference f = NewFile() assert.EqualError(t, f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil), "invalid cell reference [0, 0]") assert.EqualError(t, f.addTable("sheet1", "", 1, 1, 0, 0, 0, nil), "invalid cell reference [0, 0]") @@ -53,12 +41,8 @@ func TestSetTableHeader(t *testing.T) { func TestAutoFilter(t *testing.T) { outFile := filepath.Join("test", "TestAutoFilter%d.xlsx") - f, err := prepareTestBook1() - if !assert.NoError(t, err) { - t.FailNow() - } - + assert.NoError(t, err) formats := []string{ ``, `{"column":"B","expression":"x != blanks"}`, @@ -69,7 +53,6 @@ func TestAutoFilter(t *testing.T) { `{"column":"B","expression":"x == 1 or x == 2"}`, `{"column":"B","expression":"x == 1 or x == 2*"}`, } - for i, format := range formats { t.Run(fmt.Sprintf("Expression%d", i+1), func(t *testing.T) { err = f.AutoFilter("Sheet1", "D4", "B1", format) @@ -78,10 +61,12 @@ func TestAutoFilter(t *testing.T) { }) } - // Test add auto filter with illegal cell reference. + // Test add auto filter with invalid sheet name + assert.EqualError(t, f.AutoFilter("Sheet:1", "A1", "B1", ""), ErrSheetNameInvalid.Error()) + // 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. + // 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") @@ -132,10 +117,10 @@ func TestAutoFilterError(t *testing.T) { func TestParseFilterTokens(t *testing.T) { f := NewFile() - // Test with unknown operator. + // Test with unknown operator _, _, err := f.parseFilterTokens("", []string{"", "!"}) assert.EqualError(t, err, "unknown operator: !") - // Test invalid operator in context. + // Test invalid operator in context _, _, err = f.parseFilterTokens("", []string{"", "<", "x != blanks"}) assert.EqualError(t, err, "the operator '<' in expression '' is not valid in relation to Blanks/NonBlanks'") } diff --git a/workbook.go b/workbook.go index eb57cb5..4974f75 100644 --- a/workbook.go +++ b/workbook.go @@ -64,7 +64,7 @@ func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) { func (f *File) setWorkbook(name string, sheetID, rid int) { wb, _ := f.workbookReader() wb.Sheets.Sheet = append(wb.Sheets.Sheet, xlsxSheet{ - Name: trimSheetName(name), + Name: name, SheetID: sheetID, ID: "rId" + strconv.Itoa(rid), }) diff --git a/xmlComments.go b/xmlComments.go index 7b67e67..13b727b 100644 --- a/xmlComments.go +++ b/xmlComments.go @@ -77,6 +77,6 @@ type Comment struct { Author string `json:"author"` AuthorID int `json:"author_id"` Cell string `json:"cell"` - Text string `json:"string"` + Text string `json:"text"` Runs []RichTextRun `json:"runs"` } diff --git a/xmlDrawing.go b/xmlDrawing.go index 56ddc0e..9af6905 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -19,58 +19,30 @@ import ( // Source relationship and namespace list, associated prefixes and schema in which it was // introduced. var ( - SourceRelationship = xml.Attr{Name: xml.Name{Local: "r", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"} - SourceRelationshipCompatibility = xml.Attr{Name: xml.Name{Local: "mc", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/markup-compatibility/2006"} - SourceRelationshipChart20070802 = xml.Attr{Name: xml.Name{Local: "c14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"} - SourceRelationshipChart2014 = xml.Attr{Name: xml.Name{Local: "c16", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2014/chart"} - SourceRelationshipChart201506 = xml.Attr{Name: xml.Name{Local: "c16r2", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2015/06/chart"} - NameSpaceSpreadSheet = xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"} - NameSpaceSpreadSheetX14 = xml.Attr{Name: xml.Name{Local: "x14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"} + NameSpaceDocumentPropertiesVariantTypes = xml.Attr{Name: xml.Name{Local: "vt", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"} + NameSpaceDrawing2016SVG = xml.Attr{Name: xml.Name{Local: "asvg", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2016/SVG/main"} NameSpaceDrawingML = xml.Attr{Name: xml.Name{Local: "a", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/main"} NameSpaceDrawingMLChart = xml.Attr{Name: xml.Name{Local: "c", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/chart"} NameSpaceDrawingMLSpreadSheet = xml.Attr{Name: xml.Name{Local: "xdr", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"} - NameSpaceDrawing2016SVG = xml.Attr{Name: xml.Name{Local: "asvg", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2016/SVG/main"} - NameSpaceSpreadSheetX15 = xml.Attr{Name: xml.Name{Local: "x15", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"} - NameSpaceSpreadSheetExcel2006Main = xml.Attr{Name: xml.Name{Local: "xne", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/excel/2006/main"} NameSpaceMacExcel2008Main = xml.Attr{Name: xml.Name{Local: "mx", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/mac/excel/2008/main"} - NameSpaceDocumentPropertiesVariantTypes = xml.Attr{Name: xml.Name{Local: "vt", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"} + NameSpaceSpreadSheet = xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"} + NameSpaceSpreadSheetExcel2006Main = xml.Attr{Name: xml.Name{Local: "xne", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/excel/2006/main"} + NameSpaceSpreadSheetX14 = xml.Attr{Name: xml.Name{Local: "x14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"} + NameSpaceSpreadSheetX15 = xml.Attr{Name: xml.Name{Local: "x15", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"} + SourceRelationship = xml.Attr{Name: xml.Name{Local: "r", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"} + SourceRelationshipChart20070802 = xml.Attr{Name: xml.Name{Local: "c14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"} + SourceRelationshipChart2014 = xml.Attr{Name: xml.Name{Local: "c16", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2014/chart"} + SourceRelationshipChart201506 = xml.Attr{Name: xml.Name{Local: "c16r2", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2015/06/chart"} + SourceRelationshipCompatibility = xml.Attr{Name: xml.Name{Local: "mc", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/markup-compatibility/2006"} ) // Source relationship and namespace. const ( - SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" - SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" - SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" - SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" - SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" - SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" - SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" - SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" - SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" - SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet" - SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet" - SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" - SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" - SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" - SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject" - NameSpaceXML = "http://www.w3.org/XML/1998/namespace" - NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance" - StrictSourceRelationship = "http://purl.oclc.org/ooxml/officeDocument/relationships" - StrictSourceRelationshipOfficeDocument = "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" - StrictSourceRelationshipChart = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart" - StrictSourceRelationshipComments = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments" - StrictSourceRelationshipImage = "http://purl.oclc.org/ooxml/officeDocument/relationships/image" - StrictNameSpaceSpreadSheet = "http://purl.oclc.org/ooxml/spreadsheetml/main" - NameSpaceDublinCore = "http://purl.org/dc/elements/1.1/" - NameSpaceDublinCoreTerms = "http://purl.org/dc/terms/" - NameSpaceDublinCoreMetadataInitiative = "http://purl.org/dc/dcmitype/" + ContentTypeAddinMacro = "application/vnd.ms-excel.addin.macroEnabled.main+xml" ContentTypeDrawing = "application/vnd.openxmlformats-officedocument.drawing+xml" ContentTypeDrawingML = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" - ContentTypeSheetML = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" - ContentTypeTemplate = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" - ContentTypeAddinMacro = "application/vnd.ms-excel.addin.macroEnabled.main+xml" ContentTypeMacro = "application/vnd.ms-excel.sheet.macroEnabled.main+xml" - ContentTypeTemplateMacro = "application/vnd.ms-excel.template.macroEnabled.main+xml" + ContentTypeSheetML = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" ContentTypeSpreadSheetMLChartsheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml" ContentTypeSpreadSheetMLComments = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml" ContentTypeSpreadSheetMLPivotCacheDefinition = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml" @@ -78,44 +50,73 @@ const ( ContentTypeSpreadSheetMLSharedStrings = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" ContentTypeSpreadSheetMLTable = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml" ContentTypeSpreadSheetMLWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" + ContentTypeTemplate = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" + ContentTypeTemplateMacro = "application/vnd.ms-excel.template.macroEnabled.main+xml" ContentTypeVBA = "application/vnd.ms-office.vbaProject" ContentTypeVML = "application/vnd.openxmlformats-officedocument.vmlDrawing" + NameSpaceDublinCore = "http://purl.org/dc/elements/1.1/" + NameSpaceDublinCoreMetadataInitiative = "http://purl.org/dc/dcmitype/" + NameSpaceDublinCoreTerms = "http://purl.org/dc/terms/" + NameSpaceXML = "http://www.w3.org/XML/1998/namespace" + NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance" + SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" + SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet" + SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" + SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet" + SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" + SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" + SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" + SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" + SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" + SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" + SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" + SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" + SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" + SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject" + SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" + StrictNameSpaceSpreadSheet = "http://purl.oclc.org/ooxml/spreadsheetml/main" + StrictSourceRelationship = "http://purl.oclc.org/ooxml/officeDocument/relationships" + StrictSourceRelationshipChart = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart" + StrictSourceRelationshipComments = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments" + StrictSourceRelationshipImage = "http://purl.oclc.org/ooxml/officeDocument/relationships/image" + StrictSourceRelationshipOfficeDocument = "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" // ExtURIConditionalFormattings is the extLst child element // ([ISO/IEC29500-1:2016] section 18.2.10) of the worksheet element // ([ISO/IEC29500-1:2016] section 18.3.1.99) is extended by the addition of // new child ext elements ([ISO/IEC29500-1:2016] section 18.2.7) ExtURIConditionalFormattings = "{78C0D931-6437-407D-A8EE-F0AAD7539E65}" ExtURIDataValidations = "{CCE6A557-97BC-4B89-ADB6-D9C93CAAB3DF}" - ExtURISparklineGroups = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}" - ExtURISlicerListX14 = "{A8765BA9-456A-4DAB-B4F3-ACF838C121DE}" - ExtURISlicerCachesListX14 = "{BBE1A952-AA13-448e-AADC-164F8A28A991}" - ExtURISlicerListX15 = "{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}" - ExtURIProtectedRanges = "{FC87AEE6-9EDD-4A0A-B7FB-166176984837}" - ExtURIIgnoredErrors = "{01252117-D84E-4E92-8308-4BE1C098FCBB}" - ExtURIWebExtensions = "{F7C9EE02-42E1-4005-9D12-6889AFFD525C}" - ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}" ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}" + ExtURIIgnoredErrors = "{01252117-D84E-4E92-8308-4BE1C098FCBB}" ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}" + ExtURIProtectedRanges = "{FC87AEE6-9EDD-4A0A-B7FB-166176984837}" + ExtURISlicerCachesListX14 = "{BBE1A952-AA13-448e-AADC-164F8A28A991}" + ExtURISlicerListX14 = "{A8765BA9-456A-4DAB-B4F3-ACF838C121DE}" + ExtURISlicerListX15 = "{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}" + ExtURISparklineGroups = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}" ExtURISVG = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" + ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}" + ExtURIWebExtensions = "{F7C9EE02-42E1-4005-9D12-6889AFFD525C}" ) // Excel specifications and limits const ( - UnzipSizeLimit = 1000 << 24 - StreamChunkSize = 1 << 24 + MaxCellStyles = 64000 + MaxColumns = 16384 + MaxColumnWidth = 255 + MaxFieldLength = 255 + MaxFilePathLength = 207 MaxFontFamilyLength = 31 MaxFontSize = 409 - MaxFilePathLength = 207 - MaxFieldLength = 255 - MaxColumnWidth = 255 MaxRowHeight = 409 - MaxCellStyles = 64000 - MinFontSize = 1 - TotalRows = 1048576 + MaxSheetNameLength = 31 MinColumns = 1 - MaxColumns = 16384 - TotalSheetHyperlinks = 65529 + MinFontSize = 1 + StreamChunkSize = 1 << 24 TotalCellChars = 32767 + TotalRows = 1048576 + TotalSheetHyperlinks = 65529 + UnzipSizeLimit = 1000 << 24 // pivotTableVersion should be greater than 3. One or more of the // PivotTables chosen are created in a version of Excel earlier than // Excel 2007 or in compatibility mode. Slicer can only be used with