From b84bfa7eab84a8e065bd5acedeae3d0ba8dc5f8b Mon Sep 17 00:00:00 2001 From: Ri Xu Date: Fri, 23 Dec 2016 17:47:25 +0800 Subject: [PATCH] - Update maximum 31 characters allowed in sheet title; - Fix issue XML tag `headerFooter` and `sheetPr` element self-close errors cause file corruption; - Fix issue `Section` and `Pane` element order make file corruption in some case; - Change sheet `rId` calculation method in `/xl/workbook.xml`, fix makes file corruption in some case; - Compatibility improved: add `xlsxTabColor` struct and some XML element for worksheet --- excelize.go | 4 ++-- excelize_test.go | 2 +- sheet.go | 26 ++++++++++++---------- xmlWorkbook.go | 3 ++- xmlWorksheet.go | 57 +++++++++++++++++++++++++++++++++--------------- 5 files changed, 59 insertions(+), 33 deletions(-) diff --git a/excelize.go b/excelize.go index 162cb2c0..b4f7acce 100644 --- a/excelize.go +++ b/excelize.go @@ -179,14 +179,14 @@ func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string { oldXmlns := `` newXmlns := `` workbookMarshal = strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1) - workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) - workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) return workbookMarshal } diff --git a/excelize_test.go b/excelize_test.go index 1f215df7..e9ad6695 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -24,7 +24,7 @@ func TestExcelize(t *testing.T) { f1.UpdateLinkedValue() f1.SetCellInt("SHEET2", "A1", 100) f1.SetCellStr("SHEET2", "C11", "Knowns") - f1.NewSheet(3, "TestSheet") + f1.NewSheet(3, "Maximum 31 characters allowed in sheet title.") f1.SetCellInt("Sheet3", "A23", 10) f1.SetCellStr("SHEET3", "b230", "10") f1.SetCellStr("SHEET10", "b230", "10") diff --git a/sheet.go b/sheet.go index 35fd5ed0..351a5372 100644 --- a/sheet.go +++ b/sheet.go @@ -19,9 +19,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 - f.addXlsxWorkbookRels(index) + rid := f.addXlsxWorkbookRels(index) // Update xl/workbook.xml - f.setWorkbook(index, name) + f.setWorkbook(name, rid) } // Read and update property of contents type of XLSX. @@ -54,17 +54,17 @@ func (f *File) setSheet(index int) { f.saveFileList(path, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output)))) } -// Update workbook property of XLSX. -func (f *File) setWorkbook(index int, name string) { +// Update workbook property of XLSX. Maximum 31 characters allowed in sheet title. +func (f *File) setWorkbook(name string, rid int) { var content xlsxWorkbook + if len(name) > 31 { + name = name[0:31] + } xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content) - - rels := f.readXlsxWorkbookRels() - rID := len(rels.Relationships) content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{ Name: name, - SheetID: strconv.Itoa(index), - ID: "rId" + strconv.Itoa(rID), + SheetID: strconv.Itoa(rid), + ID: `rId` + strconv.Itoa(rid), }) output, err := xml.Marshal(content) if err != nil { @@ -81,7 +81,7 @@ func (f *File) readXlsxWorkbookRels() xlsxWorkbookRels { } // Update workbook relationships property of XLSX. -func (f *File) addXlsxWorkbookRels(sheet int) { +func (f *File) addXlsxWorkbookRels(sheet int) int { content := f.readXlsxWorkbookRels() rID := len(content.Relationships) + 1 ID := bytes.Buffer{} @@ -101,6 +101,7 @@ func (f *File) addXlsxWorkbookRels(sheet int) { fmt.Println(err) } f.saveFileList(`xl/_rels/workbook.xml.rels`, string(output)) + return rID } // Update docProps/app.xml file of XML. @@ -128,6 +129,7 @@ func replaceRelationshipsID(workbookMarshal string) string { rids = strings.Replace(rids, ``, ``, -1) rids = strings.Replace(rids, ``, ``, -1) rids = strings.Replace(rids, ``, ``, -1) + rids = strings.Replace(rids, ``, ``, -1) return strings.Replace(rids, ``, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) - workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) return workbookMarshal } diff --git a/xmlWorkbook.go b/xmlWorkbook.go index 1f355c72..b5c68797 100644 --- a/xmlWorkbook.go +++ b/xmlWorkbook.go @@ -74,7 +74,8 @@ type xlsxWorkbookPr struct { DefaultThemeVersion string `xml:"defaultThemeVersion,attr,omitempty"` BackupFile bool `xml:"backupFile,attr,omitempty"` ShowObjects string `xml:"showObjects,attr,omitempty"` - Date1904 bool `xml:"date1904,attr"` + Date1904 bool `xml:"date1904,attr,omitempty"` + CodeName string `xml:"codeName,attr,omitempty"` } // xlsxBookViews directly maps the bookViews element from the diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 2847c39b..be7dcc2c 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -132,25 +132,34 @@ type xlsxSheetViews struct { // http://schemas.openxmlformats.org/spreadsheetml/2006/main - // currently I have not checked it for completeness - it does as much // as I need. +// +// A single sheet view definition. When more than one sheet view is +// defined in the file, it means that when opening the workbook, each +// sheet view corresponds to a separate window within the spreadsheet +// application, where each window is showing the particular sheet +// containing the same workbookViewId value, the last sheetView +// definition is loaded, and the others are discarded. When multiple +// windows are viewing the same sheet, multiple sheetView elements +// (with corresponding workbookView entries) are saved. type xlsxSheetView struct { - // WindowProtection bool `xml:"windowProtection,attr"` - // ShowFormulas bool `xml:"showFormulas,attr"` - ShowGridLines string `xml:"showGridLines,attr,omitempty"` - // ShowRowColHeaders bool `xml:"showRowColHeaders,attr"` - // ShowZeros bool `xml:"showZeros,attr"` - // RightToLeft bool `xml:"rightToLeft,attr"` - TabSelected bool `xml:"tabSelected,attr"` - // ShowOutlineSymbols bool `xml:"showOutlineSymbols,attr"` - // DefaultGridColor bool `xml:"defaultGridColor,attr"` - // View string `xml:"view,attr"` - TopLeftCell string `xml:"topLeftCell,attr,omitempty"` - // ColorId int `xml:"colorId,attr"` + WindowProtection bool `xml:"windowProtection,attr,omitempty"` + ShowFormulas bool `xml:"showFormulas,attr,omitempty"` + ShowGridLines string `xml:"showGridLines,attr,omitempty"` + ShowRowColHeaders bool `xml:"showRowColHeaders,attr,omitempty"` + ShowZeros bool `xml:"showZeros,attr,omitempty"` + RightToLeft bool `xml:"rightToLeft,attr,omitempty"` + TabSelected bool `xml:"tabSelected,attr,omitempty"` + ShowOutlineSymbols bool `xml:"showOutlineSymbols,attr,omitempty"` + DefaultGridColor bool `xml:"defaultGridColor,attr"` + View string `xml:"view,attr,omitempty"` + TopLeftCell string `xml:"topLeftCell,attr,omitempty"` + ColorId int `xml:"colorId,attr,omitempty"` ZoomScale float64 `xml:"zoomScale,attr,omitempty"` ZoomScaleNormal float64 `xml:"zoomScaleNormal,attr,omitempty"` ZoomScalePageLayoutView float64 `xml:"zoomScalePageLayoutView,attr,omitempty"` WorkbookViewID int `xml:"workbookViewId,attr"` - Selection []xlsxSelection `xml:"selection"` Pane *xlsxPane `xml:"pane,omitempty"` + Selection []xlsxSelection `xml:"selection"` } // xlsxSelection directly maps the selection element in the namespace @@ -161,7 +170,7 @@ type xlsxSelection struct { Pane string `xml:"pane,attr,omitempty"` ActiveCell string `xml:"activeCell,attr,omitempty"` ActiveCellID int `xml:"activeCellId,attr"` - SQRef string `xml:"sqref,attr"` + SQRef string `xml:"sqref,attr,omitempty"` } // xlsxSelection directly maps the selection element in the namespace @@ -181,8 +190,12 @@ type xlsxPane struct { // currently I have not checked it for completeness - it does as much // as I need. type xlsxSheetPr struct { - FilterMode bool `xml:"filterMode,attr"` - PageSetUpPr []xlsxPageSetUpPr `xml:"pageSetUpPr"` + XMLName xml.Name `xml:"sheetPr"` + FilterMode bool `xml:"filterMode,attr,omitempty"` + CodeName string `xml:"codeName,attr,omitempty"` + EnableFormatConditionsCalculation int `xml:"enableFormatConditionsCalculation,attr,omitempty"` + TabColor xlsxTabColor `xml:"tabColor,omitempty"` + PageSetUpPr xlsxPageSetUpPr `xml:"pageSetUpPr"` } // xlsxPageSetUpPr directly maps the pageSetupPr element in the namespace @@ -190,7 +203,15 @@ type xlsxSheetPr struct { // currently I have not checked it for completeness - it does as much // as I need. type xlsxPageSetUpPr struct { - FitToPage bool `xml:"fitToPage,attr"` + FitToPage bool `xml:"fitToPage,attr"` // Flag indicating whether the Fit to Page print option is enabled. +} + +// xlsxTabColor directly maps the tabColor element in the namespace +// currently I have not checked it for completeness - it does as much +// as I need. +type xlsxTabColor struct { + Theme int `xml:"theme,attr,omitempty"` // (Theme Color) A zero-based index into the collection (ยง20.1.6.2), referencing a particular or value expressed in the Theme part. + Tint uint8 `xml:"tint,attr,omitempty"` // Specifies the tint value applied to the color. } // xlsxCols directly maps the cols element in the namespace @@ -278,7 +299,7 @@ type xlsxF struct { Content string `xml:",chardata"` T string `xml:"t,attr,omitempty"` // Formula type Ref string `xml:"ref,attr,omitempty"` // Shared formula ref - Si int `xml:"si,attr,omitempty"` // Shared formula index + Si string `xml:"si,attr,omitempty"` // Shared formula index } // xlsxHyperlinks directly maps the hyperlinks element in the namespace