summaryrefslogtreecommitdiff
path: root/styles.go
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2020-05-22 16:53:46 +0800
committerGitHub <noreply@github.com>2020-05-22 16:53:46 +0800
commitec14de32f0c06f7a26b6b79578f666c0cc50b72c (patch)
treec6ebd61a7d9a7da5b993ffb82e4fb6c036e75d6a /styles.go
parentaa7eadbffe6ae2f9f86201bbaaa4c1d1e8829cae (diff)
parent2efc7107ff30dc7f1e1a798082616ee488f99489 (diff)
Merge branch 'master' into fix/cell_lock
Diffstat (limited to 'styles.go')
-rw-r--r--styles.go313
1 files changed, 198 insertions, 115 deletions
diff --git a/styles.go b/styles.go
index d6d267d..72b2071 100644
--- a/styles.go
+++ b/styles.go
@@ -1,18 +1,22 @@
-// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excelâ„¢ 2007 and later. Support save file without losing original
-// charts of XLSX. This library needs Go version 1.8 or later.
+// charts of XLSX. This library needs Go version 1.10 or later.
package excelize
import (
+ "bytes"
"encoding/json"
"encoding/xml"
+ "errors"
"fmt"
+ "io"
+ "log"
"math"
"strconv"
"strings"
@@ -852,7 +856,7 @@ func formatToInt(i int, v string) string {
if err != nil {
return v
}
- return fmt.Sprintf("%d", int(f))
+ return fmt.Sprintf("%d", int64(f))
}
// formatToFloat provides a function to convert original string to float
@@ -997,11 +1001,16 @@ func is12HourTime(format string) bool {
// stylesReader provides a function to get the pointer to the structure after
// deserialization of xl/styles.xml.
func (f *File) stylesReader() *xlsxStyleSheet {
+ var err error
+
if f.Styles == nil {
- var styleSheet xlsxStyleSheet
- _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/styles.xml")), &styleSheet)
- f.Styles = &styleSheet
+ f.Styles = new(xlsxStyleSheet)
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))).
+ Decode(f.Styles); err != nil && err != io.EOF {
+ log.Printf("xml decode error: %s", err)
+ }
}
+
return f.Styles
}
@@ -1010,22 +1019,31 @@ func (f *File) stylesReader() *xlsxStyleSheet {
func (f *File) styleSheetWriter() {
if f.Styles != nil {
output, _ := xml.Marshal(f.Styles)
- f.saveFileList("xl/styles.xml", replaceWorkSheetsRelationshipsNameSpaceBytes(output))
+ f.saveFileList("xl/styles.xml", replaceRelationshipsNameSpaceBytes(output))
+ }
+}
+
+// sharedStringsWriter provides a function to save xl/sharedStrings.xml after
+// serialize structure.
+func (f *File) sharedStringsWriter() {
+ if f.SharedStrings != nil {
+ output, _ := xml.Marshal(f.SharedStrings)
+ f.saveFileList("xl/sharedStrings.xml", replaceRelationshipsNameSpaceBytes(output))
}
}
// 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 pointer. Note that the color field uses RGB color code.
//
// The following shows the border styles sorted by excelize index number:
//
@@ -1880,26 +1898,33 @@ 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
+ default:
+ return cellXfsID, errors.New("invalid parameter type")
}
+ s := f.stylesReader()
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]),
- })
+ s.Fonts.Font = append(s.Fonts.Font, f.setFont(fs))
fontID = s.Fonts.Count - 1
}
@@ -1930,12 +1955,16 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
return 0, err
}
dxf := dxf{
- Fill: setFills(fs, false),
- Alignment: setAlignment(fs),
- Border: setBorders(fs),
+ Fill: setFills(fs, false),
+ }
+ if fs.Alignment != nil {
+ dxf.Alignment = setAlignment(fs)
+ }
+ if len(fs.Border) > 0 {
+ dxf.Border = setBorders(fs)
}
if fs.Font != nil {
- dxf.Font = setFont(fs)
+ dxf.Font = f.setFont(fs)
}
dxfStr, _ := xml.Marshal(dxf)
if s.Dxfs == nil {
@@ -1948,67 +1977,97 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
return s.Dxfs.Count - 1, nil
}
+// GetDefaultFont provides the default font name currently set in the workbook
+// Documents generated by excelize start with Calibri.
+func (f *File) GetDefaultFont() string {
+ font := f.readDefaultFont()
+ return *font.Name.Val
+}
+
+// SetDefaultFont changes the default font in the workbook.
+func (f *File) SetDefaultFont(fontName string) {
+ font := f.readDefaultFont()
+ font.Name.Val = stringPtr(fontName)
+ s := f.stylesReader()
+ s.Fonts.Font[0] = font
+ custom := true
+ s.CellStyles.CellStyle[0].CustomBuiltIn = &custom
+}
+
+// readDefaultFont provides an unmarshalled font value.
+func (f *File) readDefaultFont() *xlsxFont {
+ s := f.stylesReader()
+ return s.Fonts.Font[0]
+}
+
// setFont provides a function to add font style by given cell format
// settings.
-func setFont(formatStyle *formatStyle) *font {
+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 style.Font.Color == "" {
+ style.Font.Color = "#000000"
}
- if formatStyle.Font.Color == "" {
- formatStyle.Font.Color = "#000000"
+ fnt := xlsxFont{
+ 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)},
}
- f := font{
- 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},
+ if style.Font.Bold {
+ fnt.B = &style.Font.Bold
}
- if f.Name.Val == "" {
- f.Name.Val = "Calibri"
- f.Scheme = &attrValString{Val: "minor"}
+ if style.Font.Italic {
+ fnt.I = &style.Font.Italic
}
- val, ok := fontUnderlineType[formatStyle.Font.Underline]
+ if *fnt.Name.Val == "" {
+ *fnt.Name.Val = f.GetDefaultFont()
+ }
+ if style.Font.Strike {
+ strike := true
+ fnt.Strike = &strike
+ }
+ val, ok := fontUnderlineType[style.Font.Underline]
if ok {
- f.U = &attrValString{Val: val}
+ fnt.U = &attrValString{Val: stringPtr(val)}
}
- return &f
+ return &fnt
}
// 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,
@@ -2018,61 +2077,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",
@@ -2103,15 +2162,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:
@@ -2124,7 +2183,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)
@@ -2133,18 +2192,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:
@@ -2157,36 +2216,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",
@@ -2205,7 +2264,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)
@@ -2240,21 +2299,21 @@ func setBorders(formatStyle *formatStyle) *xlsxBorder {
// cell.
func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, applyAlignment, applyProtection bool, alignment *xlsxAlignment, protection *xlsxProtection) int {
var xf xlsxXf
- xf.FontID = fontID
+ xf.FontID = intPtr(fontID)
if fontID != 0 {
- xf.ApplyFont = true
+ xf.ApplyFont = boolPtr(true)
}
- xf.NumFmtID = numFmtID
+ xf.NumFmtID = intPtr(numFmtID)
if numFmtID != 0 {
- xf.ApplyNumberFormat = true
+ xf.ApplyNumberFormat = boolPtr(true)
}
- xf.FillID = fillID
- xf.BorderID = borderID
+ xf.FillID = intPtr(fillID)
+ xf.BorderID = intPtr(borderID)
style.CellXfs.Count++
xf.Alignment = alignment
- xf.ApplyAlignment = applyAlignment
+ xf.ApplyAlignment = boolPtr(applyAlignment)
if applyProtection {
- xf.ApplyProtection = applyProtection
+ xf.ApplyProtection = boolPtr(applyProtection)
xf.Protection = protection
}
xfID := 0
@@ -2328,7 +2387,7 @@ func (f *File) GetCellStyle(sheet, axis string) (int, error) {
//
// Set font style for cell H9 on Sheet1:
//
-// style, err := f.NewStyle(`{"font":{"bold":true,"italic":true,"family":"Berlin Sans FB Demi","size":36,"color":"#777777"}}`)
+// style, err := f.NewStyle(`{"font":{"bold":true,"italic":true,"family":"Times New Roman","size":36,"color":"#777777"}}`)
// if err != nil {
// fmt.Println(err)
// }
@@ -2641,6 +2700,22 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
return err
}
+// UnsetConditionalFormat provides a function to unset the conditional format
+// by given worksheet name and range.
+func (f *File) UnsetConditionalFormat(sheet, area string) error {
+ ws, err := f.workSheetReader(sheet)
+ if err != nil {
+ return err
+ }
+ for i, cf := range ws.ConditionalFormatting {
+ if cf.SQRef == area {
+ ws.ConditionalFormatting = append(ws.ConditionalFormatting[:i], ws.ConditionalFormatting[i+1:]...)
+ return nil
+ }
+ }
+ return nil
+}
+
// 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.
@@ -2657,7 +2732,7 @@ func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule
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]
+ _, ok = map[string]bool{"equal": true, "notEqual": true, "greaterThan": true, "lessThan": true, "greaterThanOrEqual": true, "lessThanOrEqual": true, "containsText": true, "notContains": true, "beginsWith": true, "endsWith": true}[ct]
if ok {
c.Formula = append(c.Formula, format.Value)
}
@@ -2776,8 +2851,16 @@ func getPaletteColor(color string) string {
// themeReader provides a function to get the pointer to the xl/theme/theme1.xml
// structure after deserialization.
func (f *File) themeReader() *xlsxTheme {
- var theme xlsxTheme
- _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")), &theme)
+ var (
+ err error
+ theme xlsxTheme
+ )
+
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))).
+ Decode(&theme); err != nil && err != io.EOF {
+ log.Printf("xml decoder error: %s", err)
+ }
+
return &theme
}