This closes #844, support get shared formula
This commit is contained in:
parent
48c16de8bf
commit
b02f864eab
78
cell.go
78
cell.go
|
@ -392,7 +392,7 @@ func (f *File) GetCellFormula(sheet, axis string) (string, error) {
|
||||||
return "", false, nil
|
return "", false, nil
|
||||||
}
|
}
|
||||||
if c.F.T == STCellFormulaTypeShared {
|
if c.F.T == STCellFormulaTypeShared {
|
||||||
return getSharedForumula(x, c.F.Si), true, nil
|
return getSharedForumula(x, c.F.Si, c.R), true, nil
|
||||||
}
|
}
|
||||||
return c.F.Content, true, nil
|
return c.F.Content, true, nil
|
||||||
})
|
})
|
||||||
|
@ -977,6 +977,48 @@ func isOverlap(rect1, rect2 []int) bool {
|
||||||
cellInRef([]int{rect2[2], rect2[3]}, rect1)
|
cellInRef([]int{rect2[2], rect2[3]}, rect1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseSharedFormula generate dynamic part of shared formula for target cell
|
||||||
|
// by given column and rows distance and origin shared formula.
|
||||||
|
func parseSharedFormula(dCol, dRow int, orig []byte) (res string, start int) {
|
||||||
|
var (
|
||||||
|
end int
|
||||||
|
stringLiteral bool
|
||||||
|
)
|
||||||
|
for end = 0; end < len(orig); end++ {
|
||||||
|
c := orig[end]
|
||||||
|
if c == '"' {
|
||||||
|
stringLiteral = !stringLiteral
|
||||||
|
}
|
||||||
|
if stringLiteral {
|
||||||
|
continue // Skip characters in quotes
|
||||||
|
}
|
||||||
|
if c >= 'A' && c <= 'Z' || c == '$' {
|
||||||
|
res += string(orig[start:end])
|
||||||
|
start = end
|
||||||
|
end++
|
||||||
|
foundNum := false
|
||||||
|
for ; end < len(orig); end++ {
|
||||||
|
idc := orig[end]
|
||||||
|
if idc >= '0' && idc <= '9' || idc == '$' {
|
||||||
|
foundNum = true
|
||||||
|
} else if idc >= 'A' && idc <= 'Z' {
|
||||||
|
if foundNum {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundNum {
|
||||||
|
cellID := string(orig[start:end])
|
||||||
|
res += shiftCell(cellID, dCol, dRow)
|
||||||
|
start = end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// getSharedForumula find a cell contains the same formula as another cell,
|
// getSharedForumula find a cell contains the same formula as another cell,
|
||||||
// the "shared" value can be used for the t attribute and the si attribute can
|
// the "shared" value can be used for the t attribute and the si attribute can
|
||||||
// be used to refer to the cell containing the formula. Two formulas are
|
// be used to refer to the cell containing the formula. Two formulas are
|
||||||
|
@ -985,13 +1027,43 @@ func isOverlap(rect1, rect2 []int) bool {
|
||||||
//
|
//
|
||||||
// Note that this function not validate ref tag to check the cell if or not in
|
// Note that this function not validate ref tag to check the cell if or not in
|
||||||
// allow area, and always return origin shared formula.
|
// allow area, and always return origin shared formula.
|
||||||
func getSharedForumula(ws *xlsxWorksheet, si string) string {
|
func getSharedForumula(ws *xlsxWorksheet, si string, axis string) string {
|
||||||
for _, r := range ws.SheetData.Row {
|
for _, r := range ws.SheetData.Row {
|
||||||
for _, c := range r.C {
|
for _, c := range r.C {
|
||||||
if c.F != nil && c.F.Ref != "" && c.F.T == STCellFormulaTypeShared && c.F.Si == si {
|
if c.F != nil && c.F.Ref != "" && c.F.T == STCellFormulaTypeShared && c.F.Si == si {
|
||||||
return c.F.Content
|
col, row, _ := CellNameToCoordinates(axis)
|
||||||
|
sharedCol, sharedRow, _ := CellNameToCoordinates(c.R)
|
||||||
|
dCol := col - sharedCol
|
||||||
|
dRow := row - sharedRow
|
||||||
|
orig := []byte(c.F.Content)
|
||||||
|
res, start := parseSharedFormula(dCol, dRow, orig)
|
||||||
|
if start < len(orig) {
|
||||||
|
res += string(orig[start:])
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shiftCell returns the cell shifted according to dCol and dRow taking into
|
||||||
|
// consideration of absolute references with dollar sign ($)
|
||||||
|
func shiftCell(cellID string, dCol, dRow int) string {
|
||||||
|
fCol, fRow, _ := CellNameToCoordinates(cellID)
|
||||||
|
signCol, signRow := "", ""
|
||||||
|
if strings.Index(cellID, "$") == 0 {
|
||||||
|
signCol = "$"
|
||||||
|
} else {
|
||||||
|
// Shift column
|
||||||
|
fCol += dCol
|
||||||
|
}
|
||||||
|
if strings.LastIndex(cellID, "$") > 0 {
|
||||||
|
signRow = "$"
|
||||||
|
} else {
|
||||||
|
// Shift row
|
||||||
|
fRow += dRow
|
||||||
|
}
|
||||||
|
colName, _ := ColumnNumberToName(fCol)
|
||||||
|
return signCol + colName + signRow + strconv.Itoa(fRow)
|
||||||
|
}
|
||||||
|
|
22
cell_test.go
22
cell_test.go
|
@ -226,6 +226,28 @@ func TestGetCellFormula(t *testing.T) {
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A1", true))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A1", true))
|
||||||
_, err = f.GetCellFormula("Sheet1", "A1")
|
_, err = f.GetCellFormula("Sheet1", "A1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test get cell shared formula
|
||||||
|
f = NewFile()
|
||||||
|
sheetData := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData><row r="1"><c r="A1"><v>1</v></c><c r="B1"><f>2*A1</f></c></row><row r="2"><c r="A2"><v>2</v></c><c r="B2"><f t="shared" ref="B2:B7" si="0">%s</f></c></row><row r="3"><c r="A3"><v>3</v></c><c r="B3"><f t="shared" si="0"/></c></row><row r="4"><c r="A4"><v>4</v></c><c r="B4"><f t="shared" si="0"/></c></row><row r="5"><c r="A5"><v>5</v></c><c r="B5"><f t="shared" si="0"/></c></row><row r="6"><c r="A6"><v>6</v></c><c r="B6"><f t="shared" si="0"/></c></row><row r="7"><c r="A7"><v>7</v></c><c r="B7"><f t="shared" si="0"/></c></row></sheetData></worksheet>`
|
||||||
|
|
||||||
|
for sharedFormula, expected := range map[string]string{
|
||||||
|
`2*A2`: `2*A3`,
|
||||||
|
`2*A1A`: `2*A2A`,
|
||||||
|
`2*$A$2+LEN("")`: `2*$A$2+LEN("")`,
|
||||||
|
} {
|
||||||
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
|
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, sharedFormula)))
|
||||||
|
formula, err := f.GetCellFormula("Sheet1", "B3")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expected, formula)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
|
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData><row r="2"><c r="B2"><f t="shared" si="0"></f></c></row></sheetData></worksheet>`))
|
||||||
|
formula, err := f.GetCellFormula("Sheet1", "B2")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "", formula)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleFile_SetCellFloat() {
|
func ExampleFile_SetCellFloat() {
|
||||||
|
|
|
@ -83,7 +83,7 @@ func TestOpenFile(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = f.GetCellFormula("Sheet2", "I11")
|
_, err = f.GetCellFormula("Sheet2", "I11")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
getSharedForumula(&xlsxWorksheet{}, "")
|
getSharedForumula(&xlsxWorksheet{}, "", "")
|
||||||
|
|
||||||
// Test read cell value with given illegal rows number.
|
// Test read cell value with given illegal rows number.
|
||||||
_, err = f.GetCellValue("Sheet2", "a-1")
|
_, err = f.GetCellValue("Sheet2", "a-1")
|
||||||
|
|
5
lib.go
5
lib.go
|
@ -93,13 +93,12 @@ func readFile(file *zip.File) ([]byte, error) {
|
||||||
//
|
//
|
||||||
func SplitCellName(cell string) (string, int, error) {
|
func SplitCellName(cell string) (string, int, error) {
|
||||||
alpha := func(r rune) bool {
|
alpha := func(r rune) bool {
|
||||||
return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
|
return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || (r == 36)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.IndexFunc(cell, alpha) == 0 {
|
if strings.IndexFunc(cell, alpha) == 0 {
|
||||||
i := strings.LastIndexFunc(cell, alpha)
|
i := strings.LastIndexFunc(cell, alpha)
|
||||||
if i >= 0 && i < len(cell)-1 {
|
if i >= 0 && i < len(cell)-1 {
|
||||||
col, rowstr := cell[:i+1], cell[i+1:]
|
col, rowstr := strings.ReplaceAll(cell[:i+1], "$", ""), cell[i+1:]
|
||||||
if row, err := strconv.Atoi(rowstr); err == nil && row > 0 {
|
if row, err := strconv.Atoi(rowstr); err == nil && row > 0 {
|
||||||
return col, row, nil
|
return col, row, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue