250 changes: 124 additions & 126 deletions sheetpr_test.go

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions sheetview.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,21 +140,21 @@ func (o *ZoomScale) getSheetViewOption(view *xlsxSheetView) {
}

// getSheetView returns the SheetView object
func (f *File) getSheetView(sheetName string, viewIndex int) (*xlsxSheetView, error) {
xlsx, err := f.workSheetReader(sheetName)
func (f *File) getSheetView(sheet string, viewIndex int) (*xlsxSheetView, error) {
ws, err := f.workSheetReader(sheet)
if err != nil {
return nil, err
}
if viewIndex < 0 {
if viewIndex < -len(xlsx.SheetViews.SheetView) {
if viewIndex < -len(ws.SheetViews.SheetView) {
return nil, fmt.Errorf("view index %d out of range", viewIndex)
}
viewIndex = len(xlsx.SheetViews.SheetView) + viewIndex
} else if viewIndex >= len(xlsx.SheetViews.SheetView) {
viewIndex = len(ws.SheetViews.SheetView) + viewIndex
} else if viewIndex >= len(ws.SheetViews.SheetView) {
return nil, fmt.Errorf("view index %d out of range", viewIndex)
}

return &(xlsx.SheetViews.SheetView[viewIndex]), err
return &(ws.SheetViews.SheetView[viewIndex]), err
}

// SetSheetViewOptions sets sheet view options. The viewIndex may be negative
Expand Down
92 changes: 45 additions & 47 deletions sheetview_test.go
Original file line number Diff line number Diff line change
@@ -1,60 +1,58 @@
package excelize_test
package excelize

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

"github.com/360EntSecGroup-Skylar/excelize/v2"
)

var _ = []excelize.SheetViewOption{
excelize.DefaultGridColor(true),
excelize.RightToLeft(false),
excelize.ShowFormulas(false),
excelize.ShowGridLines(true),
excelize.ShowRowColHeaders(true),
excelize.TopLeftCell("B2"),
var _ = []SheetViewOption{
DefaultGridColor(true),
RightToLeft(false),
ShowFormulas(false),
ShowGridLines(true),
ShowRowColHeaders(true),
TopLeftCell("B2"),
// SheetViewOptionPtr are also SheetViewOption
new(excelize.DefaultGridColor),
new(excelize.RightToLeft),
new(excelize.ShowFormulas),
new(excelize.ShowGridLines),
new(excelize.ShowRowColHeaders),
new(excelize.TopLeftCell),
new(DefaultGridColor),
new(RightToLeft),
new(ShowFormulas),
new(ShowGridLines),
new(ShowRowColHeaders),
new(TopLeftCell),
}

var _ = []excelize.SheetViewOptionPtr{
(*excelize.DefaultGridColor)(nil),
(*excelize.RightToLeft)(nil),
(*excelize.ShowFormulas)(nil),
(*excelize.ShowGridLines)(nil),
(*excelize.ShowRowColHeaders)(nil),
(*excelize.TopLeftCell)(nil),
var _ = []SheetViewOptionPtr{
(*DefaultGridColor)(nil),
(*RightToLeft)(nil),
(*ShowFormulas)(nil),
(*ShowGridLines)(nil),
(*ShowRowColHeaders)(nil),
(*TopLeftCell)(nil),
}

func ExampleFile_SetSheetViewOptions() {
f := excelize.NewFile()
f := NewFile()
const sheet = "Sheet1"

if err := f.SetSheetViewOptions(sheet, 0,
excelize.DefaultGridColor(false),
excelize.RightToLeft(false),
excelize.ShowFormulas(true),
excelize.ShowGridLines(true),
excelize.ShowRowColHeaders(true),
excelize.ZoomScale(80),
excelize.TopLeftCell("C3"),
DefaultGridColor(false),
RightToLeft(false),
ShowFormulas(true),
ShowGridLines(true),
ShowRowColHeaders(true),
ZoomScale(80),
TopLeftCell("C3"),
); err != nil {
fmt.Println(err)
}

var zoomScale excelize.ZoomScale
var zoomScale ZoomScale
fmt.Println("Default:")
fmt.Println("- zoomScale: 80")

if err := f.SetSheetViewOptions(sheet, 0, excelize.ZoomScale(500)); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, ZoomScale(500)); err != nil {
fmt.Println(err)
}

Expand All @@ -65,7 +63,7 @@ func ExampleFile_SetSheetViewOptions() {
fmt.Println("Used out of range value:")
fmt.Println("- zoomScale:", zoomScale)

if err := f.SetSheetViewOptions(sheet, 0, excelize.ZoomScale(123)); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, ZoomScale(123)); err != nil {
fmt.Println(err)
}

Expand All @@ -87,18 +85,18 @@ func ExampleFile_SetSheetViewOptions() {
}

func ExampleFile_GetSheetViewOptions() {
f := excelize.NewFile()
f := NewFile()
const sheet = "Sheet1"

var (
defaultGridColor excelize.DefaultGridColor
rightToLeft excelize.RightToLeft
showFormulas excelize.ShowFormulas
showGridLines excelize.ShowGridLines
showZeros excelize.ShowZeros
showRowColHeaders excelize.ShowRowColHeaders
zoomScale excelize.ZoomScale
topLeftCell excelize.TopLeftCell
defaultGridColor DefaultGridColor
rightToLeft RightToLeft
showFormulas ShowFormulas
showGridLines ShowGridLines
showZeros ShowZeros
showRowColHeaders ShowRowColHeaders
zoomScale ZoomScale
topLeftCell TopLeftCell
)

if err := f.GetSheetViewOptions(sheet, 0,
Expand All @@ -124,23 +122,23 @@ func ExampleFile_GetSheetViewOptions() {
fmt.Println("- zoomScale:", zoomScale)
fmt.Println("- topLeftCell:", `"`+topLeftCell+`"`)

if err := f.SetSheetViewOptions(sheet, 0, excelize.TopLeftCell("B2")); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, TopLeftCell("B2")); err != nil {
fmt.Println(err)
}

if err := f.GetSheetViewOptions(sheet, 0, &topLeftCell); err != nil {
fmt.Println(err)
}

if err := f.SetSheetViewOptions(sheet, 0, excelize.ShowGridLines(false)); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, ShowGridLines(false)); err != nil {
fmt.Println(err)
}

if err := f.GetSheetViewOptions(sheet, 0, &showGridLines); err != nil {
fmt.Println(err)
}

if err := f.SetSheetViewOptions(sheet, 0, excelize.ShowZeros(false)); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, ShowZeros(false)); err != nil {
fmt.Println(err)
}

Expand Down Expand Up @@ -170,7 +168,7 @@ func ExampleFile_GetSheetViewOptions() {
}

func TestSheetViewOptionsErrors(t *testing.T) {
f := excelize.NewFile()
f := NewFile()
const sheet = "Sheet1"

assert.NoError(t, f.GetSheetViewOptions(sheet, 0))
Expand Down
2 changes: 1 addition & 1 deletion sparkline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func TestAddSparkline(t *testing.T) {
Negative: true,
}))

// Save xlsx file by the given path.
// Save spreadsheet by the given path.
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddSparkline.xlsx")))

// Test error exceptions
Expand Down
44 changes: 8 additions & 36 deletions stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
if err != nil {
return nil, err
}

sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
if f.streams == nil {
f.streams = make(map[string]*StreamWriter)
}
f.streams[sheetXML] = sw

sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap)
bulkAppendFields(&sw.rawData, sw.worksheet, 2, 6)
sw.rawData.WriteString(`<sheetData>`)
Expand Down Expand Up @@ -387,13 +394,8 @@ func (sw *StreamWriter) Flush() error {
sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
delete(sw.File.Sheet, sheetXML)
delete(sw.File.checked, sheetXML)
delete(sw.File.XLSX, sheetXML)

defer sw.rawData.Close()
b, err := sw.rawData.Bytes()
if err != nil {
return err
}
sw.File.XLSX[sheetXML] = b
return nil
}

Expand Down Expand Up @@ -444,36 +446,6 @@ func (bw *bufferedWriter) Reader() (io.Reader, error) {
return io.NewSectionReader(bw.tmp, 0, fi.Size()), nil
}

// Bytes returns the entire content of the bufferedWriter. If a temp file is
// used, Bytes will efficiently allocate a buffer to prevent re-allocations.
func (bw *bufferedWriter) Bytes() ([]byte, error) {
if bw.tmp == nil {
return bw.buf.Bytes(), nil
}

if err := bw.Flush(); err != nil {
return nil, err
}

var buf bytes.Buffer
if fi, err := bw.tmp.Stat(); err == nil {
if size := fi.Size() + bytes.MinRead; size > bytes.MinRead {
if int64(int(size)) == size {
buf.Grow(int(size))
} else {
return nil, bytes.ErrTooLarge
}
}
}

if _, err := bw.tmp.Seek(0, 0); err != nil {
return nil, err
}

_, err := buf.ReadFrom(bw.tmp)
return buf.Bytes(), err
}

// Sync will write the in-memory buffer to a temp file, if the in-memory
// buffer has grown large enough. Any error will be returned.
func (bw *bufferedWriter) Sync() (err error) {
Expand Down
2 changes: 1 addition & 1 deletion stream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func TestStreamWriter(t *testing.T) {
}

assert.NoError(t, streamWriter.Flush())
// Save xlsx file by the given path.
// Save spreadsheet by the given path.
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriter.xlsx")))

// Test close temporary file error.
Expand Down
135 changes: 96 additions & 39 deletions styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import (
"log"
"math"
"reflect"
"regexp"
"strconv"
"strings"
)

// Excel styles can reference number formats that are built-in, all of which
// have an id less than 164. This is a possibly incomplete list comprised of
// as many of them as I could find.
// have an id less than 164. Note that this number format code list is under
// English localization.
var builtInNumFmt = map[int]string{
0: "general",
1: "0",
Expand Down Expand Up @@ -755,7 +756,7 @@ var currencyNumFmt = map[int]string{

// builtInNumFmtFunc defined the format conversion functions map. Partial format
// code doesn't support currently and will return original string.
var builtInNumFmtFunc = map[int]func(i int, v string) string{
var builtInNumFmtFunc = map[int]func(v string, format string) string{
0: formatToString,
1: formatToInt,
2: formatToFloat,
Expand Down Expand Up @@ -847,14 +848,14 @@ var criteriaType = map[string]string{

// formatToString provides a function to return original string by given
// built-in number formats code and cell string.
func formatToString(i int, v string) string {
func formatToString(v string, format string) string {
return v
}

// formatToInt provides a function to convert original string to integer
// format as string type by given built-in number formats code and cell
// string.
func formatToInt(i int, v string) string {
func formatToInt(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
Expand All @@ -865,7 +866,7 @@ func formatToInt(i int, v string) string {
// formatToFloat provides a function to convert original string to float
// format as string type by given built-in number formats code and cell
// string.
func formatToFloat(i int, v string) string {
func formatToFloat(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
Expand All @@ -875,7 +876,7 @@ func formatToFloat(i int, v string) string {

// formatToA provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToA(i int, v string) string {
func formatToA(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
Expand All @@ -890,7 +891,7 @@ func formatToA(i int, v string) string {

// formatToB provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToB(i int, v string) string {
func formatToB(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
Expand All @@ -903,7 +904,7 @@ func formatToB(i int, v string) string {

// formatToC provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToC(i int, v string) string {
func formatToC(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
Expand All @@ -914,7 +915,7 @@ func formatToC(i int, v string) string {

// formatToD provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToD(i int, v string) string {
func formatToD(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
Expand All @@ -925,14 +926,16 @@ func formatToD(i int, v string) string {

// formatToE provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string.
func formatToE(i int, v string) string {
func formatToE(v string, format string) string {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return v
}
return fmt.Sprintf("%.e", f)
}

var dateTimeFormatsCache = map[string]string{}

// parseTime provides a function to returns a string parsed using time.Time.
// Replace Excel placeholders with Go time placeholders. For example, replace
// yyyy with 2006. These are in a specific order, due to the fact that m is
Expand All @@ -944,15 +947,46 @@ func formatToE(i int, v string) string {
// arbitrary characters unused in Excel Date formats, and then at the end,
// turn them to what they should actually be. Based off:
// http://www.ozgrid.com/Excel/CustomFormats.htm
func parseTime(i int, v string) string {
f, err := strconv.ParseFloat(v, 64)
func parseTime(v string, format string) string {
var (
f float64
err error
goFmt string
)
f, err = strconv.ParseFloat(v, 64)
if err != nil {
return v
}
val := timeFromExcelTime(f, false)
format := builtInNumFmt[i]

if format == "" {
return v
}

goFmt, found := dateTimeFormatsCache[format]
if found {
return val.Format(goFmt)
}

goFmt = format

if strings.Contains(goFmt, "[") {
var re = regexp.MustCompile(`\[.+\]`)
goFmt = re.ReplaceAllLiteralString(goFmt, "")
}

// use only first variant
if strings.Contains(goFmt, ";") {
goFmt = goFmt[:strings.IndexByte(goFmt, ';')]
}

replacements := []struct{ xltime, gotime string }{
{"YYYY", "2006"},
{"YY", "06"},
{"MM", "01"},
{"M", "1"},
{"DD", "02"},
{"D", "2"},
{"yyyy", "2006"},
{"yy", "06"},
{"mmmm", "%%%%"},
Expand All @@ -962,38 +996,59 @@ func parseTime(i int, v string) string {
{"mmm", "Jan"},
{"mmss", "0405"},
{"ss", "05"},
{"s", "5"},
{"mm:", "04:"},
{":mm", ":04"},
{"m:", "4:"},
{":m", ":4"},
{"mm", "01"},
{"am/pm", "pm"},
{"m/", "1/"},
{"%%%%", "January"},
{"&&&&", "Monday"},
}

replacementsGlobal := []struct{ xltime, gotime string }{
{"\\-", "-"},
{"\\ ", " "},
{"\\.", "."},
{"\\", ""},
}
// It is the presence of the "am/pm" indicator that determines if this is
// a 12 hour or 24 hours time format, not the number of 'h' characters.
if is12HourTime(format) {
format = strings.Replace(format, "hh", "03", 1)
format = strings.Replace(format, "h", "3", 1)
goFmt = strings.Replace(goFmt, "hh", "3", 1)
goFmt = strings.Replace(goFmt, "h", "3", 1)
goFmt = strings.Replace(goFmt, "HH", "3", 1)
goFmt = strings.Replace(goFmt, "H", "3", 1)
} else {
format = strings.Replace(format, "hh", "15", 1)
format = strings.Replace(format, "h", "15", 1)
goFmt = strings.Replace(goFmt, "hh", "15", 1)
goFmt = strings.Replace(goFmt, "h", "3", 1)
goFmt = strings.Replace(goFmt, "HH", "15", 1)
goFmt = strings.Replace(goFmt, "H", "3", 1)
}

for _, repl := range replacements {
format = strings.Replace(format, repl.xltime, repl.gotime, 1)
goFmt = strings.Replace(goFmt, repl.xltime, repl.gotime, 1)
}
for _, repl := range replacementsGlobal {
goFmt = strings.Replace(goFmt, repl.xltime, repl.gotime, -1)
}
// If the hour is optional, strip it out, along with the possible dangling
// colon that would remain.
if val.Hour() < 1 {
format = strings.Replace(format, "]:", "]", 1)
format = strings.Replace(format, "[03]", "", 1)
format = strings.Replace(format, "[3]", "", 1)
format = strings.Replace(format, "[15]", "", 1)
goFmt = strings.Replace(goFmt, "]:", "]", 1)
goFmt = strings.Replace(goFmt, "[03]", "", 1)
goFmt = strings.Replace(goFmt, "[3]", "", 1)
goFmt = strings.Replace(goFmt, "[15]", "", 1)
} else {
format = strings.Replace(format, "[3]", "3", 1)
format = strings.Replace(format, "[15]", "15", 1)
goFmt = strings.Replace(goFmt, "[3]", "3", 1)
goFmt = strings.Replace(goFmt, "[15]", "15", 1)
}
return val.Format(format)

dateTimeFormatsCache[format] = goFmt

return val.Format(goFmt)
}

// is12HourTime checks whether an Excel time format string is a 12 hours form.
Expand Down Expand Up @@ -2008,7 +2063,7 @@ var getXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{
if style.Alignment == nil {
return xf.ApplyAlignment == nil || *xf.ApplyAlignment == false
}
return reflect.DeepEqual(xf.Alignment, newAlignment(style)) && xf.ApplyBorder != nil && *xf.ApplyBorder == true
return reflect.DeepEqual(xf.Alignment, newAlignment(style))
},
"protection": func(ID int, xf xlsxXf, style *Style) bool {
if style.Protection == nil {
Expand Down Expand Up @@ -2226,6 +2281,7 @@ func newNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
// setCustomNumFmt provides a function to set custom number format code.
func setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
nf := xlsxNumFmt{FormatCode: *style.CustomNumFmt}

if styleSheet.NumFmts != nil {
nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1
styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf)
Expand Down Expand Up @@ -2524,15 +2580,15 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a
// GetCellStyle provides a function to get cell style index by given worksheet
// name and cell coordinates.
func (f *File) GetCellStyle(sheet, axis string) (int, error) {
xlsx, err := f.workSheetReader(sheet)
ws, err := f.workSheetReader(sheet)
if err != nil {
return 0, err
}
cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
cellData, col, _, err := f.prepareCell(ws, sheet, axis)
if err != nil {
return 0, err
}
return f.prepareCellStyle(xlsx, col, cellData.S), err
return f.prepareCellStyle(ws, col, cellData.S), err
}

// SetCellStyle provides a function to add style attribute for cells by given
Expand Down Expand Up @@ -2626,16 +2682,16 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) error {
vcolIdx := vcol - 1
vrowIdx := vrow - 1

xlsx, err := f.workSheetReader(sheet)
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
prepareSheetXML(xlsx, vcol, vrow)
makeContiguousColumns(xlsx, hrow, vrow, vcol)
prepareSheetXML(ws, vcol, vrow)
makeContiguousColumns(ws, hrow, vrow, vcol)

for r := hrowIdx; r <= vrowIdx; r++ {
for k := hcolIdx; k <= vcolIdx; k++ {
xlsx.SheetData.Row[r].C[k].S = styleID
ws.SheetData.Row[r].C[k].S = styleID
}
}
return err
Expand Down Expand Up @@ -2870,7 +2926,7 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
"expression": drawConfFmtExp,
}

xlsx, err := f.workSheetReader(sheet)
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
Expand All @@ -2892,7 +2948,7 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
}
}

xlsx.ConditionalFormatting = append(xlsx.ConditionalFormatting, &xlsxConditionalFormatting{
ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{
SQRef: area,
CfRule: cfRule,
})
Expand Down Expand Up @@ -3054,12 +3110,10 @@ func (f *File) themeReader() *xlsxTheme {
err error
theme xlsxTheme
)

if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))).
Decode(&theme); err != nil && err != io.EOF {
log.Printf("xml decoder error: %s", err)
}

return &theme
}

Expand All @@ -3071,7 +3125,10 @@ func ThemeColor(baseColor string, tint float64) string {
r, _ := strconv.ParseInt(baseColor[0:2], 16, 64)
g, _ := strconv.ParseInt(baseColor[2:4], 16, 64)
b, _ := strconv.ParseInt(baseColor[4:6], 16, 64)
h, s, l := RGBToHSL(uint8(r), uint8(g), uint8(b))
var h, s, l float64
if r >= 0 && r <= math.MaxUint8 && g >= 0 && g <= math.MaxUint8 && b >= 0 && b <= math.MaxUint8 {
h, s, l = RGBToHSL(uint8(r), uint8(g), uint8(b))
}
if tint < 0 {
l *= (1 + tint)
} else {
Expand Down
69 changes: 64 additions & 5 deletions styles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package excelize

import (
"fmt"
"math"
"path/filepath"
"strings"
"testing"
Expand Down Expand Up @@ -155,18 +156,18 @@ func TestSetConditionalFormat(t *testing.T) {
}}

for _, testCase := range cases {
xl := NewFile()
f := NewFile()
const sheet = "Sheet1"
const cellRange = "A1:A1"

err := xl.SetConditionalFormat(sheet, cellRange, testCase.format)
err := f.SetConditionalFormat(sheet, cellRange, testCase.format)
if err != nil {
t.Fatalf("%s", err)
}

xlsx, err := xl.workSheetReader(sheet)
ws, err := f.workSheetReader(sheet)
assert.NoError(t, err)
cf := xlsx.ConditionalFormatting
cf := ws.ConditionalFormatting
assert.Len(t, cf, 1, testCase.label)
assert.Len(t, cf[0].CfRule, 1, testCase.label)
assert.Equal(t, cellRange, cf[0].SQRef, testCase.label)
Expand All @@ -184,7 +185,7 @@ func TestUnsetConditionalFormat(t *testing.T) {
assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10"))
// Test unset conditional format on not exists worksheet.
assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN is not exist")
// Save xlsx file by the given path.
// Save spreadsheet by the given path.
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnsetConditionalFormat.xlsx")))
}

Expand All @@ -201,10 +202,44 @@ func TestNewStyle(t *testing.T) {
assert.NoError(t, err)
_, err = f.NewStyle(Style{})
assert.EqualError(t, err, "invalid parameter type")

_, err = f.NewStyle(&Style{Font: &Font{Family: strings.Repeat("s", MaxFontFamilyLength+1)}})
assert.EqualError(t, err, "the length of the font family name must be smaller than or equal to 31")
_, err = f.NewStyle(&Style{Font: &Font{Size: MaxFontSize + 1}})
assert.EqualError(t, err, "font size must be between 1 and 409 points")

// new numeric custom style
fmt := "####;####"
f.Styles.NumFmts = nil
styleID, err = f.NewStyle(&Style{
CustomNumFmt: &fmt,
})
assert.NoError(t, err)
assert.Equal(t, 2, styleID)

assert.NotNil(t, f.Styles)
assert.NotNil(t, f.Styles.CellXfs)
assert.NotNil(t, f.Styles.CellXfs.Xf)

nf := f.Styles.CellXfs.Xf[styleID]
assert.Equal(t, 164, *nf.NumFmtID)

// new currency custom style
f.Styles.NumFmts = nil
styleID, err = f.NewStyle(&Style{
Lang: "ko-kr",
NumFmt: 32, // must not be in currencyNumFmt

})
assert.NoError(t, err)
assert.Equal(t, 3, styleID)

assert.NotNil(t, f.Styles)
assert.NotNil(t, f.Styles.CellXfs)
assert.NotNil(t, f.Styles.CellXfs.Xf)

nf = f.Styles.CellXfs.Xf[styleID]
assert.Equal(t, 32, *nf.NumFmtID)
}

func TestGetDefaultFont(t *testing.T) {
Expand Down Expand Up @@ -250,3 +285,27 @@ func TestGetStyleID(t *testing.T) {
func TestGetFillID(t *testing.T) {
assert.Equal(t, -1, getFillID(NewFile().stylesReader(), &Style{Fill: Fill{Type: "unknown"}}))
}

func TestParseTime(t *testing.T) {
assert.Equal(t, "2019", parseTime("43528", "YYYY"))
assert.Equal(t, "43528", parseTime("43528", ""))

assert.Equal(t, "2019-03-04 05:05:42", parseTime("43528.2123", "YYYY-MM-DD hh:mm:ss"))
assert.Equal(t, "2019-03-04 05:05:42", parseTime("43528.2123", "YYYY-MM-DD hh:mm:ss;YYYY-MM-DD hh:mm:ss"))
assert.Equal(t, "3/4/2019 5:5:42", parseTime("43528.2123", "M/D/YYYY h:m:s"))
assert.Equal(t, "March", parseTime("43528", "mmmm"))
assert.Equal(t, "Monday", parseTime("43528", "dddd"))
}

func TestThemeColor(t *testing.T) {
for _, clr := range [][]string{
{"FF000000", ThemeColor("000000", -0.1)},
{"FF000000", ThemeColor("000000", 0)},
{"FF33FF33", ThemeColor("00FF00", 0.2)},
{"FFFFFFFF", ThemeColor("000000", 1)},
{"FFFFFFFF", ThemeColor(strings.Repeat(string(rune(math.MaxUint8+1)), 6), 1)},
{"FFFFFFFF", ThemeColor(strings.Repeat(string(rune(-1)), 6), 1)},
} {
assert.Equal(t, clr[0], clr[1])
}
}
12 changes: 6 additions & 6 deletions table.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,18 +323,18 @@ func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
// autoFilter provides a function to extract the tokens from the filter
// expression. The tokens are mainly non-whitespace groups.
func (f *File) autoFilter(sheet, ref string, refRange, col int, formatSet *formatAutoFilter) error {
xlsx, err := f.workSheetReader(sheet)
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if xlsx.SheetPr != nil {
xlsx.SheetPr.FilterMode = true
if ws.SheetPr != nil {
ws.SheetPr.FilterMode = true
}
xlsx.SheetPr = &xlsxSheetPr{FilterMode: true}
ws.SheetPr = &xlsxSheetPr{FilterMode: true}
filter := &xlsxAutoFilter{
Ref: ref,
}
xlsx.AutoFilter = filter
ws.AutoFilter = filter
if formatSet.Column == "" || formatSet.Expression == "" {
return nil
}
Expand All @@ -361,7 +361,7 @@ func (f *File) autoFilter(sheet, ref string, refRange, col int, formatSet *forma
return err
}
f.writeAutoFilter(filter, expressions, tokens)
xlsx.AutoFilter = filter
ws.AutoFilter = filter
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion xmlChart.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ type formatChartSeries struct {
Width float64 `json:"width"`
} `json:"line"`
Marker struct {
Type string `json:"type"`
Symbol string `json:"symbol"`
Size int `json:"size"`
Width float64 `json:"width"`
Border struct {
Expand Down
2 changes: 2 additions & 0 deletions xmlDrawing.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (

// Source relationship and namespace.
const (
SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Expand All @@ -50,6 +51,7 @@ const (
NameSpaceXML = "http://www.w3.org/XML/1998/namespace"
NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance"
StrictSourceRelationship = "http://purl.oclc.org/ooxml/officeDocument/relationships"
StrictSourceRelationshipOfficeDocument = "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument"
StrictSourceRelationshipChart = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart"
StrictSourceRelationshipComments = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments"
StrictSourceRelationshipImage = "http://purl.oclc.org/ooxml/officeDocument/relationships/image"
Expand Down
18 changes: 9 additions & 9 deletions xmlWorksheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,19 +314,19 @@ type xlsxSheetData struct {
// about an entire row of a worksheet, and contains all cell definitions for a
// particular row in the worksheet.
type xlsxRow struct {
Collapsed bool `xml:"collapsed,attr,omitempty"`
C []xlsxC `xml:"c"`
R int `xml:"r,attr,omitempty"`
Spans string `xml:"spans,attr,omitempty"`
S int `xml:"s,attr,omitempty"`
CustomFormat bool `xml:"customFormat,attr,omitempty"`
CustomHeight bool `xml:"customHeight,attr,omitempty"`
Hidden bool `xml:"hidden,attr,omitempty"`
Ht float64 `xml:"ht,attr,omitempty"`
Hidden bool `xml:"hidden,attr,omitempty"`
CustomHeight bool `xml:"customHeight,attr,omitempty"`
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
Ph bool `xml:"ph,attr,omitempty"`
R int `xml:"r,attr,omitempty"`
S int `xml:"s,attr,omitempty"`
Spans string `xml:"spans,attr,omitempty"`
ThickBot bool `xml:"thickBot,attr,omitempty"`
Collapsed bool `xml:"collapsed,attr,omitempty"`
ThickTop bool `xml:"thickTop,attr,omitempty"`
C []xlsxC `xml:"c"`
ThickBot bool `xml:"thickBot,attr,omitempty"`
Ph bool `xml:"ph,attr,omitempty"`
}

// xlsxSortState directly maps the sortState element. This collection
Expand Down