- Support get rows properties by `GetRowOpts` function - New exported constant `MaxCellStyles`
This commit is contained in:
parent
b8ceaf7bf6
commit
ed91cddea5
24
rows.go
24
rows.go
|
@ -80,12 +80,14 @@ type Rows struct {
|
||||||
sst *xlsxSST
|
sst *xlsxSST
|
||||||
decoder *xml.Decoder
|
decoder *xml.Decoder
|
||||||
token xml.Token
|
token xml.Token
|
||||||
|
curRowOpts, seekRowOpts RowOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next will return true if find the next row element.
|
// Next will return true if find the next row element.
|
||||||
func (rows *Rows) Next() bool {
|
func (rows *Rows) Next() bool {
|
||||||
rows.seekRow++
|
rows.seekRow++
|
||||||
if rows.curRow >= rows.seekRow {
|
if rows.curRow >= rows.seekRow {
|
||||||
|
rows.curRowOpts = rows.seekRowOpts
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
@ -101,6 +103,7 @@ func (rows *Rows) Next() bool {
|
||||||
rows.curRow = rowNum
|
rows.curRow = rowNum
|
||||||
}
|
}
|
||||||
rows.token = token
|
rows.token = token
|
||||||
|
rows.curRowOpts = extractRowOpts(xmlElement.Attr)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case xml.EndElement:
|
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.
|
// Error will return the error when the error occurs.
|
||||||
func (rows *Rows) Error() error {
|
func (rows *Rows) Error() error {
|
||||||
return rows.err
|
return rows.err
|
||||||
|
@ -151,6 +159,8 @@ func (rows *Rows) Columns(opts ...Options) ([]string, error) {
|
||||||
} else if rows.token == nil {
|
} else if rows.token == nil {
|
||||||
rows.curRow++
|
rows.curRow++
|
||||||
}
|
}
|
||||||
|
rows.token = token
|
||||||
|
rows.seekRowOpts = extractRowOpts(xmlElement.Attr)
|
||||||
if rows.curRow > rows.seekRow {
|
if rows.curRow > rows.seekRow {
|
||||||
rows.token = nil
|
rows.token = nil
|
||||||
return rowIterator.columns, rowIterator.err
|
return rowIterator.columns, rowIterator.err
|
||||||
|
@ -170,6 +180,20 @@ func (rows *Rows) Columns(opts ...Options) ([]string, error) {
|
||||||
return rowIterator.columns, rowIterator.err
|
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.
|
// appendSpace append blank characters to slice by given length and source slice.
|
||||||
func appendSpace(l int, s []string) []string {
|
func appendSpace(l int, s []string) []string {
|
||||||
for i := 1; i < l; i++ {
|
for i := 1; i < l; i++ {
|
||||||
|
|
24
rows_test.go
24
rows_test.go
|
@ -96,6 +96,30 @@ func TestRowsIterator(t *testing.T) {
|
||||||
assert.Equal(t, expectedNumRow, rowCount)
|
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) {
|
func TestRowsError(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
|
|
28
sheet.go
28
sheet.go
|
@ -928,6 +928,34 @@ func attrValToInt(name string, attrs []xml.Attr) (val int, err error) {
|
||||||
return
|
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
|
// SetHeaderFooter provides a function to set headers and footers by given
|
||||||
// worksheet name and the control characters.
|
// worksheet name and the control characters.
|
||||||
//
|
//
|
||||||
|
|
|
@ -505,3 +505,29 @@ func newSheetWithSave() {
|
||||||
}
|
}
|
||||||
_ = file.Save()
|
_ = 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)
|
||||||
|
}
|
||||||
|
|
BIN
test/Book1.xlsx
BIN
test/Book1.xlsx
Binary file not shown.
|
@ -107,6 +107,7 @@ const (
|
||||||
MaxFieldLength = 255
|
MaxFieldLength = 255
|
||||||
MaxColumnWidth = 255
|
MaxColumnWidth = 255
|
||||||
MaxRowHeight = 409
|
MaxRowHeight = 409
|
||||||
|
MaxCellStyles = 64000
|
||||||
MinFontSize = 1
|
MinFontSize = 1
|
||||||
TotalRows = 1048576
|
TotalRows = 1048576
|
||||||
MinColumns = 1
|
MinColumns = 1
|
||||||
|
|
Loading…
Reference in New Issue