Browse Source

lint issue fixed and new formula function: ATAN, AVERAGE, AVERAGEA, CONCAT, CONCATENATE, COUNT, COUNTBLANK, MAX

xuri 4 năm trước cách đây
mục cha
commit
36b7990d6b
20 tập tin đã thay đổi với 462 bổ sung125 xóa
  1. 299 36
      calc.go
  2. 91 10
      calc_test.go
  3. 6 5
      cell_test.go
  4. 1 4
      col.go
  5. 3 3
      col_test.go
  6. 9 17
      crypt.go
  7. 1 3
      drawing.go
  8. 3 3
      excelize_test.go
  9. 1 1
      file.go
  10. 1 1
      file_test.go
  11. 1 1
      lib.go
  12. 2 2
      picture.go
  13. 2 2
      pivotTable.go
  14. 1 3
      rows.go
  15. 2 2
      sheet.go
  16. 3 3
      sheet_test.go
  17. 16 16
      stream.go
  18. 2 2
      stream_test.go
  19. 9 9
      styles.go
  20. 9 2
      table.go

+ 299 - 36
calc.go

@@ -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)
 //

+ 91 - 10
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"},

+ 6 - 5
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
col.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.
 //
@@ -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

+ 3 - 3
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()
 	}

+ 9 - 17
crypt.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.
 //
@@ -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 - 3
drawing.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.
 //
@@ -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

+ 3 - 3
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 - 1
file.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.
 //

+ 1 - 1
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")

+ 1 - 1
lib.go

@@ -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)
 }
 

+ 2 - 2
picture.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.
 //
@@ -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 {

+ 2 - 2
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 - 3
rows.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.
 //
@@ -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

+ 2 - 2
sheet.go

@@ -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
 }

+ 3 - 3
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()
 }

+ 16 - 16
stream.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.
 //
@@ -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())
 		}
 	}
 }

+ 2 - 2
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")
 }
 

+ 9 - 9
styles.go

@@ -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
 	},
 }
 

+ 9 - 2
table.go

@@ -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,