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
|
||||
}
|
||||
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
|
||||
})
|
||||
|
@ -977,6 +977,48 @@ func isOverlap(rect1, rect2 []int) bool {
|
|||
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,
|
||||
// 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
|
||||
|
@ -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
|
||||
// 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 _, c := range r.C {
|
||||
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 ""
|
||||
}
|
||||
|
||||
// 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))
|
||||
_, err = f.GetCellFormula("Sheet1", "A1")
|
||||
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() {
|
||||
|
|
|
@ -83,7 +83,7 @@ func TestOpenFile(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
_, err = f.GetCellFormula("Sheet2", "I11")
|
||||
assert.NoError(t, err)
|
||||
getSharedForumula(&xlsxWorksheet{}, "")
|
||||
getSharedForumula(&xlsxWorksheet{}, "", "")
|
||||
|
||||
// Test read cell value with given illegal rows number.
|
||||
_, 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) {
|
||||
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 {
|
||||
i := strings.LastIndexFunc(cell, alpha)
|
||||
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 {
|
||||
return col, row, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue