7
7
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
8
8
// Supports complex components by high compatibility, and provided streaming
9
9
// API for generating or reading data from a worksheet with huge amounts of
10
- // data. This library needs Go version 1.16 or later.
10
+ // data. This library needs Go version 1.18 or later.
11
11
12
12
package excelize
13
13
@@ -165,7 +165,7 @@ func (f *File) adjustColDimensions(sheet string, ws *xlsxWorksheet, col, offset
165
165
worksheet .SheetData .Row [rowIdx ].C [colIdx ].R , _ = CoordinatesToCellName (newCol , cellRow )
166
166
}
167
167
}
168
- if err := f .adjustFormula (sheet , sheetN , worksheet .SheetData .Row [rowIdx ].C [colIdx ]. F , columns , col , offset , false ); err != nil {
168
+ if err := f .adjustFormula (sheet , sheetN , & worksheet .SheetData .Row [rowIdx ].C [colIdx ], columns , col , offset , false ); err != nil {
169
169
return err
170
170
}
171
171
}
@@ -228,8 +228,8 @@ func (r *xlsxRow) adjustSingleRowDimensions(offset int) {
228
228
229
229
// adjustSingleRowFormulas provides a function to adjust single row formulas.
230
230
func (f * File ) adjustSingleRowFormulas (sheet , sheetN string , r * xlsxRow , num , offset int , si bool ) error {
231
- for _ , col := range r .C {
232
- if err := f .adjustFormula (sheet , sheetN , col . F , rows , num , offset , si ); err != nil {
231
+ for i := 0 ; i < len ( r .C ); i ++ {
232
+ if err := f .adjustFormula (sheet , sheetN , & r . C [ i ] , rows , num , offset , si ); err != nil {
233
233
return err
234
234
}
235
235
}
@@ -273,37 +273,32 @@ func (f *File) adjustCellRef(ref string, dir adjustDirection, num, offset int) (
273
273
274
274
// adjustFormula provides a function to adjust formula reference and shared
275
275
// formula reference.
276
- func (f * File ) adjustFormula (sheet , sheetN string , formula * xlsxF , dir adjustDirection , num , offset int , si bool ) error {
277
- if formula == nil {
276
+ func (f * File ) adjustFormula (sheet , sheetN string , cell * xlsxC , dir adjustDirection , num , offset int , si bool ) error {
277
+ var err error
278
+ if cell .f != "" {
279
+ if cell .f , err = f .adjustFormulaRef (sheet , sheetN , cell .f , false , dir , num , offset ); err != nil {
280
+ return err
281
+ }
282
+ }
283
+ if cell .F == nil {
278
284
return nil
279
285
}
280
- var err error
281
- if formula .Ref != "" && sheet == sheetN {
282
- if formula .Ref , _ , err = f .adjustCellRef (formula .Ref , dir , num , offset ); err != nil {
286
+ if cell .F .Ref != "" && sheet == sheetN {
287
+ if cell .F .Ref , _ , err = f .adjustCellRef (cell .F .Ref , dir , num , offset ); err != nil {
283
288
return err
284
289
}
285
- if si && formula .Si != nil {
286
- formula . Si = intPtr (* formula .Si + 1 )
290
+ if si && cell . F .Si != nil {
291
+ cell . F . Si = intPtr (* cell . F .Si + 1 )
287
292
}
288
293
}
289
- if formula .Content != "" {
290
- if formula . Content , err = f .adjustFormulaRef (sheet , sheetN , formula .Content , false , dir , num , offset ); err != nil {
294
+ if cell . F .Content != "" {
295
+ if cell . F . Content , err = f .adjustFormulaRef (sheet , sheetN , cell . F .Content , false , dir , num , offset ); err != nil {
291
296
return err
292
297
}
293
298
}
294
299
return nil
295
300
}
296
301
297
- // isFunctionStop provides a function to check if token is a function stop.
298
- func isFunctionStop (token efp.Token ) bool {
299
- return token .TType == efp .TokenTypeFunction && token .TSubType == efp .TokenSubTypeStop
300
- }
301
-
302
- // isFunctionStart provides a function to check if token is a function start.
303
- func isFunctionStart (token efp.Token ) bool {
304
- return token .TType == efp .TokenTypeFunction && token .TSubType == efp .TokenSubTypeStart
305
- }
306
-
307
302
// escapeSheetName enclose sheet name in single quotation marks if the giving
308
303
// worksheet name includes spaces or non-alphabetical characters.
309
304
func escapeSheetName (name string ) string {
@@ -442,11 +437,11 @@ func (f *File) adjustFormulaRef(sheet, sheetN, formula string, keepRelative bool
442
437
val += operand
443
438
continue
444
439
}
445
- if isFunctionStart (token ) {
440
+ if isFunctionStartToken (token ) {
446
441
val += token .TValue + string (efp .ParenOpen )
447
442
continue
448
443
}
449
- if isFunctionStop (token ) {
444
+ if isFunctionStopToken (token ) {
450
445
val += token .TValue + string (efp .ParenClose )
451
446
continue
452
447
}
@@ -459,6 +454,115 @@ func (f *File) adjustFormulaRef(sheet, sheetN, formula string, keepRelative bool
459
454
return val , nil
460
455
}
461
456
457
+ // arrayFormulaOperandToken defines meta fields for transforming the array
458
+ // formula to the normal formula.
459
+ type arrayFormulaOperandToken struct {
460
+ operandTokenIndex , topLeftCol , topLeftRow , bottomRightCol , bottomRightRow int
461
+ sheetName , sourceCellRef , targetCellRef string
462
+ }
463
+
464
+ // setCoordinates convert each corner cell reference in the array formula cell
465
+ // range to the coordinate number.
466
+ func (af * arrayFormulaOperandToken ) setCoordinates () error {
467
+ for i , ref := range strings .Split (af .sourceCellRef , ":" ) {
468
+ cellRef , col , row , err := parseRef (ref )
469
+ if err != nil {
470
+ return err
471
+ }
472
+ var c , r int
473
+ if col {
474
+ if cellRef .Row = TotalRows ; i == 1 {
475
+ cellRef .Row = 1
476
+ }
477
+ }
478
+ if row {
479
+ if cellRef .Col = MaxColumns ; i == 1 {
480
+ cellRef .Col = 1
481
+ }
482
+ }
483
+ if c , r = cellRef .Col , cellRef .Row ; cellRef .Sheet != "" {
484
+ af .sheetName = cellRef .Sheet + "!"
485
+ }
486
+ if af .topLeftCol == 0 || c < af .topLeftCol {
487
+ af .topLeftCol = c
488
+ }
489
+ if af .topLeftRow == 0 || r < af .topLeftRow {
490
+ af .topLeftRow = r
491
+ }
492
+ if c > af .bottomRightCol {
493
+ af .bottomRightCol = c
494
+ }
495
+ if r > af .bottomRightRow {
496
+ af .bottomRightRow = r
497
+ }
498
+ }
499
+ return nil
500
+ }
501
+
502
+ // transformArrayFormula transforms an array formula to the normal formula by
503
+ // giving a formula tokens list and formula operand tokens list.
504
+ func transformArrayFormula (tokens []efp.Token , afs []arrayFormulaOperandToken ) string {
505
+ var val string
506
+ for i , token := range tokens {
507
+ var skip bool
508
+ for _ , af := range afs {
509
+ if af .operandTokenIndex == i {
510
+ val += af .sheetName + af .targetCellRef
511
+ skip = true
512
+ break
513
+ }
514
+ }
515
+ if skip {
516
+ continue
517
+ }
518
+ if isFunctionStartToken (token ) {
519
+ val += token .TValue + string (efp .ParenOpen )
520
+ continue
521
+ }
522
+ if isFunctionStopToken (token ) {
523
+ val += token .TValue + string (efp .ParenClose )
524
+ continue
525
+ }
526
+ if token .TType == efp .TokenTypeOperand && token .TSubType == efp .TokenSubTypeText {
527
+ val += string (efp .QuoteDouble ) + strings .ReplaceAll (token .TValue , "\" " , "\" \" " ) + string (efp .QuoteDouble )
528
+ continue
529
+ }
530
+ val += token .TValue
531
+ }
532
+ return val
533
+ }
534
+
535
+ // getArrayFormulaTokens returns parsed formula token and operand related token
536
+ // list for in array formula.
537
+ func getArrayFormulaTokens (sheet , formula string , definedNames []DefinedName ) ([]efp.Token , []arrayFormulaOperandToken , error ) {
538
+ var (
539
+ ps = efp .ExcelParser ()
540
+ tokens = ps .Parse (formula )
541
+ arrayFormulaOperandTokens []arrayFormulaOperandToken
542
+ )
543
+ for i , token := range tokens {
544
+ if token .TSubType == efp .TokenSubTypeRange && token .TType == efp .TokenTypeOperand {
545
+ tokenVal := token .TValue
546
+ for _ , definedName := range definedNames {
547
+ if (definedName .Scope == "Workbook" || definedName .Scope == sheet ) && definedName .Name == tokenVal {
548
+ tokenVal = definedName .RefersTo
549
+ }
550
+ }
551
+ if len (strings .Split (tokenVal , ":" )) > 1 {
552
+ arrayFormulaOperandToken := arrayFormulaOperandToken {
553
+ operandTokenIndex : i ,
554
+ sourceCellRef : tokenVal ,
555
+ }
556
+ if err := arrayFormulaOperandToken .setCoordinates (); err != nil {
557
+ return tokens , arrayFormulaOperandTokens , err
558
+ }
559
+ arrayFormulaOperandTokens = append (arrayFormulaOperandTokens , arrayFormulaOperandToken )
560
+ }
561
+ }
562
+ }
563
+ return tokens , arrayFormulaOperandTokens , nil
564
+ }
565
+
462
566
// adjustHyperlinks provides a function to update hyperlinks when inserting or
463
567
// deleting rows or columns.
464
568
func (f * File ) adjustHyperlinks (ws * xlsxWorksheet , sheet string , dir adjustDirection , num , offset int ) {
0 commit comments