This closes #1690, new exported function `GetConditionalStyle`

- Support get the conditional format style definition
- Update the unit test
This commit is contained in:
xuri 2023-10-17 08:52:34 +08:00
parent 27f1056929
commit b52db71d61
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
2 changed files with 158 additions and 94 deletions

230
styles.go
View File

@ -1138,6 +1138,31 @@ var (
return reflect.DeepEqual(xf.Protection, newProtection(style)) && xf.ApplyProtection != nil && *xf.ApplyProtection
},
}
// extractStyleCondFuncs provides a function set to returns if shoudle be
// extract style definition by given style.
extractStyleCondFuncs = map[string]func(xlsxXf, *xlsxStyleSheet) bool{
"fill": func(xf xlsxXf, s *xlsxStyleSheet) bool {
return xf.ApplyFill != nil && *xf.ApplyFill &&
xf.FillID != nil && s.Fills != nil &&
*xf.FillID < len(s.Fills.Fill)
},
"border": func(xf xlsxXf, s *xlsxStyleSheet) bool {
return xf.ApplyBorder != nil && *xf.ApplyBorder &&
xf.BorderID != nil && s.Borders != nil &&
*xf.BorderID < len(s.Borders.Border)
},
"font": func(xf xlsxXf, s *xlsxStyleSheet) bool {
return xf.ApplyFont != nil && *xf.ApplyFont &&
xf.FontID != nil && s.Fonts != nil &&
*xf.FontID < len(s.Fonts.Font)
},
"alignment": func(xf xlsxXf, s *xlsxStyleSheet) bool {
return xf.ApplyAlignment != nil && *xf.ApplyAlignment
},
"protection": func(xf xlsxXf, s *xlsxStyleSheet) bool {
return xf.ApplyProtection != nil && *xf.ApplyProtection
},
}
// drawContFmtFunc defines functions to create conditional formats.
drawContFmtFunc = map[string]func(p int, ct, GUID string, fmtCond *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule){
"cellIs": drawCondFmtCellIs,
@ -1205,44 +1230,39 @@ func (f *File) getThemeColor(clr *xlsxColor) string {
// extractBorders provides a function to extract borders styles settings by
// given border styles definition.
func (f *File) extractBorders(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
if xf.ApplyBorder != nil && *xf.ApplyBorder &&
xf.BorderID != nil && s.Borders != nil &&
*xf.BorderID < len(s.Borders.Border) {
if bdr := s.Borders.Border[*xf.BorderID]; bdr != nil {
var borders []Border
extractBorder := func(lineType string, line xlsxLine) {
if line.Style != "" {
borders = append(borders, Border{
Type: lineType,
Color: f.getThemeColor(line.Color),
Style: inStrSlice(styleBorders, line.Style, false),
})
}
func (f *File) extractBorders(bdr *xlsxBorder, s *xlsxStyleSheet, style *Style) {
if bdr != nil {
var borders []Border
extractBorder := func(lineType string, line xlsxLine) {
if line.Style != "" {
borders = append(borders, Border{
Type: lineType,
Color: f.getThemeColor(line.Color),
Style: inStrSlice(styleBorders, line.Style, false),
})
}
for i, line := range []xlsxLine{
bdr.Left, bdr.Right, bdr.Top, bdr.Bottom, bdr.Diagonal, bdr.Diagonal,
} {
if i < 4 {
extractBorder(styleBorderTypes[i], line)
}
if i == 4 && bdr.DiagonalUp {
extractBorder(styleBorderTypes[i], line)
}
if i == 5 && bdr.DiagonalDown {
extractBorder(styleBorderTypes[i], line)
}
}
style.Border = borders
}
for i, line := range []xlsxLine{
bdr.Left, bdr.Right, bdr.Top, bdr.Bottom, bdr.Diagonal, bdr.Diagonal,
} {
if i < 4 {
extractBorder(styleBorderTypes[i], line)
}
if i == 4 && bdr.DiagonalUp {
extractBorder(styleBorderTypes[i], line)
}
if i == 5 && bdr.DiagonalDown {
extractBorder(styleBorderTypes[i], line)
}
}
style.Border = borders
}
}
// extractFills provides a function to extract fill styles settings by
// given fill styles definition.
func (f *File) extractFills(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
if fl := s.Fills.Fill[*xf.FillID]; fl != nil {
func (f *File) extractFills(fl *xlsxFill, s *xlsxStyleSheet, style *Style) {
if fl != nil {
var fill Fill
if fl.GradientFill != nil {
fill.Type = "gradient"
@ -1274,46 +1294,42 @@ func (f *File) extractFills(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
// extractFont provides a function to extract font styles settings by given
// font styles definition.
func (f *File) extractFont(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
if xf.ApplyFont != nil && *xf.ApplyFont &&
xf.FontID != nil && s.Fonts != nil &&
*xf.FontID < len(s.Fonts.Font) {
if fnt := s.Fonts.Font[*xf.FontID]; fnt != nil {
var font Font
if fnt.B != nil {
font.Bold = fnt.B.Value()
}
if fnt.I != nil {
font.Italic = fnt.I.Value()
}
if fnt.U != nil {
font.Underline = fnt.U.Value()
}
if fnt.Name != nil {
font.Family = fnt.Name.Value()
}
if fnt.Sz != nil {
font.Size = fnt.Sz.Value()
}
if fnt.Strike != nil {
font.Strike = fnt.Strike.Value()
}
if fnt.Color != nil {
font.Color = strings.TrimPrefix(fnt.Color.RGB, "FF")
font.ColorIndexed = fnt.Color.Indexed
font.ColorTheme = fnt.Color.Theme
font.ColorTint = fnt.Color.Tint
}
style.Font = &font
func (f *File) extractFont(fnt *xlsxFont, s *xlsxStyleSheet, style *Style) {
if fnt != nil {
var font Font
if fnt.B != nil {
font.Bold = fnt.B.Value()
}
if fnt.I != nil {
font.Italic = fnt.I.Value()
}
if fnt.U != nil {
font.Underline = fnt.U.Value()
}
if fnt.Name != nil {
font.Family = fnt.Name.Value()
}
if fnt.Sz != nil {
font.Size = fnt.Sz.Value()
}
if fnt.Strike != nil {
font.Strike = fnt.Strike.Value()
}
if fnt.Color != nil {
font.Color = strings.TrimPrefix(fnt.Color.RGB, "FF")
font.ColorIndexed = fnt.Color.Indexed
font.ColorTheme = fnt.Color.Theme
font.ColorTint = fnt.Color.Tint
}
style.Font = &font
}
}
// extractNumFmt provides a function to extract number format by given styles
// definition.
func (f *File) extractNumFmt(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
if xf.NumFmtID != nil {
numFmtID := *xf.NumFmtID
func (f *File) extractNumFmt(n *int, s *xlsxStyleSheet, style *Style) {
if n != nil {
numFmtID := *n
if _, ok := builtInNumFmt[numFmtID]; ok || isLangNumFmt(numFmtID) {
style.NumFmt = numFmtID
return
@ -1339,32 +1355,32 @@ func (f *File) extractNumFmt(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
// extractAlignment provides a function to extract alignment format by
// given style definition.
func (f *File) extractAlignment(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
if xf.ApplyAlignment != nil && *xf.ApplyAlignment && xf.Alignment != nil {
func (f *File) extractAlignment(a *xlsxAlignment, s *xlsxStyleSheet, style *Style) {
if a != nil {
style.Alignment = &Alignment{
Horizontal: xf.Alignment.Horizontal,
Indent: xf.Alignment.Indent,
JustifyLastLine: xf.Alignment.JustifyLastLine,
ReadingOrder: xf.Alignment.ReadingOrder,
RelativeIndent: xf.Alignment.RelativeIndent,
ShrinkToFit: xf.Alignment.ShrinkToFit,
TextRotation: xf.Alignment.TextRotation,
Vertical: xf.Alignment.Vertical,
WrapText: xf.Alignment.WrapText,
Horizontal: a.Horizontal,
Indent: a.Indent,
JustifyLastLine: a.JustifyLastLine,
ReadingOrder: a.ReadingOrder,
RelativeIndent: a.RelativeIndent,
ShrinkToFit: a.ShrinkToFit,
TextRotation: a.TextRotation,
Vertical: a.Vertical,
WrapText: a.WrapText,
}
}
}
// extractProtection provides a function to extract protection settings by
// given format definition.
func (f *File) extractProtection(xf xlsxXf, s *xlsxStyleSheet, style *Style) {
if xf.ApplyProtection != nil && *xf.ApplyProtection && xf.Protection != nil {
func (f *File) extractProtection(p *xlsxProtection, s *xlsxStyleSheet, style *Style) {
if p != nil {
style.Protection = &Protection{}
if xf.Protection.Hidden != nil {
style.Protection.Hidden = *xf.Protection.Hidden
if p.Hidden != nil {
style.Protection.Hidden = *p.Hidden
}
if xf.Protection.Locked != nil {
style.Protection.Locked = *xf.Protection.Locked
if p.Locked != nil {
style.Protection.Locked = *p.Locked
}
}
}
@ -1383,16 +1399,22 @@ func (f *File) GetStyle(idx int) (*Style, error) {
}
style = &Style{}
xf := s.CellXfs.Xf[idx]
if xf.ApplyFill != nil && *xf.ApplyFill &&
xf.FillID != nil && s.Fills != nil &&
*xf.FillID < len(s.Fills.Fill) {
f.extractFills(xf, s, style)
if extractStyleCondFuncs["fill"](xf, s) {
f.extractFills(s.Fills.Fill[*xf.FillID], s, style)
}
f.extractBorders(xf, s, style)
f.extractFont(xf, s, style)
f.extractAlignment(xf, s, style)
f.extractProtection(xf, s, style)
f.extractNumFmt(xf, s, style)
if extractStyleCondFuncs["border"](xf, s) {
f.extractBorders(s.Borders.Border[*xf.BorderID], s, style)
}
if extractStyleCondFuncs["font"](xf, s) {
f.extractFont(s.Fonts.Font[*xf.FontID], s, style)
}
if extractStyleCondFuncs["alignment"](xf, s) {
f.extractAlignment(xf.Alignment, s, style)
}
if extractStyleCondFuncs["protection"](xf, s) {
f.extractProtection(xf.Protection, s, style)
}
f.extractNumFmt(xf.NumFmtID, s, style)
return style, nil
}
@ -1470,6 +1492,32 @@ func (f *File) NewConditionalStyle(style *Style) (int, error) {
return s.Dxfs.Count - 1, nil
}
// GetConditionalStyle returns conditional format style definition by specified
// style index.
func (f *File) GetConditionalStyle(idx int) (*Style, error) {
var style *Style
f.mu.Lock()
s, err := f.stylesReader()
if err != nil {
return style, err
}
f.mu.Unlock()
if idx < 0 || s.Dxfs == nil || len(s.Dxfs.Dxfs) <= idx {
return style, newInvalidStyleID(idx)
}
style = &Style{}
xf := s.Dxfs.Dxfs[idx]
f.extractFills(xf.Fill, s, style)
f.extractBorders(xf.Border, s, style)
f.extractFont(xf.Font, s, style)
f.extractAlignment(xf.Alignment, s, style)
f.extractProtection(xf.Protection, s, style)
if xf.NumFmt != nil {
f.extractNumFmt(&xf.NumFmt.NumFmtID, s, style)
}
return style, nil
}
// newDxfNumFmt provides a function to create number format for conditional
// format styles.
func newDxfNumFmt(styleSheet *xlsxStyleSheet, style *Style, dxf *xlsxDxf) *xlsxNumFmt {

View File

@ -352,16 +352,24 @@ func TestNewStyle(t *testing.T) {
assert.Equal(t, ErrCellStyles, err)
}
func TestNewConditionalStyle(t *testing.T) {
func TestConditionalStyle(t *testing.T) {
f := NewFile()
_, err := f.NewConditionalStyle(&Style{Protection: &Protection{Hidden: true, Locked: true}})
expected := &Style{Protection: &Protection{Hidden: true, Locked: true}}
idx, err := f.NewConditionalStyle(expected)
assert.NoError(t, err)
style, err := f.GetConditionalStyle(idx)
assert.NoError(t, err)
assert.Equal(t, expected, style)
_, err = f.NewConditionalStyle(&Style{DecimalPlaces: intPtr(4), NumFmt: 165, NegRed: true})
assert.NoError(t, err)
_, err = f.NewConditionalStyle(&Style{DecimalPlaces: intPtr(-1)})
assert.NoError(t, err)
_, err = f.NewConditionalStyle(&Style{NumFmt: 1})
expected = &Style{NumFmt: 1}
idx, err = f.NewConditionalStyle(expected)
assert.NoError(t, err)
style, err = f.GetConditionalStyle(idx)
assert.NoError(t, err)
assert.Equal(t, expected, style)
_, err = f.NewConditionalStyle(&Style{NumFmt: 27})
assert.NoError(t, err)
numFmt := "general"
@ -375,6 +383,14 @@ func TestNewConditionalStyle(t *testing.T) {
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
_, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}})
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
// Test get conditional style with invalid style index
_, err = f.GetConditionalStyle(1)
assert.Equal(t, newInvalidStyleID(1), err)
// Test get conditional style with unsupported charset style sheet
f.Styles = nil
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
_, err = f.GetConditionalStyle(1)
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
}
func TestGetDefaultFont(t *testing.T) {