diff --git a/sparkline.go b/sparkline.go
index 43a827e..b9879ac 100644
--- a/sparkline.go
+++ b/sparkline.go
@@ -14,6 +14,7 @@ package excelize
import (
"encoding/xml"
"io"
+ "sort"
"strings"
)
@@ -389,15 +390,14 @@ func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup {
// Axis | Show sparkline axis
func (f *File) AddSparkline(sheet string, opts *SparklineOptions) error {
var (
- err error
- ws *xlsxWorksheet
- sparkType string
- sparkTypes map[string]string
- specifiedSparkTypes string
- ok bool
- group *xlsxX14SparklineGroup
- groups *xlsxX14SparklineGroups
- sparklineGroupsBytes, extBytes []byte
+ err error
+ ws *xlsxWorksheet
+ sparkType string
+ sparkTypes map[string]string
+ specifiedSparkTypes string
+ ok bool
+ group *xlsxX14SparklineGroup
+ groups *xlsxX14SparklineGroups
)
// parameter validation
@@ -434,25 +434,8 @@ func (f *File) AddSparkline(sheet string, opts *SparklineOptions) error {
group.RightToLeft = opts.Reverse
}
f.addSparkline(opts, group)
- if ws.ExtLst.Ext != "" { // append mode ext
- if err = f.appendSparkline(ws, group, groups); err != nil {
- return err
- }
- } else {
- groups = &xlsxX14SparklineGroups{
- XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value,
- SparklineGroups: []*xlsxX14SparklineGroup{group},
- }
- if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil {
- return err
- }
- if extBytes, err = xml.Marshal(&xlsxWorksheetExt{
- URI: ExtURISparklineGroups,
- Content: string(sparklineGroupsBytes),
- }); err != nil {
- return err
- }
- ws.ExtLst.Ext = string(extBytes)
+ if err = f.appendSparkline(ws, group, groups); err != nil {
+ return err
}
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
return err
@@ -504,42 +487,50 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
var (
err error
idx int
- decodeExtLst *decodeWorksheetExt
+ appendMode bool
+ decodeExtLst = new(decodeWorksheetExt)
decodeSparklineGroups *decodeX14SparklineGroups
ext *xlsxWorksheetExt
sparklineGroupsBytes, sparklineGroupBytes, extLstBytes []byte
)
- decodeExtLst = new(decodeWorksheetExt)
- if err = f.xmlNewDecoder(strings.NewReader("" + ws.ExtLst.Ext + "")).
- Decode(decodeExtLst); err != nil && err != io.EOF {
- return err
- }
- for idx, ext = range decodeExtLst.Ext {
- if ext.URI == ExtURISparklineGroups {
- decodeSparklineGroups = new(decodeX14SparklineGroups)
- if err = f.xmlNewDecoder(strings.NewReader(ext.Content)).
- Decode(decodeSparklineGroups); err != nil && err != io.EOF {
- return err
- }
- if sparklineGroupBytes, err = xml.Marshal(group); err != nil {
- return err
- }
- if groups == nil {
- groups = &xlsxX14SparklineGroups{}
- }
- groups.XMLNSXM = NameSpaceSpreadSheetExcel2006Main.Value
- groups.Content = decodeSparklineGroups.Content + string(sparklineGroupBytes)
- if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil {
- return err
+ sparklineGroupBytes, _ = xml.Marshal(group)
+ if ws.ExtLst != nil { // append mode ext
+ if err = f.xmlNewDecoder(strings.NewReader("" + ws.ExtLst.Ext + "")).
+ Decode(decodeExtLst); err != nil && err != io.EOF {
+ return err
+ }
+ for idx, ext = range decodeExtLst.Ext {
+ if ext.URI == ExtURISparklineGroups {
+ decodeSparklineGroups = new(decodeX14SparklineGroups)
+ if err = f.xmlNewDecoder(strings.NewReader(ext.Content)).
+ Decode(decodeSparklineGroups); err != nil && err != io.EOF {
+ return err
+ }
+ if groups == nil {
+ groups = &xlsxX14SparklineGroups{}
+ }
+ groups.XMLNSXM = NameSpaceSpreadSheetExcel2006Main.Value
+ groups.Content = decodeSparklineGroups.Content + string(sparklineGroupBytes)
+ sparklineGroupsBytes, _ = xml.Marshal(groups)
+ decodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes)
+ appendMode = true
}
- decodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes)
}
}
- if extLstBytes, err = xml.Marshal(decodeExtLst); err != nil {
- return err
- }
- ws.ExtLst = &xlsxExtLst{
- Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), ""), ""),
+ if !appendMode {
+ sparklineGroupsBytes, _ = xml.Marshal(&xlsxX14SparklineGroups{
+ XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value,
+ SparklineGroups: []*xlsxX14SparklineGroup{group},
+ })
+ decodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxWorksheetExt{
+ URI: ExtURISparklineGroups, Content: string(sparklineGroupsBytes),
+ })
}
+ sort.Slice(decodeExtLst.Ext, func(i, j int) bool {
+ return inStrSlice(extensionURIPriority, decodeExtLst.Ext[i].URI, false) <
+ inStrSlice(extensionURIPriority, decodeExtLst.Ext[j].URI, false)
+ })
+ extLstBytes, err = xml.Marshal(decodeExtLst)
+ ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), ""), "")}
return err
}
diff --git a/sparkline_test.go b/sparkline_test.go
index e6bca25..b1d3d18 100644
--- a/sparkline_test.go
+++ b/sparkline_test.go
@@ -264,23 +264,23 @@ func TestAddSparkline(t *testing.T) {
Range: []string{"Sheet2!A3:E3"},
Style: -1,
}), ErrSparklineStyle.Error())
-
+ // Test creating a conditional format with existing extension lists
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
- ws.(*xlsxWorksheet).ExtLst.Ext = `
-
-
-
-
-
-
-
- `
+ ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: `
+
+ `}
+ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
+ Location: []string{"A3"},
+ Range: []string{"Sheet3!A2:J2"},
+ Type: "column",
+ }))
+ // Test creating a conditional format with invalid extension list characters
+ ws.(*xlsxWorksheet).ExtLst.Ext = ``
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A2"},
Range: []string{"Sheet3!A1:J1"},
- }), "XML syntax error on line 6: element closed by ")
+ }), "XML syntax error on line 1: element closed by ")
}
func TestAppendSparkline(t *testing.T) {
diff --git a/styles.go b/styles.go
index b5752fc..78083f2 100644
--- a/styles.go
+++ b/styles.go
@@ -18,6 +18,7 @@ import (
"io"
"math"
"reflect"
+ "sort"
"strconv"
"strings"
)
@@ -807,6 +808,7 @@ var validType = map[string]string{
"3_color_scale": "3_color_scale",
"data_bar": "dataBar",
"formula": "expression",
+ "iconSet": "iconSet",
}
// criteriaType defined the list of valid criteria types.
@@ -2880,7 +2882,14 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// | MaxType
// | MinValue
// | MaxValue
+// | BarBorderColor
// | BarColor
+// | BarDirection
+// | BarOnly
+// | BarSolid
+// iconSet | IconStyle
+// | ReverseIcons
+// | IconsOnly
// formula | Criteria
//
// The 'Criteria' parameter is used to set the criteria by which the cell data
@@ -3037,7 +3046,8 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// },
// )
//
-// type: duplicate - The duplicate type is used to highlight duplicate cells in a range:
+// type: duplicate - The duplicate type is used to highlight duplicate cells in
+// a range:
//
// // Highlight cells rules: Duplicate Values...
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
@@ -3055,7 +3065,8 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// },
// )
//
-// type: top - The top type is used to specify the top n values by number or percentage in a range:
+// type: top - The top type is used to specify the top n values by number or
+// percentage in a range:
//
// // Top/Bottom rules: Top 10.
// err := f.SetConditionalFormat("Sheet1", "H1:H10",
@@ -3129,7 +3140,10 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// type: data_bar - The data_bar type is used to specify Excel's "Data Bar"
// style conditional format.
//
-// MinType - The MinType and MaxType properties are available when the conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The MidType is available for 3_color_scale. The properties are used as follows:
+// MinType - The MinType and MaxType properties are available when the
+// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.
+// The MidType is available for 3_color_scale. The properties are used as
+// follows:
//
// // Data Bars: Gradient Fill.
// err := f.SetConditionalFormat("Sheet1", "K1:K10",
@@ -3194,11 +3208,41 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// BarBorderColor - Used for sets the color for the border line of a data bar,
// this is only visible in Excel 2010 and later.
//
-// BarOnly - Used for displays a bar data but not the data in the cells.
+// BarDirection - sets the direction for data bars. The available options are:
+//
+// context - Data bar direction is set by spreadsheet application based on the context of the data displayed.
+// leftToRight - Data bar direction is from right to left.
+// rightToLeft - Data bar direction is from left to right.
+//
+// BarOnly - Used for set displays a bar data but not the data in the cells.
//
// BarSolid - Used for turns on a solid (non-gradient) fill for data bars, this
// is only visible in Excel 2010 and later.
//
+// IconStyle - The available options are:
+//
+// 3Arrows
+// 3ArrowsGray
+// 3Flags
+// 3Signs
+// 3Symbols
+// 3Symbols2
+// 3TrafficLights1
+// 3TrafficLights2
+// 4Arrows
+// 4ArrowsGray
+// 4Rating
+// 4RedToBlack
+// 4TrafficLights
+// 5Arrows
+// 5ArrowsGray
+// 5Quarters
+// 5Rating
+//
+// ReverseIcons - Used for set reversed icons sets.
+//
+// IconsOnly - Used for set displayed without the cell value.
+//
// StopIfTrue - used to set the "stop if true" feature of a conditional
// formatting rule when more than one rule is applied to a cell or a range of
// cells. When this parameter is set then subsequent rules are not evaluated
@@ -3214,6 +3258,7 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
"3_color_scale": drawCondFmtColorScale,
"dataBar": drawCondFmtDataBar,
"expression": drawCondFmtExp,
+ "iconSet": drawCondFmtIconSet,
}
ws, err := f.workSheetReader(sheet)
@@ -3235,10 +3280,13 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
if ok {
// Check for valid criteria types.
ct, ok = criteriaType[v.Criteria]
- if ok || vt == "expression" {
+ if ok || vt == "expression" || vt == "iconSet" {
drawFunc, ok := drawContFmtFunc[vt]
if ok {
rule, x14rule := drawFunc(p, ct, GUID, &v)
+ if rule == nil {
+ return ErrParameterInvalid
+ }
if x14rule != nil {
if err = f.appendCfRule(ws, x14rule); err != nil {
return err
@@ -3261,16 +3309,19 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
// appendCfRule provides a function to append rules to conditional formatting.
func (f *File) appendCfRule(ws *xlsxWorksheet, rule *xlsxX14CfRule) error {
var (
- err error
- idx int
- decodeExtLst *decodeWorksheetExt
- condFmts *xlsxX14ConditionalFormattings
- decodeCondFmts *decodeX14ConditionalFormattings
- ext *xlsxWorksheetExt
- condFmtBytes, condFmtsBytes, extLstBytes, extBytes []byte
+ err error
+ idx int
+ appendMode bool
+ decodeExtLst = new(decodeWorksheetExt)
+ condFmts *xlsxX14ConditionalFormattings
+ decodeCondFmts *decodeX14ConditionalFormattings
+ ext *xlsxWorksheetExt
+ condFmtBytes, condFmtsBytes, extLstBytes []byte
)
+ condFmtBytes, _ = xml.Marshal([]*xlsxX14ConditionalFormatting{
+ {XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value, CfRule: []*xlsxX14CfRule{rule}},
+ })
if ws.ExtLst != nil { // append mode ext
- decodeExtLst = new(decodeWorksheetExt)
if err = f.xmlNewDecoder(strings.NewReader("" + ws.ExtLst.Ext + "")).
Decode(decodeExtLst); err != nil && err != io.EOF {
return err
@@ -3279,35 +3330,28 @@ func (f *File) appendCfRule(ws *xlsxWorksheet, rule *xlsxX14CfRule) error {
if ext.URI == ExtURIConditionalFormattings {
decodeCondFmts = new(decodeX14ConditionalFormattings)
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(decodeCondFmts)
- condFmtBytes, _ = xml.Marshal([]*xlsxX14ConditionalFormatting{
- {
- XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value,
- CfRule: []*xlsxX14CfRule{rule},
- },
- })
if condFmts == nil {
condFmts = &xlsxX14ConditionalFormattings{}
}
condFmts.Content = decodeCondFmts.Content + string(condFmtBytes)
condFmtsBytes, _ = xml.Marshal(condFmts)
decodeExtLst.Ext[idx].Content = string(condFmtsBytes)
+ appendMode = true
}
}
- extLstBytes, _ = xml.Marshal(decodeExtLst)
- ws.ExtLst = &xlsxExtLst{
- Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), ""), ""),
- }
- return err
}
- condFmtBytes, _ = xml.Marshal([]*xlsxX14ConditionalFormatting{
- {XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value, CfRule: []*xlsxX14CfRule{rule}},
- })
- condFmtsBytes, _ = xml.Marshal(&xlsxX14ConditionalFormattings{Content: string(condFmtBytes)})
- extBytes, err = xml.Marshal(&xlsxWorksheetExt{
- URI: ExtURIConditionalFormattings,
- Content: string(condFmtsBytes),
+ if !appendMode {
+ condFmtsBytes, _ = xml.Marshal(&xlsxX14ConditionalFormattings{Content: string(condFmtBytes)})
+ decodeExtLst.Ext = append(decodeExtLst.Ext, &xlsxWorksheetExt{
+ URI: ExtURIConditionalFormattings, Content: string(condFmtsBytes),
+ })
+ }
+ sort.Slice(decodeExtLst.Ext, func(i, j int) bool {
+ return inStrSlice(extensionURIPriority, decodeExtLst.Ext[i].URI, false) <
+ inStrSlice(extensionURIPriority, decodeExtLst.Ext[j].URI, false)
})
- ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extBytes), ""), "")}
+ extLstBytes, err = xml.Marshal(decodeExtLst)
+ ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), ""), "")}
return err
}
@@ -3424,6 +3468,7 @@ func extractCondFmtDataBar(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatO
for _, rule := range condFmt.CfRule {
if rule.DataBar != nil {
format.BarSolid = !rule.DataBar.Gradient
+ format.BarDirection = rule.DataBar.Direction
if rule.DataBar.BorderColor != nil {
format.BarBorderColor = "#" + strings.TrimPrefix(strings.ToUpper(rule.DataBar.BorderColor.RGB), "FF")
}
@@ -3466,6 +3511,20 @@ func extractCondFmtExp(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptio
return format
}
+// extractCondFmtIconSet provides a function to extract conditional format
+// settings for icon sets by given conditional formatting rule.
+func extractCondFmtIconSet(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
+ format := ConditionalFormatOptions{Type: "iconSet"}
+ 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
+}
+
// GetConditionalFormats returns conditional format settings by given worksheet
// name.
func (f *File) GetConditionalFormats(sheet string) (map[string][]ConditionalFormatOptions, error) {
@@ -3478,6 +3537,7 @@ func (f *File) GetConditionalFormats(sheet string) (map[string][]ConditionalForm
"colorScale": extractCondFmtColorScale,
"dataBar": extractCondFmtDataBar,
"expression": extractCondFmtExp,
+ "iconSet": extractCondFmtIconSet,
}
conditionalFormats := make(map[string][]ConditionalFormatOptions)
@@ -3622,19 +3682,22 @@ func drawCondFmtColorScale(p int, ct, GUID string, format *ConditionalFormatOpti
func drawCondFmtDataBar(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
var x14CfRule *xlsxX14CfRule
var extLst *xlsxExtLst
- if format.BarSolid {
+ if format.BarSolid || format.BarDirection == "leftToRight" || format.BarDirection == "rightToLeft" || format.BarBorderColor != "" {
extLst = &xlsxExtLst{Ext: fmt.Sprintf(`%s`, ExtURIConditionalFormattingRuleID, NameSpaceSpreadSheetX14.Value, GUID)}
x14CfRule = &xlsxX14CfRule{
Type: validType[format.Type],
ID: GUID,
DataBar: &xlsx14DataBar{
MaxLength: 100,
+ Border: format.BarBorderColor != "",
+ Gradient: !format.BarSolid,
+ Direction: format.BarDirection,
Cfvo: []*xlsxCfvo{{Type: "autoMin"}, {Type: "autoMax"}},
NegativeFillColor: &xlsxColor{RGB: "FFFF0000"},
AxisColor: &xlsxColor{RGB: "FFFF0000"},
},
}
- if format.BarBorderColor != "" {
+ if x14CfRule.DataBar.Border {
x14CfRule.DataBar.BorderColor = &xlsxColor{RGB: getPaletteColor(format.BarBorderColor)}
}
}
@@ -3663,6 +3726,58 @@ func drawCondFmtExp(p int, ct, GUID string, format *ConditionalFormatOptions) (*
}, nil
}
+// drawCondFmtIconSet provides a function to create conditional formatting rule
+// for icon set by given priority, criteria type and format settings.
+func drawCondFmtIconSet(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
+ cfvo3 := &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{
+ {Type: "percent", Val: "0"},
+ {Type: "percent", Val: "33"},
+ {Type: "percent", Val: "67"},
+ }}}
+ cfvo4 := &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{
+ {Type: "percent", Val: "0"},
+ {Type: "percent", Val: "25"},
+ {Type: "percent", Val: "50"},
+ {Type: "percent", Val: "75"},
+ }}}
+ cfvo5 := &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{
+ {Type: "percent", Val: "0"},
+ {Type: "percent", Val: "20"},
+ {Type: "percent", Val: "40"},
+ {Type: "percent", Val: "60"},
+ {Type: "percent", Val: "80"},
+ }}}
+ presets := map[string]*xlsxCfRule{
+ "3Arrows": cfvo3,
+ "3ArrowsGray": cfvo3,
+ "3Flags": cfvo3,
+ "3Signs": cfvo3,
+ "3Symbols": cfvo3,
+ "3Symbols2": cfvo3,
+ "3TrafficLights1": cfvo3,
+ "3TrafficLights2": cfvo3,
+ "4Arrows": cfvo4,
+ "4ArrowsGray": cfvo4,
+ "4Rating": cfvo4,
+ "4RedToBlack": cfvo4,
+ "4TrafficLights": cfvo4,
+ "5Arrows": cfvo5,
+ "5ArrowsGray": cfvo5,
+ "5Quarters": cfvo5,
+ "5Rating": cfvo5,
+ }
+ cfRule, ok := presets[format.IconStyle]
+ if !ok {
+ return nil, nil
+ }
+ cfRule.Priority = p + 1
+ cfRule.IconSet.IconSet = format.IconStyle
+ cfRule.IconSet.Reverse = format.ReverseIcons
+ cfRule.IconSet.ShowValue = boolPtr(!format.IconsOnly)
+ cfRule.Type = format.Type
+ return cfRule, nil
+}
+
// getPaletteColor provides a function to convert the RBG color by given
// string.
func getPaletteColor(color string) string {
diff --git a/styles_test.go b/styles_test.go
index cd90a3c..864f14f 100644
--- a/styles_test.go
+++ b/styles_test.go
@@ -176,11 +176,22 @@ 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 invalid extension list characters
+ f = NewFile()
+ // Test creating a conditional format with existing extension lists
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
- ws.(*xlsxWorksheet).ExtLst.Ext = ""
+ ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: `
+
+ `}
+ assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarSolid: true}}))
+ f = NewFile()
+ // Test creating a conditional format with invalid extension list characters
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: ""}
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element closed by ")
+ // Test creating a conditional format with invalid icon set style
+ assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "iconSet", IconStyle: "unknown"}}), ErrParameterInvalid.Error())
}
func TestGetConditionalFormats(t *testing.T) {
@@ -194,8 +205,10 @@ func TestGetConditionalFormats(t *testing.T) {
{{Type: "unique", Format: 1, Criteria: "="}},
{{Type: "3_color_scale", Criteria: "=", MinType: "num", MidType: "num", MaxType: "num", MinValue: "-10", MidValue: "50", MaxValue: "10", MinColor: "#FF0000", MidColor: "#00FF00", MaxColor: "#0000FF"}},
{{Type: "2_color_scale", Criteria: "=", MinType: "num", MaxType: "num", MinColor: "#FF0000", MaxColor: "#0000FF"}},
- {{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarColor: "#638EC6", BarBorderColor: "#0000FF", BarOnly: true, BarSolid: true, StopIfTrue: true}},
+ {{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarOnly: true, BarSolid: true, StopIfTrue: true}},
+ {{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarDirection: "rightToLeft", BarOnly: true, BarSolid: true, StopIfTrue: true}},
{{Type: "formula", Format: 1, Criteria: "="}},
+ {{Type: "iconSet", IconStyle: "3Arrows", ReverseIcons: true, IconsOnly: true}},
} {
f := NewFile()
err := f.SetConditionalFormat("Sheet1", "A1:A2", format)
diff --git a/xmlDrawing.go b/xmlDrawing.go
index 56ba3eb..cc9585a 100644
--- a/xmlDrawing.go
+++ b/xmlDrawing.go
@@ -107,6 +107,18 @@ const (
ExtURIWebExtensions = "{F7C9EE02-42E1-4005-9D12-6889AFFD525C}"
)
+// extensionURIPriority is the priority of URI in the extension lists.
+var extensionURIPriority = []string{
+ ExtURIConditionalFormattings,
+ ExtURIDataValidations,
+ ExtURISparklineGroups,
+ ExtURISlicerListX14,
+ ExtURIProtectedRanges,
+ ExtURIIgnoredErrors,
+ ExtURIWebExtensions,
+ ExtURITimelineRefs,
+}
+
// Excel specifications and limits
const (
MaxCellStyles = 65430
diff --git a/xmlWorksheet.go b/xmlWorksheet.go
index 07000bd..0974733 100644
--- a/xmlWorksheet.go
+++ b/xmlWorksheet.go
@@ -777,8 +777,10 @@ type decodeX14DataBar struct {
XNLName xml.Name `xml:"dataBar"`
MaxLength int `xml:"maxLength,attr"`
MinLength int `xml:"minLength,attr"`
+ Border bool `xml:"border,attr,omitempty"`
Gradient bool `xml:"gradient,attr"`
ShowValue bool `xml:"showValue,attr,omitempty"`
+ Direction string `xml:"direction,attr,omitempty"`
Cfvo []*xlsxCfvo `xml:"cfvo"`
BorderColor *xlsxColor `xml:"borderColor"`
NegativeFillColor *xlsxColor `xml:"negativeFillColor"`
@@ -801,7 +803,6 @@ type xlsxX14ConditionalFormatting struct {
// xlsxX14CfRule directly maps the cfRule element.
type xlsxX14CfRule struct {
- XNLName xml.Name `xml:"x14:cfRule"`
Type string `xml:"type,attr,omitempty"`
ID string `xml:"id,attr,omitempty"`
DataBar *xlsx14DataBar `xml:"x14:dataBar"`
@@ -809,11 +810,12 @@ type xlsxX14CfRule struct {
// xlsx14DataBar directly maps the dataBar element.
type xlsx14DataBar struct {
- XNLName xml.Name `xml:"x14:dataBar"`
MaxLength int `xml:"maxLength,attr"`
MinLength int `xml:"minLength,attr"`
+ Border bool `xml:"border,attr"`
Gradient bool `xml:"gradient,attr"`
ShowValue bool `xml:"showValue,attr,omitempty"`
+ Direction string `xml:"direction,attr,omitempty"`
Cfvo []*xlsxCfvo `xml:"x14:cfvo"`
BorderColor *xlsxColor `xml:"x14:borderColor"`
NegativeFillColor *xlsxColor `xml:"x14:negativeFillColor"`
@@ -942,8 +944,12 @@ type ConditionalFormatOptions struct {
MaxLength string
BarColor string
BarBorderColor string
+ BarDirection string
BarOnly bool
BarSolid bool
+ IconStyle string
+ ReverseIcons bool
+ IconsOnly bool
StopIfTrue bool
}