OSDN Git Service

This fixed incorrect formula calculation exception expected result
authorxuri <xuri.me@gmail.com>
Mon, 29 May 2023 16:14:44 +0000 (00:14 +0800)
committerxuri <xuri.me@gmail.com>
Mon, 29 May 2023 16:19:12 +0000 (00:19 +0800)
- Simplify and remove duplicate code for optimization
- Update documentation comments with typo fix
- Handle error return to save the workbook
- Add file path length limitation details in the error message

16 files changed:
calc.go
calc_test.go
date.go
errors.go
excelize.go
hsl.go
numfmt.go
numfmt_test.go
rows.go
rows_test.go
styles.go
table.go
xmlDecodeDrawing.go
xmlStyles.go
xmlWorkbook.go
xmlWorksheet.go

diff --git a/calc.go b/calc.go
index 402c7de..8e37bb9 100644 (file)
--- a/calc.go
+++ b/calc.go
@@ -380,17 +380,17 @@ type formulaFuncs struct {
 //     BESSELJ
 //     BESSELK
 //     BESSELY
-//     BETADIST
 //     BETA.DIST
-//     BETAINV
 //     BETA.INV
+//     BETADIST
+//     BETAINV
 //     BIN2DEC
 //     BIN2HEX
 //     BIN2OCT
-//     BINOMDIST
 //     BINOM.DIST
 //     BINOM.DIST.RANGE
 //     BINOM.INV
+//     BINOMDIST
 //     BITAND
 //     BITLSHIFT
 //     BITOR
@@ -402,12 +402,12 @@ type formulaFuncs struct {
 //     CHAR
 //     CHIDIST
 //     CHIINV
-//     CHITEST
 //     CHISQ.DIST
 //     CHISQ.DIST.RT
 //     CHISQ.INV
 //     CHISQ.INV.RT
 //     CHISQ.TEST
+//     CHITEST
 //     CHOOSE
 //     CLEAN
 //     CODE
@@ -477,8 +477,8 @@ type formulaFuncs struct {
 //     DURATION
 //     DVAR
 //     DVARP
-//     EFFECT
 //     EDATE
+//     EFFECT
 //     ENCODEURL
 //     EOMONTH
 //     ERF
@@ -492,16 +492,17 @@ type formulaFuncs struct {
 //     EXP
 //     EXPON.DIST
 //     EXPONDIST
+//     F.DIST
+//     F.DIST.RT
+//     F.INV
+//     F.INV.RT
+//     F.TEST
 //     FACT
 //     FACTDOUBLE
 //     FALSE
-//     F.DIST
-//     F.DIST.RT
 //     FDIST
 //     FIND
 //     FINDB
-//     F.INV
-//     F.INV.RT
 //     FINV
 //     FISHER
 //     FISHERINV
@@ -510,14 +511,13 @@ type formulaFuncs struct {
 //     FLOOR.MATH
 //     FLOOR.PRECISE
 //     FORMULATEXT
-//     F.TEST
 //     FTEST
 //     FV
 //     FVSCHEDULE
 //     GAMMA
 //     GAMMA.DIST
-//     GAMMADIST
 //     GAMMA.INV
+//     GAMMADIST
 //     GAMMAINV
 //     GAMMALN
 //     GAMMALN.PRECISE
@@ -579,12 +579,12 @@ type formulaFuncs struct {
 //     ISNA
 //     ISNONTEXT
 //     ISNUMBER
-//     ISODD
-//     ISREF
-//     ISTEXT
 //     ISO.CEILING
+//     ISODD
 //     ISOWEEKNUM
 //     ISPMT
+//     ISREF
+//     ISTEXT
 //     KURT
 //     LARGE
 //     LCM
@@ -597,8 +597,8 @@ type formulaFuncs struct {
 //     LOG10
 //     LOGINV
 //     LOGNORM.DIST
-//     LOGNORMDIST
 //     LOGNORM.INV
+//     LOGNORMDIST
 //     LOOKUP
 //     LOWER
 //     MATCH
@@ -633,12 +633,12 @@ type formulaFuncs struct {
 //     NETWORKDAYS.INTL
 //     NOMINAL
 //     NORM.DIST
-//     NORMDIST
 //     NORM.INV
-//     NORMINV
 //     NORM.S.DIST
-//     NORMSDIST
 //     NORM.S.INV
+//     NORMDIST
+//     NORMINV
+//     NORMSDIST
 //     NORMSINV
 //     NOT
 //     NOW
@@ -652,19 +652,19 @@ type formulaFuncs struct {
 //     OR
 //     PDURATION
 //     PEARSON
+//     PERCENTILE
 //     PERCENTILE.EXC
 //     PERCENTILE.INC
-//     PERCENTILE
+//     PERCENTRANK
 //     PERCENTRANK.EXC
 //     PERCENTRANK.INC
-//     PERCENTRANK
 //     PERMUT
 //     PERMUTATIONA
 //     PHI
 //     PI
 //     PMT
-//     POISSON.DIST
 //     POISSON
+//     POISSON.DIST
 //     POWER
 //     PPMT
 //     PRICE
@@ -734,20 +734,21 @@ type formulaFuncs struct {
 //     SWITCH
 //     SYD
 //     T
+//     T.DIST
+//     T.DIST.2T
+//     T.DIST.RT
+//     T.INV
+//     T.INV.2T
+//     T.TEST
 //     TAN
 //     TANH
 //     TBILLEQ
 //     TBILLPRICE
 //     TBILLYIELD
-//     T.DIST
-//     T.DIST.2T
-//     T.DIST.RT
 //     TDIST
 //     TEXTJOIN
 //     TIME
 //     TIMEVALUE
-//     T.INV
-//     T.INV.2T
 //     TINV
 //     TODAY
 //     TRANSPOSE
@@ -756,7 +757,6 @@ type formulaFuncs struct {
 //     TRIMMEAN
 //     TRUE
 //     TRUNC
-//     T.TEST
 //     TTEST
 //     TYPE
 //     UNICHAR
@@ -14162,17 +14162,23 @@ func (fn *formulaFuncs) COLUMN(argsList *list.List) formulaArg {
        return newNumberFormulaArg(float64(col))
 }
 
-// calcColumnsMinMax calculation min and max value for given formula arguments
-// sequence of the formula function COLUMNS.
-func calcColumnsMinMax(argsList *list.List) (min, max int) {
+// calcColsRowsMinMax calculation min and max value for given formula arguments
+// sequence of the formula functions COLUMNS and ROWS.
+func calcColsRowsMinMax(cols bool, argsList *list.List) (min, max int) {
+       getVal := func(cols bool, cell cellRef) int {
+               if cols {
+                       return cell.Col
+               }
+               return cell.Row
+       }
        if argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 {
                crs := argsList.Front().Value.(formulaArg).cellRanges
                for cr := crs.Front(); cr != nil; cr = cr.Next() {
                        if min == 0 {
-                               min = cr.Value.(cellRange).From.Col
+                               min = getVal(cols, cr.Value.(cellRange).From)
                        }
-                       if max < cr.Value.(cellRange).To.Col {
-                               max = cr.Value.(cellRange).To.Col
+                       if max < getVal(cols, cr.Value.(cellRange).To) {
+                               max = getVal(cols, cr.Value.(cellRange).To)
                        }
                }
        }
@@ -14180,10 +14186,10 @@ func calcColumnsMinMax(argsList *list.List) (min, max int) {
                cr := argsList.Front().Value.(formulaArg).cellRefs
                for refs := cr.Front(); refs != nil; refs = refs.Next() {
                        if min == 0 {
-                               min = refs.Value.(cellRef).Col
+                               min = getVal(cols, refs.Value.(cellRef))
                        }
-                       if max < refs.Value.(cellRef).Col {
-                               max = refs.Value.(cellRef).Col
+                       if max < getVal(cols, refs.Value.(cellRef)) {
+                               max = getVal(cols, refs.Value.(cellRef))
                        }
                }
        }
@@ -14198,7 +14204,7 @@ func (fn *formulaFuncs) COLUMNS(argsList *list.List) formulaArg {
        if argsList.Len() != 1 {
                return newErrorFormulaArg(formulaErrorVALUE, "COLUMNS requires 1 argument")
        }
-       min, max := calcColumnsMinMax(argsList)
+       min, max := calcColsRowsMinMax(true, argsList)
        if max == MaxColumns {
                return newNumberFormulaArg(float64(MaxColumns))
        }
@@ -14411,8 +14417,8 @@ func (fn *formulaFuncs) TRANSPOSE(argsList *list.List) formulaArg {
                return newErrorFormulaArg(formulaErrorVALUE, "TRANSPOSE requires 1 argument")
        }
        args := argsList.Back().Value.(formulaArg).ToList()
-       rmin, rmax := calcRowsMinMax(argsList)
-       cmin, cmax := calcColumnsMinMax(argsList)
+       rmin, rmax := calcColsRowsMinMax(false, argsList)
+       cmin, cmax := calcColsRowsMinMax(true, argsList)
        cols, rows := cmax-cmin+1, rmax-rmin+1
        src := make([][]formulaArg, 0)
        for i := 0; i < len(args); i += cols {
@@ -14931,34 +14937,6 @@ func (fn *formulaFuncs) ROW(argsList *list.List) formulaArg {
        return newNumberFormulaArg(float64(row))
 }
 
-// calcRowsMinMax calculation min and max value for given formula arguments
-// sequence of the formula function ROWS.
-func calcRowsMinMax(argsList *list.List) (min, max int) {
-       if argsList.Front().Value.(formulaArg).cellRanges != nil && argsList.Front().Value.(formulaArg).cellRanges.Len() > 0 {
-               crs := argsList.Front().Value.(formulaArg).cellRanges
-               for cr := crs.Front(); cr != nil; cr = cr.Next() {
-                       if min == 0 {
-                               min = cr.Value.(cellRange).From.Row
-                       }
-                       if max < cr.Value.(cellRange).To.Row {
-                               max = cr.Value.(cellRange).To.Row
-                       }
-               }
-       }
-       if argsList.Front().Value.(formulaArg).cellRefs != nil && argsList.Front().Value.(formulaArg).cellRefs.Len() > 0 {
-               cr := argsList.Front().Value.(formulaArg).cellRefs
-               for refs := cr.Front(); refs != nil; refs = refs.Next() {
-                       if min == 0 {
-                               min = refs.Value.(cellRef).Row
-                       }
-                       if max < refs.Value.(cellRef).Row {
-                               max = refs.Value.(cellRef).Row
-                       }
-               }
-       }
-       return
-}
-
 // ROWS function takes an Excel range and returns the number of rows that are
 // contained within the range. The syntax of the function is:
 //
@@ -14967,7 +14945,7 @@ func (fn *formulaFuncs) ROWS(argsList *list.List) formulaArg {
        if argsList.Len() != 1 {
                return newErrorFormulaArg(formulaErrorVALUE, "ROWS requires 1 argument")
        }
-       min, max := calcRowsMinMax(argsList)
+       min, max := calcColsRowsMinMax(false, argsList)
        if max == TotalRows {
                return newStringFormulaArg(strconv.Itoa(TotalRows))
        }
@@ -15649,35 +15627,35 @@ func (fn *formulaFuncs) prepareDataValueArgs(n int, argsList *list.List) formula
        return newListFormulaArg(dataValues)
 }
 
-// DISC function calculates the Discount Rate for a security. The syntax of
-// the function is:
-//
-//     DISC(settlement,maturity,pr,redemption,[basis])
-func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg {
+// discIntrate is an implementation of the formula functions DISC and INTRATE.
+func (fn *formulaFuncs) discIntrate(name string, argsList *list.List) formulaArg {
        if argsList.Len() != 4 && argsList.Len() != 5 {
-               return newErrorFormulaArg(formulaErrorVALUE, "DISC requires 4 or 5 arguments")
+               return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 4 or 5 arguments", name))
        }
        args := fn.prepareDataValueArgs(2, argsList)
        if args.Type != ArgList {
                return args
        }
-       settlement, maturity := args.List[0], args.List[1]
+       settlement, maturity, argName := args.List[0], args.List[1], "pr"
        if maturity.Number <= settlement.Number {
-               return newErrorFormulaArg(formulaErrorNUM, "DISC requires maturity > settlement")
+               return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name))
        }
-       pr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
-       if pr.Type != ArgNumber {
+       prInvestment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
+       if prInvestment.Type != ArgNumber {
                return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
        }
-       if pr.Number <= 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "DISC requires pr > 0")
+       if prInvestment.Number <= 0 {
+               if name == "INTRATE" {
+                       argName = "investment"
+               }
+               return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires %s > 0", name, argName))
        }
        redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
        if redemption.Type != ArgNumber {
                return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
        }
        if redemption.Number <= 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "DISC requires redemption > 0")
+               return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires redemption > 0", name))
        }
        basis := newNumberFormulaArg(0)
        if argsList.Len() == 5 {
@@ -15689,7 +15667,18 @@ func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg {
        if frac.Type != ArgNumber {
                return frac
        }
-       return newNumberFormulaArg((redemption.Number - pr.Number) / redemption.Number / frac.Number)
+       if name == "INTRATE" {
+               return newNumberFormulaArg((redemption.Number - prInvestment.Number) / prInvestment.Number / frac.Number)
+       }
+       return newNumberFormulaArg((redemption.Number - prInvestment.Number) / redemption.Number / frac.Number)
+}
+
+// DISC function calculates the Discount Rate for a security. The syntax of
+// the function is:
+//
+//     DISC(settlement,maturity,pr,redemption,[basis])
+func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg {
+       return fn.discIntrate("DISC", argsList)
 }
 
 // DOLLARDE function converts a dollar value in fractional notation, into a
@@ -16007,42 +15996,7 @@ func (fn *formulaFuncs) FVSCHEDULE(argsList *list.List) formulaArg {
 //
 //     INTRATE(settlement,maturity,investment,redemption,[basis])
 func (fn *formulaFuncs) INTRATE(argsList *list.List) formulaArg {
-       if argsList.Len() != 4 && argsList.Len() != 5 {
-               return newErrorFormulaArg(formulaErrorVALUE, "INTRATE requires 4 or 5 arguments")
-       }
-       args := fn.prepareDataValueArgs(2, argsList)
-       if args.Type != ArgList {
-               return args
-       }
-       settlement, maturity := args.List[0], args.List[1]
-       if maturity.Number <= settlement.Number {
-               return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires maturity > settlement")
-       }
-       investment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
-       if investment.Type != ArgNumber {
-               return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
-       }
-       if investment.Number <= 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires investment > 0")
-       }
-       redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
-       if redemption.Type != ArgNumber {
-               return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
-       }
-       if redemption.Number <= 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires redemption > 0")
-       }
-       basis := newNumberFormulaArg(0)
-       if argsList.Len() == 5 {
-               if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
-                       return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
-               }
-       }
-       frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))
-       if frac.Type != ArgNumber {
-               return frac
-       }
-       return newNumberFormulaArg((redemption.Number - investment.Number) / investment.Number / frac.Number)
+       return fn.discIntrate("INTRATE", argsList)
 }
 
 // IPMT function calculates the interest payment, during a specific period of a
@@ -16756,54 +16710,81 @@ func (fn *formulaFuncs) price(settlement, maturity, rate, yld, redemption, frequ
        return newNumberFormulaArg(ret)
 }
 
-// PRICE function calculates the price, per $100 face value of a security that
-// pays periodic interest. The syntax of the function is:
-//
-//     PRICE(settlement,maturity,rate,yld,redemption,frequency,[basis])
-func (fn *formulaFuncs) PRICE(argsList *list.List) formulaArg {
-       if argsList.Len() != 6 && argsList.Len() != 7 {
-               return newErrorFormulaArg(formulaErrorVALUE, "PRICE requires 6 or 7 arguments")
-       }
-       args := fn.prepareDataValueArgs(2, argsList)
-       if args.Type != ArgList {
-               return args
-       }
-       settlement, maturity := args.List[0], args.List[1]
-       rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
+// checkPriceYieldArgs checking and prepare arguments for the formula functions
+// PRICE and YIELD.
+func checkPriceYieldArgs(name string, rate, prYld, redemption, frequency formulaArg) formulaArg {
        if rate.Type != ArgNumber {
                return rate
        }
        if rate.Number < 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "PRICE requires rate >= 0")
+               return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires rate >= 0", name))
        }
-       yld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
-       if yld.Type != ArgNumber {
-               return yld
-       }
-       if yld.Number < 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "PRICE requires yld >= 0")
+       if prYld.Type != ArgNumber {
+               return prYld
        }
-       redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
        if redemption.Type != ArgNumber {
                return redemption
        }
-       if redemption.Number <= 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption > 0")
+       if name == "PRICE" {
+               if prYld.Number < 0 {
+                       return newErrorFormulaArg(formulaErrorNUM, "PRICE requires yld >= 0")
+               }
+               if redemption.Number <= 0 {
+                       return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption > 0")
+               }
+       }
+       if name == "YIELD" {
+               if prYld.Number <= 0 {
+                       return newErrorFormulaArg(formulaErrorNUM, "YIELD requires pr > 0")
+               }
+               if redemption.Number < 0 {
+                       return newErrorFormulaArg(formulaErrorNUM, "YIELD requires redemption >= 0")
+               }
        }
-       frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
        if frequency.Type != ArgNumber {
                return frequency
        }
        if !validateFrequency(frequency.Number) {
                return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
        }
+       return newEmptyFormulaArg()
+}
+
+// priceYield is an implementation of the formula functions PRICE and YIELD.
+func (fn *formulaFuncs) priceYield(name string, argsList *list.List) formulaArg {
+       if argsList.Len() != 6 && argsList.Len() != 7 {
+               return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 6 or 7 arguments", name))
+       }
+       args := fn.prepareDataValueArgs(2, argsList)
+       if args.Type != ArgList {
+               return args
+       }
+       settlement, maturity := args.List[0], args.List[1]
+       rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
+       prYld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
+       redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
+       frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
+       if arg := checkPriceYieldArgs(name, rate, prYld, redemption, frequency); arg.Type != ArgEmpty {
+               return arg
+       }
        basis := newNumberFormulaArg(0)
        if argsList.Len() == 7 {
                if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
                        return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
                }
        }
-       return fn.price(settlement, maturity, rate, yld, redemption, frequency, basis)
+       if name == "PRICE" {
+               return fn.price(settlement, maturity, rate, prYld, redemption, frequency, basis)
+       }
+       return fn.yield(settlement, maturity, rate, prYld, redemption, frequency, basis)
+}
+
+// PRICE function calculates the price, per $100 face value of a security that
+// pays periodic interest. The syntax of the function is:
+//
+//     PRICE(settlement,maturity,rate,yld,redemption,frequency,[basis])
+func (fn *formulaFuncs) PRICE(argsList *list.List) formulaArg {
+       return fn.priceYield("PRICE", argsList)
 }
 
 // PRICEDISC function calculates the price, per $100 face value of a
@@ -17535,49 +17516,7 @@ func (fn *formulaFuncs) yield(settlement, maturity, rate, pr, redemption, freque
 //
 //     YIELD(settlement,maturity,rate,pr,redemption,frequency,[basis])
 func (fn *formulaFuncs) YIELD(argsList *list.List) formulaArg {
-       if argsList.Len() != 6 && argsList.Len() != 7 {
-               return newErrorFormulaArg(formulaErrorVALUE, "YIELD requires 6 or 7 arguments")
-       }
-       args := fn.prepareDataValueArgs(2, argsList)
-       if args.Type != ArgList {
-               return args
-       }
-       settlement, maturity := args.List[0], args.List[1]
-       rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
-       if rate.Type != ArgNumber {
-               return rate
-       }
-       if rate.Number < 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "PRICE requires rate >= 0")
-       }
-       pr := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
-       if pr.Type != ArgNumber {
-               return pr
-       }
-       if pr.Number <= 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "PRICE requires pr > 0")
-       }
-       redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
-       if redemption.Type != ArgNumber {
-               return redemption
-       }
-       if redemption.Number < 0 {
-               return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption >= 0")
-       }
-       frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
-       if frequency.Type != ArgNumber {
-               return frequency
-       }
-       if !validateFrequency(frequency.Number) {
-               return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
-       }
-       basis := newNumberFormulaArg(0)
-       if argsList.Len() == 7 {
-               if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
-                       return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
-               }
-       }
-       return fn.yield(settlement, maturity, rate, pr, redemption, frequency, basis)
+       return fn.priceYield("YIELD", argsList)
 }
 
 // YIELDDISC function calculates the annual yield of a discounted security.
index b9c9a8d..a706f3d 100644 (file)
@@ -4334,9 +4334,9 @@ func TestCalcCellValue(t *testing.T) {
                "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,\"\")": {"#NUM!", "#NUM!"},
                "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,3)":      {"#NUM!", "#NUM!"},
                "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,5)":    {"#NUM!", "invalid basis"},
-               "=YIELD(\"01/01/2010\",\"06/30/2015\",-1,101,100,4)":       {"#NUM!", "PRICE requires rate >= 0"},
-               "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,0,100,4)":        {"#NUM!", "PRICE requires pr > 0"},
-               "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,-1,4)":       {"#NUM!", "PRICE requires redemption >= 0"},
+               "=YIELD(\"01/01/2010\",\"06/30/2015\",-1,101,100,4)":       {"#NUM!", "YIELD requires rate >= 0"},
+               "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,0,100,4)":        {"#NUM!", "YIELD requires pr > 0"},
+               "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,-1,4)":       {"#NUM!", "YIELD requires redemption >= 0"},
                // YIELDDISC
                "=YIELDDISC()": {"#VALUE!", "YIELDDISC requires 4 or 5 arguments"},
                "=YIELDDISC(\"\",\"06/30/2017\",97,100,0)":              {"#VALUE!", "#VALUE!"},
diff --git a/date.go b/date.go
index 94ce218..a59c694 100644 (file)
--- a/date.go
+++ b/date.go
@@ -114,7 +114,7 @@ func julianDateToGregorianTime(part1, part2 float64) time.Time {
 // "Communications of the ACM" in 1968 (published in CACM, volume 11, number
 // 10, October 1968, p.657). None of those programmers seems to have found it
 // necessary to explain the constants or variable names set out by Henry F.
-// Fliegel and Thomas C. Van Flandern.  Maybe one day I'll buy that jounal and
+// Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy that journal and
 // expand an explanation here - that day is not today.
 func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
        l := jd + 68569
@@ -163,7 +163,7 @@ func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
        return date.Truncate(time.Second)
 }
 
-// ExcelDateToTime converts a float-based excel date representation to a time.Time.
+// ExcelDateToTime converts a float-based Excel date representation to a time.Time.
 func ExcelDateToTime(excelDate float64, use1904Format bool) (time.Time, error) {
        if excelDate < 0 {
                return time.Time{}, newInvalidExcelDateError(excelDate)
index 1a6cc8a..96eed6f 100644 (file)
--- a/errors.go
+++ b/errors.go
@@ -143,7 +143,7 @@ var (
        ErrWorkbookFileFormat = errors.New("unsupported workbook file format")
        // ErrMaxFilePathLength defined the error message on receive the file path
        // length overflow.
-       ErrMaxFilePathLength = errors.New("file path length exceeds maximum limit")
+       ErrMaxFilePathLength = fmt.Errorf("file path length exceeds maximum limit %d characters", MaxFilePathLength)
        // ErrUnknownEncryptMechanism defined the error message on unsupported
        // encryption mechanism.
        ErrUnknownEncryptMechanism = errors.New("unknown encryption mechanism")
index d677285..a0eaeca 100644 (file)
@@ -60,7 +60,7 @@ type File struct {
 // the spreadsheet from non-UTF-8 encoding.
 type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
 
-// Options define the options for o`pen and reading spreadsheet.
+// Options define the options for opening and reading the spreadsheet.
 //
 // MaxCalcIterations specifies the maximum iterations for iterative
 // calculation, the default value is 0.
@@ -70,7 +70,7 @@ type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, e
 // RawCellValue specifies if apply the number format for the cell value or get
 // the raw value.
 //
-// UnzipSizeLimit specifies the unzip size limit in bytes on open the
+// UnzipSizeLimit specifies to unzip size limit in bytes on open the
 // spreadsheet, this value should be greater than or equal to
 // UnzipXMLSizeLimit, the default size limit is 16GB.
 //
@@ -106,7 +106,7 @@ type Options struct {
        CultureInfo       CultureName
 }
 
-// OpenFile take the name of an spreadsheet file and returns a populated
+// OpenFile take the name of a spreadsheet file and returns a populated
 // spreadsheet file struct for it. For example, open spreadsheet with
 // password protection:
 //
diff --git a/hsl.go b/hsl.go
index c30c165..68ddf21 100644 (file)
--- a/hsl.go
+++ b/hsl.go
@@ -60,7 +60,7 @@ func hslModel(c color.Color) color.Color {
        return HSL{h, s, l}
 }
 
-// RGBToHSL converts an RGB triple to a HSL triple.
+// RGBToHSL converts an RGB triple to an HSL triple.
 func RGBToHSL(r, g, b uint8) (h, s, l float64) {
        fR := float64(r) / 255
        fG := float64(g) / 255
@@ -95,7 +95,7 @@ func RGBToHSL(r, g, b uint8) (h, s, l float64) {
        return
 }
 
-// HSLToRGB converts an HSL triple to a RGB triple.
+// HSLToRGB converts an HSL triple to an RGB triple.
 func HSLToRGB(h, s, l float64) (r, g, b uint8) {
        var fR, fG, fB float64
        if s == 0 {
index 9e48a6e..f39ad61 100644 (file)
--- a/numfmt.go
+++ b/numfmt.go
@@ -1136,7 +1136,7 @@ func getNumberPartLen(n float64) (int, int) {
        return len(parts[0]), 0
 }
 
-// getNumberFmtConf generate the number format padding and place holder
+// getNumberFmtConf generate the number format padding and placeholder
 // configurations.
 func (nf *numberFormat) getNumberFmtConf() {
        for _, token := range nf.section[nf.sectionIdx].Items {
@@ -1183,9 +1183,9 @@ func (nf *numberFormat) printNumberLiteral(text string) string {
        if nf.usePositive {
                result += "-"
        }
-       for i, token := range nf.section[nf.sectionIdx].Items {
+       for _, token := range nf.section[nf.sectionIdx].Items {
                if token.TType == nfp.TokenTypeCurrencyLanguage {
-                       if err, changeNumFmtCode := nf.currencyLanguageHandler(i, token); err != nil || changeNumFmtCode {
+                       if err, changeNumFmtCode := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {
                                return nf.value
                        }
                        result += nf.currencyString
@@ -1321,7 +1321,7 @@ func (nf *numberFormat) dateTimeHandler() string {
        nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false
        for i, token := range nf.section[nf.sectionIdx].Items {
                if token.TType == nfp.TokenTypeCurrencyLanguage {
-                       if err, changeNumFmtCode := nf.currencyLanguageHandler(i, token); err != nil || changeNumFmtCode {
+                       if err, changeNumFmtCode := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {
                                return nf.value
                        }
                        nf.result += nf.currencyString
@@ -1392,7 +1392,7 @@ func (nf *numberFormat) positiveHandler() string {
 
 // currencyLanguageHandler will be handling currency and language types tokens
 // for a number format expression.
-func (nf *numberFormat) currencyLanguageHandler(i int, token nfp.Token) (error, bool) {
+func (nf *numberFormat) currencyLanguageHandler(token nfp.Token) (error, bool) {
        for _, part := range token.Parts {
                if inStrSlice(supportedTokenTypes, part.Token.TType, true) == -1 {
                        return ErrUnsupportedNumberFormat, false
@@ -1491,7 +1491,7 @@ func localMonthsNameFrench(t time.Time, abbr int) string {
 // localMonthsNameIrish returns the Irish name of the month.
 func localMonthsNameIrish(t time.Time, abbr int) string {
        if abbr == 3 {
-               return monthNamesIrishAbbr[int(t.Month()-1)]
+               return monthNamesIrishAbbr[(t.Month() - 1)]
        }
        if abbr == 4 {
                return monthNamesIrish[int(t.Month())-1]
@@ -1524,7 +1524,7 @@ func localMonthsNameGerman(t time.Time, abbr int) string {
 // localMonthsNameChinese1 returns the Chinese name of the month.
 func localMonthsNameChinese1(t time.Time, abbr int) string {
        if abbr == 3 {
-               return monthNamesChineseAbbrPlus[int(t.Month())]
+               return monthNamesChineseAbbrPlus[t.Month()]
        }
        if abbr == 4 {
                return monthNamesChinesePlus[int(t.Month())-1]
@@ -1543,7 +1543,7 @@ func localMonthsNameChinese2(t time.Time, abbr int) string {
 // localMonthsNameChinese3 returns the Chinese name of the month.
 func localMonthsNameChinese3(t time.Time, abbr int) string {
        if abbr == 3 || abbr == 4 {
-               return monthNamesChineseAbbrPlus[int(t.Month())]
+               return monthNamesChineseAbbrPlus[t.Month()]
        }
        return strconv.Itoa(int(t.Month()))
 }
@@ -1551,7 +1551,7 @@ func localMonthsNameChinese3(t time.Time, abbr int) string {
 // localMonthsNameKorean returns the Korean name of the month.
 func localMonthsNameKorean(t time.Time, abbr int) string {
        if abbr == 3 || abbr == 4 {
-               return monthNamesKoreanAbbrPlus[int(t.Month())]
+               return monthNamesKoreanAbbrPlus[t.Month()]
        }
        return strconv.Itoa(int(t.Month()))
 }
@@ -1562,7 +1562,7 @@ func localMonthsNameTraditionalMongolian(t time.Time, abbr int) string {
        if abbr == 5 {
                return "M"
        }
-       return monthNamesTradMongolian[int(t.Month()-1)]
+       return monthNamesTradMongolian[t.Month()-1]
 }
 
 // localMonthsNameRussian returns the Russian name of the month.
@@ -1642,12 +1642,12 @@ func localMonthsNameWelsh(t time.Time, abbr int) string {
 // localMonthsNameVietnamese returns the Vietnamese name of the month.
 func localMonthsNameVietnamese(t time.Time, abbr int) string {
        if abbr == 3 {
-               return monthNamesVietnameseAbbr3[int(t.Month()-1)]
+               return monthNamesVietnameseAbbr3[t.Month()-1]
        }
        if abbr == 5 {
-               return monthNamesVietnameseAbbr5[int(t.Month()-1)]
+               return monthNamesVietnameseAbbr5[t.Month()-1]
        }
-       return monthNamesVietnamese[int(t.Month()-1)]
+       return monthNamesVietnamese[t.Month()-1]
 }
 
 // localMonthsNameWolof returns the Wolof name of the month.
@@ -1675,7 +1675,7 @@ func localMonthsNameXhosa(t time.Time, abbr int) string {
 // localMonthsNameYi returns the Yi name of the month.
 func localMonthsNameYi(t time.Time, abbr int) string {
        if abbr == 3 || abbr == 4 {
-               return monthNamesYiSuffix[int(t.Month()-1)]
+               return monthNamesYiSuffix[t.Month()-1]
        }
        return string([]rune(monthNamesYi[int(t.Month())-1])[:1])
 }
@@ -1683,7 +1683,7 @@ func localMonthsNameYi(t time.Time, abbr int) string {
 // localMonthsNameZulu returns the Zulu name of the month.
 func localMonthsNameZulu(t time.Time, abbr int) string {
        if abbr == 3 {
-               return monthNamesZuluAbbr[int(t.Month()-1)]
+               return monthNamesZuluAbbr[t.Month()-1]
        }
        if abbr == 4 {
                return monthNamesZulu[int(t.Month())-1]
@@ -1737,8 +1737,8 @@ func (nf *numberFormat) dateTimesHandler(i int, token nfp.Token) {
                        return
                }
        }
-       nf.yearsHandler(i, token)
-       nf.daysHandler(i, token)
+       nf.yearsHandler(token)
+       nf.daysHandler(token)
        nf.hoursHandler(i, token)
        nf.minutesHandler(token)
        nf.secondsHandler(token)
@@ -1746,7 +1746,7 @@ func (nf *numberFormat) dateTimesHandler(i int, token nfp.Token) {
 
 // yearsHandler will be handling years in the date and times types tokens for a
 // number format expression.
-func (nf *numberFormat) yearsHandler(i int, token nfp.Token) {
+func (nf *numberFormat) yearsHandler(token nfp.Token) {
        years := strings.Contains(strings.ToUpper(token.TValue), "Y")
        if years && len(token.TValue) <= 2 {
                nf.result += strconv.Itoa(nf.t.Year())[2:]
@@ -1760,7 +1760,7 @@ func (nf *numberFormat) yearsHandler(i int, token nfp.Token) {
 
 // daysHandler will be handling days in the date and times types tokens for a
 // number format expression.
-func (nf *numberFormat) daysHandler(i int, token nfp.Token) {
+func (nf *numberFormat) daysHandler(token nfp.Token) {
        if strings.Contains(strings.ToUpper(token.TValue), "D") {
                switch len(token.TValue) {
                case 1:
index c41fd94..c49393f 100644 (file)
@@ -1093,7 +1093,7 @@ func TestNumFmt(t *testing.T) {
                }
        }
        nf := numberFormat{}
-       err, changeNumFmtCode := nf.currencyLanguageHandler(0, nfp.Token{Parts: []nfp.Part{{}}})
+       err, changeNumFmtCode := nf.currencyLanguageHandler(nfp.Token{Parts: []nfp.Part{{}}})
        assert.Equal(t, ErrUnsupportedNumberFormat, err)
        assert.False(t, changeNumFmtCode)
 }
diff --git a/rows.go b/rows.go
index 60b74b3..7351d16 100644 (file)
--- a/rows.go
+++ b/rows.go
@@ -79,7 +79,7 @@ type Rows struct {
        curRowOpts, seekRowOpts RowOpts
 }
 
-// Next will return true if find the next row element.
+// Next will return true if it finds the next row element.
 func (rows *Rows) Next() bool {
        rows.seekRow++
        if rows.curRow >= rows.seekRow {
@@ -297,7 +297,9 @@ func (f *File) getFromStringItem(index int) string {
        }
        needClose, decoder, tempFile, err := f.xmlDecoder(defaultXMLPathSharedStrings)
        if needClose && err == nil {
-               defer tempFile.Close()
+               defer func() {
+                       err = tempFile.Close()
+               }()
        }
        f.sharedStringItem = [][]uint{}
        f.sharedStringTemp, _ = os.CreateTemp(os.TempDir(), "excelize-")
index f94adbd..acf50ff 100644 (file)
@@ -416,6 +416,23 @@ func TestInsertRowsInEmptyFile(t *testing.T) {
        assert.NoError(t, f.SaveAs(filepath.Join("test", "TestInsertRowInEmptyFile.xlsx")))
 }
 
+func prepareTestBook2() (*File, error) {
+       f := NewFile()
+       for cell, val := range map[string]string{
+               "A1": "A1 Value",
+               "A2": "A2 Value",
+               "A3": "A3 Value",
+               "B1": "B1 Value",
+               "B2": "B2 Value",
+               "B3": "B3 Value",
+       } {
+               if err := f.SetCellStr("Sheet1", cell, val); err != nil {
+                       return f, err
+               }
+       }
+       return f, nil
+}
+
 func TestDuplicateRowFromSingleRow(t *testing.T) {
        const sheet = "Sheet1"
        outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
@@ -512,7 +529,6 @@ func TestDuplicateRowUpdateDuplicatedRows(t *testing.T) {
 func TestDuplicateRowFirstOfMultipleRows(t *testing.T) {
        const sheet = "Sheet1"
        outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
-
        cells := map[string]string{
                "A1": "A1 Value",
                "A2": "A2 Value",
@@ -521,18 +537,9 @@ func TestDuplicateRowFirstOfMultipleRows(t *testing.T) {
                "B2": "B2 Value",
                "B3": "B3 Value",
        }
-
-       newFileWithDefaults := func() *File {
-               f := NewFile()
-               for cell, val := range cells {
-                       assert.NoError(t, f.SetCellStr(sheet, cell, val))
-               }
-               return f
-       }
-
        t.Run("FirstOfMultipleRows", func(t *testing.T) {
-               f := newFileWithDefaults()
-
+               f, err := prepareTestBook2()
+               assert.NoError(t, err)
                assert.NoError(t, f.DuplicateRow(sheet, 1))
 
                if !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, "FirstOfMultipleRows"))) {
@@ -635,18 +642,9 @@ func TestDuplicateRowWithLargeOffsetToMiddleOfData(t *testing.T) {
                "B2": "B2 Value",
                "B3": "B3 Value",
        }
-
-       newFileWithDefaults := func() *File {
-               f := NewFile()
-               for cell, val := range cells {
-                       assert.NoError(t, f.SetCellStr(sheet, cell, val))
-               }
-               return f
-       }
-
        t.Run("WithLargeOffsetToMiddleOfData", func(t *testing.T) {
-               f := newFileWithDefaults()
-
+               f, err := prepareTestBook2()
+               assert.NoError(t, err)
                assert.NoError(t, f.DuplicateRowTo(sheet, 1, 3))
 
                if !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, "WithLargeOffsetToMiddleOfData"))) {
@@ -671,7 +669,6 @@ func TestDuplicateRowWithLargeOffsetToMiddleOfData(t *testing.T) {
 func TestDuplicateRowWithLargeOffsetToEmptyRows(t *testing.T) {
        const sheet = "Sheet1"
        outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
-
        cells := map[string]string{
                "A1": "A1 Value",
                "A2": "A2 Value",
@@ -680,18 +677,9 @@ func TestDuplicateRowWithLargeOffsetToEmptyRows(t *testing.T) {
                "B2": "B2 Value",
                "B3": "B3 Value",
        }
-
-       newFileWithDefaults := func() *File {
-               f := NewFile()
-               for cell, val := range cells {
-                       assert.NoError(t, f.SetCellStr(sheet, cell, val))
-               }
-               return f
-       }
-
        t.Run("WithLargeOffsetToEmptyRows", func(t *testing.T) {
-               f := newFileWithDefaults()
-
+               f, err := prepareTestBook2()
+               assert.NoError(t, err)
                assert.NoError(t, f.DuplicateRowTo(sheet, 1, 7))
 
                if !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, "WithLargeOffsetToEmptyRows"))) {
@@ -716,7 +704,6 @@ func TestDuplicateRowWithLargeOffsetToEmptyRows(t *testing.T) {
 func TestDuplicateRowInsertBefore(t *testing.T) {
        const sheet = "Sheet1"
        outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
-
        cells := map[string]string{
                "A1": "A1 Value",
                "A2": "A2 Value",
@@ -725,18 +712,9 @@ func TestDuplicateRowInsertBefore(t *testing.T) {
                "B2": "B2 Value",
                "B3": "B3 Value",
        }
-
-       newFileWithDefaults := func() *File {
-               f := NewFile()
-               for cell, val := range cells {
-                       assert.NoError(t, f.SetCellStr(sheet, cell, val))
-               }
-               return f
-       }
-
        t.Run("InsertBefore", func(t *testing.T) {
-               f := newFileWithDefaults()
-
+               f, err := prepareTestBook2()
+               assert.NoError(t, err)
                assert.NoError(t, f.DuplicateRowTo(sheet, 2, 1))
                assert.NoError(t, f.DuplicateRowTo(sheet, 10, 4))
 
@@ -763,7 +741,6 @@ func TestDuplicateRowInsertBefore(t *testing.T) {
 func TestDuplicateRowInsertBeforeWithLargeOffset(t *testing.T) {
        const sheet = "Sheet1"
        outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
-
        cells := map[string]string{
                "A1": "A1 Value",
                "A2": "A2 Value",
@@ -772,18 +749,9 @@ func TestDuplicateRowInsertBeforeWithLargeOffset(t *testing.T) {
                "B2": "B2 Value",
                "B3": "B3 Value",
        }
-
-       newFileWithDefaults := func() *File {
-               f := NewFile()
-               for cell, val := range cells {
-                       assert.NoError(t, f.SetCellStr(sheet, cell, val))
-               }
-               return f
-       }
-
        t.Run("InsertBeforeWithLargeOffset", func(t *testing.T) {
-               f := newFileWithDefaults()
-
+               f, err := prepareTestBook2()
+               assert.NoError(t, err)
                assert.NoError(t, f.DuplicateRowTo(sheet, 3, 1))
 
                if !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, "InsertBeforeWithLargeOffset"))) {
@@ -809,28 +777,11 @@ func TestDuplicateRowInsertBeforeWithLargeOffset(t *testing.T) {
 func TestDuplicateRowInsertBeforeWithMergeCells(t *testing.T) {
        const sheet = "Sheet1"
        outFile := filepath.Join("test", "TestDuplicateRow.%s.xlsx")
-
-       cells := map[string]string{
-               "A1": "A1 Value",
-               "A2": "A2 Value",
-               "A3": "A3 Value",
-               "B1": "B1 Value",
-               "B2": "B2 Value",
-               "B3": "B3 Value",
-       }
-
-       newFileWithDefaults := func() *File {
-               f := NewFile()
-               for cell, val := range cells {
-                       assert.NoError(t, f.SetCellStr(sheet, cell, val))
-               }
+       t.Run("InsertBeforeWithLargeOffset", func(t *testing.T) {
+               f, err := prepareTestBook2()
+               assert.NoError(t, err)
                assert.NoError(t, f.MergeCell(sheet, "B2", "C2"))
                assert.NoError(t, f.MergeCell(sheet, "C6", "C8"))
-               return f
-       }
-
-       t.Run("InsertBeforeWithLargeOffset", func(t *testing.T) {
-               f := newFileWithDefaults()
 
                assert.NoError(t, f.DuplicateRowTo(sheet, 2, 1))
                assert.NoError(t, f.DuplicateRowTo(sheet, 1, 8))
index 9dc142e..70c11d5 100644 (file)
--- a/styles.go
+++ b/styles.go
@@ -1309,7 +1309,7 @@ func newNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
        if !ok {
                fc, currency := currencyNumFmt[style.NumFmt]
                if !currency {
-                       return setLangNumFmt(styleSheet, style)
+                       return setLangNumFmt(style)
                }
                fc = strings.ReplaceAll(fc, "0.00", dp)
                if style.NegRed {
@@ -1375,7 +1375,7 @@ func getCustomNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (customNumFmtID
 }
 
 // setLangNumFmt provides a function to set number format code with language.
-func setLangNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
+func setLangNumFmt(style *Style) int {
        if (27 <= style.NumFmt && style.NumFmt <= 36) || (50 <= style.NumFmt && style.NumFmt <= 81) {
                return style.NumFmt
        }
@@ -1585,7 +1585,7 @@ func newBorders(style *Style) *xlsxBorder {
        return &border
 }
 
-// setCellXfs provides a function to set describes all of the formatting for a
+// setCellXfs provides a function to set describes all the formatting for a
 // cell.
 func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) (int, error) {
        var xf xlsxXf
@@ -2451,7 +2451,7 @@ func extractCondFmtDataBar(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatO
                        if ext.URI == ExtURIConditionalFormattings {
                                decodeCondFmts := new(decodeX14ConditionalFormattings)
                                if err := xml.Unmarshal([]byte(ext.Content), &decodeCondFmts); err == nil {
-                                       condFmts := []decodeX14ConditionalFormatting{}
+                                       var condFmts []decodeX14ConditionalFormatting
                                        if err = xml.Unmarshal([]byte(decodeCondFmts.Content), &condFmts); err == nil {
                                                extractDataBarRule(condFmts)
                                        }
index 094f765..b63fe27 100644 (file)
--- a/table.go
+++ b/table.go
@@ -320,7 +320,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *Tab
 //     x == *b      // ends with b
 //     x != *b      // doesn't end with b
 //     x == *b*     // contains b
-//     x != *b*     // doesn't contains b
+//     x != *b*     // doesn't contain b
 //
 // You can also use '*' to match any character or number and '?' to match any
 // single character or number. No other regular expression quantifier is
@@ -538,7 +538,7 @@ func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, str
        }
        token := tokens[2]
        // Special handling for Blanks/NonBlanks.
-       re := blankFormat.MatchString((strings.ToLower(token)))
+       re := blankFormat.MatchString(strings.ToLower(token))
        if re {
                // Only allow Equals or NotEqual in this context.
                if operator != 2 && operator != 5 {
index 612bb62..c737ac0 100644 (file)
@@ -199,7 +199,7 @@ type decodeSpPr struct {
 // decodePic elements encompass the definition of pictures within the
 // DrawingML framework. While pictures are in many ways very similar to shapes
 // they have specific properties that are unique in order to optimize for
-// picture- specific scenarios.
+// picture-specific scenarios.
 type decodePic struct {
        NvPicPr  decodeNvPicPr  `xml:"nvPicPr"`
        BlipFill decodeBlipFill `xml:"blipFill"`
index 9700919..74b9119 100644 (file)
@@ -65,10 +65,10 @@ type xlsxLine struct {
 
 // xlsxColor is a common mapping used for both the fgColor and bgColor elements.
 // Foreground color of the cell fill pattern. Cell fill patterns operate with
-// two colors: a background color and a foreground color. These combine together
+// two colors: a background color and a foreground color. These combine
 // to make a patterned cell fill. Background color of the cell fill pattern.
 // Cell fill patterns operate with two colors: a background color and a
-// foreground color. These combine together to make a patterned cell fill.
+// foreground color. These combine to make a patterned cell fill.
 type xlsxColor struct {
        Auto    bool    `xml:"auto,attr,omitempty"`
        RGB     string  `xml:"rgb,attr,omitempty"`
@@ -103,7 +103,7 @@ type xlsxFont struct {
        Scheme   *attrValString `xml:"scheme"`
 }
 
-// xlsxFills directly maps the fills element. This element defines the cell
+// xlsxFills directly maps the fills' element. This element defines the cell
 // fills portion of the Styles part, consisting of a sequence of fill records. A
 // cell fill consists of a background color, foreground color, and pattern to be
 // applied across the cell.
@@ -147,7 +147,7 @@ type xlsxGradientFillStop struct {
        Color    xlsxColor `xml:"color,omitempty"`
 }
 
-// xlsxBorders directly maps the borders element. This element contains borders
+// xlsxBorders directly maps the borders' element. This element contains borders
 // formatting information, specifying all border definitions for all cells in
 // the workbook.
 type xlsxBorders struct {
@@ -205,7 +205,7 @@ type xlsxCellStyleXfs struct {
        Xf    []xlsxXf `xml:"xf,omitempty"`
 }
 
-// xlsxXf directly maps the xf element. A single xf element describes all of the
+// xlsxXf directly maps the xf element. A single xf element describes all the
 // formatting for a cell.
 type xlsxXf struct {
        NumFmtID          *int            `xml:"numFmtId,attr"`
@@ -236,8 +236,8 @@ type xlsxCellXfs struct {
 }
 
 // xlsxDxfs directly maps the dxfs element. This element contains the master
-// differential formatting records (dxf's) which define formatting for all non-
-// cell formatting in this workbook. Whereas xf records fully specify a
+// differential formatting records (dxf's) which define formatting for all
+// non-cell formatting in this workbook. Whereas xf records fully specify a
 // particular aspect of formatting (e.g., cell borders) by referencing those
 // formatting definitions elsewhere in the Styles part, dxf records specify
 // incremental (or differential) aspects of formatting directly inline within
@@ -304,7 +304,7 @@ type xlsxNumFmt struct {
        FormatCode string `xml:"formatCode,attr,omitempty"`
 }
 
-// xlsxStyleColors directly maps the colors element. Color information
+// xlsxStyleColors directly maps the colors' element. Color information
 // associated with this stylesheet. This collection is written whenever the
 // legacy color palette has been modified (backwards compatibility settings) or
 // a custom color has been selected while using this workbook.
index bc71bd4..c006375 100644 (file)
@@ -212,7 +212,7 @@ type xlsxPivotCache struct {
 // document are specified in the markup specification and can be used to store
 // extensions to the markup specification, whether those are future version
 // extensions of the markup specification or are private extensions implemented
-// independently from the markup specification. Markup within an extension might
+// independently of the markup specification. Markup within an extension might
 // not be understood by a consumer.
 type xlsxExtLst struct {
        Ext string `xml:",innerxml"`
@@ -229,7 +229,7 @@ type xlsxDefinedNames struct {
 // xlsxDefinedName directly maps the definedName element from the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main This element
 // defines a defined name within this workbook. A defined name is descriptive
-// text that is used to represents a cell, range of cells, formula, or constant
+// text that is used to represent a cell, range of cells, formula, or constant
 // value. For a descriptions of the attributes see https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.definedname
 type xlsxDefinedName struct {
        Comment           string `xml:"comment,attr,omitempty"`
index f23c414..79170de 100644 (file)
@@ -427,7 +427,7 @@ type xlsxDataValidations struct {
        DataValidation []*DataValidation `xml:"dataValidation"`
 }
 
-// DataValidation directly maps the single item of data validation defined
+// DataValidation directly maps the single item of data validation defined
 // on a range of the worksheet.
 type DataValidation struct {
        AllowBlank       bool    `xml:"allowBlank,attr"`