refactor: handler error instead of panic,

Exported functions:

SetCellStyle
InsertCol
RemoveCol
RemoveRow
InsertRow
DuplicateRow
DuplicateRowTo
SetRowHeight
GetRowHeight
GetCellValue
GetCellFormula
GetCellHyperLink
SetCellHyperLink
SetCellInt
SetCellBool
SetCellFloat
SetCellStr
SetCellDefault
GetCellStyle
SetCellValue
MergeCell
SetSheetRow
SetRowVisible
GetRowVisible
SetRowOutlineLevel
GetRowOutlineLevel
GetRows
Columns
SearchSheet
AddTable
GetPicture
AutoFilter
GetColVisible
SetColVisible
GetColOutlineLevel
SetColOutlineLevel
SetColWidth
GetColWidth

inner functions:

adjustHelper
adjustMergeCells
adjustAutoFilter
prepareCell
setDefaultTimeStyle
timeToExcelTime
addDrawingChart
addDrawingVML
addDrawingPicture
getTotalRowsCols
checkRow
addDrawingShape
addTable
This commit is contained in:
xuri 2019-03-23 20:08:06 +08:00
parent 2874d75555
commit 40ff5dc1a7
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
23 changed files with 691 additions and 539 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
~$*.xlsx
test/Test*.xlsx
*.out
test/image3.png
*.test

View File

@ -15,11 +15,6 @@
Excelize is a library written in pure Go and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Microsoft Excel™ 2007 and later. Support save file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [docs reference](https://xuri.me/excelize/).
**WARNING!**
From version 1.5.0 all row manipulation methods uses Excel row numbering starting with `1` instead of zero-based numbering
which take place in some methods in eraler versions.
## Basic Usage
### Installation
@ -123,7 +118,6 @@ func main() {
fmt.Println(err)
}
}
```
### Add picture to XLSX file

View File

@ -15,10 +15,6 @@
Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office OpenXML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。使用本类库要求使用的 Go 语言为 1.8 或更高版本,完整的 API 使用文档请访问 [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) 或查看 [参考文档](https://xuri.me/excelize/)。
**重要提示**
从版本 1.5.0 开始,所有行操作方法都使用从 `1` 开始的 Excel 行编号,早期版本中某些方法中的基于 `0` 的行编号将不再使用。
## 快速上手
### 安装

View File

@ -30,7 +30,7 @@ const (
// TODO: adjustCalcChain, adjustPageBreaks, adjustComments,
// adjustDataValidations, adjustProtectedCells
//
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) {
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
xlsx := f.workSheetReader(sheet)
if dir == rows {
@ -39,11 +39,16 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
f.adjustColDimensions(xlsx, num, offset)
}
f.adjustHyperlinks(xlsx, sheet, dir, num, offset)
f.adjustMergeCells(xlsx, dir, num, offset)
f.adjustAutoFilter(xlsx, dir, num, offset)
if err := f.adjustMergeCells(xlsx, dir, num, offset); err != nil {
return err
}
if err := f.adjustAutoFilter(xlsx, dir, num, offset); err != nil {
return err
}
checkSheet(xlsx)
checkRow(xlsx)
return nil
}
// adjustColDimensions provides a function to update column dimensions when
@ -127,9 +132,9 @@ func (f *File) adjustHyperlinks(xlsx *xlsxWorksheet, sheet string, dir adjustDir
// adjustAutoFilter provides a function to update the auto filter when
// inserting or deleting rows or columns.
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) {
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
if xlsx.AutoFilter == nil {
return
return nil
}
rng := strings.Split(xlsx.AutoFilter.Ref, ":")
@ -138,12 +143,12 @@ func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, o
firstCol, firstRow, err := CellNameToCoordinates(firstCell)
if err != nil {
panic(err)
return err
}
lastCol, lastRow, err := CellNameToCoordinates(lastCell)
if err != nil {
panic(err)
return err
}
if (dir == rows && firstRow == num && offset < 0) || (dir == columns && firstCol == num && lastCol == num) {
@ -154,7 +159,7 @@ func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, o
rowData.Hidden = false
}
}
return
return nil
}
if dir == rows {
@ -171,13 +176,14 @@ func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, o
}
xlsx.AutoFilter.Ref = firstCell + ":" + lastCell
return nil
}
// adjustMergeCells provides a function to update merged cells when inserting
// or deleting rows or columns.
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) {
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
if xlsx.MergeCells == nil {
return
return nil
}
for i, areaData := range xlsx.MergeCells.Cells {
@ -187,12 +193,12 @@ func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, o
firstCol, firstRow, err := CellNameToCoordinates(firstCell)
if err != nil {
panic(err)
return err
}
lastCol, lastRow, err := CellNameToCoordinates(lastCell)
if err != nil {
panic(err)
return err
}
adjust := func(v int) int {
@ -224,13 +230,14 @@ func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, o
}
if firstCell, err = CoordinatesToCellName(firstCol, firstRow); err != nil {
panic(err)
return err
}
if lastCell, err = CoordinatesToCellName(lastCol, lastRow); err != nil {
panic(err)
return err
}
areaData.Ref = firstCell + ":" + lastCell
}
return nil
}

210
cell.go
View File

@ -34,13 +34,13 @@ const (
// worksheet name and axis in XLSX file. If it is possible to apply a format
// to the cell value, it will do so, if not then an error will be returned,
// along with the raw value of the cell.
func (f *File) GetCellValue(sheet, axis string) string {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool) {
func (f *File) GetCellValue(sheet, axis string) (string, error) {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
val, err := c.getValueFrom(f, f.sharedStringsReader())
if err != nil {
panic(err) // Fail fast to avoid future side effects!
return val, false, err
}
return val, true
return val, true, err
})
}
@ -68,7 +68,7 @@ func (f *File) GetCellValue(sheet, axis string) string {
//
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
// set numbers format by SetCellStyle() method.
func (f *File) SetCellValue(sheet, axis string, value interface{}) {
func (f *File) SetCellValue(sheet, axis string, value interface{}) error {
switch v := value.(type) {
case int:
f.SetCellInt(sheet, axis, v)
@ -102,9 +102,12 @@ func (f *File) SetCellValue(sheet, axis string, value interface{}) {
f.SetCellDefault(sheet, axis, strconv.FormatFloat(v.Seconds()/86400.0, 'f', -1, 32))
f.setDefaultTimeStyle(sheet, axis, 21)
case time.Time:
vv := timeToExcelTime(v)
if vv > 0 {
f.SetCellDefault(sheet, axis, strconv.FormatFloat(timeToExcelTime(v), 'f', -1, 64))
excelTime, err := timeToExcelTime(v)
if err != nil {
return err
}
if excelTime > 0 {
f.SetCellDefault(sheet, axis, strconv.FormatFloat(excelTime, 'f', -1, 64))
f.setDefaultTimeStyle(sheet, axis, 22)
} else {
f.SetCellStr(sheet, axis, v.Format(time.RFC3339Nano))
@ -116,23 +119,31 @@ func (f *File) SetCellValue(sheet, axis string, value interface{}) {
default:
f.SetCellStr(sheet, axis, fmt.Sprintf("%v", value))
}
return nil
}
// SetCellInt provides a function to set int type value of a cell by given
// worksheet name, cell coordinates and cell value.
func (f *File) SetCellInt(sheet, axis string, value int) {
func (f *File) SetCellInt(sheet, axis string, value int) error {
xlsx := f.workSheetReader(sheet)
cellData, col, _ := f.prepareCell(xlsx, sheet, axis)
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T = ""
cellData.V = strconv.Itoa(value)
return err
}
// SetCellBool provides a function to set bool type value of a cell by given
// worksheet name, cell name and cell value.
func (f *File) SetCellBool(sheet, axis string, value bool) {
func (f *File) SetCellBool(sheet, axis string, value bool) error {
xlsx := f.workSheetReader(sheet)
cellData, col, _ := f.prepareCell(xlsx, sheet, axis)
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T = "b"
if value {
@ -140,6 +151,7 @@ func (f *File) SetCellBool(sheet, axis string, value bool) {
} else {
cellData.V = "0"
}
return err
}
// SetCellFloat sets a floating point value into a cell. The prec parameter
@ -151,20 +163,26 @@ func (f *File) SetCellBool(sheet, axis string, value bool) {
// var x float32 = 1.325
// f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32)
//
func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int) {
func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int) error {
xlsx := f.workSheetReader(sheet)
cellData, col, _ := f.prepareCell(xlsx, sheet, axis)
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T = ""
cellData.V = strconv.FormatFloat(value, 'f', prec, bitSize)
return err
}
// SetCellStr provides a function to set string type value of a cell. Total
// number of characters that a cell can contain 32767 characters.
func (f *File) SetCellStr(sheet, axis, value string) {
func (f *File) SetCellStr(sheet, axis, value string) error {
xlsx := f.workSheetReader(sheet)
cellData, col, _ := f.prepareCell(xlsx, sheet, axis)
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
// Leading space(s) character detection.
if len(value) > 0 && value[0] == 32 {
cellData.XMLSpace = xml.Attr{
@ -176,42 +194,49 @@ func (f *File) SetCellStr(sheet, axis, value string) {
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T = "str"
cellData.V = value
return err
}
// SetCellDefault provides a function to set string type value of a cell as
// default format without escaping the cell.
func (f *File) SetCellDefault(sheet, axis, value string) {
func (f *File) SetCellDefault(sheet, axis, value string) error {
xlsx := f.workSheetReader(sheet)
cellData, col, _ := f.prepareCell(xlsx, sheet, axis)
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
cellData.T = ""
cellData.V = value
return err
}
// GetCellFormula provides a function to get formula from cell by given
// worksheet name and axis in XLSX file.
func (f *File) GetCellFormula(sheet, axis string) string {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool) {
func (f *File) GetCellFormula(sheet, axis string) (string, error) {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
if c.F == nil {
return "", false
return "", false, nil
}
if c.F.T == STCellFormulaTypeShared {
return getSharedForumula(x, c.F.Si), true
return getSharedForumula(x, c.F.Si), true, nil
}
return c.F.Content, true
return c.F.Content, true, nil
})
}
// SetCellFormula provides a function to set cell formula by given string and
// worksheet name.
func (f *File) SetCellFormula(sheet, axis, formula string) {
func (f *File) SetCellFormula(sheet, axis, formula string) error {
xlsx := f.workSheetReader(sheet)
cellData, _, _ := f.prepareCell(xlsx, sheet, axis)
cellData, _, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return err
}
if formula == "" {
cellData.F = nil
f.deleteCalcChain(axis)
return
return err
}
if cellData.F != nil {
@ -219,6 +244,7 @@ func (f *File) SetCellFormula(sheet, axis, formula string) {
} else {
cellData.F = &xlsxF{Content: formula}
}
return err
}
// GetCellHyperLink provides a function to get cell hyperlink by given
@ -227,28 +253,30 @@ func (f *File) SetCellFormula(sheet, axis, formula string) {
// the value of link will be false and the value of the target will be a blank
// string. For example get hyperlink of Sheet1!H6:
//
// link, target := xlsx.GetCellHyperLink("Sheet1", "H6")
// link, target, err := xlsx.GetCellHyperLink("Sheet1", "H6")
//
func (f *File) GetCellHyperLink(sheet, axis string) (bool, string) {
func (f *File) GetCellHyperLink(sheet, axis string) (bool, string, error) {
// Check for correct cell name
if _, _, err := SplitCellName(axis); err != nil {
panic(err) // Fail fast to avoid possible future side effects
return false, "", err
}
xlsx := f.workSheetReader(sheet)
axis = f.mergeCellsParser(xlsx, axis)
axis, err := f.mergeCellsParser(xlsx, axis)
if err != nil {
return false, "", err
}
if xlsx.Hyperlinks != nil {
for _, link := range xlsx.Hyperlinks.Hyperlink {
if link.Ref == axis {
if link.RID != "" {
return true, f.getSheetRelationshipsTargetByID(sheet, link.RID)
return true, f.getSheetRelationshipsTargetByID(sheet, link.RID), err
}
return true, link.Location
return true, link.Location, err
}
}
}
return false, ""
return false, "", err
}
// SetCellHyperLink provides a function to set cell hyperlink by given
@ -256,23 +284,26 @@ func (f *File) GetCellHyperLink(sheet, axis string) (bool, string) {
// hyperlink "External" for web site or "Location" for moving to one of cell
// in this workbook. The below is example for external link.
//
// xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
// err := xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
// // Set underline and font color style for the cell.
// style, _ := xlsx.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
// xlsx.SetCellStyle("Sheet1", "A3", "A3", style)
// style, err := xlsx.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
// err = xlsx.SetCellStyle("Sheet1", "A3", "A3", style)
//
// A this is another example for "Location":
//
// xlsx.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
// err := xlsx.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
//
func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) {
func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) error {
// Check for correct cell name
if _, _, err := SplitCellName(axis); err != nil {
panic(err) // Fail fast to avoid possible future side effects
return err
}
xlsx := f.workSheetReader(sheet)
axis = f.mergeCellsParser(xlsx, axis)
axis, err := f.mergeCellsParser(xlsx, axis)
if err != nil {
return err
}
var linkData xlsxHyperlink
@ -289,35 +320,36 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) {
Location: link,
}
default:
panic(fmt.Errorf("invalid link type %q", linkType))
return fmt.Errorf("invalid link type %q", linkType)
}
if xlsx.Hyperlinks == nil {
xlsx.Hyperlinks = new(xlsxHyperlinks)
}
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, linkData)
return nil
}
// MergeCell provides a function to merge cells by given coordinate area and
// sheet name. For example create a merged cell of D3:E9 on Sheet1:
//
// xlsx.MergeCell("Sheet1", "D3", "E9")
// err := xlsx.MergeCell("Sheet1", "D3", "E9")
//
// If you create a merged cell that overlaps with another existing merged cell,
// those merged cells that already exist will be removed.
func (f *File) MergeCell(sheet, hcell, vcell string) {
func (f *File) MergeCell(sheet, hcell, vcell string) error {
hcol, hrow, err := CellNameToCoordinates(hcell)
if err != nil {
panic(err)
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
if err != nil {
panic(err)
return err
}
if hcol == vcol && hrow == vrow {
return
return err
}
if vcol < hcol {
@ -340,11 +372,13 @@ func (f *File) MergeCell(sheet, hcell, vcell string) {
for _, cellData := range xlsx.MergeCells.Cells {
cc := strings.Split(cellData.Ref, ":")
if len(cc) != 2 {
panic(fmt.Errorf("invalid area %q", cellData.Ref))
return fmt.Errorf("invalid area %q", cellData.Ref)
}
if !checkCellInArea(hcell, cellData.Ref) && !checkCellInArea(vcell, cellData.Ref) &&
!checkCellInArea(cc[0], ref) && !checkCellInArea(cc[1], ref) {
c1, _ := checkCellInArea(hcell, cellData.Ref)
c2, _ := checkCellInArea(vcell, cellData.Ref)
c3, _ := checkCellInArea(cc[0], ref)
c4, _ := checkCellInArea(cc[1], ref)
if !c1 && !c2 && !c3 && !c4 {
cells = append(cells, cellData)
}
}
@ -353,24 +387,25 @@ func (f *File) MergeCell(sheet, hcell, vcell string) {
} else {
xlsx.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: hcell + ":" + vcell}}}
}
return err
}
// SetSheetRow writes an array to row by given worksheet name, starting
// coordinate and a pointer to array type 'slice'. For example, writes an
// array to row 6 start with the cell B6 on Sheet1:
//
// xlsx.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
// err := xlsx.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
//
func (f *File) SetSheetRow(sheet, axis string, slice interface{}) {
func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {
col, row, err := CellNameToCoordinates(axis)
if err != nil {
panic(err) // Fail fast to avoid future side effects!
return err
}
// Make sure 'slice' is a Ptr to Slice
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
panic(errors.New("pointer to slice expected")) // Fail fast to avoid future side effects!
return errors.New("pointer to slice expected")
}
v = v.Elem()
@ -379,35 +414,42 @@ func (f *File) SetSheetRow(sheet, axis string, slice interface{}) {
// Error should never happens here. But keep ckecking to early detect regresions
// if it will be introduced in furure
if err != nil {
panic(err) // Fail fast to avoid future side effects!
return err
}
f.SetCellValue(sheet, cell, v.Index(i).Interface())
}
return err
}
// getCellInfo does common preparation for all SetCell* methods.
func (f *File) prepareCell(xlsx *xlsxWorksheet, sheet, cell string) (*xlsxC, int, int) {
cell = f.mergeCellsParser(xlsx, cell)
func (f *File) prepareCell(xlsx *xlsxWorksheet, sheet, cell string) (*xlsxC, int, int, error) {
var err error
cell, err = f.mergeCellsParser(xlsx, cell)
if err != nil {
return nil, 0, 0, err
}
col, row, err := CellNameToCoordinates(cell)
if err != nil {
panic(err) // Fail fast and prevent future side effects
return nil, 0, 0, err
}
prepareSheetXML(xlsx, col, row)
return &xlsx.SheetData.Row[row-1].C[col-1], col, row
return &xlsx.SheetData.Row[row-1].C[col-1], col, row, err
}
// getCellStringFunc does common value extraction workflow for all GetCell* methods.
// Passed function implements specific part of required logic.
func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool)) string {
// getCellStringFunc does common value extraction workflow for all GetCell*
// methods. Passed function implements specific part of required logic.
func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {
xlsx := f.workSheetReader(sheet)
axis = f.mergeCellsParser(xlsx, axis)
var err error
axis, err = f.mergeCellsParser(xlsx, axis)
if err != nil {
return "", err
}
_, row, err := CellNameToCoordinates(axis)
if err != nil {
panic(err) // Fail fast to avoid future side effects!
return "", err
}
lastRowNum := 0
@ -417,7 +459,7 @@ func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c
// keep in mind: row starts from 1
if row > lastRowNum {
return ""
return "", nil
}
for rowIdx := range xlsx.SheetData.Row {
@ -430,12 +472,16 @@ func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c
if axis != colData.R {
continue
}
if val, ok := fn(xlsx, colData); ok {
return val
val, ok, err := fn(xlsx, colData)
if err != nil {
return "", err
}
if ok {
return val, nil
}
}
}
return ""
return "", nil
}
// formattedValue provides a function to returns a value after formatted. If
@ -468,35 +514,39 @@ func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
// mergeCellsParser provides a function to check merged cells in worksheet by
// given axis.
func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) (string, error) {
axis = strings.ToUpper(axis)
if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
ok, err := checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref)
if err != nil {
return axis, err
}
if ok {
axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
}
}
}
return axis
return axis, nil
}
// checkCellInArea provides a function to determine if a given coordinate is
// within an area.
func checkCellInArea(cell, area string) bool {
func checkCellInArea(cell, area string) (bool, error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
panic(err)
return false, err
}
rng := strings.Split(area, ":")
if len(rng) != 2 {
return false
return false, err
}
firstCol, firtsRow, _ := CellNameToCoordinates(rng[0])
lastCol, lastRow, _ := CellNameToCoordinates(rng[1])
return col >= firstCol && col <= lastCol && row >= firtsRow && row <= lastRow
return col >= firstCol && col <= lastCol && row >= firtsRow && row <= lastRow, err
}
// getSharedForumula find a cell contains the same formula as another cell,

View File

@ -17,8 +17,9 @@ func TestCheckCellInArea(t *testing.T) {
for _, expectedTrueCellInArea := range expectedTrueCellInAreaList {
cell := expectedTrueCellInArea[0]
area := expectedTrueCellInArea[1]
assert.Truef(t, checkCellInArea(cell, area),
ok, err := checkCellInArea(cell, area)
assert.NoError(t, err)
assert.Truef(t, ok,
"Expected cell %v to be in area %v, got false\n", cell, area)
}
@ -31,14 +32,15 @@ func TestCheckCellInArea(t *testing.T) {
for _, expectedFalseCellInArea := range expectedFalseCellInAreaList {
cell := expectedFalseCellInArea[0]
area := expectedFalseCellInArea[1]
assert.Falsef(t, checkCellInArea(cell, area),
ok, err := checkCellInArea(cell, area)
assert.NoError(t, err)
assert.Falsef(t, ok,
"Expected cell %v not to be inside of area %v, but got true\n", cell, area)
}
assert.Panics(t, func() {
checkCellInArea("AA0", "Z0:AB1")
})
ok, err := checkCellInArea("AA0", "Z0:AB1")
assert.EqualError(t, err, `cannot convert cell "AA0" to coordinates: invalid cell name "AA0"`)
assert.False(t, ok)
}
func TestSetCellFloat(t *testing.T) {
@ -47,20 +49,28 @@ func TestSetCellFloat(t *testing.T) {
f := NewFile()
f.SetCellFloat(sheet, "A1", 123.0, -1, 64)
f.SetCellFloat(sheet, "A2", 123.0, 1, 64)
assert.Equal(t, "123", f.GetCellValue(sheet, "A1"), "A1 should be 123")
assert.Equal(t, "123.0", f.GetCellValue(sheet, "A2"), "A2 should be 123.0")
val, err := f.GetCellValue(sheet, "A1")
assert.NoError(t, err)
assert.Equal(t, "123", val, "A1 should be 123")
val, err = f.GetCellValue(sheet, "A2")
assert.NoError(t, err)
assert.Equal(t, "123.0", val, "A2 should be 123.0")
})
t.Run("with a decimal and precision limit", func(t *testing.T) {
f := NewFile()
f.SetCellFloat(sheet, "A1", 123.42, 1, 64)
assert.Equal(t, "123.4", f.GetCellValue(sheet, "A1"), "A1 should be 123.4")
val, err := f.GetCellValue(sheet, "A1")
assert.NoError(t, err)
assert.Equal(t, "123.4", val, "A1 should be 123.4")
})
t.Run("with a decimal and no limit", func(t *testing.T) {
f := NewFile()
f.SetCellFloat(sheet, "A1", 123.42, -1, 64)
assert.Equal(t, "123.42", f.GetCellValue(sheet, "A1"), "A1 should be 123.42")
val, err := f.GetCellValue(sheet, "A1")
assert.NoError(t, err)
assert.Equal(t, "123.42", val, "A1 should be 123.42")
})
}
@ -68,6 +78,7 @@ func ExampleFile_SetCellFloat() {
f := NewFile()
var x = 3.14159265
f.SetCellFloat("Sheet1", "A1", x, 2, 64)
fmt.Println(f.GetCellValue("Sheet1", "A1"))
val, _ := f.GetCellValue("Sheet1", "A1")
fmt.Println(val)
// Output: 3.14
}

View File

@ -456,7 +456,10 @@ func (f *File) AddChart(sheet, cell, format string) error {
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
f.addDrawingChart(sheet, drawingXML, cell, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format)
err = f.addDrawingChart(sheet, drawingXML, cell, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format)
if err != nil {
return err
}
f.addChart(formatSet)
f.addContentTypePart(chartID, "chart")
f.addContentTypePart(drawingID, "drawings")
@ -1239,8 +1242,11 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
// addDrawingChart provides a function to add chart graphic frame by given
// sheet, drawingXML, cell, width, height, relationship index and format sets.
func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) {
col, row := MustCellNameToCoordinates(cell)
func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) error {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
colIdx := col - 1
rowIdx := row - 1
@ -1290,4 +1296,5 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
}
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content
return err
}

100
col.go
View File

@ -22,33 +22,39 @@ const (
// worksheet name and column name. For example, get visible state of column D
// in Sheet1:
//
// xlsx.GetColVisible("Sheet1", "D")
// visiable, err := xlsx.GetColVisible("Sheet1", "D")
//
func (f *File) GetColVisible(sheet, col string) bool {
colNum := MustColumnNameToNumber(col)
func (f *File) GetColVisible(sheet, col string) (bool, error) {
visible := true
colNum, err := ColumnNameToNumber(col)
if err != nil {
return visible, err
}
xlsx := f.workSheetReader(sheet)
if xlsx.Cols == nil {
return true
return visible, err
}
visible := true
for c := range xlsx.Cols.Col {
colData := &xlsx.Cols.Col[c]
if colData.Min <= colNum && colNum <= colData.Max {
visible = !colData.Hidden
}
}
return visible
return visible, err
}
// SetColVisible provides a function to set visible of a single column by given
// worksheet name and column name. For example, hide column D in Sheet1:
//
// xlsx.SetColVisible("Sheet1", "D", false)
// err := xlsx.SetColVisible("Sheet1", "D", false)
//
func (f *File) SetColVisible(sheet, col string, visible bool) {
colNum := MustColumnNameToNumber(col)
func (f *File) SetColVisible(sheet, col string, visible bool) error {
colNum, err := ColumnNameToNumber(col)
if err != nil {
return err
}
colData := xlsxCol{
Min: colNum,
Max: colNum,
@ -60,7 +66,7 @@ func (f *File) SetColVisible(sheet, col string, visible bool) {
cols := xlsxCols{}
cols.Col = append(cols.Col, colData)
xlsx.Cols = &cols
return
return err
}
for v := range xlsx.Cols.Col {
if xlsx.Cols.Col[v].Min <= colNum && colNum <= xlsx.Cols.Col[v].Max {
@ -72,20 +78,24 @@ func (f *File) SetColVisible(sheet, col string, visible bool) {
colData.Hidden = !visible
colData.CustomWidth = true
xlsx.Cols.Col = append(xlsx.Cols.Col, colData)
return err
}
// GetColOutlineLevel provides a function to get outline level of a single
// column by given worksheet name and column name. For example, get outline
// level of column D in Sheet1:
//
// xlsx.GetColOutlineLevel("Sheet1", "D")
// level, err := xlsx.GetColOutlineLevel("Sheet1", "D")
//
func (f *File) GetColOutlineLevel(sheet, col string) uint8 {
colNum := MustColumnNameToNumber(col)
xlsx := f.workSheetReader(sheet)
func (f *File) GetColOutlineLevel(sheet, col string) (uint8, error) {
level := uint8(0)
colNum, err := ColumnNameToNumber(col)
if err != nil {
return level, err
}
xlsx := f.workSheetReader(sheet)
if xlsx.Cols == nil {
return level
return level, err
}
for c := range xlsx.Cols.Col {
colData := &xlsx.Cols.Col[c]
@ -93,17 +103,20 @@ func (f *File) GetColOutlineLevel(sheet, col string) uint8 {
level = colData.OutlineLevel
}
}
return level
return level, err
}
// SetColOutlineLevel provides a function to set outline level of a single
// column by given worksheet name and column name. For example, set outline
// level of column D in Sheet1 to 2:
//
// xlsx.SetColOutlineLevel("Sheet1", "D", 2)
// err := xlsx.SetColOutlineLevel("Sheet1", "D", 2)
//
func (f *File) SetColOutlineLevel(sheet, col string, level uint8) {
colNum := MustColumnNameToNumber(col)
func (f *File) SetColOutlineLevel(sheet, col string, level uint8) error {
colNum, err := ColumnNameToNumber(col)
if err != nil {
return err
}
colData := xlsxCol{
Min: colNum,
Max: colNum,
@ -115,7 +128,7 @@ func (f *File) SetColOutlineLevel(sheet, col string, level uint8) {
cols := xlsxCols{}
cols.Col = append(cols.Col, colData)
xlsx.Cols = &cols
return
return err
}
for v := range xlsx.Cols.Col {
if xlsx.Cols.Col[v].Min <= colNum && colNum <= xlsx.Cols.Col[v].Max {
@ -127,21 +140,24 @@ func (f *File) SetColOutlineLevel(sheet, col string, level uint8) {
colData.OutlineLevel = level
colData.CustomWidth = true
xlsx.Cols.Col = append(xlsx.Cols.Col, colData)
return err
}
// SetColWidth provides a function to set the width of a single column or
// multiple columns. For example:
//
// xlsx := excelize.NewFile()
// xlsx.SetColWidth("Sheet1", "A", "H", 20)
// err := xlsx.Save()
// if err != nil {
// fmt.Println(err)
// }
// err := xlsx.SetColWidth("Sheet1", "A", "H", 20)
//
func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) {
min := MustColumnNameToNumber(startcol)
max := MustColumnNameToNumber(endcol)
func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) error {
min, err := ColumnNameToNumber(startcol)
if err != nil {
return err
}
max, err := ColumnNameToNumber(endcol)
if err != nil {
return err
}
if min > max {
min, max = max, min
}
@ -160,6 +176,7 @@ func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) {
cols.Col = append(cols.Col, col)
xlsx.Cols = &cols
}
return err
}
// positionObjectPixels calculate the vertices that define the position of a
@ -289,8 +306,11 @@ func (f *File) getColWidth(sheet string, col int) int {
// GetColWidth provides a function to get column width by given worksheet name
// and column index.
func (f *File) GetColWidth(sheet, col string) float64 {
colNum := MustColumnNameToNumber(col)
func (f *File) GetColWidth(sheet, col string) (float64, error) {
colNum, err := ColumnNameToNumber(col)
if err != nil {
return defaultColWidthPixels, err
}
xlsx := f.workSheetReader(sheet)
if xlsx.Cols != nil {
var width float64
@ -300,39 +320,39 @@ func (f *File) GetColWidth(sheet, col string) float64 {
}
}
if width != 0 {
return width
return width, err
}
}
// Optimisation for when the column widths haven't changed.
return defaultColWidthPixels
return defaultColWidthPixels, err
}
// InsertCol provides a function to insert a new column before given column
// index. For example, create a new column before column C in Sheet1:
//
// xlsx.InsertCol("Sheet1", "C")
// err := xlsx.InsertCol("Sheet1", "C")
//
func (f *File) InsertCol(sheet, col string) {
func (f *File) InsertCol(sheet, col string) error {
num, err := ColumnNameToNumber(col)
if err != nil {
panic(err)
return err
}
f.adjustHelper(sheet, columns, num, 1)
return f.adjustHelper(sheet, columns, num, 1)
}
// RemoveCol provides a function to remove single column by given worksheet
// name and column index. For example, remove column C in Sheet1:
//
// xlsx.RemoveCol("Sheet1", "C")
// err := xlsx.RemoveCol("Sheet1", "C")
//
// 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
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) RemoveCol(sheet, col string) {
func (f *File) RemoveCol(sheet, col string) error {
num, err := ColumnNameToNumber(col)
if err != nil {
panic(err) // Fail fast to avoid possible future side effects!
return err
}
xlsx := f.workSheetReader(sheet)
@ -346,7 +366,7 @@ func (f *File) RemoveCol(sheet, col string) {
}
}
}
f.adjustHelper(sheet, columns, num, -1)
return f.adjustHelper(sheet, columns, num, -1)
}
// convertColWidthToPixels provieds function to convert the width of a cell

View File

@ -72,7 +72,7 @@ func (f *File) getSheetComments(sheetID int) string {
// author length is 255 and the max text length is 32512. For example, add a
// comment in Sheet1!$A$30:
//
// xlsx.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
// err := xlsx.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
//
func (f *File) AddComment(sheet, cell, format string) error {
formatSet, err := parseFormatCommentsSet(format)
@ -107,15 +107,21 @@ func (f *File) AddComment(sheet, cell, format string) error {
colCount = ll
}
}
f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
err = f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
if err != nil {
return err
}
f.addContentTypePart(commentID, "comments")
return err
}
// addDrawingVML provides a function to create comment as
// xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) {
col, row := MustCellNameToCoordinates(cell)
func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) error {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
yAxis := col - 1
xAxis := row - 1
vml := f.VMLDrawing[drawingVML]
@ -206,6 +212,7 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
}
vml.Shape = append(vml.Shape, shape)
f.VMLDrawing[drawingVML] = vml
return err
}
// addComment provides a function to create chart as xl/comments%d.xml by

View File

@ -10,6 +10,7 @@
package excelize
import (
"errors"
"math"
"time"
)
@ -25,18 +26,18 @@ var (
)
// timeToExcelTime provides a function to convert time to Excel time.
func timeToExcelTime(t time.Time) float64 {
func timeToExcelTime(t time.Time) (float64, error) {
// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
// Force user to explicit convet passed value to UTC time.
// Because for example 1900-01-01 00:00:00 +0300 MSK converts to 1900-01-01 00:00:00 +0230 LMT
// probably due to daylight saving.
if t.Location() != time.UTC {
panic("only UTC time expected")
return 0.0, errors.New("only UTC time expected")
}
if t.Before(excelMinTime1900) {
return 0.0
return 0.0, nil
}
tt := t
@ -60,7 +61,7 @@ func timeToExcelTime(t time.Time) float64 {
if t.After(excelBuggyPeriodStart) {
result += 1.0
}
return result
return result, nil
}
// shiftJulianToNoon provides a function to process julian date to noon.

View File

@ -31,7 +31,9 @@ var trueExpectedDateList = []dateTest{
func TestTimeToExcelTime(t *testing.T) {
for i, test := range trueExpectedDateList {
t.Run(fmt.Sprintf("TestData%d", i+1), func(t *testing.T) {
assert.Equalf(t, test.ExcelValue, timeToExcelTime(test.GoValue),
excelTime, err := timeToExcelTime(test.GoValue)
assert.NoError(t, err)
assert.Equalf(t, test.ExcelValue, excelTime,
"Time: %s", test.GoValue.String())
})
}
@ -44,9 +46,8 @@ func TestTimeToExcelTime_Timezone(t *testing.T) {
}
for i, test := range trueExpectedDateList {
t.Run(fmt.Sprintf("TestData%d", i+1), func(t *testing.T) {
assert.Panics(t, func() {
timeToExcelTime(test.GoValue.In(location))
}, "Time: %s", test.GoValue.String())
_, err := timeToExcelTime(test.GoValue.In(location))
assert.EqualError(t, err, "only UTC time expected")
})
}
}

View File

@ -98,11 +98,16 @@ func OpenReader(r io.Reader) (*File, error) {
// setDefaultTimeStyle provides a function to set default numbers format for
// time.Time type cell value by given worksheet name, cell coordinates and
// number format code.
func (f *File) setDefaultTimeStyle(sheet, axis string, format int) {
if f.GetCellStyle(sheet, axis) == 0 {
func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error {
s, err := f.GetCellStyle(sheet, axis)
if err != nil {
return err
}
if s == 0 {
style, _ := f.NewStyle(`{"number_format": ` + strconv.Itoa(format) + `}`)
f.SetCellStyle(sheet, axis, axis, style)
}
return err
}
// workSheetReader provides a function to get the pointer to the structure
@ -218,7 +223,8 @@ func (f *File) GetMergeCells(sheet string) []MergeCell {
for i := range xlsx.MergeCells.Cells {
ref := xlsx.MergeCells.Cells[i].Ref
axis := strings.Split(ref, ":")[0]
mergeCells = append(mergeCells, []string{ref, f.GetCellValue(sheet, axis)})
val, _ := f.GetCellValue(sheet, axis)
mergeCells = append(mergeCells, []string{ref, val})
}
}

View File

@ -27,7 +27,8 @@ func TestOpenFile(t *testing.T) {
// Test get all the rows in a not exists worksheet.
xlsx.GetRows("Sheet4")
// Test get all the rows in a worksheet.
rows := xlsx.GetRows("Sheet2")
rows, err := xlsx.GetRows("Sheet2")
assert.NoError(t, err)
for _, row := range rows {
for _, cell := range row {
t.Log(cell, "\t")
@ -40,16 +41,13 @@ func TestOpenFile(t *testing.T) {
xlsx.SetCellDefault("Sheet2", "A1", strconv.FormatFloat(float64(-100.1588), 'f', -1, 64))
// Test set cell value with illegal row number.
assert.Panics(t, func() {
xlsx.SetCellDefault("Sheet2", "A", strconv.FormatFloat(float64(-100.1588), 'f', -1, 64))
})
assert.EqualError(t, xlsx.SetCellDefault("Sheet2", "A", strconv.FormatFloat(float64(-100.1588), 'f', -1, 64)),
`cannot convert cell "A" to coordinates: invalid cell name "A"`)
xlsx.SetCellInt("Sheet2", "A1", 100)
// Test set cell integer value with illegal row number.
assert.Panics(t, func() {
xlsx.SetCellInt("Sheet2", "A", 100)
})
assert.EqualError(t, xlsx.SetCellInt("Sheet2", "A", 100), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
xlsx.SetCellStr("Sheet2", "C11", "Knowns")
// Test max characters in a cell.
@ -62,35 +60,31 @@ func TestOpenFile(t *testing.T) {
xlsx.SetCellStr("Sheet10", "b230", "10")
// Test set cell string value with illegal row number.
assert.Panics(t, func() {
xlsx.SetCellStr("Sheet10", "A", "10")
})
assert.EqualError(t, xlsx.SetCellStr("Sheet10", "A", "10"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
xlsx.SetActiveSheet(2)
// Test get cell formula with given rows number.
xlsx.GetCellFormula("Sheet1", "B19")
_, err = xlsx.GetCellFormula("Sheet1", "B19")
assert.NoError(t, err)
// Test get cell formula with illegal worksheet name.
xlsx.GetCellFormula("Sheet2", "B20")
xlsx.GetCellFormula("Sheet1", "B20")
_, err = xlsx.GetCellFormula("Sheet2", "B20")
assert.NoError(t, err)
_, err = xlsx.GetCellFormula("Sheet1", "B20")
assert.NoError(t, err)
// Test get cell formula with illegal rows number.
assert.Panics(t, func() {
xlsx.GetCellFormula("Sheet1", "B")
})
_, err = xlsx.GetCellFormula("Sheet1", "B")
assert.EqualError(t, err, `cannot convert cell "B" to coordinates: invalid cell name "B"`)
// Test get shared cell formula
xlsx.GetCellFormula("Sheet2", "H11")
xlsx.GetCellFormula("Sheet2", "I11")
getSharedForumula(&xlsxWorksheet{}, "")
// Test read cell value with given illegal rows number.
assert.Panics(t, func() {
xlsx.GetCellValue("Sheet2", "a-1")
})
assert.Panics(t, func() {
xlsx.GetCellValue("Sheet2", "A")
})
_, err = xlsx.GetCellValue("Sheet2", "a-1")
assert.EqualError(t, err, `cannot convert cell "A-1" to coordinates: invalid cell name "A-1"`)
_, err = xlsx.GetCellValue("Sheet2", "A")
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
// Test read cell value with given lowercase column number.
xlsx.GetCellValue("Sheet2", "a5")
@ -127,14 +121,14 @@ func TestOpenFile(t *testing.T) {
}
for _, test := range booltest {
xlsx.SetCellValue("Sheet2", "F16", test.value)
assert.Equal(t, test.expected, xlsx.GetCellValue("Sheet2", "F16"))
val, err := xlsx.GetCellValue("Sheet2", "F16")
assert.NoError(t, err)
assert.Equal(t, test.expected, val)
}
xlsx.SetCellValue("Sheet2", "G2", nil)
assert.Panics(t, func() {
xlsx.SetCellValue("Sheet2", "G4", time.Now())
})
assert.EqualError(t, xlsx.SetCellValue("Sheet2", "G4", time.Now()), "only UTC time expected")
xlsx.SetCellValue("Sheet2", "G4", time.Now().UTC())
// 02:46:40
@ -320,19 +314,15 @@ func TestSetCellHyperLink(t *testing.T) {
t.Log(err)
}
// Test set cell hyperlink in a work sheet already have hyperlinks.
xlsx.SetCellHyperLink("Sheet1", "B19", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
assert.NoError(t, xlsx.SetCellHyperLink("Sheet1", "B19", "https://github.com/360EntSecGroup-Skylar/excelize", "External"))
// Test add first hyperlink in a work sheet.
xlsx.SetCellHyperLink("Sheet2", "C1", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
assert.NoError(t, xlsx.SetCellHyperLink("Sheet2", "C1", "https://github.com/360EntSecGroup-Skylar/excelize", "External"))
// Test add Location hyperlink in a work sheet.
xlsx.SetCellHyperLink("Sheet2", "D6", "Sheet1!D8", "Location")
assert.NoError(t, xlsx.SetCellHyperLink("Sheet2", "D6", "Sheet1!D8", "Location"))
assert.Panics(t, func() {
xlsx.SetCellHyperLink("Sheet2", "C3", "Sheet1!D8", "")
})
assert.EqualError(t, xlsx.SetCellHyperLink("Sheet2", "C3", "Sheet1!D8", ""), `invalid link type ""`)
assert.Panics(t, func() {
xlsx.SetCellHyperLink("Sheet2", "", "Sheet1!D60", "Location")
})
assert.EqualError(t, xlsx.SetCellHyperLink("Sheet2", "", "Sheet1!D60", "Location"), `invalid cell name ""`)
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellHyperLink.xlsx")))
}
@ -343,15 +333,17 @@ func TestGetCellHyperLink(t *testing.T) {
t.FailNow()
}
assert.Panics(t, func() {
xlsx.GetCellHyperLink("Sheet1", "")
})
link, target, err := xlsx.GetCellHyperLink("Sheet1", "")
assert.EqualError(t, err, `invalid cell name ""`)
link, target := xlsx.GetCellHyperLink("Sheet1", "A22")
link, target, err = xlsx.GetCellHyperLink("Sheet1", "A22")
assert.NoError(t, err)
t.Log(link, target)
link, target = xlsx.GetCellHyperLink("Sheet2", "D6")
link, target, err = xlsx.GetCellHyperLink("Sheet2", "D6")
assert.NoError(t, err)
t.Log(link, target)
link, target = xlsx.GetCellHyperLink("Sheet3", "H3")
link, target, err = xlsx.GetCellHyperLink("Sheet3", "H3")
assert.NoError(t, err)
t.Log(link, target)
}
@ -365,9 +357,7 @@ func TestSetCellFormula(t *testing.T) {
xlsx.SetCellFormula("Sheet1", "C19", "SUM(Sheet2!D2,Sheet2!D9)")
// Test set cell formula with illegal rows number.
assert.Panics(t, func() {
xlsx.SetCellFormula("Sheet1", "C", "SUM(Sheet2!D2,Sheet2!D9)")
})
assert.EqualError(t, xlsx.SetCellFormula("Sheet1", "C", "SUM(Sheet2!D2,Sheet2!D9)"), `cannot convert cell "C" to coordinates: invalid cell name "C"`)
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellFormula1.xlsx")))
@ -496,21 +486,16 @@ func TestSetCellStyleAlignment(t *testing.T) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "A22", "A22", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A22", "A22", style))
// Test set cell style with given illegal rows number.
assert.Panics(t, func() {
xlsx.SetCellStyle("Sheet1", "A", "A22", style)
})
assert.Panics(t, func() {
xlsx.SetCellStyle("Sheet1", "A22", "A", style)
})
assert.EqualError(t, xlsx.SetCellStyle("Sheet1", "A", "A22", style), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
assert.EqualError(t, xlsx.SetCellStyle("Sheet1", "A22", "A", style), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
// Test get cell style with given illegal rows number.
assert.Panics(t, func() {
xlsx.GetCellStyle("Sheet1", "A")
})
index, err := xlsx.GetCellStyle("Sheet1", "A")
assert.Equal(t, 0, index)
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleAlignment.xlsx")))
}
@ -528,19 +513,19 @@ func TestSetCellStyleBorder(t *testing.T) {
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "J21", "L25", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "J21", "L25", style))
style, err = xlsx.NewStyle(`{"border":[{"type":"left","color":"0000FF","style":2},{"type":"top","color":"00FF00","style":3},{"type":"bottom","color":"FFFF00","style":4},{"type":"right","color":"FF0000","style":5},{"type":"diagonalDown","color":"A020F0","style":6},{"type":"diagonalUp","color":"A020F0","style":7}],"fill":{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":1}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "M28", "K24", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "M28", "K24", style))
style, err = xlsx.NewStyle(`{"border":[{"type":"left","color":"0000FF","style":2},{"type":"top","color":"00FF00","style":3},{"type":"bottom","color":"FFFF00","style":4},{"type":"right","color":"FF0000","style":5},{"type":"diagonalDown","color":"A020F0","style":6},{"type":"diagonalUp","color":"A020F0","style":7}],"fill":{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":4}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "M28", "K24", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "M28", "K24", style))
// Test set border and solid style pattern fill for a single cell.
style, err = xlsx.NewStyle(`{"border":[{"type":"left","color":"0000FF","style":8},{"type":"top","color":"00FF00","style":9},{"type":"bottom","color":"FFFF00","style":10},{"type":"right","color":"FF0000","style":11},{"type":"diagonalDown","color":"A020F0","style":12},{"type":"diagonalUp","color":"A020F0","style":13}],"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`)
@ -548,7 +533,7 @@ func TestSetCellStyleBorder(t *testing.T) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "O22", "O22", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "O22", "O22", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleBorder.xlsx")))
}
@ -596,7 +581,7 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", c, c, style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", c, c, style))
t.Log(xlsx.GetCellValue("Sheet2", c))
}
}
@ -605,7 +590,7 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "L33", "L33", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "L33", "L33", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleNumberFormat.xlsx")))
}
@ -625,13 +610,13 @@ func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "A1", "A1", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A1", "A1", style))
style, err = xlsx.NewStyle(`{"number_format": 188, "decimal_places": 31, "negred": true}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "A2", "A2", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A2", "A2", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleCurrencyNumberFormat.TestBook3.xlsx")))
})
@ -654,19 +639,19 @@ func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "A1", "A1", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A1", "A1", style))
style, err = xlsx.NewStyle(`{"number_format": 31, "lang": "ko-kr"}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "A2", "A2", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A2", "A2", style))
style, err = xlsx.NewStyle(`{"number_format": 71, "lang": "th-th"}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "A2", "A2", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A2", "A2", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleCurrencyNumberFormat.TestBook4.xlsx")))
})
@ -680,12 +665,12 @@ func TestSetCellStyleCustomNumberFormat(t *testing.T) {
if err != nil {
t.Log(err)
}
xlsx.SetCellStyle("Sheet1", "A1", "A1", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A1", "A1", style))
style, err = xlsx.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
if err != nil {
t.Log(err)
}
xlsx.SetCellStyle("Sheet1", "A2", "A2", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "A2", "A2", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleCustomNumberFormat.xlsx")))
}
@ -702,25 +687,25 @@ func TestSetCellStyleFill(t *testing.T) {
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "O23", "O23", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "O23", "O23", style))
style, err = xlsx.NewStyle(`{"fill":{"type":"gradient","color":["#FFFFFF"],"shading":1}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "O23", "O23", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "O23", "O23", style))
style, err = xlsx.NewStyle(`{"fill":{"type":"pattern","color":[],"pattern":1}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "O23", "O23", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "O23", "O23", style))
style, err = xlsx.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":19}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet1", "O23", "O23", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet1", "O23", "O23", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleFill.xlsx")))
}
@ -737,35 +722,35 @@ func TestSetCellStyleFont(t *testing.T) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "A1", "A1", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "A1", "A1", style))
style, err = xlsx.NewStyle(`{"font":{"italic":true,"underline":"double"}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "A2", "A2", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "A2", "A2", style))
style, err = xlsx.NewStyle(`{"font":{"bold":true}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "A3", "A3", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "A3", "A3", style))
style, err = xlsx.NewStyle(`{"font":{"bold":true,"family":"","size":0,"color":"","underline":""}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "A4", "A4", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "A4", "A4", style))
style, err = xlsx.NewStyle(`{"font":{"color":"#777777"}}`)
if !assert.NoError(t, err) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "A5", "A5", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "A5", "A5", style))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleFont.xlsx")))
}
@ -782,7 +767,7 @@ func TestSetCellStyleProtection(t *testing.T) {
t.FailNow()
}
xlsx.SetCellStyle("Sheet2", "A6", "A6", style)
assert.NoError(t, xlsx.SetCellStyle("Sheet2", "A6", "A6", style))
err = xlsx.SaveAs(filepath.Join("test", "TestSetCellStyleProtection.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
@ -818,7 +803,8 @@ func TestGetPicture(t *testing.T) {
t.FailNow()
}
file, raw := xlsx.GetPicture("Sheet1", "F21")
file, raw, err := xlsx.GetPicture("Sheet1", "F21")
assert.NoError(t, err)
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) ||
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0644)) {
@ -826,12 +812,14 @@ func TestGetPicture(t *testing.T) {
}
// Try to get picture from a worksheet that doesn't contain any images.
file, raw = xlsx.GetPicture("Sheet3", "I9")
file, raw, err = xlsx.GetPicture("Sheet3", "I9")
assert.NoError(t, err)
assert.Empty(t, file)
assert.Empty(t, raw)
// Try to get picture from a cell that doesn't contain an image.
file, raw = xlsx.GetPicture("Sheet2", "A2")
file, raw, err = xlsx.GetPicture("Sheet2", "A2")
assert.NoError(t, err)
assert.Empty(t, file)
assert.Empty(t, raw)
@ -850,7 +838,8 @@ func TestGetPicture(t *testing.T) {
t.FailNow()
}
file, raw = xlsx.GetPicture("Sheet1", "F21")
file, raw, err = xlsx.GetPicture("Sheet1", "F21")
assert.NoError(t, err)
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) ||
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0644)) {
@ -858,7 +847,8 @@ func TestGetPicture(t *testing.T) {
}
// Try to get picture from a local storage file that doesn't contain an image.
file, raw = xlsx.GetPicture("Sheet1", "F22")
file, raw, err = xlsx.GetPicture("Sheet1", "F22")
assert.NoError(t, err)
assert.Empty(t, file)
assert.Empty(t, raw)
}
@ -913,7 +903,9 @@ func TestCopySheet(t *testing.T) {
}
xlsx.SetCellValue("Sheet4", "F1", "Hello")
assert.NotEqual(t, "Hello", xlsx.GetCellValue("Sheet1", "F1"))
val, err := xlsx.GetCellValue("Sheet1", "F1")
assert.NoError(t, err)
assert.NotEqual(t, "Hello", val)
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestCopySheet.xlsx")))
}
@ -1101,7 +1093,7 @@ func TestInsertCol(t *testing.T) {
t.FailNow()
}
xlsx.InsertCol(sheet1, "A")
assert.NoError(t, xlsx.InsertCol(sheet1, "A"))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestInsertCol.xlsx")))
}
@ -1118,8 +1110,8 @@ func TestRemoveCol(t *testing.T) {
xlsx.MergeCell(sheet1, "A1", "B1")
xlsx.MergeCell(sheet1, "A2", "B2")
xlsx.RemoveCol(sheet1, "A")
xlsx.RemoveCol(sheet1, "A")
assert.NoError(t, xlsx.RemoveCol(sheet1, "A"))
assert.NoError(t, xlsx.RemoveCol(sheet1, "A"))
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestRemoveCol.xlsx")))
}
@ -1242,18 +1234,11 @@ func TestSetSheetRow(t *testing.T) {
xlsx.SetSheetRow("Sheet1", "B27", &[]interface{}{"cell", nil, int32(42), float64(42), time.Now().UTC()})
assert.Panics(t, func() {
xlsx.SetSheetRow("Sheet1", "", &[]interface{}{"cell", nil, 2})
})
assert.Panics(t, func() {
xlsx.SetSheetRow("Sheet1", "B27", []interface{}{})
})
assert.Panics(t, func() {
xlsx.SetSheetRow("Sheet1", "B27", &xlsx)
})
assert.EqualError(t, xlsx.SetSheetRow("Sheet1", "", &[]interface{}{"cell", nil, 2}),
`cannot convert cell "" to coordinates: invalid cell name ""`)
assert.EqualError(t, xlsx.SetSheetRow("Sheet1", "B27", []interface{}{}), `pointer to slice expected`)
assert.EqualError(t, xlsx.SetSheetRow("Sheet1", "B27", &xlsx), `pointer to slice expected`)
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetSheetRow.xlsx")))
}
@ -1267,16 +1252,15 @@ func TestOutlineLevel(t *testing.T) {
xlsx.SetColOutlineLevel("Sheet2", "B", 2)
xlsx.SetRowOutlineLevel("Sheet1", 2, 250)
assert.Panics(t, func() {
xlsx.SetRowOutlineLevel("Sheet1", 0, 1)
})
assert.EqualError(t, xlsx.SetRowOutlineLevel("Sheet1", 0, 1), "invalid row number 0")
level, err := xlsx.GetRowOutlineLevel("Sheet1", 2)
assert.NoError(t, err)
assert.Equal(t, uint8(250), level)
assert.Equal(t, uint8(250), xlsx.GetRowOutlineLevel("Sheet1", 2))
_, err = xlsx.GetRowOutlineLevel("Sheet1", 0)
assert.EqualError(t, err, `invalid row number 0`)
assert.Panics(t, func() {
xlsx.GetRowOutlineLevel("Sheet1", 0)
})
err := xlsx.SaveAs(filepath.Join("test", "TestOutlineLevel.xlsx"))
err = xlsx.SaveAs(filepath.Join("test", "TestOutlineLevel.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
}
@ -1418,7 +1402,7 @@ func prepareTestBook4() (*File, error) {
func fillCells(xlsx *File, sheet string, colCount, rowCount int) {
for col := 1; col <= colCount; col++ {
for row := 1; row <= rowCount; row++ {
cell := MustCoordinatesToCellName(col, row)
cell, _ := CoordinatesToCellName(col, row)
xlsx.SetCellStr(sheet, cell, cell)
}
}

53
lib.go
View File

@ -135,22 +135,6 @@ func ColumnNameToNumber(name string) (int, error) {
return col, nil
}
// MustColumnNameToNumber provides a function to convert Excel sheet column
// name to int. Column name case insencitive.
// Function returns error if column name incorrect.
//
// Example:
//
// excelize.MustColumnNameToNumber("AK") // returns 37
//
func MustColumnNameToNumber(name string) int {
n, err := ColumnNameToNumber(name)
if err != nil {
panic(err)
}
return n
}
// ColumnNumberToName provides a function to convert integer
// to Excel sheet column title.
//
@ -174,8 +158,9 @@ func ColumnNumberToName(num int) (string, error) {
// to [X, Y] coordinates or retrusn an error.
//
// Example:
// CellCoordinates("A1") // returns 1, 1, nil
// CellCoordinates("Z3") // returns 26, 3, nil
//
// CellCoordinates("A1") // returns 1, 1, nil
// CellCoordinates("Z3") // returns 26, 3, nil
//
func CellNameToCoordinates(cell string) (int, int, error) {
const msg = "cannot convert cell %q to coordinates: %v"
@ -193,25 +178,12 @@ func CellNameToCoordinates(cell string) (int, int, error) {
return col, row, nil
}
// MustCellNameToCoordinates converts alpha-numeric cell name
// to [X, Y] coordinates or panics.
// CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell
// name or returns an error.
//
// Example:
// MustCellNameToCoordinates("A1") // returns 1, 1
// MustCellNameToCoordinates("Z3") // returns 26, 3
//
func MustCellNameToCoordinates(cell string) (int, int) {
c, r, err := CellNameToCoordinates(cell)
if err != nil {
panic(err)
}
return c, r
}
// CoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell name or returns an error.
//
// Example:
// CoordinatesToCellName(1, 1) // returns "A1", nil
// CoordinatesToCellName(1, 1) // returns "A1", nil
//
func CoordinatesToCellName(col, row int) (string, error) {
if col < 1 || row < 1 {
@ -224,19 +196,6 @@ func CoordinatesToCellName(col, row int) (string, error) {
return fmt.Sprintf("%s%d", colname, row), nil
}
// MustCoordinatesToCellName converts [X, Y] coordinates to alpha-numeric cell name or panics.
//
// Example:
// MustCoordinatesToCellName(1, 1) // returns "A1"
//
func MustCoordinatesToCellName(col, row int) string {
n, err := CoordinatesToCellName(col, row)
if err != nil {
panic(err)
}
return n
}
// boolPtr returns a pointer to a bool with the given value.
func boolPtr(b bool) *bool { return &b }

View File

@ -71,9 +71,6 @@ func TestColumnNameToNumber_Error(t *testing.T) {
if assert.Errorf(t, err, msg, col.Name) {
assert.Equalf(t, col.Num, out, msg, col.Name)
}
assert.Panicsf(t, func() {
MustColumnNameToNumber(col.Name)
}, msg, col.Name)
}
}
@ -174,9 +171,6 @@ func TestCellNameToCoordinates_Error(t *testing.T) {
assert.Equalf(t, -1, c, msg, cell)
assert.Equalf(t, -1, r, msg, cell)
}
assert.Panicsf(t, func() {
MustCellNameToCoordinates(cell)
}, msg, cell)
}
}
@ -199,9 +193,6 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
if assert.Errorf(t, err, msg, col, row) {
assert.Equalf(t, "", cell, msg, col, row)
}
assert.Panicsf(t, func() {
MustCoordinatesToCellName(col, row)
}, msg, col, row)
}
for _, col := range invalidIndexes {

View File

@ -162,7 +162,10 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
}
drawingHyperlinkRID = f.addDrawingRelationships(drawingID, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
}
f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet)
err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet)
if err != nil {
return err
}
f.addMedia(file, ext)
f.addContentTypePart(drawingID, "drawings")
return err
@ -270,8 +273,11 @@ func (f *File) countDrawings() int {
// addDrawingPicture provides a function to add picture by given sheet,
// drawingXML, cell, file name, width, height relationship index and format
// sets.
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) {
col, row := MustCellNameToCoordinates(cell)
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) error {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
width = int(float64(width) * formatSet.XScale)
height = int(float64(height) * formatSet.YScale)
col--
@ -315,6 +321,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
}
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content
return err
}
// addDrawingRelationships provides a function to add image part relationships
@ -468,7 +475,7 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
// fmt.Println(err)
// return
// }
// file, raw := xlsx.GetPicture("Sheet1", "A2")
// file, raw, err := xlsx.GetPicture("Sheet1", "A2")
// if file == "" {
// return
// }
@ -477,13 +484,16 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
// fmt.Println(err)
// }
//
func (f *File) GetPicture(sheet, cell string) (string, []byte) {
col, row := MustCellNameToCoordinates(cell)
func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
col, row, err := CellNameToCoordinates(cell)
if err != nil {
return "", []byte{}, err
}
col--
row--
xlsx := f.workSheetReader(sheet)
if xlsx.Drawing == nil {
return "", []byte{}
return "", []byte{}, err
}
target := f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
@ -503,7 +513,7 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte) {
if ok {
return filepath.Base(xlsxWorkbookRelation.Target),
[]byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target,
"..", "xl", -1)])
"..", "xl", -1)]), err
}
}
}
@ -511,7 +521,7 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte) {
_, ok := f.XLSX[drawingXML]
if !ok {
return "", nil
return "", nil, err
}
decodeWsDr := decodeWsDr{}
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(drawingXML)), &decodeWsDr)
@ -523,12 +533,12 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte) {
xlsxWorkbookRelation := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed)
_, ok := supportImageTypes[filepath.Ext(xlsxWorkbookRelation.Target)]
if ok {
return filepath.Base(xlsxWorkbookRelation.Target), []byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target, "..", "xl", -1)])
return filepath.Base(xlsxWorkbookRelation.Target), []byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target, "..", "xl", -1)]), err
}
}
}
}
return "", []byte{}
return "", []byte{}, err
}
// getDrawingRelationships provides a function to get drawing relationships

View File

@ -4,17 +4,18 @@ import (
"fmt"
_ "image/png"
"io/ioutil"
"path/filepath"
"testing"
)
func BenchmarkAddPictureFromBytes(b *testing.B) {
f := NewFile()
imgFile, err := ioutil.ReadFile("logo.png")
imgFile, err := ioutil.ReadFile(filepath.Join("test", "images", "excel.png"))
if err != nil {
panic("unable to load image for benchmark")
b.Error("unable to load image for benchmark")
}
b.ResetTimer()
for i := 1; i <= b.N; i++ {
f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", i), "", "logo", ".png", imgFile)
f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", i), "", "excel", ".png", imgFile)
}
}

157
rows.go
View File

@ -21,17 +21,18 @@ import (
// GetRows return all the rows in a sheet by given worksheet name (case
// sensitive). For example:
//
// for _, row := range xlsx.GetRows("Sheet1") {
// rows, err := xlsx.GetRows("Sheet1")
// for _, row := range rows {
// for _, colCell := range row {
// fmt.Print(colCell, "\t")
// }
// fmt.Println()
// }
//
func (f *File) GetRows(sheet string) [][]string {
func (f *File) GetRows(sheet string) ([][]string, error) {
name, ok := f.sheetMap[trimSheetName(sheet)]
if !ok {
return nil
return nil, nil
}
xlsx := f.workSheetReader(sheet)
@ -47,7 +48,10 @@ func (f *File) GetRows(sheet string) [][]string {
rowData xlsxRow
)
rowCount, colCount := f.getTotalRowsCols(name)
rowCount, colCount, err := f.getTotalRowsCols(name)
if err != nil {
return nil, nil
}
rows := make([][]string, rowCount)
for i := range rows {
rows[i] = make([]string, colCount+1)
@ -68,7 +72,10 @@ func (f *File) GetRows(sheet string) [][]string {
_ = decoder.DecodeElement(&rowData, &startElement)
cr := rowData.R - 1
for _, colCell := range rowData.C {
col, _ := MustCellNameToCoordinates(colCell.R)
col, _, err := CellNameToCoordinates(colCell.R)
if err != nil {
return nil, err
}
val, _ := colCell.getValueFrom(f, d)
rows[cr][col-1] = val
if val != "" {
@ -79,7 +86,7 @@ func (f *File) GetRows(sheet string) [][]string {
default:
}
}
return rows[:row]
return rows[:row], nil
}
// Rows defines an iterator to a sheet
@ -117,9 +124,9 @@ func (rows *Rows) Error() error {
}
// Columns return the current row's column values
func (rows *Rows) Columns() []string {
func (rows *Rows) Columns() ([]string, error) {
if rows.token == nil {
return []string{}
return []string{}, nil
}
startElement := rows.token.(xml.StartElement)
r := xlsxRow{}
@ -127,11 +134,14 @@ func (rows *Rows) Columns() []string {
d := rows.f.sharedStringsReader()
columns := make([]string, len(r.C))
for _, colCell := range r.C {
col, _ := MustCellNameToCoordinates(colCell.R)
col, _, err := CellNameToCoordinates(colCell.R)
if err != nil {
return columns, err
}
val, _ := colCell.getValueFrom(rows.f, d)
columns[col-1] = val
}
return columns
return columns, nil
}
// ErrSheetNotExist defines an error of sheet is not exist
@ -147,7 +157,8 @@ func (err ErrSheetNotExist) Error() string {
//
// rows, err := xlsx.Rows("Sheet1")
// for rows.Next() {
// for _, colCell := range rows.Columns() {
// row, err := rows.Columns()
// for _, colCell := range row {
// fmt.Print(colCell, "\t")
// }
// fmt.Println()
@ -171,7 +182,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
// getTotalRowsCols provides a function to get total columns and rows in a
// worksheet.
func (f *File) getTotalRowsCols(name string) (int, int) {
func (f *File) getTotalRowsCols(name string) (int, int, error) {
decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name)))
var inElement string
var r xlsxRow
@ -189,7 +200,10 @@ func (f *File) getTotalRowsCols(name string) (int, int) {
_ = decoder.DecodeElement(&r, &startElement)
tr = r.R
for _, colCell := range r.C {
col, _ := MustCellNameToCoordinates(colCell.R)
col, _, err := CellNameToCoordinates(colCell.R)
if err != nil {
return tr, tc, err
}
if col > tc {
tc = col
}
@ -198,17 +212,17 @@ func (f *File) getTotalRowsCols(name string) (int, int) {
default:
}
}
return tr, tc
return tr, tc, nil
}
// SetRowHeight provides a function to set the height of a single row. For
// example, set the height of the first row in Sheet1:
//
// xlsx.SetRowHeight("Sheet1", 1, 50)
// err := xlsx.SetRowHeight("Sheet1", 1, 50)
//
func (f *File) SetRowHeight(sheet string, row int, height float64) {
func (f *File) SetRowHeight(sheet string, row int, height float64) error {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
@ -218,6 +232,7 @@ func (f *File) SetRowHeight(sheet string, row int, height float64) {
rowIdx := row - 1
xlsx.SheetData.Row[rowIdx].Ht = height
xlsx.SheetData.Row[rowIdx].CustomHeight = true
return nil
}
// getRowHeight provides a function to get row height in pixels by given sheet
@ -236,24 +251,24 @@ func (f *File) getRowHeight(sheet string, row int) int {
// GetRowHeight provides a function to get row height by given worksheet name
// and row index. For example, get the height of the first row in Sheet1:
//
// xlsx.GetRowHeight("Sheet1", 1)
// height, err := xlsx.GetRowHeight("Sheet1", 1)
//
func (f *File) GetRowHeight(sheet string, row int) float64 {
func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return defaultRowHeightPixels, newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
if row > len(xlsx.SheetData.Row) {
return defaultRowHeightPixels // it will be better to use 0, but we take care with BC
return defaultRowHeightPixels, nil // it will be better to use 0, but we take care with BC
}
for _, v := range xlsx.SheetData.Row {
if v.R == row && v.Ht != 0 {
return v.Ht
return v.Ht, nil
}
}
// Optimisation for when the row heights haven't changed.
return defaultRowHeightPixels
return defaultRowHeightPixels, nil
}
// sharedStringsReader provides a function to get the pointer to the structure
@ -299,138 +314,140 @@ func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
// SetRowVisible provides a function to set visible of a single row by given
// worksheet name and Excel row number. For example, hide row 2 in Sheet1:
//
// xlsx.SetRowVisible("Sheet1", 2, false)
// err := xlsx.SetRowVisible("Sheet1", 2, false)
//
func (f *File) SetRowVisible(sheet string, row int, visible bool) {
func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
prepareSheetXML(xlsx, 0, row)
xlsx.SheetData.Row[row-1].Hidden = !visible
return nil
}
// GetRowVisible provides a function to get visible of a single row by given
// worksheet name and Excel row number. For example, get visible state of row
// 2 in Sheet1:
//
// xlsx.GetRowVisible("Sheet1", 2)
// visible, err := xlsx.GetRowVisible("Sheet1", 2)
//
func (f *File) GetRowVisible(sheet string, row int) bool {
func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return false, newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
if row > len(xlsx.SheetData.Row) {
return false
return false, nil
}
return !xlsx.SheetData.Row[row-1].Hidden
return !xlsx.SheetData.Row[row-1].Hidden, nil
}
// SetRowOutlineLevel provides a function to set outline level number of a
// single row by given worksheet name and Excel row number. For example,
// outline row 2 in Sheet1 to level 1:
//
// xlsx.SetRowOutlineLevel("Sheet1", 2, 1)
// err := xlsx.SetRowOutlineLevel("Sheet1", 2, 1)
//
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) {
func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
prepareSheetXML(xlsx, 0, row)
xlsx.SheetData.Row[row-1].OutlineLevel = level
return nil
}
// GetRowOutlineLevel provides a function to get outline level number of a
// single row by given worksheet name and Excel row number. For example, get
// outline number of row 2 in Sheet1:
//
// xlsx.GetRowOutlineLevel("Sheet1", 2)
// level, err := xlsx.GetRowOutlineLevel("Sheet1", 2)
//
func (f *File) GetRowOutlineLevel(sheet string, row int) uint8 {
func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return 0, newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
if row > len(xlsx.SheetData.Row) {
return 0
return 0, nil
}
return xlsx.SheetData.Row[row-1].OutlineLevel
return xlsx.SheetData.Row[row-1].OutlineLevel, nil
}
// RemoveRow provides a function to remove single row by given worksheet name
// and Excel row number. For example, remove row 3 in Sheet1:
//
// xlsx.RemoveRow("Sheet1", 3)
// err := xlsx.RemoveRow("Sheet1", 3)
//
// 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
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) RemoveRow(sheet string, row int) {
func (f *File) RemoveRow(sheet string, row int) error {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
if row > len(xlsx.SheetData.Row) {
return
return nil
}
for rowIdx := range xlsx.SheetData.Row {
if xlsx.SheetData.Row[rowIdx].R == row {
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:rowIdx],
xlsx.SheetData.Row[rowIdx+1:]...)[:len(xlsx.SheetData.Row)-1]
f.adjustHelper(sheet, rows, row, -1)
return
return f.adjustHelper(sheet, rows, row, -1)
}
}
return nil
}
// InsertRow provides a function to insert a new row after given Excel row
// number starting from 1. For example, create a new row before row 3 in
// Sheet1:
//
// xlsx.InsertRow("Sheet1", 3)
// err := elsx.InsertRow("Sheet1", 3)
//
func (f *File) InsertRow(sheet string, row int) {
func (f *File) InsertRow(sheet string, row int) error {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return newInvalidRowNumberError(row)
}
f.adjustHelper(sheet, rows, row, 1)
return f.adjustHelper(sheet, rows, row, 1)
}
// DuplicateRow inserts a copy of specified row (by it Excel row number) below
//
// xlsx.DuplicateRow("Sheet1", 2)
// err := xlsx.DuplicateRow("Sheet1", 2)
//
// 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
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) DuplicateRow(sheet string, row int) {
f.DuplicateRowTo(sheet, row, row+1)
func (f *File) DuplicateRow(sheet string, row int) error {
return f.DuplicateRowTo(sheet, row, row+1)
}
// DuplicateRowTo inserts a copy of specified row by it Excel number
// to specified row position moving down exists rows after target position
//
// xlsx.DuplicateRowTo("Sheet1", 2, 7)
// err := xlsx.DuplicateRowTo("Sheet1", 2, 7)
//
// 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
// worksheet, it will cause a file error when you open it. The excelize only
// partially updates these references currently.
func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
if row < 1 {
panic(newInvalidRowNumberError(row)) // Fail fast to avoid possible future side effects!
return newInvalidRowNumberError(row)
}
xlsx := f.workSheetReader(sheet)
if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 {
return
return nil
}
var ok bool
@ -444,10 +461,12 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
}
}
if !ok {
return
return nil
}
f.adjustHelper(sheet, rows, row2, 1)
if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
return err
}
idx2 := -1
for i, r := range xlsx.SheetData.Row {
@ -457,7 +476,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
}
}
if idx2 == -1 && len(xlsx.SheetData.Row) >= row2 {
return
return nil
}
rowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)
@ -468,6 +487,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
} else {
xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowCopy)
}
return nil
}
// checkRow provides a function to check and fill each column element for all
@ -494,7 +514,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
//
// Noteice: this method could be very slow for large spreadsheets (more than
// 3000 rows one sheet).
func checkRow(xlsx *xlsxWorksheet) {
func checkRow(xlsx *xlsxWorksheet) error {
for rowIdx := range xlsx.SheetData.Row {
rowData := &xlsx.SheetData.Row[rowIdx]
@ -502,7 +522,10 @@ func checkRow(xlsx *xlsxWorksheet) {
if colCount == 0 {
continue
}
lastCol, _ := MustCellNameToCoordinates(rowData.C[colCount-1].R)
lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
if err != nil {
return err
}
if colCount < lastCol {
oldList := rowData.C
@ -511,18 +534,26 @@ func checkRow(xlsx *xlsxWorksheet) {
rowData.C = xlsx.SheetData.Row[rowIdx].C[:0]
for colIdx := 0; colIdx < lastCol; colIdx++ {
newlist = append(newlist, xlsxC{R: MustCoordinatesToCellName(colIdx+1, rowIdx+1)})
cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
if err != nil {
return err
}
newlist = append(newlist, xlsxC{R: cellName})
}
rowData.C = newlist
for colIdx := range oldList {
colData := &oldList[colIdx]
colNum, _ := MustCellNameToCoordinates(colData.R)
colNum, _, err := CellNameToCoordinates(colData.R)
if err != nil {
return err
}
xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData
}
}
}
return nil
}
// convertRowHeightToPixels provides a function to convert the height of a

View File

@ -23,13 +23,16 @@ func TestRows(t *testing.T) {
collectedRows := make([][]string, 0)
for rows.Next() {
collectedRows = append(collectedRows, trimSliceSpace(rows.Columns()))
columns, err := rows.Columns()
assert.NoError(t, err)
collectedRows = append(collectedRows, trimSliceSpace(columns))
}
if !assert.NoError(t, rows.Error()) {
t.FailNow()
}
returnedRows := xlsx.GetRows(sheet2)
returnedRows, err := xlsx.GetRows(sheet2)
assert.NoError(t, err)
for i := range returnedRows {
returnedRows[i] = trimSliceSpace(returnedRows[i])
}
@ -54,21 +57,22 @@ func TestRowHeight(t *testing.T) {
xlsx := NewFile()
sheet1 := xlsx.GetSheetName(1)
assert.Panics(t, func() {
xlsx.SetRowHeight(sheet1, 0, defaultRowHeightPixels+1.0)
})
assert.EqualError(t, xlsx.SetRowHeight(sheet1, 0, defaultRowHeightPixels+1.0), "invalid row number 0")
assert.Panics(t, func() {
xlsx.GetRowHeight("Sheet1", 0)
})
height, err := xlsx.GetRowHeight("Sheet1", 0)
assert.EqualError(t, err, "invalid row number 0")
xlsx.SetRowHeight(sheet1, 1, 111.0)
assert.Equal(t, 111.0, xlsx.GetRowHeight(sheet1, 1))
assert.NoError(t, xlsx.SetRowHeight(sheet1, 1, 111.0))
height, err = xlsx.GetRowHeight(sheet1, 1)
assert.NoError(t, err)
assert.Equal(t, 111.0, height)
xlsx.SetRowHeight(sheet1, 4, 444.0)
assert.Equal(t, 444.0, xlsx.GetRowHeight(sheet1, 4))
assert.NoError(t, xlsx.SetRowHeight(sheet1, 4, 444.0))
height, err = xlsx.GetRowHeight(sheet1, 4)
assert.NoError(t, err)
assert.Equal(t, 444.0, height)
err := xlsx.SaveAs(filepath.Join("test", "TestRowHeight.xlsx"))
err = xlsx.SaveAs(filepath.Join("test", "TestRowHeight.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
}
@ -86,13 +90,11 @@ func TestRowVisibility(t *testing.T) {
xlsx.SetRowVisible("Sheet3", 2, true)
xlsx.GetRowVisible("Sheet3", 2)
assert.Panics(t, func() {
xlsx.SetRowVisible("Sheet3", 0, true)
})
assert.EqualError(t, xlsx.SetRowVisible("Sheet3", 0, true), "invalid row number 0")
assert.Panics(t, func() {
xlsx.GetRowVisible("Sheet3", 0)
})
visible, err := xlsx.GetRowVisible("Sheet3", 0)
assert.Equal(t, false, visible)
assert.EqualError(t, err, "invalid row number 0")
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestRowVisibility.xlsx")))
}
@ -110,27 +112,23 @@ func TestRemoveRow(t *testing.T) {
xlsx.SetCellHyperLink(sheet1, "A5", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
assert.Panics(t, func() {
xlsx.RemoveRow(sheet1, -1)
})
assert.EqualError(t, xlsx.RemoveRow(sheet1, -1), "invalid row number -1")
assert.Panics(t, func() {
xlsx.RemoveRow(sheet1, 0)
})
assert.EqualError(t, xlsx.RemoveRow(sheet1, 0), "invalid row number 0")
xlsx.RemoveRow(sheet1, 4)
assert.NoError(t, xlsx.RemoveRow(sheet1, 4))
if !assert.Len(t, r.SheetData.Row, rowCount-1) {
t.FailNow()
}
xlsx.MergeCell(sheet1, "B3", "B5")
xlsx.RemoveRow(sheet1, 2)
assert.NoError(t, xlsx.RemoveRow(sheet1, 2))
if !assert.Len(t, r.SheetData.Row, rowCount-2) {
t.FailNow()
}
xlsx.RemoveRow(sheet1, 4)
assert.NoError(t, xlsx.RemoveRow(sheet1, 4))
if !assert.Len(t, r.SheetData.Row, rowCount-3) {
t.FailNow()
}
@ -140,17 +138,17 @@ func TestRemoveRow(t *testing.T) {
t.FailNow()
}
xlsx.RemoveRow(sheet1, 1)
assert.NoError(t, xlsx.RemoveRow(sheet1, 1))
if !assert.Len(t, r.SheetData.Row, rowCount-4) {
t.FailNow()
}
xlsx.RemoveRow(sheet1, 2)
assert.NoError(t, xlsx.RemoveRow(sheet1, 2))
if !assert.Len(t, r.SheetData.Row, rowCount-5) {
t.FailNow()
}
xlsx.RemoveRow(sheet1, 1)
assert.NoError(t, xlsx.RemoveRow(sheet1, 1))
if !assert.Len(t, r.SheetData.Row, rowCount-6) {
t.FailNow()
}
@ -171,20 +169,16 @@ func TestInsertRow(t *testing.T) {
xlsx.SetCellHyperLink(sheet1, "A5", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
assert.Panics(t, func() {
xlsx.InsertRow(sheet1, -1)
})
assert.EqualError(t, xlsx.InsertRow(sheet1, -1), "invalid row number -1")
assert.Panics(t, func() {
xlsx.InsertRow(sheet1, 0)
})
assert.EqualError(t, xlsx.InsertRow(sheet1, 0), "invalid row number 0")
xlsx.InsertRow(sheet1, 1)
assert.NoError(t, xlsx.InsertRow(sheet1, 1))
if !assert.Len(t, r.SheetData.Row, rowCount+1) {
t.FailNow()
}
xlsx.InsertRow(sheet1, 4)
assert.NoError(t, xlsx.InsertRow(sheet1, 4))
if !assert.Len(t, r.SheetData.Row, rowCount+2) {
t.FailNow()
}
@ -198,11 +192,11 @@ func TestInsertRowInEmptyFile(t *testing.T) {
xlsx := NewFile()
sheet1 := xlsx.GetSheetName(1)
r := xlsx.workSheetReader(sheet1)
xlsx.InsertRow(sheet1, 1)
assert.NoError(t, xlsx.InsertRow(sheet1, 1))
assert.Len(t, r.SheetData.Row, 0)
xlsx.InsertRow(sheet1, 2)
assert.NoError(t, xlsx.InsertRow(sheet1, 2))
assert.Len(t, r.SheetData.Row, 0)
xlsx.InsertRow(sheet1, 99)
assert.NoError(t, xlsx.InsertRow(sheet1, 99))
assert.Len(t, r.SheetData.Row, 0)
assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestInsertRowInEmptyFile.xlsx")))
}
@ -234,7 +228,7 @@ func TestDuplicateRow(t *testing.T) {
xlsx.SetCellStr(sheet, "A1", cells["A1"])
xlsx.SetCellStr(sheet, "B1", cells["B1"])
xlsx.DuplicateRow(sheet, 1)
assert.NoError(t, xlsx.DuplicateRow(sheet, 1))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.FromSingleRow_1"))) {
t.FailNow()
}
@ -243,12 +237,14 @@ func TestDuplicateRow(t *testing.T) {
"A2": cells["A1"], "B2": cells["B1"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
xlsx.DuplicateRow(sheet, 2)
assert.NoError(t, xlsx.DuplicateRow(sheet, 2))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.FromSingleRow_2"))) {
t.FailNow()
}
@ -258,7 +254,9 @@ func TestDuplicateRow(t *testing.T) {
"A3": cells["A1"], "B3": cells["B1"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -269,7 +267,7 @@ func TestDuplicateRow(t *testing.T) {
xlsx.SetCellStr(sheet, "A1", cells["A1"])
xlsx.SetCellStr(sheet, "B1", cells["B1"])
xlsx.DuplicateRow(sheet, 1)
assert.NoError(t, xlsx.DuplicateRow(sheet, 1))
xlsx.SetCellStr(sheet, "A2", cells["A2"])
xlsx.SetCellStr(sheet, "B2", cells["B2"])
@ -282,7 +280,9 @@ func TestDuplicateRow(t *testing.T) {
"A2": cells["A2"], "B2": cells["B2"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -291,7 +291,7 @@ func TestDuplicateRow(t *testing.T) {
t.Run("FirstOfMultipleRows", func(t *testing.T) {
xlsx := newFileWithDefaults()
xlsx.DuplicateRow(sheet, 1)
assert.NoError(t, xlsx.DuplicateRow(sheet, 1))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.FirstOfMultipleRows"))) {
t.FailNow()
@ -303,7 +303,9 @@ func TestDuplicateRow(t *testing.T) {
"A4": cells["A3"], "B4": cells["B3"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -312,26 +314,35 @@ func TestDuplicateRow(t *testing.T) {
t.Run("ZeroWithNoRows", func(t *testing.T) {
xlsx := NewFile()
assert.Panics(t, func() {
xlsx.DuplicateRow(sheet, 0)
})
assert.EqualError(t, xlsx.DuplicateRow(sheet, 0), "invalid row number 0")
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.ZeroWithNoRows"))) {
t.FailNow()
}
assert.Equal(t, "", xlsx.GetCellValue(sheet, "A1"))
assert.Equal(t, "", xlsx.GetCellValue(sheet, "B1"))
assert.Equal(t, "", xlsx.GetCellValue(sheet, "A2"))
assert.Equal(t, "", xlsx.GetCellValue(sheet, "B2"))
val, err := xlsx.GetCellValue(sheet, "A1")
assert.NoError(t, err)
assert.Equal(t, "", val)
val, err = xlsx.GetCellValue(sheet, "B1")
assert.NoError(t, err)
assert.Equal(t, "", val)
val, err = xlsx.GetCellValue(sheet, "A2")
assert.NoError(t, err)
assert.Equal(t, "", val)
val, err = xlsx.GetCellValue(sheet, "B2")
assert.NoError(t, err)
assert.Equal(t, "", val)
assert.NoError(t, err)
expect := map[string]string{
"A1": "", "B1": "",
"A2": "", "B2": "",
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -340,7 +351,7 @@ func TestDuplicateRow(t *testing.T) {
t.Run("MiddleRowOfEmptyFile", func(t *testing.T) {
xlsx := NewFile()
xlsx.DuplicateRow(sheet, 99)
assert.NoError(t, xlsx.DuplicateRow(sheet, 99))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.MiddleRowOfEmptyFile"))) {
t.FailNow()
@ -351,7 +362,9 @@ func TestDuplicateRow(t *testing.T) {
"A100": "",
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -360,7 +373,7 @@ func TestDuplicateRow(t *testing.T) {
t.Run("WithLargeOffsetToMiddleOfData", func(t *testing.T) {
xlsx := newFileWithDefaults()
xlsx.DuplicateRowTo(sheet, 1, 3)
assert.NoError(t, xlsx.DuplicateRowTo(sheet, 1, 3))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.WithLargeOffsetToMiddleOfData"))) {
t.FailNow()
@ -372,7 +385,9 @@ func TestDuplicateRow(t *testing.T) {
"A4": cells["A3"], "B4": cells["B3"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -381,7 +396,7 @@ func TestDuplicateRow(t *testing.T) {
t.Run("WithLargeOffsetToEmptyRows", func(t *testing.T) {
xlsx := newFileWithDefaults()
xlsx.DuplicateRowTo(sheet, 1, 7)
assert.NoError(t, xlsx.DuplicateRowTo(sheet, 1, 7))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.WithLargeOffsetToEmptyRows"))) {
t.FailNow()
@ -393,7 +408,9 @@ func TestDuplicateRow(t *testing.T) {
"A7": cells["A1"], "B7": cells["B1"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -402,7 +419,7 @@ func TestDuplicateRow(t *testing.T) {
t.Run("InsertBefore", func(t *testing.T) {
xlsx := newFileWithDefaults()
xlsx.DuplicateRowTo(sheet, 2, 1)
assert.NoError(t, xlsx.DuplicateRowTo(sheet, 2, 1))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.InsertBefore"))) {
t.FailNow()
@ -415,7 +432,9 @@ func TestDuplicateRow(t *testing.T) {
"A4": cells["A3"], "B4": cells["B3"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell), cell) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v, cell) {
t.FailNow()
}
}
@ -424,7 +443,7 @@ func TestDuplicateRow(t *testing.T) {
t.Run("InsertBeforeWithLargeOffset", func(t *testing.T) {
xlsx := newFileWithDefaults()
xlsx.DuplicateRowTo(sheet, 3, 1)
assert.NoError(t, xlsx.DuplicateRowTo(sheet, 3, 1))
if !assert.NoError(t, xlsx.SaveAs(fmt.Sprintf(outFile, "TestDuplicateRow.InsertBeforeWithLargeOffset"))) {
t.FailNow()
@ -437,7 +456,9 @@ func TestDuplicateRow(t *testing.T) {
"A4": cells["A3"], "B4": cells["B3"],
}
for cell, val := range expect {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, cell)) {
v, err := xlsx.GetCellValue(sheet, cell)
assert.NoError(t, err)
if !assert.Equal(t, val, v) {
t.FailNow()
}
}
@ -467,12 +488,12 @@ func TestDuplicateRowInvalidRownum(t *testing.T) {
xlsx.SetCellStr(sheet, col, val)
}
assert.Panics(t, func() {
xlsx.DuplicateRow(sheet, row)
})
assert.EqualError(t, xlsx.DuplicateRow(sheet, row), fmt.Sprintf("invalid row number %d", row))
for col, val := range cells {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, col)) {
v, err := xlsx.GetCellValue(sheet, col)
assert.NoError(t, err)
if !assert.Equal(t, val, v) {
t.FailNow()
}
}
@ -489,12 +510,12 @@ func TestDuplicateRowInvalidRownum(t *testing.T) {
xlsx.SetCellStr(sheet, col, val)
}
assert.Panics(t, func() {
xlsx.DuplicateRowTo(sheet, row1, row2)
})
assert.EqualError(t, xlsx.DuplicateRowTo(sheet, row1, row2), fmt.Sprintf("invalid row number %d", row1))
for col, val := range cells {
if !assert.Equal(t, val, xlsx.GetCellValue(sheet, col)) {
v, err := xlsx.GetCellValue(sheet, col)
assert.NoError(t, err)
if !assert.Equal(t, val, v) {
t.FailNow()
}
}

View File

@ -275,15 +275,21 @@ func (f *File) AddShape(sheet, cell, format string) error {
rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
f.addSheetDrawing(sheet, rID)
}
f.addDrawingShape(sheet, drawingXML, cell, formatSet)
err = f.addDrawingShape(sheet, drawingXML, cell, formatSet)
if err != nil {
return err
}
f.addContentTypePart(drawingID, "drawings")
return err
}
// addDrawingShape provides a function to add preset geometry by given sheet,
// drawingXMLand format sets.
func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) {
fromCol, fromRow := MustCellNameToCoordinates(cell)
func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) error {
fromCol, fromRow, err := CellNameToCoordinates(cell)
if err != nil {
return err
}
colIdx := fromCol - 1
rowIdx := fromRow - 1
@ -421,6 +427,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
}
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content
return err
}
// setShapeRef provides a function to set color with hex model by given actual

View File

@ -275,11 +275,11 @@ func (f *File) GetActiveSheetIndex() int {
return 0
}
// SetSheetName provides a function to set the worksheet name be given old and new
// worksheet name. Maximum 31 characters are allowed in sheet title and this
// function only changes the name of the sheet and will not update the sheet
// name in the formula or reference associated with the cell. So there may be
// problem formula error or reference missing.
// SetSheetName provides a function to set the worksheet name be given old and
// new worksheet name. Maximum 31 characters are allowed in sheet title and
// this function only changes the name of the sheet and will not update the
// sheet name in the formula or reference associated with the cell. So there
// may be problem formula error or reference missing.
func (f *File) SetSheetName(oldName, newName string) {
oldName = trimSheetName(oldName)
newName = trimSheetName(newName)
@ -665,14 +665,14 @@ func (f *File) GetSheetVisible(name string) bool {
//
// An example of search the coordinates of the value of "100" on Sheet1:
//
// xlsx.SearchSheet("Sheet1", "100")
// result, err := xlsx.SearchSheet("Sheet1", "100")
//
// An example of search the coordinates where the numerical value in the range
// of "0-9" of Sheet1 is described:
//
// xlsx.SearchSheet("Sheet1", "[0-9]", true)
// result, err := xlsx.SearchSheet("Sheet1", "[0-9]", true)
//
func (f *File) SearchSheet(sheet, value string, reg ...bool) []string {
func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
var regSearch bool
for _, r := range reg {
regSearch = r
@ -683,7 +683,7 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) []string {
)
name, ok := f.sheetMap[trimSheetName(sheet)]
if !ok {
return result
return result, nil
}
if xlsx != nil {
output, _ := xml.Marshal(f.Sheet[name])
@ -718,14 +718,21 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) []string {
}
}
cellCol, _ := MustCellNameToCoordinates(colCell.R)
result = append(result, MustCoordinatesToCellName(cellCol, r.R))
cellCol, _, err := CellNameToCoordinates(colCell.R)
if err != nil {
return result, err
}
cellName, err := CoordinatesToCellName(cellCol, r.R)
if err != nil {
return result, err
}
result = append(result, cellName)
}
}
default:
}
}
return result
return result, nil
}
// ProtectSheet provides a function to prevent other users from accidentally

View File

@ -1880,8 +1880,8 @@ func parseFormatStyleSet(style string) (*formatStyle, error) {
//
// xlsx := excelize.NewFile()
// xlsx.SetCellValue("Sheet1", "A6", 42920.5)
// style, _ := xlsx.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
// xlsx.SetCellStyle("Sheet1", "A6", "A6", style)
// style, err := xlsx.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
// err = xlsx.SetCellStyle("Sheet1", "A6", "A6", style)
//
// Cell Sheet1!A6 in the Excel Application: martes, 04 de Julio de 2017
//
@ -2265,10 +2265,13 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a
// GetCellStyle provides a function to get cell style index by given worksheet
// name and cell coordinates.
func (f *File) GetCellStyle(sheet, axis string) int {
func (f *File) GetCellStyle(sheet, axis string) (int, error) {
xlsx := f.workSheetReader(sheet)
cellData, col, _ := f.prepareCell(xlsx, sheet, axis)
return f.prepareCellStyle(xlsx, col, cellData.S)
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
if err != nil {
return 0, err
}
return f.prepareCellStyle(xlsx, col, cellData.S), err
}
// SetCellStyle provides a function to add style attribute for cells by given
@ -2282,7 +2285,7 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
// Set gradient fill with vertical variants shading styles for cell H9 on
// Sheet1:
@ -2291,7 +2294,7 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
// Set solid style pattern fill for cell H9 on Sheet1:
//
@ -2299,7 +2302,7 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
// Set alignment style for cell H9 on Sheet1:
//
@ -2307,7 +2310,7 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
// Dates and times in Excel are represented by real numbers, for example "Apr 7
// 2017 12:00 PM" is represented by the number 42920.5. Set date and time format
@ -2318,7 +2321,7 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
// Set font style for cell H9 on Sheet1:
//
@ -2326,7 +2329,7 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
// Hide and lock for cell H9 on Sheet1:
//
@ -2334,17 +2337,17 @@ func (f *File) GetCellStyle(sheet, axis string) int {
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
// err = xlsx.SetCellStyle("Sheet1", "H9", "H9", style)
//
func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) {
func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) error {
hcol, hrow, err := CellNameToCoordinates(hcell)
if err != nil {
panic(err)
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
if err != nil {
panic(err)
return err
}
// Normalize the coordinate area, such correct C1:B3 to B1:C3.
@ -2370,6 +2373,7 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) {
xlsx.SheetData.Row[r].C[k].S = styleID
}
}
return nil
}
// SetConditionalFormat provides a function to create conditional formatting

View File

@ -33,11 +33,11 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) {
// name, coordinate area and format set. For example, create a table of A1:D5
// on Sheet1:
//
// xlsx.AddTable("Sheet1", "A1", "D5", ``)
// err := xlsx.AddTable("Sheet1", "A1", "D5", ``)
//
// Create a table of F2:H6 on Sheet2 with format set:
//
// xlsx.AddTable("Sheet2", "F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
// err := xlsx.AddTable("Sheet2", "F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
//
// Note that the table at least two lines include string type header. Multiple
// tables coordinate areas can't have an intersection.
@ -56,8 +56,14 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error {
return err
}
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
hcol, hrow := MustCellNameToCoordinates(hcell)
vcol, vrow := MustCellNameToCoordinates(vcell)
hcol, hrow, err := CellNameToCoordinates(hcell)
if err != nil {
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
if err != nil {
return err
}
if vcol < hcol {
vcol, hcol = hcol, vcol
@ -73,7 +79,10 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error {
// Add first table for given sheet.
rID := f.addSheetRelationships(sheet, SourceRelationshipTable, sheetRelationshipsTableXML, "")
f.addSheetTable(sheet, rID)
f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet)
err = f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet)
if err != nil {
return err
}
f.addContentTypePart(tableID, "table")
return err
}
@ -106,24 +115,33 @@ func (f *File) addSheetTable(sheet string, rID int) {
// addTable provides a function to add table by given worksheet name,
// coordinate area and format set.
func (f *File) addTable(sheet, tableXML string, hcol, hrow, vcol, vrow, i int, formatSet *formatTable) {
func (f *File) addTable(sheet, tableXML string, hcol, hrow, vcol, vrow, i int, formatSet *formatTable) error {
// Correct the minimum number of rows, the table at least two lines.
if hrow == vrow {
vrow++
}
// Correct table reference coordinate area, such correct C1:B3 to B1:C3.
ref := MustCoordinatesToCellName(hcol, hrow) + ":" + MustCoordinatesToCellName(vcol, vrow)
hcell, err := CoordinatesToCellName(hcol, hrow)
if err != nil {
return err
}
vcell, err := CoordinatesToCellName(vcol, vrow)
if err != nil {
return err
}
ref := hcell + ":" + vcell
var (
tableColumn []*xlsxTableColumn
)
var tableColumn []*xlsxTableColumn
idx := 0
for i := hcol; i <= vcol; i++ {
idx++
cell := MustCoordinatesToCellName(i, hrow)
name := f.GetCellValue(sheet, cell)
cell, err := CoordinatesToCellName(i, hrow)
if err != nil {
return err
}
name, _ := f.GetCellValue(sheet, cell)
if _, err := strconv.Atoi(name); err == nil {
f.SetCellStr(sheet, cell, name)
}
@ -163,6 +181,7 @@ func (f *File) addTable(sheet, tableXML string, hcol, hrow, vcol, vrow, i int, f
}
table, _ := xml.Marshal(t)
f.saveFileList(tableXML, table)
return nil
}
// parseAutoFilterSet provides a function to parse the settings of the auto
@ -244,8 +263,14 @@ func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
// Price < 2000
//
func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
hcol, hrow := MustCellNameToCoordinates(hcell)
vcol, vrow := MustCellNameToCoordinates(vcell)
hcol, hrow, err := CellNameToCoordinates(hcell)
if err != nil {
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
if err != nil {
return err
}
if vcol < hcol {
vcol, hcol = hcol, vcol
@ -256,7 +281,17 @@ func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
}
formatSet, _ := parseAutoFilterSet(format)
ref := MustCoordinatesToCellName(hcol, hrow) + ":" + MustCoordinatesToCellName(vcol, vrow)
var cellStart, cellEnd string
cellStart, err = CoordinatesToCellName(hcol, hrow)
if err != nil {
return err
}
cellEnd, err = CoordinatesToCellName(vcol, vrow)
if err != nil {
return err
}
ref := cellStart + ":" + cellEnd
refRange := vcol - hcol
return f.autoFilter(sheet, ref, refRange, hcol, formatSet)
}
@ -277,7 +312,10 @@ func (f *File) autoFilter(sheet, ref string, refRange, col int, formatSet *forma
return nil
}
fsCol := MustColumnNameToNumber(formatSet.Column)
fsCol, err := ColumnNameToNumber(formatSet.Column)
if err != nil {
return err
}
offset := fsCol - col
if offset < 0 || offset > refRange {
return fmt.Errorf("incorrect index of column '%s'", formatSet.Column)