diff --git a/README.md b/README.md
index 89d2d001e3..f73266fff0 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,11 @@ import (
)
func main() {
- f := excelize.NewFile()
+ f, err := excelize.NewFile()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
// Create a new sheet.
index := f.NewSheet("Sheet2")
// Set value of a cell.
@@ -126,7 +130,11 @@ func main() {
"B1": "Apple", "C1": "Orange", "D1": "Pear"}
values := map[string]int{
"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
- f := excelize.NewFile()
+ f, err := excelize.NewFile()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
for k, v := range categories {
f.SetCellValue("Sheet1", k, v)
}
diff --git a/README_zh.md b/README_zh.md
index d67b63cb03..458363bd15 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -43,7 +43,11 @@ import (
)
func main() {
- f := excelize.NewFile()
+ f, err := excelize.NewFile()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
// 创建一个工作表
index := f.NewSheet("Sheet2")
// 设置单元格的值
@@ -126,7 +130,11 @@ func main() {
"B1": "Apple", "C1": "Orange", "D1": "Pear"}
values := map[string]int{
"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
- f := excelize.NewFile()
+ f, err := excelize.NewFile()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
for k, v := range categories {
f.SetCellValue("Sheet1", k, v)
}
diff --git a/adjust_test.go b/adjust_test.go
index ab6bedcc06..445badd9dc 100644
--- a/adjust_test.go
+++ b/adjust_test.go
@@ -7,7 +7,8 @@ import (
)
func TestAdjustMergeCells(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// testing adjustAutoFilter with illegal cell coordinates.
assert.EqualError(t, f.adjustMergeCells(&xlsxWorksheet{
MergeCells: &xlsxMergeCells{
@@ -264,7 +265,8 @@ func TestAdjustMergeCells(t *testing.T) {
assert.Equal(t, 0, len(c.ws.MergeCells.Cells), c.label)
}
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
p1, p2 := f.adjustMergeCellsHelper(2, 1, 0, 0)
assert.Equal(t, 1, p1)
assert.Equal(t, 2, p2)
@@ -272,7 +274,8 @@ func TestAdjustMergeCells(t *testing.T) {
}
func TestAdjustAutoFilter(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.adjustAutoFilter(&xlsxWorksheet{
SheetData: xlsxSheetData{
Row: []xlsxRow{{Hidden: true, R: 2}},
@@ -295,7 +298,8 @@ func TestAdjustAutoFilter(t *testing.T) {
}
func TestAdjustHelper(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.NewSheet("Sheet2")
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}},
@@ -311,7 +315,8 @@ func TestAdjustHelper(t *testing.T) {
}
func TestAdjustCalcChain(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.CalcChain = &xlsxCalcChain{
C: []xlsxCalcChainC{
{R: "B2", I: 2}, {R: "B2", I: 1},
diff --git a/calc_test.go b/calc_test.go
index 8ad3c775f7..aec833f1c4 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -11,7 +11,10 @@ import (
)
func prepareCalcData(cellData [][]interface{}) *File {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ return nil
+ }
for r, row := range cellData {
for c, value := range row {
cell, _ := CoordinatesToCellName(c+1, r+1)
@@ -4673,7 +4676,9 @@ func TestCalcDatabase(t *testing.T) {
}
func TestCalcFORMULATEXT(t *testing.T) {
- f, formulaText := NewFile(), "=SUM(B1:C1)"
+ f, err := NewFile()
+ assert.NoError(t, err)
+ formulaText := "=SUM(B1:C1)"
assert.NoError(t, f.SetCellFormula("Sheet1", "A1", formulaText))
for _, formula := range []string{"=FORMULATEXT(A1)", "=FORMULATEXT(A:A)", "=FORMULATEXT(A1:B1)"} {
assert.NoError(t, f.SetCellFormula("Sheet1", "D1", formula), formula)
@@ -4918,7 +4923,8 @@ func TestCalcIRR(t *testing.T) {
}
func TestCalcMAXMINIFS(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, row := range map[string][]interface{}{
"A1": {1, -math.MaxFloat64 - 1},
"A2": {2, -math.MaxFloat64 - 2},
@@ -5202,7 +5208,8 @@ func TestCalcXNPV(t *testing.T) {
}
func TestCalcMATCH(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, row := range map[string][]interface{}{
"A1": {"cccc", 7, 4, 16},
"A2": {"dddd", 2, 6, 11},
@@ -5244,7 +5251,8 @@ func TestCalcMATCH(t *testing.T) {
}
func TestCalcISFORMULA(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", "=ISFORMULA(A1)"))
for _, formula := range []string{"=NA()", "=SUM(A1:A3)"} {
assert.NoError(t, f.SetCellFormula("Sheet1", "A1", formula))
@@ -5384,7 +5392,8 @@ func TestCalcSLOP(t *testing.T) {
}
func TestCalcSHEET(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.NewSheet("Sheet2")
formulaList := map[string]string{
"=SHEET(\"Sheet2\")": "2",
@@ -5400,7 +5409,8 @@ func TestCalcSHEET(t *testing.T) {
}
func TestCalcSHEETS(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.NewSheet("Sheet2")
formulaList := map[string]string{
"=SHEETS(Sheet1!A1:B1)": "1",
@@ -5629,7 +5639,8 @@ func TestCalcNETWORKDAYSandWORKDAY(t *testing.T) {
}
func TestCalcZTEST(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]int{4, 5, 2, 5, 8, 9, 3, 2, 3, 8, 9, 5}))
formulaList := map[string]string{
"=Z.TEST(A1:L1,5)": "0.371103278558538",
@@ -5700,7 +5711,8 @@ func TestCalcBetainvProbIterator(t *testing.T) {
}
func TestNestedFunctionsWithOperators(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
formulaList := map[string]string{
`=LEN("KEEP")`: "4",
`=LEN("REMOVEKEEP") - LEN("REMOVE")`: "4",
diff --git a/calcchain.go b/calcchain.go
index a1f9c0c56a..80f6423c9a 100644
--- a/calcchain.go
+++ b/calcchain.go
@@ -14,33 +14,42 @@ package excelize
import (
"bytes"
"encoding/xml"
+ "fmt"
"io"
- "log"
)
-// calcChainReader provides a function to get the pointer to the structure
+// NewCalcChainReader provides a function to get the pointer to the structure
// after deserialization of xl/calcChain.xml.
-func (f *File) calcChainReader() *xlsxCalcChain {
+func (f *File) NewCalcChainReader() (*xlsxCalcChain, error) {
var err error
if f.CalcChain == nil {
f.CalcChain = new(xlsxCalcChain)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathCalcChain)))).
Decode(f.CalcChain); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return f.CalcChain, fmt.Errorf("xml decode error: %w", err)
}
}
+ return f.CalcChain, nil
+}
+
+// calcChainReader provides a function to get the CalcChain.
+func (f *File) calcChainReader() *xlsxCalcChain {
return f.CalcChain
}
// calcChainWriter provides a function to save xl/calcChain.xml after
// serialize structure.
-func (f *File) calcChainWriter() {
+func (f *File) calcChainWriter() error {
if f.CalcChain != nil && f.CalcChain.C != nil {
- output, _ := xml.Marshal(f.CalcChain)
+ output, err := xml.Marshal(f.CalcChain)
+ if err != nil {
+ return err
+ }
f.saveFileList(defaultXMLPathCalcChain, output)
}
+ return nil
}
// deleteCalcChain provides a function to remove cell reference on the
@@ -52,10 +61,13 @@ func (f *File) deleteCalcChain(index int, axis string) {
return !((c.I == index && c.R == axis) || (c.I == index && axis == ""))
})
}
- if len(calc.C) == 0 {
+ if calc == nil || len(calc.C) == 0 {
f.CalcChain = nil
f.Pkg.Delete(defaultXMLPathCalcChain)
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
for k, v := range content.Overrides {
diff --git a/calcchain_test.go b/calcchain_test.go
index c36655bc5e..eceeb80cdb 100644
--- a/calcchain_test.go
+++ b/calcchain_test.go
@@ -1,16 +1,21 @@
package excelize
-import "testing"
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
func TestCalcChainReader(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.CalcChain = nil
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
- f.calcChainReader()
}
func TestDeleteCalcChain(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.CalcChain = &xlsxCalcChain{C: []xlsxCalcChainC{}}
f.ContentTypes.Overrides = append(f.ContentTypes.Overrides, xlsxOverride{
PartName: "/xl/calcChain.xml",
diff --git a/cell.go b/cell.go
index 286085b3ab..5e8b3edbc3 100644
--- a/cell.go
+++ b/cell.go
@@ -62,8 +62,12 @@ var cellTypes = map[string]CellType{
// returned, along with the raw value of the cell. All cells' values will be
// the same in a merged range.
func (f *File) GetCellValue(sheet, axis string, opts ...Options) (string, error) {
+ sst, err := f.sharedStringsReader()
+ if err != nil {
+ return "", err
+ }
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
- val, err := c.getValueFrom(f, f.sharedStringsReader(), parseOptions(opts...).RawCellValue)
+ val, err := c.getValueFrom(f, sst, parseOptions(opts...).RawCellValue)
return val, true, err
})
}
@@ -404,7 +408,10 @@ func (f *File) setSharedString(val string) (int, error) {
if err := f.sharedStringsLoader(); err != nil {
return 0, err
}
- sst := f.sharedStringsReader()
+ sst, err := f.sharedStringsReader()
+ if err != nil {
+ return 0, err
+ }
f.Lock()
defer f.Unlock()
if i, ok := f.sharedStringsMap[val]; ok {
@@ -544,7 +551,11 @@ type FormulaOpts struct {
// )
//
// func main() {
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
// if err := f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row); err != nil {
// fmt.Println(err)
@@ -612,7 +623,7 @@ func (f *File) SetCellFormula(sheet, axis, formula string, opts ...FormulaOpts)
func (ws *xlsxWorksheet) setSharedFormula(ref string) error {
coordinates, err := areaRefToCoordinates(ref)
if err != nil {
- return err
+ return fmt.Errorf("setSharedFormula: %w", err)
}
_ = sortCoordinates(coordinates)
cnt := ws.countSharedFormula()
@@ -627,7 +638,7 @@ func (ws *xlsxWorksheet) setSharedFormula(ref string) error {
cell.F.Si = &cnt
}
}
- return err
+ return nil
}
// countSharedFormula count shared formula in the given worksheet.
@@ -747,7 +758,10 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype
case "External":
sheetPath := f.sheetMap[trimSheetName(sheet)]
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
- rID := f.setRels(linkData.RID, sheetRels, SourceRelationshipHyperLink, link, linkType)
+ rID, err := f.setRels(linkData.RID, sheetRels, SourceRelationshipHyperLink, link, linkType)
+ if err != nil {
+ return err
+ }
linkData = xlsxHyperlink{
Ref: axis,
}
@@ -826,7 +840,10 @@ func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err erro
if err != nil || cellData.T != "s" {
return
}
- sst := f.sharedStringsReader()
+ sst, err := f.sharedStringsReader()
+ if err != nil {
+ return
+ }
if len(sst.SI) <= siIdx || siIdx < 0 {
return
}
@@ -878,7 +895,11 @@ func newRpr(fnt *Font) *xlsxRPr {
// )
//
// func main() {
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// if err := f.SetRowHeight("Sheet1", 1, 35); err != nil {
// fmt.Println(err)
// return
@@ -998,7 +1019,10 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
}
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
si := xlsxSI{}
- sst := f.sharedStringsReader()
+ sst, err := f.sharedStringsReader()
+ if err != nil {
+ return err
+ }
var textRuns []xlsxR
totalCellChars := 0
for _, textRun := range runs {
@@ -1133,16 +1157,17 @@ func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c
// formattedValue provides a function to returns a value after formatted. If
// it is possible to apply a format to the cell value, it will do so, if not
// then an error will be returned, along with the raw value of the cell.
-func (f *File) formattedValue(s int, v string, raw bool) string {
+func (f *File) formattedValue(s int, v string, raw bool) (formatted string) {
+ formatted = v
if raw {
- return v
+ return
}
if s == 0 {
- return v
+ return
}
styleSheet := f.stylesReader()
if s >= len(styleSheet.CellXfs.Xf) {
- return v
+ return
}
var numFmtID int
if styleSheet.CellXfs.Xf[s].NumFmtID != nil {
@@ -1153,17 +1178,19 @@ func (f *File) formattedValue(s int, v string, raw bool) string {
date1904 = wb.WorkbookPr.Date1904
}
if ok := builtInNumFmtFunc[numFmtID]; ok != nil {
- return ok(v, builtInNumFmt[numFmtID], date1904)
+ formatted = ok(v, builtInNumFmt[numFmtID], date1904)
+ return
}
if styleSheet == nil || styleSheet.NumFmts == nil {
- return v
+ return
}
for _, xlsxFmt := range styleSheet.NumFmts.NumFmt {
if xlsxFmt.NumFmtID == numFmtID {
- return format(v, xlsxFmt.FormatCode, date1904)
+ formatted = format(v, xlsxFmt.FormatCode, date1904)
+ return
}
}
- return v
+ return
}
// prepareCellStyle provides a function to prepare style index of cell in
diff --git a/cell_test.go b/cell_test.go
index fb1e8ef585..8deac006b0 100644
--- a/cell_test.go
+++ b/cell_test.go
@@ -79,7 +79,8 @@ func TestConcurrency(t *testing.T) {
}
func TestCheckCellInArea(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
expectedTrueCellInAreaList := [][2]string{
{"c2", "A1:AAZ32"},
{"B9", "A1:B9"},
@@ -122,7 +123,8 @@ func TestCheckCellInArea(t *testing.T) {
func TestSetCellFloat(t *testing.T) {
sheet := "Sheet1"
t.Run("with no decimal", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellFloat(sheet, "A1", 123.0, -1, 64))
assert.NoError(t, f.SetCellFloat(sheet, "A2", 123.0, 1, 64))
val, err := f.GetCellValue(sheet, "A1")
@@ -134,7 +136,8 @@ func TestSetCellFloat(t *testing.T) {
})
t.Run("with a decimal and precision limit", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellFloat(sheet, "A1", 123.42, 1, 64))
val, err := f.GetCellValue(sheet, "A1")
assert.NoError(t, err)
@@ -142,18 +145,21 @@ func TestSetCellFloat(t *testing.T) {
})
t.Run("with a decimal and no limit", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellFloat(sheet, "A1", 123.42, -1, 64))
val, err := f.GetCellValue(sheet, "A1")
assert.NoError(t, err)
assert.Equal(t, "123.42", val, "A1 should be 123.42")
})
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.SetCellFloat(sheet, "A", 123.42, -1, 64), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
func TestSetCellValue(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
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
@@ -174,8 +180,9 @@ func TestSetCellValue(t *testing.T) {
}
func TestSetCellValues(t *testing.T) {
- f := NewFile()
- err := f.SetCellValue("Sheet1", "A1", time.Date(2010, time.December, 31, 0, 0, 0, 0, time.UTC))
+ f, err := NewFile()
+ assert.NoError(t, err)
+ err = f.SetCellValue("Sheet1", "A1", time.Date(2010, time.December, 31, 0, 0, 0, 0, time.UTC))
assert.NoError(t, err)
v, err := f.GetCellValue("Sheet1", "A1")
@@ -192,7 +199,8 @@ func TestSetCellValues(t *testing.T) {
}
func TestSetCellBool(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.SetCellBool("Sheet1", "A", true), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
@@ -216,7 +224,8 @@ func TestSetCellTime(t *testing.T) {
func TestGetCellValue(t *testing.T) {
// Test get cell value without r attribute of the row.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheetData := `%s`
f.Sheet.Delete("xl/worksheets/sheet1.xml")
@@ -349,7 +358,8 @@ func TestGetCellValue(t *testing.T) {
}
func TestGetCellType(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
cellType, err := f.GetCellType("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, CellTypeUnset, cellType)
@@ -362,17 +372,21 @@ func TestGetCellType(t *testing.T) {
}
func TestGetValueFrom(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
c := xlsxC{T: "s"}
- value, err := c.getValueFrom(f, f.sharedStringsReader(), false)
+ sst, err := f.sharedStringsReader()
+ assert.NoError(t, err)
+ value, err := c.getValueFrom(f, sst, false)
assert.NoError(t, err)
assert.Equal(t, "", value)
}
func TestGetCellFormula(t *testing.T) {
// Test get cell formula on not exist worksheet.
- f := NewFile()
- _, err := f.GetCellFormula("SheetN", "A1")
+ f, err := NewFile()
+ assert.NoError(t, err)
+ _, err = f.GetCellFormula("SheetN", "A1")
assert.EqualError(t, err, "sheet SheetN is not exist")
// Test get cell formula on no formula cell.
@@ -381,7 +395,8 @@ func TestGetCellFormula(t *testing.T) {
assert.NoError(t, err)
// Test get cell shared formula
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
sheetData := `12*A1
2%s
3
4
5
6
7
`
for sharedFormula, expected := range map[string]string{
@@ -404,7 +419,10 @@ func TestGetCellFormula(t *testing.T) {
}
func ExampleFile_SetCellFloat() {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
x := 3.14159265
if err := f.SetCellFloat("Sheet1", "A1", x, 2, 64); err != nil {
fmt.Println(err)
@@ -417,7 +435,8 @@ func ExampleFile_SetCellFloat() {
func BenchmarkSetCellValue(b *testing.B) {
values := []string{"First", "Second", "Third", "Fourth", "Fifth", "Sixth"}
cols := []string{"A", "B", "C", "D", "E", "F"}
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(b, err)
b.ResetTimer()
for i := 1; i <= b.N; i++ {
for j := 0; j < len(values); j++ {
@@ -468,7 +487,8 @@ func TestSetCellFormula(t *testing.T) {
assert.NoError(t, f.Close())
// Test set shared formula for the cells.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
for r := 1; r <= 5; r++ {
assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", r), &[]interface{}{r, r + 1}))
}
@@ -482,11 +502,12 @@ func TestSetCellFormula(t *testing.T) {
ref = "D1:D5"
assert.NoError(t, f.SetCellFormula("Sheet1", "D1", "=A1+C1", FormulaOpts{Ref: &ref, Type: &formulaType}))
ref = ""
- assert.EqualError(t, f.SetCellFormula("Sheet1", "D1", "=A1+C1", FormulaOpts{Ref: &ref, Type: &formulaType}), ErrParameterInvalid.Error())
+ assert.ErrorIs(t, f.SetCellFormula("Sheet1", "D1", "=A1+C1", FormulaOpts{Ref: &ref, Type: &formulaType}), ErrRangeLength)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula5.xlsx")))
// Test set table formula for the cells.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row))
}
@@ -497,7 +518,8 @@ func TestSetCellFormula(t *testing.T) {
}
func TestGetCellRichText(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
runsSource := []RichTextRun{
{
@@ -562,7 +584,8 @@ func TestGetCellRichText(t *testing.T) {
}
func TestSetCellRichText(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetRowHeight("Sheet1", 1, 35))
assert.NoError(t, f.SetColWidth("Sheet1", "A", "A", 44))
richTextRun := []RichTextRun{
@@ -661,7 +684,8 @@ func TestSetCellRichText(t *testing.T) {
}
func TestFormattedValue2(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
v := f.formattedValue(0, "43528", false)
assert.Equal(t, "43528", v)
@@ -671,7 +695,7 @@ func TestFormattedValue2(t *testing.T) {
v = f.formattedValue(1, "43528", false)
assert.Equal(t, "43528", v)
customNumFmt := "[$-409]MM/DD/YYYY"
- _, err := f.NewStyle(&Style{
+ _, err = f.NewStyle(&Style{
CustomNumFmt: &customNumFmt,
})
assert.NoError(t, err)
diff --git a/chart.go b/chart.go
index 7dcbe19bec..12ec96303d 100644
--- a/chart.go
+++ b/chart.go
@@ -514,7 +514,11 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
// "B1": "Apple", "C1": "Orange", "D1": "Pear"}
// values := map[string]int{
// "B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// for k, v := range categories {
// f.SetCellValue("Sheet1", k, v)
// }
@@ -787,7 +791,11 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
// "B1": "Apple", "C1": "Orange", "D1": "Pear"}
// values := map[string]int{
// "B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// for k, v := range categories {
// f.SetCellValue("Sheet1", k, v)
// }
@@ -883,6 +891,10 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
// }
//
func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
// Read sheet data.
ws, err := f.workSheetReader(sheet)
if err != nil {
@@ -896,14 +908,23 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
drawingID := f.countDrawings() + 1
chartID := f.countCharts() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
- drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
+ drawingID, drawingXML, err = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
+ if err != nil {
+ return err
+ }
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
- drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
+ drawingRID, err := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
+ if err != nil {
+ return err
+ }
err = f.addDrawingChart(sheet, drawingXML, cell, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format)
if err != nil {
return err
}
- f.addChart(formatSet, comboCharts)
+ err = f.addChart(formatSet, comboCharts)
+ if err != nil {
+ return err
+ }
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(drawingID, "drawings")
f.addSheetNameSpace(sheet, SourceRelationship)
@@ -915,6 +936,10 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
// and properties set. In Excel a chartsheet is a worksheet that only contains
// a chart.
func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
// Check if the worksheet already exists
if f.GetSheetIndex(sheet) != -1 {
return ErrExistsWorksheet
@@ -943,19 +968,41 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
drawingID := f.countDrawings() + 1
chartID := f.countCharts() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
- f.prepareChartSheetDrawing(&cs, drawingID, sheet)
+ err = f.prepareChartSheetDrawing(&cs, drawingID, sheet)
+ if err != nil {
+ return err
+ }
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
- drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
- f.addSheetDrawingChart(drawingXML, drawingRID, &formatSet.Format)
- f.addChart(formatSet, comboCharts)
+ drawingRID, err := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
+ if err != nil {
+ return err
+ }
+ err = f.addSheetDrawingChart(drawingXML, drawingRID, &formatSet.Format)
+ if err != nil {
+ return err
+ }
+ err = f.addChart(formatSet, comboCharts)
+ if err != nil {
+ return err
+ }
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(sheetID, "chartsheet")
f.addContentTypePart(drawingID, "drawings")
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return err
+ }
// Update workbook.xml.rels
- rID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipChartsheet, fmt.Sprintf("/xl/chartsheets/sheet%d.xml", sheetID), "")
+ rID, err := f.addRels(wrp, SourceRelationshipChartsheet, fmt.Sprintf("/xl/chartsheets/sheet%d.xml", sheetID), "")
+ if err != nil {
+ return err
+ }
// Update workbook.xml
f.setWorkbook(sheet, sheetID, rID)
- chartsheet, _ := xml.Marshal(cs)
+ chartsheet, err := xml.Marshal(cs)
+ if err != nil {
+ return err
+ }
f.addSheetNameSpace(sheet, NameSpaceSpreadSheet)
f.saveFileList(path, replaceRelationshipsBytes(f.replaceNameSpaceBytes(path, chartsheet)))
return err
diff --git a/chart_test.go b/chart_test.go
index 9f2287e802..3c3975de02 100644
--- a/chart_test.go
+++ b/chart_test.go
@@ -11,7 +11,8 @@ import (
)
func TestChartSize(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
categories := map[string]string{
@@ -93,7 +94,8 @@ func TestChartSize(t *testing.T) {
}
func TestAddDrawingChart(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.addDrawingChart("SheetN", "", "", 0, 0, 0, nil), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error())
}
@@ -212,7 +214,8 @@ func TestAddChart(t *testing.T) {
func TestAddChartSheet(t *testing.T) {
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for k, v := range categories {
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
}
@@ -254,13 +257,16 @@ func TestDeleteChart(t *testing.T) {
// Test delete chart with invalid coordinates.
assert.EqualError(t, f.DeleteChart("Sheet1", ""), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error())
// Test delete chart on no chart worksheet.
- assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1"))
+ xlsxFile, err := NewFile()
+ assert.NoError(t, err)
+ assert.NoError(t, xlsxFile.DeleteChart("Sheet1", "A1"))
assert.NoError(t, f.Close())
}
func TestChartWithLogarithmicBase(t *testing.T) {
// Create test XLSX file with data
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
categories := map[string]float64{
"A1": 1,
diff --git a/col.go b/col.go
index ee1a4074d5..59592bbf2f 100644
--- a/col.go
+++ b/col.go
@@ -90,7 +90,10 @@ func (cols *Cols) Rows(opts ...Options) ([]string, error) {
return rows, err
}
cols.rawCellValue = parseOptions(opts...).RawCellValue
- d := cols.f.sharedStringsReader()
+ d, err := cols.f.sharedStringsReader()
+ if err != nil {
+ return rows, err
+ }
decoder := cols.f.xmlNewDecoder(bytes.NewReader(cols.sheetXML))
for {
token, _ := decoder.Token()
@@ -204,7 +207,10 @@ func (f *File) Cols(sheet string) (*Cols, error) {
worksheet := ws.(*xlsxWorksheet)
worksheet.Lock()
defer worksheet.Unlock()
- output, _ := xml.Marshal(worksheet)
+ output, err := xml.Marshal(worksheet)
+ if err != nil {
+ return nil, err
+ }
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
var colIterator columnXMLIterator
@@ -449,8 +455,8 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error {
// SetColWidth provides a function to set the width of a single column or
// multiple columns. For example:
//
-// f := excelize.NewFile()
-// err := f.SetColWidth("Sheet1", "A", "H", 20)
+// f, err := excelize.NewFile()
+// err = f.SetColWidth("Sheet1", "A", "H", 20)
//
func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error {
min, err := ColumnNameToNumber(startCol)
diff --git a/col_test.go b/col_test.go
index df74523a27..be31c849f0 100644
--- a/col_test.go
+++ b/col_test.go
@@ -41,7 +41,8 @@ func TestCols(t *testing.T) {
}
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
cells := []string{"C2", "C3", "C4"}
for _, cell := range cells {
assert.NoError(t, f.SetCellValue("Sheet1", cell, 1))
@@ -73,7 +74,9 @@ func TestColumnsIterator(t *testing.T) {
assert.Equal(t, expectedNumCol, colCount)
assert.NoError(t, f.Close())
- f, sheetName, colCount, expectedNumCol = NewFile(), "Sheet1", 0, 4
+ xlsxFile, err := NewFile()
+ assert.NoError(t, err)
+ f, sheetName, colCount, expectedNumCol = xlsxFile, "Sheet1", 0, 4
cells := []string{"C2", "C3", "C4", "D2", "D3", "D4"}
for _, cell := range cells {
assert.NoError(t, f.SetCellValue(sheetName, cell, 1))
@@ -107,7 +110,8 @@ func TestGetColsError(t *testing.T) {
assert.EqualError(t, err, "sheet SheetN is not exist")
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`B
`))
f.checked = nil
@@ -118,7 +122,8 @@ func TestGetColsError(t *testing.T) {
_, err = f.GetCols("Sheet1")
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
cols, err := f.Cols("Sheet1")
assert.NoError(t, err)
cols.totalRows = 2
@@ -135,10 +140,11 @@ func TestGetColsError(t *testing.T) {
}
func TestColsRows(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.NewSheet("Sheet1")
- _, err := f.Cols("Sheet1")
+ _, err = f.Cols("Sheet1")
assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 1))
@@ -148,13 +154,15 @@ func TestColsRows(t *testing.T) {
},
})
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Pkg.Store("xl/worksheets/sheet1.xml", nil)
_, err = f.Cols("Sheet1")
if !assert.NoError(t, err) {
t.FailNow()
}
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
cols, err := f.Cols("Sheet1")
if !assert.NoError(t, err) {
t.FailNow()
@@ -229,7 +237,8 @@ func TestColumnVisibility(t *testing.T) {
}
func TestOutlineLevel(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
level, err := f.GetColOutlineLevel("Sheet1", "D")
assert.Equal(t, uint8(0), level)
assert.NoError(t, err)
@@ -287,7 +296,8 @@ func TestOutlineLevel(t *testing.T) {
}
func TestSetColStyle(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "B2", "Hello"))
styleID, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#94d3a2"],"pattern":1}}`)
assert.NoError(t, err)
@@ -311,7 +321,8 @@ func TestSetColStyle(t *testing.T) {
}
func TestColWidth(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetColWidth("Sheet1", "B", "A", 12))
assert.NoError(t, f.SetColWidth("Sheet1", "A", "B", 12))
width, err := f.GetColWidth("Sheet1", "A")
@@ -340,7 +351,8 @@ func TestColWidth(t *testing.T) {
}
func TestInsertCol(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
fillCells(f, sheet1, 10, 10)
@@ -358,7 +370,8 @@ func TestInsertCol(t *testing.T) {
}
func TestRemoveCol(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
fillCells(f, sheet1, 10, 15)
diff --git a/comment.go b/comment.go
index 0e3945dfa2..ecf3f30ab2 100644
--- a/comment.go
+++ b/comment.go
@@ -17,7 +17,6 @@ import (
"encoding/xml"
"fmt"
"io"
- "log"
"path/filepath"
"strconv"
"strings"
@@ -36,17 +35,27 @@ func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
// GetComments retrieves all comments and returns a map of worksheet name to
// the worksheet comments.
-func (f *File) GetComments() (comments map[string][]Comment) {
+func (f *File) GetComments() (comments map[string][]Comment, firstError error) {
comments = map[string][]Comment{}
for n, path := range f.sheetMap {
- target := f.getSheetComments(filepath.Base(path))
+ target, err := f.getSheetComments(filepath.Base(path))
+ if err != nil && firstError == nil {
+ firstError = err
+ }
if target == "" {
continue
}
if !strings.HasPrefix(target, "/") {
target = "xl" + strings.TrimPrefix(target, "..")
}
- if d := f.commentsReader(strings.TrimPrefix(target, "/")); d != nil {
+ d, err := f.commentsReader(strings.TrimPrefix(target, "/"))
+ if err != nil {
+ if firstError == nil {
+ firstError = err
+ }
+ continue
+ }
+ if d != nil {
var sheetComments []Comment
for _, comment := range d.CommentList.Comment {
sheetComment := Comment{}
@@ -73,18 +82,22 @@ func (f *File) GetComments() (comments map[string][]Comment) {
// getSheetComments provides the method to get the target comment reference by
// given worksheet file path.
-func (f *File) getSheetComments(sheetFile string) string {
+func (f *File) getSheetComments(sheetFile string) (string, error) {
rels := "xl/worksheets/_rels/" + sheetFile + ".rels"
- if sheetRels := f.relsReader(rels); sheetRels != nil {
+ sheetRels, err := f.relsReader(rels)
+ if err != nil {
+ return "", err
+ }
+ if sheetRels != nil {
sheetRels.Lock()
defer sheetRels.Unlock()
for _, v := range sheetRels.Relationships {
if v.Type == SourceRelationshipComments {
- return v.Target
+ return v.Target, err
}
}
}
- return ""
+ return "", err
}
// AddComment provides the method to add comment in a sheet by given worksheet
@@ -95,6 +108,10 @@ func (f *File) getSheetComments(sheetFile string) string {
// err := f.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
//
func (f *File) AddComment(sheet, cell, format string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
formatSet, err := parseFormatCommentsSet(format)
if err != nil {
return err
@@ -116,8 +133,14 @@ func (f *File) AddComment(sheet, cell, format string) error {
} else {
// Add first comment for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
- f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "")
+ rID, err := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
+ if err != nil {
+ return err
+ }
+ _, err = f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "")
+ if err != nil {
+ return err
+ }
f.addSheetNameSpace(sheet, SourceRelationship)
f.addSheetLegacyDrawing(sheet, rID)
}
@@ -135,7 +158,10 @@ func (f *File) AddComment(sheet, cell, format string) error {
if err != nil {
return err
}
- f.addComment(commentsXML, cell, formatSet)
+ err = f.addComment(commentsXML, cell, formatSet)
+ if err != nil {
+ return err
+ }
f.addContentTypePart(commentID, "comments")
return err
}
@@ -212,7 +238,10 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
Column: yAxis,
},
}
- s, _ := xml.Marshal(sp)
+ s, err := xml.Marshal(sp)
+ if err != nil {
+ return err
+ }
shape := xlsxShape{
ID: "_x0000_s1025",
Type: "#_x0000_t202",
@@ -221,7 +250,10 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
Strokecolor: "#edeaa1",
Val: string(s[13 : len(s)-14]),
}
- d := f.decodeVMLDrawingReader(drawingVML)
+ d, err := f.decodeVMLDrawingReader(drawingVML)
+ if err != nil {
+ return err
+ }
if d != nil {
for _, v := range d.Shape {
s := xlsxShape{
@@ -242,7 +274,7 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
// addComment provides a function to create chart as xl/comments%d.xml by
// given cell and format sets.
-func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
+func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) error {
a := formatSet.Author
t := formatSet.Text
if len(a) > MaxFieldLength {
@@ -251,7 +283,10 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
if len(t) > 32512 {
t = t[:32512]
}
- comments := f.commentsReader(commentsXML)
+ comments, err := f.commentsReader(commentsXML)
+ if err != nil {
+ return err
+ }
authorID := 0
if comments == nil {
comments = &xlsxComments{Authors: xlsxAuthor{Author: []string{formatSet.Author}}}
@@ -295,6 +330,7 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
}
comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
f.Comments[commentsXML] = comments
+ return err
}
// countComments provides a function to get comments files count storage in
@@ -320,7 +356,7 @@ func (f *File) countComments() int {
// decodeVMLDrawingReader provides a function to get the pointer to the
// structure after deserialization of xl/drawings/vmlDrawing%d.xml.
-func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing {
+func (f *File) decodeVMLDrawingReader(path string) (*decodeVmlDrawing, error) {
var err error
if f.DecodeVMLDrawing[path] == nil {
@@ -329,27 +365,32 @@ func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing {
f.DecodeVMLDrawing[path] = new(decodeVmlDrawing)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c.([]byte)))).
Decode(f.DecodeVMLDrawing[path]); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return nil, fmt.Errorf("xml decode error: %w", err)
}
}
}
- return f.DecodeVMLDrawing[path]
+ return f.DecodeVMLDrawing[path], nil
}
// vmlDrawingWriter provides a function to save xl/drawings/vmlDrawing%d.xml
// after serialize structure.
-func (f *File) vmlDrawingWriter() {
+func (f *File) vmlDrawingWriter() (firstError error) {
for path, vml := range f.VMLDrawing {
if vml != nil {
- v, _ := xml.Marshal(vml)
+ v, err := xml.Marshal(vml)
+ if err != nil && firstError == nil {
+ firstError = err
+ continue
+ }
f.Pkg.Store(path, v)
}
}
+ return
}
// commentsReader provides a function to get the pointer to the structure
// after deserialization of xl/comments%d.xml.
-func (f *File) commentsReader(path string) *xlsxComments {
+func (f *File) commentsReader(path string) (*xlsxComments, error) {
var err error
if f.Comments[path] == nil {
content, ok := f.Pkg.Load(path)
@@ -357,20 +398,25 @@ func (f *File) commentsReader(path string) *xlsxComments {
f.Comments[path] = new(xlsxComments)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
Decode(f.Comments[path]); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return nil, fmt.Errorf("xml decode error: %w", err)
}
}
}
- return f.Comments[path]
+ return f.Comments[path], nil
}
// commentsWriter provides a function to save xl/comments%d.xml after
// serialize structure.
-func (f *File) commentsWriter() {
+func (f *File) commentsWriter() (firstError error) {
for path, c := range f.Comments {
if c != nil {
- v, _ := xml.Marshal(c)
+ v, err := xml.Marshal(c)
+ if err != nil && firstError == nil {
+ firstError = err
+ continue
+ }
f.saveFileList(path, v)
}
}
+ return
}
diff --git a/comment_test.go b/comment_test.go
index 01f1e42e3e..5fcab98743 100644
--- a/comment_test.go
+++ b/comment_test.go
@@ -35,33 +35,47 @@ func TestAddComments(t *testing.T) {
// Test add comment on with illegal cell coordinates
assert.EqualError(t, f.AddComment("Sheet1", "A", `{"author":"Excelize: ","text":"This is a comment."}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
if assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddComments.xlsx"))) {
- assert.Len(t, f.GetComments(), 2)
+ d, err := f.GetComments()
+ assert.NoError(t, err)
+ assert.Len(t, d, 2)
}
f.Comments["xl/comments2.xml"] = nil
f.Pkg.Store("xl/comments2.xml", []byte(xml.Header+`Excelize: Excelize: `))
- comments := f.GetComments()
+ comments, err := f.GetComments()
+ assert.NoError(t, err)
assert.EqualValues(t, 2, len(comments["Sheet1"]))
assert.EqualValues(t, 1, len(comments["Sheet2"]))
- assert.EqualValues(t, len(NewFile().GetComments()), 0)
+ xlsxFile, err := NewFile()
+ assert.NoError(t, err)
+ d, err := xlsxFile.GetComments()
+ assert.NoError(t, err)
+ assert.EqualValues(t, len(d), 0)
}
func TestDecodeVMLDrawingReader(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
path := "xl/drawings/vmlDrawing1.xml"
f.Pkg.Store(path, MacintoshCyrillicCharset)
- f.decodeVMLDrawingReader(path)
+ // Test unsupported charset.
+ _, err = f.decodeVMLDrawingReader(path)
+ assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
func TestCommentsReader(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
path := "xl/comments1.xml"
f.Pkg.Store(path, MacintoshCyrillicCharset)
- f.commentsReader(path)
+ // Test unsupported charset.
+ _, err = f.commentsReader(path)
+ assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
func TestCountComments(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Comments["xl/comments1.xml"] = nil
assert.Equal(t, f.countComments(), 1)
}
diff --git a/datavalidation_test.go b/datavalidation_test.go
index d9e060a54e..8effcfa09c 100644
--- a/datavalidation_test.go
+++ b/datavalidation_test.go
@@ -23,7 +23,8 @@ import (
func TestDataValidation(t *testing.T) {
resultFile := filepath.Join("test", "TestDataValidation.xlsx")
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
dvRange := NewDataValidation(true)
dvRange.Sqref = "A1:B2"
@@ -73,7 +74,8 @@ func TestDataValidation(t *testing.T) {
func TestDataValidationError(t *testing.T) {
resultFile := filepath.Join("test", "TestDataValidationError.xlsx")
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellStr("Sheet1", "E1", "E1"))
assert.NoError(t, f.SetCellStr("Sheet1", "E2", "E2"))
assert.NoError(t, f.SetCellStr("Sheet1", "E3", "E3"))
@@ -86,7 +88,7 @@ func TestDataValidationError(t *testing.T) {
assert.NoError(t, f.AddDataValidation("Sheet1", dvRange))
dvRange = NewDataValidation(true)
- err := dvRange.SetDropList(make([]string, 258))
+ err = dvRange.SetDropList(make([]string, 258))
if dvRange.Formula1 != "" {
t.Errorf("data validation error. Formula1 must be empty!")
return
@@ -126,12 +128,14 @@ func TestDataValidationError(t *testing.T) {
assert.NoError(t, f.SaveAs(resultFile))
// Test add data validation on no exists worksheet.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.AddDataValidation("SheetN", nil), "sheet SheetN is not exist")
}
func TestDeleteDataValidation(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.DeleteDataValidation("Sheet1", "A1:B2"))
dvRange := NewDataValidation(true)
diff --git a/docProps.go b/docProps.go
index 41ea18e258..888098b933 100644
--- a/docProps.go
+++ b/docProps.go
@@ -77,9 +77,10 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
app = new(xlsxProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
Decode(app); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
+ err = nil
fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"}
immutable, mutable = reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem()
for _, field = range fields {
@@ -95,6 +96,9 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
}
app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value
output, err = xml.Marshal(app)
+ if err != nil {
+ return
+ }
f.saveFileList(defaultXMLPathDocPropsApp, output)
return
}
@@ -104,9 +108,10 @@ func (f *File) GetAppProps() (ret *AppProperties, err error) {
app := new(xlsxProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
Decode(app); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
+ err = nil
ret, err = &AppProperties{
Application: app.Application,
ScaleCrop: app.ScaleCrop,
@@ -183,9 +188,10 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
core = new(decodeCoreProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
Decode(core); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
+ err = nil
newProps, err = &xlsxCoreProperties{
Dc: NameSpaceDublinCore,
Dcterms: NameSpaceDublinCoreTerms,
@@ -222,6 +228,9 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
newProps.Modified.Text = docProperties.Modified
}
output, err = xml.Marshal(newProps)
+ if err != nil {
+ return
+ }
f.saveFileList(defaultXMLPathDocPropsCore, output)
return
@@ -233,9 +242,10 @@ func (f *File) GetDocProps() (ret *DocProperties, err error) {
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
Decode(core); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
+ err = nil
ret, err = &DocProperties{
Category: core.Category,
ContentStatus: core.ContentStatus,
diff --git a/docProps_test.go b/docProps_test.go
index 545059d8df..314fe28fab 100644
--- a/docProps_test.go
+++ b/docProps_test.go
@@ -40,7 +40,8 @@ func TestSetAppProps(t *testing.T) {
assert.NoError(t, f.Close())
// Test unsupported charset
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Pkg.Store(defaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
assert.EqualError(t, f.SetAppProps(&AppProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -52,14 +53,15 @@ func TestGetAppProps(t *testing.T) {
}
props, err := f.GetAppProps()
assert.NoError(t, err)
- assert.Equal(t, props.Application, "Microsoft Macintosh Excel")
+ assert.NotEmpty(t, props.Application, "expected application name in app properties to be not empty")
f.Pkg.Store(defaultXMLPathDocPropsApp, nil)
_, err = f.GetAppProps()
assert.NoError(t, err)
assert.NoError(t, f.Close())
// Test unsupported charset
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Pkg.Store(defaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
_, err = f.GetAppProps()
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
@@ -92,7 +94,8 @@ func TestSetDocProps(t *testing.T) {
assert.NoError(t, f.Close())
// Test unsupported charset
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Pkg.Store(defaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
assert.EqualError(t, f.SetDocProps(&DocProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -111,7 +114,8 @@ func TestGetDocProps(t *testing.T) {
assert.NoError(t, f.Close())
// Test unsupported charset
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Pkg.Store(defaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
_, err = f.GetDocProps()
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
diff --git a/drawing.go b/drawing.go
index 7de0fb9136..b05b16f173 100644
--- a/drawing.go
+++ b/drawing.go
@@ -16,7 +16,6 @@ import (
"encoding/xml"
"fmt"
"io"
- "log"
"reflect"
"strconv"
"strings"
@@ -24,7 +23,7 @@ import (
// prepareDrawing provides a function to prepare drawing ID and XML by given
// drawingID, worksheet name and default drawingXML.
-func (f *File) prepareDrawing(ws *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {
+func (f *File) prepareDrawing(ws *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string, error) {
sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
if ws.Drawing != nil {
// The worksheet already has a picture or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
@@ -34,28 +33,35 @@ func (f *File) prepareDrawing(ws *xlsxWorksheet, drawingID int, sheet, drawingXM
} else {
// Add first picture for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ rID, err := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ if err != nil {
+ return 0, "", err
+ }
f.addSheetDrawing(sheet, rID)
}
- return drawingID, drawingXML
+ return drawingID, drawingXML, nil
}
// prepareChartSheetDrawing provides a function to prepare drawing ID and XML
// by given drawingID, worksheet name and default drawingXML.
-func (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet string) {
+func (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet string) error {
sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
// Only allow one chart in a chartsheet.
sheetRels := "xl/chartsheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/chartsheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ rID, err := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ if err != nil {
+ return err
+ }
f.addSheetNameSpace(sheet, SourceRelationship)
cs.Drawing = &xlsxDrawing{
RID: "rId" + strconv.Itoa(rID),
}
+ return err
}
// addChart provides a function to create chart as xl/charts/chart%d.xml by
// given format sets.
-func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
+func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) error {
count := f.countCharts()
xlsxChartSpace := xlsxChartSpace{
XMLNSa: NameSpaceDrawingML.Value,
@@ -256,9 +262,13 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](comboCharts[idx]))
order += len(comboCharts[idx].Series)
}
- chart, _ := xml.Marshal(xlsxChartSpace)
+ chart, err := xml.Marshal(xlsxChartSpace)
+ if err != nil {
+ return err
+ }
media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
f.saveFileList(media, chart)
+ return err
}
// drawBaseChart provides a function to draw the c:plotArea element for bar,
@@ -1148,7 +1158,7 @@ func (f *File) drawPlotAreaTxPr() *cTxPr {
// the problem that the label structure is changed after serialization and
// deserialization, two different structures: decodeWsDr and encodeWsDr are
// defined.
-func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
+func (f *File) drawingParser(path string) (*xlsxWsDr, int, error) {
var (
err error
ok bool
@@ -1162,8 +1172,9 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
decodeWsDr := decodeWsDr{}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
Decode(&decodeWsDr); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return nil, 0, fmt.Errorf("xml decode error: %w", err)
}
+ err = nil
content.R = decodeWsDr.R
for _, v := range decodeWsDr.AlternateContent {
content.AlternateContent = append(content.AlternateContent, &xlsxAlternateContent{
@@ -1192,7 +1203,7 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
}
wsDr.Lock()
defer wsDr.Unlock()
- return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2
+ return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2, err
}
// addDrawingChart provides a function to add chart graphic frame by given
@@ -1208,7 +1219,10 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
width = int(float64(width) * formatSet.XScale)
height = int(float64(height) * formatSet.YScale)
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.OffsetX, formatSet.OffsetY, width, height)
- content, cNvPrID := f.drawingParser(drawingXML)
+ content, cNvPrID, err := f.drawingParser(drawingXML)
+ if err != nil {
+ return err
+ }
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Positioning
from := xlsxFrom{}
@@ -1242,7 +1256,10 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
},
},
}
- graphic, _ := xml.Marshal(graphicFrame)
+ graphic, err := xml.Marshal(graphicFrame)
+ if err != nil {
+ return err
+ }
twoCellAnchor.GraphicFrame = string(graphic)
twoCellAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.FLocksWithSheet,
@@ -1256,8 +1273,11 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
// addSheetDrawingChart provides a function to add chart graphic frame for
// chartsheet by given sheet, drawingXML, width, height, relationship index
// and format sets.
-func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *formatPicture) {
- content, cNvPrID := f.drawingParser(drawingXML)
+func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *formatPicture) error {
+ content, cNvPrID, err := f.drawingParser(drawingXML)
+ if err != nil {
+ return err
+ }
absoluteAnchor := xdrCellAnchor{
EditAs: formatSet.Positioning,
Pos: &xlsxPoint2D{},
@@ -1282,7 +1302,10 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma
},
},
}
- graphic, _ := xml.Marshal(graphicFrame)
+ graphic, err := xml.Marshal(graphicFrame)
+ if err != nil {
+ return err
+ }
absoluteAnchor.GraphicFrame = string(graphic)
absoluteAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.FLocksWithSheet,
@@ -1290,6 +1313,7 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma
}
content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
f.Drawings.Store(drawingXML, content)
+ return err
}
// deleteDrawing provides a function to delete chart graphic frame by given by
@@ -1307,9 +1331,12 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
"Chart": func(anchor *decodeTwoCellAnchor) bool { return anchor.Pic == nil },
"Pic": func(anchor *decodeTwoCellAnchor) bool { return anchor.Pic != nil },
}
- wsDr, _ = f.drawingParser(drawingXML)
+ wsDr, _, err = f.drawingParser(drawingXML)
+ if err != nil {
+ return
+ }
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
- if err = nil; wsDr.TwoCellAnchor[idx].From != nil && xdrCellAnchorFuncs[drawingType](wsDr.TwoCellAnchor[idx]) {
+ if wsDr.TwoCellAnchor[idx].From != nil && xdrCellAnchorFuncs[drawingType](wsDr.TwoCellAnchor[idx]) {
if wsDr.TwoCellAnchor[idx].From.Col == col && wsDr.TwoCellAnchor[idx].From.Row == row {
wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
idx--
@@ -1320,10 +1347,11 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
deTwoCellAnchor = new(decodeTwoCellAnchor)
if err = f.xmlNewDecoder(strings.NewReader("" + wsDr.TwoCellAnchor[idx].GraphicFrame + "")).
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
- if err = nil; deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) {
+ err = nil
+ if deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) {
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
idx--
diff --git a/drawing_test.go b/drawing_test.go
index e37b771fdd..48b790525a 100644
--- a/drawing_test.go
+++ b/drawing_test.go
@@ -15,6 +15,8 @@ import (
"encoding/xml"
"sync"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestDrawingParser(t *testing.T) {
@@ -25,11 +27,14 @@ func TestDrawingParser(t *testing.T) {
f.Pkg.Store("charset", MacintoshCyrillicCharset)
f.Pkg.Store("wsDr", []byte(xml.Header+``))
// Test with one cell anchor
- f.drawingParser("wsDr")
+ _, _, err := f.drawingParser("wsDr")
+ assert.NoError(t, err)
// Test with unsupported charset
- f.drawingParser("charset")
+ _, _, err = f.drawingParser("charset")
+ assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
// Test with alternate content
f.Drawings = sync.Map{}
f.Pkg.Store("wsDr", []byte(xml.Header+``))
- f.drawingParser("wsDr")
+ _, _, err = f.drawingParser("wsDr")
+ assert.NoError(t, err)
}
diff --git a/errors.go b/errors.go
index 6606f1eaf5..10a88b57d6 100644
--- a/errors.go
+++ b/errors.go
@@ -64,6 +64,11 @@ func newFieldLengthError(name string) error {
return fmt.Errorf("field %s must be less or equal than 255 characters", name)
}
+// newRangeLengthError defined the error message on receiving the invalid range length.
+func newRangeLengthError(rangeValue string) error {
+ return fmt.Errorf("%w: range of %q is smaller than minimum range length of 2", ErrRangeLength, rangeValue)
+}
+
// newCellNameToCoordinatesError defined the error message on converts
// alphanumeric cell name to coordinates.
func newCellNameToCoordinatesError(cell string, err error) error {
@@ -106,6 +111,8 @@ var (
// ErrImgExt defined the error message on receive an unsupported image
// extension.
ErrImgExt = errors.New("unsupported image extension")
+ // ErrIncompleteFileSetup defined the error message on invalid File setup.
+ ErrIncompleteFileSetup = errors.New("file is setup incorrectly")
// ErrWorkbookFileFormat defined the error message on receive an
// unsupported workbook file format.
ErrWorkbookFileFormat = errors.New("unsupported workbook file format")
@@ -166,6 +173,8 @@ var (
// ErrCellCharsLength defined the error message for receiving a cell
// characters length that exceeds the limit.
ErrCellCharsLength = fmt.Errorf("cell value must be 0-%d characters", TotalCellChars)
+ // ErrRangeLength defined the error message for handling a range of invalid length.
+ ErrRangeLength = fmt.Errorf("invalid range length")
// ErrOptionsUnzipSizeLimit defined the error message for receiving
// invalid UnzipSizeLimit and UnzipXMLSizeLimit.
ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to UnzipXMLSizeLimit")
diff --git a/excelize.go b/excelize.go
index 580bc292ed..6025d0ba73 100644
--- a/excelize.go
+++ b/excelize.go
@@ -174,10 +174,31 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
for k, v := range file {
f.Pkg.Store(k, v)
}
- f.CalcChain = f.calcChainReader()
- f.sheetMap = f.getSheetMap()
- f.Styles = f.stylesReader()
- f.Theme = f.themeReader()
+ f.CalcChain, err = f.NewCalcChainReader()
+ if err != nil {
+ return nil, err
+ }
+ f.ContentTypes, err = f.NewContentTypesReader()
+ if err != nil {
+ return nil, err
+ }
+ f.WorkBook, err = f.NewWorkbookReader()
+ if err != nil {
+ return nil, err
+ }
+ f.sheetMap, err = f.getSheetMap()
+ if err != nil {
+ return nil, err
+ }
+ f.Styles, err = f.NewStylesReader()
+ if err != nil {
+ return nil, err
+ }
+ f.Theme, err = f.NewThemeReader()
+ if err != nil {
+ return nil, err
+ }
+
return f, nil
}
@@ -191,6 +212,13 @@ func parseOptions(opts ...Options) *Options {
return opt
}
+// IsValid returns true if the File's main components are initialized.
+// Note: CalcChain can be nil, in case there is no calculation chain.
+func (f *File) IsValid() bool {
+ return f.ContentTypes != nil && f.WorkBook != nil &&
+ f.sheetMap != nil && f.Styles != nil && f.Theme != nil
+}
+
// CharsetTranscoder Set user defined codepage transcoder function for open
// XLSX from non UTF-8 encoding.
func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f }
@@ -247,7 +275,7 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {
}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name)))).
Decode(ws); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
err = nil
@@ -330,9 +358,9 @@ func checkSheetR0(ws *xlsxWorksheet, sheetData *xlsxSheetData, r0 *xlsxRow) {
// setRels provides a function to set relationships by given relationship ID,
// XML path, relationship type, target and target mode.
-func (f *File) setRels(rID, relPath, relType, target, targetMode string) int {
- rels := f.relsReader(relPath)
- if rels == nil || rID == "" {
+func (f *File) setRels(rID, relPath, relType, target, targetMode string) (int, error) {
+ rels, err := f.relsReader(relPath)
+ if err != nil || rels == nil || rID == "" {
return f.addRels(relPath, relType, target, targetMode)
}
rels.Lock()
@@ -347,18 +375,19 @@ func (f *File) setRels(rID, relPath, relType, target, targetMode string) int {
break
}
}
- return ID
+ return ID, err
}
// addRels provides a function to add relationships by given XML path,
// relationship type, target and target mode.
-func (f *File) addRels(relPath, relType, target, targetMode string) int {
+func (f *File) addRels(relPath, relType, target, targetMode string) (int, error) {
uniqPart := map[string]string{
SourceRelationshipSharedStrings: "/xl/sharedStrings.xml",
}
- rels := f.relsReader(relPath)
- if rels == nil {
+ rels, err := f.relsReader(relPath)
+ if err != nil || rels == nil {
rels = &xlsxRelationships{}
+ err = nil
}
rels.Lock()
defer rels.Unlock()
@@ -371,14 +400,20 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
if relType == rel.Type {
if partName, ok := uniqPart[rel.Type]; ok {
rels.Relationships[idx].Target = partName
- return rID
+ return rID, err
}
}
}
rID++
var ID bytes.Buffer
- ID.WriteString("rId")
- ID.WriteString(strconv.Itoa(rID))
+ _, err = ID.WriteString("rId")
+ if err != nil {
+ return rID, err
+ }
+ _, err = ID.WriteString(strconv.Itoa(rID))
+ if err != nil {
+ return rID, err
+ }
rels.Relationships = append(rels.Relationships, xlsxRelationship{
ID: ID.String(),
Type: relType,
@@ -386,7 +421,7 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
TargetMode: targetMode,
})
f.Relationships.Store(relPath, rels)
- return rID
+ return rID, err
}
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
@@ -416,6 +451,9 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
//
func (f *File) UpdateLinkedValue() error {
wb := f.workbookReader()
+ if wb == nil {
+ return ErrIncompleteFileSetup
+ }
// recalculate formulas
wb.CalcPr = nil
for _, name := range f.GetSheetList() {
@@ -460,7 +498,14 @@ func (f *File) AddVBAProject(bin string) error {
if path.Ext(bin) != ".bin" {
return ErrAddVBAProject
}
- wb := f.relsReader(f.getWorkbookRelsPath())
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return err
+ }
+ wb, err := f.relsReader(wrp)
+ if err != nil {
+ return err
+ }
wb.Lock()
defer wb.Unlock()
var rID int
@@ -483,7 +528,10 @@ func (f *File) AddVBAProject(bin string) error {
Type: SourceRelationshipVBAProject,
})
}
- file, _ := ioutil.ReadFile(filepath.Clean(bin))
+ file, err := ioutil.ReadFile(filepath.Clean(bin))
+ if err != nil {
+ return err
+ }
f.Pkg.Store("xl/vbaProject.bin", file)
return err
}
@@ -493,6 +541,9 @@ func (f *File) AddVBAProject(bin string) error {
func (f *File) setContentTypePartProjectExtensions(contentType string) {
var ok bool
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
for _, v := range content.Defaults {
diff --git a/excelize_test.go b/excelize_test.go
index f1b9903cbb..3808a401fd 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -202,7 +202,8 @@ func TestSaveAsWrongPath(t *testing.T) {
}
func TestCharsetTranscoder(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.CharsetTranscoder(*new(charsetTranscoderFn))
}
@@ -275,7 +276,7 @@ func TestBrokenFile(t *testing.T) {
t.Run("SaveAsEmptyStruct", func(t *testing.T) {
// Test write file with broken file struct with given path.
- assert.NoError(t, f.SaveAs(filepath.Join("test", "BadWorkbook.SaveAsEmptyStruct.xlsx")))
+ assert.EqualError(t, f.SaveAs(filepath.Join("test", "BadWorkbook.SaveAsEmptyStruct.xlsx")), ErrIncompleteFileSetup.Error())
})
t.Run("OpenBadWorkbook", func(t *testing.T) {
@@ -298,7 +299,8 @@ func TestBrokenFile(t *testing.T) {
func TestNewFile(t *testing.T) {
// Test create a spreadsheet file.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.NewSheet("Sheet1")
f.NewSheet("XLSXSheet2")
f.NewSheet("XLSXSheet3")
@@ -307,7 +309,7 @@ func TestNewFile(t *testing.T) {
f.SetActiveSheet(0)
// Test add picture to sheet with scaling and positioning.
- err := f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
+ 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()
@@ -329,9 +331,20 @@ func TestNewFile(t *testing.T) {
assert.NoError(t, f.Save())
}
+func TestIsValid(t *testing.T) {
+ f, err := NewFile()
+ assert.NoError(t, err)
+ assert.EqualValues(t, true, f.IsValid(), "expected that new file is valid")
+
+ f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
+ assert.NoError(t, err)
+ assert.EqualValues(t, true, f.IsValid(), "expected that new file is valid")
+}
+
func TestAddDrawingVML(t *testing.T) {
// Test addDrawingVML with illegal cell coordinates.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.addDrawingVML(0, "", "*", 0, 0), newCellNameToCoordinatesError("*", newInvalidCellNameError("*")).Error())
}
@@ -358,7 +371,8 @@ func TestSetCellHyperLink(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellHyperLink.xlsx")))
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
@@ -366,7 +380,8 @@ func TestSetCellHyperLink(t *testing.T) {
ws.(*xlsxWorksheet).Hyperlinks = &xlsxHyperlinks{Hyperlink: make([]xlsxHyperlink, 65530)}
assert.EqualError(t, f.SetCellHyperLink("Sheet1", "A65531", "https://github.com/xuri/excelize", "External"), ErrTotalSheetHyperlinks.Error())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
@@ -376,7 +391,8 @@ func TestSetCellHyperLink(t *testing.T) {
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
// Test update cell hyperlink
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellHyperLink("Sheet1", "A1", "https://github.com", "External"))
assert.NoError(t, f.SetCellHyperLink("Sheet1", "A1", "https://github.com/xuri/excelize", "External"))
link, target, err := f.GetCellHyperLink("Sheet1", "A1")
@@ -405,7 +421,8 @@ func TestGetCellHyperLink(t *testing.T) {
t.Log(link, target)
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
@@ -476,7 +493,8 @@ func TestWriteArrayFormula(t *testing.T) {
return c
}
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sample := []string{"Sample 1", "Sample 2", "Sample 3"}
values := []int{1855, 1709, 1462, 1115, 1524, 625, 773, 126, 1027, 1696, 1078, 1917, 1109, 1753, 1884, 659, 994, 1911, 1925, 899, 196, 244, 1488, 1056, 1986, 66, 784, 725, 767, 1722, 1541, 1026, 1455, 264, 1538, 877, 1581, 1098, 383, 762, 237, 493, 29, 1923, 474, 430, 585, 688, 308, 200, 1259, 622, 798, 1048, 996, 601, 582, 332, 377, 805, 250, 1860, 1360, 840, 911, 1346, 1651, 1651, 665, 584, 1057, 1145, 925, 1752, 202, 149, 1917, 1398, 1894, 818, 714, 624, 1085, 1566, 635, 78, 313, 1686, 1820, 494, 614, 1913, 271, 1016, 338, 1301, 489, 1733, 1483, 1141}
@@ -800,7 +818,8 @@ func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
}
func TestSetCellStyleCustomNumberFormat(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 42920.5))
assert.NoError(t, f.SetCellValue("Sheet1", "A2", 42920.5))
style, err := f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
@@ -951,7 +970,8 @@ func TestCopySheet(t *testing.T) {
t.FailNow()
}
- 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"))
@@ -977,42 +997,50 @@ func TestCopySheetError(t *testing.T) {
}
func TestGetSheetComments(t *testing.T) {
- f := NewFile()
- assert.Equal(t, "", f.getSheetComments("sheet0"))
+ f, err := NewFile()
+ assert.NoError(t, err)
+ comments, err := f.getSheetComments("sheet0")
+ assert.NoError(t, err)
+ assert.Equal(t, "", comments)
}
func TestSetSheetVisible(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.WorkBook.Sheets.Sheet[0].Name = "SheetN"
assert.EqualError(t, f.SetSheetVisible("Sheet1", false), "sheet SheetN is not exist")
}
func TestGetActiveSheetIndex(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.WorkBook.BookViews = nil
assert.Equal(t, 0, f.GetActiveSheetIndex())
}
func TestRelsWriter(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Relationships.Store("xl/worksheets/sheet/rels/sheet1.xml.rel", &xlsxRelationships{})
- f.relsWriter()
+ err = f.relsWriter()
+ assert.NoError(t, err)
}
func TestGetSheetView(t *testing.T) {
- f := NewFile()
- _, err := f.getSheetView("SheetN", 0)
+ f, err := NewFile()
+ assert.NoError(t, err)
+ _, err = f.getSheetView("SheetN", 0)
assert.EqualError(t, err, "sheet SheetN is not exist")
}
func TestConditionalFormat(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
fillCells(f, sheet1, 10, 15)
var format1, format2, format3, format4 int
- var err error
// Rose format for bad conditional.
format1, err = f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
if !assert.NoError(t, err) {
@@ -1093,13 +1121,14 @@ func TestConditionalFormat(t *testing.T) {
}
func TestConditionalFormatError(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
fillCells(f, sheet1, 10, 15)
// Set conditional format with illegal JSON string should return error.
- _, err := f.NewConditionalStyle("")
+ _, err = f.NewConditionalStyle("")
if !assert.EqualError(t, err, "unexpected end of JSON input") {
t.FailNow()
}
@@ -1173,7 +1202,8 @@ func TestHSL(t *testing.T) {
}
func TestProtectSheet(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheetName := f.GetSheetName(0)
assert.NoError(t, f.ProtectSheet(sheetName, nil))
// Test protect worksheet with XOR hash algorithm
@@ -1229,7 +1259,8 @@ func TestUnprotectSheet(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnprotectSheet.xlsx")))
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
sheetName := f.GetSheetName(0)
assert.NoError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{Password: "password"}))
// Test remove sheet protection with an incorrect password
@@ -1248,7 +1279,8 @@ func TestUnprotectSheet(t *testing.T) {
}
func TestSetDefaultTimeStyle(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test set default time style on not exists worksheet.
assert.EqualError(t, f.setDefaultTimeStyle("SheetN", "", 0), "sheet SheetN is not exist")
@@ -1257,7 +1289,8 @@ func TestSetDefaultTimeStyle(t *testing.T) {
}
func TestAddVBAProject(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetSheetPrOptions("Sheet1", CodeName("Sheet1")))
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())
@@ -1269,31 +1302,33 @@ func TestAddVBAProject(t *testing.T) {
func TestContentTypesReader(t *testing.T) {
// Test unsupported charset.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
- f.contentTypesReader()
}
func TestWorkbookReader(t *testing.T) {
// Test unsupported charset.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.WorkBook = nil
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
- f.workbookReader()
}
func TestWorkSheetReader(t *testing.T) {
// Test unsupported charset.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
- _, err := f.workSheetReader("Sheet1")
+ _, err = f.workSheetReader("Sheet1")
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
assert.EqualError(t, f.UpdateLinkedValue(), "xml decode error: XML syntax error on line 1: invalid UTF-8")
// Test on no checked worksheet.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(``))
f.checked = nil
@@ -1303,18 +1338,24 @@ func TestWorkSheetReader(t *testing.T) {
func TestRelsReader(t *testing.T) {
// Test unsupported charset.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
rels := "xl/_rels/workbook.xml.rels"
f.Relationships.Store(rels, nil)
f.Pkg.Store(rels, MacintoshCyrillicCharset)
- f.relsReader(rels)
+ _, err = f.relsReader(rels)
+ assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
func TestDeleteSheetFromWorkbookRels(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
rels := "xl/_rels/workbook.xml.rels"
f.Relationships.Store(rels, nil)
- assert.Equal(t, f.deleteSheetFromWorkbookRels("rID"), "")
+
+ target, err := f.deleteSheetFromWorkbookRels("rID")
+ assert.NoError(t, err)
+ assert.Equal(t, target, "")
}
func TestAttrValToInt(t *testing.T) {
@@ -1357,7 +1398,10 @@ func prepareTestBook1() (*File, error) {
}
func prepareTestBook3() (*File, error) {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ return f, err
+ }
f.NewSheet("Sheet1")
f.NewSheet("XLSXSheet2")
f.NewSheet("XLSXSheet3")
@@ -1369,7 +1413,7 @@ func prepareTestBook3() (*File, error) {
}
f.SetActiveSheet(0)
- err := f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
+ err = f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
`{"x_scale": 0.5, "y_scale": 0.5, "positioning": "absolute"}`)
if err != nil {
return nil, err
@@ -1384,7 +1428,10 @@ func prepareTestBook3() (*File, error) {
}
func prepareTestBook4() (*File, error) {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ return f, err
+ }
if err := f.SetColWidth("Sheet1", "B", "A", 12); err != nil {
return f, err
}
diff --git a/file.go b/file.go
index 5931bdb4fb..3241cc10ab 100644
--- a/file.go
+++ b/file.go
@@ -26,8 +26,8 @@ import (
//
// f := NewFile()
//
-func NewFile() *File {
- f := newFile()
+func NewFile() (f *File, err error) {
+ f = newFile()
f.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
f.Pkg.Store(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
f.Pkg.Store(defaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))
@@ -38,21 +38,56 @@ func NewFile() *File {
f.Pkg.Store(defaultXMLPathWorkbook, []byte(xml.Header+templateWorkbook))
f.Pkg.Store(defaultXMLPathContentTypes, []byte(xml.Header+templateContentTypes))
f.SheetCount = 1
- f.CalcChain = f.calcChainReader()
+
+ handleFailure := func() {
+ f.Close()
+ f = nil
+ }
+
+ f.CalcChain, err = f.NewCalcChainReader()
+ if err != nil {
+ handleFailure()
+ return
+ }
f.Comments = make(map[string]*xlsxComments)
- f.ContentTypes = f.contentTypesReader()
+ f.ContentTypes, err = f.NewContentTypesReader()
+ if err != nil {
+ handleFailure()
+ return
+ }
f.Drawings = sync.Map{}
- f.Styles = f.stylesReader()
+ f.Styles, err = f.NewStylesReader()
+ if err != nil {
+ handleFailure()
+ return
+ }
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
f.VMLDrawing = make(map[string]*vmlDrawing)
- f.WorkBook = f.workbookReader()
+ f.WorkBook, err = f.NewWorkbookReader()
+ if err != nil {
+ handleFailure()
+ return
+ }
f.Relationships = sync.Map{}
- f.Relationships.Store("xl/_rels/workbook.xml.rels", f.relsReader("xl/_rels/workbook.xml.rels"))
+ rels, err := f.relsReader("xl/_rels/workbook.xml.rels")
+ if err != nil {
+ handleFailure()
+ return
+ }
+ f.Relationships.Store("xl/_rels/workbook.xml.rels", rels)
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
- ws, _ := f.workSheetReader("Sheet1")
+ ws, err := f.workSheetReader("Sheet1")
+ if err != nil {
+ handleFailure()
+ return
+ }
f.Sheet.Store("xl/worksheets/sheet1.xml", ws)
- f.Theme = f.themeReader()
- return f
+ f.Theme, err = f.NewThemeReader()
+ if err != nil {
+ handleFailure()
+ return
+ }
+ return
}
// Save provides a function to override the spreadsheet with origin path.
@@ -72,6 +107,9 @@ func (f *File) SaveAs(name string, opt ...Options) error {
if len(name) > MaxFilePathLength {
return ErrMaxFilePathLength
}
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
f.Path = name
contentType, ok := map[string]string{
".xlam": ContentTypeAddinMacro,
@@ -170,37 +208,70 @@ func (f *File) writeDirectToWriter(w io.Writer) error {
}
// writeToZip provides a function to write to zip.Writer
-func (f *File) writeToZip(zw *zip.Writer) error {
- f.calcChainWriter()
- f.commentsWriter()
- f.contentTypesWriter()
- f.drawingsWriter()
- f.vmlDrawingWriter()
- f.workBookWriter()
- f.workSheetWriter()
- f.relsWriter()
- f.sharedStringsLoader()
- f.sharedStringsWriter()
- f.styleSheetWriter()
+func (f *File) writeToZip(zw *zip.Writer) (err error) {
+ err = f.calcChainWriter()
+ if err != nil {
+ return
+ }
+ err = f.commentsWriter()
+ if err != nil {
+ return
+ }
+ err = f.contentTypesWriter()
+ if err != nil {
+ return
+ }
+ err = f.drawingsWriter()
+ if err != nil {
+ return
+ }
+ err = f.vmlDrawingWriter()
+ if err != nil {
+ return
+ }
+ err = f.workBookWriter()
+ if err != nil {
+ return
+ }
+ err = f.workSheetWriter()
+ if err != nil {
+ return
+ }
+ err = f.relsWriter()
+ if err != nil {
+ return
+ }
+ err = f.sharedStringsLoader()
+ if err != nil {
+ return
+ }
+ err = f.sharedStringsWriter()
+ if err != nil {
+ return
+ }
+ err = f.styleSheetWriter()
+ if err != nil {
+ return
+ }
for path, stream := range f.streams {
- fi, err := zw.Create(path)
+ var fi io.Writer
+ fi, err = zw.Create(path)
if err != nil {
- return err
+ return
}
var from io.Reader
from, err = stream.rawData.Reader()
if err != nil {
_ = stream.rawData.Close()
- return err
+ return
}
_, err = io.Copy(fi, from)
if err != nil {
- return err
+ return
}
_ = stream.rawData.Close()
}
- var err error
f.Pkg.Range(func(path, content interface{}) bool {
if err != nil {
return false
@@ -214,7 +285,7 @@ func (f *File) writeToZip(zw *zip.Writer) error {
return false
}
_, err = fi.Write(content.([]byte))
- return true
+ return err == nil
})
f.tempFiles.Range(func(path, content interface{}) bool {
if _, ok := f.Pkg.Load(path); ok {
@@ -226,7 +297,7 @@ func (f *File) writeToZip(zw *zip.Writer) error {
return false
}
_, err = fi.Write(f.readBytes(path.(string)))
- return true
+ return err == nil
})
- return err
+ return
}
diff --git a/file_test.go b/file_test.go
index 8e65c5d46d..e6778fedb1 100644
--- a/file_test.go
+++ b/file_test.go
@@ -15,7 +15,8 @@ import (
func BenchmarkWrite(b *testing.B) {
const s = "This is test data"
for i := 0; i < b.N; i++ {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(b, err)
for row := 1; row <= 10000; row++ {
for col := 1; col <= 20; col++ {
val, err := CoordinatesToCellName(col, row)
@@ -28,7 +29,7 @@ func BenchmarkWrite(b *testing.B) {
}
}
// Save spreadsheet by the given path.
- err := f.SaveAs("./test.xlsx")
+ err = f.SaveAs("./test.xlsx")
if err != nil {
b.Error(err)
}
@@ -74,7 +75,8 @@ func TestWriteTo(t *testing.T) {
}
func TestClose(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.tempFiles.Store("/d/", "/d/")
require.Error(t, f.Close())
}
diff --git a/lib.go b/lib.go
index f285a40dbc..5f13358ced 100644
--- a/lib.go
+++ b/lib.go
@@ -286,7 +286,7 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
func areaRefToCoordinates(ref string) ([]int, error) {
rng := strings.Split(strings.ReplaceAll(ref, "$", ""), ":")
if len(rng) < 2 {
- return nil, ErrParameterInvalid
+ return nil, newRangeLengthError(ref)
}
return areaRangeToCoordinates(rng[0], rng[1])
}
diff --git a/lib_test.go b/lib_test.go
index 027e5dd6b6..c050039a4a 100644
--- a/lib_test.go
+++ b/lib_test.go
@@ -217,8 +217,9 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
}
func TestCoordinatesToAreaRef(t *testing.T) {
- f := NewFile()
- _, err := f.coordinatesToAreaRef([]int{})
+ f, err := NewFile()
+ assert.NoError(t, err)
+ _, err = f.coordinatesToAreaRef([]int{})
assert.EqualError(t, err, ErrCoordinates.Error())
_, err = f.coordinatesToAreaRef([]int{1, -1, 1, 1})
assert.EqualError(t, err, "invalid cell coordinates [1, -1]")
@@ -277,7 +278,8 @@ func TestGetRootElement(t *testing.T) {
}
func TestSetIgnorableNameSpace(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.xmlAttr["xml_path"] = []xml.Attr{{}}
f.setIgnorableNameSpace("xml_path", 0, xml.Attr{Name: xml.Name{Local: "c14"}})
assert.EqualValues(t, "c14", f.xmlAttr["xml_path"][0].Value)
@@ -343,7 +345,8 @@ func TestUnzipToTemp(t *testing.T) {
os.Setenv("TMPDIR", "test")
defer os.Unsetenv("TMPDIR")
assert.NoError(t, os.Chmod(os.TempDir(), 0o444))
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
data := []byte("PK\x03\x040000000PK\x01\x0200000" +
"0000000000000000000\x00" +
"\x00\x00\x00\x00\x00000000000000PK\x01" +
diff --git a/merge.go b/merge.go
index 0f57826e37..b87d7d2e95 100644
--- a/merge.go
+++ b/merge.go
@@ -11,7 +11,10 @@
package excelize
-import "strings"
+import (
+ "fmt"
+ "strings"
+)
// Rect gets merged cell rectangle coordinates sequence.
func (mc *xlsxMergeCell) Rect() ([]int, error) {
@@ -207,7 +210,7 @@ func flatMergedCells(ws *xlsxWorksheet, matrix [][]*xlsxMergeCell) error {
func (f *File) mergeOverlapCells(ws *xlsxWorksheet) error {
rows, cols, err := overlapRange(ws)
if err != nil {
- return err
+ return fmt.Errorf("merge overlapping cells: %w", err)
}
if rows == 0 || cols == 0 {
return nil
diff --git a/merge_test.go b/merge_test.go
index 597d4b58a8..c96fece746 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -70,7 +70,8 @@ func TestMergeCell(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestMergeCell.xlsx")))
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
@@ -79,12 +80,13 @@ func TestMergeCell(t *testing.T) {
}
func TestMergeCellOverlap(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.MergeCell("Sheet1", "A1", "C2"))
assert.NoError(t, f.MergeCell("Sheet1", "B2", "D3"))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestMergeCellOverlap.xlsx")))
- f, err := OpenFile(filepath.Join("test", "TestMergeCellOverlap.xlsx"))
+ f, err = OpenFile(filepath.Join("test", "TestMergeCellOverlap.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
}
@@ -167,7 +169,8 @@ func TestUnmergeCell(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnmergeCell.xlsx")))
assert.NoError(t, f.Close())
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
// Test unmerged area on not exists worksheet.
assert.EqualError(t, f.UnmergeCell("SheetN", "A1", "A1"), "sheet SheetN is not exist")
@@ -185,15 +188,15 @@ func TestUnmergeCell(t *testing.T) {
ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}
- assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), ErrParameterInvalid.Error())
+ assert.ErrorIs(t, f.UnmergeCell("Sheet1", "A2", "B3"), ErrRangeLength)
ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
- assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
+ assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), "merge overlapping cells: "+newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
func TestFlatMergedCells(t *testing.T) {
ws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}}
- assert.EqualError(t, flatMergedCells(ws, [][]*xlsxMergeCell{}), ErrParameterInvalid.Error())
+ assert.ErrorIs(t, flatMergedCells(ws, [][]*xlsxMergeCell{}), ErrRangeLength)
}
diff --git a/picture.go b/picture.go
index 44f1f3b3e8..a930359ec8 100644
--- a/picture.go
+++ b/picture.go
@@ -53,7 +53,11 @@ func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
// )
//
// func main() {
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// // Insert a picture.
// if err := f.AddPicture("Sheet1", "A2", "image.jpg", ""); err != nil {
// fmt.Println(err)
@@ -137,7 +141,11 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
// )
//
// func main() {
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
//
// file, err := ioutil.ReadFile("image.jpg")
// if err != nil {
@@ -152,6 +160,10 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
// }
//
func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
var drawingHyperlinkRID int
var hyperlinkType string
ext, ok := supportedImageTypes[extension]
@@ -175,16 +187,25 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
drawingID := f.countDrawings() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
- drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
+ drawingID, drawingXML, err = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
+ if err != nil {
+ return err
+ }
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl")
- drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
+ drawingRID, err := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
+ if err != nil {
+ return err
+ }
// Add picture with hyperlink.
if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" {
if formatSet.HyperlinkType == "External" {
hyperlinkType = formatSet.HyperlinkType
}
- drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
+ drawingHyperlinkRID, err = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
+ if err != nil {
+ return err
+ }
}
ws.Unlock()
err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet)
@@ -205,7 +226,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
name = strings.ToLower(sheet) + ".xml"
}
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
- sheetRels := f.relsReader(rels)
+ sheetRels, _ := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
}
@@ -284,7 +305,10 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
col--
row--
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
- content, cNvPrID := f.drawingParser(drawingXML)
+ content, cNvPrID, err := f.drawingParser(drawingXML)
+ if err != nil {
+ return err
+ }
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Positioning
from := xlsxFrom{}
@@ -368,6 +392,9 @@ func (f *File) addMedia(file []byte, ext string) string {
func (f *File) setContentTypePartImageExtensions() {
imageTypes := map[string]string{"jpeg": "image/", "png": "image/", "gif": "image/", "tiff": "image/", "emf": "image/x-", "wmf": "image/x-"}
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
for _, file := range content.Defaults {
@@ -386,6 +413,9 @@ func (f *File) setContentTypePartImageExtensions() {
func (f *File) setContentTypePartVMLExtensions() {
vml := false
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
for _, v := range content.Defaults {
@@ -433,6 +463,9 @@ func (f *File) addContentTypePart(index int, contentType string) {
s()
}
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
for _, v := range content.Overrides {
@@ -455,7 +488,7 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
name = strings.ToLower(sheet) + ".xml"
}
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
- sheetRels := f.relsReader(rels)
+ sheetRels, _ := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
}
@@ -550,14 +583,17 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
deTwoCellAnchor *decodeTwoCellAnchor
)
- wsDr, _ = f.drawingParser(drawingXML)
- if ret, buf = f.getPictureFromWsDr(row, col, drawingRelationships, wsDr); len(buf) > 0 {
+ wsDr, _, err = f.drawingParser(drawingXML)
+ if err != nil {
+ return
+ }
+ if ret, buf, err = f.getPictureFromWsDr(row, col, drawingRelationships, wsDr); len(buf) > 0 || err != nil {
return
}
deWsDr = new(decodeWsDr)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
Decode(deWsDr); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
err = nil
@@ -565,12 +601,16 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
deTwoCellAnchor = new(decodeTwoCellAnchor)
if err = f.xmlNewDecoder(strings.NewReader("" + anchor.Content + "")).
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ err = fmt.Errorf("xml decode error: %w", err)
return
}
- if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil {
+ err = nil
+ if deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil {
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
- drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
+ drawRel, err = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
+ if err != nil {
+ return
+ }
if _, ok = supportedImageTypes[filepath.Ext(drawRel.Target)]; ok {
ret = filepath.Base(drawRel.Target)
if buffer, _ := f.Pkg.Load(strings.ReplaceAll(drawRel.Target, "..", "xl")); buffer != nil {
@@ -587,7 +627,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
// getPictureFromWsDr provides a function to get picture base name and raw
// content in worksheet drawing by given coordinates and drawing
// relationships.
-func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (ret string, buf []byte) {
+func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (ret string, buf []byte, err error) {
var (
ok bool
anchor *xdrCellAnchor
@@ -598,8 +638,11 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD
for _, anchor = range wsDr.TwoCellAnchor {
if anchor.From != nil && anchor.Pic != nil {
if anchor.From.Col == col && anchor.From.Row == row {
- if drawRel = f.getDrawingRelationships(drawingRelationships,
- anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
+ drawRel, err = f.getDrawingRelationships(drawingRelationships, anchor.Pic.BlipFill.Blip.Embed)
+ if err != nil {
+ return
+ }
+ if drawRel != nil {
if _, ok = supportedImageTypes[filepath.Ext(drawRel.Target)]; ok {
ret = filepath.Base(drawRel.Target)
if buffer, _ := f.Pkg.Load(strings.ReplaceAll(drawRel.Target, "..", "xl")); buffer != nil {
@@ -617,29 +660,40 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD
// getDrawingRelationships provides a function to get drawing relationships
// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
// relationship ID.
-func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
- if drawingRels := f.relsReader(rels); drawingRels != nil {
+func (f *File) getDrawingRelationships(rels, rID string) (*xlsxRelationship, error) {
+ drawingRels, err := f.relsReader(rels)
+ if err != nil {
+ return nil, err
+ }
+ if drawingRels != nil {
drawingRels.Lock()
defer drawingRels.Unlock()
for _, v := range drawingRels.Relationships {
if v.ID == rID {
- return &v
+ return &v, err
}
}
}
- return nil
+ return nil, err
}
// drawingsWriter provides a function to save xl/drawings/drawing%d.xml after
// serialize structure.
-func (f *File) drawingsWriter() {
+func (f *File) drawingsWriter() (firstError error) {
f.Drawings.Range(func(path, d interface{}) bool {
if d != nil {
- v, _ := xml.Marshal(d.(*xlsxWsDr))
- f.saveFileList(path.(string), v)
+ v, err := xml.Marshal(d.(*xlsxWsDr))
+ if err != nil {
+ if firstError == nil {
+ firstError = err
+ }
+ } else {
+ f.saveFileList(path.(string), v)
+ }
}
return true
})
+ return
}
// drawingResize calculate the height and width after resizing.
diff --git a/picture_test.go b/picture_test.go
index 60c6ac17cc..b06c33f487 100644
--- a/picture_test.go
+++ b/picture_test.go
@@ -19,7 +19,8 @@ import (
)
func BenchmarkAddPictureFromBytes(b *testing.B) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(b, err)
imgFile, err := ioutil.ReadFile(filepath.Join("test", "images", "excel.png"))
if err != nil {
b.Error("unable to load image for benchmark")
@@ -134,8 +135,10 @@ func TestGetPicture(t *testing.T) {
assert.Empty(t, file)
assert.Empty(t, raw)
- f.getDrawingRelationships("xl/worksheets/_rels/sheet1.xml.rels", "rId8")
- f.getDrawingRelationships("", "")
+ _, err = f.getDrawingRelationships("xl/worksheets/_rels/sheet1.xml.rels", "rId8")
+ assert.NoError(t, err)
+ _, err = f.getDrawingRelationships("", "")
+ assert.NoError(t, err)
f.getSheetRelationshipsTargetByID("", "")
f.deleteSheetRelationships("", "")
@@ -160,7 +163,8 @@ func TestGetPicture(t *testing.T) {
assert.NoError(t, f.Close())
// Test get picture from none drawing worksheet.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
file, raw, err = f.GetPicture("Sheet1", "F22")
assert.NoError(t, err)
assert.Empty(t, file)
@@ -174,12 +178,14 @@ func TestGetPicture(t *testing.T) {
func TestAddDrawingPicture(t *testing.T) {
// testing addDrawingPicture with illegal cell coordinates.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", 0, 0, 0, 0, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
func TestAddPictureFromBytes(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
imgFile, err := ioutil.ReadFile("logo.png")
assert.NoError(t, err, "Unable to load logo for test")
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), "", "logo", ".png", imgFile))
@@ -208,13 +214,16 @@ func TestDeletePicture(t *testing.T) {
assert.EqualError(t, f.DeletePicture("Sheet1", ""), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error())
assert.NoError(t, f.Close())
// Test delete picture on no chart worksheet.
- assert.NoError(t, NewFile().DeletePicture("Sheet1", "A1"))
+ xlsxFile, err := NewFile()
+ assert.NoError(t, err)
+ assert.NoError(t, xlsxFile.DeletePicture("Sheet1", "A1"))
}
func TestDrawingResize(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test calculate drawing resize on not exists worksheet.
- _, _, _, _, err := f.drawingResize("SheetN", "A1", 1, 1, nil)
+ _, _, _, _, err = f.drawingResize("SheetN", "A1", 1, 1, nil)
assert.EqualError(t, err, "sheet SheetN is not exist")
// Test calculate drawing resize with invalid coordinates.
_, _, _, _, err = f.drawingResize("Sheet1", "", 1, 1, nil)
@@ -222,5 +231,5 @@ func TestDrawingResize(t *testing.T) {
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
- assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
+ assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`), "merge overlapping cells: "+newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
diff --git a/pivotTable.go b/pivotTable.go
index 10c48cef3a..8930d27bef 100644
--- a/pivotTable.go
+++ b/pivotTable.go
@@ -95,7 +95,11 @@ type PivotTableField struct {
// )
//
// func main() {
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// // Create some data in a sheet
// month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
// year := []int{2017, 2018, 2019}
@@ -131,6 +135,10 @@ type PivotTableField struct {
// }
//
func (f *File) AddPivotTable(opt *PivotTableOption) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
// parameter validation
_, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opt)
if err != nil {
@@ -148,23 +156,36 @@ func (f *File) AddPivotTable(opt *PivotTableOption) error {
return err
}
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return err
+ }
// workbook pivot cache
- workBookPivotCacheRID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipPivotCache, fmt.Sprintf("/xl/pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
+ workBookPivotCacheRID, err := f.addRels(wrp, SourceRelationshipPivotCache, fmt.Sprintf("/xl/pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
+ if err != nil {
+ return err
+ }
cacheID := f.addWorkbookPivotCache(workBookPivotCacheRID)
pivotCacheRels := "xl/pivotTables/_rels/pivotTable" + strconv.Itoa(pivotTableID) + ".xml.rels"
// rId not used
- _ = f.addRels(pivotCacheRels, SourceRelationshipPivotCache, fmt.Sprintf("../pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
+ _, err = f.addRels(pivotCacheRels, SourceRelationshipPivotCache, fmt.Sprintf("../pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
+ if err != nil {
+ return err
+ }
err = f.addPivotTable(cacheID, pivotTableID, pivotTableXML, opt)
if err != nil {
return err
}
pivotTableSheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(pivotTableSheetPath, "xl/worksheets/") + ".rels"
- f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
+ _, err = f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
+ if err != nil {
+ return err
+ }
f.addContentTypePart(pivotTableID, "pivotTable")
f.addContentTypePart(pivotCacheID, "pivotCache")
- return nil
+ return err
}
// parseFormatPivotTableSet provides a function to validate pivot table
@@ -307,6 +328,9 @@ func (f *File) addPivotCache(pivotCacheXML string, opt *PivotTableOption) error
}
pc.CacheFields.Count = len(pc.CacheFields.CacheField)
pivotCache, err := xml.Marshal(pc)
+ if err != nil {
+ return err
+ }
f.saveFileList(pivotCacheXML, pivotCache)
return err
}
@@ -386,6 +410,9 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, op
_ = f.addPivotDataFields(&pt, opt)
pivotTable, err := xml.Marshal(pt)
+ if err != nil {
+ return err
+ }
f.saveFileList(pivotTableXML, pivotTable)
return err
}
@@ -699,6 +726,9 @@ func (f *File) getPivotTableFieldOptions(name string, fields []PivotTableField)
// addWorkbookPivotCache add the association ID of the pivot cache in workbook.xml.
func (f *File) addWorkbookPivotCache(RID int) int {
wb := f.workbookReader()
+ if wb == nil {
+ return 0
+ }
if wb.PivotCaches == nil {
wb.PivotCaches = &xlsxPivotCaches{}
}
diff --git a/pivotTable_test.go b/pivotTable_test.go
index adba2eb197..70d1df63ee 100644
--- a/pivotTable_test.go
+++ b/pivotTable_test.go
@@ -11,7 +11,8 @@ import (
)
func TestAddPivotTable(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Create some data in a sheet
month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
year := []int{2017, 2018, 2019}
@@ -226,11 +227,11 @@ func TestAddPivotTable(t *testing.T) {
}))
// Test adjust range with invalid range
- _, _, err := f.adjustRange("")
+ _, _, err = f.adjustRange("")
assert.EqualError(t, err, ErrParameterRequired.Error())
// Test adjust range with incorrect range
_, _, err = f.adjustRange("sheet1!")
- assert.EqualError(t, err, "parameter is invalid")
+ assert.ErrorIs(t, err, ErrRangeLength)
// Test get pivot fields order with empty data range
_, err = f.getPivotFieldsOrder(&PivotTableOption{})
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
@@ -262,7 +263,8 @@ func TestAddPivotTable(t *testing.T) {
}
func TestAddPivotRowFields(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test invalid data range
assert.EqualError(t, f.addPivotRowFields(&xlsxPivotTableDefinition{}, &PivotTableOption{
DataRange: "Sheet1!$A$1:$A$1",
@@ -270,7 +272,8 @@ func TestAddPivotRowFields(t *testing.T) {
}
func TestAddPivotPageFields(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test invalid data range
assert.EqualError(t, f.addPivotPageFields(&xlsxPivotTableDefinition{}, &PivotTableOption{
DataRange: "Sheet1!$A$1:$A$1",
@@ -278,7 +281,8 @@ func TestAddPivotPageFields(t *testing.T) {
}
func TestAddPivotDataFields(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test invalid data range
assert.EqualError(t, f.addPivotDataFields(&xlsxPivotTableDefinition{}, &PivotTableOption{
DataRange: "Sheet1!$A$1:$A$1",
@@ -286,7 +290,8 @@ func TestAddPivotDataFields(t *testing.T) {
}
func TestAddPivotColFields(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test invalid data range
assert.EqualError(t, f.addPivotColFields(&xlsxPivotTableDefinition{}, &PivotTableOption{
DataRange: "Sheet1!$A$1:$A$1",
@@ -295,13 +300,15 @@ func TestAddPivotColFields(t *testing.T) {
}
func TestGetPivotFieldsOrder(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test get pivot fields order with not exist worksheet
- _, err := f.getPivotFieldsOrder(&PivotTableOption{DataRange: "SheetN!$A$1:$E$31"})
+ _, err = f.getPivotFieldsOrder(&PivotTableOption{DataRange: "SheetN!$A$1:$E$31"})
assert.EqualError(t, err, "sheet SheetN is not exist")
}
func TestGetPivotTableFieldName(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.getPivotTableFieldName("-", []PivotTableField{})
}
diff --git a/rows.go b/rows.go
index f83d425382..5cf99d85ac 100644
--- a/rows.go
+++ b/rows.go
@@ -17,7 +17,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "log"
"math"
"math/big"
"os"
@@ -131,7 +130,14 @@ func (rows *Rows) Columns(opts ...Options) ([]string, error) {
}
var rowIterator rowXMLIterator
var token xml.Token
- rows.rawCellValue, rows.sst = parseOptions(opts...).RawCellValue, rows.f.sharedStringsReader()
+ rows.rawCellValue = parseOptions(opts...).RawCellValue
+
+ var err error
+ rows.sst, err = rows.f.sharedStringsReader()
+ if err != nil {
+ return nil, err
+ }
+
for {
if rows.token != nil {
token = rows.token
@@ -242,7 +248,10 @@ func (f *File) Rows(sheet string) (*Rows, error) {
worksheet.Lock()
defer worksheet.Unlock()
// flush data
- output, _ := xml.Marshal(worksheet)
+ output, err := xml.Marshal(worksheet)
+ if err != nil {
+ return nil, err
+ }
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
var err error
@@ -386,18 +395,26 @@ func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
// sharedStringsReader provides a function to get the pointer to the structure
// after deserialization of xl/sharedStrings.xml.
-func (f *File) sharedStringsReader() *xlsxSST {
+func (f *File) sharedStringsReader() (*xlsxSST, error) {
var err error
f.Lock()
defer f.Unlock()
- relPath := f.getWorkbookRelsPath()
+ relPath, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return nil, err
+ }
if f.SharedStrings == nil {
+ if !f.IsValid() {
+ return nil, ErrIncompleteFileSetup
+ }
+
var sharedStrings xlsxSST
ss := f.readXML(defaultXMLPathSharedStrings)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
Decode(&sharedStrings); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return nil, fmt.Errorf("xml decode error: %s", err)
}
+ err = nil
if sharedStrings.Count == 0 {
sharedStrings.Count = len(sharedStrings.SI)
}
@@ -411,23 +428,29 @@ func (f *File) sharedStringsReader() *xlsxSST {
}
}
f.addContentTypePart(0, "sharedStrings")
- rels := f.relsReader(relPath)
+ rels, err := f.relsReader(relPath)
+ if err != nil {
+ return f.SharedStrings, err
+ }
for _, rel := range rels.Relationships {
if rel.Target == "/xl/sharedStrings.xml" {
- return f.SharedStrings
+ return f.SharedStrings, nil
}
}
// Update workbook.xml.rels
f.addRels(relPath, SourceRelationshipSharedStrings, "/xl/sharedStrings.xml", "")
}
- return f.SharedStrings
+ return f.SharedStrings, nil
}
// getValueFrom return a value from a column/row cell, this function is
// intended to be used with for range on rows an argument with the spreadsheet
// opened file.
func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
+ if !f.IsValid() {
+ return "", ErrIncompleteFileSetup
+ }
f.Lock()
defer f.Unlock()
switch c.T {
diff --git a/rows_test.go b/rows_test.go
index 014b2d853f..1f6e2a0c70 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -82,7 +82,9 @@ func TestRowsIterator(t *testing.T) {
assert.NoError(t, f.Close())
// Valued cell sparse distribution test
- f, sheetName, rowCount, expectedNumRow = NewFile(), "Sheet1", 0, 3
+ f, err = NewFile()
+ assert.NoError(t, err)
+ sheetName, rowCount, expectedNumRow = "Sheet1", 0, 3
cells := []string{"C1", "E1", "A3", "B3", "C3", "D3", "E3"}
for _, cell := range cells {
assert.NoError(t, f.SetCellValue(sheetName, cell, 1))
@@ -107,12 +109,13 @@ func TestRowsError(t *testing.T) {
}
func TestRowHeight(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
assert.EqualError(t, f.SetRowHeight(sheet1, 0, defaultRowHeightPixels+1.0), newInvalidRowNumberError(0).Error())
- _, err := f.GetRowHeight("Sheet1", 0)
+ _, err = f.GetRowHeight("Sheet1", 0)
assert.EqualError(t, err, newInvalidRowNumberError(0).Error())
assert.NoError(t, f.SetRowHeight(sheet1, 1, 111.0))
@@ -165,7 +168,8 @@ func TestRowHeight(t *testing.T) {
}
func TestColumns(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
rows, err := f.Rows("Sheet1")
assert.NoError(t, err)
@@ -198,7 +202,8 @@ func TestColumns(t *testing.T) {
}
func TestSharedStringsReader(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
f.sharedStringsReader()
si := xlsxSI{}
@@ -232,7 +237,8 @@ func TestRowVisibility(t *testing.T) {
}
func TestRemoveRow(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
r, err := f.workSheetReader(sheet1)
assert.NoError(t, err)
@@ -293,7 +299,8 @@ func TestRemoveRow(t *testing.T) {
}
func TestInsertRow(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
r, err := f.workSheetReader(sheet1)
assert.NoError(t, err)
@@ -326,7 +333,8 @@ func TestInsertRow(t *testing.T) {
// for insert workflow to be constant to avoid side effect with functions
// related to internal structure.
func TestInsertRowInEmptyFile(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheet1 := f.GetSheetName(0)
r, err := f.workSheetReader(sheet1)
assert.NoError(t, err)
@@ -353,7 +361,8 @@ func TestDuplicateRowFromSingleRow(t *testing.T) {
}
t.Run("FromSingleRow", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellStr(sheet, "A1", cells["A1"]))
assert.NoError(t, f.SetCellStr(sheet, "B1", cells["B1"]))
@@ -406,7 +415,8 @@ func TestDuplicateRowUpdateDuplicatedRows(t *testing.T) {
}
t.Run("UpdateDuplicatedRows", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellStr(sheet, "A1", cells["A1"]))
assert.NoError(t, f.SetCellStr(sheet, "B1", cells["B1"]))
@@ -446,7 +456,8 @@ func TestDuplicateRowFirstOfMultipleRows(t *testing.T) {
}
newFileWithDefaults := func() *File {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, cell, val))
}
@@ -482,7 +493,8 @@ func TestDuplicateRowZeroWithNoRows(t *testing.T) {
outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
t.Run("ZeroWithNoRows", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.DuplicateRow(sheet, 0), newInvalidRowNumberError(0).Error())
@@ -524,7 +536,8 @@ func TestDuplicateRowMiddleRowOfEmptyFile(t *testing.T) {
outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
t.Run("MiddleRowOfEmptyFile", func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.DuplicateRow(sheet, 99))
@@ -560,7 +573,8 @@ func TestDuplicateRowWithLargeOffsetToMiddleOfData(t *testing.T) {
}
newFileWithDefaults := func() *File {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, cell, val))
}
@@ -605,7 +619,8 @@ func TestDuplicateRowWithLargeOffsetToEmptyRows(t *testing.T) {
}
newFileWithDefaults := func() *File {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, cell, val))
}
@@ -650,7 +665,8 @@ func TestDuplicateRowInsertBefore(t *testing.T) {
}
newFileWithDefaults := func() *File {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, cell, val))
}
@@ -697,7 +713,8 @@ func TestDuplicateRowInsertBeforeWithLargeOffset(t *testing.T) {
}
newFileWithDefaults := func() *File {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, cell, val))
}
@@ -743,7 +760,8 @@ func TestDuplicateRowInsertBeforeWithMergeCells(t *testing.T) {
}
newFileWithDefaults := func() *File {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for cell, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, cell, val))
}
@@ -796,7 +814,8 @@ func TestDuplicateRowInvalidRowNum(t *testing.T) {
for _, row := range invalidIndexes {
name := fmt.Sprintf("%d", row)
t.Run(name, func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for col, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, col, val))
}
@@ -818,7 +837,8 @@ func TestDuplicateRowInvalidRowNum(t *testing.T) {
for _, row2 := range invalidIndexes {
name := fmt.Sprintf("[%d,%d]", row1, row2)
t.Run(name, func(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
for col, val := range cells {
assert.NoError(t, f.SetCellStr(sheet, col, val))
}
@@ -839,7 +859,9 @@ func TestDuplicateRowInvalidRowNum(t *testing.T) {
}
func TestDuplicateRowTo(t *testing.T) {
- f, sheetName := NewFile(), "Sheet1"
+ f, err := NewFile()
+ assert.NoError(t, err)
+ sheetName := "Sheet1"
// Test duplicate row with invalid target row number
assert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 0))
// Test duplicate row with equal source and target row number
@@ -867,7 +889,8 @@ func TestDuplicateMergeCells(t *testing.T) {
func TestGetValueFromInlineStr(t *testing.T) {
c := &xlsxC{T: "inlineStr"}
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
d := &xlsxSST{}
val, err := c.getValueFrom(f, d, false)
assert.NoError(t, err)
@@ -876,7 +899,8 @@ func TestGetValueFromInlineStr(t *testing.T) {
func TestGetValueFromNumber(t *testing.T) {
c := &xlsxC{T: "n"}
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
d := &xlsxSST{}
for input, expected := range map[string]string{
"2.2.": "2.2.",
@@ -901,12 +925,14 @@ func TestErrSheetNotExistError(t *testing.T) {
}
func TestCheckRow(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+`12345
`))
- _, err := f.GetRows("Sheet1")
+ _, err = f.GetRows("Sheet1")
assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", false))
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+`12345
`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
delete(f.checked, "xl/worksheets/sheet1.xml")
@@ -914,7 +940,8 @@ func TestCheckRow(t *testing.T) {
}
func TestSetRowStyle(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
style1, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#63BE7B"],"pattern":1}}`)
assert.NoError(t, err)
style2, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`)
diff --git a/shape.go b/shape.go
index ddf9e317c6..82031d6c70 100644
--- a/shape.go
+++ b/shape.go
@@ -279,6 +279,10 @@ func parseFormatShapeSet(formatSet string) (*formatShape, error) {
// wavyDbl
//
func (f *File) AddShape(sheet, cell, format string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
formatSet, err := parseFormatShapeSet(format)
if err != nil {
return err
@@ -301,7 +305,10 @@ func (f *File) AddShape(sheet, cell, format string) error {
} else {
// Add first shape for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ rID, err := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+ if err != nil {
+ return err
+ }
f.addSheetDrawing(sheet, rID)
f.addSheetNameSpace(sheet, SourceRelationship)
}
@@ -349,7 +356,10 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.Format.OffsetX, formatSet.Format.OffsetY,
width, height)
- content, cNvPrID := f.drawingParser(drawingXML)
+ content, cNvPrID, err := f.drawingParser(drawingXML)
+ if err != nil {
+ return err
+ }
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Format.Positioning
from := xlsxFrom{}
diff --git a/shape_test.go b/shape_test.go
index 2f005894d0..79732f2763 100644
--- a/shape_test.go
+++ b/shape_test.go
@@ -60,7 +60,8 @@ func TestAddShape(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape1.xlsx")))
// Test add first shape for given sheet.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.AddShape("Sheet1", "A1", `{
"type": "ellipseRibbon",
"color":
diff --git a/sheet.go b/sheet.go
index 45b724f7bb..b5864f6645 100644
--- a/sheet.go
+++ b/sheet.go
@@ -18,7 +18,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "log"
"os"
"path"
"path/filepath"
@@ -38,13 +37,15 @@ import (
// (spreadsheet) after it appended. Note that the worksheet names are not
// case-sensitive, when creating a new spreadsheet file, the default
// worksheet named `Sheet1` will be created.
-func (f *File) NewSheet(name string) int {
+func (f *File) NewSheet(name string) (int, error) {
+ if !f.IsValid() {
+ return -1, ErrIncompleteFileSetup
+ }
// Check if the worksheet already exists
index := f.GetSheetIndex(name)
if index != -1 {
- return index
+ return index, nil
}
- f.DeleteSheet(name)
f.SheetCount++
wb := f.workbookReader()
sheetID := 0
@@ -61,49 +62,67 @@ func (f *File) NewSheet(name string) int {
// Create new sheet /xl/worksheets/sheet%d.xml
f.setSheet(sheetID, name)
// Update workbook.xml.rels
- rID := f.addRels(f.getWorkbookRelsPath(), SourceRelationshipWorkSheet, fmt.Sprintf("/xl/worksheets/sheet%d.xml", sheetID), "")
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return index, err
+ }
+ rID, err := f.addRels(wrp, SourceRelationshipWorkSheet, fmt.Sprintf("/xl/worksheets/sheet%d.xml", sheetID), "")
+ if err != nil {
+ return index, err
+ }
// Update workbook.xml
f.setWorkbook(name, sheetID, rID)
- return f.GetSheetIndex(name)
+ return f.GetSheetIndex(name), err
}
-// contentTypesReader provides a function to get the pointer to the
-// [Content_Types].xml structure after deserialization.
-func (f *File) contentTypesReader() *xlsxTypes {
- var err error
-
+// NewContentTypesReader provides a function to get the pointer to ContentTypes.
+func (f *File) NewContentTypesReader() (*xlsxTypes, error) {
if f.ContentTypes == nil {
f.ContentTypes = new(xlsxTypes)
f.ContentTypes.Lock()
defer f.ContentTypes.Unlock()
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).
Decode(f.ContentTypes); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return f.ContentTypes, fmt.Errorf("xml decode error: %w", err)
}
}
+ return f.ContentTypes, nil
+}
+
+// contentTypesReader provides a function to get the pointer to ContentTypes.
+func (f *File) contentTypesReader() *xlsxTypes {
return f.ContentTypes
}
// contentTypesWriter provides a function to save [Content_Types].xml after
// serialize structure.
-func (f *File) contentTypesWriter() {
+func (f *File) contentTypesWriter() error {
if f.ContentTypes != nil {
- output, _ := xml.Marshal(f.ContentTypes)
+ output, err := xml.Marshal(f.ContentTypes)
+ if err != nil {
+ return err
+ }
f.saveFileList(defaultXMLPathContentTypes, output)
}
+ return nil
}
// getWorksheetPath construct a target XML as xl/worksheets/sheet%d by split
// path, compatible with different types of relative paths in
// workbook.xml.rels, for example: worksheets/sheet%d.xml
// and /xl/worksheets/sheet%d.xml
-func (f *File) getWorksheetPath(relTarget string) (path string) {
+func (f *File) getWorksheetPath(relTarget string) (path string, err error) {
+ var wrp string
+ wrp, err = f.getWorkbookPath()
+ if err != nil {
+ return
+ }
path = filepath.ToSlash(strings.TrimPrefix(
- strings.ReplaceAll(filepath.Clean(fmt.Sprintf("%s/%s", filepath.Dir(f.getWorkbookPath()), relTarget)), "\\", "/"), "/"))
+ strings.ReplaceAll(filepath.Clean(fmt.Sprintf("%s/%s", filepath.Dir(wrp), relTarget)), "\\", "/"), "/"))
if strings.HasPrefix(relTarget, "/") {
path = filepath.ToSlash(strings.TrimPrefix(strings.ReplaceAll(filepath.Clean(relTarget), "\\", "/"), "/"))
}
- return path
+ return
}
// mergeExpandedCols merge expanded columns.
@@ -139,7 +158,7 @@ func (f *File) mergeExpandedCols(ws *xlsxWorksheet) {
// workSheetWriter provides a function to save xl/worksheets/sheet%d.xml after
// serialize structure.
-func (f *File) workSheetWriter() {
+func (f *File) workSheetWriter() (firstError error) {
var arr []byte
buffer := bytes.NewBuffer(arr)
encoder := xml.NewEncoder(buffer)
@@ -147,7 +166,11 @@ func (f *File) workSheetWriter() {
if ws != nil {
sheet := ws.(*xlsxWorksheet)
if sheet.MergeCells != nil && len(sheet.MergeCells.Cells) > 0 {
- _ = f.mergeOverlapCells(sheet)
+ err := f.mergeOverlapCells(sheet)
+ if err != nil && firstError == nil {
+ sheetName := f.getSheetNameBySheetXMLPath(p.(string))
+ firstError = fmt.Errorf("error in sheet %q: %w", sheetName, err)
+ }
}
if sheet.Cols != nil && len(sheet.Cols.Col) > 0 {
f.mergeExpandedCols(sheet)
@@ -177,6 +200,7 @@ func (f *File) workSheetWriter() {
}
return true
})
+ return
}
// trimCell provides a function to trim blank cells which created by fillColumns.
@@ -203,6 +227,9 @@ func trimCell(column []xlsxC) []xlsxC {
// type of the spreadsheet.
func (f *File) setContentTypes(partName, contentType string) {
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
content.Overrides = append(content.Overrides, xlsxOverride{
@@ -227,17 +254,24 @@ func (f *File) setSheet(index int, name string) {
// relsWriter provides a function to save relationships after
// serialize structure.
-func (f *File) relsWriter() {
+func (f *File) relsWriter() (firstError error) {
f.Relationships.Range(func(path, rel interface{}) bool {
if rel != nil {
- output, _ := xml.Marshal(rel.(*xlsxRelationships))
- if strings.HasPrefix(path.(string), "xl/worksheets/sheet/rels/sheet") {
- output = f.replaceNameSpaceBytes(path.(string), output)
+ output, err := xml.Marshal(rel.(*xlsxRelationships))
+ if err != nil {
+ if firstError == nil {
+ firstError = err
+ }
+ } else {
+ if strings.HasPrefix(path.(string), "xl/worksheets/sheet/rels/sheet") {
+ output = f.replaceNameSpaceBytes(path.(string), output)
+ }
+ f.saveFileList(path.(string), replaceRelationshipsBytes(output))
}
- f.saveFileList(path.(string), replaceRelationshipsBytes(output))
}
return true
})
+ return
}
// setAppXML update docProps/app.xml file of XML.
@@ -259,6 +293,9 @@ func replaceRelationshipsBytes(content []byte) []byte {
// ID returned by function GetSheetMap(). It should be greater or equal to 0
// and less than the total worksheet numbers.
func (f *File) SetActiveSheet(index int) {
+ if !f.IsValid() {
+ return
+ }
if index < 0 {
index = 0
}
@@ -349,6 +386,9 @@ func (f *File) SetSheetName(oldName, newName string) {
return
}
content := f.workbookReader()
+ if content == nil {
+ return
+ }
for k, v := range content.Sheets.Sheet {
if v.Name == oldName {
content.Sheets.Sheet[k].Name = newName
@@ -437,12 +477,27 @@ func (f *File) GetSheetList() (list []string) {
// getSheetMap provides a function to get worksheet name and XML file path map
// of the spreadsheet.
-func (f *File) getSheetMap() map[string]string {
+func (f *File) getSheetMap() (map[string]string, error) {
maps := map[string]string{}
- for _, v := range f.workbookReader().Sheets.Sheet {
- for _, rel := range f.relsReader(f.getWorkbookRelsPath()).Relationships {
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return maps, err
+ }
+ wbRels, err := f.relsReader(wrp)
+ if err != nil {
+ return maps, err
+ }
+ wb := f.workbookReader()
+ if wb == nil {
+ return maps, ErrIncompleteFileSetup
+ }
+ for _, v := range wb.Sheets.Sheet {
+ for _, rel := range wbRels.Relationships {
if rel.ID == v.ID {
- sheetXMLPath := f.getWorksheetPath(rel.Target)
+ sheetXMLPath, err := f.getWorksheetPath(rel.Target)
+ if err != nil {
+ return maps, err
+ }
if _, ok := f.Pkg.Load(sheetXMLPath); ok {
maps[v.Name] = sheetXMLPath
}
@@ -452,7 +507,23 @@ func (f *File) getSheetMap() map[string]string {
}
}
}
- return maps
+ return maps, nil
+}
+
+// getSheetNameBySheetXMLPath provides a function to get worksheet name
+// by checking the sheet map against the given XML file path.
+func (f *File) getSheetNameBySheetXMLPath(sheetXMLPath string) (sheet string) {
+ sheetMap, err := f.getSheetMap()
+ if err != nil {
+ return
+ }
+ for k, v := range sheetMap {
+ if v == sheetXMLPath {
+ sheet = k
+ return
+ }
+ }
+ return
}
// SetSheetBackground provides a function to set background picture by given
@@ -467,10 +538,16 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
if !ok {
return ErrImgExt
}
- file, _ := ioutil.ReadFile(filepath.Clean(picture))
+ file, err := ioutil.ReadFile(filepath.Clean(picture))
+ if err != nil {
+ return err
+ }
name := f.addMedia(file, ext)
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
+ rID, err := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
+ if err != nil {
+ return err
+ }
f.addSheetPicture(sheet, rID)
f.addSheetNameSpace(sheet, SourceRelationship)
f.setContentTypePartImageExtensions()
@@ -484,13 +561,23 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
// referenced value of the deleted worksheet, it will cause a file error when
// you open it. This function will be invalid when only the one worksheet is
// left.
-func (f *File) DeleteSheet(name string) {
+func (f *File) DeleteSheet(name string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
if f.SheetCount == 1 || f.GetSheetIndex(name) == -1 {
- return
+ return nil
}
sheetName := trimSheetName(name)
wb := f.workbookReader()
- wbRels := f.relsReader(f.getWorkbookRelsPath())
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return err
+ }
+ wbRels, err := f.relsReader(wrp)
+ if err != nil {
+ return err
+ }
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
deleteLocalSheetID := f.GetSheetIndex(name)
deleteAndAdjustDefinedNames(wb, deleteLocalSheetID)
@@ -505,12 +592,18 @@ func (f *File) DeleteSheet(name string) {
if wbRels != nil {
for _, rel := range wbRels.Relationships {
if rel.ID == sheet.ID {
- sheetXML = f.getWorksheetPath(rel.Target)
+ sheetXML, err = f.getWorksheetPath(rel.Target)
+ if err != nil {
+ return err
+ }
rels = "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[sheetName], "xl/worksheets/") + ".rels"
}
}
}
- target := f.deleteSheetFromWorkbookRels(sheet.ID)
+ target, err := f.deleteSheetFromWorkbookRels(sheet.ID)
+ if err != nil {
+ return err
+ }
f.deleteSheetFromContentTypes(target)
f.deleteCalcChain(sheet.SheetID, "")
delete(f.sheetMap, sheet.Name)
@@ -522,6 +615,7 @@ func (f *File) DeleteSheet(name string) {
f.SheetCount--
}
f.SetActiveSheet(f.GetSheetIndex(activeSheetName))
+ return err
}
// deleteAndAdjustDefinedNames delete and adjust defined name in the workbook
@@ -546,17 +640,24 @@ func deleteAndAdjustDefinedNames(wb *xlsxWorkbook, deleteLocalSheetID int) {
// deleteSheetFromWorkbookRels provides a function to remove worksheet
// relationships by given relationships ID in the file workbook.xml.rels.
-func (f *File) deleteSheetFromWorkbookRels(rID string) string {
- content := f.relsReader(f.getWorkbookRelsPath())
+func (f *File) deleteSheetFromWorkbookRels(rID string) (string, error) {
+ wrp, err := f.getWorkbookRelsPath()
+ if err != nil {
+ return "", err
+ }
+ content, err := f.relsReader(wrp)
+ if err != nil {
+ return "", err
+ }
content.Lock()
defer content.Unlock()
for k, v := range content.Relationships {
if v.ID == rID {
content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...)
- return v.Target
+ return v.Target, nil
}
}
- return ""
+ return "", nil
}
// deleteSheetFromContentTypes provides a function to remove worksheet
@@ -566,6 +667,9 @@ func (f *File) deleteSheetFromContentTypes(target string) {
target = "/xl/" + target
}
content := f.contentTypesReader()
+ if content == nil {
+ return
+ }
content.Lock()
defer content.Unlock()
for k, v := range content.Overrides {
@@ -634,6 +738,9 @@ func (f *File) copySheet(from, to int) error {
// err := f.SetSheetVisible("Sheet1", false)
//
func (f *File) SetSheetVisible(name string, visible bool) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
name = trimSheetName(name)
content := f.workbookReader()
if visible {
@@ -801,6 +908,9 @@ func (f *File) SetPanes(sheet, panes string) error {
//
func (f *File) GetSheetVisible(name string) bool {
content := f.workbookReader()
+ if content == nil {
+ return false
+ }
visible := false
for k, v := range content.Sheets.Sheet {
if v.Name == trimSheetName(name) {
@@ -841,7 +951,10 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
}
if ws, ok := f.Sheet.Load(name); ok && ws != nil {
// flush data
- output, _ := xml.Marshal(ws.(*xlsxWorksheet))
+ output, err := xml.Marshal(ws.(*xlsxWorksheet))
+ if err != nil {
+ return result, err
+ }
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
return f.searchSheet(name, value, regSearch)
@@ -856,7 +969,10 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string,
d *xlsxSST
)
- d = f.sharedStringsReader()
+ d, err = f.sharedStringsReader()
+ if err != nil {
+ return
+ }
decoder := f.xmlNewDecoder(bytes.NewReader(f.readBytes(name)))
for {
var token xml.Token
@@ -878,8 +994,15 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string,
}
if inElement == "c" {
colCell := xlsxC{}
- _ = decoder.DecodeElement(&colCell, &xmlElement)
- val, _ := colCell.getValueFrom(f, d, false)
+ err = decoder.DecodeElement(&colCell, &xmlElement)
+ if err != nil {
+ return
+ }
+ var val string
+ val, err = colCell.getValueFrom(f, d, false)
+ if err != nil {
+ return
+ }
if regSearch {
regex := regexp.MustCompile(value)
if !regex.MatchString(val) {
@@ -892,11 +1015,11 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string,
}
cellCol, _, err = CellNameToCoordinates(colCell.R)
if err != nil {
- return result, err
+ return
}
cellName, err = CoordinatesToCellName(cellCol, row)
if err != nil {
- return result, err
+ return
}
result = append(result, cellName)
}
@@ -1520,6 +1643,9 @@ func (f *File) GetPageLayout(sheet string, opts ...PageLayoutOptionPtr) error {
//
func (f *File) SetDefinedName(definedName *DefinedName) error {
wb := f.workbookReader()
+ if wb == nil {
+ return ErrIncompleteFileSetup
+ }
d := xlsxDefinedName{
Name: definedName.Name,
Comment: definedName.Comment,
@@ -1560,6 +1686,9 @@ func (f *File) SetDefinedName(definedName *DefinedName) error {
//
func (f *File) DeleteDefinedName(definedName *DefinedName) error {
wb := f.workbookReader()
+ if wb == nil {
+ return ErrIncompleteFileSetup
+ }
if wb.DefinedNames != nil {
for idx, dn := range wb.DefinedNames.DefinedName {
scope := "Workbook"
@@ -1584,6 +1713,9 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
func (f *File) GetDefinedName() []DefinedName {
var definedNames []DefinedName
wb := f.workbookReader()
+ if wb == nil {
+ return definedNames
+ }
if wb.DefinedNames != nil {
for _, dn := range wb.DefinedNames.DefinedName {
definedName := DefinedName{
@@ -1768,23 +1900,22 @@ func (f *File) RemovePageBreak(sheet, cell string) (err error) {
// relsReader provides a function to get the pointer to the structure
// after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
-func (f *File) relsReader(path string) *xlsxRelationships {
- var err error
+func (f *File) relsReader(path string) (*xlsxRelationships, error) {
rels, _ := f.Relationships.Load(path)
if rels == nil {
if _, ok := f.Pkg.Load(path); ok {
c := xlsxRelationships{}
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
Decode(&c); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return nil, fmt.Errorf("xml decode error: %w", err)
}
f.Relationships.Store(path, &c)
}
}
if rels, _ = f.Relationships.Load(path); rels != nil {
- return rels.(*xlsxRelationships)
+ return rels.(*xlsxRelationships), nil
}
- return nil
+ return nil, nil
}
// fillSheetData ensures there are enough rows, and columns in the chosen
diff --git a/sheet_test.go b/sheet_test.go
index 3ad0e75245..7506f79db0 100644
--- a/sheet_test.go
+++ b/sheet_test.go
@@ -13,7 +13,10 @@ import (
)
func ExampleFile_SetPageLayout() {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
if err := f.SetPageLayout(
"Sheet1",
BlackAndWhite(true),
@@ -30,7 +33,10 @@ func ExampleFile_SetPageLayout() {
}
func ExampleFile_GetPageLayout() {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
var (
blackAndWhite BlackAndWhite
firstPageNumber FirstPageNumber
@@ -81,19 +87,26 @@ func ExampleFile_GetPageLayout() {
}
func TestNewSheet(t *testing.T) {
- f := NewFile()
- f.NewSheet("Sheet2")
- sheetID := f.NewSheet("sheet2")
+ f, err := NewFile()
+ assert.NoError(t, err)
+ _, err = f.NewSheet("Sheet2")
+ assert.NoError(t, err)
+ sheetID, err := f.NewSheet("sheet2")
+ assert.NoError(t, err)
f.SetActiveSheet(sheetID)
// delete original sheet
- f.DeleteSheet(f.GetSheetName(f.GetSheetIndex("Sheet1")))
+ err = f.DeleteSheet(f.GetSheetName(f.GetSheetIndex("Sheet1")))
+ assert.NoError(t, err)
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"))
+ sheetID, err = f.NewSheet("Sheet2")
+ assert.NoError(t, err)
+ assert.Equal(t, f.GetSheetIndex("Sheet2"), sheetID)
}
func TestSetPane(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":false,"split":false}`))
f.NewSheet("Panes 2")
assert.NoError(t, f.SetPanes("Panes 2", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`))
@@ -105,7 +118,8 @@ func TestSetPane(t *testing.T) {
assert.EqualError(t, f.SetPanes("SheetN", ""), "sheet SheetN is not exist")
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx")))
// Test add pane on empty sheet views worksheet
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.checked = nil
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(``))
@@ -137,7 +151,8 @@ func TestPageLayoutOption(t *testing.T) {
val1 := deepcopy.Copy(def).(PageLayoutOptionPtr)
val2 := deepcopy.Copy(def).(PageLayoutOptionPtr)
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Get the default value
assert.NoError(t, f.GetPageLayout(sheet, def), opt)
// Get again and check
@@ -198,12 +213,14 @@ func TestSearchSheet(t *testing.T) {
assert.NoError(t, f.Close())
// Test search worksheet data after set cell value
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", true))
_, err = f.SearchSheet("Sheet1", "")
assert.NoError(t, err)
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`A
`))
f.checked = nil
@@ -223,19 +240,22 @@ func TestSearchSheet(t *testing.T) {
}
func TestSetPageLayout(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test set page layout on not exists worksheet.
assert.EqualError(t, f.SetPageLayout("SheetN"), "sheet SheetN is not exist")
}
func TestGetPageLayout(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test get page layout on not exists worksheet.
assert.EqualError(t, f.GetPageLayout("SheetN"), "sheet SheetN is not exist")
}
func TestSetHeaderFooter(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellStr("Sheet1", "A1", "Test SetHeaderFooter"))
// Test set header and footer on not exists worksheet.
assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN is not exist")
@@ -266,7 +286,8 @@ func TestSetHeaderFooter(t *testing.T) {
}
func TestDefinedName(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetDefinedName(&DefinedName{
Name: "Amount",
RefersTo: "Sheet1!$A$2:$D$5",
@@ -296,7 +317,8 @@ func TestDefinedName(t *testing.T) {
}
func TestGroupSheets(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheets := []string{"Sheet2", "Sheet3"}
for _, sheet := range sheets {
f.NewSheet(sheet)
@@ -308,7 +330,8 @@ func TestGroupSheets(t *testing.T) {
}
func TestUngroupSheets(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sheets := []string{"Sheet2", "Sheet3", "Sheet4", "Sheet5"}
for _, sheet := range sheets {
f.NewSheet(sheet)
@@ -317,7 +340,8 @@ func TestUngroupSheets(t *testing.T) {
}
func TestInsertPageBreak(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.InsertPageBreak("Sheet1", "A1"))
assert.NoError(t, f.InsertPageBreak("Sheet1", "B2"))
assert.NoError(t, f.InsertPageBreak("Sheet1", "C3"))
@@ -328,7 +352,8 @@ func TestInsertPageBreak(t *testing.T) {
}
func TestRemovePageBreak(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.RemovePageBreak("Sheet1", "A2"))
assert.NoError(t, f.InsertPageBreak("Sheet1", "A2"))
@@ -378,8 +403,28 @@ func TestGetSheetMap(t *testing.T) {
assert.NoError(t, f.Close())
}
+func TestGetSheetNameBySheetXMLPath(t *testing.T) {
+ expectedNames := map[string]string{
+ "xl/worksheets/sheet1.xml": "Sheet1",
+ "xl/worksheets/sheet2.xml": "Sheet2",
+ }
+ f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
+ assert.NoError(t, err)
+
+ sheetMap, err := f.getSheetMap()
+ assert.NoError(t, err)
+
+ for _, v := range sheetMap {
+ sheet := f.getSheetNameBySheetXMLPath(v)
+ assert.Equal(t, expectedNames[v], sheet)
+ }
+ assert.Equal(t, len(sheetMap), 2)
+ assert.NoError(t, f.Close())
+}
+
func TestSetActiveSheet(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.WorkBook.BookViews = nil
f.SetActiveSheet(1)
f.WorkBook.BookViews = &xlsxBookViews{WorkBookView: []xlsxWorkBookView{}}
@@ -391,13 +436,16 @@ func TestSetActiveSheet(t *testing.T) {
assert.True(t, ok)
ws.(*xlsxWorksheet).SheetViews = nil
f.SetActiveSheet(1)
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.SetActiveSheet(-1)
assert.Equal(t, f.GetActiveSheetIndex(), 0)
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
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{}}
@@ -405,48 +453,61 @@ func TestSetActiveSheet(t *testing.T) {
}
func TestSetSheetName(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test set worksheet with the same name.
f.SetSheetName("Sheet1", "Sheet1")
assert.Equal(t, "Sheet1", f.GetSheetName(0))
}
func TestWorksheetWriter(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test set cell value with alternate content
f.Sheet.Delete("xl/worksheets/sheet1.xml")
worksheet := xml.Header + `%d
`
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(worksheet, 1)))
f.checked = nil
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 2))
- f.workSheetWriter()
+ err = f.workSheetWriter()
+ assert.NoError(t, err)
value, ok := f.Pkg.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
assert.Equal(t, fmt.Sprintf(worksheet, 2), string(value.([]byte)))
}
func TestGetWorkbookPath(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Pkg.Delete("_rels/.rels")
- assert.Equal(t, "", f.getWorkbookPath())
+ wp, err := f.getWorkbookPath()
+ assert.NoError(t, err)
+ assert.Equal(t, "", wp)
}
func TestGetWorkbookRelsPath(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.Pkg.Delete("xl/_rels/.rels")
f.Pkg.Store("_rels/.rels", []byte(xml.Header+``))
- assert.Equal(t, "_rels/workbook.xml.rels", f.getWorkbookRelsPath())
+ wrp, err := f.getWorkbookRelsPath()
+ assert.NoError(t, err)
+ assert.Equal(t, "_rels/workbook.xml.rels", wrp)
}
func TestDeleteSheet(t *testing.T) {
- f := NewFile()
- f.SetActiveSheet(f.NewSheet("Sheet2"))
+ f, err := NewFile()
+ assert.NoError(t, err)
+ sheet, err := f.NewSheet("Sheet2")
+ assert.NoError(t, err)
+ f.SetActiveSheet(sheet)
f.NewSheet("Sheet3")
f.DeleteSheet("Sheet1")
assert.Equal(t, "Sheet2", f.GetSheetName(f.GetActiveSheetIndex()))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet.xlsx")))
// Test with auto filter defined names
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.NewSheet("Sheet2")
f.NewSheet("Sheet3")
assert.NoError(t, f.SetCellValue("Sheet1", "A1", "A"))
@@ -474,7 +535,10 @@ func BenchmarkNewSheet(b *testing.B) {
}
func newSheetWithSet() {
- file := NewFile()
+ file, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
file.NewSheet("sheet1")
for i := 0; i < 1000; i++ {
_ = file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i)
@@ -491,7 +555,10 @@ func BenchmarkFile_SaveAs(b *testing.B) {
}
func newSheetWithSave() {
- file := NewFile()
+ file, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
file.NewSheet("sheet1")
for i := 0; i < 1000; i++ {
_ = file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i)
diff --git a/sheetpr_test.go b/sheetpr_test.go
index 91685d8837..c948b34bf6 100644
--- a/sheetpr_test.go
+++ b/sheetpr_test.go
@@ -29,7 +29,8 @@ var _ = []SheetPrOptionPtr{
}
func ExampleFile_SetSheetPrOptions() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
if err := f.SetSheetPrOptions(sheet,
@@ -47,7 +48,8 @@ func ExampleFile_SetSheetPrOptions() {
}
func ExampleFile_GetSheetPrOptions() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
var (
@@ -115,7 +117,8 @@ func TestSheetPrOptions(t *testing.T) {
val1 := deepcopy.Copy(def).(SheetPrOptionPtr)
val2 := deepcopy.Copy(def).(SheetPrOptionPtr)
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Get the default value
assert.NoError(t, f.GetSheetPrOptions(sheet, def), opt)
// Get again and check
@@ -153,14 +156,16 @@ func TestSheetPrOptions(t *testing.T) {
}
func TestSetSheetPrOptions(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
assert.NoError(t, f.SetSheetPrOptions("Sheet1", TabColor("")))
// Test SetSheetPrOptions on not exists worksheet.
assert.EqualError(t, f.SetSheetPrOptions("SheetN"), "sheet SheetN is not exist")
}
func TestGetSheetPrOptions(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
// Test GetSheetPrOptions on not exists worksheet.
assert.EqualError(t, f.GetSheetPrOptions("SheetN"), "sheet SheetN is not exist")
}
@@ -184,7 +189,8 @@ var _ = []PageMarginsOptionsPtr{
}
func ExampleFile_SetPageMargins() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
if err := f.SetPageMargins(sheet,
@@ -201,7 +207,8 @@ func ExampleFile_SetPageMargins() {
}
func ExampleFile_GetPageMargins() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
var (
@@ -264,7 +271,8 @@ func TestPageMarginsOption(t *testing.T) {
val1 := deepcopy.Copy(def).(PageMarginsOptionsPtr)
val2 := deepcopy.Copy(def).(PageMarginsOptionsPtr)
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Get the default value
assert.NoError(t, f.GetPageMargins(sheet, def), opt)
// Get again and check
@@ -302,19 +310,22 @@ func TestPageMarginsOption(t *testing.T) {
}
func TestSetPageMargins(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
// Test set page margins on not exists worksheet.
assert.EqualError(t, f.SetPageMargins("SheetN"), "sheet SheetN is not exist")
}
func TestGetPageMargins(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
// Test get page margins on not exists worksheet.
assert.EqualError(t, f.GetPageMargins("SheetN"), "sheet SheetN is not exist")
}
func ExampleFile_SetSheetFormatPr() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
if err := f.SetSheetFormatPr(sheet,
@@ -332,7 +343,8 @@ func ExampleFile_SetSheetFormatPr() {
}
func ExampleFile_GetSheetFormatPr() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
var (
@@ -400,7 +412,8 @@ func TestSheetFormatPrOptions(t *testing.T) {
val1 := deepcopy.Copy(def).(SheetFormatPrOptionsPtr)
val2 := deepcopy.Copy(def).(SheetFormatPrOptionsPtr)
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Get the default value
assert.NoError(t, f.GetSheetFormatPr(sheet, def), opt)
// Get again and check
@@ -438,7 +451,8 @@ func TestSheetFormatPrOptions(t *testing.T) {
}
func TestSetSheetFormatPr(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
@@ -449,7 +463,8 @@ func TestSetSheetFormatPr(t *testing.T) {
}
func TestGetSheetFormatPr(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
diff --git a/sheetview_test.go b/sheetview_test.go
index 2bba8f9802..0e0119a77a 100644
--- a/sheetview_test.go
+++ b/sheetview_test.go
@@ -45,7 +45,8 @@ var _ = []SheetViewOptionPtr{
}
func ExampleFile_SetSheetViewOptions() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
if err := f.SetSheetViewOptions(sheet, 0,
@@ -98,7 +99,8 @@ func ExampleFile_SetSheetViewOptions() {
}
func ExampleFile_GetSheetViewOptions() {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
const sheet = "Sheet1"
var (
@@ -199,7 +201,8 @@ func ExampleFile_GetSheetViewOptions() {
}
func TestSheetViewOptionsErrors(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
const sheet = "Sheet1"
assert.NoError(t, f.GetSheetViewOptions(sheet, 0))
diff --git a/sparkline_test.go b/sparkline_test.go
index c21687cbb9..ac1c4e5291 100644
--- a/sparkline_test.go
+++ b/sparkline_test.go
@@ -273,7 +273,8 @@ func TestAddSparkline(t *testing.T) {
func TestAppendSparkline(t *testing.T) {
// Test unsupported charset.
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
ws, err := f.workSheetReader("Sheet1")
assert.NoError(t, err)
ws.ExtLst = &xlsxExtLst{Ext: string(MacintoshCyrillicCharset)}
@@ -281,7 +282,8 @@ func TestAppendSparkline(t *testing.T) {
}
func prepareSparklineDataset() *File {
- f := NewFile()
+ f, err := NewFile()
+ fmt.Println(err)
sheet2 := [][]int{
{-2, 2, 3, -1, 0},
{30, 20, 33, 20, 15},
diff --git a/stream.go b/stream.go
index 641340ede9..47f95de804 100644
--- a/stream.go
+++ b/stream.go
@@ -141,6 +141,10 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
//
// See File.AddTable for details on the table format.
func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
+ if !sw.File.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
formatSet, err := parseFormatTableSet(format)
if err != nil {
return err
@@ -211,15 +215,21 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
// Add first table for given sheet.
sheetPath := sw.File.sheetMap[trimSheetName(sw.Sheet)]
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
- rID := sw.File.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
+ rID, err := sw.File.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
+ if err != nil {
+ return err
+ }
sw.tableParts = fmt.Sprintf(``, rID)
sw.File.addContentTypePart(tableID, "table")
- b, _ := xml.Marshal(table)
+ b, err := xml.Marshal(table)
+ if err != nil {
+ return err
+ }
sw.File.saveFileList(tableXML, b)
- return nil
+ return err
}
// Extract values from a row in the StreamWriter.
diff --git a/stream_test.go b/stream_test.go
index 9776b384a9..775ab761c9 100644
--- a/stream_test.go
+++ b/stream_test.go
@@ -15,7 +15,8 @@ import (
)
func BenchmarkStreamWriter(b *testing.B) {
- file := NewFile()
+ file, err := NewFile()
+ assert.NoError(b, err)
row := make([]interface{}, 10)
for colID := 0; colID < 10; colID++ {
@@ -34,7 +35,8 @@ func BenchmarkStreamWriter(b *testing.B) {
}
func TestStreamWriter(t *testing.T) {
- file := NewFile()
+ file, err := NewFile()
+ assert.NoError(t, err)
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
@@ -78,7 +80,8 @@ func TestStreamWriter(t *testing.T) {
assert.EqualError(t, streamWriter.SetRow("XFD1", []interface{}{"A", "B", "C"}), ErrColumnNumber.Error())
// Test close temporary file error.
- file = NewFile()
+ file, err = NewFile()
+ assert.NoError(t, err)
streamWriter, err = file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
for rowID := 10; rowID <= 25600; rowID++ {
@@ -100,14 +103,16 @@ func TestStreamWriter(t *testing.T) {
assert.NoError(t, os.Remove(streamWriter.rawData.tmp.Name()))
// Test unsupported charset
- file = NewFile()
+ file, err = NewFile()
+ assert.NoError(t, err)
file.Sheet.Delete("xl/worksheets/sheet1.xml")
file.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
_, err = file.NewStreamWriter("Sheet1")
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
// Test read cell.
- file = NewFile()
+ file, err = NewFile()
+ assert.NoError(t, err)
streamWriter, err = file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{Cell{StyleID: styleID, Value: "Data"}}))
@@ -135,7 +140,8 @@ func TestStreamWriter(t *testing.T) {
}
func TestStreamSetColWidth(t *testing.T) {
- file := NewFile()
+ file, err := NewFile()
+ assert.NoError(t, err)
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.NoError(t, streamWriter.SetColWidth(3, 2, 20))
@@ -147,7 +153,8 @@ func TestStreamSetColWidth(t *testing.T) {
}
func TestStreamTable(t *testing.T) {
- file := NewFile()
+ file, err := NewFile()
+ assert.NoError(t, err)
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
@@ -181,7 +188,8 @@ func TestStreamTable(t *testing.T) {
}
func TestStreamMergeCells(t *testing.T) {
- file := NewFile()
+ file, err := NewFile()
+ assert.NoError(t, err)
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.NoError(t, streamWriter.MergeCell("A1", "D1"))
@@ -194,8 +202,9 @@ func TestStreamMergeCells(t *testing.T) {
func TestNewStreamWriter(t *testing.T) {
// Test error exceptions
- file := NewFile()
- _, err := file.NewStreamWriter("Sheet1")
+ file, err := NewFile()
+ assert.NoError(t, err)
+ _, err = file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
_, err = file.NewStreamWriter("SheetN")
assert.EqualError(t, err, "sheet SheetN is not exist")
@@ -203,14 +212,16 @@ func TestNewStreamWriter(t *testing.T) {
func TestSetRow(t *testing.T) {
// Test error exceptions
- file := NewFile()
+ file, err := NewFile()
+ assert.NoError(t, err)
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
func TestSetCellValFunc(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
sw, err := f.NewStreamWriter("Sheet1")
assert.NoError(t, err)
c := &xlsxC{}
diff --git a/styles.go b/styles.go
index 0220e9c976..f5d1bfd194 100644
--- a/styles.go
+++ b/styles.go
@@ -17,7 +17,6 @@ import (
"encoding/xml"
"fmt"
"io"
- "log"
"math"
"reflect"
"strconv"
@@ -922,35 +921,48 @@ func formatToE(v, format string, date1904 bool) string {
return fmt.Sprintf("%.2E", f)
}
-// stylesReader provides a function to get the pointer to the structure after
+// NewStylesReader provides a function to get the pointer to the structure after
// deserialization of xl/styles.xml.
-func (f *File) stylesReader() *xlsxStyleSheet {
+func (f *File) NewStylesReader() (*xlsxStyleSheet, error) {
if f.Styles == nil {
f.Styles = new(xlsxStyleSheet)
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathStyles)))).
Decode(f.Styles); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
+ return f.Styles, fmt.Errorf("xml decode error: %w", err)
}
}
+ return f.Styles, nil
+}
+
+// stylesReader provides a function to get the pointer to Styles.
+func (f *File) stylesReader() *xlsxStyleSheet {
return f.Styles
}
// styleSheetWriter provides a function to save xl/styles.xml after serialize
// structure.
-func (f *File) styleSheetWriter() {
+func (f *File) styleSheetWriter() error {
if f.Styles != nil {
- output, _ := xml.Marshal(f.Styles)
+ output, err := xml.Marshal(f.Styles)
+ if err != nil {
+ return err
+ }
f.saveFileList(defaultXMLPathStyles, f.replaceNameSpaceBytes(defaultXMLPathStyles, output))
}
+ return nil
}
// sharedStringsWriter provides a function to save xl/sharedStrings.xml after
// serialize structure.
-func (f *File) sharedStringsWriter() {
+func (f *File) sharedStringsWriter() error {
if f.SharedStrings != nil {
- output, _ := xml.Marshal(f.SharedStrings)
+ output, err := xml.Marshal(f.SharedStrings)
+ if err != nil {
+ return err
+ }
f.saveFileList(defaultXMLPathSharedStrings, f.replaceNameSpaceBytes(defaultXMLPathSharedStrings, output))
}
+ return nil
}
// parseFormatStyleSet provides a function to parse the format settings of the
@@ -1834,7 +1846,11 @@ func parseFormatStyleSet(style interface{}) (*Style, error) {
// Excelize support set custom number format for cell. For example, set number
// as date type in Uruguay (Spanish) format for Sheet1!A6:
//
-// f := excelize.NewFile()
+// f, err := excelize.NewFile()
+// if err != nil {
+// fmt.Println(err)
+// return
+// }
// f.SetCellValue("Sheet1", "A6", 42920.5)
// exp := "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"
// style, err := f.NewStyle(&excelize.Style{CustomNumFmt: &exp})
@@ -1854,6 +1870,9 @@ func (f *File) NewStyle(style interface{}) (int, error) {
fs.DecimalPlaces = 2
}
s := f.stylesReader()
+ if s == nil {
+ return cellXfsID, ErrIncompleteFileSetup
+ }
s.Lock()
defer s.Unlock()
// check given style already exist.
@@ -1971,6 +1990,10 @@ func (f *File) getStyleID(ss *xlsxStyleSheet, style *Style) (styleID int) {
// NewStyle(). Note that the color field uses RGB color code and only support
// to set font, fills, alignment and borders currently.
func (f *File) NewConditionalStyle(style string) (int, error) {
+ if f.Styles == nil {
+ return 0, ErrIncompleteFileSetup
+ }
+
s := f.stylesReader()
fs, err := parseFormatStyleSet(style)
if err != nil {
@@ -1988,7 +2011,10 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
if fs.Font != nil {
dxf.Font = f.newFont(fs)
}
- dxfStr, _ := xml.Marshal(dxf)
+ dxfStr, err := xml.Marshal(dxf)
+ if err != nil {
+ return 0, err
+ }
if s.Dxfs == nil {
s.Dxfs = &xlsxDxfs{}
}
@@ -2003,11 +2029,18 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
// workbook. The spreadsheet generated by excelize default font is Calibri.
func (f *File) GetDefaultFont() string {
font := f.readDefaultFont()
+ if font == nil {
+ return ""
+ }
return *font.Name.Val
}
// SetDefaultFont changes the default font in the workbook.
func (f *File) SetDefaultFont(fontName string) {
+ if f.Styles == nil {
+ return
+ }
+
font := f.readDefaultFont()
font.Name.Val = stringPtr(fontName)
s := f.stylesReader()
@@ -3021,18 +3054,15 @@ func getPaletteColor(color string) string {
return "FF" + strings.ReplaceAll(strings.ToUpper(color), "#", "")
}
-// themeReader provides a function to get the pointer to the xl/theme/theme1.xml
+// NewThemeReader provides a function to get the pointer to the xl/theme/theme1.xml
// structure after deserialization.
-func (f *File) themeReader() *xlsxTheme {
- var (
- err error
- theme xlsxTheme
- )
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))).
+func (f *File) NewThemeReader() (*xlsxTheme, error) {
+ var theme xlsxTheme
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))).
Decode(&theme); err != nil && err != io.EOF {
- log.Printf("xml decoder error: %s", err)
+ return &theme, fmt.Errorf("xml decode error: %w", err)
}
- return &theme
+ return &theme, nil
}
// ThemeColor applied the color with tint value.
diff --git a/styles_test.go b/styles_test.go
index 156b4e33b1..8a122958d6 100644
--- a/styles_test.go
+++ b/styles_test.go
@@ -26,7 +26,8 @@ func TestStyleFill(t *testing.T) {
}}
for _, testCase := range cases {
- xl := NewFile()
+ xl, err := NewFile()
+ assert.NoError(t, err)
styleID, err := xl.NewStyle(testCase.format)
assert.NoError(t, err)
@@ -38,7 +39,8 @@ func TestStyleFill(t *testing.T) {
assert.Equal(t, *style.FillID, 0, testCase.label)
}
}
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
styleID1, err := f.NewStyle(`{"fill":{"type":"pattern","pattern":1,"color":["#000000"]}}`)
assert.NoError(t, err)
styleID2, err := f.NewStyle(`{"fill":{"type":"pattern","pattern":1,"color":["#000000"]}}`)
@@ -156,11 +158,12 @@ func TestSetConditionalFormat(t *testing.T) {
}}
for _, testCase := range cases {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
const sheet = "Sheet1"
const cellRange = "A1:A1"
- err := f.SetConditionalFormat(sheet, cellRange, testCase.format)
+ err = f.SetConditionalFormat(sheet, cellRange, testCase.format)
if err != nil {
t.Fatalf("%s", err)
}
@@ -176,7 +179,8 @@ func TestSetConditionalFormat(t *testing.T) {
}
func TestUnsetConditionalFormat(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 7))
assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10"))
format, err := f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
@@ -190,7 +194,8 @@ func TestUnsetConditionalFormat(t *testing.T) {
}
func TestNewStyle(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
styleID, err := f.NewStyle(`{"font":{"bold":true,"italic":true,"family":"Times New Roman","size":36,"color":"#777777"}}`)
assert.NoError(t, err)
styles := f.stylesReader()
@@ -253,7 +258,8 @@ func TestNewStyle(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, [][]string{{"1.23E+00", "1.23E+00"}}, rows)
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
// Test currency number format
customNumFmt := "[$$-409]#,##0.00"
style1, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt})
@@ -266,14 +272,16 @@ func TestNewStyle(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 2, style3)
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Styles.NumFmts = nil
f.Styles.CellXfs.Xf = nil
style4, err := f.NewStyle(&Style{NumFmt: 160, Lang: "unknown"})
assert.NoError(t, err)
assert.Equal(t, 0, style4)
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
f.Styles.NumFmts = nil
f.Styles.CellXfs.Xf = nil
style5, err := f.NewStyle(&Style{NumFmt: 160, Lang: "zh-cn"})
@@ -282,13 +290,15 @@ func TestNewStyle(t *testing.T) {
}
func TestGetDefaultFont(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
s := f.GetDefaultFont()
assert.Equal(t, s, "Calibri", "Default font should be Calibri")
}
func TestSetDefaultFont(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
f.SetDefaultFont("Arial")
styles := f.stylesReader()
s := f.GetDefaultFont()
@@ -297,32 +307,43 @@ func TestSetDefaultFont(t *testing.T) {
}
func TestStylesReader(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test read styles with unsupported charset.
f.Styles = nil
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
- assert.EqualValues(t, new(xlsxStyleSheet), f.stylesReader())
+ stylesReader, err := f.NewStylesReader()
+ assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
+ assert.EqualValues(t, new(xlsxStyleSheet), stylesReader)
}
-func TestThemeReader(t *testing.T) {
- f := NewFile()
+func TestNewThemeReader(t *testing.T) {
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test read theme with unsupported charset.
f.Pkg.Store("xl/theme/theme1.xml", MacintoshCyrillicCharset)
- assert.EqualValues(t, new(xlsxTheme), f.themeReader())
+ theme, err := f.NewThemeReader()
+ assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
+ assert.EqualValues(t, new(xlsxTheme), theme)
}
func TestSetCellStyle(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test set cell style on not exists worksheet.
assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN is not exist")
}
func TestGetStyleID(t *testing.T) {
- assert.Equal(t, -1, NewFile().getStyleID(&xlsxStyleSheet{}, nil))
+ f, err := NewFile()
+ assert.NoError(t, err)
+ assert.Equal(t, -1, f.getStyleID(&xlsxStyleSheet{}, nil))
}
func TestGetFillID(t *testing.T) {
- assert.Equal(t, -1, getFillID(NewFile().stylesReader(), &Style{Fill: Fill{Type: "unknown"}}))
+ f, err := NewFile()
+ assert.NoError(t, err)
+ assert.Equal(t, -1, getFillID(f.stylesReader(), &Style{Fill: Fill{Type: "unknown"}}))
}
func TestThemeColor(t *testing.T) {
@@ -339,7 +360,8 @@ func TestThemeColor(t *testing.T) {
}
func TestGetNumFmtID(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
fs1, err := parseFormatStyleSet(`{"protection":{"hidden":false,"locked":false},"number_format":10}`)
assert.NoError(t, err)
diff --git a/table.go b/table.go
index 413118c31a..14538d1ff8 100644
--- a/table.go
+++ b/table.go
@@ -59,6 +59,10 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) {
// TableStyleDark1 - TableStyleDark11
//
func (f *File) AddTable(sheet, hCell, vCell, format string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
formatSet, err := parseFormatTableSet(format)
if err != nil {
return err
@@ -86,7 +90,10 @@ func (f *File) AddTable(sheet, hCell, vCell, format string) error {
tableXML := strings.ReplaceAll(sheetRelationshipsTableXML, "..", "xl")
// Add first table for given sheet.
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
- rID := f.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
+ rID, err := f.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
+ if err != nil {
+ return err
+ }
if err = f.addSheetTable(sheet, rID); err != nil {
return err
}
@@ -152,13 +159,22 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
if err != nil {
return err
}
- name, _ := f.GetCellValue(sheet, cell)
+ name, err := f.GetCellValue(sheet, cell)
+ if err != nil {
+ return err
+ }
if _, err := strconv.Atoi(name); err == nil {
- _ = f.SetCellStr(sheet, cell, name)
+ err = f.SetCellStr(sheet, cell, name)
+ if err != nil {
+ return err
+ }
}
if name == "" {
name = "Column" + strconv.Itoa(idx)
- _ = f.SetCellStr(sheet, cell, name)
+ err = f.SetCellStr(sheet, cell, name)
+ if err != nil {
+ return err
+ }
}
tableColumn = append(tableColumn, &xlsxTableColumn{
ID: idx,
@@ -190,9 +206,12 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
ShowColumnStripes: formatSet.ShowColumnStripes,
},
}
- table, _ := xml.Marshal(t)
+ table, err := xml.Marshal(t)
+ if err != nil {
+ return err
+ }
f.saveFileList(tableXML, table)
- return nil
+ return err
}
// parseAutoFilterSet provides a function to parse the settings of the auto
@@ -274,6 +293,10 @@ func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
// Price < 2000
//
func (f *File) AutoFilter(sheet, hCell, vCell, format string) error {
+ if !f.IsValid() {
+ return ErrIncompleteFileSetup
+ }
+
hCol, hRow, err := CellNameToCoordinates(hCell)
if err != nil {
return err
diff --git a/table_test.go b/table_test.go
index 0a74b1b568..94ac9a653c 100644
--- a/table_test.go
+++ b/table_test.go
@@ -40,7 +40,8 @@ func TestAddTable(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx")))
// Test addTable with illegal cell coordinates.
- f = NewFile()
+ f, err = NewFile()
+ assert.NoError(t, err)
assert.EqualError(t, f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil), "invalid cell coordinates [0, 0]")
assert.EqualError(t, f.addTable("sheet1", "", 1, 1, 0, 0, 0, nil), "invalid cell coordinates [0, 0]")
}
@@ -121,9 +122,10 @@ func TestAutoFilterError(t *testing.T) {
}
func TestParseFilterTokens(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
// Test with unknown operator.
- _, _, err := f.parseFilterTokens("", []string{"", "!"})
+ _, _, err = f.parseFilterTokens("", []string{"", "!"})
assert.EqualError(t, err, "unknown operator: !")
// Test invalid operator in context.
_, _, err = f.parseFilterTokens("", []string{"", "<", "x != blanks"})
diff --git a/test/Book1.xlsx b/test/Book1.xlsx
index 6a497e33af..f6a78d0055 100644
Binary files a/test/Book1.xlsx and b/test/Book1.xlsx differ
diff --git a/workbook.go b/workbook.go
index 417524b1da..d5d5b76879 100644
--- a/workbook.go
+++ b/workbook.go
@@ -14,8 +14,8 @@ package excelize
import (
"bytes"
"encoding/xml"
+ "fmt"
"io"
- "log"
"path/filepath"
"strconv"
"strings"
@@ -45,6 +45,9 @@ type (
// characters are allowed in sheet title.
func (f *File) setWorkbook(name string, sheetID, rid int) {
content := f.workbookReader()
+ if content == nil {
+ return
+ }
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
Name: trimSheetName(name),
SheetID: sheetID,
@@ -54,8 +57,13 @@ func (f *File) setWorkbook(name string, sheetID, rid int) {
// getWorkbookPath provides a function to get the path of the workbook.xml in
// the spreadsheet.
-func (f *File) getWorkbookPath() (path string) {
- if rels := f.relsReader("_rels/.rels"); rels != nil {
+func (f *File) getWorkbookPath() (path string, err error) {
+ var rels *xlsxRelationships
+ rels, err = f.relsReader("_rels/.rels")
+ if err != nil {
+ return
+ }
+ if rels != nil {
rels.Lock()
defer rels.Unlock()
for _, rel := range rels.Relationships {
@@ -70,8 +78,12 @@ func (f *File) getWorkbookPath() (path string) {
// getWorkbookRelsPath provides a function to get the path of the workbook.xml.rels
// in the spreadsheet.
-func (f *File) getWorkbookRelsPath() (path string) {
- wbPath := f.getWorkbookPath()
+func (f *File) getWorkbookRelsPath() (path string, err error) {
+ var wbPath string
+ wbPath, err = f.getWorkbookPath()
+ if err != nil {
+ return
+ }
wbDir := filepath.Dir(wbPath)
if wbDir == "." {
path = "_rels/" + filepath.Base(wbPath) + ".rels"
@@ -81,29 +93,34 @@ func (f *File) getWorkbookRelsPath() (path string) {
return
}
-// workbookReader provides a function to get the pointer to the workbook.xml
+// NewWorkbookReader provides a function to get the pointer to the workbook.xml
// structure after deserialization.
-func (f *File) workbookReader() *xlsxWorkbook {
- var err error
- if f.WorkBook == nil {
- wbPath := f.getWorkbookPath()
- f.WorkBook = new(xlsxWorkbook)
- if _, ok := f.xmlAttr[wbPath]; !ok {
- d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath))))
- f.xmlAttr[wbPath] = append(f.xmlAttr[wbPath], getRootElement(d)...)
- f.addNameSpaces(wbPath, SourceRelationship)
- }
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
- Decode(f.WorkBook); err != nil && err != io.EOF {
- log.Printf("xml decode error: %s", err)
- }
+func (f *File) NewWorkbookReader() (*xlsxWorkbook, error) {
+ wbPath, err := f.getWorkbookPath()
+ if err != nil {
+ return nil, err
+ }
+ f.WorkBook = new(xlsxWorkbook)
+ if _, ok := f.xmlAttr[wbPath]; !ok {
+ d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath))))
+ f.xmlAttr[wbPath] = append(f.xmlAttr[wbPath], getRootElement(d)...)
+ f.addNameSpaces(wbPath, SourceRelationship)
}
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
+ Decode(f.WorkBook); err != nil && err != io.EOF {
+ return f.WorkBook, fmt.Errorf("xml decode error: %w", err)
+ }
+ return f.WorkBook, nil
+}
+
+// workbookReader provides a function to get the pointer to WorkBook.
+func (f *File) workbookReader() *xlsxWorkbook {
return f.WorkBook
}
// workBookWriter provides a function to save workbook.xml after serialize
// structure.
-func (f *File) workBookWriter() {
+func (f *File) workBookWriter() error {
if f.WorkBook != nil {
if f.WorkBook.DecodeAlternateContent != nil {
f.WorkBook.AlternateContent = &xlsxAlternateContent{
@@ -112,9 +129,17 @@ func (f *File) workBookWriter() {
}
}
f.WorkBook.DecodeAlternateContent = nil
- output, _ := xml.Marshal(f.WorkBook)
- f.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output)))
+ output, err := xml.Marshal(f.WorkBook)
+ if err != nil {
+ return err
+ }
+ wp, err := f.getWorkbookPath()
+ if err != nil {
+ return err
+ }
+ f.saveFileList(wp, replaceRelationshipsBytes(f.replaceNameSpaceBytes(wp, output)))
}
+ return nil
}
// SetWorkbookPrOptions provides a function to sets workbook properties.
@@ -125,6 +150,9 @@ func (f *File) workBookWriter() {
// CodeName(string)
func (f *File) SetWorkbookPrOptions(opts ...WorkbookPrOption) error {
wb := f.workbookReader()
+ if wb == nil {
+ return ErrIncompleteFileSetup
+ }
pr := wb.WorkbookPr
if pr == nil {
pr = new(xlsxWorkbookPr)
@@ -159,6 +187,9 @@ func (o CodeName) setWorkbookPrOption(pr *xlsxWorkbookPr) {
// CodeName(string)
func (f *File) GetWorkbookPrOptions(opts ...WorkbookPrOptionPtr) error {
wb := f.workbookReader()
+ if wb == nil {
+ return ErrIncompleteFileSetup
+ }
pr := wb.WorkbookPr
for _, opt := range opts {
opt.getWorkbookPrOption(pr)
diff --git a/workbook_test.go b/workbook_test.go
index 18b222c00f..e922a642f1 100644
--- a/workbook_test.go
+++ b/workbook_test.go
@@ -8,7 +8,10 @@ import (
)
func ExampleFile_SetWorkbookPrOptions() {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
if err := f.SetWorkbookPrOptions(
Date1904(false),
FilterPrivacy(false),
@@ -20,7 +23,10 @@ func ExampleFile_SetWorkbookPrOptions() {
}
func ExampleFile_GetWorkbookPrOptions() {
- f := NewFile()
+ f, err := NewFile()
+ if err != nil {
+ fmt.Println(err)
+ }
var (
date1904 Date1904
filterPrivacy FilterPrivacy
@@ -47,7 +53,8 @@ func ExampleFile_GetWorkbookPrOptions() {
}
func TestWorkbookPr(t *testing.T) {
- f := NewFile()
+ f, err := NewFile()
+ assert.NoError(t, err)
wb := f.workbookReader()
wb.WorkbookPr = nil
var date1904 Date1904