diff --git a/rows.go b/rows.go index 853c8f7..457f59b 100644 --- a/rows.go +++ b/rows.go @@ -80,12 +80,14 @@ type Rows struct { sst *xlsxSST decoder *xml.Decoder token xml.Token + curRowOpts, seekRowOpts RowOpts } // Next will return true if find the next row element. func (rows *Rows) Next() bool { rows.seekRow++ if rows.curRow >= rows.seekRow { + rows.curRowOpts = rows.seekRowOpts return true } for { @@ -101,6 +103,7 @@ func (rows *Rows) Next() bool { rows.curRow = rowNum } rows.token = token + rows.curRowOpts = extractRowOpts(xmlElement.Attr) return true } case xml.EndElement: @@ -111,6 +114,11 @@ func (rows *Rows) Next() bool { } } +// GetRowOpts will return the RowOpts of the current row. +func (rows *Rows) GetRowOpts() RowOpts { + return rows.curRowOpts +} + // Error will return the error when the error occurs. func (rows *Rows) Error() error { return rows.err @@ -151,6 +159,8 @@ func (rows *Rows) Columns(opts ...Options) ([]string, error) { } else if rows.token == nil { rows.curRow++ } + rows.token = token + rows.seekRowOpts = extractRowOpts(xmlElement.Attr) if rows.curRow > rows.seekRow { rows.token = nil return rowIterator.columns, rowIterator.err @@ -170,6 +180,20 @@ func (rows *Rows) Columns(opts ...Options) ([]string, error) { return rowIterator.columns, rowIterator.err } +func extractRowOpts(attrs []xml.Attr) RowOpts { + rowOpts := RowOpts{Height: defaultRowHeight} + if styleID, err := attrValToInt("s", attrs); err == nil && styleID > 0 && styleID < MaxCellStyles { + rowOpts.StyleID = styleID + } + if hidden, err := attrValToBool("hidden", attrs); err == nil { + rowOpts.Hidden = hidden + } + if height, err := attrValToFloat("ht", attrs); err == nil { + rowOpts.Height = height + } + return rowOpts +} + // appendSpace append blank characters to slice by given length and source slice. func appendSpace(l int, s []string) []string { for i := 1; i < l; i++ { diff --git a/rows_test.go b/rows_test.go index 4fe2851..4b57c34 100644 --- a/rows_test.go +++ b/rows_test.go @@ -96,6 +96,30 @@ func TestRowsIterator(t *testing.T) { assert.Equal(t, expectedNumRow, rowCount) } +func TestRowsGetRowOpts(t *testing.T) { + sheetName := "Sheet2" + expectedRowStyleID1 := RowOpts{Height: 17.0, Hidden: false, StyleID: 1} + expectedRowStyleID2 := RowOpts{Height: 17.0, Hidden: false, StyleID: 0} + expectedRowStyleID3 := RowOpts{Height: 17.0, Hidden: false, StyleID: 2} + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + require.NoError(t, err) + + rows, err := f.Rows(sheetName) + require.NoError(t, err) + + rows.Next() + rows.Columns() // Columns() may change the XML iterator, so better check with and without calling it + got := rows.GetRowOpts() + assert.Equal(t, expectedRowStyleID1, got) + rows.Next() + got = rows.GetRowOpts() + assert.Equal(t, expectedRowStyleID2, got) + rows.Next() + rows.Columns() + got = rows.GetRowOpts() + assert.Equal(t, expectedRowStyleID3, got) +} + func TestRowsError(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) if !assert.NoError(t, err) { diff --git a/sheet.go b/sheet.go index 01dd167..1f2dcea 100644 --- a/sheet.go +++ b/sheet.go @@ -928,6 +928,34 @@ func attrValToInt(name string, attrs []xml.Attr) (val int, err error) { return } +// attrValToFloat provides a function to convert the local names to a float64 +// by given XML attributes and specified names. +func attrValToFloat(name string, attrs []xml.Attr) (val float64, err error) { + for _, attr := range attrs { + if attr.Name.Local == name { + val, err = strconv.ParseFloat(attr.Value, 64) + if err != nil { + return + } + } + } + return +} + +// attrValToBool provides a function to convert the local names to a boolean +// by given XML attributes and specified names. +func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) { + for _, attr := range attrs { + if attr.Name.Local == name { + val, err = strconv.ParseBool(attr.Value) + if err != nil { + return + } + } + } + return +} + // SetHeaderFooter provides a function to set headers and footers by given // worksheet name and the control characters. // diff --git a/sheet_test.go b/sheet_test.go index c68ad31..9b0caf4 100644 --- a/sheet_test.go +++ b/sheet_test.go @@ -505,3 +505,29 @@ func newSheetWithSave() { } _ = file.Save() } + +func TestAttrValToBool(t *testing.T) { + _, err := attrValToBool("hidden", []xml.Attr{ + {Name: xml.Name{Local: "hidden"}}, + }) + assert.EqualError(t, err, `strconv.ParseBool: parsing "": invalid syntax`) + + got, err := attrValToBool("hidden", []xml.Attr{ + {Name: xml.Name{Local: "hidden"}, Value: "1"}, + }) + assert.NoError(t, err) + assert.Equal(t, true, got) +} + +func TestAttrValToFloat(t *testing.T) { + _, err := attrValToFloat("ht", []xml.Attr{ + {Name: xml.Name{Local: "ht"}}, + }) + assert.EqualError(t, err, `strconv.ParseFloat: parsing "": invalid syntax`) + + got, err := attrValToFloat("ht", []xml.Attr{ + {Name: xml.Name{Local: "ht"}, Value: "42.1"}, + }) + assert.NoError(t, err) + assert.Equal(t, 42.1, got) +} diff --git a/test/Book1.xlsx b/test/Book1.xlsx index 6a497e3..ed3e292 100644 Binary files a/test/Book1.xlsx and b/test/Book1.xlsx differ diff --git a/xmlDrawing.go b/xmlDrawing.go index 8c3d734..b4fdccc 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -107,6 +107,7 @@ const ( MaxFieldLength = 255 MaxColumnWidth = 255 MaxRowHeight = 409 + MaxCellStyles = 64000 MinFontSize = 1 TotalRows = 1048576 MinColumns = 1