diff --git a/cell.go b/cell.go index f67319c5..c40012ac 100644 --- a/cell.go +++ b/cell.go @@ -9,10 +9,8 @@ import ( // GetCellValue provides function to get value from cell by given sheet index // and axis in XLSX file. func (f *File) GetCellValue(sheet, axis string) string { + xlsx := f.workSheetReader(sheet) axis = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -60,10 +58,8 @@ func (f *File) GetCellValue(sheet, axis string) string { // GetCellFormula provides function to get formula from cell by given sheet // index and axis in XLSX file. func (f *File) GetCellFormula(sheet, axis string) string { + xlsx := f.workSheetReader(sheet) axis = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -102,18 +98,8 @@ func (f *File) GetCellFormula(sheet, axis string) string { // SetCellFormula provides function to set cell formula by given string and // sheet index. func (f *File) SetCellFormula(sheet, axis, formula string) { + xlsx := f.workSheetReader(sheet) axis = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -129,8 +115,8 @@ func (f *File) SetCellFormula(sheet, axis, formula string) { rows := xAxis + 1 cell := yAxis + 1 - completeRow(&xlsx, rows, cell) - completeCol(&xlsx, rows, cell) + completeRow(xlsx, rows, cell) + completeCol(xlsx, rows, cell) if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil { xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula @@ -140,25 +126,13 @@ func (f *File) SetCellFormula(sheet, axis, formula string) { } xlsx.SheetData.Row[xAxis].C[yAxis].F = &f } - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // SetCellHyperLink provides function to set cell hyperlink by given sheet index // and link URL address. Only support external link currently. func (f *File) SetCellHyperLink(sheet, axis, link string) { + xlsx := f.workSheetReader(sheet) axis = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -178,8 +152,6 @@ func (f *File) SetCellHyperLink(sheet, axis, link string) { hyperlinks.Hyperlink = append(hyperlinks.Hyperlink, hyperlink) xlsx.Hyperlinks = &hyperlinks } - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // MergeCell provides function to merge cells by given coordinate area and sheet @@ -218,17 +190,7 @@ func (f *File) MergeCell(sheet, hcell, vcell string) { vyAxis, hyAxis = hyAxis, vyAxis } - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } + xlsx := f.workSheetReader(sheet) if xlsx.MergeCells != nil { mergeCell := xlsxMergeCell{} // Correct the coordinate area, such correct C1:B3 to B1:C3. @@ -250,8 +212,6 @@ func (f *File) MergeCell(sheet, hcell, vcell string) { mergeCells.Cells = append(mergeCells.Cells, &mergeCell) xlsx.MergeCells = &mergeCells } - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // checkCellInArea provides function to determine if a given coordinate is diff --git a/col.go b/col.go index 4d8f7a37..cb1c3b43 100644 --- a/col.go +++ b/col.go @@ -1,7 +1,6 @@ package excelize import ( - "encoding/xml" "math" "strconv" "strings" @@ -31,9 +30,7 @@ func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) { if min > max { min, max = max, min } - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader(sheet) col := xlsxCol{ Min: min, Max: max, @@ -47,8 +44,6 @@ func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) { cols.Col = append(cols.Col, col) xlsx.Cols = &cols } - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // positionObjectPixels calculate the vertices that define the position of a @@ -160,9 +155,7 @@ func (f *File) positionObjectPixels(sheet string, colStart, rowStart, x1, y1, wi // getColWidth provides function to get column width in pixels by given sheet // name and column index. func (f *File) getColWidth(sheet string, col int) int { - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader(sheet) if xlsx.Cols != nil { var width float64 for _, v := range xlsx.Cols.Col { @@ -181,9 +174,7 @@ func (f *File) getColWidth(sheet string, col int) int { // getRowHeight provides function to get row height in pixels by given sheet // name and row index. func (f *File) getRowHeight(sheet string, row int) int { - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader(sheet) for _, v := range xlsx.SheetData.Row { if v.R == row && v.Ht != "" { ht, _ := strconv.ParseFloat(v.Ht, 64) diff --git a/excelize.go b/excelize.go index 62456ee6..1fbfcc21 100644 --- a/excelize.go +++ b/excelize.go @@ -16,6 +16,7 @@ type File struct { checked map[string]bool XLSX map[string]string Path string + Sheet map[string]*xlsxWorksheet SheetCount int } @@ -52,6 +53,7 @@ func OpenReader(r io.Reader) (*File, error) { return nil, err } return &File{ + Sheet: make(map[string]*xlsxWorksheet), checked: make(map[string]bool), XLSX: file, Path: "", @@ -85,21 +87,32 @@ func (f *File) SetCellValue(sheet, axis string, value interface{}) { } } +// workSheetReader provides function to get the pointer to the structure after +// deserialization by given worksheet index. +func (f *File) workSheetReader(sheet string) *xlsxWorksheet { + name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" + worksheet := f.Sheet[name] + if worksheet == nil { + var xlsx xlsxWorksheet + xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + if f.checked == nil { + f.checked = make(map[string]bool) + } + ok := f.checked[name] + if !ok { + checkRow(&xlsx) + f.checked[name] = true + } + f.Sheet[name] = &xlsx + worksheet = f.Sheet[name] + } + return worksheet +} + // SetCellInt provides function to set int type value of a cell. func (f *File) SetCellInt(sheet, axis string, value int) { + xlsx := f.workSheetReader(sheet) axis = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } - if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -115,31 +128,18 @@ func (f *File) SetCellInt(sheet, axis string, value int) { rows := xAxis + 1 cell := yAxis + 1 - completeRow(&xlsx, rows, cell) - completeCol(&xlsx, rows, cell) + completeRow(xlsx, rows, cell) + completeCol(xlsx, rows, cell) xlsx.SheetData.Row[xAxis].C[yAxis].T = "" xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value) - - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // 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 = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -158,31 +158,18 @@ func (f *File) SetCellStr(sheet, axis, value string) { rows := xAxis + 1 cell := yAxis + 1 - completeRow(&xlsx, rows, cell) - completeCol(&xlsx, rows, cell) + completeRow(xlsx, rows, cell) + completeCol(xlsx, rows, cell) xlsx.SheetData.Row[xAxis].C[yAxis].T = "str" xlsx.SheetData.Row[xAxis].C[yAxis].V = value - - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // 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 = strings.ToUpper(axis) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } if xlsx.MergeCells != nil { for i := 0; i < len(xlsx.MergeCells.Cells); i++ { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { @@ -198,14 +185,11 @@ func (f *File) SetCellDefault(sheet, axis, value string) { rows := xAxis + 1 cell := yAxis + 1 - completeRow(&xlsx, rows, cell) - completeCol(&xlsx, rows, cell) + completeRow(xlsx, rows, cell) + completeCol(xlsx, rows, cell) xlsx.SheetData.Row[xAxis].C[yAxis].T = "" xlsx.SheetData.Row[xAxis].C[yAxis].V = value - - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // Completion column element tags of XML in a sheet. @@ -364,9 +348,7 @@ func checkRow(xlsx *xlsxWorksheet) { // func (f *File) UpdateLinkedValue() { for i := 1; i <= f.SheetCount; i++ { - var xlsx xlsxWorksheet - name := "xl/worksheets/sheet" + strconv.Itoa(i) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader("sheet" + strconv.Itoa(i)) for indexR, row := range xlsx.SheetData.Row { for indexC, col := range row.C { if col.F != nil && col.V != "" { @@ -375,7 +357,5 @@ func (f *File) UpdateLinkedValue() { } } } - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } } diff --git a/file.go b/file.go index 17a8bbaa..aea7f8ea 100644 --- a/file.go +++ b/file.go @@ -3,6 +3,7 @@ package excelize import ( "archive/zip" "bytes" + "encoding/xml" "fmt" "io" "os" @@ -25,7 +26,8 @@ func CreateFile() *File { file["xl/workbook.xml"] = templateWorkbook file["[Content_Types].xml"] = templateContentTypes return &File{ - XLSX: file, + XLSX: file, + Sheet: make(map[string]*xlsxWorksheet), } } @@ -52,6 +54,16 @@ func (f *File) WriteTo(name string) error { func (f *File) Write(w io.Writer) error { buf := new(bytes.Buffer) zw := zip.NewWriter(buf) + for path, sheet := range f.Sheet { + if sheet == nil { + continue + } + output, err := xml.Marshal(sheet) + if err != nil { + return err + } + f.saveFileList(path, replaceWorkSheetsRelationshipsNameSpace(string(output))) + } for path, content := range f.XLSX { fi, err := zw.Create(path) if err != nil { diff --git a/picture.go b/picture.go index fc4eda75..ab24f326 100644 --- a/picture.go +++ b/picture.go @@ -87,9 +87,7 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error { _, file := filepath.Split(picture) formatSet := parseFormatPictureSet(format) // Read sheet data. - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader(sheet) // Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder. drawingID := f.countDrawings() + 1 pictureID := f.countMedia() + 1 @@ -149,33 +147,19 @@ func (f *File) addSheetRelationships(sheet, relType, target, targetMode string) // addSheetDrawing provides function to add drawing element to // xl/worksheets/sheet%d.xml by given sheet name and relationship index. func (f *File) addSheetDrawing(sheet string, rID int) { - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader(sheet) xlsx.Drawing = &xlsxDrawing{ RID: "rId" + strconv.Itoa(rID), } - output, err := xml.Marshal(xlsx) - if err != nil { - fmt.Println(err) - } - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // addSheetPicture provides function to add picture element to // xl/worksheets/sheet%d.xml by given sheet name and relationship index. func (f *File) addSheetPicture(sheet string, rID int) { - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) + xlsx := f.workSheetReader(sheet) xlsx.Picture = &xlsxPicture{ RID: "rId" + strconv.Itoa(rID), } - output, err := xml.Marshal(xlsx) - if err != nil { - fmt.Println(err) - } - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // countDrawings provides function to get drawing files count storage in the diff --git a/rows.go b/rows.go index ad33a368..b76a8230 100644 --- a/rows.go +++ b/rows.go @@ -17,13 +17,15 @@ import ( // } // func (f *File) GetRows(sheet string) [][]string { + xlsx := f.workSheetReader(sheet) rows := [][]string{} name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - decoder := xml.NewDecoder(strings.NewReader(f.readXML(name))) - d, err := readXMLSST(f) - if err != nil { - return rows + if xlsx != nil { + output, _ := xml.Marshal(f.Sheet[name]) + f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } + decoder := xml.NewDecoder(strings.NewReader(f.readXML(name))) + d, _ := readXMLSST(f) var inElement string var r xlsxRow var row []string @@ -105,20 +107,12 @@ func (f *File) getTotalRowsCols(sheet string) (int, int) { // } // func (f *File) SetRowHeight(sheet string, rowIndex int, height float64) { - xlsx := xlsxWorksheet{} - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - + xlsx := f.workSheetReader(sheet) rows := rowIndex + 1 cells := 0 - - completeRow(&xlsx, rows, cells) - + completeRow(xlsx, rows, cells) xlsx.SheetData.Row[rowIndex].Ht = strconv.FormatFloat(height, 'f', -1, 64) xlsx.SheetData.Row[rowIndex].CustomHeight = true - - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) } // readXMLSST read xmlSST simple function. diff --git a/sheet.go b/sheet.go index 156d7384..e317b93e 100644 --- a/sheet.go +++ b/sheet.go @@ -22,9 +22,9 @@ func (f *File) NewSheet(index int, name string) { // Create new sheet /xl/worksheets/sheet%d.xml f.setSheet(index) // Update xl/_rels/workbook.xml.rels - rid := f.addXlsxWorkbookRels(index) + rID := f.addXlsxWorkbookRels(index) // Update xl/workbook.xml - f.setWorkbook(name, rid) + f.setWorkbook(name, rID) } // Read and update property of contents type of XLSX. @@ -49,12 +49,8 @@ func (f *File) setSheet(index int) { xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{ WorkbookViewID: 0, }) - output, err := xml.Marshal(xlsx) - if err != nil { - fmt.Println(err) - } path := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml" - f.saveFileList(path, replaceWorkSheetsRelationshipsNameSpace(string(output))) + f.Sheet[path] = &xlsx } // setWorkbook update workbook property of XLSX. Maximum 31 characters are @@ -151,14 +147,9 @@ func (f *File) SetActiveSheet(index int) { } 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(strconv.Itoa(sheetIndex)) - buffer.WriteString(".xml") - xml.Unmarshal([]byte(f.readXML(buffer.String())), &xlsx) + xlsx := f.workSheetReader("sheet" + strconv.Itoa(sheetIndex)) if index == sheetIndex { if len(xlsx.SheetViews.SheetView) > 0 { xlsx.SheetViews.SheetView[0].TabSelected = true @@ -172,12 +163,6 @@ func (f *File) SetActiveSheet(index int) { xlsx.SheetViews.SheetView[0].TabSelected = false } } - sheet, err := xml.Marshal(xlsx) - if err != nil { - fmt.Println(err) - } - f.saveFileList(buffer.String(), replaceWorkSheetsRelationshipsNameSpace(string(sheet))) - buffer.Reset() } return } @@ -275,10 +260,6 @@ func (f *File) SetSheetBackground(sheet, picture string) error { if !ok { return errors.New("Unsupported image extension") } - // Read sheet data. - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) pictureID := f.countMedia() + 1 rID := f.addSheetRelationships(sheet, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, "") f.addSheetPicture(sheet, rID) diff --git a/styles.go b/styles.go index cac92a04..f572f84d 100644 --- a/styles.go +++ b/styles.go @@ -196,20 +196,10 @@ func (f *File) setCellStyle(sheet, hcell, vcell string, styleID int) { hcell = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1) vcell = toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1) - var xlsx xlsxWorksheet - name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" - xml.Unmarshal([]byte(f.readXML(name)), &xlsx) - if f.checked == nil { - f.checked = make(map[string]bool) - } - ok := f.checked[name] - if !ok { - checkRow(&xlsx) - f.checked[name] = true - } + xlsx := f.workSheetReader(sheet) - completeRow(&xlsx, vxAxis+1, vyAxis+1) - completeCol(&xlsx, vxAxis+1, vyAxis+1) + completeRow(xlsx, vxAxis+1, vyAxis+1) + completeCol(xlsx, vxAxis+1, vyAxis+1) for r, row := range xlsx.SheetData.Row { for k, c := range row.C { @@ -218,6 +208,4 @@ func (f *File) setCellStyle(sheet, hcell, vcell string, styleID int) { } } } - output, _ := xml.Marshal(xlsx) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) }