Browse Source

support parse and generate XML element namespace dynamic, fix #651

xuri 5 years ago
parent
commit
c922c32fb7
16 changed files with 171 additions and 45 deletions
  1. 1 0
      cell.go
  2. 3 1
      chart.go
  3. 1 1
      col.go
  4. 1 0
      comment.go
  5. 4 3
      drawing.go
  6. 6 8
      excelize.go
  7. 99 2
      lib.go
  8. 3 2
      picture.go
  9. 10 5
      rows.go
  10. 1 0
      shape.go
  11. 15 6
      sheet.go
  12. 1 1
      sparkline.go
  13. 1 1
      stream.go
  14. 2 2
      styles.go
  15. 16 10
      table.go
  16. 7 3
      xmlDrawing.go

+ 1 - 0
cell.go

@@ -490,6 +490,7 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) error {
 		sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
 		rID := f.addRels(sheetRels, SourceRelationshipHyperLink, link, linkType)
 		linkData.RID = "rId" + strconv.Itoa(rID)
+		f.addSheetNameSpace(sheet, SourceRelationship)
 	case "Location":
 		linkData = xlsxHyperlink{
 			Ref:      axis,

+ 3 - 1
chart.go

@@ -755,6 +755,7 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
 	f.addChart(formatSet, comboCharts)
 	f.addContentTypePart(chartID, "chart")
 	f.addContentTypePart(drawingID, "drawings")
+	f.addSheetNameSpace(sheet, SourceRelationship)
 	return err
 }
 
@@ -804,7 +805,8 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
 	// Update xl/workbook.xml
 	f.setWorkbook(sheet, sheetID, rID)
 	chartsheet, _ := xml.Marshal(cs)
-	f.saveFileList(path, replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(chartsheet)))
+	f.addSheetNameSpace(sheet, NameSpaceSpreadSheet)
+	f.saveFileList(path, replaceRelationshipsBytes(f.replaceNameSpaceBytes(path, chartsheet)))
 	return err
 }
 

+ 1 - 1
col.go

@@ -159,7 +159,7 @@ func (f *File) Cols(sheet string) (*Cols, error) {
 	}
 	if f.Sheet[name] != nil {
 		output, _ := xml.Marshal(f.Sheet[name])
-		f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
+		f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
 	}
 	var (
 		inElement            string

+ 1 - 0
comment.go

@@ -109,6 +109,7 @@ func (f *File) AddComment(sheet, cell, format string) error {
 		sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
 		rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
 		f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "")
+		f.addSheetNameSpace(sheet, SourceRelationship)
 		f.addSheetLegacyDrawing(sheet, rID)
 	}
 	commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"

+ 4 - 3
drawing.go

@@ -47,6 +47,7 @@ func (f *File) prepareChartSheetDrawing(xlsx *xlsxChartsheet, drawingID int, she
 	// Only allow one chart in a chartsheet.
 	sheetRels := "xl/chartsheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/chartsheets/") + ".rels"
 	rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
+	f.addSheetNameSpace(sheet, SourceRelationship)
 	xlsx.Drawing = &xlsxDrawing{
 		RID: "rId" + strconv.Itoa(rID),
 	}
@@ -60,7 +61,7 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
 	xlsxChartSpace := xlsxChartSpace{
 		XMLNSc:         NameSpaceDrawingMLChart,
 		XMLNSa:         NameSpaceDrawingML,
-		XMLNSr:         SourceRelationship,
+		XMLNSr:         SourceRelationship.Value,
 		XMLNSc16r2:     SourceRelationshipChart201506,
 		Date1904:       &attrValBool{Val: boolPtr(false)},
 		Lang:           &attrValString{Val: stringPtr("en-US")},
@@ -1212,7 +1213,7 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
 				URI: NameSpaceDrawingMLChart,
 				Chart: &xlsxChart{
 					C:   NameSpaceDrawingMLChart,
-					R:   SourceRelationship,
+					R:   SourceRelationship.Value,
 					RID: "rId" + strconv.Itoa(rID),
 				},
 			},
@@ -1252,7 +1253,7 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma
 				URI: NameSpaceDrawingMLChart,
 				Chart: &xlsxChart{
 					C:   NameSpaceDrawingMLChart,
-					R:   SourceRelationship,
+					R:   SourceRelationship.Value,
 					RID: "rId" + strconv.Itoa(rID),
 				},
 			},

+ 6 - 8
excelize.go

@@ -30,6 +30,7 @@ import (
 
 // File define a populated spreadsheet file struct.
 type File struct {
+	xmlAttr          map[string][]xml.Attr
 	checked          map[string]bool
 	sheetMap         map[string]string
 	CalcChain        *xlsxCalcChain
@@ -72,6 +73,7 @@ func OpenFile(filename string) (*File, error) {
 // newFile is object builder
 func newFile() *File {
 	return &File{
+		xmlAttr:          make(map[string][]xml.Attr),
 		checked:          make(map[string]bool),
 		sheetMap:         make(map[string]string),
 		Comments:         make(map[string]*xlsxComments),
@@ -166,6 +168,10 @@ func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) {
 			return
 		}
 		xlsx = new(xlsxWorksheet)
+		if _, ok := f.xmlAttr[name]; !ok {
+			d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name))))
+			f.xmlAttr[name] = append(f.xmlAttr[name], getRootElement(d)...)
+		}
 		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
 			Decode(xlsx); err != nil && err != io.EOF {
 			err = fmt.Errorf("xml decode error: %s", err)
@@ -254,14 +260,6 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
 	return rID
 }
 
-// replaceRelationshipsNameSpaceBytes provides a function to replace
-// XML tags to self-closing for compatible Microsoft Office Excel 2007.
-func replaceRelationshipsNameSpaceBytes(contentMarshal []byte) []byte {
-	var oldXmlns = stringToBytes(` xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
-	var newXmlns = []byte(templateNamespaceIDMap)
-	return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
-}
-
 // UpdateLinkedValue fix linked values within a spreadsheet are not updating in
 // Office Excel 2007 and 2010. This function will be remove value tag when met a
 // cell have a linked value. Reference

+ 99 - 2
lib.go

@@ -15,6 +15,7 @@ import (
 	"archive/zip"
 	"bytes"
 	"container/list"
+	"encoding/xml"
 	"fmt"
 	"io"
 	"strconv"
@@ -243,11 +244,11 @@ func parseFormatSet(formatSet string) []byte {
 // Transitional namespaces.
 func namespaceStrictToTransitional(content []byte) []byte {
 	var namespaceTranslationDic = map[string]string{
-		StrictSourceRelationship:         SourceRelationship,
+		StrictSourceRelationship:         SourceRelationship.Value,
 		StrictSourceRelationshipChart:    SourceRelationshipChart,
 		StrictSourceRelationshipComments: SourceRelationshipComments,
 		StrictSourceRelationshipImage:    SourceRelationshipImage,
-		StrictNameSpaceSpreadSheet:       NameSpaceSpreadSheet,
+		StrictNameSpaceSpreadSheet:       NameSpaceSpreadSheet.Value,
 	}
 	for s, n := range namespaceTranslationDic {
 		content = bytesReplace(content, stringToBytes(s), stringToBytes(n), -1)
@@ -319,6 +320,102 @@ func genSheetPasswd(plaintext string) string {
 	return strings.ToUpper(strconv.FormatInt(password, 16))
 }
 
+// getRootElement extract root element attributes by given XML decoder.
+func getRootElement(d *xml.Decoder) []xml.Attr {
+	tokenIdx := 0
+	for {
+		token, _ := d.Token()
+		if token == nil {
+			break
+		}
+		switch startElement := token.(type) {
+		case xml.StartElement:
+			tokenIdx++
+			if tokenIdx == 1 {
+				return startElement.Attr
+			}
+		}
+	}
+	return nil
+}
+
+// genXMLNamespace generate serialized XML attributes with a multi namespace
+// by given element attributes.
+func genXMLNamespace(attr []xml.Attr) string {
+	var rootElement string
+	for _, v := range attr {
+		if lastSpace := getXMLNamespace(v.Name.Space, attr); lastSpace != "" {
+			rootElement += fmt.Sprintf("%s:%s=\"%s\" ", lastSpace, v.Name.Local, v.Value)
+			continue
+		}
+		rootElement += fmt.Sprintf("%s=\"%s\" ", v.Name.Local, v.Value)
+	}
+	return strings.TrimSpace(rootElement) + ">"
+}
+
+// getXMLNamespace extract XML namespace from specified element name and attributes.
+func getXMLNamespace(space string, attr []xml.Attr) string {
+	for _, attribute := range attr {
+		if attribute.Value == space {
+			return attribute.Name.Local
+		}
+	}
+	return space
+}
+
+// replaceNameSpaceBytes provides a function to replace the XML root element
+// attribute by the given component part path and XML content.
+func (f *File) replaceNameSpaceBytes(path string, contentMarshal []byte) []byte {
+	var oldXmlns = stringToBytes(`xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
+	var newXmlns = []byte(templateNamespaceIDMap)
+	if attr, ok := f.xmlAttr[path]; ok {
+		newXmlns = []byte(genXMLNamespace(attr))
+	}
+	return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
+}
+
+// addNameSpaces provides a function to add a XML attribute by the given
+// component part path.
+func (f *File) addNameSpaces(path string, ns xml.Attr) {
+	exist := false
+	mc := false
+	ignore := false
+	if attr, ok := f.xmlAttr[path]; ok {
+		for _, attribute := range attr {
+			if attribute.Name.Local == ns.Name.Local && attribute.Name.Space == ns.Name.Space {
+				exist = true
+			}
+			if attribute.Name.Local == "Ignorable" && attribute.Name.Space == "mc" {
+				ignore = true
+			}
+			if attribute.Name.Local == "mc" && attribute.Name.Space == "xmlns" {
+				mc = true
+			}
+		}
+	}
+	if !exist {
+		f.xmlAttr[path] = append(f.xmlAttr[path], ns)
+		if !mc {
+			f.xmlAttr[path] = append(f.xmlAttr[path], xml.Attr{
+				Name:  xml.Name{Local: "mc", Space: "xmlns"},
+				Value: SourceRelationshipCompatibility,
+			})
+		}
+		if !ignore {
+			f.xmlAttr[path] = append(f.xmlAttr[path], xml.Attr{
+				Name:  xml.Name{Local: "Ignorable", Space: "mc"},
+				Value: ns.Name.Local,
+			})
+		}
+	}
+}
+
+// addSheetNameSpace add XML attribute for worksheet.
+func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) {
+	name, _ := f.sheetMap[trimSheetName(sheet)]
+	f.addNameSpaces(name, ns)
+}
+
 // Stack defined an abstract data type that serves as a collection of elements.
 type Stack struct {
 	list *list.List

+ 3 - 2
picture.go

@@ -168,6 +168,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
 		return err
 	}
 	f.addContentTypePart(drawingID, "drawings")
+	f.addSheetNameSpace(sheet, SourceRelationship)
 	return err
 }
 
@@ -279,11 +280,11 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
 	pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
 	if hyperlinkRID != 0 {
 		pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
-			R:   SourceRelationship,
+			R:   SourceRelationship.Value,
 			RID: "rId" + strconv.Itoa(hyperlinkRID),
 		}
 	}
-	pic.BlipFill.Blip.R = SourceRelationship
+	pic.BlipFill.Blip.R = SourceRelationship.Value
 	pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
 	pic.SpPr.PrstGeom.Prst = "rect"
 

+ 10 - 5
rows.go

@@ -121,11 +121,8 @@ func (rows *Rows) Columns() ([]string, error) {
 					}
 				}
 				blank := cellCol - len(columns)
-				for i := 1; i < blank; i++ {
-					columns = append(columns, "")
-				}
 				val, _ := colCell.getValueFrom(rows.f, d)
-				columns = append(columns, val)
+				columns = append(appendSpace(blank, columns), val)
 			}
 		case xml.EndElement:
 			inElement = startElement.Name.Local
@@ -137,6 +134,14 @@ func (rows *Rows) Columns() ([]string, error) {
 	return columns, err
 }
 
+// appendSpace append blank characters to slice by given length and source slice.
+func appendSpace(l int, s []string) []string {
+	for i := 1; i < l; i++ {
+		s = append(s, "")
+	}
+	return s
+}
+
 // ErrSheetNotExist defines an error of sheet is not exist
 type ErrSheetNotExist struct {
 	SheetName string
@@ -173,7 +178,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
 	if f.Sheet[name] != nil {
 		// flush data
 		output, _ := xml.Marshal(f.Sheet[name])
-		f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
+		f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
 	}
 	var (
 		err       error

+ 1 - 0
shape.go

@@ -280,6 +280,7 @@ func (f *File) AddShape(sheet, cell, format string) error {
 		sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
 		rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
 		f.addSheetDrawing(sheet, rID)
+		f.addSheetNameSpace(sheet, SourceRelationship)
 	}
 	err = f.addDrawingShape(sheet, drawingXML, cell, formatSet)
 	if err != nil {

+ 15 - 6
sheet.go

@@ -92,15 +92,18 @@ func (f *File) contentTypesWriter() {
 // structure after deserialization.
 func (f *File) workbookReader() *xlsxWorkbook {
 	var err error
-
 	if f.WorkBook == nil {
 		f.WorkBook = new(xlsxWorkbook)
+		if _, ok := f.xmlAttr["xl/workbook.xml"]; !ok {
+			d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/workbook.xml"))))
+			f.xmlAttr["xl/workbook.xml"] = append(f.xmlAttr["xl/workbook.xml"], getRootElement(d)...)
+			f.addNameSpaces("xl/workbook.xml", SourceRelationship)
+		}
 		if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")))).
 			Decode(f.WorkBook); err != nil && err != io.EOF {
 			log.Printf("xml decode error: %s", err)
 		}
 	}
-
 	return f.WorkBook
 }
 
@@ -109,7 +112,7 @@ func (f *File) workbookReader() *xlsxWorkbook {
 func (f *File) workBookWriter() {
 	if f.WorkBook != nil {
 		output, _ := xml.Marshal(f.WorkBook)
-		f.saveFileList("xl/workbook.xml", replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(output)))
+		f.saveFileList("xl/workbook.xml", replaceRelationshipsBytes(f.replaceNameSpaceBytes("xl/workbook.xml", output)))
 	}
 }
 
@@ -122,7 +125,7 @@ func (f *File) workSheetWriter() {
 				f.Sheet[p].SheetData.Row[k].C = trimCell(v.C)
 			}
 			output, _ := xml.Marshal(sheet)
-			f.saveFileList(p, replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(output)))
+			f.saveFileList(p, replaceRelationshipsBytes(f.replaceNameSpaceBytes(p, output)))
 			ok := f.checked[p]
 			if ok {
 				delete(f.Sheet, p)
@@ -173,6 +176,7 @@ func (f *File) setSheet(index int, name string) {
 	path := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml"
 	f.sheetMap[trimSheetName(name)] = path
 	f.Sheet[path] = &xlsx
+	f.xmlAttr[path] = append(f.xmlAttr[path], NameSpaceSpreadSheet)
 }
 
 // setWorkbook update workbook property of the spreadsheet. Maximum 31
@@ -193,7 +197,7 @@ func (f *File) relsWriter() {
 		if rel != nil {
 			output, _ := xml.Marshal(rel)
 			if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") {
-				output = replaceRelationshipsNameSpaceBytes(output)
+				output = f.replaceNameSpaceBytes(path, output)
 			}
 			f.saveFileList(path, replaceRelationshipsBytes(output))
 		}
@@ -440,6 +444,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
 	sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
 	rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
 	f.addSheetPicture(sheet, rID)
+	f.addSheetNameSpace(sheet, SourceRelationship)
 	f.setContentTypePartImageExtensions()
 	return err
 }
@@ -479,6 +484,7 @@ func (f *File) DeleteSheet(name string) {
 			delete(f.XLSX, rels)
 			delete(f.Relationships, rels)
 			delete(f.Sheet, sheetXML)
+			delete(f.xmlAttr, sheetXML)
 			f.SheetCount--
 		}
 	}
@@ -557,6 +563,9 @@ func (f *File) copySheet(from, to int) error {
 	if ok {
 		f.XLSX[toRels] = f.XLSX[fromRels]
 	}
+	fromSheetXMLPath, _ := f.sheetMap[trimSheetName(fromSheet)]
+	fromSheetAttr, _ := f.xmlAttr[fromSheetXMLPath]
+	f.xmlAttr[path] = fromSheetAttr
 	return err
 }
 
@@ -779,7 +788,7 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
 	if f.Sheet[name] != nil {
 		// flush data
 		output, _ := xml.Marshal(f.Sheet[name])
-		f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
+		f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
 	}
 	return f.searchSheet(name, value, regSearch)
 }

+ 1 - 1
sparkline.go

@@ -455,7 +455,7 @@ func (f *File) AddSparkline(sheet string, opt *SparklineOption) (err error) {
 		}
 		ws.ExtLst.Ext = string(extBytes)
 	}
-
+	f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
 	return
 }
 

+ 1 - 1
stream.go

@@ -151,7 +151,7 @@ func (sw *StreamWriter) AddTable(hcell, vcell, format string) error {
 	}
 
 	table := xlsxTable{
-		XMLNS:       NameSpaceSpreadSheet,
+		XMLNS:       NameSpaceSpreadSheet.Value,
 		ID:          tableID,
 		Name:        name,
 		DisplayName: name,

+ 2 - 2
styles.go

@@ -1022,7 +1022,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
 func (f *File) styleSheetWriter() {
 	if f.Styles != nil {
 		output, _ := xml.Marshal(f.Styles)
-		f.saveFileList("xl/styles.xml", replaceRelationshipsNameSpaceBytes(output))
+		f.saveFileList("xl/styles.xml", f.replaceNameSpaceBytes("xl/styles.xml", output))
 	}
 }
 
@@ -1031,7 +1031,7 @@ func (f *File) styleSheetWriter() {
 func (f *File) sharedStringsWriter() {
 	if f.SharedStrings != nil {
 		output, _ := xml.Marshal(f.SharedStrings)
-		f.saveFileList("xl/sharedStrings.xml", replaceRelationshipsNameSpaceBytes(output))
+		f.saveFileList("xl/sharedStrings.xml", f.replaceNameSpaceBytes("xl/sharedStrings.xml", output))
 	}
 }
 

+ 16 - 10
table.go

@@ -83,9 +83,11 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error {
 	// Add first table for given sheet.
 	sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
 	rID := f.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
-	f.addSheetTable(sheet, rID)
-	err = f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet)
-	if err != nil {
+	if err = f.addSheetTable(sheet, rID); err != nil {
+		return err
+	}
+	f.addSheetNameSpace(sheet, SourceRelationship)
+	if err = f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet); err != nil {
 		return err
 	}
 	f.addContentTypePart(tableID, "table")
@@ -106,16 +108,20 @@ func (f *File) countTables() int {
 
 // addSheetTable provides a function to add tablePart element to
 // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
-func (f *File) addSheetTable(sheet string, rID int) {
-	xlsx, _ := f.workSheetReader(sheet)
+func (f *File) addSheetTable(sheet string, rID int) error {
+	ws, err := f.workSheetReader(sheet)
+	if err != nil {
+		return err
+	}
 	table := &xlsxTablePart{
 		RID: "rId" + strconv.Itoa(rID),
 	}
-	if xlsx.TableParts == nil {
-		xlsx.TableParts = &xlsxTableParts{}
+	if ws.TableParts == nil {
+		ws.TableParts = &xlsxTableParts{}
 	}
-	xlsx.TableParts.Count++
-	xlsx.TableParts.TableParts = append(xlsx.TableParts.TableParts, table)
+	ws.TableParts.Count++
+	ws.TableParts.TableParts = append(ws.TableParts.TableParts, table)
+	return err
 }
 
 // addTable provides a function to add table by given worksheet name,
@@ -159,7 +165,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
 		name = "Table" + strconv.Itoa(i)
 	}
 	t := xlsxTable{
-		XMLNS:       NameSpaceSpreadSheet,
+		XMLNS:       NameSpaceSpreadSheet.Value,
 		ID:          i,
 		Name:        name,
 		DisplayName: name,

+ 7 - 3
xmlDrawing.go

@@ -13,9 +13,15 @@ package excelize
 
 import "encoding/xml"
 
+// Source relationship and namespace.
+var (
+	SourceRelationship      = xml.Attr{Name: xml.Name{Local: "r", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"}
+	NameSpaceSpreadSheet    = xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
+	NameSpaceSpreadSheetX14 = xml.Attr{Name: xml.Name{Local: "x14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"}
+)
+
 // Source relationship and namespace.
 const (
-	SourceRelationship                           = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
 	SourceRelationshipChart                      = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
 	SourceRelationshipComments                   = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
 	SourceRelationshipImage                      = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
@@ -37,8 +43,6 @@ const (
 	NameSpaceDrawingML                           = "http://schemas.openxmlformats.org/drawingml/2006/main"
 	NameSpaceDrawingMLChart                      = "http://schemas.openxmlformats.org/drawingml/2006/chart"
 	NameSpaceDrawingMLSpreadSheet                = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
-	NameSpaceSpreadSheet                         = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
-	NameSpaceSpreadSheetX14                      = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
 	NameSpaceSpreadSheetX15                      = "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"
 	NameSpaceSpreadSheetExcel2006Main            = "http://schemas.microsoft.com/office/excel/2006/main"
 	NameSpaceMacExcel2008Main                    = "http://schemas.microsoft.com/office/mac/excel/2008/main"