This closes #1047, stream writer support set panes (#1123)

- New exported error `ErrStreamSetPanes` has been added
This commit is contained in:
Harrison 2022-10-10 13:05:02 -03:00 committed by GitHub
parent 2f5704b114
commit c02346bafc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 42 deletions

View File

@ -91,6 +91,9 @@ var (
// ErrStreamSetColWidth defined the error message on set column width in // ErrStreamSetColWidth defined the error message on set column width in
// stream writing mode. // stream writing mode.
ErrStreamSetColWidth = errors.New("must call the SetColWidth function before the SetRow function") ErrStreamSetColWidth = errors.New("must call the SetColWidth function before the SetRow function")
// ErrStreamSetPanes defined the error message on set panes in stream
// writing mode.
ErrStreamSetPanes = errors.New("must call the SetPanes function before the SetRow function")
// ErrColumnNumber defined the error message on receive an invalid column // ErrColumnNumber defined the error message on receive an invalid column
// number. // number.
ErrColumnNumber = fmt.Errorf(`the column number must be greater than or equal to %d and less than or equal to %d`, MinColumns, MaxColumns) ErrColumnNumber = fmt.Errorf(`the column number must be greater than or equal to %d and less than or equal to %d`, MinColumns, MaxColumns)

View File

@ -683,8 +683,44 @@ func parsePanesOptions(opts string) (*panesOptions, error) {
return &format, err return &format, err
} }
// setPanes set create freeze panes and split panes by given options.
func (ws *xlsxWorksheet) setPanes(panes string) error {
opts, err := parsePanesOptions(panes)
if err != nil {
return err
}
p := &xlsxPane{
ActivePane: opts.ActivePane,
TopLeftCell: opts.TopLeftCell,
XSplit: float64(opts.XSplit),
YSplit: float64(opts.YSplit),
}
if opts.Freeze {
p.State = "frozen"
}
if ws.SheetViews == nil {
ws.SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}
}
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = p
if !(opts.Freeze) && !(opts.Split) {
if len(ws.SheetViews.SheetView) > 0 {
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = nil
}
}
var s []*xlsxSelection
for _, p := range opts.Panes {
s = append(s, &xlsxSelection{
ActiveCell: p.ActiveCell,
Pane: p.Pane,
SQRef: p.SQRef,
})
}
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Selection = s
return err
}
// SetPanes provides a function to create and remove freeze panes and split panes // SetPanes provides a function to create and remove freeze panes and split panes
// by given worksheet name and panes format set. // by given worksheet name and panes options.
// //
// activePane defines the pane that is active. The possible values for this // activePane defines the pane that is active. The possible values for this
// attribute are defined in the following table: // attribute are defined in the following table:
@ -768,39 +804,11 @@ func parsePanesOptions(opts string) (*panesOptions, error) {
// //
// f.SetPanes("Sheet1", `{"freeze":false,"split":false}`) // f.SetPanes("Sheet1", `{"freeze":false,"split":false}`)
func (f *File) SetPanes(sheet, panes string) error { func (f *File) SetPanes(sheet, panes string) error {
fs, _ := parsePanesOptions(panes)
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
} }
p := &xlsxPane{ return ws.setPanes(panes)
ActivePane: fs.ActivePane,
TopLeftCell: fs.TopLeftCell,
XSplit: float64(fs.XSplit),
YSplit: float64(fs.YSplit),
}
if fs.Freeze {
p.State = "frozen"
}
if ws.SheetViews == nil {
ws.SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}
}
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = p
if !(fs.Freeze) && !(fs.Split) {
if len(ws.SheetViews.SheetView) > 0 {
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = nil
}
}
var s []*xlsxSelection
for _, p := range fs.Panes {
s = append(s, &xlsxSelection{
ActiveCell: p.ActiveCell,
Pane: p.Pane,
SQRef: p.SQRef,
})
}
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Selection = s
return err
} }
// GetSheetVisible provides a function to get worksheet visible by given worksheet // GetSheetVisible provides a function to get worksheet visible by given worksheet

View File

@ -34,7 +34,7 @@ func TestSetPane(t *testing.T) {
assert.NoError(t, f.SetPanes("Panes 3", `{"freeze":false,"split":true,"x_split":3270,"y_split":1800,"top_left_cell":"N57","active_pane":"bottomLeft","panes":[{"sqref":"I36","active_cell":"I36"},{"sqref":"G33","active_cell":"G33","pane":"topRight"},{"sqref":"J60","active_cell":"J60","pane":"bottomLeft"},{"sqref":"O60","active_cell":"O60","pane":"bottomRight"}]}`)) assert.NoError(t, f.SetPanes("Panes 3", `{"freeze":false,"split":true,"x_split":3270,"y_split":1800,"top_left_cell":"N57","active_pane":"bottomLeft","panes":[{"sqref":"I36","active_cell":"I36"},{"sqref":"G33","active_cell":"G33","pane":"topRight"},{"sqref":"J60","active_cell":"J60","pane":"bottomLeft"},{"sqref":"O60","active_cell":"O60","pane":"bottomRight"}]}`))
f.NewSheet("Panes 4") f.NewSheet("Panes 4")
assert.NoError(t, f.SetPanes("Panes 4", `{"freeze":true,"split":false,"x_split":0,"y_split":9,"top_left_cell":"A34","active_pane":"bottomLeft","panes":[{"sqref":"A11:XFD11","active_cell":"A11","pane":"bottomLeft"}]}`)) assert.NoError(t, f.SetPanes("Panes 4", `{"freeze":true,"split":false,"x_split":0,"y_split":9,"top_left_cell":"A34","active_pane":"bottomLeft","panes":[{"sqref":"A11:XFD11","active_cell":"A11","pane":"bottomLeft"}]}`))
assert.NoError(t, f.SetPanes("Panes 4", "")) assert.EqualError(t, f.SetPanes("Panes 4", ""), "unexpected end of JSON input")
assert.EqualError(t, f.SetPanes("SheetN", ""), "sheet SheetN does not exist") assert.EqualError(t, f.SetPanes("SheetN", ""), "sheet SheetN does not exist")
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx")))
// Test add pane on empty sheet views worksheet // Test add pane on empty sheet views worksheet

View File

@ -119,7 +119,7 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
f.streams[sheetXMLPath] = sw f.streams[sheetXMLPath] = sw
_, _ = sw.rawData.WriteString(xml.Header + `<worksheet` + templateNamespaceIDMap) _, _ = sw.rawData.WriteString(xml.Header + `<worksheet` + templateNamespaceIDMap)
bulkAppendFields(&sw.rawData, sw.worksheet, 2, 5) bulkAppendFields(&sw.rawData, sw.worksheet, 2, 3)
return sw, err return sw, err
} }
@ -351,13 +351,7 @@ func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpt
if err != nil { if err != nil {
return err return err
} }
if !sw.sheetWritten { sw.writeSheetData()
if len(sw.cols) > 0 {
_, _ = sw.rawData.WriteString("<cols>" + sw.cols + "</cols>")
}
_, _ = sw.rawData.WriteString(`<sheetData>`)
sw.sheetWritten = true
}
options := parseRowOpts(opts...) options := parseRowOpts(opts...)
attrs, err := options.marshalAttrs() attrs, err := options.marshalAttrs()
if err != nil { if err != nil {
@ -415,6 +409,16 @@ func (sw *StreamWriter) SetColWidth(min, max int, width float64) error {
return nil return nil
} }
// SetPanes provides a function to create and remove freeze panes and split
// panes by given worksheet name and panes options for the StreamWriter. Note
// that you must call the 'SetPanes' function before the 'SetRow' function.
func (sw *StreamWriter) SetPanes(panes string) error {
if sw.sheetWritten {
return ErrStreamSetPanes
}
return sw.worksheet.setPanes(panes)
}
// MergeCell provides a function to merge cells by a given range reference for // MergeCell provides a function to merge cells by a given range reference for
// the StreamWriter. Don't create a merged cell that overlaps with another // the StreamWriter. Don't create a merged cell that overlaps with another
// existing merged cell. // existing merged cell.
@ -507,6 +511,7 @@ func setCellIntFunc(c *xlsxC, val interface{}) (err error) {
return return
} }
// writeCell constructs a cell XML and writes it to the buffer.
func writeCell(buf *bufferedWriter, c xlsxC) { func writeCell(buf *bufferedWriter, c xlsxC) {
_, _ = buf.WriteString(`<c`) _, _ = buf.WriteString(`<c`)
if c.XMLSpace.Value != "" { if c.XMLSpace.Value != "" {
@ -539,12 +544,22 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
_, _ = buf.WriteString(`</c>`) _, _ = buf.WriteString(`</c>`)
} }
// Flush ending the streaming writing process. // writeSheetData prepares the element preceding sheetData and writes the
func (sw *StreamWriter) Flush() error { // sheetData XML start element to the buffer.
func (sw *StreamWriter) writeSheetData() {
if !sw.sheetWritten { if !sw.sheetWritten {
bulkAppendFields(&sw.rawData, sw.worksheet, 4, 5)
if len(sw.cols) > 0 {
_, _ = sw.rawData.WriteString("<cols>" + sw.cols + "</cols>")
}
_, _ = sw.rawData.WriteString(`<sheetData>`) _, _ = sw.rawData.WriteString(`<sheetData>`)
sw.sheetWritten = true sw.sheetWritten = true
} }
}
// Flush ending the streaming writing process.
func (sw *StreamWriter) Flush() error {
sw.writeSheetData()
_, _ = sw.rawData.WriteString(`</sheetData>`) _, _ = sw.rawData.WriteString(`</sheetData>`)
bulkAppendFields(&sw.rawData, sw.worksheet, 8, 15) bulkAppendFields(&sw.rawData, sw.worksheet, 8, 15)
mergeCells := strings.Builder{} mergeCells := strings.Builder{}

View File

@ -146,7 +146,17 @@ func TestStreamSetColWidth(t *testing.T) {
assert.ErrorIs(t, streamWriter.SetColWidth(MaxColumns+1, 3, 20), ErrColumnNumber) assert.ErrorIs(t, streamWriter.SetColWidth(MaxColumns+1, 3, 20), ErrColumnNumber)
assert.EqualError(t, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1), ErrColumnWidth.Error()) assert.EqualError(t, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1), ErrColumnWidth.Error())
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"})) assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
assert.EqualError(t, streamWriter.SetColWidth(2, 3, 20), ErrStreamSetColWidth.Error()) assert.ErrorIs(t, streamWriter.SetColWidth(2, 3, 20), ErrStreamSetColWidth)
}
func TestStreamSetPanes(t *testing.T) {
file, paneOpts := NewFile(), `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.NoError(t, streamWriter.SetPanes(paneOpts))
assert.EqualError(t, streamWriter.SetPanes(""), "unexpected end of JSON input")
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
assert.ErrorIs(t, streamWriter.SetPanes(paneOpts), ErrStreamSetPanes)
} }
func TestStreamTable(t *testing.T) { func TestStreamTable(t *testing.T) {