- Readme and go test updated
This commit is contained in:
parent
3b2c80ddc3
commit
6aa59a1af2
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
# Excelize
|
# Excelize
|
||||||
|
|
||||||
[](https://travis-ci.org/xuri/excelize)
|
[](https://travis-ci.org/360EntSecGroup-Skylar/excelize)
|
||||||
[](https://codecov.io/gh/xuri/excelize)
|
[](https://codecov.io/gh/360EntSecGroup-Skylar/excelize)
|
||||||
[](https://goreportcard.com/report/github.com/xuri/excelize)
|
[](https://goreportcard.com/report/github.com/xuri/excelize)
|
||||||
[](https://godoc.org/github.com/xuri/excelize)
|
[](https://godoc.org/github.com/xuri/excelize)
|
||||||
[](https://opensource.org/licenses/BSD-3-Clause)
|
[](https://opensource.org/licenses/BSD-3-Clause)
|
||||||
|
|
157
cell.go
157
cell.go
|
@ -1,8 +1,11 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mergeCellsParser provides function to check merged cells in worksheet by
|
// mergeCellsParser provides function to check merged cells in worksheet by
|
||||||
|
@ -19,6 +22,53 @@ func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
|
||||||
return axis
|
return axis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCellValue provides function to set value of a cell. The following shows
|
||||||
|
// the supported data types:
|
||||||
|
//
|
||||||
|
// int
|
||||||
|
// int8
|
||||||
|
// int16
|
||||||
|
// int32
|
||||||
|
// int64
|
||||||
|
// float32
|
||||||
|
// float64
|
||||||
|
// string
|
||||||
|
// []byte
|
||||||
|
// time.Time
|
||||||
|
// nil
|
||||||
|
//
|
||||||
|
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
|
||||||
|
// set numbers format by SetCellStyle() method.
|
||||||
|
func (f *File) SetCellValue(sheet, axis string, value interface{}) {
|
||||||
|
switch t := value.(type) {
|
||||||
|
case int:
|
||||||
|
f.SetCellInt(sheet, axis, value.(int))
|
||||||
|
case int8:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int8)))
|
||||||
|
case int16:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int16)))
|
||||||
|
case int32:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int32)))
|
||||||
|
case int64:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int64)))
|
||||||
|
case float32:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32))
|
||||||
|
case float64:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float64)), 'f', -1, 64))
|
||||||
|
case string:
|
||||||
|
f.SetCellStr(sheet, axis, t)
|
||||||
|
case []byte:
|
||||||
|
f.SetCellStr(sheet, axis, string(t))
|
||||||
|
case time.Time:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(timeToExcelTime(timeToUTCTime(value.(time.Time)))), 'f', -1, 32))
|
||||||
|
f.setDefaultTimeStyle(sheet, axis)
|
||||||
|
case nil:
|
||||||
|
f.SetCellStr(sheet, axis, "")
|
||||||
|
default:
|
||||||
|
f.SetCellStr(sheet, axis, fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetCellValue provides function to get formatted value from cell by given
|
// GetCellValue provides function to get formatted value from cell by given
|
||||||
// sheet index and axis in XLSX file. If it is possible to apply a format to the
|
// sheet index and axis in XLSX file. If it is possible to apply a format to the
|
||||||
// cell value, it will do so, if not then an error will be returned, along with
|
// cell value, it will do so, if not then an error will be returned, along with
|
||||||
|
@ -77,6 +127,25 @@ func (f *File) formattedValue(s int, v string) string {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCellStyle provides function to get cell style index by given worksheet
|
||||||
|
// name and cell coordinates.
|
||||||
|
func (f *File) GetCellStyle(sheet, axis string) int {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
}
|
||||||
|
|
||||||
// GetCellFormula provides function to get formula from cell by given sheet
|
// GetCellFormula provides function to get formula from cell by given sheet
|
||||||
// index and axis in XLSX file.
|
// index and axis in XLSX file.
|
||||||
func (f *File) GetCellFormula(sheet, axis string) string {
|
func (f *File) GetCellFormula(sheet, axis string) string {
|
||||||
|
@ -215,6 +284,94 @@ func (f *File) MergeCell(sheet, hcell, vcell string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCellInt provides function to set int type value of a cell by given
|
||||||
|
// worksheet name, cell coordinates and cell value.
|
||||||
|
func (f *File) SetCellInt(sheet, axis string, value int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareCellStyle provides function to prepare style index of cell in
|
||||||
|
// worksheet by given column index and style index.
|
||||||
|
func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
|
||||||
|
if xlsx.Cols != nil && style == 0 {
|
||||||
|
for _, v := range xlsx.Cols.Col {
|
||||||
|
if v.Min <= col && col <= v.Max {
|
||||||
|
style = v.Style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellStr provides function to set string type value of a cell. Total number
|
||||||
|
// of characters that a cell can contain 32767 characters.
|
||||||
|
func (f *File) SetCellStr(sheet, axis, value string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
if len(value) > 32767 {
|
||||||
|
value = value[0:32767]
|
||||||
|
}
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
// Leading space(s) character detection.
|
||||||
|
if len(value) > 0 {
|
||||||
|
if value[0] == 32 {
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].XMLSpace = xml.Attr{
|
||||||
|
Name: xml.Name{Space: NameSpaceXML, Local: "space"},
|
||||||
|
Value: "preserve",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = "str"
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellDefault provides function to set string type value of a cell as
|
||||||
|
// default format without escaping the cell.
|
||||||
|
func (f *File) SetCellDefault(sheet, axis, value string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
||||||
|
}
|
||||||
|
|
||||||
// checkCellInArea provides function to determine if a given coordinate is
|
// checkCellInArea provides function to determine if a given coordinate is
|
||||||
// within an area.
|
// within an area.
|
||||||
func checkCellInArea(cell, area string) bool {
|
func checkCellInArea(cell, area string) bool {
|
||||||
|
|
69
col.go
69
col.go
|
@ -1,7 +1,9 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -243,6 +245,61 @@ func (f *File) GetColWidth(sheet, column string) float64 {
|
||||||
return defaultColWidthPixels
|
return defaultColWidthPixels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InsertCol provides function to insert a new column before given column index.
|
||||||
|
// For example, create a new column before column C in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.InsertCol("Sheet1", "C")
|
||||||
|
//
|
||||||
|
func (f *File) InsertCol(sheet, column string) {
|
||||||
|
col := TitleToNumber(strings.ToUpper(column))
|
||||||
|
f.adjustHelper(sheet, col, -1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCol provides function to remove single column by given worksheet index
|
||||||
|
// and column index. For example, remove column C in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.RemoveCol("Sheet1", "C")
|
||||||
|
//
|
||||||
|
func (f *File) RemoveCol(sheet, column string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
for k, v := range r.C {
|
||||||
|
axis := v.R
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
if col == column {
|
||||||
|
xlsx.SheetData.Row[i].C = append(xlsx.SheetData.Row[i].C[:k], xlsx.SheetData.Row[i].C[k+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
col := TitleToNumber(strings.ToUpper(column))
|
||||||
|
f.adjustHelper(sheet, col, -1, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completion column element tags of XML in a sheet.
|
||||||
|
func completeCol(xlsx *xlsxWorksheet, row, cell int) {
|
||||||
|
if len(xlsx.SheetData.Row) < cell {
|
||||||
|
for i := len(xlsx.SheetData.Row); i < cell; i++ {
|
||||||
|
xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
|
||||||
|
R: i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
for k, v := range xlsx.SheetData.Row {
|
||||||
|
if len(v.C) < cell {
|
||||||
|
start := len(v.C)
|
||||||
|
for iii := start; iii < cell; iii++ {
|
||||||
|
buffer.WriteString(ToAlphaString(iii))
|
||||||
|
buffer.WriteString(strconv.Itoa(k + 1))
|
||||||
|
xlsx.SheetData.Row[k].C = append(xlsx.SheetData.Row[k].C, xlsxC{
|
||||||
|
R: buffer.String(),
|
||||||
|
})
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convertColWidthToPixels provieds function to convert the width of a cell from
|
// convertColWidthToPixels provieds function to convert the width of a cell from
|
||||||
// user's units to pixels. Excel rounds the column width to the nearest pixel.
|
// user's units to pixels. Excel rounds the column width to the nearest pixel.
|
||||||
// If the width hasn't been set by the user we use the default value. If the
|
// If the width hasn't been set by the user we use the default value. If the
|
||||||
|
@ -261,15 +318,3 @@ func convertColWidthToPixels(width float64) float64 {
|
||||||
pixels = (width*maxDigitWidth + 0.5) + padding
|
pixels = (width*maxDigitWidth + 0.5) + padding
|
||||||
return math.Ceil(pixels)
|
return math.Ceil(pixels)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertRowHeightToPixels provides function to convert the height of a cell
|
|
||||||
// from user's units to pixels. If the height hasn't been set by the user we use
|
|
||||||
// the default value. If the row is hidden it has a value of zero.
|
|
||||||
func convertRowHeightToPixels(height float64) float64 {
|
|
||||||
var pixels float64
|
|
||||||
if height == 0 {
|
|
||||||
return pixels
|
|
||||||
}
|
|
||||||
pixels = math.Ceil(4.0 / 3.0 * height)
|
|
||||||
return pixels
|
|
||||||
}
|
|
||||||
|
|
489
excelize.go
489
excelize.go
|
@ -4,13 +4,11 @@ import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// File define a populated XLSX file struct.
|
// File define a populated XLSX file struct.
|
||||||
|
@ -67,72 +65,6 @@ func OpenReader(r io.Reader) (*File, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCellValue provides function to set value of a cell. The following shows
|
|
||||||
// the supported data types:
|
|
||||||
//
|
|
||||||
// int
|
|
||||||
// int8
|
|
||||||
// int16
|
|
||||||
// int32
|
|
||||||
// int64
|
|
||||||
// float32
|
|
||||||
// float64
|
|
||||||
// string
|
|
||||||
// []byte
|
|
||||||
// time.Time
|
|
||||||
// nil
|
|
||||||
//
|
|
||||||
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
|
|
||||||
// set numbers format by SetCellStyle() method.
|
|
||||||
func (f *File) SetCellValue(sheet, axis string, value interface{}) {
|
|
||||||
switch t := value.(type) {
|
|
||||||
case int:
|
|
||||||
f.SetCellInt(sheet, axis, value.(int))
|
|
||||||
case int8:
|
|
||||||
f.SetCellInt(sheet, axis, int(value.(int8)))
|
|
||||||
case int16:
|
|
||||||
f.SetCellInt(sheet, axis, int(value.(int16)))
|
|
||||||
case int32:
|
|
||||||
f.SetCellInt(sheet, axis, int(value.(int32)))
|
|
||||||
case int64:
|
|
||||||
f.SetCellInt(sheet, axis, int(value.(int64)))
|
|
||||||
case float32:
|
|
||||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32))
|
|
||||||
case float64:
|
|
||||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float64)), 'f', -1, 64))
|
|
||||||
case string:
|
|
||||||
f.SetCellStr(sheet, axis, t)
|
|
||||||
case []byte:
|
|
||||||
f.SetCellStr(sheet, axis, string(t))
|
|
||||||
case time.Time:
|
|
||||||
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(timeToExcelTime(timeToUTCTime(value.(time.Time)))), 'f', -1, 32))
|
|
||||||
f.setDefaultTimeStyle(sheet, axis)
|
|
||||||
case nil:
|
|
||||||
f.SetCellStr(sheet, axis, "")
|
|
||||||
default:
|
|
||||||
f.SetCellStr(sheet, axis, fmt.Sprintf("%v", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCellStyle provides function to get cell style index by given worksheet
|
|
||||||
// name and cell coordinates.
|
|
||||||
func (f *File) GetCellStyle(sheet, axis string) int {
|
|
||||||
xlsx := f.workSheetReader(sheet)
|
|
||||||
axis = f.mergeCellsParser(xlsx, axis)
|
|
||||||
col := string(strings.Map(letterOnlyMapF, axis))
|
|
||||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
|
||||||
xAxis := row - 1
|
|
||||||
yAxis := TitleToNumber(col)
|
|
||||||
|
|
||||||
rows := xAxis + 1
|
|
||||||
cell := yAxis + 1
|
|
||||||
|
|
||||||
completeRow(xlsx, rows, cell)
|
|
||||||
completeCol(xlsx, rows, cell)
|
|
||||||
|
|
||||||
return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setDefaultTimeStyle provides function to set default numbers format for
|
// setDefaultTimeStyle provides function to set default numbers format for
|
||||||
// time.Time type cell value by given worksheet name and cell coordinates.
|
// time.Time type cell value by given worksheet name and cell coordinates.
|
||||||
func (f *File) setDefaultTimeStyle(sheet, axis string) {
|
func (f *File) setDefaultTimeStyle(sheet, axis string) {
|
||||||
|
@ -163,151 +95,6 @@ func (f *File) workSheetReader(sheet string) *xlsxWorksheet {
|
||||||
return f.Sheet[name]
|
return f.Sheet[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCellInt provides function to set int type value of a cell by given
|
|
||||||
// worksheet name, cell coordinates and cell value.
|
|
||||||
func (f *File) SetCellInt(sheet, axis string, value int) {
|
|
||||||
xlsx := f.workSheetReader(sheet)
|
|
||||||
axis = f.mergeCellsParser(xlsx, axis)
|
|
||||||
col := string(strings.Map(letterOnlyMapF, axis))
|
|
||||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
|
||||||
xAxis := row - 1
|
|
||||||
yAxis := TitleToNumber(col)
|
|
||||||
|
|
||||||
rows := xAxis + 1
|
|
||||||
cell := yAxis + 1
|
|
||||||
|
|
||||||
completeRow(xlsx, rows, cell)
|
|
||||||
completeCol(xlsx, rows, cell)
|
|
||||||
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepareCellStyle provides function to prepare style index of cell in
|
|
||||||
// worksheet by given column index and style index.
|
|
||||||
func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
|
|
||||||
if xlsx.Cols != nil && style == 0 {
|
|
||||||
for _, v := range xlsx.Cols.Col {
|
|
||||||
if v.Min <= col && col <= v.Max {
|
|
||||||
style = v.Style
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return style
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCellStr provides function to set string type value of a cell. Total number
|
|
||||||
// of characters that a cell can contain 32767 characters.
|
|
||||||
func (f *File) SetCellStr(sheet, axis, value string) {
|
|
||||||
xlsx := f.workSheetReader(sheet)
|
|
||||||
axis = f.mergeCellsParser(xlsx, axis)
|
|
||||||
if len(value) > 32767 {
|
|
||||||
value = value[0:32767]
|
|
||||||
}
|
|
||||||
col := string(strings.Map(letterOnlyMapF, axis))
|
|
||||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
|
||||||
xAxis := row - 1
|
|
||||||
yAxis := TitleToNumber(col)
|
|
||||||
|
|
||||||
rows := xAxis + 1
|
|
||||||
cell := yAxis + 1
|
|
||||||
|
|
||||||
completeRow(xlsx, rows, cell)
|
|
||||||
completeCol(xlsx, rows, cell)
|
|
||||||
|
|
||||||
// Leading space(s) character detection.
|
|
||||||
if len(value) > 0 {
|
|
||||||
if value[0] == 32 {
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].XMLSpace = xml.Attr{
|
|
||||||
Name: xml.Name{Space: NameSpaceXML, Local: "space"},
|
|
||||||
Value: "preserve",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = "str"
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCellDefault provides function to set string type value of a cell as
|
|
||||||
// default format without escaping the cell.
|
|
||||||
func (f *File) SetCellDefault(sheet, axis, value string) {
|
|
||||||
xlsx := f.workSheetReader(sheet)
|
|
||||||
axis = f.mergeCellsParser(xlsx, axis)
|
|
||||||
col := string(strings.Map(letterOnlyMapF, axis))
|
|
||||||
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
|
||||||
xAxis := row - 1
|
|
||||||
yAxis := TitleToNumber(col)
|
|
||||||
|
|
||||||
rows := xAxis + 1
|
|
||||||
cell := yAxis + 1
|
|
||||||
|
|
||||||
completeRow(xlsx, rows, cell)
|
|
||||||
completeCol(xlsx, rows, cell)
|
|
||||||
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
|
||||||
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Completion column element tags of XML in a sheet.
|
|
||||||
func completeCol(xlsx *xlsxWorksheet, row, cell int) {
|
|
||||||
if len(xlsx.SheetData.Row) < cell {
|
|
||||||
for i := len(xlsx.SheetData.Row); i < cell; i++ {
|
|
||||||
xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
|
|
||||||
R: i + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer := bytes.Buffer{}
|
|
||||||
for k, v := range xlsx.SheetData.Row {
|
|
||||||
if len(v.C) < cell {
|
|
||||||
start := len(v.C)
|
|
||||||
for iii := start; iii < cell; iii++ {
|
|
||||||
buffer.WriteString(ToAlphaString(iii))
|
|
||||||
buffer.WriteString(strconv.Itoa(k + 1))
|
|
||||||
xlsx.SheetData.Row[k].C = append(xlsx.SheetData.Row[k].C, xlsxC{
|
|
||||||
R: buffer.String(),
|
|
||||||
})
|
|
||||||
buffer.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// completeRow provides function to check and fill each column element for a
|
|
||||||
// single row and make that is continuous in a worksheet of XML by given row
|
|
||||||
// index and axis.
|
|
||||||
func completeRow(xlsx *xlsxWorksheet, row, cell int) {
|
|
||||||
currentRows := len(xlsx.SheetData.Row)
|
|
||||||
if currentRows > 1 {
|
|
||||||
lastRow := xlsx.SheetData.Row[currentRows-1].R
|
|
||||||
if lastRow >= row {
|
|
||||||
row = lastRow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := currentRows; i < row; i++ {
|
|
||||||
xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
|
|
||||||
R: i + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
buffer := bytes.Buffer{}
|
|
||||||
for ii := currentRows; ii < row; ii++ {
|
|
||||||
start := len(xlsx.SheetData.Row[ii].C)
|
|
||||||
if start == 0 {
|
|
||||||
for iii := start; iii < cell; iii++ {
|
|
||||||
buffer.WriteString(ToAlphaString(iii))
|
|
||||||
buffer.WriteString(strconv.Itoa(ii + 1))
|
|
||||||
xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{
|
|
||||||
R: buffer.String(),
|
|
||||||
})
|
|
||||||
buffer.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkSheet provides function to fill each row element and make that is
|
// checkSheet provides function to fill each row element and make that is
|
||||||
// continuous in a worksheet of XML.
|
// continuous in a worksheet of XML.
|
||||||
func checkSheet(xlsx *xlsxWorksheet) {
|
func checkSheet(xlsx *xlsxWorksheet) {
|
||||||
|
@ -346,61 +133,6 @@ func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string {
|
||||||
return workbookMarshal
|
return workbookMarshal
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkRow provides function to check and fill each column element for all rows
|
|
||||||
// and make that is continuous in a worksheet of XML. For example:
|
|
||||||
//
|
|
||||||
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
|
||||||
// <c r="A15" s="2" />
|
|
||||||
// <c r="B15" s="2" />
|
|
||||||
// <c r="F15" s="1" />
|
|
||||||
// <c r="G15" s="1" />
|
|
||||||
// </row>
|
|
||||||
//
|
|
||||||
// in this case, we should to change it to
|
|
||||||
//
|
|
||||||
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
|
||||||
// <c r="A15" s="2" />
|
|
||||||
// <c r="B15" s="2" />
|
|
||||||
// <c r="C15" s="2" />
|
|
||||||
// <c r="D15" s="2" />
|
|
||||||
// <c r="E15" s="2" />
|
|
||||||
// <c r="F15" s="1" />
|
|
||||||
// <c r="G15" s="1" />
|
|
||||||
// </row>
|
|
||||||
//
|
|
||||||
// Noteice: this method could be very slow for large spreadsheets (more than
|
|
||||||
// 3000 rows one sheet).
|
|
||||||
func checkRow(xlsx *xlsxWorksheet) {
|
|
||||||
buffer := bytes.Buffer{}
|
|
||||||
for k, v := range xlsx.SheetData.Row {
|
|
||||||
lenCol := len(v.C)
|
|
||||||
if lenCol < 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
endR := string(strings.Map(letterOnlyMapF, v.C[lenCol-1].R))
|
|
||||||
endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, v.C[lenCol-1].R))
|
|
||||||
endCol := TitleToNumber(endR) + 1
|
|
||||||
if lenCol < endCol {
|
|
||||||
oldRow := xlsx.SheetData.Row[k].C
|
|
||||||
xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
|
|
||||||
tmp := []xlsxC{}
|
|
||||||
for i := 0; i <= endCol; i++ {
|
|
||||||
buffer.WriteString(ToAlphaString(i))
|
|
||||||
buffer.WriteString(strconv.Itoa(endRow))
|
|
||||||
tmp = append(tmp, xlsxC{
|
|
||||||
R: buffer.String(),
|
|
||||||
})
|
|
||||||
buffer.Reset()
|
|
||||||
}
|
|
||||||
xlsx.SheetData.Row[k].C = tmp
|
|
||||||
for _, y := range oldRow {
|
|
||||||
colAxis := TitleToNumber(string(strings.Map(letterOnlyMapF, y.R)))
|
|
||||||
xlsx.SheetData.Row[k].C[colAxis] = y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
||||||
// Office Excel 2007 and 2010. This function will be remove value tag when met a
|
// Office Excel 2007 and 2010. This function will be remove value tag when met a
|
||||||
// cell have a linked value. Reference
|
// cell have a linked value. Reference
|
||||||
|
@ -439,3 +171,224 @@ func (f *File) UpdateLinkedValue() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjustHelper provides function to adjust rows and columns dimensions,
|
||||||
|
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
|
||||||
|
// columns.
|
||||||
|
//
|
||||||
|
// sheet: Worksheet index that we're editing
|
||||||
|
// column: Index number of the column we're inserting/deleting before
|
||||||
|
// row: Index number of the row we're inserting/deleting before
|
||||||
|
// offset: Number of rows/column to insert/delete negative values indicate deletion
|
||||||
|
//
|
||||||
|
// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells
|
||||||
|
//
|
||||||
|
func (f *File) adjustHelper(sheet string, column, row, offset int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
f.adjustRowDimensions(xlsx, row, offset)
|
||||||
|
f.adjustColDimensions(xlsx, column, offset)
|
||||||
|
f.adjustHyperlinks(sheet, column, row, offset)
|
||||||
|
f.adjustMergeCells(xlsx, column, row, offset)
|
||||||
|
f.adjustAutoFilter(xlsx, column, row, offset)
|
||||||
|
checkSheet(xlsx)
|
||||||
|
checkRow(xlsx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustColDimensions provides function to update column dimensions when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, column, offset int) {
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
for k, v := range r.C {
|
||||||
|
axis := v.R
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
if yAxis >= column && column != -1 {
|
||||||
|
xlsx.SheetData.Row[i].C[k].R = ToAlphaString(yAxis+offset) + strconv.Itoa(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustRowDimensions provides function to update row dimensions when inserting
|
||||||
|
// or deleting rows or columns.
|
||||||
|
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, rowIndex, offset int) {
|
||||||
|
if rowIndex == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if r.R < rowIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[i].R += offset
|
||||||
|
for k, v := range xlsx.SheetData.Row[i].C {
|
||||||
|
axis := v.R
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row + offset
|
||||||
|
xlsx.SheetData.Row[i].C[k].R = col + strconv.Itoa(xAxis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustHyperlinks provides function to update hyperlinks when inserting or
|
||||||
|
// deleting rows or columns.
|
||||||
|
func (f *File) adjustHyperlinks(sheet string, column, rowIndex, offset int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
|
||||||
|
// order is important
|
||||||
|
if xlsx.Hyperlinks != nil && offset < 0 {
|
||||||
|
for i, v := range xlsx.Hyperlinks.Hyperlink {
|
||||||
|
axis := v.Ref
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
if row == rowIndex || yAxis == column {
|
||||||
|
f.deleteSheetRelationships(sheet, v.RID)
|
||||||
|
if len(xlsx.Hyperlinks.Hyperlink) > 1 {
|
||||||
|
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:i], xlsx.Hyperlinks.Hyperlink[i+1:]...)
|
||||||
|
} else {
|
||||||
|
xlsx.Hyperlinks = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if xlsx.Hyperlinks != nil {
|
||||||
|
for i, v := range xlsx.Hyperlinks.Hyperlink {
|
||||||
|
axis := v.Ref
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row + offset
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
if rowIndex != -1 && row >= rowIndex {
|
||||||
|
xlsx.Hyperlinks.Hyperlink[i].Ref = col + strconv.Itoa(xAxis)
|
||||||
|
}
|
||||||
|
if column != -1 && yAxis >= column {
|
||||||
|
xlsx.Hyperlinks.Hyperlink[i].Ref = ToAlphaString(yAxis+offset) + strconv.Itoa(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustMergeCellsHelper provides function to update merged cells when inserting or
|
||||||
|
// deleting rows or columns.
|
||||||
|
func (f *File) adjustMergeCellsHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
if xlsx.MergeCells != nil {
|
||||||
|
for k, v := range xlsx.MergeCells.Cells {
|
||||||
|
beg := strings.Split(v.Ref, ":")[0]
|
||||||
|
end := strings.Split(v.Ref, ":")[1]
|
||||||
|
|
||||||
|
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||||
|
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||||
|
begxAxis := begrow + offset
|
||||||
|
begyAxis := TitleToNumber(begcol)
|
||||||
|
|
||||||
|
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||||
|
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||||
|
endxAxis := endrow + offset
|
||||||
|
endyAxis := TitleToNumber(endcol)
|
||||||
|
|
||||||
|
if rowIndex != -1 {
|
||||||
|
if begrow > 1 && begrow >= rowIndex {
|
||||||
|
beg = begcol + strconv.Itoa(begxAxis)
|
||||||
|
}
|
||||||
|
if endrow > 1 && endrow >= rowIndex {
|
||||||
|
end = endcol + strconv.Itoa(endxAxis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if column != -1 {
|
||||||
|
if begyAxis >= column {
|
||||||
|
beg = ToAlphaString(begyAxis+offset) + strconv.Itoa(endrow)
|
||||||
|
}
|
||||||
|
if endyAxis >= column {
|
||||||
|
end = ToAlphaString(endyAxis+offset) + strconv.Itoa(endrow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xlsx.MergeCells.Cells[k].Ref = beg + ":" + end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustMergeCells provides function to update merged cells when inserting or
|
||||||
|
// deleting rows or columns.
|
||||||
|
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
f.adjustMergeCellsHelper(xlsx, column, rowIndex, offset)
|
||||||
|
|
||||||
|
if xlsx.MergeCells != nil && offset < 0 {
|
||||||
|
for k, v := range xlsx.MergeCells.Cells {
|
||||||
|
beg := strings.Split(v.Ref, ":")[0]
|
||||||
|
end := strings.Split(v.Ref, ":")[1]
|
||||||
|
if beg == end {
|
||||||
|
xlsx.MergeCells.Count += offset
|
||||||
|
if len(xlsx.MergeCells.Cells) > 1 {
|
||||||
|
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:k], xlsx.MergeCells.Cells[k+1:]...)
|
||||||
|
} else {
|
||||||
|
xlsx.MergeCells = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustAutoFilter provides function to update the auto filter when inserting
|
||||||
|
// or deleting rows or columns.
|
||||||
|
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
f.adjustAutoFilterHelper(xlsx, column, rowIndex, offset)
|
||||||
|
|
||||||
|
if xlsx.AutoFilter != nil {
|
||||||
|
beg := strings.Split(xlsx.AutoFilter.Ref, ":")[0]
|
||||||
|
end := strings.Split(xlsx.AutoFilter.Ref, ":")[1]
|
||||||
|
|
||||||
|
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||||
|
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||||
|
begxAxis := begrow + offset
|
||||||
|
|
||||||
|
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||||
|
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||||
|
endxAxis := endrow + offset
|
||||||
|
endyAxis := TitleToNumber(endcol)
|
||||||
|
|
||||||
|
if rowIndex != -1 {
|
||||||
|
if begrow >= rowIndex {
|
||||||
|
beg = begcol + strconv.Itoa(begxAxis)
|
||||||
|
}
|
||||||
|
if endrow >= rowIndex {
|
||||||
|
end = endcol + strconv.Itoa(endxAxis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if column != -1 && endyAxis >= column {
|
||||||
|
end = ToAlphaString(endyAxis+offset) + strconv.Itoa(endrow)
|
||||||
|
}
|
||||||
|
xlsx.AutoFilter.Ref = beg + ":" + end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustAutoFilterHelper provides function to update the auto filter when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustAutoFilterHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
if xlsx.AutoFilter != nil {
|
||||||
|
beg := strings.Split(xlsx.AutoFilter.Ref, ":")[0]
|
||||||
|
end := strings.Split(xlsx.AutoFilter.Ref, ":")[1]
|
||||||
|
|
||||||
|
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||||
|
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||||
|
begyAxis := TitleToNumber(begcol)
|
||||||
|
|
||||||
|
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||||
|
endyAxis := TitleToNumber(endcol)
|
||||||
|
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||||
|
|
||||||
|
if (begrow == rowIndex && offset < 0) || (column == begyAxis && column == endyAxis) {
|
||||||
|
xlsx.AutoFilter = nil
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if begrow < r.R && r.R <= endrow {
|
||||||
|
xlsx.SheetData.Row[i].Hidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -750,7 +750,88 @@ func TestAddChart(t *testing.T) {
|
||||||
xlsx.AddChart("SHEET2", "P1", `{"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"}],"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":"Fruit 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"}`)
|
xlsx.AddChart("SHEET2", "P1", `{"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"}],"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":"Fruit 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"}`)
|
||||||
xlsx.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"}],"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":"Fruit 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"}`)
|
xlsx.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"}],"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":"Fruit 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"}`)
|
||||||
// Save xlsx file by the given path.
|
// Save xlsx file by the given path.
|
||||||
err = xlsx.SaveAs("./test/Workbook_6.xlsx")
|
err = xlsx.SaveAs("./test/Workbook_addchart.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertCol(t *testing.T) {
|
||||||
|
xlsx := NewFile()
|
||||||
|
for j := 1; j <= 10; j++ {
|
||||||
|
for i := 0; i <= 10; i++ {
|
||||||
|
axis := ToAlphaString(i) + strconv.Itoa(j)
|
||||||
|
xlsx.SetCellStr("Sheet1", axis, axis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SetCellHyperLink("Sheet1", "A5", "https://github.com/xuri/excelize")
|
||||||
|
xlsx.MergeCell("sheet1", "A1", "C3")
|
||||||
|
err := xlsx.AutoFilter("Sheet1", "A2", "B2", `{"column":"B","expression":"x != blanks"}`)
|
||||||
|
t.Log(err)
|
||||||
|
xlsx.InsertCol("Sheet1", "A")
|
||||||
|
err = xlsx.SaveAs("./test/Workbook_insertcol.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveCol(t *testing.T) {
|
||||||
|
xlsx := NewFile()
|
||||||
|
for j := 1; j <= 10; j++ {
|
||||||
|
for i := 0; i <= 10; i++ {
|
||||||
|
axis := ToAlphaString(i) + strconv.Itoa(j)
|
||||||
|
xlsx.SetCellStr("Sheet1", axis, axis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SetCellHyperLink("Sheet1", "A5", "https://github.com/xuri/excelize")
|
||||||
|
xlsx.SetCellHyperLink("Sheet1", "C5", "https://github.com")
|
||||||
|
xlsx.MergeCell("sheet1", "A1", "B1")
|
||||||
|
xlsx.MergeCell("sheet1", "A2", "B2")
|
||||||
|
xlsx.RemoveCol("Sheet1", "A")
|
||||||
|
xlsx.RemoveCol("Sheet1", "A")
|
||||||
|
err := xlsx.SaveAs("./test/Workbook_removecol.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertRow(t *testing.T) {
|
||||||
|
xlsx := NewFile()
|
||||||
|
for j := 1; j <= 10; j++ {
|
||||||
|
for i := 0; i <= 10; i++ {
|
||||||
|
axis := ToAlphaString(i) + strconv.Itoa(j)
|
||||||
|
xlsx.SetCellStr("Sheet1", axis, axis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SetCellHyperLink("Sheet1", "A5", "https://github.com/xuri/excelize")
|
||||||
|
xlsx.InsertRow("Sheet1", -1)
|
||||||
|
xlsx.InsertRow("Sheet1", 4)
|
||||||
|
err := xlsx.SaveAs("./test/Workbook_insertrow.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRow(t *testing.T) {
|
||||||
|
xlsx := NewFile()
|
||||||
|
for j := 1; j <= 10; j++ {
|
||||||
|
for i := 0; i <= 10; i++ {
|
||||||
|
axis := ToAlphaString(i) + strconv.Itoa(j)
|
||||||
|
xlsx.SetCellStr("Sheet1", axis, axis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SetCellHyperLink("Sheet1", "A5", "https://github.com/xuri/excelize")
|
||||||
|
xlsx.RemoveRow("Sheet1", -1)
|
||||||
|
xlsx.RemoveRow("Sheet1", 4)
|
||||||
|
xlsx.MergeCell("sheet1", "B3", "B5")
|
||||||
|
xlsx.RemoveRow("Sheet1", 2)
|
||||||
|
xlsx.RemoveRow("Sheet1", 4)
|
||||||
|
err := xlsx.AutoFilter("Sheet1", "A2", "A2", `{"column":"A","expression":"x != blanks"}`)
|
||||||
|
t.Log(err)
|
||||||
|
xlsx.RemoveRow("Sheet1", 0)
|
||||||
|
xlsx.RemoveRow("Sheet1", 1)
|
||||||
|
xlsx.RemoveRow("Sheet1", 0)
|
||||||
|
err = xlsx.SaveAs("./test/Workbook_removerow.xlsx")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
}
|
}
|
||||||
|
|
17
picture.go
17
picture.go
|
@ -127,6 +127,23 @@ func (f *File) addSheetRelationships(sheet, relType, target, targetMode string)
|
||||||
return rID
|
return rID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteSheetRelationships provides function to delete relationships in
|
||||||
|
// xl/worksheets/_rels/sheet%d.xml.rels by given sheet name and relationship
|
||||||
|
// index.
|
||||||
|
func (f *File) deleteSheetRelationships(sheet, rID string) {
|
||||||
|
var rels = "xl/worksheets/_rels/" + strings.ToLower(sheet) + ".xml.rels"
|
||||||
|
var sheetRels xlsxWorkbookRels
|
||||||
|
xml.Unmarshal([]byte(f.readXML(rels)), &sheetRels)
|
||||||
|
for k, v := range sheetRels.Relationships {
|
||||||
|
if v.ID != rID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...)
|
||||||
|
}
|
||||||
|
output, _ := xml.Marshal(sheetRels)
|
||||||
|
f.saveFileList(rels, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
// addSheetLegacyDrawing provides function to add legacy drawing element to
|
// addSheetLegacyDrawing provides function to add legacy drawing element to
|
||||||
// xl/worksheets/sheet%d.xml by given sheet name and relationship index.
|
// xl/worksheets/sheet%d.xml by given sheet name and relationship index.
|
||||||
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
||||||
|
|
135
rows.go
135
rows.go
|
@ -1,7 +1,9 @@
|
||||||
package excelize
|
package excelize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -209,3 +211,136 @@ func (f *File) GetRowVisible(sheet string, rowIndex int) bool {
|
||||||
completeRow(xlsx, rows, cells)
|
completeRow(xlsx, rows, cells)
|
||||||
return !xlsx.SheetData.Row[rowIndex].Hidden
|
return !xlsx.SheetData.Row[rowIndex].Hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveRow provides function to remove single row by given worksheet index and
|
||||||
|
// row index. For example, remove row 3 in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.RemoveRow("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) RemoveRow(sheet string, row int) {
|
||||||
|
if row < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
row++
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if r.R != row {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:i], xlsx.SheetData.Row[i+1:]...)
|
||||||
|
f.adjustHelper(sheet, -1, row, -1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertRow provides function to insert a new row before given row index. For
|
||||||
|
// example, create a new row before row 3 in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.InsertRow("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) InsertRow(sheet string, row int) {
|
||||||
|
if row < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
row++
|
||||||
|
f.adjustHelper(sheet, -1, row, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRow provides function to check and fill each column element for all rows
|
||||||
|
// and make that is continuous in a worksheet of XML. For example:
|
||||||
|
//
|
||||||
|
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
||||||
|
// <c r="A15" s="2" />
|
||||||
|
// <c r="B15" s="2" />
|
||||||
|
// <c r="F15" s="1" />
|
||||||
|
// <c r="G15" s="1" />
|
||||||
|
// </row>
|
||||||
|
//
|
||||||
|
// in this case, we should to change it to
|
||||||
|
//
|
||||||
|
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
||||||
|
// <c r="A15" s="2" />
|
||||||
|
// <c r="B15" s="2" />
|
||||||
|
// <c r="C15" s="2" />
|
||||||
|
// <c r="D15" s="2" />
|
||||||
|
// <c r="E15" s="2" />
|
||||||
|
// <c r="F15" s="1" />
|
||||||
|
// <c r="G15" s="1" />
|
||||||
|
// </row>
|
||||||
|
//
|
||||||
|
// Noteice: this method could be very slow for large spreadsheets (more than
|
||||||
|
// 3000 rows one sheet).
|
||||||
|
func checkRow(xlsx *xlsxWorksheet) {
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
for k, v := range xlsx.SheetData.Row {
|
||||||
|
lenCol := len(v.C)
|
||||||
|
if lenCol < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
endR := string(strings.Map(letterOnlyMapF, v.C[lenCol-1].R))
|
||||||
|
endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, v.C[lenCol-1].R))
|
||||||
|
endCol := TitleToNumber(endR) + 1
|
||||||
|
if lenCol < endCol {
|
||||||
|
oldRow := xlsx.SheetData.Row[k].C
|
||||||
|
xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
|
||||||
|
tmp := []xlsxC{}
|
||||||
|
for i := 0; i <= endCol; i++ {
|
||||||
|
buffer.WriteString(ToAlphaString(i))
|
||||||
|
buffer.WriteString(strconv.Itoa(endRow))
|
||||||
|
tmp = append(tmp, xlsxC{
|
||||||
|
R: buffer.String(),
|
||||||
|
})
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[k].C = tmp
|
||||||
|
for _, y := range oldRow {
|
||||||
|
colAxis := TitleToNumber(string(strings.Map(letterOnlyMapF, y.R)))
|
||||||
|
xlsx.SheetData.Row[k].C[colAxis] = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// completeRow provides function to check and fill each column element for a
|
||||||
|
// single row and make that is continuous in a worksheet of XML by given row
|
||||||
|
// index and axis.
|
||||||
|
func completeRow(xlsx *xlsxWorksheet, row, cell int) {
|
||||||
|
currentRows := len(xlsx.SheetData.Row)
|
||||||
|
if currentRows > 1 {
|
||||||
|
lastRow := xlsx.SheetData.Row[currentRows-1].R
|
||||||
|
if lastRow >= row {
|
||||||
|
row = lastRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := currentRows; i < row; i++ {
|
||||||
|
xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
|
||||||
|
R: i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
for ii := currentRows; ii < row; ii++ {
|
||||||
|
start := len(xlsx.SheetData.Row[ii].C)
|
||||||
|
if start == 0 {
|
||||||
|
for iii := start; iii < cell; iii++ {
|
||||||
|
buffer.WriteString(ToAlphaString(iii))
|
||||||
|
buffer.WriteString(strconv.Itoa(ii + 1))
|
||||||
|
xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{
|
||||||
|
R: buffer.String(),
|
||||||
|
})
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertRowHeightToPixels provides function to convert the height of a cell
|
||||||
|
// from user's units to pixels. If the height hasn't been set by the user we use
|
||||||
|
// the default value. If the row is hidden it has a value of zero.
|
||||||
|
func convertRowHeightToPixels(height float64) float64 {
|
||||||
|
var pixels float64
|
||||||
|
if height == 0 {
|
||||||
|
return pixels
|
||||||
|
}
|
||||||
|
pixels = math.Ceil(4.0 / 3.0 * height)
|
||||||
|
return pixels
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue