Support creating a conditional format with an "icon sets" rule

- Improvement compatibility for the worksheet extension lists
- Update unit test
This commit is contained in:
xuri 2023-02-08 00:03:45 +08:00
parent 3e2406096f
commit 753969dc4e
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
6 changed files with 245 additions and 108 deletions

View File

@ -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("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
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
sparklineGroupBytes, _ = xml.Marshal(group)
if ws.ExtLst != nil { // append mode ext
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
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
}
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
}
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), "<extLst>"), "</extLst>"),
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), "<extLst>"), "</extLst>")}
return err
}

View File

@ -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 = `<extLst>
<ext x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
<x14:sparklineGroups
xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
<x14:sparklineGroup>
</x14:sparklines>
</x14:sparklineGroup>
</x14:sparklineGroups>
</ext>
</extLst>`
ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: `
<ext uri="{A8765BA9-456A-4dab-B4F3-ACF838C121DE}"><x14:slicerList /></ext>
<ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"><x14:sparklineGroups /></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 = `<ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"><x14:sparklineGroups><x14:sparklineGroup></x14:sparklines></x14:sparklineGroup></x14:sparklineGroups></ext>`
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A2"},
Range: []string{"Sheet3!A1:J1"},
}), "XML syntax error on line 6: element <sparklineGroup> closed by </sparklines>")
}), "XML syntax error on line 1: element <sparklineGroup> closed by </sparklines>")
}
func TestAppendSparkline(t *testing.T) {

183
styles.go
View File

@ -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("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
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), "<extLst>"), "</extLst>"),
}
return err
}
condFmtBytes, _ = xml.Marshal([]*xlsxX14ConditionalFormatting{
{XMLNSXM: NameSpaceSpreadSheetExcel2006Main.Value, CfRule: []*xlsxX14CfRule{rule}},
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)
})
condFmtsBytes, _ = xml.Marshal(&xlsxX14ConditionalFormattings{Content: string(condFmtBytes)})
extBytes, err = xml.Marshal(&xlsxWorksheetExt{
URI: ExtURIConditionalFormattings,
Content: string(condFmtsBytes),
})
ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extBytes), "<extLst>"), "</extLst>")}
extLstBytes, err = xml.Marshal(decodeExtLst)
ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>")}
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(`<ext uri="%s" xmlns:x14="%s"><x14:id>%s</x14:id></ext>`, 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 {

View File

@ -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 = "<extLst><ext><x14:conditionalFormattings></x14:conditionalFormatting></x14:conditionalFormattings></ext></extLst>"
ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: `
<ext uri="{A8765BA9-456A-4dab-B4F3-ACF838C121DE}"><x14:slicerList /></ext>
<ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"><x14:sparklineGroups /></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: "<ext><x14:conditionalFormattings></x14:conditionalFormatting></x14:conditionalFormattings></ext>"}
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element <conditionalFormattings> closed by </conditionalFormatting>")
// 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)

View File

@ -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

View File

@ -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
}