summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2020-03-10 00:04:23 +0800
committerxuri <xuri.me@gmail.com>2020-03-10 00:04:23 +0800
commit9e2318cefa4ebaa7bf6b1dbc95b30ad7a32366b1 (patch)
tree5662b564bb64068e0f60f1ade1dd5679f9df3974
parent2ccb8f62edd5d1ce039e663591964b9066fd2f4e (diff)
Resolve #470, export Style structs to allow create the style for cells by given JSON or structure
-rw-r--r--excelize.go2
-rw-r--r--excelize_test.go40
-rw-r--r--shape.go2
-rw-r--r--styles.go176
-rw-r--r--styles_test.go23
-rw-r--r--xmlChart.go2
-rw-r--r--xmlDrawing.go4
-rw-r--r--xmlStyles.go84
8 files changed, 207 insertions, 126 deletions
diff --git a/excelize.go b/excelize.go
index 0962122..795120d 100644
--- a/excelize.go
+++ b/excelize.go
@@ -137,7 +137,7 @@ func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error {
return err
}
if s == 0 {
- style, _ := f.NewStyle(`{"number_format": ` + strconv.Itoa(format) + `}`)
+ style, _ := f.NewStyle(&Style{NumFmt: format})
_ = f.SetCellStyle(sheet, axis, axis, style)
}
return err
diff --git a/excelize_test.go b/excelize_test.go
index b78aac8..1ce4fe9 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -580,7 +580,45 @@ func TestSetCellStyleBorder(t *testing.T) {
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
// Test set border and solid style pattern fill for a single cell.
- style, err = f.NewStyle(`{"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}}`)
+ style, err = f.NewStyle(&Style{
+ Border: []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: Fill{
+ Type: "pattern",
+ Color: []string{"#E0EBF5"},
+ Pattern: 1,
+ },
+ })
if !assert.NoError(t, err) {
t.FailNow()
}
diff --git a/shape.go b/shape.go
index e9bdb42..0455b22 100644
--- a/shape.go
+++ b/shape.go
@@ -378,7 +378,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
if len(formatSet.Paragraph) < 1 {
formatSet.Paragraph = []formatShapeParagraph{
{
- Font: formatFont{
+ Font: Font{
Bold: false,
Italic: false,
Underline: "none",
diff --git a/styles.go b/styles.go
index caf2732..175a17c 100644
--- a/styles.go
+++ b/styles.go
@@ -1024,16 +1024,16 @@ func (f *File) styleSheetWriter() {
// parseFormatStyleSet provides a function to parse the format settings of the
// cells and conditional formats.
-func parseFormatStyleSet(style string) (*formatStyle, error) {
- format := formatStyle{
+func parseFormatStyleSet(style string) (*Style, error) {
+ format := Style{
DecimalPlaces: 2,
}
err := json.Unmarshal([]byte(style), &format)
return &format, err
}
-// NewStyle provides a function to create style for cells by given style
-// format. Note that the color field uses RGB color code.
+// NewStyle provides a function to create the style for cells by given JSON or
+// structure. Note that the color field uses RGB color code.
//
// The following shows the border styles sorted by excelize index number:
//
@@ -1888,18 +1888,26 @@ func parseFormatStyleSet(style string) (*formatStyle, error) {
//
// f := excelize.NewFile()
// f.SetCellValue("Sheet1", "A6", 42920.5)
-// style, err := f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
+// exp := "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"
+// style, err := f.NewStyle(&excelize.Style{CustomNumFmt: &exp})
// err = f.SetCellStyle("Sheet1", "A6", "A6", style)
//
// Cell Sheet1!A6 in the Excel Application: martes, 04 de Julio de 2017
//
-func (f *File) NewStyle(style string) (int, error) {
+func (f *File) NewStyle(style interface{}) (int, error) {
+ var fs *Style
+ var err error
var cellXfsID, fontID, borderID, fillID int
- s := f.stylesReader()
- fs, err := parseFormatStyleSet(style)
- if err != nil {
- return cellXfsID, err
+ switch v := style.(type) {
+ case string:
+ fs, err = parseFormatStyleSet(v)
+ if err != nil {
+ return cellXfsID, err
+ }
+ case *Style:
+ fs = v
}
+ s := f.stylesReader()
numFmtID := setNumFmt(s, fs)
if fs.Font != nil {
@@ -1978,34 +1986,34 @@ func (f *File) readDefaultFont() *xlsxFont {
// setFont provides a function to add font style by given cell format
// settings.
-func (f *File) setFont(formatStyle *formatStyle) *xlsxFont {
+func (f *File) setFont(style *Style) *xlsxFont {
fontUnderlineType := map[string]string{"single": "single", "double": "double"}
- if formatStyle.Font.Size < 1 {
- formatStyle.Font.Size = 11
+ if style.Font.Size < 1 {
+ style.Font.Size = 11
}
- if formatStyle.Font.Color == "" {
- formatStyle.Font.Color = "#000000"
+ if style.Font.Color == "" {
+ style.Font.Color = "#000000"
}
fnt := xlsxFont{
- Sz: &attrValFloat{Val: float64Ptr(formatStyle.Font.Size)},
- Color: &xlsxColor{RGB: getPaletteColor(formatStyle.Font.Color)},
- Name: &attrValString{Val: stringPtr(formatStyle.Font.Family)},
+ Sz: &attrValFloat{Val: float64Ptr(style.Font.Size)},
+ Color: &xlsxColor{RGB: getPaletteColor(style.Font.Color)},
+ Name: &attrValString{Val: stringPtr(style.Font.Family)},
Family: &attrValInt{Val: intPtr(2)},
}
- if formatStyle.Font.Bold {
- fnt.B = &formatStyle.Font.Bold
+ if style.Font.Bold {
+ fnt.B = &style.Font.Bold
}
- if formatStyle.Font.Italic {
- fnt.I = &formatStyle.Font.Italic
+ if style.Font.Italic {
+ fnt.I = &style.Font.Italic
}
if *fnt.Name.Val == "" {
*fnt.Name.Val = f.GetDefaultFont()
}
- if formatStyle.Font.Strike {
+ if style.Font.Strike {
strike := true
fnt.Strike = &strike
}
- val, ok := fontUnderlineType[formatStyle.Font.Underline]
+ val, ok := fontUnderlineType[style.Font.Underline]
if ok {
fnt.U = &attrValString{Val: stringPtr(val)}
}
@@ -2014,36 +2022,36 @@ func (f *File) setFont(formatStyle *formatStyle) *xlsxFont {
// setNumFmt provides a function to check if number format code in the range
// of built-in values.
-func setNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
+func setNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
dp := "0."
numFmtID := 164 // Default custom number format code from 164.
- if formatStyle.DecimalPlaces < 0 || formatStyle.DecimalPlaces > 30 {
- formatStyle.DecimalPlaces = 2
+ if style.DecimalPlaces < 0 || style.DecimalPlaces > 30 {
+ style.DecimalPlaces = 2
}
- for i := 0; i < formatStyle.DecimalPlaces; i++ {
+ for i := 0; i < style.DecimalPlaces; i++ {
dp += "0"
}
- if formatStyle.CustomNumFmt != nil {
- return setCustomNumFmt(style, formatStyle)
+ if style.CustomNumFmt != nil {
+ return setCustomNumFmt(styleSheet, style)
}
- _, ok := builtInNumFmt[formatStyle.NumFmt]
+ _, ok := builtInNumFmt[style.NumFmt]
if !ok {
- fc, currency := currencyNumFmt[formatStyle.NumFmt]
+ fc, currency := currencyNumFmt[style.NumFmt]
if !currency {
- return setLangNumFmt(style, formatStyle)
+ return setLangNumFmt(styleSheet, style)
}
fc = strings.Replace(fc, "0.00", dp, -1)
- if formatStyle.NegRed {
+ if style.NegRed {
fc = fc + ";[Red]" + fc
}
- if style.NumFmts != nil {
- numFmtID = style.NumFmts.NumFmt[len(style.NumFmts.NumFmt)-1].NumFmtID + 1
+ if styleSheet.NumFmts != nil {
+ numFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1
nf := xlsxNumFmt{
FormatCode: fc,
NumFmtID: numFmtID,
}
- style.NumFmts.NumFmt = append(style.NumFmts.NumFmt, &nf)
- style.NumFmts.Count++
+ styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf)
+ styleSheet.NumFmts.Count++
} else {
nf := xlsxNumFmt{
FormatCode: fc,
@@ -2053,61 +2061,61 @@ func setNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
NumFmt: []*xlsxNumFmt{&nf},
Count: 1,
}
- style.NumFmts = &numFmts
+ styleSheet.NumFmts = &numFmts
}
return numFmtID
}
- return formatStyle.NumFmt
+ return style.NumFmt
}
// setCustomNumFmt provides a function to set custom number format code.
-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)
- style.NumFmts.Count++
+func setCustomNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
+ nf := xlsxNumFmt{FormatCode: *style.CustomNumFmt}
+ if styleSheet.NumFmts != nil {
+ nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1
+ styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf)
+ styleSheet.NumFmts.Count++
} else {
nf.NumFmtID = 164
numFmts := xlsxNumFmts{
NumFmt: []*xlsxNumFmt{&nf},
Count: 1,
}
- style.NumFmts = &numFmts
+ styleSheet.NumFmts = &numFmts
}
return nf.NumFmtID
}
// setLangNumFmt provides a function to set number format code with language.
-func setLangNumFmt(style *xlsxStyleSheet, formatStyle *formatStyle) int {
- numFmts, ok := langNumFmt[formatStyle.Lang]
+func setLangNumFmt(styleSheet *xlsxStyleSheet, style *Style) int {
+ numFmts, ok := langNumFmt[style.Lang]
if !ok {
return 0
}
var fc string
- fc, ok = numFmts[formatStyle.NumFmt]
+ fc, ok = numFmts[style.NumFmt]
if !ok {
return 0
}
nf := xlsxNumFmt{FormatCode: fc}
- if style.NumFmts != nil {
- nf.NumFmtID = style.NumFmts.NumFmt[len(style.NumFmts.NumFmt)-1].NumFmtID + 1
- style.NumFmts.NumFmt = append(style.NumFmts.NumFmt, &nf)
- style.NumFmts.Count++
+ if styleSheet.NumFmts != nil {
+ nf.NumFmtID = styleSheet.NumFmts.NumFmt[len(styleSheet.NumFmts.NumFmt)-1].NumFmtID + 1
+ styleSheet.NumFmts.NumFmt = append(styleSheet.NumFmts.NumFmt, &nf)
+ styleSheet.NumFmts.Count++
} else {
- nf.NumFmtID = formatStyle.NumFmt
+ nf.NumFmtID = style.NumFmt
numFmts := xlsxNumFmts{
NumFmt: []*xlsxNumFmt{&nf},
Count: 1,
}
- style.NumFmts = &numFmts
+ styleSheet.NumFmts = &numFmts
}
return nf.NumFmtID
}
// setFills provides a function to add fill elements in the styles.xml by
// given cell format settings.
-func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
+func setFills(style *Style, fg bool) *xlsxFill {
var patterns = []string{
"none",
"solid",
@@ -2138,15 +2146,15 @@ func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
}
var fill xlsxFill
- switch formatStyle.Fill.Type {
+ switch style.Fill.Type {
case "gradient":
- if len(formatStyle.Fill.Color) != 2 {
+ if len(style.Fill.Color) != 2 {
break
}
var gradient xlsxGradientFill
- switch formatStyle.Fill.Shading {
+ switch style.Fill.Shading {
case 0, 1, 2, 3:
- gradient.Degree = variants[formatStyle.Fill.Shading]
+ gradient.Degree = variants[style.Fill.Shading]
case 4:
gradient.Type = "path"
case 5:
@@ -2159,7 +2167,7 @@ func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
break
}
var stops []*xlsxGradientFillStop
- for index, color := range formatStyle.Fill.Color {
+ for index, color := range style.Fill.Color {
var stop xlsxGradientFillStop
stop.Position = float64(index)
stop.Color.RGB = getPaletteColor(color)
@@ -2168,18 +2176,18 @@ func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
gradient.Stop = stops
fill.GradientFill = &gradient
case "pattern":
- if formatStyle.Fill.Pattern > 18 || formatStyle.Fill.Pattern < 0 {
+ if style.Fill.Pattern > 18 || style.Fill.Pattern < 0 {
break
}
- if len(formatStyle.Fill.Color) < 1 {
+ if len(style.Fill.Color) < 1 {
break
}
var pattern xlsxPatternFill
- pattern.PatternType = patterns[formatStyle.Fill.Pattern]
+ pattern.PatternType = patterns[style.Fill.Pattern]
if fg {
- pattern.FgColor.RGB = getPaletteColor(formatStyle.Fill.Color[0])
+ pattern.FgColor.RGB = getPaletteColor(style.Fill.Color[0])
} else {
- pattern.BgColor.RGB = getPaletteColor(formatStyle.Fill.Color[0])
+ pattern.BgColor.RGB = getPaletteColor(style.Fill.Color[0])
}
fill.PatternFill = &pattern
default:
@@ -2192,36 +2200,36 @@ func setFills(formatStyle *formatStyle, fg bool) *xlsxFill {
// 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(formatStyle *formatStyle) *xlsxAlignment {
+func setAlignment(style *Style) *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
+ if style.Alignment != nil {
+ alignment.Horizontal = style.Alignment.Horizontal
+ alignment.Indent = style.Alignment.Indent
+ alignment.JustifyLastLine = style.Alignment.JustifyLastLine
+ alignment.ReadingOrder = style.Alignment.ReadingOrder
+ alignment.RelativeIndent = style.Alignment.RelativeIndent
+ alignment.ShrinkToFit = style.Alignment.ShrinkToFit
+ alignment.TextRotation = style.Alignment.TextRotation
+ alignment.Vertical = style.Alignment.Vertical
+ alignment.WrapText = style.Alignment.WrapText
}
return &alignment
}
// setProtection provides a function to set protection properties associated
// with the cell.
-func setProtection(formatStyle *formatStyle) *xlsxProtection {
+func setProtection(style *Style) *xlsxProtection {
var protection xlsxProtection
- if formatStyle.Protection != nil {
- protection.Hidden = formatStyle.Protection.Hidden
- protection.Locked = formatStyle.Protection.Locked
+ if style.Protection != nil {
+ protection.Hidden = style.Protection.Hidden
+ protection.Locked = style.Protection.Locked
}
return &protection
}
// setBorders provides a function to add border elements in the styles.xml by
// given borders format settings.
-func setBorders(formatStyle *formatStyle) *xlsxBorder {
+func setBorders(style *Style) *xlsxBorder {
var styles = []string{
"none",
"thin",
@@ -2240,7 +2248,7 @@ func setBorders(formatStyle *formatStyle) *xlsxBorder {
}
var border xlsxBorder
- for _, v := range formatStyle.Border {
+ for _, v := range style.Border {
if 0 <= v.Style && v.Style < 14 {
var color xlsxColor
color.RGB = getPaletteColor(v.Color)
diff --git a/styles_test.go b/styles_test.go
index 4e9b411..5a9a771 100644
--- a/styles_test.go
+++ b/styles_test.go
@@ -191,6 +191,8 @@ func TestNewStyle(t *testing.T) {
font := styles.Fonts.Font[fontID]
assert.Contains(t, *font.Name.Val, "Times New Roman", "Stored font should contain font name")
assert.Equal(t, 2, styles.CellXfs.Count, "Should have 2 styles")
+ _, err = f.NewStyle(&Style{})
+ assert.NoError(t, err)
}
func TestGetDefaultFont(t *testing.T) {
@@ -207,3 +209,24 @@ func TestSetDefaultFont(t *testing.T) {
assert.Equal(t, s, "Ariel", "Default font should change to Ariel")
assert.Equal(t, *styles.CellStyles.CellStyle[0].CustomBuiltIn, true)
}
+
+func TestStylesReader(t *testing.T) {
+ f := NewFile()
+ // Test read styles with unsupport charset.
+ f.Styles = nil
+ f.XLSX["xl/styles.xml"] = MacintoshCyrillicCharset
+ assert.EqualValues(t, new(xlsxStyleSheet), f.stylesReader())
+}
+
+func TestThemeReader(t *testing.T) {
+ f := NewFile()
+ // Test read theme with unsupport charset.
+ f.XLSX["xl/theme/theme1.xml"] = MacintoshCyrillicCharset
+ assert.EqualValues(t, new(xlsxTheme), f.themeReader())
+}
+
+func TestSetCellStyle(t *testing.T) {
+ f := NewFile()
+ // Test set cell style on not exists worksheet.
+ assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN is not exist")
+}
diff --git a/xmlChart.go b/xmlChart.go
index 8d24552..03b47a1 100644
--- a/xmlChart.go
+++ b/xmlChart.go
@@ -604,7 +604,7 @@ type formatChart struct {
type formatChartLegend struct {
None bool `json:"none"`
DeleteSeries []int `json:"delete_series"`
- Font formatFont `json:"font"`
+ Font Font `json:"font"`
Layout formatLayout `json:"layout"`
Position string `json:"position"`
ShowLegendEntry bool `json:"show_legend_entry"`
diff --git a/xmlDrawing.go b/xmlDrawing.go
index 5bb5977..2bad16a 100644
--- a/xmlDrawing.go
+++ b/xmlDrawing.go
@@ -417,8 +417,8 @@ type formatShape struct {
// formatShapeParagraph directly maps the format settings of the paragraph in
// the shape.
type formatShapeParagraph struct {
- Font formatFont `json:"font"`
- Text string `json:"text"`
+ Font Font `json:"font"`
+ Text string `json:"text"`
}
// formatShapeColor directly maps the color settings of the shape.
diff --git a/xmlStyles.go b/xmlStyles.go
index 0313008..d6aa4f9 100644
--- a/xmlStyles.go
+++ b/xmlStyles.go
@@ -313,8 +313,28 @@ type xlsxStyleColors struct {
Color string `xml:",innerxml"`
}
-// formatFont directly maps the styles settings of the fonts.
-type formatFont struct {
+// Alignment directly maps the alignment settings of the cells.
+type Alignment struct {
+ Horizontal string `json:"horizontal"`
+ Indent int `json:"indent"`
+ JustifyLastLine bool `json:"justify_last_line"`
+ ReadingOrder uint64 `json:"reading_order"`
+ RelativeIndent int `json:"relative_indent"`
+ ShrinkToFit bool `json:"shrink_to_fit"`
+ TextRotation int `json:"text_rotation"`
+ Vertical string `json:"vertical"`
+ WrapText bool `json:"wrap_text"`
+}
+
+// Border directly maps the border settings of the cells.
+type Border struct {
+ Type string `json:"type"`
+ Color string `json:"color"`
+ Style int `json:"style"`
+}
+
+// Font directly maps the font settings of the fonts.
+type Font struct {
Bold bool `json:"bold"`
Italic bool `json:"italic"`
Underline string `json:"underline"`
@@ -324,38 +344,30 @@ type formatFont struct {
Color string `json:"color"`
}
-// formatStyle directly maps the styles settings of the cells.
-type formatStyle 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"`
- Font *formatFont `json:"font"`
- Alignment *struct {
- Horizontal string `json:"horizontal"`
- Indent int `json:"indent"`
- JustifyLastLine bool `json:"justify_last_line"`
- ReadingOrder uint64 `json:"reading_order"`
- RelativeIndent int `json:"relative_indent"`
- ShrinkToFit bool `json:"shrink_to_fit"`
- TextRotation int `json:"text_rotation"`
- Vertical string `json:"vertical"`
- WrapText bool `json:"wrap_text"`
- } `json:"alignment"`
- Protection *struct {
- Hidden bool `json:"hidden"`
- Locked bool `json:"locked"`
- } `json:"protection"`
- NumFmt int `json:"number_format"`
- DecimalPlaces int `json:"decimal_places"`
- CustomNumFmt *string `json:"custom_number_format"`
- Lang string `json:"lang"`
- NegRed bool `json:"negred"`
+// Fill directly maps the fill settings of the cells.
+type Fill struct {
+ Type string `json:"type"`
+ Pattern int `json:"pattern"`
+ Color []string `json:"color"`
+ Shading int `json:"shading"`
+}
+
+// Protection directly maps the protection settings of the cells.
+type Protection struct {
+ Hidden bool `json:"hidden"`
+ Locked bool `json:"locked"`
+}
+
+// Style directly maps the style settings of the cells.
+type Style struct {
+ Border []Border `json:"border"`
+ Fill Fill `json:"fill"`
+ Font *Font `json:"font"`
+ Alignment *Alignment `json:"alignment"`
+ Protection *Protection `json:"protection"`
+ NumFmt int `json:"number_format"`
+ DecimalPlaces int `json:"decimal_places"`
+ CustomNumFmt *string `json:"custom_number_format"`
+ Lang string `json:"lang"`
+ NegRed bool `json:"negred"`
}