From be12cc27f1d774154b17763c071e1dc6f91eab8c Mon Sep 17 00:00:00 2001 From: xuri Date: Mon, 10 May 2021 00:09:24 +0800 Subject: [PATCH] This closes #652, new SetColWidth API, support set column width in stream writing mode, and export error message --- adjust.go | 5 ++--- adjust_test.go | 4 ++-- calc.go | 8 ++++---- calc_test.go | 12 ++++++------ cell.go | 2 +- chart.go | 2 +- chart_test.go | 2 +- col.go | 5 ++--- col_test.go | 6 +++--- date.go | 3 +-- date_test.go | 2 +- errors.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- excelize.go | 3 +-- excelize_test.go | 10 +++++----- file.go | 3 +-- lib.go | 4 ++-- lib_test.go | 4 ++-- picture.go | 5 ++--- picture_test.go | 4 ++-- rows.go | 5 ++--- rows_test.go | 2 +- sheet.go | 2 +- stream.go | 40 +++++++++++++++++++++++++++++++++++++--- stream_test.go | 17 ++++++++++++++++- 24 files changed, 141 insertions(+), 55 deletions(-) diff --git a/adjust.go b/adjust.go index e06d4f6..28b62cc 100644 --- a/adjust.go +++ b/adjust.go @@ -12,7 +12,6 @@ package excelize import ( - "errors" "strings" ) @@ -219,7 +218,7 @@ func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) { // correct C1:B3 to B1:C3. func sortCoordinates(coordinates []int) error { if len(coordinates) != 4 { - return errors.New("coordinates length must be 4") + return ErrCoordinates } if coordinates[2] < coordinates[0] { coordinates[2], coordinates[0] = coordinates[0], coordinates[2] @@ -234,7 +233,7 @@ func sortCoordinates(coordinates []int) error { // to area reference. func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) { if len(coordinates) != 4 { - return "", errors.New("coordinates length must be 4") + return "", ErrCoordinates } firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1]) if err != nil { diff --git a/adjust_test.go b/adjust_test.go index bdbaebe..0d63ed6 100644 --- a/adjust_test.go +++ b/adjust_test.go @@ -113,7 +113,7 @@ func TestAdjustCalcChain(t *testing.T) { func TestCoordinatesToAreaRef(t *testing.T) { f := NewFile() _, err := f.coordinatesToAreaRef([]int{}) - assert.EqualError(t, err, "coordinates length must be 4") + assert.EqualError(t, err, ErrCoordinates.Error()) _, err = f.coordinatesToAreaRef([]int{1, -1, 1, 1}) assert.EqualError(t, err, "invalid cell coordinates [1, -1]") _, err = f.coordinatesToAreaRef([]int{1, 1, 1, -1}) @@ -124,5 +124,5 @@ func TestCoordinatesToAreaRef(t *testing.T) { } func TestSortCoordinates(t *testing.T) { - assert.EqualError(t, sortCoordinates(make([]int, 3)), "coordinates length must be 4") + assert.EqualError(t, sortCoordinates(make([]int, 3)), ErrCoordinates.Error()) } diff --git a/calc.go b/calc.go index b65a3c4..573abf2 100644 --- a/calc.go +++ b/calc.go @@ -647,7 +647,7 @@ func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (efp.Token, optStack.Pop() } if opdStack.Len() == 0 { - return efp.Token{}, errors.New("formula not valid") + return efp.Token{}, ErrInvalidFormula } return opdStack.Peek().(efp.Token), err } @@ -849,7 +849,7 @@ func calcDiv(rOpd, lOpd string, opdStack *Stack) error { func calculate(opdStack *Stack, opt efp.Token) error { if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorPrefix { if opdStack.Len() < 1 { - return errors.New("formula not valid") + return ErrInvalidFormula } opd := opdStack.Pop().(efp.Token) opdVal, err := strconv.ParseFloat(opd.TValue, 64) @@ -874,7 +874,7 @@ func calculate(opdStack *Stack, opt efp.Token) error { } if opt.TValue == "-" && opt.TType == efp.TokenTypeOperatorInfix { if opdStack.Len() < 2 { - return errors.New("formula not valid") + return ErrInvalidFormula } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) @@ -885,7 +885,7 @@ func calculate(opdStack *Stack, opt efp.Token) error { fn, ok := tokenCalcFunc[opt.TValue] if ok { if opdStack.Len() < 2 { - return errors.New("formula not valid") + return ErrInvalidFormula } rOpd := opdStack.Pop().(efp.Token) lOpd := opdStack.Pop().(efp.Token) diff --git a/calc_test.go b/calc_test.go index d5d5439..23568ff 100644 --- a/calc_test.go +++ b/calc_test.go @@ -1710,12 +1710,12 @@ func TestCalcCellValue(t *testing.T) { "=POISSON(0,0,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax", "=POISSON(0,-1,TRUE)": "#N/A", // SUM - "=SUM((": "formula not valid", - "=SUM(-)": "formula not valid", - "=SUM(1+)": "formula not valid", - "=SUM(1-)": "formula not valid", - "=SUM(1*)": "formula not valid", - "=SUM(1/)": "formula not valid", + "=SUM((": ErrInvalidFormula.Error(), + "=SUM(-)": ErrInvalidFormula.Error(), + "=SUM(1+)": ErrInvalidFormula.Error(), + "=SUM(1-)": ErrInvalidFormula.Error(), + "=SUM(1*)": ErrInvalidFormula.Error(), + "=SUM(1/)": ErrInvalidFormula.Error(), // SUMIF "=SUMIF()": "SUMIF requires at least 2 argument", // SUMSQ diff --git a/cell.go b/cell.go index 27d24d9..f94b81e 100644 --- a/cell.go +++ b/cell.go @@ -475,7 +475,7 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype } if len(ws.Hyperlinks.Hyperlink) > TotalSheetHyperlinks { - return errors.New("over maximum limit hyperlinks in a worksheet") + return ErrTotalSheetHyperlinks } switch linkType { diff --git a/chart.go b/chart.go index 3ac460b..23ea776 100644 --- a/chart.go +++ b/chart.go @@ -919,7 +919,7 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error { func (f *File) AddChartSheet(sheet, format string, combo ...string) error { // Check if the worksheet already exists if f.GetSheetIndex(sheet) != -1 { - return errors.New("the same name worksheet already exists") + return ErrExistsWorksheet } formatSet, comboCharts, err := f.getFormatChart(format, combo) if err != nil { diff --git a/chart_test.go b/chart_test.go index 6ee0a0f..657230b 100644 --- a/chart_test.go +++ b/chart_test.go @@ -232,7 +232,7 @@ func TestAddChartSheet(t *testing.T) { // Test cell value on chartsheet assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is chart sheet") // Test add chartsheet on already existing name sheet - assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "the same name worksheet already exists") + assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), ErrExistsWorksheet.Error()) // Test with unsupported chart type assert.EqualError(t, f.AddChartSheet("Chart2", `{"type":"unknown","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "unsupported chart type unknown") diff --git a/col.go b/col.go index 09a172a..e1ac5a5 100644 --- a/col.go +++ b/col.go @@ -14,7 +14,6 @@ package excelize import ( "bytes" "encoding/xml" - "errors" "math" "strconv" "strings" @@ -360,7 +359,7 @@ func (f *File) parseColRange(columns string) (start, end int, err error) { // func (f *File) SetColOutlineLevel(sheet, col string, level uint8) error { if level > 7 || level < 1 { - return errors.New("invalid outline level") + return ErrOutlineLevel } colNum, err := ColumnNameToNumber(col) if err != nil { @@ -452,7 +451,7 @@ func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) error return err } if width > MaxColumnWidth { - return errors.New("the width of the column must be smaller than or equal to 255 characters") + return ErrColumnWidth } if min > max { min, max = max, min diff --git a/col_test.go b/col_test.go index add1c11..6ab5e57 100644 --- a/col_test.go +++ b/col_test.go @@ -246,12 +246,12 @@ func TestOutlineLevel(t *testing.T) { assert.EqualError(t, err, "sheet Shee2 is not exist") assert.NoError(t, f.SetColWidth("Sheet2", "A", "D", 13)) - assert.EqualError(t, f.SetColWidth("Sheet2", "A", "D", MaxColumnWidth+1), "the width of the column must be smaller than or equal to 255 characters") + assert.EqualError(t, f.SetColWidth("Sheet2", "A", "D", MaxColumnWidth+1), ErrColumnWidth.Error()) assert.NoError(t, f.SetColOutlineLevel("Sheet2", "B", 2)) assert.NoError(t, f.SetRowOutlineLevel("Sheet1", 2, 7)) - assert.EqualError(t, f.SetColOutlineLevel("Sheet1", "D", 8), "invalid outline level") - assert.EqualError(t, f.SetRowOutlineLevel("Sheet1", 2, 8), "invalid outline level") + assert.EqualError(t, f.SetColOutlineLevel("Sheet1", "D", 8), ErrOutlineLevel.Error()) + assert.EqualError(t, f.SetRowOutlineLevel("Sheet1", 2, 8), ErrOutlineLevel.Error()) // Test set row outline level on not exists worksheet. assert.EqualError(t, f.SetRowOutlineLevel("SheetN", 1, 4), "sheet SheetN is not exist") // Test get row outline level on not exists worksheet. diff --git a/date.go b/date.go index 3e50e3d..9ef5caf 100644 --- a/date.go +++ b/date.go @@ -12,7 +12,6 @@ package excelize import ( - "errors" "math" "time" ) @@ -35,7 +34,7 @@ func timeToExcelTime(t time.Time) (float64, error) { // 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 { - return 0.0, errors.New("only UTC time expected") + return 0.0, ErrToExcelTime } if t.Before(excelMinTime1900) { diff --git a/date_test.go b/date_test.go index 79462c5..ecc96ba 100644 --- a/date_test.go +++ b/date_test.go @@ -55,7 +55,7 @@ func TestTimeToExcelTime_Timezone(t *testing.T) { for i, test := range trueExpectedDateList { t.Run(fmt.Sprintf("TestData%d", i+1), func(t *testing.T) { _, err := timeToExcelTime(test.GoValue.In(location)) - assert.EqualError(t, err, "only UTC time expected") + assert.EqualError(t, err, ErrToExcelTime.Error()) }) } } diff --git a/errors.go b/errors.go index 0ab2642..a0c61c8 100644 --- a/errors.go +++ b/errors.go @@ -11,7 +11,10 @@ package excelize -import "fmt" +import ( + "errors" + "fmt" +) func newInvalidColumnNameError(col string) error { return fmt.Errorf("invalid column name %q", col) @@ -28,3 +31,44 @@ func newInvalidCellNameError(cell string) error { func newInvalidExcelDateError(dateValue float64) error { return fmt.Errorf("invalid date value %f, negative values are not supported supported", dateValue) } + +var ( + // ErrStreamSetColWidth defined the error message on set column width in + // stream writing mode. + ErrStreamSetColWidth = errors.New("must call the SetColWidth function before the SetRow function") + // ErrColumnNumber defined the error message on receive an invalid column + // number. + ErrColumnNumber = errors.New("column number exceeds maximum limit") + // ErrColumnWidth defined the error message on receive an invalid column + // width. + ErrColumnWidth = errors.New("the width of the column must be smaller than or equal to 255 characters") + // ErrOutlineLevel defined the error message on receive an invalid outline + // level number. + ErrOutlineLevel = errors.New("invalid outline level") + // ErrCoordinates defined the error message on invalid coordinates tuples + // length. + ErrCoordinates = errors.New("coordinates length must be 4") + // ErrExistsWorksheet defined the error message on given worksheet already + // exists. + ErrExistsWorksheet = errors.New("the same name worksheet already exists") + // ErrTotalSheetHyperlinks defined the error message on hyperlinks count + // overflow. + ErrTotalSheetHyperlinks = errors.New("over maximum limit hyperlinks in a worksheet") + // ErrInvalidFormula defined the error message on receive an invalid + // formula. + ErrInvalidFormula = errors.New("formula not valid") + // ErrAddVBAProject defined the error message on add the VBA project in + // the workbook. + ErrAddVBAProject = errors.New("unsupported VBA project extension") + // ErrToExcelTime defined the error message on receive a not UTC time. + ErrToExcelTime = errors.New("only UTC time expected") + // ErrMaxRowHeight defined the error message on receive an invalid row + // height. + ErrMaxRowHeight = errors.New("the height of the row must be smaller than or equal to 409 points") + // ErrImgExt defined the error message on receive an unsupported image + // extension. + ErrImgExt = errors.New("unsupported image extension") + // ErrMaxFileNameLength defined the error message on receive the file name + // length overflow. + ErrMaxFileNameLength = errors.New("file name length exceeds maximum limit") +) diff --git a/excelize.go b/excelize.go index 6b3d406..940acf1 100644 --- a/excelize.go +++ b/excelize.go @@ -16,7 +16,6 @@ import ( "archive/zip" "bytes" "encoding/xml" - "errors" "fmt" "io" "io/ioutil" @@ -351,7 +350,7 @@ func (f *File) AddVBAProject(bin string) error { return fmt.Errorf("stat %s: no such file or directory", bin) } if path.Ext(bin) != ".bin" { - return errors.New("unsupported VBA project extension") + return ErrAddVBAProject } f.setContentTypePartVBAProjectExtensions() wb := f.relsReader(f.getWorkbookRelsPath()) diff --git a/excelize_test.go b/excelize_test.go index 0dcfacb..0c42178 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -144,7 +144,7 @@ func TestOpenFile(t *testing.T) { assert.NoError(t, f.SetCellValue("Sheet2", "G2", nil)) - assert.EqualError(t, f.SetCellValue("Sheet2", "G4", time.Now()), "only UTC time expected") + assert.EqualError(t, f.SetCellValue("Sheet2", "G4", time.Now()), ErrToExcelTime.Error()) assert.NoError(t, f.SetCellValue("Sheet2", "G4", time.Now().UTC())) // 02:46:40 @@ -166,7 +166,7 @@ func TestOpenFile(t *testing.T) { assert.NoError(t, f.SetCellStr("Sheet2", "c"+strconv.Itoa(i), strconv.Itoa(i))) } assert.NoError(t, f.SaveAs(filepath.Join("test", "TestOpenFile.xlsx"))) - assert.EqualError(t, f.SaveAs(filepath.Join("test", strings.Repeat("c", 199), ".xlsx")), "file name length exceeds maximum limit") + assert.EqualError(t, f.SaveAs(filepath.Join("test", strings.Repeat("c", 199), ".xlsx")), ErrMaxFileNameLength.Error()) } func TestSaveFile(t *testing.T) { @@ -344,7 +344,7 @@ func TestSetCellHyperLink(t *testing.T) { _, err = f.workSheetReader("Sheet1") assert.NoError(t, err) f.Sheet["xl/worksheets/sheet1.xml"].Hyperlinks = &xlsxHyperlinks{Hyperlink: make([]xlsxHyperlink, 65530)} - assert.EqualError(t, f.SetCellHyperLink("Sheet1", "A65531", "https://github.com/360EntSecGroup-Skylar/excelize", "External"), "over maximum limit hyperlinks in a worksheet") + assert.EqualError(t, f.SetCellHyperLink("Sheet1", "A65531", "https://github.com/360EntSecGroup-Skylar/excelize", "External"), ErrTotalSheetHyperlinks.Error()) f = NewFile() _, err = f.workSheetReader("Sheet1") @@ -449,7 +449,7 @@ func TestSetSheetBackgroundErrors(t *testing.T) { } err = f.SetSheetBackground("Sheet2", filepath.Join("test", "Book1.xlsx")) - assert.EqualError(t, err, "unsupported image extension") + assert.EqualError(t, err, ErrImgExt.Error()) } // TestWriteArrayFormula tests the extended options of SetCellFormula by writing an array function @@ -1187,7 +1187,7 @@ func TestAddVBAProject(t *testing.T) { f := NewFile() assert.NoError(t, f.SetSheetPrOptions("Sheet1", CodeName("Sheet1"))) assert.EqualError(t, f.AddVBAProject("macros.bin"), "stat macros.bin: no such file or directory") - assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "Book1.xlsx")), "unsupported VBA project extension") + assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "Book1.xlsx")), ErrAddVBAProject.Error()) assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin"))) // Test add VBA project twice. assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin"))) diff --git a/file.go b/file.go index 8a37aef..36b1a42 100644 --- a/file.go +++ b/file.go @@ -14,7 +14,6 @@ package excelize import ( "archive/zip" "bytes" - "errors" "fmt" "io" "os" @@ -66,7 +65,7 @@ func (f *File) Save() error { // provided path. func (f *File) SaveAs(name string, opt ...Options) error { if len(name) > MaxFileNameLength { - return errors.New("file name length exceeds maximum limit") + return ErrMaxFileNameLength } file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666) if err != nil { diff --git a/lib.go b/lib.go index 3a9e807..99083b1 100644 --- a/lib.go +++ b/lib.go @@ -149,7 +149,7 @@ func ColumnNameToNumber(name string) (int, error) { multi *= 26 } if col > TotalColumns { - return -1, fmt.Errorf("column number exceeds maximum limit") + return -1, ErrColumnNumber } return col, nil } @@ -166,7 +166,7 @@ func ColumnNumberToName(num int) (string, error) { return "", fmt.Errorf("incorrect column number %d", num) } if num > TotalColumns { - return "", fmt.Errorf("column number exceeds maximum limit") + return "", ErrColumnNumber } var col string for num > 0 { diff --git a/lib_test.go b/lib_test.go index 10d7c3a..eb0a289 100644 --- a/lib_test.go +++ b/lib_test.go @@ -73,7 +73,7 @@ func TestColumnNameToNumber_Error(t *testing.T) { } } _, err := ColumnNameToNumber("XFE") - assert.EqualError(t, err, "column number exceeds maximum limit") + assert.EqualError(t, err, ErrColumnNumber.Error()) } func TestColumnNumberToName_OK(t *testing.T) { @@ -98,7 +98,7 @@ func TestColumnNumberToName_Error(t *testing.T) { } _, err = ColumnNumberToName(TotalColumns + 1) - assert.EqualError(t, err, "column number exceeds maximum limit") + assert.EqualError(t, err, ErrColumnNumber.Error()) } func TestSplitCellName_OK(t *testing.T) { diff --git a/picture.go b/picture.go index de7e0f8..92e0106 100644 --- a/picture.go +++ b/picture.go @@ -15,7 +15,6 @@ import ( "bytes" "encoding/json" "encoding/xml" - "errors" "fmt" "image" "io" @@ -93,7 +92,7 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error { } ext, ok := supportImageTypes[path.Ext(picture)] if !ok { - return errors.New("unsupported image extension") + return ErrImgExt } file, _ := ioutil.ReadFile(picture) _, name := filepath.Split(picture) @@ -134,7 +133,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, var hyperlinkType string ext, ok := supportImageTypes[extension] if !ok { - return errors.New("unsupported image extension") + return ErrImgExt } formatSet, err := parseFormatPictureSet(format) if err != nil { diff --git a/picture_test.go b/picture_test.go index 28d8aa8..be917b8 100644 --- a/picture_test.go +++ b/picture_test.go @@ -82,10 +82,10 @@ func TestAddPictureErrors(t *testing.T) { // Test add picture to worksheet with unsupported file type. err = xlsx.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), "") - assert.EqualError(t, err, "unsupported image extension") + assert.EqualError(t, err, ErrImgExt.Error()) err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1)) - assert.EqualError(t, err, "unsupported image extension") + assert.EqualError(t, err, ErrImgExt.Error()) // Test add picture to worksheet with invalid file data. err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1)) diff --git a/rows.go b/rows.go index 76a8f67..451dbe9 100644 --- a/rows.go +++ b/rows.go @@ -14,7 +14,6 @@ package excelize import ( "bytes" "encoding/xml" - "errors" "fmt" "io" "log" @@ -245,7 +244,7 @@ func (f *File) SetRowHeight(sheet string, row int, height float64) error { return newInvalidRowNumberError(row) } if height > MaxRowHeight { - return errors.New("the height of the row must be smaller than or equal to 409 points") + return ErrMaxRowHeight } ws, err := f.workSheetReader(sheet) if err != nil { @@ -436,7 +435,7 @@ func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error { return newInvalidRowNumberError(row) } if level > 7 || level < 1 { - return errors.New("invalid outline level") + return ErrOutlineLevel } ws, err := f.workSheetReader(sheet) if err != nil { diff --git a/rows_test.go b/rows_test.go index 0180498..5e7fd37 100644 --- a/rows_test.go +++ b/rows_test.go @@ -109,7 +109,7 @@ func TestRowHeight(t *testing.T) { assert.Equal(t, 111.0, height) // Test set row height overflow max row height limit. - assert.EqualError(t, f.SetRowHeight(sheet1, 4, MaxRowHeight+1), "the height of the row must be smaller than or equal to 409 points") + assert.EqualError(t, f.SetRowHeight(sheet1, 4, MaxRowHeight+1), ErrMaxRowHeight.Error()) // Test get row height that rows index over exists rows. height, err = f.GetRowHeight(sheet1, 5) diff --git a/sheet.go b/sheet.go index 7541f43..97ef57e 100644 --- a/sheet.go +++ b/sheet.go @@ -482,7 +482,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error { } ext, ok := supportImageTypes[path.Ext(picture)] if !ok { - return errors.New("unsupported image extension") + return ErrImgExt } file, _ := ioutil.ReadFile(picture) name := f.addMedia(file, ext) diff --git a/stream.go b/stream.go index 57bf4a2..f12b201 100644 --- a/stream.go +++ b/stream.go @@ -29,6 +29,8 @@ type StreamWriter struct { File *File Sheet string SheetID int + sheetWritten bool + cols string worksheet *xlsxWorksheet rawData bufferedWriter mergeCellsCount int @@ -104,8 +106,7 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) { f.streams[sheetXML] = sw _, _ = sw.rawData.WriteString(XMLHeader + ``) + bulkAppendFields(&sw.rawData, sw.worksheet, 2, 5) return sw, err } @@ -298,7 +299,13 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}) error { if err != nil { return err } - + if !sw.sheetWritten { + if len(sw.cols) > 0 { + sw.rawData.WriteString("" + sw.cols + "") + } + _, _ = sw.rawData.WriteString(``) + sw.sheetWritten = true + } fmt.Fprintf(&sw.rawData, ``, row) for i, val := range values { axis, err := CoordinatesToCellName(col+i, row) @@ -325,6 +332,33 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}) error { return sw.rawData.Sync() } +// SetColWidth provides a function to set the width of a single column or +// multiple columns for the the StreamWriter. Note that you must call +// the 'SetColWidth' function before the 'SetRow' function. For example set +// the width column B:C as 20: +// +// err := streamWriter.SetColWidth(2, 3, 20) +// +func (sw *StreamWriter) SetColWidth(min, max int, width float64) error { + if sw.sheetWritten { + return ErrStreamSetColWidth + } + if min > TotalColumns || max > TotalColumns { + return ErrColumnNumber + } + if min < 1 || max < 1 { + return ErrColumnNumber + } + if width > MaxColumnWidth { + return ErrColumnWidth + } + if min > max { + min, max = max, min + } + sw.cols += fmt.Sprintf(``, min, max, width) + return nil +} + // MergeCell provides a function to merge cells by a given coordinate area for // the StreamWriter. Don't create a merged cell that overlaps with another // existing merged cell. diff --git a/stream_test.go b/stream_test.go index d36883a..0834a2d 100644 --- a/stream_test.go +++ b/stream_test.go @@ -57,7 +57,7 @@ func TestStreamWriter(t *testing.T) { assert.NoError(t, err) assert.NoError(t, streamWriter.SetRow("A4", []interface{}{Cell{StyleID: styleID}, Cell{Formula: "SUM(A10,B10)"}})) assert.NoError(t, streamWriter.SetRow("A5", []interface{}{&Cell{StyleID: styleID, Value: "cell"}, &Cell{Formula: "SUM(A10,B10)"}})) - assert.EqualError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}), "only UTC time expected") + assert.EqualError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}), ErrToExcelTime.Error()) for rowID := 10; rowID <= 51200; rowID++ { row := make([]interface{}, 50) @@ -68,6 +68,9 @@ func TestStreamWriter(t *testing.T) { assert.NoError(t, streamWriter.SetRow(cell, row)) } + // Test set cell column overflow. + assert.EqualError(t, streamWriter.SetRow("XFD1", []interface{}{"A", "B", "C"}), ErrColumnNumber.Error()) + assert.NoError(t, streamWriter.Flush()) // Save spreadsheet by the given path. assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriter.xlsx"))) @@ -112,6 +115,18 @@ func TestStreamWriter(t *testing.T) { assert.Equal(t, "Data", cellValue) } +func TestStreamSetColWidth(t *testing.T) { + file := NewFile() + streamWriter, err := file.NewStreamWriter("Sheet1") + assert.NoError(t, err) + assert.NoError(t, streamWriter.SetColWidth(3, 2, 20)) + assert.EqualError(t, streamWriter.SetColWidth(0, 3, 20), ErrColumnNumber.Error()) + assert.EqualError(t, streamWriter.SetColWidth(TotalColumns+1, 3, 20), ErrColumnNumber.Error()) + assert.EqualError(t, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1), ErrColumnWidth.Error()) + assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"})) + assert.EqualError(t, streamWriter.SetColWidth(2, 3, 20), ErrStreamSetColWidth.Error()) +} + func TestStreamTable(t *testing.T) { file := NewFile() streamWriter, err := file.NewStreamWriter("Sheet1")