Breaking changes: replace the type `ShapeParagraph` with `RichTextRun`

- This removes the `Color` field from the type `Shape`, and uses the `Fill` instead of it
- Remove sharp symbol from hex RGB color
- Update unit tests
This commit is contained in:
xuri 2023-02-27 00:05:36 +08:00
parent 669c432ca1
commit 65a53b3ec6
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
19 changed files with 120 additions and 112 deletions

View File

@ -13489,7 +13489,7 @@ func (fn *formulaFuncs) LENB(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "LENB requires 1 string argument") return newErrorFormulaArg(formulaErrorVALUE, "LENB requires 1 string argument")
} }
bytes := 0 bytes := 0
for _, r := range []rune(argsList.Front().Value.(formulaArg).String) { for _, r := range argsList.Front().Value.(formulaArg).Value() {
b := utf8.RuneLen(r) b := utf8.RuneLen(r)
if b == 1 { if b == 1 {
bytes++ bytes++

View File

@ -843,7 +843,7 @@ type HyperlinkOpts struct {
// } // }
// // Set underline and font color style for the cell. // // Set underline and font color style for the cell.
// style, err := f.NewStyle(&excelize.Style{ // style, err := f.NewStyle(&excelize.Style{
// Font: &excelize.Font{Color: "#1265BE", Underline: "single"}, // Font: &excelize.Font{Color: "1265BE", Underline: "single"},
// }) // })
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err)

View File

@ -37,7 +37,7 @@ func TestConcurrency(t *testing.T) {
uint64(1<<32 - 1), true, complex64(5 + 10i), uint64(1<<32 - 1), true, complex64(5 + 10i),
})) }))
// Concurrency create style // Concurrency create style
style, err := f.NewStyle(&Style{Font: &Font{Color: "#1265BE", Underline: "single"}}) style, err := f.NewStyle(&Style{Font: &Font{Color: "1265BE", Underline: "single"}})
assert.NoError(t, err) assert.NoError(t, err)
// Concurrency set cell style // Concurrency set cell style
assert.NoError(t, f.SetCellStyle("Sheet1", "A3", "A3", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "A3", "A3", style))
@ -948,3 +948,7 @@ func TestSharedStringsError(t *testing.T) {
return assert.NoError(t, os.Remove(v.(string))) return assert.NoError(t, os.Remove(v.(string)))
}) })
} }
func TestSIString(t *testing.T) {
assert.Empty(t, xlsxSI{}.String())
}

View File

@ -200,7 +200,7 @@ func TestAddChart(t *testing.T) {
sheetName, cell string sheetName, cell string
opts *Chart opts *Chart
}{ }{
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: "col", Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "#000000"}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "#777777"}}}}, {sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: "col", Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "000000"}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}}}},
{sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: "colStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: "colStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: "colPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "100% Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: "colPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "100% Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: "col3DClustered", Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "3D Clustered Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: "col3DClustered", Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "3D Clustered Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},

View File

@ -320,7 +320,7 @@ func TestSetColStyle(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.SetCellValue("Sheet1", "B2", "Hello")) assert.NoError(t, f.SetCellValue("Sheet1", "B2", "Hello"))
styleID, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"#94d3a2"}, Pattern: 1}}) styleID, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"94D3A2"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
// Test set column style on not exists worksheet // Test set column style on not exists worksheet
assert.EqualError(t, f.SetColStyle("SheetN", "E", styleID), "sheet SheetN does not exist") assert.EqualError(t, f.SetColStyle("SheetN", "E", styleID), "sheet SheetN does not exist")

View File

@ -228,8 +228,8 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
ID: "_x0000_s1025", ID: "_x0000_s1025",
Type: "#_x0000_t202", Type: "#_x0000_t202",
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden", Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
Fillcolor: "#fbf6d6", Fillcolor: "#FBF6D6",
Strokecolor: "#edeaa1", Strokecolor: "#EDEAA1",
Val: v.Val, Val: v.Val,
} }
vml.Shape = append(vml.Shape, s) vml.Shape = append(vml.Shape, s)
@ -238,7 +238,7 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
} }
sp := encodeShape{ sp := encodeShape{
Fill: &vFill{ Fill: &vFill{
Color2: "#fbfe82", Color2: "#FBFE82",
Angle: -180, Angle: -180,
Type: "gradient", Type: "gradient",
Fill: &oFill{ Fill: &oFill{
@ -275,8 +275,8 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
ID: "_x0000_s1025", ID: "_x0000_s1025",
Type: "#_x0000_t202", Type: "#_x0000_t202",
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden", Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
Fillcolor: "#fbf6d6", Fillcolor: "#FBF6D6",
Strokecolor: "#edeaa1", Strokecolor: "#EDEAA1",
Val: string(s[13 : len(s)-14]), Val: string(s[13 : len(s)-14]),
} }
vml.Shape = append(vml.Shape, shape) vml.Shape = append(vml.Shape, shape)

View File

@ -677,11 +677,11 @@ func TestSetCellStyleBorder(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "J21", "L25", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "J21", "L25", style))
style, err = f.NewStyle(&Style{Border: []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: Fill{Type: "gradient", Color: []string{"#FFFFFF", "#E0EBF5"}, Shading: 1}}) style, err = f.NewStyle(&Style{Border: []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: Fill{Type: "gradient", Color: []string{"FFFFFF", "E0EBF5"}, Shading: 1}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
style, err = f.NewStyle(&Style{Border: []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: Fill{Type: "gradient", Color: []string{"#FFFFFF", "#E0EBF5"}, Shading: 4}}) style, err = f.NewStyle(&Style{Border: []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: Fill{Type: "gradient", Color: []string{"FFFFFF", "E0EBF5"}, Shading: 4}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
@ -721,7 +721,7 @@ func TestSetCellStyleBorder(t *testing.T) {
}, },
Fill: Fill{ Fill: Fill{
Type: "pattern", Type: "pattern",
Color: []string{"#E0EBF5"}, Color: []string{"E0EBF5"},
Pattern: 1, Pattern: 1,
}, },
}) })
@ -767,7 +767,7 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
} else { } else {
assert.NoError(t, f.SetCellValue("Sheet2", c, val)) assert.NoError(t, f.SetCellValue("Sheet2", c, val))
} }
style, err := f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"#FFFFFF", "#E0EBF5"}, Shading: 5}, NumFmt: d}) style, err := f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"FFFFFF", "E0EBF5"}, Shading: 5}, NumFmt: d})
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@ -839,7 +839,7 @@ func TestSetCellStyleCustomNumberFormat(t *testing.T) {
style, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt}) style, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
style, err = f.NewStyle(&Style{CustomNumFmt: &customNumFmt, Font: &Font{Color: "#9A0511"}}) style, err = f.NewStyle(&Style{CustomNumFmt: &customNumFmt, Font: &Font{Color: "9A0511"}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
@ -855,11 +855,11 @@ func TestSetCellStyleFill(t *testing.T) {
var style int var style int
// Test set fill for cell with invalid parameter // Test set fill for cell with invalid parameter
style, err = f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"#FFFFFF", "#E0EBF5"}, Shading: 6}}) style, err = f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"FFFFFF", "E0EBF5"}, Shading: 6}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
style, err = f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"#FFFFFF"}, Shading: 1}}) style, err = f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"FFFFFF"}, Shading: 1}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
@ -879,7 +879,7 @@ func TestSetCellStyleFont(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
var style int var style int
style, err = f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: "Times New Roman", Size: 36, Color: "#777777", Underline: "single"}}) style, err = f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: "Times New Roman", Size: 36, Color: "777777", Underline: "single"}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet2", "A1", "A1", style)) assert.NoError(t, f.SetCellStyle("Sheet2", "A1", "A1", style))
@ -899,7 +899,7 @@ func TestSetCellStyleFont(t *testing.T) {
assert.NoError(t, f.SetCellStyle("Sheet2", "A4", "A4", style)) assert.NoError(t, f.SetCellStyle("Sheet2", "A4", "A4", style))
style, err = f.NewStyle(&Style{Font: &Font{Color: "#777777", Strike: true}}) style, err = f.NewStyle(&Style{Font: &Font{Color: "777777", Strike: true}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet2", "A5", "A5", style)) assert.NoError(t, f.SetCellStyle("Sheet2", "A5", "A5", style))
@ -1002,19 +1002,19 @@ func TestConditionalFormat(t *testing.T) {
var format1, format2, format3, format4 int var format1, format2, format3, format4 int
var err error var err error
// Rose format for bad conditional // Rose format for bad conditional
format1, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "#9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1}}) format1, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
// Light yellow format for neutral conditional // Light yellow format for neutral conditional
format2, err = f.NewConditionalStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"#FEEAA0"}, Pattern: 1}}) format2, err = f.NewConditionalStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"FEEAA0"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
// Light green format for good conditional // Light green format for good conditional
format3, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "#09600B"}, Fill: Fill{Type: "pattern", Color: []string{"#C7EECF"}, Pattern: 1}}) format3, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "09600B"}, Fill: Fill{Type: "pattern", Color: []string{"C7EECF"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
// conditional style with align and left border // conditional style with align and left border
format4, err = f.NewConditionalStyle(&Style{Alignment: &Alignment{WrapText: true}, Border: []Border{{Type: "left", Color: "#000000", Style: 1}}}) format4, err = f.NewConditionalStyle(&Style{Alignment: &Alignment{WrapText: true}, Border: []Border{{Type: "left", Color: "000000", Style: 1}}})
assert.NoError(t, err) assert.NoError(t, err)
// Color scales: 2 color // Color scales: 2 color
@ -1206,7 +1206,7 @@ func TestConditionalFormat(t *testing.T) {
f, err = OpenFile(filepath.Join("test", "Book1.xlsx")) f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
assert.NoError(t, err) assert.NoError(t, err)
_, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "#9A0511"}, Fill: Fill{Type: "", Color: []string{"#FEC7CE"}, Pattern: 1}}) _, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "", Color: []string{"FEC7CE"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
} }

View File

@ -70,7 +70,7 @@ func TestAddPivotTable(t *testing.T) {
})) }))
assert.NoError(t, f.AddPivotTable(&PivotTableOptions{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$G$39:$W$52", PivotTableRange: "Sheet1!$G$42:$W$55",
Rows: []PivotTableField{{Data: "Month"}}, Rows: []PivotTableField{{Data: "Month"}},
Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}},
Data: []PivotTableField{{Data: "Sales", Subtotal: "CountNums", Name: "Summarize by CountNums"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "CountNums", Name: "Summarize by CountNums"}},

View File

@ -990,9 +990,9 @@ func TestCheckRow(t *testing.T) {
func TestSetRowStyle(t *testing.T) { func TestSetRowStyle(t *testing.T) {
f := NewFile() f := NewFile()
style1, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"#63BE7B"}, Pattern: 1}}) style1, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"63BE7B"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
style2, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"#E0EBF5"}, Pattern: 1}}) style2, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"E0EBF5"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "B2", "B2", style1)) assert.NoError(t, f.SetCellStyle("Sheet1", "B2", "B2", style1))
assert.EqualError(t, f.SetRowStyle("Sheet1", 5, -1, style2), newInvalidRowNumberError(-1).Error()) assert.EqualError(t, f.SetRowStyle("Sheet1", 5, -1, style2), newInvalidRowNumberError(-1).Error())

View File

@ -54,24 +54,24 @@ func parseShapeOptions(opts *Shape) (*Shape, error) {
// lineWidth := 1.2 // lineWidth := 1.2
// err := f.AddShape("Sheet1", "G6", // err := f.AddShape("Sheet1", "G6",
// &excelize.Shape{ // &excelize.Shape{
// Type: "rect", // Type: "rect",
// Color: excelize.ShapeColor{Line: "#4286f4", Fill: "#8eb9ff"}, // Line: excelize.ShapeLine{Color: "4286F4", Width: &lineWidth},
// Paragraph: []excelize.ShapeParagraph{ // Fill: excelize.Fill{Color: []string{"8EB9FF"}},
// Paragraph: []excelize.RichTextRun{
// { // {
// Text: "Rectangle Shape", // Text: "Rectangle Shape",
// Font: excelize.Font{ // Font: &excelize.Font{
// Bold: true, // Bold: true,
// Italic: true, // Italic: true,
// Family: "Times New Roman", // Family: "Times New Roman",
// Size: 18, // Size: 18,
// Color: "#777777", // Color: "777777",
// Underline: "sng", // Underline: "sng",
// }, // },
// }, // },
// }, // },
// Width: 180, // Width: 180,
// Height: 40, // Height: 40,
// Line: excelize.ShapeLine{Width: &lineWidth},
// }, // },
// ) // )
// //
@ -352,6 +352,10 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
to.RowOff = y2 * EMU to.RowOff = y2 * EMU
twoCellAnchor.From = &from twoCellAnchor.From = &from
twoCellAnchor.To = &to twoCellAnchor.To = &to
var solidColor string
if len(opts.Fill.Color) == 1 {
solidColor = opts.Fill.Color[0]
}
shape := xdrSp{ shape := xdrSp{
Macro: opts.Macro, Macro: opts.Macro,
NvSpPr: &xdrNvSpPr{ NvSpPr: &xdrNvSpPr{
@ -369,9 +373,9 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
}, },
}, },
Style: &xdrStyle{ Style: &xdrStyle{
LnRef: setShapeRef(opts.Color.Line, 2), LnRef: setShapeRef(opts.Line.Color, 2),
FillRef: setShapeRef(opts.Color.Fill, 1), FillRef: setShapeRef(solidColor, 1),
EffectRef: setShapeRef(opts.Color.Effect, 0), EffectRef: setShapeRef("", 0),
FontRef: &aFontRef{ FontRef: &aFontRef{
Idx: "minor", Idx: "minor",
SchemeClr: &attrValString{ SchemeClr: &attrValString{
@ -399,15 +403,15 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
return err return err
} }
if len(opts.Paragraph) < 1 { if len(opts.Paragraph) < 1 {
opts.Paragraph = []ShapeParagraph{ opts.Paragraph = []RichTextRun{
{ {
Font: Font{ Font: &Font{
Bold: false, Bold: false,
Italic: false, Italic: false,
Underline: "none", Underline: "none",
Family: defaultFont, Family: defaultFont,
Size: 11, Size: 11,
Color: "#000000", Color: "000000",
}, },
Text: " ", Text: " ",
}, },
@ -415,7 +419,11 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
} }
for _, p := range opts.Paragraph { for _, p := range opts.Paragraph {
u := "none" u := "none"
if idx := inStrSlice(supportedDrawingUnderlineTypes, p.Font.Underline, true); idx != -1 { font := &Font{}
if p.Font != nil {
font = p.Font
}
if idx := inStrSlice(supportedDrawingUnderlineTypes, font.Underline, true); idx != -1 {
u = supportedDrawingUnderlineTypes[idx] u = supportedDrawingUnderlineTypes[idx]
} }
text := p.Text text := p.Text
@ -425,13 +433,13 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
paragraph := &aP{ paragraph := &aP{
R: &aR{ R: &aR{
RPr: aRPr{ RPr: aRPr{
I: p.Font.Italic, I: font.Italic,
B: p.Font.Bold, B: font.Bold,
Lang: "en-US", Lang: "en-US",
AltLang: "en-US", AltLang: "en-US",
U: u, U: u,
Sz: p.Font.Size * 100, Sz: font.Size * 100,
Latin: &xlsxCTTextFont{Typeface: p.Font.Family}, Latin: &xlsxCTTextFont{Typeface: font.Family},
}, },
T: text, T: text,
}, },
@ -439,7 +447,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) erro
Lang: "en-US", Lang: "en-US",
}, },
} }
srgbClr := strings.ReplaceAll(strings.ToUpper(p.Font.Color), "#", "") srgbClr := strings.ReplaceAll(strings.ToUpper(font.Color), "#", "")
if len(srgbClr) == 6 { if len(srgbClr) == 6 {
paragraph.R.RPr.SolidFill = &aSolidFill{ paragraph.R.RPr.SolidFill = &aSolidFill{
SrgbClr: &attrValString{ SrgbClr: &attrValString{

View File

@ -14,26 +14,27 @@ func TestAddShape(t *testing.T) {
} }
shape := &Shape{ shape := &Shape{
Type: "rect", Type: "rect",
Paragraph: []ShapeParagraph{ Paragraph: []RichTextRun{
{Text: "Rectangle", Font: Font{Color: "CD5C5C"}}, {Text: "Rectangle", Font: &Font{Color: "CD5C5C"}},
{Text: "Shape", Font: Font{Bold: true, Color: "2980B9"}}, {Text: "Shape", Font: &Font{Bold: true, Color: "2980B9"}},
}, },
} }
assert.NoError(t, f.AddShape("Sheet1", "A30", shape)) assert.NoError(t, f.AddShape("Sheet1", "A30", shape))
assert.NoError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []ShapeParagraph{{Text: "Rectangle"}, {}}})) assert.NoError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []RichTextRun{{Text: "Rectangle"}, {}}}))
assert.NoError(t, f.AddShape("Sheet1", "C30", &Shape{Type: "rect"})) assert.NoError(t, f.AddShape("Sheet1", "C30", &Shape{Type: "rect"}))
assert.EqualError(t, f.AddShape("Sheet3", "H1", assert.EqualError(t, f.AddShape("Sheet3", "H1",
&Shape{ &Shape{
Type: "ellipseRibbon", Type: "ellipseRibbon",
Color: ShapeColor{Line: "#4286f4", Fill: "#8eb9ff"}, Line: ShapeLine{Color: "4286F4"},
Paragraph: []ShapeParagraph{ Fill: Fill{Color: []string{"8EB9FF"}},
Paragraph: []RichTextRun{
{ {
Font: Font{ Font: &Font{
Bold: true, Bold: true,
Italic: true, Italic: true,
Family: "Times New Roman", Family: "Times New Roman",
Size: 36, Size: 36,
Color: "#777777", Color: "777777",
Underline: "single", Underline: "single",
}, },
}, },
@ -49,22 +50,22 @@ func TestAddShape(t *testing.T) {
lineWidth := 1.2 lineWidth := 1.2
assert.NoError(t, f.AddShape("Sheet1", "A1", assert.NoError(t, f.AddShape("Sheet1", "A1",
&Shape{ &Shape{
Type: "ellipseRibbon", Type: "ellipseRibbon",
Color: ShapeColor{Line: "#4286f4", Fill: "#8eb9ff"}, Line: ShapeLine{Color: "4286F4", Width: &lineWidth},
Paragraph: []ShapeParagraph{ Fill: Fill{Color: []string{"8EB9FF"}},
Paragraph: []RichTextRun{
{ {
Font: Font{ Font: &Font{
Bold: true, Bold: true,
Italic: true, Italic: true,
Family: "Times New Roman", Family: "Times New Roman",
Size: 36, Size: 36,
Color: "#777777", Color: "777777",
Underline: "single", Underline: "single",
}, },
}, },
}, },
Height: 90, Height: 90,
Line: ShapeLine{Width: &lineWidth},
})) }))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx")))
// Test add shape with invalid sheet name // Test add shape with invalid sheet name
@ -72,12 +73,12 @@ func TestAddShape(t *testing.T) {
// Test add shape with unsupported charset style sheet // Test add shape with unsupported charset style sheet
f.Styles = nil f.Styles = nil
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
assert.EqualError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []ShapeParagraph{{Text: "Rectangle"}, {}}}), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []RichTextRun{{Text: "Rectangle"}, {}}}), "XML syntax error on line 1: invalid UTF-8")
// Test add shape with unsupported charset content types // Test add shape with unsupported charset content types
f = NewFile() f = NewFile()
f.ContentTypes = nil f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
assert.EqualError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []ShapeParagraph{{Text: "Rectangle"}, {}}}), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []RichTextRun{{Text: "Rectangle"}, {}}}), "XML syntax error on line 1: invalid UTF-8")
} }
func TestAddDrawingShape(t *testing.T) { func TestAddDrawingShape(t *testing.T) {

View File

@ -58,7 +58,7 @@ func TestSetSheetProps(t *testing.T) {
AutoPageBreaks: enable, AutoPageBreaks: enable,
FitToPage: enable, FitToPage: enable,
TabColorIndexed: intPtr(1), TabColorIndexed: intPtr(1),
TabColorRGB: stringPtr("#FFFF00"), TabColorRGB: stringPtr("FFFF00"),
TabColorTheme: intPtr(1), TabColorTheme: intPtr(1),
TabColorTint: float64Ptr(1), TabColorTint: float64Ptr(1),
OutlineSummaryBelow: enable, OutlineSummaryBelow: enable,
@ -79,7 +79,7 @@ func TestSetSheetProps(t *testing.T) {
ws.(*xlsxWorksheet).SheetPr = nil ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{FitToPage: enable})) assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{FitToPage: enable}))
ws.(*xlsxWorksheet).SheetPr = nil ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorRGB: stringPtr("#FFFF00")})) assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorRGB: stringPtr("FFFF00")}))
ws.(*xlsxWorksheet).SheetPr = nil ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorTheme: intPtr(1)})) assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorTheme: intPtr(1)}))
ws.(*xlsxWorksheet).SheetPr = nil ws.(*xlsxWorksheet).SheetPr = nil

View File

@ -374,20 +374,21 @@ func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup {
// //
// The following shows the formatting options of sparkline supported by excelize: // The following shows the formatting options of sparkline supported by excelize:
// //
// Parameter | Description // Parameter | Description
// -----------+-------------------------------------------- // -------------+--------------------------------------------
// Location | Required, must have the same number with 'Range' parameter // Location | Required, must have the same number with 'Range' parameter
// Range | Required, must have the same number with 'Location' parameter // Range | Required, must have the same number with 'Location' parameter
// Type | Enumeration value: line, column, win_loss // Type | Enumeration value: line, column, win_loss
// Style | Value range: 0 - 35 // Style | Value range: 0 - 35
// Hight | Toggle sparkline high points // Hight | Toggle sparkline high points
// Low | Toggle sparkline low points // Low | Toggle sparkline low points
// First | Toggle sparkline first points // First | Toggle sparkline first points
// Last | Toggle sparkline last points // Last | Toggle sparkline last points
// Negative | Toggle sparkline negative points // Negative | Toggle sparkline negative points
// Markers | Toggle sparkline markers // Markers | Toggle sparkline markers
// ColorAxis | An RGB Color is specified as RRGGBB // Axis | Used to specify if show horizontal axis
// Axis | Show sparkline axis // Reverse | Used to specify if enable plot data right-to-left
// SeriesColor | An RGB Color is specified as RRGGBB
func (f *File) AddSparkline(sheet string, opts *SparklineOptions) error { func (f *File) AddSparkline(sheet string, opts *SparklineOptions) error {
var ( var (
err error err error

View File

@ -136,7 +136,7 @@ func TestAddSparkline(t *testing.T) {
Location: []string{"A18"}, Location: []string{"A18"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
SeriesColor: "#E965E0", SeriesColor: "E965E0",
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B20", "A win/loss sparkline.")) assert.NoError(t, f.SetCellValue("Sheet1", "B20", "A win/loss sparkline."))

View File

@ -58,7 +58,7 @@ type StreamWriter struct {
// fmt.Println(err) // fmt.Println(err)
// return // return
// } // }
// styleID, err := file.NewStyle(&excelize.Style{Font: &excelize.Font{Color: "#777777"}}) // styleID, err := file.NewStyle(&excelize.Style{Font: &excelize.Font{Color: "777777"}})
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err)
// return // return

View File

@ -56,7 +56,7 @@ func TestStreamWriter(t *testing.T) {
assert.NoError(t, streamWriter.SetRow("A3", row)) assert.NoError(t, streamWriter.SetRow("A3", row))
// Test set cell with style and rich text // Test set cell with style and rich text
styleID, err := file.NewStyle(&Style{Font: &Font{Color: "#777777"}}) styleID, err := file.NewStyle(&Style{Font: &Font{Color: "777777"}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, streamWriter.SetRow("A4", []interface{}{ assert.NoError(t, streamWriter.SetRow("A4", []interface{}{
Cell{StyleID: styleID}, Cell{StyleID: styleID},
@ -67,8 +67,8 @@ func TestStreamWriter(t *testing.T) {
&Cell{StyleID: styleID, Value: "cell <>&'\""}, &Cell{StyleID: styleID, Value: "cell <>&'\""},
&Cell{Formula: "SUM(A10,B10)"}, &Cell{Formula: "SUM(A10,B10)"},
[]RichTextRun{ []RichTextRun{
{Text: "Rich ", Font: &Font{Color: "2354e8"}}, {Text: "Rich ", Font: &Font{Color: "2354E8"}},
{Text: "Text", Font: &Font{Color: "e83723"}}, {Text: "Text", Font: &Font{Color: "E83723"}},
}, },
})) }))
assert.NoError(t, streamWriter.SetRow("A6", []interface{}{time.Now()})) assert.NoError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}))
@ -318,9 +318,9 @@ func TestStreamSetRowWithStyle(t *testing.T) {
assert.NoError(t, file.Close()) assert.NoError(t, file.Close())
}() }()
zeroStyleID := 0 zeroStyleID := 0
grayStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "#777777"}}) grayStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "777777"}})
assert.NoError(t, err) assert.NoError(t, err)
blueStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "#0000FF"}}) blueStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "0000FF"}})
assert.NoError(t, err) assert.NoError(t, err)
streamWriter, err := file.NewStreamWriter("Sheet1") streamWriter, err := file.NewStreamWriter("Sheet1")

View File

@ -2702,7 +2702,7 @@ func (f *File) GetCellStyle(sheet, cell string) (int, error) {
// Sheet1: // Sheet1:
// //
// style, err := f.NewStyle(&excelize.Style{ // style, err := f.NewStyle(&excelize.Style{
// Fill: excelize.Fill{Type: "gradient", Color: []string{"#FFFFFF", "#E0EBF5"}, Shading: 1}, // Fill: excelize.Fill{Type: "gradient", Color: []string{"FFFFFF", "E0EBF5"}, Shading: 1},
// }) // })
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err)
@ -2712,7 +2712,7 @@ func (f *File) GetCellStyle(sheet, cell string) (int, error) {
// Set solid style pattern fill for cell H9 on Sheet1: // Set solid style pattern fill for cell H9 on Sheet1:
// //
// style, err := f.NewStyle(&excelize.Style{ // style, err := f.NewStyle(&excelize.Style{
// Fill: excelize.Fill{Type: "pattern", Color: []string{"#E0EBF5"}, Pattern: 1}, // Fill: excelize.Fill{Type: "pattern", Color: []string{"E0EBF5"}, Pattern: 1},
// }) // })
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err)
@ -2758,7 +2758,7 @@ func (f *File) GetCellStyle(sheet, cell string) (int, error) {
// Italic: true, // Italic: true,
// Family: "Times New Roman", // Family: "Times New Roman",
// Size: 36, // Size: 36,
// Color: "#777777", // Color: "777777",
// }, // },
// }) // })
// if err != nil { // if err != nil {
@ -2945,9 +2945,9 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// //
// format, err := f.NewConditionalStyle( // format, err := f.NewConditionalStyle(
// &excelize.Style{ // &excelize.Style{
// Font: &excelize.Font{Color: "#9A0511"}, // Font: &excelize.Font{Color: "9A0511"},
// Fill: excelize.Fill{ // Fill: excelize.Fill{
// Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1, // Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1,
// }, // },
// }, // },
// ) // )
@ -2972,7 +2972,7 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// // Rose format for bad conditional. // // Rose format for bad conditional.
// format1, err := f.NewConditionalStyle( // format1, err := f.NewConditionalStyle(
// &excelize.Style{ // &excelize.Style{
// Font: &excelize.Font{Color: "#9A0511"}, // Font: &excelize.Font{Color: "9A0511"},
// Fill: excelize.Fill{ // Fill: excelize.Fill{
// Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1, // Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1,
// }, // },
@ -2982,9 +2982,9 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// // Light yellow format for neutral conditional. // // Light yellow format for neutral conditional.
// format2, err := f.NewConditionalStyle( // format2, err := f.NewConditionalStyle(
// &excelize.Style{ // &excelize.Style{
// Font: &excelize.Font{Color: "#9B5713"}, // Font: &excelize.Font{Color: "9B5713"},
// Fill: excelize.Fill{ // Fill: excelize.Fill{
// Type: "pattern", Color: []string{"#FEEAA0"}, Pattern: 1, // Type: "pattern", Color: []string{"FEEAA0"}, Pattern: 1,
// }, // },
// }, // },
// ) // )
@ -2992,9 +2992,9 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// // Light green format for good conditional. // // Light green format for good conditional.
// format3, err := f.NewConditionalStyle( // format3, err := f.NewConditionalStyle(
// &excelize.Style{ // &excelize.Style{
// Font: &excelize.Font{Color: "#09600B"}, // Font: &excelize.Font{Color: "09600B"},
// Fill: excelize.Fill{ // Fill: excelize.Fill{
// Type: "pattern", Color: []string{"#C7EECF"}, Pattern: 1, // Type: "pattern", Color: []string{"C7EECF"}, Pattern: 1,
// }, // },
// }, // },
// ) // )

View File

@ -20,7 +20,7 @@ func TestStyleFill(t *testing.T) {
expectFill: false, expectFill: false,
}, { }, {
label: "fill", label: "fill",
format: &Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"#000000"}}}, format: &Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"000000"}}},
expectFill: true, expectFill: true,
}} }}
@ -39,9 +39,9 @@ func TestStyleFill(t *testing.T) {
} }
} }
f := NewFile() f := NewFile()
styleID1, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"#000000"}}}) styleID1, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"000000"}}})
assert.NoError(t, err) assert.NoError(t, err)
styleID2, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"#000000"}}}) styleID2, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"000000"}}})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, styleID1, styleID2) assert.Equal(t, styleID1, styleID2)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestStyleFill.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestStyleFill.xlsx")))
@ -230,7 +230,7 @@ func TestUnsetConditionalFormat(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 7)) assert.NoError(t, f.SetCellValue("Sheet1", "A1", 7))
assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10")) assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10"))
format, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "#9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1}}) format, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A10", []ConditionalFormatOptions{{Type: "cell", Criteria: ">", Format: format, Value: "6"}})) assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A10", []ConditionalFormatOptions{{Type: "cell", Criteria: ">", Format: format, Value: "6"}}))
assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10")) assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10"))
@ -246,12 +246,12 @@ func TestNewStyle(t *testing.T) {
f := NewFile() f := NewFile()
for i := 0; i < 18; i++ { for i := 0; i < 18; i++ {
_, err := f.NewStyle(&Style{ _, err := f.NewStyle(&Style{
Fill: Fill{Type: "gradient", Color: []string{"#FFFFFF", "#4E71BE"}, Shading: i}, Fill: Fill{Type: "gradient", Color: []string{"FFFFFF", "4E71BE"}, Shading: i},
}) })
assert.NoError(t, err) assert.NoError(t, err)
} }
f = NewFile() f = NewFile()
styleID, err := f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: "Times New Roman", Size: 36, Color: "#777777"}}) styleID, err := f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: "Times New Roman", Size: 36, Color: "777777"}})
assert.NoError(t, err) assert.NoError(t, err)
styles, err := f.stylesReader() styles, err := f.stylesReader()
assert.NoError(t, err) assert.NoError(t, err)
@ -360,7 +360,7 @@ func TestNewConditionalStyle(t *testing.T) {
// Test create conditional style with unsupported charset style sheet // Test create conditional style with unsupported charset style sheet
f.Styles = nil f.Styles = nil
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
_, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "#9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1}}) _, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}})
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
} }

View File

@ -598,21 +598,14 @@ type GraphicOptions struct {
// Shape directly maps the format settings of the shape. // Shape directly maps the format settings of the shape.
type Shape struct { type Shape struct {
Macro string
Type string Type string
Macro string
Width uint Width uint
Height uint Height uint
Format GraphicOptions Format GraphicOptions
Color ShapeColor Fill Fill
Line ShapeLine Line ShapeLine
Paragraph []ShapeParagraph Paragraph []RichTextRun
}
// ShapeParagraph directly maps the format settings of the paragraph in
// the shape.
type ShapeParagraph struct {
Font Font
Text string
} }
// ShapeColor directly maps the color settings of the shape. // ShapeColor directly maps the color settings of the shape.
@ -624,5 +617,6 @@ type ShapeColor struct {
// ShapeLine directly maps the line settings of the shape. // ShapeLine directly maps the line settings of the shape.
type ShapeLine struct { type ShapeLine struct {
Color string
Width *float64 Width *float64
} }