summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--calc.go70
-rw-r--r--calc_test.go44
-rw-r--r--cell.go6
-rw-r--r--sheet.go2
4 files changed, 119 insertions, 3 deletions
diff --git a/calc.go b/calc.go
index 629aacf..a2efbdb 100644
--- a/calc.go
+++ b/calc.go
@@ -288,6 +288,8 @@ var tokenPriority = map[string]int{
// ISO.CEILING
// KURT
// LCM
+// LEFT
+// LEFTB
// LEN
// LENB
// LN
@@ -321,6 +323,8 @@ var tokenPriority = map[string]int{
// RAND
// RANDBETWEEN
// REPT
+// RIGHT
+// RIGHTB
// ROMAN
// ROUND
// ROUNDDOWN
@@ -4635,6 +4639,54 @@ func (fn *formulaFuncs) EXACT(argsList *list.List) formulaArg {
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
// function is:
//
@@ -4742,6 +4794,24 @@ func (fn *formulaFuncs) REPT(argsList *list.List) formulaArg {
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
// syntax of the function is:
//
diff --git a/calc_test.go b/calc_test.go
index 49af523..1b04df8 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -723,6 +723,18 @@ func TestCalcCellValue(t *testing.T) {
"=EXACT(1,\"1\")": "TRUE",
"=EXACT(1,1)": "TRUE",
"=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(\"\")": "0",
"=LEN(D1)": "5",
@@ -746,6 +758,18 @@ func TestCalcCellValue(t *testing.T) {
"=REPT(\"*\",0)": "",
"=REPT(\"*\",1)": "*",
"=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(\"test\")": "TEST",
"=UPPER(\"TEST\")": "TEST",
@@ -1308,6 +1332,16 @@ func TestCalcCellValue(t *testing.T) {
// EXACT
"=EXACT()": "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 requires 1 string argument",
// LENB
@@ -1329,6 +1363,16 @@ func TestCalcCellValue(t *testing.T) {
"=REPT(INT(0),2)": "REPT requires first argument to be a string",
"=REPT(\"*\",\"*\")": "REPT requires second argument to be a number",
"=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
// IF
"=IF()": "IF requires at least 1 argument",
diff --git a/cell.go b/cell.go
index ea4e1d0..892a906 100644
--- a/cell.go
+++ b/cell.go
@@ -522,13 +522,13 @@ func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err erro
font := Font{}
font.Bold = v.RPr.B != 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
}
- if nil != v.RPr.RFont {
+ if v.RPr.RFont != nil && v.RPr.RFont.Val != nil {
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.Strike = v.RPr.Strike != nil
diff --git a/sheet.go b/sheet.go
index 26c0081..d4362b1 100644
--- a/sheet.go
+++ b/sheet.go
@@ -1762,6 +1762,7 @@ func prepareSheetXML(ws *xlsxWorksheet, col int, row int) {
fillColumns(rowData, col, row)
}
+// fillColumns fill cells in the column of the row as contiguous.
func fillColumns(rowData *xlsxRow, col, row int) {
cellCount := len(rowData.C)
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) {
for ; fromRow < toRow; fromRow++ {
rowData := &ws.SheetData.Row[fromRow-1]