Преглед на файлове

check empty rich text run properties; new formula fn: LEFT, LEFTB, RIGHT, RIGHTB

xuri преди 4 години
родител
ревизия
d84050921e
променени са 4 файла, в които са добавени 119 реда и са изтрити 3 реда
  1. 70 0
      calc.go
  2. 44 0
      calc_test.go
  3. 3 3
      cell.go
  4. 2 0
      sheet.go

+ 70 - 0
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:
 //

+ 44 - 0
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",

+ 3 - 3
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

+ 2 - 0
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]