Skip to content

Commit 0e0e2da

Browse files
authored
This close qax-os#2075, add new function SetColStyle for streaming writer to support set columns style (qax-os#2076)
- Add new exported error variable ErrStreamSetColStyle - Fix cell default style doesn't override by none-zero row style when set row by stream writer - Update unit tests Signed-off-by: mengzhongyuan <mengzhongyuan@bytedance.com>
1 parent 7e614c5 commit 0e0e2da

File tree

4 files changed

+119
-33
lines changed

4 files changed

+119
-33
lines changed

col.go

+23-11
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,21 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error {
450450
}
451451
s.mu.Unlock()
452452
ws.mu.Lock()
453+
ws.setColStyle(minVal, maxVal, styleID)
454+
ws.mu.Unlock()
455+
if rows := len(ws.SheetData.Row); rows > 0 {
456+
for col := minVal; col <= maxVal; col++ {
457+
from, _ := CoordinatesToCellName(col, 1)
458+
to, _ := CoordinatesToCellName(col, rows)
459+
err = f.SetCellStyle(sheet, from, to, styleID)
460+
}
461+
}
462+
return err
463+
}
464+
465+
// setColStyle provides a function to set the style of a single column or
466+
// multiple columns.
467+
func (ws *xlsxWorksheet) setColStyle(minVal, maxVal, styleID int) {
453468
if ws.Cols == nil {
454469
ws.Cols = &xlsxCols{}
455470
}
@@ -472,15 +487,6 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error {
472487
fc.Width = c.Width
473488
return fc
474489
})
475-
ws.mu.Unlock()
476-
if rows := len(ws.SheetData.Row); rows > 0 {
477-
for col := minVal; col <= maxVal; col++ {
478-
from, _ := CoordinatesToCellName(col, 1)
479-
to, _ := CoordinatesToCellName(col, rows)
480-
err = f.SetCellStyle(sheet, from, to, styleID)
481-
}
482-
}
483-
return err
484490
}
485491

486492
// SetColWidth provides a function to set the width of a single column or
@@ -504,6 +510,13 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
504510
f.mu.Unlock()
505511
ws.mu.Lock()
506512
defer ws.mu.Unlock()
513+
ws.setColWidth(minVal, maxVal, width)
514+
return err
515+
}
516+
517+
// setColWidth provides a function to set the width of a single column or
518+
// multiple columns.
519+
func (ws *xlsxWorksheet) setColWidth(minVal, maxVal int, width float64) {
507520
col := xlsxCol{
508521
Min: minVal,
509522
Max: maxVal,
@@ -514,7 +527,7 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
514527
cols := xlsxCols{}
515528
cols.Col = append(cols.Col, col)
516529
ws.Cols = &cols
517-
return err
530+
return
518531
}
519532
ws.Cols.Col = flatCols(col, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {
520533
fc.BestFit = c.BestFit
@@ -525,7 +538,6 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
525538
fc.Style = c.Style
526539
return fc
527540
})
528-
return err
529541
}
530542

531543
// flatCols provides a method for the column's operation functions to flatten

errors.go

+3
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ var (
135135
// ErrSparklineType defined the error message on receive the invalid
136136
// sparkline Type parameters.
137137
ErrSparklineType = errors.New("parameter 'Type' must be 'line', 'column' or 'win_loss'")
138+
// ErrStreamSetColStyle defined the error message on set column style in
139+
// stream writing mode.
140+
ErrStreamSetColStyle = errors.New("must call the SetColStyle function before the SetRow function")
138141
// ErrStreamSetColWidth defined the error message on set column width in
139142
// stream writing mode.
140143
ErrStreamSetColWidth = errors.New("must call the SetColWidth function before the SetRow function")

stream.go

+56-18
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ type StreamWriter struct {
2929
Sheet string
3030
SheetID int
3131
sheetWritten bool
32-
cols strings.Builder
3332
worksheet *xlsxWorksheet
3433
rawData bufferedWriter
3534
rows int
@@ -413,16 +412,18 @@ func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpt
413412
if err != nil {
414413
return err
415414
}
416-
c := xlsxC{R: ref, S: options.StyleID}
415+
c := xlsxC{R: ref, S: sw.worksheet.prepareCellStyle(col, row, options.StyleID)}
416+
var s int
417417
if v, ok := val.(Cell); ok {
418-
c.S = v.StyleID
419-
val = v.Value
418+
s, val = v.StyleID, v.Value
420419
setCellFormula(&c, v.Formula)
421420
} else if v, ok := val.(*Cell); ok && v != nil {
422-
c.S = v.StyleID
423-
val = v.Value
421+
s, val = v.StyleID, v.Value
424422
setCellFormula(&c, v.Formula)
425423
}
424+
if s > 0 {
425+
c.S = s
426+
}
426427
if err = sw.setCellValFunc(&c, val); err != nil {
427428
_, _ = sw.rawData.WriteString(`</row>`)
428429
return err
@@ -433,6 +434,33 @@ func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpt
433434
return sw.rawData.Sync()
434435
}
435436

437+
// SetColStyle provides a function to set the style of a single column or
438+
// multiple columns for the StreamWriter. Note that you must call
439+
// the 'SetColStyle' function before the 'SetRow' function. For example set
440+
// style of column H on Sheet1:
441+
//
442+
// err := sw.SetColStyle(8, 8, style)
443+
func (sw *StreamWriter) SetColStyle(minVal, maxVal, styleID int) error {
444+
if sw.sheetWritten {
445+
return ErrStreamSetColStyle
446+
}
447+
if minVal < MinColumns || minVal > MaxColumns || maxVal < MinColumns || maxVal > MaxColumns {
448+
return ErrColumnNumber
449+
}
450+
if maxVal < minVal {
451+
minVal, maxVal = maxVal, minVal
452+
}
453+
s, err := sw.file.stylesReader()
454+
if err != nil {
455+
return err
456+
}
457+
if styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {
458+
return newInvalidStyleID(styleID)
459+
}
460+
sw.worksheet.setColStyle(minVal, maxVal, styleID)
461+
return nil
462+
}
463+
436464
// SetColWidth provides a function to set the width of a single column or
437465
// multiple columns for the StreamWriter. Note that you must call
438466
// the 'SetColWidth' function before the 'SetRow' function. For example set
@@ -452,14 +480,7 @@ func (sw *StreamWriter) SetColWidth(minVal, maxVal int, width float64) error {
452480
if minVal > maxVal {
453481
minVal, maxVal = maxVal, minVal
454482
}
455-
456-
sw.cols.WriteString(`<col min="`)
457-
sw.cols.WriteString(strconv.Itoa(minVal))
458-
sw.cols.WriteString(`" max="`)
459-
sw.cols.WriteString(strconv.Itoa(maxVal))
460-
sw.cols.WriteString(`" width="`)
461-
sw.cols.WriteString(strconv.FormatFloat(width, 'f', -1, 64))
462-
sw.cols.WriteString(`" customWidth="1"/>`)
483+
sw.worksheet.setColWidth(minVal, maxVal, width)
463484
return nil
464485
}
465486

@@ -642,10 +663,27 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
642663
func (sw *StreamWriter) writeSheetData() {
643664
if !sw.sheetWritten {
644665
bulkAppendFields(&sw.rawData, sw.worksheet, 4, 5)
645-
if sw.cols.Len() > 0 {
646-
_, _ = sw.rawData.WriteString("<cols>")
647-
_, _ = sw.rawData.WriteString(sw.cols.String())
648-
_, _ = sw.rawData.WriteString("</cols>")
666+
if sw.worksheet.Cols != nil {
667+
for _, col := range sw.worksheet.Cols.Col {
668+
_, _ = sw.rawData.WriteString("<cols>")
669+
sw.rawData.WriteString(`<col min="`)
670+
sw.rawData.WriteString(strconv.Itoa(col.Min))
671+
sw.rawData.WriteString(`" max="`)
672+
sw.rawData.WriteString(strconv.Itoa(col.Max))
673+
sw.rawData.WriteString(`"`)
674+
if col.Width != nil {
675+
sw.rawData.WriteString(` width="`)
676+
sw.rawData.WriteString(strconv.FormatFloat(*col.Width, 'f', -1, 64))
677+
sw.rawData.WriteString(`" customWidth="1"`)
678+
}
679+
if col.Style != 0 {
680+
sw.rawData.WriteString(` style="`)
681+
sw.rawData.WriteString(strconv.Itoa(col.Style))
682+
sw.rawData.WriteString(`"`)
683+
}
684+
sw.rawData.WriteString(`/>`)
685+
_, _ = sw.rawData.WriteString("</cols>")
686+
}
649687
}
650688
_, _ = sw.rawData.WriteString(`<sheetData>`)
651689
sw.sheetWritten = true

stream_test.go

+37-4
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,53 @@ func TestStreamWriter(t *testing.T) {
154154
assert.NoError(t, file.Close())
155155
}
156156

157+
func TestStreamSetColStyle(t *testing.T) {
158+
file := NewFile()
159+
defer func() {
160+
assert.NoError(t, file.Close())
161+
}()
162+
streamWriter, err := file.NewStreamWriter("Sheet1")
163+
assert.NoError(t, err)
164+
assert.NoError(t, streamWriter.SetColStyle(3, 2, 0))
165+
assert.Equal(t, ErrColumnNumber, streamWriter.SetColStyle(0, 3, 20))
166+
assert.Equal(t, ErrColumnNumber, streamWriter.SetColStyle(MaxColumns+1, 3, 20))
167+
assert.Equal(t, newInvalidStyleID(2), streamWriter.SetColStyle(1, 3, 2))
168+
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
169+
assert.Equal(t, ErrStreamSetColStyle, streamWriter.SetColStyle(2, 3, 0))
170+
171+
file = NewFile()
172+
defer func() {
173+
assert.NoError(t, file.Close())
174+
}()
175+
// Test set column style with unsupported charset style sheet
176+
file.Styles = nil
177+
file.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
178+
streamWriter, err = file.NewStreamWriter("Sheet1")
179+
assert.NoError(t, err)
180+
assert.EqualError(t, streamWriter.SetColStyle(3, 2, 0), "XML syntax error on line 1: invalid UTF-8")
181+
}
182+
157183
func TestStreamSetColWidth(t *testing.T) {
158184
file := NewFile()
159185
defer func() {
160186
assert.NoError(t, file.Close())
161187
}()
188+
styleID, err := file.NewStyle(&Style{
189+
Fill: Fill{Type: "pattern", Color: []string{"E0EBF5"}, Pattern: 1},
190+
})
191+
if err != nil {
192+
fmt.Println(err)
193+
}
162194
streamWriter, err := file.NewStreamWriter("Sheet1")
163195
assert.NoError(t, err)
164196
assert.NoError(t, streamWriter.SetColWidth(3, 2, 20))
197+
assert.NoError(t, streamWriter.SetColStyle(3, 2, styleID))
165198
assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(0, 3, 20))
166199
assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(MaxColumns+1, 3, 20))
167200
assert.Equal(t, ErrColumnWidth, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1))
168201
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
169202
assert.Equal(t, ErrStreamSetColWidth, streamWriter.SetColWidth(2, 3, 20))
203+
assert.NoError(t, streamWriter.Flush())
170204
}
171205

172206
func TestStreamSetPanes(t *testing.T) {
@@ -323,7 +357,6 @@ func TestStreamSetRowWithStyle(t *testing.T) {
323357
defer func() {
324358
assert.NoError(t, file.Close())
325359
}()
326-
zeroStyleID := 0
327360
grayStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "777777"}})
328361
assert.NoError(t, err)
329362
blueStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "0000FF"}})
@@ -342,7 +375,7 @@ func TestStreamSetRowWithStyle(t *testing.T) {
342375

343376
ws, err := file.workSheetReader("Sheet1")
344377
assert.NoError(t, err)
345-
for colIdx, expected := range []int{grayStyleID, zeroStyleID, zeroStyleID, blueStyleID, blueStyleID} {
378+
for colIdx, expected := range []int{grayStyleID, grayStyleID, grayStyleID, blueStyleID, blueStyleID} {
346379
assert.Equal(t, expected, ws.SheetData.Row[0].C[colIdx].S)
347380
}
348381
}
@@ -381,8 +414,8 @@ func TestStreamSetCellValFunc(t *testing.T) {
381414
}
382415

383416
func TestSetCellIntFunc(t *testing.T) {
384-
cases := []struct{
385-
val interface{}
417+
cases := []struct {
418+
val interface{}
386419
target string
387420
}{
388421
{val: 128, target: "128"},

0 commit comments

Comments
 (0)