Forráskód Böngészése

This closes #652, new SetColWidth API, support set column width in stream writing mode, and export error message

xuri 4 éve
szülő
commit
be12cc27f1
24 módosított fájl, 141 hozzáadás és 55 törlés
  1. 2 3
      adjust.go
  2. 2 2
      adjust_test.go
  3. 4 4
      calc.go
  4. 6 6
      calc_test.go
  5. 1 1
      cell.go
  6. 1 1
      chart.go
  7. 1 1
      chart_test.go
  8. 2 3
      col.go
  9. 3 3
      col_test.go
  10. 1 2
      date.go
  11. 1 1
      date_test.go
  12. 45 1
      errors.go
  13. 1 2
      excelize.go
  14. 5 5
      excelize_test.go
  15. 1 2
      file.go
  16. 2 2
      lib.go
  17. 2 2
      lib_test.go
  18. 2 3
      picture.go
  19. 2 2
      picture_test.go
  20. 2 3
      rows.go
  21. 1 1
      rows_test.go
  22. 1 1
      sheet.go
  23. 37 3
      stream.go
  24. 16 1
      stream_test.go

+ 2 - 3
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 {

+ 2 - 2
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())
 }

+ 4 - 4
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)

+ 6 - 6
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

+ 1 - 1
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 {

+ 1 - 1
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 {

+ 1 - 1
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")
 

+ 2 - 3
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

+ 3 - 3
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.

+ 1 - 2
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) {

+ 1 - 1
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())
 		})
 	}
 }

+ 45 - 1
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")
+)

+ 1 - 2
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())

+ 5 - 5
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")))

+ 1 - 2
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 {

+ 2 - 2
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 {

+ 2 - 2
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) {

+ 2 - 3
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 {

+ 2 - 2
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))

+ 2 - 3
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 {

+ 1 - 1
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)

+ 1 - 1
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)

+ 37 - 3
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 + `<worksheet` + templateNamespaceIDMap)
-	bulkAppendFields(&sw.rawData, sw.worksheet, 2, 6)
-	_, _ = sw.rawData.WriteString(`<sheetData>`)
+	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("<cols>" + sw.cols + "</cols>")
+		}
+		_, _ = sw.rawData.WriteString(`<sheetData>`)
+		sw.sheetWritten = true
+	}
 	fmt.Fprintf(&sw.rawData, `<row r="%d">`, 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(`<col min="%d" max="%d" width="%f" customWidth="1"/>`, 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.

+ 16 - 1
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")