- 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:
parent
1f69f6b24a
commit
3e2406096f
2
cell.go
2
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.
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
263
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("<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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
126
xmlWorksheet.go
126
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.
|
||||
|
|
Loading…
Reference in New Issue