Przeglądaj źródła

- 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

Ri Xu 9 lat temu
rodzic
commit
b84bfa7eab
5 zmienionych plików z 59 dodań i 33 usunięć
  1. 2 2
      excelize.go
  2. 1 1
      excelize_test.go
  3. 15 11
      sheet.go
  4. 2 1
      xmlWorkbook.go
  5. 39 18
      xmlWorksheet.go

+ 2 - 2
excelize.go

@@ -179,14 +179,14 @@ func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string {
 	oldXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
 	newXmlns := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">`
 	workbookMarshal = strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></sheetPr>`, ` />`, -1)
+	workbookMarshal = strings.Replace(workbookMarshal, `></tablePart>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></dimension>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></selection>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></sheetFormatPr>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></printOptions>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></pageSetup>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></pageMargins>`, ` />`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></headerFooter>`, ` />`, -1)
+	workbookMarshal = strings.Replace(workbookMarshal, `></mergeCell>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></drawing>`, ` />`, -1)
 	return workbookMarshal
 }

+ 1 - 1
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")

+ 15 - 11
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, `<tableParts count="0"></tableParts>`, ``, -1)
 	rids = strings.Replace(rids, `<picture></picture>`, ``, -1)
 	rids = strings.Replace(rids, `<legacyDrawing></legacyDrawing>`, ``, -1)
+	rids = strings.Replace(rids, `<tabColor></tabColor>`, ``, -1)
 	return strings.Replace(rids, `<drawing rid="`, `<drawing r:id="`, -1)
 }
 
@@ -191,10 +193,12 @@ func workBookCompatibility(workbookMarshal string) string {
 	workbookMarshal = strings.Replace(workbookMarshal, `></workbookView>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></fileVersion>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></workbookPr>`, ` />`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></definedNames>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></calcPr>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></workbookProtection>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></fileRecoveryPr>`, ` />`, -1)
 	workbookMarshal = strings.Replace(workbookMarshal, `></hyperlink>`, ` />`, -1)
+	workbookMarshal = strings.Replace(workbookMarshal, `></tabColor>`, ` />`, -1)
+	workbookMarshal = strings.Replace(workbookMarshal, `></pageSetUpPr>`, ` />`, -1)
+	workbookMarshal = strings.Replace(workbookMarshal, `></pane>`, ` />`, -1)
 	return workbookMarshal
 }

+ 2 - 1
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

+ 39 - 18
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 <clrScheme> collection (§20.1.6.2), referencing a particular <sysClr> or <srgbClr> 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