Breaking change: changed the function signature for 11 exported functions
* Change `func (f *File) NewConditionalStyle(style string) (int, error)` to `func (f *File) NewConditionalStyle(style *Style) (int, error)` * Change `func (f *File) NewStyle(style interface{}) (int, error)` to `func (f *File) NewStyle(style *Style) (int, error)` * Change `func (f *File) AddChart(sheet, cell, opts string, combo ...string) error` to `func (f *File) AddChart(sheet, cell string, chart *ChartOptions, combo ...*ChartOptions) error` * Change `func (f *File) AddChartSheet(sheet, opts string, combo ...string) error` to `func (f *File) AddChartSheet(sheet string, chart *ChartOptions, combo ...*ChartOptions) error` * Change `func (f *File) AddShape(sheet, cell, opts string) error` to `func (f *File) AddShape(sheet, cell string, opts *Shape) error` * Change `func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, file []byte) error` to `func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *PictureOptions) error` * Change `func (f *File) AddTable(sheet, hCell, vCell, opts string) error` to `func (f *File) AddTable(sheet, reference string, opts *TableOptions) error` * Change `func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error` to `func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error` * Change `func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error` to `func (f *File) AutoFilter(sheet, reference string, opts *AutoFilterOptions) error` * Change `func (f *File) SetPanes(sheet, panes string) error` to `func (f *File) SetPanes(sheet string, panes *Panes) error` * Change `func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error` to `func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error` * Change `func (f *File) SetConditionalFormat(sheet, reference, opts string) error` to `func (f *File) SetConditionalFormat(sheet, reference string, opts []ConditionalFormatOptions) error` * Add exported types: * AutoFilterListOptions * AutoFilterOptions * Chart * ChartAxis * ChartDimension * ChartLegend * ChartLine * ChartMarker * ChartPlotArea * ChartSeries * ChartTitle * ConditionalFormatOptions * PaneOptions * Panes * PictureOptions * Shape * ShapeColor * ShapeLine * ShapeParagraph * TableOptions * This added support for set sheet visible as very hidden * Return error when missing required parameters for set defined name * Update unit test and comments
This commit is contained in:
parent
a57203a03a
commit
f58dabd492
85
README.md
85
README.md
|
@ -121,41 +121,40 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
categories := map[string]string{
|
|
||||||
"A2": "Small", "A3": "Normal", "A4": "Large",
|
|
||||||
"B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
|
||||||
values := map[string]int{
|
|
||||||
"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
|
||||||
f := excelize.NewFile()
|
f := excelize.NewFile()
|
||||||
for k, v := range categories {
|
for idx, row := range [][]interface{}{
|
||||||
f.SetCellValue("Sheet1", k, v)
|
{nil, "Apple", "Orange", "Pear"}, {"Small", 2, 3, 3},
|
||||||
}
|
{"Normal", 5, 2, 4}, {"Large", 6, 7, 8},
|
||||||
for k, v := range values {
|
} {
|
||||||
f.SetCellValue("Sheet1", k, v)
|
cell, err := excelize.CoordinatesToCellName(1, idx+1)
|
||||||
}
|
if err != nil {
|
||||||
if err := f.AddChart("Sheet1", "E1", `{
|
fmt.Println(err)
|
||||||
"type": "col3DClustered",
|
return
|
||||||
"series": [
|
|
||||||
{
|
|
||||||
"name": "Sheet1!$A$2",
|
|
||||||
"categories": "Sheet1!$B$1:$D$1",
|
|
||||||
"values": "Sheet1!$B$2:$D$2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Sheet1!$A$3",
|
|
||||||
"categories": "Sheet1!$B$1:$D$1",
|
|
||||||
"values": "Sheet1!$B$3:$D$3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Sheet1!$A$4",
|
|
||||||
"categories": "Sheet1!$B$1:$D$1",
|
|
||||||
"values": "Sheet1!$B$4:$D$4"
|
|
||||||
}],
|
|
||||||
"title":
|
|
||||||
{
|
|
||||||
"name": "Fruit 3D Clustered Column Chart"
|
|
||||||
}
|
}
|
||||||
}`); err != nil {
|
f.SetSheetRow("Sheet1", cell, &row)
|
||||||
|
}
|
||||||
|
if err := f.AddChart("Sheet1", "E1", &excelize.Chart{
|
||||||
|
Type: "col3DClustered",
|
||||||
|
Series: []excelize.ChartSeries{
|
||||||
|
{
|
||||||
|
Name: "Sheet1!$A$2",
|
||||||
|
Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
Values: "Sheet1!$B$2:$D$2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Sheet1!$A$3",
|
||||||
|
Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
Values: "Sheet1!$B$3:$D$3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Sheet1!$A$4",
|
||||||
|
Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
Values: "Sheet1!$B$4:$D$4",
|
||||||
|
}},
|
||||||
|
Title: excelize.ChartTitle{
|
||||||
|
Name: "Fruit 3D Clustered Column Chart",
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -193,22 +192,24 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
// Insert a picture.
|
// Insert a picture.
|
||||||
if err := f.AddPicture("Sheet1", "A2", "image.png", ""); err != nil {
|
if err := f.AddPicture("Sheet1", "A2", "image.png", nil); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
// Insert a picture to worksheet with scaling.
|
// Insert a picture to worksheet with scaling.
|
||||||
|
enable, disable, scale := true, false, 0.5
|
||||||
if err := f.AddPicture("Sheet1", "D2", "image.jpg",
|
if err := f.AddPicture("Sheet1", "D2", "image.jpg",
|
||||||
`{"x_scale": 0.5, "y_scale": 0.5}`); err != nil {
|
&excelize.PictureOptions{XScale: &scale, YScale: &scale}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
// Insert a picture offset in the cell with printing support.
|
// Insert a picture offset in the cell with printing support.
|
||||||
if err := f.AddPicture("Sheet1", "H2", "image.gif", `{
|
if err := f.AddPicture("Sheet1", "H2", "image.gif",
|
||||||
"x_offset": 15,
|
&excelize.PictureOptions{
|
||||||
"y_offset": 10,
|
PrintObject: &enable,
|
||||||
"print_obj": true,
|
LockAspectRatio: false,
|
||||||
"lock_aspect_ratio": false,
|
OffsetX: 15,
|
||||||
"locked": false
|
OffsetY: 10,
|
||||||
}`); err != nil {
|
Locked: &disable,
|
||||||
|
}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
// Save the spreadsheet with the origin path.
|
// Save the spreadsheet with the origin path.
|
||||||
|
|
85
README_zh.md
85
README_zh.md
|
@ -121,41 +121,40 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
categories := map[string]string{
|
|
||||||
"A2": "Small", "A3": "Normal", "A4": "Large",
|
|
||||||
"B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
|
||||||
values := map[string]int{
|
|
||||||
"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
|
||||||
f := excelize.NewFile()
|
f := excelize.NewFile()
|
||||||
for k, v := range categories {
|
for idx, row := range [][]interface{}{
|
||||||
f.SetCellValue("Sheet1", k, v)
|
{nil, "Apple", "Orange", "Pear"}, {"Small", 2, 3, 3},
|
||||||
}
|
{"Normal", 5, 2, 4}, {"Large", 6, 7, 8},
|
||||||
for k, v := range values {
|
} {
|
||||||
f.SetCellValue("Sheet1", k, v)
|
cell, err := excelize.CoordinatesToCellName(1, idx+1)
|
||||||
}
|
if err != nil {
|
||||||
if err := f.AddChart("Sheet1", "E1", `{
|
fmt.Println(err)
|
||||||
"type": "col3DClustered",
|
return
|
||||||
"series": [
|
|
||||||
{
|
|
||||||
"name": "Sheet1!$A$2",
|
|
||||||
"categories": "Sheet1!$B$1:$D$1",
|
|
||||||
"values": "Sheet1!$B$2:$D$2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Sheet1!$A$3",
|
|
||||||
"categories": "Sheet1!$B$1:$D$1",
|
|
||||||
"values": "Sheet1!$B$3:$D$3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Sheet1!$A$4",
|
|
||||||
"categories": "Sheet1!$B$1:$D$1",
|
|
||||||
"values": "Sheet1!$B$4:$D$4"
|
|
||||||
}],
|
|
||||||
"title":
|
|
||||||
{
|
|
||||||
"name": "Fruit 3D Clustered Column Chart"
|
|
||||||
}
|
}
|
||||||
}`); err != nil {
|
f.SetSheetRow("Sheet1", cell, &row)
|
||||||
|
}
|
||||||
|
if err := f.AddChart("Sheet1", "E1", &excelize.Chart{
|
||||||
|
Type: "col3DClustered",
|
||||||
|
Series: []excelize.ChartSeries{
|
||||||
|
{
|
||||||
|
Name: "Sheet1!$A$2",
|
||||||
|
Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
Values: "Sheet1!$B$2:$D$2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Sheet1!$A$3",
|
||||||
|
Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
Values: "Sheet1!$B$3:$D$3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Sheet1!$A$4",
|
||||||
|
Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
Values: "Sheet1!$B$4:$D$4",
|
||||||
|
}},
|
||||||
|
Title: excelize.ChartTitle{
|
||||||
|
Name: "Fruit 3D Clustered Column Chart",
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -193,22 +192,24 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
// 插入图片
|
// 插入图片
|
||||||
if err := f.AddPicture("Sheet1", "A2", "image.png", ""); err != nil {
|
if err := f.AddPicture("Sheet1", "A2", "image.png", nil); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
// 在工作表中插入图片,并设置图片的缩放比例
|
// 在工作表中插入图片,并设置图片的缩放比例
|
||||||
|
enable, disable, scale := true, false, 0.5
|
||||||
if err := f.AddPicture("Sheet1", "D2", "image.jpg",
|
if err := f.AddPicture("Sheet1", "D2", "image.jpg",
|
||||||
`{"x_scale": 0.5, "y_scale": 0.5}`); err != nil {
|
&excelize.PictureOptions{XScale: &scale, YScale: &scale}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
// 在工作表中插入图片,并设置图片的打印属性
|
// 在工作表中插入图片,并设置图片的打印属性
|
||||||
if err := f.AddPicture("Sheet1", "H2", "image.gif", `{
|
if err := f.AddPicture("Sheet1", "H2", "image.gif",
|
||||||
"x_offset": 15,
|
&excelize.PictureOptions{
|
||||||
"y_offset": 10,
|
PrintObject: &enable,
|
||||||
"print_obj": true,
|
LockAspectRatio: false,
|
||||||
"lock_aspect_ratio": false,
|
OffsetX: 15,
|
||||||
"locked": false
|
OffsetY: 10,
|
||||||
}`); err != nil {
|
Locked: &disable,
|
||||||
|
}); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
// 保存工作簿
|
// 保存工作簿
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
func TestAdjustMergeCells(t *testing.T) {
|
func TestAdjustMergeCells(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test adjustAutoFilter with illegal cell reference.
|
// Test adjustAutoFilter with illegal cell reference
|
||||||
assert.EqualError(t, f.adjustMergeCells(&xlsxWorksheet{
|
assert.EqualError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||||
MergeCells: &xlsxMergeCells{
|
MergeCells: &xlsxMergeCells{
|
||||||
Cells: []*xlsxMergeCell{
|
Cells: []*xlsxMergeCell{
|
||||||
|
@ -57,7 +57,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
||||||
},
|
},
|
||||||
}, columns, 1, -1))
|
}, columns, 1, -1))
|
||||||
|
|
||||||
// Test adjustMergeCells.
|
// Test adjust merge cells
|
||||||
var cases []struct {
|
var cases []struct {
|
||||||
label string
|
label string
|
||||||
ws *xlsxWorksheet
|
ws *xlsxWorksheet
|
||||||
|
@ -68,7 +68,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
||||||
expectRect []int
|
expectRect []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test insert.
|
// Test adjust merged cell when insert rows and columns
|
||||||
cases = []struct {
|
cases = []struct {
|
||||||
label string
|
label string
|
||||||
ws *xlsxWorksheet
|
ws *xlsxWorksheet
|
||||||
|
@ -139,7 +139,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
||||||
assert.Equal(t, c.expectRect, c.ws.MergeCells.Cells[0].rect, c.label)
|
assert.Equal(t, c.expectRect, c.ws.MergeCells.Cells[0].rect, c.label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test delete,
|
// Test adjust merged cells when delete rows and columns
|
||||||
cases = []struct {
|
cases = []struct {
|
||||||
label string
|
label string
|
||||||
ws *xlsxWorksheet
|
ws *xlsxWorksheet
|
||||||
|
@ -292,7 +292,7 @@ func TestAdjustAutoFilter(t *testing.T) {
|
||||||
Ref: "A1:A3",
|
Ref: "A1:A3",
|
||||||
},
|
},
|
||||||
}, rows, 1, -1))
|
}, rows, 1, -1))
|
||||||
// Test adjustAutoFilter with illegal cell reference.
|
// Test adjustAutoFilter with illegal cell reference
|
||||||
assert.EqualError(t, f.adjustAutoFilter(&xlsxWorksheet{
|
assert.EqualError(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||||
AutoFilter: &xlsxAutoFilter{
|
AutoFilter: &xlsxAutoFilter{
|
||||||
Ref: "A:B1",
|
Ref: "A:B1",
|
||||||
|
@ -307,15 +307,15 @@ func TestAdjustAutoFilter(t *testing.T) {
|
||||||
|
|
||||||
func TestAdjustTable(t *testing.T) {
|
func TestAdjustTable(t *testing.T) {
|
||||||
f, sheetName := NewFile(), "Sheet1"
|
f, sheetName := NewFile(), "Sheet1"
|
||||||
for idx, tableRange := range [][]string{{"B2", "C3"}, {"E3", "F5"}, {"H5", "H8"}, {"J5", "K9"}} {
|
for idx, reference := range []string{"B2:C3", "E3:F5", "H5:H8", "J5:K9"} {
|
||||||
assert.NoError(t, f.AddTable(sheetName, tableRange[0], tableRange[1], fmt.Sprintf(`{
|
assert.NoError(t, f.AddTable(sheetName, reference, &TableOptions{
|
||||||
"table_name": "table%d",
|
Name: fmt.Sprintf("table%d", idx),
|
||||||
"table_style": "TableStyleMedium2",
|
StyleName: "TableStyleMedium2",
|
||||||
"show_first_column": true,
|
ShowFirstColumn: true,
|
||||||
"show_last_column": true,
|
ShowLastColumn: true,
|
||||||
"show_row_stripes": false,
|
ShowRowStripes: boolPtr(false),
|
||||||
"show_column_stripes": true
|
ShowColumnStripes: true,
|
||||||
}`, idx)))
|
}))
|
||||||
}
|
}
|
||||||
assert.NoError(t, f.RemoveRow(sheetName, 2))
|
assert.NoError(t, f.RemoveRow(sheetName, 2))
|
||||||
assert.NoError(t, f.RemoveRow(sheetName, 3))
|
assert.NoError(t, f.RemoveRow(sheetName, 3))
|
||||||
|
@ -323,31 +323,32 @@ func TestAdjustTable(t *testing.T) {
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAdjustTable.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAdjustTable.xlsx")))
|
||||||
|
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
assert.NoError(t, f.AddTable(sheetName, "A1", "D5", ""))
|
assert.NoError(t, f.AddTable(sheetName, "A1:D5", nil))
|
||||||
// Test adjust table with non-table part.
|
// Test adjust table with non-table part
|
||||||
f.Pkg.Delete("xl/tables/table1.xml")
|
f.Pkg.Delete("xl/tables/table1.xml")
|
||||||
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
||||||
// Test adjust table with unsupported charset.
|
// Test adjust table with unsupported charset
|
||||||
f.Pkg.Store("xl/tables/table1.xml", MacintoshCyrillicCharset)
|
f.Pkg.Store("xl/tables/table1.xml", MacintoshCyrillicCharset)
|
||||||
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
||||||
// Test adjust table with invalid table range reference.
|
// Test adjust table with invalid table range reference
|
||||||
f.Pkg.Store("xl/tables/table1.xml", []byte(`<table ref="-" />`))
|
f.Pkg.Store("xl/tables/table1.xml", []byte(`<table ref="-" />`))
|
||||||
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdjustHelper(t *testing.T) {
|
func TestAdjustHelper(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet2")
|
_, err := f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
|
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
|
||||||
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}},
|
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}},
|
||||||
})
|
})
|
||||||
f.Sheet.Store("xl/worksheets/sheet2.xml", &xlsxWorksheet{
|
f.Sheet.Store("xl/worksheets/sheet2.xml", &xlsxWorksheet{
|
||||||
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"},
|
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"},
|
||||||
})
|
})
|
||||||
// Test adjustHelper with illegal cell reference.
|
// Test adjustHelper with illegal cell reference
|
||||||
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||||
// Test adjustHelper on not exists worksheet.
|
// Test adjustHelper on not exists worksheet
|
||||||
assert.EqualError(t, f.adjustHelper("SheetN", rows, 0, 0), "sheet SheetN does not exist")
|
assert.EqualError(t, f.adjustHelper("SheetN", rows, 0, 0), "sheet SheetN does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
calc.go
4
calc.go
|
@ -48,7 +48,7 @@ const (
|
||||||
formulaErrorSPILL = "#SPILL!"
|
formulaErrorSPILL = "#SPILL!"
|
||||||
formulaErrorCALC = "#CALC!"
|
formulaErrorCALC = "#CALC!"
|
||||||
formulaErrorGETTINGDATA = "#GETTING_DATA"
|
formulaErrorGETTINGDATA = "#GETTING_DATA"
|
||||||
// formula criteria condition enumeration.
|
// Formula criteria condition enumeration
|
||||||
_ byte = iota
|
_ byte = iota
|
||||||
criteriaEq
|
criteriaEq
|
||||||
criteriaLe
|
criteriaLe
|
||||||
|
@ -100,7 +100,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// tokenPriority defined basic arithmetic operator priority.
|
// tokenPriority defined basic arithmetic operator priority
|
||||||
tokenPriority = map[string]int{
|
tokenPriority = map[string]int{
|
||||||
"^": 5,
|
"^": 5,
|
||||||
"*": 4,
|
"*": 4,
|
||||||
|
|
|
@ -5478,7 +5478,8 @@ func TestCalcSLOP(t *testing.T) {
|
||||||
|
|
||||||
func TestCalcSHEET(t *testing.T) {
|
func TestCalcSHEET(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet2")
|
_, err := f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
formulaList := map[string]string{
|
formulaList := map[string]string{
|
||||||
"=SHEET(\"Sheet2\")": "2",
|
"=SHEET(\"Sheet2\")": "2",
|
||||||
"=SHEET(Sheet2!A1)": "2",
|
"=SHEET(Sheet2!A1)": "2",
|
||||||
|
@ -5494,7 +5495,8 @@ func TestCalcSHEET(t *testing.T) {
|
||||||
|
|
||||||
func TestCalcSHEETS(t *testing.T) {
|
func TestCalcSHEETS(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet2")
|
_, err := f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
formulaList := map[string]string{
|
formulaList := map[string]string{
|
||||||
"=SHEETS(Sheet1!A1:B1)": "1",
|
"=SHEETS(Sheet1!A1:B1)": "1",
|
||||||
"=SHEETS(Sheet1!A1:Sheet1!A1)": "1",
|
"=SHEETS(Sheet1!A1:Sheet1!A1)": "1",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
func TestCalcChainReader(t *testing.T) {
|
func TestCalcChainReader(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test read calculation chain with unsupported charset.
|
// Test read calculation chain with unsupported charset
|
||||||
f.CalcChain = nil
|
f.CalcChain = nil
|
||||||
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
||||||
_, err := f.calcChainReader()
|
_, err := f.calcChainReader()
|
||||||
|
@ -34,12 +34,12 @@ func TestDeleteCalcChain(t *testing.T) {
|
||||||
formulaType, ref := STCellFormulaTypeShared, "C1:C5"
|
formulaType, ref := STCellFormulaTypeShared, "C1:C5"
|
||||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", "=A1+B1", FormulaOpts{Ref: &ref, Type: &formulaType}))
|
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", "=A1+B1", FormulaOpts{Ref: &ref, Type: &formulaType}))
|
||||||
|
|
||||||
// Test delete calculation chain with unsupported charset calculation chain.
|
// Test delete calculation chain with unsupported charset calculation chain
|
||||||
f.CalcChain = nil
|
f.CalcChain = nil
|
||||||
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathCalcChain, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.SetCellValue("Sheet1", "C1", true), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.SetCellValue("Sheet1", "C1", true), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
||||||
// Test delete calculation chain with unsupported charset content types.
|
// Test delete calculation chain with unsupported charset content types
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.deleteCalcChain(1, "A1"), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.deleteCalcChain(1, "A1"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
15
cell.go
15
cell.go
|
@ -655,9 +655,9 @@ type FormulaOpts struct {
|
||||||
//
|
//
|
||||||
// Example 5, set range array formula "A1:A2" for the cell "A3" on "Sheet1":
|
// Example 5, set range array formula "A1:A2" for the cell "A3" on "Sheet1":
|
||||||
//
|
//
|
||||||
// formulaType, ref := excelize.STCellFormulaTypeArray, "A3:A3"
|
// formulaType, ref := excelize.STCellFormulaTypeArray, "A3:A3"
|
||||||
// err := f.SetCellFormula("Sheet1", "A3", "=A1:A2",
|
// err := f.SetCellFormula("Sheet1", "A3", "=A1:A2",
|
||||||
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
|
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
|
||||||
//
|
//
|
||||||
// Example 6, set shared formula "=A1+B1" for the cell "C1:C5"
|
// Example 6, set shared formula "=A1+B1" for the cell "C1:C5"
|
||||||
// on "Sheet1", "C1" is the master cell:
|
// on "Sheet1", "C1" is the master cell:
|
||||||
|
@ -681,12 +681,13 @@ type FormulaOpts struct {
|
||||||
// f := excelize.NewFile()
|
// f := excelize.NewFile()
|
||||||
// for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
|
// for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
|
||||||
// if err := f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row); err != nil {
|
// if err := f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// if err := f.AddTable("Sheet1", "A1", "C2",
|
// if err := f.AddTable("Sheet1", "A1:C2", &excelize.TableOptions{
|
||||||
// `{"table_name":"Table1","table_style":"TableStyleMedium2"}`); err != nil {
|
// Name: "Table1", StyleName: "TableStyleMedium2",
|
||||||
|
// }); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
15
cell_test.go
15
cell_test.go
|
@ -37,13 +37,20 @@ 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(`{"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))
|
||||||
// Concurrency add picture
|
// Concurrency add picture
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"),
|
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"),
|
||||||
`{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`))
|
&PictureOptions{
|
||||||
|
OffsetX: 10,
|
||||||
|
OffsetY: 10,
|
||||||
|
Hyperlink: "https://github.com/xuri/excelize",
|
||||||
|
HyperlinkType: "External",
|
||||||
|
Positioning: "oneCell",
|
||||||
|
},
|
||||||
|
))
|
||||||
// Concurrency get cell picture
|
// Concurrency get cell picture
|
||||||
name, raw, err := f.GetPicture("Sheet1", "A1")
|
name, raw, err := f.GetPicture("Sheet1", "A1")
|
||||||
assert.Equal(t, "", name)
|
assert.Equal(t, "", name)
|
||||||
|
@ -556,7 +563,7 @@ func TestSetCellFormula(t *testing.T) {
|
||||||
for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
|
for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
|
||||||
assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row))
|
assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row))
|
||||||
}
|
}
|
||||||
assert.NoError(t, f.AddTable("Sheet1", "A1", "C2", `{"table_name":"Table1","table_style":"TableStyleMedium2"}`))
|
assert.NoError(t, f.AddTable("Sheet1", "A1:C2", &TableOptions{Name: "Table1", StyleName: "TableStyleMedium2"}))
|
||||||
formulaType = STCellFormulaTypeDataTable
|
formulaType = STCellFormulaTypeDataTable
|
||||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C2", "=SUM(Table1[[A]:[B]])", FormulaOpts{Type: &formulaType}))
|
assert.NoError(t, f.SetCellFormula("Sheet1", "C2", "=SUM(Table1[[A]:[B]])", FormulaOpts{Type: &formulaType}))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula6.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula6.xlsx")))
|
||||||
|
@ -874,7 +881,7 @@ func TestSharedStringsError(t *testing.T) {
|
||||||
assert.Equal(t, "1", f.getFromStringItem(1))
|
assert.Equal(t, "1", f.getFromStringItem(1))
|
||||||
// Cleanup undelete temporary files
|
// Cleanup undelete temporary files
|
||||||
assert.NoError(t, os.Remove(tempFile.(string)))
|
assert.NoError(t, os.Remove(tempFile.(string)))
|
||||||
// Test reload the file error on set cell value and rich text. The error message was different between macOS and Windows.
|
// Test reload the file error on set cell value and rich text. The error message was different between macOS and Windows
|
||||||
err = f.SetCellValue("Sheet1", "A19", "A19")
|
err = f.SetCellValue("Sheet1", "A19", "A19")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
|
479
chart.go
479
chart.go
|
@ -12,7 +12,6 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -480,28 +479,41 @@ var (
|
||||||
|
|
||||||
// parseChartOptions provides a function to parse the format settings of the
|
// parseChartOptions provides a function to parse the format settings of the
|
||||||
// chart with default value.
|
// chart with default value.
|
||||||
func parseChartOptions(opts string) (*chartOptions, error) {
|
func parseChartOptions(opts *Chart) (*Chart, error) {
|
||||||
options := chartOptions{
|
if opts == nil {
|
||||||
Dimension: chartDimensionOptions{
|
return nil, ErrParameterInvalid
|
||||||
Width: 480,
|
|
||||||
Height: 290,
|
|
||||||
},
|
|
||||||
Format: pictureOptions{
|
|
||||||
FPrintsWithSheet: true,
|
|
||||||
XScale: 1,
|
|
||||||
YScale: 1,
|
|
||||||
},
|
|
||||||
Legend: chartLegendOptions{
|
|
||||||
Position: "bottom",
|
|
||||||
},
|
|
||||||
Title: chartTitleOptions{
|
|
||||||
Name: " ",
|
|
||||||
},
|
|
||||||
VaryColors: true,
|
|
||||||
ShowBlanksAs: "gap",
|
|
||||||
}
|
}
|
||||||
err := json.Unmarshal([]byte(opts), &options)
|
if opts.Dimension.Width == nil {
|
||||||
return &options, err
|
opts.Dimension.Width = intPtr(defaultChartDimensionWidth)
|
||||||
|
}
|
||||||
|
if opts.Dimension.Height == nil {
|
||||||
|
opts.Dimension.Height = intPtr(defaultChartDimensionHeight)
|
||||||
|
}
|
||||||
|
if opts.Format.PrintObject == nil {
|
||||||
|
opts.Format.PrintObject = boolPtr(true)
|
||||||
|
}
|
||||||
|
if opts.Format.Locked == nil {
|
||||||
|
opts.Format.Locked = boolPtr(false)
|
||||||
|
}
|
||||||
|
if opts.Format.XScale == nil {
|
||||||
|
opts.Format.XScale = float64Ptr(defaultPictureScale)
|
||||||
|
}
|
||||||
|
if opts.Format.YScale == nil {
|
||||||
|
opts.Format.YScale = float64Ptr(defaultPictureScale)
|
||||||
|
}
|
||||||
|
if opts.Legend.Position == nil {
|
||||||
|
opts.Legend.Position = stringPtr(defaultChartLegendPosition)
|
||||||
|
}
|
||||||
|
if opts.Title.Name == "" {
|
||||||
|
opts.Title.Name = " "
|
||||||
|
}
|
||||||
|
if opts.VaryColors == nil {
|
||||||
|
opts.VaryColors = boolPtr(true)
|
||||||
|
}
|
||||||
|
if opts.ShowBlanksAs == "" {
|
||||||
|
opts.ShowBlanksAs = defaultChartShowBlanksAs
|
||||||
|
}
|
||||||
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddChart provides the method to add chart in a sheet by given chart format
|
// AddChart provides the method to add chart in a sheet by given chart format
|
||||||
|
@ -518,66 +530,53 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// func main() {
|
// func main() {
|
||||||
// categories := map[string]string{
|
|
||||||
// "A2": "Small", "A3": "Normal", "A4": "Large",
|
|
||||||
// "B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
|
||||||
// values := map[string]int{
|
|
||||||
// "B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
|
||||||
// f := excelize.NewFile()
|
// f := excelize.NewFile()
|
||||||
// for k, v := range categories {
|
// for idx, row := range [][]interface{}{
|
||||||
// f.SetCellValue("Sheet1", k, v)
|
// {nil, "Apple", "Orange", "Pear"}, {"Small", 2, 3, 3},
|
||||||
// }
|
// {"Normal", 5, 2, 4}, {"Large", 6, 7, 8},
|
||||||
// for k, v := range values {
|
// } {
|
||||||
// f.SetCellValue("Sheet1", k, v)
|
// cell, err := excelize.CoordinatesToCellName(1, idx+1)
|
||||||
// }
|
// if err != nil {
|
||||||
// if err := f.AddChart("Sheet1", "E1", `{
|
// fmt.Println(err)
|
||||||
// "type": "col3DClustered",
|
// return
|
||||||
// "series": [
|
|
||||||
// {
|
|
||||||
// "name": "Sheet1!$A$2",
|
|
||||||
// "categories": "Sheet1!$B$1:$D$1",
|
|
||||||
// "values": "Sheet1!$B$2:$D$2"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "name": "Sheet1!$A$3",
|
|
||||||
// "categories": "Sheet1!$B$1:$D$1",
|
|
||||||
// "values": "Sheet1!$B$3:$D$3"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "name": "Sheet1!$A$4",
|
|
||||||
// "categories": "Sheet1!$B$1:$D$1",
|
|
||||||
// "values": "Sheet1!$B$4:$D$4"
|
|
||||||
// }],
|
|
||||||
// "title":
|
|
||||||
// {
|
|
||||||
// "name": "Fruit 3D Clustered Column Chart"
|
|
||||||
// },
|
|
||||||
// "legend":
|
|
||||||
// {
|
|
||||||
// "none": false,
|
|
||||||
// "position": "bottom",
|
|
||||||
// "show_legend_key": false
|
|
||||||
// },
|
|
||||||
// "plotarea":
|
|
||||||
// {
|
|
||||||
// "show_bubble_size": true,
|
|
||||||
// "show_cat_name": false,
|
|
||||||
// "show_leader_lines": false,
|
|
||||||
// "show_percent": true,
|
|
||||||
// "show_series_name": true,
|
|
||||||
// "show_val": true
|
|
||||||
// },
|
|
||||||
// "show_blanks_as": "zero",
|
|
||||||
// "x_axis":
|
|
||||||
// {
|
|
||||||
// "reverse_order": true
|
|
||||||
// },
|
|
||||||
// "y_axis":
|
|
||||||
// {
|
|
||||||
// "maximum": 7.5,
|
|
||||||
// "minimum": 0.5
|
|
||||||
// }
|
// }
|
||||||
// }`); err != nil {
|
// f.SetSheetRow("Sheet1", cell, &row)
|
||||||
|
// }
|
||||||
|
// positionBottom := "bottom"
|
||||||
|
// if err := f.AddChart("Sheet1", "E1", &excelize.Chart{
|
||||||
|
// Type: "col3DClustered",
|
||||||
|
// Series: []excelize.ChartSeries{
|
||||||
|
// {
|
||||||
|
// Name: "Sheet1!$A$2",
|
||||||
|
// Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
// Values: "Sheet1!$B$2:$D$2",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Name: "Sheet1!$A$3",
|
||||||
|
// Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
// Values: "Sheet1!$B$3:$D$3",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Name: "Sheet1!$A$4",
|
||||||
|
// Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
// Values: "Sheet1!$B$4:$D$4",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Title: excelize.ChartTitle{
|
||||||
|
// Name: "Fruit 3D Clustered Column Chart",
|
||||||
|
// },
|
||||||
|
// Legend: excelize.ChartLegend{
|
||||||
|
// None: false, Position: &positionBottom, ShowLegendKey: false,
|
||||||
|
// },
|
||||||
|
// PlotArea: excelize.ChartPlotArea{
|
||||||
|
// ShowBubbleSize: true,
|
||||||
|
// ShowCatName: false,
|
||||||
|
// ShowLeaderLines: false,
|
||||||
|
// ShowPercent: true,
|
||||||
|
// ShowSerName: true,
|
||||||
|
// ShowVal: true,
|
||||||
|
// },
|
||||||
|
// }); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
@ -651,21 +650,21 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
||||||
//
|
//
|
||||||
// The series options that can be set are:
|
// The series options that can be set are:
|
||||||
//
|
//
|
||||||
// name
|
// Name
|
||||||
// categories
|
// Categories
|
||||||
// values
|
// Values
|
||||||
// line
|
// Line
|
||||||
// marker
|
// Marker
|
||||||
//
|
//
|
||||||
// name: Set the name for the series. The name is displayed in the chart legend and in the formula bar. The name property is optional and if it isn't supplied it will default to Series 1..n. The name can also be a formula such as Sheet1!$A$1
|
// Name: Set the name for the series. The name is displayed in the chart legend and in the formula bar. The 'Name' property is optional and if it isn't supplied it will default to Series 1..n. The name can also be a formula such as Sheet1!$A$1
|
||||||
//
|
//
|
||||||
// categories: This sets the chart category labels. The category is more or less the same as the X axis. In most chart types the categories property is optional and the chart will just assume a sequential series from 1..n.
|
// Categories: This sets the chart category labels. The category is more or less the same as the X axis. In most chart types the 'Categories' property is optional and the chart will just assume a sequential series from 1..n.
|
||||||
//
|
//
|
||||||
// values: This is the most important property of a series and is the only mandatory option for every chart object. This option links the chart with the worksheet data that it displays.
|
// Values: This is the most important property of a series and is the only mandatory option for every chart object. This option links the chart with the worksheet data that it displays.
|
||||||
//
|
//
|
||||||
// line: This sets the line format of the line chart. The line property is optional and if it isn't supplied it will default style. The options that can be set are width and color. The range of width is 0.25pt - 999pt. If the value of width is outside the range, the default width of the line is 2pt. The value for color should be represented in hex format (e.g., #000000 - #FFFFFF)
|
// Line: This sets the line format of the line chart. The 'Line' property is optional and if it isn't supplied it will default style. The options that can be set are width and color. The range of width is 0.25pt - 999pt. If the value of width is outside the range, the default width of the line is 2pt. The value for color should be represented in hex format (e.g., #000000 - #FFFFFF)
|
||||||
//
|
//
|
||||||
// marker: This sets the marker of the line chart and scatter chart. The range of optional field 'size' is 2-72 (default value is 5). The enumeration value of optional field 'symbol' are (default value is 'auto'):
|
// Marker: This sets the marker of the line chart and scatter chart. The range of optional field 'size' is 2-72 (default value is 5). The enumeration value of optional field 'Symbol' are (default value is 'auto'):
|
||||||
//
|
//
|
||||||
// circle
|
// circle
|
||||||
// dash
|
// dash
|
||||||
|
@ -682,13 +681,13 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
||||||
//
|
//
|
||||||
// Set properties of the chart legend. The options that can be set are:
|
// Set properties of the chart legend. The options that can be set are:
|
||||||
//
|
//
|
||||||
// none
|
// None
|
||||||
// position
|
// Position
|
||||||
// show_legend_key
|
// ShowLegendKey
|
||||||
//
|
//
|
||||||
// none: Specified if show the legend without overlapping the chart. The default value is 'false'.
|
// None: Specified if show the legend without overlapping the chart. The default value is 'false'.
|
||||||
//
|
//
|
||||||
// position: Set the position of the chart legend. The default legend position is right. This parameter only takes effect when 'none' is false. The available positions are:
|
// Position: Set the position of the chart legend. The default legend position is right. This parameter only takes effect when 'none' is false. The available positions are:
|
||||||
//
|
//
|
||||||
// top
|
// top
|
||||||
// bottom
|
// bottom
|
||||||
|
@ -696,15 +695,15 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
||||||
// right
|
// right
|
||||||
// top_right
|
// top_right
|
||||||
//
|
//
|
||||||
// show_legend_key: Set the legend keys shall be shown in data labels. The default value is false.
|
// ShowLegendKey: Set the legend keys shall be shown in data labels. The default value is false.
|
||||||
//
|
//
|
||||||
// Set properties of the chart title. The properties that can be set are:
|
// Set properties of the chart title. The properties that can be set are:
|
||||||
//
|
//
|
||||||
// title
|
// Title
|
||||||
//
|
//
|
||||||
// name: Set the name (title) for the chart. The name is displayed above the chart. The name can also be a formula such as Sheet1!$A$1 or a list with a sheet name. The name property is optional. The default is to have no chart title.
|
// Name: Set the name (title) for the chart. The name is displayed above the chart. The name can also be a formula such as Sheet1!$A$1 or a list with a sheet name. The name property is optional. The default is to have no chart title.
|
||||||
//
|
//
|
||||||
// Specifies how blank cells are plotted on the chart by show_blanks_as. The default value is gap. The options that can be set are:
|
// Specifies how blank cells are plotted on the chart by ShowBlanksAs. The default value is gap. The options that can be set are:
|
||||||
//
|
//
|
||||||
// gap
|
// gap
|
||||||
// span
|
// span
|
||||||
|
@ -716,80 +715,80 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
||||||
//
|
//
|
||||||
// zero: Specifies that blank values shall be treated as zero.
|
// zero: Specifies that blank values shall be treated as zero.
|
||||||
//
|
//
|
||||||
// Specifies that each data marker in the series has a different color by vary_colors. The default value is true.
|
// Specifies that each data marker in the series has a different color by VaryColors. The default value is true.
|
||||||
//
|
//
|
||||||
// Set chart offset, scale, aspect ratio setting and print settings by format, same as function AddPicture.
|
// Set chart offset, scale, aspect ratio setting and print settings by format, same as function AddPicture.
|
||||||
//
|
//
|
||||||
// Set the position of the chart plot area by plotarea. The properties that can be set are:
|
// Set the position of the chart plot area by PlotArea. The properties that can be set are:
|
||||||
//
|
//
|
||||||
// show_bubble_size
|
// ShowBubbleSize
|
||||||
// show_cat_name
|
// ShowCatName
|
||||||
// show_leader_lines
|
// ShowLeaderLines
|
||||||
// show_percent
|
// ShowPercent
|
||||||
// show_series_name
|
// ShowSerName
|
||||||
// show_val
|
// ShowVal
|
||||||
//
|
//
|
||||||
// show_bubble_size: Specifies the bubble size shall be shown in a data label. The show_bubble_size property is optional. The default value is false.
|
// ShowBubbleSize: Specifies the bubble size shall be shown in a data label. The ShowBubbleSize property is optional. The default value is false.
|
||||||
//
|
//
|
||||||
// show_cat_name: Specifies that the category name shall be shown in the data label. The show_cat_name property is optional. The default value is true.
|
// ShowCatName: Specifies that the category name shall be shown in the data label. The ShowCatName property is optional. The default value is true.
|
||||||
//
|
//
|
||||||
// show_leader_lines: Specifies leader lines shall be shown for data labels. The show_leader_lines property is optional. The default value is false.
|
// ShowLeaderLines: Specifies leader lines shall be shown for data labels. The ShowLeaderLines property is optional. The default value is false.
|
||||||
//
|
//
|
||||||
// show_percent: Specifies that the percentage shall be shown in a data label. The show_percent property is optional. The default value is false.
|
// ShowPercent: Specifies that the percentage shall be shown in a data label. The ShowPercent property is optional. The default value is false.
|
||||||
//
|
//
|
||||||
// show_series_name: Specifies that the series name shall be shown in a data label. The show_series_name property is optional. The default value is false.
|
// ShowSerName: Specifies that the series name shall be shown in a data label. The ShowSerName property is optional. The default value is false.
|
||||||
//
|
//
|
||||||
// show_val: Specifies that the value shall be shown in a data label. The show_val property is optional. The default value is false.
|
// ShowVal: Specifies that the value shall be shown in a data label. The ShowVal property is optional. The default value is false.
|
||||||
//
|
//
|
||||||
// Set the primary horizontal and vertical axis options by x_axis and y_axis. The properties of x_axis that can be set are:
|
// Set the primary horizontal and vertical axis options by XAxis and YAxis. The properties of XAxis that can be set are:
|
||||||
//
|
//
|
||||||
// none
|
// None
|
||||||
// major_grid_lines
|
// MajorGridLines
|
||||||
// minor_grid_lines
|
// MinorGridLines
|
||||||
// tick_label_skip
|
// TickLabelSkip
|
||||||
// reverse_order
|
// ReverseOrder
|
||||||
// maximum
|
// Maximum
|
||||||
// minimum
|
// Minimum
|
||||||
// font
|
// Font
|
||||||
//
|
//
|
||||||
// The properties of y_axis that can be set are:
|
// The properties of YAxis that can be set are:
|
||||||
//
|
//
|
||||||
// none
|
// None
|
||||||
// major_grid_lines
|
// MajorGridLines
|
||||||
// minor_grid_lines
|
// MinorGridLines
|
||||||
// major_unit
|
// MajorUnit
|
||||||
// tick_label_skip
|
// TickLabelSkip
|
||||||
// reverse_order
|
// ReverseOrder
|
||||||
// maximum
|
// Maximum
|
||||||
// minimum
|
// Minimum
|
||||||
// font
|
// Font
|
||||||
//
|
//
|
||||||
// none: Disable axes.
|
// none: Disable axes.
|
||||||
//
|
//
|
||||||
// major_grid_lines: Specifies major grid lines.
|
// MajorGridLines: Specifies major grid lines.
|
||||||
//
|
//
|
||||||
// minor_grid_lines: Specifies minor grid lines.
|
// MinorGridLines: Specifies minor grid lines.
|
||||||
//
|
//
|
||||||
// major_unit: Specifies the distance between major ticks. Shall contain a positive floating-point number. The major_unit property is optional. The default value is auto.
|
// MajorUnit: Specifies the distance between major ticks. Shall contain a positive floating-point number. The MajorUnit property is optional. The default value is auto.
|
||||||
//
|
//
|
||||||
// tick_label_skip: Specifies how many tick labels to skip between label that is drawn. The tick_label_skip property is optional. The default value is auto.
|
// TickLabelSkip: Specifies how many tick labels to skip between label that is drawn. The TickLabelSkip property is optional. The default value is auto.
|
||||||
//
|
//
|
||||||
// reverse_order: Specifies that the categories or values on reverse order (orientation of the chart). The reverse_order property is optional. The default value is false.
|
// ReverseOrder: Specifies that the categories or values on reverse order (orientation of the chart). The ReverseOrder property is optional. The default value is false.
|
||||||
//
|
//
|
||||||
// maximum: Specifies that the fixed maximum, 0 is auto. The maximum property is optional. The default value is auto.
|
// Maximum: Specifies that the fixed maximum, 0 is auto. The Maximum property is optional. The default value is auto.
|
||||||
//
|
//
|
||||||
// minimum: Specifies that the fixed minimum, 0 is auto. The minimum property is optional. The default value is auto.
|
// Minimum: Specifies that the fixed minimum, 0 is auto. The Minimum property is optional. The default value is auto.
|
||||||
//
|
//
|
||||||
// font: Specifies that the font of the horizontal and vertical axis. The properties of font that can be set are:
|
// Font: Specifies that the font of the horizontal and vertical axis. The properties of font that can be set are:
|
||||||
//
|
//
|
||||||
// bold
|
// Bold
|
||||||
// italic
|
// Italic
|
||||||
// underline
|
// Underline
|
||||||
// family
|
// Family
|
||||||
// size
|
// Size
|
||||||
// strike
|
// Strike
|
||||||
// color
|
// Color
|
||||||
// vertAlign
|
// VertAlign
|
||||||
//
|
//
|
||||||
// Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290.
|
// Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290.
|
||||||
//
|
//
|
||||||
|
@ -806,112 +805,100 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// func main() {
|
// func main() {
|
||||||
// categories := map[string]string{
|
|
||||||
// "A2": "Small", "A3": "Normal", "A4": "Large",
|
|
||||||
// "B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
|
||||||
// values := map[string]int{
|
|
||||||
// "B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
|
||||||
// f := excelize.NewFile()
|
// f := excelize.NewFile()
|
||||||
// for k, v := range categories {
|
// for idx, row := range [][]interface{}{
|
||||||
// f.SetCellValue("Sheet1", k, v)
|
// {nil, "Apple", "Orange", "Pear"}, {"Small", 2, 3, 3},
|
||||||
// }
|
// {"Normal", 5, 2, 4}, {"Large", 6, 7, 8},
|
||||||
// for k, v := range values {
|
// } {
|
||||||
// f.SetCellValue("Sheet1", k, v)
|
// cell, err := excelize.CoordinatesToCellName(1, idx+1)
|
||||||
// }
|
// if err != nil {
|
||||||
// if err := f.AddChart("Sheet1", "E1", `{
|
// fmt.Println(err)
|
||||||
// "type": "col",
|
// return
|
||||||
// "series": [
|
|
||||||
// {
|
|
||||||
// "name": "Sheet1!$A$2",
|
|
||||||
// "categories": "",
|
|
||||||
// "values": "Sheet1!$B$2:$D$2"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "name": "Sheet1!$A$3",
|
|
||||||
// "categories": "Sheet1!$B$1:$D$1",
|
|
||||||
// "values": "Sheet1!$B$3:$D$3"
|
|
||||||
// }],
|
|
||||||
// "format":
|
|
||||||
// {
|
|
||||||
// "x_scale": 1.0,
|
|
||||||
// "y_scale": 1.0,
|
|
||||||
// "x_offset": 15,
|
|
||||||
// "y_offset": 10,
|
|
||||||
// "print_obj": true,
|
|
||||||
// "lock_aspect_ratio": false,
|
|
||||||
// "locked": false
|
|
||||||
// },
|
|
||||||
// "title":
|
|
||||||
// {
|
|
||||||
// "name": "Clustered Column - Line Chart"
|
|
||||||
// },
|
|
||||||
// "legend":
|
|
||||||
// {
|
|
||||||
// "position": "left",
|
|
||||||
// "show_legend_key": false
|
|
||||||
// },
|
|
||||||
// "plotarea":
|
|
||||||
// {
|
|
||||||
// "show_bubble_size": true,
|
|
||||||
// "show_cat_name": false,
|
|
||||||
// "show_leader_lines": false,
|
|
||||||
// "show_percent": true,
|
|
||||||
// "show_series_name": true,
|
|
||||||
// "show_val": true
|
|
||||||
// }
|
// }
|
||||||
// }`, `{
|
// f.SetSheetRow("Sheet1", cell, &row)
|
||||||
// "type": "line",
|
// }
|
||||||
// "series": [
|
// enable, disable, scale := true, false, 1.0
|
||||||
// {
|
// positionLeft, positionRight := "left", "right"
|
||||||
// "name": "Sheet1!$A$4",
|
// if err := f.AddChart("Sheet1", "E1", &excelize.Chart{
|
||||||
// "categories": "Sheet1!$B$1:$D$1",
|
// Type: "col",
|
||||||
// "values": "Sheet1!$B$4:$D$4",
|
// Series: []excelize.ChartSeries{
|
||||||
// "marker":
|
|
||||||
// {
|
// {
|
||||||
// "symbol": "none",
|
// Name: "Sheet1!$A$2",
|
||||||
// "size": 10
|
// Categories: "Sheet1!$B$1:$D$1",
|
||||||
// }
|
// Values: "Sheet1!$B$2:$D$2",
|
||||||
// }],
|
// },
|
||||||
// "format":
|
|
||||||
// {
|
|
||||||
// "x_scale": 1,
|
|
||||||
// "y_scale": 1,
|
|
||||||
// "x_offset": 15,
|
|
||||||
// "y_offset": 10,
|
|
||||||
// "print_obj": true,
|
|
||||||
// "lock_aspect_ratio": false,
|
|
||||||
// "locked": false
|
|
||||||
// },
|
// },
|
||||||
// "legend":
|
// Format: excelize.Picture{
|
||||||
// {
|
// XScale: &scale,
|
||||||
// "position": "right",
|
// YScale: &scale,
|
||||||
// "show_legend_key": false
|
// OffsetX: 15,
|
||||||
|
// OffsetY: 10,
|
||||||
|
// PrintObject: &enable,
|
||||||
|
// LockAspectRatio: false,
|
||||||
|
// Locked: &disable,
|
||||||
// },
|
// },
|
||||||
// "plotarea":
|
// Title: excelize.ChartTitle{
|
||||||
// {
|
// Name: "Clustered Column - Line Chart",
|
||||||
// "show_bubble_size": true,
|
// },
|
||||||
// "show_cat_name": false,
|
// Legend: excelize.ChartLegend{
|
||||||
// "show_leader_lines": false,
|
// Position: &positionLeft, ShowLegendKey: false,
|
||||||
// "show_percent": true,
|
// },
|
||||||
// "show_series_name": true,
|
// PlotArea: excelize.ChartPlotArea{
|
||||||
// "show_val": true
|
// ShowBubbleSize: true,
|
||||||
// }
|
// ShowCatName: false,
|
||||||
// }`); err != nil {
|
// ShowLeaderLines: false,
|
||||||
|
// ShowPercent: true,
|
||||||
|
// ShowSerName: true,
|
||||||
|
// ShowVal: true,
|
||||||
|
// },
|
||||||
|
// }, &excelize.Chart{
|
||||||
|
// Type: "line",
|
||||||
|
// Series: []excelize.ChartSeries{
|
||||||
|
// {
|
||||||
|
// Name: "Sheet1!$A$4",
|
||||||
|
// Categories: "Sheet1!$B$1:$D$1",
|
||||||
|
// Values: "Sheet1!$B$4:$D$4",
|
||||||
|
// Marker: excelize.ChartMarker{
|
||||||
|
// Symbol: "none", Size: 10,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Format: excelize.Picture{
|
||||||
|
// XScale: &scale,
|
||||||
|
// YScale: &scale,
|
||||||
|
// OffsetX: 15,
|
||||||
|
// OffsetY: 10,
|
||||||
|
// PrintObject: &enable,
|
||||||
|
// LockAspectRatio: false,
|
||||||
|
// Locked: &disable,
|
||||||
|
// },
|
||||||
|
// Legend: excelize.ChartLegend{
|
||||||
|
// Position: &positionRight, ShowLegendKey: false,
|
||||||
|
// },
|
||||||
|
// PlotArea: excelize.ChartPlotArea{
|
||||||
|
// ShowBubbleSize: true,
|
||||||
|
// ShowCatName: false,
|
||||||
|
// ShowLeaderLines: false,
|
||||||
|
// ShowPercent: true,
|
||||||
|
// ShowSerName: true,
|
||||||
|
// ShowVal: true,
|
||||||
|
// },
|
||||||
|
// }); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
// // Save spreadsheet file by the given path.
|
// // Save spreadsheet by the given path.
|
||||||
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
func (f *File) AddChart(sheet, cell, opts string, combo ...string) error {
|
func (f *File) AddChart(sheet, cell string, chart *Chart, combo ...*Chart) error {
|
||||||
// Read sheet data.
|
// Read worksheet data
|
||||||
ws, err := f.workSheetReader(sheet)
|
ws, err := f.workSheetReader(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
options, comboCharts, err := f.getChartOptions(opts, combo)
|
opts, comboCharts, err := f.getChartOptions(chart, combo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -922,11 +909,11 @@ func (f *File) AddChart(sheet, cell, opts string, combo ...string) error {
|
||||||
drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
|
drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
|
||||||
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
||||||
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
|
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
|
||||||
err = f.addDrawingChart(sheet, drawingXML, cell, options.Dimension.Width, options.Dimension.Height, drawingRID, &options.Format)
|
err = f.addDrawingChart(sheet, drawingXML, cell, *opts.Dimension.Width, *opts.Dimension.Height, drawingRID, &opts.Format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addChart(options, comboCharts)
|
f.addChart(opts, comboCharts)
|
||||||
if err = f.addContentTypePart(chartID, "chart"); err != nil {
|
if err = f.addContentTypePart(chartID, "chart"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -939,7 +926,7 @@ func (f *File) AddChart(sheet, cell, opts string, combo ...string) error {
|
||||||
// format set (such as offset, scale, aspect ratio setting and print settings)
|
// format set (such as offset, scale, aspect ratio setting and print settings)
|
||||||
// and properties set. In Excel a chartsheet is a worksheet that only contains
|
// and properties set. In Excel a chartsheet is a worksheet that only contains
|
||||||
// a chart.
|
// a chart.
|
||||||
func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
|
func (f *File) AddChartSheet(sheet string, chart *Chart, combo ...*Chart) error {
|
||||||
// Check if the worksheet already exists
|
// Check if the worksheet already exists
|
||||||
idx, err := f.GetSheetIndex(sheet)
|
idx, err := f.GetSheetIndex(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -948,7 +935,7 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
|
||||||
if idx != -1 {
|
if idx != -1 {
|
||||||
return ErrExistsSheet
|
return ErrExistsSheet
|
||||||
}
|
}
|
||||||
options, comboCharts, err := f.getChartOptions(opts, combo)
|
opts, comboCharts, err := f.getChartOptions(chart, combo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -975,10 +962,10 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
|
||||||
f.prepareChartSheetDrawing(&cs, drawingID, sheet)
|
f.prepareChartSheetDrawing(&cs, drawingID, sheet)
|
||||||
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
||||||
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
|
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
|
||||||
if err = f.addSheetDrawingChart(drawingXML, drawingRID, &options.Format); err != nil {
|
if err = f.addSheetDrawingChart(drawingXML, drawingRID, &opts.Format); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addChart(options, comboCharts)
|
f.addChart(opts, comboCharts)
|
||||||
if err = f.addContentTypePart(chartID, "chart"); err != nil {
|
if err = f.addContentTypePart(chartID, "chart"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -996,8 +983,8 @@ func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
|
||||||
|
|
||||||
// getChartOptions provides a function to check format set of the chart and
|
// getChartOptions provides a function to check format set of the chart and
|
||||||
// create chart format.
|
// create chart format.
|
||||||
func (f *File) getChartOptions(opts string, combo []string) (*chartOptions, []*chartOptions, error) {
|
func (f *File) getChartOptions(opts *Chart, combo []*Chart) (*Chart, []*Chart, error) {
|
||||||
var comboCharts []*chartOptions
|
var comboCharts []*Chart
|
||||||
options, err := parseChartOptions(opts)
|
options, err := parseChartOptions(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return options, comboCharts, err
|
return options, comboCharts, err
|
||||||
|
|
316
chart_test.go
316
chart_test.go
|
@ -41,11 +41,20 @@ func TestChartSize(t *testing.T) {
|
||||||
assert.NoError(t, f.SetCellValue(sheet1, cell, v))
|
assert.NoError(t, f.SetCellValue(sheet1, cell, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "E4", `{"type":"col3DClustered","dimension":{"width":640, "height":480},`+
|
width, height := 640, 480
|
||||||
`"series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},`+
|
assert.NoError(t, f.AddChart("Sheet1", "E4", &Chart{
|
||||||
`{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},`+
|
Type: "col3DClustered",
|
||||||
`{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],`+
|
Dimension: ChartDimension{
|
||||||
`"title":{"name":"3D Clustered Column Chart"}}`))
|
Width: &width,
|
||||||
|
Height: &height,
|
||||||
|
},
|
||||||
|
Series: []ChartSeries{
|
||||||
|
{Name: "Sheet1!$A$2", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$2:$D$2"},
|
||||||
|
{Name: "Sheet1!$A$3", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$3:$D$3"},
|
||||||
|
{Name: "Sheet1!$A$4", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$4:$D$4"},
|
||||||
|
},
|
||||||
|
Title: ChartTitle{Name: "3D Clustered Column Chart"},
|
||||||
|
}))
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
@ -98,14 +107,14 @@ func TestAddDrawingChart(t *testing.T) {
|
||||||
|
|
||||||
path := "xl/drawings/drawing1.xml"
|
path := "xl/drawings/drawing1.xml"
|
||||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.addDrawingChart("Sheet1", path, "A1", 0, 0, 0, &pictureOptions{}), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.addDrawingChart("Sheet1", path, "A1", 0, 0, 0, &PictureOptions{PrintObject: boolPtr(true), Locked: boolPtr(false), XScale: float64Ptr(defaultPictureScale), YScale: float64Ptr(defaultPictureScale)}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddSheetDrawingChart(t *testing.T) {
|
func TestAddSheetDrawingChart(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
path := "xl/drawings/drawing1.xml"
|
path := "xl/drawings/drawing1.xml"
|
||||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.addSheetDrawingChart(path, 0, &pictureOptions{}), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.addSheetDrawingChart(path, 0, &PictureOptions{PrintObject: boolPtr(true), Locked: boolPtr(false), XScale: float64Ptr(defaultPictureScale), YScale: float64Ptr(defaultPictureScale)}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteDrawing(t *testing.T) {
|
func TestDeleteDrawing(t *testing.T) {
|
||||||
|
@ -129,75 +138,124 @@ func TestAddChart(t *testing.T) {
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
|
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
|
||||||
}
|
}
|
||||||
assert.EqualError(t, f.AddChart("Sheet1", "P1", ""), "unexpected end of JSON input")
|
assert.EqualError(t, f.AddChart("Sheet1", "P1", nil), ErrParameterInvalid.Error())
|
||||||
|
|
||||||
// Test add chart on not exists worksheet.
|
// Test add chart on not exists worksheet
|
||||||
assert.EqualError(t, f.AddChart("SheetN", "P1", "{}"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.AddChart("SheetN", "P1", nil), "sheet SheetN does not exist")
|
||||||
|
positionLeft, positionBottom, positionRight, positionTop, positionTopRight := "left", "bottom", "right", "top", "top_right"
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"none":true,"show_legend_key":true},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"font":{"bold":true,"italic":true,"underline":"dbl","color":"#000000"}},"y_axis":{"font":{"bold":false,"italic":false,"underline":"sng","color":"#777777"}}}`))
|
maximum, minimum, zero := 7.5, 0.5, .0
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "X1", `{"type":"colStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Stacked Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
series := []ChartSeries{
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "P16", `{"type":"colPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"100% Stacked Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "X16", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"bottom","show_legend_key":false},"title":{"name":"3D Clustered Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "P30", `{"type":"col3DStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Stacked Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "X30", `{"type":"col3DPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D 100% Stacked Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "X45", `{"type":"radar","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"top_right","show_legend_key":false},"title":{"name":"Radar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"span"}`))
|
{Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AF1", `{"type":"col3DConeStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cone Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AF16", `{"type":"col3DConeClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cone Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AF30", `{"type":"col3DConePercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cone Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AF45", `{"type":"col3DCone","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cone Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
}
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AN1", `{"type":"col3DPyramidStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Pyramid Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
series2 := []ChartSeries{
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AN16", `{"type":"col3DPyramidClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Pyramid Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Marker: ChartMarker{Symbol: "none", Size: 10}, Line: ChartLine{Color: "#000000"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AN30", `{"type":"col3DPyramidPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Pyramid Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AN45", `{"type":"col3DPyramid","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Pyramid Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AV1", `{"type":"col3DCylinderStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cylinder Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AV16", `{"type":"col3DCylinderClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cylinder Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AV30", `{"type":"col3DCylinderPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cylinder Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "AV45", `{"type":"col3DCylinder","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Cylinder Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36"},
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "P45", `{"type":"col3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Line: ChartLine{Width: 0.25}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "P1", `{"type":"line3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30","marker":{"symbol":"none","size":10}, "line":{"color":"#000000"}},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37","line":{"width":0.25}}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"top","show_legend_key":false},"title":{"name":"3D Line Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true,"minor_grid_lines":true,"tick_label_skip":1},"y_axis":{"major_grid_lines":true,"minor_grid_lines":true,"major_unit":1}}`))
|
}
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "X1", `{"type":"scatter","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"bottom","show_legend_key":false},"title":{"name":"Scatter Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
series3 := []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "P16", `{"type":"doughnut","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"right","show_legend_key":false},"title":{"name":"Doughnut Chart"},"plotarea":{"show_bubble_size":false,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":false,"show_val":false},"show_blanks_as":"zero","hole_size":30}`))
|
format := PictureOptions{
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "X16", `{"type":"line","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30","marker":{"symbol":"none","size":10}, "line":{"color":"#000000"}},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37","line":{"width":0.25}}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"top","show_legend_key":false},"title":{"name":"Line Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true,"minor_grid_lines":true,"tick_label_skip":1},"y_axis":{"major_grid_lines":true,"minor_grid_lines":true,"major_unit":1}}`))
|
XScale: float64Ptr(defaultPictureScale),
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "P32", `{"type":"pie3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"bottom","show_legend_key":false},"title":{"name":"3D Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":false,"show_val":false},"show_blanks_as":"zero"}`))
|
YScale: float64Ptr(defaultPictureScale),
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "X32", `{"type":"pie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"bottom","show_legend_key":false},"title":{"name":"Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":false,"show_val":false},"show_blanks_as":"gap"}`))
|
OffsetX: 15,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "P48", `{"type":"bar","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Clustered Bar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
OffsetY: 10,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "X48", `{"type":"barStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Stacked Bar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
PrintObject: boolPtr(true),
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "P64", `{"type":"barPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Stacked 100% Bar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
LockAspectRatio: false,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "X64", `{"type":"bar3DClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Clustered Bar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
Locked: boolPtr(false),
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "P80", `{"type":"bar3DStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Stacked Bar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"maximum":7.5,"minimum":0.5}}`))
|
}
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "X80", `{"type":"bar3DPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D 100% Stacked Bar Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"reverse_order":true,"minimum":0},"y_axis":{"reverse_order":true,"maximum":0,"minimum":0}}`))
|
legend := ChartLegend{Position: &positionLeft, ShowLegendKey: false}
|
||||||
// area series charts
|
plotArea := ChartPlotArea{
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AF1", `{"type":"area","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
ShowBubbleSize: true,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AN1", `{"type":"areaStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Stacked Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
ShowCatName: true,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AF16", `{"type":"areaPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D 100% Stacked Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
ShowLeaderLines: false,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AN16", `{"type":"area3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
ShowPercent: true,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AF32", `{"type":"area3DStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Stacked Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
ShowSerName: true,
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AN32", `{"type":"area3DPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D 100% Stacked Area Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
ShowVal: true,
|
||||||
// cylinder series chart
|
}
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AF48", `{"type":"bar3DCylinderStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cylinder Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
for _, c := range []struct {
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AF64", `{"type":"bar3DCylinderClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cylinder Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
sheetName, cell string
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AF80", `{"type":"bar3DCylinderPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cylinder Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
opts *Chart
|
||||||
// cone series chart
|
}{
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AN48", `{"type":"bar3DConeStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cone Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: "col", Series: series, Format: format, Legend: ChartLegend{None: true, 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"}}}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AN64", `{"type":"bar3DConeClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cone Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"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"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AN80", `{"type":"bar3DConePercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Cone Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"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"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AV48", `{"type":"bar3DPyramidStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Pyramid Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: "col3DClustered", Series: series, Format: format, Legend: ChartLegend{Position: &positionBottom, ShowLegendKey: false}, Title: ChartTitle{Name: "3D Clustered Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AV64", `{"type":"bar3DPyramidClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Pyramid Clustered Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "P30", opts: &Chart{Type: "col3DStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AV80", `{"type":"bar3DPyramidPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Bar Pyramid Percent Stacked Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "X30", opts: &Chart{Type: "col3DPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D 100% Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
// surface series chart
|
{sheetName: "Sheet1", cell: "X45", opts: &Chart{Type: "radar", Series: series, Format: format, Legend: ChartLegend{Position: &positionTopRight, ShowLegendKey: false}, Title: ChartTitle{Name: "Radar Chart"}, PlotArea: plotArea, ShowBlanksAs: "span"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AV1", `{"type":"surface3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Surface Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"major_grid_lines":true}}`))
|
{sheetName: "Sheet1", cell: "AF1", opts: &Chart{Type: "col3DConeStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AV16", `{"type":"wireframeSurface3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Wireframe Surface Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"major_grid_lines":true}}`))
|
{sheetName: "Sheet1", cell: "AF16", opts: &Chart{Type: "col3DConeClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "AV32", `{"type":"contour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "AF30", opts: &Chart{Type: "col3DConePercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "BD1", `{"type":"wireframeContour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Wireframe Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "AF45", opts: &Chart{Type: "col3DCone", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cone Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
// bubble chart
|
{sheetName: "Sheet1", cell: "AN1", opts: &Chart{Type: "col3DPyramidStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "BD16", `{"type":"bubble","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
{sheetName: "Sheet1", cell: "AN16", opts: &Chart{Type: "col3DPyramidClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "BD32", `{"type":"bubble3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`))
|
{sheetName: "Sheet1", cell: "AN30", opts: &Chart{Type: "col3DPyramidPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
// pie of pie chart
|
{sheetName: "Sheet1", cell: "AN45", opts: &Chart{Type: "col3DPyramid", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Pyramid Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "BD48", `{"type":"pieOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Pie of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`))
|
{sheetName: "Sheet1", cell: "AV1", opts: &Chart{Type: "col3DCylinderStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
// bar of pie chart
|
{sheetName: "Sheet1", cell: "AV16", opts: &Chart{Type: "col3DCylinderClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
assert.NoError(t, f.AddChart("Sheet2", "BD64", `{"type":"barOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`))
|
{sheetName: "Sheet1", cell: "AV30", opts: &Chart{Type: "col3DCylinderPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet1", cell: "AV45", opts: &Chart{Type: "col3DCylinder", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Cylinder Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet1", cell: "P45", opts: &Chart{Type: "col3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "P1", opts: &Chart{Type: "line3D", Series: series2, Format: format, Legend: ChartLegend{Position: &positionTop, ShowLegendKey: false}, Title: ChartTitle{Name: "3D Line Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}},
|
||||||
|
{sheetName: "Sheet2", cell: "X1", opts: &Chart{Type: "scatter", Series: series, Format: format, Legend: ChartLegend{Position: &positionBottom, ShowLegendKey: false}, Title: ChartTitle{Name: "Scatter Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "P16", opts: &Chart{Type: "doughnut", Series: series3, Format: format, Legend: ChartLegend{Position: &positionRight, ShowLegendKey: false}, Title: ChartTitle{Name: "Doughnut Chart"}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "zero", HoleSize: 30}},
|
||||||
|
{sheetName: "Sheet2", cell: "X16", opts: &Chart{Type: "line", Series: series2, Format: format, Legend: ChartLegend{Position: &positionTop, ShowLegendKey: false}, Title: ChartTitle{Name: "Line Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}},
|
||||||
|
{sheetName: "Sheet2", cell: "P32", opts: &Chart{Type: "pie3D", Series: series3, Format: format, Legend: ChartLegend{Position: &positionBottom, ShowLegendKey: false}, Title: ChartTitle{Name: "3D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "X32", opts: &Chart{Type: "pie", Series: series3, Format: format, Legend: ChartLegend{Position: &positionBottom, ShowLegendKey: false}, Title: ChartTitle{Name: "Pie Chart"}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "gap"}},
|
||||||
|
// bar series chart
|
||||||
|
{sheetName: "Sheet2", cell: "P48", opts: &Chart{Type: "bar", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Clustered Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "X48", opts: &Chart{Type: "barStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "P64", opts: &Chart{Type: "barPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked 100% Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "X64", opts: &Chart{Type: "bar3DClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Clustered Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "P80", opts: &Chart{Type: "bar3DStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Stacked Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{Maximum: &maximum, Minimum: &minimum}}},
|
||||||
|
{sheetName: "Sheet2", cell: "X80", opts: &Chart{Type: "bar3DPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D 100% Stacked Bar Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{ReverseOrder: true, Minimum: &zero}, YAxis: ChartAxis{ReverseOrder: true, Minimum: &zero}}},
|
||||||
|
// area series chart
|
||||||
|
{sheetName: "Sheet2", cell: "AF1", opts: &Chart{Type: "area", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AN1", opts: &Chart{Type: "areaStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AF16", opts: &Chart{Type: "areaPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D 100% Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AN16", opts: &Chart{Type: "area3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AF32", opts: &Chart{Type: "area3DStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AN32", opts: &Chart{Type: "area3DPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D 100% Stacked Area Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
// cylinder series chart
|
||||||
|
{sheetName: "Sheet2", cell: "AF48", opts: &Chart{Type: "bar3DCylinderStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cylinder Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AF64", opts: &Chart{Type: "bar3DCylinderClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cylinder Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AF80", opts: &Chart{Type: "bar3DCylinderPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cylinder Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
// cone series chart
|
||||||
|
{sheetName: "Sheet2", cell: "AN48", opts: &Chart{Type: "bar3DConeStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cone Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AN64", opts: &Chart{Type: "bar3DConeClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cone Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AN80", opts: &Chart{Type: "bar3DConePercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Cone Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AV48", opts: &Chart{Type: "bar3DPyramidStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Pyramid Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AV64", opts: &Chart{Type: "bar3DPyramidClustered", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Pyramid Clustered Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "AV80", opts: &Chart{Type: "bar3DPyramidPercentStacked", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Bar Pyramid Percent Stacked Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
// surface series chart
|
||||||
|
{sheetName: "Sheet2", cell: "AV1", opts: &Chart{Type: "surface3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Surface Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{MajorGridLines: true}}},
|
||||||
|
{sheetName: "Sheet2", cell: "AV16", opts: &Chart{Type: "wireframeSurface3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "3D Wireframe Surface Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", YAxis: ChartAxis{MajorGridLines: true}}},
|
||||||
|
{sheetName: "Sheet2", cell: "AV32", opts: &Chart{Type: "contour", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "BD1", opts: &Chart{Type: "wireframeContour", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Wireframe Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
// bubble chart
|
||||||
|
{sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: "bubble", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||||
|
{sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: "bubble3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
|
||||||
|
// pie of pie chart
|
||||||
|
{sheetName: "Sheet2", cell: "BD48", opts: &Chart{Type: "pieOfPie", Series: series3, Format: format, Legend: legend, Title: ChartTitle{Name: "Pie of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
|
||||||
|
// bar of pie chart
|
||||||
|
{sheetName: "Sheet2", cell: "BD64", opts: &Chart{Type: "barOfPie", Series: series3, Format: format, Legend: legend, Title: ChartTitle{Name: "Bar of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
|
||||||
|
} {
|
||||||
|
assert.NoError(t, f.AddChart(c.sheetName, c.cell, c.opts))
|
||||||
|
}
|
||||||
// combo chart
|
// combo chart
|
||||||
f.NewSheet("Combo Charts")
|
_, err = f.NewSheet("Combo Charts")
|
||||||
|
assert.NoError(t, err)
|
||||||
clusteredColumnCombo := [][]string{
|
clusteredColumnCombo := [][]string{
|
||||||
{"A1", "line", "Clustered Column - Line Chart"},
|
{"A1", "line", "Clustered Column - Line Chart"},
|
||||||
{"I1", "bubble", "Clustered Column - Bubble Chart"},
|
{"I1", "bubble", "Clustered Column - Bubble Chart"},
|
||||||
|
@ -205,7 +263,7 @@ func TestAddChart(t *testing.T) {
|
||||||
{"Y1", "doughnut", "Clustered Column - Doughnut Chart"},
|
{"Y1", "doughnut", "Clustered Column - Doughnut Chart"},
|
||||||
}
|
}
|
||||||
for _, props := range clusteredColumnCombo {
|
for _, props := range clusteredColumnCombo {
|
||||||
assert.NoError(t, f.AddChart("Combo Charts", props[0], fmt.Sprintf(`{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"%s"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[2]), fmt.Sprintf(`{"type":"%s","series":[{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[1])))
|
assert.NoError(t, f.AddChart("Combo Charts", props[0], &Chart{Type: "col", Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[2]}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[1], Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}))
|
||||||
}
|
}
|
||||||
stackedAreaCombo := map[string][]string{
|
stackedAreaCombo := map[string][]string{
|
||||||
"A16": {"line", "Stacked Area - Line Chart"},
|
"A16": {"line", "Stacked Area - Line Chart"},
|
||||||
|
@ -214,25 +272,25 @@ func TestAddChart(t *testing.T) {
|
||||||
"Y16": {"doughnut", "Stacked Area - Doughnut Chart"},
|
"Y16": {"doughnut", "Stacked Area - Doughnut Chart"},
|
||||||
}
|
}
|
||||||
for axis, props := range stackedAreaCombo {
|
for axis, props := range stackedAreaCombo {
|
||||||
assert.NoError(t, f.AddChart("Combo Charts", axis, fmt.Sprintf(`{"type":"areaStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"%s"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[1]), fmt.Sprintf(`{"type":"%s","series":[{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true}}`, props[0])))
|
assert.NoError(t, f.AddChart("Combo Charts", axis, &Chart{Type: "areaStacked", Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[1]}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[0], Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}))
|
||||||
}
|
}
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
|
||||||
// Test with invalid sheet name
|
// Test with invalid sheet name
|
||||||
assert.EqualError(t, f.AddChart("Sheet:1", "A1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}]}`), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AddChart("Sheet:1", "A1", &Chart{Type: "col", Series: series[:1]}), ErrSheetNameInvalid.Error())
|
||||||
// Test with illegal cell reference
|
// Test with illegal cell reference
|
||||||
assert.EqualError(t, f.AddChart("Sheet2", "A", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AddChart("Sheet2", "A", &Chart{Type: "col", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
// Test with unsupported chart type
|
// Test with unsupported chart type
|
||||||
assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), "unsupported chart type unknown")
|
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: "unknown", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}), "unsupported chart type unknown")
|
||||||
// Test add combo chart with invalid format set
|
// Test add combo chart with invalid format set
|
||||||
assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`, ""), "unexpected end of JSON input")
|
assert.EqualError(t, f.AddChart("Sheet2", "BD32", &Chart{Type: "col", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}, nil), ErrParameterInvalid.Error())
|
||||||
// Test add combo chart with unsupported chart type
|
// Test add combo chart with unsupported chart type
|
||||||
assert.EqualError(t, f.AddChart("Sheet2", "BD64", `{"type":"barOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`, `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`), "unsupported chart type unknown")
|
assert.EqualError(t, f.AddChart("Sheet2", "BD64", &Chart{Type: "barOfPie", Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}, Format: format, Legend: legend, Title: ChartTitle{Name: "Bar of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}, &Chart{Type: "unknown", Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}, Format: format, Legend: legend, Title: ChartTitle{Name: "Bar of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}), "unsupported chart type unknown")
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
// Test add chart with unsupported charset content types.
|
// Test add chart with unsupported charset content types.
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}],"title":{"name":"2D Column Chart"}}`), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AddChart("Sheet1", "P1", &Chart{Type: "col", Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"}}, Title: ChartTitle{Name: "2D Column Chart"}}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddChartSheet(t *testing.T) {
|
func TestAddChartSheet(t *testing.T) {
|
||||||
|
@ -245,7 +303,12 @@ func TestAddChartSheet(t *testing.T) {
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
|
assert.NoError(t, f.SetCellValue("Sheet1", k, v))
|
||||||
}
|
}
|
||||||
assert.NoError(t, f.AddChartSheet("Chart1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`))
|
series := []ChartSeries{
|
||||||
|
{Name: "Sheet1!$A$2", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$2:$D$2"},
|
||||||
|
{Name: "Sheet1!$A$3", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$3:$D$3"},
|
||||||
|
{Name: "Sheet1!$A$4", Categories: "Sheet1!$B$1:$D$1", Values: "Sheet1!$B$4:$D$4"},
|
||||||
|
}
|
||||||
|
assert.NoError(t, f.AddChartSheet("Chart1", &Chart{Type: "col3DClustered", Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}))
|
||||||
// Test set the chartsheet as active sheet
|
// Test set the chartsheet as active sheet
|
||||||
var sheetIdx int
|
var sheetIdx int
|
||||||
for idx, sheetName := range f.GetSheetList() {
|
for idx, sheetName := range f.GetSheetList() {
|
||||||
|
@ -259,11 +322,12 @@ func TestAddChartSheet(t *testing.T) {
|
||||||
// Test cell value on chartsheet
|
// Test cell value on chartsheet
|
||||||
assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is not a worksheet")
|
assert.EqualError(t, f.SetCellValue("Chart1", "A1", true), "sheet Chart1 is not a worksheet")
|
||||||
// Test add chartsheet on already existing name sheet
|
// Test add chartsheet on already existing name sheet
|
||||||
assert.EqualError(t, f.AddChartSheet("Sheet1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), ErrExistsSheet.Error())
|
|
||||||
|
assert.EqualError(t, f.AddChartSheet("Sheet1", &Chart{Type: "col3DClustered", Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}), ErrExistsSheet.Error())
|
||||||
// Test add chartsheet with invalid sheet name
|
// Test add chartsheet with invalid sheet name
|
||||||
assert.EqualError(t, f.AddChartSheet("Sheet:1", "A1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AddChartSheet("Sheet:1", nil, &Chart{Type: "col3DClustered", Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}), ErrSheetNameInvalid.Error())
|
||||||
// Test with unsupported chart type
|
// Test with unsupported chart type
|
||||||
assert.EqualError(t, f.AddChartSheet("Chart2", `{"type":"unknown","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`), "unsupported chart type unknown")
|
assert.EqualError(t, f.AddChartSheet("Chart2", &Chart{Type: "unknown", Series: series, Title: ChartTitle{Name: "Fruit 3D Clustered Column Chart"}}), "unsupported chart type unknown")
|
||||||
|
|
||||||
assert.NoError(t, f.UpdateLinkedValue())
|
assert.NoError(t, f.UpdateLinkedValue())
|
||||||
|
|
||||||
|
@ -272,14 +336,43 @@ func TestAddChartSheet(t *testing.T) {
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.AddChartSheet("Chart4", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"}],"title":{"name":"2D Column Chart"}}`), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AddChartSheet("Chart4", &Chart{Type: "col", Series: []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"}}, Title: ChartTitle{Name: "2D Column Chart"}}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteChart(t *testing.T) {
|
func TestDeleteChart(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)
|
||||||
assert.NoError(t, f.DeleteChart("Sheet1", "A1"))
|
assert.NoError(t, f.DeleteChart("Sheet1", "A1"))
|
||||||
assert.NoError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
positionLeft := "left"
|
||||||
|
series := []ChartSeries{
|
||||||
|
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30"},
|
||||||
|
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31"},
|
||||||
|
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32"},
|
||||||
|
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33"},
|
||||||
|
{Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34"},
|
||||||
|
{Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35"},
|
||||||
|
{Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36"},
|
||||||
|
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"},
|
||||||
|
}
|
||||||
|
format := PictureOptions{
|
||||||
|
XScale: float64Ptr(defaultPictureScale),
|
||||||
|
YScale: float64Ptr(defaultPictureScale),
|
||||||
|
OffsetX: 15,
|
||||||
|
OffsetY: 10,
|
||||||
|
PrintObject: boolPtr(true),
|
||||||
|
LockAspectRatio: false,
|
||||||
|
Locked: boolPtr(false),
|
||||||
|
}
|
||||||
|
legend := ChartLegend{Position: &positionLeft, ShowLegendKey: false}
|
||||||
|
plotArea := ChartPlotArea{
|
||||||
|
ShowBubbleSize: true,
|
||||||
|
ShowCatName: true,
|
||||||
|
ShowLeaderLines: false,
|
||||||
|
ShowPercent: true,
|
||||||
|
ShowSerName: true,
|
||||||
|
ShowVal: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, f.AddChart("Sheet1", "P1", &Chart{Type: "col", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}))
|
||||||
assert.NoError(t, f.DeleteChart("Sheet1", "P1"))
|
assert.NoError(t, f.DeleteChart("Sheet1", "P1"))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteChart.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteChart.xlsx")))
|
||||||
// Test delete chart with invalid sheet name
|
// Test delete chart with invalid sheet name
|
||||||
|
@ -322,37 +415,22 @@ func TestChartWithLogarithmicBase(t *testing.T) {
|
||||||
for cell, v := range categories {
|
for cell, v := range categories {
|
||||||
assert.NoError(t, f.SetCellValue(sheet1, cell, v))
|
assert.NoError(t, f.SetCellValue(sheet1, cell, v))
|
||||||
}
|
}
|
||||||
|
series := []ChartSeries{{Name: "value", Categories: "Sheet1!$A$1:$A$19", Values: "Sheet1!$B$1:$B$10"}}
|
||||||
// Add two chart, one without and one with log scaling
|
dimension := []int{640, 480, 320, 240}
|
||||||
assert.NoError(t, f.AddChart(sheet1, "C1",
|
for _, c := range []struct {
|
||||||
`{"type":"line","dimension":{"width":640, "height":480},`+
|
cell string
|
||||||
`"series":[{"name":"value","categories":"Sheet1!$A$1:$A$19","values":"Sheet1!$B$1:$B$10"}],`+
|
opts *Chart
|
||||||
`"title":{"name":"Line chart without log scaling"}}`))
|
}{
|
||||||
assert.NoError(t, f.AddChart(sheet1, "M1",
|
{cell: "C1", opts: &Chart{Type: "line", Dimension: ChartDimension{Width: &dimension[0], Height: &dimension[1]}, Series: series, Title: ChartTitle{Name: "Line chart without log scaling"}}},
|
||||||
`{"type":"line","dimension":{"width":640, "height":480},`+
|
{cell: "M1", opts: &Chart{Type: "line", Dimension: ChartDimension{Width: &dimension[0], Height: &dimension[1]}, Series: series, Title: ChartTitle{Name: "Line chart with log 10.5 scaling"}, YAxis: ChartAxis{LogBase: 10.5}}},
|
||||||
`"series":[{"name":"value","categories":"Sheet1!$A$1:$A$19","values":"Sheet1!$B$1:$B$10"}],`+
|
{cell: "A25", opts: &Chart{Type: "line", Dimension: ChartDimension{Width: &dimension[2], Height: &dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 1.9 scaling"}, YAxis: ChartAxis{LogBase: 1.9}}},
|
||||||
`"y_axis":{"logbase":10.5},`+
|
{cell: "F25", opts: &Chart{Type: "line", Dimension: ChartDimension{Width: &dimension[2], Height: &dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 2 scaling"}, YAxis: ChartAxis{LogBase: 2}}},
|
||||||
`"title":{"name":"Line chart with log 10 scaling"}}`))
|
{cell: "K25", opts: &Chart{Type: "line", Dimension: ChartDimension{Width: &dimension[2], Height: &dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 1000.1 scaling"}, YAxis: ChartAxis{LogBase: 1000.1}}},
|
||||||
assert.NoError(t, f.AddChart(sheet1, "A25",
|
{cell: "P25", opts: &Chart{Type: "line", Dimension: ChartDimension{Width: &dimension[2], Height: &dimension[3]}, Series: series, Title: ChartTitle{Name: "Line chart with log 1000 scaling"}, YAxis: ChartAxis{LogBase: 1000}}},
|
||||||
`{"type":"line","dimension":{"width":320, "height":240},`+
|
} {
|
||||||
`"series":[{"name":"value","categories":"Sheet1!$A$1:$A$19","values":"Sheet1!$B$1:$B$10"}],`+
|
// Add two chart, one without and one with log scaling
|
||||||
`"y_axis":{"logbase":1.9},`+
|
assert.NoError(t, f.AddChart(sheet1, c.cell, c.opts))
|
||||||
`"title":{"name":"Line chart with log 1.9 scaling"}}`))
|
}
|
||||||
assert.NoError(t, f.AddChart(sheet1, "F25",
|
|
||||||
`{"type":"line","dimension":{"width":320, "height":240},`+
|
|
||||||
`"series":[{"name":"value","categories":"Sheet1!$A$1:$A$19","values":"Sheet1!$B$1:$B$10"}],`+
|
|
||||||
`"y_axis":{"logbase":2},`+
|
|
||||||
`"title":{"name":"Line chart with log 2 scaling"}}`))
|
|
||||||
assert.NoError(t, f.AddChart(sheet1, "K25",
|
|
||||||
`{"type":"line","dimension":{"width":320, "height":240},`+
|
|
||||||
`"series":[{"name":"value","categories":"Sheet1!$A$1:$A$19","values":"Sheet1!$B$1:$B$10"}],`+
|
|
||||||
`"y_axis":{"logbase":1000.1},`+
|
|
||||||
`"title":{"name":"Line chart with log 1000.1 scaling"}}`))
|
|
||||||
assert.NoError(t, f.AddChart(sheet1, "P25",
|
|
||||||
`{"type":"line","dimension":{"width":320, "height":240},`+
|
|
||||||
`"series":[{"name":"value","categories":"Sheet1!$A$1:$A$19","values":"Sheet1!$B$1:$B$10"}],`+
|
|
||||||
`"y_axis":{"logbase":1000},`+
|
|
||||||
`"title":{"name":"Line chart with log 1000 scaling"}}`))
|
|
||||||
|
|
||||||
// Export XLSX file for human confirmation
|
// Export XLSX file for human confirmation
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestChartWithLogarithmicBase10.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestChartWithLogarithmicBase10.xlsx")))
|
||||||
|
|
12
col_test.go
12
col_test.go
|
@ -151,7 +151,6 @@ func TestGetColsError(t *testing.T) {
|
||||||
|
|
||||||
func TestColsRows(t *testing.T) {
|
func TestColsRows(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet1")
|
|
||||||
|
|
||||||
_, err := f.Cols("Sheet1")
|
_, err := f.Cols("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -231,7 +230,8 @@ func TestColumnVisibility(t *testing.T) {
|
||||||
// Test set column visible with invalid sheet name
|
// Test set column visible with invalid sheet name
|
||||||
assert.EqualError(t, f.SetColVisible("Sheet:1", "A", false), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.SetColVisible("Sheet:1", "A", false), ErrSheetNameInvalid.Error())
|
||||||
|
|
||||||
f.NewSheet("Sheet3")
|
_, err = f.NewSheet("Sheet3")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetColVisible("Sheet3", "E", false))
|
assert.NoError(t, f.SetColVisible("Sheet3", "E", false))
|
||||||
assert.EqualError(t, f.SetColVisible("Sheet1", "A:-1", true), newInvalidColumnNameError("-1").Error())
|
assert.EqualError(t, f.SetColVisible("Sheet1", "A:-1", true), newInvalidColumnNameError("-1").Error())
|
||||||
assert.EqualError(t, f.SetColVisible("SheetN", "E", false), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetColVisible("SheetN", "E", false), "sheet SheetN does not exist")
|
||||||
|
@ -253,7 +253,8 @@ func TestOutlineLevel(t *testing.T) {
|
||||||
assert.Equal(t, uint8(0), level)
|
assert.Equal(t, uint8(0), level)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
f.NewSheet("Sheet2")
|
_, err = f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetColOutlineLevel("Sheet1", "D", 4))
|
assert.NoError(t, f.SetColOutlineLevel("Sheet1", "D", 4))
|
||||||
|
|
||||||
level, err = f.GetColOutlineLevel("Sheet1", "D")
|
level, err = f.GetColOutlineLevel("Sheet1", "D")
|
||||||
|
@ -318,7 +319,8 @@ func TestOutlineLevel(t *testing.T) {
|
||||||
func TestSetColStyle(t *testing.T) {
|
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(`{"fill":{"type":"pattern","color":["#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")
|
||||||
|
@ -410,7 +412,7 @@ func TestInsertCols(t *testing.T) {
|
||||||
assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External"))
|
assert.NoError(t, f.SetCellHyperLink(sheet1, "A5", "https://github.com/xuri/excelize", "External"))
|
||||||
assert.NoError(t, f.MergeCell(sheet1, "A1", "C3"))
|
assert.NoError(t, f.MergeCell(sheet1, "A1", "C3"))
|
||||||
|
|
||||||
assert.NoError(t, f.AutoFilter(sheet1, "A2", "B2", `{"column":"B","expression":"x != blanks"}`))
|
assert.NoError(t, f.AutoFilter(sheet1, "A2:B2", &AutoFilterOptions{Column: "B", Expression: "x != blanks"}))
|
||||||
assert.NoError(t, f.InsertCols(sheet1, "A", 1))
|
assert.NoError(t, f.InsertCols(sheet1, "A", 1))
|
||||||
|
|
||||||
// Test insert column with illegal cell reference
|
// Test insert column with illegal cell reference
|
||||||
|
|
|
@ -51,7 +51,8 @@ func TestDataValidation(t *testing.T) {
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(resultFile))
|
assert.NoError(t, f.SaveAs(resultFile))
|
||||||
|
|
||||||
f.NewSheet("Sheet2")
|
_, err = f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetSheetRow("Sheet2", "A2", &[]interface{}{"B2", 1}))
|
assert.NoError(t, f.SetSheetRow("Sheet2", "A2", &[]interface{}{"B2", 1}))
|
||||||
assert.NoError(t, f.SetSheetRow("Sheet2", "A3", &[]interface{}{"B3", 3}))
|
assert.NoError(t, f.SetSheetRow("Sheet2", "A3", &[]interface{}{"B3", 3}))
|
||||||
dvRange = NewDataValidation(true)
|
dvRange = NewDataValidation(true)
|
||||||
|
|
|
@ -147,6 +147,13 @@ func (f *File) GetAppProps() (ret *AppProperties, err error) {
|
||||||
// Category | A categorization of the content of this package.
|
// Category | A categorization of the content of this package.
|
||||||
// |
|
// |
|
||||||
// Version | The version number. This value is set by the user or by the application.
|
// Version | The version number. This value is set by the user or by the application.
|
||||||
|
// |
|
||||||
|
// Modified | The created time of the content of the resource which
|
||||||
|
// | represent in ISO 8601 UTC format, for example "2019-06-04T22:00:10Z".
|
||||||
|
// |
|
||||||
|
// Modified | The modified time of the content of the resource which
|
||||||
|
// | represent in ISO 8601 UTC format, for example "2019-06-04T22:00:10Z".
|
||||||
|
// |
|
||||||
//
|
//
|
||||||
// For example:
|
// For example:
|
||||||
//
|
//
|
||||||
|
|
|
@ -58,7 +58,7 @@ func TestGetAppProps(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
// Test get application properties with unsupported charset.
|
// Test get application properties with unsupported charset
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.Pkg.Store(defaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
|
||||||
_, err = f.GetAppProps()
|
_, err = f.GetAppProps()
|
||||||
|
@ -110,7 +110,7 @@ func TestGetDocProps(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
// Test get workbook properties with unsupported charset.
|
// Test get workbook properties with unsupported charset
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.Pkg.Store(defaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
|
||||||
_, err = f.GetDocProps()
|
_, err = f.GetDocProps()
|
||||||
|
|
112
drawing.go
112
drawing.go
|
@ -55,7 +55,7 @@ func (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet
|
||||||
|
|
||||||
// addChart provides a function to create chart as xl/charts/chart%d.xml by
|
// addChart provides a function to create chart as xl/charts/chart%d.xml by
|
||||||
// given format sets.
|
// given format sets.
|
||||||
func (f *File) addChart(opts *chartOptions, comboCharts []*chartOptions) {
|
func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
|
||||||
count := f.countCharts()
|
count := f.countCharts()
|
||||||
xlsxChartSpace := xlsxChartSpace{
|
xlsxChartSpace := xlsxChartSpace{
|
||||||
XMLNSa: NameSpaceDrawingML.Value,
|
XMLNSa: NameSpaceDrawingML.Value,
|
||||||
|
@ -139,7 +139,7 @@ func (f *File) addChart(opts *chartOptions, comboCharts []*chartOptions) {
|
||||||
},
|
},
|
||||||
PlotArea: &cPlotArea{},
|
PlotArea: &cPlotArea{},
|
||||||
Legend: &cLegend{
|
Legend: &cLegend{
|
||||||
LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[opts.Legend.Position])},
|
LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[*opts.Legend.Position])},
|
||||||
Overlay: &attrValBool{Val: boolPtr(false)},
|
Overlay: &attrValBool{Val: boolPtr(false)},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ func (f *File) addChart(opts *chartOptions, comboCharts []*chartOptions) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
plotAreaFunc := map[string]func(*chartOptions) *cPlotArea{
|
plotAreaFunc := map[string]func(*Chart) *cPlotArea{
|
||||||
Area: f.drawBaseChart,
|
Area: f.drawBaseChart,
|
||||||
AreaStacked: f.drawBaseChart,
|
AreaStacked: f.drawBaseChart,
|
||||||
AreaPercentStacked: f.drawBaseChart,
|
AreaPercentStacked: f.drawBaseChart,
|
||||||
|
@ -264,7 +264,7 @@ func (f *File) addChart(opts *chartOptions, comboCharts []*chartOptions) {
|
||||||
|
|
||||||
// drawBaseChart provides a function to draw the c:plotArea element for bar,
|
// drawBaseChart provides a function to draw the c:plotArea element for bar,
|
||||||
// and column series charts by given format sets.
|
// and column series charts by given format sets.
|
||||||
func (f *File) drawBaseChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawBaseChart(opts *Chart) *cPlotArea {
|
||||||
c := cCharts{
|
c := cCharts{
|
||||||
BarDir: &attrValString{
|
BarDir: &attrValString{
|
||||||
Val: stringPtr("col"),
|
Val: stringPtr("col"),
|
||||||
|
@ -273,7 +273,7 @@ func (f *File) drawBaseChart(opts *chartOptions) *cPlotArea {
|
||||||
Val: stringPtr("clustered"),
|
Val: stringPtr("clustered"),
|
||||||
},
|
},
|
||||||
VaryColors: &attrValBool{
|
VaryColors: &attrValBool{
|
||||||
Val: boolPtr(opts.VaryColors),
|
Val: opts.VaryColors,
|
||||||
},
|
},
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
Shape: f.drawChartShape(opts),
|
Shape: f.drawChartShape(opts),
|
||||||
|
@ -513,7 +513,7 @@ func (f *File) drawBaseChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawDoughnutChart provides a function to draw the c:plotArea element for
|
// drawDoughnutChart provides a function to draw the c:plotArea element for
|
||||||
// doughnut chart by given format sets.
|
// doughnut chart by given format sets.
|
||||||
func (f *File) drawDoughnutChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawDoughnutChart(opts *Chart) *cPlotArea {
|
||||||
holeSize := 75
|
holeSize := 75
|
||||||
if opts.HoleSize > 0 && opts.HoleSize <= 90 {
|
if opts.HoleSize > 0 && opts.HoleSize <= 90 {
|
||||||
holeSize = opts.HoleSize
|
holeSize = opts.HoleSize
|
||||||
|
@ -522,7 +522,7 @@ func (f *File) drawDoughnutChart(opts *chartOptions) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
DoughnutChart: &cCharts{
|
DoughnutChart: &cCharts{
|
||||||
VaryColors: &attrValBool{
|
VaryColors: &attrValBool{
|
||||||
Val: boolPtr(opts.VaryColors),
|
Val: opts.VaryColors,
|
||||||
},
|
},
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
HoleSize: &attrValInt{Val: intPtr(holeSize)},
|
HoleSize: &attrValInt{Val: intPtr(holeSize)},
|
||||||
|
@ -532,7 +532,7 @@ func (f *File) drawDoughnutChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawLineChart provides a function to draw the c:plotArea element for line
|
// drawLineChart provides a function to draw the c:plotArea element for line
|
||||||
// chart by given format sets.
|
// chart by given format sets.
|
||||||
func (f *File) drawLineChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawLineChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
LineChart: &cCharts{
|
LineChart: &cCharts{
|
||||||
Grouping: &attrValString{
|
Grouping: &attrValString{
|
||||||
|
@ -555,7 +555,7 @@ func (f *File) drawLineChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawLine3DChart provides a function to draw the c:plotArea element for line
|
// drawLine3DChart provides a function to draw the c:plotArea element for line
|
||||||
// chart by given format sets.
|
// chart by given format sets.
|
||||||
func (f *File) drawLine3DChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawLine3DChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
Line3DChart: &cCharts{
|
Line3DChart: &cCharts{
|
||||||
Grouping: &attrValString{
|
Grouping: &attrValString{
|
||||||
|
@ -578,11 +578,11 @@ func (f *File) drawLine3DChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawPieChart provides a function to draw the c:plotArea element for pie
|
// drawPieChart provides a function to draw the c:plotArea element for pie
|
||||||
// chart by given format sets.
|
// chart by given format sets.
|
||||||
func (f *File) drawPieChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawPieChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
PieChart: &cCharts{
|
PieChart: &cCharts{
|
||||||
VaryColors: &attrValBool{
|
VaryColors: &attrValBool{
|
||||||
Val: boolPtr(opts.VaryColors),
|
Val: opts.VaryColors,
|
||||||
},
|
},
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
},
|
},
|
||||||
|
@ -591,11 +591,11 @@ func (f *File) drawPieChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawPie3DChart provides a function to draw the c:plotArea element for 3D
|
// drawPie3DChart provides a function to draw the c:plotArea element for 3D
|
||||||
// pie chart by given format sets.
|
// pie chart by given format sets.
|
||||||
func (f *File) drawPie3DChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawPie3DChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
Pie3DChart: &cCharts{
|
Pie3DChart: &cCharts{
|
||||||
VaryColors: &attrValBool{
|
VaryColors: &attrValBool{
|
||||||
Val: boolPtr(opts.VaryColors),
|
Val: opts.VaryColors,
|
||||||
},
|
},
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
},
|
},
|
||||||
|
@ -604,14 +604,14 @@ func (f *File) drawPie3DChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawPieOfPieChart provides a function to draw the c:plotArea element for
|
// drawPieOfPieChart provides a function to draw the c:plotArea element for
|
||||||
// pie chart by given format sets.
|
// pie chart by given format sets.
|
||||||
func (f *File) drawPieOfPieChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawPieOfPieChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
OfPieChart: &cCharts{
|
OfPieChart: &cCharts{
|
||||||
OfPieType: &attrValString{
|
OfPieType: &attrValString{
|
||||||
Val: stringPtr("pie"),
|
Val: stringPtr("pie"),
|
||||||
},
|
},
|
||||||
VaryColors: &attrValBool{
|
VaryColors: &attrValBool{
|
||||||
Val: boolPtr(opts.VaryColors),
|
Val: opts.VaryColors,
|
||||||
},
|
},
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
SerLines: &attrValString{},
|
SerLines: &attrValString{},
|
||||||
|
@ -621,14 +621,14 @@ func (f *File) drawPieOfPieChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawBarOfPieChart provides a function to draw the c:plotArea element for
|
// drawBarOfPieChart provides a function to draw the c:plotArea element for
|
||||||
// pie chart by given format sets.
|
// pie chart by given format sets.
|
||||||
func (f *File) drawBarOfPieChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawBarOfPieChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
OfPieChart: &cCharts{
|
OfPieChart: &cCharts{
|
||||||
OfPieType: &attrValString{
|
OfPieType: &attrValString{
|
||||||
Val: stringPtr("bar"),
|
Val: stringPtr("bar"),
|
||||||
},
|
},
|
||||||
VaryColors: &attrValBool{
|
VaryColors: &attrValBool{
|
||||||
Val: boolPtr(opts.VaryColors),
|
Val: opts.VaryColors,
|
||||||
},
|
},
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
SerLines: &attrValString{},
|
SerLines: &attrValString{},
|
||||||
|
@ -638,7 +638,7 @@ func (f *File) drawBarOfPieChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawRadarChart provides a function to draw the c:plotArea element for radar
|
// drawRadarChart provides a function to draw the c:plotArea element for radar
|
||||||
// chart by given format sets.
|
// chart by given format sets.
|
||||||
func (f *File) drawRadarChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawRadarChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
RadarChart: &cCharts{
|
RadarChart: &cCharts{
|
||||||
RadarStyle: &attrValString{
|
RadarStyle: &attrValString{
|
||||||
|
@ -661,7 +661,7 @@ func (f *File) drawRadarChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawScatterChart provides a function to draw the c:plotArea element for
|
// drawScatterChart provides a function to draw the c:plotArea element for
|
||||||
// scatter chart by given format sets.
|
// scatter chart by given format sets.
|
||||||
func (f *File) drawScatterChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawScatterChart(opts *Chart) *cPlotArea {
|
||||||
return &cPlotArea{
|
return &cPlotArea{
|
||||||
ScatterChart: &cCharts{
|
ScatterChart: &cCharts{
|
||||||
ScatterStyle: &attrValString{
|
ScatterStyle: &attrValString{
|
||||||
|
@ -684,7 +684,7 @@ func (f *File) drawScatterChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawSurface3DChart provides a function to draw the c:surface3DChart element by
|
// drawSurface3DChart provides a function to draw the c:surface3DChart element by
|
||||||
// given format sets.
|
// given format sets.
|
||||||
func (f *File) drawSurface3DChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawSurface3DChart(opts *Chart) *cPlotArea {
|
||||||
plotArea := &cPlotArea{
|
plotArea := &cPlotArea{
|
||||||
Surface3DChart: &cCharts{
|
Surface3DChart: &cCharts{
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
|
@ -706,7 +706,7 @@ func (f *File) drawSurface3DChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawSurfaceChart provides a function to draw the c:surfaceChart element by
|
// drawSurfaceChart provides a function to draw the c:surfaceChart element by
|
||||||
// given format sets.
|
// given format sets.
|
||||||
func (f *File) drawSurfaceChart(opts *chartOptions) *cPlotArea {
|
func (f *File) drawSurfaceChart(opts *Chart) *cPlotArea {
|
||||||
plotArea := &cPlotArea{
|
plotArea := &cPlotArea{
|
||||||
SurfaceChart: &cCharts{
|
SurfaceChart: &cCharts{
|
||||||
Ser: f.drawChartSeries(opts),
|
Ser: f.drawChartSeries(opts),
|
||||||
|
@ -728,7 +728,7 @@ func (f *File) drawSurfaceChart(opts *chartOptions) *cPlotArea {
|
||||||
|
|
||||||
// drawChartShape provides a function to draw the c:shape element by given
|
// drawChartShape provides a function to draw the c:shape element by given
|
||||||
// format sets.
|
// format sets.
|
||||||
func (f *File) drawChartShape(opts *chartOptions) *attrValString {
|
func (f *File) drawChartShape(opts *Chart) *attrValString {
|
||||||
shapes := map[string]string{
|
shapes := map[string]string{
|
||||||
Bar3DConeClustered: "cone",
|
Bar3DConeClustered: "cone",
|
||||||
Bar3DConeStacked: "cone",
|
Bar3DConeStacked: "cone",
|
||||||
|
@ -760,7 +760,7 @@ func (f *File) drawChartShape(opts *chartOptions) *attrValString {
|
||||||
|
|
||||||
// drawChartSeries provides a function to draw the c:ser element by given
|
// drawChartSeries provides a function to draw the c:ser element by given
|
||||||
// format sets.
|
// format sets.
|
||||||
func (f *File) drawChartSeries(opts *chartOptions) *[]cSer {
|
func (f *File) drawChartSeries(opts *Chart) *[]cSer {
|
||||||
var ser []cSer
|
var ser []cSer
|
||||||
for k := range opts.Series {
|
for k := range opts.Series {
|
||||||
ser = append(ser, cSer{
|
ser = append(ser, cSer{
|
||||||
|
@ -790,7 +790,7 @@ func (f *File) drawChartSeries(opts *chartOptions) *[]cSer {
|
||||||
|
|
||||||
// drawChartSeriesSpPr provides a function to draw the c:spPr element by given
|
// drawChartSeriesSpPr provides a function to draw the c:spPr element by given
|
||||||
// format sets.
|
// format sets.
|
||||||
func (f *File) drawChartSeriesSpPr(i int, opts *chartOptions) *cSpPr {
|
func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr {
|
||||||
var srgbClr *attrValString
|
var srgbClr *attrValString
|
||||||
var schemeClr *aSchemeClr
|
var schemeClr *aSchemeClr
|
||||||
|
|
||||||
|
@ -823,7 +823,7 @@ func (f *File) drawChartSeriesSpPr(i int, opts *chartOptions) *cSpPr {
|
||||||
|
|
||||||
// drawChartSeriesDPt provides a function to draw the c:dPt element by given
|
// drawChartSeriesDPt provides a function to draw the c:dPt element by given
|
||||||
// data index and format sets.
|
// data index and format sets.
|
||||||
func (f *File) drawChartSeriesDPt(i int, opts *chartOptions) []*cDPt {
|
func (f *File) drawChartSeriesDPt(i int, opts *Chart) []*cDPt {
|
||||||
dpt := []*cDPt{{
|
dpt := []*cDPt{{
|
||||||
IDx: &attrValInt{Val: intPtr(i)},
|
IDx: &attrValInt{Val: intPtr(i)},
|
||||||
Bubble3D: &attrValBool{Val: boolPtr(false)},
|
Bubble3D: &attrValBool{Val: boolPtr(false)},
|
||||||
|
@ -852,7 +852,7 @@ func (f *File) drawChartSeriesDPt(i int, opts *chartOptions) []*cDPt {
|
||||||
|
|
||||||
// drawChartSeriesCat provides a function to draw the c:cat element by given
|
// drawChartSeriesCat provides a function to draw the c:cat element by given
|
||||||
// chart series and format sets.
|
// chart series and format sets.
|
||||||
func (f *File) drawChartSeriesCat(v chartSeriesOptions, opts *chartOptions) *cCat {
|
func (f *File) drawChartSeriesCat(v ChartSeries, opts *Chart) *cCat {
|
||||||
cat := &cCat{
|
cat := &cCat{
|
||||||
StrRef: &cStrRef{
|
StrRef: &cStrRef{
|
||||||
F: v.Categories,
|
F: v.Categories,
|
||||||
|
@ -867,7 +867,7 @@ func (f *File) drawChartSeriesCat(v chartSeriesOptions, opts *chartOptions) *cCa
|
||||||
|
|
||||||
// drawChartSeriesVal provides a function to draw the c:val element by given
|
// drawChartSeriesVal provides a function to draw the c:val element by given
|
||||||
// chart series and format sets.
|
// chart series and format sets.
|
||||||
func (f *File) drawChartSeriesVal(v chartSeriesOptions, opts *chartOptions) *cVal {
|
func (f *File) drawChartSeriesVal(v ChartSeries, opts *Chart) *cVal {
|
||||||
val := &cVal{
|
val := &cVal{
|
||||||
NumRef: &cNumRef{
|
NumRef: &cNumRef{
|
||||||
F: v.Values,
|
F: v.Values,
|
||||||
|
@ -882,7 +882,7 @@ func (f *File) drawChartSeriesVal(v chartSeriesOptions, opts *chartOptions) *cVa
|
||||||
|
|
||||||
// drawChartSeriesMarker provides a function to draw the c:marker element by
|
// drawChartSeriesMarker provides a function to draw the c:marker element by
|
||||||
// given data index and format sets.
|
// given data index and format sets.
|
||||||
func (f *File) drawChartSeriesMarker(i int, opts *chartOptions) *cMarker {
|
func (f *File) drawChartSeriesMarker(i int, opts *Chart) *cMarker {
|
||||||
defaultSymbol := map[string]*attrValString{Scatter: {Val: stringPtr("circle")}}
|
defaultSymbol := map[string]*attrValString{Scatter: {Val: stringPtr("circle")}}
|
||||||
marker := &cMarker{
|
marker := &cMarker{
|
||||||
Symbol: defaultSymbol[opts.Type],
|
Symbol: defaultSymbol[opts.Type],
|
||||||
|
@ -917,7 +917,7 @@ func (f *File) drawChartSeriesMarker(i int, opts *chartOptions) *cMarker {
|
||||||
|
|
||||||
// drawChartSeriesXVal provides a function to draw the c:xVal element by given
|
// drawChartSeriesXVal provides a function to draw the c:xVal element by given
|
||||||
// chart series and format sets.
|
// chart series and format sets.
|
||||||
func (f *File) drawChartSeriesXVal(v chartSeriesOptions, opts *chartOptions) *cCat {
|
func (f *File) drawChartSeriesXVal(v ChartSeries, opts *Chart) *cCat {
|
||||||
cat := &cCat{
|
cat := &cCat{
|
||||||
StrRef: &cStrRef{
|
StrRef: &cStrRef{
|
||||||
F: v.Categories,
|
F: v.Categories,
|
||||||
|
@ -929,7 +929,7 @@ func (f *File) drawChartSeriesXVal(v chartSeriesOptions, opts *chartOptions) *cC
|
||||||
|
|
||||||
// drawChartSeriesYVal provides a function to draw the c:yVal element by given
|
// drawChartSeriesYVal provides a function to draw the c:yVal element by given
|
||||||
// chart series and format sets.
|
// chart series and format sets.
|
||||||
func (f *File) drawChartSeriesYVal(v chartSeriesOptions, opts *chartOptions) *cVal {
|
func (f *File) drawChartSeriesYVal(v ChartSeries, opts *Chart) *cVal {
|
||||||
val := &cVal{
|
val := &cVal{
|
||||||
NumRef: &cNumRef{
|
NumRef: &cNumRef{
|
||||||
F: v.Values,
|
F: v.Values,
|
||||||
|
@ -941,7 +941,7 @@ func (f *File) drawChartSeriesYVal(v chartSeriesOptions, opts *chartOptions) *cV
|
||||||
|
|
||||||
// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
|
// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
|
||||||
// element by given chart series and format sets.
|
// element by given chart series and format sets.
|
||||||
func (f *File) drawCharSeriesBubbleSize(v chartSeriesOptions, opts *chartOptions) *cVal {
|
func (f *File) drawCharSeriesBubbleSize(v ChartSeries, opts *Chart) *cVal {
|
||||||
if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok {
|
if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -954,7 +954,7 @@ func (f *File) drawCharSeriesBubbleSize(v chartSeriesOptions, opts *chartOptions
|
||||||
|
|
||||||
// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
|
// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
|
||||||
// by given format sets.
|
// by given format sets.
|
||||||
func (f *File) drawCharSeriesBubble3D(opts *chartOptions) *attrValBool {
|
func (f *File) drawCharSeriesBubble3D(opts *Chart) *attrValBool {
|
||||||
if _, ok := map[string]bool{Bubble3D: true}[opts.Type]; !ok {
|
if _, ok := map[string]bool{Bubble3D: true}[opts.Type]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -963,21 +963,21 @@ func (f *File) drawCharSeriesBubble3D(opts *chartOptions) *attrValBool {
|
||||||
|
|
||||||
// drawChartDLbls provides a function to draw the c:dLbls element by given
|
// drawChartDLbls provides a function to draw the c:dLbls element by given
|
||||||
// format sets.
|
// format sets.
|
||||||
func (f *File) drawChartDLbls(opts *chartOptions) *cDLbls {
|
func (f *File) drawChartDLbls(opts *Chart) *cDLbls {
|
||||||
return &cDLbls{
|
return &cDLbls{
|
||||||
ShowLegendKey: &attrValBool{Val: boolPtr(opts.Legend.ShowLegendKey)},
|
ShowLegendKey: &attrValBool{Val: boolPtr(opts.Legend.ShowLegendKey)},
|
||||||
ShowVal: &attrValBool{Val: boolPtr(opts.Plotarea.ShowVal)},
|
ShowVal: &attrValBool{Val: boolPtr(opts.PlotArea.ShowVal)},
|
||||||
ShowCatName: &attrValBool{Val: boolPtr(opts.Plotarea.ShowCatName)},
|
ShowCatName: &attrValBool{Val: boolPtr(opts.PlotArea.ShowCatName)},
|
||||||
ShowSerName: &attrValBool{Val: boolPtr(opts.Plotarea.ShowSerName)},
|
ShowSerName: &attrValBool{Val: boolPtr(opts.PlotArea.ShowSerName)},
|
||||||
ShowBubbleSize: &attrValBool{Val: boolPtr(opts.Plotarea.ShowBubbleSize)},
|
ShowBubbleSize: &attrValBool{Val: boolPtr(opts.PlotArea.ShowBubbleSize)},
|
||||||
ShowPercent: &attrValBool{Val: boolPtr(opts.Plotarea.ShowPercent)},
|
ShowPercent: &attrValBool{Val: boolPtr(opts.PlotArea.ShowPercent)},
|
||||||
ShowLeaderLines: &attrValBool{Val: boolPtr(opts.Plotarea.ShowLeaderLines)},
|
ShowLeaderLines: &attrValBool{Val: boolPtr(opts.PlotArea.ShowLeaderLines)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawChartSeriesDLbls provides a function to draw the c:dLbls element by
|
// drawChartSeriesDLbls provides a function to draw the c:dLbls element by
|
||||||
// given format sets.
|
// given format sets.
|
||||||
func (f *File) drawChartSeriesDLbls(opts *chartOptions) *cDLbls {
|
func (f *File) drawChartSeriesDLbls(opts *Chart) *cDLbls {
|
||||||
dLbls := f.drawChartDLbls(opts)
|
dLbls := f.drawChartDLbls(opts)
|
||||||
chartSeriesDLbls := map[string]*cDLbls{
|
chartSeriesDLbls := map[string]*cDLbls{
|
||||||
Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil,
|
Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil,
|
||||||
|
@ -989,7 +989,7 @@ func (f *File) drawChartSeriesDLbls(opts *chartOptions) *cDLbls {
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawPlotAreaCatAx provides a function to draw the c:catAx element.
|
// drawPlotAreaCatAx provides a function to draw the c:catAx element.
|
||||||
func (f *File) drawPlotAreaCatAx(opts *chartOptions) []*cAxs {
|
func (f *File) drawPlotAreaCatAx(opts *Chart) []*cAxs {
|
||||||
max := &attrValFloat{Val: opts.XAxis.Maximum}
|
max := &attrValFloat{Val: opts.XAxis.Maximum}
|
||||||
min := &attrValFloat{Val: opts.XAxis.Minimum}
|
min := &attrValFloat{Val: opts.XAxis.Minimum}
|
||||||
if opts.XAxis.Maximum == nil {
|
if opts.XAxis.Maximum == nil {
|
||||||
|
@ -1025,10 +1025,10 @@ func (f *File) drawPlotAreaCatAx(opts *chartOptions) []*cAxs {
|
||||||
NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},
|
NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if opts.XAxis.MajorGridlines {
|
if opts.XAxis.MajorGridLines {
|
||||||
axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
||||||
}
|
}
|
||||||
if opts.XAxis.MinorGridlines {
|
if opts.XAxis.MinorGridLines {
|
||||||
axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
||||||
}
|
}
|
||||||
if opts.XAxis.TickLabelSkip != 0 {
|
if opts.XAxis.TickLabelSkip != 0 {
|
||||||
|
@ -1038,7 +1038,7 @@ func (f *File) drawPlotAreaCatAx(opts *chartOptions) []*cAxs {
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawPlotAreaValAx provides a function to draw the c:valAx element.
|
// drawPlotAreaValAx provides a function to draw the c:valAx element.
|
||||||
func (f *File) drawPlotAreaValAx(opts *chartOptions) []*cAxs {
|
func (f *File) drawPlotAreaValAx(opts *Chart) []*cAxs {
|
||||||
max := &attrValFloat{Val: opts.YAxis.Maximum}
|
max := &attrValFloat{Val: opts.YAxis.Maximum}
|
||||||
min := &attrValFloat{Val: opts.YAxis.Minimum}
|
min := &attrValFloat{Val: opts.YAxis.Minimum}
|
||||||
if opts.YAxis.Maximum == nil {
|
if opts.YAxis.Maximum == nil {
|
||||||
|
@ -1076,10 +1076,10 @@ func (f *File) drawPlotAreaValAx(opts *chartOptions) []*cAxs {
|
||||||
CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[opts.Type])},
|
CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[opts.Type])},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if opts.YAxis.MajorGridlines {
|
if opts.YAxis.MajorGridLines {
|
||||||
axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
||||||
}
|
}
|
||||||
if opts.YAxis.MinorGridlines {
|
if opts.YAxis.MinorGridLines {
|
||||||
axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
||||||
}
|
}
|
||||||
if pos, ok := valTickLblPos[opts.Type]; ok {
|
if pos, ok := valTickLblPos[opts.Type]; ok {
|
||||||
|
@ -1092,7 +1092,7 @@ func (f *File) drawPlotAreaValAx(opts *chartOptions) []*cAxs {
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawPlotAreaSerAx provides a function to draw the c:serAx element.
|
// drawPlotAreaSerAx provides a function to draw the c:serAx element.
|
||||||
func (f *File) drawPlotAreaSerAx(opts *chartOptions) []*cAxs {
|
func (f *File) drawPlotAreaSerAx(opts *Chart) []*cAxs {
|
||||||
max := &attrValFloat{Val: opts.YAxis.Maximum}
|
max := &attrValFloat{Val: opts.YAxis.Maximum}
|
||||||
min := &attrValFloat{Val: opts.YAxis.Minimum}
|
min := &attrValFloat{Val: opts.YAxis.Minimum}
|
||||||
if opts.YAxis.Maximum == nil {
|
if opts.YAxis.Maximum == nil {
|
||||||
|
@ -1139,7 +1139,7 @@ func (f *File) drawPlotAreaSpPr() *cSpPr {
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawPlotAreaTxPr provides a function to draw the c:txPr element.
|
// drawPlotAreaTxPr provides a function to draw the c:txPr element.
|
||||||
func (f *File) drawPlotAreaTxPr(opts *chartAxisOptions) *cTxPr {
|
func (f *File) drawPlotAreaTxPr(opts *ChartAxis) *cTxPr {
|
||||||
cTxPr := &cTxPr{
|
cTxPr := &cTxPr{
|
||||||
BodyPr: aBodyPr{
|
BodyPr: aBodyPr{
|
||||||
Rot: -60000000,
|
Rot: -60000000,
|
||||||
|
@ -1242,7 +1242,7 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int, error) {
|
||||||
|
|
||||||
// addDrawingChart provides a function to add chart graphic frame by given
|
// addDrawingChart provides a function to add chart graphic frame by given
|
||||||
// sheet, drawingXML, cell, width, height, relationship index and format sets.
|
// sheet, drawingXML, cell, width, height, relationship index and format sets.
|
||||||
func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, opts *pictureOptions) error {
|
func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, opts *PictureOptions) error {
|
||||||
col, row, err := CellNameToCoordinates(cell)
|
col, row, err := CellNameToCoordinates(cell)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1250,8 +1250,8 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
|
||||||
colIdx := col - 1
|
colIdx := col - 1
|
||||||
rowIdx := row - 1
|
rowIdx := row - 1
|
||||||
|
|
||||||
width = int(float64(width) * opts.XScale)
|
width = int(float64(width) * *opts.XScale)
|
||||||
height = int(float64(height) * opts.YScale)
|
height = int(float64(height) * *opts.YScale)
|
||||||
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, opts.OffsetX, opts.OffsetY, width, height)
|
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, opts.OffsetX, opts.OffsetY, width, height)
|
||||||
content, cNvPrID, err := f.drawingParser(drawingXML)
|
content, cNvPrID, err := f.drawingParser(drawingXML)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1293,8 +1293,8 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
|
||||||
graphic, _ := xml.Marshal(graphicFrame)
|
graphic, _ := xml.Marshal(graphicFrame)
|
||||||
twoCellAnchor.GraphicFrame = string(graphic)
|
twoCellAnchor.GraphicFrame = string(graphic)
|
||||||
twoCellAnchor.ClientData = &xdrClientData{
|
twoCellAnchor.ClientData = &xdrClientData{
|
||||||
FLocksWithSheet: opts.FLocksWithSheet,
|
FLocksWithSheet: *opts.Locked,
|
||||||
FPrintsWithSheet: opts.FPrintsWithSheet,
|
FPrintsWithSheet: *opts.PrintObject,
|
||||||
}
|
}
|
||||||
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
||||||
f.Drawings.Store(drawingXML, content)
|
f.Drawings.Store(drawingXML, content)
|
||||||
|
@ -1304,7 +1304,7 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
|
||||||
// addSheetDrawingChart provides a function to add chart graphic frame for
|
// addSheetDrawingChart provides a function to add chart graphic frame for
|
||||||
// chartsheet by given sheet, drawingXML, width, height, relationship index
|
// chartsheet by given sheet, drawingXML, width, height, relationship index
|
||||||
// and format sets.
|
// and format sets.
|
||||||
func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *pictureOptions) error {
|
func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *PictureOptions) error {
|
||||||
content, cNvPrID, err := f.drawingParser(drawingXML)
|
content, cNvPrID, err := f.drawingParser(drawingXML)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1336,8 +1336,8 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *pictureOpt
|
||||||
graphic, _ := xml.Marshal(graphicFrame)
|
graphic, _ := xml.Marshal(graphicFrame)
|
||||||
absoluteAnchor.GraphicFrame = string(graphic)
|
absoluteAnchor.GraphicFrame = string(graphic)
|
||||||
absoluteAnchor.ClientData = &xdrClientData{
|
absoluteAnchor.ClientData = &xdrClientData{
|
||||||
FLocksWithSheet: opts.FLocksWithSheet,
|
FLocksWithSheet: *opts.Locked,
|
||||||
FPrintsWithSheet: opts.FPrintsWithSheet,
|
FPrintsWithSheet: *opts.PrintObject,
|
||||||
}
|
}
|
||||||
content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
|
content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
|
||||||
f.Drawings.Store(drawingXML, content)
|
f.Drawings.Store(drawingXML, content)
|
||||||
|
|
|
@ -26,13 +26,13 @@ func TestDrawingParser(t *testing.T) {
|
||||||
}
|
}
|
||||||
f.Pkg.Store("charset", MacintoshCyrillicCharset)
|
f.Pkg.Store("charset", MacintoshCyrillicCharset)
|
||||||
f.Pkg.Store("wsDr", []byte(xml.Header+`<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`))
|
f.Pkg.Store("wsDr", []byte(xml.Header+`<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`))
|
||||||
// Test with one cell anchor.
|
// Test with one cell anchor
|
||||||
_, _, err := f.drawingParser("wsDr")
|
_, _, err := f.drawingParser("wsDr")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Test with unsupported charset.
|
// Test with unsupported charset
|
||||||
_, _, err = f.drawingParser("charset")
|
_, _, err = f.drawingParser("charset")
|
||||||
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")
|
||||||
// Test with alternate content.
|
// Test with alternate content
|
||||||
f.Drawings = sync.Map{}
|
f.Drawings = sync.Map{}
|
||||||
f.Pkg.Store("wsDr", []byte(xml.Header+`<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" Requires="a14"><xdr:twoCellAnchor editAs="oneCell"></xdr:twoCellAnchor></mc:Choice><mc:Fallback/></mc:AlternateContent></xdr:wsDr>`))
|
f.Pkg.Store("wsDr", []byte(xml.Header+`<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" Requires="a14"><xdr:twoCellAnchor editAs="oneCell"></xdr:twoCellAnchor></mc:Choice><mc:Fallback/></mc:AlternateContent></xdr:wsDr>`))
|
||||||
_, _, err = f.drawingParser("wsDr")
|
_, _, err = f.drawingParser("wsDr")
|
||||||
|
|
600
excelize_test.go
600
excelize_test.go
|
@ -71,9 +71,10 @@ func TestOpenFile(t *testing.T) {
|
||||||
assert.NoError(t, f.SetCellStr("Sheet2", "C11", "Knowns"))
|
assert.NoError(t, f.SetCellStr("Sheet2", "C11", "Knowns"))
|
||||||
// Test max characters in a cell
|
// Test max characters in a cell
|
||||||
assert.NoError(t, f.SetCellStr("Sheet2", "D11", strings.Repeat("c", TotalCellChars+2)))
|
assert.NoError(t, f.SetCellStr("Sheet2", "D11", strings.Repeat("c", TotalCellChars+2)))
|
||||||
f.NewSheet(":\\/?*[]Maximum 31 characters allowed in sheet title.")
|
_, err = f.NewSheet(":\\/?*[]Maximum 31 characters allowed in sheet title.")
|
||||||
|
assert.EqualError(t, err, ErrSheetNameLength.Error())
|
||||||
// Test set worksheet name with illegal name
|
// Test set worksheet name with illegal name
|
||||||
f.SetSheetName("Maximum 31 characters allowed i", "[Rename]:\\/?* Maximum 31 characters allowed in sheet title.")
|
assert.EqualError(t, f.SetSheetName("Maximum 31 characters allowed i", "[Rename]:\\/?* Maximum 31 characters allowed in sheet title."), ErrSheetNameLength.Error())
|
||||||
assert.EqualError(t, f.SetCellInt("Sheet3", "A23", 10), "sheet Sheet3 does not exist")
|
assert.EqualError(t, f.SetCellInt("Sheet3", "A23", 10), "sheet Sheet3 does not exist")
|
||||||
assert.EqualError(t, f.SetCellStr("Sheet3", "b230", "10"), "sheet Sheet3 does not exist")
|
assert.EqualError(t, f.SetCellStr("Sheet3", "b230", "10"), "sheet Sheet3 does not exist")
|
||||||
assert.EqualError(t, f.SetCellStr("Sheet10", "b230", "10"), "sheet Sheet10 does not exist")
|
assert.EqualError(t, f.SetCellStr("Sheet10", "b230", "10"), "sheet Sheet10 does not exist")
|
||||||
|
@ -102,13 +103,13 @@ func TestOpenFile(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
getSharedFormula(&xlsxWorksheet{}, 0, "")
|
getSharedFormula(&xlsxWorksheet{}, 0, "")
|
||||||
|
|
||||||
// Test read cell value with given illegal rows number.
|
// Test read cell value with given illegal rows number
|
||||||
_, err = f.GetCellValue("Sheet2", "a-1")
|
_, err = f.GetCellValue("Sheet2", "a-1")
|
||||||
assert.EqualError(t, err, newCellNameToCoordinatesError("A-1", newInvalidCellNameError("A-1")).Error())
|
assert.EqualError(t, err, newCellNameToCoordinatesError("A-1", newInvalidCellNameError("A-1")).Error())
|
||||||
_, err = f.GetCellValue("Sheet2", "A")
|
_, err = f.GetCellValue("Sheet2", "A")
|
||||||
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
|
|
||||||
// Test read cell value with given lowercase column number.
|
// Test read cell value with given lowercase column number
|
||||||
_, err = f.GetCellValue("Sheet2", "a5")
|
_, err = f.GetCellValue("Sheet2", "a5")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = f.GetCellValue("Sheet2", "C11")
|
_, err = f.GetCellValue("Sheet2", "C11")
|
||||||
|
@ -145,7 +146,7 @@ func TestOpenFile(t *testing.T) {
|
||||||
assert.EqualError(t, f.SetCellHyperLink("SheetN", "A1", "Sheet1!A40", "Location"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetCellHyperLink("SheetN", "A1", "Sheet1!A40", "Location"), "sheet SheetN does not exist")
|
||||||
|
|
||||||
// Test boolean write
|
// Test boolean write
|
||||||
booltest := []struct {
|
boolTest := []struct {
|
||||||
value bool
|
value bool
|
||||||
raw bool
|
raw bool
|
||||||
expected string
|
expected string
|
||||||
|
@ -155,7 +156,7 @@ func TestOpenFile(t *testing.T) {
|
||||||
{false, false, "FALSE"},
|
{false, false, "FALSE"},
|
||||||
{true, false, "TRUE"},
|
{true, false, "TRUE"},
|
||||||
}
|
}
|
||||||
for _, test := range booltest {
|
for _, test := range boolTest {
|
||||||
assert.NoError(t, f.SetCellValue("Sheet2", "F16", test.value))
|
assert.NoError(t, f.SetCellValue("Sheet2", "F16", test.value))
|
||||||
val, err := f.GetCellValue("Sheet2", "F16", Options{RawCellValue: test.raw})
|
val, err := f.GetCellValue("Sheet2", "F16", Options{RawCellValue: test.raw})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -175,10 +176,12 @@ func TestOpenFile(t *testing.T) {
|
||||||
// Test read cell value with given cell reference large than exists row
|
// Test read cell value with given cell reference large than exists row
|
||||||
_, err = f.GetCellValue("Sheet2", "E231")
|
_, err = f.GetCellValue("Sheet2", "E231")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Test get active worksheet of spreadsheet and get worksheet name of spreadsheet by given worksheet index
|
// Test get active worksheet of spreadsheet and get worksheet name of
|
||||||
|
// spreadsheet by given worksheet index
|
||||||
f.GetSheetName(f.GetActiveSheetIndex())
|
f.GetSheetName(f.GetActiveSheetIndex())
|
||||||
// Test get worksheet index of spreadsheet by given worksheet name
|
// Test get worksheet index of spreadsheet by given worksheet name
|
||||||
f.GetSheetIndex("Sheet1")
|
_, err = f.GetSheetIndex("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
// Test get worksheet name of spreadsheet by given invalid worksheet index
|
// Test get worksheet name of spreadsheet by given invalid worksheet index
|
||||||
f.GetSheetName(4)
|
f.GetSheetName(4)
|
||||||
// Test get worksheet map of workbook
|
// Test get worksheet map of workbook
|
||||||
|
@ -339,31 +342,23 @@ func TestBrokenFile(t *testing.T) {
|
||||||
func TestNewFile(t *testing.T) {
|
func TestNewFile(t *testing.T) {
|
||||||
// Test create a spreadsheet file
|
// Test create a spreadsheet file
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet1")
|
_, err := f.NewSheet("Sheet1")
|
||||||
f.NewSheet("XLSXSheet2")
|
assert.NoError(t, err)
|
||||||
f.NewSheet("XLSXSheet3")
|
_, err = f.NewSheet("XLSXSheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = f.NewSheet("XLSXSheet3")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetCellInt("XLSXSheet2", "A23", 56))
|
assert.NoError(t, f.SetCellInt("XLSXSheet2", "A23", 56))
|
||||||
assert.NoError(t, f.SetCellStr("Sheet1", "B20", "42"))
|
assert.NoError(t, f.SetCellStr("Sheet1", "B20", "42"))
|
||||||
f.SetActiveSheet(0)
|
f.SetActiveSheet(0)
|
||||||
|
|
||||||
// Test add picture to sheet with scaling and positioning
|
// Test add picture to sheet with scaling and positioning
|
||||||
err := f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
|
scale := 0.5
|
||||||
`{"x_scale": 0.5, "y_scale": 0.5, "positioning": "absolute"}`)
|
assert.NoError(t, f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
|
||||||
if !assert.NoError(t, err) {
|
&PictureOptions{XScale: &scale, YScale: &scale, Positioning: "absolute"}))
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test add picture to worksheet without options
|
// Test add picture to worksheet without options
|
||||||
err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), "")
|
assert.NoError(t, f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), nil))
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test add picture to worksheet with invalid options
|
|
||||||
err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), `{`)
|
|
||||||
if !assert.Error(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewFile.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewFile.xlsx")))
|
||||||
assert.NoError(t, f.Save())
|
assert.NoError(t, f.Save())
|
||||||
|
@ -385,7 +380,7 @@ func TestSetCellHyperLink(t *testing.T) {
|
||||||
assert.NoError(t, f.SetCellHyperLink("Sheet1", "B19", "https://github.com/xuri/excelize", "External"))
|
assert.NoError(t, f.SetCellHyperLink("Sheet1", "B19", "https://github.com/xuri/excelize", "External"))
|
||||||
// Test add first hyperlink in a work sheet
|
// Test add first hyperlink in a work sheet
|
||||||
assert.NoError(t, f.SetCellHyperLink("Sheet2", "C1", "https://github.com/xuri/excelize", "External"))
|
assert.NoError(t, f.SetCellHyperLink("Sheet2", "C1", "https://github.com/xuri/excelize", "External"))
|
||||||
// Test add Location hyperlink in a work sheet.
|
// Test add Location hyperlink in a work sheet
|
||||||
assert.NoError(t, f.SetCellHyperLink("Sheet2", "D6", "Sheet1!D8", "Location"))
|
assert.NoError(t, f.SetCellHyperLink("Sheet2", "D6", "Sheet1!D8", "Location"))
|
||||||
// Test add Location hyperlink with display & tooltip in a work sheet
|
// Test add Location hyperlink with display & tooltip in a work sheet
|
||||||
display, tooltip := "Display value", "Hover text"
|
display, tooltip := "Display value", "Hover text"
|
||||||
|
@ -429,9 +424,7 @@ func TestSetCellHyperLink(t *testing.T) {
|
||||||
|
|
||||||
func TestGetCellHyperLink(t *testing.T) {
|
func TestGetCellHyperLink(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = f.GetCellHyperLink("Sheet1", "")
|
_, _, err = f.GetCellHyperLink("Sheet1", "")
|
||||||
assert.EqualError(t, err, `invalid cell name ""`)
|
assert.EqualError(t, err, `invalid cell name ""`)
|
||||||
|
@ -513,9 +506,9 @@ func TestSetSheetBackgroundErrors(t *testing.T) {
|
||||||
assert.EqualError(t, f.SetSheetBackground("Sheet1", filepath.Join("test", "images", "background.jpg")), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.SetSheetBackground("Sheet1", filepath.Join("test", "images", "background.jpg")), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestWriteArrayFormula tests the extended options of SetCellFormula by writing an array function
|
// TestWriteArrayFormula tests the extended options of SetCellFormula by writing
|
||||||
// to a workbook. In the resulting file, the lines 2 and 3 as well as 4 and 5 should have matching
|
// an array function to a workbook. In the resulting file, the lines 2 and 3 as
|
||||||
// contents.
|
// well as 4 and 5 should have matching contents
|
||||||
func TestWriteArrayFormula(t *testing.T) {
|
func TestWriteArrayFormula(t *testing.T) {
|
||||||
cell := func(col, row int) string {
|
cell := func(col, row int) string {
|
||||||
c, err := CoordinatesToCellName(col, row)
|
c, err := CoordinatesToCellName(col, row)
|
||||||
|
@ -620,15 +613,11 @@ func TestWriteArrayFormula(t *testing.T) {
|
||||||
|
|
||||||
func TestSetCellStyleAlignment(t *testing.T) {
|
func TestSetCellStyleAlignment(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
var style int
|
var style int
|
||||||
style, err = f.NewStyle(`{"alignment":{"horizontal":"center","ident":1,"justify_last_line":true,"reading_order":0,"relative_indent":1,"shrink_to_fit":true,"text_rotation":45,"vertical":"top","wrap_text":true}}`)
|
style, err = f.NewStyle(&Style{Alignment: &Alignment{Horizontal: "center", Indent: 1, JustifyLastLine: true, ReadingOrder: 0, RelativeIndent: 1, ShrinkToFit: true, TextRotation: 45, Vertical: "top", WrapText: true}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A22", "A22", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A22", "A22", style))
|
||||||
|
|
||||||
|
@ -651,13 +640,11 @@ func TestSetCellStyleAlignment(t *testing.T) {
|
||||||
|
|
||||||
func TestSetCellStyleBorder(t *testing.T) {
|
func TestSetCellStyleBorder(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
var style int
|
var style int
|
||||||
|
|
||||||
// Test set border on overlapping range with vertical variants shading styles gradient fill.
|
// Test set border on overlapping range with vertical variants shading styles gradient fill
|
||||||
style, err = f.NewStyle(&Style{
|
style, err = f.NewStyle(&Style{
|
||||||
Border: []Border{
|
Border: []Border{
|
||||||
{Type: "left", Color: "0000FF", Style: 3},
|
{Type: "left", Color: "0000FF", Style: 3},
|
||||||
|
@ -668,24 +655,18 @@ func TestSetCellStyleBorder(t *testing.T) {
|
||||||
{Type: "diagonalUp", Color: "A020F0", Style: 8},
|
{Type: "diagonalUp", Color: "A020F0", Style: 8},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "J21", "L25", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "J21", "L25", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"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}}`)
|
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}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"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":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}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "M28", "K24", style))
|
||||||
|
|
||||||
// Test set border and solid style pattern fill for a single cell.
|
// Test set border and solid style pattern fill for a single cell
|
||||||
style, err = f.NewStyle(&Style{
|
style, err = f.NewStyle(&Style{
|
||||||
Border: []Border{
|
Border: []Border{
|
||||||
{
|
{
|
||||||
|
@ -725,9 +706,7 @@ func TestSetCellStyleBorder(t *testing.T) {
|
||||||
Pattern: 1,
|
Pattern: 1,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "O22", "O22", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "O22", "O22", style))
|
||||||
|
|
||||||
|
@ -736,30 +715,18 @@ func TestSetCellStyleBorder(t *testing.T) {
|
||||||
|
|
||||||
func TestSetCellStyleBorderErrors(t *testing.T) {
|
func TestSetCellStyleBorderErrors(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set border with invalid style parameter.
|
// Set border with invalid style index number
|
||||||
_, err = f.NewStyle("")
|
_, err = f.NewStyle(&Style{Border: []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 !assert.EqualError(t, err, "unexpected end of JSON input") {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set border with invalid style index number.
|
|
||||||
_, err = f.NewStyle(`{"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 !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetCellStyleNumberFormat(t *testing.T) {
|
func TestSetCellStyleNumberFormat(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test only set fill and number format for a cell.
|
// Test only set fill and number format for a cell
|
||||||
col := []string{"L", "M", "N", "O", "P"}
|
col := []string{"L", "M", "N", "O", "P"}
|
||||||
data := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
|
data := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
|
||||||
value := []string{"37947.7500001", "-37947.7500001", "0.007", "2.1", "String"}
|
value := []string{"37947.7500001", "-37947.7500001", "0.007", "2.1", "String"}
|
||||||
|
@ -781,7 +748,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(`{"fill":{"type":"gradient","color":["#FFFFFF","#E0EBF5"],"shading":5},"number_format": ` + strconv.Itoa(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()
|
||||||
}
|
}
|
||||||
|
@ -792,10 +759,8 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var style int
|
var style int
|
||||||
style, err = f.NewStyle(`{"number_format":-1}`)
|
style, err = f.NewStyle(&Style{NumFmt: -1})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "L33", "L33", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "L33", "L33", style))
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleNumberFormat.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleNumberFormat.xlsx")))
|
||||||
|
@ -804,23 +769,17 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
|
||||||
func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
|
func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
|
||||||
t.Run("TestBook3", func(t *testing.T) {
|
t.Run("TestBook3", func(t *testing.T) {
|
||||||
f, err := prepareTestBook3()
|
f, err := prepareTestBook3()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 56))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 56))
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A2", -32.3))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A2", -32.3))
|
||||||
var style int
|
var style int
|
||||||
style, err = f.NewStyle(`{"number_format": 188, "decimal_places": -1}`)
|
style, err = f.NewStyle(&Style{NumFmt: 188, DecimalPlaces: -1})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
|
||||||
style, err = f.NewStyle(`{"number_format": 188, "decimal_places": 31, "negred": true}`)
|
style, err = f.NewStyle(&Style{NumFmt: 188, DecimalPlaces: 31, NegRed: true})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
|
||||||
|
|
||||||
|
@ -829,34 +788,24 @@ func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
|
||||||
|
|
||||||
t.Run("TestBook4", func(t *testing.T) {
|
t.Run("TestBook4", func(t *testing.T) {
|
||||||
f, err := prepareTestBook4()
|
f, err := prepareTestBook4()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 42920.5))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 42920.5))
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A2", 42920.5))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A2", 42920.5))
|
||||||
|
|
||||||
_, err = f.NewStyle(`{"number_format": 26, "lang": "zh-tw"}`)
|
_, err = f.NewStyle(&Style{NumFmt: 26, Lang: "zh-tw"})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
style, err := f.NewStyle(`{"number_format": 27}`)
|
style, err := f.NewStyle(&Style{NumFmt: 27})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
|
||||||
style, err = f.NewStyle(`{"number_format": 31, "lang": "ko-kr"}`)
|
style, err = f.NewStyle(&Style{NumFmt: 31, Lang: "ko-kr"})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"number_format": 71, "lang": "th-th"}`)
|
style, err = f.NewStyle(&Style{NumFmt: 71, Lang: "th-th"})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A2", "A2", style))
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleCurrencyNumberFormat.TestBook4.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleCurrencyNumberFormat.TestBook4.xlsx")))
|
||||||
|
@ -867,42 +816,40 @@ func TestSetCellStyleCustomNumberFormat(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 42920.5))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 42920.5))
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A2", 42920.5))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A2", 42920.5))
|
||||||
style, err := f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"}`)
|
customNumFmt := "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@"
|
||||||
|
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(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yyyy;@","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))
|
||||||
|
|
||||||
_, err = f.NewStyle(`{"custom_number_format": "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yy;@"}`)
|
customNumFmt = "[$-380A]dddd\\,\\ dd\" de \"mmmm\" de \"yy;@"
|
||||||
|
_, err = f.NewStyle(&Style{CustomNumFmt: &customNumFmt})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleCustomNumberFormat.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleCustomNumberFormat.xlsx")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetCellStyleFill(t *testing.T) {
|
func TestSetCellStyleFill(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
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(`{"fill":{"type":"gradient","color":["#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(`{"fill":{"type":"gradient","color":["#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))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"fill":{"type":"pattern","color":[],"pattern":1}}`)
|
style, err = f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{}, 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))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":19}}`)
|
style, err = f.NewStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"E0EBF5"}, Pattern: 19}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "O23", "O23", style))
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleFill.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellStyleFill.xlsx")))
|
||||||
|
@ -910,43 +857,31 @@ func TestSetCellStyleFill(t *testing.T) {
|
||||||
|
|
||||||
func TestSetCellStyleFont(t *testing.T) {
|
func TestSetCellStyleFont(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
var style int
|
var style int
|
||||||
style, err = f.NewStyle(`{"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"}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "A1", "A1", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "A1", "A1", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"font":{"italic":true,"underline":"double"}}`)
|
style, err = f.NewStyle(&Style{Font: &Font{Italic: true, Underline: "double"}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "A2", "A2", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "A2", "A2", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"font":{"bold":true}}`)
|
style, err = f.NewStyle(&Style{Font: &Font{Bold: true}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "A3", "A3", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "A3", "A3", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"font":{"bold":true,"family":"","size":0,"color":"","underline":""}}`)
|
style, err = f.NewStyle(&Style{Font: &Font{Bold: true, Family: "", Size: 0, Color: "", Underline: ""}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "A4", "A4", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "A4", "A4", style))
|
||||||
|
|
||||||
style, err = f.NewStyle(`{"font":{"color":"#777777","strike":true}}`)
|
style, err = f.NewStyle(&Style{Font: &Font{Color: "#777777", Strike: true}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "A5", "A5", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "A5", "A5", style))
|
||||||
|
|
||||||
|
@ -955,40 +890,30 @@ func TestSetCellStyleFont(t *testing.T) {
|
||||||
|
|
||||||
func TestSetCellStyleProtection(t *testing.T) {
|
func TestSetCellStyleProtection(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
var style int
|
var style int
|
||||||
style, err = f.NewStyle(`{"protection":{"hidden":true, "locked":true}}`)
|
style, err = f.NewStyle(&Style{Protection: &Protection{Hidden: true, Locked: true}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet2", "A6", "A6", style))
|
assert.NoError(t, f.SetCellStyle("Sheet2", "A6", "A6", style))
|
||||||
err = f.SaveAs(filepath.Join("test", "TestSetCellStyleProtection.xlsx"))
|
err = f.SaveAs(filepath.Join("test", "TestSetCellStyleProtection.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetDeleteSheet(t *testing.T) {
|
func TestSetDeleteSheet(t *testing.T) {
|
||||||
t.Run("TestBook3", func(t *testing.T) {
|
t.Run("TestBook3", func(t *testing.T) {
|
||||||
f, err := prepareTestBook3()
|
f, err := prepareTestBook3()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
f.DeleteSheet("XLSXSheet3")
|
assert.NoError(t, f.DeleteSheet("XLSXSheet3"))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook3.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook3.xlsx")))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("TestBook4", func(t *testing.T) {
|
t.Run("TestBook4", func(t *testing.T) {
|
||||||
f, err := prepareTestBook4()
|
f, err := prepareTestBook4()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
assert.NoError(t, f.DeleteSheet("Sheet1"))
|
||||||
}
|
|
||||||
f.DeleteSheet("Sheet1")
|
|
||||||
assert.NoError(t, f.AddComment("Sheet1", Comment{Cell: "A1", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}))
|
assert.NoError(t, f.AddComment("Sheet1", Comment{Cell: "A1", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook4.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook4.xlsx")))
|
||||||
})
|
})
|
||||||
|
@ -996,11 +921,10 @@ func TestSetDeleteSheet(t *testing.T) {
|
||||||
|
|
||||||
func TestSheetVisibility(t *testing.T) {
|
func TestSheetVisibility(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetSheetVisible("Sheet2", false))
|
assert.NoError(t, f.SetSheetVisible("Sheet2", false))
|
||||||
|
assert.NoError(t, f.SetSheetVisible("Sheet2", false, true))
|
||||||
assert.NoError(t, f.SetSheetVisible("Sheet1", false))
|
assert.NoError(t, f.SetSheetVisible("Sheet1", false))
|
||||||
assert.NoError(t, f.SetSheetVisible("Sheet1", true))
|
assert.NoError(t, f.SetSheetVisible("Sheet1", true))
|
||||||
visible, err := f.GetSheetVisible("Sheet1")
|
visible, err := f.GetSheetVisible("Sheet1")
|
||||||
|
@ -1058,123 +982,231 @@ 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(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
|
format1, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "#9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Light yellow format for neutral conditional.
|
// Light yellow format for neutral conditional
|
||||||
format2, err = f.NewConditionalStyle(`{"fill":{"type":"pattern","color":["#FEEAA0"],"pattern":1}}`)
|
format2, err = f.NewConditionalStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"#FEEAA0"}, Pattern: 1}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Light green format for good conditional.
|
// Light green format for good conditional
|
||||||
format3, err = f.NewConditionalStyle(`{"font":{"color":"#09600B"},"fill":{"type":"pattern","color":["#C7EECF"],"pattern":1}}`)
|
format3, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "#09600B"}, Fill: Fill{Type: "pattern", Color: []string{"#C7EECF"}, Pattern: 1}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// conditional style with align and left border.
|
// conditional style with align and left border
|
||||||
format4, err = f.NewConditionalStyle(`{"alignment":{"wrap_text":true},"border":[{"type":"left","color":"#000000","style":1}]}`)
|
format4, err = f.NewConditionalStyle(&Style{Alignment: &Alignment{WrapText: true}, Border: []Border{{Type: "left", Color: "#000000", Style: 1}}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color scales: 2 color
|
// Color scales: 2 color
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "A1:A10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "2_color_scale",
|
||||||
|
Criteria: "=",
|
||||||
|
MinType: "min",
|
||||||
|
MaxType: "max",
|
||||||
|
MinColor: "#F8696B",
|
||||||
|
MaxColor: "#63BE7B",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Color scales: 3 color
|
// Color scales: 3 color
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "B1:B10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "3_color_scale",
|
||||||
|
Criteria: "=",
|
||||||
|
MinType: "min",
|
||||||
|
MidType: "percentile",
|
||||||
|
MaxType: "max",
|
||||||
|
MinColor: "#F8696B",
|
||||||
|
MidColor: "#FFEB84",
|
||||||
|
MaxColor: "#63BE7B",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Highlight cells rules: between...
|
// Highlight cells rules: between...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "C1:C10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format1)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "C1:C10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "cell",
|
||||||
|
Criteria: "between",
|
||||||
|
Format: format1,
|
||||||
|
Minimum: "6",
|
||||||
|
Maximum: "8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Highlight cells rules: Greater Than...
|
// Highlight cells rules: Greater Than...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format3)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "D1:D10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "cell",
|
||||||
|
Criteria: ">",
|
||||||
|
Format: format3,
|
||||||
|
Value: "6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Highlight cells rules: Equal To...
|
// Highlight cells rules: Equal To...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "E1:E10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d}]`, format3)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "E1:E10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "top",
|
||||||
|
Criteria: "=",
|
||||||
|
Format: format3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Highlight cells rules: Not Equal To...
|
// Highlight cells rules: Not Equal To...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "F1:F10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format2)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "F1:F10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "unique",
|
||||||
|
Criteria: "=",
|
||||||
|
Format: format2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Highlight cells rules: Duplicate Values...
|
// Highlight cells rules: Duplicate Values...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "G1:G10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format2)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "G1:G10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "duplicate",
|
||||||
|
Criteria: "=",
|
||||||
|
Format: format2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Top/Bottom rules: Top 10%.
|
// Top/Bottom rules: Top 10%.
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6","percent":true}]`, format1)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "H1:H10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "top",
|
||||||
|
Criteria: "=",
|
||||||
|
Format: format1,
|
||||||
|
Value: "6",
|
||||||
|
Percent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Top/Bottom rules: Above Average...
|
// Top/Bottom rules: Above Average...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "I1:I10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": true}]`, format3)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "I1:I10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "average",
|
||||||
|
Criteria: "=",
|
||||||
|
Format: format3,
|
||||||
|
AboveAverage: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Top/Bottom rules: Below Average...
|
// Top/Bottom rules: Below Average...
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "J1:J10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": false}]`, format1)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "J1:J10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "average",
|
||||||
|
Criteria: "=",
|
||||||
|
Format: format1,
|
||||||
|
AboveAverage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Data Bars: Gradient Fill
|
// Data Bars: Gradient Fill
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10", `[{"type":"data_bar", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "data_bar",
|
||||||
|
Criteria: "=",
|
||||||
|
MinType: "min",
|
||||||
|
MaxType: "max",
|
||||||
|
BarColor: "#638EC6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Use a formula to determine which cells to format
|
// Use a formula to determine which cells to format
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "L1:L10", fmt.Sprintf(`[{"type":"formula", "criteria":"L2<3", "format":%d}]`, format1)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "L1:L10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "formula",
|
||||||
|
Criteria: "L2<3",
|
||||||
|
Format: format1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Alignment/Border cells rules
|
// Alignment/Border cells rules
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "M1:M10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"0"}]`, format4)))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "M1:M10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
// Test set invalid format set in conditional format
|
{
|
||||||
assert.EqualError(t, f.SetConditionalFormat(sheet1, "L1:L10", ""), "unexpected end of JSON input")
|
Type: "cell",
|
||||||
|
Criteria: ">",
|
||||||
|
Format: format4,
|
||||||
|
Value: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Test set conditional format on not exists worksheet
|
// Test set conditional format on not exists worksheet
|
||||||
assert.EqualError(t, f.SetConditionalFormat("SheetN", "L1:L10", "[]"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetConditionalFormat("SheetN", "L1:L10", nil), "sheet SheetN does not exist")
|
||||||
// Test set conditional format with invalid sheet name
|
// Test set conditional format with invalid sheet name
|
||||||
assert.EqualError(t, f.SetConditionalFormat("Sheet:1", "L1:L10", "[]"), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.SetConditionalFormat("Sheet:1", "L1:L10", nil), ErrSheetNameInvalid.Error())
|
||||||
|
|
||||||
err = f.SaveAs(filepath.Join("test", "TestConditionalFormat.xlsx"))
|
err = f.SaveAs(filepath.Join("test", "TestConditionalFormat.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set conditional format with illegal valid type
|
// Set conditional format with illegal valid type
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10", `[{"type":"", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "",
|
||||||
|
Criteria: "=",
|
||||||
|
MinType: "min",
|
||||||
|
MaxType: "max",
|
||||||
|
BarColor: "#638EC6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
// Set conditional format with illegal criteria type
|
// Set conditional format with illegal criteria type
|
||||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10", `[{"type":"data_bar", "criteria":"", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`))
|
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10",
|
||||||
|
[]ConditionalFormatOptions{
|
||||||
|
{
|
||||||
|
Type: "data_bar",
|
||||||
|
Criteria: "",
|
||||||
|
MinType: "min",
|
||||||
|
MaxType: "max",
|
||||||
|
BarColor: "#638EC6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
// Test create conditional format with invalid custom number format
|
||||||
|
var exp string
|
||||||
|
_, err = f.NewConditionalStyle(&Style{CustomNumFmt: &exp})
|
||||||
|
assert.EqualError(t, err, ErrCustomNumFmt.Error())
|
||||||
|
|
||||||
// Set conditional format with file without dxfs element should not return error
|
// Set conditional format with file without dxfs element should not return error
|
||||||
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
|
_, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "#9A0511"}, Fill: Fill{Type: "", Color: []string{"#FEC7CE"}, Pattern: 1}})
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConditionalFormatError(t *testing.T) {
|
|
||||||
f := NewFile()
|
|
||||||
sheet1 := f.GetSheetName(0)
|
|
||||||
|
|
||||||
fillCells(f, sheet1, 10, 15)
|
|
||||||
|
|
||||||
// Set conditional format with illegal JSON string should return error.
|
|
||||||
_, err := f.NewConditionalStyle("")
|
|
||||||
if !assert.EqualError(t, err, "unexpected end of JSON input") {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSharedStrings(t *testing.T) {
|
func TestSharedStrings(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "SharedStrings.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "SharedStrings.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
rows, err := f.GetRows("Sheet1")
|
rows, err := f.GetRows("Sheet1")
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, "A", rows[0][0])
|
assert.Equal(t, "A", rows[0][0])
|
||||||
rows, err = f.GetRows("Sheet2")
|
rows, err = f.GetRows("Sheet2")
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, "Test Weight (Kgs)", rows[0][0])
|
assert.Equal(t, "Test Weight (Kgs)", rows[0][0])
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetSheetCol(t *testing.T) {
|
func TestSetSheetCol(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetSheetCol("Sheet1", "B27", &[]interface{}{"cell", nil, int32(42), float64(42), time.Now().UTC()}))
|
assert.NoError(t, f.SetSheetCol("Sheet1", "B27", &[]interface{}{"cell", nil, int32(42), float64(42), time.Now().UTC()}))
|
||||||
|
|
||||||
|
@ -1190,9 +1222,7 @@ func TestSetSheetCol(t *testing.T) {
|
||||||
|
|
||||||
func TestSetSheetRow(t *testing.T) {
|
func TestSetSheetRow(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.SetSheetRow("Sheet1", "B27", &[]interface{}{"cell", nil, int32(42), float64(42), time.Now().UTC()}))
|
assert.NoError(t, f.SetSheetRow("Sheet1", "B27", &[]interface{}{"cell", nil, int32(42), float64(42), time.Now().UTC()}))
|
||||||
|
|
||||||
|
@ -1300,10 +1330,8 @@ func TestProtectSheet(t *testing.T) {
|
||||||
|
|
||||||
func TestUnprotectSheet(t *testing.T) {
|
func TestUnprotectSheet(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
// Test remove protection on not exists worksheet
|
||||||
}
|
|
||||||
// Test remove protection on not exists worksheet.
|
|
||||||
assert.EqualError(t, f.UnprotectSheet("SheetN"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.UnprotectSheet("SheetN"), "sheet SheetN does not exist")
|
||||||
|
|
||||||
assert.NoError(t, f.UnprotectSheet("Sheet1"))
|
assert.NoError(t, f.UnprotectSheet("Sheet1"))
|
||||||
|
@ -1343,7 +1371,7 @@ func TestProtectWorkbook(t *testing.T) {
|
||||||
assert.Equal(t, 24, len(wb.WorkbookProtection.WorkbookSaltValue))
|
assert.Equal(t, 24, len(wb.WorkbookProtection.WorkbookSaltValue))
|
||||||
assert.Equal(t, 88, len(wb.WorkbookProtection.WorkbookHashValue))
|
assert.Equal(t, 88, len(wb.WorkbookProtection.WorkbookHashValue))
|
||||||
assert.Equal(t, int(workbookProtectionSpinCount), wb.WorkbookProtection.WorkbookSpinCount)
|
assert.Equal(t, int(workbookProtectionSpinCount), wb.WorkbookProtection.WorkbookSpinCount)
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestProtectWorkbook.xlsx")))
|
|
||||||
// Test protect workbook with password exceeds the limit length
|
// Test protect workbook with password exceeds the limit length
|
||||||
assert.EqualError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{
|
assert.EqualError(t, f.ProtectWorkbook(&WorkbookProtectionOptions{
|
||||||
AlgorithmName: "MD4",
|
AlgorithmName: "MD4",
|
||||||
|
@ -1354,13 +1382,15 @@ func TestProtectWorkbook(t *testing.T) {
|
||||||
AlgorithmName: "RIPEMD-160",
|
AlgorithmName: "RIPEMD-160",
|
||||||
Password: "password",
|
Password: "password",
|
||||||
}), ErrUnsupportedHashAlgorithm.Error())
|
}), ErrUnsupportedHashAlgorithm.Error())
|
||||||
|
// Test protect workbook with unsupported charset workbook
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.ProtectWorkbook(nil), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnprotectWorkbook(t *testing.T) {
|
func TestUnprotectWorkbook(t *testing.T) {
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, f.UnprotectWorkbook())
|
assert.NoError(t, f.UnprotectWorkbook())
|
||||||
assert.EqualError(t, f.UnprotectWorkbook("password"), ErrUnprotectWorkbook.Error())
|
assert.EqualError(t, f.UnprotectWorkbook("password"), ErrUnprotectWorkbook.Error())
|
||||||
|
@ -1382,6 +1412,10 @@ func TestUnprotectWorkbook(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
wb.WorkbookProtection.WorkbookSaltValue = "YWJjZA====="
|
wb.WorkbookProtection.WorkbookSaltValue = "YWJjZA====="
|
||||||
assert.EqualError(t, f.UnprotectWorkbook("wrongPassword"), "illegal base64 data at input byte 8")
|
assert.EqualError(t, f.UnprotectWorkbook("wrongPassword"), "illegal base64 data at input byte 8")
|
||||||
|
// Test remove workbook protection with unsupported charset workbook
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.UnprotectWorkbook(), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetDefaultTimeStyle(t *testing.T) {
|
func TestSetDefaultTimeStyle(t *testing.T) {
|
||||||
|
@ -1409,7 +1443,7 @@ func TestAddVBAProject(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContentTypesReader(t *testing.T) {
|
func TestContentTypesReader(t *testing.T) {
|
||||||
// Test unsupported charset.
|
// Test unsupported charset
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
@ -1418,7 +1452,7 @@ func TestContentTypesReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkbookReader(t *testing.T) {
|
func TestWorkbookReader(t *testing.T) {
|
||||||
// Test unsupported charset.
|
// Test unsupported charset
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
@ -1427,7 +1461,7 @@ func TestWorkbookReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWorkSheetReader(t *testing.T) {
|
func TestWorkSheetReader(t *testing.T) {
|
||||||
// Test unsupported charset.
|
// Test unsupported charset
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
||||||
|
@ -1435,7 +1469,7 @@ func TestWorkSheetReader(t *testing.T) {
|
||||||
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")
|
||||||
assert.EqualError(t, f.UpdateLinkedValue(), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.UpdateLinkedValue(), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
||||||
// Test on no checked worksheet.
|
// Test on no checked worksheet
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData/></worksheet>`))
|
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData/></worksheet>`))
|
||||||
|
@ -1445,7 +1479,7 @@ func TestWorkSheetReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRelsReader(t *testing.T) {
|
func TestRelsReader(t *testing.T) {
|
||||||
// Test unsupported charset.
|
// Test unsupported charset
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
rels := defaultXMLPathWorkbookRels
|
rels := defaultXMLPathWorkbookRels
|
||||||
f.Relationships.Store(rels, nil)
|
f.Relationships.Store(rels, nil)
|
||||||
|
@ -1463,7 +1497,7 @@ func TestDeleteSheetFromWorkbookRels(t *testing.T) {
|
||||||
|
|
||||||
func TestUpdateLinkedValue(t *testing.T) {
|
func TestUpdateLinkedValue(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test update lined value with unsupported charset workbook.
|
// Test update lined value with unsupported charset workbook
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.UpdateLinkedValue(), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.UpdateLinkedValue(), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
@ -1482,16 +1516,21 @@ func prepareTestBook1() (*File, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"),
|
if err = f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"),
|
||||||
`{"x_offset": 140, "y_offset": 120, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`)
|
&PictureOptions{OffsetX: 140, OffsetY: 120, Hyperlink: "#Sheet2!D8", HyperlinkType: "Location"}); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test add picture to worksheet with offset, external hyperlink and positioning.
|
// Test add picture to worksheet with offset, external hyperlink and positioning
|
||||||
err = f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.png"),
|
if err := f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.png"),
|
||||||
`{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`)
|
&PictureOptions{
|
||||||
if err != nil {
|
OffsetX: 10,
|
||||||
|
OffsetY: 10,
|
||||||
|
Hyperlink: "https://github.com/xuri/excelize",
|
||||||
|
HyperlinkType: "External",
|
||||||
|
Positioning: "oneCell",
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1500,7 +1539,7 @@ func prepareTestBook1() (*File, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = f.AddPictureFromBytes("Sheet1", "Q1", "", "Excel Logo", ".jpg", file)
|
err = f.AddPictureFromBytes("Sheet1", "Q1", "Excel Logo", ".jpg", file, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1510,9 +1549,12 @@ func prepareTestBook1() (*File, error) {
|
||||||
|
|
||||||
func prepareTestBook3() (*File, error) {
|
func prepareTestBook3() (*File, error) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet1")
|
if _, err := f.NewSheet("XLSXSheet2"); err != nil {
|
||||||
f.NewSheet("XLSXSheet2")
|
return nil, err
|
||||||
f.NewSheet("XLSXSheet3")
|
}
|
||||||
|
if _, err := f.NewSheet("XLSXSheet3"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := f.SetCellInt("XLSXSheet2", "A23", 56); err != nil {
|
if err := f.SetCellInt("XLSXSheet2", "A23", 56); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1520,18 +1562,14 @@ func prepareTestBook3() (*File, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f.SetActiveSheet(0)
|
f.SetActiveSheet(0)
|
||||||
|
scale := 0.5
|
||||||
err := f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
|
if err := f.AddPicture("Sheet1", "H2", filepath.Join("test", "images", "excel.gif"),
|
||||||
`{"x_scale": 0.5, "y_scale": 0.5, "positioning": "absolute"}`)
|
&PictureOptions{XScale: &scale, YScale: &scale, Positioning: "absolute"}); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), nil); err != nil {
|
||||||
err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
lib.go
15
lib.go
|
@ -313,15 +313,15 @@ func sortCoordinates(coordinates []int) error {
|
||||||
|
|
||||||
// coordinatesToRangeRef provides a function to convert a pair of coordinates
|
// coordinatesToRangeRef provides a function to convert a pair of coordinates
|
||||||
// to range reference.
|
// to range reference.
|
||||||
func (f *File) coordinatesToRangeRef(coordinates []int) (string, error) {
|
func (f *File) coordinatesToRangeRef(coordinates []int, abs ...bool) (string, error) {
|
||||||
if len(coordinates) != 4 {
|
if len(coordinates) != 4 {
|
||||||
return "", ErrCoordinates
|
return "", ErrCoordinates
|
||||||
}
|
}
|
||||||
firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1])
|
firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1], abs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
lastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3])
|
lastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3], abs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -493,15 +493,6 @@ func (avb *attrValBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallbackOptions provides a method to convert format string to []byte and
|
|
||||||
// handle empty string.
|
|
||||||
func fallbackOptions(opts string) []byte {
|
|
||||||
if opts != "" {
|
|
||||||
return []byte(opts)
|
|
||||||
}
|
|
||||||
return []byte("{}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// namespaceStrictToTransitional provides a method to convert Strict and
|
// namespaceStrictToTransitional provides a method to convert Strict and
|
||||||
// Transitional namespaces.
|
// Transitional namespaces.
|
||||||
func namespaceStrictToTransitional(content []byte) []byte {
|
func namespaceStrictToTransitional(content []byte) []byte {
|
||||||
|
|
|
@ -37,7 +37,8 @@ func TestMergeCell(t *testing.T) {
|
||||||
assert.Equal(t, "SUM(Sheet1!B19,Sheet1!C19)", value)
|
assert.Equal(t, "SUM(Sheet1!B19,Sheet1!C19)", value)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
f.NewSheet("Sheet3")
|
_, err = f.NewSheet("Sheet3")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.MergeCell("Sheet3", "D11", "F13"))
|
assert.NoError(t, f.MergeCell("Sheet3", "D11", "F13"))
|
||||||
assert.NoError(t, f.MergeCell("Sheet3", "G10", "K12"))
|
assert.NoError(t, f.MergeCell("Sheet3", "G10", "K12"))
|
||||||
|
|
||||||
|
|
107
picture.go
107
picture.go
|
@ -13,7 +13,6 @@ package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
|
@ -26,14 +25,28 @@ import (
|
||||||
|
|
||||||
// parsePictureOptions provides a function to parse the format settings of
|
// parsePictureOptions provides a function to parse the format settings of
|
||||||
// the picture with default value.
|
// the picture with default value.
|
||||||
func parsePictureOptions(opts string) (*pictureOptions, error) {
|
func parsePictureOptions(opts *PictureOptions) *PictureOptions {
|
||||||
format := pictureOptions{
|
if opts == nil {
|
||||||
FPrintsWithSheet: true,
|
return &PictureOptions{
|
||||||
XScale: 1,
|
PrintObject: boolPtr(true),
|
||||||
YScale: 1,
|
Locked: boolPtr(false),
|
||||||
|
XScale: float64Ptr(defaultPictureScale),
|
||||||
|
YScale: float64Ptr(defaultPictureScale),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(fallbackOptions(opts), &format)
|
if opts.PrintObject == nil {
|
||||||
return &format, err
|
opts.PrintObject = boolPtr(true)
|
||||||
|
}
|
||||||
|
if opts.Locked == nil {
|
||||||
|
opts.Locked = boolPtr(false)
|
||||||
|
}
|
||||||
|
if opts.XScale == nil {
|
||||||
|
opts.XScale = float64Ptr(defaultPictureScale)
|
||||||
|
}
|
||||||
|
if opts.YScale == nil {
|
||||||
|
opts.YScale = float64Ptr(defaultPictureScale)
|
||||||
|
}
|
||||||
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPicture provides the method to add picture in a sheet by given picture
|
// AddPicture provides the method to add picture in a sheet by given picture
|
||||||
|
@ -44,6 +57,7 @@ func parsePictureOptions(opts string) (*pictureOptions, error) {
|
||||||
// package main
|
// package main
|
||||||
//
|
//
|
||||||
// import (
|
// import (
|
||||||
|
// "fmt"
|
||||||
// _ "image/gif"
|
// _ "image/gif"
|
||||||
// _ "image/jpeg"
|
// _ "image/jpeg"
|
||||||
// _ "image/png"
|
// _ "image/png"
|
||||||
|
@ -54,15 +68,33 @@ func parsePictureOptions(opts string) (*pictureOptions, error) {
|
||||||
// func main() {
|
// func main() {
|
||||||
// f := excelize.NewFile()
|
// f := excelize.NewFile()
|
||||||
// // Insert a picture.
|
// // Insert a picture.
|
||||||
// if err := f.AddPicture("Sheet1", "A2", "image.jpg", ""); err != nil {
|
// if err := f.AddPicture("Sheet1", "A2", "image.jpg", nil); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// // Insert a picture scaling in the cell with location hyperlink.
|
// // Insert a picture scaling in the cell with location hyperlink.
|
||||||
// if err := f.AddPicture("Sheet1", "D2", "image.png", `{"x_scale": 0.5, "y_scale": 0.5, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`); err != nil {
|
// enable, scale := true, 0.5
|
||||||
|
// if err := f.AddPicture("Sheet1", "D2", "image.png",
|
||||||
|
// &excelize.PictureOptions{
|
||||||
|
// XScale: &scale,
|
||||||
|
// YScale: &scale,
|
||||||
|
// Hyperlink: "#Sheet2!D8",
|
||||||
|
// HyperlinkType: "Location",
|
||||||
|
// },
|
||||||
|
// ); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// // Insert a picture offset in the cell with external hyperlink, printing and positioning support.
|
// // Insert a picture offset in the cell with external hyperlink, printing and positioning support.
|
||||||
// if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "print_obj": true, "lock_aspect_ratio": false, "locked": false, "positioning": "oneCell"}`); err != nil {
|
// if err := f.AddPicture("Sheet1", "H2", "image.gif",
|
||||||
|
// &excelize.PictureOptions{
|
||||||
|
// PrintObject: &enable,
|
||||||
|
// LockAspectRatio: false,
|
||||||
|
// OffsetX: 15,
|
||||||
|
// OffsetY: 10,
|
||||||
|
// Hyperlink: "https://github.com/xuri/excelize",
|
||||||
|
// HyperlinkType: "External",
|
||||||
|
// Positioning: "oneCell",
|
||||||
|
// },
|
||||||
|
// ); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
||||||
|
@ -70,42 +102,42 @@ func parsePictureOptions(opts string) (*pictureOptions, error) {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The optional parameter "autofit" specifies if you make image size auto-fits the
|
// The optional parameter "Autofit" specifies if you make image size auto-fits the
|
||||||
// cell, the default value of that is 'false'.
|
// cell, the default value of that is 'false'.
|
||||||
//
|
//
|
||||||
// The optional parameter "hyperlink" specifies the hyperlink of the image.
|
// The optional parameter "Hyperlink" specifies the hyperlink of the image.
|
||||||
//
|
//
|
||||||
// The optional parameter "hyperlink_type" defines two types of
|
// The optional parameter "HyperlinkType" defines two types of
|
||||||
// hyperlink "External" for website or "Location" for moving to one of the
|
// hyperlink "External" for website or "Location" for moving to one of the
|
||||||
// cells in this workbook. When the "hyperlink_type" is "Location",
|
// cells in this workbook. When the "hyperlink_type" is "Location",
|
||||||
// coordinates need to start with "#".
|
// coordinates need to start with "#".
|
||||||
//
|
//
|
||||||
// The optional parameter "positioning" defines two types of the position of an
|
// The optional parameter "Positioning" defines two types of the position of an
|
||||||
// image in an Excel spreadsheet, "oneCell" (Move but don't size with
|
// image in an Excel spreadsheet, "oneCell" (Move but don't size with
|
||||||
// cells) or "absolute" (Don't move or size with cells). If you don't set this
|
// cells) or "absolute" (Don't move or size with cells). If you don't set this
|
||||||
// parameter, the default positioning is move and size with cells.
|
// parameter, the default positioning is move and size with cells.
|
||||||
//
|
//
|
||||||
// The optional parameter "print_obj" indicates whether the image is printed
|
// The optional parameter "PrintObject" indicates whether the image is printed
|
||||||
// when the worksheet is printed, the default value of that is 'true'.
|
// when the worksheet is printed, the default value of that is 'true'.
|
||||||
//
|
//
|
||||||
// The optional parameter "lock_aspect_ratio" indicates whether lock aspect
|
// The optional parameter "LockAspectRatio" indicates whether lock aspect
|
||||||
// ratio for the image, the default value of that is 'false'.
|
// ratio for the image, the default value of that is 'false'.
|
||||||
//
|
//
|
||||||
// The optional parameter "locked" indicates whether lock the image. Locking
|
// The optional parameter "Locked" indicates whether lock the image. Locking
|
||||||
// an object has no effect unless the sheet is protected.
|
// an object has no effect unless the sheet is protected.
|
||||||
//
|
//
|
||||||
// The optional parameter "x_offset" specifies the horizontal offset of the
|
// The optional parameter "OffsetX" specifies the horizontal offset of the
|
||||||
// image with the cell, the default value of that is 0.
|
// image with the cell, the default value of that is 0.
|
||||||
//
|
//
|
||||||
// The optional parameter "x_scale" specifies the horizontal scale of images,
|
// The optional parameter "XScale" specifies the horizontal scale of images,
|
||||||
// the default value of that is 1.0 which presents 100%.
|
// the default value of that is 1.0 which presents 100%.
|
||||||
//
|
//
|
||||||
// The optional parameter "y_offset" specifies the vertical offset of the
|
// The optional parameter "OffsetY" specifies the vertical offset of the
|
||||||
// image with the cell, the default value of that is 0.
|
// image with the cell, the default value of that is 0.
|
||||||
//
|
//
|
||||||
// The optional parameter "y_scale" specifies the vertical scale of images,
|
// The optional parameter "YScale" specifies the vertical scale of images,
|
||||||
// the default value of that is 1.0 which presents 100%.
|
// the default value of that is 1.0 which presents 100%.
|
||||||
func (f *File) AddPicture(sheet, cell, picture, format string) error {
|
func (f *File) AddPicture(sheet, cell, picture string, opts *PictureOptions) error {
|
||||||
var err error
|
var err error
|
||||||
// Check picture exists first.
|
// Check picture exists first.
|
||||||
if _, err = os.Stat(picture); os.IsNotExist(err) {
|
if _, err = os.Stat(picture); os.IsNotExist(err) {
|
||||||
|
@ -117,7 +149,7 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
|
||||||
}
|
}
|
||||||
file, _ := os.ReadFile(filepath.Clean(picture))
|
file, _ := os.ReadFile(filepath.Clean(picture))
|
||||||
_, name := filepath.Split(picture)
|
_, name := filepath.Split(picture)
|
||||||
return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
|
return f.AddPictureFromBytes(sheet, cell, name, ext, file, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPictureFromBytes provides the method to add picture in a sheet by given
|
// AddPictureFromBytes provides the method to add picture in a sheet by given
|
||||||
|
@ -143,24 +175,21 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// if err := f.AddPictureFromBytes("Sheet1", "A2", "", "Excel Logo", ".jpg", file); err != nil {
|
// if err := f.AddPictureFromBytes("Sheet1", "A2", "Excel Logo", ".jpg", file, nil); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, file []byte) error {
|
func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *PictureOptions) error {
|
||||||
var drawingHyperlinkRID int
|
var drawingHyperlinkRID int
|
||||||
var hyperlinkType string
|
var hyperlinkType string
|
||||||
ext, ok := supportedImageTypes[extension]
|
ext, ok := supportedImageTypes[extension]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrImgExt
|
return ErrImgExt
|
||||||
}
|
}
|
||||||
options, err := parsePictureOptions(opts)
|
options := parsePictureOptions(opts)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
img, _, err := image.DecodeConfig(bytes.NewReader(file))
|
img, _, err := image.DecodeConfig(bytes.NewReader(file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -276,20 +305,20 @@ func (f *File) countDrawings() int {
|
||||||
// addDrawingPicture provides a function to add picture by given sheet,
|
// addDrawingPicture provides a function to add picture by given sheet,
|
||||||
// drawingXML, cell, file name, width, height relationship index and format
|
// drawingXML, cell, file name, width, height relationship index and format
|
||||||
// sets.
|
// sets.
|
||||||
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID, hyperlinkRID int, img image.Config, opts *pictureOptions) error {
|
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID, hyperlinkRID int, img image.Config, opts *PictureOptions) error {
|
||||||
col, row, err := CellNameToCoordinates(cell)
|
col, row, err := CellNameToCoordinates(cell)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
width, height := img.Width, img.Height
|
width, height := img.Width, img.Height
|
||||||
if opts.Autofit {
|
if opts.AutoFit {
|
||||||
width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts)
|
width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
width = int(float64(width) * opts.XScale)
|
width = int(float64(width) * *opts.XScale)
|
||||||
height = int(float64(height) * opts.YScale)
|
height = int(float64(height) * *opts.YScale)
|
||||||
}
|
}
|
||||||
col--
|
col--
|
||||||
row--
|
row--
|
||||||
|
@ -313,7 +342,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID,
|
||||||
twoCellAnchor.From = &from
|
twoCellAnchor.From = &from
|
||||||
twoCellAnchor.To = &to
|
twoCellAnchor.To = &to
|
||||||
pic := xlsxPic{}
|
pic := xlsxPic{}
|
||||||
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.NoChangeAspect
|
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.LockAspectRatio
|
||||||
pic.NvPicPr.CNvPr.ID = cNvPrID
|
pic.NvPicPr.CNvPr.ID = cNvPrID
|
||||||
pic.NvPicPr.CNvPr.Descr = file
|
pic.NvPicPr.CNvPr.Descr = file
|
||||||
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
|
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
|
||||||
|
@ -342,8 +371,8 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID,
|
||||||
|
|
||||||
twoCellAnchor.Pic = &pic
|
twoCellAnchor.Pic = &pic
|
||||||
twoCellAnchor.ClientData = &xdrClientData{
|
twoCellAnchor.ClientData = &xdrClientData{
|
||||||
FLocksWithSheet: opts.FLocksWithSheet,
|
FLocksWithSheet: *opts.Locked,
|
||||||
FPrintsWithSheet: opts.FPrintsWithSheet,
|
FPrintsWithSheet: *opts.PrintObject,
|
||||||
}
|
}
|
||||||
content.Lock()
|
content.Lock()
|
||||||
defer content.Unlock()
|
defer content.Unlock()
|
||||||
|
@ -682,7 +711,7 @@ func (f *File) drawingsWriter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawingResize calculate the height and width after resizing.
|
// drawingResize calculate the height and width after resizing.
|
||||||
func (f *File) drawingResize(sheet, cell string, width, height float64, opts *pictureOptions) (w, h, c, r int, err error) {
|
func (f *File) drawingResize(sheet, cell string, width, height float64, opts *PictureOptions) (w, h, c, r int, err error) {
|
||||||
var mergeCells []MergeCell
|
var mergeCells []MergeCell
|
||||||
mergeCells, err = f.GetMergeCells(sheet)
|
mergeCells, err = f.GetMergeCells(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -725,6 +754,6 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, opts *pi
|
||||||
height, width = float64(cellHeight), width*asp
|
height, width = float64(cellHeight), width*asp
|
||||||
}
|
}
|
||||||
width, height = width-float64(opts.OffsetX), height-float64(opts.OffsetY)
|
width, height = width-float64(opts.OffsetX), height-float64(opts.OffsetY)
|
||||||
w, h = int(width*opts.XScale), int(height*opts.YScale)
|
w, h = int(width**opts.XScale), int(height**opts.YScale)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ func BenchmarkAddPictureFromBytes(b *testing.B) {
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 1; i <= b.N; i++ {
|
for i := 1; i <= b.N; i++ {
|
||||||
if err := f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", i), "", "excel", ".png", imgFile); err != nil {
|
if err := f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", i), "excel", ".png", imgFile, nil); err != nil {
|
||||||
b.Error(err)
|
b.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,32 +37,33 @@ func TestAddPicture(t *testing.T) {
|
||||||
|
|
||||||
// Test add picture to worksheet with offset and location hyperlink
|
// Test add picture to worksheet with offset and location hyperlink
|
||||||
assert.NoError(t, f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"),
|
assert.NoError(t, f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"),
|
||||||
`{"x_offset": 140, "y_offset": 120, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`))
|
&PictureOptions{OffsetX: 140, OffsetY: 120, Hyperlink: "#Sheet2!D8", HyperlinkType: "Location"}))
|
||||||
// Test add picture to worksheet with offset, external hyperlink and positioning
|
// Test add picture to worksheet with offset, external hyperlink and positioning
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"),
|
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"),
|
||||||
`{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`))
|
&PictureOptions{OffsetX: 10, OffsetY: 10, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External", Positioning: "oneCell"}))
|
||||||
|
|
||||||
file, err := os.ReadFile(filepath.Join("test", "images", "excel.png"))
|
file, err := os.ReadFile(filepath.Join("test", "images", "excel.png"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Test add picture to worksheet with autofit
|
// Test add picture to worksheet with autofit
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "A30", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`))
|
assert.NoError(t, f.AddPicture("Sheet1", "A30", filepath.Join("test", "images", "excel.jpg"), &PictureOptions{AutoFit: true}))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "B30", filepath.Join("test", "images", "excel.jpg"), `{"x_offset": 10, "y_offset": 10, "autofit": true}`))
|
assert.NoError(t, f.AddPicture("Sheet1", "B30", filepath.Join("test", "images", "excel.jpg"), &PictureOptions{OffsetX: 10, OffsetY: 10, AutoFit: true}))
|
||||||
f.NewSheet("AddPicture")
|
_, err = f.NewSheet("AddPicture")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetRowHeight("AddPicture", 10, 30))
|
assert.NoError(t, f.SetRowHeight("AddPicture", 10, 30))
|
||||||
assert.NoError(t, f.MergeCell("AddPicture", "B3", "D9"))
|
assert.NoError(t, f.MergeCell("AddPicture", "B3", "D9"))
|
||||||
assert.NoError(t, f.MergeCell("AddPicture", "B1", "D1"))
|
assert.NoError(t, f.MergeCell("AddPicture", "B1", "D1"))
|
||||||
assert.NoError(t, f.AddPicture("AddPicture", "C6", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`))
|
assert.NoError(t, f.AddPicture("AddPicture", "C6", filepath.Join("test", "images", "excel.jpg"), &PictureOptions{AutoFit: true}))
|
||||||
assert.NoError(t, f.AddPicture("AddPicture", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`))
|
assert.NoError(t, f.AddPicture("AddPicture", "A1", filepath.Join("test", "images", "excel.jpg"), &PictureOptions{AutoFit: true}))
|
||||||
|
|
||||||
// Test add picture to worksheet from bytes
|
// Test add picture to worksheet from bytes
|
||||||
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", "", "Excel Logo", ".png", file))
|
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", "Excel Logo", ".png", file, nil))
|
||||||
// Test add picture to worksheet from bytes with illegal cell reference
|
// Test add picture to worksheet from bytes with illegal cell reference
|
||||||
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "A", "", "Excel Logo", ".png", file), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "A", "Excel Logo", ".png", file, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
|
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q8", filepath.Join("test", "images", "excel.gif"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q8", filepath.Join("test", "images", "excel.gif"), nil))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q15", filepath.Join("test", "images", "excel.jpg"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q15", filepath.Join("test", "images", "excel.jpg"), nil))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q22", filepath.Join("test", "images", "excel.tif"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q22", filepath.Join("test", "images", "excel.tif"), nil))
|
||||||
|
|
||||||
// Test write file to given path
|
// Test write file to given path
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture1.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture1.xlsx")))
|
||||||
|
@ -72,10 +73,10 @@ func TestAddPicture(t *testing.T) {
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "Q1", "", "Excel Logo", ".png", file), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "Q1", "Excel Logo", ".png", file, nil), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
|
||||||
// Test add picture with invalid sheet name
|
// Test add picture with invalid sheet name
|
||||||
assert.EqualError(t, f.AddPicture("Sheet:1", "A1", filepath.Join("test", "images", "excel.jpg"), ""), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AddPicture("Sheet:1", "A1", filepath.Join("test", "images", "excel.jpg"), nil), ErrSheetNameInvalid.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPictureErrors(t *testing.T) {
|
func TestAddPictureErrors(t *testing.T) {
|
||||||
|
@ -83,14 +84,14 @@ func TestAddPictureErrors(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Test add picture to worksheet with invalid file path
|
// Test add picture to worksheet with invalid file path
|
||||||
assert.Error(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), ""))
|
assert.Error(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), nil))
|
||||||
|
|
||||||
// Test add picture to worksheet with unsupported file type
|
// Test add picture to worksheet with unsupported file type
|
||||||
assert.EqualError(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), ""), ErrImgExt.Error())
|
assert.EqualError(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), nil), ErrImgExt.Error())
|
||||||
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1)), ErrImgExt.Error())
|
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "Excel Logo", "jpg", make([]byte, 1), nil), ErrImgExt.Error())
|
||||||
|
|
||||||
// Test add picture to worksheet with invalid file data
|
// Test add picture to worksheet with invalid file data
|
||||||
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1)), image.ErrFormat.Error())
|
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "Excel Logo", ".jpg", make([]byte, 1), nil), image.ErrFormat.Error())
|
||||||
|
|
||||||
// Test add picture with custom image decoder and encoder
|
// Test add picture with custom image decoder and encoder
|
||||||
decode := func(r io.Reader) (image.Image, error) { return nil, nil }
|
decode := func(r io.Reader) (image.Image, error) { return nil, nil }
|
||||||
|
@ -100,18 +101,19 @@ func TestAddPictureErrors(t *testing.T) {
|
||||||
image.RegisterFormat("emz", "", decode, decodeConfig)
|
image.RegisterFormat("emz", "", decode, decodeConfig)
|
||||||
image.RegisterFormat("wmz", "", decode, decodeConfig)
|
image.RegisterFormat("wmz", "", decode, decodeConfig)
|
||||||
image.RegisterFormat("svg", "", decode, decodeConfig)
|
image.RegisterFormat("svg", "", decode, decodeConfig)
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q1", filepath.Join("test", "images", "excel.emf"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q1", filepath.Join("test", "images", "excel.emf"), nil))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q7", filepath.Join("test", "images", "excel.wmf"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q7", filepath.Join("test", "images", "excel.wmf"), nil))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q13", filepath.Join("test", "images", "excel.emz"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q13", filepath.Join("test", "images", "excel.emz"), nil))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q19", filepath.Join("test", "images", "excel.wmz"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "Q19", filepath.Join("test", "images", "excel.wmz"), nil))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "Q25", "excelize.svg", `{"x_scale": 2.1}`))
|
xScale := 2.1
|
||||||
|
assert.NoError(t, f.AddPicture("Sheet1", "Q25", "excelize.svg", &PictureOptions{XScale: &xScale}))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture2.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture2.xlsx")))
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPicture(t *testing.T) {
|
func TestGetPicture(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.png"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.png"), nil))
|
||||||
name, content, err := f.GetPicture("Sheet1", "A1")
|
name, content, err := f.GetPicture("Sheet1", "A1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 13233, len(content))
|
assert.Equal(t, 13233, len(content))
|
||||||
|
@ -196,19 +198,20 @@ func TestGetPicture(t *testing.T) {
|
||||||
func TestAddDrawingPicture(t *testing.T) {
|
func TestAddDrawingPicture(t *testing.T) {
|
||||||
// Test addDrawingPicture with illegal cell reference
|
// Test addDrawingPicture with illegal cell reference
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", "", 0, 0, image.Config{}, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
opts := &PictureOptions{PrintObject: boolPtr(true), Locked: boolPtr(false), XScale: float64Ptr(defaultPictureScale), YScale: float64Ptr(defaultPictureScale)}
|
||||||
|
assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", "", 0, 0, image.Config{}, opts), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
|
|
||||||
path := "xl/drawings/drawing1.xml"
|
path := "xl/drawings/drawing1.xml"
|
||||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.addDrawingPicture("sheet1", path, "A1", "", "", 0, 0, image.Config{}, &pictureOptions{}), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.addDrawingPicture("sheet1", path, "A1", "", "", 0, 0, image.Config{}, opts), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPictureFromBytes(t *testing.T) {
|
func TestAddPictureFromBytes(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
imgFile, err := os.ReadFile("logo.png")
|
imgFile, err := os.ReadFile("logo.png")
|
||||||
assert.NoError(t, err, "Unable to load logo for test")
|
assert.NoError(t, err, "Unable to load logo for test")
|
||||||
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), "", "logo", ".png", imgFile))
|
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), "logo", ".png", imgFile, nil))
|
||||||
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), "", "logo", ".png", imgFile))
|
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), "logo", ".png", imgFile, nil))
|
||||||
imageCount := 0
|
imageCount := 0
|
||||||
f.Pkg.Range(func(fileName, v interface{}) bool {
|
f.Pkg.Range(func(fileName, v interface{}) bool {
|
||||||
if strings.Contains(fileName.(string), "media/image") {
|
if strings.Contains(fileName.(string), "media/image") {
|
||||||
|
@ -217,16 +220,16 @@ func TestAddPictureFromBytes(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.")
|
assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.")
|
||||||
assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), "", "logo", ".png", imgFile), "sheet SheetN does not exist")
|
assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), "logo", ".png", imgFile, nil), "sheet SheetN does not exist")
|
||||||
// Test add picture from bytes with invalid sheet name
|
// Test add picture from bytes with invalid sheet name
|
||||||
assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", fmt.Sprint("A", 1), "", "logo", ".png", imgFile), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", fmt.Sprint("A", 1), "logo", ".png", imgFile, nil), ErrSheetNameInvalid.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeletePicture(t *testing.T) {
|
func TestDeletePicture(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)
|
||||||
assert.NoError(t, f.DeletePicture("Sheet1", "A1"))
|
assert.NoError(t, f.DeletePicture("Sheet1", "A1"))
|
||||||
assert.NoError(t, f.AddPicture("Sheet1", "P1", filepath.Join("test", "images", "excel.jpg"), ""))
|
assert.NoError(t, f.AddPicture("Sheet1", "P1", filepath.Join("test", "images", "excel.jpg"), nil))
|
||||||
assert.NoError(t, f.DeletePicture("Sheet1", "P1"))
|
assert.NoError(t, f.DeletePicture("Sheet1", "P1"))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture.xlsx")))
|
||||||
// Test delete picture on not exists worksheet
|
// Test delete picture on not exists worksheet
|
||||||
|
@ -251,7 +254,7 @@ func TestDrawingResize(t *testing.T) {
|
||||||
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
|
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
|
ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
|
||||||
assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), &PictureOptions{AutoFit: true}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetContentTypePartImageExtensions(t *testing.T) {
|
func TestSetContentTypePartImageExtensions(t *testing.T) {
|
||||||
|
|
|
@ -27,26 +27,26 @@ import (
|
||||||
// PivotStyleDark1 - PivotStyleDark28
|
// PivotStyleDark1 - PivotStyleDark28
|
||||||
type PivotTableOptions struct {
|
type PivotTableOptions struct {
|
||||||
pivotTableSheetName string
|
pivotTableSheetName string
|
||||||
DataRange string `json:"data_range"`
|
DataRange string
|
||||||
PivotTableRange string `json:"pivot_table_range"`
|
PivotTableRange string
|
||||||
Rows []PivotTableField `json:"rows"`
|
Rows []PivotTableField
|
||||||
Columns []PivotTableField `json:"columns"`
|
Columns []PivotTableField
|
||||||
Data []PivotTableField `json:"data"`
|
Data []PivotTableField
|
||||||
Filter []PivotTableField `json:"filter"`
|
Filter []PivotTableField
|
||||||
RowGrandTotals bool `json:"row_grand_totals"`
|
RowGrandTotals bool
|
||||||
ColGrandTotals bool `json:"col_grand_totals"`
|
ColGrandTotals bool
|
||||||
ShowDrill bool `json:"show_drill"`
|
ShowDrill bool
|
||||||
UseAutoFormatting bool `json:"use_auto_formatting"`
|
UseAutoFormatting bool
|
||||||
PageOverThenDown bool `json:"page_over_then_down"`
|
PageOverThenDown bool
|
||||||
MergeItem bool `json:"merge_item"`
|
MergeItem bool
|
||||||
CompactData bool `json:"compact_data"`
|
CompactData bool
|
||||||
ShowError bool `json:"show_error"`
|
ShowError bool
|
||||||
ShowRowHeaders bool `json:"show_row_headers"`
|
ShowRowHeaders bool
|
||||||
ShowColHeaders bool `json:"show_col_headers"`
|
ShowColHeaders bool
|
||||||
ShowRowStripes bool `json:"show_row_stripes"`
|
ShowRowStripes bool
|
||||||
ShowColStripes bool `json:"show_col_stripes"`
|
ShowColStripes bool
|
||||||
ShowLastColumn bool `json:"show_last_column"`
|
ShowLastColumn bool
|
||||||
PivotTableStyleName string `json:"pivot_table_style_name"`
|
PivotTableStyleName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PivotTableField directly maps the field settings of the pivot table.
|
// PivotTableField directly maps the field settings of the pivot table.
|
||||||
|
@ -69,12 +69,12 @@ type PivotTableOptions struct {
|
||||||
// Name specifies the name of the data field. Maximum 255 characters
|
// Name specifies the name of the data field. Maximum 255 characters
|
||||||
// are allowed in data field name, excess characters will be truncated.
|
// are allowed in data field name, excess characters will be truncated.
|
||||||
type PivotTableField struct {
|
type PivotTableField struct {
|
||||||
Compact bool `json:"compact"`
|
Compact bool
|
||||||
Data string `json:"data"`
|
Data string
|
||||||
Name string `json:"name"`
|
Name string
|
||||||
Outline bool `json:"outline"`
|
Outline bool
|
||||||
Subtotal string `json:"subtotal"`
|
Subtotal string
|
||||||
DefaultSubtotal bool `json:"default_subtotal"`
|
DefaultSubtotal bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPivotTable provides the method to add pivot table by given pivot table
|
// AddPivotTable provides the method to add pivot table by given pivot table
|
||||||
|
|
|
@ -109,7 +109,8 @@ func TestAddPivotTable(t *testing.T) {
|
||||||
ShowLastColumn: true,
|
ShowLastColumn: true,
|
||||||
PivotTableStyleName: "PivotStyleLight19",
|
PivotTableStyleName: "PivotStyleLight19",
|
||||||
}))
|
}))
|
||||||
f.NewSheet("Sheet2")
|
_, err := f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
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: "Sheet2!$A$1:$AR$15",
|
PivotTableRange: "Sheet2!$A$1:$AR$15",
|
||||||
|
@ -232,7 +233,7 @@ func TestAddPivotTable(t *testing.T) {
|
||||||
Rows: []PivotTableField{{Data: "Year"}},
|
Rows: []PivotTableField{{Data: "Year"}},
|
||||||
}), ErrSheetNameInvalid.Error())
|
}), ErrSheetNameInvalid.Error())
|
||||||
// Test adjust range with invalid range
|
// Test adjust range with invalid range
|
||||||
_, _, err := f.adjustRange("")
|
_, _, err = f.adjustRange("")
|
||||||
assert.EqualError(t, err, ErrParameterRequired.Error())
|
assert.EqualError(t, err, ErrParameterRequired.Error())
|
||||||
// Test adjust range with incorrect range
|
// Test adjust range with incorrect range
|
||||||
_, _, err = f.adjustRange("sheet1!")
|
_, _, err = f.adjustRange("sheet1!")
|
||||||
|
|
16
rows_test.go
16
rows_test.go
|
@ -189,7 +189,8 @@ func TestRowHeight(t *testing.T) {
|
||||||
// Test set row height with custom default row height with prepare XML
|
// Test set row height with custom default row height with prepare XML
|
||||||
assert.NoError(t, f.SetCellValue(sheet1, "A10", "A10"))
|
assert.NoError(t, f.SetCellValue(sheet1, "A10", "A10"))
|
||||||
|
|
||||||
f.NewSheet("Sheet2")
|
_, err = f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetCellValue("Sheet2", "A2", true))
|
assert.NoError(t, f.SetCellValue("Sheet2", "A2", true))
|
||||||
height, err = f.GetRowHeight("Sheet2", 1)
|
height, err = f.GetRowHeight("Sheet2", 1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -258,10 +259,9 @@ func TestSharedStringsReader(t *testing.T) {
|
||||||
|
|
||||||
func TestRowVisibility(t *testing.T) {
|
func TestRowVisibility(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
_, err = f.NewSheet("Sheet3")
|
||||||
}
|
assert.NoError(t, err)
|
||||||
f.NewSheet("Sheet3")
|
|
||||||
assert.NoError(t, f.SetRowVisible("Sheet3", 2, false))
|
assert.NoError(t, f.SetRowVisible("Sheet3", 2, false))
|
||||||
assert.NoError(t, f.SetRowVisible("Sheet3", 2, true))
|
assert.NoError(t, f.SetRowVisible("Sheet3", 2, true))
|
||||||
visible, err := f.GetRowVisible("Sheet3", 2)
|
visible, err := f.GetRowVisible("Sheet3", 2)
|
||||||
|
@ -320,7 +320,7 @@ func TestRemoveRow(t *testing.T) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = f.AutoFilter(sheet1, "A2", "A2", `{"column":"A","expression":"x != blanks"}`)
|
err = f.AutoFilter(sheet1, "A2:A2", &AutoFilterOptions{Column: "A", Expression: "x != blanks"})
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
@ -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(`{"fill":{"type":"pattern","color":["#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(`{"fill":{"type":"pattern","color":["#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())
|
||||||
|
|
104
shape.go
104
shape.go
|
@ -12,26 +12,38 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseShapeOptions provides a function to parse the format settings of the
|
// parseShapeOptions provides a function to parse the format settings of the
|
||||||
// shape with default value.
|
// shape with default value.
|
||||||
func parseShapeOptions(opts string) (*shapeOptions, error) {
|
func parseShapeOptions(opts *Shape) (*Shape, error) {
|
||||||
options := shapeOptions{
|
if opts == nil {
|
||||||
Width: 160,
|
return nil, ErrParameterInvalid
|
||||||
Height: 160,
|
|
||||||
Format: pictureOptions{
|
|
||||||
FPrintsWithSheet: true,
|
|
||||||
XScale: 1,
|
|
||||||
YScale: 1,
|
|
||||||
},
|
|
||||||
Line: lineOptions{Width: 1},
|
|
||||||
}
|
}
|
||||||
err := json.Unmarshal([]byte(opts), &options)
|
if opts.Width == nil {
|
||||||
return &options, err
|
opts.Width = intPtr(defaultShapeSize)
|
||||||
|
}
|
||||||
|
if opts.Height == nil {
|
||||||
|
opts.Height = intPtr(defaultShapeSize)
|
||||||
|
}
|
||||||
|
if opts.Format.PrintObject == nil {
|
||||||
|
opts.Format.PrintObject = boolPtr(true)
|
||||||
|
}
|
||||||
|
if opts.Format.Locked == nil {
|
||||||
|
opts.Format.Locked = boolPtr(false)
|
||||||
|
}
|
||||||
|
if opts.Format.XScale == nil {
|
||||||
|
opts.Format.XScale = float64Ptr(defaultPictureScale)
|
||||||
|
}
|
||||||
|
if opts.Format.YScale == nil {
|
||||||
|
opts.Format.YScale = float64Ptr(defaultPictureScale)
|
||||||
|
}
|
||||||
|
if opts.Line.Width == nil {
|
||||||
|
opts.Line.Width = float64Ptr(defaultShapeLineWidth)
|
||||||
|
}
|
||||||
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddShape provides the method to add shape in a sheet by given worksheet
|
// AddShape provides the method to add shape in a sheet by given worksheet
|
||||||
|
@ -39,33 +51,29 @@ func parseShapeOptions(opts string) (*shapeOptions, error) {
|
||||||
// print settings) and properties set. For example, add text box (rect shape)
|
// print settings) and properties set. For example, add text box (rect shape)
|
||||||
// in Sheet1:
|
// in Sheet1:
|
||||||
//
|
//
|
||||||
// err := f.AddShape("Sheet1", "G6", `{
|
// width, height, lineWidth := 180, 90, 1.2
|
||||||
// "type": "rect",
|
// err := f.AddShape("Sheet1", "G6",
|
||||||
// "color":
|
// &excelize.Shape{
|
||||||
// {
|
// Type: "rect",
|
||||||
// "line": "#4286F4",
|
// Color: excelize.ShapeColor{Line: "#4286f4", Fill: "#8eb9ff"},
|
||||||
// "fill": "#8eb9ff"
|
// Paragraph: []excelize.ShapeParagraph{
|
||||||
|
// {
|
||||||
|
// Text: "Rectangle Shape",
|
||||||
|
// Font: excelize.Font{
|
||||||
|
// Bold: true,
|
||||||
|
// Italic: true,
|
||||||
|
// Family: "Times New Roman",
|
||||||
|
// Size: 36,
|
||||||
|
// Color: "#777777",
|
||||||
|
// Underline: "sng",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Width: &width,
|
||||||
|
// Height: &height,
|
||||||
|
// Line: excelize.ShapeLine{Width: &lineWidth},
|
||||||
// },
|
// },
|
||||||
// "paragraph": [
|
// )
|
||||||
// {
|
|
||||||
// "text": "Rectangle Shape",
|
|
||||||
// "font":
|
|
||||||
// {
|
|
||||||
// "bold": true,
|
|
||||||
// "italic": true,
|
|
||||||
// "family": "Times New Roman",
|
|
||||||
// "size": 36,
|
|
||||||
// "color": "#777777",
|
|
||||||
// "underline": "sng"
|
|
||||||
// }
|
|
||||||
// }],
|
|
||||||
// "width": 180,
|
|
||||||
// "height": 90,
|
|
||||||
// "line":
|
|
||||||
// {
|
|
||||||
// "width": 1.2
|
|
||||||
// }
|
|
||||||
// }`)
|
|
||||||
//
|
//
|
||||||
// The following shows the type of shape supported by excelize:
|
// The following shows the type of shape supported by excelize:
|
||||||
//
|
//
|
||||||
|
@ -277,7 +285,7 @@ func parseShapeOptions(opts string) (*shapeOptions, error) {
|
||||||
// wavy
|
// wavy
|
||||||
// wavyHeavy
|
// wavyHeavy
|
||||||
// wavyDbl
|
// wavyDbl
|
||||||
func (f *File) AddShape(sheet, cell, opts string) error {
|
func (f *File) AddShape(sheet, cell string, opts *Shape) error {
|
||||||
options, err := parseShapeOptions(opts)
|
options, err := parseShapeOptions(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -313,7 +321,7 @@ func (f *File) AddShape(sheet, cell, opts string) error {
|
||||||
|
|
||||||
// addDrawingShape provides a function to add preset geometry by given sheet,
|
// addDrawingShape provides a function to add preset geometry by given sheet,
|
||||||
// drawingXMLand format sets.
|
// drawingXMLand format sets.
|
||||||
func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOptions) error {
|
func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *Shape) error {
|
||||||
fromCol, fromRow, err := CellNameToCoordinates(cell)
|
fromCol, fromRow, err := CellNameToCoordinates(cell)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -321,8 +329,8 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOption
|
||||||
colIdx := fromCol - 1
|
colIdx := fromCol - 1
|
||||||
rowIdx := fromRow - 1
|
rowIdx := fromRow - 1
|
||||||
|
|
||||||
width := int(float64(opts.Width) * opts.Format.XScale)
|
width := int(float64(*opts.Width) * *opts.Format.XScale)
|
||||||
height := int(float64(opts.Height) * opts.Format.YScale)
|
height := int(float64(*opts.Height) * *opts.Format.YScale)
|
||||||
|
|
||||||
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, opts.Format.OffsetX, opts.Format.OffsetY,
|
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, opts.Format.OffsetX, opts.Format.OffsetY,
|
||||||
width, height)
|
width, height)
|
||||||
|
@ -381,9 +389,9 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOption
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if opts.Line.Width != 1 {
|
if *opts.Line.Width != 1 {
|
||||||
shape.SpPr.Ln = xlsxLineProperties{
|
shape.SpPr.Ln = xlsxLineProperties{
|
||||||
W: f.ptToEMUs(opts.Line.Width),
|
W: f.ptToEMUs(*opts.Line.Width),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defaultFont, err := f.GetDefaultFont()
|
defaultFont, err := f.GetDefaultFont()
|
||||||
|
@ -391,7 +399,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOption
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(opts.Paragraph) < 1 {
|
if len(opts.Paragraph) < 1 {
|
||||||
opts.Paragraph = []shapeParagraphOptions{
|
opts.Paragraph = []ShapeParagraph{
|
||||||
{
|
{
|
||||||
Font: Font{
|
Font: Font{
|
||||||
Bold: false,
|
Bold: false,
|
||||||
|
@ -443,8 +451,8 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOption
|
||||||
}
|
}
|
||||||
twoCellAnchor.Sp = &shape
|
twoCellAnchor.Sp = &shape
|
||||||
twoCellAnchor.ClientData = &xdrClientData{
|
twoCellAnchor.ClientData = &xdrClientData{
|
||||||
FLocksWithSheet: opts.Format.FLocksWithSheet,
|
FLocksWithSheet: *opts.Format.Locked,
|
||||||
FPrintsWithSheet: opts.Format.FPrintsWithSheet,
|
FPrintsWithSheet: *opts.Format.PrintObject,
|
||||||
}
|
}
|
||||||
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
||||||
f.Drawings.Store(drawingXML, content)
|
f.Drawings.Store(drawingXML, content)
|
||||||
|
|
135
shape_test.go
135
shape_test.go
|
@ -12,97 +12,88 @@ func TestAddShape(t *testing.T) {
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
shape := &Shape{
|
||||||
assert.NoError(t, f.AddShape("Sheet1", "A30", `{"type":"rect","paragraph":[{"text":"Rectangle","font":{"color":"CD5C5C"}},{"text":"Shape","font":{"bold":true,"color":"2980B9"}}]}`))
|
Type: "rect",
|
||||||
assert.NoError(t, f.AddShape("Sheet1", "B30", `{"type":"rect","paragraph":[{"text":"Rectangle"},{}]}`))
|
Paragraph: []ShapeParagraph{
|
||||||
assert.NoError(t, f.AddShape("Sheet1", "C30", `{"type":"rect","paragraph":[]}`))
|
{Text: "Rectangle", Font: Font{Color: "CD5C5C"}},
|
||||||
assert.EqualError(t, f.AddShape("Sheet3", "H1", `{
|
{Text: "Shape", Font: Font{Bold: true, Color: "2980B9"}},
|
||||||
"type": "ellipseRibbon",
|
|
||||||
"color":
|
|
||||||
{
|
|
||||||
"line": "#4286f4",
|
|
||||||
"fill": "#8eb9ff"
|
|
||||||
},
|
},
|
||||||
"paragraph": [
|
}
|
||||||
{
|
assert.NoError(t, f.AddShape("Sheet1", "A30", shape))
|
||||||
"font":
|
assert.NoError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []ShapeParagraph{{Text: "Rectangle"}, {}}}))
|
||||||
{
|
assert.NoError(t, f.AddShape("Sheet1", "C30", &Shape{Type: "rect"}))
|
||||||
"bold": true,
|
assert.EqualError(t, f.AddShape("Sheet3", "H1",
|
||||||
"italic": true,
|
&Shape{
|
||||||
"family": "Times New Roman",
|
Type: "ellipseRibbon",
|
||||||
"size": 36,
|
Color: ShapeColor{Line: "#4286f4", Fill: "#8eb9ff"},
|
||||||
"color": "#777777",
|
Paragraph: []ShapeParagraph{
|
||||||
"underline": "single"
|
{
|
||||||
}
|
Font: Font{
|
||||||
}],
|
Bold: true,
|
||||||
"height": 90
|
Italic: true,
|
||||||
}`), "sheet Sheet3 does not exist")
|
Family: "Times New Roman",
|
||||||
assert.EqualError(t, f.AddShape("Sheet3", "H1", ""), "unexpected end of JSON input")
|
Size: 36,
|
||||||
assert.EqualError(t, f.AddShape("Sheet1", "A", `{
|
Color: "#777777",
|
||||||
"type": "rect",
|
Underline: "single",
|
||||||
"paragraph": [
|
},
|
||||||
{
|
},
|
||||||
"text": "Rectangle",
|
},
|
||||||
"font":
|
|
||||||
{
|
|
||||||
"color": "CD5C5C"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
), "sheet Sheet3 does not exist")
|
||||||
"text": "Shape",
|
assert.EqualError(t, f.AddShape("Sheet3", "H1", nil), ErrParameterInvalid.Error())
|
||||||
"font":
|
assert.EqualError(t, f.AddShape("Sheet1", "A", shape), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
{
|
|
||||||
"bold": true,
|
|
||||||
"color": "2980B9"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape1.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape1.xlsx")))
|
||||||
|
|
||||||
// Test add first shape for given sheet
|
// Test add first shape for given sheet
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
assert.NoError(t, f.AddShape("Sheet1", "A1", `{
|
width, height := 1.2, 90
|
||||||
"type": "ellipseRibbon",
|
assert.NoError(t, f.AddShape("Sheet1", "A1",
|
||||||
"color":
|
&Shape{
|
||||||
{
|
Type: "ellipseRibbon",
|
||||||
"line": "#4286f4",
|
Color: ShapeColor{Line: "#4286f4", Fill: "#8eb9ff"},
|
||||||
"fill": "#8eb9ff"
|
Paragraph: []ShapeParagraph{
|
||||||
},
|
{
|
||||||
"paragraph": [
|
Font: Font{
|
||||||
{
|
Bold: true,
|
||||||
"font":
|
Italic: true,
|
||||||
{
|
Family: "Times New Roman",
|
||||||
"bold": true,
|
Size: 36,
|
||||||
"italic": true,
|
Color: "#777777",
|
||||||
"family": "Times New Roman",
|
Underline: "single",
|
||||||
"size": 36,
|
},
|
||||||
"color": "#777777",
|
},
|
||||||
"underline": "single"
|
},
|
||||||
}
|
Height: &height,
|
||||||
}],
|
Line: ShapeLine{Width: &width},
|
||||||
"height": 90,
|
}))
|
||||||
"line":
|
|
||||||
{
|
|
||||||
"width": 1.2
|
|
||||||
}
|
|
||||||
}`))
|
|
||||||
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
|
||||||
assert.EqualError(t, f.AddShape("Sheet:1", "A30", `{"type":"rect","paragraph":[{"text":"Rectangle","font":{"color":"CD5C5C"}},{"text":"Shape","font":{"bold":true,"color":"2980B9"}}]}`), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AddShape("Sheet:1", "A30", shape), ErrSheetNameInvalid.Error())
|
||||||
// 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", `{"type":"rect","paragraph":[{"text":"Rectangle"},{}]}`), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []ShapeParagraph{{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", `{"type":"rect","paragraph":[{"text":"Rectangle"},{}]}`), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AddShape("Sheet1", "B30", &Shape{Type: "rect", Paragraph: []ShapeParagraph{{Text: "Rectangle"}, {}}}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddDrawingShape(t *testing.T) {
|
func TestAddDrawingShape(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
path := "xl/drawings/drawing1.xml"
|
path := "xl/drawings/drawing1.xml"
|
||||||
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
f.Pkg.Store(path, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.addDrawingShape("sheet1", path, "A1", &shapeOptions{}), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.addDrawingShape("sheet1", path, "A1",
|
||||||
|
&Shape{
|
||||||
|
Width: intPtr(defaultShapeSize),
|
||||||
|
Height: intPtr(defaultShapeSize),
|
||||||
|
Format: PictureOptions{
|
||||||
|
PrintObject: boolPtr(true),
|
||||||
|
Locked: boolPtr(false),
|
||||||
|
XScale: float64Ptr(defaultPictureScale),
|
||||||
|
YScale: float64Ptr(defaultPictureScale),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
203
sheet.go
203
sheet.go
|
@ -13,7 +13,6 @@ package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -45,7 +44,7 @@ func (f *File) NewSheet(sheet string) (int, error) {
|
||||||
if index != -1 {
|
if index != -1 {
|
||||||
return index, err
|
return index, err
|
||||||
}
|
}
|
||||||
f.DeleteSheet(sheet)
|
_ = f.DeleteSheet(sheet)
|
||||||
f.SheetCount++
|
f.SheetCount++
|
||||||
wb, _ := f.workbookReader()
|
wb, _ := f.workbookReader()
|
||||||
sheetID := 0
|
sheetID := 0
|
||||||
|
@ -682,19 +681,24 @@ func (f *File) copySheet(from, to int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSheetVisible provides a function to set worksheet visible by given worksheet
|
// getSheetState returns sheet visible enumeration by given hidden status.
|
||||||
// name. A workbook must contain at least one visible worksheet. If the given
|
func getSheetState(visible bool, veryHidden []bool) string {
|
||||||
// worksheet has been activated, this setting will be invalidated. Sheet state
|
state := "hidden"
|
||||||
// values as defined by https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.sheetstatevalues
|
if !visible && len(veryHidden) > 0 && veryHidden[0] {
|
||||||
//
|
state = "veryHidden"
|
||||||
// visible
|
}
|
||||||
// hidden
|
return state
|
||||||
// veryHidden
|
}
|
||||||
|
|
||||||
|
// SetSheetVisible provides a function to set worksheet visible by given
|
||||||
|
// worksheet name. A workbook must contain at least one visible worksheet. If
|
||||||
|
// the given worksheet has been activated, this setting will be invalidated.
|
||||||
|
// The third optional veryHidden parameter only works when visible was false.
|
||||||
//
|
//
|
||||||
// For example, hide Sheet1:
|
// For example, hide Sheet1:
|
||||||
//
|
//
|
||||||
// err := f.SetSheetVisible("Sheet1", false)
|
// err := f.SetSheetVisible("Sheet1", false)
|
||||||
func (f *File) SetSheetVisible(sheet string, visible bool) error {
|
func (f *File) SetSheetVisible(sheet string, visible bool, veryHidden ...bool) error {
|
||||||
if err := checkSheetName(sheet); err != nil {
|
if err := checkSheetName(sheet); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -710,9 +714,9 @@ func (f *File) SetSheetVisible(sheet string, visible bool) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
count := 0
|
count, state := 0, getSheetState(visible, veryHidden)
|
||||||
for _, v := range wb.Sheets.Sheet {
|
for _, v := range wb.Sheets.Sheet {
|
||||||
if v.State != "hidden" {
|
if v.State != state {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -726,45 +730,37 @@ func (f *File) SetSheetVisible(sheet string, visible bool) error {
|
||||||
tabSelected = ws.SheetViews.SheetView[0].TabSelected
|
tabSelected = ws.SheetViews.SheetView[0].TabSelected
|
||||||
}
|
}
|
||||||
if strings.EqualFold(v.Name, sheet) && count > 1 && !tabSelected {
|
if strings.EqualFold(v.Name, sheet) && count > 1 && !tabSelected {
|
||||||
wb.Sheets.Sheet[k].State = "hidden"
|
wb.Sheets.Sheet[k].State = state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePanesOptions provides a function to parse the panes settings.
|
|
||||||
func parsePanesOptions(opts string) (*panesOptions, error) {
|
|
||||||
format := panesOptions{}
|
|
||||||
err := json.Unmarshal([]byte(opts), &format)
|
|
||||||
return &format, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPanes set create freeze panes and split panes by given options.
|
// setPanes set create freeze panes and split panes by given options.
|
||||||
func (ws *xlsxWorksheet) setPanes(panes string) error {
|
func (ws *xlsxWorksheet) setPanes(panes *Panes) error {
|
||||||
opts, err := parsePanesOptions(panes)
|
if panes == nil {
|
||||||
if err != nil {
|
return ErrParameterInvalid
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
p := &xlsxPane{
|
p := &xlsxPane{
|
||||||
ActivePane: opts.ActivePane,
|
ActivePane: panes.ActivePane,
|
||||||
TopLeftCell: opts.TopLeftCell,
|
TopLeftCell: panes.TopLeftCell,
|
||||||
XSplit: float64(opts.XSplit),
|
XSplit: float64(panes.XSplit),
|
||||||
YSplit: float64(opts.YSplit),
|
YSplit: float64(panes.YSplit),
|
||||||
}
|
}
|
||||||
if opts.Freeze {
|
if panes.Freeze {
|
||||||
p.State = "frozen"
|
p.State = "frozen"
|
||||||
}
|
}
|
||||||
if ws.SheetViews == nil {
|
if ws.SheetViews == nil {
|
||||||
ws.SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}
|
ws.SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}
|
||||||
}
|
}
|
||||||
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = p
|
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = p
|
||||||
if !(opts.Freeze) && !(opts.Split) {
|
if !(panes.Freeze) && !(panes.Split) {
|
||||||
if len(ws.SheetViews.SheetView) > 0 {
|
if len(ws.SheetViews.SheetView) > 0 {
|
||||||
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = nil
|
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Pane = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var s []*xlsxSelection
|
var s []*xlsxSelection
|
||||||
for _, p := range opts.Panes {
|
for _, p := range panes.Panes {
|
||||||
s = append(s, &xlsxSelection{
|
s = append(s, &xlsxSelection{
|
||||||
ActiveCell: p.ActiveCell,
|
ActiveCell: p.ActiveCell,
|
||||||
Pane: p.Pane,
|
Pane: p.Pane,
|
||||||
|
@ -772,94 +768,128 @@ func (ws *xlsxWorksheet) setPanes(panes string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Selection = s
|
ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1].Selection = s
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPanes provides a function to create and remove freeze panes and split panes
|
// SetPanes provides a function to create and remove freeze panes and split panes
|
||||||
// by given worksheet name and panes options.
|
// by given worksheet name and panes options.
|
||||||
//
|
//
|
||||||
// activePane defines the pane that is active. The possible values for this
|
// ActivePane defines the pane that is active. The possible values for this
|
||||||
// attribute are defined in the following table:
|
// attribute are defined in the following table:
|
||||||
//
|
//
|
||||||
// Enumeration Value | Description
|
// Enumeration Value | Description
|
||||||
// --------------------------------+-------------------------------------------------------------
|
// ---------------------------------+-------------------------------------------------------------
|
||||||
// bottomLeft (Bottom Left Pane) | Bottom left pane, when both vertical and horizontal
|
// bottomLeft (Bottom Left Pane) | Bottom left pane, when both vertical and horizontal
|
||||||
// | splits are applied.
|
// | splits are applied.
|
||||||
// |
|
// |
|
||||||
// | This value is also used when only a horizontal split has
|
// | This value is also used when only a horizontal split has
|
||||||
// | been applied, dividing the pane into upper and lower
|
// | been applied, dividing the pane into upper and lower
|
||||||
// | regions. In that case, this value specifies the bottom
|
// | regions. In that case, this value specifies the bottom
|
||||||
// | pane.
|
// | pane.
|
||||||
// |
|
// |
|
||||||
// bottomRight (Bottom Right Pane) | Bottom right pane, when both vertical and horizontal
|
// bottomRight (Bottom Right Pane) | Bottom right pane, when both vertical and horizontal
|
||||||
// | splits are applied.
|
// | splits are applied.
|
||||||
// |
|
// |
|
||||||
// topLeft (Top Left Pane) | Top left pane, when both vertical and horizontal splits
|
// topLeft (Top Left Pane) | Top left pane, when both vertical and horizontal splits
|
||||||
// | are applied.
|
// | are applied.
|
||||||
// |
|
// |
|
||||||
// | This value is also used when only a horizontal split has
|
// | This value is also used when only a horizontal split has
|
||||||
// | been applied, dividing the pane into upper and lower
|
// | been applied, dividing the pane into upper and lower
|
||||||
// | regions. In that case, this value specifies the top pane.
|
// | regions. In that case, this value specifies the top pane.
|
||||||
// |
|
// |
|
||||||
// | This value is also used when only a vertical split has
|
// | This value is also used when only a vertical split has
|
||||||
// | been applied, dividing the pane into right and left
|
// | been applied, dividing the pane into right and left
|
||||||
// | regions. In that case, this value specifies the left pane
|
// | regions. In that case, this value specifies the left pane
|
||||||
// |
|
// |
|
||||||
// topRight (Top Right Pane) | Top right pane, when both vertical and horizontal
|
// topRight (Top Right Pane) | Top right pane, when both vertical and horizontal
|
||||||
// | splits are applied.
|
// | splits are applied.
|
||||||
// |
|
// |
|
||||||
// | This value is also used when only a vertical split has
|
// | This value is also used when only a vertical split has
|
||||||
// | been applied, dividing the pane into right and left
|
// | been applied, dividing the pane into right and left
|
||||||
// | regions. In that case, this value specifies the right
|
// | regions. In that case, this value specifies the right
|
||||||
// | pane.
|
// | pane.
|
||||||
//
|
//
|
||||||
// Pane state type is restricted to the values supported currently listed in the following table:
|
// Pane state type is restricted to the values supported currently listed in the following table:
|
||||||
//
|
//
|
||||||
// Enumeration Value | Description
|
// Enumeration Value | Description
|
||||||
// --------------------------------+-------------------------------------------------------------
|
// ---------------------------------+-------------------------------------------------------------
|
||||||
// frozen (Frozen) | Panes are frozen, but were not split being frozen. In
|
// frozen (Frozen) | Panes are frozen, but were not split being frozen. In
|
||||||
// | this state, when the panes are unfrozen again, a single
|
// | this state, when the panes are unfrozen again, a single
|
||||||
// | pane results, with no split.
|
// | pane results, with no split.
|
||||||
// |
|
// |
|
||||||
// | In this state, the split bars are not adjustable.
|
// | In this state, the split bars are not adjustable.
|
||||||
// |
|
// |
|
||||||
// split (Split) | Panes are split, but not frozen. In this state, the split
|
// split (Split) | Panes are split, but not frozen. In this state, the split
|
||||||
// | bars are adjustable by the user.
|
// | bars are adjustable by the user.
|
||||||
//
|
//
|
||||||
// x_split (Horizontal Split Position): Horizontal position of the split, in
|
// XSplit (Horizontal Split Position): Horizontal position of the split, in
|
||||||
// 1/20th of a point; 0 (zero) if none. If the pane is frozen, this value
|
// 1/20th of a point; 0 (zero) if none. If the pane is frozen, this value
|
||||||
// indicates the number of columns visible in the top pane.
|
// indicates the number of columns visible in the top pane.
|
||||||
//
|
//
|
||||||
// y_split (Vertical Split Position): Vertical position of the split, in 1/20th
|
// YSplit (Vertical Split Position): Vertical position of the split, in 1/20th
|
||||||
// of a point; 0 (zero) if none. If the pane is frozen, this value indicates the
|
// of a point; 0 (zero) if none. If the pane is frozen, this value indicates the
|
||||||
// number of rows visible in the left pane. The possible values for this
|
// number of rows visible in the left pane. The possible values for this
|
||||||
// attribute are defined by the W3C XML Schema double datatype.
|
// attribute are defined by the W3C XML Schema double datatype.
|
||||||
//
|
//
|
||||||
// top_left_cell: Location of the top left visible cell in the bottom right pane
|
// TopLeftCell: Location of the top left visible cell in the bottom right pane
|
||||||
// (when in Left-To-Right mode).
|
// (when in Left-To-Right mode).
|
||||||
//
|
//
|
||||||
// sqref (Sequence of References): Range of the selection. Can be non-contiguous
|
// SQRef (Sequence of References): Range of the selection. Can be non-contiguous
|
||||||
// set of ranges.
|
// set of ranges.
|
||||||
//
|
//
|
||||||
// An example of how to freeze column A in the Sheet1 and set the active cell on
|
// An example of how to freeze column A in the Sheet1 and set the active cell on
|
||||||
// Sheet1!K16:
|
// Sheet1!K16:
|
||||||
//
|
//
|
||||||
// f.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`)
|
// err := f.SetPanes("Sheet1", &excelize.Panes{
|
||||||
|
// Freeze: true,
|
||||||
|
// Split: false,
|
||||||
|
// XSplit: 1,
|
||||||
|
// YSplit: 0,
|
||||||
|
// TopLeftCell: "B1",
|
||||||
|
// ActivePane: "topRight",
|
||||||
|
// Panes: []excelize.PaneOptions{
|
||||||
|
// {SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
|
||||||
|
// },
|
||||||
|
// })
|
||||||
//
|
//
|
||||||
// An example of how to freeze rows 1 to 9 in the Sheet1 and set the active cell
|
// An example of how to freeze rows 1 to 9 in the Sheet1 and set the active cell
|
||||||
// ranges on Sheet1!A11:XFD11:
|
// ranges on Sheet1!A11:XFD11:
|
||||||
//
|
//
|
||||||
// f.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":0,"y_split":9,"top_left_cell":"A34","active_pane":"bottomLeft","panes":[{"sqref":"A11:XFD11","active_cell":"A11","pane":"bottomLeft"}]}`)
|
// err := f.SetPanes("Sheet1", &excelize.Panes{
|
||||||
|
// Freeze: true,
|
||||||
|
// Split: false,
|
||||||
|
// XSplit: 0,
|
||||||
|
// YSplit: 9,
|
||||||
|
// TopLeftCell: "A34",
|
||||||
|
// ActivePane: "bottomLeft",
|
||||||
|
// Panes: []excelize.PaneOptions{
|
||||||
|
// {SQRef: "A11:XFD11", ActiveCell: "A11", Pane: "bottomLeft"},
|
||||||
|
// },
|
||||||
|
// })
|
||||||
//
|
//
|
||||||
// An example of how to create split panes in the Sheet1 and set the active cell
|
// An example of how to create split panes in the Sheet1 and set the active cell
|
||||||
// on Sheet1!J60:
|
// on Sheet1!J60:
|
||||||
//
|
//
|
||||||
// f.SetPanes("Sheet1", `{"freeze":false,"split":true,"x_split":3270,"y_split":1800,"top_left_cell":"N57","active_pane":"bottomLeft","panes":[{"sqref":"I36","active_cell":"I36"},{"sqref":"G33","active_cell":"G33","pane":"topRight"},{"sqref":"J60","active_cell":"J60","pane":"bottomLeft"},{"sqref":"O60","active_cell":"O60","pane":"bottomRight"}]}`)
|
// err := f.SetPanes("Sheet1", &excelize.Panes{
|
||||||
|
// Freeze: false,
|
||||||
|
// Split: true,
|
||||||
|
// XSplit: 3270,
|
||||||
|
// YSplit: 1800,
|
||||||
|
// TopLeftCell: "N57",
|
||||||
|
// ActivePane: "bottomLeft",
|
||||||
|
// Panes: []excelize.PaneOptions{
|
||||||
|
// {SQRef: "G33", ActiveCell: "G33", Pane: "topRight"},
|
||||||
|
// {SQRef: "I36", ActiveCell: "I36"},
|
||||||
|
// {SQRef: "G33", ActiveCell: "G33", Pane: "topRight"},
|
||||||
|
// {SQRef: "J60", ActiveCell: "J60", Pane: "bottomLeft"},
|
||||||
|
// {SQRef: "O60", ActiveCell: "O60", Pane: "bottomRight"},
|
||||||
|
// },
|
||||||
|
// })
|
||||||
//
|
//
|
||||||
// An example of how to unfreeze and remove all panes on Sheet1:
|
// An example of how to unfreeze and remove all panes on Sheet1:
|
||||||
//
|
//
|
||||||
// f.SetPanes("Sheet1", `{"freeze":false,"split":false}`)
|
// err := f.SetPanes("Sheet1", &excelize.Panes{Freeze: false, Split: false})
|
||||||
func (f *File) SetPanes(sheet, panes string) error {
|
func (f *File) SetPanes(sheet string, panes *Panes) error {
|
||||||
ws, err := f.workSheetReader(sheet)
|
ws, err := f.workSheetReader(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -870,7 +900,7 @@ func (f *File) SetPanes(sheet, panes string) error {
|
||||||
// GetSheetVisible provides a function to get worksheet visible by given worksheet
|
// GetSheetVisible provides a function to get worksheet visible by given worksheet
|
||||||
// name. For example, get visible state of Sheet1:
|
// name. For example, get visible state of Sheet1:
|
||||||
//
|
//
|
||||||
// f.GetSheetVisible("Sheet1")
|
// visible, err := f.GetSheetVisible("Sheet1")
|
||||||
func (f *File) GetSheetVisible(sheet string) (bool, error) {
|
func (f *File) GetSheetVisible(sheet string) (bool, error) {
|
||||||
var visible bool
|
var visible bool
|
||||||
if err := checkSheetName(sheet); err != nil {
|
if err := checkSheetName(sheet); err != nil {
|
||||||
|
@ -1509,6 +1539,9 @@ func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
|
||||||
// Scope: "Sheet2",
|
// Scope: "Sheet2",
|
||||||
// })
|
// })
|
||||||
func (f *File) SetDefinedName(definedName *DefinedName) error {
|
func (f *File) SetDefinedName(definedName *DefinedName) error {
|
||||||
|
if definedName.Name == "" || definedName.RefersTo == "" {
|
||||||
|
return ErrParameterInvalid
|
||||||
|
}
|
||||||
wb, err := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
140
sheet_test.go
140
sheet_test.go
|
@ -15,14 +15,15 @@ import (
|
||||||
|
|
||||||
func TestNewSheet(t *testing.T) {
|
func TestNewSheet(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet2")
|
_, err := f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
sheetID, err := f.NewSheet("sheet2")
|
sheetID, err := f.NewSheet("sheet2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
f.SetActiveSheet(sheetID)
|
f.SetActiveSheet(sheetID)
|
||||||
// Test delete original sheet
|
// Test delete original sheet
|
||||||
idx, err := f.GetSheetIndex("Sheet1")
|
idx, err := f.GetSheetIndex("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
f.DeleteSheet(f.GetSheetName(idx))
|
assert.NoError(t, f.DeleteSheet(f.GetSheetName(idx)))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx")))
|
||||||
// Test create new worksheet with already exists name
|
// Test create new worksheet with already exists name
|
||||||
sheetID, err = f.NewSheet("Sheet2")
|
sheetID, err = f.NewSheet("Sheet2")
|
||||||
|
@ -38,24 +39,79 @@ func TestNewSheet(t *testing.T) {
|
||||||
|
|
||||||
func TestSetPanes(t *testing.T) {
|
func TestSetPanes(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":false,"split":false}`))
|
|
||||||
f.NewSheet("Panes 2")
|
assert.NoError(t, f.SetPanes("Sheet1", &Panes{Freeze: false, Split: false}))
|
||||||
assert.NoError(t, f.SetPanes("Panes 2", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`))
|
_, err := f.NewSheet("Panes 2")
|
||||||
f.NewSheet("Panes 3")
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetPanes("Panes 3", `{"freeze":false,"split":true,"x_split":3270,"y_split":1800,"top_left_cell":"N57","active_pane":"bottomLeft","panes":[{"sqref":"I36","active_cell":"I36"},{"sqref":"G33","active_cell":"G33","pane":"topRight"},{"sqref":"J60","active_cell":"J60","pane":"bottomLeft"},{"sqref":"O60","active_cell":"O60","pane":"bottomRight"}]}`))
|
assert.NoError(t, f.SetPanes("Panes 2",
|
||||||
f.NewSheet("Panes 4")
|
&Panes{
|
||||||
assert.NoError(t, f.SetPanes("Panes 4", `{"freeze":true,"split":false,"x_split":0,"y_split":9,"top_left_cell":"A34","active_pane":"bottomLeft","panes":[{"sqref":"A11:XFD11","active_cell":"A11","pane":"bottomLeft"}]}`))
|
Freeze: true,
|
||||||
assert.EqualError(t, f.SetPanes("Panes 4", ""), "unexpected end of JSON input")
|
Split: false,
|
||||||
assert.EqualError(t, f.SetPanes("SheetN", ""), "sheet SheetN does not exist")
|
XSplit: 1,
|
||||||
|
YSplit: 0,
|
||||||
|
TopLeftCell: "B1",
|
||||||
|
ActivePane: "topRight",
|
||||||
|
Panes: []PaneOptions{
|
||||||
|
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
_, err = f.NewSheet("Panes 3")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, f.SetPanes("Panes 3",
|
||||||
|
&Panes{
|
||||||
|
Freeze: false,
|
||||||
|
Split: true,
|
||||||
|
XSplit: 3270,
|
||||||
|
YSplit: 1800,
|
||||||
|
TopLeftCell: "N57",
|
||||||
|
ActivePane: "bottomLeft",
|
||||||
|
Panes: []PaneOptions{
|
||||||
|
{SQRef: "I36", ActiveCell: "I36"},
|
||||||
|
{SQRef: "G33", ActiveCell: "G33", Pane: "topRight"},
|
||||||
|
{SQRef: "J60", ActiveCell: "J60", Pane: "bottomLeft"},
|
||||||
|
{SQRef: "O60", ActiveCell: "O60", Pane: "bottomRight"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
_, err = f.NewSheet("Panes 4")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, f.SetPanes("Panes 4",
|
||||||
|
&Panes{
|
||||||
|
Freeze: true,
|
||||||
|
Split: false,
|
||||||
|
XSplit: 0,
|
||||||
|
YSplit: 9,
|
||||||
|
TopLeftCell: "A34",
|
||||||
|
ActivePane: "bottomLeft",
|
||||||
|
Panes: []PaneOptions{
|
||||||
|
{SQRef: "A11:XFD11", ActiveCell: "A11", Pane: "bottomLeft"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
|
assert.EqualError(t, f.SetPanes("Panes 4", nil), ErrParameterInvalid.Error())
|
||||||
|
assert.EqualError(t, f.SetPanes("SheetN", nil), "sheet SheetN does not exist")
|
||||||
// Test set panes with invalid sheet name
|
// Test set panes with invalid sheet name
|
||||||
assert.EqualError(t, f.SetPanes("Sheet:1", `{"freeze":false,"split":false}`), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.SetPanes("Sheet:1", &Panes{Freeze: false, Split: false}), ErrSheetNameInvalid.Error())
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx")))
|
||||||
// Test add pane on empty sheet views worksheet
|
// Test add pane on empty sheet views worksheet
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.checked = nil
|
f.checked = nil
|
||||||
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData/></worksheet>`))
|
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData/></worksheet>`))
|
||||||
assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`))
|
assert.NoError(t, f.SetPanes("Sheet1",
|
||||||
|
&Panes{
|
||||||
|
Freeze: true,
|
||||||
|
Split: false,
|
||||||
|
XSplit: 1,
|
||||||
|
YSplit: 0,
|
||||||
|
TopLeftCell: "B1",
|
||||||
|
ActivePane: "topRight",
|
||||||
|
Panes: []PaneOptions{
|
||||||
|
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchSheet(t *testing.T) {
|
func TestSearchSheet(t *testing.T) {
|
||||||
|
@ -108,7 +164,7 @@ func TestSearchSheet(t *testing.T) {
|
||||||
assert.EqualError(t, err, "invalid cell reference [1, 0]")
|
assert.EqualError(t, err, "invalid cell reference [1, 0]")
|
||||||
assert.Equal(t, []string(nil), result)
|
assert.Equal(t, []string(nil), result)
|
||||||
|
|
||||||
// Test search sheet with unsupported charset shared strings table.
|
// Test search sheet with unsupported charset shared strings table
|
||||||
f.SharedStrings = nil
|
f.SharedStrings = nil
|
||||||
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
|
||||||
_, err = f.SearchSheet("Sheet1", "A")
|
_, err = f.SearchSheet("Sheet1", "A")
|
||||||
|
@ -204,6 +260,14 @@ func TestDefinedName(t *testing.T) {
|
||||||
assert.EqualError(t, f.DeleteDefinedName(&DefinedName{
|
assert.EqualError(t, f.DeleteDefinedName(&DefinedName{
|
||||||
Name: "No Exist Defined Name",
|
Name: "No Exist Defined Name",
|
||||||
}), ErrDefinedNameScope.Error())
|
}), ErrDefinedNameScope.Error())
|
||||||
|
// Test set defined name without name
|
||||||
|
assert.EqualError(t, f.SetDefinedName(&DefinedName{
|
||||||
|
RefersTo: "Sheet1!$A$2:$D$5",
|
||||||
|
}), ErrParameterInvalid.Error())
|
||||||
|
// Test set defined name without reference
|
||||||
|
assert.EqualError(t, f.SetDefinedName(&DefinedName{
|
||||||
|
Name: "Amount",
|
||||||
|
}), ErrParameterInvalid.Error())
|
||||||
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[1].RefersTo)
|
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[1].RefersTo)
|
||||||
assert.NoError(t, f.DeleteDefinedName(&DefinedName{
|
assert.NoError(t, f.DeleteDefinedName(&DefinedName{
|
||||||
Name: "Amount",
|
Name: "Amount",
|
||||||
|
@ -228,7 +292,8 @@ func TestGroupSheets(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
sheets := []string{"Sheet2", "Sheet3"}
|
sheets := []string{"Sheet2", "Sheet3"}
|
||||||
for _, sheet := range sheets {
|
for _, sheet := range sheets {
|
||||||
f.NewSheet(sheet)
|
_, err := f.NewSheet(sheet)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
assert.EqualError(t, f.GroupSheets([]string{"Sheet1", "SheetN"}), "sheet SheetN does not exist")
|
assert.EqualError(t, f.GroupSheets([]string{"Sheet1", "SheetN"}), "sheet SheetN does not exist")
|
||||||
assert.EqualError(t, f.GroupSheets([]string{"Sheet2", "Sheet3"}), "group worksheet must contain an active worksheet")
|
assert.EqualError(t, f.GroupSheets([]string{"Sheet2", "Sheet3"}), "group worksheet must contain an active worksheet")
|
||||||
|
@ -242,7 +307,8 @@ func TestUngroupSheets(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
sheets := []string{"Sheet2", "Sheet3", "Sheet4", "Sheet5"}
|
sheets := []string{"Sheet2", "Sheet3", "Sheet4", "Sheet5"}
|
||||||
for _, sheet := range sheets {
|
for _, sheet := range sheets {
|
||||||
f.NewSheet(sheet)
|
_, err := f.NewSheet(sheet)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
assert.NoError(t, f.UngroupSheets())
|
assert.NoError(t, f.UngroupSheets())
|
||||||
}
|
}
|
||||||
|
@ -276,7 +342,8 @@ func TestRemovePageBreak(t *testing.T) {
|
||||||
assert.NoError(t, f.RemovePageBreak("Sheet1", "B3"))
|
assert.NoError(t, f.RemovePageBreak("Sheet1", "B3"))
|
||||||
assert.NoError(t, f.RemovePageBreak("Sheet1", "A3"))
|
assert.NoError(t, f.RemovePageBreak("Sheet1", "A3"))
|
||||||
|
|
||||||
f.NewSheet("Sheet2")
|
_, err := f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.InsertPageBreak("Sheet2", "B2"))
|
assert.NoError(t, f.InsertPageBreak("Sheet2", "B2"))
|
||||||
assert.NoError(t, f.InsertPageBreak("Sheet2", "C2"))
|
assert.NoError(t, f.InsertPageBreak("Sheet2", "C2"))
|
||||||
assert.NoError(t, f.RemovePageBreak("Sheet2", "B2"))
|
assert.NoError(t, f.RemovePageBreak("Sheet2", "B2"))
|
||||||
|
@ -381,20 +448,23 @@ func TestDeleteSheet(t *testing.T) {
|
||||||
idx, err := f.NewSheet("Sheet2")
|
idx, err := f.NewSheet("Sheet2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
f.SetActiveSheet(idx)
|
f.SetActiveSheet(idx)
|
||||||
f.NewSheet("Sheet3")
|
_, err = f.NewSheet("Sheet3")
|
||||||
f.DeleteSheet("Sheet1")
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, f.DeleteSheet("Sheet1"))
|
||||||
assert.Equal(t, "Sheet2", f.GetSheetName(f.GetActiveSheetIndex()))
|
assert.Equal(t, "Sheet2", f.GetSheetName(f.GetActiveSheetIndex()))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet.xlsx")))
|
||||||
// Test with auto filter defined names
|
// Test with auto filter defined names
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.NewSheet("Sheet2")
|
_, err = f.NewSheet("Sheet2")
|
||||||
f.NewSheet("Sheet3")
|
assert.NoError(t, err)
|
||||||
|
_, err = f.NewSheet("Sheet3")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "A1", "A"))
|
assert.NoError(t, f.SetCellValue("Sheet1", "A1", "A"))
|
||||||
assert.NoError(t, f.SetCellValue("Sheet2", "A1", "A"))
|
assert.NoError(t, f.SetCellValue("Sheet2", "A1", "A"))
|
||||||
assert.NoError(t, f.SetCellValue("Sheet3", "A1", "A"))
|
assert.NoError(t, f.SetCellValue("Sheet3", "A1", "A"))
|
||||||
assert.NoError(t, f.AutoFilter("Sheet1", "A1", "A1", ""))
|
assert.NoError(t, f.AutoFilter("Sheet1", "A1:A1", nil))
|
||||||
assert.NoError(t, f.AutoFilter("Sheet2", "A1", "A1", ""))
|
assert.NoError(t, f.AutoFilter("Sheet2", "A1:A1", nil))
|
||||||
assert.NoError(t, f.AutoFilter("Sheet3", "A1", "A1", ""))
|
assert.NoError(t, f.AutoFilter("Sheet3", "A1:A1", nil))
|
||||||
assert.NoError(t, f.DeleteSheet("Sheet2"))
|
assert.NoError(t, f.DeleteSheet("Sheet2"))
|
||||||
assert.NoError(t, f.DeleteSheet("Sheet1"))
|
assert.NoError(t, f.DeleteSheet("Sheet1"))
|
||||||
// Test delete sheet with invalid sheet name
|
// Test delete sheet with invalid sheet name
|
||||||
|
@ -408,9 +478,10 @@ func TestDeleteAndAdjustDefinedNames(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSheetID(t *testing.T) {
|
func TestGetSheetID(t *testing.T) {
|
||||||
file := NewFile()
|
f := NewFile()
|
||||||
file.NewSheet("Sheet1")
|
_, err := f.NewSheet("Sheet1")
|
||||||
id := file.getSheetID("sheet1")
|
assert.NoError(t, err)
|
||||||
|
id := f.getSheetID("sheet1")
|
||||||
assert.NotEqual(t, -1, id)
|
assert.NotEqual(t, -1, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +515,7 @@ func TestGetSheetIndex(t *testing.T) {
|
||||||
|
|
||||||
func TestSetContentTypes(t *testing.T) {
|
func TestSetContentTypes(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test set content type with unsupported charset content types.
|
// Test set content type with unsupported charset content types
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.setContentTypes("/xl/worksheets/sheet1.xml", ContentTypeSpreadSheetMLWorksheet), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.setContentTypes("/xl/worksheets/sheet1.xml", ContentTypeSpreadSheetMLWorksheet), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
@ -452,7 +523,7 @@ func TestSetContentTypes(t *testing.T) {
|
||||||
|
|
||||||
func TestDeleteSheetFromContentTypes(t *testing.T) {
|
func TestDeleteSheetFromContentTypes(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test delete sheet from content types with unsupported charset content types.
|
// Test delete sheet from content types with unsupported charset content types
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.deleteSheetFromContentTypes("/xl/worksheets/sheet1.xml"), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.deleteSheetFromContentTypes("/xl/worksheets/sheet1.xml"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
@ -468,9 +539,8 @@ func BenchmarkNewSheet(b *testing.B) {
|
||||||
|
|
||||||
func newSheetWithSet() {
|
func newSheetWithSet() {
|
||||||
file := NewFile()
|
file := NewFile()
|
||||||
file.NewSheet("sheet1")
|
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
_ = file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i)
|
_ = file.SetCellInt("Sheet1", "A"+strconv.Itoa(i+1), i)
|
||||||
}
|
}
|
||||||
file = nil
|
file = nil
|
||||||
}
|
}
|
||||||
|
@ -485,9 +555,8 @@ func BenchmarkFile_SaveAs(b *testing.B) {
|
||||||
|
|
||||||
func newSheetWithSave() {
|
func newSheetWithSave() {
|
||||||
file := NewFile()
|
file := NewFile()
|
||||||
file.NewSheet("sheet1")
|
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
_ = file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i)
|
_ = file.SetCellInt("Sheet1", "A"+strconv.Itoa(i+1), i)
|
||||||
}
|
}
|
||||||
_ = file.Save()
|
_ = file.Save()
|
||||||
}
|
}
|
||||||
|
@ -520,12 +589,13 @@ func TestAttrValToFloat(t *testing.T) {
|
||||||
|
|
||||||
func TestSetSheetBackgroundFromBytes(t *testing.T) {
|
func TestSetSheetBackgroundFromBytes(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.SetSheetName("Sheet1", ".svg")
|
assert.NoError(t, f.SetSheetName("Sheet1", ".svg"))
|
||||||
for i, imageTypes := range []string{".svg", ".emf", ".emz", ".gif", ".jpg", ".png", ".tif", ".wmf", ".wmz"} {
|
for i, imageTypes := range []string{".svg", ".emf", ".emz", ".gif", ".jpg", ".png", ".tif", ".wmf", ".wmz"} {
|
||||||
file := fmt.Sprintf("excelize%s", imageTypes)
|
file := fmt.Sprintf("excelize%s", imageTypes)
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
file = filepath.Join("test", "images", fmt.Sprintf("excel%s", imageTypes))
|
file = filepath.Join("test", "images", fmt.Sprintf("excel%s", imageTypes))
|
||||||
f.NewSheet(imageTypes)
|
_, err := f.NewSheet(imageTypes)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
img, err := os.Open(file)
|
img, err := os.Open(file)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -28,10 +28,10 @@ func TestSetView(t *testing.T) {
|
||||||
opts, err := f.GetSheetView("Sheet1", 0)
|
opts, err := f.GetSheetView("Sheet1", 0)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expected, opts)
|
assert.Equal(t, expected, opts)
|
||||||
// Test set sheet view options with invalid view index.
|
// Test set sheet view options with invalid view index
|
||||||
assert.EqualError(t, f.SetSheetView("Sheet1", 1, nil), "view index 1 out of range")
|
assert.EqualError(t, f.SetSheetView("Sheet1", 1, nil), "view index 1 out of range")
|
||||||
assert.EqualError(t, f.SetSheetView("Sheet1", -2, nil), "view index -2 out of range")
|
assert.EqualError(t, f.SetSheetView("Sheet1", -2, nil), "view index -2 out of range")
|
||||||
// Test set sheet view options on not exists worksheet.
|
// Test set sheet view options on not exists worksheet
|
||||||
assert.EqualError(t, f.SetSheetView("SheetN", 0, nil), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetSheetView("SheetN", 0, nil), "sheet SheetN does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,12 +39,12 @@ func TestGetView(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
_, err := f.getSheetView("SheetN", 0)
|
_, err := f.getSheetView("SheetN", 0)
|
||||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
// Test get sheet view options with invalid view index.
|
// Test get sheet view options with invalid view index
|
||||||
_, err = f.GetSheetView("Sheet1", 1)
|
_, err = f.GetSheetView("Sheet1", 1)
|
||||||
assert.EqualError(t, err, "view index 1 out of range")
|
assert.EqualError(t, err, "view index 1 out of range")
|
||||||
_, err = f.GetSheetView("Sheet1", -2)
|
_, err = f.GetSheetView("Sheet1", -2)
|
||||||
assert.EqualError(t, err, "view index -2 out of range")
|
assert.EqualError(t, err, "view index -2 out of range")
|
||||||
// Test get sheet view options on not exists worksheet.
|
// Test get sheet view options on not exists worksheet
|
||||||
_, err = f.GetSheetView("SheetN", 0)
|
_, err = f.GetSheetView("SheetN", 0)
|
||||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddSparkline(t *testing.T) {
|
func TestAddSparkline(t *testing.T) {
|
||||||
f := prepareSparklineDataset()
|
f, err := prepareSparklineDataset()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Set the columns widths to make the output clearer
|
// Set the columns widths to make the output clearer
|
||||||
style, err := f.NewStyle(`{"font":{"bold":true}}`)
|
style, err := f.NewStyle(&Style{Font: &Font{Bold: true}})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "B1", style))
|
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "B1", style))
|
||||||
viewOpts, err := f.GetSheetView("Sheet1", 0)
|
viewOpts, err := f.GetSheetView("Sheet1", 0)
|
||||||
|
@ -291,7 +292,7 @@ func TestAppendSparkline(t *testing.T) {
|
||||||
assert.EqualError(t, f.appendSparkline(ws, &xlsxX14SparklineGroup{}, &xlsxX14SparklineGroups{}), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.appendSparkline(ws, &xlsxX14SparklineGroup{}, &xlsxX14SparklineGroups{}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareSparklineDataset() *File {
|
func prepareSparklineDataset() (*File, error) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
sheet2 := [][]int{
|
sheet2 := [][]int{
|
||||||
{-2, 2, 3, -1, 0},
|
{-2, 2, 3, -1, 0},
|
||||||
|
@ -307,8 +308,12 @@ func prepareSparklineDataset() *File {
|
||||||
{3, -1, 0, -2, 3, 2, 1, 0, 2, 1},
|
{3, -1, 0, -2, 3, 2, 1, 0, 2, 1},
|
||||||
{0, -2, 3, 2, 1, 0, 1, 2, 3, 1},
|
{0, -2, 3, 2, 1, 0, 1, 2, 3, 1},
|
||||||
}
|
}
|
||||||
f.NewSheet("Sheet2")
|
if _, err := f.NewSheet("Sheet2"); err != nil {
|
||||||
f.NewSheet("Sheet3")
|
return f, err
|
||||||
|
}
|
||||||
|
if _, err := f.NewSheet("Sheet3"); err != nil {
|
||||||
|
return f, err
|
||||||
|
}
|
||||||
for row, data := range sheet2 {
|
for row, data := range sheet2 {
|
||||||
if err := f.SetSheetRow("Sheet2", fmt.Sprintf("A%d", row+1), &data); err != nil {
|
if err := f.SetSheetRow("Sheet2", fmt.Sprintf("A%d", row+1), &data); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -319,5 +324,5 @@ func prepareSparklineDataset() *File {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
37
stream.go
37
stream.go
|
@ -134,18 +134,19 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
|
||||||
// AddTable creates an Excel table for the StreamWriter using the given
|
// AddTable creates an Excel table for the StreamWriter using the given
|
||||||
// cell range and format set. For example, create a table of A1:D5:
|
// cell range and format set. For example, create a table of A1:D5:
|
||||||
//
|
//
|
||||||
// err := sw.AddTable("A1", "D5", "")
|
// err := sw.AddTable("A1:D5", nil)
|
||||||
//
|
//
|
||||||
// Create a table of F2:H6 with format set:
|
// Create a table of F2:H6 with format set:
|
||||||
//
|
//
|
||||||
// err := sw.AddTable("F2", "H6", `{
|
// disable := false
|
||||||
// "table_name": "table",
|
// err := sw.AddTable("F2:H6", &excelize.TableOptions{
|
||||||
// "table_style": "TableStyleMedium2",
|
// Name: "table",
|
||||||
// "show_first_column": true,
|
// StyleName: "TableStyleMedium2",
|
||||||
// "show_last_column": true,
|
// ShowFirstColumn: true,
|
||||||
// "show_row_stripes": false,
|
// ShowLastColumn: true,
|
||||||
// "show_column_stripes": true
|
// ShowRowStripes: &disable,
|
||||||
// }`)
|
// ShowColumnStripes: true,
|
||||||
|
// })
|
||||||
//
|
//
|
||||||
// Note that the table must be at least two lines including the header. The
|
// Note that the table must be at least two lines including the header. The
|
||||||
// header cells must contain strings and must be unique.
|
// header cells must contain strings and must be unique.
|
||||||
|
@ -154,13 +155,9 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
|
||||||
// called after the rows are written but before Flush.
|
// called after the rows are written but before Flush.
|
||||||
//
|
//
|
||||||
// See File.AddTable for details on the table format.
|
// See File.AddTable for details on the table format.
|
||||||
func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error {
|
func (sw *StreamWriter) AddTable(reference string, opts *TableOptions) error {
|
||||||
options, err := parseTableOptions(opts)
|
options := parseTableOptions(opts)
|
||||||
if err != nil {
|
coordinates, err := rangeRefToCoordinates(reference)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
coordinates, err := cellRefsToCoordinates(hCell, vCell)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -192,7 +189,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error {
|
||||||
|
|
||||||
tableID := sw.file.countTables() + 1
|
tableID := sw.file.countTables() + 1
|
||||||
|
|
||||||
name := options.TableName
|
name := options.Name
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "Table" + strconv.Itoa(tableID)
|
name = "Table" + strconv.Itoa(tableID)
|
||||||
}
|
}
|
||||||
|
@ -211,10 +208,10 @@ func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error {
|
||||||
TableColumn: tableColumn,
|
TableColumn: tableColumn,
|
||||||
},
|
},
|
||||||
TableStyleInfo: &xlsxTableStyleInfo{
|
TableStyleInfo: &xlsxTableStyleInfo{
|
||||||
Name: options.TableStyle,
|
Name: options.StyleName,
|
||||||
ShowFirstColumn: options.ShowFirstColumn,
|
ShowFirstColumn: options.ShowFirstColumn,
|
||||||
ShowLastColumn: options.ShowLastColumn,
|
ShowLastColumn: options.ShowLastColumn,
|
||||||
ShowRowStripes: options.ShowRowStripes,
|
ShowRowStripes: *options.ShowRowStripes,
|
||||||
ShowColumnStripes: options.ShowColumnStripes,
|
ShowColumnStripes: options.ShowColumnStripes,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -462,7 +459,7 @@ func (sw *StreamWriter) InsertPageBreak(cell string) error {
|
||||||
// SetPanes provides a function to create and remove freeze panes and split
|
// SetPanes provides a function to create and remove freeze panes and split
|
||||||
// panes by giving panes options for the StreamWriter. Note that you must call
|
// panes by giving panes options for the StreamWriter. Note that you must call
|
||||||
// the 'SetPanes' function before the 'SetRow' function.
|
// the 'SetPanes' function before the 'SetRow' function.
|
||||||
func (sw *StreamWriter) SetPanes(panes string) error {
|
func (sw *StreamWriter) SetPanes(panes *Panes) error {
|
||||||
if sw.sheetWritten {
|
if sw.sheetWritten {
|
||||||
return ErrStreamSetPanes
|
return ErrStreamSetPanes
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,12 @@ func TestStreamWriter(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Test max characters in a cell.
|
// Test max characters in a cell
|
||||||
row := make([]interface{}, 1)
|
row := make([]interface{}, 1)
|
||||||
row[0] = strings.Repeat("c", TotalCellChars+2)
|
row[0] = strings.Repeat("c", TotalCellChars+2)
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", row))
|
assert.NoError(t, streamWriter.SetRow("A1", row))
|
||||||
|
|
||||||
// Test leading and ending space(s) character characters in a cell.
|
// Test leading and ending space(s) character characters in a cell
|
||||||
row = make([]interface{}, 1)
|
row = make([]interface{}, 1)
|
||||||
row[0] = " characters"
|
row[0] = " characters"
|
||||||
assert.NoError(t, streamWriter.SetRow("A2", row))
|
assert.NoError(t, streamWriter.SetRow("A2", row))
|
||||||
|
@ -55,7 +55,7 @@ func TestStreamWriter(t *testing.T) {
|
||||||
row[0] = []byte("Word")
|
row[0] = []byte("Word")
|
||||||
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{}{
|
||||||
|
@ -85,14 +85,14 @@ func TestStreamWriter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
// Save spreadsheet by the given path.
|
// Save spreadsheet by the given path
|
||||||
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriter.xlsx")))
|
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriter.xlsx")))
|
||||||
|
|
||||||
// Test set cell column overflow.
|
// Test set cell column overflow
|
||||||
assert.ErrorIs(t, streamWriter.SetRow("XFD51201", []interface{}{"A", "B", "C"}), ErrColumnNumber)
|
assert.ErrorIs(t, streamWriter.SetRow("XFD51201", []interface{}{"A", "B", "C"}), ErrColumnNumber)
|
||||||
assert.NoError(t, file.Close())
|
assert.NoError(t, file.Close())
|
||||||
|
|
||||||
// Test close temporary file error.
|
// Test close temporary file error
|
||||||
file = NewFile()
|
file = NewFile()
|
||||||
streamWriter, err = file.NewStreamWriter("Sheet1")
|
streamWriter, err = file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -114,7 +114,7 @@ func TestStreamWriter(t *testing.T) {
|
||||||
assert.NoError(t, streamWriter.rawData.tmp.Close())
|
assert.NoError(t, streamWriter.rawData.tmp.Close())
|
||||||
assert.NoError(t, os.Remove(streamWriter.rawData.tmp.Name()))
|
assert.NoError(t, os.Remove(streamWriter.rawData.tmp.Name()))
|
||||||
|
|
||||||
// Test create stream writer with unsupported charset.
|
// Test create stream writer with unsupported charset
|
||||||
file = NewFile()
|
file = NewFile()
|
||||||
file.Sheet.Delete("xl/worksheets/sheet1.xml")
|
file.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
file.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
file.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
||||||
|
@ -122,7 +122,7 @@ func TestStreamWriter(t *testing.T) {
|
||||||
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")
|
||||||
assert.NoError(t, file.Close())
|
assert.NoError(t, file.Close())
|
||||||
|
|
||||||
// Test read cell.
|
// Test read cell
|
||||||
file = NewFile()
|
file = NewFile()
|
||||||
streamWriter, err = file.NewStreamWriter("Sheet1")
|
streamWriter, err = file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -132,7 +132,7 @@ func TestStreamWriter(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "Data", cellValue)
|
assert.Equal(t, "Data", cellValue)
|
||||||
|
|
||||||
// Test stream reader for a worksheet with huge amounts of data.
|
// Test stream reader for a worksheet with huge amounts of data
|
||||||
file, err = OpenFile(filepath.Join("test", "TestStreamWriter.xlsx"))
|
file, err = OpenFile(filepath.Join("test", "TestStreamWriter.xlsx"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
rows, err := file.Rows("Sheet1")
|
rows, err := file.Rows("Sheet1")
|
||||||
|
@ -166,14 +166,24 @@ func TestStreamSetColWidth(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamSetPanes(t *testing.T) {
|
func TestStreamSetPanes(t *testing.T) {
|
||||||
file, paneOpts := NewFile(), `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`
|
file, paneOpts := NewFile(), &Panes{
|
||||||
|
Freeze: true,
|
||||||
|
Split: false,
|
||||||
|
XSplit: 1,
|
||||||
|
YSplit: 0,
|
||||||
|
TopLeftCell: "B1",
|
||||||
|
ActivePane: "topRight",
|
||||||
|
Panes: []PaneOptions{
|
||||||
|
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
|
||||||
|
},
|
||||||
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
assert.NoError(t, file.Close())
|
assert.NoError(t, file.Close())
|
||||||
}()
|
}()
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, streamWriter.SetPanes(paneOpts))
|
assert.NoError(t, streamWriter.SetPanes(paneOpts))
|
||||||
assert.EqualError(t, streamWriter.SetPanes(""), "unexpected end of JSON input")
|
assert.EqualError(t, streamWriter.SetPanes(nil), ErrParameterInvalid.Error())
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
||||||
assert.ErrorIs(t, streamWriter.SetPanes(paneOpts), ErrStreamSetPanes)
|
assert.ErrorIs(t, streamWriter.SetPanes(paneOpts), ErrStreamSetPanes)
|
||||||
}
|
}
|
||||||
|
@ -185,19 +195,20 @@ func TestStreamTable(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
// Test add table without table header
|
||||||
// Write some rows. We want enough rows to force a temp file (>16MB).
|
assert.EqualError(t, streamWriter.AddTable("A1:C2", nil), "XML syntax error on line 2: unexpected EOF")
|
||||||
|
// Write some rows. We want enough rows to force a temp file (>16MB)
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
||||||
row := []interface{}{1, 2, 3}
|
row := []interface{}{1, 2, 3}
|
||||||
for r := 2; r < 10000; r++ {
|
for r := 2; r < 10000; r++ {
|
||||||
assert.NoError(t, streamWriter.SetRow(fmt.Sprintf("A%d", r), row))
|
assert.NoError(t, streamWriter.SetRow(fmt.Sprintf("A%d", r), row))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a table.
|
// Write a table
|
||||||
assert.NoError(t, streamWriter.AddTable("A1", "C2", ""))
|
assert.NoError(t, streamWriter.AddTable("A1:C2", nil))
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
|
|
||||||
// Verify the table has names.
|
// Verify the table has names
|
||||||
var table xlsxTable
|
var table xlsxTable
|
||||||
val, ok := file.Pkg.Load("xl/tables/table1.xml")
|
val, ok := file.Pkg.Load("xl/tables/table1.xml")
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
@ -206,17 +217,15 @@ func TestStreamTable(t *testing.T) {
|
||||||
assert.Equal(t, "B", table.TableColumns.TableColumn[1].Name)
|
assert.Equal(t, "B", table.TableColumns.TableColumn[1].Name)
|
||||||
assert.Equal(t, "C", table.TableColumns.TableColumn[2].Name)
|
assert.Equal(t, "C", table.TableColumns.TableColumn[2].Name)
|
||||||
|
|
||||||
assert.NoError(t, streamWriter.AddTable("A1", "C1", ""))
|
assert.NoError(t, streamWriter.AddTable("A1:C1", nil))
|
||||||
|
|
||||||
// Test add table with illegal options.
|
// Test add table with illegal cell reference
|
||||||
assert.EqualError(t, streamWriter.AddTable("B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string")
|
assert.EqualError(t, streamWriter.AddTable("A:B1", nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
// Test add table with illegal cell reference.
|
assert.EqualError(t, streamWriter.AddTable("A1:B", nil), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||||
assert.EqualError(t, streamWriter.AddTable("A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
// Test add table with unsupported charset content types
|
||||||
assert.EqualError(t, streamWriter.AddTable("A1", "B", `{}`), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
|
||||||
// Test add table with unsupported charset content types.
|
|
||||||
file.ContentTypes = nil
|
file.ContentTypes = nil
|
||||||
file.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
file.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, streamWriter.AddTable("A1", "C2", ""), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, streamWriter.AddTable("A1:C2", nil), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamMergeCells(t *testing.T) {
|
func TestStreamMergeCells(t *testing.T) {
|
||||||
|
@ -227,10 +236,10 @@ func TestStreamMergeCells(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, streamWriter.MergeCell("A1", "D1"))
|
assert.NoError(t, streamWriter.MergeCell("A1", "D1"))
|
||||||
// Test merge cells with illegal cell reference.
|
// Test merge cells with illegal cell reference
|
||||||
assert.EqualError(t, streamWriter.MergeCell("A", "D1"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, streamWriter.MergeCell("A", "D1"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
// Save spreadsheet by the given path.
|
// Save spreadsheet by the given path
|
||||||
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamMergeCells.xlsx")))
|
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamMergeCells.xlsx")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +252,7 @@ func TestStreamInsertPageBreak(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, streamWriter.InsertPageBreak("A1"))
|
assert.NoError(t, streamWriter.InsertPageBreak("A1"))
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
// Save spreadsheet by the given path.
|
// Save spreadsheet by the given path
|
||||||
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamInsertPageBreak.xlsx")))
|
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamInsertPageBreak.xlsx")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +279,7 @@ func TestStreamMarshalAttrs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamSetRow(t *testing.T) {
|
func TestStreamSetRow(t *testing.T) {
|
||||||
// Test error exceptions.
|
// Test error exceptions
|
||||||
file := NewFile()
|
file := NewFile()
|
||||||
defer func() {
|
defer func() {
|
||||||
assert.NoError(t, file.Close())
|
assert.NoError(t, file.Close())
|
||||||
|
@ -278,10 +287,10 @@ func TestStreamSetRow(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
// Test set row with non-ascending row number.
|
// Test set row with non-ascending row number
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{}))
|
||||||
assert.EqualError(t, streamWriter.SetRow("A1", []interface{}{}), newStreamSetRowError(1).Error())
|
assert.EqualError(t, streamWriter.SetRow("A1", []interface{}{}), newStreamSetRowError(1).Error())
|
||||||
// Test set row with unsupported charset workbook.
|
// Test set row with unsupported charset workbook
|
||||||
file.WorkBook = nil
|
file.WorkBook = nil
|
||||||
file.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
file.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, streamWriter.SetRow("A2", []interface{}{time.Now()}), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, streamWriter.SetRow("A2", []interface{}{time.Now()}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
@ -367,13 +376,13 @@ func TestStreamWriterOutlineLevel(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Test set outlineLevel in row.
|
// Test set outlineLevel in row
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", nil, RowOpts{OutlineLevel: 1}))
|
assert.NoError(t, streamWriter.SetRow("A1", nil, RowOpts{OutlineLevel: 1}))
|
||||||
assert.NoError(t, streamWriter.SetRow("A2", nil, RowOpts{OutlineLevel: 7}))
|
assert.NoError(t, streamWriter.SetRow("A2", nil, RowOpts{OutlineLevel: 7}))
|
||||||
assert.ErrorIs(t, ErrOutlineLevel, streamWriter.SetRow("A3", nil, RowOpts{OutlineLevel: 8}))
|
assert.ErrorIs(t, ErrOutlineLevel, streamWriter.SetRow("A3", nil, RowOpts{OutlineLevel: 8}))
|
||||||
|
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
// Save spreadsheet by the given path.
|
// Save spreadsheet by the given path
|
||||||
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriterSetRowOutlineLevel.xlsx")))
|
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriterSetRowOutlineLevel.xlsx")))
|
||||||
|
|
||||||
file, err = OpenFile(filepath.Join("test", "TestStreamWriterSetRowOutlineLevel.xlsx"))
|
file, err = OpenFile(filepath.Join("test", "TestStreamWriterSetRowOutlineLevel.xlsx"))
|
||||||
|
|
434
styles.go
434
styles.go
|
@ -13,7 +13,6 @@ package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -1076,35 +1075,25 @@ func (f *File) sharedStringsWriter() {
|
||||||
|
|
||||||
// parseFormatStyleSet provides a function to parse the format settings of the
|
// parseFormatStyleSet provides a function to parse the format settings of the
|
||||||
// cells and conditional formats.
|
// cells and conditional formats.
|
||||||
func parseFormatStyleSet(style interface{}) (*Style, error) {
|
func parseFormatStyleSet(style *Style) (*Style, error) {
|
||||||
fs := Style{}
|
|
||||||
var err error
|
var err error
|
||||||
switch v := style.(type) {
|
if style.Font != nil {
|
||||||
case string:
|
if len(style.Font.Family) > MaxFontFamilyLength {
|
||||||
err = json.Unmarshal([]byte(v), &fs)
|
return style, ErrFontLength
|
||||||
case *Style:
|
|
||||||
fs = *v
|
|
||||||
default:
|
|
||||||
err = ErrParameterInvalid
|
|
||||||
}
|
|
||||||
if fs.Font != nil {
|
|
||||||
if len(fs.Font.Family) > MaxFontFamilyLength {
|
|
||||||
return &fs, ErrFontLength
|
|
||||||
}
|
}
|
||||||
if fs.Font.Size > MaxFontSize {
|
if style.Font.Size > MaxFontSize {
|
||||||
return &fs, ErrFontSize
|
return style, ErrFontSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fs.CustomNumFmt != nil && len(*fs.CustomNumFmt) == 0 {
|
if style.CustomNumFmt != nil && len(*style.CustomNumFmt) == 0 {
|
||||||
err = ErrCustomNumFmt
|
err = ErrCustomNumFmt
|
||||||
}
|
}
|
||||||
return &fs, err
|
return style, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStyle provides a function to create the style for cells by given structure
|
// NewStyle provides a function to create the style for cells by given style
|
||||||
// pointer or JSON. This function is concurrency safe. Note that
|
// options. This function is concurrency safe. Note that the 'Font.Color' field
|
||||||
// the 'Font.Color' field uses an RGB color represented in 'RRGGBB' hexadecimal
|
// uses an RGB color represented in 'RRGGBB' hexadecimal notation.
|
||||||
// notation.
|
|
||||||
//
|
//
|
||||||
// The following table shows the border types used in 'Border.Type' supported by
|
// The following table shows the border types used in 'Border.Type' supported by
|
||||||
// excelize:
|
// excelize:
|
||||||
|
@ -1983,13 +1972,16 @@ func parseFormatStyleSet(style interface{}) (*Style, error) {
|
||||||
// err = f.SetCellStyle("Sheet1", "A6", "A6", style)
|
// err = f.SetCellStyle("Sheet1", "A6", "A6", style)
|
||||||
//
|
//
|
||||||
// Cell Sheet1!A6 in the Excel Application: martes, 04 de Julio de 2017
|
// Cell Sheet1!A6 in the Excel Application: martes, 04 de Julio de 2017
|
||||||
func (f *File) NewStyle(style interface{}) (int, error) {
|
func (f *File) NewStyle(style *Style) (int, error) {
|
||||||
var (
|
var (
|
||||||
fs *Style
|
fs *Style
|
||||||
font *xlsxFont
|
font *xlsxFont
|
||||||
err error
|
err error
|
||||||
cellXfsID, fontID, borderID, fillID int
|
cellXfsID, fontID, borderID, fillID int
|
||||||
)
|
)
|
||||||
|
if style == nil {
|
||||||
|
return cellXfsID, err
|
||||||
|
}
|
||||||
fs, err = parseFormatStyleSet(style)
|
fs, err = parseFormatStyleSet(style)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cellXfsID, err
|
return cellXfsID, err
|
||||||
|
@ -2123,9 +2115,8 @@ func (f *File) getStyleID(ss *xlsxStyleSheet, style *Style) (int, error) {
|
||||||
|
|
||||||
// NewConditionalStyle provides a function to create style for conditional
|
// NewConditionalStyle provides a function to create style for conditional
|
||||||
// format by given style format. The parameters are the same with the NewStyle
|
// format by given style format. The parameters are the same with the NewStyle
|
||||||
// function. Note that the color field uses RGB color code and only support to
|
// function.
|
||||||
// set font, fills, alignment and borders currently.
|
func (f *File) NewConditionalStyle(style *Style) (int, error) {
|
||||||
func (f *File) NewConditionalStyle(style string) (int, error) {
|
|
||||||
s, err := f.stylesReader()
|
s, err := f.stylesReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -2836,51 +2827,51 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
|
||||||
//
|
//
|
||||||
// Type | Parameters
|
// Type | Parameters
|
||||||
// ---------------+------------------------------------
|
// ---------------+------------------------------------
|
||||||
// cell | criteria
|
// cell | Criteria
|
||||||
// | value
|
// | Value
|
||||||
// | minimum
|
// | Minimum
|
||||||
// | maximum
|
// | Maximum
|
||||||
// date | criteria
|
// date | Criteria
|
||||||
// | value
|
// | Value
|
||||||
// | minimum
|
// | Minimum
|
||||||
// | maximum
|
// | Maximum
|
||||||
// time_period | criteria
|
// time_period | Criteria
|
||||||
// text | criteria
|
// text | Criteria
|
||||||
// | value
|
// | Value
|
||||||
// average | criteria
|
// average | Criteria
|
||||||
// duplicate | (none)
|
// duplicate | (none)
|
||||||
// unique | (none)
|
// unique | (none)
|
||||||
// top | criteria
|
// top | Criteria
|
||||||
// | value
|
// | Value
|
||||||
// bottom | criteria
|
// bottom | Criteria
|
||||||
// | value
|
// | Value
|
||||||
// blanks | (none)
|
// blanks | (none)
|
||||||
// no_blanks | (none)
|
// no_blanks | (none)
|
||||||
// errors | (none)
|
// errors | (none)
|
||||||
// no_errors | (none)
|
// no_errors | (none)
|
||||||
// 2_color_scale | min_type
|
// 2_color_scale | MinType
|
||||||
// | max_type
|
// | MaxType
|
||||||
// | min_value
|
// | MinValue
|
||||||
// | max_value
|
// | MaxValue
|
||||||
// | min_color
|
// | MinColor
|
||||||
// | max_color
|
// | MaxColor
|
||||||
// 3_color_scale | min_type
|
// 3_color_scale | MinType
|
||||||
// | mid_type
|
// | MidType
|
||||||
// | max_type
|
// | MaxType
|
||||||
// | min_value
|
// | MinValue
|
||||||
// | mid_value
|
// | MidValue
|
||||||
// | max_value
|
// | MaxValue
|
||||||
// | min_color
|
// | MinColor
|
||||||
// | mid_color
|
// | MidColor
|
||||||
// | max_color
|
// | MaxColor
|
||||||
// data_bar | min_type
|
// data_bar | MinType
|
||||||
// | max_type
|
// | MaxType
|
||||||
// | min_value
|
// | MinValue
|
||||||
// | max_value
|
// | MaxValue
|
||||||
// | bar_color
|
// | BarColor
|
||||||
// formula | criteria
|
// formula | Criteria
|
||||||
//
|
//
|
||||||
// The criteria parameter is used to set the criteria by which the cell data
|
// The 'Criteria' parameter is used to set the criteria by which the cell data
|
||||||
// will be evaluated. It has no default value. The most common criteria as
|
// will be evaluated. It has no default value. The most common criteria as
|
||||||
// applied to {"type":"cell"} are:
|
// applied to {"type":"cell"} are:
|
||||||
//
|
//
|
||||||
|
@ -2902,22 +2893,51 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
|
||||||
// value: The value is generally used along with the criteria parameter to set
|
// value: The value is generally used along with the criteria parameter to set
|
||||||
// the rule by which the cell data will be evaluated:
|
// the rule by which the cell data will be evaluated:
|
||||||
//
|
//
|
||||||
// f.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "D1:D10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "cell",
|
||||||
|
// Criteria: ">",
|
||||||
|
// Format: format,
|
||||||
|
// Value: "6",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// The value property can also be an cell reference:
|
// The value property can also be an cell reference:
|
||||||
//
|
//
|
||||||
// f.SetConditionalFormat("Sheet1", "D1:D10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"$C$1"}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "D1:D10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "cell",
|
||||||
|
// Criteria: ">",
|
||||||
|
// Format: format,
|
||||||
|
// Value: "$C$1",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: format - The format parameter is used to specify the format that will
|
// type: format - The format parameter is used to specify the format that will
|
||||||
// be applied to the cell when the conditional formatting criterion is met. The
|
// be applied to the cell when the conditional formatting criterion is met. The
|
||||||
// format is created using the NewConditionalStyle() method in the same way as
|
// format is created using the NewConditionalStyle function in the same way as
|
||||||
// cell formats:
|
// cell formats:
|
||||||
//
|
//
|
||||||
// format, err = f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
|
// format, err := f.NewConditionalStyle(
|
||||||
|
// &excelize.Style{
|
||||||
|
// Font: &excelize.Font{Color: "#9A0511"},
|
||||||
|
// Fill: excelize.Fill{
|
||||||
|
// Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format))
|
// err = f.SetConditionalFormat("Sheet1", "D1:D10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {Type: "cell", Criteria: ">", Format: format, Value: "6"},
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// Note: In Excel, a conditional format is superimposed over the existing cell
|
// Note: In Excel, a conditional format is superimposed over the existing cell
|
||||||
// format and not all cell format properties can be modified. Properties that
|
// format and not all cell format properties can be modified. Properties that
|
||||||
|
@ -2929,19 +2949,50 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
|
||||||
// These can be replicated using the following excelize formats:
|
// These can be replicated using the following excelize formats:
|
||||||
//
|
//
|
||||||
// // Rose format for bad conditional.
|
// // Rose format for bad conditional.
|
||||||
// format1, err = f.NewConditionalStyle(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#FEC7CE"],"pattern":1}}`)
|
// format1, err := f.NewConditionalStyle(
|
||||||
|
// &excelize.Style{
|
||||||
|
// Font: &excelize.Font{Color: "#9A0511"},
|
||||||
|
// Fill: excelize.Fill{
|
||||||
|
// Type: "pattern", Color: []string{"#FEC7CE"}, Pattern: 1,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// // Light yellow format for neutral conditional.
|
// // Light yellow format for neutral conditional.
|
||||||
// format2, err = f.NewConditionalStyle(`{"font":{"color":"#9B5713"},"fill":{"type":"pattern","color":["#FEEAA0"],"pattern":1}}`)
|
// format2, err := f.NewConditionalStyle(
|
||||||
|
// &excelize.Style{
|
||||||
|
// Font: &excelize.Font{Color: "#9B5713"},
|
||||||
|
// Fill: excelize.Fill{
|
||||||
|
// Type: "pattern", Color: []string{"#FEEAA0"}, Pattern: 1,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// // Light green format for good conditional.
|
// // Light green format for good conditional.
|
||||||
// format3, err = f.NewConditionalStyle(`{"font":{"color":"#09600B"},"fill":{"type":"pattern","color":["#C7EECF"],"pattern":1}}`)
|
// format3, err := f.NewConditionalStyle(
|
||||||
|
// &excelize.Style{
|
||||||
|
// Font: &excelize.Font{Color: "#09600B"},
|
||||||
|
// Fill: excelize.Fill{
|
||||||
|
// Type: "pattern", Color: []string{"#C7EECF"}, Pattern: 1,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: minimum - The minimum parameter is used to set the lower limiting value
|
// type: minimum - The minimum parameter is used to set the lower limiting value
|
||||||
// when the criteria is either "between" or "not between".
|
// when the criteria is either "between" or "not between".
|
||||||
//
|
//
|
||||||
// // Highlight cells rules: between...
|
// // Highlight cells rules: between...
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"cell","criteria":"between","format":%d,"minimum":"6","maximum":"8"}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "cell",
|
||||||
|
// Criteria: "between",
|
||||||
|
// Format: format,
|
||||||
|
// Minimum: "6",
|
||||||
|
// Maximum: "8",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: maximum - The maximum parameter is used to set the upper limiting value
|
// type: maximum - The maximum parameter is used to set the upper limiting value
|
||||||
// when the criteria is either "between" or "not between". See the previous
|
// when the criteria is either "between" or "not between". See the previous
|
||||||
|
@ -2951,98 +3002,184 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
|
||||||
// conditional format:
|
// conditional format:
|
||||||
//
|
//
|
||||||
// // Top/Bottom rules: Above Average...
|
// // Top/Bottom rules: Above Average...
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": true}]`, format1))
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "average",
|
||||||
|
// Criteria: "=",
|
||||||
|
// Format: format1,
|
||||||
|
// AboveAverage: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// // Top/Bottom rules: Below Average...
|
// // Top/Bottom rules: Below Average...
|
||||||
// f.SetConditionalFormat("Sheet1", "B1:B10", fmt.Sprintf(`[{"type":"average","criteria":"=","format":%d, "above_average": false}]`, format2))
|
// err := f.SetConditionalFormat("Sheet1", "B1:B10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "average",
|
||||||
|
// Criteria: "=",
|
||||||
|
// Format: format2,
|
||||||
|
// AboveAverage: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: duplicate - The duplicate type is used to highlight duplicate cells in a range:
|
// type: duplicate - The duplicate type is used to highlight duplicate cells in a range:
|
||||||
//
|
//
|
||||||
// // Highlight cells rules: Duplicate Values...
|
// // Highlight cells rules: Duplicate Values...
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"duplicate","criteria":"=","format":%d}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {Type: "duplicate", Criteria: "=", Format: format},
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: unique - The unique type is used to highlight unique cells in a range:
|
// type: unique - The unique type is used to highlight unique cells in a range:
|
||||||
//
|
//
|
||||||
// // Highlight cells rules: Not Equal To...
|
// // Highlight cells rules: Not Equal To...
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"unique","criteria":"=","format":%d}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {Type: "unique", Criteria: "=", Format: format},
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: top - The top type is used to specify the top n values by number or percentage in a range:
|
// type: top - The top type is used to specify the top n values by number or percentage in a range:
|
||||||
//
|
//
|
||||||
// // Top/Bottom rules: Top 10.
|
// // Top/Bottom rules: Top 10.
|
||||||
// f.SetConditionalFormat("Sheet1", "H1:H10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6"}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "H1:H10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "top",
|
||||||
|
// Criteria: "=",
|
||||||
|
// Format: format,
|
||||||
|
// Value: "6",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// The criteria can be used to indicate that a percentage condition is required:
|
// The criteria can be used to indicate that a percentage condition is required:
|
||||||
//
|
//
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", fmt.Sprintf(`[{"type":"top","criteria":"=","format":%d,"value":"6","percent":true}]`, format))
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "top",
|
||||||
|
// Criteria: "=",
|
||||||
|
// Format: format,
|
||||||
|
// Value: "6",
|
||||||
|
// Percent: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// type: 2_color_scale - The 2_color_scale type is used to specify Excel's "2
|
// type: 2_color_scale - The 2_color_scale type is used to specify Excel's "2
|
||||||
// Color Scale" style conditional format:
|
// Color Scale" style conditional format:
|
||||||
//
|
//
|
||||||
// // Color scales: 2 color.
|
// // Color scales: 2 color.
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", `[{"type":"2_color_scale","criteria":"=","min_type":"min","max_type":"max","min_color":"#F8696B","max_color":"#63BE7B"}]`)
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "2_color_scale",
|
||||||
|
// Criteria: "=",
|
||||||
|
// MinType: "min",
|
||||||
|
// MaxType: "max",
|
||||||
|
// MinColor: "#F8696B",
|
||||||
|
// MaxColor: "#63BE7B",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// This conditional type can be modified with min_type, max_type, min_value,
|
// This conditional type can be modified with MinType, MaxType, MinValue,
|
||||||
// max_value, min_color and max_color, see below.
|
// MaxValue, MinColor and MaxColor, see below.
|
||||||
//
|
//
|
||||||
// type: 3_color_scale - The 3_color_scale type is used to specify Excel's "3
|
// type: 3_color_scale - The 3_color_scale type is used to specify Excel's "3
|
||||||
// Color Scale" style conditional format:
|
// Color Scale" style conditional format:
|
||||||
//
|
//
|
||||||
// // Color scales: 3 color.
|
// // Color scales: 3 color.
|
||||||
// f.SetConditionalFormat("Sheet1", "A1:A10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)
|
// err := f.SetConditionalFormat("Sheet1", "A1:A10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "3_color_scale",
|
||||||
|
// Criteria: "=",
|
||||||
|
// MinType: "min",
|
||||||
|
// MidType: "percentile",
|
||||||
|
// MaxType: "max",
|
||||||
|
// MinColor: "#F8696B",
|
||||||
|
// MidColor: "#FFEB84",
|
||||||
|
// MaxColor: "#63BE7B",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// This conditional type can be modified with min_type, mid_type, max_type,
|
// This conditional type can be modified with MinType, MidType, MaxType,
|
||||||
// min_value, mid_value, max_value, min_color, mid_color and max_color, see
|
// MinValue, MidValue, MaxValue, MinColor, MidColor and MaxColor, see
|
||||||
// below.
|
// below.
|
||||||
//
|
//
|
||||||
// type: data_bar - The data_bar type is used to specify Excel's "Data Bar"
|
// type: data_bar - The data_bar type is used to specify Excel's "Data Bar"
|
||||||
// style conditional format.
|
// style conditional format.
|
||||||
//
|
//
|
||||||
// min_type - The min_type and max_type properties are available when the conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The mid_type is available for 3_color_scale. The properties are used as follows:
|
// MinType - The MinType and MaxType properties are available when the conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The MidType is available for 3_color_scale. The properties are used as follows:
|
||||||
//
|
//
|
||||||
// // Data Bars: Gradient Fill.
|
// // Data Bars: Gradient Fill.
|
||||||
// f.SetConditionalFormat("Sheet1", "K1:K10", `[{"type":"data_bar", "criteria":"=", "min_type":"min","max_type":"max","bar_color":"#638EC6"}]`)
|
// err := f.SetConditionalFormat("Sheet1", "K1:K10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "data_bar",
|
||||||
|
// Criteria: "=",
|
||||||
|
// MinType: "min",
|
||||||
|
// MaxType: "max",
|
||||||
|
// BarColor: "#638EC6",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// The available min/mid/max types are:
|
// The available min/mid/max types are:
|
||||||
//
|
//
|
||||||
// min (for min_type only)
|
// min (for MinType only)
|
||||||
// num
|
// num
|
||||||
// percent
|
// percent
|
||||||
// percentile
|
// percentile
|
||||||
// formula
|
// formula
|
||||||
// max (for max_type only)
|
// max (for MaxType only)
|
||||||
//
|
//
|
||||||
// mid_type - Used for 3_color_scale. Same as min_type, see above.
|
// MidType - Used for 3_color_scale. Same as MinType, see above.
|
||||||
//
|
//
|
||||||
// max_type - Same as min_type, see above.
|
// MaxType - Same as MinType, see above.
|
||||||
//
|
//
|
||||||
// min_value - The min_value and max_value properties are available when the
|
// MinValue - The MinValue and MaxValue properties are available when the
|
||||||
// conditional formatting type is 2_color_scale, 3_color_scale or data_bar. The
|
|
||||||
// mid_value is available for 3_color_scale.
|
|
||||||
//
|
|
||||||
// mid_value - Used for 3_color_scale. Same as min_value, see above.
|
|
||||||
//
|
|
||||||
// max_value - Same as min_value, see above.
|
|
||||||
//
|
|
||||||
// min_color - The min_color and max_color properties are available when the
|
|
||||||
// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.
|
// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.
|
||||||
// The mid_color is available for 3_color_scale. The properties are used as
|
//
|
||||||
// follows:
|
// MidValue - The MidValue is available for 3_color_scale. Same as MinValue,
|
||||||
|
// see above.
|
||||||
|
//
|
||||||
|
// MaxValue - Same as MinValue, see above.
|
||||||
|
//
|
||||||
|
// MinColor - The MinColor and MaxColor properties are available when the
|
||||||
|
// conditional formatting type is 2_color_scale, 3_color_scale or data_bar.
|
||||||
|
//
|
||||||
|
// MidColor - The MidColor is available for 3_color_scale. The properties
|
||||||
|
// are used as follows:
|
||||||
//
|
//
|
||||||
// // Color scales: 3 color.
|
// // Color scales: 3 color.
|
||||||
// f.SetConditionalFormat("Sheet1", "B1:B10", `[{"type":"3_color_scale","criteria":"=","min_type":"min","mid_type":"percentile","max_type":"max","min_color":"#F8696B","mid_color":"#FFEB84","max_color":"#63BE7B"}]`)
|
// err := f.SetConditionalFormat("Sheet1", "B1:B10",
|
||||||
|
// []excelize.ConditionalFormatOptions{
|
||||||
|
// {
|
||||||
|
// Type: "3_color_scale",
|
||||||
|
// Criteria: "=",
|
||||||
|
// MinType: "min",
|
||||||
|
// MidType: "percentile",
|
||||||
|
// MaxType: "max",
|
||||||
|
// MinColor: "#F8696B",
|
||||||
|
// MidColor: "#FFEB84",
|
||||||
|
// MaxColor: "#63BE7B",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )
|
||||||
//
|
//
|
||||||
// mid_color - Used for 3_color_scale. Same as min_color, see above.
|
// MaxColor - Same as MinColor, see above.
|
||||||
//
|
//
|
||||||
// max_color - Same as min_color, see above.
|
// BarColor - Used for data_bar. Same as MinColor, see above.
|
||||||
//
|
func (f *File) SetConditionalFormat(sheet, reference string, opts []ConditionalFormatOptions) error {
|
||||||
// bar_color - Used for data_bar. Same as min_color, see above.
|
drawContFmtFunc := map[string]func(p int, ct string, fmtCond *ConditionalFormatOptions) *xlsxCfRule{
|
||||||
func (f *File) SetConditionalFormat(sheet, reference, opts string) error {
|
|
||||||
var format []*conditionalOptions
|
|
||||||
err := json.Unmarshal([]byte(opts), &format)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
drawContFmtFunc := map[string]func(p int, ct string, fmtCond *conditionalOptions) *xlsxCfRule{
|
|
||||||
"cellIs": drawCondFmtCellIs,
|
"cellIs": drawCondFmtCellIs,
|
||||||
"top10": drawCondFmtTop10,
|
"top10": drawCondFmtTop10,
|
||||||
"aboveAverage": drawCondFmtAboveAverage,
|
"aboveAverage": drawCondFmtAboveAverage,
|
||||||
|
@ -3059,7 +3196,7 @@ func (f *File) SetConditionalFormat(sheet, reference, opts string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var cfRule []*xlsxCfRule
|
var cfRule []*xlsxCfRule
|
||||||
for p, v := range format {
|
for p, v := range opts {
|
||||||
var vt, ct string
|
var vt, ct string
|
||||||
var ok bool
|
var ok bool
|
||||||
// "type" is a required parameter, check for valid validation types.
|
// "type" is a required parameter, check for valid validation types.
|
||||||
|
@ -3070,7 +3207,7 @@ func (f *File) SetConditionalFormat(sheet, reference, opts string) error {
|
||||||
if ok || vt == "expression" {
|
if ok || vt == "expression" {
|
||||||
drawFunc, ok := drawContFmtFunc[vt]
|
drawFunc, ok := drawContFmtFunc[vt]
|
||||||
if ok {
|
if ok {
|
||||||
cfRule = append(cfRule, drawFunc(p, ct, v))
|
cfRule = append(cfRule, drawFunc(p, ct, &v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3086,21 +3223,21 @@ func (f *File) SetConditionalFormat(sheet, reference, opts string) error {
|
||||||
// extractCondFmtCellIs provides a function to extract conditional format
|
// extractCondFmtCellIs provides a function to extract conditional format
|
||||||
// settings for cell value (include between, not between, equal, not equal,
|
// settings for cell value (include between, not between, equal, not equal,
|
||||||
// greater than and less than) by given conditional formatting rule.
|
// greater than and less than) by given conditional formatting rule.
|
||||||
func extractCondFmtCellIs(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtCellIs(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
format := conditionalOptions{Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID}
|
format := ConditionalFormatOptions{Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID}
|
||||||
if len(c.Formula) == 2 {
|
if len(c.Formula) == 2 {
|
||||||
format.Minimum, format.Maximum = c.Formula[0], c.Formula[1]
|
format.Minimum, format.Maximum = c.Formula[0], c.Formula[1]
|
||||||
return &format
|
return format
|
||||||
}
|
}
|
||||||
format.Value = c.Formula[0]
|
format.Value = c.Formula[0]
|
||||||
return &format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractCondFmtTop10 provides a function to extract conditional format
|
// extractCondFmtTop10 provides a function to extract conditional format
|
||||||
// settings for top N (default is top 10) by given conditional formatting
|
// settings for top N (default is top 10) by given conditional formatting
|
||||||
// rule.
|
// rule.
|
||||||
func extractCondFmtTop10(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtTop10(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
format := conditionalOptions{
|
format := ConditionalFormatOptions{
|
||||||
Type: "top",
|
Type: "top",
|
||||||
Criteria: "=",
|
Criteria: "=",
|
||||||
Format: *c.DxfID,
|
Format: *c.DxfID,
|
||||||
|
@ -3110,14 +3247,14 @@ func extractCondFmtTop10(c *xlsxCfRule) *conditionalOptions {
|
||||||
if c.Bottom {
|
if c.Bottom {
|
||||||
format.Type = "bottom"
|
format.Type = "bottom"
|
||||||
}
|
}
|
||||||
return &format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractCondFmtAboveAverage provides a function to extract conditional format
|
// extractCondFmtAboveAverage provides a function to extract conditional format
|
||||||
// settings for above average and below average by given conditional formatting
|
// settings for above average and below average by given conditional formatting
|
||||||
// rule.
|
// rule.
|
||||||
func extractCondFmtAboveAverage(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtAboveAverage(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
return &conditionalOptions{
|
return ConditionalFormatOptions{
|
||||||
Type: "average",
|
Type: "average",
|
||||||
Criteria: "=",
|
Criteria: "=",
|
||||||
Format: *c.DxfID,
|
Format: *c.DxfID,
|
||||||
|
@ -3128,8 +3265,8 @@ func extractCondFmtAboveAverage(c *xlsxCfRule) *conditionalOptions {
|
||||||
// extractCondFmtDuplicateUniqueValues provides a function to extract
|
// extractCondFmtDuplicateUniqueValues provides a function to extract
|
||||||
// conditional format settings for duplicate and unique values by given
|
// conditional format settings for duplicate and unique values by given
|
||||||
// conditional formatting rule.
|
// conditional formatting rule.
|
||||||
func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
return &conditionalOptions{
|
return ConditionalFormatOptions{
|
||||||
Type: map[string]string{
|
Type: map[string]string{
|
||||||
"duplicateValues": "duplicate",
|
"duplicateValues": "duplicate",
|
||||||
"uniqueValues": "unique",
|
"uniqueValues": "unique",
|
||||||
|
@ -3142,8 +3279,8 @@ func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) *conditionalOptions {
|
||||||
// extractCondFmtColorScale provides a function to extract conditional format
|
// extractCondFmtColorScale provides a function to extract conditional format
|
||||||
// settings for color scale (include 2 color scale and 3 color scale) by given
|
// settings for color scale (include 2 color scale and 3 color scale) by given
|
||||||
// conditional formatting rule.
|
// conditional formatting rule.
|
||||||
func extractCondFmtColorScale(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtColorScale(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
var format conditionalOptions
|
var format ConditionalFormatOptions
|
||||||
format.Type, format.Criteria = "2_color_scale", "="
|
format.Type, format.Criteria = "2_color_scale", "="
|
||||||
values := len(c.ColorScale.Cfvo)
|
values := len(c.ColorScale.Cfvo)
|
||||||
colors := len(c.ColorScale.Color)
|
colors := len(c.ColorScale.Color)
|
||||||
|
@ -3172,35 +3309,35 @@ func extractCondFmtColorScale(c *xlsxCfRule) *conditionalOptions {
|
||||||
}
|
}
|
||||||
format.MaxColor = "#" + strings.TrimPrefix(strings.ToUpper(c.ColorScale.Color[2].RGB), "FF")
|
format.MaxColor = "#" + strings.TrimPrefix(strings.ToUpper(c.ColorScale.Color[2].RGB), "FF")
|
||||||
}
|
}
|
||||||
return &format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractCondFmtDataBar provides a function to extract conditional format
|
// extractCondFmtDataBar provides a function to extract conditional format
|
||||||
// settings for data bar by given conditional formatting rule.
|
// settings for data bar by given conditional formatting rule.
|
||||||
func extractCondFmtDataBar(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtDataBar(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
format := conditionalOptions{Type: "data_bar", Criteria: "="}
|
format := ConditionalFormatOptions{Type: "data_bar", Criteria: "="}
|
||||||
if c.DataBar != nil {
|
if c.DataBar != nil {
|
||||||
format.MinType = c.DataBar.Cfvo[0].Type
|
format.MinType = c.DataBar.Cfvo[0].Type
|
||||||
format.MaxType = c.DataBar.Cfvo[1].Type
|
format.MaxType = c.DataBar.Cfvo[1].Type
|
||||||
format.BarColor = "#" + strings.TrimPrefix(strings.ToUpper(c.DataBar.Color[0].RGB), "FF")
|
format.BarColor = "#" + strings.TrimPrefix(strings.ToUpper(c.DataBar.Color[0].RGB), "FF")
|
||||||
}
|
}
|
||||||
return &format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractCondFmtExp provides a function to extract conditional format settings
|
// extractCondFmtExp provides a function to extract conditional format settings
|
||||||
// for expression by given conditional formatting rule.
|
// for expression by given conditional formatting rule.
|
||||||
func extractCondFmtExp(c *xlsxCfRule) *conditionalOptions {
|
func extractCondFmtExp(c *xlsxCfRule) ConditionalFormatOptions {
|
||||||
format := conditionalOptions{Type: "formula", Format: *c.DxfID}
|
format := ConditionalFormatOptions{Type: "formula", Format: *c.DxfID}
|
||||||
if len(c.Formula) > 0 {
|
if len(c.Formula) > 0 {
|
||||||
format.Criteria = c.Formula[0]
|
format.Criteria = c.Formula[0]
|
||||||
}
|
}
|
||||||
return &format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConditionalFormats returns conditional format settings by given worksheet
|
// GetConditionalFormats returns conditional format settings by given worksheet
|
||||||
// name.
|
// name.
|
||||||
func (f *File) GetConditionalFormats(sheet string) (map[string]string, error) {
|
func (f *File) GetConditionalFormats(sheet string) (map[string][]ConditionalFormatOptions, error) {
|
||||||
extractContFmtFunc := map[string]func(c *xlsxCfRule) *conditionalOptions{
|
extractContFmtFunc := map[string]func(c *xlsxCfRule) ConditionalFormatOptions{
|
||||||
"cellIs": extractCondFmtCellIs,
|
"cellIs": extractCondFmtCellIs,
|
||||||
"top10": extractCondFmtTop10,
|
"top10": extractCondFmtTop10,
|
||||||
"aboveAverage": extractCondFmtAboveAverage,
|
"aboveAverage": extractCondFmtAboveAverage,
|
||||||
|
@ -3211,20 +3348,19 @@ func (f *File) GetConditionalFormats(sheet string) (map[string]string, error) {
|
||||||
"expression": extractCondFmtExp,
|
"expression": extractCondFmtExp,
|
||||||
}
|
}
|
||||||
|
|
||||||
conditionalFormats := make(map[string]string)
|
conditionalFormats := make(map[string][]ConditionalFormatOptions)
|
||||||
ws, err := f.workSheetReader(sheet)
|
ws, err := f.workSheetReader(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return conditionalFormats, err
|
return conditionalFormats, err
|
||||||
}
|
}
|
||||||
for _, cf := range ws.ConditionalFormatting {
|
for _, cf := range ws.ConditionalFormatting {
|
||||||
var opts []*conditionalOptions
|
var opts []ConditionalFormatOptions
|
||||||
for _, cr := range cf.CfRule {
|
for _, cr := range cf.CfRule {
|
||||||
if extractFunc, ok := extractContFmtFunc[cr.Type]; ok {
|
if extractFunc, ok := extractContFmtFunc[cr.Type]; ok {
|
||||||
opts = append(opts, extractFunc(cr))
|
opts = append(opts, extractFunc(cr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options, _ := json.Marshal(opts)
|
conditionalFormats[cf.SQRef] = opts
|
||||||
conditionalFormats[cf.SQRef] = string(options)
|
|
||||||
}
|
}
|
||||||
return conditionalFormats, err
|
return conditionalFormats, err
|
||||||
}
|
}
|
||||||
|
@ -3248,7 +3384,7 @@ func (f *File) UnsetConditionalFormat(sheet, reference string) error {
|
||||||
// drawCondFmtCellIs provides a function to create conditional formatting rule
|
// drawCondFmtCellIs provides a function to create conditional formatting rule
|
||||||
// for cell value (include between, not between, equal, not equal, greater
|
// for cell value (include between, not between, equal, not equal, greater
|
||||||
// than and less than) by given priority, criteria type and format settings.
|
// than and less than) by given priority, criteria type and format settings.
|
||||||
func drawCondFmtCellIs(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtCellIs(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
c := &xlsxCfRule{
|
c := &xlsxCfRule{
|
||||||
Priority: p + 1,
|
Priority: p + 1,
|
||||||
Type: validType[format.Type],
|
Type: validType[format.Type],
|
||||||
|
@ -3268,7 +3404,7 @@ func drawCondFmtCellIs(p int, ct string, format *conditionalOptions) *xlsxCfRule
|
||||||
// drawCondFmtTop10 provides a function to create conditional formatting rule
|
// drawCondFmtTop10 provides a function to create conditional formatting rule
|
||||||
// for top N (default is top 10) by given priority, criteria type and format
|
// for top N (default is top 10) by given priority, criteria type and format
|
||||||
// settings.
|
// settings.
|
||||||
func drawCondFmtTop10(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtTop10(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
c := &xlsxCfRule{
|
c := &xlsxCfRule{
|
||||||
Priority: p + 1,
|
Priority: p + 1,
|
||||||
Bottom: format.Type == "bottom",
|
Bottom: format.Type == "bottom",
|
||||||
|
@ -3286,7 +3422,7 @@ func drawCondFmtTop10(p int, ct string, format *conditionalOptions) *xlsxCfRule
|
||||||
// drawCondFmtAboveAverage provides a function to create conditional
|
// drawCondFmtAboveAverage provides a function to create conditional
|
||||||
// formatting rule for above average and below average by given priority,
|
// formatting rule for above average and below average by given priority,
|
||||||
// criteria type and format settings.
|
// criteria type and format settings.
|
||||||
func drawCondFmtAboveAverage(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtAboveAverage(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
return &xlsxCfRule{
|
return &xlsxCfRule{
|
||||||
Priority: p + 1,
|
Priority: p + 1,
|
||||||
Type: validType[format.Type],
|
Type: validType[format.Type],
|
||||||
|
@ -3298,7 +3434,7 @@ func drawCondFmtAboveAverage(p int, ct string, format *conditionalOptions) *xlsx
|
||||||
// drawCondFmtDuplicateUniqueValues provides a function to create conditional
|
// drawCondFmtDuplicateUniqueValues provides a function to create conditional
|
||||||
// formatting rule for duplicate and unique values by given priority, criteria
|
// formatting rule for duplicate and unique values by given priority, criteria
|
||||||
// type and format settings.
|
// type and format settings.
|
||||||
func drawCondFmtDuplicateUniqueValues(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtDuplicateUniqueValues(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
return &xlsxCfRule{
|
return &xlsxCfRule{
|
||||||
Priority: p + 1,
|
Priority: p + 1,
|
||||||
Type: validType[format.Type],
|
Type: validType[format.Type],
|
||||||
|
@ -3309,7 +3445,7 @@ func drawCondFmtDuplicateUniqueValues(p int, ct string, format *conditionalOptio
|
||||||
// drawCondFmtColorScale provides a function to create conditional formatting
|
// drawCondFmtColorScale provides a function to create conditional formatting
|
||||||
// rule for color scale (include 2 color scale and 3 color scale) by given
|
// rule for color scale (include 2 color scale and 3 color scale) by given
|
||||||
// priority, criteria type and format settings.
|
// priority, criteria type and format settings.
|
||||||
func drawCondFmtColorScale(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtColorScale(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
minValue := format.MinValue
|
minValue := format.MinValue
|
||||||
if minValue == "" {
|
if minValue == "" {
|
||||||
minValue = "0"
|
minValue = "0"
|
||||||
|
@ -3346,7 +3482,7 @@ func drawCondFmtColorScale(p int, ct string, format *conditionalOptions) *xlsxCf
|
||||||
|
|
||||||
// drawCondFmtDataBar provides a function to create conditional formatting
|
// drawCondFmtDataBar provides a function to create conditional formatting
|
||||||
// rule for data bar by given priority, criteria type and format settings.
|
// rule for data bar by given priority, criteria type and format settings.
|
||||||
func drawCondFmtDataBar(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtDataBar(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
return &xlsxCfRule{
|
return &xlsxCfRule{
|
||||||
Priority: p + 1,
|
Priority: p + 1,
|
||||||
Type: validType[format.Type],
|
Type: validType[format.Type],
|
||||||
|
@ -3359,7 +3495,7 @@ func drawCondFmtDataBar(p int, ct string, format *conditionalOptions) *xlsxCfRul
|
||||||
|
|
||||||
// drawCondFmtExp provides a function to create conditional formatting rule
|
// drawCondFmtExp provides a function to create conditional formatting rule
|
||||||
// for expression by given priority, criteria type and format settings.
|
// for expression by given priority, criteria type and format settings.
|
||||||
func drawCondFmtExp(p int, ct string, format *conditionalOptions) *xlsxCfRule {
|
func drawCondFmtExp(p int, ct string, format *ConditionalFormatOptions) *xlsxCfRule {
|
||||||
return &xlsxCfRule{
|
return &xlsxCfRule{
|
||||||
Priority: p + 1,
|
Priority: p + 1,
|
||||||
Type: validType[format.Type],
|
Type: validType[format.Type],
|
||||||
|
|
125
styles_test.go
125
styles_test.go
|
@ -1,7 +1,6 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -13,15 +12,15 @@ import (
|
||||||
func TestStyleFill(t *testing.T) {
|
func TestStyleFill(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
label string
|
label string
|
||||||
format string
|
format *Style
|
||||||
expectFill bool
|
expectFill bool
|
||||||
}{{
|
}{{
|
||||||
label: "no_fill",
|
label: "no_fill",
|
||||||
format: `{"alignment":{"wrap_text":true}}`,
|
format: &Style{Alignment: &Alignment{WrapText: true}},
|
||||||
expectFill: false,
|
expectFill: false,
|
||||||
}, {
|
}, {
|
||||||
label: "fill",
|
label: "fill",
|
||||||
format: `{"fill":{"type":"pattern","pattern":1,"color":["#000000"]}}`,
|
format: &Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"#000000"}}},
|
||||||
expectFill: true,
|
expectFill: true,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -40,9 +39,9 @@ func TestStyleFill(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
styleID1, err := f.NewStyle(`{"fill":{"type":"pattern","pattern":1,"color":["#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(`{"fill":{"type":"pattern","pattern":1,"color":["#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")))
|
||||||
|
@ -51,23 +50,23 @@ func TestStyleFill(t *testing.T) {
|
||||||
func TestSetConditionalFormat(t *testing.T) {
|
func TestSetConditionalFormat(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
label string
|
label string
|
||||||
format string
|
format []ConditionalFormatOptions
|
||||||
rules []*xlsxCfRule
|
rules []*xlsxCfRule
|
||||||
}{{
|
}{{
|
||||||
label: "3_color_scale",
|
label: "3_color_scale",
|
||||||
format: `[{
|
format: []ConditionalFormatOptions{{
|
||||||
"type":"3_color_scale",
|
Type: "3_color_scale",
|
||||||
"criteria":"=",
|
Criteria: "=",
|
||||||
"min_type":"num",
|
MinType: "num",
|
||||||
"mid_type":"num",
|
MidType: "num",
|
||||||
"max_type":"num",
|
MaxType: "num",
|
||||||
"min_value": "-10",
|
MinValue: "-10",
|
||||||
"mid_value": "0",
|
MidValue: "0",
|
||||||
"max_value": "10",
|
MaxValue: "10",
|
||||||
"min_color":"ff0000",
|
MinColor: "ff0000",
|
||||||
"mid_color":"00ff00",
|
MidColor: "00ff00",
|
||||||
"max_color":"0000ff"
|
MaxColor: "0000ff",
|
||||||
}]`,
|
}},
|
||||||
rules: []*xlsxCfRule{{
|
rules: []*xlsxCfRule{{
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Type: "colorScale",
|
Type: "colorScale",
|
||||||
|
@ -93,16 +92,16 @@ func TestSetConditionalFormat(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
label: "3_color_scale default min/mid/max",
|
label: "3_color_scale default min/mid/max",
|
||||||
format: `[{
|
format: []ConditionalFormatOptions{{
|
||||||
"type":"3_color_scale",
|
Type: "3_color_scale",
|
||||||
"criteria":"=",
|
Criteria: "=",
|
||||||
"min_type":"num",
|
MinType: "num",
|
||||||
"mid_type":"num",
|
MidType: "num",
|
||||||
"max_type":"num",
|
MaxType: "num",
|
||||||
"min_color":"ff0000",
|
MinColor: "ff0000",
|
||||||
"mid_color":"00ff00",
|
MidColor: "00ff00",
|
||||||
"max_color":"0000ff"
|
MaxColor: "0000ff",
|
||||||
}]`,
|
}},
|
||||||
rules: []*xlsxCfRule{{
|
rules: []*xlsxCfRule{{
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Type: "colorScale",
|
Type: "colorScale",
|
||||||
|
@ -128,14 +127,14 @@ func TestSetConditionalFormat(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
label: "2_color_scale default min/max",
|
label: "2_color_scale default min/max",
|
||||||
format: `[{
|
format: []ConditionalFormatOptions{{
|
||||||
"type":"2_color_scale",
|
Type: "2_color_scale",
|
||||||
"criteria":"=",
|
Criteria: "=",
|
||||||
"min_type":"num",
|
MinType: "num",
|
||||||
"max_type":"num",
|
MaxType: "num",
|
||||||
"min_color":"ff0000",
|
MinColor: "ff0000",
|
||||||
"max_color":"0000ff"
|
MaxColor: "0000ff",
|
||||||
}]`,
|
}},
|
||||||
rules: []*xlsxCfRule{{
|
rules: []*xlsxCfRule{{
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Type: "colorScale",
|
Type: "colorScale",
|
||||||
|
@ -177,18 +176,18 @@ func TestSetConditionalFormat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetConditionalFormats(t *testing.T) {
|
func TestGetConditionalFormats(t *testing.T) {
|
||||||
for _, format := range []string{
|
for _, format := range [][]ConditionalFormatOptions{
|
||||||
`[{"type":"cell","format":1,"criteria":"greater than","value":"6"}]`,
|
{{Type: "cell", Format: 1, Criteria: "greater than", Value: "6"}},
|
||||||
`[{"type":"cell","format":1,"criteria":"between","minimum":"6","maximum":"8"}]`,
|
{{Type: "cell", Format: 1, Criteria: "between", Minimum: "6", Maximum: "8"}},
|
||||||
`[{"type":"top","format":1,"criteria":"=","value":"6"}]`,
|
{{Type: "top", Format: 1, Criteria: "=", Value: "6"}},
|
||||||
`[{"type":"bottom","format":1,"criteria":"=","value":"6"}]`,
|
{{Type: "bottom", Format: 1, Criteria: "=", Value: "6"}},
|
||||||
`[{"type":"average","above_average":true,"format":1,"criteria":"="}]`,
|
{{Type: "average", AboveAverage: true, Format: 1, Criteria: "="}},
|
||||||
`[{"type":"duplicate","format":1,"criteria":"="}]`,
|
{{Type: "duplicate", Format: 1, Criteria: "="}},
|
||||||
`[{"type":"unique","format":1,"criteria":"="}]`,
|
{{Type: "unique", Format: 1, Criteria: "="}},
|
||||||
`[{"type":"3_color_scale","criteria":"=","min_type":"num","mid_type":"num","max_type":"num","min_value":"-10","mid_value":"50","max_value":"10","min_color":"#FF0000","mid_color":"#00FF00","max_color":"#0000FF"}]`,
|
{{Type: "3_color_scale", Criteria: "=", MinType: "num", MidType: "num", MaxType: "num", MinValue: "-10", MidValue: "50", MaxValue: "10", MinColor: "#FF0000", MidColor: "#00FF00", MaxColor: "#0000FF"}},
|
||||||
`[{"type":"2_color_scale","criteria":"=","min_type":"num","max_type":"num","min_color":"#FF0000","max_color":"#0000FF"}]`,
|
{{Type: "2_color_scale", Criteria: "=", MinType: "num", MaxType: "num", MinColor: "#FF0000", MaxColor: "#0000FF"}},
|
||||||
`[{"type":"data_bar","criteria":"=","min_type":"min","max_type":"max","bar_color":"#638EC6"}]`,
|
{{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarColor: "#638EC6"}},
|
||||||
`[{"type":"formula","format":1,"criteria":"="}]`,
|
{{Type: "formula", Format: 1, Criteria: "="}},
|
||||||
} {
|
} {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
err := f.SetConditionalFormat("Sheet1", "A1:A2", format)
|
err := f.SetConditionalFormat("Sheet1", "A1:A2", format)
|
||||||
|
@ -210,9 +209,9 @@ 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(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#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", fmt.Sprintf(`[{"type":"cell","criteria":">","format":%d,"value":"6"}]`, format)))
|
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"))
|
||||||
// Test unset conditional format on not exists worksheet
|
// Test unset conditional format on not exists worksheet
|
||||||
assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN does not exist")
|
||||||
|
@ -224,7 +223,7 @@ func TestUnsetConditionalFormat(t *testing.T) {
|
||||||
|
|
||||||
func TestNewStyle(t *testing.T) {
|
func TestNewStyle(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
styleID, err := f.NewStyle(`{"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)
|
||||||
|
@ -234,8 +233,8 @@ func TestNewStyle(t *testing.T) {
|
||||||
assert.Equal(t, 2, styles.CellXfs.Count, "Should have 2 styles")
|
assert.Equal(t, 2, styles.CellXfs.Count, "Should have 2 styles")
|
||||||
_, err = f.NewStyle(&Style{})
|
_, err = f.NewStyle(&Style{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = f.NewStyle(Style{})
|
_, err = f.NewStyle(nil)
|
||||||
assert.EqualError(t, err, ErrParameterInvalid.Error())
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var exp string
|
var exp string
|
||||||
_, err = f.NewStyle(&Style{CustomNumFmt: &exp})
|
_, err = f.NewStyle(&Style{CustomNumFmt: &exp})
|
||||||
|
@ -326,7 +325,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(`{"font":{"color":"#9A0511"},"fill":{"type":"pattern","color":["#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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,13 +377,13 @@ func TestThemeReader(t *testing.T) {
|
||||||
|
|
||||||
func TestSetCellStyle(t *testing.T) {
|
func TestSetCellStyle(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test set cell style on not exists worksheet.
|
// Test set cell style on not exists worksheet
|
||||||
assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN does not exist")
|
||||||
// Test set cell style with invalid style ID.
|
// Test set cell style with invalid style ID
|
||||||
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", -1), newInvalidStyleID(-1).Error())
|
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", -1), newInvalidStyleID(-1).Error())
|
||||||
// Test set cell style with not exists style ID.
|
// Test set cell style with not exists style ID
|
||||||
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", 10), newInvalidStyleID(10).Error())
|
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", 10), newInvalidStyleID(10).Error())
|
||||||
// Test set cell style with unsupported charset style sheet.
|
// Test set cell style 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.SetCellStyle("Sheet1", "A1", "A2", 1), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", 1), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
@ -395,7 +394,7 @@ func TestGetStyleID(t *testing.T) {
|
||||||
styleID, err := f.getStyleID(&xlsxStyleSheet{}, nil)
|
styleID, err := f.getStyleID(&xlsxStyleSheet{}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, -1, styleID)
|
assert.Equal(t, -1, styleID)
|
||||||
// Test get style ID with unsupported charset style sheet.
|
// Test get style ID with unsupported charset style sheet
|
||||||
f.Styles = nil
|
f.Styles = nil
|
||||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||||
_, err = f.getStyleID(&xlsxStyleSheet{
|
_, err = f.getStyleID(&xlsxStyleSheet{
|
||||||
|
@ -429,11 +428,11 @@ func TestThemeColor(t *testing.T) {
|
||||||
func TestGetNumFmtID(t *testing.T) {
|
func TestGetNumFmtID(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
|
|
||||||
fs1, err := parseFormatStyleSet(`{"protection":{"hidden":false,"locked":false},"number_format":10}`)
|
fs1, err := parseFormatStyleSet(&Style{Protection: &Protection{Hidden: false, Locked: false}, NumFmt: 10})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
id1 := getNumFmtID(&xlsxStyleSheet{}, fs1)
|
id1 := getNumFmtID(&xlsxStyleSheet{}, fs1)
|
||||||
|
|
||||||
fs2, err := parseFormatStyleSet(`{"protection":{"hidden":false,"locked":false},"number_format":0}`)
|
fs2, err := parseFormatStyleSet(&Style{Protection: &Protection{Hidden: false, Locked: false}, NumFmt: 0})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
id2 := getNumFmtID(&xlsxStyleSheet{}, fs2)
|
id2 := getNumFmtID(&xlsxStyleSheet{}, fs2)
|
||||||
|
|
||||||
|
|
122
table.go
122
table.go
|
@ -12,7 +12,6 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -22,64 +21,54 @@ import (
|
||||||
|
|
||||||
// parseTableOptions provides a function to parse the format settings of the
|
// parseTableOptions provides a function to parse the format settings of the
|
||||||
// table with default value.
|
// table with default value.
|
||||||
func parseTableOptions(opts string) (*tableOptions, error) {
|
func parseTableOptions(opts *TableOptions) *TableOptions {
|
||||||
options := tableOptions{ShowRowStripes: true}
|
if opts == nil {
|
||||||
err := json.Unmarshal(fallbackOptions(opts), &options)
|
return &TableOptions{ShowRowStripes: boolPtr(true)}
|
||||||
return &options, err
|
}
|
||||||
|
if opts.ShowRowStripes == nil {
|
||||||
|
opts.ShowRowStripes = boolPtr(true)
|
||||||
|
}
|
||||||
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTable provides the method to add table in a worksheet by given worksheet
|
// AddTable provides the method to add table in a worksheet by given worksheet
|
||||||
// name, range reference and format set. For example, create a table of A1:D5
|
// name, range reference and format set. For example, create a table of A1:D5
|
||||||
// on Sheet1:
|
// on Sheet1:
|
||||||
//
|
//
|
||||||
// err := f.AddTable("Sheet1", "A1", "D5", "")
|
// err := f.AddTable("Sheet1", "A1:D5", nil)
|
||||||
//
|
//
|
||||||
// Create a table of F2:H6 on Sheet2 with format set:
|
// Create a table of F2:H6 on Sheet2 with format set:
|
||||||
//
|
//
|
||||||
// err := f.AddTable("Sheet2", "F2", "H6", `{
|
// err := f.AddTable("Sheet2", "F2:H6", &excelize.TableOptions{
|
||||||
// "table_name": "table",
|
// Name: "table",
|
||||||
// "table_style": "TableStyleMedium2",
|
// StyleName: "TableStyleMedium2",
|
||||||
// "show_first_column": true,
|
// ShowFirstColumn: true,
|
||||||
// "show_last_column": true,
|
// ShowLastColumn: true,
|
||||||
// "show_row_stripes": false,
|
// ShowRowStripes: &disable,
|
||||||
// "show_column_stripes": true
|
// ShowColumnStripes: true,
|
||||||
// }`)
|
// })
|
||||||
//
|
//
|
||||||
// Note that the table must be at least two lines including the header. The
|
// Note that the table must be at least two lines including the header. The
|
||||||
// header cells must contain strings and must be unique, and must set the
|
// header cells must contain strings and must be unique, and must set the
|
||||||
// header row data of the table before calling the AddTable function. Multiple
|
// header row data of the table before calling the AddTable function. Multiple
|
||||||
// tables range reference that can't have an intersection.
|
// tables range reference that can't have an intersection.
|
||||||
//
|
//
|
||||||
// table_name: The name of the table, in the same worksheet name of the table should be unique
|
// Name: The name of the table, in the same worksheet name of the table should be unique
|
||||||
//
|
//
|
||||||
// table_style: The built-in table style names
|
// StyleName: The built-in table style names
|
||||||
//
|
//
|
||||||
// TableStyleLight1 - TableStyleLight21
|
// TableStyleLight1 - TableStyleLight21
|
||||||
// TableStyleMedium1 - TableStyleMedium28
|
// TableStyleMedium1 - TableStyleMedium28
|
||||||
// TableStyleDark1 - TableStyleDark11
|
// TableStyleDark1 - TableStyleDark11
|
||||||
func (f *File) AddTable(sheet, hCell, vCell, opts string) error {
|
func (f *File) AddTable(sheet, reference string, opts *TableOptions) error {
|
||||||
options, err := parseTableOptions(opts)
|
options := parseTableOptions(opts)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
||||||
hCol, hRow, err := CellNameToCoordinates(hCell)
|
coordinates, err := rangeRefToCoordinates(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vCol, vRow, err := CellNameToCoordinates(vCell)
|
// Correct table reference range, such correct C1:B3 to B1:C3.
|
||||||
if err != nil {
|
_ = sortCoordinates(coordinates)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if vCol < hCol {
|
|
||||||
vCol, hCol = hCol, vCol
|
|
||||||
}
|
|
||||||
|
|
||||||
if vRow < hRow {
|
|
||||||
vRow, hRow = hRow, vRow
|
|
||||||
}
|
|
||||||
|
|
||||||
tableID := f.countTables() + 1
|
tableID := f.countTables() + 1
|
||||||
sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
|
sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
|
||||||
tableXML := strings.ReplaceAll(sheetRelationshipsTableXML, "..", "xl")
|
tableXML := strings.ReplaceAll(sheetRelationshipsTableXML, "..", "xl")
|
||||||
|
@ -91,7 +80,7 @@ func (f *File) AddTable(sheet, hCell, vCell, opts string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.addSheetNameSpace(sheet, SourceRelationship)
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
||||||
if err = f.addTable(sheet, tableXML, hCol, hRow, vCol, vRow, tableID, options); err != nil {
|
if err = f.addTable(sheet, tableXML, coordinates[0], coordinates[1], coordinates[2], coordinates[3], tableID, options); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return f.addContentTypePart(tableID, "table")
|
return f.addContentTypePart(tableID, "table")
|
||||||
|
@ -159,7 +148,7 @@ func (f *File) setTableHeader(sheet string, x1, y1, x2 int) ([]*xlsxTableColumn,
|
||||||
|
|
||||||
// addTable provides a function to add table by given worksheet name,
|
// addTable provides a function to add table by given worksheet name,
|
||||||
// range reference and format set.
|
// range reference and format set.
|
||||||
func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *tableOptions) error {
|
func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *TableOptions) error {
|
||||||
// Correct the minimum number of rows, the table at least two lines.
|
// Correct the minimum number of rows, the table at least two lines.
|
||||||
if y1 == y2 {
|
if y1 == y2 {
|
||||||
y2++
|
y2++
|
||||||
|
@ -171,7 +160,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *tab
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tableColumns, _ := f.setTableHeader(sheet, x1, y1, x2)
|
tableColumns, _ := f.setTableHeader(sheet, x1, y1, x2)
|
||||||
name := opts.TableName
|
name := opts.Name
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "Table" + strconv.Itoa(i)
|
name = "Table" + strconv.Itoa(i)
|
||||||
}
|
}
|
||||||
|
@ -189,10 +178,10 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *tab
|
||||||
TableColumn: tableColumns,
|
TableColumn: tableColumns,
|
||||||
},
|
},
|
||||||
TableStyleInfo: &xlsxTableStyleInfo{
|
TableStyleInfo: &xlsxTableStyleInfo{
|
||||||
Name: opts.TableStyle,
|
Name: opts.StyleName,
|
||||||
ShowFirstColumn: opts.ShowFirstColumn,
|
ShowFirstColumn: opts.ShowFirstColumn,
|
||||||
ShowLastColumn: opts.ShowLastColumn,
|
ShowLastColumn: opts.ShowLastColumn,
|
||||||
ShowRowStripes: opts.ShowRowStripes,
|
ShowRowStripes: *opts.ShowRowStripes,
|
||||||
ShowColumnStripes: opts.ShowColumnStripes,
|
ShowColumnStripes: opts.ShowColumnStripes,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -201,36 +190,30 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *tab
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseAutoFilterOptions provides a function to parse the settings of the auto
|
|
||||||
// filter.
|
|
||||||
func parseAutoFilterOptions(opts string) (*autoFilterOptions, error) {
|
|
||||||
options := autoFilterOptions{}
|
|
||||||
err := json.Unmarshal([]byte(opts), &options)
|
|
||||||
return &options, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoFilter provides the method to add auto filter in a worksheet by given
|
// AutoFilter provides the method to add auto filter in a worksheet by given
|
||||||
// worksheet name, range reference and settings. An auto filter in Excel is a
|
// worksheet name, range reference and settings. An auto filter in Excel is a
|
||||||
// way of filtering a 2D range of data based on some simple criteria. For
|
// way of filtering a 2D range of data based on some simple criteria. For
|
||||||
// example applying an auto filter to a cell range A1:D4 in the Sheet1:
|
// example applying an auto filter to a cell range A1:D4 in the Sheet1:
|
||||||
//
|
//
|
||||||
// err := f.AutoFilter("Sheet1", "A1", "D4", "")
|
// err := f.AutoFilter("Sheet1", "A1:D4", nil)
|
||||||
//
|
//
|
||||||
// Filter data in an auto filter:
|
// Filter data in an auto filter:
|
||||||
//
|
//
|
||||||
// err := f.AutoFilter("Sheet1", "A1", "D4", `{"column":"B","expression":"x != blanks"}`)
|
// err := f.AutoFilter("Sheet1", "A1:D4", &excelize.AutoFilterOptions{
|
||||||
|
// Column: "B", Expression: "x != blanks",
|
||||||
|
// })
|
||||||
//
|
//
|
||||||
// column defines the filter columns in an auto filter range based on simple
|
// Column defines the filter columns in an auto filter range based on simple
|
||||||
// criteria
|
// criteria
|
||||||
//
|
//
|
||||||
// It isn't sufficient to just specify the filter condition. You must also
|
// It isn't sufficient to just specify the filter condition. You must also
|
||||||
// hide any rows that don't match the filter condition. Rows are hidden using
|
// hide any rows that don't match the filter condition. Rows are hidden using
|
||||||
// the SetRowVisible() method. Excelize can't filter rows automatically since
|
// the SetRowVisible function. Excelize can't filter rows automatically since
|
||||||
// this isn't part of the file format.
|
// this isn't part of the file format.
|
||||||
//
|
//
|
||||||
// Setting a filter criteria for a column:
|
// Setting a filter criteria for a column:
|
||||||
//
|
//
|
||||||
// expression defines the conditions, the following operators are available
|
// Expression defines the conditions, the following operators are available
|
||||||
// for setting the filter criteria:
|
// for setting the filter criteria:
|
||||||
//
|
//
|
||||||
// ==
|
// ==
|
||||||
|
@ -278,28 +261,15 @@ func parseAutoFilterOptions(opts string) (*autoFilterOptions, error) {
|
||||||
// x < 2000
|
// x < 2000
|
||||||
// col < 2000
|
// col < 2000
|
||||||
// Price < 2000
|
// Price < 2000
|
||||||
func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error {
|
func (f *File) AutoFilter(sheet, reference string, opts *AutoFilterOptions) error {
|
||||||
hCol, hRow, err := CellNameToCoordinates(hCell)
|
coordinates, err := rangeRefToCoordinates(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vCol, vRow, err := CellNameToCoordinates(vCell)
|
_ = sortCoordinates(coordinates)
|
||||||
if err != nil {
|
// Correct reference range, such correct C1:B3 to B1:C3.
|
||||||
return err
|
ref, _ := f.coordinatesToRangeRef(coordinates, true)
|
||||||
}
|
filterDB := "_xlnm._FilterDatabase"
|
||||||
|
|
||||||
if vCol < hCol {
|
|
||||||
vCol, hCol = hCol, vCol
|
|
||||||
}
|
|
||||||
|
|
||||||
if vRow < hRow {
|
|
||||||
vRow, hRow = hRow, vRow
|
|
||||||
}
|
|
||||||
|
|
||||||
options, _ := parseAutoFilterOptions(opts)
|
|
||||||
cellStart, _ := CoordinatesToCellName(hCol, hRow, true)
|
|
||||||
cellEnd, _ := CoordinatesToCellName(vCol, vRow, true)
|
|
||||||
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
|
|
||||||
wb, err := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -332,13 +302,13 @@ func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error {
|
||||||
wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d)
|
wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refRange := vCol - hCol
|
refRange := coordinates[2] - coordinates[0]
|
||||||
return f.autoFilter(sheet, ref, refRange, hCol, options)
|
return f.autoFilter(sheet, ref, refRange, coordinates[0], opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// autoFilter provides a function to extract the tokens from the filter
|
// autoFilter provides a function to extract the tokens from the filter
|
||||||
// expression. The tokens are mainly non-whitespace groups.
|
// expression. The tokens are mainly non-whitespace groups.
|
||||||
func (f *File) autoFilter(sheet, ref string, refRange, col int, opts *autoFilterOptions) error {
|
func (f *File) autoFilter(sheet, ref string, refRange, col int, opts *AutoFilterOptions) error {
|
||||||
ws, err := f.workSheetReader(sheet)
|
ws, err := f.workSheetReader(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -351,7 +321,7 @@ func (f *File) autoFilter(sheet, ref string, refRange, col int, opts *autoFilter
|
||||||
Ref: ref,
|
Ref: ref,
|
||||||
}
|
}
|
||||||
ws.AutoFilter = filter
|
ws.AutoFilter = filter
|
||||||
if opts.Column == "" || opts.Expression == "" {
|
if opts == nil || opts.Column == "" || opts.Expression == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,22 +11,28 @@ import (
|
||||||
func TestAddTable(t *testing.T) {
|
func TestAddTable(t *testing.T) {
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.AddTable("Sheet1", "B26", "A21", `{}`))
|
assert.NoError(t, f.AddTable("Sheet1", "B26:A21", nil))
|
||||||
assert.NoError(t, f.AddTable("Sheet2", "A2", "B5", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`))
|
assert.NoError(t, f.AddTable("Sheet2", "A2:B5", &TableOptions{
|
||||||
assert.NoError(t, f.AddTable("Sheet2", "F1", "F1", `{"table_style":"TableStyleMedium8"}`))
|
Name: "table",
|
||||||
|
StyleName: "TableStyleMedium2",
|
||||||
|
ShowFirstColumn: true,
|
||||||
|
ShowLastColumn: true,
|
||||||
|
ShowRowStripes: boolPtr(true),
|
||||||
|
ShowColumnStripes: true,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
assert.NoError(t, f.AddTable("Sheet2", "F1:F1", &TableOptions{StyleName: "TableStyleMedium8"}))
|
||||||
|
|
||||||
// Test add table in not exist worksheet
|
// Test add table in not exist worksheet
|
||||||
assert.EqualError(t, f.AddTable("SheetN", "B26", "A21", `{}`), "sheet SheetN does not exist")
|
assert.EqualError(t, f.AddTable("SheetN", "B26:A21", nil), "sheet SheetN does not exist")
|
||||||
// Test add table with illegal options
|
|
||||||
assert.EqualError(t, f.AddTable("Sheet1", "B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string")
|
|
||||||
// Test add table with illegal cell reference
|
// Test add table with illegal cell reference
|
||||||
assert.EqualError(t, f.AddTable("Sheet1", "A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AddTable("Sheet1", "A:B1", nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.EqualError(t, f.AddTable("Sheet1", "A1", "B", `{}`), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.EqualError(t, f.AddTable("Sheet1", "A1:B", nil), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||||
|
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx")))
|
||||||
|
|
||||||
// Test add table with invalid sheet name
|
// Test add table with invalid sheet name
|
||||||
assert.EqualError(t, f.AddTable("Sheet:1", "B26", "A21", `{}`), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AddTable("Sheet:1", "B26:A21", nil), ErrSheetNameInvalid.Error())
|
||||||
// Test addTable with illegal cell reference
|
// Test addTable with illegal cell reference
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
assert.EqualError(t, f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil), "invalid cell reference [0, 0]")
|
assert.EqualError(t, f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil), "invalid cell reference [0, 0]")
|
||||||
|
@ -43,73 +49,66 @@ func TestAutoFilter(t *testing.T) {
|
||||||
outFile := filepath.Join("test", "TestAutoFilter%d.xlsx")
|
outFile := filepath.Join("test", "TestAutoFilter%d.xlsx")
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
formats := []string{
|
for i, opts := range []*AutoFilterOptions{
|
||||||
``,
|
nil,
|
||||||
`{"column":"B","expression":"x != blanks"}`,
|
{Column: "B", Expression: ""},
|
||||||
`{"column":"B","expression":"x == blanks"}`,
|
{Column: "B", Expression: "x != blanks"},
|
||||||
`{"column":"B","expression":"x != nonblanks"}`,
|
{Column: "B", Expression: "x == blanks"},
|
||||||
`{"column":"B","expression":"x == nonblanks"}`,
|
{Column: "B", Expression: "x != nonblanks"},
|
||||||
`{"column":"B","expression":"x <= 1 and x >= 2"}`,
|
{Column: "B", Expression: "x == nonblanks"},
|
||||||
`{"column":"B","expression":"x == 1 or x == 2"}`,
|
{Column: "B", Expression: "x <= 1 and x >= 2"},
|
||||||
`{"column":"B","expression":"x == 1 or x == 2*"}`,
|
{Column: "B", Expression: "x == 1 or x == 2"},
|
||||||
}
|
{Column: "B", Expression: "x == 1 or x == 2*"},
|
||||||
for i, format := range formats {
|
} {
|
||||||
t.Run(fmt.Sprintf("Expression%d", i+1), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Expression%d", i+1), func(t *testing.T) {
|
||||||
err = f.AutoFilter("Sheet1", "D4", "B1", format)
|
assert.NoError(t, f.AutoFilter("Sheet1", "D4:B1", opts))
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, i+1)))
|
assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, i+1)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test add auto filter with invalid sheet name
|
// Test add auto filter with invalid sheet name
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet:1", "A1", "B1", ""), ErrSheetNameInvalid.Error())
|
assert.EqualError(t, f.AutoFilter("Sheet:1", "A1:B1", nil), ErrSheetNameInvalid.Error())
|
||||||
// Test add auto filter with illegal cell reference
|
// Test add auto filter with illegal cell reference
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "A", "B1", ""), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.EqualError(t, f.AutoFilter("Sheet1", "A:B1", nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "A1", "B", ""), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.EqualError(t, f.AutoFilter("Sheet1", "A1:B", nil), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||||
// Test add auto filter with unsupported charset workbook
|
// Test add auto filter with unsupported charset workbook
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "D4", "B1", formats[0]), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.AutoFilter("Sheet1", "D4:B1", nil), "XML syntax error on line 1: invalid UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAutoFilterError(t *testing.T) {
|
func TestAutoFilterError(t *testing.T) {
|
||||||
outFile := filepath.Join("test", "TestAutoFilterError%d.xlsx")
|
outFile := filepath.Join("test", "TestAutoFilterError%d.xlsx")
|
||||||
|
|
||||||
f, err := prepareTestBook1()
|
f, err := prepareTestBook1()
|
||||||
if !assert.NoError(t, err) {
|
assert.NoError(t, err)
|
||||||
t.FailNow()
|
for i, opts := range []*AutoFilterOptions{
|
||||||
}
|
{Column: "B", Expression: "x <= 1 and x >= blanks"},
|
||||||
|
{Column: "B", Expression: "x -- y or x == *2*"},
|
||||||
formats := []string{
|
{Column: "B", Expression: "x != y or x ? *2"},
|
||||||
`{"column":"B","expression":"x <= 1 and x >= blanks"}`,
|
{Column: "B", Expression: "x -- y o r x == *2"},
|
||||||
`{"column":"B","expression":"x -- y or x == *2*"}`,
|
{Column: "B", Expression: "x -- y"},
|
||||||
`{"column":"B","expression":"x != y or x ? *2"}`,
|
{Column: "A", Expression: "x -- y"},
|
||||||
`{"column":"B","expression":"x -- y o r x == *2"}`,
|
} {
|
||||||
`{"column":"B","expression":"x -- y"}`,
|
|
||||||
`{"column":"A","expression":"x -- y"}`,
|
|
||||||
}
|
|
||||||
for i, format := range formats {
|
|
||||||
t.Run(fmt.Sprintf("Expression%d", i+1), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Expression%d", i+1), func(t *testing.T) {
|
||||||
err = f.AutoFilter("Sheet2", "D4", "B1", format)
|
if assert.Error(t, f.AutoFilter("Sheet2", "D4:B1", opts)) {
|
||||||
if assert.Error(t, err) {
|
|
||||||
assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, i+1)))
|
assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, i+1)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.EqualError(t, f.autoFilter("SheetN", "A1", 1, 1, &autoFilterOptions{
|
assert.EqualError(t, f.autoFilter("SheetN", "A1", 1, 1, &AutoFilterOptions{
|
||||||
Column: "A",
|
Column: "A",
|
||||||
Expression: "",
|
Expression: "",
|
||||||
}), "sheet SheetN does not exist")
|
}), "sheet SheetN does not exist")
|
||||||
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &autoFilterOptions{
|
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &AutoFilterOptions{
|
||||||
Column: "-",
|
Column: "-",
|
||||||
Expression: "-",
|
Expression: "-",
|
||||||
}), newInvalidColumnNameError("-").Error())
|
}), newInvalidColumnNameError("-").Error())
|
||||||
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 100, &autoFilterOptions{
|
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 100, &AutoFilterOptions{
|
||||||
Column: "A",
|
Column: "A",
|
||||||
Expression: "-",
|
Expression: "-",
|
||||||
}), `incorrect index of column 'A'`)
|
}), `incorrect index of column 'A'`)
|
||||||
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &autoFilterOptions{
|
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &AutoFilterOptions{
|
||||||
Column: "A",
|
Column: "A",
|
||||||
Expression: "-",
|
Expression: "-",
|
||||||
}), `incorrect number of tokens in criteria '-'`)
|
}), `incorrect number of tokens in criteria '-'`)
|
||||||
|
|
18
workbook.go
18
workbook.go
|
@ -59,8 +59,18 @@ func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
|
||||||
return opts, err
|
return opts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtectWorkbook provides a function to prevent other users from accidentally or
|
// ProtectWorkbook provides a function to prevent other users from viewing
|
||||||
// deliberately changing, moving, or deleting data in a workbook.
|
// hidden worksheets, adding, moving, deleting, or hiding worksheets, and
|
||||||
|
// renaming worksheets in a workbook. The optional field AlgorithmName
|
||||||
|
// specified hash algorithm, support XOR, MD4, MD5, SHA-1, SHA2-56, SHA-384,
|
||||||
|
// and SHA-512 currently, if no hash algorithm specified, will be using the XOR
|
||||||
|
// algorithm as default. The generated workbook only works on Microsoft Office
|
||||||
|
// 2007 and later. For example, protect workbook with protection settings:
|
||||||
|
//
|
||||||
|
// err := f.ProtectWorkbook(&excelize.WorkbookProtectionOptions{
|
||||||
|
// Password: "password",
|
||||||
|
// LockStructure: true,
|
||||||
|
// })
|
||||||
func (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {
|
func (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {
|
||||||
wb, err := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,8 +103,8 @@ func (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnprotectWorkbook provides a function to remove protection for workbook,
|
// UnprotectWorkbook provides a function to remove protection for workbook,
|
||||||
// specified the second optional password parameter to remove workbook
|
// specified the optional password parameter to remove workbook protection with
|
||||||
// protection with password verification.
|
// password verification.
|
||||||
func (f *File) UnprotectWorkbook(password ...string) error {
|
func (f *File) UnprotectWorkbook(password ...string) error {
|
||||||
wb, err := f.workbookReader()
|
wb, err := f.workbookReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -21,11 +21,11 @@ func TestWorkbookProps(t *testing.T) {
|
||||||
opts, err := f.GetWorkbookProps()
|
opts, err := f.GetWorkbookProps()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expected, opts)
|
assert.Equal(t, expected, opts)
|
||||||
// Test set workbook properties with unsupported charset workbook.
|
// Test set workbook properties with unsupported charset workbook
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
assert.EqualError(t, f.SetWorkbookProps(&expected), "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, f.SetWorkbookProps(&expected), "XML syntax error on line 1: invalid UTF-8")
|
||||||
// Test get workbook properties with unsupported charset workbook.
|
// Test get workbook properties with unsupported charset workbook
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
_, err = f.GetWorkbookProps()
|
_, err = f.GetWorkbookProps()
|
||||||
|
|
14
xmlApp.go
14
xmlApp.go
|
@ -15,13 +15,13 @@ import "encoding/xml"
|
||||||
|
|
||||||
// AppProperties directly maps the document application properties.
|
// AppProperties directly maps the document application properties.
|
||||||
type AppProperties struct {
|
type AppProperties struct {
|
||||||
Application string `json:"application"`
|
Application string
|
||||||
ScaleCrop bool `json:"scale_crop"`
|
ScaleCrop bool
|
||||||
DocSecurity int `json:"doc_security"`
|
DocSecurity int
|
||||||
Company string `json:"company"`
|
Company string
|
||||||
LinksUpToDate bool `json:"links_up_to_date"`
|
LinksUpToDate bool
|
||||||
HyperlinksChanged bool `json:"hyperlinks_changed"`
|
HyperlinksChanged bool
|
||||||
AppVersion string `json:"app_version"`
|
AppVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
// xlsxProperties specifies to an OOXML document properties such as the
|
// xlsxProperties specifies to an OOXML document properties such as the
|
||||||
|
|
187
xmlChart.go
187
xmlChart.go
|
@ -518,136 +518,83 @@ type cPageMargins struct {
|
||||||
T float64 `xml:"t,attr"`
|
T float64 `xml:"t,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// chartAxisOptions directly maps the format settings of the chart axis.
|
// ChartAxis directly maps the format settings of the chart axis.
|
||||||
type chartAxisOptions struct {
|
type ChartAxis struct {
|
||||||
None bool `json:"none"`
|
None bool
|
||||||
Crossing string `json:"crossing"`
|
MajorGridLines bool
|
||||||
MajorGridlines bool `json:"major_grid_lines"`
|
MinorGridLines bool
|
||||||
MinorGridlines bool `json:"minor_grid_lines"`
|
MajorUnit float64
|
||||||
MajorTickMark string `json:"major_tick_mark"`
|
TickLabelSkip int
|
||||||
MinorTickMark string `json:"minor_tick_mark"`
|
ReverseOrder bool
|
||||||
MinorUnitType string `json:"minor_unit_type"`
|
Maximum *float64
|
||||||
MajorUnit float64 `json:"major_unit"`
|
Minimum *float64
|
||||||
MajorUnitType string `json:"major_unit_type"`
|
Font Font
|
||||||
TickLabelSkip int `json:"tick_label_skip"`
|
LogBase float64
|
||||||
DisplayUnits string `json:"display_units"`
|
|
||||||
DisplayUnitsVisible bool `json:"display_units_visible"`
|
|
||||||
DateAxis bool `json:"date_axis"`
|
|
||||||
ReverseOrder bool `json:"reverse_order"`
|
|
||||||
Maximum *float64 `json:"maximum"`
|
|
||||||
Minimum *float64 `json:"minimum"`
|
|
||||||
NumFormat string `json:"number_format"`
|
|
||||||
Font Font `json:"font"`
|
|
||||||
LogBase float64 `json:"logbase"`
|
|
||||||
NameLayout layoutOptions `json:"name_layout"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// chartDimensionOptions directly maps the dimension of the chart.
|
// ChartDimension directly maps the dimension of the chart.
|
||||||
type chartDimensionOptions struct {
|
type ChartDimension struct {
|
||||||
Width int `json:"width"`
|
Width *int
|
||||||
Height int `json:"height"`
|
Height *int
|
||||||
}
|
}
|
||||||
|
|
||||||
// chartOptions directly maps the format settings of the chart.
|
// ChartPlotArea directly maps the format settings of the plot area.
|
||||||
type chartOptions struct {
|
type ChartPlotArea struct {
|
||||||
Type string `json:"type"`
|
ShowBubbleSize bool
|
||||||
Series []chartSeriesOptions `json:"series"`
|
ShowCatName bool
|
||||||
Format pictureOptions `json:"format"`
|
ShowLeaderLines bool
|
||||||
Dimension chartDimensionOptions `json:"dimension"`
|
ShowPercent bool
|
||||||
Legend chartLegendOptions `json:"legend"`
|
ShowSerName bool
|
||||||
Title chartTitleOptions `json:"title"`
|
ShowVal bool
|
||||||
VaryColors bool `json:"vary_colors"`
|
|
||||||
XAxis chartAxisOptions `json:"x_axis"`
|
|
||||||
YAxis chartAxisOptions `json:"y_axis"`
|
|
||||||
Chartarea struct {
|
|
||||||
Border struct {
|
|
||||||
None bool `json:"none"`
|
|
||||||
} `json:"border"`
|
|
||||||
Fill struct {
|
|
||||||
Color string `json:"color"`
|
|
||||||
} `json:"fill"`
|
|
||||||
Pattern struct {
|
|
||||||
Pattern string `json:"pattern"`
|
|
||||||
FgColor string `json:"fg_color"`
|
|
||||||
BgColor string `json:"bg_color"`
|
|
||||||
} `json:"pattern"`
|
|
||||||
} `json:"chartarea"`
|
|
||||||
Plotarea struct {
|
|
||||||
ShowBubbleSize bool `json:"show_bubble_size"`
|
|
||||||
ShowCatName bool `json:"show_cat_name"`
|
|
||||||
ShowLeaderLines bool `json:"show_leader_lines"`
|
|
||||||
ShowPercent bool `json:"show_percent"`
|
|
||||||
ShowSerName bool `json:"show_series_name"`
|
|
||||||
ShowVal bool `json:"show_val"`
|
|
||||||
Gradient struct {
|
|
||||||
Colors []string `json:"colors"`
|
|
||||||
} `json:"gradient"`
|
|
||||||
Border struct {
|
|
||||||
Color string `json:"color"`
|
|
||||||
Width int `json:"width"`
|
|
||||||
DashType string `json:"dash_type"`
|
|
||||||
} `json:"border"`
|
|
||||||
Fill struct {
|
|
||||||
Color string `json:"color"`
|
|
||||||
} `json:"fill"`
|
|
||||||
Layout layoutOptions `json:"layout"`
|
|
||||||
} `json:"plotarea"`
|
|
||||||
ShowBlanksAs string `json:"show_blanks_as"`
|
|
||||||
ShowHiddenData bool `json:"show_hidden_data"`
|
|
||||||
SetRotation int `json:"set_rotation"`
|
|
||||||
HoleSize int `json:"hole_size"`
|
|
||||||
order int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// chartLegendOptions directly maps the format settings of the chart legend.
|
// Chart directly maps the format settings of the chart.
|
||||||
type chartLegendOptions struct {
|
type Chart struct {
|
||||||
None bool `json:"none"`
|
Type string
|
||||||
DeleteSeries []int `json:"delete_series"`
|
Series []ChartSeries
|
||||||
Font Font `json:"font"`
|
Format PictureOptions
|
||||||
Layout layoutOptions `json:"layout"`
|
Dimension ChartDimension
|
||||||
Position string `json:"position"`
|
Legend ChartLegend
|
||||||
ShowLegendEntry bool `json:"show_legend_entry"`
|
Title ChartTitle
|
||||||
ShowLegendKey bool `json:"show_legend_key"`
|
VaryColors *bool
|
||||||
|
XAxis ChartAxis
|
||||||
|
YAxis ChartAxis
|
||||||
|
PlotArea ChartPlotArea
|
||||||
|
ShowBlanksAs string
|
||||||
|
HoleSize int
|
||||||
|
order int
|
||||||
}
|
}
|
||||||
|
|
||||||
// chartSeriesOptions directly maps the format settings of the chart series.
|
// ChartLegend directly maps the format settings of the chart legend.
|
||||||
type chartSeriesOptions struct {
|
type ChartLegend struct {
|
||||||
Name string `json:"name"`
|
None bool
|
||||||
Categories string `json:"categories"`
|
Position *string
|
||||||
Values string `json:"values"`
|
ShowLegendKey bool
|
||||||
Line struct {
|
|
||||||
None bool `json:"none"`
|
|
||||||
Color string `json:"color"`
|
|
||||||
Smooth bool `json:"smooth"`
|
|
||||||
Width float64 `json:"width"`
|
|
||||||
} `json:"line"`
|
|
||||||
Marker struct {
|
|
||||||
Symbol string `json:"symbol"`
|
|
||||||
Size int `json:"size"`
|
|
||||||
Width float64 `json:"width"`
|
|
||||||
Border struct {
|
|
||||||
Color string `json:"color"`
|
|
||||||
None bool `json:"none"`
|
|
||||||
} `json:"border"`
|
|
||||||
Fill struct {
|
|
||||||
Color string `json:"color"`
|
|
||||||
None bool `json:"none"`
|
|
||||||
} `json:"fill"`
|
|
||||||
} `json:"marker"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// chartTitleOptions directly maps the format settings of the chart title.
|
// ChartMarker directly maps the format settings of the chart marker.
|
||||||
type chartTitleOptions struct {
|
type ChartMarker struct {
|
||||||
None bool `json:"none"`
|
Symbol string
|
||||||
Name string `json:"name"`
|
Size int
|
||||||
Overlay bool `json:"overlay"`
|
|
||||||
Layout layoutOptions `json:"layout"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// layoutOptions directly maps the format settings of the element layout.
|
// ChartLine directly maps the format settings of the chart line.
|
||||||
type layoutOptions struct {
|
type ChartLine struct {
|
||||||
X float64 `json:"x"`
|
Color string
|
||||||
Y float64 `json:"y"`
|
Smooth bool
|
||||||
Width float64 `json:"width"`
|
Width float64
|
||||||
Height float64 `json:"height"`
|
}
|
||||||
|
|
||||||
|
// ChartSeries directly maps the format settings of the chart series.
|
||||||
|
type ChartSeries struct {
|
||||||
|
Name string
|
||||||
|
Categories string
|
||||||
|
Values string
|
||||||
|
Line ChartLine
|
||||||
|
Marker ChartMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChartTitle directly maps the format settings of the chart title.
|
||||||
|
type ChartTitle struct {
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,9 +74,9 @@ type xlsxPhoneticRun struct {
|
||||||
|
|
||||||
// Comment directly maps the comment information.
|
// Comment directly maps the comment information.
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
Author string `json:"author"`
|
Author string
|
||||||
AuthorID int `json:"author_id"`
|
AuthorID int
|
||||||
Cell string `json:"cell"`
|
Cell string
|
||||||
Text string `json:"text"`
|
Text string
|
||||||
Runs []RichTextRun `json:"runs"`
|
Runs []RichTextRun
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,14 @@ const (
|
||||||
// PivotTables chosen are created in a version of Excel earlier than
|
// PivotTables chosen are created in a version of Excel earlier than
|
||||||
// Excel 2007 or in compatibility mode. Slicer can only be used with
|
// Excel 2007 or in compatibility mode. Slicer can only be used with
|
||||||
// PivotTables created in Excel 2007 or a newer version of Excel.
|
// PivotTables created in Excel 2007 or a newer version of Excel.
|
||||||
pivotTableVersion = 3
|
pivotTableVersion = 3
|
||||||
|
defaultPictureScale = 1.0
|
||||||
|
defaultChartDimensionWidth = 480
|
||||||
|
defaultChartDimensionHeight = 290
|
||||||
|
defaultChartLegendPosition = "bottom"
|
||||||
|
defaultChartShowBlanksAs = "gap"
|
||||||
|
defaultShapeSize = 160
|
||||||
|
defaultShapeLineWidth = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// ColorMappingType is the type of color transformation.
|
// ColorMappingType is the type of color transformation.
|
||||||
|
@ -554,48 +561,48 @@ type xdrTxBody struct {
|
||||||
P []*aP `xml:"a:p"`
|
P []*aP `xml:"a:p"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// pictureOptions directly maps the format settings of the picture.
|
// PictureOptions directly maps the format settings of the picture.
|
||||||
type pictureOptions struct {
|
type PictureOptions struct {
|
||||||
FPrintsWithSheet bool `json:"print_obj"`
|
PrintObject *bool
|
||||||
FLocksWithSheet bool `json:"locked"`
|
Locked *bool
|
||||||
NoChangeAspect bool `json:"lock_aspect_ratio"`
|
LockAspectRatio bool
|
||||||
Autofit bool `json:"autofit"`
|
AutoFit bool
|
||||||
OffsetX int `json:"x_offset"`
|
OffsetX int
|
||||||
OffsetY int `json:"y_offset"`
|
OffsetY int
|
||||||
XScale float64 `json:"x_scale"`
|
XScale *float64
|
||||||
YScale float64 `json:"y_scale"`
|
YScale *float64
|
||||||
Hyperlink string `json:"hyperlink"`
|
Hyperlink string
|
||||||
HyperlinkType string `json:"hyperlink_type"`
|
HyperlinkType string
|
||||||
Positioning string `json:"positioning"`
|
Positioning string
|
||||||
}
|
}
|
||||||
|
|
||||||
// shapeOptions directly maps the format settings of the shape.
|
// Shape directly maps the format settings of the shape.
|
||||||
type shapeOptions struct {
|
type Shape struct {
|
||||||
Macro string `json:"macro"`
|
Macro string
|
||||||
Type string `json:"type"`
|
Type string
|
||||||
Width int `json:"width"`
|
Width *int
|
||||||
Height int `json:"height"`
|
Height *int
|
||||||
Format pictureOptions `json:"format"`
|
Format PictureOptions
|
||||||
Color shapeColorOptions `json:"color"`
|
Color ShapeColor
|
||||||
Line lineOptions `json:"line"`
|
Line ShapeLine
|
||||||
Paragraph []shapeParagraphOptions `json:"paragraph"`
|
Paragraph []ShapeParagraph
|
||||||
}
|
}
|
||||||
|
|
||||||
// shapeParagraphOptions directly maps the format settings of the paragraph in
|
// ShapeParagraph directly maps the format settings of the paragraph in
|
||||||
// the shape.
|
// the shape.
|
||||||
type shapeParagraphOptions struct {
|
type ShapeParagraph struct {
|
||||||
Font Font `json:"font"`
|
Font Font
|
||||||
Text string `json:"text"`
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
// shapeColorOptions directly maps the color settings of the shape.
|
// ShapeColor directly maps the color settings of the shape.
|
||||||
type shapeColorOptions struct {
|
type ShapeColor struct {
|
||||||
Line string `json:"line"`
|
Line string
|
||||||
Fill string `json:"fill"`
|
Fill string
|
||||||
Effect string `json:"effect"`
|
Effect string
|
||||||
}
|
}
|
||||||
|
|
||||||
// lineOptions directly maps the line settings of the shape.
|
// ShapeLine directly maps the line settings of the shape.
|
||||||
type lineOptions struct {
|
type ShapeLine struct {
|
||||||
Width float64 `json:"width"`
|
Width *float64
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,6 @@ type xlsxRPr struct {
|
||||||
|
|
||||||
// RichTextRun directly maps the settings of the rich text run.
|
// RichTextRun directly maps the settings of the rich text run.
|
||||||
type RichTextRun struct {
|
type RichTextRun struct {
|
||||||
Font *Font `json:"font"`
|
Font *Font
|
||||||
Text string `json:"text"`
|
Text string
|
||||||
}
|
}
|
||||||
|
|
78
xmlStyles.go
78
xmlStyles.go
|
@ -314,63 +314,63 @@ type xlsxStyleColors struct {
|
||||||
|
|
||||||
// Alignment directly maps the alignment settings of the cells.
|
// Alignment directly maps the alignment settings of the cells.
|
||||||
type Alignment struct {
|
type Alignment struct {
|
||||||
Horizontal string `json:"horizontal"`
|
Horizontal string
|
||||||
Indent int `json:"indent"`
|
Indent int
|
||||||
JustifyLastLine bool `json:"justify_last_line"`
|
JustifyLastLine bool
|
||||||
ReadingOrder uint64 `json:"reading_order"`
|
ReadingOrder uint64
|
||||||
RelativeIndent int `json:"relative_indent"`
|
RelativeIndent int
|
||||||
ShrinkToFit bool `json:"shrink_to_fit"`
|
ShrinkToFit bool
|
||||||
TextRotation int `json:"text_rotation"`
|
TextRotation int
|
||||||
Vertical string `json:"vertical"`
|
Vertical string
|
||||||
WrapText bool `json:"wrap_text"`
|
WrapText bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Border directly maps the border settings of the cells.
|
// Border directly maps the border settings of the cells.
|
||||||
type Border struct {
|
type Border struct {
|
||||||
Type string `json:"type"`
|
Type string
|
||||||
Color string `json:"color"`
|
Color string
|
||||||
Style int `json:"style"`
|
Style int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Font directly maps the font settings of the fonts.
|
// Font directly maps the font settings of the fonts.
|
||||||
type Font struct {
|
type Font struct {
|
||||||
Bold bool `json:"bold"`
|
Bold bool
|
||||||
Italic bool `json:"italic"`
|
Italic bool
|
||||||
Underline string `json:"underline"`
|
Underline string
|
||||||
Family string `json:"family"`
|
Family string
|
||||||
Size float64 `json:"size"`
|
Size float64
|
||||||
Strike bool `json:"strike"`
|
Strike bool
|
||||||
Color string `json:"color"`
|
Color string
|
||||||
ColorIndexed int `json:"color_indexed"`
|
ColorIndexed int
|
||||||
ColorTheme *int `json:"color_theme"`
|
ColorTheme *int
|
||||||
ColorTint float64 `json:"color_tint"`
|
ColorTint float64
|
||||||
VertAlign string `json:"vertAlign"`
|
VertAlign string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill directly maps the fill settings of the cells.
|
// Fill directly maps the fill settings of the cells.
|
||||||
type Fill struct {
|
type Fill struct {
|
||||||
Type string `json:"type"`
|
Type string
|
||||||
Pattern int `json:"pattern"`
|
Pattern int
|
||||||
Color []string `json:"color"`
|
Color []string
|
||||||
Shading int `json:"shading"`
|
Shading int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protection directly maps the protection settings of the cells.
|
// Protection directly maps the protection settings of the cells.
|
||||||
type Protection struct {
|
type Protection struct {
|
||||||
Hidden bool `json:"hidden"`
|
Hidden bool
|
||||||
Locked bool `json:"locked"`
|
Locked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Style directly maps the style settings of the cells.
|
// Style directly maps the style settings of the cells.
|
||||||
type Style struct {
|
type Style struct {
|
||||||
Border []Border `json:"border"`
|
Border []Border
|
||||||
Fill Fill `json:"fill"`
|
Fill Fill
|
||||||
Font *Font `json:"font"`
|
Font *Font
|
||||||
Alignment *Alignment `json:"alignment"`
|
Alignment *Alignment
|
||||||
Protection *Protection `json:"protection"`
|
Protection *Protection
|
||||||
NumFmt int `json:"number_format"`
|
NumFmt int
|
||||||
DecimalPlaces int `json:"decimal_places"`
|
DecimalPlaces int
|
||||||
CustomNumFmt *string `json:"custom_number_format"`
|
CustomNumFmt *string
|
||||||
Lang string `json:"lang"`
|
Lang string
|
||||||
NegRed bool `json:"negred"`
|
NegRed bool
|
||||||
}
|
}
|
||||||
|
|
35
xmlTable.go
35
xmlTable.go
|
@ -196,22 +196,25 @@ type xlsxTableStyleInfo struct {
|
||||||
ShowColumnStripes bool `xml:"showColumnStripes,attr"`
|
ShowColumnStripes bool `xml:"showColumnStripes,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// tableOptions directly maps the format settings of the table.
|
// TableOptions directly maps the format settings of the table.
|
||||||
type tableOptions struct {
|
type TableOptions struct {
|
||||||
TableName string `json:"table_name"`
|
Name string
|
||||||
TableStyle string `json:"table_style"`
|
StyleName string
|
||||||
ShowFirstColumn bool `json:"show_first_column"`
|
ShowFirstColumn bool
|
||||||
ShowLastColumn bool `json:"show_last_column"`
|
ShowLastColumn bool
|
||||||
ShowRowStripes bool `json:"show_row_stripes"`
|
ShowRowStripes *bool
|
||||||
ShowColumnStripes bool `json:"show_column_stripes"`
|
ShowColumnStripes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// autoFilterOptions directly maps the auto filter settings.
|
// AutoFilterListOptions directly maps the auto filter list settings.
|
||||||
type autoFilterOptions struct {
|
type AutoFilterListOptions struct {
|
||||||
Column string `json:"column"`
|
Column string
|
||||||
Expression string `json:"expression"`
|
Value []int
|
||||||
FilterList []struct {
|
}
|
||||||
Column string `json:"column"`
|
|
||||||
Value []int `json:"value"`
|
// AutoFilterOptions directly maps the auto filter settings.
|
||||||
} `json:"filter_list"`
|
type AutoFilterOptions struct {
|
||||||
|
Column string
|
||||||
|
Expression string
|
||||||
|
FilterList []AutoFilterListOptions
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,23 +308,23 @@ type xlsxCustomWorkbookView struct {
|
||||||
// DefinedName directly maps the name for a cell or cell range on a
|
// DefinedName directly maps the name for a cell or cell range on a
|
||||||
// worksheet.
|
// worksheet.
|
||||||
type DefinedName struct {
|
type DefinedName struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string
|
||||||
Comment string `json:"comment,omitempty"`
|
Comment string
|
||||||
RefersTo string `json:"refers_to,omitempty"`
|
RefersTo string
|
||||||
Scope string `json:"scope,omitempty"`
|
Scope string
|
||||||
}
|
}
|
||||||
|
|
||||||
// WorkbookPropsOptions directly maps the settings of workbook proprieties.
|
// WorkbookPropsOptions directly maps the settings of workbook proprieties.
|
||||||
type WorkbookPropsOptions struct {
|
type WorkbookPropsOptions struct {
|
||||||
Date1904 *bool `json:"date_1994,omitempty"`
|
Date1904 *bool
|
||||||
FilterPrivacy *bool `json:"filter_privacy,omitempty"`
|
FilterPrivacy *bool
|
||||||
CodeName *string `json:"code_name,omitempty"`
|
CodeName *string
|
||||||
}
|
}
|
||||||
|
|
||||||
// WorkbookProtectionOptions directly maps the settings of workbook protection.
|
// WorkbookProtectionOptions directly maps the settings of workbook protection.
|
||||||
type WorkbookProtectionOptions struct {
|
type WorkbookProtectionOptions struct {
|
||||||
AlgorithmName string `json:"algorithmName,omitempty"`
|
AlgorithmName string
|
||||||
Password string `json:"password,omitempty"`
|
Password string
|
||||||
LockStructure bool `json:"lockStructure,omitempty"`
|
LockStructure bool
|
||||||
LockWindows bool `json:"lockWindows,omitempty"`
|
LockWindows bool
|
||||||
}
|
}
|
||||||
|
|
268
xmlWorksheet.go
268
xmlWorksheet.go
|
@ -794,141 +794,143 @@ type xlsxX14Sparkline struct {
|
||||||
|
|
||||||
// SparklineOptions directly maps the settings of the sparkline.
|
// SparklineOptions directly maps the settings of the sparkline.
|
||||||
type SparklineOptions struct {
|
type SparklineOptions struct {
|
||||||
Location []string `json:"location"`
|
Location []string
|
||||||
Range []string `json:"range"`
|
Range []string
|
||||||
Max int `json:"max"`
|
Max int
|
||||||
CustMax int `json:"cust_max"`
|
CustMax int
|
||||||
Min int `json:"min"`
|
Min int
|
||||||
CustMin int `json:"cust_min"`
|
CustMin int
|
||||||
Type string `json:"hype"`
|
Type string
|
||||||
Weight float64 `json:"weight"`
|
Weight float64
|
||||||
DateAxis bool `json:"date_axis"`
|
DateAxis bool
|
||||||
Markers bool `json:"markers"`
|
Markers bool
|
||||||
High bool `json:"high"`
|
High bool
|
||||||
Low bool `json:"low"`
|
Low bool
|
||||||
First bool `json:"first"`
|
First bool
|
||||||
Last bool `json:"last"`
|
Last bool
|
||||||
Negative bool `json:"negative"`
|
Negative bool
|
||||||
Axis bool `json:"axis"`
|
Axis bool
|
||||||
Hidden bool `json:"hidden"`
|
Hidden bool
|
||||||
Reverse bool `json:"reverse"`
|
Reverse bool
|
||||||
Style int `json:"style"`
|
Style int
|
||||||
SeriesColor string `json:"series_color"`
|
SeriesColor string
|
||||||
NegativeColor string `json:"negative_color"`
|
NegativeColor string
|
||||||
MarkersColor string `json:"markers_color"`
|
MarkersColor string
|
||||||
FirstColor string `json:"first_color"`
|
FirstColor string
|
||||||
LastColor string `json:"last_color"`
|
LastColor string
|
||||||
HightColor string `json:"hight_color"`
|
HightColor string
|
||||||
LowColor string `json:"low_color"`
|
LowColor string
|
||||||
EmptyCells string `json:"empty_cells"`
|
EmptyCells string
|
||||||
}
|
}
|
||||||
|
|
||||||
// panesOptions directly maps the settings of the panes.
|
// PaneOptions directly maps the settings of the pane.
|
||||||
type panesOptions struct {
|
type PaneOptions struct {
|
||||||
Freeze bool `json:"freeze"`
|
SQRef string
|
||||||
Split bool `json:"split"`
|
ActiveCell string
|
||||||
XSplit int `json:"x_split"`
|
Pane string
|
||||||
YSplit int `json:"y_split"`
|
|
||||||
TopLeftCell string `json:"top_left_cell"`
|
|
||||||
ActivePane string `json:"active_pane"`
|
|
||||||
Panes []struct {
|
|
||||||
SQRef string `json:"sqref"`
|
|
||||||
ActiveCell string `json:"active_cell"`
|
|
||||||
Pane string `json:"pane"`
|
|
||||||
} `json:"panes"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// conditionalOptions directly maps the conditional format settings of the cells.
|
// Panes directly maps the settings of the panes.
|
||||||
type conditionalOptions struct {
|
type Panes struct {
|
||||||
Type string `json:"type"`
|
Freeze bool
|
||||||
AboveAverage bool `json:"above_average,omitempty"`
|
Split bool
|
||||||
Percent bool `json:"percent,omitempty"`
|
XSplit int
|
||||||
Format int `json:"format,omitempty"`
|
YSplit int
|
||||||
Criteria string `json:"criteria,omitempty"`
|
TopLeftCell string
|
||||||
Value string `json:"value,omitempty"`
|
ActivePane string
|
||||||
Minimum string `json:"minimum,omitempty"`
|
Panes []PaneOptions
|
||||||
Maximum string `json:"maximum,omitempty"`
|
}
|
||||||
MinType string `json:"min_type,omitempty"`
|
|
||||||
MidType string `json:"mid_type,omitempty"`
|
// ConditionalFormatOptions directly maps the conditional format settings of the cells.
|
||||||
MaxType string `json:"max_type,omitempty"`
|
type ConditionalFormatOptions struct {
|
||||||
MinValue string `json:"min_value,omitempty"`
|
Type string
|
||||||
MidValue string `json:"mid_value,omitempty"`
|
AboveAverage bool
|
||||||
MaxValue string `json:"max_value,omitempty"`
|
Percent bool
|
||||||
MinColor string `json:"min_color,omitempty"`
|
Format int
|
||||||
MidColor string `json:"mid_color,omitempty"`
|
Criteria string
|
||||||
MaxColor string `json:"max_color,omitempty"`
|
Value string
|
||||||
MinLength string `json:"min_length,omitempty"`
|
Minimum string
|
||||||
MaxLength string `json:"max_length,omitempty"`
|
Maximum string
|
||||||
MultiRange string `json:"multi_range,omitempty"`
|
MinType string
|
||||||
BarColor string `json:"bar_color,omitempty"`
|
MidType string
|
||||||
|
MaxType string
|
||||||
|
MinValue string
|
||||||
|
MidValue string
|
||||||
|
MaxValue string
|
||||||
|
MinColor string
|
||||||
|
MidColor string
|
||||||
|
MaxColor string
|
||||||
|
MinLength string
|
||||||
|
MaxLength string
|
||||||
|
BarColor string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SheetProtectionOptions directly maps the settings of worksheet protection.
|
// SheetProtectionOptions directly maps the settings of worksheet protection.
|
||||||
type SheetProtectionOptions struct {
|
type SheetProtectionOptions struct {
|
||||||
AlgorithmName string `json:"algorithm_name,omitempty"`
|
AlgorithmName string
|
||||||
AutoFilter bool `json:"auto_filter,omitempty"`
|
AutoFilter bool
|
||||||
DeleteColumns bool `json:"delete_columns,omitempty"`
|
DeleteColumns bool
|
||||||
DeleteRows bool `json:"delete_rows,omitempty"`
|
DeleteRows bool
|
||||||
EditObjects bool `json:"edit_objects,omitempty"`
|
EditObjects bool
|
||||||
EditScenarios bool `json:"edit_scenarios,omitempty"`
|
EditScenarios bool
|
||||||
FormatCells bool `json:"format_cells,omitempty"`
|
FormatCells bool
|
||||||
FormatColumns bool `json:"format_columns,omitempty"`
|
FormatColumns bool
|
||||||
FormatRows bool `json:"format_rows,omitempty"`
|
FormatRows bool
|
||||||
InsertColumns bool `json:"insert_columns,omitempty"`
|
InsertColumns bool
|
||||||
InsertHyperlinks bool `json:"insert_hyperlinks,omitempty"`
|
InsertHyperlinks bool
|
||||||
InsertRows bool `json:"insert_rows,omitempty"`
|
InsertRows bool
|
||||||
Password string `json:"password,omitempty"`
|
Password string
|
||||||
PivotTables bool `json:"pivot_tables,omitempty"`
|
PivotTables bool
|
||||||
SelectLockedCells bool `json:"select_locked_cells,omitempty"`
|
SelectLockedCells bool
|
||||||
SelectUnlockedCells bool `json:"select_unlocked_cells,omitempty"`
|
SelectUnlockedCells bool
|
||||||
Sort bool `json:"sort,omitempty"`
|
Sort bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeaderFooterOptions directly maps the settings of header and footer.
|
// HeaderFooterOptions directly maps the settings of header and footer.
|
||||||
type HeaderFooterOptions struct {
|
type HeaderFooterOptions struct {
|
||||||
AlignWithMargins bool `json:"align_with_margins,omitempty"`
|
AlignWithMargins bool
|
||||||
DifferentFirst bool `json:"different_first,omitempty"`
|
DifferentFirst bool
|
||||||
DifferentOddEven bool `json:"different_odd_even,omitempty"`
|
DifferentOddEven bool
|
||||||
ScaleWithDoc bool `json:"scale_with_doc,omitempty"`
|
ScaleWithDoc bool
|
||||||
OddHeader string `json:"odd_header,omitempty"`
|
OddHeader string
|
||||||
OddFooter string `json:"odd_footer,omitempty"`
|
OddFooter string
|
||||||
EvenHeader string `json:"even_header,omitempty"`
|
EvenHeader string
|
||||||
EvenFooter string `json:"even_footer,omitempty"`
|
EvenFooter string
|
||||||
FirstHeader string `json:"first_header,omitempty"`
|
FirstHeader string
|
||||||
FirstFooter string `json:"first_footer,omitempty"`
|
FirstFooter string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageLayoutMarginsOptions directly maps the settings of page layout margins.
|
// PageLayoutMarginsOptions directly maps the settings of page layout margins.
|
||||||
type PageLayoutMarginsOptions struct {
|
type PageLayoutMarginsOptions struct {
|
||||||
Bottom *float64 `json:"bottom,omitempty"`
|
Bottom *float64
|
||||||
Footer *float64 `json:"footer,omitempty"`
|
Footer *float64
|
||||||
Header *float64 `json:"header,omitempty"`
|
Header *float64
|
||||||
Left *float64 `json:"left,omitempty"`
|
Left *float64
|
||||||
Right *float64 `json:"right,omitempty"`
|
Right *float64
|
||||||
Top *float64 `json:"top,omitempty"`
|
Top *float64
|
||||||
Horizontally *bool `json:"horizontally,omitempty"`
|
Horizontally *bool
|
||||||
Vertically *bool `json:"vertically,omitempty"`
|
Vertically *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageLayoutOptions directly maps the settings of page layout.
|
// PageLayoutOptions directly maps the settings of page layout.
|
||||||
type PageLayoutOptions struct {
|
type PageLayoutOptions struct {
|
||||||
// Size defines the paper size of the worksheet.
|
// Size defines the paper size of the worksheet.
|
||||||
Size *int `json:"size,omitempty"`
|
Size *int
|
||||||
// Orientation defines the orientation of page layout for a worksheet.
|
// Orientation defines the orientation of page layout for a worksheet.
|
||||||
Orientation *string `json:"orientation,omitempty"`
|
Orientation *string
|
||||||
// FirstPageNumber specified the first printed page number. If no value is
|
// FirstPageNumber specified the first printed page number. If no value is
|
||||||
// specified, then 'automatic' is assumed.
|
// specified, then 'automatic' is assumed.
|
||||||
FirstPageNumber *uint `json:"first_page_number,omitempty"`
|
FirstPageNumber *uint
|
||||||
// AdjustTo defines the print scaling. This attribute is restricted to
|
// AdjustTo defines the print scaling. This attribute is restricted to
|
||||||
// value ranging from 10 (10%) to 400 (400%). This setting is overridden
|
// value ranging from 10 (10%) to 400 (400%). This setting is overridden
|
||||||
// when fitToWidth and/or fitToHeight are in use.
|
// when fitToWidth and/or fitToHeight are in use.
|
||||||
AdjustTo *uint `json:"adjust_to,omitempty"`
|
AdjustTo *uint
|
||||||
// FitToHeight specified the number of vertical pages to fit on.
|
// FitToHeight specified the number of vertical pages to fit on.
|
||||||
FitToHeight *int `json:"fit_to_height,omitempty"`
|
FitToHeight *int
|
||||||
// FitToWidth specified the number of horizontal pages to fit on.
|
// FitToWidth specified the number of horizontal pages to fit on.
|
||||||
FitToWidth *int `json:"fit_to_width,omitempty"`
|
FitToWidth *int
|
||||||
// BlackAndWhite specified print black and white.
|
// BlackAndWhite specified print black and white.
|
||||||
BlackAndWhite *bool `json:"black_and_white,omitempty"`
|
BlackAndWhite *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewOptions directly maps the settings of sheet view.
|
// ViewOptions directly maps the settings of sheet view.
|
||||||
|
@ -936,37 +938,37 @@ type ViewOptions struct {
|
||||||
// DefaultGridColor indicating that the consuming application should use
|
// DefaultGridColor indicating that the consuming application should use
|
||||||
// the default grid lines color(system dependent). Overrides any color
|
// the default grid lines color(system dependent). Overrides any color
|
||||||
// specified in colorId.
|
// specified in colorId.
|
||||||
DefaultGridColor *bool `json:"default_grid_color,omitempty"`
|
DefaultGridColor *bool
|
||||||
// RightToLeft indicating whether the sheet is in 'right to left' display
|
// RightToLeft indicating whether the sheet is in 'right to left' display
|
||||||
// mode. When in this mode, Column A is on the far right, Column B; is one
|
// mode. When in this mode, Column A is on the far right, Column B; is one
|
||||||
// column left of Column A, and so on. Also, information in cells is
|
// column left of Column A, and so on. Also, information in cells is
|
||||||
// displayed in the Right to Left format.
|
// displayed in the Right to Left format.
|
||||||
RightToLeft *bool `json:"right_to_left,omitempty"`
|
RightToLeft *bool
|
||||||
// ShowFormulas indicating whether this sheet should display formulas.
|
// ShowFormulas indicating whether this sheet should display formulas.
|
||||||
ShowFormulas *bool `json:"show_formulas,omitempty"`
|
ShowFormulas *bool
|
||||||
// ShowGridLines indicating whether this sheet should display grid lines.
|
// ShowGridLines indicating whether this sheet should display grid lines.
|
||||||
ShowGridLines *bool `json:"show_grid_lines,omitempty"`
|
ShowGridLines *bool
|
||||||
// ShowRowColHeaders indicating whether the sheet should display row and
|
// ShowRowColHeaders indicating whether the sheet should display row and
|
||||||
// column headings.
|
// column headings.
|
||||||
ShowRowColHeaders *bool `json:"show_row_col_headers,omitempty"`
|
ShowRowColHeaders *bool
|
||||||
// ShowRuler indicating this sheet should display ruler.
|
// ShowRuler indicating this sheet should display ruler.
|
||||||
ShowRuler *bool `json:"show_ruler,omitempty"`
|
ShowRuler *bool
|
||||||
// ShowZeros indicating whether to "show a zero in cells that have zero
|
// ShowZeros indicating whether to "show a zero in cells that have zero
|
||||||
// value". When using a formula to reference another cell which is empty,
|
// value". When using a formula to reference another cell which is empty,
|
||||||
// the referenced value becomes 0 when the flag is true. (Default setting
|
// the referenced value becomes 0 when the flag is true. (Default setting
|
||||||
// is true.)
|
// is true.)
|
||||||
ShowZeros *bool `json:"show_zeros,omitempty"`
|
ShowZeros *bool
|
||||||
// TopLeftCell specifies a location of the top left visible cell Location
|
// TopLeftCell specifies a location of the top left visible cell Location
|
||||||
// of the top left visible cell in the bottom right pane (when in
|
// of the top left visible cell in the bottom right pane (when in
|
||||||
// Left-to-Right mode).
|
// Left-to-Right mode).
|
||||||
TopLeftCell *string `json:"top_left_cell,omitempty"`
|
TopLeftCell *string
|
||||||
// View indicating how sheet is displayed, by default it uses empty string
|
// View indicating how sheet is displayed, by default it uses empty string
|
||||||
// available options: normal, pageLayout, pageBreakPreview
|
// available options: normal, pageLayout, pageBreakPreview
|
||||||
View *string `json:"low_color,omitempty"`
|
View *string
|
||||||
// ZoomScale specifies a window zoom magnification for current view
|
// ZoomScale specifies a window zoom magnification for current view
|
||||||
// representing percent values. This attribute is restricted to values
|
// representing percent values. This attribute is restricted to values
|
||||||
// ranging from 10 to 400. Horizontal & Vertical scale together.
|
// ranging from 10 to 400. Horizontal & Vertical scale together.
|
||||||
ZoomScale *float64 `json:"zoom_scale,omitempty"`
|
ZoomScale *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// SheetPropsOptions directly maps the settings of sheet view.
|
// SheetPropsOptions directly maps the settings of sheet view.
|
||||||
|
@ -974,55 +976,55 @@ type SheetPropsOptions struct {
|
||||||
// Specifies a stable name of the sheet, which should not change over time,
|
// Specifies a stable name of the sheet, which should not change over time,
|
||||||
// and does not change from user input. This name should be used by code
|
// and does not change from user input. This name should be used by code
|
||||||
// to reference a particular sheet.
|
// to reference a particular sheet.
|
||||||
CodeName *string `json:"code_name,omitempty"`
|
CodeName *string
|
||||||
// EnableFormatConditionsCalculation indicating whether the conditional
|
// EnableFormatConditionsCalculation indicating whether the conditional
|
||||||
// formatting calculations shall be evaluated. If set to false, then the
|
// formatting calculations shall be evaluated. If set to false, then the
|
||||||
// min/max values of color scales or data bars or threshold values in Top N
|
// min/max values of color scales or data bars or threshold values in Top N
|
||||||
// rules shall not be updated. Essentially the conditional
|
// rules shall not be updated. Essentially the conditional
|
||||||
// formatting "calc" is off.
|
// formatting "calc" is off.
|
||||||
EnableFormatConditionsCalculation *bool `json:"enable_format_conditions_calculation,omitempty"`
|
EnableFormatConditionsCalculation *bool
|
||||||
// Published indicating whether the worksheet is published.
|
// Published indicating whether the worksheet is published.
|
||||||
Published *bool `json:"published,omitempty"`
|
Published *bool
|
||||||
// AutoPageBreaks indicating whether the sheet displays Automatic Page
|
// AutoPageBreaks indicating whether the sheet displays Automatic Page
|
||||||
// Breaks.
|
// Breaks.
|
||||||
AutoPageBreaks *bool `json:"auto_page_breaks,omitempty"`
|
AutoPageBreaks *bool
|
||||||
// FitToPage indicating whether the Fit to Page print option is enabled.
|
// FitToPage indicating whether the Fit to Page print option is enabled.
|
||||||
FitToPage *bool `json:"fit_to_page,omitempty"`
|
FitToPage *bool
|
||||||
// TabColorIndexed represents the indexed color value.
|
// TabColorIndexed represents the indexed color value.
|
||||||
TabColorIndexed *int `json:"tab_color_indexed,omitempty"`
|
TabColorIndexed *int
|
||||||
// TabColorRGB represents the standard Alpha Red Green Blue color value.
|
// TabColorRGB represents the standard Alpha Red Green Blue color value.
|
||||||
TabColorRGB *string `json:"tab_color_rgb,omitempty"`
|
TabColorRGB *string
|
||||||
// TabColorTheme represents the zero-based index into the collection,
|
// TabColorTheme represents the zero-based index into the collection,
|
||||||
// referencing a particular value expressed in the Theme part.
|
// referencing a particular value expressed in the Theme part.
|
||||||
TabColorTheme *int `json:"tab_color_theme,omitempty"`
|
TabColorTheme *int
|
||||||
// TabColorTint specifies the tint value applied to the color.
|
// TabColorTint specifies the tint value applied to the color.
|
||||||
TabColorTint *float64 `json:"tab_color_tint,omitempty"`
|
TabColorTint *float64
|
||||||
// OutlineSummaryBelow indicating whether summary rows appear below detail
|
// OutlineSummaryBelow indicating whether summary rows appear below detail
|
||||||
// in an outline, when applying an outline.
|
// in an outline, when applying an outline.
|
||||||
OutlineSummaryBelow *bool `json:"outline_summary_below,omitempty"`
|
OutlineSummaryBelow *bool
|
||||||
// OutlineSummaryRight indicating whether summary columns appear to the
|
// OutlineSummaryRight indicating whether summary columns appear to the
|
||||||
// right of detail in an outline, when applying an outline.
|
// right of detail in an outline, when applying an outline.
|
||||||
OutlineSummaryRight *bool `json:"outline_summary_right,omitempty"`
|
OutlineSummaryRight *bool
|
||||||
// BaseColWidth specifies the number of characters of the maximum digit
|
// BaseColWidth specifies the number of characters of the maximum digit
|
||||||
// width of the normal style's font. This value does not include margin
|
// width of the normal style's font. This value does not include margin
|
||||||
// padding or extra padding for grid lines. It is only the number of
|
// padding or extra padding for grid lines. It is only the number of
|
||||||
// characters.
|
// characters.
|
||||||
BaseColWidth *uint8 `json:"base_col_width,omitempty"`
|
BaseColWidth *uint8
|
||||||
// DefaultColWidth specifies the default column width measured as the
|
// DefaultColWidth specifies the default column width measured as the
|
||||||
// number of characters of the maximum digit width of the normal style's
|
// number of characters of the maximum digit width of the normal style's
|
||||||
// font.
|
// font.
|
||||||
DefaultColWidth *float64 `json:"default_col_width,omitempty"`
|
DefaultColWidth *float64
|
||||||
// DefaultRowHeight specifies the default row height measured in point
|
// DefaultRowHeight specifies the default row height measured in point
|
||||||
// size. Optimization so we don't have to write the height on all rows.
|
// size. Optimization so we don't have to write the height on all rows.
|
||||||
// This can be written out if most rows have custom height, to achieve the
|
// This can be written out if most rows have custom height, to achieve the
|
||||||
// optimization.
|
// optimization.
|
||||||
DefaultRowHeight *float64 `json:"default_row_height,omitempty"`
|
DefaultRowHeight *float64
|
||||||
// CustomHeight specifies the custom height.
|
// CustomHeight specifies the custom height.
|
||||||
CustomHeight *bool `json:"custom_height,omitempty"`
|
CustomHeight *bool
|
||||||
// ZeroHeight specifies if rows are hidden.
|
// ZeroHeight specifies if rows are hidden.
|
||||||
ZeroHeight *bool `json:"zero_height,omitempty"`
|
ZeroHeight *bool
|
||||||
// ThickTop specifies if rows have a thick top border by default.
|
// ThickTop specifies if rows have a thick top border by default.
|
||||||
ThickTop *bool `json:"thick_top,omitempty"`
|
ThickTop *bool
|
||||||
// ThickBottom specifies if rows have a thick bottom border by default.
|
// ThickBottom specifies if rows have a thick bottom border by default.
|
||||||
ThickBottom *bool `json:"thick_bottom,omitempty"`
|
ThickBottom *bool
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue