@ -17,7 +17,6 @@ import (
"encoding/xml"
"encoding/xml"
"fmt"
"fmt"
"io"
"io"
"log"
"os"
"os"
"path"
"path"
"path/filepath"
"path/filepath"
@ -47,7 +46,7 @@ func (f *File) NewSheet(sheet string) int {
}
}
f . DeleteSheet ( sheet )
f . DeleteSheet ( sheet )
f . SheetCount ++
f . SheetCount ++
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
sheetID := 0
sheetID := 0
for _ , v := range wb . Sheets . Sheet {
for _ , v := range wb . Sheets . Sheet {
if v . SheetID > sheetID {
if v . SheetID > sheetID {
@ -56,7 +55,7 @@ func (f *File) NewSheet(sheet string) int {
}
}
sheetID ++
sheetID ++
// Update [Content_Types].xml
// Update [Content_Types].xml
f. setContentTypes ( "/xl/worksheets/sheet" + strconv . Itoa ( sheetID ) + ".xml" , ContentTypeSpreadSheetMLWorksheet )
_ = f. setContentTypes ( "/xl/worksheets/sheet" + strconv . Itoa ( sheetID ) + ".xml" , ContentTypeSpreadSheetMLWorksheet )
// Create new sheet /xl/worksheets/sheet%d.xml
// Create new sheet /xl/worksheets/sheet%d.xml
f . setSheet ( sheetID , sheet )
f . setSheet ( sheetID , sheet )
// Update workbook.xml.rels
// Update workbook.xml.rels
@ -68,19 +67,17 @@ func (f *File) NewSheet(sheet string) int {
// contentTypesReader provides a function to get the pointer to the
// contentTypesReader provides a function to get the pointer to the
// [Content_Types].xml structure after deserialization.
// [Content_Types].xml structure after deserialization.
func ( f * File ) contentTypesReader ( ) * xlsxTypes {
func ( f * File ) contentTypesReader ( ) ( * xlsxTypes , error ) {
var err error
if f . ContentTypes == nil {
if f . ContentTypes == nil {
f . ContentTypes = new ( xlsxTypes )
f . ContentTypes = new ( xlsxTypes )
f . ContentTypes . Lock ( )
f . ContentTypes . Lock ( )
defer f . ContentTypes . Unlock ( )
defer f . ContentTypes . Unlock ( )
if err = f . xmlNewDecoder ( bytes . NewReader ( namespaceStrictToTransitional ( f . readXML ( defaultXMLPathContentTypes ) ) ) ) .
if err : = f . xmlNewDecoder ( bytes . NewReader ( namespaceStrictToTransitional ( f . readXML ( defaultXMLPathContentTypes ) ) ) ) .
Decode ( f . ContentTypes ) ; err != nil && err != io . EOF {
Decode ( f . ContentTypes ) ; err != nil && err != io . EOF {
log . Printf ( "xml decode error: %s" , err )
return f . ContentTypes , err
}
}
}
}
return f . ContentTypes
return f . ContentTypes , nil
}
}
// contentTypesWriter provides a function to save [Content_Types].xml after
// contentTypesWriter provides a function to save [Content_Types].xml after
@ -215,14 +212,18 @@ func trimCell(column []xlsxC) []xlsxC {
// setContentTypes provides a function to read and update property of contents
// setContentTypes provides a function to read and update property of contents
// type of the spreadsheet.
// type of the spreadsheet.
func ( f * File ) setContentTypes ( partName , contentType string ) {
func ( f * File ) setContentTypes ( partName , contentType string ) error {
content := f . contentTypesReader ( )
content , err := f . contentTypesReader ( )
if err != nil {
return err
}
content . Lock ( )
content . Lock ( )
defer content . Unlock ( )
defer content . Unlock ( )
content . Overrides = append ( content . Overrides , xlsxOverride {
content . Overrides = append ( content . Overrides , xlsxOverride {
PartName : partName ,
PartName : partName ,
ContentType : contentType ,
ContentType : contentType ,
} )
} )
return err
}
}
// setSheet provides a function to update sheet property by given index.
// setSheet provides a function to update sheet property by given index.
@ -271,7 +272,7 @@ func (f *File) SetActiveSheet(index int) {
if index < 0 {
if index < 0 {
index = 0
index = 0
}
}
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
for activeTab := range wb . Sheets . Sheet {
for activeTab := range wb . Sheets . Sheet {
if activeTab == index {
if activeTab == index {
if wb . BookViews == nil {
if wb . BookViews == nil {
@ -316,7 +317,7 @@ func (f *File) SetActiveSheet(index int) {
// spreadsheet. If not found the active sheet will be return integer 0.
// spreadsheet. If not found the active sheet will be return integer 0.
func ( f * File ) GetActiveSheetIndex ( ) ( index int ) {
func ( f * File ) GetActiveSheetIndex ( ) ( index int ) {
sheetID := f . getActiveSheetID ( )
sheetID := f . getActiveSheetID ( )
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
if wb != nil {
if wb != nil {
for idx , sheet := range wb . Sheets . Sheet {
for idx , sheet := range wb . Sheets . Sheet {
if sheet . SheetID == sheetID {
if sheet . SheetID == sheetID {
@ -331,7 +332,7 @@ func (f *File) GetActiveSheetIndex() (index int) {
// getActiveSheetID provides a function to get active sheet ID of the
// getActiveSheetID provides a function to get active sheet ID of the
// spreadsheet. If not found the active sheet will be return integer 0.
// spreadsheet. If not found the active sheet will be return integer 0.
func ( f * File ) getActiveSheetID ( ) int {
func ( f * File ) getActiveSheetID ( ) int {
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
if wb != nil {
if wb != nil {
if wb . BookViews != nil && len ( wb . BookViews . WorkBookView ) > 0 {
if wb . BookViews != nil && len ( wb . BookViews . WorkBookView ) > 0 {
activeTab := wb . BookViews . WorkBookView [ 0 ] . ActiveTab
activeTab := wb . BookViews . WorkBookView [ 0 ] . ActiveTab
@ -357,10 +358,10 @@ func (f *File) SetSheetName(source, target string) {
if strings . EqualFold ( target , source ) {
if strings . EqualFold ( target , source ) {
return
return
}
}
content := f . workbookReader ( )
wb, _ := f . workbookReader ( )
for k , v := range content . Sheets . Sheet {
for k , v := range wb . Sheets . Sheet {
if v . Name == source {
if v . Name == source {
content . Sheets . Sheet [ k ] . Name = target
wb . Sheets . Sheet [ k ] . Name = target
f . sheetMap [ target ] = f . sheetMap [ source ]
f . sheetMap [ target ] = f . sheetMap [ source ]
delete ( f . sheetMap , source )
delete ( f . sheetMap , source )
}
}
@ -422,7 +423,7 @@ func (f *File) GetSheetIndex(sheet string) int {
// fmt.Println(index, name)
// fmt.Println(index, name)
// }
// }
func ( f * File ) GetSheetMap ( ) map [ int ] string {
func ( f * File ) GetSheetMap ( ) map [ int ] string {
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
sheetMap := map [ int ] string { }
sheetMap := map [ int ] string { }
if wb != nil {
if wb != nil {
for _ , sheet := range wb . Sheets . Sheet {
for _ , sheet := range wb . Sheets . Sheet {
@ -435,7 +436,7 @@ func (f *File) GetSheetMap() map[int]string {
// GetSheetList provides a function to get worksheets, chart sheets, and
// GetSheetList provides a function to get worksheets, chart sheets, and
// dialog sheets name list of the workbook.
// dialog sheets name list of the workbook.
func ( f * File ) GetSheetList ( ) ( list [ ] string ) {
func ( f * File ) GetSheetList ( ) ( list [ ] string ) {
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
if wb != nil {
if wb != nil {
for _ , sheet := range wb . Sheets . Sheet {
for _ , sheet := range wb . Sheets . Sheet {
list = append ( list , sheet . Name )
list = append ( list , sheet . Name )
@ -448,8 +449,10 @@ func (f *File) GetSheetList() (list []string) {
// of the spreadsheet.
// of the spreadsheet.
func ( f * File ) getSheetMap ( ) map [ string ] string {
func ( f * File ) getSheetMap ( ) map [ string ] string {
maps := map [ string ] string { }
maps := map [ string ] string { }
for _ , v := range f . workbookReader ( ) . Sheets . Sheet {
wb , _ := f . workbookReader ( )
for _ , rel := range f . relsReader ( f . getWorkbookRelsPath ( ) ) . Relationships {
rels , _ := f . relsReader ( f . getWorkbookRelsPath ( ) )
for _ , v := range wb . Sheets . Sheet {
for _ , rel := range rels . Relationships {
if rel . ID == v . ID {
if rel . ID == v . ID {
sheetXMLPath := f . getWorksheetPath ( rel . Target )
sheetXMLPath := f . getWorksheetPath ( rel . Target )
if _ , ok := f . Pkg . Load ( sheetXMLPath ) ; ok {
if _ , ok := f . Pkg . Load ( sheetXMLPath ) ; ok {
@ -498,10 +501,11 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
sheetXMLPath , _ := f . getSheetXMLPath ( sheet )
sheetXMLPath , _ := f . getSheetXMLPath ( sheet )
sheetRels := "xl/worksheets/_rels/" + strings . TrimPrefix ( sheetXMLPath , "xl/worksheets/" ) + ".rels"
sheetRels := "xl/worksheets/_rels/" + strings . TrimPrefix ( sheetXMLPath , "xl/worksheets/" ) + ".rels"
rID := f . addRels ( sheetRels , SourceRelationshipImage , strings . Replace ( name , "xl" , ".." , 1 ) , "" )
rID := f . addRels ( sheetRels , SourceRelationshipImage , strings . Replace ( name , "xl" , ".." , 1 ) , "" )
f . addSheetPicture ( sheet , rID )
if err = f . addSheetPicture ( sheet , rID ) ; err != nil {
return err
}
f . addSheetNameSpace ( sheet , SourceRelationship )
f . addSheetNameSpace ( sheet , SourceRelationship )
f . setContentTypePartImageExtensions ( )
return f . setContentTypePartImageExtensions ( )
return err
}
}
// DeleteSheet provides a function to delete worksheet in a workbook by given
// DeleteSheet provides a function to delete worksheet in a workbook by given
@ -514,8 +518,8 @@ func (f *File) DeleteSheet(sheet string) {
return
return
}
}
sheetName := trimSheetName ( sheet )
sheetName := trimSheetName ( sheet )
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
wbRels := f . relsReader ( f . getWorkbookRelsPath ( ) )
wbRels , _ := f . relsReader ( f . getWorkbookRelsPath ( ) )
activeSheetName := f . GetSheetName ( f . GetActiveSheetIndex ( ) )
activeSheetName := f . GetSheetName ( f . GetActiveSheetIndex ( ) )
deleteLocalSheetID := f . GetSheetIndex ( sheet )
deleteLocalSheetID := f . GetSheetIndex ( sheet )
deleteAndAdjustDefinedNames ( wb , deleteLocalSheetID )
deleteAndAdjustDefinedNames ( wb , deleteLocalSheetID )
@ -537,8 +541,8 @@ func (f *File) DeleteSheet(sheet string) {
}
}
}
}
target := f . deleteSheetFromWorkbookRels ( v . ID )
target := f . deleteSheetFromWorkbookRels ( v . ID )
f. deleteSheetFromContentTypes ( target )
_ = f. deleteSheetFromContentTypes ( target )
f. deleteCalcChain ( f . getSheetID ( sheet ) , "" )
_ = f. deleteCalcChain ( f . getSheetID ( sheet ) , "" )
delete ( f . sheetMap , v . Name )
delete ( f . sheetMap , v . Name )
f . Pkg . Delete ( sheetXML )
f . Pkg . Delete ( sheetXML )
f . Pkg . Delete ( rels )
f . Pkg . Delete ( rels )
@ -573,12 +577,12 @@ func deleteAndAdjustDefinedNames(wb *xlsxWorkbook, deleteLocalSheetID int) {
// deleteSheetFromWorkbookRels provides a function to remove worksheet
// deleteSheetFromWorkbookRels provides a function to remove worksheet
// relationships by given relationships ID in the file workbook.xml.rels.
// relationships by given relationships ID in the file workbook.xml.rels.
func ( f * File ) deleteSheetFromWorkbookRels ( rID string ) string {
func ( f * File ) deleteSheetFromWorkbookRels ( rID string ) string {
content := f . relsReader ( f . getWorkbookRelsPath ( ) )
rels, _ := f . relsReader ( f . getWorkbookRelsPath ( ) )
content . Lock ( )
rels . Lock ( )
defer content . Unlock ( )
defer rels . Unlock ( )
for k , v := range content . Relationships {
for k , v := range rels . Relationships {
if v . ID == rID {
if v . ID == rID {
content. Relationships = append ( content . Relationships [ : k ] , content . Relationships [ k + 1 : ] ... )
rels. Relationships = append ( rels . Relationships [ : k ] , rels . Relationships [ k + 1 : ] ... )
return v . Target
return v . Target
}
}
}
}
@ -587,11 +591,14 @@ func (f *File) deleteSheetFromWorkbookRels(rID string) string {
// deleteSheetFromContentTypes provides a function to remove worksheet
// deleteSheetFromContentTypes provides a 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 ) deleteSheetFromContentTypes ( target string ) {
func ( f * File ) deleteSheetFromContentTypes ( target string ) error {
if ! strings . HasPrefix ( target , "/" ) {
if ! strings . HasPrefix ( target , "/" ) {
target = "/xl/" + target
target = "/xl/" + target
}
}
content := f . contentTypesReader ( )
content , err := f . contentTypesReader ( )
if err != nil {
return err
}
content . Lock ( )
content . Lock ( )
defer content . Unlock ( )
defer content . Unlock ( )
for k , v := range content . Overrides {
for k , v := range content . Overrides {
@ -599,6 +606,7 @@ func (f *File) deleteSheetFromContentTypes(target string) {
content . Overrides = append ( content . Overrides [ : k ] , content . Overrides [ k + 1 : ] ... )
content . Overrides = append ( content . Overrides [ : k ] , content . Overrides [ k + 1 : ] ... )
}
}
}
}
return err
}
}
// CopySheet provides a function to duplicate a worksheet by gave source and
// CopySheet provides a function to duplicate a worksheet by gave source and
@ -659,22 +667,25 @@ func (f *File) copySheet(from, to int) error {
// 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 ) error {
sheet = trimSheetName ( sheet )
sheet = trimSheetName ( sheet )
content := f . workbookReader ( )
wb , err := f . workbookReader ( )
if err != nil {
return err
}
if visible {
if visible {
for k , v := range content . Sheets . Sheet {
for k , v := range wb . Sheets . Sheet {
if strings . EqualFold ( v . Name , sheet ) {
if strings . EqualFold ( v . Name , sheet ) {
content . Sheets . Sheet [ k ] . State = ""
wb . Sheets . Sheet [ k ] . State = ""
}
}
}
}
return nil
return err
}
}
count := 0
count := 0
for _ , v := range content . Sheets . Sheet {
for _ , v := range wb . Sheets . Sheet {
if v . State != "hidden" {
if v . State != "hidden" {
count ++
count ++
}
}
}
}
for k , v := range content . Sheets . Sheet {
for k , v := range wb . Sheets . Sheet {
ws , err := f . workSheetReader ( v . Name )
ws , err := f . workSheetReader ( v . Name )
if err != nil {
if err != nil {
return err
return err
@ -684,10 +695,10 @@ 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 {
content . Sheets . Sheet [ k ] . State = "hidden"
wb . Sheets . Sheet [ k ] . State = "hidden"
}
}
}
}
return nil
return err
}
}
// parsePanesOptions provides a function to parse the panes settings.
// parsePanesOptions provides a function to parse the panes settings.
@ -830,10 +841,11 @@ func (f *File) SetPanes(sheet, panes string) error {
//
//
// f.GetSheetVisible("Sheet1")
// f.GetSheetVisible("Sheet1")
func ( f * File ) GetSheetVisible ( sheet string ) bool {
func ( f * File ) GetSheetVisible ( sheet string ) bool {
content , name , visible := f . workbookReader ( ) , trimSheetName ( sheet ) , false
name , visible := trimSheetName ( sheet ) , false
for k , v := range content . Sheets . Sheet {
wb , _ := f . workbookReader ( )
for k , v := range wb . Sheets . Sheet {
if strings . EqualFold ( v . Name , name ) {
if strings . EqualFold ( v . Name , name ) {
if content . Sheets . Sheet [ k ] . State == "" || content . Sheets . Sheet [ k ] . State == "visible" {
if wb . Sheets . Sheet [ k ] . State == "" || wb . Sheets . Sheet [ k ] . State == "visible" {
visible = true
visible = true
}
}
}
}
@ -1460,7 +1472,10 @@ 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 {
wb := f . workbookReader ( )
wb , err := f . workbookReader ( )
if err != nil {
return err
}
d := xlsxDefinedName {
d := xlsxDefinedName {
Name : definedName . Name ,
Name : definedName . Name ,
Comment : definedName . Comment ,
Comment : definedName . Comment ,
@ -1499,7 +1514,10 @@ func (f *File) SetDefinedName(definedName *DefinedName) error {
// Scope: "Sheet2",
// Scope: "Sheet2",
// })
// })
func ( f * File ) DeleteDefinedName ( definedName * DefinedName ) error {
func ( f * File ) DeleteDefinedName ( definedName * DefinedName ) error {
wb := f . workbookReader ( )
wb , err := f . workbookReader ( )
if err != nil {
return err
}
if wb . DefinedNames != nil {
if wb . DefinedNames != nil {
for idx , dn := range wb . DefinedNames . DefinedName {
for idx , dn := range wb . DefinedNames . DefinedName {
scope := "Workbook"
scope := "Workbook"
@ -1512,7 +1530,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
}
}
if scope == deleteScope && dn . Name == definedName . Name {
if scope == deleteScope && dn . Name == definedName . Name {
wb . DefinedNames . DefinedName = append ( wb . DefinedNames . DefinedName [ : idx ] , wb . DefinedNames . DefinedName [ idx + 1 : ] ... )
wb . DefinedNames . DefinedName = append ( wb . DefinedNames . DefinedName [ : idx ] , wb . DefinedNames . DefinedName [ idx + 1 : ] ... )
return nil
return err
}
}
}
}
}
}
@ -1523,7 +1541,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
// or worksheet.
// or worksheet.
func ( f * File ) GetDefinedName ( ) [ ] DefinedName {
func ( f * File ) GetDefinedName ( ) [ ] DefinedName {
var definedNames [ ] DefinedName
var definedNames [ ] DefinedName
wb := f . workbookReader ( )
wb , _ := f . workbookReader ( )
if wb . DefinedNames != nil {
if wb . DefinedNames != nil {
for _ , dn := range wb . DefinedNames . DefinedName {
for _ , dn := range wb . DefinedNames . DefinedName {
definedName := DefinedName {
definedName := DefinedName {
@ -1715,23 +1733,22 @@ func (f *File) RemovePageBreak(sheet, cell string) error {
// relsReader provides a function to get the pointer to the structure
// relsReader provides a function to get the pointer to the structure
// after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
// after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
func ( f * File ) relsReader ( path string ) * xlsxRelationships {
func ( f * File ) relsReader ( path string ) ( * xlsxRelationships , error ) {
var err error
rels , _ := f . Relationships . Load ( path )
rels , _ := f . Relationships . Load ( path )
if rels == nil {
if rels == nil {
if _ , ok := f . Pkg . Load ( path ) ; ok {
if _ , ok := f . Pkg . Load ( path ) ; ok {
c := xlsxRelationships { }
c := xlsxRelationships { }
if err = f . xmlNewDecoder ( bytes . NewReader ( namespaceStrictToTransitional ( f . readXML ( path ) ) ) ) .
if err : = f . xmlNewDecoder ( bytes . NewReader ( namespaceStrictToTransitional ( f . readXML ( path ) ) ) ) .
Decode ( & c ) ; err != nil && err != io . EOF {
Decode ( & c ) ; err != nil && err != io . EOF {
log . Printf ( "xml decode error: %s" , err )
return nil , err
}
}
f . Relationships . Store ( path , & c )
f . Relationships . Store ( path , & c )
}
}
}
}
if rels , _ = f . Relationships . Load ( path ) ; rels != nil {
if rels , _ = f . Relationships . Load ( path ) ; rels != nil {
return rels . ( * xlsxRelationships )
return rels . ( * xlsxRelationships ) , nil
}
}
return nil
return nil , nil
}
}
// fillSheetData ensures there are enough rows, and columns in the chosen
// fillSheetData ensures there are enough rows, and columns in the chosen