diff options
| author | Ri Xu <xuri.me@gmail.com> | 2017-03-19 17:14:40 +0800 | 
|---|---|---|
| committer | Ri Xu <xuri.me@gmail.com> | 2017-03-19 17:14:40 +0800 | 
| commit | 68b4d1f54623ba3900675e7fd3f568f29991e651 (patch) | |
| tree | f19b7ac5552b10326752bbbc10d58c4eb27ff770 | |
| parent | de6e075713069bd71243930756ae6f707babf44e (diff) | |
- Set cell background color and style support;
- Rename function `SetBorder` to `SetCellStyle`;
- Complete `xlsxColor` structure definition;
- go test updated
| -rw-r--r-- | excelize_test.go | 53 | ||||
| -rw-r--r-- | styles.go | 176 | ||||
| -rw-r--r-- | xmlStyles.go | 45 | 
3 files changed, 240 insertions, 34 deletions
diff --git a/excelize_test.go b/excelize_test.go index e4892e6..c7c44a2 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -272,35 +272,72 @@ func TestSetRowHeight(t *testing.T) {  	}  } -func TestSetBorder(t *testing.T) { +func TestSetCellStyle(t *testing.T) {  	xlsx, err := OpenFile("./test/Workbook_2.xlsx")  	if err != nil {  		t.Log(err)  	}  	// Test set border with invalid style parameter. -	err = xlsx.SetBorder("Sheet1", "J21", "L25", "") +	err = xlsx.SetCellStyle("Sheet1", "J21", "L25", "")  	if err != nil {  		t.Log(err)  	}  	// Test set border with invalid style index number. -	err = xlsx.SetBorder("Sheet1", "J21", "L25", `{"border":[{"type":"left","color":"0000FF","style":-1},{"type":"top","color":"00FF00","style":14},{"type":"bottom","color":"FFFF00","style":5},{"type":"right","color":"FF0000","style":6},{"type":"diagonalDown","color":"A020F0","style":9},{"type":"diagonalUp","color":"A020F0","style":8}]}`) +	err = xlsx.SetCellStyle("Sheet1", "J21", "L25", `{"border":[{"type":"left","color":"0000FF","style":-1},{"type":"top","color":"00FF00","style":14},{"type":"bottom","color":"FFFF00","style":5},{"type":"right","color":"FF0000","style":6},{"type":"diagonalDown","color":"A020F0","style":9},{"type":"diagonalUp","color":"A020F0","style":8}]}`)  	if err != nil {  		t.Log(err)  	}  	if err != nil {  		t.Log(err)  	} -	// Test set border on overlapping area. -	err = xlsx.SetBorder("Sheet1", "J21", "L25", `{"border":[{"type":"left","color":"0000FF","style":2},{"type":"top","color":"00FF00","style":12},{"type":"bottom","color":"FFFF00","style":5},{"type":"right","color":"FF0000","style":6},{"type":"diagonalDown","color":"A020F0","style":9},{"type":"diagonalUp","color":"A020F0","style":8}]}`) +	// Test set border on overlapping area with vertical variants shading styles gradient fill. +	err = xlsx.SetCellStyle("Sheet1", "J21", "L25", `{"border":[{"type":"left","color":"0000FF","style":2},{"type":"top","color":"00FF00","style":12},{"type":"bottom","color":"FFFF00","style":5},{"type":"right","color":"FF0000","style":6},{"type":"diagonalDown","color":"A020F0","style":9},{"type":"diagonalUp","color":"A020F0","style":8}]}`)  	if err != nil {  		t.Log(err)  	} -	err = xlsx.SetBorder("Sheet1", "M28", "K24", `{"border":[{"type":"left","color":"0000FF","style":2},{"type":"top","color":"00FF00","style":3},{"type":"bottom","color":"FFFF00","style":4},{"type":"right","color":"FF0000","style":5},{"type":"diagonalDown","color":"A020F0","style":6},{"type":"diagonalUp","color":"A020F0","style":7}]}`) +	err = xlsx.SetCellStyle("Sheet1", "M28", "K24", `{"border":[{"type":"left","color":"0000FF","style":2},{"type":"top","color":"00FF00","style":3},{"type":"bottom","color":"FFFF00","style":4},{"type":"right","color":"FF0000","style":5},{"type":"diagonalDown","color":"A020F0","style":6},{"type":"diagonalUp","color":"A020F0","style":7}],"fill":[{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":1}]}`)  	if err != nil {  		t.Log(err)  	} -	// Test set border for a single cell. -	err = xlsx.SetBorder("Sheet1", "O22", "O22", `{"border":[{"type":"left","color":"0000FF","style":8},{"type":"top","color":"00FF00","style":9},{"type":"bottom","color":"FFFF00","style":10},{"type":"right","color":"FF0000","style":11},{"type":"diagonalDown","color":"A020F0","style":12},{"type":"diagonalUp","color":"A020F0","style":13}]}`) +	// Test set border and solid style pattern fill for a single cell. +	err = xlsx.SetCellStyle("Sheet1", "O22", "O22", `{"border":[{"type":"left","color":"0000FF","style":8},{"type":"top","color":"00FF00","style":9},{"type":"bottom","color":"FFFF00","style":10},{"type":"right","color":"FF0000","style":11},{"type":"diagonalDown","color":"A020F0","style":12},{"type":"diagonalUp","color":"A020F0","style":13}],"fill":[{"type":"pattern","color":["#E0EBF5"],"pattern":1}]}`) +	if err != nil { +		t.Log(err) +	} +	err = xlsx.Save() +	if err != nil { +		t.Log(err) +	} +} + +func TestSetCellStyleFill(t *testing.T) { +	xlsx, err := OpenFile("./test/Workbook_2.xlsx") +	if err != nil { +		t.Log(err) +	} +	// Test only set fill for a cell. +	err = xlsx.SetCellStyle("Sheet1", "N23", "N23", `{"fill":[{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":4}]}`) +	if err != nil { +		t.Log(err) +	} +	err = xlsx.SetCellStyle("Sheet1", "N24", "N24", `{"fill":[{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":5}]}`) +	if err != nil { +		t.Log(err) +	} +	// Test set fill for cell with invalid parameter. +	err = xlsx.SetCellStyle("Sheet1", "O23", "O23", `{"fill":[{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":6}]}`) +	if err != nil { +		t.Log(err) +	} +	err = xlsx.SetCellStyle("Sheet1", "O23", "O23", `{"fill":[{"type":"gradient","color":["#FFFFFF"],"shading":1}]}`) +	if err != nil { +		t.Log(err) +	} +	err = xlsx.SetCellStyle("Sheet1", "O23", "O23", `{"fill":[{"type":"pattern","color":[],"pattern":1}]}`) +	if err != nil { +		t.Log(err) +	} +	err = xlsx.SetCellStyle("Sheet1", "O23", "O23", `{"fill":[{"type":"pattern","color":["#E0EBF5"],"pattern":19}]}`)  	if err != nil {  		t.Log(err)  	} @@ -7,27 +7,41 @@ import (  	"strings"  ) -// parseFormatBordersSet provides function to parse the format settings of the +// parseFormatStyleSet provides function to parse the format settings of the  // borders. -func parseFormatBordersSet(bordersSet string) (*formatBorder, error) { -	var format formatBorder -	err := json.Unmarshal([]byte(bordersSet), &format) +func parseFormatStyleSet(style string) (*formatCellStyle, error) { +	var format formatCellStyle +	err := json.Unmarshal([]byte(style), &format)  	return &format, err  } -// SetBorder provides function to get value from cell by given sheet index and -// coordinate area in XLSX file. Note that the color field uses RGB color code -// and diagonalDown and diagonalUp type border should be use same color in the -// same coordinate area. +// SetCellStyle provides function to get value from cell by given sheet index +// and coordinate area in XLSX file. Note that the color field uses RGB color +// code and diagonalDown and diagonalUp type border should be use same color in +// the same coordinate area.  // -// For example create a borders of cell H9 on -// Sheet1: +// For example create a borders of cell H9 on Sheet1:  //  //    err := xlsx.SetBorder("Sheet1", "H9", "H9", `{"border":[{"type":"left","color":"0000FF","style":3},{"type":"top","color":"00FF00","style":4},{"type":"bottom","color":"FFFF00","style":5},{"type":"right","color":"FF0000","style":6},{"type":"diagonalDown","color":"A020F0","style":7},{"type":"diagonalUp","color":"A020F0","style":8}]}`)  //    if err != nil {  //        fmt.Println(err)  //    }  // +// Set gradient fill with vertical variants shading styles for cell H9 on +// Sheet1: +// +//    err := xlsx.SetBorder("Sheet1", "H9", "H9", `{"fill":[{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":1}]}`) +//    if err != nil { +//        fmt.Println(err) +//    } +// +// Set solid style pattern fill for cell H9 on Sheet1: +// +//    err := xlsx.SetBorder("Sheet1", "H9", "H9", `{"fill":[{"type":"pattern","color":["#E0EBF5"],"pattern":1}]}`) +//    if err != nil { +//        fmt.Println(err) +//    } +//  // The following shows the border styles sorted by excelize index number:  //  //    +-------+---------------+--------+-----------------+ @@ -82,15 +96,54 @@ func parseFormatBordersSet(bordersSet string) (*formatBorder, error) {  //    | 1     | ``-----------`` | 6     | ``===========`` |  //    +-------+-----------------+-------+-----------------+  // -func (f *File) SetBorder(sheet, hcell, vcell, style string) error { +// The following shows the shading styles sorted by excelize index number: +// +//    +-------+-----------------+-------+-----------------+ +//    | Index | Style           | Index | Style           | +//    +=======+=================+=======+=================+ +//    | 0     | Horizontal      | 3     | Diagonal down   | +//    +-------+-----------------+-------+-----------------+ +//    | 1     | Vertical        | 4     | From corner     | +//    +-------+-----------------+-------+-----------------+ +//    | 2     | Diagonal Up     | 5     | From center     | +//    +-------+-----------------+-------+-----------------+ +// +// The following shows the patterns styles sorted by excelize index number: +// +//    +-------+-----------------+-------+-----------------+ +//    | Index | Style           | Index | Style           | +//    +=======+=================+=======+=================+ +//    | 0     | None            | 10    | darkTrellis     | +//    +-------+-----------------+-------+-----------------+ +//    | 1     | solid           | 11    | lightHorizontal | +//    +-------+-----------------+-------+-----------------+ +//    | 2     | mediumGray      | 12    | lightVertical   | +//    +-------+-----------------+-------+-----------------+ +//    | 3     | darkGray        | 13    | lightDown       | +//    +-------+-----------------+-------+-----------------+ +//    | 4     | lightGray       | 14    | lightUp         | +//    +-------+-----------------+-------+-----------------+ +//    | 5     | darkHorizontal  | 15    | lightGrid       | +//    +-------+-----------------+-------+-----------------+ +//    | 6     | darkVertical    | 16    | lightTrellis    | +//    +-------+-----------------+-------+-----------------+ +//    | 7     | darkDown        | 17    | gray125         | +//    +-------+-----------------+-------+-----------------+ +//    | 8     | darkUp          | 18    | gray0625        | +//    +-------+-----------------+-------+-----------------+ +//    | 9     | darkGrid        |       |                 | +//    +-------+-----------------+-------+-----------------+ +// +func (f *File) SetCellStyle(sheet, hcell, vcell, style string) error {  	var styleSheet xlsxStyleSheet  	xml.Unmarshal([]byte(f.readXML("xl/styles.xml")), &styleSheet) -	formatBorder, err := parseFormatBordersSet(style) +	formatCellStyle, err := parseFormatStyleSet(style)  	if err != nil {  		return err  	} -	borderID := setBorders(&styleSheet, formatBorder) -	cellXfsID := setCellXfs(&styleSheet, borderID) +	borderID := setBorders(&styleSheet, formatCellStyle) +	fillID := setFills(&styleSheet, formatCellStyle) +	cellXfsID := setCellXfs(&styleSheet, fillID, borderID)  	output, err := xml.Marshal(styleSheet)  	if err != nil {  		return err @@ -100,9 +153,90 @@ func (f *File) SetBorder(sheet, hcell, vcell, style string) error {  	return err  } +// setFills provides function to add fill elements in the styles.xml by given +// cell format settings. +func setFills(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int { +	var patterns = []string{ +		"none", +		"solid", +		"mediumGray", +		"darkGray", +		"lightGray", +		"darkHorizontal", +		"darkVertical", +		"darkDown", +		"darkUp", +		"darkGrid", +		"darkTrellis", +		"lightHorizontal", +		"lightVertical", +		"lightDown", +		"lightUp", +		"lightGrid", +		"lightTrellis", +		"gray125", +		"gray0625", +	} + +	var variants = []float64{ +		90, +		0, +		45, +		135, +	} + +	var fill xlsxFill +	for _, v := range formatCellStyle.Fill { +		switch v.Type { +		case "gradient": +			if len(v.Color) != 2 { +				continue +			} +			var gradient xlsxGradientFill +			switch v.Shading { +			case 0, 1, 2, 3: +				gradient.Degree = variants[v.Shading] +			case 4: +				gradient.Type = "path" +			case 5: +				gradient.Type = "path" +				gradient.Bottom = 0.5 +				gradient.Left = 0.5 +				gradient.Right = 0.5 +				gradient.Top = 0.5 +			default: +				continue +			} +			var stops []*xlsxGradientFillStop +			for index, color := range v.Color { +				var stop xlsxGradientFillStop +				stop.Position = float64(index) +				stop.Color.RGB = getPaletteColor(color) +				stops = append(stops, &stop) +			} +			gradient.Stop = stops +			fill.GradientFill = &gradient +		case "pattern": +			if v.Pattern > 18 || v.Pattern < 0 { +				continue +			} +			if len(v.Color) < 1 { +				continue +			} +			var pattern xlsxPatternFill +			pattern.PatternType = patterns[v.Pattern] +			pattern.FgColor.RGB = getPaletteColor(v.Color[0]) +			fill.PatternFill = &pattern +		} +	} +	style.Fills.Count++ +	style.Fills.Fill = append(style.Fills.Fill, &fill) +	return style.Fills.Count - 1 +} +  // setBorders provides function to add border elements in the styles.xml by  // given borders format settings. -func setBorders(style *xlsxStyleSheet, formatBorder *formatBorder) int { +func setBorders(style *xlsxStyleSheet, formatCellStyle *formatCellStyle) int {  	var styles = []string{  		"none",  		"thin", @@ -121,12 +255,12 @@ func setBorders(style *xlsxStyleSheet, formatBorder *formatBorder) int {  	}  	var border xlsxBorder -	for _, v := range formatBorder.Border { +	for _, v := range formatCellStyle.Border {  		if v.Style > 13 || v.Style < 0 {  			continue  		}  		var color xlsxColor -		color.RGB = v.Color +		color.RGB = getPaletteColor(v.Color)  		switch v.Type {  		case "left":  			border.Left.Style = styles[v.Style] @@ -157,8 +291,9 @@ func setBorders(style *xlsxStyleSheet, formatBorder *formatBorder) int {  // setCellXfs provides function to set describes all of the formatting for a  // cell. -func setCellXfs(style *xlsxStyleSheet, borderID int) int { +func setCellXfs(style *xlsxStyleSheet, fillID, borderID int) int {  	var xf xlsxXf +	xf.FillID = fillID  	xf.BorderID = borderID  	style.CellXfs.Count++  	style.CellXfs.Xf = append(style.CellXfs.Xf, xf) @@ -209,3 +344,8 @@ func (f *File) setCellStyle(sheet, hcell, vcell string, styleID int) {  		}  	}  } + +// getPaletteColor provides function to convert the RBG color by given string. +func getPaletteColor(color string) string { +	return "FF" + strings.Replace(strings.ToUpper(color), "#", "", -1) +} diff --git a/xmlStyles.go b/xmlStyles.go index 7aa6479..954e431 100644 --- a/xmlStyles.go +++ b/xmlStyles.go @@ -43,10 +43,14 @@ type xlsxLine struct {  	Color *xlsxColor `xml:"color,omitempty"`  } -// xlsxColor is a common mapping used for both the fgColor and bgColor elements -// in the namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main - -// currently I have not checked it for completeness - it does as much as I need. +// xlsxColor is a common mapping used for both the fgColor and bgColor elements. +// Foreground color of the cell fill pattern. Cell fill patterns operate with +// two colors: a background color and a foreground color. These combine together +// to make a patterned cell fill. Background color of the cell fill pattern. +// Cell fill patterns operate with two colors: a background color and a +// foreground color. These combine together to make a patterned cell fill.  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"` @@ -71,14 +75,15 @@ type xlsxFont struct {  // cell fill consists of a background color, foreground color, and pattern to be  // applied across the cell.  type xlsxFills struct { -	Count int        `xml:"count,attr"` -	Fill  []xlsxFill `xml:"fill,omitempty"` +	Count int         `xml:"count,attr"` +	Fill  []*xlsxFill `xml:"fill,omitempty"`  }  // xlsxFill directly maps the fill element. This element specifies fill  // formatting.  type xlsxFill struct { -	PatternFill xlsxPatternFill `xml:"patternFill,omitempty"` +	PatternFill  *xlsxPatternFill  `xml:"patternFill,omitempty"` +	GradientFill *xlsxGradientFill `xml:"gradientFill,omitempty"`  }  // xlsxPatternFill directly maps the patternFill element in the namespace @@ -94,6 +99,24 @@ type xlsxPatternFill struct {  	BgColor     xlsxColor `xml:"bgColor,omitempty"`  } +// xlsxGradientFill defines a gradient-style cell fill. Gradient cell fills can +// use one or two colors as the end points of color interpolation. +type xlsxGradientFill struct { +	Bottom float64                 `xml:"bottom,attr,omitempty"` +	Degree float64                 `xml:"degree,attr,omitempty"` +	Left   float64                 `xml:"left,attr,omitempty"` +	Right  float64                 `xml:"right,attr,omitempty"` +	Top    float64                 `xml:"top,attr,omitempty"` +	Type   string                  `xml:"type,attr,omitempty"` +	Stop   []*xlsxGradientFillStop `xml:"stop,omitempty"` +} + +// xlsxGradientFillStop directly maps the stop element. +type xlsxGradientFillStop struct { +	Position float64   `xml:"position,attr"` +	Color    xlsxColor `xml:"color,omitempty"` +} +  // xlsxBorders directly maps the borders element. This element contains borders  // formatting information, specifying all border definitions for all cells in  // the workbook. @@ -247,11 +270,17 @@ type xlsxStyleColors struct {  	Color string `xml:",innerxml"`  } -// formatBorder directly maps the format settings of the borders. -type formatBorder struct { +// formatCellStyle directly maps the styles settings of the borders. +type formatCellStyle struct {  	Border []struct {  		Type  string `json:"type"`  		Color string `json:"color"`  		Style int    `json:"style"`  	} `json:"border"` +	Fill []struct { +		Type    string   `json:"type"` +		Pattern int      `json:"pattern"` +		Color   []string `json:"color"` +		Shading int      `json:"shading"` +	} `json:"fill"`  }  | 
