- Init conditional format support, relate issue #75;

- go test and godoc updated
This commit is contained in:
Ri Xu 2017-08-11 23:15:33 +08:00
parent a8cf38ebd5
commit 67636039f6
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 700 additions and 97 deletions

View File

@ -1,6 +1,7 @@
package excelize
import (
"fmt"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
@ -895,3 +896,62 @@ func TestRemoveRow(t *testing.T) {
t.Log(err)
}
}
func TestConditionalFormat(t *testing.T) {
xlsx := NewFile()
for j := 1; j <= 10; j++ {
for i := 0; i <= 10; i++ {
xlsx.SetCellInt("Sheet1", ToAlphaString(i)+strconv.Itoa(j), j)
}
}
var format1, format2, format3 int
var err error
// Rose format for bad conditional.
format1, err = xlsx.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
t.Log(err)
// Light yellow format for neutral conditional.
format2, err = xlsx.NewConditionalStyle(`{"fill":{"type":"pattern","color":["#FEEAA0"],"pattern":1}}`)
t.Log(err)
// Light green format for good conditional.
format3, err = xlsx.NewConditionalStyle(`{"font":{"color":"#09600B"},"fill":{"type":"pattern","color":["#C7EECF"],"pattern":1}}`)
t.Log(err)
// Color scales: 2 color.
xlsx.SetConditionalFormat("Sheet1", "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`)
// Color scales: 3 color.
xlsx.SetConditionalFormat("Sheet1", "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)
// Hightlight cells rules: between...
xlsx.SetConditionalFormat("Sheet1", "C1:C10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format1))
// Hightlight cells rules: Greater Than...
xlsx.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format3))
// Hightlight cells rules: Equal To...
xlsx.SetConditionalFormat("Sheet1", "E1:E10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d}]`, format3))
// Hightlight cells rules: Not Equal To...
xlsx.SetConditionalFormat("Sheet1", "F1:F10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format2))
// Hightlight cells rules: Duplicate Values...
xlsx.SetConditionalFormat("Sheet1", "G1:G10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format2))
// Top/Bottom rules: Top 10%.
xlsx.SetConditionalFormat("Sheet1", "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6","percent":true}]`, format1))
// Top/Bottom rules: Above Average...
xlsx.SetConditionalFormat("Sheet1", "I1:I10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": true}]`, format3))
// Top/Bottom rules: Below Average...
xlsx.SetConditionalFormat("Sheet1", "J1:J10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": false}]`, format1))
// Data Bars: Gradient Fill.
xlsx.SetConditionalFormat("Sheet1", "K1:K10", `[{"type":"data_bar", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)
err = xlsx.SaveAs("./test/Workbook_conditional_format.xlsx")
if err != nil {
t.Log(err)
}
// Set conditional format with illegal JSON string.
_, err = xlsx.NewConditionalStyle("")
t.Log(err)
// Set conditional format with illegal valid type.
xlsx.SetConditionalFormat("Sheet1", "K1:K10", `[{"type":"", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)
// Set conditional format with illegal criteria type.
xlsx.SetConditionalFormat("Sheet1", "K1:K10", `[{"type":"data_bar", "criteria":"", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)
// Set conditional format with file without dxfs element.
xlsx, err = OpenFile("./test/Workbook1.xlsx")
t.Log(err)
_, err = xlsx.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
t.Log(err)
}

612
styles.go
View File

@ -774,6 +774,61 @@ var builtInNumFmtFunc = map[int]func(i int, v string) string{
49: formatToString,
}
// validType defined the list of valid validation types.
var validType = map[string]string{
"cell": "cellIs",
"date": "date", // Doesn't support currently
"time": "time", // Doesn't support currently
"average": "aboveAverage",
"duplicate": "duplicateValues",
"unique": "uniqueValues",
"top": "top10",
"bottom": "top10",
"text": "text", // Doesn't support currently
"time_period": "timePeriod", // Doesn't support currently
"blanks": "containsBlanks", // Doesn't support currently
"no_blanks": "notContainsBlanks", // Doesn't support currently
"errors": "containsErrors", // Doesn't support currently
"no_errors": "notContainsErrors", // Doesn't support currently
"2_color_scale": "2_color_scale",
"3_color_scale": "3_color_scale",
"data_bar": "dataBar",
"formula": "expression", // Doesn't support currently
}
// criteriaType defined the list of valid criteria types.
var criteriaType = map[string]string{
"between": "between",
"not between": "notBetween",
"equal to": "equal",
"=": "equal",
"==": "equal",
"not equal to": "notEqual",
"!=": "notEqual",
"<>": "notEqual",
"greater than": "greaterThan",
">": "greaterThan",
"less than": "lessThan",
"<": "lessThan",
"greater than or equal to": "greaterThanOrEqual",
">=": "greaterThanOrEqual",
"less than or equal to": "lessThanOrEqual",
"<=": "lessThanOrEqual",
"containing": "containsText",
"not containing": "notContains",
"begins with": "beginsWith",
"ends with": "endsWith",
"yesterday": "yesterday",
"today": "today",
"last 7 days": "last7Days",
"last week": "lastWeek",
"this week": "thisWeek",
"continue week": "continueWeek",
"last month": "lastMonth",
"this month": "thisMonth",
"continue month": "continueMonth",
}
// formatToString provides function to return original string by given built-in
// number formats code and cell string.
func formatToString(i int, v string) string {
@ -935,9 +990,9 @@ func (f *File) styleSheetWriter() {
}
// parseFormatStyleSet provides function to parse the format settings of the
// borders.
func parseFormatStyleSet(style string) (*formatCellStyle, error) {
format := formatCellStyle{
// cells and conditional formats.
func parseFormatStyleSet(style string) (*formatStyle, error) {
format := formatStyle{
DecimalPlaces: 2,
}
err := json.Unmarshal([]byte(style), &format)
@ -1806,79 +1861,114 @@ func parseFormatStyleSet(style string) (*formatCellStyle, error) {
// Cell Sheet1!A6 in the Excel Application: martes, 04 de Julio de 2017
//
func (f *File) NewStyle(style string) (int, error) {
var cellXfsID int
styleSheet := f.stylesReader()
formatCellStyle, err := parseFormatStyleSet(style)
var cellXfsID, fontID, borderID, fillID int
s := f.stylesReader()
fs, err := parseFormatStyleSet(style)
if err != nil {
return cellXfsID, err
}
numFmtID := setNumFmt(styleSheet, formatCellStyle)
fontID := setFont(styleSheet, formatCellStyle)
borderID := setBorders(styleSheet, formatCellStyle)
fillID := setFills(styleSheet, formatCellStyle)
applyAlignment, alignment := setAlignment(styleSheet, formatCellStyle)
cellXfsID = setCellXfs(styleSheet, fontID, numFmtID, fillID, borderID, applyAlignment, alignment)
numFmtID := setNumFmt(s, fs)
if fs.Font != nil {
font, _ := xml.Marshal(setFont(fs))
s.Fonts.Count++
s.Fonts.Font = append(s.Fonts.Font, &xlsxFont{
Font: string(font[6 : len(font)-7]),
})
fontID = s.Fonts.Count - 1
}
s.Borders.Count++
s.Borders.Border = append(s.Borders.Border, setBorders(fs))
borderID = s.Borders.Count - 1
s.Fills.Count++
s.Fills.Fill = append(s.Fills.Fill, setFills(fs, true))
fillID = s.Fills.Count - 1
applyAlignment, alignment := fs.Alignment != nil, setAlignment(fs)
cellXfsID = setCellXfs(s, fontID, numFmtID, fillID, borderID, applyAlignment, alignment)
return cellXfsID, nil
}
// NewConditionalStyle provides function to create style for conditional format
// by given style format. Note that the color field uses RGB color code. Only
// support set font, fills, alignment and borders currently.
func (f *File) NewConditionalStyle(style string) (int, error) {
s := f.stylesReader()
fs, err := parseFormatStyleSet(style)
if err != nil {
return 0, err
}
dxf := dxf{
Fill: setFills(fs, false),
Alignment: setAlignment(fs),
Border: setBorders(fs),
}
if fs.Font != nil {
dxf.Font = setFont(fs)
}
dxfStr, _ := xml.Marshal(dxf)
if s.Dxfs == nil {
s.Dxfs = &xlsxDxfs{}
}
s.Dxfs.Count++
s.Dxfs.Dxfs = append(s.Dxfs.Dxfs, &xlsxDxf{
Dxf: string(dxfStr[5 : len(dxfStr)-6]),
})
return s.Dxfs.Count - 1, nil
}
// setFont provides function to add font style by given cell format settings.
func setFont(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
if formatCellStyle.Font == nil {
return 0
}
func setFont(formatStyle *formatStyle) *font {
fontUnderlineType := map[string]string{"single": "single", "double": "double"}
if formatCellStyle.Font.Family == "" {
formatCellStyle.Font.Family = "Calibri"
if formatStyle.Font.Family == "" {
formatStyle.Font.Family = "Calibri"
}
if formatCellStyle.Font.Size < 1 {
formatCellStyle.Font.Size = 11
if formatStyle.Font.Size < 1 {
formatStyle.Font.Size = 11
}
if formatCellStyle.Font.Color == "" {
formatCellStyle.Font.Color = "#000000"
if formatStyle.Font.Color == "" {
formatStyle.Font.Color = "#000000"
}
f := font{
B: formatCellStyle.Font.Bold,
I: formatCellStyle.Font.Italic,
Sz: &attrValInt{Val: formatCellStyle.Font.Size},
Color: &xlsxColor{RGB: getPaletteColor(formatCellStyle.Font.Color)},
Name: &attrValString{Val: formatCellStyle.Font.Family},
B: formatStyle.Font.Bold,
I: formatStyle.Font.Italic,
Sz: &attrValInt{Val: formatStyle.Font.Size},
Color: &xlsxColor{RGB: getPaletteColor(formatStyle.Font.Color)},
Name: &attrValString{Val: formatStyle.Font.Family},
Family: &attrValInt{Val: 2},
Scheme: &attrValString{Val: "minor"},
}
val, ok := fontUnderlineType[formatCellStyle.Font.Underline]
val, ok := fontUnderlineType[formatStyle.Font.Underline]
if ok {
f.U = &attrValString{Val: val}
}
font, _ := xml.Marshal(f)
style.Fonts.Count++
style.Fonts.Font = append(style.Fonts.Font, &xlsxFont{
Font: string(font[6 : len(font)-7]),
})
return style.Fonts.Count - 1
return &f
}
// setNumFmt provides function to check if number format code in the range of
// built-in values.
func setNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
func setNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
dp := "0."
numFmtID := 164 // Default custom number format code from 164.
if formatCellStyle.DecimalPlaces < 0 || formatCellStyle.DecimalPlaces > 30 {
formatCellStyle.DecimalPlaces = 2
if formatStyle.DecimalPlaces < 0 || formatStyle.DecimalPlaces > 30 {
formatStyle.DecimalPlaces = 2
}
for i := 0; i < formatCellStyle.DecimalPlaces; i++ {
for i := 0; i < formatStyle.DecimalPlaces; i++ {
dp += "0"
}
if formatCellStyle.CustomNumFmt != nil {
return setCustomNumFmt(style, formatCellStyle)
if formatStyle.CustomNumFmt != nil {
return setCustomNumFmt(style, formatStyle)
}
_, ok := builtInNumFmt[formatCellStyle.NumFmt]
_, ok := builtInNumFmt[formatStyle.NumFmt]
if !ok {
fc, currency := currencyNumFmt[formatCellStyle.NumFmt]
fc, currency := currencyNumFmt[formatStyle.NumFmt]
if !currency {
return setLangNumFmt(style, formatCellStyle)
return setLangNumFmt(style, formatStyle)
}
fc = strings.Replace(fc, "0.00", dp, -1)
if formatCellStyle.NegRed {
if formatStyle.NegRed {
fc = fc + ";[Red]" + fc
}
if style.NumFmts != nil {
@ -1902,12 +1992,12 @@ func setNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
}
return numFmtID
}
return formatCellStyle.NumFmt
return formatStyle.NumFmt
}
// setCustomNumFmt provides function to set custom number format code.
func setCustomNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
nf := xlsxNumFmt{FormatCode: *formatCellStyle.CustomNumFmt}
func setCustomNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
nf := xlsxNumFmt{FormatCode: *formatStyle.CustomNumFmt}
if style.NumFmts != nil {
nf.NumFmtID = style.NumFmts.NumFmt[len(style.NumFmts.NumFmt)-1].NumFmtID + 1
style.NumFmts.NumFmt = append(style.NumFmts.NumFmt, &nf)
@ -1924,13 +2014,13 @@ func setCustomNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) in
}
// setLangNumFmt provides function to set number format code with language.
func setLangNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
numFmts, ok := langNumFmt[formatCellStyle.Lang]
func setLangNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
numFmts, ok := langNumFmt[formatStyle.Lang]
if !ok {
return 0
}
var fc string
fc, ok = numFmts[formatCellStyle.NumFmt]
fc, ok = numFmts[formatStyle.NumFmt]
if !ok {
return 0
}
@ -1940,7 +2030,7 @@ func setLangNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int
style.NumFmts.NumFmt = append(style.NumFmts.NumFmt, &nf)
style.NumFmts.Count++
} else {
nf.NumFmtID = formatCellStyle.NumFmt
nf.NumFmtID = formatStyle.NumFmt
numFmts := xlsxNumFmts{
NumFmt: []*xlsxNumFmt{&nf},
Count: 1,
@ -1952,7 +2042,7 @@ func setLangNumFmt(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int
// setFills provides function to add fill elements in the styles.xml by given
// cell format settings.
func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
var patterns = []string{
"none",
"solid",
@ -1983,15 +2073,15 @@ func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
}
var fill xlsxFill
switch formatCellStyle.Fill.Type {
switch formatStyle.Fill.Type {
case "gradient":
if len(formatCellStyle.Fill.Color) != 2 {
if len(formatStyle.Fill.Color) != 2 {
break
}
var gradient xlsxGradientFill
switch formatCellStyle.Fill.Shading {
switch formatStyle.Fill.Shading {
case 0, 1, 2, 3:
gradient.Degree = variants[formatCellStyle.Fill.Shading]
gradient.Degree = variants[formatStyle.Fill.Shading]
case 4:
gradient.Type = "path"
case 5:
@ -2004,7 +2094,7 @@ func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
break
}
var stops []*xlsxGradientFillStop
for index, color := range formatCellStyle.Fill.Color {
for index, color := range formatStyle.Fill.Color {
var stop xlsxGradientFillStop
stop.Position = float64(index)
stop.Color.RGB = getPaletteColor(color)
@ -2013,46 +2103,46 @@ func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
gradient.Stop = stops
fill.GradientFill = &gradient
case "pattern":
if formatCellStyle.Fill.Pattern > 18 || formatCellStyle.Fill.Pattern < 0 {
if formatStyle.Fill.Pattern > 18 || formatStyle.Fill.Pattern < 0 {
break
}
if len(formatCellStyle.Fill.Color) < 1 {
if len(formatStyle.Fill.Color) < 1 {
break
}
var pattern xlsxPatternFill
pattern.PatternType = patterns[formatCellStyle.Fill.Pattern]
pattern.FgColor.RGB = getPaletteColor(formatCellStyle.Fill.Color[0])
pattern.PatternType = patterns[formatStyle.Fill.Pattern]
if fg {
pattern.FgColor.RGB = getPaletteColor(formatStyle.Fill.Color[0])
} else {
pattern.BgColor.RGB = getPaletteColor(formatStyle.Fill.Color[0])
}
fill.PatternFill = &pattern
}
style.Fills.Count++
style.Fills.Fill = append(style.Fills.Fill, &fill)
return style.Fills.Count - 1
return &fill
}
// setAlignment provides function to formatting information pertaining to text
// alignment in cells. There are a variety of choices for how text is aligned
// both horizontally and vertically, as well as indentation settings, and so on.
func setAlignment(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) (bool, *xlsxAlignment) {
if formatCellStyle.Alignment == nil {
return false, &xlsxAlignment{}
func setAlignment(formatStyle *formatStyle) *xlsxAlignment {
var alignment xlsxAlignment
if formatStyle.Alignment != nil {
alignment.Horizontal = formatStyle.Alignment.Horizontal
alignment.Indent = formatStyle.Alignment.Indent
alignment.JustifyLastLine = formatStyle.Alignment.JustifyLastLine
alignment.ReadingOrder = formatStyle.Alignment.ReadingOrder
alignment.RelativeIndent = formatStyle.Alignment.RelativeIndent
alignment.ShrinkToFit = formatStyle.Alignment.ShrinkToFit
alignment.TextRotation = formatStyle.Alignment.TextRotation
alignment.Vertical = formatStyle.Alignment.Vertical
alignment.WrapText = formatStyle.Alignment.WrapText
}
var alignment = xlsxAlignment{
Horizontal: formatCellStyle.Alignment.Horizontal,
Indent: formatCellStyle.Alignment.Indent,
JustifyLastLine: formatCellStyle.Alignment.JustifyLastLine,
ReadingOrder: formatCellStyle.Alignment.ReadingOrder,
RelativeIndent: formatCellStyle.Alignment.RelativeIndent,
ShrinkToFit: formatCellStyle.Alignment.ShrinkToFit,
TextRotation: formatCellStyle.Alignment.TextRotation,
Vertical: formatCellStyle.Alignment.Vertical,
WrapText: formatCellStyle.Alignment.WrapText,
}
return true, &alignment
return &alignment
}
// setBorders provides function to add border elements in the styles.xml by
// given borders format settings.
func setBorders(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
func setBorders(formatStyle *formatStyle) *xlsxBorder {
var styles = []string{
"none",
"thin",
@ -2071,7 +2161,7 @@ func setBorders(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
}
var border xlsxBorder
for _, v := range formatCellStyle.Border {
for _, v := range formatStyle.Border {
if v.Style > 13 || v.Style < 0 {
continue
}
@ -2100,9 +2190,7 @@ func setBorders(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {
border.DiagonalDown = true
}
}
style.Borders.Count++
style.Borders.Border = append(style.Borders.Border, &border)
return style.Borders.Count - 1
return &border
}
// setCellXfs provides function to set describes all of the formatting for a
@ -2228,6 +2316,362 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) {
}
}
// SetConditionalFormat provides function to create conditional formatting rule
// for cell value. Conditional formatting is a feature of Excel which allows you
// to apply a format to a cell or a range of cells based on certain criteria.
//
// The type option is a required parameter and it has no default value.
// Allowable type values and their associated parameters are:
//
// Type | Parameters
// ---------------+------------------------------------
// cell | criteria
// | value
// | minimum
// | maximum
// date | criteria
// | value
// | minimum
// | maximum
// time_period | criteria
// text | criteria
// | value
// average | criteria
// duplicate | (none)
// unique | (none)
// top | criteria
// | value
// bottom | criteria
// | value
// blanks | (none)
// no_blanks | (none)
// errors | (none)
// no_errors | (none)
// 2_color_scale | min_type
// | max_type
// | min_value
// | max_value
// | min_color
// | max_color
// 3_color_scale | min_type
// | mid_type
// | max_type
// | min_value
// | mid_value
// | max_value
// | min_color
// | mid_color
// | max_color
// data_bar | min_type
// | max_type
// | min_value
// | max_value
// | bar_color
// formula | criteria
//
// The criteria parameter is used to set the criteria by which the cell data
// will be evaluated. It has no default value. The most common criteria as
// applied to {'type': 'cell'} are:
//
// between |
// not between |
// equal to | ==
// not equal to | !=
// greater than | >
// less than | <
// greater than or equal to | >=
// less than or equal to | <=
//
// You can either use Excel's textual description strings, in the first column
// above, or the more common symbolic alternatives.
//
// Additional criteria which are specific to other conditional format types are
// shown in the relevant sections below.
//
// value: The value is generally used along with the criteria parameter to set
// the rule by which the cell data will be evaluated:
//
// xlsx.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format))
//
// The value property can also be an cell reference:
//
// xlsx.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"$C$1"}]`, format))
//
// type: format - The format parameter is used to specify the format that will
// be applied to the cell when the conditional formatting criterion is met. The
// format is created using the NewConditionalStyle() method in the same way as
// cell formats:
//
// format1, err = xlsx.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
// if err != nil {
// fmt.Println(err)
// }
// xlsx.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format1))
//
// Note: In Excel, a conditional format is superimposed over the existing cell
// format and not all cell format properties can be modified. Properties that
// cannot be modified in a conditional format are font name, font size,
// superscript and subscript, diagonal borders, all alignment properties and all
// protection properties.
//
// Excel specifies some default formats to be used with conditional formatting.
// These can be replicated using the following excelize formats:
//
// // Rose format for bad conditional.
// format1, err = xlsx.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
//
// // Light yellow format for neutral conditional.
// format2, err = xlsx.NewConditionalStyle(`{"fill":{"type":"pattern","color":["#FEEAA0"],"pattern":1}}`)
//
// // Light green format for good conditional.
// format3, err = xlsx.NewConditionalStyle(`{"font":{"color":"#09600B"},"fill":{"type":"pattern","color":["#C7EECF"],"pattern":1}}`)
// t.Log(err)
//
// type: minimum - The minimum parameter is used to set the lower limiting value
// when the criteria is either "between" or "not between".
//
// // Hightlight cells rules: between...
// xlsx.SetConditionalFormat("Sheet1", "C1:C10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format1))
//
// type: maximum - The maximum parameter is used to set the upper limiting value
// when the criteria is either "between" or "not between". See the previous
// example.
//
// type: average - The average type is used to specify Excel's "Average" style
// conditional format:
//
// // Top/Bottom rules: Above Average...
// xlsx.SetConditionalFormat("Sheet1", "I1:I10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": true}]`, format1))
//
// // Top/Bottom rules: Below Average...
// xlsx.SetConditionalFormat("Sheet1", "J1:J10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": false}]`, format2))
//
// type: duplicate - The duplicate type is used to highlight duplicate cells in a range:
//
// // Hightlight cells rules: Duplicate Values...
// xlsx.SetConditionalFormat("Sheet1", "G1:G10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format1))
//
// type: unique - The unique type is used to highlight unique cells in a range:
//
// // Hightlight cells rules: Not Equal To...
// xlsx.SetConditionalFormat("Sheet1", "F1:F10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format1))
//
// type: top - The top type is used to specify the top n values by number or percentage in a range:
//
// // Top/Bottom rules: Top 10.
// xlsx.SetConditionalFormat("Sheet1", "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6"}]`, format1))
//
// The criteria can be used to indicate that a percentage condition is required:
//
// xlsx.SetConditionalFormat("Sheet1", "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6","percent":true}]`, format1))
//
// type: 2_color_scale - The 2_color_scale type is used to specify Excel's "2
// Color Scale" style conditional format:
//
// // Color scales: 2 color.
// xlsx.SetConditionalFormat("Sheet1", "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`)
//
// This conditional type can be modified with min_type, max_type, min_value,
// max_value, min_color and max_color, see below.
//
// type: 3_color_scale - The 3_color_scale type is used to specify Excel's "3
// Color Scale" style conditional format:
//
// // Color scales: 3 color.
// xlsx.SetConditionalFormat("Sheet1", "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)
//
// This conditional type can be modified with min_type, mid_type, max_type,
// min_value, mid_value, max_value, min_color, mid_color and max_color, see
// below.
//
// type: data_bar - The data_bar type is used to specify Excel's "Data Bar"
// style conditional format.
//
// min_type - The min_type and max_type properties are available when the conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The mid_type is available for 3_color_scale. The properties are used as follows:
//
// // Data Bars: Gradient Fill.
// xlsx.SetConditionalFormat("Sheet1", "K1:K10", `[{"type":"data_bar", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)
//
// The available min/mid/max types are:
//
// min (for min_type only)
// num
// percent
// percentile
// formula
// max (for max_type only)
//
// mid_type - Used for 3_color_scale. Same as min_type, see above.
//
// max_type - Same as min_type, see above.
//
// min_value - The min_value and max_value properties are available when the
// conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The
// mid_value is available for 3_color_scale.
//
// mid_value - Used for 3_color_scale. Same as min_value, see above.
//
// max_value - Same as min_value, see above.
//
// min_color - The min_color and max_color properties are available when the
// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.
// The mid_color is available for 3_color_scale. The properties are used as
// follows:
//
// // Color scales: 3 color.
// xlsx.SetConditionalFormat("Sheet1", "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)
//
// mid_color - Used for 3_color_scale. Same as min_color, see above.
//
// max_color - Same as min_color, see above.
//
// bar_color - Used for data_bar. Same as min_color, see above.
//
func (f *File) SetConditionalFormat(sheet, area, formatSet string) {
var format []*formatConditional
json.Unmarshal([]byte(formatSet), &format)
drawContFmtFunc := map[string]func(p int, ct string, fmtCond *formatConditional) *xlsxCfRule{
"cellIs": drawCondFmtCellIs,
"top10": drawCondFmtTop10,
"aboveAverage": drawCondFmtAboveAverage,
"duplicateValues": drawCondFmtDuplicateUniqueValues,
"uniqueValues": drawCondFmtDuplicateUniqueValues,
"2_color_scale": drawCondFmtColorScale,
"3_color_scale": drawCondFmtColorScale,
"dataBar": drawCondFmtDataBar,
}
xlsx := f.workSheetReader(sheet)
cfRule := []*xlsxCfRule{}
for p, v := range format {
var vt, ct string
var ok bool
// "type" is a required parameter, check for valid validation types.
vt, ok = validType[v.Type]
if !ok {
continue
}
// Check for valid criteria types.
ct, ok = criteriaType[v.Criteria]
if !ok {
continue
}
drawfunc, ok := drawContFmtFunc[vt]
if ok {
cfRule = append(cfRule, drawfunc(p, ct, v))
}
}
xlsx.ConditionalFormatting = append(xlsx.ConditionalFormatting, &xlsxConditionalFormatting{
SQRef: area,
CfRule: cfRule,
})
}
// drawCondFmtCellIs provides 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 *formatConditional) *xlsxCfRule {
c := &xlsxCfRule{
Priority: p + 1,
Type: validType[format.Type],
Operator: ct,
DxfID: &format.Format,
}
// "between" and "not between" criteria require 2 values.
_, ok := map[string]bool{"between": true, "notBetween": true}[ct]
if ok {
c.Formula = append(c.Formula, format.Minimum)
c.Formula = append(c.Formula, format.Maximum)
}
_, ok = map[string]bool{"equal": true, "notEqual": true, "greaterThan": true, "lessThan": true}[ct]
if ok {
c.Formula = append(c.Formula, format.Value)
}
return c
}
// drawCondFmtTop10 provides 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 *formatConditional) *xlsxCfRule {
c := &xlsxCfRule{
Priority: p + 1,
Type: validType[format.Type],
Rank: 10,
DxfID: &format.Format,
Percent: format.Percent,
}
rank, err := strconv.Atoi(format.Value)
if err == nil {
c.Rank = rank
}
return c
}
// drawCondFmtAboveAverage provides 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 *formatConditional) *xlsxCfRule {
return &xlsxCfRule{
Priority: p + 1,
Type: validType[format.Type],
AboveAverage: &format.AboveAverage,
DxfID: &format.Format,
}
}
// drawCondFmtDuplicateUniqueValues provides 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 *formatConditional) *xlsxCfRule {
return &xlsxCfRule{
Priority: p + 1,
Type: validType[format.Type],
DxfID: &format.Format,
}
}
// drawCondFmtColorScale provides 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 *formatConditional) *xlsxCfRule {
c := &xlsxCfRule{
Priority: p + 1,
Type: "colorScale",
ColorScale: &xlsxColorScale{
Cfvo: []*xlsxCfvo{
{Type: format.MinType},
},
Color: []*xlsxColor{
{RGB: getPaletteColor(format.MinColor)},
},
},
}
if validType[format.Type] == "3_color_scale" {
c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MidType, Val: 50})
c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MidColor)})
}
c.ColorScale.Cfvo = append(c.ColorScale.Cfvo, &xlsxCfvo{Type: format.MaxType})
c.ColorScale.Color = append(c.ColorScale.Color, &xlsxColor{RGB: getPaletteColor(format.MaxColor)})
return c
}
// drawCondFmtDataBar provides function to create conditional formatting rule
// for data bar by given priority, criteria type and format settings.
func drawCondFmtDataBar(p int, ct string, format *formatConditional) *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)}},
},
}
}
// getPaletteColor provides function to convert the RBG color by given string.
func getPaletteColor(color string) string {
return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1)

Binary file not shown.

View File

@ -62,7 +62,7 @@ type xlsxColor struct {
Auto bool `xml:"auto,attr,omitempty"`
RGB string `xml:"rgb,attr,omitempty"`
Indexed int `xml:"indexed,attr,omitempty"`
Theme int `xml:"theme,attr,omitempty"`
Theme *int `xml:"theme,attr"`
Tint float64 `xml:"tint,attr,omitempty"`
}
@ -75,14 +75,20 @@ type xlsxFonts struct {
// font directly maps the font element.
type font struct {
B bool `xml:"b,omitempty"`
I bool `xml:"i,omitempty"`
U *attrValString `xml:"u"`
Sz *attrValInt `xml:"sz"`
Color *xlsxColor `xml:"color"`
Name *attrValString `xml:"name"`
Family *attrValInt `xml:"family"`
Scheme *attrValString `xml:"scheme"`
Name *attrValString `xml:"name"`
Charset *attrValInt `xml:"charset"`
Family *attrValInt `xml:"family"`
B bool `xml:"b,omitempty"`
I bool `xml:"i,omitempty"`
Strike bool `xml:"strike,omitempty"`
Outline bool `xml:"outline,omitempty"`
Shadow bool `xml:"shadow,omitempty"`
Condense bool `xml:"condense,omitempty"`
Extend bool `xml:"extend,omitempty"`
Color *xlsxColor `xml:"color"`
Sz *attrValInt `xml:"sz"`
U *attrValString `xml:"u"`
Scheme *attrValString `xml:"scheme"`
}
// xlsxFont directly maps the font element. This element defines the properties
@ -245,6 +251,17 @@ type xlsxDxf struct {
Dxf string `xml:",innerxml"`
}
// dxf directly maps the dxf element.
type dxf struct {
Font *font `xml:"font"`
NumFmt *xlsxNumFmt `xml:"numFmt"`
Fill *xlsxFill `xml:"fill"`
Alignment *xlsxAlignment `xml:"alignment"`
Border *xlsxBorder `xml:"border"`
Protection *xlsxProtection `xml:"protection"`
ExtLst *xlsxExt `xml:"extLst"`
}
// xlsxTableStyles directly maps the tableStyles element. This element
// represents a collection of Table style definitions for Table styles and
// PivotTable styles used in this workbook. It consists of a sequence of
@ -302,8 +319,8 @@ type formatFont struct {
Color string `json:"color"`
}
// formatCellStyle directly maps the styles settings of the cells.
type formatCellStyle struct {
// formatStyle directly maps the styles settings of the cells.
type formatStyle struct {
Border []struct {
Type string `json:"type"`
Color string `json:"color"`

View File

@ -365,8 +365,65 @@ type xlsxPhoneticPr struct {
// condition is true. This collection expresses conditional formatting rules
// applied to a particular cell or range.
type xlsxConditionalFormatting struct {
SQRef string `xml:"sqref,attr,omitempty"`
CfRule string `xml:",innerxml"`
SQRef string `xml:"sqref,attr,omitempty"`
CfRule []*xlsxCfRule `xml:"cfRule"`
}
// xlsxCfRule (Conditional Formatting Rule) represents a description of a
// conditional formatting rule.
type xlsxCfRule struct {
AboveAverage *bool `xml:"aboveAverage,attr"`
Bottom bool `xml:"bottom,attr,omitempty"`
DxfID *int `xml:"dxfId,attr"`
EqualAverage bool `xml:"equalAverage,attr,omitempty"`
Operator string `xml:"operator,attr,omitempty"`
Percent bool `xml:"percent,attr,omitempty"`
Priority int `xml:"priority,attr,omitempty"`
Rank int `xml:"rank,attr,omitempty"`
StdDev int `xml:"stdDev,attr,omitempty"`
StopIfTrue bool `xml:"stopIfTrue,attr,omitempty"`
Text string `xml:"text,attr,omitempty"`
TimePeriod string `xml:"timePeriod,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Formula []string `xml:"formula,omitempty"`
ColorScale *xlsxColorScale `xml:"colorScale"`
DataBar *xlsxDataBar `xml:"dataBar"`
IconSet *xlsxIconSet `xml:"iconSet"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxColorScale (Color Scale) describes a gradated color scale in this
// conditional formatting rule.
type xlsxColorScale struct {
Cfvo []*xlsxCfvo `xml:"cfvo"`
Color []*xlsxColor `xml:"color"`
}
// dataBar (Data Bar) describes a data bar conditional formatting rule.
type xlsxDataBar struct {
MaxLength int `xml:"maxLength,attr,omitempty"`
MinLength int `xml:"minLength,attr,omitempty"`
ShowValue bool `xml:"showValue,attr,omitempty"`
Cfvo []*xlsxCfvo `xml:"cfvo"`
Color []*xlsxColor `xml:"color"`
}
// xlsxIconSet (Icon Set) describes an icon set conditional formatting rule.
type xlsxIconSet struct {
Cfvo []*xlsxCfvo `xml:"cfvo"`
IconSet string `xml:"iconSet,attr,omitempty"`
ShowValue bool `xml:"showValue,attr,omitempty"`
Percent bool `xml:"percent,attr,omitempty"`
Reverse bool `xml:"reverse,attr,omitempty"`
}
// cfvo (Conditional Format Value Object) describes the values of the
// interpolation points in a gradient scale.
type xlsxCfvo struct {
Gte bool `xml:"gte,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Val int `xml:"val,attr"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxHyperlinks directly maps the hyperlinks element in the namespace
@ -465,3 +522,28 @@ type formatPanes struct {
Pane string `json:"pane"`
} `json:"panes"`
}
// formatConditional
type formatConditional struct {
Type string `json:"type"`
AboveAverage bool `json:"above_average"`
Percent bool `json:"percent"`
Format int `json:"format"`
Criteria string `json:"criteria"`
Value string `json:"value,omitempty"`
Minimum string `json:"minimum,omitempty"`
Maximum string `json:"maximum,omitempty"`
MinType string `json:"min_type,omitempty"`
MidType string `json:"mid_type,omitempty"`
MaxType string `json:"max_type,omitempty"`
MinValue string `json:"min_value,omitempty"`
MidValue string `json:"mid_value,omitempty"`
MaxValue string `json:"max_value,omitempty"`
MinColor string `json:"min_color,omitempty"`
MidColor string `json:"mid_color,omitempty"`
MaxColor string `json:"max_color,omitempty"`
MinLength string `json:"min_length,omitempty"`
MaxLength string `json:"max_length,omitempty"`
MultiRange string `json:"multi_range,omitempty"`
BarColor string `json:"bar_color,omitempty"`
}