- 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:
parent
addcc1a0b2
commit
efcf599dfe
|
@ -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)
|
||||
|
|
16
adjust.go
16
adjust.go
|
@ -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
20
calc.go
|
@ -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 {
|
||||
|
|
|
@ -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
23
cell.go
|
@ -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 {
|
||||
|
|
38
cell_test.go
38
cell_test.go
|
@ -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
2
col.go
|
@ -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:
|
||||
//
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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
55
lib.go
|
@ -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 (
|
||||
|
|
10
lib_test.go
10
lib_test.go
|
@ -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")
|
||||
}
|
||||
|
|
12
merge.go
12
merge.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
12
numfmt.go
12
numfmt.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
30
rows.go
|
@ -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)
|
||||
|
|
|
@ -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++ {
|
||||
|
|
3
sheet.go
3
sheet.go
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
11
styles.go
11
styles.go
|
@ -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
|
||||
|
|
4
table.go
4
table.go
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue