Skip to content

Commit 5bba8f9

Browse files
committed
This improves compatibility for adjusting tables and formula references on inserting/deleting columns or rows
1 parent cf3e016 commit 5bba8f9

File tree

5 files changed

+104
-70
lines changed

5 files changed

+104
-70
lines changed

adjust.go

+43-31
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
5353
return err
5454
}
5555
f.adjustHyperlinks(ws, sheet, dir, num, offset)
56+
ws.checkSheet()
57+
_ = ws.checkRow()
5658
f.adjustTable(ws, sheet, dir, num, offset)
5759
if err = f.adjustMergeCells(ws, dir, num, offset); err != nil {
5860
return err
@@ -63,9 +65,6 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
6365
if err = f.adjustCalcChain(dir, num, offset, sheetID); err != nil {
6466
return err
6567
}
66-
ws.checkSheet()
67-
_ = ws.checkRow()
68-
6968
if ws.MergeCells != nil && len(ws.MergeCells.Cells) == 0 {
7069
ws.MergeCells = nil
7170
}
@@ -279,10 +278,40 @@ func adjustFormulaRowNumber(name string, dir adjustDirection, num, offset int) (
279278
return name, nil
280279
}
281280

281+
// adjustFormulaOperandRef adjust cell reference in the operand tokens for the formula.
282+
func adjustFormulaOperandRef(row, col, operand string, dir adjustDirection, num int, offset int) (string, string, string, error) {
283+
if col != "" {
284+
name, err := adjustFormulaColumnName(col, dir, num, offset)
285+
if err != nil {
286+
return row, col, operand, err
287+
}
288+
operand += name
289+
col = ""
290+
}
291+
if row != "" {
292+
name, err := adjustFormulaRowNumber(row, dir, num, offset)
293+
if err != nil {
294+
return row, col, operand, err
295+
}
296+
operand += name
297+
row = ""
298+
}
299+
return row, col, operand, nil
300+
}
301+
282302
// adjustFormulaOperand adjust range operand tokens for the formula.
283303
func (f *File) adjustFormulaOperand(token efp.Token, dir adjustDirection, num int, offset int) (string, error) {
284-
var col, row, operand string
285-
for _, r := range token.TValue {
304+
var (
305+
err error
306+
sheet, col, row, operand string
307+
cell = token.TValue
308+
tokens = strings.Split(token.TValue, "!")
309+
)
310+
if len(tokens) == 2 { // have a worksheet
311+
sheet, cell = tokens[0], tokens[1]
312+
operand = string(efp.QuoteSingle) + sheet + string(efp.QuoteSingle) + "!"
313+
}
314+
for _, r := range cell {
286315
if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') {
287316
col += string(r)
288317
continue
@@ -299,21 +328,8 @@ func (f *File) adjustFormulaOperand(token efp.Token, dir adjustDirection, num in
299328
}
300329
continue
301330
}
302-
if row != "" {
303-
name, err := adjustFormulaRowNumber(row, dir, num, offset)
304-
if err != nil {
305-
return operand, err
306-
}
307-
operand += name
308-
row = ""
309-
}
310-
if col != "" {
311-
name, err := adjustFormulaColumnName(col, dir, num, offset)
312-
if err != nil {
313-
return operand, err
314-
}
315-
operand += name
316-
col = ""
331+
if row, col, operand, err = adjustFormulaOperandRef(row, col, operand, dir, num, offset); err != nil {
332+
return operand, err
317333
}
318334
operand += string(r)
319335
}
@@ -365,6 +381,10 @@ func (f *File) adjustFormulaRef(sheet, formula string, dir adjustDirection, num,
365381
val += token.TValue + string(efp.ParenClose)
366382
continue
367383
}
384+
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {
385+
val += string(efp.QuoteDouble) + token.TValue + string(efp.QuoteDouble)
386+
continue
387+
}
368388
val += token.TValue
369389
}
370390
return val, nil
@@ -400,16 +420,7 @@ func (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirec
400420
}
401421
for i := range ws.Hyperlinks.Hyperlink {
402422
link := &ws.Hyperlinks.Hyperlink[i] // get reference
403-
colNum, rowNum, _ := CellNameToCoordinates(link.Ref)
404-
if dir == rows {
405-
if rowNum >= num {
406-
link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset)
407-
}
408-
} else {
409-
if colNum >= num {
410-
link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum)
411-
}
412-
}
423+
link.Ref, _ = f.adjustFormulaRef(sheet, link.Ref, dir, num, offset)
413424
}
414425
}
415426

@@ -455,7 +466,8 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
455466
if t.AutoFilter != nil {
456467
t.AutoFilter.Ref = t.Ref
457468
}
458-
_, _ = f.setTableHeader(sheet, true, x1, y1, x2)
469+
_ = f.setTableColumns(sheet, true, x1, y1, x2, &t)
470+
t.TotalsRowCount = 0
459471
table, _ := xml.Marshal(t)
460472
f.saveFileList(tableXML, table)
461473
}

adjust_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -583,11 +583,11 @@ func TestAdjustFormula(t *testing.T) {
583583

584584
// Test adjust formula on duplicate row with relative and absolute cell references
585585
f = NewFile()
586-
assert.NoError(t, f.SetCellFormula("Sheet1", "B10", "A$10+$A11"))
586+
assert.NoError(t, f.SetCellFormula("Sheet1", "B10", "A$10+$A11&\" \""))
587587
assert.NoError(t, f.DuplicateRowTo("Sheet1", 10, 2))
588588
formula, err = f.GetCellFormula("Sheet1", "B2")
589589
assert.NoError(t, err)
590-
assert.Equal(t, "A$2+$A3", formula)
590+
assert.Equal(t, "A$2+$A3&\" \"", formula)
591591

592592
t.Run("for_cells_affected_directly", func(t *testing.T) {
593593
// Test insert row in middle of range with relative and absolute cell references
@@ -699,17 +699,17 @@ func TestAdjustFormula(t *testing.T) {
699699

700700
f = NewFile()
701701
// Test adjust formula on insert row in the middle of the range
702-
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", "SUM(A2,A3)"))
702+
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", "SUM('Sheet1'!A2,A3)"))
703703
assert.NoError(t, f.InsertRows("Sheet1", 3, 1))
704704
formula, err = f.GetCellFormula("Sheet1", "B1")
705705
assert.NoError(t, err)
706-
assert.Equal(t, "SUM(A2,A4)", formula)
706+
assert.Equal(t, "SUM('Sheet1'!A2,A4)", formula)
707707

708708
// Test adjust formula on insert row at the top of the range
709709
assert.NoError(t, f.InsertRows("Sheet1", 2, 1))
710710
formula, err = f.GetCellFormula("Sheet1", "B1")
711711
assert.NoError(t, err)
712-
assert.Equal(t, "SUM(A3,A5)", formula)
712+
assert.Equal(t, "SUM('Sheet1'!A3,A5)", formula)
713713

714714
f = NewFile()
715715
// Test adjust formula on insert col in the middle of the range

table.go

+30-13
Original file line numberDiff line numberDiff line change
@@ -254,37 +254,58 @@ func (f *File) addSheetTable(sheet string, rID int) error {
254254
return err
255255
}
256256

257-
// setTableHeader provides a function to set cells value in header row for the
257+
// setTableColumns provides a function to set cells value in header row for the
258258
// table.
259-
func (f *File) setTableHeader(sheet string, showHeaderRow bool, x1, y1, x2 int) ([]*xlsxTableColumn, error) {
259+
func (f *File) setTableColumns(sheet string, showHeaderRow bool, x1, y1, x2 int, tbl *xlsxTable) error {
260260
var (
261-
tableColumns []*xlsxTableColumn
262-
idx int
261+
idx int
262+
header []string
263+
tableColumns []*xlsxTableColumn
264+
getTableColumn = func(name string) *xlsxTableColumn {
265+
if tbl != nil && tbl.TableColumns != nil {
266+
for _, column := range tbl.TableColumns.TableColumn {
267+
if column.Name == name {
268+
return column
269+
}
270+
}
271+
}
272+
return nil
273+
}
263274
)
264275
for i := x1; i <= x2; i++ {
265276
idx++
266277
cell, err := CoordinatesToCellName(i, y1)
267278
if err != nil {
268-
return tableColumns, err
279+
return err
269280
}
270-
name, _ := f.GetCellValue(sheet, cell)
281+
name, _ := f.GetCellValue(sheet, cell, Options{RawCellValue: true})
271282
if _, err := strconv.Atoi(name); err == nil {
272283
if showHeaderRow {
273284
_ = f.SetCellStr(sheet, cell, name)
274285
}
275286
}
276-
if name == "" {
287+
if name == "" || inStrSlice(header, name, true) != -1 {
277288
name = "Column" + strconv.Itoa(idx)
278289
if showHeaderRow {
279290
_ = f.SetCellStr(sheet, cell, name)
280291
}
281292
}
293+
header = append(header, name)
294+
if column := getTableColumn(name); column != nil {
295+
column.ID, column.DataDxfID = idx, 0
296+
tableColumns = append(tableColumns, column)
297+
continue
298+
}
282299
tableColumns = append(tableColumns, &xlsxTableColumn{
283300
ID: idx,
284301
Name: name,
285302
})
286303
}
287-
return tableColumns, nil
304+
tbl.TableColumns = &xlsxTableColumns{
305+
Count: len(tableColumns),
306+
TableColumn: tableColumns,
307+
}
308+
return nil
288309
}
289310

290311
// checkDefinedName check whether there are illegal characters in the defined
@@ -326,7 +347,6 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *Tab
326347
if err != nil {
327348
return err
328349
}
329-
tableColumns, _ := f.setTableHeader(sheet, !hideHeaderRow, x1, y1, x2)
330350
name := opts.Name
331351
if name == "" {
332352
name = "Table" + strconv.Itoa(i)
@@ -340,10 +360,6 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *Tab
340360
AutoFilter: &xlsxAutoFilter{
341361
Ref: ref,
342362
},
343-
TableColumns: &xlsxTableColumns{
344-
Count: len(tableColumns),
345-
TableColumn: tableColumns,
346-
},
347363
TableStyleInfo: &xlsxTableStyleInfo{
348364
Name: opts.StyleName,
349365
ShowFirstColumn: opts.ShowFirstColumn,
@@ -352,6 +368,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *Tab
352368
ShowColumnStripes: opts.ShowColumnStripes,
353369
},
354370
}
371+
_ = f.setTableColumns(sheet, !hideHeaderRow, x1, y1, x2, &t)
355372
if hideHeaderRow {
356373
t.AutoFilter = nil
357374
t.HeaderRowCount = intPtr(0)

table_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,9 @@ func TestDeleteTable(t *testing.T) {
137137
assert.Equal(t, "Values", val)
138138
}
139139

140-
func TestSetTableHeader(t *testing.T) {
140+
func TestSetTableColumns(t *testing.T) {
141141
f := NewFile()
142-
_, err := f.setTableHeader("Sheet1", true, 1, 0, 1)
143-
assert.Equal(t, newCoordinatesToCellNameError(1, 0), err)
142+
assert.Equal(t, newCoordinatesToCellNameError(1, 0), f.setTableColumns("Sheet1", true, 1, 0, 1, nil))
144143
}
145144

146145
func TestAutoFilter(t *testing.T) {

xmlTable.go

+24-18
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,28 @@ import "encoding/xml"
2121
type xlsxTable struct {
2222
XMLName xml.Name `xml:"table"`
2323
XMLNS string `xml:"xmlns,attr"`
24-
DataCellStyle string `xml:"dataCellStyle,attr,omitempty"`
25-
DataDxfID int `xml:"dataDxfId,attr,omitempty"`
24+
ID int `xml:"id,attr"`
25+
Name string `xml:"name,attr"`
2626
DisplayName string `xml:"displayName,attr,omitempty"`
27-
HeaderRowBorderDxfID int `xml:"headerRowBorderDxfId,attr,omitempty"`
28-
HeaderRowCellStyle string `xml:"headerRowCellStyle,attr,omitempty"`
27+
Comment string `xml:"comment,attr,omitempty"`
28+
Ref string `xml:"ref,attr"`
29+
TableType string `xml:"tableType,attr,omitempty"`
2930
HeaderRowCount *int `xml:"headerRowCount,attr"`
30-
HeaderRowDxfID int `xml:"headerRowDxfId,attr,omitempty"`
31-
ID int `xml:"id,attr"`
3231
InsertRow bool `xml:"insertRow,attr,omitempty"`
3332
InsertRowShift bool `xml:"insertRowShift,attr,omitempty"`
34-
Name string `xml:"name,attr"`
35-
Published bool `xml:"published,attr,omitempty"`
36-
Ref string `xml:"ref,attr"`
3733
TotalsRowCount int `xml:"totalsRowCount,attr,omitempty"`
34+
TotalsRowShown *bool `xml:"totalsRowShown,attr"`
35+
Published bool `xml:"published,attr,omitempty"`
36+
HeaderRowDxfID int `xml:"headerRowDxfId,attr,omitempty"`
37+
DataDxfID int `xml:"dataDxfId,attr,omitempty"`
3838
TotalsRowDxfID int `xml:"totalsRowDxfId,attr,omitempty"`
39-
TotalsRowShown bool `xml:"totalsRowShown,attr"`
39+
HeaderRowBorderDxfID int `xml:"headerRowBorderDxfId,attr,omitempty"`
40+
TableBorderDxfId int `xml:"tableBorderDxfId,attr,omitempty"`
41+
TotalsRowBorderDxfId int `xml:"totalsRowBorderDxfId,attr,omitempty"`
42+
HeaderRowCellStyle string `xml:"headerRowCellStyle,attr,omitempty"`
43+
DataCellStyle string `xml:"dataCellStyle,attr,omitempty"`
44+
TotalsRowCellStyle string `xml:"totalsRowCellStyle,attr,omitempty"`
45+
ConnectionId int `xml:"connectionId,attr,omitempty"`
4046
AutoFilter *xlsxAutoFilter `xml:"autoFilter"`
4147
TableColumns *xlsxTableColumns `xml:"tableColumns"`
4248
TableStyleInfo *xlsxTableStyleInfo `xml:"tableStyleInfo"`
@@ -171,18 +177,18 @@ type xlsxTableColumns struct {
171177
// xlsxTableColumn directly maps the element representing a single column for
172178
// this table.
173179
type xlsxTableColumn struct {
174-
DataCellStyle string `xml:"dataCellStyle,attr,omitempty"`
175-
DataDxfID int `xml:"dataDxfId,attr,omitempty"`
176-
HeaderRowCellStyle string `xml:"headerRowCellStyle,attr,omitempty"`
177-
HeaderRowDxfID int `xml:"headerRowDxfId,attr,omitempty"`
178180
ID int `xml:"id,attr"`
181+
UniqueName string `xml:"uniqueName,attr,omitempty"`
179182
Name string `xml:"name,attr"`
180-
QueryTableFieldID int `xml:"queryTableFieldId,attr,omitempty"`
181-
TotalsRowCellStyle string `xml:"totalsRowCellStyle,attr,omitempty"`
182-
TotalsRowDxfID int `xml:"totalsRowDxfId,attr,omitempty"`
183183
TotalsRowFunction string `xml:"totalsRowFunction,attr,omitempty"`
184184
TotalsRowLabel string `xml:"totalsRowLabel,attr,omitempty"`
185-
UniqueName string `xml:"uniqueName,attr,omitempty"`
185+
QueryTableFieldID int `xml:"queryTableFieldId,attr,omitempty"`
186+
HeaderRowDxfID int `xml:"headerRowDxfId,attr,omitempty"`
187+
DataDxfID int `xml:"dataDxfId,attr,omitempty"`
188+
TotalsRowDxfID int `xml:"totalsRowDxfId,attr,omitempty"`
189+
HeaderRowCellStyle string `xml:"headerRowCellStyle,attr,omitempty"`
190+
DataCellStyle string `xml:"dataCellStyle,attr,omitempty"`
191+
TotalsRowCellStyle string `xml:"totalsRowCellStyle,attr,omitempty"`
186192
}
187193

188194
// xlsxTableStyleInfo directly maps the tableStyleInfo element. This element

0 commit comments

Comments
 (0)