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.