- New function `SetSheetName` and `SetColWidth` added, support rename sheet and set column width;

- Add escape characters of sheet name;
- Update go test and fix typo
This commit is contained in:
Ri Xu 2017-01-18 14:47:23 +08:00
parent a99f0227b0
commit f05df2a018
10 changed files with 151 additions and 65 deletions

14
cell.go
View File

@ -13,7 +13,7 @@ func (f *File) GetCellValue(sheet string, axis string) string {
var xlsx xlsxWorksheet
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
xAxis := row - 1
name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
rows := len(xlsx.SheetData.Row)
if rows > 1 {
@ -23,7 +23,7 @@ func (f *File) GetCellValue(sheet string, axis string) string {
}
}
if rows <= xAxis {
return ``
return ""
}
for _, v := range xlsx.SheetData.Row {
if v.R != row {
@ -38,7 +38,7 @@ func (f *File) GetCellValue(sheet string, axis string) string {
shardStrings := xlsxSST{}
xlsxSI := 0
xlsxSI, _ = strconv.Atoi(r.V)
xml.Unmarshal([]byte(f.readXML(`xl/sharedStrings.xml`)), &shardStrings)
xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings)
return shardStrings.SI[xlsxSI].T
case "str":
return r.V
@ -47,7 +47,7 @@ func (f *File) GetCellValue(sheet string, axis string) string {
}
}
}
return ``
return ""
}
// GetCellFormula provide function get formula from cell by given sheet index and axis in XLSX file.
@ -56,7 +56,7 @@ func (f *File) GetCellFormula(sheet string, axis string) string {
var xlsx xlsxWorksheet
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
xAxis := row - 1
name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
rows := len(xlsx.SheetData.Row)
if rows > 1 {
@ -66,7 +66,7 @@ func (f *File) GetCellFormula(sheet string, axis string) string {
}
}
if rows <= xAxis {
return ``
return ""
}
for _, v := range xlsx.SheetData.Row {
if v.R != row {
@ -81,5 +81,5 @@ func (f *File) GetCellFormula(sheet string, axis string) string {
}
}
}
return ``
return ""
}

43
col.go Normal file
View File

@ -0,0 +1,43 @@
package excelize
import (
"encoding/xml"
"strings"
)
// SetColWidth provides function to set the width of a single column or multiple columns.
// For example:
//
// xlsx := excelize.CreateFile()
// xlsx.SetColWidth("Sheet1", "A", "H", 20)
// err := xlsx.Save()
// if err != nil {
// fmt.Println(err)
// os.Exit(1)
// }
//
func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) {
min := titleToNumber(strings.ToUpper(startcol)) + 1
max := titleToNumber(strings.ToUpper(endcol)) + 1
if min > max {
min, max = max, min
}
var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
col := xlsxCol{
Min: min,
Max: max,
Width: width,
CustomWidth: true,
}
if xlsx.Cols != nil {
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
} else {
cols := xlsxCols{}
cols.Col = append(cols.Col, col)
xlsx.Cols = &cols
}
output, _ := xml.Marshal(xlsx)
f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}

View File

@ -56,7 +56,7 @@ func (f *File) SetCellValue(sheet string, axis string, value interface{}) {
case []byte:
f.SetCellStr(sheet, axis, string(t))
default:
f.SetCellStr(sheet, axis, ``)
f.SetCellStr(sheet, axis, "")
}
}
@ -296,7 +296,7 @@ func (f *File) UpdateLinkedValue() {
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
for indexR, row := range xlsx.SheetData.Row {
for indexC, col := range row.C {
if col.F != nil && col.V != `` {
if col.F != nil && col.V != "" {
xlsx.SheetData.Row[indexR].C[indexC].V = ""
xlsx.SheetData.Row[indexR].C[indexC].T = ""
}

View File

@ -5,7 +5,7 @@ import (
"testing"
)
func TestExcelize(t *testing.T) {
func TestOpenFile(t *testing.T) {
// Test update a XLSX file.
f1, err := OpenFile("./test/Workbook1.xlsx")
if err != nil {
@ -26,7 +26,9 @@ func TestExcelize(t *testing.T) {
f1.SetCellDefault("SHEET2", "A1", strconv.FormatFloat(float64(-100.1588), 'f', -1, 64))
f1.SetCellInt("SHEET2", "A1", 100)
f1.SetCellStr("SHEET2", "C11", "Knowns")
f1.NewSheet(3, "Maximum 31 characters allowed in sheet title.")
f1.NewSheet(3, ":\\/?*[]Maximum 31 characters allowed in sheet title.")
// Test set sheet name with illegal name.
f1.SetSheetName("Maximum 31 characters allowed i", "[Rename]:\\/?* Maximum 31 characters allowed in sheet title.")
f1.SetCellInt("Sheet3", "A23", 10)
f1.SetCellStr("SHEET3", "b230", "10")
f1.SetCellStr("SHEET10", "b230", "10")
@ -122,7 +124,9 @@ func TestExcelize(t *testing.T) {
if err != nil {
t.Log(err)
}
}
func TestCreateFile(t *testing.T) {
// Test create a XLSX file.
f3 := CreateFile()
f3.NewSheet(2, "XLSXSheet2")
@ -131,7 +135,7 @@ func TestExcelize(t *testing.T) {
f3.SetCellStr("SHEET1", "B20", "42")
f3.SetActiveSheet(0)
// Test add picture to sheet.
err = f3.AddPicture("Sheet1", "H2", "K12", "./test/images/excel.gif")
err := f3.AddPicture("Sheet1", "H2", "K12", "./test/images/excel.gif")
if err != nil {
t.Log(err)
}
@ -143,7 +147,9 @@ func TestExcelize(t *testing.T) {
if err != nil {
t.Log(err)
}
}
func TestBrokenFile(t *testing.T) {
// Test set active sheet without BookViews and Sheets maps in xl/workbook.xml.
f4, err := OpenFile("./test/badWorkbook.xlsx")
f4.SetActiveSheet(2)
@ -157,3 +163,16 @@ func TestExcelize(t *testing.T) {
t.Log(err)
}
}
func TestSetColWidth(t *testing.T) {
f5, err := OpenFile("./test/Workbook1.xlsx")
if err != nil {
t.Log(err)
}
f5.SetColWidth("sheet1", "B", "A", 12)
f5.SetColWidth("sheet1", "A", "B", 12)
err = f5.Save()
if err != nil {
t.Log(err)
}
}

18
file.go
View File

@ -11,15 +11,15 @@ import (
// xlsx := CreateFile()
func CreateFile() *File {
file := make(map[string]string)
file[`_rels/.rels`] = templateRels
file[`docProps/app.xml`] = templateDocpropsApp
file[`docProps/core.xml`] = templateDocpropsCore
file[`xl/_rels/workbook.xml.rels`] = templateWorkbookRels
file[`xl/theme/theme1.xml`] = templateTheme
file[`xl/worksheets/sheet1.xml`] = templateSheet
file[`xl/styles.xml`] = templateStyles
file[`xl/workbook.xml`] = templateWorkbook
file[`[Content_Types].xml`] = templateContentTypes
file["_rels/.rels"] = templateRels
file["docProps/app.xml"] = templateDocpropsApp
file["docProps/core.xml"] = templateDocpropsCore
file["xl/_rels/workbook.xml.rels"] = templateWorkbookRels
file["xl/theme/theme1.xml"] = templateTheme
file["xl/worksheets/sheet1.xml"] = templateSheet
file["xl/styles.xml"] = templateStyles
file["xl/workbook.xml"] = templateWorkbook
file["[Content_Types].xml"] = templateContentTypes
return &File{
XLSX: file,
}

2
lib.go
View File

@ -25,7 +25,7 @@ func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
for _, v := range r.File {
fileList[v.Name] = readFile(v)
if len(v.Name) > 18 {
if v.Name[0:19] == `xl/worksheets/sheet` {
if v.Name[0:19] == "xl/worksheets/sheet" {
var xlsx xlsxWorksheet
xml.Unmarshal([]byte(fileList[v.Name]), &xlsx)
xlsx = checkRow(xlsx)

View File

@ -19,7 +19,7 @@ import (
func (f *File) GetRows(sheet string) [][]string {
xlsx := xlsxWorksheet{}
r := [][]string{}
name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
err := xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if err != nil {
return r
@ -39,7 +39,7 @@ func (f *File) GetRows(sheet string) [][]string {
// readXMLSST read xmlSST simple function.
func readXMLSST(f *File) (xlsxSST, error) {
shardStrings := xlsxSST{}
err := xml.Unmarshal([]byte(f.readXML(`xl/sharedStrings.xml`)), &shardStrings)
err := xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings)
return shardStrings, err
}

View File

@ -27,22 +27,22 @@ func (f *File) NewSheet(index int, name string) {
// Read and update property of contents type of XLSX.
func (f *File) setContentTypes(index int) {
var content xlsxTypes
xml.Unmarshal([]byte(f.readXML(`[Content_Types].xml`)), &content)
xml.Unmarshal([]byte(f.readXML("[Content_Types].xml")), &content)
content.Overrides = append(content.Overrides, xlsxOverride{
PartName: `/xl/worksheets/sheet` + strconv.Itoa(index) + `.xml`,
ContentType: `application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml`,
PartName: "/xl/worksheets/sheet" + strconv.Itoa(index) + ".xml",
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
})
output, err := xml.Marshal(content)
if err != nil {
fmt.Println(err)
}
f.saveFileList(`[Content_Types].xml`, string(output))
f.saveFileList("[Content_Types].xml", string(output))
}
// Update sheet property by given index.
func (f *File) setSheet(index int) {
var xlsx xlsxWorksheet
xlsx.Dimension.Ref = `A1`
xlsx.Dimension.Ref = "A1"
xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{
WorkbookViewID: 0,
})
@ -50,33 +50,35 @@ func (f *File) setSheet(index int) {
if err != nil {
fmt.Println(err)
}
path := `xl/worksheets/sheet` + strconv.Itoa(index) + `.xml`
path := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml"
f.saveFileList(path, replaceWorkSheetsRelationshipsNameSpace(string(output)))
}
// Update workbook property of XLSX. Maximum 31 characters allowed in sheet title.
// Update workbook property of XLSX. Maximum 31 characters are allowed in sheet title.
func (f *File) setWorkbook(name string, rid int) {
var content xlsxWorkbook
r := strings.NewReplacer(":", "", "\\", "", "/", "", "?", "", "*", "", "[", "", "]", "")
name = r.Replace(name)
if len(name) > 31 {
name = name[0:31]
}
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
Name: name,
SheetID: strconv.Itoa(rid),
ID: `rId` + strconv.Itoa(rid),
ID: "rId" + strconv.Itoa(rid),
})
output, err := xml.Marshal(content)
if err != nil {
fmt.Println(err)
}
f.saveFileList(`xl/workbook.xml`, replaceRelationshipsNameSpace(string(output)))
f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
}
// Read and unmarshal workbook relationships of XLSX.
func (f *File) readXlsxWorkbookRels() xlsxWorkbookRels {
var content xlsxWorkbookRels
xml.Unmarshal([]byte(f.readXML(`xl/_rels/workbook.xml.rels`)), &content)
xml.Unmarshal([]byte(f.readXML("xl/_rels/workbook.xml.rels")), &content)
return content
}
@ -85,28 +87,28 @@ func (f *File) addXlsxWorkbookRels(sheet int) int {
content := f.readXlsxWorkbookRels()
rID := len(content.Relationships) + 1
ID := bytes.Buffer{}
ID.WriteString(`rId`)
ID.WriteString("rId")
ID.WriteString(strconv.Itoa(rID))
target := bytes.Buffer{}
target.WriteString(`worksheets/sheet`)
target.WriteString("worksheets/sheet")
target.WriteString(strconv.Itoa(sheet))
target.WriteString(`.xml`)
target.WriteString(".xml")
content.Relationships = append(content.Relationships, xlsxWorkbookRelation{
ID: ID.String(),
Target: target.String(),
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet`,
Type: SourceRelationshipWorkSheet,
})
output, err := xml.Marshal(content)
if err != nil {
fmt.Println(err)
}
f.saveFileList(`xl/_rels/workbook.xml.rels`, string(output))
f.saveFileList("xl/_rels/workbook.xml.rels", string(output))
return rID
}
// Update docProps/app.xml file of XML.
func (f *File) setAppXML() {
f.saveFileList(`docProps/app.xml`, templateDocpropsApp)
f.saveFileList("docProps/app.xml", templateDocpropsApp)
}
// Some tools that read XLSX files have very strict requirements about
@ -129,7 +131,7 @@ func (f *File) SetActiveSheet(index int) {
index = 1
}
index--
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
if len(content.BookViews.WorkBookView) > 0 {
content.BookViews.WorkBookView[0].ActiveTab = index
} else {
@ -142,15 +144,15 @@ func (f *File) SetActiveSheet(index int) {
if err != nil {
fmt.Println(err)
}
f.saveFileList(`xl/workbook.xml`, replaceRelationshipsNameSpace(string(output)))
f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
index++
buffer := bytes.Buffer{}
for i := 0; i < sheets; i++ {
xlsx := xlsxWorksheet{}
sheetIndex := i + 1
buffer.WriteString(`xl/worksheets/sheet`)
buffer.WriteString("xl/worksheets/sheet")
buffer.WriteString(strconv.Itoa(sheetIndex))
buffer.WriteString(`.xml`)
buffer.WriteString(".xml")
xml.Unmarshal([]byte(f.readXML(buffer.String())), &xlsx)
if index == sheetIndex {
if len(xlsx.SheetViews.SheetView) > 0 {
@ -175,21 +177,21 @@ func (f *File) SetActiveSheet(index int) {
return
}
// GetActiveSheetIndex provide function to get active sheet of XLSX. If not found
// GetActiveSheetIndex provides function to get active sheet of XLSX. If not found
// the active sheet will be return integer 0.
func (f *File) GetActiveSheetIndex() int {
content := xlsxWorkbook{}
buffer := bytes.Buffer{}
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
for _, v := range content.Sheets.Sheet {
xlsx := xlsxWorksheet{}
buffer.WriteString(`xl/worksheets/sheet`)
buffer.WriteString(strings.TrimPrefix(v.ID, `rId`))
buffer.WriteString(`.xml`)
buffer.WriteString("xl/worksheets/sheet")
buffer.WriteString(strings.TrimPrefix(v.ID, "rId"))
buffer.WriteString(".xml")
xml.Unmarshal([]byte(f.readXML(buffer.String())), &xlsx)
for _, sheetView := range xlsx.SheetViews.SheetView {
if sheetView.TabSelected {
id, _ := strconv.Atoi(strings.TrimPrefix(v.ID, `rId`))
id, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId"))
return id
}
}
@ -198,20 +200,41 @@ func (f *File) GetActiveSheetIndex() int {
return 0
}
// GetSheetName provide function to get sheet name of XLSX by given sheet index.
// SetSheetName provides function to set the sheet name be given old and new sheet name.
// Maximum 31 characters are allowed in sheet title and this function only changes the
// name of the sheet and will not update the sheet name in the formula or reference
// associated with the cell. So there may be problem formula error or reference missing.
func (f *File) SetSheetName(oldName, newName string) {
var content = xlsxWorkbook{}
r := strings.NewReplacer(":", "", "\\", "", "/", "", "?", "", "*", "", "[", "", "]", "")
newName = r.Replace(newName)
if len(newName) > 31 {
newName = newName[0:31]
}
xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
for k, v := range content.Sheets.Sheet {
if v.Name == oldName {
content.Sheets.Sheet[k].Name = newName
}
}
output, _ := xml.Marshal(content)
f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
}
// GetSheetName provides function to get sheet name of XLSX by given sheet index.
// If given sheet index is invalid, will return an empty string.
func (f *File) GetSheetName(index int) string {
content := xlsxWorkbook{}
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
var content = xlsxWorkbook{}
xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
for _, v := range content.Sheets.Sheet {
if v.ID == `rId`+strconv.Itoa(index) {
if v.ID == "rId"+strconv.Itoa(index) {
return v.Name
}
}
return ``
return ""
}
// GetSheetMap provide function to get sheet map of XLSX. For example:
// GetSheetMap provides function to get sheet map of XLSX. For example:
//
// xlsx, err := excelize.OpenFile("/tmp/Workbook.xlsx")
// if err != nil {
@ -225,9 +248,9 @@ func (f *File) GetSheetName(index int) string {
func (f *File) GetSheetMap() map[int]string {
content := xlsxWorkbook{}
sheetMap := map[int]string{}
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
for _, v := range content.Sheets.Sheet {
id, _ := strconv.Atoi(strings.TrimPrefix(v.ID, `rId`))
id, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId"))
sheetMap[id] = v.Name
}
return sheetMap

View File

@ -5,6 +5,7 @@ const (
SourceRelationship = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
NameSpaceDrawingML = "http://schemas.openxmlformats.org/drawingml/2006/main"
NameSpaceSpreadSheetDrawing = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
)

View File

@ -228,19 +228,19 @@ type xlsxCols struct {
Col []xlsxCol `xml:"col"`
}
// xlsxCol directly maps the col element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
// currently I have not checked it for completeness - it does as much
// as I need.
// xlsxCol directly maps the col (Column Width & Formatting). Defines column
// width and column formatting for one or more columns of the worksheet.
type xlsxCol struct {
BestFit bool `xml:"bestFit,attr,omitempty"`
Collapsed bool `xml:"collapsed,attr"`
CustomWidth bool `xml:"customWidth,attr,omitempty"`
Hidden bool `xml:"hidden,attr"`
Max int `xml:"max,attr"`
Min int `xml:"min,attr"`
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
Phonetic bool `xml:"phonetic,attr,omitempty"`
Style int `xml:"style,attr"`
Width float64 `xml:"width,attr"`
CustomWidth int `xml:"customWidth,attr,omitempty"`
OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"`
}
// xlsxDimension directly maps the dimension element in the namespace