Skip to content

Commit 3ee3c38

Browse files
committed
Fix file corrupted in some cases, check file extension and format code
Fix file corrupted when save as in XLAM / XLSM / XLTM / XLTX extension in some case New exported error ErrWorkbookExt has been added, and check file extension on save the workbook Format source code with `gofumpt`
1 parent 74f6ea9 commit 3ee3c38

33 files changed

+225
-150
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
~$*.xlsx
2-
test/Test*.xlsx
2+
test/Test*.xlam
33
test/Test*.xlsm
4+
test/Test*.xlsx
5+
test/Test*.xltm
6+
test/Test*.xltx
47
# generated files
58
test/BadEncrypt.xlsx
69
test/BadWorkbook.SaveAsEmptyStruct.xlsx

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
## Introduction
1515

16-
Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLSX / XLSM / XLTM / XLTX files. Supports reading and writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports complex components by high compatibility, and provided streaming API for generating or reading data from a worksheet with huge amounts of data. This library needs Go version 1.15 or later. The full API docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) and [docs reference](https://xuri.me/excelize/).
16+
Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports complex components by high compatibility, and provided streaming API for generating or reading data from a worksheet with huge amounts of data. This library needs Go version 1.15 or later. The full API docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) and [docs reference](https://xuri.me/excelize/).
1717

1818
## Basic Usage
1919

README_zh.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
## 简介
1515

16-
Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档。支持 XLSX / XLSM / XLTM / XLTX 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写 API,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.15 或更高版本,完整的 API 使用文档请访问 [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) 或查看 [参考文档](https://xuri.me/excelize/)
16+
Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档。支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写 API,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.15 或更高版本,完整的 API 使用文档请访问 [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) 或查看 [参考文档](https://xuri.me/excelize/)
1717

1818
## 快速上手
1919

adjust_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,11 @@ func TestAdjustHelper(t *testing.T) {
298298
f := NewFile()
299299
f.NewSheet("Sheet2")
300300
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
301-
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}}})
301+
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}},
302+
})
302303
f.Sheet.Store("xl/worksheets/sheet2.xml", &xlsxWorksheet{
303-
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"}})
304+
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"},
305+
})
304306
// testing adjustHelper with illegal cell coordinates.
305307
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
306308
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())

calc.go

+103-31
Original file line numberDiff line numberDiff line change
@@ -1377,7 +1377,7 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, e
13771377
if cellRanges.Len() > 0 {
13781378
arg.Type = ArgMatrix
13791379
for row := valueRange[0]; row <= valueRange[1]; row++ {
1380-
var matrixRow = []formulaArg{}
1380+
matrixRow := []formulaArg{}
13811381
for col := valueRange[2]; col <= valueRange[3]; col++ {
13821382
var cell, value string
13831383
if cell, err = CoordinatesToCellName(col, row); err != nil {
@@ -1473,7 +1473,7 @@ func formulaCriteriaParser(exp string) (fc *formulaCriteria) {
14731473
func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, err error) {
14741474
var value, expected float64
14751475
var e error
1476-
var prepareValue = func(val, cond string) (value float64, expected float64, err error) {
1476+
prepareValue := func(val, cond string) (value float64, expected float64, err error) {
14771477
if value, err = strconv.ParseFloat(val, 64); err != nil {
14781478
return
14791479
}
@@ -3385,7 +3385,7 @@ func (fn *formulaFuncs) DECIMAL(argsList *list.List) formulaArg {
33853385
if argsList.Len() != 2 {
33863386
return newErrorFormulaArg(formulaErrorVALUE, "DECIMAL requires 2 numeric arguments")
33873387
}
3388-
var text = argsList.Front().Value.(formulaArg).String
3388+
text := argsList.Front().Value.(formulaArg).String
33893389
var radix int
33903390
var err error
33913391
radix, err = strconv.Atoi(argsList.Back().Value.(formulaArg).String)
@@ -3934,7 +3934,7 @@ func (fn *formulaFuncs) MDETERM(argsList *list.List) (result formulaArg) {
39343934
return newErrorFormulaArg(formulaErrorVALUE, "MDETERM requires at least 1 argument")
39353935
}
39363936
strMtx = argsList.Front().Value.(formulaArg).Matrix
3937-
var rows = len(strMtx)
3937+
rows := len(strMtx)
39383938
for _, row := range argsList.Front().Value.(formulaArg).Matrix {
39393939
if len(row) != rows {
39403940
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
@@ -4257,30 +4257,107 @@ type romanNumerals struct {
42574257

42584258
var romanTable = [][]romanNumerals{
42594259
{
4260-
{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"},
4261-
{50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"},
4260+
{1000, "M"},
4261+
{900, "CM"},
4262+
{500, "D"},
4263+
{400, "CD"},
4264+
{100, "C"},
4265+
{90, "XC"},
4266+
{50, "L"},
4267+
{40, "XL"},
4268+
{10, "X"},
4269+
{9, "IX"},
4270+
{5, "V"},
4271+
{4, "IV"},
4272+
{1, "I"},
42624273
},
42634274
{
4264-
{1000, "M"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {450, "LD"}, {400, "CD"},
4265-
{100, "C"}, {95, "VC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"},
4266-
{10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"},
4275+
{1000, "M"},
4276+
{950, "LM"},
4277+
{900, "CM"},
4278+
{500, "D"},
4279+
{450, "LD"},
4280+
{400, "CD"},
4281+
{100, "C"},
4282+
{95, "VC"},
4283+
{90, "XC"},
4284+
{50, "L"},
4285+
{45, "VL"},
4286+
{40, "XL"},
4287+
{10, "X"},
4288+
{9, "IX"},
4289+
{5, "V"},
4290+
{4, "IV"},
4291+
{1, "I"},
42674292
},
42684293
{
4269-
{1000, "M"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"}, {490, "XD"},
4270-
{450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"},
4271-
{45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"},
4294+
{1000, "M"},
4295+
{990, "XM"},
4296+
{950, "LM"},
4297+
{900, "CM"},
4298+
{500, "D"},
4299+
{490, "XD"},
4300+
{450, "LD"},
4301+
{400, "CD"},
4302+
{100, "C"},
4303+
{99, "IC"},
4304+
{90, "XC"},
4305+
{50, "L"},
4306+
{45, "VL"},
4307+
{40, "XL"},
4308+
{10, "X"},
4309+
{9, "IX"},
4310+
{5, "V"},
4311+
{4, "IV"},
4312+
{1, "I"},
42724313
},
42734314
{
4274-
{1000, "M"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"}, {500, "D"},
4275-
{495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"}, {100, "C"}, {99, "IC"},
4276-
{90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"}, {10, "X"}, {9, "IX"},
4277-
{5, "V"}, {4, "IV"}, {1, "I"},
4315+
{1000, "M"},
4316+
{995, "VM"},
4317+
{990, "XM"},
4318+
{950, "LM"},
4319+
{900, "CM"},
4320+
{500, "D"},
4321+
{495, "VD"},
4322+
{490, "XD"},
4323+
{450, "LD"},
4324+
{400, "CD"},
4325+
{100, "C"},
4326+
{99, "IC"},
4327+
{90, "XC"},
4328+
{50, "L"},
4329+
{45, "VL"},
4330+
{40, "XL"},
4331+
{10, "X"},
4332+
{9, "IX"},
4333+
{5, "V"},
4334+
{4, "IV"},
4335+
{1, "I"},
42784336
},
42794337
{
4280-
{1000, "M"}, {999, "IM"}, {995, "VM"}, {990, "XM"}, {950, "LM"}, {900, "CM"},
4281-
{500, "D"}, {499, "ID"}, {495, "VD"}, {490, "XD"}, {450, "LD"}, {400, "CD"},
4282-
{100, "C"}, {99, "IC"}, {90, "XC"}, {50, "L"}, {45, "VL"}, {40, "XL"},
4283-
{10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"},
4338+
{1000, "M"},
4339+
{999, "IM"},
4340+
{995, "VM"},
4341+
{990, "XM"},
4342+
{950, "LM"},
4343+
{900, "CM"},
4344+
{500, "D"},
4345+
{499, "ID"},
4346+
{495, "VD"},
4347+
{490, "XD"},
4348+
{450, "LD"},
4349+
{400, "CD"},
4350+
{100, "C"},
4351+
{99, "IC"},
4352+
{90, "XC"},
4353+
{50, "L"},
4354+
{45, "VL"},
4355+
{40, "XL"},
4356+
{10, "X"},
4357+
{9, "IX"},
4358+
{5, "V"},
4359+
{4, "IV"},
4360+
{1, "I"},
42844361
},
42854362
}
42864363

@@ -4751,8 +4828,8 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) formulaArg {
47514828
if argsList.Len() < 2 {
47524829
return newErrorFormulaArg(formulaErrorVALUE, "SUMIF requires at least 2 arguments")
47534830
}
4754-
var criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String)
4755-
var rangeMtx = argsList.Front().Value.(formulaArg).Matrix
4831+
criteria := formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String)
4832+
rangeMtx := argsList.Front().Value.(formulaArg).Matrix
47564833
var sumRange [][]formulaArg
47574834
if argsList.Len() == 3 {
47584835
sumRange = argsList.Back().Value.(formulaArg).Matrix
@@ -5886,7 +5963,7 @@ func (fn *formulaFuncs) MEDIAN(argsList *list.List) formulaArg {
58865963
if argsList.Len() == 0 {
58875964
return newErrorFormulaArg(formulaErrorVALUE, "MEDIAN requires at least 1 argument")
58885965
}
5889-
var values = []float64{}
5966+
values := []float64{}
58905967
var median, digits float64
58915968
var err error
58925969
for token := argsList.Front(); token != nil; token = token.Next() {
@@ -9047,7 +9124,6 @@ func compareFormulaArg(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte
90479124
if matchPattern(rs, ls) {
90489125
return criteriaEq
90499126
}
9050-
90519127
}
90529128
return map[int]byte{1: criteriaG, -1: criteriaL, 0: criteriaEq}[strings.Compare(ls, rs)]
90539129
case ArgEmpty:
@@ -9931,9 +10007,8 @@ func (fn *formulaFuncs) ACCRINT(argsList *list.List) formulaArg {
993110007
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
993210008
}
993310009
}
9934-
cm := newBoolFormulaArg(true)
993510010
if argsList.Len() == 8 {
9936-
if cm = argsList.Back().Value.(formulaArg).ToBool(); cm.Type != ArgNumber {
10011+
if cm := argsList.Back().Value.(formulaArg).ToBool(); cm.Type != ArgNumber {
993710012
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
993810013
}
993910014
}
@@ -10206,10 +10281,8 @@ func coupdays(from, to time.Time, basis int) float64 {
1020610281
date = date.AddDate(0, 13-int(date.Month()), 0)
1020710282
}
1020810283
days += getDaysInMonthRange(toY, int(date.Month()), int(toM)-1, basis)
10209-
date = date.AddDate(0, int(toM)-int(date.Month()), 0)
1021010284
}
10211-
days += toDay - fromDay
10212-
if days > 0 {
10285+
if days += toDay - fromDay; days > 0 {
1021310286
return float64(days)
1021410287
}
1021510288
return 0
@@ -11375,8 +11448,7 @@ func (fn *formulaFuncs) ODDFPRICE(argsList *list.List) formulaArg {
1137511448
if args.Type != ArgList {
1137611449
return args
1137711450
}
11378-
settlement, maturity, issue, firstCoupon, rate, yld, redemption, frequency, basisArg :=
11379-
args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6], args.List[7], args.List[8]
11451+
settlement, maturity, issue, firstCoupon, rate, yld, redemption, frequency, basisArg := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5], args.List[6], args.List[7], args.List[8]
1138011452
if basisArg.Number < 0 || basisArg.Number > 4 {
1138111453
return newErrorFormulaArg(formulaErrorNUM, "invalid basis")
1138211454
}

calc_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -3386,7 +3386,6 @@ func TestCalcCellValue(t *testing.T) {
33863386
_, err = f.CalcCellValue("Sheet1", "A1")
33873387
assert.EqualError(t, err, "not support UNSUPPORT function")
33883388
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestCalcCellValue.xlsx")))
3389-
33903389
}
33913390

33923391
func TestCalculate(t *testing.T) {
@@ -3438,7 +3437,6 @@ func TestCalcWithDefinedName(t *testing.T) {
34383437
result, err = f.CalcCellValue("Sheet1", "D1")
34393438
assert.NoError(t, err)
34403439
assert.Equal(t, "YES", result, `=IF("B1_as_string"=defined_name1,"YES","NO")`)
3441-
34423440
}
34433441

34443442
func TestCalcISBLANK(t *testing.T) {
@@ -3748,7 +3746,8 @@ func TestCalcXIRR(t *testing.T) {
37483746
{25.00, "02/01/2017"},
37493747
{8.00, "03/01/2017"},
37503748
{15.00, "06/01/2017"},
3751-
{-1e-10, "09/01/2017"}}
3749+
{-1e-10, "09/01/2017"},
3750+
}
37523751
f := prepareCalcData(cellData)
37533752
formulaList := map[string]string{
37543753
"=XIRR(A1:A4,B1:B4)": "-0.196743861298328",
@@ -3886,7 +3885,8 @@ func TestCalcXLOOKUP(t *testing.T) {
38863885
}
38873886

38883887
func TestCalcXNPV(t *testing.T) {
3889-
cellData := [][]interface{}{{nil, 0.05},
3888+
cellData := [][]interface{}{
3889+
{nil, 0.05},
38903890
{"01/01/2016", -10000, nil},
38913891
{"02/01/2016", 2000},
38923892
{"05/01/2016", 2400},
@@ -3895,7 +3895,8 @@ func TestCalcXNPV(t *testing.T) {
38953895
{"01/01/2017", 4100},
38963896
{},
38973897
{"02/01/2016"},
3898-
{"01/01/2016"}}
3898+
{"01/01/2016"},
3899+
}
38993900
f := prepareCalcData(cellData)
39003901
formulaList := map[string]string{
39013902
"=XNPV(B1,B2:B7,A2:A7)": "4447.938009440515",

cell_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ func TestConcurrency(t *testing.T) {
3030
_, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val))
3131
assert.NoError(t, err)
3232
// Concurrency set rows
33-
assert.NoError(t, f.SetSheetRow("Sheet1", "B6", &[]interface{}{" Hello",
33+
assert.NoError(t, f.SetSheetRow("Sheet1", "B6", &[]interface{}{
34+
" Hello",
3435
[]byte("World"), 42, int8(1<<8/2 - 1), int16(1<<16/2 - 1), int32(1<<32/2 - 1),
3536
int64(1<<32/2 - 1), float32(42.65418), float64(-42.65418), float32(42), float64(42),
3637
uint(1<<32 - 1), uint8(1<<8 - 1), uint16(1<<16 - 1), uint32(1<<32 - 1),
37-
uint64(1<<32 - 1), true, complex64(5 + 10i)}))
38+
uint64(1<<32 - 1), true, complex64(5 + 10i),
39+
}))
3840
// Concurrency create style
3941
style, err := f.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
4042
assert.NoError(t, err)
@@ -384,7 +386,7 @@ func TestGetCellFormula(t *testing.T) {
384386

385387
func ExampleFile_SetCellFloat() {
386388
f := NewFile()
387-
var x = 3.14159265
389+
x := 3.14159265
388390
if err := f.SetCellFloat("Sheet1", "A1", x, 2, 64); err != nil {
389391
fmt.Println(err)
390392
}
@@ -534,6 +536,7 @@ func TestGetCellRichText(t *testing.T) {
534536
_, err = f.GetCellRichText("Sheet1", "A")
535537
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
536538
}
539+
537540
func TestSetCellRichText(t *testing.T) {
538541
f := NewFile()
539542
assert.NoError(t, f.SetRowHeight("Sheet1", 1, 35))

comment.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (f *File) GetComments() (comments map[string][]Comment) {
7474
// getSheetComments provides the method to get the target comment reference by
7575
// given worksheet file path.
7676
func (f *File) getSheetComments(sheetFile string) string {
77-
var rels = "xl/worksheets/_rels/" + sheetFile + ".rels"
77+
rels := "xl/worksheets/_rels/" + sheetFile + ".rels"
7878
if sheetRels := f.relsReader(rels); sheetRels != nil {
7979
sheetRels.Lock()
8080
defer sheetRels.Unlock()

0 commit comments

Comments
 (0)