@@ -16,6 +16,8 @@ import (
16
16
"encoding/xml"
17
17
"io"
18
18
"strings"
19
+
20
+ "github.com/xuri/efp"
19
21
)
20
22
21
23
type adjustDirection bool
@@ -42,9 +44,9 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
42
44
}
43
45
sheetID := f .getSheetID (sheet )
44
46
if dir == rows {
45
- err = f .adjustRowDimensions (ws , num , offset )
47
+ err = f .adjustRowDimensions (sheet , ws , num , offset )
46
48
} else {
47
- err = f .adjustColDimensions (ws , num , offset )
49
+ err = f .adjustColDimensions (sheet , ws , num , offset )
48
50
}
49
51
if err != nil {
50
52
return err
@@ -116,7 +118,7 @@ func (f *File) adjustCols(ws *xlsxWorksheet, col, offset int) error {
116
118
117
119
// adjustColDimensions provides a function to update column dimensions when
118
120
// inserting or deleting rows or columns.
119
- func (f * File ) adjustColDimensions (ws * xlsxWorksheet , col , offset int ) error {
121
+ func (f * File ) adjustColDimensions (sheet string , ws * xlsxWorksheet , col , offset int ) error {
120
122
for rowIdx := range ws .SheetData .Row {
121
123
for _ , v := range ws .SheetData .Row [rowIdx ].C {
122
124
if cellCol , _ , _ := CellNameToCoordinates (v .R ); col <= cellCol {
@@ -131,50 +133,61 @@ func (f *File) adjustColDimensions(ws *xlsxWorksheet, col, offset int) error {
131
133
if cellCol , cellRow , _ := CellNameToCoordinates (v .R ); col <= cellCol {
132
134
if newCol := cellCol + offset ; newCol > 0 {
133
135
ws .SheetData .Row [rowIdx ].C [colIdx ].R , _ = CoordinatesToCellName (newCol , cellRow )
134
- _ = f .adjustFormula (ws .SheetData .Row [rowIdx ].C [colIdx ].F , columns , offset , false )
135
136
}
136
137
}
138
+ if err := f .adjustFormula (sheet , ws .SheetData .Row [rowIdx ].C [colIdx ].F , columns , col , offset , false ); err != nil {
139
+ return err
140
+ }
137
141
}
138
142
}
139
143
return f .adjustCols (ws , col , offset )
140
144
}
141
145
142
146
// adjustRowDimensions provides a function to update row dimensions when
143
147
// inserting or deleting rows or columns.
144
- func (f * File ) adjustRowDimensions (ws * xlsxWorksheet , row , offset int ) error {
148
+ func (f * File ) adjustRowDimensions (sheet string , ws * xlsxWorksheet , row , offset int ) error {
145
149
totalRows := len (ws .SheetData .Row )
146
150
if totalRows == 0 {
147
151
return nil
148
152
}
149
153
lastRow := & ws .SheetData .Row [totalRows - 1 ]
150
- if newRow := lastRow .R + offset ; lastRow .R >= row && newRow > 0 && newRow >= TotalRows {
154
+ if newRow := lastRow .R + offset ; lastRow .R >= row && newRow > 0 && newRow > TotalRows {
151
155
return ErrMaxRows
152
156
}
153
157
for i := 0 ; i < len (ws .SheetData .Row ); i ++ {
154
158
r := & ws .SheetData .Row [i ]
155
159
if newRow := r .R + offset ; r .R >= row && newRow > 0 {
156
- f .adjustSingleRowDimensions (r , newRow , offset , false )
160
+ if err := f .adjustSingleRowDimensions (sheet , r , row , offset , false ); err != nil {
161
+ return err
162
+ }
157
163
}
158
164
}
159
165
return nil
160
166
}
161
167
162
168
// adjustSingleRowDimensions provides a function to adjust single row dimensions.
163
- func (f * File ) adjustSingleRowDimensions (r * xlsxRow , num , offset int , si bool ) {
164
- r .R = num
169
+ func (f * File ) adjustSingleRowDimensions (sheet string , r * xlsxRow , num , offset int , si bool ) error {
170
+ r .R += offset
165
171
for i , col := range r .C {
166
172
colName , _ , _ := SplitCellName (col .R )
167
- r .C [i ].R , _ = JoinCellName (colName , num )
168
- _ = f .adjustFormula (col .F , rows , offset , si )
173
+ r .C [i ].R , _ = JoinCellName (colName , r .R )
174
+ if err := f .adjustFormula (sheet , col .F , rows , num , offset , si ); err != nil {
175
+ return err
176
+ }
169
177
}
178
+ return nil
170
179
}
171
180
172
- // adjustFormula provides a function to adjust shared formula reference.
173
- func (f * File ) adjustFormula (formula * xlsxF , dir adjustDirection , offset int , si bool ) error {
174
- if formula != nil && formula .Ref != "" {
175
- coordinates , err := rangeRefToCoordinates (formula .Ref )
181
+ // adjustFormula provides a function to adjust formula reference and shared
182
+ // formula reference.
183
+ func (f * File ) adjustFormula (sheet string , formula * xlsxF , dir adjustDirection , num , offset int , si bool ) error {
184
+ if formula == nil {
185
+ return nil
186
+ }
187
+ adjustRef := func (ref string ) (string , error ) {
188
+ coordinates , err := rangeRefToCoordinates (ref )
176
189
if err != nil {
177
- return err
190
+ return ref , err
178
191
}
179
192
if dir == columns {
180
193
coordinates [0 ] += offset
@@ -183,16 +196,72 @@ func (f *File) adjustFormula(formula *xlsxF, dir adjustDirection, offset int, si
183
196
coordinates [1 ] += offset
184
197
coordinates [3 ] += offset
185
198
}
186
- if formula .Ref , err = f .coordinatesToRangeRef (coordinates ); err != nil {
199
+ return f .coordinatesToRangeRef (coordinates )
200
+ }
201
+ var err error
202
+ if formula .Ref != "" {
203
+ if formula .Ref , err = adjustRef (formula .Ref ); err != nil {
187
204
return err
188
205
}
189
206
if si && formula .Si != nil {
190
207
formula .Si = intPtr (* formula .Si + 1 )
191
208
}
192
209
}
210
+ if formula .T == STCellFormulaTypeArray {
211
+ formula .Content , err = adjustRef (strings .TrimPrefix (formula .Content , "=" ))
212
+ return err
213
+ }
214
+ if formula .Content != "" && ! strings .ContainsAny (formula .Content , "[:]" ) {
215
+ content , err := f .adjustFormulaRef (sheet , formula .Content , dir , num , offset )
216
+ if err != nil {
217
+ return err
218
+ }
219
+ formula .Content = content
220
+ }
193
221
return nil
194
222
}
195
223
224
+ // adjustFormulaRef returns adjusted formula text by giving adjusting direction
225
+ // and the base number of column or row, and offset.
226
+ func (f * File ) adjustFormulaRef (sheet string , text string , dir adjustDirection , num , offset int ) (string , error ) {
227
+ var (
228
+ formulaText string
229
+ definedNames []string
230
+ ps = efp .ExcelParser ()
231
+ )
232
+ for _ , definedName := range f .GetDefinedName () {
233
+ if definedName .Scope == "Workbook" || definedName .Scope == sheet {
234
+ definedNames = append (definedNames , definedName .Name )
235
+ }
236
+ }
237
+ for _ , token := range ps .Parse (text ) {
238
+ if token .TType == efp .TokenTypeOperand && token .TSubType == efp .TokenSubTypeRange {
239
+ if inStrSlice (definedNames , token .TValue , true ) != - 1 {
240
+ formulaText += token .TValue
241
+ continue
242
+ }
243
+ c , r , err := CellNameToCoordinates (token .TValue )
244
+ if err != nil {
245
+ return formulaText , err
246
+ }
247
+ if dir == columns && c >= num {
248
+ c += offset
249
+ }
250
+ if dir == rows {
251
+ r += offset
252
+ }
253
+ cell , err := CoordinatesToCellName (c , r , strings .Contains (token .TValue , "$" ))
254
+ if err != nil {
255
+ return formulaText , err
256
+ }
257
+ formulaText += cell
258
+ continue
259
+ }
260
+ formulaText += token .TValue
261
+ }
262
+ return formulaText , nil
263
+ }
264
+
196
265
// adjustHyperlinks provides a function to update hyperlinks when inserting or
197
266
// deleting rows or columns.
198
267
func (f * File ) adjustHyperlinks (ws * xlsxWorksheet , sheet string , dir adjustDirection , num , offset int ) {
@@ -260,7 +329,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
260
329
return
261
330
}
262
331
// Remove the table when deleting the header row of the table
263
- if dir == rows && num == coordinates [0 ] {
332
+ if dir == rows && num == coordinates [0 ] && offset == - 1 {
264
333
ws .TableParts .TableParts = append (ws .TableParts .TableParts [:idx ], ws .TableParts .TableParts [idx + 1 :]... )
265
334
ws .TableParts .Count = len (ws .TableParts .TableParts )
266
335
idx --
@@ -316,8 +385,8 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off
316
385
}
317
386
318
387
// adjustAutoFilterHelper provides a function for adjusting auto filter to
319
- // compare and calculate cell reference by the given adjust direction, operation
320
- // reference and offset.
388
+ // compare and calculate cell reference by the giving adjusting direction,
389
+ // operation reference and offset.
321
390
func (f * File ) adjustAutoFilterHelper (dir adjustDirection , coordinates []int , num , offset int ) []int {
322
391
if dir == rows {
323
392
if coordinates [1 ] >= num {
@@ -422,13 +491,34 @@ func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
422
491
}
423
492
}
424
493
494
+ // adjustCalcChainRef update the cell reference in calculation chain when
495
+ // inserting or deleting rows or columns.
496
+ func (f * File ) adjustCalcChainRef (i , c , r , offset int , dir adjustDirection ) {
497
+ if dir == rows {
498
+ if rn := r + offset ; rn > 0 {
499
+ f .CalcChain .C [i ].R , _ = CoordinatesToCellName (c , rn )
500
+ }
501
+ return
502
+ }
503
+ if nc := c + offset ; nc > 0 {
504
+ f .CalcChain .C [i ].R , _ = CoordinatesToCellName (nc , r )
505
+ }
506
+ }
507
+
425
508
// adjustCalcChain provides a function to update the calculation chain when
426
509
// inserting or deleting rows or columns.
427
510
func (f * File ) adjustCalcChain (dir adjustDirection , num , offset , sheetID int ) error {
428
511
if f .CalcChain == nil {
429
512
return nil
430
513
}
514
+ // If sheet ID is omitted, it is assumed to be the same as the i value of
515
+ // the previous cell.
516
+ var prevSheetID int
431
517
for index , c := range f .CalcChain .C {
518
+ if c .I == 0 {
519
+ c .I = prevSheetID
520
+ }
521
+ prevSheetID = c .I
432
522
if c .I != sheetID {
433
523
continue
434
524
}
@@ -437,14 +527,18 @@ func (f *File) adjustCalcChain(dir adjustDirection, num, offset, sheetID int) er
437
527
return err
438
528
}
439
529
if dir == rows && num <= rowNum {
440
- if newRow := rowNum + offset ; newRow > 0 {
441
- f .CalcChain .C [index ].R , _ = CoordinatesToCellName (colNum , newRow )
530
+ if num == rowNum && offset == - 1 {
531
+ _ = f .deleteCalcChain (c .I , c .R )
532
+ continue
442
533
}
534
+ f .adjustCalcChainRef (index , colNum , rowNum , offset , dir )
443
535
}
444
536
if dir == columns && num <= colNum {
445
- if newCol := colNum + offset ; newCol > 0 {
446
- f .CalcChain .C [index ].R , _ = CoordinatesToCellName (newCol , rowNum )
537
+ if num == colNum && offset == - 1 {
538
+ _ = f .deleteCalcChain (c .I , c .R )
539
+ continue
447
540
}
541
+ f .adjustCalcChainRef (index , colNum , rowNum , offset , dir )
448
542
}
449
543
}
450
544
return nil
0 commit comments