forked from p30928647/excelize
check empty rich text run properties; new formula fn: LEFT, LEFTB, RIGHT, RIGHTB
This commit is contained in:
parent
bbb8ebfa8c
commit
d84050921e
70
calc.go
70
calc.go
|
@ -288,6 +288,8 @@ var tokenPriority = map[string]int{
|
||||||
// ISO.CEILING
|
// ISO.CEILING
|
||||||
// KURT
|
// KURT
|
||||||
// LCM
|
// LCM
|
||||||
|
// LEFT
|
||||||
|
// LEFTB
|
||||||
// LEN
|
// LEN
|
||||||
// LENB
|
// LENB
|
||||||
// LN
|
// LN
|
||||||
|
@ -321,6 +323,8 @@ var tokenPriority = map[string]int{
|
||||||
// RAND
|
// RAND
|
||||||
// RANDBETWEEN
|
// RANDBETWEEN
|
||||||
// REPT
|
// REPT
|
||||||
|
// RIGHT
|
||||||
|
// RIGHTB
|
||||||
// ROMAN
|
// ROMAN
|
||||||
// ROUND
|
// ROUND
|
||||||
// ROUNDDOWN
|
// ROUNDDOWN
|
||||||
|
@ -4635,6 +4639,54 @@ func (fn *formulaFuncs) EXACT(argsList *list.List) formulaArg {
|
||||||
return newBoolFormulaArg(text1 == text2)
|
return newBoolFormulaArg(text1 == text2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LEFT function returns a specified number of characters from the start of a
|
||||||
|
// supplied text string. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// LEFT(text,[num_chars])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) LEFT(argsList *list.List) formulaArg {
|
||||||
|
return fn.leftRight("LEFT", argsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEFTB returns the first character or characters in a text string, based on
|
||||||
|
// the number of bytes you specify. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// LEFTB(text,[num_bytes])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) LEFTB(argsList *list.List) formulaArg {
|
||||||
|
return fn.leftRight("LEFTB", argsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// leftRight is an implementation of the formula function LEFT, LEFTB, RIGHT,
|
||||||
|
// RIGHTB. TODO: support DBCS include Japanese, Chinese (Simplified), Chinese
|
||||||
|
// (Traditional), and Korean.
|
||||||
|
func (fn *formulaFuncs) leftRight(name string, argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() < 1 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name))
|
||||||
|
}
|
||||||
|
if argsList.Len() > 2 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s allows at most 2 arguments", name))
|
||||||
|
}
|
||||||
|
text, numChars := argsList.Front().Value.(formulaArg).Value(), 1
|
||||||
|
if argsList.Len() == 2 {
|
||||||
|
numArg := argsList.Back().Value.(formulaArg).ToNumber()
|
||||||
|
if numArg.Type != ArgNumber {
|
||||||
|
return numArg
|
||||||
|
}
|
||||||
|
if numArg.Number < 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||||
|
}
|
||||||
|
numChars = int(numArg.Number)
|
||||||
|
}
|
||||||
|
if len(text) > numChars {
|
||||||
|
if name == "LEFT" || name == "LEFTB" {
|
||||||
|
return newStringFormulaArg(text[:numChars])
|
||||||
|
}
|
||||||
|
return newStringFormulaArg(text[len(text)-numChars:])
|
||||||
|
}
|
||||||
|
return newStringFormulaArg(text)
|
||||||
|
}
|
||||||
|
|
||||||
// LEN returns the length of a supplied text string. The syntax of the
|
// LEN returns the length of a supplied text string. The syntax of the
|
||||||
// function is:
|
// function is:
|
||||||
//
|
//
|
||||||
|
@ -4742,6 +4794,24 @@ func (fn *formulaFuncs) REPT(argsList *list.List) formulaArg {
|
||||||
return newStringFormulaArg(buf.String())
|
return newStringFormulaArg(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RIGHT function returns a specified number of characters from the end of a
|
||||||
|
// supplied text string. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// RIGHT(text,[num_chars])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) RIGHT(argsList *list.List) formulaArg {
|
||||||
|
return fn.leftRight("RIGHT", argsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RIGHTB returns the last character or characters in a text string, based on
|
||||||
|
// the number of bytes you specify. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// RIGHTB(text,[num_bytes])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) RIGHTB(argsList *list.List) formulaArg {
|
||||||
|
return fn.leftRight("RIGHTB", argsList)
|
||||||
|
}
|
||||||
|
|
||||||
// UPPER converts all characters in a supplied text string to upper case. The
|
// UPPER converts all characters in a supplied text string to upper case. The
|
||||||
// syntax of the function is:
|
// syntax of the function is:
|
||||||
//
|
//
|
||||||
|
|
44
calc_test.go
44
calc_test.go
|
@ -723,6 +723,18 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=EXACT(1,\"1\")": "TRUE",
|
"=EXACT(1,\"1\")": "TRUE",
|
||||||
"=EXACT(1,1)": "TRUE",
|
"=EXACT(1,1)": "TRUE",
|
||||||
"=EXACT(\"A\",\"a\")": "FALSE",
|
"=EXACT(\"A\",\"a\")": "FALSE",
|
||||||
|
// LEFT
|
||||||
|
"=LEFT(\"Original Text\")": "O",
|
||||||
|
"=LEFT(\"Original Text\",4)": "Orig",
|
||||||
|
"=LEFT(\"Original Text\",0)": "",
|
||||||
|
"=LEFT(\"Original Text\",13)": "Original Text",
|
||||||
|
"=LEFT(\"Original Text\",20)": "Original Text",
|
||||||
|
// LEFTB
|
||||||
|
"=LEFTB(\"Original Text\")": "O",
|
||||||
|
"=LEFTB(\"Original Text\",4)": "Orig",
|
||||||
|
"=LEFTB(\"Original Text\",0)": "",
|
||||||
|
"=LEFTB(\"Original Text\",13)": "Original Text",
|
||||||
|
"=LEFTB(\"Original Text\",20)": "Original Text",
|
||||||
// LEN
|
// LEN
|
||||||
"=LEN(\"\")": "0",
|
"=LEN(\"\")": "0",
|
||||||
"=LEN(D1)": "5",
|
"=LEN(D1)": "5",
|
||||||
|
@ -746,6 +758,18 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=REPT(\"*\",0)": "",
|
"=REPT(\"*\",0)": "",
|
||||||
"=REPT(\"*\",1)": "*",
|
"=REPT(\"*\",1)": "*",
|
||||||
"=REPT(\"**\",2)": "****",
|
"=REPT(\"**\",2)": "****",
|
||||||
|
// RIGHT
|
||||||
|
"=RIGHT(\"Original Text\")": "t",
|
||||||
|
"=RIGHT(\"Original Text\",4)": "Text",
|
||||||
|
"=RIGHT(\"Original Text\",0)": "",
|
||||||
|
"=RIGHT(\"Original Text\",13)": "Original Text",
|
||||||
|
"=RIGHT(\"Original Text\",20)": "Original Text",
|
||||||
|
// RIGHTB
|
||||||
|
"=RIGHTB(\"Original Text\")": "t",
|
||||||
|
"=RIGHTB(\"Original Text\",4)": "Text",
|
||||||
|
"=RIGHTB(\"Original Text\",0)": "",
|
||||||
|
"=RIGHTB(\"Original Text\",13)": "Original Text",
|
||||||
|
"=RIGHTB(\"Original Text\",20)": "Original Text",
|
||||||
// UPPER
|
// UPPER
|
||||||
"=UPPER(\"test\")": "TEST",
|
"=UPPER(\"test\")": "TEST",
|
||||||
"=UPPER(\"TEST\")": "TEST",
|
"=UPPER(\"TEST\")": "TEST",
|
||||||
|
@ -1308,6 +1332,16 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
// EXACT
|
// EXACT
|
||||||
"=EXACT()": "EXACT requires 2 arguments",
|
"=EXACT()": "EXACT requires 2 arguments",
|
||||||
"=EXACT(1,2,3)": "EXACT requires 2 arguments",
|
"=EXACT(1,2,3)": "EXACT requires 2 arguments",
|
||||||
|
// LEFT
|
||||||
|
"=LEFT()": "LEFT requires at least 1 argument",
|
||||||
|
"=LEFT(\"\",2,3)": "LEFT allows at most 2 arguments",
|
||||||
|
"=LEFT(\"\",\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=LEFT(\"\",-1)": "#VALUE!",
|
||||||
|
// LEFTB
|
||||||
|
"=LEFTB()": "LEFTB requires at least 1 argument",
|
||||||
|
"=LEFTB(\"\",2,3)": "LEFTB allows at most 2 arguments",
|
||||||
|
"=LEFTB(\"\",\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=LEFTB(\"\",-1)": "#VALUE!",
|
||||||
// LEN
|
// LEN
|
||||||
"=LEN()": "LEN requires 1 string argument",
|
"=LEN()": "LEN requires 1 string argument",
|
||||||
// LENB
|
// LENB
|
||||||
|
@ -1329,6 +1363,16 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=REPT(INT(0),2)": "REPT requires first argument to be a string",
|
"=REPT(INT(0),2)": "REPT requires first argument to be a string",
|
||||||
"=REPT(\"*\",\"*\")": "REPT requires second argument to be a number",
|
"=REPT(\"*\",\"*\")": "REPT requires second argument to be a number",
|
||||||
"=REPT(\"*\",-1)": "REPT requires second argument to be >= 0",
|
"=REPT(\"*\",-1)": "REPT requires second argument to be >= 0",
|
||||||
|
// RIGHT
|
||||||
|
"=RIGHT()": "RIGHT requires at least 1 argument",
|
||||||
|
"=RIGHT(\"\",2,3)": "RIGHT allows at most 2 arguments",
|
||||||
|
"=RIGHT(\"\",\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=RIGHT(\"\",-1)": "#VALUE!",
|
||||||
|
// RIGHTB
|
||||||
|
"=RIGHTB()": "RIGHTB requires at least 1 argument",
|
||||||
|
"=RIGHTB(\"\",2,3)": "RIGHTB allows at most 2 arguments",
|
||||||
|
"=RIGHTB(\"\",\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=RIGHTB(\"\",-1)": "#VALUE!",
|
||||||
// Conditional Functions
|
// Conditional Functions
|
||||||
// IF
|
// IF
|
||||||
"=IF()": "IF requires at least 1 argument",
|
"=IF()": "IF requires at least 1 argument",
|
||||||
|
|
6
cell.go
6
cell.go
|
@ -522,13 +522,13 @@ func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err erro
|
||||||
font := Font{}
|
font := Font{}
|
||||||
font.Bold = v.RPr.B != nil
|
font.Bold = v.RPr.B != nil
|
||||||
font.Italic = v.RPr.I != nil
|
font.Italic = v.RPr.I != nil
|
||||||
if nil != v.RPr.U {
|
if v.RPr.U != nil && v.RPr.U.Val != nil {
|
||||||
font.Underline = *v.RPr.U.Val
|
font.Underline = *v.RPr.U.Val
|
||||||
}
|
}
|
||||||
if nil != v.RPr.RFont {
|
if v.RPr.RFont != nil && v.RPr.RFont.Val != nil {
|
||||||
font.Family = *v.RPr.RFont.Val
|
font.Family = *v.RPr.RFont.Val
|
||||||
}
|
}
|
||||||
if nil != v.RPr.Sz {
|
if v.RPr.Sz != nil && v.RPr.Sz.Val != nil {
|
||||||
font.Size = *v.RPr.Sz.Val
|
font.Size = *v.RPr.Sz.Val
|
||||||
}
|
}
|
||||||
font.Strike = v.RPr.Strike != nil
|
font.Strike = v.RPr.Strike != nil
|
||||||
|
|
2
sheet.go
2
sheet.go
|
@ -1762,6 +1762,7 @@ func prepareSheetXML(ws *xlsxWorksheet, col int, row int) {
|
||||||
fillColumns(rowData, col, row)
|
fillColumns(rowData, col, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fillColumns fill cells in the column of the row as contiguous.
|
||||||
func fillColumns(rowData *xlsxRow, col, row int) {
|
func fillColumns(rowData *xlsxRow, col, row int) {
|
||||||
cellCount := len(rowData.C)
|
cellCount := len(rowData.C)
|
||||||
if cellCount < col {
|
if cellCount < col {
|
||||||
|
@ -1772,6 +1773,7 @@ func fillColumns(rowData *xlsxRow, col, row int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeContiguousColumns make columns in specific rows as contiguous.
|
||||||
func makeContiguousColumns(ws *xlsxWorksheet, fromRow, toRow, colCount int) {
|
func makeContiguousColumns(ws *xlsxWorksheet, fromRow, toRow, colCount int) {
|
||||||
for ; fromRow < toRow; fromRow++ {
|
for ; fromRow < toRow; fromRow++ {
|
||||||
rowData := &ws.SheetData.Row[fromRow-1]
|
rowData := &ws.SheetData.Row[fromRow-1]
|
||||||
|
|
Loading…
Reference in New Issue