diff options
-rw-r--r-- | calc.go | 335 | ||||
-rw-r--r-- | calc_test.go | 101 | ||||
-rw-r--r-- | cell_test.go | 11 | ||||
-rw-r--r-- | col.go | 5 | ||||
-rw-r--r-- | col_test.go | 6 | ||||
-rw-r--r-- | crypt.go | 26 | ||||
-rw-r--r-- | drawing.go | 4 | ||||
-rw-r--r-- | excelize_test.go | 6 | ||||
-rw-r--r-- | file.go | 2 | ||||
-rw-r--r-- | file_test.go | 2 | ||||
-rw-r--r-- | lib.go | 2 | ||||
-rw-r--r-- | picture.go | 4 | ||||
-rw-r--r-- | pivotTable.go | 4 | ||||
-rw-r--r-- | rows.go | 4 | ||||
-rw-r--r-- | sheet.go | 4 | ||||
-rw-r--r-- | sheet_test.go | 6 | ||||
-rw-r--r-- | stream.go | 32 | ||||
-rw-r--r-- | stream_test.go | 4 | ||||
-rw-r--r-- | styles.go | 18 | ||||
-rw-r--r-- | table.go | 11 |
20 files changed, 462 insertions, 125 deletions
@@ -100,7 +100,6 @@ const ( // formulaArg is the argument of a formula or function. type formulaArg struct { - f *File SheetName string Number float64 String string @@ -164,6 +163,21 @@ func (fa formulaArg) ToBool() formulaArg { return newBoolFormulaArg(b) } +// ToList returns a formula argument with array data type. +func (fa formulaArg) ToList() []formulaArg { + if fa.Type == ArgMatrix { + list := []formulaArg{} + for _, row := range fa.Matrix { + list = append(list, row...) + } + return list + } + if fa.Type == ArgList { + return fa.List + } + return nil +} + // formulaFuncs is the type of the formula functions. type formulaFuncs struct { f *File @@ -201,8 +215,11 @@ var tokenPriority = map[string]int{ // ARABIC // ASIN // ASINH +// ATAN // ATAN2 // ATANH +// AVERAGE +// AVERAGEA // BASE // CEILING // CEILING.MATH @@ -211,11 +228,15 @@ var tokenPriority = map[string]int{ // CLEAN // COMBIN // COMBINA +// CONCAT +// CONCATENATE // COS // COSH // COT // COTH +// COUNT // COUNTA +// COUNTBLANK // CSC // CSCH // DATE @@ -254,6 +275,7 @@ var tokenPriority = map[string]int{ // LOG10 // LOOKUP // LOWER +// MAX // MDETERM // MEDIAN // MOD @@ -322,7 +344,7 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) { // getPriority calculate arithmetic operator priority. func getPriority(token efp.Token) (pri int) { - pri, _ = tokenPriority[token.TValue] + pri = tokenPriority[token.TValue] if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix { pri = 6 } @@ -962,7 +984,7 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, e err = errors.New(formulaErrorVALUE) } rng := []int{cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row} - sortCoordinates(rng) + _ = sortCoordinates(rng) cr.From.Col, cr.From.Row, cr.To.Col, cr.To.Row = rng[0], rng[1], rng[2], rng[3] prepareValueRange(cr, valueRange) if cr.From.Sheet != "" { @@ -1208,7 +1230,7 @@ func (fn *formulaFuncs) ARABIC(argsList *list.List) formulaArg { prefix = -1 continue } - digit, _ = charMap[char] + digit = charMap[char] val += digit switch { case last == digit && (last == 5 || last == 50 || last == 500): @@ -1950,22 +1972,18 @@ func (fn *formulaFuncs) GCD(argsList *list.List) formulaArg { var ( val float64 nums = []float64{} - err error ) for arg := argsList.Front(); arg != nil; arg = arg.Next() { token := arg.Value.(formulaArg) switch token.Type { case ArgString: - if token.String == "" { - continue - } - if val, err = strconv.ParseFloat(token.String, 64); err != nil { - return newErrorFormulaArg(formulaErrorVALUE, err.Error()) + num := token.ToNumber() + if num.Type == ArgError { + return num } - break + val = num.Number case ArgNumber: val = token.Number - break } nums = append(nums, val) } @@ -2083,10 +2101,8 @@ func (fn *formulaFuncs) LCM(argsList *list.List) formulaArg { if val, err = strconv.ParseFloat(token.String, 64); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - break case ArgNumber: val = token.Number - break } nums = append(nums, val) } @@ -2321,10 +2337,8 @@ func (fn *formulaFuncs) MULTINOMIAL(argsList *list.List) formulaArg { if val, err = strconv.ParseFloat(token.String, 64); err != nil { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } - break case ArgNumber: val = token.Number - break } num += val denom *= fact(val) @@ -2449,10 +2463,8 @@ func (fn *formulaFuncs) PRODUCT(argsList *list.List) formulaArg { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } product = product * val - break case ArgNumber: product = product * token.Number - break case ArgMatrix: for _, row := range token.Matrix { for _, value := range row { @@ -2934,10 +2946,8 @@ func (fn *formulaFuncs) SUMSQ(argsList *list.List) formulaArg { return newErrorFormulaArg(formulaErrorVALUE, err.Error()) } sq += val * val - break case ArgNumber: sq += token.Number - break case ArgMatrix: for _, row := range token.Matrix { for _, value := range row { @@ -3023,7 +3033,98 @@ func (fn *formulaFuncs) TRUNC(argsList *list.List) formulaArg { return newNumberFormulaArg(float64(int(number.Number*adjust)) / adjust) } -// Statistical functions +// Statistical Functions + +// AVERAGE function returns the arithmetic mean of a list of supplied numbers. +// The syntax of the function is: +// +// AVERAGE(number1,[number2],...) +// +func (fn *formulaFuncs) AVERAGE(argsList *list.List) formulaArg { + args := []formulaArg{} + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + args = append(args, arg.Value.(formulaArg)) + } + count, sum := fn.countSum(false, args) + if count == 0 { + return newErrorFormulaArg(formulaErrorDIV, "AVERAGE divide by zero") + } + return newNumberFormulaArg(sum / count) +} + +// AVERAGEA function returns the arithmetic mean of a list of supplied numbers +// with text cell and zero values. The syntax of the function is: +// +// AVERAGEA(number1,[number2],...) +// +func (fn *formulaFuncs) AVERAGEA(argsList *list.List) formulaArg { + args := []formulaArg{} + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + args = append(args, arg.Value.(formulaArg)) + } + count, sum := fn.countSum(true, args) + if count == 0 { + return newErrorFormulaArg(formulaErrorDIV, "AVERAGEA divide by zero") + } + return newNumberFormulaArg(sum / count) +} + +// countSum get count and sum for a formula arguments array. +func (fn *formulaFuncs) countSum(countText bool, args []formulaArg) (count, sum float64) { + for _, arg := range args { + switch arg.Type { + case ArgNumber: + if countText || !arg.Boolean { + sum += arg.Number + count++ + } + case ArgString: + num := arg.ToNumber() + if countText && num.Type == ArgError && arg.String != "" { + count++ + } + if num.Type == ArgNumber { + sum += num.Number + count++ + } + case ArgList, ArgMatrix: + cnt, summary := fn.countSum(countText, arg.ToList()) + sum += summary + count += cnt + } + } + return +} + +// COUNT function returns the count of numeric values in a supplied set of +// cells or values. This count includes both numbers and dates. The syntax of +// the function is: +// +// COUNT(value1,[value2],...) +// +func (fn *formulaFuncs) COUNT(argsList *list.List) formulaArg { + var count int + for token := argsList.Front(); token != nil; token = token.Next() { + arg := token.Value.(formulaArg) + switch arg.Type { + case ArgString: + if arg.ToNumber().Type != ArgError { + count++ + } + case ArgNumber: + count++ + case ArgMatrix: + for _, row := range arg.Matrix { + for _, value := range row { + if value.ToNumber().Type != ArgError { + count++ + } + } + } + } + } + return newNumberFormulaArg(float64(count)) +} // COUNTA function returns the number of non-blanks within a supplied set of // cells or values. The syntax of the function is: @@ -3039,17 +3140,135 @@ func (fn *formulaFuncs) COUNTA(argsList *list.List) formulaArg { if arg.String != "" { count++ } + case ArgNumber: + count++ case ArgMatrix: - for _, row := range arg.Matrix { - for _, value := range row { - if value.String != "" { + for _, row := range arg.ToList() { + switch row.Type { + case ArgString: + if row.String != "" { count++ } + case ArgNumber: + count++ + } + } + } + } + return newNumberFormulaArg(float64(count)) +} + +// COUNTBLANK function returns the number of blank cells in a supplied range. +// The syntax of the function is: +// +// COUNTBLANK(range) +// +func (fn *formulaFuncs) COUNTBLANK(argsList *list.List) formulaArg { + if argsList.Len() != 1 { + return newErrorFormulaArg(formulaErrorVALUE, "COUNTBLANK requires 1 argument") + } + var count int + token := argsList.Front().Value.(formulaArg) + switch token.Type { + case ArgString: + if token.String == "" { + count++ + } + case ArgList, ArgMatrix: + for _, row := range token.ToList() { + switch row.Type { + case ArgString: + if row.String == "" { + count++ + } + case ArgEmpty: + count++ + } + } + case ArgEmpty: + count++ + } + return newNumberFormulaArg(float64(count)) +} + +// MAX function returns the largest value from a supplied set of numeric +// values. The syntax of the function is: +// +// MAX(number1,[number2],...) +// +func (fn *formulaFuncs) MAX(argsList *list.List) formulaArg { + if argsList.Len() == 0 { + return newErrorFormulaArg(formulaErrorVALUE, "MAX requires at least 1 argument") + } + return fn.max(false, argsList) +} + +// MAXA function returns the largest value from a supplied set of numeric values, while counting text and the logical value FALSE as the value 0 and counting the logical value TRUE as the value 1. The syntax of the function is: +// +// MAXA(number1,[number2],...) +// +func (fn *formulaFuncs) MAXA(argsList *list.List) formulaArg { + if argsList.Len() == 0 { + return newErrorFormulaArg(formulaErrorVALUE, "MAXA requires at least 1 argument") + } + return fn.max(true, argsList) +} + +// max is an implementation of the formula function MAX and MAXA. +func (fn *formulaFuncs) max(maxa bool, argsList *list.List) formulaArg { + max := -math.MaxFloat64 + for token := argsList.Front(); token != nil; token = token.Next() { + arg := token.Value.(formulaArg) + switch arg.Type { + case ArgString: + if !maxa && (arg.Value() == "TRUE" || arg.Value() == "FALSE") { + continue + } else { + num := arg.ToBool() + if num.Type == ArgNumber && num.Number > max { + max = num.Number + continue + } + } + num := arg.ToNumber() + if num.Type != ArgError && num.Number > max { + max = num.Number + } + case ArgNumber: + if arg.Number > max { + max = arg.Number + } + case ArgList, ArgMatrix: + for _, row := range arg.ToList() { + switch row.Type { + case ArgString: + if !maxa && (row.Value() == "TRUE" || row.Value() == "FALSE") { + continue + } else { + num := row.ToBool() + if num.Type == ArgNumber && num.Number > max { + max = num.Number + continue + } + } + num := row.ToNumber() + if num.Type != ArgError && num.Number > max { + max = num.Number + } + case ArgNumber: + if row.Number > max { + max = row.Number + } } } + case ArgError: + return arg } } - return newStringFormulaArg(fmt.Sprintf("%d", count)) + if max == -math.MaxFloat64 { + max = 0 + } + return newNumberFormulaArg(max) } // MEDIAN function returns the statistical median (the middle value) of a list @@ -3068,14 +3287,13 @@ func (fn *formulaFuncs) MEDIAN(argsList *list.List) formulaArg { arg := token.Value.(formulaArg) switch arg.Type { case ArgString: - if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { - return newErrorFormulaArg(formulaErrorVALUE, err.Error()) + num := arg.ToNumber() + if num.Type == ArgError { + return newErrorFormulaArg(formulaErrorVALUE, num.Error) } - values = append(values, digits) - break + values = append(values, num.Number) case ArgNumber: values = append(values, arg.Number) - break case ArgMatrix: for _, row := range arg.Matrix { for _, value := range row { @@ -3099,7 +3317,7 @@ func (fn *formulaFuncs) MEDIAN(argsList *list.List) formulaArg { return newNumberFormulaArg(median) } -// Information functions +// Information Functions // ISBLANK function tests if a specified cell is blank (empty) and if so, // returns TRUE; Otherwise the function returns FALSE. The syntax of the @@ -3137,7 +3355,7 @@ func (fn *formulaFuncs) ISERR(argsList *list.List) formulaArg { } token := argsList.Front().Value.(formulaArg) result := "FALSE" - if token.Type == ArgString { + if token.Type == ArgError { for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} { if errType == token.String { result = "TRUE" @@ -3159,7 +3377,7 @@ func (fn *formulaFuncs) ISERROR(argsList *list.List) formulaArg { } token := argsList.Front().Value.(formulaArg) result := "FALSE" - if token.Type == ArgString { + if token.Type == ArgError { for _, errType := range []string{formulaErrorDIV, formulaErrorNAME, formulaErrorNA, formulaErrorNUM, formulaErrorVALUE, formulaErrorREF, formulaErrorNULL, formulaErrorSPILL, formulaErrorCALC, formulaErrorGETTINGDATA} { if errType == token.String { result = "TRUE" @@ -3208,7 +3426,7 @@ func (fn *formulaFuncs) ISNA(argsList *list.List) formulaArg { } token := argsList.Front().Value.(formulaArg) result := "FALSE" - if token.Type == ArgString && token.String == formulaErrorNA { + if token.Type == ArgError && token.String == formulaErrorNA { result = "TRUE" } return newStringFormulaArg(result) @@ -3304,7 +3522,7 @@ func (fn *formulaFuncs) NA(argsList *list.List) formulaArg { if argsList.Len() != 0 { return newErrorFormulaArg(formulaErrorVALUE, "NA accepts no arguments") } - return newStringFormulaArg(formulaErrorNA) + return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) } // SHEET function returns the Sheet number for a specified reference. The @@ -3536,6 +3754,49 @@ func (fn *formulaFuncs) CLEAN(argsList *list.List) formulaArg { return newStringFormulaArg(b.String()) } +// CONCAT function joins together a series of supplied text strings into one +// combined text string. +// +// CONCAT(text1,[text2],...) +// +func (fn *formulaFuncs) CONCAT(argsList *list.List) formulaArg { + return fn.concat("CONCAT", argsList) +} + +// CONCATENATE function joins together a series of supplied text strings into +// one combined text string. +// +// CONCATENATE(text1,[text2],...) +// +func (fn *formulaFuncs) CONCATENATE(argsList *list.List) formulaArg { + return fn.concat("CONCATENATE", argsList) +} + +// concat is an implementation of the formula function CONCAT and CONCATENATE. +func (fn *formulaFuncs) concat(name string, argsList *list.List) formulaArg { + buf := bytes.Buffer{} + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + token := arg.Value.(formulaArg) + switch token.Type { + case ArgString: + buf.WriteString(token.String) + case ArgNumber: + if token.Boolean { + if token.Number == 0 { + buf.WriteString("FALSE") + } else { + buf.WriteString("TRUE") + } + } else { + buf.WriteString(token.Value()) + } + default: + return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires arguments to be strings", name)) + } + } + return newStringFormulaArg(buf.String()) +} + // EXACT function tests if two supplied text strings or values are exactly // equal and if so, returns TRUE; Otherwise, the function returns FALSE. The // function is case-sensitive. The syntax of the function is: @@ -4142,7 +4403,9 @@ func lookupCol(arr formulaArg) []formulaArg { // Web Functions -// ENCODEURL function returns a URL-encoded string, replacing certain non-alphanumeric characters with the percentage symbol (%) and a hexadecimal number. The syntax of the function is: +// ENCODEURL function returns a URL-encoded string, replacing certain +// non-alphanumeric characters with the percentage symbol (%) and a +// hexadecimal number. The syntax of the function is: // // ENCODEURL(url) // diff --git a/calc_test.go b/calc_test.go index 437a7b5..ef028a9 100644 --- a/calc_test.go +++ b/calc_test.go @@ -15,7 +15,7 @@ func prepareCalcData(cellData [][]interface{}) *File { for r, row := range cellData { for c, value := range row { cell, _ := CoordinatesToCellName(c+1, r+1) - f.SetCellValue("Sheet1", cell, value) + _ = f.SetCellValue("Sheet1", cell, value) } } return f @@ -245,7 +245,6 @@ func TestCalcCellValue(t *testing.T) { "=_xlfn.FLOOR.PRECISE(_xlfn.FLOOR.PRECISE(26.75),-5)": "25", // GCD "=GCD(0)": "0", - `=GCD("",1)`: "1", "=GCD(1,0)": "1", "=GCD(1,5)": "1", "=GCD(15,10,25)": "5", @@ -469,10 +468,43 @@ func TestCalcCellValue(t *testing.T) { "=TRUNC(-99.999,-1)": "-90", "=TRUNC(TRUNC(1),-1)": "0", // Statistical Functions + // AVERAGE + "=AVERAGE(INT(1))": "1", + "=AVERAGE(A1)": "1", + "=AVERAGE(A1:A2)": "1.5", + "=AVERAGE(D2:F9)": "38014.125", + // AVERAGEA + "=AVERAGEA(INT(1))": "1", + "=AVERAGEA(A1)": "1", + "=AVERAGEA(A1:A2)": "1.5", + "=AVERAGEA(D2:F9)": "12671.375", + // COUNT + "=COUNT()": "0", + "=COUNT(E1:F2,\"text\",1,INT(2))": "3", // COUNTA - `=COUNTA()`: "0", - `=COUNTA(A1:A5,B2:B5,"text",1,2)`: "8", - `=COUNTA(COUNTA(1))`: "1", + "=COUNTA()": "0", + "=COUNTA(A1:A5,B2:B5,\"text\",1,INT(2))": "8", + "=COUNTA(COUNTA(1),MUNIT(1))": "2", + // COUNTBLANK + "=COUNTBLANK(MUNIT(1))": "0", + "=COUNTBLANK(1)": "0", + "=COUNTBLANK(B1:C1)": "1", + "=COUNTBLANK(C1)": "1", + // MAX + "=MAX(1)": "1", + "=MAX(TRUE())": "1", + "=MAX(0.5,TRUE())": "1", + "=MAX(FALSE())": "0", + "=MAX(MUNIT(2))": "1", + "=MAX(INT(1))": "1", + // MAXA + "=MAXA(1)": "1", + "=MAXA(TRUE())": "1", + "=MAXA(0.5,TRUE())": "1", + "=MAXA(FALSE())": "0", + "=MAXA(MUNIT(2))": "1", + "=MAXA(INT(1))": "1", + "=MAXA(A1:B4,MUNIT(1),INT(0),1,E1:F2,\"\")": "36693", // MEDIAN "=MEDIAN(A1:A5,12)": "2", "=MEDIAN(A1:A5)": "1.5", @@ -482,8 +514,9 @@ func TestCalcCellValue(t *testing.T) { "=ISBLANK(A1)": "FALSE", "=ISBLANK(A5)": "TRUE", // ISERR - "=ISERR(A1)": "FALSE", - "=ISERR(NA())": "FALSE", + "=ISERR(A1)": "FALSE", + "=ISERR(NA())": "FALSE", + "=ISERR(POWER(0,-1)))": "TRUE", // ISERROR "=ISERROR(A1)": "FALSE", "=ISERROR(NA())": "TRUE", @@ -497,7 +530,7 @@ func TestCalcCellValue(t *testing.T) { "=ISNONTEXT(A1)": "FALSE", "=ISNONTEXT(A5)": "TRUE", `=ISNONTEXT("Excelize")`: "FALSE", - "=ISNONTEXT(NA())": "FALSE", + "=ISNONTEXT(NA())": "TRUE", // ISNUMBER "=ISNUMBER(A1)": "TRUE", "=ISNUMBER(D1)": "FALSE", @@ -507,8 +540,6 @@ func TestCalcCellValue(t *testing.T) { // ISTEXT "=ISTEXT(D1)": "TRUE", "=ISTEXT(A1)": "FALSE", - // NA - "=NA()": "#N/A", // SHEET "SHEET()": "1", // Logical Functions @@ -547,6 +578,10 @@ func TestCalcCellValue(t *testing.T) { // CLEAN "=CLEAN(\"\u0009clean text\")": "clean text", "=CLEAN(0)": "0", + // CONCAT + "=CONCAT(TRUE(),1,FALSE(),\"0\",INT(2))": "TRUE1FALSE02", + // CONCATENATE + "=CONCATENATE(TRUE(),1,FALSE(),\"0\",INT(2))": "TRUE1FALSE02", // EXACT "=EXACT(1,\"1\")": "TRUE", "=EXACT(1,1)": "TRUE", @@ -756,6 +791,7 @@ func TestCalcCellValue(t *testing.T) { `=_xlfn.FLOOR.PRECISE(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // GCD "=GCD()": "GCD requires at least 1 argument", + "=GCD(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", "=GCD(-1)": "GCD only accepts positive arguments", "=GCD(1,-1)": "GCD only accepts positive arguments", `=GCD("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", @@ -897,8 +933,22 @@ func TestCalcCellValue(t *testing.T) { `=TRUNC("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", `=TRUNC(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // Statistical Functions + // AVERAGE + "=AVERAGE(H1)": "AVERAGE divide by zero", + // AVERAGE + "=AVERAGEA(H1)": "AVERAGEA divide by zero", + // COUNTBLANK + "=COUNTBLANK()": "COUNTBLANK requires 1 argument", + "=COUNTBLANK(1,2)": "COUNTBLANK requires 1 argument", + // MAX + "=MAX()": "MAX requires at least 1 argument", + "=MAX(NA())": "#N/A", + // MAXA + "=MAXA()": "MAXA requires at least 1 argument", + "=MAXA(NA())": "#N/A", // MEDIAN "=MEDIAN()": "MEDIAN requires at least 1 argument", + "=MEDIAN(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", "=MEDIAN(D1:D2)": "strconv.ParseFloat: parsing \"Month\": invalid syntax", // Information Functions // ISBLANK @@ -922,6 +972,7 @@ func TestCalcCellValue(t *testing.T) { // ISTEXT "=ISTEXT()": "ISTEXT requires 1 argument", // NA + "=NA()": "#N/A", "=NA(1)": "NA accepts no arguments", // SHEET "=SHEET(1)": "SHEET accepts no arguments", @@ -956,6 +1007,10 @@ func TestCalcCellValue(t *testing.T) { // CLEAN "=CLEAN()": "CLEAN requires 1 argument", "=CLEAN(1,2)": "CLEAN requires 1 argument", + // CONCAT + "=CONCAT(MUNIT(2))": "CONCAT requires arguments to be strings", + // CONCATENATE + "=CONCATENATE(MUNIT(2))": "CONCATENATE requires arguments to be strings", // EXACT "=EXACT()": "EXACT requires 2 arguments", "=EXACT(1,2,3)": "EXACT requires 2 arguments", @@ -1197,6 +1252,13 @@ func TestCalcToBool(t *testing.T) { assert.Equal(t, b.Boolean, true) assert.Equal(t, b.Number, 1.0) } + +func TestCalcToList(t *testing.T) { + assert.Equal(t, []formulaArg(nil), newEmptyFormulaArg().ToList()) + formulaList := []formulaArg{newEmptyFormulaArg()} + assert.Equal(t, formulaList, newListFormulaArg(formulaList).ToList()) +} + func TestCalcCompareFormulaArg(t *testing.T) { assert.Equal(t, compareFormulaArg(newEmptyFormulaArg(), newEmptyFormulaArg(), false, false), criteriaEq) lhs := newListFormulaArg([]formulaArg{newEmptyFormulaArg()}) @@ -1253,6 +1315,25 @@ func TestCalcVLOOKUP(t *testing.T) { } } +func TestCalcMAX(t *testing.T) { + cellData := [][]interface{}{ + {0.5, "TRUE"}, + } + f := prepareCalcData(cellData) + formulaList := map[string]string{ + "=MAX(0.5,B1)": "0.5", + "=MAX(A1:B1)": "0.5", + "=MAXA(A1:B1)": "1", + "=MAXA(0.5,B1)": "1", + } + for formula, expected := range formulaList { + assert.NoError(t, f.SetCellFormula("Sheet1", "B10", formula)) + result, err := f.CalcCellValue("Sheet1", "B10") + assert.NoError(t, err, formula) + assert.Equal(t, expected, result, formula) + } +} + func TestCalcHLOOKUP(t *testing.T) { cellData := [][]interface{}{ {"Example Result Table"}, diff --git a/cell_test.go b/cell_test.go index 93e9f4c..c3c20f7 100644 --- a/cell_test.go +++ b/cell_test.go @@ -16,12 +16,13 @@ func TestConcurrency(t *testing.T) { wg := new(sync.WaitGroup) for i := 1; i <= 5; i++ { wg.Add(1) - go func(val int) { - f.SetCellValue("Sheet1", fmt.Sprintf("A%d", val), val) - f.SetCellValue("Sheet1", fmt.Sprintf("B%d", val), strconv.Itoa(val)) - f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val)) + go func(val int, t *testing.T) { + assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", val), val)) + assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", val), strconv.Itoa(val))) + _, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val)) + assert.NoError(t, err) wg.Done() - }(i) + }(i, t) } wg.Wait() val, err := f.GetCellValue("Sheet1", "A1") @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -35,7 +35,6 @@ type Cols struct { err error curCol, totalCol, stashCol, totalRow int sheet string - cols []xlsxCols f *File sheetXML []byte } @@ -140,7 +139,6 @@ func (cols *Cols) Rows() ([]string, error) { // columnXMLIterator defined runtime use field for the worksheet column SAX parser. type columnXMLIterator struct { err error - inElement string cols Cols cellCol, curRow, row int } @@ -175,7 +173,6 @@ func columnXMLHandler(colIterator *columnXMLIterator, xmlElement *xml.StartEleme colIterator.cols.totalCol = colIterator.cellCol } } - return } // Cols returns a columns iterator, used for streaming reading data for a diff --git a/col_test.go b/col_test.go index 97c4b7f..706f90a 100644 --- a/col_test.go +++ b/col_test.go @@ -138,7 +138,7 @@ func TestColsRows(t *testing.T) { f := NewFile() f.NewSheet("Sheet1") - cols, err := f.Cols("Sheet1") + _, err := f.Cols("Sheet1") assert.NoError(t, err) assert.NoError(t, f.SetCellValue("Sheet1", "A1", 1)) @@ -150,12 +150,12 @@ func TestColsRows(t *testing.T) { f = NewFile() f.XLSX["xl/worksheets/sheet1.xml"] = nil - cols, err = f.Cols("Sheet1") + _, err = f.Cols("Sheet1") if !assert.NoError(t, err) { t.FailNow() } f = NewFile() - cols, err = f.Cols("Sheet1") + cols, err := f.Cols("Sheet1") if !assert.NoError(t, err) { t.FailNow() } @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -42,14 +42,7 @@ var ( packageOffset = 8 // First 8 bytes are the size of the stream packageEncryptionChunkSize = 4096 iterCount = 50000 - cryptoIdentifier = []byte{ // checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier - 0x3c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, - 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, - 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x44, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x61, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - } - oleIdentifier = []byte{ + oleIdentifier = []byte{ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1, } ) @@ -153,7 +146,6 @@ func Decrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { return standardDecrypt(encryptionInfoBuf, encryptedPackageBuf, opt) default: err = errors.New("unsupport encryption mechanism") - break } return } @@ -209,11 +201,11 @@ func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { return } // Use the package key and the IV to encrypt the HMAC key. - encryptedHmacKey, err := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, packageKey, hmacKeyIV, hmacKey) + encryptedHmacKey, _ := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, packageKey, hmacKeyIV, hmacKey) // Create the HMAC. h := hmac.New(sha512.New, append(hmacKey, encryptedPackage...)) for _, buf := range [][]byte{hmacKey, encryptedPackage} { - h.Write(buf) + _, _ = h.Write(buf) } hmacValue := h.Sum(nil) // Generate an initialization vector for encrypting the resulting HMAC value. @@ -222,7 +214,7 @@ func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { return } // Encrypt the value. - encryptedHmacValue, err := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, packageKey, hmacValueIV, hmacValue) + encryptedHmacValue, _ := crypt(true, encryptionInfo.KeyData.CipherAlgorithm, encryptionInfo.KeyData.CipherChaining, packageKey, hmacValueIV, hmacValue) // Put the encrypted key and value on the encryption info. encryptionInfo.DataIntegrity.EncryptedHmacKey = base64.StdEncoding.EncodeToString(encryptedHmacKey) encryptionInfo.DataIntegrity.EncryptedHmacValue = base64.StdEncoding.EncodeToString(encryptedHmacValue) @@ -235,7 +227,7 @@ func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) { return } // Encrypt the package key with the encryption key. - encryptedKeyValue, err := crypt(true, encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.CipherAlgorithm, encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.CipherChaining, key, keyEncryptors, packageKey) + encryptedKeyValue, _ := crypt(true, encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.CipherAlgorithm, encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.CipherChaining, key, keyEncryptors, packageKey) encryptionInfo.KeyEncryptors.KeyEncryptor[0].EncryptedKey.EncryptedKeyValue = base64.StdEncoding.EncodeToString(encryptedKeyValue) // Verifier hash @@ -412,7 +404,7 @@ func standardConvertPasswdToKey(header StandardEncryptionHeader, verifier Standa // standardXORBytes perform XOR operations for two bytes slice. func standardXORBytes(a, b []byte) []byte { - r := make([][2]byte, len(a), len(a)) + r := make([][2]byte, len(a)) for i, e := range a { r[i] = [2]byte{e, b[i]} } @@ -447,7 +439,7 @@ func agileDecrypt(encryptionInfoBuf, encryptedPackageBuf []byte, opt *Options) ( if err != nil { return } - packageKey, err := crypt(false, encryptedKey.CipherAlgorithm, encryptedKey.CipherChaining, key, saltValue, encryptedKeyValue) + packageKey, _ := crypt(false, encryptedKey.CipherAlgorithm, encryptedKey.CipherChaining, key, saltValue, encryptedKeyValue) // Use the package key to decrypt the package. return cryptPackage(false, packageKey, encryptedPackageBuf, encryptionInfo) } @@ -503,7 +495,7 @@ func hashing(hashAlgorithm string, buffer ...[]byte) (key []byte) { return key } for _, buf := range buffer { - handler.Write(buf) + _, _ = handler.Write(buf) } key = handler.Sum(nil) return key @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -51,7 +51,6 @@ func (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet cs.Drawing = &xlsxDrawing{ RID: "rId" + strconv.Itoa(rID), } - return } // addChart provides a function to create chart as xl/charts/chart%d.xml by @@ -1272,7 +1271,6 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma } content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor) f.Drawings[drawingXML] = content - return } // deleteDrawing provides a function to delete chart graphic frame by given by diff --git a/excelize_test.go b/excelize_test.go index 1b48872..8bce6d1 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -542,10 +542,10 @@ func TestWriteArrayFormula(t *testing.T) { assert.NoError(t, f.SetCellFormula("Sheet1", avgCell, fmt.Sprintf("ROUND(AVERAGEIF(%s,%s,%s),0)", assocRange, nameCell, valRange))) ref := stdevCell + ":" + stdevCell - t := STCellFormulaTypeArray + arr := STCellFormulaTypeArray // Use an array formula for standard deviation - f.SetCellFormula("Sheet1", stdevCell, fmt.Sprintf("ROUND(STDEVP(IF(%s=%s,%s)),0)", assocRange, nameCell, valRange), - FormulaOpts{}, FormulaOpts{Type: &t}, FormulaOpts{Ref: &ref}) + assert.NoError(t, f.SetCellFormula("Sheet1", stdevCell, fmt.Sprintf("ROUND(STDEVP(IF(%s=%s,%s)),0)", assocRange, nameCell, valRange), + FormulaOpts{}, FormulaOpts{Type: &arr}, FormulaOpts{Ref: &ref})) } assert.NoError(t, f.SaveAs(filepath.Join("test", "TestWriteArrayFormula.xlsx"))) @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // diff --git a/file_test.go b/file_test.go index 0f979b8..656271f 100644 --- a/file_test.go +++ b/file_test.go @@ -35,7 +35,7 @@ func BenchmarkWrite(b *testing.B) { func TestWriteTo(t *testing.T) { f := File{} buf := bytes.Buffer{} - f.XLSX = make(map[string][]byte, 0) + f.XLSX = make(map[string][]byte) f.XLSX["/d/"] = []byte("s") _, err := f.WriteTo(bufio.NewWriter(&buf)) assert.EqualError(t, err, "zip: write to directory") @@ -421,7 +421,7 @@ func (f *File) setIgnorableNameSpace(path string, index int, ns xml.Attr) { // addSheetNameSpace add XML attribute for worksheet. func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) { - name, _ := f.sheetMap[trimSheetName(sheet)] + name := f.sheetMap[trimSheetName(sheet)] f.addNameSpaces(name, ns) } @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -613,7 +613,7 @@ func (f *File) drawingResize(sheet string, cell string, width, height float64, f } if inMergeCell { rng, _ = areaRangeToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis()) - sortCoordinates(rng) + _ = sortCoordinates(rng) } } if inMergeCell { diff --git a/pivotTable.go b/pivotTable.go index bffda17..ff21ac1 100644 --- a/pivotTable.go +++ b/pivotTable.go @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -615,7 +615,7 @@ func (f *File) getPivotTableFieldsSubtotal(fields []PivotTableField) []string { enums := []string{"average", "count", "countNums", "max", "min", "product", "stdDev", "stdDevp", "sum", "var", "varp"} inEnums := func(enums []string, val string) string { for _, enum := range enums { - if strings.ToLower(enum) == strings.ToLower(val) { + if strings.EqualFold(enum, val) { return enum } } @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -60,7 +60,6 @@ type Rows struct { err error curRow, totalRow, stashRow int sheet string - rows []xlsxRow f *File decoder *xml.Decoder } @@ -165,7 +164,6 @@ func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement) { val, _ := colCell.getValueFrom(rowIterator.rows.f, rowIterator.d) rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val) } - return } // Rows returns a rows iterator, used for streaming reading data for a @@ -610,8 +610,8 @@ func (f *File) copySheet(from, to int) error { if ok { f.XLSX[toRels] = f.XLSX[fromRels] } - fromSheetXMLPath, _ := f.sheetMap[trimSheetName(fromSheet)] - fromSheetAttr, _ := f.xmlAttr[fromSheetXMLPath] + fromSheetXMLPath := f.sheetMap[trimSheetName(fromSheet)] + fromSheetAttr := f.xmlAttr[fromSheetXMLPath] f.xmlAttr[path] = fromSheetAttr return err } diff --git a/sheet_test.go b/sheet_test.go index f218da7..a721472 100644 --- a/sheet_test.go +++ b/sheet_test.go @@ -409,7 +409,7 @@ func newSheetWithSet() { file := NewFile() file.NewSheet("sheet1") for i := 0; i < 1000; i++ { - file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i) + _ = file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i) } file = nil } @@ -426,7 +426,7 @@ func newSheetWithSave() { file := NewFile() file.NewSheet("sheet1") for i := 0; i < 1000; i++ { - file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i) + _ = file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i) } - file.Save() + _ = file.Save() } @@ -1,4 +1,4 @@ -// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in // the LICENSE file. // @@ -93,9 +93,9 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) { } f.streams[sheetXML] = sw - sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap) + _, _ = sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap) bulkAppendFields(&sw.rawData, sw.worksheet, 2, 6) - sw.rawData.WriteString(`<sheetData>`) + _, _ = sw.rawData.WriteString(`<sheetData>`) return sw, err } @@ -184,7 +184,7 @@ func (sw *StreamWriter) AddTable(hcell, vcell, format string) error { tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1) // Add first table for given sheet. - sheetPath, _ := sw.File.sheetMap[trimSheetName(sw.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, "") @@ -296,12 +296,12 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}) error { val = v.Value } if err = setCellValFunc(&c, val); err != nil { - sw.rawData.WriteString(`</row>`) + _, _ = sw.rawData.WriteString(`</row>`) return err } writeCell(&sw.rawData, c) } - sw.rawData.WriteString(`</row>`) + _, _ = sw.rawData.WriteString(`</row>`) return sw.rawData.Sync() } @@ -361,7 +361,7 @@ func setCellIntFunc(c *xlsxC, val interface{}) (err error) { } func writeCell(buf *bufferedWriter, c xlsxC) { - buf.WriteString(`<c`) + _, _ = buf.WriteString(`<c`) if c.XMLSpace.Value != "" { fmt.Fprintf(buf, ` xml:%s="%s"`, c.XMLSpace.Name.Local, c.XMLSpace.Value) } @@ -372,22 +372,22 @@ func writeCell(buf *bufferedWriter, c xlsxC) { if c.T != "" { fmt.Fprintf(buf, ` t="%s"`, c.T) } - buf.WriteString(`>`) + _, _ = buf.WriteString(`>`) if c.V != "" { - buf.WriteString(`<v>`) - xml.EscapeText(buf, []byte(c.V)) - buf.WriteString(`</v>`) + _, _ = buf.WriteString(`<v>`) + _ = xml.EscapeText(buf, []byte(c.V)) + _, _ = buf.WriteString(`</v>`) } - buf.WriteString(`</c>`) + _, _ = buf.WriteString(`</c>`) } // Flush ending the streaming writing process. func (sw *StreamWriter) Flush() error { - sw.rawData.WriteString(`</sheetData>`) + _, _ = sw.rawData.WriteString(`</sheetData>`) bulkAppendFields(&sw.rawData, sw.worksheet, 8, 38) - sw.rawData.WriteString(sw.tableParts) + _, _ = sw.rawData.WriteString(sw.tableParts) bulkAppendFields(&sw.rawData, sw.worksheet, 40, 40) - sw.rawData.WriteString(`</worksheet>`) + _, _ = sw.rawData.WriteString(`</worksheet>`) if err := sw.rawData.Flush(); err != nil { return err } @@ -407,7 +407,7 @@ func bulkAppendFields(w io.Writer, ws *xlsxWorksheet, from, to int) { enc := xml.NewEncoder(w) for i := 0; i < s.NumField(); i++ { if from <= i && i <= to { - enc.Encode(s.Field(i).Interface()) + _ = enc.Encode(s.Field(i).Interface()) } } } diff --git a/stream_test.go b/stream_test.go index ec7bd08..7c6eb9b 100644 --- a/stream_test.go +++ b/stream_test.go @@ -26,7 +26,7 @@ func BenchmarkStreamWriter(b *testing.B) { streamWriter, _ := file.NewStreamWriter("Sheet1") for rowID := 10; rowID <= 110; rowID++ { cell, _ := CoordinatesToCellName(1, rowID) - streamWriter.SetRow(cell, row) + _ = streamWriter.SetRow(cell, row) } } @@ -98,7 +98,7 @@ func TestStreamWriter(t *testing.T) { file = NewFile() delete(file.Sheet, "xl/worksheets/sheet1.xml") file.XLSX["xl/worksheets/sheet1.xml"] = MacintoshCyrillicCharset - streamWriter, err = file.NewStreamWriter("Sheet1") + _, err = file.NewStreamWriter("Sheet1") assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8") } @@ -2043,33 +2043,33 @@ var getXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{ }, "font": func(fontID int, xf xlsxXf, style *Style) bool { if style.Font == nil { - return (xf.FontID == nil || *xf.FontID == 0) && (xf.ApplyFont == nil || *xf.ApplyFont == false) + return (xf.FontID == nil || *xf.FontID == 0) && (xf.ApplyFont == nil || !*xf.ApplyFont) } - return xf.FontID != nil && *xf.FontID == fontID && xf.ApplyFont != nil && *xf.ApplyFont == true + return xf.FontID != nil && *xf.FontID == fontID && xf.ApplyFont != nil && *xf.ApplyFont }, "fill": func(fillID int, xf xlsxXf, style *Style) bool { if style.Fill.Type == "" { - return (xf.FillID == nil || *xf.FillID == 0) && (xf.ApplyFill == nil || *xf.ApplyFill == false) + return (xf.FillID == nil || *xf.FillID == 0) && (xf.ApplyFill == nil || !*xf.ApplyFill) } - return xf.FillID != nil && *xf.FillID == fillID && xf.ApplyFill != nil && *xf.ApplyFill == true + return xf.FillID != nil && *xf.FillID == fillID && xf.ApplyFill != nil && *xf.ApplyFill }, "border": func(borderID int, xf xlsxXf, style *Style) bool { if len(style.Border) == 0 { - return (xf.BorderID == nil || *xf.BorderID == 0) && (xf.ApplyBorder == nil || *xf.ApplyBorder == false) + return (xf.BorderID == nil || *xf.BorderID == 0) && (xf.ApplyBorder == nil || !*xf.ApplyBorder) } - return xf.BorderID != nil && *xf.BorderID == borderID && xf.ApplyBorder != nil && *xf.ApplyBorder == true + return xf.BorderID != nil && *xf.BorderID == borderID && xf.ApplyBorder != nil && *xf.ApplyBorder }, "alignment": func(ID int, xf xlsxXf, style *Style) bool { if style.Alignment == nil { - return xf.ApplyAlignment == nil || *xf.ApplyAlignment == false + return xf.ApplyAlignment == nil || !*xf.ApplyAlignment } return reflect.DeepEqual(xf.Alignment, newAlignment(style)) }, "protection": func(ID int, xf xlsxXf, style *Style) bool { if style.Protection == nil { - return xf.ApplyProtection == nil || *xf.ApplyProtection == false + return xf.ApplyProtection == nil || !*xf.ApplyProtection } - return reflect.DeepEqual(xf.Protection, newProtection(style)) && xf.ApplyProtection != nil && *xf.ApplyProtection == true + return reflect.DeepEqual(xf.Protection, newProtection(style)) && xf.ApplyProtection != nil && *xf.ApplyProtection }, } @@ -39,7 +39,14 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) { // // Create a table of F2:H6 on Sheet2 with format set: // -// err := f.AddTable("Sheet2", "F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`) +// err := f.AddTable("Sheet2", "F2", "H6", `{ +// "table_name": "table", +// "table_style": "TableStyleMedium2", +// "show_first_column": true, +// "show_last_column": true, +// "show_row_stripes": false, +// "show_column_stripes": true +// }`) // // Note that the table must be at least two lines including the header. The // header cells must contain strings and must be unique, and must set the @@ -153,7 +160,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet } if name == "" { name = "Column" + strconv.Itoa(idx) - f.SetCellStr(sheet, cell, name) + _ = f.SetCellStr(sheet, cell, name) } tableColumn = append(tableColumn, &xlsxTableColumn{ ID: idx, |