This closes #1462 and closes #1464

- Support creating a conditional format with a "stop if true" rule
- Support set border color and create solid color for the color data bar
- Fix incorrect cell type when modifying string cell with the time number
- Update unit test for the add pivot table to avoid pivot table range overlap
This commit is contained in:
xuri 2023-02-07 00:08:11 +08:00
parent 1f69f6b24a
commit 3e2406096f
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
7 changed files with 341 additions and 103 deletions

View File

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

View File

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

View File

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

263
styles.go
View File

@ -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("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
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), "<extLst>"), "</extLst>"),
}
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), "<extLst>"), "</extLst>")}
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>"+extLst.Ext+"</extLst>"), 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 {
return &xlsxCfRule{
Priority: p + 1,
Type: validType[format.Type],
DataBar: &xlsxDataBar{
Cfvo: []*xlsxCfvo{{Type: format.MinType}, {Type: format.MaxType}},
Color: []*xlsxColor{{RGB: getPaletteColor(format.BarColor)}},
},
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(`<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,
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,
StopIfTrue: format.StopIfTrue,
Type: validType[format.Type],
DataBar: &xlsxDataBar{
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

View File

@ -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 = "<extLst><ext><x14:conditionalFormattings></x14:conditionalFormatting></x14:conditionalFormattings></ext></extLst>"
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element <conditionalFormattings> closed by </conditionalFormatting>")
}
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()

View File

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

View File

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