Skip to content

Commit

Permalink
WIP add support for new icon sets
Browse files Browse the repository at this point in the history
  • Loading branch information
imirkin committed Dec 11, 2024
1 parent 3ca60f8 commit c2f09da
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 16 deletions.
67 changes: 54 additions & 13 deletions styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,26 @@ var (
"5Quarters": cfvo5,
"5Rating": cfvo5,
}

// cfvo3 defined the icon set conditional formatting rules.
x14cfvo3 = &xlsxX14CfRule{IconSet: &xlsx14IconSet{Cfvo: []*xlsx14Cfvo{
{Type: "percent", Val: "0"},
{Type: "percent", Val: "33"},
{Type: "percent", Val: "67"},
}}}
// cfvo5 defined the icon set conditional formatting rules.
x14cfvo5 = &xlsxX14CfRule{IconSet: &xlsx14IconSet{Cfvo: []*xlsx14Cfvo{
{Type: "percent", Val: "0"},
{Type: "percent", Val: "20"},
{Type: "percent", Val: "40"},
{Type: "percent", Val: "60"},
{Type: "percent", Val: "80"},
}}}
condFmtNewIconSetPresets = map[string]*xlsxX14CfRule{
"3Stars": x14cfvo3,
"3Triangles": x14cfvo3,
"5Boxes": x14cfvo5,
}
)

// colorChoice returns a hex color code from the actual color values.
Expand Down Expand Up @@ -2764,17 +2784,20 @@ func (f *File) SetCellStyle(sheet, topLeftCell, bottomRightCell string, styleID
// 3ArrowsGray
// 3Flags
// 3Signs
// 3Stars
// 3Symbols
// 3Symbols2
// 3TrafficLights1
// 3TrafficLights2
// 3Triangles
// 4Arrows
// 4ArrowsGray
// 4Rating
// 4RedToBlack
// 4TrafficLights
// 5Arrows
// 5ArrowsGray
// 5Boxes
// 5Quarters
// 5Rating
//
Expand Down Expand Up @@ -2802,6 +2825,7 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
}
var (
cfRule []*xlsxCfRule
x14CfRule []*xlsxX14CfRule
noCriteriaTypes = []string{
"containsBlanks",
"notContainsBlanks",
Expand All @@ -2825,16 +2849,15 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
priority := rules + i
rule, x14rule := drawFunc(priority, ct, mastCell,
fmt.Sprintf("{00000000-0000-0000-%04X-%012X}", f.getSheetID(sheet), priority), &opt)
if rule == nil {
if rule == nil && x14rule == nil {
return ErrParameterInvalid
}
if x14rule != nil {
if err = f.appendCfRule(ws, x14rule); err != nil {
return err
}
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
x14CfRule = append(x14CfRule, x14rule)
}
if rule != nil {
cfRule = append(cfRule, rule)
}
cfRule = append(cfRule, rule)
continue
}
}
Expand All @@ -2843,10 +2866,19 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
return ErrParameterInvalid
}

ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{
SQRef: SQRef,
CfRule: cfRule,
})
if len(cfRule) > 0 {
ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{
SQRef: SQRef,
CfRule: cfRule,
})
}

if len(x14CfRule) > 0 {
if err = f.appendCfRule(ws, x14CfRule, SQRef); err != nil {
return err
}
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
}
return err
}

Expand Down Expand Up @@ -2892,7 +2924,7 @@ func prepareConditionalFormatRange(rangeRef string) (string, string, error) {
}

// appendCfRule provides a function to append rules to conditional formatting.
func (f *File) appendCfRule(ws *xlsxWorksheet, rule *xlsxX14CfRule) error {
func (f *File) appendCfRule(ws *xlsxWorksheet, rules []*xlsxX14CfRule, SQRef string) error {
var (
err error
idx int
Expand All @@ -2904,7 +2936,7 @@ func (f *File) appendCfRule(ws *xlsxWorksheet, rule *xlsxX14CfRule) error {
condFmtBytes, condFmtsBytes, extLstBytes []byte
)
condFmtBytes, _ = xml.Marshal([]*xlsxX14ConditionalFormatting{
{XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value, CfRule: []*xlsxX14CfRule{rule}},
{XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value, CfRule: rules, SQRef: SQRef},
})
if ws.ExtLst != nil { // append mode ext
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
Expand Down Expand Up @@ -3467,7 +3499,16 @@ func drawCondFmtNoBlanks(p int, ct, ref, GUID string, format *ConditionalFormatO
func drawCondFmtIconSet(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
cfRule, ok := condFmtIconSetPresets[format.IconStyle]
if !ok {
return nil, nil
x14CfRule, ok := condFmtNewIconSetPresets[format.IconStyle]
if !ok {
return nil, nil
}
x14CfRule.Priority = p + 1
x14CfRule.IconSet.IconSet = format.IconStyle
x14CfRule.IconSet.Reverse = format.ReverseIcons
x14CfRule.IconSet.ShowValue = boolPtr(!format.IconsOnly)
x14CfRule.Type = validType[format.Type]
return nil, x14CfRule
}
cfRule.Priority = p + 1
cfRule.IconSet.IconSet = format.IconStyle
Expand Down
10 changes: 10 additions & 0 deletions styles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ func TestSetConditionalFormat(t *testing.T) {
for _, ref := range []string{"A1:A2", "B1:B2"} {
assert.NoError(t, f.SetConditionalFormat("Sheet1", ref, condFmts))
}
// Test creating a conditional format with a "new" icon set
f = NewFile()
condFmts = []ConditionalFormatOptions{
{Type: "icon_set", IconStyle: "3Triangles"},
}
for _, ref := range []string{"A1:A2", "B1:B2"} {
assert.NoError(t, f.SetConditionalFormat("Sheet1", ref, condFmts))
}

f = NewFile()
// Test creating a conditional format without cell reference
assert.Equal(t, ErrParameterRequired, f.SetConditionalFormat("Sheet1", "", nil))
Expand Down Expand Up @@ -274,6 +283,7 @@ func TestGetConditionalFormats(t *testing.T) {
{{Type: "errors", Format: intPtr(1)}},
{{Type: "no_errors", Format: intPtr(1)}},
{{Type: "icon_set", IconStyle: "3Arrows", ReverseIcons: true, IconsOnly: true}},
// TODO: {{Type: "icon_set", IconStyle: "3Triangles", ReverseIcons: true, IconsOnly: true}},
} {
f := NewFile()
err := f.SetConditionalFormat("Sheet1", "A2:A1,B:B,2:2", format)
Expand Down
25 changes: 22 additions & 3 deletions xmlWorksheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,13 +772,16 @@ type xlsxX14ConditionalFormatting struct {
XMLName xml.Name `xml:"x14:conditionalFormatting"`
XMLNSXM string `xml:"xmlns:xm,attr"`
CfRule []*xlsxX14CfRule `xml:"x14:cfRule"`
SQRef string `xml:"xm:sqref"`
}

// xlsxX14CfRule directly maps the cfRule element.
type xlsxX14CfRule struct {
Type string `xml:"type,attr,omitempty"`
ID string `xml:"id,attr,omitempty"`
DataBar *xlsx14DataBar `xml:"x14:dataBar"`
Type string `xml:"type,attr,omitempty"`
ID string `xml:"id,attr,omitempty"`
Priority int `xml:"priority,attr,omitempty"`
DataBar *xlsx14DataBar `xml:"x14:dataBar"`
IconSet *xlsx14IconSet `xml:"x14:iconSet"`
}

// xlsx14DataBar directly maps the dataBar element.
Expand All @@ -795,6 +798,22 @@ type xlsx14DataBar struct {
AxisColor *xlsxColor `xml:"x14:axisColor"`
}

// xlsxIconSet (Icon Set) describes an icon set conditional formatting rule.
type xlsx14IconSet struct {
Cfvo []*xlsx14Cfvo `xml:"x14:cfvo"`
IconSet string `xml:"iconSet,attr,omitempty"`
ShowValue *bool `xml:"showValue,attr"`
Reverse bool `xml:"reverse,attr,omitempty"`
}

// cfvo (Conditional Format Value Object) describes the values of the
// interpolation points in a gradient scale.
type xlsx14Cfvo struct {
Gte bool `xml:"gte,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Val string `xml:"xm:f"`
}

// xlsxX14SparklineGroups directly maps the sparklineGroups element.
type xlsxX14SparklineGroups struct {
XMLName xml.Name `xml:"x14:sparklineGroups"`
Expand Down

0 comments on commit c2f09da

Please sign in to comment.