This closes #1360, closes #1361

- Fix default number format parse issue with a long string of digits
- Fix creating a sheet with an empty name cause a corrupted file
- The `GetCellStyle` function no longer return master cell style of the merge cell range
- Using the specialized name in variables and functions
This commit is contained in:
xuri 2022-09-28 00:04:17 +08:00
parent addcc1a0b2
commit efcf599dfe
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
23 changed files with 126 additions and 145 deletions

View File

@ -83,7 +83,7 @@ func main() {
fmt.Println(err)
}
}()
// Get value from cell by given worksheet name and axis.
// Get value from cell by given worksheet name and cell reference.
cell, err := f.GetCellValue("Sheet1", "B2")
if err != nil {
fmt.Println(err)

View File

@ -185,7 +185,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
Decode(&t); err != nil && err != io.EOF {
return
}
coordinates, err := areaRefToCoordinates(t.Ref)
coordinates, err := rangeRefToCoordinates(t.Ref)
if err != nil {
return
}
@ -204,7 +204,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
idx--
continue
}
t.Ref, _ = f.coordinatesToAreaRef([]int{x1, y1, x2, y2})
t.Ref, _ = f.coordinatesToRangeRef([]int{x1, y1, x2, y2})
if t.AutoFilter != nil {
t.AutoFilter.Ref = t.Ref
}
@ -221,7 +221,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off
return nil
}
coordinates, err := areaRefToCoordinates(ws.AutoFilter.Ref)
coordinates, err := rangeRefToCoordinates(ws.AutoFilter.Ref)
if err != nil {
return err
}
@ -241,7 +241,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off
coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if ws.AutoFilter.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
if ws.AutoFilter.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {
return err
}
return nil
@ -277,8 +277,8 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off
}
for i := 0; i < len(ws.MergeCells.Cells); i++ {
areaData := ws.MergeCells.Cells[i]
coordinates, err := areaRefToCoordinates(areaData.Ref)
mergedCells := ws.MergeCells.Cells[i]
coordinates, err := rangeRefToCoordinates(mergedCells.Ref)
if err != nil {
return err
}
@ -305,8 +305,8 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off
i--
continue
}
areaData.rect = []int{x1, y1, x2, y2}
if areaData.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
mergedCells.rect = []int{x1, y1, x2, y2}
if mergedCells.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {
return err
}
}

20
calc.go
View File

@ -789,10 +789,14 @@ func (f *File) calcCellValue(ctx *calcContext, sheet, cell string) (result strin
return
}
result = token.Value()
isNum, precision := isNumeric(result)
if isNum && (precision > 15 || precision == 0) {
num := roundPrecision(result, -1)
result = strings.ToUpper(num)
if isNum, precision, decimal := isNumeric(result); isNum {
if precision > 15 {
result = strings.ToUpper(strconv.FormatFloat(decimal, 'G', 15, 64))
return
}
if !strings.HasPrefix(result, "0") {
result = strings.ToUpper(strconv.FormatFloat(decimal, 'f', -1, 64))
}
}
return
}
@ -2089,13 +2093,13 @@ func (fn *formulaFuncs) COMPLEX(argsList *list.List) formulaArg {
// cmplx2str replace complex number string characters.
func cmplx2str(num complex128, suffix string) string {
realPart, imagPart := fmt.Sprint(real(num)), fmt.Sprint(imag(num))
isNum, i := isNumeric(realPart)
isNum, i, decimal := isNumeric(realPart)
if isNum && i > 15 {
realPart = roundPrecision(realPart, -1)
realPart = strconv.FormatFloat(decimal, 'G', 15, 64)
}
isNum, i = isNumeric(imagPart)
isNum, i, decimal = isNumeric(imagPart)
if isNum && i > 15 {
imagPart = roundPrecision(imagPart, -1)
imagPart = strconv.FormatFloat(decimal, 'G', 15, 64)
}
c := realPart
if imag(num) > 0 {

View File

@ -1736,7 +1736,7 @@ func TestCalcCellValue(t *testing.T) {
"=UPPER(\"TEST 123\")": "TEST 123",
// VALUE
"=VALUE(\"50\")": "50",
"=VALUE(\"1.0E-07\")": "1E-07",
"=VALUE(\"1.0E-07\")": "0.0000001",
"=VALUE(\"5,000\")": "5000",
"=VALUE(\"20%\")": "0.2",
"=VALUE(\"12:00:00\")": "0.5",

23
cell.go
View File

@ -485,7 +485,7 @@ func (f *File) SetCellDefault(sheet, cell, value string) error {
// setCellDefault prepares cell type and string type cell value by a given
// string.
func setCellDefault(value string) (t string, v string) {
if ok, _ := isNumeric(value); !ok {
if ok, _, _ := isNumeric(value); !ok {
t = "str"
}
v = value
@ -631,7 +631,7 @@ func (f *File) SetCellFormula(sheet, cell, formula string, opts ...FormulaOpts)
// setSharedFormula set shared formula for the cells.
func (ws *xlsxWorksheet) setSharedFormula(ref string) error {
coordinates, err := areaRefToCoordinates(ref)
coordinates, err := rangeRefToCoordinates(ref)
if err != nil {
return err
}
@ -1098,7 +1098,7 @@ func (f *File) setSheetCells(sheet, cell string, slice interface{}, dir adjustDi
return err
}
// getCellInfo does common preparation for all SetCell* methods.
// getCellInfo does common preparation for all set cell value functions.
func (f *File) prepareCell(ws *xlsxWorksheet, cell string) (*xlsxC, int, int, error) {
var err error
cell, err = f.mergeCellsParser(ws, cell)
@ -1116,8 +1116,9 @@ func (f *File) prepareCell(ws *xlsxWorksheet, cell string) (*xlsxC, int, int, er
return &ws.SheetData.Row[row-1].C[col-1], col, row, err
}
// getCellStringFunc does common value extraction workflow for all GetCell*
// methods. Passed function implements specific part of required logic.
// getCellStringFunc does common value extraction workflow for all get cell
// value function. Passed function implements specific part of required
// logic.
func (f *File) getCellStringFunc(sheet, cell string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {
ws, err := f.workSheetReader(sheet)
if err != nil {
@ -1235,7 +1236,7 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error)
i--
continue
}
ok, err := f.checkCellInArea(cell, ws.MergeCells.Cells[i].Ref)
ok, err := f.checkCellInRangeRef(cell, ws.MergeCells.Cells[i].Ref)
if err != nil {
return cell, err
}
@ -1247,18 +1248,18 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error)
return cell, nil
}
// checkCellInArea provides a function to determine if a given cell reference
// checkCellInRangeRef provides a function to determine if a given cell reference
// in a range.
func (f *File) checkCellInArea(cell, area string) (bool, error) {
func (f *File) checkCellInRangeRef(cell, reference string) (bool, error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return false, err
}
if rng := strings.Split(area, ":"); len(rng) != 2 {
if rng := strings.Split(reference, ":"); len(rng) != 2 {
return false, err
}
coordinates, err := areaRefToCoordinates(area)
coordinates, err := rangeRefToCoordinates(reference)
if err != nil {
return false, err
}
@ -1333,7 +1334,7 @@ func parseSharedFormula(dCol, dRow int, orig []byte) (res string, start int) {
// R1C1-reference notation, are the same.
//
// Note that this function not validate ref tag to check the cell whether in
// allow area, and always return origin shared formula.
// allow range reference, and always return origin shared formula.
func getSharedFormula(ws *xlsxWorksheet, si int, cell string) string {
for _, r := range ws.SheetData.Row {
for _, c := range r.C {

View File

@ -95,43 +95,43 @@ func TestConcurrency(t *testing.T) {
assert.NoError(t, f.Close())
}
func TestCheckCellInArea(t *testing.T) {
func TestCheckCellInRangeRef(t *testing.T) {
f := NewFile()
expectedTrueCellInAreaList := [][2]string{
expectedTrueCellInRangeRefList := [][2]string{
{"c2", "A1:AAZ32"},
{"B9", "A1:B9"},
{"C2", "C2:C2"},
}
for _, expectedTrueCellInArea := range expectedTrueCellInAreaList {
cell := expectedTrueCellInArea[0]
area := expectedTrueCellInArea[1]
ok, err := f.checkCellInArea(cell, area)
for _, expectedTrueCellInRangeRef := range expectedTrueCellInRangeRefList {
cell := expectedTrueCellInRangeRef[0]
reference := expectedTrueCellInRangeRef[1]
ok, err := f.checkCellInRangeRef(cell, reference)
assert.NoError(t, err)
assert.Truef(t, ok,
"Expected cell %v to be in area %v, got false\n", cell, area)
"Expected cell %v to be in range reference %v, got false\n", cell, reference)
}
expectedFalseCellInAreaList := [][2]string{
expectedFalseCellInRangeRefList := [][2]string{
{"c2", "A4:AAZ32"},
{"C4", "D6:A1"}, // weird case, but you never know
{"AEF42", "BZ40:AEF41"},
}
for _, expectedFalseCellInArea := range expectedFalseCellInAreaList {
cell := expectedFalseCellInArea[0]
area := expectedFalseCellInArea[1]
ok, err := f.checkCellInArea(cell, area)
for _, expectedFalseCellInRangeRef := range expectedFalseCellInRangeRefList {
cell := expectedFalseCellInRangeRef[0]
reference := expectedFalseCellInRangeRef[1]
ok, err := f.checkCellInRangeRef(cell, reference)
assert.NoError(t, err)
assert.Falsef(t, ok,
"Expected cell %v not to be inside of area %v, but got true\n", cell, area)
"Expected cell %v not to be inside of range reference %v, but got true\n", cell, reference)
}
ok, err := f.checkCellInArea("A1", "A:B")
ok, err := f.checkCellInRangeRef("A1", "A:B")
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
assert.False(t, ok)
ok, err = f.checkCellInArea("AA0", "Z0:AB1")
ok, err = f.checkCellInRangeRef("AA0", "Z0:AB1")
assert.EqualError(t, err, newCellNameToCoordinatesError("AA0", newInvalidCellNameError("AA0")).Error())
assert.False(t, ok)
}
@ -326,8 +326,10 @@ func TestGetCellValue(t *testing.T) {
<c r="Y1"><v>275.39999999999998</v></c>
<c r="Z1"><v>68.900000000000006</v></c>
<c r="AA1"><v>1.1000000000000001</v></c>
<c r="AA2"><v>1234567890123_4</v></c>
<c r="AA3"><v>123456789_0123_4</v></c>
<c r="AB1" t="str"><v>1234567890123_4</v></c>
<c r="AC1" t="str"><v>123456789_0123_4</v></c>
<c r="AD1"><v>+0.0000000000000000002399999999999992E-4</v></c>
<c r="AE1"><v>7.2399999999999992E-2</v></c>
</row>`)))
f.checked = nil
rows, err = f.GetRows("Sheet1")
@ -361,6 +363,8 @@ func TestGetCellValue(t *testing.T) {
"1.1",
"1234567890123_4",
"123456789_0123_4",
"2.39999999999999E-23",
"0.0724",
}}, rows)
assert.NoError(t, err)
}

2
col.go
View File

@ -560,7 +560,7 @@ func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol)
// | | | (x2,y2)|
// +-----+------------+------------+
//
// Example of an object that covers some area from cell A1 to B2.
// Example of an object that covers some range reference from cell A1 to B2.
//
// Based on the width and height of the object we need to calculate 8 vars:
//

View File

@ -333,7 +333,7 @@ func (f *File) squashSqref(cells [][]int) []string {
l, r := 0, 0
for i := 1; i < len(cells); i++ {
if cells[i][0] == cells[r][0] && cells[i][1]-cells[r][1] > 1 {
curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...))
curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...))
if l == r {
curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
}
@ -343,7 +343,7 @@ func (f *File) squashSqref(cells [][]int) []string {
r++
}
}
curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...))
curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...))
if l == r {
curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
}

View File

@ -615,7 +615,7 @@ func TestSetCellStyleBorder(t *testing.T) {
var style int
// Test set border on overlapping area with vertical variants shading styles gradient fill.
// Test set border on overlapping range with vertical variants shading styles gradient fill.
style, err = f.NewStyle(&Style{
Border: []Border{
{Type: "left", Color: "0000FF", Style: 3},

55
lib.go
View File

@ -19,6 +19,7 @@ import (
"fmt"
"io"
"io/ioutil"
"math/big"
"os"
"regexp"
"strconv"
@ -273,19 +274,19 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
return sign + colName + sign + strconv.Itoa(row), err
}
// areaRefToCoordinates provides a function to convert range reference to a
// rangeRefToCoordinates provides a function to convert range reference to a
// pair of coordinates.
func areaRefToCoordinates(ref string) ([]int, error) {
func rangeRefToCoordinates(ref string) ([]int, error) {
rng := strings.Split(strings.ReplaceAll(ref, "$", ""), ":")
if len(rng) < 2 {
return nil, ErrParameterInvalid
}
return areaRangeToCoordinates(rng[0], rng[1])
return cellRefsToCoordinates(rng[0], rng[1])
}
// areaRangeToCoordinates provides a function to convert cell range to a
// cellRefsToCoordinates provides a function to convert cell range to a
// pair of coordinates.
func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) {
func cellRefsToCoordinates(firstCell, lastCell string) ([]int, error) {
coordinates := make([]int, 4)
var err error
coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)
@ -311,9 +312,9 @@ func sortCoordinates(coordinates []int) error {
return nil
}
// coordinatesToAreaRef provides a function to convert a pair of coordinates
// to area reference.
func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
// coordinatesToRangeRef provides a function to convert a pair of coordinates
// to range reference.
func (f *File) coordinatesToRangeRef(coordinates []int) (string, error) {
if len(coordinates) != 4 {
return "", ErrCoordinates
}
@ -364,7 +365,7 @@ func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) {
}
cells[col] = append(cells[col], []int{col, row})
case 2:
if coordinates, err = areaRefToCoordinates(ref); err != nil {
if coordinates, err = rangeRefToCoordinates(ref); err != nil {
return
}
_ = sortCoordinates(coordinates)
@ -691,34 +692,16 @@ func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) {
// isNumeric determines whether an expression is a valid numeric type and get
// the precision for the numeric.
func isNumeric(s string) (bool, int) {
dot, e, n, p := false, false, false, 0
for i, v := range s {
if v == '.' {
if dot {
return false, 0
func isNumeric(s string) (bool, int, float64) {
var decimal big.Float
_, ok := decimal.SetString(s)
if !ok {
return false, 0, 0
}
dot = true
} else if v == 'E' || v == 'e' {
e = true
} else if v < '0' || v > '9' {
if i == 0 && v == '-' {
continue
}
if e && v == '-' {
return true, 0
}
if e && v == '+' {
p = 15
continue
}
return false, 0
} else {
p++
}
n = true
}
return n, p
var noScientificNotation string
flt, _ := decimal.Float64()
noScientificNotation = strconv.FormatFloat(flt, 'f', -1, 64)
return true, len(strings.ReplaceAll(noScientificNotation, ".", "")), flt
}
var (

View File

@ -217,15 +217,15 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
}
}
func TestCoordinatesToAreaRef(t *testing.T) {
func TestCoordinatesToRangeRef(t *testing.T) {
f := NewFile()
_, err := f.coordinatesToAreaRef([]int{})
_, err := f.coordinatesToRangeRef([]int{})
assert.EqualError(t, err, ErrCoordinates.Error())
_, err = f.coordinatesToAreaRef([]int{1, -1, 1, 1})
_, err = f.coordinatesToRangeRef([]int{1, -1, 1, 1})
assert.EqualError(t, err, "invalid cell reference [1, -1]")
_, err = f.coordinatesToAreaRef([]int{1, 1, 1, -1})
_, err = f.coordinatesToRangeRef([]int{1, 1, 1, -1})
assert.EqualError(t, err, "invalid cell reference [1, -1]")
ref, err := f.coordinatesToAreaRef([]int{1, 1, 1, 1})
ref, err := f.coordinatesToRangeRef([]int{1, 1, 1, 1})
assert.NoError(t, err)
assert.EqualValues(t, ref, "A1:A1")
}

View File

@ -17,7 +17,7 @@ import "strings"
func (mc *xlsxMergeCell) Rect() ([]int, error) {
var err error
if mc.rect == nil {
mc.rect, err = areaRefToCoordinates(mc.Ref)
mc.rect, err = rangeRefToCoordinates(mc.Ref)
}
return mc.rect, err
}
@ -46,7 +46,7 @@ func (mc *xlsxMergeCell) Rect() ([]int, error) {
// |A8(x3,y4) C8(x4,y4)|
// +------------------------+
func (f *File) MergeCell(sheet, hCell, vCell string) error {
rect, err := areaRefToCoordinates(hCell + ":" + vCell)
rect, err := rangeRefToCoordinates(hCell + ":" + vCell)
if err != nil {
return err
}
@ -73,11 +73,11 @@ func (f *File) MergeCell(sheet, hCell, vCell string) error {
}
// UnmergeCell provides a function to unmerge a given range reference.
// For example unmerge area D3:E9 on Sheet1:
// For example unmerge range reference D3:E9 on Sheet1:
//
// err := f.UnmergeCell("Sheet1", "D3", "E9")
//
// Attention: overlapped areas will also be unmerged.
// Attention: overlapped range will also be unmerged.
func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
@ -85,7 +85,7 @@ func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
}
ws.Lock()
defer ws.Unlock()
rect1, err := areaRefToCoordinates(hCell + ":" + vCell)
rect1, err := rangeRefToCoordinates(hCell + ":" + vCell)
if err != nil {
return err
}
@ -105,7 +105,7 @@ func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
if mergeCell == nil {
continue
}
rect2, _ := areaRefToCoordinates(mergeCell.Ref)
rect2, _ := rangeRefToCoordinates(mergeCell.Ref)
if isOverlap(rect1, rect2) {
continue
}

View File

@ -169,7 +169,7 @@ func TestUnmergeCell(t *testing.T) {
f = NewFile()
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
// Test unmerged area on not exists worksheet.
// Test unmerged range reference on not exists worksheet.
assert.EqualError(t, f.UnmergeCell("SheetN", "A1", "A1"), "sheet SheetN does not exist")
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")

View File

@ -279,7 +279,7 @@ var (
// prepareNumberic split the number into two before and after parts by a
// decimal point.
func (nf *numberFormat) prepareNumberic(value string) {
if nf.isNumeric, _ = isNumeric(value); !nf.isNumeric {
if nf.isNumeric, _, _ = isNumeric(value); !nf.isNumeric {
return
}
}
@ -338,13 +338,13 @@ func (nf *numberFormat) positiveHandler() (result string) {
continue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder && token.TValue == strings.Repeat("0", len(token.TValue)) {
if isNum, precision := isNumeric(nf.value); isNum {
if isNum, precision, decimal := isNumeric(nf.value); isNum {
if nf.number < 1 {
nf.result += "0"
continue
}
if precision > 15 {
nf.result += roundPrecision(nf.value, 15)
nf.result += strconv.FormatFloat(decimal, 'f', -1, 64)
} else {
nf.result += fmt.Sprintf("%.f", nf.number)
}
@ -902,13 +902,13 @@ func (nf *numberFormat) negativeHandler() (result string) {
continue
}
if token.TType == nfp.TokenTypeZeroPlaceHolder && token.TValue == strings.Repeat("0", len(token.TValue)) {
if isNum, precision := isNumeric(nf.value); isNum {
if isNum, precision, decimal := isNumeric(nf.value); isNum {
if math.Abs(nf.number) < 1 {
nf.result += "0"
continue
}
if precision > 15 {
nf.result += strings.TrimLeft(roundPrecision(nf.value, 15), "-")
nf.result += strings.TrimLeft(strconv.FormatFloat(decimal, 'f', -1, 64), "-")
} else {
nf.result += fmt.Sprintf("%.f", math.Abs(nf.number))
}
@ -941,7 +941,7 @@ func (nf *numberFormat) textHandler() (result string) {
// getValueSectionType returns its applicable number format expression section
// based on the given value.
func (nf *numberFormat) getValueSectionType(value string) (float64, string) {
isNum, _ := isNumeric(value)
isNum, _, _ := isNumeric(value)
if !isNum {
return 0, nfp.TokenSectionText
}

View File

@ -652,11 +652,11 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, formatSe
if inMergeCell {
continue
}
if inMergeCell, err = f.checkCellInArea(cell, mergeCell[0]); err != nil {
if inMergeCell, err = f.checkCellInRangeRef(cell, mergeCell[0]); err != nil {
return
}
if inMergeCell {
rng, _ = areaRangeToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis())
rng, _ = cellRefsToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis())
_ = sortCoordinates(rng)
}
}

View File

@ -81,8 +81,9 @@ type PivotTableField struct {
// options. Note that the same fields can not in Columns, Rows and Filter
// fields at the same time.
//
// For example, create a pivot table on the Sheet1!$G$2:$M$34 area with the
// region Sheet1!$A$1:$E$31 as the data source, summarize by sum for sales:
// For example, create a pivot table on the Sheet1!$G$2:$M$34 range reference
// with the region Sheet1!$A$1:$E$31 as the data source, summarize by sum for
// sales:
//
// package main
//
@ -205,7 +206,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
return "", []int{}, ErrParameterInvalid
}
trimRng := strings.ReplaceAll(rng[1], "$", "")
coordinates, err := areaRefToCoordinates(trimRng)
coordinates, err := rangeRefToCoordinates(trimRng)
if err != nil {
return rng[0], []int{}, err
}

30
rows.go
View File

@ -19,7 +19,6 @@ import (
"io/ioutil"
"log"
"math"
"math/big"
"os"
"strconv"
@ -486,32 +485,17 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
}
return f.formattedValue(c.S, c.V, raw), nil
default:
if isNum, precision := isNumeric(c.V); isNum && !raw {
if precision == 0 {
c.V = roundPrecision(c.V, 15)
if isNum, precision, decimal := isNumeric(c.V); isNum && !raw {
if precision > 15 {
c.V = strconv.FormatFloat(decimal, 'G', 15, 64)
} else {
c.V = roundPrecision(c.V, -1)
c.V = strconv.FormatFloat(decimal, 'f', -1, 64)
}
}
return f.formattedValue(c.S, c.V, raw), nil
}
}
// roundPrecision provides a function to format floating-point number text
// with precision, if the given text couldn't be parsed to float, this will
// return the original string.
func roundPrecision(text string, prec int) string {
decimal := big.Float{}
if _, ok := decimal.SetString(text); ok {
flt, _ := decimal.Float64()
if prec == -1 {
return strconv.FormatFloat(flt, 'G', 15, 64)
}
return strconv.FormatFloat(flt, 'f', -1, 64)
}
return text
}
// SetRowVisible provides a function to set visible of a single row by given
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
//
@ -732,7 +716,7 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in
row++
}
for _, rng := range ws.MergeCells.Cells {
coordinates, err := areaRefToCoordinates(rng.Ref)
coordinates, err := rangeRefToCoordinates(rng.Ref)
if err != nil {
return err
}
@ -741,8 +725,8 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in
}
}
for i := 0; i < len(ws.MergeCells.Cells); i++ {
areaData := ws.MergeCells.Cells[i]
coordinates, _ := areaRefToCoordinates(areaData.Ref)
mergedCells := ws.MergeCells.Cells[i]
coordinates, _ := rangeRefToCoordinates(mergedCells.Ref)
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if y1 == y2 && y1 == row {
from, _ := CoordinatesToCellName(x1, row2)

View File

@ -995,10 +995,6 @@ func TestNumberFormats(t *testing.T) {
assert.NoError(t, f.Close())
}
func TestRoundPrecision(t *testing.T) {
assert.Equal(t, "text", roundPrecision("text", 0))
}
func BenchmarkRows(b *testing.B) {
f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
for i := 0; i < b.N; i++ {

View File

@ -39,6 +39,9 @@ import (
// `Sheet1` will be created.
func (f *File) NewSheet(sheet string) int {
// Check if the worksheet already exists
if trimSheetName(sheet) == "" {
return -1
}
index := f.GetSheetIndex(sheet)
if index != -1 {
return index

View File

@ -90,6 +90,8 @@ func TestNewSheet(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx")))
// create new worksheet with already exists name
assert.Equal(t, f.GetSheetIndex("Sheet2"), f.NewSheet("Sheet2"))
// create new worksheet with empty sheet name
assert.Equal(t, -1, f.NewSheet(":\\/?*[]"))
}
func TestSetPane(t *testing.T) {

View File

@ -145,7 +145,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
return err
}
coordinates, err := areaRangeToCoordinates(hCell, vCell)
coordinates, err := cellRefsToCoordinates(hCell, vCell)
if err != nil {
return err
}
@ -157,7 +157,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
}
// Correct table reference range, such correct C1:B3 to B1:C3.
ref, err := sw.File.coordinatesToAreaRef(coordinates)
ref, err := sw.File.coordinatesToRangeRef(coordinates)
if err != nil {
return err
}
@ -412,7 +412,7 @@ func (sw *StreamWriter) SetColWidth(min, max int, width float64) error {
// the StreamWriter. Don't create a merged cell that overlaps with another
// existing merged cell.
func (sw *StreamWriter) MergeCell(hCell, vCell string) error {
_, err := areaRangeToCoordinates(hCell, vCell)
_, err := cellRefsToCoordinates(hCell, vCell)
if err != nil {
return err
}

View File

@ -2486,11 +2486,14 @@ func (f *File) GetCellStyle(sheet, cell string) (int, error) {
if err != nil {
return 0, err
}
c, col, row, err := f.prepareCell(ws, cell)
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return 0, err
}
return f.prepareCellStyle(ws, col, row, c.S), err
prepareSheetXML(ws, col, row)
ws.Lock()
defer ws.Unlock()
return f.prepareCellStyle(ws, col, row, ws.SheetData.Row[row-1].C[col-1].S), err
}
// SetCellStyle provides a function to add style attribute for cells by given
@ -2856,7 +2859,7 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// max_color - Same as min_color, see above.
//
// bar_color - Used for data_bar. Same as min_color, see above.
func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
func (f *File) SetConditionalFormat(sheet, reference, formatSet string) error {
var format []*formatConditional
err := json.Unmarshal([]byte(formatSet), &format)
if err != nil {
@ -2897,7 +2900,7 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
}
ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{
SQRef: area,
SQRef: reference,
CfRule: cfRule,
})
return err

View File

@ -48,7 +48,7 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) {
// Note that the table must be at least two lines including the header. The
// header cells must contain strings and must be unique, and must set the
// header row data of the table before calling the AddTable function. Multiple
// tables coordinate areas that can't have an intersection.
// tables range reference that can't have an intersection.
//
// table_name: The name of the table, in the same worksheet name of the table should be unique
//
@ -167,7 +167,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
}
// Correct table range reference, such correct C1:B3 to B1:C3.
ref, err := f.coordinatesToAreaRef([]int{x1, y1, x2, y2})
ref, err := f.coordinatesToRangeRef([]int{x1, y1, x2, y2})
if err != nil {
return err
}