From 121ac17ca0e5458d4915aa8743abc26fc56075c5 Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 30 May 2023 00:14:44 +0800 Subject: [PATCH] This fixed incorrect formula calculation exception expected result - 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 --- calc.go | 323 ++++++++++++++++++-------------------------- calc_test.go | 6 +- date.go | 4 +- errors.go | 2 +- excelize.go | 6 +- hsl.go | 4 +- numfmt.go | 38 +++--- numfmt_test.go | 2 +- rows.go | 6 +- rows_test.go | 109 ++++----------- styles.go | 8 +- table.go | 4 +- xmlDecodeDrawing.go | 2 +- xmlStyles.go | 16 +-- xmlWorkbook.go | 4 +- xmlWorksheet.go | 2 +- 16 files changed, 214 insertions(+), 322 deletions(-) diff --git a/calc.go b/calc.go index 402c7dee..8e37bb97 100644 --- 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,13 +16710,50 @@ 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 { +// 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, fmt.Sprintf("%s requires rate >= 0", name)) + } + if prYld.Type != ArgNumber { + return prYld + } + if redemption.Type != ArgNumber { + return redemption + } + 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") + } + } + 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, "PRICE requires 6 or 7 arguments") + return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 6 or 7 arguments", name)) } args := fn.prepareDataValueArgs(2, argsList) if args.Type != ArgList { @@ -16770,32 +16761,11 @@ func (fn *formulaFuncs) PRICE(argsList *list.List) formulaArg { } 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") - } - 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") - } + prYld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() 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) + if arg := checkPriceYieldArgs(name, rate, prYld, redemption, frequency); arg.Type != ArgEmpty { + return arg } basis := newNumberFormulaArg(0) if argsList.Len() == 7 { @@ -16803,7 +16773,18 @@ func (fn *formulaFuncs) PRICE(argsList *list.List) formulaArg { 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. diff --git a/calc_test.go b/calc_test.go index b9c9a8d8..a706f3de 100644 --- a/calc_test.go +++ b/calc_test.go @@ -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 94ce2183..a59c6947 100644 --- 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) diff --git a/errors.go b/errors.go index 1a6cc8a0..96eed6fc 100644 --- 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") diff --git a/excelize.go b/excelize.go index d6772856..a0eaecae 100644 --- a/excelize.go +++ b/excelize.go @@ -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 c30c165a..68ddf217 100644 --- 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 { diff --git a/numfmt.go b/numfmt.go index 9e48a6e5..f39ad611 100644 --- 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: diff --git a/numfmt_test.go b/numfmt_test.go index c41fd940..c49393f6 100644 --- a/numfmt_test.go +++ b/numfmt_test.go @@ -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 60b74b3a..7351d160 100644 --- 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-") diff --git a/rows_test.go b/rows_test.go index f94adbd0..acf50ff9 100644 --- a/rows_test.go +++ b/rows_test.go @@ -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)) diff --git a/styles.go b/styles.go index 9dc142e5..70c11d59 100644 --- 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) } diff --git a/table.go b/table.go index 094f7659..b63fe276 100644 --- 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 { diff --git a/xmlDecodeDrawing.go b/xmlDecodeDrawing.go index 612bb62e..c737ac08 100644 --- a/xmlDecodeDrawing.go +++ b/xmlDecodeDrawing.go @@ -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"` diff --git a/xmlStyles.go b/xmlStyles.go index 9700919a..74b9119b 100644 --- a/xmlStyles.go +++ b/xmlStyles.go @@ -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. diff --git a/xmlWorkbook.go b/xmlWorkbook.go index bc71bd4c..c0063750 100644 --- a/xmlWorkbook.go +++ b/xmlWorkbook.go @@ -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"` diff --git a/xmlWorksheet.go b/xmlWorksheet.go index f23c4142..79170dec 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -427,7 +427,7 @@ type xlsxDataValidations struct { DataValidation []*DataValidation `xml:"dataValidation"` } -// DataValidation directly maps the a 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"`