Skip to content

Commit ebea684

Browse files
committed
Fix potential file corrupted and change worksheet name case-insensitive
- Using sheet ID instead of sheet index when delete the cell in calculation chain - Update documentation for exported functions - Using `sheet` represent the sheet name in the function parameters
1 parent 0d4c97c commit ebea684

17 files changed

+148
-110
lines changed

calcchain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (f *File) deleteCalcChain(index int, axis string) {
4949
calc := f.calcChainReader()
5050
if calc != nil {
5151
calc.C = xlsxCalcChainCollection(calc.C).Filter(func(c xlsxCalcChainC) bool {
52-
return !((c.I == index && c.R == axis) || (c.I == index && axis == ""))
52+
return !((c.I == index && c.R == axis) || (c.I == index && axis == "") || (c.I == 0 && c.R == axis))
5353
})
5454
}
5555
if len(calc.C) == 0 {

cell.go

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,21 @@ func (c *xlsxC) hasValue() bool {
170170
}
171171

172172
// removeFormula delete formula for the cell.
173-
func (c *xlsxC) removeFormula(ws *xlsxWorksheet) {
174-
if c.F != nil && c.F.T == STCellFormulaTypeShared && c.F.Ref != "" {
175-
si := c.F.Si
176-
for r, row := range ws.SheetData.Row {
177-
for col, cell := range row.C {
178-
if cell.F != nil && cell.F.Si != nil && *cell.F.Si == *si {
179-
ws.SheetData.Row[r].C[col].F = nil
173+
func (f *File) removeFormula(c *xlsxC, ws *xlsxWorksheet, sheet string) {
174+
if c.F != nil && c.Vm == nil {
175+
f.deleteCalcChain(f.getSheetID(sheet), c.R)
176+
if c.F.T == STCellFormulaTypeShared && c.F.Ref != "" {
177+
si := c.F.Si
178+
for r, row := range ws.SheetData.Row {
179+
for col, cell := range row.C {
180+
if cell.F != nil && cell.F.Si != nil && *cell.F.Si == *si {
181+
ws.SheetData.Row[r].C[col].F = nil
182+
}
180183
}
181184
}
182185
}
186+
c.F = nil
183187
}
184-
c.F = nil
185188
}
186189

187190
// setCellIntFunc is a wrapper of SetCellInt.
@@ -281,8 +284,8 @@ func (f *File) SetCellInt(sheet, axis string, value int) error {
281284
defer ws.Unlock()
282285
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
283286
cellData.T, cellData.V = setCellInt(value)
284-
cellData.removeFormula(ws)
285287
cellData.IS = nil
288+
f.removeFormula(cellData, ws, sheet)
286289
return err
287290
}
288291

@@ -308,8 +311,8 @@ func (f *File) SetCellBool(sheet, axis string, value bool) error {
308311
defer ws.Unlock()
309312
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
310313
cellData.T, cellData.V = setCellBool(value)
311-
cellData.removeFormula(ws)
312314
cellData.IS = nil
315+
f.removeFormula(cellData, ws, sheet)
313316
return err
314317
}
315318

@@ -347,8 +350,8 @@ func (f *File) SetCellFloat(sheet, axis string, value float64, precision, bitSiz
347350
defer ws.Unlock()
348351
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
349352
cellData.T, cellData.V = setCellFloat(value, precision, bitSize)
350-
cellData.removeFormula(ws)
351353
cellData.IS = nil
354+
f.removeFormula(cellData, ws, sheet)
352355
return err
353356
}
354357

@@ -374,8 +377,8 @@ func (f *File) SetCellStr(sheet, axis, value string) error {
374377
defer ws.Unlock()
375378
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
376379
cellData.T, cellData.V, err = f.setCellString(value)
377-
cellData.removeFormula(ws)
378380
cellData.IS = nil
381+
f.removeFormula(cellData, ws, sheet)
379382
return err
380383
}
381384

@@ -474,8 +477,8 @@ func (f *File) SetCellDefault(sheet, axis, value string) error {
474477
defer ws.Unlock()
475478
cellData.S = f.prepareCellStyle(ws, col, row, cellData.S)
476479
cellData.T, cellData.V = setCellDefault(value)
477-
cellData.removeFormula(ws)
478480
cellData.IS = nil
481+
f.removeFormula(cellData, ws, sheet)
479482
return err
480483
}
481484

@@ -510,13 +513,12 @@ type FormulaOpts struct {
510513
}
511514

512515
// SetCellFormula provides a function to set formula on the cell is taken
513-
// according to the given worksheet name (case-sensitive) and cell formula
514-
// settings. The result of the formula cell can be calculated when the
515-
// worksheet is opened by the Office Excel application or can be using
516-
// the "CalcCellValue" function also can get the calculated cell value. If
517-
// the Excel application doesn't calculate the formula automatically when the
518-
// workbook has been opened, please call "UpdateLinkedValue" after setting
519-
// the cell formula functions.
516+
// according to the given worksheet name and cell formula settings. The result
517+
// of the formula cell can be calculated when the worksheet is opened by the
518+
// Office Excel application or can be using the "CalcCellValue" function also
519+
// can get the calculated cell value. If the Excel application doesn't
520+
// calculate the formula automatically when the workbook has been opened,
521+
// please call "UpdateLinkedValue" after setting the cell formula functions.
520522
//
521523
// Example 1, set normal formula "=SUM(A1,B1)" for the cell "A3" on "Sheet1":
522524
//
@@ -662,11 +664,12 @@ func (ws *xlsxWorksheet) countSharedFormula() (count int) {
662664
return
663665
}
664666

665-
// GetCellHyperLink provides a function to get cell hyperlink by given
666-
// worksheet name and axis. Boolean type value link will be true if the cell
667-
// has a hyperlink and the target is the address of the hyperlink. Otherwise,
668-
// the value of link will be false and the value of the target will be a blank
669-
// string. For example get hyperlink of Sheet1!H6:
667+
// GetCellHyperLink gets a cell hyperlink based on the given worksheet name and
668+
// cell coordinates. If the cell has a hyperlink, it will return 'true' and
669+
// the link address, otherwise it will return 'false' and an empty link
670+
// address.
671+
//
672+
// For example, get a hyperlink to a 'H6' cell on a worksheet named 'Sheet1':
670673
//
671674
// link, target, err := f.GetCellHyperLink("Sheet1", "H6")
672675
//
@@ -765,7 +768,7 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype
765768

766769
switch linkType {
767770
case "External":
768-
sheetPath := f.sheetMap[trimSheetName(sheet)]
771+
sheetPath, _ := f.getSheetXMLPath(sheet)
769772
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
770773
rID := f.setRels(linkData.RID, sheetRels, SourceRelationshipHyperLink, link, linkType)
771774
linkData = xlsxHyperlink{

col.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,15 @@ type Cols struct {
4040
sheetXML []byte
4141
}
4242

43-
// GetCols return all the columns in a sheet by given worksheet name (case-sensitive). For example:
43+
// GetCols gets the value of all cells by columns on the worksheet based on the
44+
// given worksheet name, returned as a two-dimensional array, where the value
45+
// of the cell is converted to the `string` type. If the cell format can be
46+
// applied to the value of the cell, the applied value will be used, otherwise
47+
// the original value will be used.
48+
//
49+
// For example, get and traverse the value of all cells by columns on a
50+
// worksheet named
51+
// 'Sheet1':
4452
//
4553
// cols, err := f.GetCols("Sheet1")
4654
// if err != nil {
@@ -196,7 +204,7 @@ func columnXMLHandler(colIterator *columnXMLIterator, xmlElement *xml.StartEleme
196204
// }
197205
//
198206
func (f *File) Cols(sheet string) (*Cols, error) {
199-
name, ok := f.sheetMap[trimSheetName(sheet)]
207+
name, ok := f.getSheetXMLPath(sheet)
200208
if !ok {
201209
return nil, ErrSheetNotExist{sheet}
202210
}

comment.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ func (f *File) AddComment(sheet, cell, format string) error {
115115
drawingVML = strings.ReplaceAll(sheetRelationshipsDrawingVML, "..", "xl")
116116
} else {
117117
// Add first comment for given sheet.
118-
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
118+
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
119+
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
119120
rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
120121
f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "")
121122
f.addSheetNameSpace(sheet, SourceRelationship)

drawing.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ func (f *File) prepareDrawing(ws *xlsxWorksheet, drawingID int, sheet, drawingXM
3333
drawingXML = strings.ReplaceAll(sheetRelationshipsDrawingXML, "..", "xl")
3434
} else {
3535
// Add first picture for given sheet.
36-
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
36+
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
37+
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
3738
rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
3839
f.addSheetDrawing(sheet, rID)
3940
}
@@ -45,7 +46,8 @@ func (f *File) prepareDrawing(ws *xlsxWorksheet, drawingID int, sheet, drawingXM
4546
func (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet string) {
4647
sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
4748
// Only allow one chart in a chartsheet.
48-
sheetRels := "xl/chartsheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/chartsheets/") + ".rels"
49+
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
50+
sheetRels := "xl/chartsheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/chartsheets/") + ".rels"
4951
rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
5052
f.addSheetNameSpace(sheet, SourceRelationship)
5153
cs.Drawing = &xlsxDrawing{

excelize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {
230230
name string
231231
ok bool
232232
)
233-
if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok {
233+
if name, ok = f.getSheetXMLPath(sheet); !ok {
234234
err = fmt.Errorf("sheet %s is not exist", sheet)
235235
return
236236
}

lib.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ func JoinCellName(col string, row int) (string, error) {
187187
}
188188

189189
// ColumnNameToNumber provides a function to convert Excel sheet column name
190-
// to int. Column name case-insensitive. The function returns an error if
191-
// column name incorrect.
190+
// (case-insensitive) to int. The function returns an error if column name
191+
// incorrect.
192192
//
193193
// Example:
194194
//
@@ -690,7 +690,7 @@ func (f *File) setIgnorableNameSpace(path string, index int, ns xml.Attr) {
690690

691691
// addSheetNameSpace add XML attribute for worksheet.
692692
func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) {
693-
name := f.sheetMap[trimSheetName(sheet)]
693+
name, _ := f.getSheetXMLPath(sheet)
694694
f.addNameSpaces(name, ns)
695695
}
696696

picture.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
200200
// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
201201
// relationship index.
202202
func (f *File) deleteSheetRelationships(sheet, rID string) {
203-
name, ok := f.sheetMap[trimSheetName(sheet)]
203+
name, ok := f.getSheetXMLPath(sheet)
204204
if !ok {
205205
name = strings.ToLower(sheet) + ".xml"
206206
}
@@ -450,7 +450,7 @@ func (f *File) addContentTypePart(index int, contentType string) {
450450
// value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
451451
// relationship index.
452452
func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
453-
name, ok := f.sheetMap[trimSheetName(sheet)]
453+
name, ok := f.getSheetXMLPath(sheet)
454454
if !ok {
455455
name = strings.ToLower(sheet) + ".xml"
456456
}

pivotTable.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func (f *File) parseFormatPivotTableSet(opt *PivotTableOption) (*xlsxWorksheet,
190190
if err != nil {
191191
return dataSheet, "", err
192192
}
193-
pivotTableSheetPath, ok := f.sheetMap[trimSheetName(pivotTableSheetName)]
193+
pivotTableSheetPath, ok := f.getSheetXMLPath(pivotTableSheetName)
194194
if !ok {
195195
return dataSheet, pivotTableSheetPath, fmt.Errorf("sheet %s is not exist", pivotTableSheetName)
196196
}

rows.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ import (
2626
"github.com/mohae/deepcopy"
2727
)
2828

29-
// GetRows return all the rows in a sheet by given worksheet name
30-
// (case sensitive), returned as a two-dimensional array, where the value of
31-
// the cell is converted to the string type. If the cell format can be applied
32-
// to the value of the cell, the applied value will be used, otherwise the
33-
// original value will be used. GetRows fetched the rows with value or formula
34-
// cells, the continually blank cells in the tail of each row will be skipped,
35-
// so the length of each row may be inconsistent. For example:
29+
// GetRows return all the rows in a sheet by given worksheet name, returned as
30+
// a two-dimensional array, where the value of the cell is converted to the
31+
// string type. If the cell format can be applied to the value of the cell,
32+
// the applied value will be used, otherwise the original value will be used.
33+
// GetRows fetched the rows with value or formula cells, the continually blank
34+
// cells in the tail of each row will be skipped, so the length of each row
35+
// may be inconsistent.
36+
//
37+
// For example, get and traverse the value of all cells by rows on a worksheet
38+
// named 'Sheet1':
3639
//
3740
// rows, err := f.GetRows("Sheet1")
3841
// if err != nil {
@@ -233,7 +236,7 @@ func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.Sta
233236
// }
234237
//
235238
func (f *File) Rows(sheet string) (*Rows, error) {
236-
name, ok := f.sheetMap[trimSheetName(sheet)]
239+
name, ok := f.getSheetXMLPath(sheet)
237240
if !ok {
238241
return nil, ErrSheetNotExist{sheet}
239242
}

0 commit comments

Comments
 (0)