This closes #1061, support multi-byte language on set header footer

typo fixed and simplify code for read the data values arguments of formula functions
This commit is contained in:
xuri 2021-11-16 00:40:44 +08:00
parent 72410361b0
commit bda8e7f812
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
10 changed files with 140 additions and 244 deletions

334
calc.go
View File

@ -2849,7 +2849,7 @@ func (fn *formulaFuncs) ARABIC(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "ARABIC requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "ARABIC requires 1 numeric argument")
} }
text := argsList.Front().Value.(formulaArg).Value() text := argsList.Front().Value.(formulaArg).Value()
if len(text) > 255 { if len(text) > MaxFieldLength {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
text = strings.ToUpper(text) text = strings.ToUpper(text)
@ -7400,47 +7400,12 @@ func (fn *formulaFuncs) DAYS(argsList *list.List) formulaArg {
if argsList.Len() != 2 { if argsList.Len() != 2 {
return newErrorFormulaArg(formulaErrorVALUE, "DAYS requires 2 arguments") return newErrorFormulaArg(formulaErrorVALUE, "DAYS requires 2 arguments")
} }
var end, start float64 args := fn.prepareDataValueArgs(2, argsList)
endArg, startArg := argsList.Front().Value.(formulaArg), argsList.Back().Value.(formulaArg) if args.Type != ArgList {
switch endArg.Type { return args
case ArgNumber:
end = endArg.Number
case ArgString:
endNum := endArg.ToNumber()
if endNum.Type == ArgNumber {
end = endNum.Number
} else {
args := list.New()
args.PushBack(endArg)
endValue := fn.DATEVALUE(args)
if endValue.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
end = endValue.Number end, start := args.List[0], args.List[1]
} return newNumberFormulaArg(end.Number - start.Number)
default:
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
switch startArg.Type {
case ArgNumber:
start = startArg.Number
case ArgString:
startNum := startArg.ToNumber()
if startNum.Type == ArgNumber {
start = startNum.Number
} else {
args := list.New()
args.PushBack(startArg)
startValue := fn.DATEVALUE(args)
if startValue.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
start = startValue.Number
}
default:
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
return newNumberFormulaArg(end - start)
} }
// ISOWEEKNUM function returns the ISO week number of a supplied date. The // ISOWEEKNUM function returns the ISO week number of a supplied date. The
@ -7695,28 +7660,18 @@ func (fn *formulaFuncs) YEARFRAC(argsList *list.List) formulaArg {
if argsList.Len() != 2 && argsList.Len() != 3 { if argsList.Len() != 2 && argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, "YEARFRAC requires 3 or 4 arguments") return newErrorFormulaArg(formulaErrorVALUE, "YEARFRAC requires 3 or 4 arguments")
} }
var basisArg formulaArg args := fn.prepareDataValueArgs(2, argsList)
startArg, endArg := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Front().Next().Value.(formulaArg).ToNumber() if args.Type != ArgList {
args := list.New().Init() return args
if startArg.Type != ArgNumber {
args.PushBack(argsList.Front().Value.(formulaArg))
if startArg = fn.DATEVALUE(args); startArg.Type != ArgNumber {
return startArg
}
}
if endArg.Type != ArgNumber {
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
if endArg = fn.DATEVALUE(args); endArg.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
} }
start, end := args.List[0], args.List[1]
basis := newNumberFormulaArg(0)
if argsList.Len() == 3 { if argsList.Len() == 3 {
if basisArg = argsList.Back().Value.(formulaArg).ToNumber(); basisArg.Type != ArgNumber { if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
return basisArg return basis
} }
} }
return yearFrac(startArg.Number, endArg.Number, int(basisArg.Number)) return yearFrac(start.Number, end.Number, int(basis.Number))
} }
// NOW function returns the current date and time. The function receives no // NOW function returns the current date and time. The function receives no
@ -7859,7 +7814,7 @@ func (fn *formulaFuncs) CHAR(argsList *list.List) formulaArg {
return arg return arg
} }
num := int(arg.Number) num := int(arg.Number)
if num < 0 || num > 255 { if num < 0 || num > MaxFieldLength {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
return newStringFormulaArg(fmt.Sprintf("%c", num)) return newStringFormulaArg(fmt.Sprintf("%c", num))
@ -9413,24 +9368,11 @@ func (fn *formulaFuncs) ACCRINT(argsList *list.List) formulaArg {
if argsList.Len() > 8 { if argsList.Len() > 8 {
return newErrorFormulaArg(formulaErrorVALUE, "ACCRINT allows at most 8 arguments") return newErrorFormulaArg(formulaErrorVALUE, "ACCRINT allows at most 8 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(3, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
issue := fn.DATEVALUE(args) return args
if issue.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
fi := fn.DATEVALUE(args)
if fi.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Next().Value.(formulaArg))
settlement := fn.DATEVALUE(args)
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
issue, settlement := args.List[0], args.List[2]
rate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() rate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
par := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() par := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber() frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
@ -9468,18 +9410,11 @@ func (fn *formulaFuncs) ACCRINTM(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 { if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "ACCRINTM requires 4 or 5 arguments") return newErrorFormulaArg(formulaErrorVALUE, "ACCRINTM requires 4 or 5 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
issue := fn.DATEVALUE(args) return args
if issue.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
settlement := fn.DATEVALUE(args)
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
issue, settlement := args.List[0], args.List[1]
if settlement.Number < issue.Number { if settlement.Number < issue.Number {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
} }
@ -9644,24 +9579,11 @@ func (fn *formulaFuncs) prepareCouponArgs(name string, argsList *list.List) form
if argsList.Len() != 3 && argsList.Len() != 4 { if argsList.Len() != 3 && argsList.Len() != 4 {
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 or 4 arguments", name)) return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 or 4 arguments", name))
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
settlement := argsList.Front().Value.(formulaArg).ToNumber() if args.Type != ArgList {
if settlement.Type != ArgNumber { return args
args.PushBack(argsList.Front().Value.(formulaArg))
settlement = fn.DATEVALUE(args)
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
}
maturity := argsList.Front().Next().Value.(formulaArg).ToNumber()
if maturity.Type != ArgNumber {
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity = fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
} }
settlement, maturity := args.List[0], args.List[1]
if settlement.Number >= maturity.Number { if settlement.Number >= maturity.Number {
return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name)) return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name))
} }
@ -10048,6 +9970,43 @@ func (fn *formulaFuncs) DDB(argsList *list.List) formulaArg {
return newNumberFormulaArg(depreciation) return newNumberFormulaArg(depreciation)
} }
// prepareDataValueArgs convert first N arguments to data value for the
// formula functions.
func (fn *formulaFuncs) prepareDataValueArgs(n int, argsList *list.List) formulaArg {
l := list.New()
dataValues := []formulaArg{}
getDateValue := func(arg formulaArg, l *list.List) formulaArg {
switch arg.Type {
case ArgNumber:
break
case ArgString:
num := arg.ToNumber()
if num.Type == ArgNumber {
arg = num
break
}
l.Init()
l.PushBack(arg)
arg = fn.DATEVALUE(l)
if arg.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
default:
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
return arg
}
for i, arg := 0, argsList.Front(); i < n; arg = arg.Next() {
dataValue := getDateValue(arg.Value.(formulaArg), l)
if dataValue.Type != ArgNumber {
return dataValue
}
dataValues = append(dataValues, dataValue)
i++
}
return newListFormulaArg(dataValues)
}
// DISC function calculates the Discount Rate for a security. The syntax of // DISC function calculates the Discount Rate for a security. The syntax of
// the function is: // the function is:
// //
@ -10057,18 +10016,11 @@ func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 { if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "DISC requires 4 or 5 arguments") return newErrorFormulaArg(formulaErrorVALUE, "DISC requires 4 or 5 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
if maturity.Number <= settlement.Number { if maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "DISC requires maturity > settlement") return newErrorFormulaArg(formulaErrorNUM, "DISC requires maturity > settlement")
} }
@ -10253,18 +10205,11 @@ func (fn *formulaFuncs) INTRATE(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 { if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "INTRATE requires 4 or 5 arguments") return newErrorFormulaArg(formulaErrorVALUE, "INTRATE requires 4 or 5 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
if maturity.Number <= settlement.Number { if maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires maturity > settlement") return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires maturity > settlement")
} }
@ -10707,18 +10652,11 @@ func (fn *formulaFuncs) PRICEDISC(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 { if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "PRICEDISC requires 4 or 5 arguments") return newErrorFormulaArg(formulaErrorVALUE, "PRICEDISC requires 4 or 5 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
if maturity.Number <= settlement.Number { if maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "PRICEDISC requires maturity > settlement") return newErrorFormulaArg(formulaErrorNUM, "PRICEDISC requires maturity > settlement")
} }
@ -10758,27 +10696,14 @@ func (fn *formulaFuncs) PRICEMAT(argsList *list.List) formulaArg {
if argsList.Len() != 5 && argsList.Len() != 6 { if argsList.Len() != 5 && argsList.Len() != 6 {
return newErrorFormulaArg(formulaErrorVALUE, "PRICEMAT requires 5 or 6 arguments") return newErrorFormulaArg(formulaErrorVALUE, "PRICEMAT requires 5 or 6 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(3, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity, issue := args.List[0], args.List[1], args.List[2]
if settlement.Number >= maturity.Number { if settlement.Number >= maturity.Number {
return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires maturity > settlement") return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires maturity > settlement")
} }
args.Init()
args.PushBack(argsList.Front().Next().Next().Value.(formulaArg))
issue := fn.DATEVALUE(args)
if issue.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if issue.Number >= settlement.Number { if issue.Number >= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires settlement > issue") return newErrorFormulaArg(formulaErrorNUM, "PRICEMAT requires settlement > issue")
} }
@ -10938,18 +10863,11 @@ func (fn *formulaFuncs) RECEIVED(argsList *list.List) formulaArg {
if argsList.Len() > 5 { if argsList.Len() > 5 {
return newErrorFormulaArg(formulaErrorVALUE, "RECEIVED allows at most 5 arguments") return newErrorFormulaArg(formulaErrorVALUE, "RECEIVED allows at most 5 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
investment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() investment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
if investment.Type != ArgNumber { if investment.Type != ArgNumber {
return investment return investment
@ -11061,18 +10979,11 @@ func (fn *formulaFuncs) TBILLEQ(argsList *list.List) formulaArg {
if argsList.Len() != 3 { if argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, "TBILLEQ requires 3 arguments") return newErrorFormulaArg(formulaErrorVALUE, "TBILLEQ requires 3 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
dsm := maturity.Number - settlement.Number dsm := maturity.Number - settlement.Number
if dsm > 365 || maturity.Number <= settlement.Number { if dsm > 365 || maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
@ -11096,18 +11007,11 @@ func (fn *formulaFuncs) TBILLPRICE(argsList *list.List) formulaArg {
if argsList.Len() != 3 { if argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, "TBILLPRICE requires 3 arguments") return newErrorFormulaArg(formulaErrorVALUE, "TBILLPRICE requires 3 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
dsm := maturity.Number - settlement.Number dsm := maturity.Number - settlement.Number
if dsm > 365 || maturity.Number <= settlement.Number { if dsm > 365 || maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
@ -11131,18 +11035,11 @@ func (fn *formulaFuncs) TBILLYIELD(argsList *list.List) formulaArg {
if argsList.Len() != 3 { if argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, "TBILLYIELD requires 3 arguments") return newErrorFormulaArg(formulaErrorVALUE, "TBILLYIELD requires 3 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
dsm := maturity.Number - settlement.Number dsm := maturity.Number - settlement.Number
if dsm > 365 || maturity.Number <= settlement.Number { if dsm > 365 || maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
@ -11231,18 +11128,11 @@ func (fn *formulaFuncs) YIELDDISC(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 { if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "YIELDDISC requires 4 or 5 arguments") return newErrorFormulaArg(formulaErrorVALUE, "YIELDDISC requires 4 or 5 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
settlement, maturity := args.List[0], args.List[1]
pr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() pr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
if pr.Type != ArgNumber { if pr.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
@ -11279,24 +11169,20 @@ func (fn *formulaFuncs) YIELDMAT(argsList *list.List) formulaArg {
if argsList.Len() != 5 && argsList.Len() != 6 { if argsList.Len() != 5 && argsList.Len() != 6 {
return newErrorFormulaArg(formulaErrorVALUE, "YIELDMAT requires 5 or 6 arguments") return newErrorFormulaArg(formulaErrorVALUE, "YIELDMAT requires 5 or 6 arguments")
} }
args := list.New().Init() args := fn.prepareDataValueArgs(2, argsList)
args.PushBack(argsList.Front().Value.(formulaArg)) if args.Type != ArgList {
settlement := fn.DATEVALUE(args) return args
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
args.Init() settlement, maturity := args.List[0], args.List[1]
args.PushBack(argsList.Front().Next().Value.(formulaArg)) arg := list.New().Init()
maturity := fn.DATEVALUE(args) issue := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
if maturity.Type != ArgNumber { if issue.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) arg.PushBack(argsList.Front().Next().Next().Value.(formulaArg))
} issue = fn.DATEVALUE(arg)
args.Init()
args.PushBack(argsList.Front().Next().Next().Value.(formulaArg))
issue := fn.DATEVALUE(args)
if issue.Type != ArgNumber { if issue.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
} }
}
if issue.Number >= settlement.Number { if issue.Number >= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "YIELDMAT requires settlement > issue") return newErrorFormulaArg(formulaErrorNUM, "YIELDMAT requires settlement > issue")
} }

View File

@ -245,11 +245,11 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) { func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
a := formatSet.Author a := formatSet.Author
t := formatSet.Text t := formatSet.Text
if len(a) > 255 { if len(a) > MaxFieldLength {
a = a[0:255] a = a[:MaxFieldLength]
} }
if len(t) > 32512 { if len(t) > 32512 {
t = t[0:32512] t = t[:32512]
} }
comments := f.commentsReader(commentsXML) comments := f.commentsReader(commentsXML)
authorID := 0 authorID := 0

View File

@ -35,11 +35,6 @@ const (
DataValidationTypeWhole DataValidationTypeWhole
) )
const (
// dataValidationFormulaStrLen 255 characters
dataValidationFormulaStrLen = 255
)
// DataValidationErrorStyle defined the style of data validation error alert. // DataValidationErrorStyle defined the style of data validation error alert.
type DataValidationErrorStyle int type DataValidationErrorStyle int
@ -120,7 +115,7 @@ func (dd *DataValidation) SetInput(title, msg string) {
// SetDropList data validation list. // SetDropList data validation list.
func (dd *DataValidation) SetDropList(keys []string) error { func (dd *DataValidation) SetDropList(keys []string) error {
formula := strings.Join(keys, ",") formula := strings.Join(keys, ",")
if dataValidationFormulaStrLen < len(utf16.Encode([]rune(formula))) { if MaxFieldLength < len(utf16.Encode([]rune(formula))) {
return ErrDataValidationFormulaLenth return ErrDataValidationFormulaLenth
} }
dd.Formula1 = fmt.Sprintf(`<formula1>"%s"</formula1>`, formulaEscaper.Replace(formula)) dd.Formula1 = fmt.Sprintf(`<formula1>"%s"</formula1>`, formulaEscaper.Replace(formula))

View File

@ -54,8 +54,8 @@ func TestDataValidation(t *testing.T) {
dvRange.Sqref = "A5:B6" dvRange.Sqref = "A5:B6"
for _, listValid := range [][]string{ for _, listValid := range [][]string{
{"1", "2", "3"}, {"1", "2", "3"},
{strings.Repeat("&", 255)}, {strings.Repeat("&", MaxFieldLength)},
{strings.Repeat("\u4E00", 255)}, {strings.Repeat("\u4E00", MaxFieldLength)},
{strings.Repeat("\U0001F600", 100), strings.Repeat("\u4E01", 50), "<&>"}, {strings.Repeat("\U0001F600", 100), strings.Repeat("\u4E01", 50), "<&>"},
{`A<`, `B>`, `C"`, "D\t", `E'`, `F`}, {`A<`, `B>`, `C"`, "D\t", `E'`, `F`},
} { } {

View File

@ -51,6 +51,11 @@ func newInvalidStyleID(styleID int) error {
return fmt.Errorf("invalid style ID %d, negative values are not supported", styleID) return fmt.Errorf("invalid style ID %d, negative values are not supported", styleID)
} }
// newFieldLengthError defined the error message on receiving the field length overflow.
func newFieldLengthError(name string) error {
return fmt.Errorf("field %s must be less or equal than 255 characters", name)
}
var ( var (
// ErrStreamSetColWidth defined the error message on set column width in // ErrStreamSetColWidth defined the error message on set column width in
// stream writing mode. // stream writing mode.

View File

@ -662,8 +662,8 @@ func (f *File) getPivotTableFieldsSubtotal(fields []PivotTableField) []string {
func (f *File) getPivotTableFieldsName(fields []PivotTableField) []string { func (f *File) getPivotTableFieldsName(fields []PivotTableField) []string {
field := make([]string, len(fields)) field := make([]string, len(fields))
for idx, fld := range fields { for idx, fld := range fields {
if len(fld.Name) > 255 { if len(fld.Name) > MaxFieldLength {
field[idx] = fld.Name[0:255] field[idx] = fld.Name[:MaxFieldLength]
continue continue
} }
field[idx] = fld.Name field[idx] = fld.Name

View File

@ -27,6 +27,7 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"unicode/utf16"
"unicode/utf8" "unicode/utf8"
"github.com/mohae/deepcopy" "github.com/mohae/deepcopy"
@ -1092,8 +1093,8 @@ func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error
// Check 6 string type fields: OddHeader, OddFooter, EvenHeader, EvenFooter, // Check 6 string type fields: OddHeader, OddFooter, EvenHeader, EvenFooter,
// FirstFooter, FirstHeader // FirstFooter, FirstHeader
for i := 4; i < v.NumField()-1; i++ { for i := 4; i < v.NumField()-1; i++ {
if v.Field(i).Len() >= 255 { if len(utf16.Encode([]rune(v.Field(i).String()))) > MaxFieldLength {
return fmt.Errorf("field %s must be less than 255 characters", v.Type().Field(i).Name) return newFieldLengthError(v.Type().Field(i).Name)
} }
} }
ws.HeaderFooter = &xlsxHeaderFooter{ ws.HeaderFooter = &xlsxHeaderFooter{

View File

@ -217,10 +217,18 @@ func TestSetHeaderFooter(t *testing.T) {
assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN is not exist") assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN is not exist")
// Test set header and footer with illegal setting. // Test set header and footer with illegal setting.
assert.EqualError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{ assert.EqualError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{
OddHeader: strings.Repeat("c", 256), OddHeader: strings.Repeat("c", MaxFieldLength+1),
}), "field OddHeader must be less than 255 characters") }), "field OddHeader must be less or equal than 255 characters")
assert.NoError(t, f.SetHeaderFooter("Sheet1", nil)) assert.NoError(t, f.SetHeaderFooter("Sheet1", nil))
text := strings.Repeat("一", MaxFieldLength)
assert.NoError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{
OddHeader: text,
OddFooter: text,
EvenHeader: text,
EvenFooter: text,
FirstHeader: text,
}))
assert.NoError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{ assert.NoError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{
DifferentFirst: true, DifferentFirst: true,
DifferentOddEven: true, DifferentOddEven: true,

View File

@ -262,10 +262,10 @@ func TestGetDefaultFont(t *testing.T) {
func TestSetDefaultFont(t *testing.T) { func TestSetDefaultFont(t *testing.T) {
f := NewFile() f := NewFile()
f.SetDefaultFont("Ariel") f.SetDefaultFont("Arial")
styles := f.stylesReader() styles := f.stylesReader()
s := f.GetDefaultFont() s := f.GetDefaultFont()
assert.Equal(t, s, "Ariel", "Default font should change to Ariel") assert.Equal(t, s, "Arial", "Default font should change to Arial")
assert.Equal(t, *styles.CellStyles.CellStyle[0].CustomBuiltIn, true) assert.Equal(t, *styles.CellStyles.CellStyle[0].CustomBuiltIn, true)
} }

View File

@ -99,6 +99,7 @@ const (
MaxFontFamilyLength = 31 MaxFontFamilyLength = 31
MaxFontSize = 409 MaxFontSize = 409
MaxFileNameLength = 207 MaxFileNameLength = 207
MaxFieldLength = 255
MaxColumnWidth = 255 MaxColumnWidth = 255
MaxRowHeight = 409 MaxRowHeight = 409
TotalRows = 1048576 TotalRows = 1048576