extend cell value load to support custom datetime format (#703)

* extend cell value load to support custom datetime format

* cleanup incorrect imports

* fix numeric values conversion as done in legacy Excel

* fix tests coverage

* revert temporary package name fix

* remove personal info from test XLSX files

* remove unused dependencies

* update format conversion in parseTime

* new UT to increase code coverage

* Resolve code review issue for PR #703

* Rename broken file name generated by unit test

Co-authored-by: xuri <xuri.me@gmail.com>
This commit is contained in:
Artem Kustikov 2020-10-04 16:07:39 +03:00 committed by GitHub
parent 9055a835a8
commit f2b8798a34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 452 additions and 248 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
~$*.xlsx ~$*.xlsx
test/Test*.xlsx test/Test*.xlsx
test/Test*.xlsm
*.out *.out
*.test *.test
.idea .idea

18
cell.go
View File

@ -762,9 +762,23 @@ func (f *File) formattedValue(s int, v string) string {
return v return v
} }
styleSheet := f.stylesReader() styleSheet := f.stylesReader()
ok := builtInNumFmtFunc[*styleSheet.CellXfs.Xf[s].NumFmtID] if s >= len(styleSheet.CellXfs.Xf) {
return v
}
numFmtId := *styleSheet.CellXfs.Xf[s].NumFmtID
ok := builtInNumFmtFunc[numFmtId]
if ok != nil { if ok != nil {
return ok(*styleSheet.CellXfs.Xf[s].NumFmtID, v) return ok(v, builtInNumFmt[numFmtId])
}
for _, xlsxFmt := range styleSheet.NumFmts.NumFmt {
if xlsxFmt.NumFmtID == numFmtId {
format := strings.ToLower(xlsxFmt.FormatCode)
if strings.Contains(format, "y") || strings.Contains(format, "m") || strings.Contains(format, "d") || strings.Contains(format, "h") {
return parseTime(v, format)
}
return v
}
} }
return v return v
} }

View File

@ -111,6 +111,23 @@ func TestSetCellValue(t *testing.T) {
assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Duration(1e13)), `cannot convert cell "A" to coordinates: invalid cell name "A"`) assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Duration(1e13)), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
} }
func TestSetCellValues(t *testing.T) {
f := NewFile()
err := f.SetCellValue("Sheet1", "A1", time.Date(2010, time.December, 31, 0, 0, 0, 0, time.UTC))
assert.NoError(t, err)
v, err := f.GetCellValue("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, v, "12/31/10 12:00")
// test date value lower than min date supported by Excel
err = f.SetCellValue("Sheet1", "A1", time.Date(1600, time.December, 31, 0, 0, 0, 0, time.UTC))
assert.NoError(t, err)
_, err = f.GetCellValue("Sheet1", "A1")
assert.EqualError(t, err, `strconv.ParseFloat: parsing "1600-12-31T00:00:00Z": invalid syntax`)
}
func TestSetCellBool(t *testing.T) { func TestSetCellBool(t *testing.T) {
f := NewFile() f := NewFile()
assert.EqualError(t, f.SetCellBool("Sheet1", "A", true), `cannot convert cell "A" to coordinates: invalid cell name "A"`) assert.EqualError(t, f.SetCellBool("Sheet1", "A", true), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
@ -264,3 +281,22 @@ func TestSetCellRichText(t *testing.T) {
// Test set cell rich text with illegal cell coordinates // Test set cell rich text with illegal cell coordinates
assert.EqualError(t, f.SetCellRichText("Sheet1", "A", richTextRun), `cannot convert cell "A" to coordinates: invalid cell name "A"`) assert.EqualError(t, f.SetCellRichText("Sheet1", "A", richTextRun), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
} }
func TestFormattedValue(t *testing.T) {
f := NewFile()
v := f.formattedValue(0, "43528")
assert.Equal(t, "43528", v)
v = f.formattedValue(15, "43528")
assert.Equal(t, "43528", v)
v = f.formattedValue(1, "43528")
assert.Equal(t, "43528", v)
customNumFmt := "[$-409]MM/DD/YYYY"
_, err := f.NewStyle(&Style{
CustomNumFmt: &customNumFmt,
})
assert.NoError(t, err)
v = f.formattedValue(1, "43528")
assert.Equal(t, "03/04/2019", v)
}

View File

@ -19,5 +19,5 @@ import (
func TestEncrypt(t *testing.T) { func TestEncrypt(t *testing.T) {
f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"}) f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"})
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualError(t, f.SaveAs(filepath.Join("test", "TestEncrypt.xlsx"), Options{Password: "password"}), "not support encryption currently") assert.EqualError(t, f.SaveAs(filepath.Join("test", "BadEncrypt.xlsx"), Options{Password: "password"}), "not support encryption currently")
} }

View File

@ -158,7 +158,7 @@ func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error {
} }
if s == 0 { if s == 0 {
style, _ := f.NewStyle(&Style{NumFmt: format}) style, _ := f.NewStyle(&Style{NumFmt: format})
_ = f.SetCellStyle(sheet, axis, axis, style) err = f.SetCellStyle(sheet, axis, axis, style)
} }
return err return err
} }

View File

@ -257,7 +257,7 @@ func TestBrokenFile(t *testing.T) {
t.Run("SaveAsEmptyStruct", func(t *testing.T) { t.Run("SaveAsEmptyStruct", func(t *testing.T) {
// Test write file with broken file struct with given path. // Test write file with broken file struct with given path.
assert.NoError(t, f.SaveAs(filepath.Join("test", "BrokenFile.SaveAsEmptyStruct.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "BadWorkbook.SaveAsEmptyStruct.xlsx")))
}) })
t.Run("OpenBadWorkbook", func(t *testing.T) { t.Run("OpenBadWorkbook", func(t *testing.T) {
@ -1175,6 +1175,9 @@ func TestSetDefaultTimeStyle(t *testing.T) {
f := NewFile() f := NewFile()
// Test set default time style on not exists worksheet. // Test set default time style on not exists worksheet.
assert.EqualError(t, f.setDefaultTimeStyle("SheetN", "", 0), "sheet SheetN is not exist") assert.EqualError(t, f.setDefaultTimeStyle("SheetN", "", 0), "sheet SheetN is not exist")
// Test set default time style on invalid cell
assert.EqualError(t, f.setDefaultTimeStyle("Sheet1", "", 42), "cannot convert cell \"\" to coordinates: invalid cell name \"\"")
} }
func TestAddVBAProject(t *testing.T) { func TestAddVBAProject(t *testing.T) {

View File

@ -123,7 +123,7 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
} }
} }
if f.options != nil { if f.options != nil && f.options.Password != "" {
if err := zw.Close(); err != nil { if err := zw.Close(); err != nil {
return buf, err return buf, err
} }

19
rows.go
View File

@ -345,6 +345,25 @@ func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
} }
return f.formattedValue(xlsx.S, xlsx.V), nil return f.formattedValue(xlsx.S, xlsx.V), nil
default: default:
// correct numeric values as legacy Excel app
// https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel
// In the top figure the fraction 1/9000 in Excel is displayed.
// Although this number has a decimal representation that is an infinite string of ones,
// Excel displays only the leading 15 figures. In the second line, the number one is added to the fraction, and again Excel displays only 15 figures.
const precision = 1000000000000000
if len(xlsx.V) > 16 {
num, err := strconv.ParseFloat(xlsx.V, 64)
if err != nil {
return "", err
}
num = math.Round(num*precision) / precision
val := fmt.Sprintf("%g", num)
if val != xlsx.V {
return f.formattedValue(xlsx.S, val), nil
}
}
return f.formattedValue(xlsx.S, xlsx.V), nil return f.formattedValue(xlsx.S, xlsx.V), nil
} }
} }

View File

@ -817,7 +817,7 @@ func TestDuplicateMergeCells(t *testing.T) {
assert.EqualError(t, f.duplicateMergeCells("SheetN", xlsx, 1, 2), "sheet SheetN is not exist") assert.EqualError(t, f.duplicateMergeCells("SheetN", xlsx, 1, 2), "sheet SheetN is not exist")
} }
func TestGetValueFrom(t *testing.T) { func TestGetValueFromInlineStr(t *testing.T) {
c := &xlsxC{T: "inlineStr"} c := &xlsxC{T: "inlineStr"}
f := NewFile() f := NewFile()
d := &xlsxSST{} d := &xlsxSST{}
@ -826,6 +826,20 @@ func TestGetValueFrom(t *testing.T) {
assert.Equal(t, "", val) assert.Equal(t, "", val)
} }
func TestGetValueFromNumber(t *testing.T) {
c := &xlsxC{T: "n", V: "2.2200000000000002"}
f := NewFile()
d := &xlsxSST{}
val, err := c.getValueFrom(f, d)
assert.NoError(t, err)
assert.Equal(t, "2.22", val)
c = &xlsxC{T: "n", V: "2.220000ddsf0000000002-r"}
val, err = c.getValueFrom(f, d)
assert.NotNil(t, err)
assert.Equal(t, "strconv.ParseFloat: parsing \"2.220000ddsf0000000002-r\": invalid syntax", err.Error())
}
func TestErrSheetNotExistError(t *testing.T) { func TestErrSheetNotExistError(t *testing.T) {
err := ErrSheetNotExist{SheetName: "Sheet1"} err := ErrSheetNotExist{SheetName: "Sheet1"}
assert.EqualValues(t, err.Error(), "sheet Sheet1 is not exist") assert.EqualValues(t, err.Error(), "sheet Sheet1 is not exist")
@ -842,6 +856,27 @@ func TestCheckRow(t *testing.T) {
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", false), `cannot convert cell "-" to coordinates: invalid cell name "-"`) assert.EqualError(t, f.SetCellValue("Sheet1", "A1", false), `cannot convert cell "-" to coordinates: invalid cell name "-"`)
} }
func TestNumberFormats(t *testing.T) {
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
if !assert.NoError(t, err) {
t.FailNow()
}
cells := make([][]string, 0)
cols, err := f.Cols("Sheet2")
if !assert.NoError(t, err) {
t.FailNow()
}
for cols.Next() {
col, err := cols.Rows()
assert.NoError(t, err)
if err != nil {
break
}
cells = append(cells, col)
}
assert.Equal(t, []string{"", "200", "450", "200", "510", "315", "127", "89", "348", "53", "37"}, cells[3])
}
func BenchmarkRows(b *testing.B) { func BenchmarkRows(b *testing.B) {
f, _ := OpenFile(filepath.Join("test", "Book1.xlsx")) f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@ -1,4 +1,4 @@
package excelize_test package excelize
import ( import (
"fmt" "fmt"
@ -6,26 +6,24 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/360EntSecGroup-Skylar/excelize/v2"
"github.com/mohae/deepcopy" "github.com/mohae/deepcopy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func ExampleFile_SetPageLayout() { func ExampleFile_SetPageLayout() {
f := excelize.NewFile() f := NewFile()
if err := f.SetPageLayout( if err := f.SetPageLayout(
"Sheet1", "Sheet1",
excelize.PageLayoutOrientation(excelize.OrientationLandscape), PageLayoutOrientation(OrientationLandscape),
); err != nil { ); err != nil {
fmt.Println(err) fmt.Println(err)
} }
if err := f.SetPageLayout( if err := f.SetPageLayout(
"Sheet1", "Sheet1",
excelize.PageLayoutPaperSize(10), PageLayoutPaperSize(10),
excelize.FitToHeight(2), FitToHeight(2),
excelize.FitToWidth(2), FitToWidth(2),
); err != nil { ); err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -33,12 +31,12 @@ func ExampleFile_SetPageLayout() {
} }
func ExampleFile_GetPageLayout() { func ExampleFile_GetPageLayout() {
f := excelize.NewFile() f := NewFile()
var ( var (
orientation excelize.PageLayoutOrientation orientation PageLayoutOrientation
paperSize excelize.PageLayoutPaperSize paperSize PageLayoutPaperSize
fitToHeight excelize.FitToHeight fitToHeight FitToHeight
fitToWidth excelize.FitToWidth fitToWidth FitToWidth
) )
if err := f.GetPageLayout("Sheet1", &orientation); err != nil { if err := f.GetPageLayout("Sheet1", &orientation); err != nil {
fmt.Println(err) fmt.Println(err)
@ -67,7 +65,7 @@ func ExampleFile_GetPageLayout() {
} }
func TestNewSheet(t *testing.T) { func TestNewSheet(t *testing.T) {
f := excelize.NewFile() f := NewFile()
sheetID := f.NewSheet("Sheet2") sheetID := f.NewSheet("Sheet2")
f.SetActiveSheet(sheetID) f.SetActiveSheet(sheetID)
// delete original sheet // delete original sheet
@ -76,7 +74,7 @@ func TestNewSheet(t *testing.T) {
} }
func TestSetPane(t *testing.T) { func TestSetPane(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":false,"split":false}`)) assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":false,"split":false}`))
f.NewSheet("Panes 2") f.NewSheet("Panes 2")
assert.NoError(t, f.SetPanes("Panes 2", `{"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"}]}`)) assert.NoError(t, f.SetPanes("Panes 2", `{"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"}]}`))
@ -93,13 +91,13 @@ func TestPageLayoutOption(t *testing.T) {
const sheet = "Sheet1" const sheet = "Sheet1"
testData := []struct { testData := []struct {
container excelize.PageLayoutOptionPtr container PageLayoutOptionPtr
nonDefault excelize.PageLayoutOption nonDefault PageLayoutOption
}{ }{
{new(excelize.PageLayoutOrientation), excelize.PageLayoutOrientation(excelize.OrientationLandscape)}, {new(PageLayoutOrientation), PageLayoutOrientation(OrientationLandscape)},
{new(excelize.PageLayoutPaperSize), excelize.PageLayoutPaperSize(10)}, {new(PageLayoutPaperSize), PageLayoutPaperSize(10)},
{new(excelize.FitToHeight), excelize.FitToHeight(2)}, {new(FitToHeight), FitToHeight(2)},
{new(excelize.FitToWidth), excelize.FitToWidth(2)}, {new(FitToWidth), FitToWidth(2)},
} }
for i, test := range testData { for i, test := range testData {
@ -108,11 +106,11 @@ func TestPageLayoutOption(t *testing.T) {
opt := test.nonDefault opt := test.nonDefault
t.Logf("option %T", opt) t.Logf("option %T", opt)
def := deepcopy.Copy(test.container).(excelize.PageLayoutOptionPtr) def := deepcopy.Copy(test.container).(PageLayoutOptionPtr)
val1 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr) val1 := deepcopy.Copy(def).(PageLayoutOptionPtr)
val2 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr) val2 := deepcopy.Copy(def).(PageLayoutOptionPtr)
f := excelize.NewFile() f := NewFile()
// Get the default value // Get the default value
assert.NoError(t, f.GetPageLayout(sheet, def), opt) assert.NoError(t, f.GetPageLayout(sheet, def), opt)
// Get again and check // Get again and check
@ -150,7 +148,7 @@ func TestPageLayoutOption(t *testing.T) {
} }
func TestSearchSheet(t *testing.T) { func TestSearchSheet(t *testing.T) {
f, err := excelize.OpenFile(filepath.Join("test", "SharedStrings.xlsx")) f, err := OpenFile(filepath.Join("test", "SharedStrings.xlsx"))
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -172,36 +170,36 @@ func TestSearchSheet(t *testing.T) {
assert.EqualValues(t, expected, result) assert.EqualValues(t, expected, result)
// Test search worksheet data after set cell value // Test search worksheet data after set cell value
f = excelize.NewFile() f = NewFile()
assert.NoError(t, f.SetCellValue("Sheet1", "A1", true)) assert.NoError(t, f.SetCellValue("Sheet1", "A1", true))
_, err = f.SearchSheet("Sheet1", "") _, err = f.SearchSheet("Sheet1", "")
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestSetPageLayout(t *testing.T) { func TestSetPageLayout(t *testing.T) {
f := excelize.NewFile() f := NewFile()
// Test set page layout on not exists worksheet. // Test set page layout on not exists worksheet.
assert.EqualError(t, f.SetPageLayout("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.SetPageLayout("SheetN"), "sheet SheetN is not exist")
} }
func TestGetPageLayout(t *testing.T) { func TestGetPageLayout(t *testing.T) {
f := excelize.NewFile() f := NewFile()
// Test get page layout on not exists worksheet. // Test get page layout on not exists worksheet.
assert.EqualError(t, f.GetPageLayout("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.GetPageLayout("SheetN"), "sheet SheetN is not exist")
} }
func TestSetHeaderFooter(t *testing.T) { func TestSetHeaderFooter(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.SetCellStr("Sheet1", "A1", "Test SetHeaderFooter")) assert.NoError(t, f.SetCellStr("Sheet1", "A1", "Test SetHeaderFooter"))
// Test set header and footer on not exists worksheet. // Test set header and footer on not exists worksheet.
assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN is not exist") assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN is not exist")
// Test set header and footer with illegal setting. // Test set header and footer with illegal setting.
assert.EqualError(t, f.SetHeaderFooter("Sheet1", &excelize.FormatHeaderFooter{ assert.EqualError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{
OddHeader: strings.Repeat("c", 256), OddHeader: strings.Repeat("c", 256),
}), "field OddHeader must be less than 255 characters") }), "field OddHeader must be less than 255 characters")
assert.NoError(t, f.SetHeaderFooter("Sheet1", nil)) assert.NoError(t, f.SetHeaderFooter("Sheet1", nil))
assert.NoError(t, f.SetHeaderFooter("Sheet1", &excelize.FormatHeaderFooter{ assert.NoError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{
DifferentFirst: true, DifferentFirst: true,
DifferentOddEven: true, DifferentOddEven: true,
OddHeader: "&R&P", OddHeader: "&R&P",
@ -214,28 +212,28 @@ func TestSetHeaderFooter(t *testing.T) {
} }
func TestDefinedName(t *testing.T) { func TestDefinedName(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.SetDefinedName(&excelize.DefinedName{ assert.NoError(t, f.SetDefinedName(&DefinedName{
Name: "Amount", Name: "Amount",
RefersTo: "Sheet1!$A$2:$D$5", RefersTo: "Sheet1!$A$2:$D$5",
Comment: "defined name comment", Comment: "defined name comment",
Scope: "Sheet1", Scope: "Sheet1",
})) }))
assert.NoError(t, f.SetDefinedName(&excelize.DefinedName{ assert.NoError(t, f.SetDefinedName(&DefinedName{
Name: "Amount", Name: "Amount",
RefersTo: "Sheet1!$A$2:$D$5", RefersTo: "Sheet1!$A$2:$D$5",
Comment: "defined name comment", Comment: "defined name comment",
})) }))
assert.EqualError(t, f.SetDefinedName(&excelize.DefinedName{ assert.EqualError(t, f.SetDefinedName(&DefinedName{
Name: "Amount", Name: "Amount",
RefersTo: "Sheet1!$A$2:$D$5", RefersTo: "Sheet1!$A$2:$D$5",
Comment: "defined name comment", Comment: "defined name comment",
}), "the same name already exists on the scope") }), "the same name already exists on the scope")
assert.EqualError(t, f.DeleteDefinedName(&excelize.DefinedName{ assert.EqualError(t, f.DeleteDefinedName(&DefinedName{
Name: "No Exist Defined Name", Name: "No Exist Defined Name",
}), "no defined name on the scope") }), "no defined name on the scope")
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[1].RefersTo) assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[1].RefersTo)
assert.NoError(t, f.DeleteDefinedName(&excelize.DefinedName{ assert.NoError(t, f.DeleteDefinedName(&DefinedName{
Name: "Amount", Name: "Amount",
})) }))
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[0].RefersTo) assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[0].RefersTo)
@ -244,7 +242,7 @@ func TestDefinedName(t *testing.T) {
} }
func TestGroupSheets(t *testing.T) { func TestGroupSheets(t *testing.T) {
f := excelize.NewFile() f := NewFile()
sheets := []string{"Sheet2", "Sheet3"} sheets := []string{"Sheet2", "Sheet3"}
for _, sheet := range sheets { for _, sheet := range sheets {
f.NewSheet(sheet) f.NewSheet(sheet)
@ -256,7 +254,7 @@ func TestGroupSheets(t *testing.T) {
} }
func TestUngroupSheets(t *testing.T) { func TestUngroupSheets(t *testing.T) {
f := excelize.NewFile() f := NewFile()
sheets := []string{"Sheet2", "Sheet3", "Sheet4", "Sheet5"} sheets := []string{"Sheet2", "Sheet3", "Sheet4", "Sheet5"}
for _, sheet := range sheets { for _, sheet := range sheets {
f.NewSheet(sheet) f.NewSheet(sheet)
@ -265,7 +263,7 @@ func TestUngroupSheets(t *testing.T) {
} }
func TestInsertPageBreak(t *testing.T) { func TestInsertPageBreak(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.InsertPageBreak("Sheet1", "A1")) assert.NoError(t, f.InsertPageBreak("Sheet1", "A1"))
assert.NoError(t, f.InsertPageBreak("Sheet1", "B2")) assert.NoError(t, f.InsertPageBreak("Sheet1", "B2"))
assert.NoError(t, f.InsertPageBreak("Sheet1", "C3")) assert.NoError(t, f.InsertPageBreak("Sheet1", "C3"))
@ -276,7 +274,7 @@ func TestInsertPageBreak(t *testing.T) {
} }
func TestRemovePageBreak(t *testing.T) { func TestRemovePageBreak(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.RemovePageBreak("Sheet1", "A2")) assert.NoError(t, f.RemovePageBreak("Sheet1", "A2"))
assert.NoError(t, f.InsertPageBreak("Sheet1", "A2")) assert.NoError(t, f.InsertPageBreak("Sheet1", "A2"))
@ -302,7 +300,7 @@ func TestRemovePageBreak(t *testing.T) {
} }
func TestGetSheetName(t *testing.T) { func TestGetSheetName(t *testing.T) {
f, _ := excelize.OpenFile(filepath.Join("test", "Book1.xlsx")) f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
assert.Equal(t, "Sheet1", f.GetSheetName(0)) assert.Equal(t, "Sheet1", f.GetSheetName(0))
assert.Equal(t, "Sheet2", f.GetSheetName(1)) assert.Equal(t, "Sheet2", f.GetSheetName(1))
assert.Equal(t, "", f.GetSheetName(-1)) assert.Equal(t, "", f.GetSheetName(-1))
@ -314,7 +312,7 @@ func TestGetSheetMap(t *testing.T) {
1: "Sheet1", 1: "Sheet1",
2: "Sheet2", 2: "Sheet2",
} }
f, _ := excelize.OpenFile(filepath.Join("test", "Book1.xlsx")) f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
sheetMap := f.GetSheetMap() sheetMap := f.GetSheetMap()
for idx, name := range sheetMap { for idx, name := range sheetMap {
assert.Equal(t, expectedMap[idx], name) assert.Equal(t, expectedMap[idx], name)

View File

@ -1,4 +1,4 @@
package excelize_test package excelize
import ( import (
"fmt" "fmt"
@ -6,39 +6,37 @@ import (
"github.com/mohae/deepcopy" "github.com/mohae/deepcopy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/360EntSecGroup-Skylar/excelize/v2"
) )
var _ = []excelize.SheetPrOption{ var _ = []SheetPrOption{
excelize.CodeName("hello"), CodeName("hello"),
excelize.EnableFormatConditionsCalculation(false), EnableFormatConditionsCalculation(false),
excelize.Published(false), Published(false),
excelize.FitToPage(true), FitToPage(true),
excelize.AutoPageBreaks(true), AutoPageBreaks(true),
excelize.OutlineSummaryBelow(true), OutlineSummaryBelow(true),
} }
var _ = []excelize.SheetPrOptionPtr{ var _ = []SheetPrOptionPtr{
(*excelize.CodeName)(nil), (*CodeName)(nil),
(*excelize.EnableFormatConditionsCalculation)(nil), (*EnableFormatConditionsCalculation)(nil),
(*excelize.Published)(nil), (*Published)(nil),
(*excelize.FitToPage)(nil), (*FitToPage)(nil),
(*excelize.AutoPageBreaks)(nil), (*AutoPageBreaks)(nil),
(*excelize.OutlineSummaryBelow)(nil), (*OutlineSummaryBelow)(nil),
} }
func ExampleFile_SetSheetPrOptions() { func ExampleFile_SetSheetPrOptions() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
if err := f.SetSheetPrOptions(sheet, if err := f.SetSheetPrOptions(sheet,
excelize.CodeName("code"), CodeName("code"),
excelize.EnableFormatConditionsCalculation(false), EnableFormatConditionsCalculation(false),
excelize.Published(false), Published(false),
excelize.FitToPage(true), FitToPage(true),
excelize.AutoPageBreaks(true), AutoPageBreaks(true),
excelize.OutlineSummaryBelow(false), OutlineSummaryBelow(false),
); err != nil { ); err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -46,16 +44,16 @@ func ExampleFile_SetSheetPrOptions() {
} }
func ExampleFile_GetSheetPrOptions() { func ExampleFile_GetSheetPrOptions() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
var ( var (
codeName excelize.CodeName codeName CodeName
enableFormatConditionsCalculation excelize.EnableFormatConditionsCalculation enableFormatConditionsCalculation EnableFormatConditionsCalculation
published excelize.Published published Published
fitToPage excelize.FitToPage fitToPage FitToPage
autoPageBreaks excelize.AutoPageBreaks autoPageBreaks AutoPageBreaks
outlineSummaryBelow excelize.OutlineSummaryBelow outlineSummaryBelow OutlineSummaryBelow
) )
if err := f.GetSheetPrOptions(sheet, if err := f.GetSheetPrOptions(sheet,
@ -89,15 +87,15 @@ func TestSheetPrOptions(t *testing.T) {
const sheet = "Sheet1" const sheet = "Sheet1"
testData := []struct { testData := []struct {
container excelize.SheetPrOptionPtr container SheetPrOptionPtr
nonDefault excelize.SheetPrOption nonDefault SheetPrOption
}{ }{
{new(excelize.CodeName), excelize.CodeName("xx")}, {new(CodeName), CodeName("xx")},
{new(excelize.EnableFormatConditionsCalculation), excelize.EnableFormatConditionsCalculation(false)}, {new(EnableFormatConditionsCalculation), EnableFormatConditionsCalculation(false)},
{new(excelize.Published), excelize.Published(false)}, {new(Published), Published(false)},
{new(excelize.FitToPage), excelize.FitToPage(true)}, {new(FitToPage), FitToPage(true)},
{new(excelize.AutoPageBreaks), excelize.AutoPageBreaks(true)}, {new(AutoPageBreaks), AutoPageBreaks(true)},
{new(excelize.OutlineSummaryBelow), excelize.OutlineSummaryBelow(false)}, {new(OutlineSummaryBelow), OutlineSummaryBelow(false)},
} }
for i, test := range testData { for i, test := range testData {
@ -106,11 +104,11 @@ func TestSheetPrOptions(t *testing.T) {
opt := test.nonDefault opt := test.nonDefault
t.Logf("option %T", opt) t.Logf("option %T", opt)
def := deepcopy.Copy(test.container).(excelize.SheetPrOptionPtr) def := deepcopy.Copy(test.container).(SheetPrOptionPtr)
val1 := deepcopy.Copy(def).(excelize.SheetPrOptionPtr) val1 := deepcopy.Copy(def).(SheetPrOptionPtr)
val2 := deepcopy.Copy(def).(excelize.SheetPrOptionPtr) val2 := deepcopy.Copy(def).(SheetPrOptionPtr)
f := excelize.NewFile() f := NewFile()
// Get the default value // Get the default value
assert.NoError(t, f.GetSheetPrOptions(sheet, def), opt) assert.NoError(t, f.GetSheetPrOptions(sheet, def), opt)
// Get again and check // Get again and check
@ -148,46 +146,46 @@ func TestSheetPrOptions(t *testing.T) {
} }
func TestSetSheetrOptions(t *testing.T) { func TestSetSheetrOptions(t *testing.T) {
f := excelize.NewFile() f := NewFile()
// Test SetSheetrOptions on not exists worksheet. // Test SetSheetrOptions on not exists worksheet.
assert.EqualError(t, f.SetSheetPrOptions("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.SetSheetPrOptions("SheetN"), "sheet SheetN is not exist")
} }
func TestGetSheetPrOptions(t *testing.T) { func TestGetSheetPrOptions(t *testing.T) {
f := excelize.NewFile() f := NewFile()
// Test GetSheetPrOptions on not exists worksheet. // Test GetSheetPrOptions on not exists worksheet.
assert.EqualError(t, f.GetSheetPrOptions("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.GetSheetPrOptions("SheetN"), "sheet SheetN is not exist")
} }
var _ = []excelize.PageMarginsOptions{ var _ = []PageMarginsOptions{
excelize.PageMarginBottom(1.0), PageMarginBottom(1.0),
excelize.PageMarginFooter(1.0), PageMarginFooter(1.0),
excelize.PageMarginHeader(1.0), PageMarginHeader(1.0),
excelize.PageMarginLeft(1.0), PageMarginLeft(1.0),
excelize.PageMarginRight(1.0), PageMarginRight(1.0),
excelize.PageMarginTop(1.0), PageMarginTop(1.0),
} }
var _ = []excelize.PageMarginsOptionsPtr{ var _ = []PageMarginsOptionsPtr{
(*excelize.PageMarginBottom)(nil), (*PageMarginBottom)(nil),
(*excelize.PageMarginFooter)(nil), (*PageMarginFooter)(nil),
(*excelize.PageMarginHeader)(nil), (*PageMarginHeader)(nil),
(*excelize.PageMarginLeft)(nil), (*PageMarginLeft)(nil),
(*excelize.PageMarginRight)(nil), (*PageMarginRight)(nil),
(*excelize.PageMarginTop)(nil), (*PageMarginTop)(nil),
} }
func ExampleFile_SetPageMargins() { func ExampleFile_SetPageMargins() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
if err := f.SetPageMargins(sheet, if err := f.SetPageMargins(sheet,
excelize.PageMarginBottom(1.0), PageMarginBottom(1.0),
excelize.PageMarginFooter(1.0), PageMarginFooter(1.0),
excelize.PageMarginHeader(1.0), PageMarginHeader(1.0),
excelize.PageMarginLeft(1.0), PageMarginLeft(1.0),
excelize.PageMarginRight(1.0), PageMarginRight(1.0),
excelize.PageMarginTop(1.0), PageMarginTop(1.0),
); err != nil { ); err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -195,16 +193,16 @@ func ExampleFile_SetPageMargins() {
} }
func ExampleFile_GetPageMargins() { func ExampleFile_GetPageMargins() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
var ( var (
marginBottom excelize.PageMarginBottom marginBottom PageMarginBottom
marginFooter excelize.PageMarginFooter marginFooter PageMarginFooter
marginHeader excelize.PageMarginHeader marginHeader PageMarginHeader
marginLeft excelize.PageMarginLeft marginLeft PageMarginLeft
marginRight excelize.PageMarginRight marginRight PageMarginRight
marginTop excelize.PageMarginTop marginTop PageMarginTop
) )
if err := f.GetPageMargins(sheet, if err := f.GetPageMargins(sheet,
@ -238,15 +236,15 @@ func TestPageMarginsOption(t *testing.T) {
const sheet = "Sheet1" const sheet = "Sheet1"
testData := []struct { testData := []struct {
container excelize.PageMarginsOptionsPtr container PageMarginsOptionsPtr
nonDefault excelize.PageMarginsOptions nonDefault PageMarginsOptions
}{ }{
{new(excelize.PageMarginTop), excelize.PageMarginTop(1.0)}, {new(PageMarginTop), PageMarginTop(1.0)},
{new(excelize.PageMarginBottom), excelize.PageMarginBottom(1.0)}, {new(PageMarginBottom), PageMarginBottom(1.0)},
{new(excelize.PageMarginLeft), excelize.PageMarginLeft(1.0)}, {new(PageMarginLeft), PageMarginLeft(1.0)},
{new(excelize.PageMarginRight), excelize.PageMarginRight(1.0)}, {new(PageMarginRight), PageMarginRight(1.0)},
{new(excelize.PageMarginHeader), excelize.PageMarginHeader(1.0)}, {new(PageMarginHeader), PageMarginHeader(1.0)},
{new(excelize.PageMarginFooter), excelize.PageMarginFooter(1.0)}, {new(PageMarginFooter), PageMarginFooter(1.0)},
} }
for i, test := range testData { for i, test := range testData {
@ -255,11 +253,11 @@ func TestPageMarginsOption(t *testing.T) {
opt := test.nonDefault opt := test.nonDefault
t.Logf("option %T", opt) t.Logf("option %T", opt)
def := deepcopy.Copy(test.container).(excelize.PageMarginsOptionsPtr) def := deepcopy.Copy(test.container).(PageMarginsOptionsPtr)
val1 := deepcopy.Copy(def).(excelize.PageMarginsOptionsPtr) val1 := deepcopy.Copy(def).(PageMarginsOptionsPtr)
val2 := deepcopy.Copy(def).(excelize.PageMarginsOptionsPtr) val2 := deepcopy.Copy(def).(PageMarginsOptionsPtr)
f := excelize.NewFile() f := NewFile()
// Get the default value // Get the default value
assert.NoError(t, f.GetPageMargins(sheet, def), opt) assert.NoError(t, f.GetPageMargins(sheet, def), opt)
// Get again and check // Get again and check
@ -297,29 +295,29 @@ func TestPageMarginsOption(t *testing.T) {
} }
func TestSetPageMargins(t *testing.T) { func TestSetPageMargins(t *testing.T) {
f := excelize.NewFile() f := NewFile()
// Test set page margins on not exists worksheet. // Test set page margins on not exists worksheet.
assert.EqualError(t, f.SetPageMargins("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.SetPageMargins("SheetN"), "sheet SheetN is not exist")
} }
func TestGetPageMargins(t *testing.T) { func TestGetPageMargins(t *testing.T) {
f := excelize.NewFile() f := NewFile()
// Test get page margins on not exists worksheet. // Test get page margins on not exists worksheet.
assert.EqualError(t, f.GetPageMargins("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.GetPageMargins("SheetN"), "sheet SheetN is not exist")
} }
func ExampleFile_SetSheetFormatPr() { func ExampleFile_SetSheetFormatPr() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
if err := f.SetSheetFormatPr(sheet, if err := f.SetSheetFormatPr(sheet,
excelize.BaseColWidth(1.0), BaseColWidth(1.0),
excelize.DefaultColWidth(1.0), DefaultColWidth(1.0),
excelize.DefaultRowHeight(1.0), DefaultRowHeight(1.0),
excelize.CustomHeight(true), CustomHeight(true),
excelize.ZeroHeight(true), ZeroHeight(true),
excelize.ThickTop(true), ThickTop(true),
excelize.ThickBottom(true), ThickBottom(true),
); err != nil { ); err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -327,17 +325,17 @@ func ExampleFile_SetSheetFormatPr() {
} }
func ExampleFile_GetSheetFormatPr() { func ExampleFile_GetSheetFormatPr() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
var ( var (
baseColWidth excelize.BaseColWidth baseColWidth BaseColWidth
defaultColWidth excelize.DefaultColWidth defaultColWidth DefaultColWidth
defaultRowHeight excelize.DefaultRowHeight defaultRowHeight DefaultRowHeight
customHeight excelize.CustomHeight customHeight CustomHeight
zeroHeight excelize.ZeroHeight zeroHeight ZeroHeight
thickTop excelize.ThickTop thickTop ThickTop
thickBottom excelize.ThickBottom thickBottom ThickBottom
) )
if err := f.GetSheetFormatPr(sheet, if err := f.GetSheetFormatPr(sheet,
@ -374,16 +372,16 @@ func TestSheetFormatPrOptions(t *testing.T) {
const sheet = "Sheet1" const sheet = "Sheet1"
testData := []struct { testData := []struct {
container excelize.SheetFormatPrOptionsPtr container SheetFormatPrOptionsPtr
nonDefault excelize.SheetFormatPrOptions nonDefault SheetFormatPrOptions
}{ }{
{new(excelize.BaseColWidth), excelize.BaseColWidth(1.0)}, {new(BaseColWidth), BaseColWidth(1.0)},
{new(excelize.DefaultColWidth), excelize.DefaultColWidth(1.0)}, {new(DefaultColWidth), DefaultColWidth(1.0)},
{new(excelize.DefaultRowHeight), excelize.DefaultRowHeight(1.0)}, {new(DefaultRowHeight), DefaultRowHeight(1.0)},
{new(excelize.CustomHeight), excelize.CustomHeight(true)}, {new(CustomHeight), CustomHeight(true)},
{new(excelize.ZeroHeight), excelize.ZeroHeight(true)}, {new(ZeroHeight), ZeroHeight(true)},
{new(excelize.ThickTop), excelize.ThickTop(true)}, {new(ThickTop), ThickTop(true)},
{new(excelize.ThickBottom), excelize.ThickBottom(true)}, {new(ThickBottom), ThickBottom(true)},
} }
for i, test := range testData { for i, test := range testData {
@ -392,11 +390,11 @@ func TestSheetFormatPrOptions(t *testing.T) {
opt := test.nonDefault opt := test.nonDefault
t.Logf("option %T", opt) t.Logf("option %T", opt)
def := deepcopy.Copy(test.container).(excelize.SheetFormatPrOptionsPtr) def := deepcopy.Copy(test.container).(SheetFormatPrOptionsPtr)
val1 := deepcopy.Copy(def).(excelize.SheetFormatPrOptionsPtr) val1 := deepcopy.Copy(def).(SheetFormatPrOptionsPtr)
val2 := deepcopy.Copy(def).(excelize.SheetFormatPrOptionsPtr) val2 := deepcopy.Copy(def).(SheetFormatPrOptionsPtr)
f := excelize.NewFile() f := NewFile()
// Get the default value // Get the default value
assert.NoError(t, f.GetSheetFormatPr(sheet, def), opt) assert.NoError(t, f.GetSheetFormatPr(sheet, def), opt)
// Get again and check // Get again and check
@ -434,26 +432,26 @@ func TestSheetFormatPrOptions(t *testing.T) {
} }
func TestSetSheetFormatPr(t *testing.T) { func TestSetSheetFormatPr(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.GetSheetFormatPr("Sheet1")) assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
f.Sheet["xl/worksheets/sheet1.xml"].SheetFormatPr = nil f.Sheet["xl/worksheets/sheet1.xml"].SheetFormatPr = nil
assert.NoError(t, f.SetSheetFormatPr("Sheet1", excelize.BaseColWidth(1.0))) assert.NoError(t, f.SetSheetFormatPr("Sheet1", BaseColWidth(1.0)))
// Test set formatting properties on not exists worksheet. // Test set formatting properties on not exists worksheet.
assert.EqualError(t, f.SetSheetFormatPr("SheetN"), "sheet SheetN is not exist") assert.EqualError(t, f.SetSheetFormatPr("SheetN"), "sheet SheetN is not exist")
} }
func TestGetSheetFormatPr(t *testing.T) { func TestGetSheetFormatPr(t *testing.T) {
f := excelize.NewFile() f := NewFile()
assert.NoError(t, f.GetSheetFormatPr("Sheet1")) assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
f.Sheet["xl/worksheets/sheet1.xml"].SheetFormatPr = nil f.Sheet["xl/worksheets/sheet1.xml"].SheetFormatPr = nil
var ( var (
baseColWidth excelize.BaseColWidth baseColWidth BaseColWidth
defaultColWidth excelize.DefaultColWidth defaultColWidth DefaultColWidth
defaultRowHeight excelize.DefaultRowHeight defaultRowHeight DefaultRowHeight
customHeight excelize.CustomHeight customHeight CustomHeight
zeroHeight excelize.ZeroHeight zeroHeight ZeroHeight
thickTop excelize.ThickTop thickTop ThickTop
thickBottom excelize.ThickBottom thickBottom ThickBottom
) )
assert.NoError(t, f.GetSheetFormatPr("Sheet1", assert.NoError(t, f.GetSheetFormatPr("Sheet1",
&baseColWidth, &baseColWidth,

View File

@ -1,60 +1,58 @@
package excelize_test package excelize
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/360EntSecGroup-Skylar/excelize/v2"
) )
var _ = []excelize.SheetViewOption{ var _ = []SheetViewOption{
excelize.DefaultGridColor(true), DefaultGridColor(true),
excelize.RightToLeft(false), RightToLeft(false),
excelize.ShowFormulas(false), ShowFormulas(false),
excelize.ShowGridLines(true), ShowGridLines(true),
excelize.ShowRowColHeaders(true), ShowRowColHeaders(true),
excelize.TopLeftCell("B2"), TopLeftCell("B2"),
// SheetViewOptionPtr are also SheetViewOption // SheetViewOptionPtr are also SheetViewOption
new(excelize.DefaultGridColor), new(DefaultGridColor),
new(excelize.RightToLeft), new(RightToLeft),
new(excelize.ShowFormulas), new(ShowFormulas),
new(excelize.ShowGridLines), new(ShowGridLines),
new(excelize.ShowRowColHeaders), new(ShowRowColHeaders),
new(excelize.TopLeftCell), new(TopLeftCell),
} }
var _ = []excelize.SheetViewOptionPtr{ var _ = []SheetViewOptionPtr{
(*excelize.DefaultGridColor)(nil), (*DefaultGridColor)(nil),
(*excelize.RightToLeft)(nil), (*RightToLeft)(nil),
(*excelize.ShowFormulas)(nil), (*ShowFormulas)(nil),
(*excelize.ShowGridLines)(nil), (*ShowGridLines)(nil),
(*excelize.ShowRowColHeaders)(nil), (*ShowRowColHeaders)(nil),
(*excelize.TopLeftCell)(nil), (*TopLeftCell)(nil),
} }
func ExampleFile_SetSheetViewOptions() { func ExampleFile_SetSheetViewOptions() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
if err := f.SetSheetViewOptions(sheet, 0, if err := f.SetSheetViewOptions(sheet, 0,
excelize.DefaultGridColor(false), DefaultGridColor(false),
excelize.RightToLeft(false), RightToLeft(false),
excelize.ShowFormulas(true), ShowFormulas(true),
excelize.ShowGridLines(true), ShowGridLines(true),
excelize.ShowRowColHeaders(true), ShowRowColHeaders(true),
excelize.ZoomScale(80), ZoomScale(80),
excelize.TopLeftCell("C3"), TopLeftCell("C3"),
); err != nil { ); err != nil {
fmt.Println(err) fmt.Println(err)
} }
var zoomScale excelize.ZoomScale var zoomScale ZoomScale
fmt.Println("Default:") fmt.Println("Default:")
fmt.Println("- zoomScale: 80") 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) fmt.Println(err)
} }
@ -65,7 +63,7 @@ func ExampleFile_SetSheetViewOptions() {
fmt.Println("Used out of range value:") fmt.Println("Used out of range value:")
fmt.Println("- zoomScale:", zoomScale) 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) fmt.Println(err)
} }
@ -87,18 +85,18 @@ func ExampleFile_SetSheetViewOptions() {
} }
func ExampleFile_GetSheetViewOptions() { func ExampleFile_GetSheetViewOptions() {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
var ( var (
defaultGridColor excelize.DefaultGridColor defaultGridColor DefaultGridColor
rightToLeft excelize.RightToLeft rightToLeft RightToLeft
showFormulas excelize.ShowFormulas showFormulas ShowFormulas
showGridLines excelize.ShowGridLines showGridLines ShowGridLines
showZeros excelize.ShowZeros showZeros ShowZeros
showRowColHeaders excelize.ShowRowColHeaders showRowColHeaders ShowRowColHeaders
zoomScale excelize.ZoomScale zoomScale ZoomScale
topLeftCell excelize.TopLeftCell topLeftCell TopLeftCell
) )
if err := f.GetSheetViewOptions(sheet, 0, if err := f.GetSheetViewOptions(sheet, 0,
@ -124,7 +122,7 @@ func ExampleFile_GetSheetViewOptions() {
fmt.Println("- zoomScale:", zoomScale) fmt.Println("- zoomScale:", zoomScale)
fmt.Println("- topLeftCell:", `"`+topLeftCell+`"`) 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) fmt.Println(err)
} }
@ -132,7 +130,7 @@ func ExampleFile_GetSheetViewOptions() {
fmt.Println(err) 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) fmt.Println(err)
} }
@ -140,7 +138,7 @@ func ExampleFile_GetSheetViewOptions() {
fmt.Println(err) 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) fmt.Println(err)
} }
@ -170,7 +168,7 @@ func ExampleFile_GetSheetViewOptions() {
} }
func TestSheetViewOptionsErrors(t *testing.T) { func TestSheetViewOptionsErrors(t *testing.T) {
f := excelize.NewFile() f := NewFile()
const sheet = "Sheet1" const sheet = "Sheet1"
assert.NoError(t, f.GetSheetViewOptions(sheet, 0)) assert.NoError(t, f.GetSheetViewOptions(sheet, 0))

104
styles.go
View File

@ -21,6 +21,7 @@ import (
"log" "log"
"math" "math"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
@ -755,7 +756,7 @@ var currencyNumFmt = map[int]string{
// builtInNumFmtFunc defined the format conversion functions map. Partial format // builtInNumFmtFunc defined the format conversion functions map. Partial format
// code doesn't support currently and will return original string. // 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, 0: formatToString,
1: formatToInt, 1: formatToInt,
2: formatToFloat, 2: formatToFloat,
@ -847,14 +848,14 @@ var criteriaType = map[string]string{
// formatToString provides a function to return original string by given // formatToString provides a function to return original string by given
// built-in number formats code and cell string. // built-in number formats code and cell string.
func formatToString(i int, v string) string { func formatToString(v string, format string) string {
return v return v
} }
// formatToInt provides a function to convert original string to integer // formatToInt provides a function to convert original string to integer
// format as string type by given built-in number formats code and cell // format as string type by given built-in number formats code and cell
// string. // string.
func formatToInt(i int, v string) string { func formatToInt(v string, format string) string {
f, err := strconv.ParseFloat(v, 64) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -865,7 +866,7 @@ func formatToInt(i int, v string) string {
// formatToFloat provides a function to convert original string to float // formatToFloat provides a function to convert original string to float
// format as string type by given built-in number formats code and cell // format as string type by given built-in number formats code and cell
// string. // string.
func formatToFloat(i int, v string) string { func formatToFloat(v string, format string) string {
f, err := strconv.ParseFloat(v, 64) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -875,7 +876,7 @@ func formatToFloat(i int, v string) string {
// formatToA provides a function to convert original string to special format // formatToA provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string. // 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) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -890,7 +891,7 @@ func formatToA(i int, v string) string {
// formatToB provides a function to convert original string to special format // formatToB provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string. // 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) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -903,7 +904,7 @@ func formatToB(i int, v string) string {
// formatToC provides a function to convert original string to special format // formatToC provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string. // 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) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -914,7 +915,7 @@ func formatToC(i int, v string) string {
// formatToD provides a function to convert original string to special format // formatToD provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string. // 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) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -925,7 +926,7 @@ func formatToD(i int, v string) string {
// formatToE provides a function to convert original string to special format // formatToE provides a function to convert original string to special format
// as string type by given built-in number formats code and cell string. // 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) f, err := strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
@ -933,6 +934,8 @@ func formatToE(i int, v string) string {
return fmt.Sprintf("%.e", f) return fmt.Sprintf("%.e", f)
} }
var dateTimeFormatsCache = map[string]string{}
// parseTime provides a function to returns a string parsed using time.Time. // parseTime provides a function to returns a string parsed using time.Time.
// Replace Excel placeholders with Go time placeholders. For example, replace // 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 // yyyy with 2006. These are in a specific order, due to the fact that m is
@ -944,15 +947,46 @@ func formatToE(i int, v string) string {
// arbitrary characters unused in Excel Date formats, and then at the end, // arbitrary characters unused in Excel Date formats, and then at the end,
// turn them to what they should actually be. Based off: // turn them to what they should actually be. Based off:
// http://www.ozgrid.com/Excel/CustomFormats.htm // http://www.ozgrid.com/Excel/CustomFormats.htm
func parseTime(i int, v string) string { func parseTime(v string, format string) string {
f, err := strconv.ParseFloat(v, 64) var (
f float64
err error
goFmt string
)
f, err = strconv.ParseFloat(v, 64)
if err != nil { if err != nil {
return v return v
} }
val := timeFromExcelTime(f, false) 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 }{ replacements := []struct{ xltime, gotime string }{
{"YYYY", "2006"},
{"YY", "06"},
{"MM", "01"},
{"M", "1"},
{"DD", "02"},
{"D", "2"},
{"yyyy", "2006"}, {"yyyy", "2006"},
{"yy", "06"}, {"yy", "06"},
{"mmmm", "%%%%"}, {"mmmm", "%%%%"},
@ -962,38 +996,59 @@ func parseTime(i int, v string) string {
{"mmm", "Jan"}, {"mmm", "Jan"},
{"mmss", "0405"}, {"mmss", "0405"},
{"ss", "05"}, {"ss", "05"},
{"s", "5"},
{"mm:", "04:"}, {"mm:", "04:"},
{":mm", ":04"}, {":mm", ":04"},
{"m:", "4:"},
{":m", ":4"},
{"mm", "01"}, {"mm", "01"},
{"am/pm", "pm"}, {"am/pm", "pm"},
{"m/", "1/"}, {"m/", "1/"},
{"%%%%", "January"}, {"%%%%", "January"},
{"&&&&", "Monday"}, {"&&&&", "Monday"},
} }
replacementsGlobal := []struct{ xltime, gotime string }{
{"\\-", "-"},
{"\\ ", " "},
{"\\.", "."},
{"\\", ""},
}
// It is the presence of the "am/pm" indicator that determines if this is // 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. // a 12 hour or 24 hours time format, not the number of 'h' characters.
if is12HourTime(format) { if is12HourTime(format) {
format = strings.Replace(format, "hh", "03", 1) goFmt = strings.Replace(goFmt, "hh", "3", 1)
format = strings.Replace(format, "h", "3", 1) goFmt = strings.Replace(goFmt, "h", "3", 1)
goFmt = strings.Replace(goFmt, "HH", "3", 1)
goFmt = strings.Replace(goFmt, "H", "3", 1)
} else { } else {
format = strings.Replace(format, "hh", "15", 1) goFmt = strings.Replace(goFmt, "hh", "15", 1)
format = strings.Replace(format, "h", "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 { 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 // If the hour is optional, strip it out, along with the possible dangling
// colon that would remain. // colon that would remain.
if val.Hour() < 1 { if val.Hour() < 1 {
format = strings.Replace(format, "]:", "]", 1) goFmt = strings.Replace(goFmt, "]:", "]", 1)
format = strings.Replace(format, "[03]", "", 1) goFmt = strings.Replace(goFmt, "[03]", "", 1)
format = strings.Replace(format, "[3]", "", 1) goFmt = strings.Replace(goFmt, "[3]", "", 1)
format = strings.Replace(format, "[15]", "", 1) goFmt = strings.Replace(goFmt, "[15]", "", 1)
} else { } else {
format = strings.Replace(format, "[3]", "3", 1) goFmt = strings.Replace(goFmt, "[3]", "3", 1)
format = strings.Replace(format, "[15]", "15", 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. // is12HourTime checks whether an Excel time format string is a 12 hours form.
@ -2226,6 +2281,7 @@ func newNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
// setCustomNumFmt provides a function to set custom number format code. // setCustomNumFmt provides a function to set custom number format code.
func setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int { func setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
nf := xlsxNumFmt{FormatCode: *style.CustomNumFmt} nf := xlsxNumFmt{FormatCode: *style.CustomNumFmt}
if styleSheet.NumFmts != nil { if styleSheet.NumFmts != nil {
nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1 nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1
styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf) styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf)

View File

@ -201,10 +201,44 @@ func TestNewStyle(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
_, err = f.NewStyle(Style{}) _, err = f.NewStyle(Style{})
assert.EqualError(t, err, "invalid parameter type") assert.EqualError(t, err, "invalid parameter type")
_, err = f.NewStyle(&Style{Font: &Font{Family: strings.Repeat("s", MaxFontFamilyLength+1)}}) _, 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") 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}}) _, err = f.NewStyle(&Style{Font: &Font{Size: MaxFontSize + 1}})
assert.EqualError(t, err, "font size must be between 1 and 409 points") 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) { func TestGetDefaultFont(t *testing.T) {
@ -250,3 +284,15 @@ func TestGetStyleID(t *testing.T) {
func TestGetFillID(t *testing.T) { func TestGetFillID(t *testing.T) {
assert.Equal(t, -1, getFillID(NewFile().stylesReader(), &Style{Fill: Fill{Type: "unknown"}})) 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"))
}