Browse Source

- Fix issue: sheet protection and conditional formatting proprietary missing after save;
- Update workbook and sheet relationships and self-close tag replacement hack functions

Ri Xu 9 years ago
parent
commit
f958f05a3b
4 changed files with 121 additions and 64 deletions
  1. 0 9
      excelize.go
  2. 3 4
      lib.go
  3. 64 32
      sheet.go
  4. 54 19
      xmlWorksheet.go

+ 0 - 9
excelize.go

@@ -183,15 +183,6 @@ 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, `></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, `></mergeCell>`, ` />`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></drawing>`, ` />`, -1)
 	return workbookMarshal
 }
 

+ 3 - 4
lib.go

@@ -7,7 +7,6 @@ import (
 	"io"
 	"log"
 	"math"
-	"strings"
 )
 
 // ReadZip takes a pointer to a zip.ReadCloser and returns a
@@ -28,7 +27,7 @@ func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
 		if len(v.Name) > 18 {
 			if v.Name[0:19] == `xl/worksheets/sheet` {
 				var xlsx xlsxWorksheet
-				xml.Unmarshal([]byte(strings.Replace(fileList[v.Name], `<drawing r:id=`, `<drawing rid=`, -1)), &xlsx)
+				xml.Unmarshal([]byte(fileList[v.Name]), &xlsx)
 				xlsx = checkRow(xlsx)
 				output, _ := xml.Marshal(xlsx)
 				fileList[v.Name] = replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output)))
@@ -39,10 +38,10 @@ func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
 	return fileList, worksheets, nil
 }
 
-// Read XML content as string and replace drawing property in XML namespace of sheet.
+// Read XML content as string.
 func (f *File) readXML(name string) string {
 	if content, ok := f.XLSX[name]; ok {
-		return strings.Replace(content, `<drawing r:id=`, `<drawing rid=`, -1)
+		return content
 	}
 	return ``
 }

+ 64 - 32
sheet.go

@@ -8,6 +8,55 @@ import (
 	"strings"
 )
 
+// Define the empty element and self-close XML tags hack rules for
+// xl/workbook.xml and xl/worksheets/sheet%d.xml.
+var (
+	WorkbookRules = []map[string]string{
+		{`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="`: `r:id="`},
+		{`></sheet>`: ` />`},
+		{`></workbookView>`: ` />`},
+		{`></fileVersion>`: ` />`},
+		{`></workbookPr>`: ` />`},
+		{`></calcPr>`: ` />`},
+		{`></workbookProtection>`: ` />`},
+		{`></fileRecoveryPr>`: ` />`},
+		{`></hyperlink>`: ` />`},
+		{`></tabColor>`: ` />`},
+		{`></pageSetUpPr>`: ` />`},
+		{`></pane>`: ` />`},
+		{`<extLst></extLst>`: ``},
+		{`<fileRecoveryPr />`: ``},
+		{`<workbookProtection />`: ``},
+		{`<pivotCaches></pivotCaches>`: ``},
+		{`<externalReferences></externalReferences>`: ``},
+		{`<workbookProtection></workbookProtection>`: ``},
+		{`<definedNames></definedNames>`: ``},
+		{`<fileRecoveryPr></fileRecoveryPr>`: ``},
+		{`<workbookPr />`: ``},
+	}
+	SheetRules = []map[string]string{
+		{`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="`: `r:id="`},
+		{`<drawing></drawing>`: ``},
+		{`<drawing />`: ``},
+		{`<hyperlinks></hyperlinks>`: ``},
+		{`<tableParts count="0"></tableParts>`: ``},
+		{`<picture></picture>`: ``},
+		{`<legacyDrawing></legacyDrawing>`: ``},
+		{`<tabColor></tabColor>`: ``},
+		{`<sheetProtection></sheetProtection>`: ``},
+		{`<conditionalFormatting></conditionalFormatting>`: ``},
+		{`<extLst></extLst>`: ``},
+		{`></tablePart>`: ` />`},
+		{`></dimension>`: ` />`},
+		{`></selection>`: ` />`},
+		{`></sheetFormatPr>`: ` />`},
+		{`></printOptions>`: ` />`},
+		{`></pageSetup>`: ` />`},
+		{`></pageMargins>`: ` />`},
+		{`></mergeCell>`: ` />`},
+	}
+)
+
 // NewSheet provice function to greate a new sheet by given index, when
 // creating a new XLSX file, the default sheet will be create, when you
 // create a new file, you need to ensure that the index is continuous.
@@ -122,17 +171,6 @@ func replaceRelationshipsNameSpace(workbookMarshal string) string {
 	return strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
 }
 
-// replace relationships ID in worksheets/sheet%d.xml
-func replaceRelationshipsID(workbookMarshal string) string {
-	rids := strings.Replace(workbookMarshal, `<drawing rid="" />`, ``, -1)
-	rids = strings.Replace(rids, `<hyperlinks></hyperlinks>`, ``, -1)
-	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)
-}
-
 // SetActiveSheet provide function to set default active sheet of XLSX by given index.
 func (f *File) SetActiveSheet(index int) {
 	var content xlsxWorkbook
@@ -188,26 +226,20 @@ func (f *File) SetActiveSheet(index int) {
 
 // Replace xl/workbook.xml XML tags to self-closing for compatible Office Excel 2007.
 func workBookCompatibility(workbookMarshal string) string {
-	workbookMarshal = strings.Replace(workbookMarshal, `xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="`, `r:id="`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></sheet>`, ` />`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></workbookView>`, ` />`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></fileVersion>`, ` />`, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `></workbookPr>`, ` />`, -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)
-	workbookMarshal = strings.Replace(workbookMarshal, `<extLst></extLst>`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<fileRecoveryPr />`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<workbookProtection />`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<pivotCaches></pivotCaches>`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<externalReferences></externalReferences>`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<workbookProtection></workbookProtection>`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<definedNames></definedNames>`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<fileRecoveryPr></fileRecoveryPr>`, ``, -1)
-	workbookMarshal = strings.Replace(workbookMarshal, `<workbookPr />`, ``, -1)
+	for _, rules := range WorkbookRules {
+		for k, v := range rules {
+			workbookMarshal = strings.Replace(workbookMarshal, k, v, -1)
+		}
+	}
+	return workbookMarshal
+}
+
+// replace relationships ID in worksheets/sheet%d.xml
+func replaceRelationshipsID(workbookMarshal string) string {
+	for _, rules := range SheetRules {
+		for k, v := range rules {
+			workbookMarshal = strings.Replace(workbookMarshal, k, v, -1)
+		}
+	}
 	return workbookMarshal
 }

+ 54 - 19
xmlWorksheet.go

@@ -9,29 +9,31 @@ import "encoding/xml"
 // currently I have not checked it for completeness - it does as much
 // as I need.
 type xlsxWorksheet struct {
-	XMLName       xml.Name          `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
-	SheetPr       xlsxSheetPr       `xml:"sheetPr"`
-	Dimension     xlsxDimension     `xml:"dimension"`
-	SheetViews    xlsxSheetViews    `xml:"sheetViews"`
-	SheetFormatPr xlsxSheetFormatPr `xml:"sheetFormatPr"`
-	Cols          *xlsxCols         `xml:"cols,omitempty"`
-	SheetData     xlsxSheetData     `xml:"sheetData"`
-	Hyperlinks    xlsxHyperlinks    `xml:"hyperlinks"`
-	MergeCells    *xlsxMergeCells   `xml:"mergeCells,omitempty"`
-	PrintOptions  xlsxPrintOptions  `xml:"printOptions"`
-	PageMargins   xlsxPageMargins   `xml:"pageMargins"`
-	PageSetUp     xlsxPageSetUp     `xml:"pageSetup"`
-	HeaderFooter  xlsxHeaderFooter  `xml:"headerFooter"`
-	Drawing       xlsxDrawing       `xml:"drawing"`
-	LegacyDrawing xlsxLegacyDrawing `xml:"legacyDrawing"`
-	Picture       xlsxPicture       `xml:"picture"`
-	TableParts    xlsxTableParts    `xml:"tableParts"`
-	ExtLst        xlsxExtLst        `xml:"extLst"`
+	XMLName               xml.Name                  `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"`
+	SheetPr               xlsxSheetPr               `xml:"sheetPr"`
+	Dimension             xlsxDimension             `xml:"dimension"`
+	SheetViews            xlsxSheetViews            `xml:"sheetViews"`
+	SheetFormatPr         xlsxSheetFormatPr         `xml:"sheetFormatPr"`
+	Cols                  *xlsxCols                 `xml:"cols,omitempty"`
+	SheetData             xlsxSheetData             `xml:"sheetData"`
+	SheetProtection       xlsxSheetProtection       `xml:"sheetProtection"`
+	MergeCells            *xlsxMergeCells           `xml:"mergeCells,omitempty"`
+	ConditionalFormatting xlsxConditionalFormatting `xml:"conditionalFormatting"`
+	Hyperlinks            xlsxHyperlinks            `xml:"hyperlinks"`
+	PrintOptions          xlsxPrintOptions          `xml:"printOptions"`
+	PageMargins           xlsxPageMargins           `xml:"pageMargins"`
+	PageSetUp             xlsxPageSetUp             `xml:"pageSetup"`
+	HeaderFooter          xlsxHeaderFooter          `xml:"headerFooter"`
+	Drawing               xlsxDrawing               `xml:"drawing"`
+	LegacyDrawing         xlsxLegacyDrawing         `xml:"legacyDrawing"`
+	Picture               xlsxPicture               `xml:"picture"`
+	TableParts            xlsxTableParts            `xml:"tableParts"`
+	ExtLst                xlsxExtLst                `xml:"extLst"`
 }
 
 // xlsxDrawing change r:id to rid in the namespace.
 type xlsxDrawing struct {
-	RID string `xml:"rid,attr"`
+	RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
 }
 
 // xlsxHeaderFooter directly maps the headerFooter element in the namespace
@@ -303,6 +305,39 @@ type xlsxF struct {
 	Si      string `xml:"si,attr,omitempty"`  // Shared formula index
 }
 
+// xlsxSheetProtection collection expresses the sheet protection options
+// to enforce when the sheet is protected.
+type xlsxSheetProtection struct {
+	AlgorithmName      string `xml:"algorithmName,attr,omitempty"`
+	AutoFilter         int    `xml:"autoFilter,attr,omitempty"`
+	DeleteColumns      int    `xml:"deleteColumns,attr,omitempty"`
+	DeleteRows         int    `xml:"deleteRows,attr,omitempty"`
+	FormatCells        int    `xml:"formatCells,attr,omitempty"`
+	FormatColumns      int    `xml:"formatColumns,attr,omitempty"`
+	FormatRows         int    `xml:"formatRows,attr,omitempty"`
+	HashValue          string `xml:"hashValue,attr,omitempty"`
+	InsertColumns      int    `xml:"insertColumns,attr,omitempty"`
+	InsertHyperlinks   int    `xml:"insertHyperlinks,attr,omitempty"`
+	InsertRows         int    `xml:"insertRows,attr,omitempty"`
+	Objects            int    `xml:"objects,attr,omitempty"`
+	PivotTables        int    `xml:"pivotTables,attr,omitempty"`
+	SaltValue          string `xml:"saltValue,attr,omitempty"`
+	Scenarios          int    `xml:"scenarios,attr,omitempty"`
+	SelectLockedCells  int    `xml:"selectLockedCells,attr,omitempty"`
+	SelectUnlockedCell int    `xml:"selectUnlockedCell,attr,omitempty"`
+	Sheet              int    `xml:"sheet,attr,omitempty"`
+	Sort               int    `xml:"sort,attr,omitempty"`
+	SpinCount          int    `xml:"spinCount,attr,omitempty"`
+}
+
+// A Conditional Format is a format, such as cell shading or font color,
+// that a spreadsheet application can automatically apply to cells if a
+// specified condition is true. This collection expresses conditional
+// formatting rules applied to a particular cell or range.
+type xlsxConditionalFormatting struct {
+	CfRule string `xml:",innerxml"`
+}
+
 // xlsxHyperlinks directly maps the hyperlinks element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main
 type xlsxHyperlinks struct {