|
@@ -4,7 +4,6 @@ import (
|
|
|
"bytes"
|
|
"bytes"
|
|
|
"encoding/xml"
|
|
"encoding/xml"
|
|
|
"errors"
|
|
"errors"
|
|
|
- "fmt"
|
|
|
|
|
"os"
|
|
"os"
|
|
|
"path"
|
|
"path"
|
|
|
"strconv"
|
|
"strconv"
|
|
@@ -25,21 +24,67 @@ func (f *File) NewSheet(index int, name string) {
|
|
|
rID := f.addXlsxWorkbookRels(index)
|
|
rID := f.addXlsxWorkbookRels(index)
|
|
|
// Update xl/workbook.xml
|
|
// Update xl/workbook.xml
|
|
|
f.setWorkbook(name, rID)
|
|
f.setWorkbook(name, rID)
|
|
|
|
|
+ f.SheetCount++
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// contentTypesReader provides function to get the pointer to the
|
|
|
|
|
+// [Content_Types].xml structure after deserialization.
|
|
|
|
|
+func (f *File) contentTypesReader() *xlsxTypes {
|
|
|
|
|
+ if f.ContentTypes == nil {
|
|
|
|
|
+ var content xlsxTypes
|
|
|
|
|
+ xml.Unmarshal([]byte(f.readXML("[Content_Types].xml")), &content)
|
|
|
|
|
+ f.ContentTypes = &content
|
|
|
|
|
+ }
|
|
|
|
|
+ return f.ContentTypes
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// contentTypesWriter provides function to save [Content_Types].xml after
|
|
|
|
|
+// serialize structure.
|
|
|
|
|
+func (f *File) contentTypesWriter() {
|
|
|
|
|
+ if f.ContentTypes != nil {
|
|
|
|
|
+ output, _ := xml.Marshal(f.ContentTypes)
|
|
|
|
|
+ f.saveFileList("[Content_Types].xml", string(output))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// workbookReader provides function to get the pointer to the xl/workbook.xml
|
|
|
|
|
+// structure after deserialization.
|
|
|
|
|
+func (f *File) workbookReader() *xlsxWorkbook {
|
|
|
|
|
+ if f.WorkBook == nil {
|
|
|
|
|
+ var content xlsxWorkbook
|
|
|
|
|
+ xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
+ f.WorkBook = &content
|
|
|
|
|
+ }
|
|
|
|
|
+ return f.WorkBook
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// workbookWriter provides function to save xl/workbook.xml after serialize
|
|
|
|
|
+// structure.
|
|
|
|
|
+func (f *File) workbookWriter() {
|
|
|
|
|
+ if f.WorkBook != nil {
|
|
|
|
|
+ output, _ := xml.Marshal(f.WorkBook)
|
|
|
|
|
+ f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// worksheetWriter provides function to save xl/worksheets/sheet%d.xml after
|
|
|
|
|
+// serialize structure.
|
|
|
|
|
+func (f *File) worksheetWriter() {
|
|
|
|
|
+ for path, sheet := range f.Sheet {
|
|
|
|
|
+ if sheet != nil {
|
|
|
|
|
+ output, _ := xml.Marshal(sheet)
|
|
|
|
|
+ f.saveFileList(path, replaceWorkSheetsRelationshipsNameSpace(string(output)))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Read and update property of contents type of XLSX.
|
|
// Read and update property of contents type of XLSX.
|
|
|
func (f *File) setContentTypes(index int) {
|
|
func (f *File) setContentTypes(index int) {
|
|
|
- var content xlsxTypes
|
|
|
|
|
- xml.Unmarshal([]byte(f.readXML("[Content_Types].xml")), &content)
|
|
|
|
|
|
|
+ content := f.contentTypesReader()
|
|
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
|
|
PartName: "/xl/worksheets/sheet" + strconv.Itoa(index) + ".xml",
|
|
PartName: "/xl/worksheets/sheet" + strconv.Itoa(index) + ".xml",
|
|
|
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+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))
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Update sheet property by given index.
|
|
// Update sheet property by given index.
|
|
@@ -56,35 +101,42 @@ func (f *File) setSheet(index int) {
|
|
|
// setWorkbook update workbook property of XLSX. Maximum 31 characters are
|
|
// setWorkbook update workbook property of XLSX. Maximum 31 characters are
|
|
|
// allowed in sheet title.
|
|
// allowed in sheet title.
|
|
|
func (f *File) setWorkbook(name string, rid int) {
|
|
func (f *File) setWorkbook(name string, rid int) {
|
|
|
- var content xlsxWorkbook
|
|
|
|
|
r := strings.NewReplacer(":", "", "\\", "", "/", "", "?", "", "*", "", "[", "", "]", "")
|
|
r := strings.NewReplacer(":", "", "\\", "", "/", "", "?", "", "*", "", "[", "", "]", "")
|
|
|
name = r.Replace(name)
|
|
name = r.Replace(name)
|
|
|
if len(name) > 31 {
|
|
if len(name) > 31 {
|
|
|
name = name[0:31]
|
|
name = name[0:31]
|
|
|
}
|
|
}
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
|
|
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
|
|
|
Name: name,
|
|
Name: name,
|
|
|
SheetID: strconv.Itoa(rid),
|
|
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)
|
|
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// workbookRelsReader provides function to read and unmarshal workbook
|
|
|
|
|
+// relationships of XLSX file.
|
|
|
|
|
+func (f *File) workbookRelsReader() *xlsxWorkbookRels {
|
|
|
|
|
+ if f.WorkBookRels == nil {
|
|
|
|
|
+ var content xlsxWorkbookRels
|
|
|
|
|
+ xml.Unmarshal([]byte(f.readXML("xl/_rels/workbook.xml.rels")), &content)
|
|
|
|
|
+ f.WorkBookRels = &content
|
|
|
}
|
|
}
|
|
|
- f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
|
|
|
|
|
|
|
+ return f.WorkBookRels
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// readXlsxWorkbookRels read and unmarshal workbook relationships of XLSX file.
|
|
|
|
|
-func (f *File) readXlsxWorkbookRels() xlsxWorkbookRels {
|
|
|
|
|
- var content xlsxWorkbookRels
|
|
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/_rels/workbook.xml.rels")), &content)
|
|
|
|
|
- return content
|
|
|
|
|
|
|
+// workbookRelsWriter provides function to save xl/_rels/workbook.xml.rels after
|
|
|
|
|
+// serialize structure.
|
|
|
|
|
+func (f *File) workbookRelsWriter() {
|
|
|
|
|
+ if f.WorkBookRels != nil {
|
|
|
|
|
+ output, _ := xml.Marshal(f.WorkBookRels)
|
|
|
|
|
+ f.saveFileList("xl/_rels/workbook.xml.rels", string(output))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// addXlsxWorkbookRels update workbook relationships property of XLSX.
|
|
// addXlsxWorkbookRels update workbook relationships property of XLSX.
|
|
|
func (f *File) addXlsxWorkbookRels(sheet int) int {
|
|
func (f *File) addXlsxWorkbookRels(sheet int) int {
|
|
|
- content := f.readXlsxWorkbookRels()
|
|
|
|
|
|
|
+ content := f.workbookRelsReader()
|
|
|
rID := 0
|
|
rID := 0
|
|
|
for _, v := range content.Relationships {
|
|
for _, v := range content.Relationships {
|
|
|
t, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId"))
|
|
t, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId"))
|
|
@@ -105,11 +157,6 @@ func (f *File) addXlsxWorkbookRels(sheet int) int {
|
|
|
Target: target.String(),
|
|
Target: target.String(),
|
|
|
Type: SourceRelationshipWorkSheet,
|
|
Type: SourceRelationshipWorkSheet,
|
|
|
})
|
|
})
|
|
|
- output, err := xml.Marshal(content)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- fmt.Println(err)
|
|
|
|
|
- }
|
|
|
|
|
- f.saveFileList("xl/_rels/workbook.xml.rels", string(output))
|
|
|
|
|
return rID
|
|
return rID
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -134,12 +181,11 @@ func replaceRelationshipsNameSpace(workbookMarshal string) string {
|
|
|
// SetActiveSheet provides function to set default active sheet of XLSX by given
|
|
// SetActiveSheet provides function to set default active sheet of XLSX by given
|
|
|
// index.
|
|
// index.
|
|
|
func (f *File) SetActiveSheet(index int) {
|
|
func (f *File) SetActiveSheet(index int) {
|
|
|
- var content xlsxWorkbook
|
|
|
|
|
if index < 1 {
|
|
if index < 1 {
|
|
|
index = 1
|
|
index = 1
|
|
|
}
|
|
}
|
|
|
index--
|
|
index--
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
if len(content.BookViews.WorkBookView) > 0 {
|
|
if len(content.BookViews.WorkBookView) > 0 {
|
|
|
content.BookViews.WorkBookView[0].ActiveTab = index
|
|
content.BookViews.WorkBookView[0].ActiveTab = index
|
|
|
} else {
|
|
} else {
|
|
@@ -148,11 +194,6 @@ func (f *File) SetActiveSheet(index int) {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
sheets := len(content.Sheets.Sheet)
|
|
sheets := len(content.Sheets.Sheet)
|
|
|
- output, err := xml.Marshal(content)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- fmt.Println(err)
|
|
|
|
|
- }
|
|
|
|
|
- f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
|
|
|
|
|
index++
|
|
index++
|
|
|
for i := 0; i < sheets; i++ {
|
|
for i := 0; i < sheets; i++ {
|
|
|
sheetIndex := i + 1
|
|
sheetIndex := i + 1
|
|
@@ -177,9 +218,8 @@ func (f *File) SetActiveSheet(index int) {
|
|
|
// GetActiveSheetIndex provides function to get active sheet of XLSX. If not
|
|
// GetActiveSheetIndex provides function to get active sheet of XLSX. If not
|
|
|
// found the active sheet will be return integer 0.
|
|
// found the active sheet will be return integer 0.
|
|
|
func (f *File) GetActiveSheetIndex() int {
|
|
func (f *File) GetActiveSheetIndex() int {
|
|
|
- content := xlsxWorkbook{}
|
|
|
|
|
buffer := bytes.Buffer{}
|
|
buffer := bytes.Buffer{}
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
for _, v := range content.Sheets.Sheet {
|
|
for _, v := range content.Sheets.Sheet {
|
|
|
xlsx := xlsxWorksheet{}
|
|
xlsx := xlsxWorksheet{}
|
|
|
buffer.WriteString("xl/worksheets/sheet")
|
|
buffer.WriteString("xl/worksheets/sheet")
|
|
@@ -203,27 +243,23 @@ func (f *File) GetActiveSheetIndex() int {
|
|
|
// name in the formula or reference associated with the cell. So there may be
|
|
// name in the formula or reference associated with the cell. So there may be
|
|
|
// problem formula error or reference missing.
|
|
// problem formula error or reference missing.
|
|
|
func (f *File) SetSheetName(oldName, newName string) {
|
|
func (f *File) SetSheetName(oldName, newName string) {
|
|
|
- var content = xlsxWorkbook{}
|
|
|
|
|
r := strings.NewReplacer(":", "", "\\", "", "/", "", "?", "", "*", "", "[", "", "]", "")
|
|
r := strings.NewReplacer(":", "", "\\", "", "/", "", "?", "", "*", "", "[", "", "]", "")
|
|
|
newName = r.Replace(newName)
|
|
newName = r.Replace(newName)
|
|
|
if len(newName) > 31 {
|
|
if len(newName) > 31 {
|
|
|
newName = newName[0:31]
|
|
newName = newName[0:31]
|
|
|
}
|
|
}
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
for k, v := range content.Sheets.Sheet {
|
|
for k, v := range content.Sheets.Sheet {
|
|
|
if v.Name == oldName {
|
|
if v.Name == oldName {
|
|
|
content.Sheets.Sheet[k].Name = newName
|
|
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
|
|
// GetSheetName provides function to get sheet name of XLSX by given sheet
|
|
|
// index. If given sheet index is invalid, will return an empty string.
|
|
// index. If given sheet index is invalid, will return an empty string.
|
|
|
func (f *File) GetSheetName(index int) string {
|
|
func (f *File) GetSheetName(index int) string {
|
|
|
- var content = xlsxWorkbook{}
|
|
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
for _, v := range content.Sheets.Sheet {
|
|
for _, v := range content.Sheets.Sheet {
|
|
|
if v.ID == "rId"+strconv.Itoa(index) {
|
|
if v.ID == "rId"+strconv.Itoa(index) {
|
|
|
return v.Name
|
|
return v.Name
|
|
@@ -244,9 +280,8 @@ func (f *File) GetSheetName(index int) string {
|
|
|
// }
|
|
// }
|
|
|
//
|
|
//
|
|
|
func (f *File) GetSheetMap() map[int]string {
|
|
func (f *File) GetSheetMap() map[int]string {
|
|
|
- content := xlsxWorkbook{}
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
sheetMap := map[int]string{}
|
|
sheetMap := map[int]string{}
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
for _, v := range content.Sheets.Sheet {
|
|
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
|
|
sheetMap[id] = v.Name
|
|
@@ -280,15 +315,12 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
|
|
|
// value of the deleted worksheet, it will cause a file error when you open it.
|
|
// value of the deleted worksheet, it will cause a file error when you open it.
|
|
|
// This function will be invalid when only the one worksheet is left.
|
|
// This function will be invalid when only the one worksheet is left.
|
|
|
func (f *File) DeleteSheet(name string) {
|
|
func (f *File) DeleteSheet(name string) {
|
|
|
- var content xlsxWorkbook
|
|
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/workbook.xml")), &content)
|
|
|
|
|
|
|
+ content := f.workbookReader()
|
|
|
for k, v := range content.Sheets.Sheet {
|
|
for k, v := range content.Sheets.Sheet {
|
|
|
if v.Name != name || len(content.Sheets.Sheet) < 2 {
|
|
if v.Name != name || len(content.Sheets.Sheet) < 2 {
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
content.Sheets.Sheet = append(content.Sheets.Sheet[:k], content.Sheets.Sheet[k+1:]...)
|
|
content.Sheets.Sheet = append(content.Sheets.Sheet[:k], content.Sheets.Sheet[k+1:]...)
|
|
|
- output, _ := xml.Marshal(content)
|
|
|
|
|
- f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpace(string(output)))
|
|
|
|
|
sheet := "xl/worksheets/sheet" + strings.TrimPrefix(v.ID, "rId") + ".xml"
|
|
sheet := "xl/worksheets/sheet" + strings.TrimPrefix(v.ID, "rId") + ".xml"
|
|
|
rels := "xl/worksheets/_rels/sheet" + strings.TrimPrefix(v.ID, "rId") + ".xml.rels"
|
|
rels := "xl/worksheets/_rels/sheet" + strings.TrimPrefix(v.ID, "rId") + ".xml.rels"
|
|
|
target := f.deteleSheetFromWorkbookRels(v.ID)
|
|
target := f.deteleSheetFromWorkbookRels(v.ID)
|
|
@@ -305,6 +337,7 @@ func (f *File) DeleteSheet(name string) {
|
|
|
if ok {
|
|
if ok {
|
|
|
delete(f.Sheet, sheet)
|
|
delete(f.Sheet, sheet)
|
|
|
}
|
|
}
|
|
|
|
|
+ f.SheetCount--
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -312,15 +345,12 @@ func (f *File) DeleteSheet(name string) {
|
|
|
// relationships by given relationships ID in the file
|
|
// relationships by given relationships ID in the file
|
|
|
// xl/_rels/workbook.xml.rels.
|
|
// xl/_rels/workbook.xml.rels.
|
|
|
func (f *File) deteleSheetFromWorkbookRels(rID string) string {
|
|
func (f *File) deteleSheetFromWorkbookRels(rID string) string {
|
|
|
- var content xlsxWorkbookRels
|
|
|
|
|
- xml.Unmarshal([]byte(f.readXML("xl/_rels/workbook.xml.rels")), &content)
|
|
|
|
|
|
|
+ content := f.workbookRelsReader()
|
|
|
for k, v := range content.Relationships {
|
|
for k, v := range content.Relationships {
|
|
|
if v.ID != rID {
|
|
if v.ID != rID {
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...)
|
|
content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...)
|
|
|
- output, _ := xml.Marshal(content)
|
|
|
|
|
- f.saveFileList("xl/_rels/workbook.xml.rels", string(output))
|
|
|
|
|
return v.Target
|
|
return v.Target
|
|
|
}
|
|
}
|
|
|
return ""
|
|
return ""
|
|
@@ -329,14 +359,11 @@ func (f *File) deteleSheetFromWorkbookRels(rID string) string {
|
|
|
// deteleSheetFromContentTypes provides function to remove worksheet
|
|
// deteleSheetFromContentTypes provides function to remove worksheet
|
|
|
// relationships by given target name in the file [Content_Types].xml.
|
|
// relationships by given target name in the file [Content_Types].xml.
|
|
|
func (f *File) deteleSheetFromContentTypes(target string) {
|
|
func (f *File) deteleSheetFromContentTypes(target string) {
|
|
|
- var content xlsxTypes
|
|
|
|
|
- xml.Unmarshal([]byte(f.readXML("[Content_Types].xml")), &content)
|
|
|
|
|
|
|
+ content := f.contentTypesReader()
|
|
|
for k, v := range content.Overrides {
|
|
for k, v := range content.Overrides {
|
|
|
if v.PartName != "/xl/"+target {
|
|
if v.PartName != "/xl/"+target {
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
|
|
content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
|
|
|
- output, _ := xml.Marshal(content)
|
|
|
|
|
- f.saveFileList("[Content_Types].xml", string(output))
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|