Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP add support for new icon sets #2042

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 91 additions & 13 deletions styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,26 @@
"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 @@
// 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 @@
}
var (
cfRule []*xlsxCfRule
x14CfRule []*xlsxX14CfRule
noCriteriaTypes = []string{
"containsBlanks",
"notContainsBlanks",
Expand All @@ -2825,16 +2849,15 @@
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 @@
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 @@
}

// 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 @@
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 @@ -3155,6 +3187,20 @@
return format
}

// extractCondFmtIconSetRule provides a function to extract conditional format
// settings for icon set by given conditional formatting rule extension list.
func (f *File) extractCondFmtIconSetRule(c *decodeX14CfRule) ConditionalFormatOptions {
format := ConditionalFormatOptions{Type: "icon_set"}
if c.IconSet != nil {
if c.IconSet.ShowValue != nil {
format.IconsOnly = !*c.IconSet.ShowValue
}
format.IconStyle = c.IconSet.IconSet
format.ReverseIcons = c.IconSet.Reverse
}
return format
}

// extractCondFmtIconSet provides a function to extract conditional format
// settings for icon sets by given conditional formatting rule.
func (f *File) extractCondFmtIconSet(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
Expand Down Expand Up @@ -3186,6 +3232,29 @@
}
conditionalFormats[cf.SQRef] = opts
}
if ws.ExtLst != nil {
decodeExtLst := new(decodeExtLst)
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
Decode(decodeExtLst); err != nil && err != io.EOF {
return conditionalFormats, err
}

Check warning on line 3240 in styles.go

View check run for this annotation

Codecov / codecov/patch

styles.go#L3239-L3240

Added lines #L3239 - L3240 were not covered by tests
for _, ext := range decodeExtLst.Ext {
if ext.URI == ExtURIConditionalFormattings {
decodeCondFmts := new(decodeX14ConditionalFormattingRules)
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeCondFmts)
for _, condFmt := range decodeCondFmts.CondFmt {
var opts []ConditionalFormatOptions
for _, rule := range condFmt.CfRule {
if rule.Type == "iconSet" {
opts = append(opts, f.extractCondFmtIconSetRule(rule))
}
}
conditionalFormats[condFmt.SQRef] = append(conditionalFormats[condFmt.SQRef], opts...)
}
}
}
}

return conditionalFormats, err
}

Expand Down Expand Up @@ -3467,7 +3536,16 @@
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}},
{{Type: "icon_set", IconStyle: "3Triangles", ReverseIcons: true, IconsOnly: true}},
} {
f := NewFile()
err := f.SetConditionalFormat("Sheet1", "A2:A1,B:B,2:2", format)
Expand Down
53 changes: 46 additions & 7 deletions xmlWorksheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -735,14 +735,17 @@ type decodeX14ConditionalFormattingRules struct {
type decodeX14ConditionalFormatting struct {
XMLName xml.Name `xml:"conditionalFormatting"`
CfRule []*decodeX14CfRule `xml:"cfRule"`
SQRef string `xml:"sqref"`
}

// decodeX14CfRule directly maps the cfRule element.
type decodeX14CfRule struct {
XMLName xml.Name `xml:"cfRule"`
Type string `xml:"type,attr,omitempty"`
ID string `xml:"id,attr,omitempty"`
DataBar *decodeX14DataBar `xml:"dataBar"`
XMLName xml.Name `xml:"cfRule"`
Type string `xml:"type,attr,omitempty"`
ID string `xml:"id,attr,omitempty"`
Priority int `xml:"priority,attr,omitempty"`
DataBar *decodeX14DataBar `xml:"dataBar"`
IconSet *decodeX14IconSet `xml:"iconSet"`
}

// decodeX14DataBar directly maps the dataBar element.
Expand All @@ -760,6 +763,23 @@ type decodeX14DataBar struct {
AxisColor *xlsxColor `xml:"axisColor"`
}

// decodeX14IconSet directly maps the iconSet element.
type decodeX14IconSet struct {
XMLName xml.Name `xml:"iconSet"`
Cfvo []*decodeX14Cfvo `xml:"cfvo"`
IconSet string `xml:"iconSet,attr,omitempty"`
ShowValue *bool `xml:"showValue,attr"`
Reverse bool `xml:"reverse,attr,omitempty"`
}

// decodeX14Cfvo directly maps the cfvo element.
type decodeX14Cfvo struct {
XMLName xml.Name `xml:"cfvo"`
Gte bool `xml:"gte,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Val string `xml:"f"`
}

// xlsxX14ConditionalFormattings directly maps the conditionalFormattings
// element.
type xlsxX14ConditionalFormattings struct {
Expand All @@ -772,13 +792,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 +818,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