forked from p30928647/excelize
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:
parent
3e2406096f
commit
753969dc4e
105
sparkline.go
105
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("<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
|
||||
}
|
||||
|
|
|
@ -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
183
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("<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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue