Skip to content

Commit a546427

Browse files
committed
Resolve qax-os#643, avoid creating duplicate style
1 parent 7f78464 commit a546427

File tree

4 files changed

+215
-50
lines changed

4 files changed

+215
-50
lines changed

excelize_test.go

+8-16
Original file line numberDiff line numberDiff line change
@@ -768,16 +768,14 @@ func TestSetCellStyleCustomNumberFormat(t *testing.T) {
768768
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 42920.5))
769769
assert.NoError(t, f.SetCellValue("Sheet1", "A2", 42920.5))
770770
style, err := f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
771-
if err != nil {
772-
t.Log(err)
773-
}
771+
assert.NoError(t, err)
774772
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
775-
style, err = f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
776-
if err != nil {
777-
t.Log(err)
778-
}
773+
style, err = f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@","font":{"color":"#9A0511"}}`)
774+
assert.NoError(t, err)
779775
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
780776

777+
_, err = f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yy;@"}`)
778+
assert.NoError(t, err)
781779
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleCustomNumberFormat.xlsx")))
782780
}
783781

@@ -790,21 +788,15 @@ func TestSetCellStyleFill(t *testing.T) {
790788
var style int
791789
// Test set fill for cell with invalid parameter.
792790
style, err = f.NewStyle(`{"fill":{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":6}}`)
793-
if !assert.NoError(t, err) {
794-
t.FailNow()
795-
}
791+
assert.NoError(t, err)
796792
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
797793

798794
style, err = f.NewStyle(`{"fill":{"type":"gradient","color":["#FFFFFF"],"shading":1}}`)
799-
if !assert.NoError(t, err) {
800-
t.FailNow()
801-
}
795+
assert.NoError(t, err)
802796
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
803797

804798
style, err = f.NewStyle(`{"fill":{"type":"pattern","color":[],"pattern":1}}`)
805-
if !assert.NoError(t, err) {
806-
t.FailNow()
807-
}
799+
assert.NoError(t, err)
808800
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
809801

810802
style, err = f.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":19}}`)

styles.go

+190-30
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"io"
1919
"log"
2020
"math"
21+
"reflect"
2122
"strconv"
2223
"strings"
2324
)
@@ -1920,30 +1921,110 @@ func (f *File) NewStyle(style interface{}) (int, error) {
19201921
return cellXfsID, errors.New("invalid parameter type")
19211922
}
19221923
s := f.stylesReader()
1923-
numFmtID := setNumFmt(s, fs)
1924+
// check given style already exist.
1925+
if cellXfsID = f.getStyleID(s, fs); cellXfsID != -1 {
1926+
return cellXfsID, err
1927+
}
1928+
1929+
numFmtID := newNumFmt(s, fs)
19241930

19251931
if fs.Font != nil {
1926-
s.Fonts.Count++
1927-
s.Fonts.Font = append(s.Fonts.Font, f.setFont(fs))
1928-
fontID = s.Fonts.Count - 1
1932+
fontID = f.getFontID(s, fs)
1933+
if fontID == -1 {
1934+
s.Fonts.Count++
1935+
s.Fonts.Font = append(s.Fonts.Font, f.newFont(fs))
1936+
fontID = s.Fonts.Count - 1
1937+
}
19291938
}
19301939

1931-
s.Borders.Count++
1932-
s.Borders.Border = append(s.Borders.Border, setBorders(fs))
1933-
borderID = s.Borders.Count - 1
1940+
borderID = getBorderID(s, fs)
1941+
if borderID == -1 {
1942+
if len(fs.Border) == 0 {
1943+
borderID = 0
1944+
} else {
1945+
s.Borders.Count++
1946+
s.Borders.Border = append(s.Borders.Border, newBorders(fs))
1947+
borderID = s.Borders.Count - 1
1948+
}
1949+
}
19341950

1935-
if fill := setFills(fs, true); fill != nil {
1936-
s.Fills.Count++
1937-
s.Fills.Fill = append(s.Fills.Fill, fill)
1938-
fillID = s.Fills.Count - 1
1951+
if fillID = getFillID(s, fs); fillID == -1 {
1952+
if fill := newFills(fs, true); fill != nil {
1953+
s.Fills.Count++
1954+
s.Fills.Fill = append(s.Fills.Fill, fill)
1955+
fillID = s.Fills.Count - 1
1956+
} else {
1957+
fillID = 0
1958+
}
19391959
}
19401960

1941-
applyAlignment, alignment := fs.Alignment != nil, setAlignment(fs)
1942-
applyProtection, protection := fs.Protection != nil, setProtection(fs)
1961+
applyAlignment, alignment := fs.Alignment != nil, newAlignment(fs)
1962+
applyProtection, protection := fs.Protection != nil, newProtection(fs)
19431963
cellXfsID = setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, applyProtection, alignment, protection)
19441964
return cellXfsID, nil
19451965
}
19461966

1967+
var getXfIDFuncs = map[string]func(int, xlsxXf, *Style) bool{
1968+
"numFmt": func(numFmtID int, xf xlsxXf, style *Style) bool {
1969+
return xf.NumFmtID != nil && *xf.NumFmtID == numFmtID
1970+
},
1971+
"font": func(fontID int, xf xlsxXf, style *Style) bool {
1972+
if style.Font == nil {
1973+
return (xf.FontID == nil || *xf.FontID == 0) && (xf.ApplyFont == nil || *xf.ApplyFont == false)
1974+
}
1975+
return xf.FontID != nil && *xf.FontID == fontID && xf.ApplyFont != nil && *xf.ApplyFont == true
1976+
},
1977+
"fill": func(fillID int, xf xlsxXf, style *Style) bool {
1978+
if style.Fill.Type == "" {
1979+
return (xf.FillID == nil || *xf.FillID == 0) && (xf.ApplyFill == nil || *xf.ApplyFill == false)
1980+
}
1981+
return xf.FillID != nil && *xf.FillID == fillID && xf.ApplyFill != nil && *xf.ApplyFill == true
1982+
},
1983+
"border": func(borderID int, xf xlsxXf, style *Style) bool {
1984+
if len(style.Border) == 0 {
1985+
return (xf.BorderID == nil || *xf.BorderID == 0) && (xf.ApplyBorder == nil || *xf.ApplyBorder == false)
1986+
}
1987+
return xf.BorderID != nil && *xf.BorderID == borderID && xf.ApplyBorder != nil && *xf.ApplyBorder == true
1988+
},
1989+
"alignment": func(ID int, xf xlsxXf, style *Style) bool {
1990+
if style.Alignment == nil {
1991+
return xf.ApplyAlignment == nil || *xf.ApplyAlignment == false
1992+
}
1993+
return reflect.DeepEqual(xf.Alignment, newAlignment(style)) && xf.ApplyBorder != nil && *xf.ApplyBorder == true
1994+
},
1995+
"protection": func(ID int, xf xlsxXf, style *Style) bool {
1996+
if style.Protection == nil {
1997+
return xf.ApplyProtection == nil || *xf.ApplyProtection == false
1998+
}
1999+
return reflect.DeepEqual(xf.Protection, newProtection(style)) && xf.ApplyProtection != nil && *xf.ApplyProtection == true
2000+
},
2001+
}
2002+
2003+
// getStyleID provides a function to get styleID by given style. If given
2004+
// style is not exist, will return -1.
2005+
func (f *File) getStyleID(ss *xlsxStyleSheet, style *Style) (styleID int) {
2006+
styleID = -1
2007+
if ss.CellXfs == nil {
2008+
return
2009+
}
2010+
numFmtID, borderID, fillID, fontID := style.NumFmt, getBorderID(ss, style), getFillID(ss, style), f.getFontID(ss, style)
2011+
if style.CustomNumFmt != nil {
2012+
numFmtID = getCustomNumFmtID(ss, style)
2013+
}
2014+
for xfID, xf := range ss.CellXfs.Xf {
2015+
if getXfIDFuncs["numFmt"](numFmtID, xf, style) &&
2016+
getXfIDFuncs["font"](fontID, xf, style) &&
2017+
getXfIDFuncs["fill"](fillID, xf, style) &&
2018+
getXfIDFuncs["border"](borderID, xf, style) &&
2019+
getXfIDFuncs["alignment"](0, xf, style) &&
2020+
getXfIDFuncs["protection"](0, xf, style) {
2021+
styleID = xfID
2022+
return
2023+
}
2024+
}
2025+
return
2026+
}
2027+
19472028
// NewConditionalStyle provides a function to create style for conditional
19482029
// format by given style format. The parameters are the same as function
19492030
// NewStyle(). Note that the color field uses RGB color code and only support
@@ -1955,16 +2036,16 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
19552036
return 0, err
19562037
}
19572038
dxf := dxf{
1958-
Fill: setFills(fs, false),
2039+
Fill: newFills(fs, false),
19592040
}
19602041
if fs.Alignment != nil {
1961-
dxf.Alignment = setAlignment(fs)
2042+
dxf.Alignment = newAlignment(fs)
19622043
}
19632044
if len(fs.Border) > 0 {
1964-
dxf.Border = setBorders(fs)
2045+
dxf.Border = newBorders(fs)
19652046
}
19662047
if fs.Font != nil {
1967-
dxf.Font = f.setFont(fs)
2048+
dxf.Font = f.newFont(fs)
19682049
}
19692050
dxfStr, _ := xml.Marshal(dxf)
19702051
if s.Dxfs == nil {
@@ -2000,9 +2081,25 @@ func (f *File) readDefaultFont() *xlsxFont {
20002081
return s.Fonts.Font[0]
20012082
}
20022083

2003-
// setFont provides a function to add font style by given cell format
2084+
// getFontID provides a function to get font ID.
2085+
// If given font is not exist, will return -1.
2086+
func (f *File) getFontID(styleSheet *xlsxStyleSheet, style *Style) (fontID int) {
2087+
fontID = -1
2088+
if styleSheet.Fonts == nil || style.Font == nil {
2089+
return
2090+
}
2091+
for idx, fnt := range styleSheet.Fonts.Font {
2092+
if reflect.DeepEqual(*fnt, *f.newFont(style)) {
2093+
fontID = idx
2094+
return
2095+
}
2096+
}
2097+
return
2098+
}
2099+
2100+
// newFont provides a function to add font style by given cell format
20042101
// settings.
2005-
func (f *File) setFont(style *Style) *xlsxFont {
2102+
func (f *File) newFont(style *Style) *xlsxFont {
20062103
fontUnderlineType := map[string]string{"single": "single", "double": "double"}
20072104
if style.Font.Size < 1 {
20082105
style.Font.Size = 11
@@ -2036,9 +2133,9 @@ func (f *File) setFont(style *Style) *xlsxFont {
20362133
return &fnt
20372134
}
20382135

2039-
// setNumFmt provides a function to check if number format code in the range
2136+
// newNumFmt provides a function to check if number format code in the range
20402137
// of built-in values.
2041-
func setNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
2138+
func newNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
20422139
dp := "0."
20432140
numFmtID := 164 // Default custom number format code from 164.
20442141
if style.DecimalPlaces < 0 || style.DecimalPlaces > 30 {
@@ -2048,6 +2145,9 @@ func setNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
20482145
dp += "0"
20492146
}
20502147
if style.CustomNumFmt != nil {
2148+
if customNumFmtID := getCustomNumFmtID(styleSheet, style); customNumFmtID != -1 {
2149+
return customNumFmtID
2150+
}
20512151
return setCustomNumFmt(styleSheet, style)
20522152
}
20532153
_, ok := builtInNumFmt[style.NumFmt]
@@ -2102,6 +2202,22 @@ func setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
21022202
return nf.NumFmtID
21032203
}
21042204

2205+
// getCustomNumFmtID provides a function to get custom number format code ID.
2206+
// If given custom number format code is not exist, will return -1.
2207+
func getCustomNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (customNumFmtID int) {
2208+
customNumFmtID = -1
2209+
if styleSheet.NumFmts == nil {
2210+
return
2211+
}
2212+
for _, numFmt := range styleSheet.NumFmts.NumFmt {
2213+
if style.CustomNumFmt != nil && numFmt.FormatCode == *style.CustomNumFmt {
2214+
customNumFmtID = numFmt.NumFmtID
2215+
return
2216+
}
2217+
}
2218+
return
2219+
}
2220+
21052221
// setLangNumFmt provides a function to set number format code with language.
21062222
func setLangNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
21072223
numFmts, ok := langNumFmt[style.Lang]
@@ -2129,9 +2245,29 @@ func setLangNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
21292245
return nf.NumFmtID
21302246
}
21312247

2132-
// setFills provides a function to add fill elements in the styles.xml by
2248+
// getFillID provides a function to get fill ID. If given fill is not
2249+
// exist, will return -1.
2250+
func getFillID(styleSheet *xlsxStyleSheet, style *Style) (fillID int) {
2251+
fillID = -1
2252+
if styleSheet.Fills == nil || style.Fill.Type == "" {
2253+
return
2254+
}
2255+
fills := newFills(style, true)
2256+
if fills == nil {
2257+
return
2258+
}
2259+
for idx, fill := range styleSheet.Fills.Fill {
2260+
if reflect.DeepEqual(fill, fills) {
2261+
fillID = idx
2262+
return
2263+
}
2264+
}
2265+
return
2266+
}
2267+
2268+
// newFills provides a function to add fill elements in the styles.xml by
21332269
// given cell format settings.
2134-
func setFills(style *Style, fg bool) *xlsxFill {
2270+
func newFills(style *Style, fg bool) *xlsxFill {
21352271
var patterns = []string{
21362272
"none",
21372273
"solid",
@@ -2212,11 +2348,11 @@ func setFills(style *Style, fg bool) *xlsxFill {
22122348
return &fill
22132349
}
22142350

2215-
// setAlignment provides a function to formatting information pertaining to
2351+
// newAlignment provides a function to formatting information pertaining to
22162352
// text alignment in cells. There are a variety of choices for how text is
22172353
// aligned both horizontally and vertically, as well as indentation settings,
22182354
// and so on.
2219-
func setAlignment(style *Style) *xlsxAlignment {
2355+
func newAlignment(style *Style) *xlsxAlignment {
22202356
var alignment xlsxAlignment
22212357
if style.Alignment != nil {
22222358
alignment.Horizontal = style.Alignment.Horizontal
@@ -2232,9 +2368,9 @@ func setAlignment(style *Style) *xlsxAlignment {
22322368
return &alignment
22332369
}
22342370

2235-
// setProtection provides a function to set protection properties associated
2371+
// newProtection provides a function to set protection properties associated
22362372
// with the cell.
2237-
func setProtection(style *Style) *xlsxProtection {
2373+
func newProtection(style *Style) *xlsxProtection {
22382374
var protection xlsxProtection
22392375
if style.Protection != nil {
22402376
protection.Hidden = style.Protection.Hidden
@@ -2243,9 +2379,25 @@ func setProtection(style *Style) *xlsxProtection {
22432379
return &protection
22442380
}
22452381

2246-
// setBorders provides a function to add border elements in the styles.xml by
2382+
// getBorderID provides a function to get border ID. If given border is not
2383+
// exist, will return -1.
2384+
func getBorderID(styleSheet *xlsxStyleSheet, style *Style) (borderID int) {
2385+
borderID = -1
2386+
if styleSheet.Borders == nil || len(style.Border) == 0 {
2387+
return
2388+
}
2389+
for idx, border := range styleSheet.Borders.Border {
2390+
if reflect.DeepEqual(*border, *newBorders(style)) {
2391+
borderID = idx
2392+
return
2393+
}
2394+
}
2395+
return
2396+
}
2397+
2398+
// newBorders provides a function to add border elements in the styles.xml by
22472399
// given borders format settings.
2248-
func setBorders(style *Style) *xlsxBorder {
2400+
func newBorders(style *Style) *xlsxBorder {
22492401
var styles = []string{
22502402
"none",
22512403
"thin",
@@ -2308,10 +2460,18 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a
23082460
xf.ApplyNumberFormat = boolPtr(true)
23092461
}
23102462
xf.FillID = intPtr(fillID)
2463+
if fillID != 0 {
2464+
xf.ApplyFill = boolPtr(true)
2465+
}
23112466
xf.BorderID = intPtr(borderID)
2467+
if borderID != 0 {
2468+
xf.ApplyBorder = boolPtr(true)
2469+
}
23122470
style.CellXfs.Count++
23132471
xf.Alignment = alignment
2314-
xf.ApplyAlignment = boolPtr(applyAlignment)
2472+
if alignment != nil {
2473+
xf.ApplyAlignment = boolPtr(applyAlignment)
2474+
}
23152475
if applyProtection {
23162476
xf.ApplyProtection = boolPtr(applyProtection)
23172477
xf.Protection = protection

0 commit comments

Comments
 (0)