diff --git a/cell.go b/cell.go
index 38366e4..a263b84 100644
--- a/cell.go
+++ b/cell.go
@@ -527,7 +527,7 @@ func (c *xlsxC) setCellDefault(value string) {
c.T, c.V, c.IS = value, value, nil
return
}
- c.V = value
+ c.T, c.V = "", value
}
// getCellDate parse cell value which contains a date in the ISO 8601 format.
diff --git a/numfmt.go b/numfmt.go
index dcad4db..af95b54 100644
--- a/numfmt.go
+++ b/numfmt.go
@@ -305,7 +305,7 @@ var (
monthNamesYiSuffix = []string{"\ua2cd\ua1aa", "\ua44d\ua1aa", "\ua315\ua1aa", "\ua1d6\ua1aa", "\ua26c\ua1aa", "\ua0d8\ua1aa", "\ua3c3\ua1aa", "\ua246\ua1aa", "\ua22c\ua1aa", "\ua2b0\ua1aa", "\ua2b0\ua2aa\ua1aa", "\ua2b0\ua44b\ua1aa"}
// monthNamesZulu list the month names in the Zulu.
monthNamesZulu = []string{"Januwari", "Febhuwari", "Mashi", "Ephreli", "Meyi", "Juni", "Julayi", "Agasti", "Septemba", "Okthoba", "Novemba", "Disemba"}
- // monthNamesZuluAbbr list teh month name abbreviations in Zulu
+ // monthNamesZuluAbbr list the month name abbreviations in Zulu
monthNamesZuluAbbr = []string{"Jan", "Feb", "Mas", "Eph", "Mey", "Jun", "Jul", "Agas", "Sep", "Okt", "Nov", "Dis"}
// apFmtAfrikaans defined the AM/PM name in the Afrikaans.
apFmtAfrikaans = "vm./nm."
diff --git a/pivotTable_test.go b/pivotTable_test.go
index fbf60b3..520d56d 100644
--- a/pivotTable_test.go
+++ b/pivotTable_test.go
@@ -70,7 +70,7 @@ func TestAddPivotTable(t *testing.T) {
}))
assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31",
- PivotTableRange: "Sheet1!$G$37:$W$50",
+ PivotTableRange: "Sheet1!$G$39:$W$52",
Rows: []PivotTableField{{Data: "Month"}},
Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}},
Data: []PivotTableField{{Data: "Sales", Subtotal: "CountNums", Name: "Summarize by CountNums"}},
diff --git a/styles.go b/styles.go
index 5a77bc7..b5752fc 100644
--- a/styles.go
+++ b/styles.go
@@ -3190,8 +3190,21 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// MaxColor - Same as MinColor, see above.
//
// BarColor - Used for data_bar. Same as MinColor, see above.
+//
+// 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.
+//
+// BarSolid - Used for turns on a solid (non-gradient) fill for data bars, this
+// is only visible in Excel 2010 and later.
+//
+// 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
+// if the current rule is true.
func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFormatOptions) error {
- drawContFmtFunc := map[string]func(p int, ct string, fmtCond *ConditionalFormatOptions) *xlsxCfRule{
+ drawContFmtFunc := map[string]func(p int, ct, GUID string, fmtCond *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule){
"cellIs": drawCondFmtCellIs,
"top10": drawCondFmtTop10,
"aboveAverage": drawCondFmtAboveAverage,
@@ -3207,6 +3220,12 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
if err != nil {
return err
}
+ // Create a pseudo GUID for each unique rule.
+ var rules int
+ for _, cf := range ws.ConditionalFormatting {
+ rules += len(cf.CfRule)
+ }
+ GUID := fmt.Sprintf("{00000000-0000-0000-%04X-%012X}", f.getSheetID(sheet), rules)
var cfRule []*xlsxCfRule
for p, v := range opts {
var vt, ct string
@@ -3219,7 +3238,14 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
if ok || vt == "expression" {
drawFunc, ok := drawContFmtFunc[vt]
if ok {
- cfRule = append(cfRule, drawFunc(p, ct, &v))
+ rule, x14rule := drawFunc(p, ct, GUID, &v)
+ if x14rule != nil {
+ if err = f.appendCfRule(ws, x14rule); err != nil {
+ return err
+ }
+ f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
+ }
+ cfRule = append(cfRule, rule)
}
}
}
@@ -3232,11 +3258,64 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
return err
}
+// 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
+ )
+ 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
+ }
+ for idx, ext = range decodeExtLst.Ext {
+ 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)
+ }
+ }
+ 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),
+ })
+ ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extBytes), ""), "")}
+ return err
+}
+
// extractCondFmtCellIs provides a function to extract conditional format
// settings for cell value (include between, not between, equal, not equal,
// greater than and less than) by given conditional formatting rule.
-func extractCondFmtCellIs(c *xlsxCfRule) ConditionalFormatOptions {
- format := ConditionalFormatOptions{Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID}
+func extractCondFmtCellIs(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
+ format := ConditionalFormatOptions{StopIfTrue: c.StopIfTrue, Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID}
if len(c.Formula) == 2 {
format.Minimum, format.Maximum = c.Formula[0], c.Formula[1]
return format
@@ -3248,13 +3327,14 @@ func extractCondFmtCellIs(c *xlsxCfRule) ConditionalFormatOptions {
// extractCondFmtTop10 provides a function to extract conditional format
// settings for top N (default is top 10) by given conditional formatting
// rule.
-func extractCondFmtTop10(c *xlsxCfRule) ConditionalFormatOptions {
+func extractCondFmtTop10(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
format := ConditionalFormatOptions{
- Type: "top",
- Criteria: "=",
- Format: *c.DxfID,
- Percent: c.Percent,
- Value: strconv.Itoa(c.Rank),
+ StopIfTrue: c.StopIfTrue,
+ Type: "top",
+ Criteria: "=",
+ Format: *c.DxfID,
+ Percent: c.Percent,
+ Value: strconv.Itoa(c.Rank),
}
if c.Bottom {
format.Type = "bottom"
@@ -3265,8 +3345,9 @@ func extractCondFmtTop10(c *xlsxCfRule) ConditionalFormatOptions {
// extractCondFmtAboveAverage provides a function to extract conditional format
// settings for above average and below average by given conditional formatting
// rule.
-func extractCondFmtAboveAverage(c *xlsxCfRule) ConditionalFormatOptions {
+func extractCondFmtAboveAverage(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
return ConditionalFormatOptions{
+ StopIfTrue: c.StopIfTrue,
Type: "average",
Criteria: "=",
Format: *c.DxfID,
@@ -3277,8 +3358,9 @@ func extractCondFmtAboveAverage(c *xlsxCfRule) ConditionalFormatOptions {
// extractCondFmtDuplicateUniqueValues provides a function to extract
// conditional format settings for duplicate and unique values by given
// conditional formatting rule.
-func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) ConditionalFormatOptions {
+func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
return ConditionalFormatOptions{
+ StopIfTrue: c.StopIfTrue,
Type: map[string]string{
"duplicateValues": "duplicate",
"uniqueValues": "unique",
@@ -3291,8 +3373,8 @@ func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) ConditionalFormatOptions
// extractCondFmtColorScale provides a function to extract conditional format
// settings for color scale (include 2 color scale and 3 color scale) by given
// conditional formatting rule.
-func extractCondFmtColorScale(c *xlsxCfRule) ConditionalFormatOptions {
- var format ConditionalFormatOptions
+func extractCondFmtColorScale(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
+ format := ConditionalFormatOptions{StopIfTrue: c.StopIfTrue}
format.Type, format.Criteria = "2_color_scale", "="
values := len(c.ColorScale.Cfvo)
colors := len(c.ColorScale.Color)
@@ -3326,20 +3408,58 @@ func extractCondFmtColorScale(c *xlsxCfRule) ConditionalFormatOptions {
// extractCondFmtDataBar provides a function to extract conditional format
// settings for data bar by given conditional formatting rule.
-func extractCondFmtDataBar(c *xlsxCfRule) ConditionalFormatOptions {
+func extractCondFmtDataBar(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
format := ConditionalFormatOptions{Type: "data_bar", Criteria: "="}
if c.DataBar != nil {
+ format.StopIfTrue = c.StopIfTrue
format.MinType = c.DataBar.Cfvo[0].Type
format.MaxType = c.DataBar.Cfvo[1].Type
format.BarColor = "#" + strings.TrimPrefix(strings.ToUpper(c.DataBar.Color[0].RGB), "FF")
+ if c.DataBar.ShowValue != nil {
+ format.BarOnly = !*c.DataBar.ShowValue
+ }
+ }
+ extractDataBarRule := func(condFmts []decodeX14ConditionalFormatting) {
+ for _, condFmt := range condFmts {
+ for _, rule := range condFmt.CfRule {
+ if rule.DataBar != nil {
+ format.BarSolid = !rule.DataBar.Gradient
+ if rule.DataBar.BorderColor != nil {
+ format.BarBorderColor = "#" + strings.TrimPrefix(strings.ToUpper(rule.DataBar.BorderColor.RGB), "FF")
+ }
+ }
+ }
+ }
+ }
+ extractExtLst := func(extLst *decodeWorksheetExt) {
+ for _, ext := range extLst.Ext {
+ if ext.URI == ExtURIConditionalFormattings {
+ decodeCondFmts := new(decodeX14ConditionalFormattings)
+ if err := xml.Unmarshal([]byte(ext.Content), &decodeCondFmts); err == nil {
+ condFmts := []decodeX14ConditionalFormatting{}
+ if err = xml.Unmarshal([]byte(decodeCondFmts.Content), &condFmts); err == nil {
+ extractDataBarRule(condFmts)
+ }
+ }
+ }
+ }
+ }
+ if c.ExtLst != nil {
+ ext := decodeX14ConditionalFormattingExt{}
+ if err := xml.Unmarshal([]byte(c.ExtLst.Ext), &ext); err == nil && extLst != nil {
+ decodeExtLst := new(decodeWorksheetExt)
+ if err = xml.Unmarshal([]byte(""+extLst.Ext+""), decodeExtLst); err == nil {
+ extractExtLst(decodeExtLst)
+ }
+ }
}
return format
}
// extractCondFmtExp provides a function to extract conditional format settings
// for expression by given conditional formatting rule.
-func extractCondFmtExp(c *xlsxCfRule) ConditionalFormatOptions {
- format := ConditionalFormatOptions{Type: "formula", Format: *c.DxfID}
+func extractCondFmtExp(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
+ format := ConditionalFormatOptions{StopIfTrue: c.StopIfTrue, Type: "formula", Format: *c.DxfID}
if len(c.Formula) > 0 {
format.Criteria = c.Formula[0]
}
@@ -3349,7 +3469,7 @@ func extractCondFmtExp(c *xlsxCfRule) ConditionalFormatOptions {
// GetConditionalFormats returns conditional format settings by given worksheet
// name.
func (f *File) GetConditionalFormats(sheet string) (map[string][]ConditionalFormatOptions, error) {
- extractContFmtFunc := map[string]func(c *xlsxCfRule) ConditionalFormatOptions{
+ extractContFmtFunc := map[string]func(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions{
"cellIs": extractCondFmtCellIs,
"top10": extractCondFmtTop10,
"aboveAverage": extractCondFmtAboveAverage,
@@ -3369,7 +3489,7 @@ func (f *File) GetConditionalFormats(sheet string) (map[string][]ConditionalForm
var opts []ConditionalFormatOptions
for _, cr := range cf.CfRule {
if extractFunc, ok := extractContFmtFunc[cr.Type]; ok {
- opts = append(opts, extractFunc(cr))
+ opts = append(opts, extractFunc(cr, ws.ExtLst))
}
}
conditionalFormats[cf.SQRef] = opts
@@ -3396,12 +3516,13 @@ func (f *File) UnsetConditionalFormat(sheet, rangeRef string) error {
// drawCondFmtCellIs provides a function to create conditional formatting rule
// for cell value (include between, not between, equal, not equal, greater
// than and less than) by given priority, criteria type and format settings.
-func drawCondFmtCellIs(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtCellIs(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
c := &xlsxCfRule{
- Priority: p + 1,
- Type: validType[format.Type],
- Operator: ct,
- DxfID: &format.Format,
+ Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
+ Type: validType[format.Type],
+ Operator: ct,
+ DxfID: &format.Format,
}
// "between" and "not between" criteria require 2 values.
if ct == "between" || ct == "notBetween" {
@@ -3410,54 +3531,57 @@ func drawCondFmtCellIs(p int, ct string, format *ConditionalFormatOptions) *xlsx
if idx := inStrSlice([]string{"equal", "notEqual", "greaterThan", "lessThan", "greaterThanOrEqual", "lessThanOrEqual", "containsText", "notContains", "beginsWith", "endsWith"}, ct, true); idx != -1 {
c.Formula = append(c.Formula, format.Value)
}
- return c
+ return c, nil
}
// drawCondFmtTop10 provides a function to create conditional formatting rule
// for top N (default is top 10) by given priority, criteria type and format
// settings.
-func drawCondFmtTop10(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtTop10(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
c := &xlsxCfRule{
- Priority: p + 1,
- Bottom: format.Type == "bottom",
- Type: validType[format.Type],
- Rank: 10,
- DxfID: &format.Format,
- Percent: format.Percent,
+ Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
+ Bottom: format.Type == "bottom",
+ Type: validType[format.Type],
+ Rank: 10,
+ DxfID: &format.Format,
+ Percent: format.Percent,
}
if rank, err := strconv.Atoi(format.Value); err == nil {
c.Rank = rank
}
- return c
+ return c, nil
}
// drawCondFmtAboveAverage provides a function to create conditional
// formatting rule for above average and below average by given priority,
// criteria type and format settings.
-func drawCondFmtAboveAverage(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtAboveAverage(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
return &xlsxCfRule{
Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
Type: validType[format.Type],
AboveAverage: &format.AboveAverage,
DxfID: &format.Format,
- }
+ }, nil
}
// drawCondFmtDuplicateUniqueValues provides a function to create conditional
// formatting rule for duplicate and unique values by given priority, criteria
// type and format settings.
-func drawCondFmtDuplicateUniqueValues(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtDuplicateUniqueValues(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
return &xlsxCfRule{
- Priority: p + 1,
- Type: validType[format.Type],
- DxfID: &format.Format,
- }
+ Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
+ Type: validType[format.Type],
+ DxfID: &format.Format,
+ }, nil
}
// drawCondFmtColorScale provides a function to create conditional formatting
// rule for color scale (include 2 color scale and 3 color scale) by given
// priority, criteria type and format settings.
-func drawCondFmtColorScale(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtColorScale(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
minValue := format.MinValue
if minValue == "" {
minValue = "0"
@@ -3472,8 +3596,9 @@ func drawCondFmtColorScale(p int, ct string, format *ConditionalFormatOptions) *
}
c := &xlsxCfRule{
- Priority: p + 1,
- Type: "colorScale",
+ Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
+ Type: "colorScale",
ColorScale: &xlsxColorScale{
Cfvo: []*xlsxCfvo{
{Type: format.MinType, Val: minValue},
@@ -3489,31 +3614,53 @@ func drawCondFmtColorScale(p int, ct string, format *ConditionalFormatOptions) *
}
c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MaxType, Val: maxValue})
c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MaxColor)})
- return c
+ return c, nil
}
// drawCondFmtDataBar provides a function to create conditional formatting
// rule for data bar by given priority, criteria type and format settings.
-func drawCondFmtDataBar(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtDataBar(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
+ var x14CfRule *xlsxX14CfRule
+ var extLst *xlsxExtLst
+ if format.BarSolid {
+ extLst = &xlsxExtLst{Ext: fmt.Sprintf(`%s`, ExtURIConditionalFormattingRuleID, NameSpaceSpreadSheetX14.Value, GUID)}
+ x14CfRule = &xlsxX14CfRule{
+ Type: validType[format.Type],
+ ID: GUID,
+ DataBar: &xlsx14DataBar{
+ MaxLength: 100,
+ Cfvo: []*xlsxCfvo{{Type: "autoMin"}, {Type: "autoMax"}},
+ NegativeFillColor: &xlsxColor{RGB: "FFFF0000"},
+ AxisColor: &xlsxColor{RGB: "FFFF0000"},
+ },
+ }
+ if format.BarBorderColor != "" {
+ x14CfRule.DataBar.BorderColor = &xlsxColor{RGB: getPaletteColor(format.BarBorderColor)}
+ }
+ }
return &xlsxCfRule{
- Priority: p + 1,
- Type: validType[format.Type],
+ Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
+ Type: validType[format.Type],
DataBar: &xlsxDataBar{
- Cfvo: []*xlsxCfvo{{Type: format.MinType}, {Type: format.MaxType}},
- Color: []*xlsxColor{{RGB: getPaletteColor(format.BarColor)}},
+ ShowValue: boolPtr(!format.BarOnly),
+ Cfvo: []*xlsxCfvo{{Type: format.MinType}, {Type: format.MaxType}},
+ Color: []*xlsxColor{{RGB: getPaletteColor(format.BarColor)}},
},
- }
+ ExtLst: extLst,
+ }, x14CfRule
}
// drawCondFmtExp provides a function to create conditional formatting rule
// for expression by given priority, criteria type and format settings.
-func drawCondFmtExp(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
+func drawCondFmtExp(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
return &xlsxCfRule{
- Priority: p + 1,
- Type: validType[format.Type],
- Formula: []string{format.Criteria},
- DxfID: &format.Format,
- }
+ Priority: p + 1,
+ StopIfTrue: format.StopIfTrue,
+ Type: validType[format.Type],
+ Formula: []string{format.Criteria},
+ DxfID: &format.Format,
+ }, nil
}
// getPaletteColor provides a function to convert the RBG color by given
diff --git a/styles_test.go b/styles_test.go
index 86860fa..cd90a3c 100644
--- a/styles_test.go
+++ b/styles_test.go
@@ -159,12 +159,7 @@ func TestSetConditionalFormat(t *testing.T) {
f := NewFile()
const sheet = "Sheet1"
const rangeRef = "A1:A1"
-
- err := f.SetConditionalFormat(sheet, rangeRef, testCase.format)
- if err != nil {
- t.Fatalf("%s", err)
- }
-
+ assert.NoError(t, f.SetConditionalFormat(sheet, rangeRef, testCase.format))
ws, err := f.workSheetReader(sheet)
assert.NoError(t, err)
cf := ws.ConditionalFormatting
@@ -173,6 +168,19 @@ func TestSetConditionalFormat(t *testing.T) {
assert.Equal(t, rangeRef, cf[0].SQRef, testCase.label)
assert.EqualValues(t, testCase.rules, cf[0].CfRule, testCase.label)
}
+ // Test creating a conditional format with a solid color data bar style
+ f := NewFile()
+ condFmts := []ConditionalFormatOptions{
+ {Type: "data_bar", BarColor: "#A9D08E", BarSolid: true, Format: 0, Criteria: "=", MinType: "min", MaxType: "max"},
+ }
+ 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
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).ExtLst.Ext = ""
+ assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element closed by ")
}
func TestGetConditionalFormats(t *testing.T) {
@@ -186,7 +194,7 @@ 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"}},
+ {{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarColor: "#638EC6", BarBorderColor: "#0000FF", BarOnly: true, BarSolid: true, StopIfTrue: true}},
{{Type: "formula", Format: 1, Criteria: "="}},
} {
f := NewFile()
diff --git a/xmlDrawing.go b/xmlDrawing.go
index a0fcfb0..56ba3eb 100644
--- a/xmlDrawing.go
+++ b/xmlDrawing.go
@@ -91,19 +91,20 @@ const (
// ([ISO/IEC29500-1:2016] section 18.2.10) of the worksheet element
// ([ISO/IEC29500-1:2016] section 18.3.1.99) is extended by the addition of
// new child ext elements ([ISO/IEC29500-1:2016] section 18.2.7)
- ExtURIConditionalFormattings = "{78C0D931-6437-407D-A8EE-F0AAD7539E65}"
- ExtURIDataValidations = "{CCE6A557-97BC-4B89-ADB6-D9C93CAAB3DF}"
- ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}"
- ExtURIIgnoredErrors = "{01252117-D84E-4E92-8308-4BE1C098FCBB}"
- ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}"
- ExtURIProtectedRanges = "{FC87AEE6-9EDD-4A0A-B7FB-166176984837}"
- ExtURISlicerCachesListX14 = "{BBE1A952-AA13-448e-AADC-164F8A28A991}"
- ExtURISlicerListX14 = "{A8765BA9-456A-4DAB-B4F3-ACF838C121DE}"
- ExtURISlicerListX15 = "{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}"
- ExtURISparklineGroups = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"
- ExtURISVG = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"
- ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}"
- ExtURIWebExtensions = "{F7C9EE02-42E1-4005-9D12-6889AFFD525C}"
+ ExtURIConditionalFormattingRuleID = "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}"
+ ExtURIConditionalFormattings = "{78C0D931-6437-407d-A8EE-F0AAD7539E65}"
+ ExtURIDataValidations = "{CCE6A557-97BC-4B89-ADB6-D9C93CAAB3DF}"
+ ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}"
+ ExtURIIgnoredErrors = "{01252117-D84E-4E92-8308-4BE1C098FCBB}"
+ ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}"
+ ExtURIProtectedRanges = "{FC87AEE6-9EDD-4A0A-B7FB-166176984837}"
+ ExtURISlicerCachesListX14 = "{BBE1A952-AA13-448e-AADC-164F8A28A991}"
+ ExtURISlicerListX14 = "{A8765BA9-456A-4DAB-B4F3-ACF838C121DE}"
+ ExtURISlicerListX15 = "{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}"
+ ExtURISparklineGroups = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"
+ ExtURISVG = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"
+ ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}"
+ ExtURIWebExtensions = "{F7C9EE02-42E1-4005-9D12-6889AFFD525C}"
)
// Excel specifications and limits
diff --git a/xmlWorksheet.go b/xmlWorksheet.go
index 8b45a34..07000bd 100644
--- a/xmlWorksheet.go
+++ b/xmlWorksheet.go
@@ -592,7 +592,7 @@ type xlsxColorScale struct {
type xlsxDataBar struct {
MaxLength int `xml:"maxLength,attr,omitempty"`
MinLength int `xml:"minLength,attr,omitempty"`
- ShowValue bool `xml:"showValue,attr,omitempty"`
+ ShowValue *bool `xml:"showValue,attr"`
Cfvo []*xlsxCfvo `xml:"cfvo"`
Color []*xlsxColor `xml:"color"`
}
@@ -601,7 +601,7 @@ type xlsxDataBar struct {
type xlsxIconSet struct {
Cfvo []*xlsxCfvo `xml:"cfvo"`
IconSet string `xml:"iconSet,attr,omitempty"`
- ShowValue bool `xml:"showValue,attr,omitempty"`
+ ShowValue *bool `xml:"showValue,attr"`
Percent bool `xml:"percent,attr,omitempty"`
Reverse bool `xml:"reverse,attr,omitempty"`
}
@@ -742,6 +742,84 @@ type decodeX14SparklineGroups struct {
Content string `xml:",innerxml"`
}
+// decodeX14ConditionalFormattingExt directly maps the ext
+// element.
+type decodeX14ConditionalFormattingExt struct {
+ XMLName xml.Name `xml:"ext"`
+ ID string `xml:"id"`
+}
+
+// decodeX14ConditionalFormattings directly maps the conditionalFormattings
+// element.
+type decodeX14ConditionalFormattings struct {
+ XMLName xml.Name `xml:"conditionalFormattings"`
+ XMLNSXM string `xml:"xmlns:xm,attr"`
+ Content string `xml:",innerxml"`
+}
+
+// decodeX14ConditionalFormatting directly maps the conditionalFormatting
+// element.
+type decodeX14ConditionalFormatting struct {
+ XMLName xml.Name `xml:"conditionalFormatting"`
+ CfRule []*decodeX14CfRule `xml:"cfRule"`
+}
+
+// decodeX14CfRule directly maps the cfRule element.
+type decodeX14CfRule struct {
+ XNLName xml.Name `xml:"cfRule"`
+ Type string `xml:"type,attr,omitempty"`
+ ID string `xml:"id,attr,omitempty"`
+ DataBar *decodeX14DataBar `xml:"dataBar"`
+}
+
+// decodeX14DataBar directly maps the dataBar element.
+type decodeX14DataBar struct {
+ XNLName xml.Name `xml:"dataBar"`
+ MaxLength int `xml:"maxLength,attr"`
+ MinLength int `xml:"minLength,attr"`
+ Gradient bool `xml:"gradient,attr"`
+ ShowValue bool `xml:"showValue,attr,omitempty"`
+ Cfvo []*xlsxCfvo `xml:"cfvo"`
+ BorderColor *xlsxColor `xml:"borderColor"`
+ NegativeFillColor *xlsxColor `xml:"negativeFillColor"`
+ AxisColor *xlsxColor `xml:"axisColor"`
+}
+
+// xlsxX14ConditionalFormattings directly maps the conditionalFormattings
+// element.
+type xlsxX14ConditionalFormattings struct {
+ XMLName xml.Name `xml:"x14:conditionalFormattings"`
+ Content string `xml:",innerxml"`
+}
+
+// xlsxX14ConditionalFormatting directly maps the conditionalFormatting element.
+type xlsxX14ConditionalFormatting struct {
+ XMLName xml.Name `xml:"x14:conditionalFormatting"`
+ XMLNSXM string `xml:"xmlns:xm,attr"`
+ CfRule []*xlsxX14CfRule `xml:"x14:cfRule"`
+}
+
+// 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"`
+}
+
+// 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"`
+ Gradient bool `xml:"gradient,attr"`
+ ShowValue bool `xml:"showValue,attr,omitempty"`
+ Cfvo []*xlsxCfvo `xml:"x14:cfvo"`
+ BorderColor *xlsxColor `xml:"x14:borderColor"`
+ NegativeFillColor *xlsxColor `xml:"x14:negativeFillColor"`
+ AxisColor *xlsxColor `xml:"x14:axisColor"`
+}
+
// xlsxX14SparklineGroups directly maps the sparklineGroups element.
type xlsxX14SparklineGroups struct {
XMLName xml.Name `xml:"x14:sparklineGroups"`
@@ -843,26 +921,30 @@ type Panes struct {
// ConditionalFormatOptions directly maps the conditional format settings of the cells.
type ConditionalFormatOptions struct {
- Type string
- AboveAverage bool
- Percent bool
- Format int
- Criteria string
- Value string
- Minimum string
- Maximum string
- MinType string
- MidType string
- MaxType string
- MinValue string
- MidValue string
- MaxValue string
- MinColor string
- MidColor string
- MaxColor string
- MinLength string
- MaxLength string
- BarColor string
+ Type string
+ AboveAverage bool
+ Percent bool
+ Format int
+ Criteria string
+ Value string
+ Minimum string
+ Maximum string
+ MinType string
+ MidType string
+ MaxType string
+ MinValue string
+ MidValue string
+ MaxValue string
+ MinColor string
+ MidColor string
+ MaxColor string
+ MinLength string
+ MaxLength string
+ BarColor string
+ BarBorderColor string
+ BarOnly bool
+ BarSolid bool
+ StopIfTrue bool
}
// SheetProtectionOptions directly maps the settings of worksheet protection.