@@ -21,6 +21,8 @@ import (
2121 "strings"
2222 "time"
2323 "unicode/utf8"
24+
25+ "github.com/xuri/efp"
2426)
2527
2628// CellType is the type of cell value type.
@@ -1640,44 +1642,16 @@ func isOverlap(rect1, rect2 []int) bool {
16401642
16411643// parseSharedFormula generate dynamic part of shared formula for target cell
16421644// by given column and rows distance and origin shared formula.
1643- func parseSharedFormula (dCol , dRow int , orig []byte ) (res string , start int ) {
1644- var (
1645- end int
1646- stringLiteral bool
1647- )
1648- for end = 0 ; end < len (orig ); end ++ {
1649- c := orig [end ]
1650- if c == '"' {
1651- stringLiteral = ! stringLiteral
1652- }
1653- if stringLiteral {
1654- continue // Skip characters in quotes
1655- }
1656- if c >= 'A' && c <= 'Z' || c == '$' {
1657- res += string (orig [start :end ])
1658- start = end
1659- end ++
1660- foundNum := false
1661- for ; end < len (orig ); end ++ {
1662- idc := orig [end ]
1663- if idc >= '0' && idc <= '9' || idc == '$' {
1664- foundNum = true
1665- } else if idc >= 'A' && idc <= 'Z' {
1666- if foundNum {
1667- break
1668- }
1669- } else {
1670- break
1671- }
1672- }
1673- if foundNum {
1674- cellID := string (orig [start :end ])
1675- res += shiftCell (cellID , dCol , dRow )
1676- start = end
1677- }
1645+ func parseSharedFormula (dCol , dRow int , orig string ) string {
1646+ ps := efp .ExcelParser ()
1647+ tokens := ps .Parse (string (orig ))
1648+ for i := 0 ; i < len (tokens ); i ++ {
1649+ token := tokens [i ]
1650+ if token .TType == efp .TokenTypeOperand && token .TSubType == efp .TokenSubTypeRange {
1651+ tokens [i ].TValue = shiftCell (token .TValue , dCol , dRow )
16781652 }
16791653 }
1680- return
1654+ return ps . Render ()
16811655}
16821656
16831657// getSharedFormula find a cell contains the same formula as another cell,
@@ -1698,12 +1672,7 @@ func getSharedFormula(ws *xlsxWorksheet, si int, cell string) string {
16981672 sharedCol , sharedRow , _ := CellNameToCoordinates (c .R )
16991673 dCol := col - sharedCol
17001674 dRow := row - sharedRow
1701- orig := []byte (c .F .Content )
1702- res , start := parseSharedFormula (dCol , dRow , orig )
1703- if start < len (orig ) {
1704- res += string (orig [start :])
1705- }
1706- return res
1675+ return parseSharedFormula (dCol , dRow , c .F .Content )
17071676 }
17081677 }
17091678 }
@@ -1712,21 +1681,39 @@ func getSharedFormula(ws *xlsxWorksheet, si int, cell string) string {
17121681
17131682// shiftCell returns the cell shifted according to dCol and dRow taking into
17141683// consideration absolute references with dollar sign ($)
1715- func shiftCell (cellID string , dCol , dRow int ) string {
1716- fCol , fRow , _ := CellNameToCoordinates (cellID )
1717- signCol , signRow := "" , ""
1718- if strings .Index (cellID , "$" ) == 0 {
1719- signCol = "$"
1720- } else {
1721- // Shift column
1722- fCol += dCol
1723- }
1724- if strings .LastIndex (cellID , "$" ) > 0 {
1725- signRow = "$"
1726- } else {
1727- // Shift row
1728- fRow += dRow
1684+ func shiftCell (val string , dCol , dRow int ) string {
1685+ parts := strings .Split (val , ":" )
1686+ for j := 0 ; j < len (parts ); j ++ {
1687+ cell := parts [j ]
1688+ trimmedCellName := strings .ReplaceAll (cell , "$" , "" )
1689+ c , r , err := CellNameToCoordinates (trimmedCellName )
1690+ if err == nil {
1691+ absCol := strings .Index (cell , "$" ) == 0
1692+ absRow := strings .LastIndex (cell , "$" ) > 0
1693+ if ! absCol && ! absRow {
1694+ parts [j ], _ = CoordinatesToCellName (c + dCol , r + dRow )
1695+ }
1696+ if ! absCol && absRow {
1697+ colName , _ := ColumnNumberToName (c + dCol )
1698+ parts [j ] = colName + "$" + strconv .Itoa (r )
1699+ }
1700+ if absCol && ! absRow {
1701+ colName , _ := ColumnNumberToName (c )
1702+ parts [j ] = "$" + colName + strconv .Itoa (r + dRow )
1703+ }
1704+ continue
1705+ }
1706+ // Cell reference is a column name
1707+ c , err = ColumnNameToNumber (trimmedCellName )
1708+ if err == nil && ! strings .HasPrefix (cell , "$" ) {
1709+ parts [j ], _ = ColumnNumberToName (c + dCol )
1710+ continue
1711+ }
1712+ // Cell reference is a row number
1713+ r , err = strconv .Atoi (trimmedCellName )
1714+ if err == nil && ! strings .HasPrefix (cell , "$" ) {
1715+ parts [j ] = strconv .Itoa (r + dRow )
1716+ }
17291717 }
1730- colName , _ := ColumnNumberToName (fCol )
1731- return signCol + colName + signRow + strconv .Itoa (fRow )
1718+ return strings .Join (parts , ":" )
17321719}
0 commit comments