diff --git a/adjust.go b/adjust.go
index e1c0e15..d766b3e 100644
--- a/adjust.go
+++ b/adjust.go
@@ -11,6 +11,13 @@
package excelize
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "strings"
+)
+
type adjustDirection bool
const (
@@ -41,6 +48,7 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
f.adjustColDimensions(ws, num, offset)
}
f.adjustHyperlinks(ws, sheet, dir, num, offset)
+ f.adjustTable(ws, sheet, dir, num, offset)
if err = f.adjustMergeCells(ws, dir, num, offset); err != nil {
return err
}
@@ -138,6 +146,54 @@ func (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirec
}
}
+// adjustTable provides a function to update the table when inserting or
+// deleting rows or columns.
+func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
+ if ws.TableParts == nil || len(ws.TableParts.TableParts) == 0 {
+ return
+ }
+ for idx := 0; idx < len(ws.TableParts.TableParts); idx++ {
+ tbl := ws.TableParts.TableParts[idx]
+ target := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)
+ tableXML := strings.ReplaceAll(target, "..", "xl")
+ content, ok := f.Pkg.Load(tableXML)
+ if !ok {
+ continue
+ }
+ t := xlsxTable{}
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
+ Decode(&t); err != nil && err != io.EOF {
+ return
+ }
+ coordinates, err := areaRefToCoordinates(t.Ref)
+ if err != nil {
+ return
+ }
+ // Remove the table when deleting the header row of the table
+ if dir == rows && num == coordinates[0] {
+ ws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)
+ ws.TableParts.Count = len(ws.TableParts.TableParts)
+ idx--
+ continue
+ }
+ coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
+ x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
+ if y2-y1 < 2 || x2-x1 < 1 {
+ ws.TableParts.TableParts = append(ws.TableParts.TableParts[:idx], ws.TableParts.TableParts[idx+1:]...)
+ ws.TableParts.Count = len(ws.TableParts.TableParts)
+ idx--
+ continue
+ }
+ t.Ref, _ = f.coordinatesToAreaRef([]int{x1, y1, x2, y2})
+ if t.AutoFilter != nil {
+ t.AutoFilter.Ref = t.Ref
+ }
+ _, _ = f.setTableHeader(sheet, x1, y1, x2)
+ table, _ := xml.Marshal(t)
+ f.saveFileList(tableXML, table)
+ }
+}
+
// adjustAutoFilter provides a function to update the auto filter when
// inserting or deleting rows or columns.
func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, offset int) error {
@@ -182,10 +238,13 @@ func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, nu
if coordinates[3] >= num {
coordinates[3] += offset
}
- } else {
- if coordinates[2] >= num {
- coordinates[2] += offset
- }
+ return coordinates
+ }
+ if coordinates[0] >= num {
+ coordinates[0] += offset
+ }
+ if coordinates[2] >= num {
+ coordinates[2] += offset
}
return coordinates
}
diff --git a/adjust_test.go b/adjust_test.go
index ab6bedc..1d80705 100644
--- a/adjust_test.go
+++ b/adjust_test.go
@@ -1,6 +1,8 @@
package excelize
import (
+ "fmt"
+ "path/filepath"
"testing"
"github.com/stretchr/testify/assert"
@@ -281,7 +283,7 @@ func TestAdjustAutoFilter(t *testing.T) {
Ref: "A1:A3",
},
}, rows, 1, -1))
- // testing adjustAutoFilter with illegal cell coordinates.
+ // Test adjustAutoFilter with illegal cell coordinates.
assert.EqualError(t, f.adjustAutoFilter(&xlsxWorksheet{
AutoFilter: &xlsxAutoFilter{
Ref: "A:B1",
@@ -294,6 +296,36 @@ func TestAdjustAutoFilter(t *testing.T) {
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
}
+func TestAdjustTable(t *testing.T) {
+ f, sheetName := NewFile(), "Sheet1"
+ for idx, tableRange := range [][]string{{"B2", "C3"}, {"E3", "F5"}, {"H5", "H8"}, {"J5", "K9"}} {
+ assert.NoError(t, f.AddTable(sheetName, tableRange[0], tableRange[1], fmt.Sprintf(`{
+ "table_name": "table%d",
+ "table_style": "TableStyleMedium2",
+ "show_first_column": true,
+ "show_last_column": true,
+ "show_row_stripes": false,
+ "show_column_stripes": true
+ }`, idx)))
+ }
+ assert.NoError(t, f.RemoveRow(sheetName, 2))
+ assert.NoError(t, f.RemoveRow(sheetName, 3))
+ assert.NoError(t, f.RemoveCol(sheetName, "H"))
+ assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAdjustTable.xlsx")))
+
+ f = NewFile()
+ assert.NoError(t, f.AddTable(sheetName, "A1", "D5", ""))
+ // Test adjust table with non-table part
+ f.Pkg.Delete("xl/tables/table1.xml")
+ assert.NoError(t, f.RemoveRow(sheetName, 1))
+ // Test adjust table with unsupported charset
+ f.Pkg.Store("xl/tables/table1.xml", MacintoshCyrillicCharset)
+ assert.NoError(t, f.RemoveRow(sheetName, 1))
+ // Test adjust table with invalid table range reference
+ f.Pkg.Store("xl/tables/table1.xml", []byte(`
`))
+ assert.NoError(t, f.RemoveRow(sheetName, 1))
+}
+
func TestAdjustHelper(t *testing.T) {
f := NewFile()
f.NewSheet("Sheet2")
@@ -303,10 +335,10 @@ func TestAdjustHelper(t *testing.T) {
f.Sheet.Store("xl/worksheets/sheet2.xml", &xlsxWorksheet{
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"},
})
- // testing adjustHelper with illegal cell coordinates.
+ // Test adjustHelper with illegal cell coordinates.
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
- // testing adjustHelper on not exists worksheet.
+ // Test adjustHelper on not exists worksheet.
assert.EqualError(t, f.adjustHelper("SheetN", rows, 0, 0), "sheet SheetN is not exist")
}
diff --git a/cell.go b/cell.go
index 286085b..97425c5 100644
--- a/cell.go
+++ b/cell.go
@@ -169,6 +169,21 @@ func (c *xlsxC) hasValue() bool {
return c.S != 0 || c.V != "" || c.F != nil || c.T != ""
}
+// removeFormula delete formula for the cell.
+func (c *xlsxC) removeFormula(ws *xlsxWorksheet) {
+ if c.F != nil && c.F.T == STCellFormulaTypeShared && c.F.Ref != "" {
+ si := c.F.Si
+ for r, row := range ws.SheetData.Row {
+ for col, cell := range row.C {
+ if cell.F != nil && cell.F.Si != nil && *cell.F.Si == *si {
+ ws.SheetData.Row[r].C[col].F = nil
+ }
+ }
+ }
+ }
+ c.F = nil
+}
+
// setCellIntFunc is a wrapper of SetCellInt.
func (f *File) setCellIntFunc(sheet, axis string, value interface{}) error {
var err error
@@ -266,7 +281,8 @@ func (f *File) SetCellInt(sheet, axis string, value int) error {
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
cellData.T, cellData.V = setCellInt(value)
- cellData.F, cellData.IS = nil, nil
+ cellData.removeFormula(ws)
+ cellData.IS = nil
return err
}
@@ -292,7 +308,8 @@ func (f *File) SetCellBool(sheet, axis string, value bool) error {
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
cellData.T, cellData.V = setCellBool(value)
- cellData.F, cellData.IS = nil, nil
+ cellData.removeFormula(ws)
+ cellData.IS = nil
return err
}
@@ -330,7 +347,8 @@ func (f *File) SetCellFloat(sheet, axis string, value float64, precision, bitSiz
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
cellData.T, cellData.V = setCellFloat(value, precision, bitSize)
- cellData.F, cellData.IS = nil, nil
+ cellData.removeFormula(ws)
+ cellData.IS = nil
return err
}
@@ -356,7 +374,8 @@ func (f *File) SetCellStr(sheet, axis, value string) error {
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
cellData.T, cellData.V, err = f.setCellString(value)
- cellData.F, cellData.IS = nil, nil
+ cellData.removeFormula(ws)
+ cellData.IS = nil
return err
}
@@ -455,7 +474,8 @@ func (f *File) SetCellDefault(sheet, axis, value string) error {
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
cellData.T, cellData.V = setCellDefault(value)
- cellData.F, cellData.IS = nil, nil
+ cellData.removeFormula(ws)
+ cellData.IS = nil
return err
}
diff --git a/picture_test.go b/picture_test.go
index 60c6ac1..3ac1afb 100644
--- a/picture_test.go
+++ b/picture_test.go
@@ -173,7 +173,7 @@ func TestGetPicture(t *testing.T) {
}
func TestAddDrawingPicture(t *testing.T) {
- // testing addDrawingPicture with illegal cell coordinates.
+ // Test addDrawingPicture with illegal cell coordinates.
f := NewFile()
assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", 0, 0, 0, 0, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}
diff --git a/rows_test.go b/rows_test.go
index 014b2d8..4fe2851 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -322,7 +322,7 @@ func TestInsertRow(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestInsertRow.xlsx")))
}
-// Testing internal structure state after insert operations. It is important
+// Test internal structure state after insert operations. It is important
// for insert workflow to be constant to avoid side effect with functions
// related to internal structure.
func TestInsertRowInEmptyFile(t *testing.T) {
diff --git a/sheet.go b/sheet.go
index 2a722c9..6dda811 100644
--- a/sheet.go
+++ b/sheet.go
@@ -478,12 +478,11 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
}
// DeleteSheet provides a function to delete worksheet in a workbook by given
-// worksheet name, the sheet names are not case-sensitive. The sheet names are
-// not case-sensitive. Use this method with caution, which will affect
-// changes in references such as formulas, charts, and so on. If there is any
-// referenced value of the deleted worksheet, it will cause a file error when
-// you open it. This function will be invalid when only the one worksheet is
-// left.
+// worksheet name, the sheet names are not case-sensitive. Use this method
+// with caution, which will affect changes in references such as formulas,
+// charts, and so on. If there is any referenced value of the deleted
+// worksheet, it will cause a file error when you open it. This function will
+// be invalid when only one worksheet is left
func (f *File) DeleteSheet(name string) {
if f.SheetCount == 1 || f.GetSheetIndex(name) == -1 {
return
diff --git a/table.go b/table.go
index 413118c..84445b7 100644
--- a/table.go
+++ b/table.go
@@ -129,28 +129,18 @@ func (f *File) addSheetTable(sheet string, rID int) error {
return err
}
-// addTable provides a function to add table by given worksheet name,
-// coordinate area and format set.
-func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet *formatTable) error {
- // Correct the minimum number of rows, the table at least two lines.
- if y1 == y2 {
- y2++
- }
-
- // Correct table reference coordinate area, such correct C1:B3 to B1:C3.
- ref, err := f.coordinatesToAreaRef([]int{x1, y1, x2, y2})
- if err != nil {
- return err
- }
-
- var tableColumn []*xlsxTableColumn
-
- idx := 0
+// setTableHeader provides a function to set cells value in header row for the
+// table.
+func (f *File) setTableHeader(sheet string, x1, y1, x2 int) ([]*xlsxTableColumn, error) {
+ var (
+ tableColumns []*xlsxTableColumn
+ idx int
+ )
for i := x1; i <= x2; i++ {
idx++
cell, err := CoordinatesToCellName(i, y1)
if err != nil {
- return err
+ return tableColumns, err
}
name, _ := f.GetCellValue(sheet, cell)
if _, err := strconv.Atoi(name); err == nil {
@@ -160,11 +150,28 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
name = "Column" + strconv.Itoa(idx)
_ = f.SetCellStr(sheet, cell, name)
}
- tableColumn = append(tableColumn, &xlsxTableColumn{
+ tableColumns = append(tableColumns, &xlsxTableColumn{
ID: idx,
Name: name,
})
}
+ return tableColumns, nil
+}
+
+// addTable provides a function to add table by given worksheet name,
+// coordinate area and format set.
+func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet *formatTable) error {
+ // Correct the minimum number of rows, the table at least two lines.
+ if y1 == y2 {
+ y2++
+ }
+
+ // Correct table reference coordinate area, such correct C1:B3 to B1:C3.
+ ref, err := f.coordinatesToAreaRef([]int{x1, y1, x2, y2})
+ if err != nil {
+ return err
+ }
+ tableColumns, _ := f.setTableHeader(sheet, x1, y1, x2)
name := formatSet.TableName
if name == "" {
name = "Table" + strconv.Itoa(i)
@@ -179,8 +186,8 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
Ref: ref,
},
TableColumns: &xlsxTableColumns{
- Count: idx,
- TableColumn: tableColumn,
+ Count: len(tableColumns),
+ TableColumn: tableColumns,
},
TableStyleInfo: &xlsxTableStyleInfo{
Name: formatSet.TableStyle,
diff --git a/table_test.go b/table_test.go
index 0a74b1b..5941c50 100644
--- a/table_test.go
+++ b/table_test.go
@@ -45,6 +45,12 @@ func TestAddTable(t *testing.T) {
assert.EqualError(t, f.addTable("sheet1", "", 1, 1, 0, 0, 0, nil), "invalid cell coordinates [0, 0]")
}
+func TestSetTableHeader(t *testing.T) {
+ f := NewFile()
+ _, err := f.setTableHeader("Sheet1", 1, 0, 1)
+ assert.EqualError(t, err, "invalid cell coordinates [1, 0]")
+}
+
func TestAutoFilter(t *testing.T) {
outFile := filepath.Join("test", "TestAutoFilter%d.xlsx")
@@ -72,7 +78,7 @@ func TestAutoFilter(t *testing.T) {
})
}
- // testing AutoFilter with illegal cell coordinates.
+ // Test AutoFilter with illegal cell coordinates.
assert.EqualError(t, f.AutoFilter("Sheet1", "A", "B1", ""), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
assert.EqualError(t, f.AutoFilter("Sheet1", "A1", "B", ""), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
}