diff --git a/README.md b/README.md index 7f9cf70..998a4c1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Excelize logo

+

Excelize logo

Build Status diff --git a/README_zh.md b/README_zh.md index 6c2b190..d4cac66 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,4 +1,4 @@ -

Excelize logo

+

Excelize logo

Build Status diff --git a/cell.go b/cell.go index e897379..1da46aa 100644 --- a/cell.go +++ b/cell.go @@ -378,7 +378,9 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) error { linkData = xlsxHyperlink{ Ref: axis, } - rID := f.addSheetRelationships(sheet, SourceRelationshipHyperLink, link, linkType) + sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + rID := f.addRels(sheetRels, SourceRelationshipHyperLink, link, linkType) linkData.RID = "rId" + strconv.Itoa(rID) case "Location": linkData = xlsxHyperlink{ diff --git a/chart.go b/chart.go index e1eb81f..db2df1e 100644 --- a/chart.go +++ b/chart.go @@ -727,7 +727,8 @@ func (f *File) AddChart(sheet, cell, format string) error { chartID := f.countCharts() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML) - drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") + drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" + drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") err = f.addDrawingChart(sheet, drawingXML, cell, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format) if err != nil { return err @@ -761,7 +762,9 @@ func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawing drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1) } else { // Add first picture for given sheet. - rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "") + sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "") f.addSheetDrawing(sheet, rID) } return drawingID, drawingXML diff --git a/comment.go b/comment.go index 97e0e9b..7f3b10d 100644 --- a/comment.go +++ b/comment.go @@ -60,7 +60,7 @@ func (f *File) GetComments() (comments map[string][]Comment) { // given worksheet index. func (f *File) getSheetComments(sheetID int) string { var rels = "xl/worksheets/_rels/sheet" + strconv.Itoa(sheetID) + ".xml.rels" - if sheetRels := f.workSheetRelsReader(rels); sheetRels != nil { + if sheetRels := f.relsReader(rels); sheetRels != nil { for _, v := range sheetRels.Relationships { if v.Type == SourceRelationshipComments { return v.Target @@ -98,8 +98,10 @@ func (f *File) AddComment(sheet, cell, format string) error { drawingVML = strings.Replace(sheetRelationshipsDrawingVML, "..", "xl", -1) } else { // Add first comment for given sheet. - rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "") - f.addSheetRelationships(sheet, SourceRelationshipComments, sheetRelationshipsComments, "") + sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "") + f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "") f.addSheetLegacyDrawing(sheet, rID) } commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml" diff --git a/excelize.go b/excelize.go index b734e57..4d46b94 100644 --- a/excelize.go +++ b/excelize.go @@ -31,7 +31,6 @@ type File struct { CalcChain *xlsxCalcChain Comments map[string]*xlsxComments ContentTypes *xlsxTypes - DrawingRels map[string]*xlsxWorkbookRels Drawings map[string]*xlsxWsDr Path string SharedStrings *xlsxSST @@ -42,8 +41,7 @@ type File struct { DecodeVMLDrawing map[string]*decodeVmlDrawing VMLDrawing map[string]*vmlDrawing WorkBook *xlsxWorkbook - WorkBookRels *xlsxWorkbookRels - WorkSheetRels map[string]*xlsxWorkbookRels + Relationships map[string]*xlsxRelationships XLSX map[string][]byte } @@ -93,13 +91,12 @@ func OpenReader(r io.Reader) (*File, error) { f := &File{ checked: make(map[string]bool), Comments: make(map[string]*xlsxComments), - DrawingRels: make(map[string]*xlsxWorkbookRels), Drawings: make(map[string]*xlsxWsDr), Sheet: make(map[string]*xlsxWorksheet), SheetCount: sheetCount, DecodeVMLDrawing: make(map[string]*decodeVmlDrawing), VMLDrawing: make(map[string]*vmlDrawing), - WorkSheetRels: make(map[string]*xlsxWorkbookRels), + Relationships: make(map[string]*xlsxRelationships), XLSX: file, } f.CalcChain = f.calcChainReader() @@ -176,6 +173,28 @@ func checkSheet(xlsx *xlsxWorksheet) { xlsx.SheetData = sheetData } +// addRels provides a function to add relationships by given XML path, +// relationship type, target and target mode. +func (f *File) addRels(relPath, relType, target, targetMode string) int { + rels := f.relsReader(relPath) + rID := 0 + if rels == nil { + rels = &xlsxRelationships{} + } + rID = len(rels.Relationships) + 1 + var ID bytes.Buffer + ID.WriteString("rId") + ID.WriteString(strconv.Itoa(rID)) + rels.Relationships = append(rels.Relationships, xlsxRelationship{ + ID: ID.String(), + Type: relType, + Target: target, + TargetMode: targetMode, + }) + f.Relationships[relPath] = rels + return rID +} + // replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace // xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft // Office Excel 2007. @@ -265,7 +284,7 @@ func (f *File) AddVBAProject(bin string) error { return errors.New("unsupported VBA project extension") } f.setContentTypePartVBAProjectExtensions() - wb := f.workbookRelsReader() + wb := f.relsReader("xl/_rels/workbook.xml.rels") var rID int var ok bool for _, rel := range wb.Relationships { @@ -280,7 +299,7 @@ func (f *File) AddVBAProject(bin string) error { } rID++ if !ok { - wb.Relationships = append(wb.Relationships, xlsxWorkbookRelation{ + wb.Relationships = append(wb.Relationships, xlsxRelationship{ ID: "rId" + strconv.Itoa(rID), Target: "vbaProject.bin", Type: SourceRelationshipVBAProject, diff --git a/excelize.png b/excelize.png deleted file mode 100644 index 8ba520e..0000000 Binary files a/excelize.png and /dev/null differ diff --git a/excelize.svg b/excelize.svg new file mode 100644 index 0000000..afa8828 --- /dev/null +++ b/excelize.svg @@ -0,0 +1 @@ +Excelize logo \ No newline at end of file diff --git a/file.go b/file.go index 46f1f62..2e0d27b 100644 --- a/file.go +++ b/file.go @@ -42,14 +42,13 @@ func NewFile() *File { f.CalcChain = f.calcChainReader() f.Comments = make(map[string]*xlsxComments) f.ContentTypes = f.contentTypesReader() - f.DrawingRels = make(map[string]*xlsxWorkbookRels) f.Drawings = make(map[string]*xlsxWsDr) f.Styles = f.stylesReader() f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing) f.VMLDrawing = make(map[string]*vmlDrawing) f.WorkBook = f.workbookReader() - f.WorkBookRels = f.workbookRelsReader() - f.WorkSheetRels = make(map[string]*xlsxWorkbookRels) + f.Relationships = make(map[string]*xlsxRelationships) + f.Relationships["xl/_rels/workbook.xml.rels"] = f.relsReader("xl/_rels/workbook.xml.rels") f.Sheet["xl/worksheets/sheet1.xml"], _ = f.workSheetReader("Sheet1") f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml" f.Theme = f.themeReader() @@ -97,13 +96,11 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) { f.calcChainWriter() f.commentsWriter() f.contentTypesWriter() - f.drawingRelsWriter() f.drawingsWriter() f.vmlDrawingWriter() f.workBookWriter() - f.workBookRelsWriter() f.workSheetWriter() - f.workSheetRelsWriter() + f.relsWriter() f.styleSheetWriter() for path, content := range f.XLSX { diff --git a/picture.go b/picture.go index a5904ff..518463a 100644 --- a/picture.go +++ b/picture.go @@ -155,14 +155,15 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, drawingID := f.countDrawings() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML) + drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl") - drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipImage, mediaStr, hyperlinkType) + drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType) // Add picture with hyperlink. if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" { if formatSet.HyperlinkType == "External" { hyperlinkType = formatSet.HyperlinkType } - drawingHyperlinkRID = f.addDrawingRelationships(drawingID, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType) + drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType) } err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet) if err != nil { @@ -172,37 +173,6 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, return err } -// addSheetRelationships provides a function to add -// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name, relationship -// type and target. -func (f *File) addSheetRelationships(sheet, relType, target, targetMode string) int { - name, ok := f.sheetMap[trimSheetName(sheet)] - if !ok { - name = strings.ToLower(sheet) + ".xml" - } - var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels" - sheetRels := f.workSheetRelsReader(rels) - if sheetRels == nil { - sheetRels = &xlsxWorkbookRels{} - } - var rID = 1 - var ID bytes.Buffer - ID.WriteString("rId") - ID.WriteString(strconv.Itoa(rID)) - ID.Reset() - rID = len(sheetRels.Relationships) + 1 - ID.WriteString("rId") - ID.WriteString(strconv.Itoa(rID)) - sheetRels.Relationships = append(sheetRels.Relationships, xlsxWorkbookRelation{ - ID: ID.String(), - Type: relType, - Target: target, - TargetMode: targetMode, - }) - f.WorkSheetRels[rels] = sheetRels - return rID -} - // deleteSheetRelationships provides a function to delete relationships in // xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and // relationship index. @@ -212,16 +182,16 @@ func (f *File) deleteSheetRelationships(sheet, rID string) { name = strings.ToLower(sheet) + ".xml" } var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels" - sheetRels := f.workSheetRelsReader(rels) + sheetRels := f.relsReader(rels) if sheetRels == nil { - sheetRels = &xlsxWorkbookRels{} + sheetRels = &xlsxRelationships{} } for k, v := range sheetRels.Relationships { if v.ID == rID { sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...) } } - f.WorkSheetRels[rels] = sheetRels + f.Relationships[rels] = sheetRels } // addSheetLegacyDrawing provides a function to add legacy drawing element to @@ -325,33 +295,6 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he return err } -// addDrawingRelationships provides a function to add image part relationships -// in the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index, -// relationship type and target. -func (f *File) addDrawingRelationships(index int, relType, target, targetMode string) int { - var rels = "xl/drawings/_rels/drawing" + strconv.Itoa(index) + ".xml.rels" - var rID = 1 - var ID bytes.Buffer - ID.WriteString("rId") - ID.WriteString(strconv.Itoa(rID)) - drawingRels := f.drawingRelsReader(rels) - if drawingRels == nil { - drawingRels = &xlsxWorkbookRels{} - } - ID.Reset() - rID = len(drawingRels.Relationships) + 1 - ID.WriteString("rId") - ID.WriteString(strconv.Itoa(rID)) - drawingRels.Relationships = append(drawingRels.Relationships, xlsxWorkbookRelation{ - ID: ID.String(), - Type: relType, - Target: target, - TargetMode: targetMode, - }) - f.DrawingRels[rels] = drawingRels - return rID -} - // countMedia provides a function to get media files count storage in the // folder xl/media/image. func (f *File) countMedia() int { @@ -429,16 +372,20 @@ func (f *File) addContentTypePart(index int, contentType string) { "drawings": f.setContentTypePartImageExtensions, } partNames := map[string]string{ - "chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml", - "comments": "/xl/comments" + strconv.Itoa(index) + ".xml", - "drawings": "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml", - "table": "/xl/tables/table" + strconv.Itoa(index) + ".xml", + "chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml", + "comments": "/xl/comments" + strconv.Itoa(index) + ".xml", + "drawings": "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml", + "table": "/xl/tables/table" + strconv.Itoa(index) + ".xml", + "pivotTable": "/xl/pivotTables/pivotTable" + strconv.Itoa(index) + ".xml", + "pivotCache": "/xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(index) + ".xml", } contentTypes := map[string]string{ - "chart": "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", - "comments": "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", - "drawings": "application/vnd.openxmlformats-officedocument.drawing+xml", - "table": "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml", + "chart": "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", + "comments": "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", + "drawings": "application/vnd.openxmlformats-officedocument.drawing+xml", + "table": "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml", + "pivotTable": "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml", + "pivotCache": "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml", } s, ok := setContentType[contentType] if ok { @@ -465,9 +412,9 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string { name = strings.ToLower(sheet) + ".xml" } var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels" - sheetRels := f.workSheetRelsReader(rels) + sheetRels := f.relsReader(rels) if sheetRels == nil { - sheetRels = &xlsxWorkbookRels{} + sheetRels = &xlsxRelationships{} } for _, v := range sheetRels.Relationships { if v.ID == rID { @@ -529,12 +476,12 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) for _, anchor := range wsDr.TwoCellAnchor { if anchor.From != nil && anchor.Pic != nil { if anchor.From.Col == col && anchor.From.Row == row { - xlsxWorkbookRelation := f.getDrawingRelationships(drawingRelationships, + xlsxRelationship := f.getDrawingRelationships(drawingRelationships, anchor.Pic.BlipFill.Blip.Embed) - _, ok := supportImageTypes[filepath.Ext(xlsxWorkbookRelation.Target)] + _, ok := supportImageTypes[filepath.Ext(xlsxRelationship.Target)] if ok { - return filepath.Base(xlsxWorkbookRelation.Target), - []byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target, + return filepath.Base(xlsxRelationship.Target), + []byte(f.XLSX[strings.Replace(xlsxRelationship.Target, "..", "xl", -1)]), nil } } @@ -548,10 +495,10 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) _ = xml.Unmarshal([]byte(""+anchor.Content+""), &decodeTwoCellAnchor) if decodeTwoCellAnchor.From != nil && decodeTwoCellAnchor.Pic != nil { if decodeTwoCellAnchor.From.Col == col && decodeTwoCellAnchor.From.Row == row { - xlsxWorkbookRelation := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed) - _, ok := supportImageTypes[filepath.Ext(xlsxWorkbookRelation.Target)] + xlsxRelationship := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed) + _, ok := supportImageTypes[filepath.Ext(xlsxRelationship.Target)] if ok { - return filepath.Base(xlsxWorkbookRelation.Target), []byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target, "..", "xl", -1)]), nil + return filepath.Base(xlsxRelationship.Target), []byte(f.XLSX[strings.Replace(xlsxRelationship.Target, "..", "xl", -1)]), nil } } } @@ -562,8 +509,8 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) // getDrawingRelationships provides a function to get drawing relationships // from xl/drawings/_rels/drawing%s.xml.rels by given file name and // relationship ID. -func (f *File) getDrawingRelationships(rels, rID string) *xlsxWorkbookRelation { - if drawingRels := f.drawingRelsReader(rels); drawingRels != nil { +func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship { + if drawingRels := f.relsReader(rels); drawingRels != nil { for _, v := range drawingRels.Relationships { if v.ID == rID { return &v @@ -573,31 +520,6 @@ func (f *File) getDrawingRelationships(rels, rID string) *xlsxWorkbookRelation { return nil } -// drawingRelsReader provides a function to get the pointer to the structure -// after deserialization of xl/drawings/_rels/drawing%d.xml.rels. -func (f *File) drawingRelsReader(rel string) *xlsxWorkbookRels { - if f.DrawingRels[rel] == nil { - _, ok := f.XLSX[rel] - if ok { - d := xlsxWorkbookRels{} - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(rel)), &d) - f.DrawingRels[rel] = &d - } - } - return f.DrawingRels[rel] -} - -// drawingRelsWriter provides a function to save -// xl/drawings/_rels/drawing%d.xml.rels after serialize structure. -func (f *File) drawingRelsWriter() { - for path, d := range f.DrawingRels { - if d != nil { - v, _ := xml.Marshal(d) - f.saveFileList(path, v) - } - } -} - // drawingsWriter provides a function to save xl/drawings/drawing%d.xml after // serialize structure. func (f *File) drawingsWriter() { diff --git a/shape.go b/shape.go index 8d95849..e6a2ff3 100644 --- a/shape.go +++ b/shape.go @@ -275,7 +275,9 @@ func (f *File) AddShape(sheet, cell, format string) error { drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1) } else { // Add first shape for given sheet. - rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "") + name, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels" + rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "") f.addSheetDrawing(sheet, rID) } err = f.addDrawingShape(sheet, drawingXML, cell, formatSet) diff --git a/sheet.go b/sheet.go index ed6d888..951baf9 100644 --- a/sheet.go +++ b/sheet.go @@ -52,7 +52,7 @@ func (f *File) NewSheet(name string) int { // Create new sheet /xl/worksheets/sheet%d.xml f.setSheet(sheetID, name) // Update xl/_rels/workbook.xml.rels - rID := f.addXlsxWorkbookRels(sheetID) + rID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipWorkSheet, fmt.Sprintf("worksheets/sheet%d.xml", sheetID), "") // Update xl/workbook.xml f.setWorkbook(name, sheetID, rID) return sheetID @@ -163,50 +163,18 @@ func (f *File) setWorkbook(name string, sheetID, rid int) { }) } -// workbookRelsReader provides a function to read and unmarshal workbook -// relationships of XLSX file. -func (f *File) workbookRelsReader() *xlsxWorkbookRels { - if f.WorkBookRels == nil { - var content xlsxWorkbookRels - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/_rels/workbook.xml.rels")), &content) - f.WorkBookRels = &content - } - return f.WorkBookRels -} - -// workBookRelsWriter provides a function to save xl/_rels/workbook.xml.rels after +// relsWriter provides a function to save relationships after // serialize structure. -func (f *File) workBookRelsWriter() { - if f.WorkBookRels != nil { - output, _ := xml.Marshal(f.WorkBookRels) - f.saveFileList("xl/_rels/workbook.xml.rels", output) - } -} - -// addXlsxWorkbookRels update workbook relationships property of XLSX. -func (f *File) addXlsxWorkbookRels(sheet int) int { - content := f.workbookRelsReader() - rID := 0 - for _, v := range content.Relationships { - t, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId")) - if t > rID { - rID = t +func (f *File) relsWriter() { + for path, rel := range f.Relationships { + if rel != nil { + output, _ := xml.Marshal(rel) + if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") { + output = replaceWorkSheetsRelationshipsNameSpaceBytes(output) + } + f.saveFileList(path, replaceRelationshipsBytes(output)) } } - rID++ - ID := bytes.Buffer{} - ID.WriteString("rId") - ID.WriteString(strconv.Itoa(rID)) - target := bytes.Buffer{} - target.WriteString("worksheets/sheet") - target.WriteString(strconv.Itoa(sheet)) - target.WriteString(".xml") - content.Relationships = append(content.Relationships, xlsxWorkbookRelation{ - ID: ID.String(), - Target: target.String(), - Type: SourceRelationshipWorkSheet, - }) - return rID } // setAppXML update docProps/app.xml file of XML. @@ -365,7 +333,7 @@ func (f *File) GetSheetMap() map[int]string { // of XLSX. func (f *File) getSheetMap() map[string]string { content := f.workbookReader() - rels := f.workbookRelsReader() + rels := f.relsReader("xl/_rels/workbook.xml.rels") maps := map[string]string{} for _, v := range content.Sheets.Sheet { for _, rel := range rels.Relationships { @@ -396,7 +364,9 @@ func (f *File) SetSheetBackground(sheet, picture string) error { } file, _ := ioutil.ReadFile(picture) name := f.addMedia(file, ext) - rID := f.addSheetRelationships(sheet, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") + sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") f.addSheetPicture(sheet, rID) f.setContentTypePartImageExtensions() return err @@ -413,7 +383,7 @@ func (f *File) DeleteSheet(name string) { } sheetName := trimSheetName(name) wb := f.workbookReader() - wbRels := f.workbookRelsReader() + wbRels := f.relsReader("xl/_rels/workbook.xml.rels") for idx, sheet := range wb.Sheets.Sheet { if sheet.Name == sheetName { wb.Sheets.Sheet = append(wb.Sheets.Sheet[:idx], wb.Sheets.Sheet[idx+1:]...) @@ -443,7 +413,7 @@ func (f *File) DeleteSheet(name string) { // relationships by given relationships ID in the file // xl/_rels/workbook.xml.rels. func (f *File) deleteSheetFromWorkbookRels(rID string) string { - content := f.workbookRelsReader() + content := f.relsReader("xl/_rels/workbook.xml.rels") for k, v := range content.Relationships { if v.ID == rID { content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...) @@ -1387,29 +1357,18 @@ func (f *File) UngroupSheets() error { return nil } -// workSheetRelsReader provides a function to get the pointer to the structure +// relsReader provides a function to get the pointer to the structure // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. -func (f *File) workSheetRelsReader(path string) *xlsxWorkbookRels { - if f.WorkSheetRels[path] == nil { +func (f *File) relsReader(path string) *xlsxRelationships { + if f.Relationships[path] == nil { _, ok := f.XLSX[path] if ok { - c := xlsxWorkbookRels{} + c := xlsxRelationships{} _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(path)), &c) - f.WorkSheetRels[path] = &c - } - } - return f.WorkSheetRels[path] -} - -// workSheetRelsWriter provides a function to save -// xl/worksheets/_rels/sheet%d.xml.rels after serialize structure. -func (f *File) workSheetRelsWriter() { - for p, r := range f.WorkSheetRels { - if r != nil { - v, _ := xml.Marshal(r) - f.saveFileList(p, v) + f.Relationships[path] = &c } } + return f.Relationships[path] } // fillSheetData ensures there are enough rows, and columns in the chosen diff --git a/table.go b/table.go index 45a1622..d26f8fd 100644 --- a/table.go +++ b/table.go @@ -77,7 +77,9 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error { sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml" tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1) // Add first table for given sheet. - rID := f.addSheetRelationships(sheet, SourceRelationshipTable, sheetRelationshipsTableXML, "") + sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "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 { diff --git a/xmlDrawing.go b/xmlDrawing.go index ade6261..4338c5e 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -22,6 +22,8 @@ const ( SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" + SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" + SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject" SourceRelationshipChart201506 = "http://schemas.microsoft.com/office/drawing/2015/06/chart" SourceRelationshipChart20070802 = "http://schemas.microsoft.com/office/drawing/2007/8/2/chart" diff --git a/xmlPivotCache.go b/xmlPivotCache.go index 9e07931..0c00832 100644 --- a/xmlPivotCache.go +++ b/xmlPivotCache.go @@ -2,11 +2,11 @@ package excelize import "encoding/xml" -// pivotCacheDefinition represents the pivotCacheDefinition part. This part +// xlsxPivotCacheDefinition represents the pivotCacheDefinition part. This part // defines each field in the source data, including the name, the string // resources of the instance data (for shared items), and information about // the type of data that appears in the field. -type xmlPivotCacheDefinition struct { +type xlsxPivotCacheDefinition struct { XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotCacheDefinition"` RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"` Invalid bool `xml:"invalid,attr,omitempty"` diff --git a/xmlPivotTable.go b/xmlPivotTable.go index 16c469f..6f2a8e7 100644 --- a/xmlPivotTable.go +++ b/xmlPivotTable.go @@ -15,78 +15,84 @@ import "encoding/xml" // non-null PivotTables. There exists one pivotTableDefinition for each // PivotTableDefinition part type xlsxPivotTableDefinition struct { - XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotTableDefinition"` - Name string `xml:"name,attr"` - CacheID int `xml:"cacheId,attr"` - DataOnRows bool `xml:"dataOnRows,attr"` - DataPosition int `xml:"dataPosition,attr"` - DataCaption string `xml:"dataCaption,attr"` - GrandTotalCaption string `xml:"grandTotalCaption,attr"` - ErrorCaption string `xml:"errorCaption,attr"` - ShowError bool `xml:"showError,attr"` - MissingCaption string `xml:"missingCaption,attr"` - ShowMissing bool `xml:"showMissing,attr"` - PageStyle string `xml:"pageStyle,attr"` - PivotTableStyle string `xml:"pivotTableStyle,attr"` - VacatedStyle string `xml:"vacatedStyle,attr"` - Tag string `xml:"tag,attr"` - UpdatedVersion int `xml:"updatedVersion,attr"` - MinRefreshableVersion int `xml:"minRefreshableVersion,attr"` - AsteriskTotals bool `xml:"asteriskTotals,attr"` - ShowItems bool `xml:"showItems,attr"` - EditData bool `xml:"editData,attr"` - DisableFieldList bool `xml:"disableFieldList,attr"` - ShowCalcMbrs bool `xml:"showCalcMbrs,attr"` - VisualTotals bool `xml:"visualTotals,attr"` - ShowMultipleLabel bool `xml:"showMultipleLabel,attr"` - ShowDataDropDown bool `xml:"showDataDropDown,attr"` - ShowDrill bool `xml:"showDrill,attr"` - PrintDrill bool `xml:"printDrill,attr"` - ShowMemberPropertyTips bool `xml:"showMemberPropertyTips,attr"` - ShowDataTips bool `xml:"showDataTips,attr"` - EnableWizard bool `xml:"enableWizard,attr"` - EnableDrill bool `xml:"enableDrill,attr"` - EnableFieldProperties bool `xml:"enableFieldProperties,attr"` - PreserveFormatting bool `xml:"preserveFormatting,attr"` - UseAutoFormatting bool `xml:"useAutoFormatting,attr"` - PageWrap int `xml:"pageWrap,attr"` - PageOverThenDown bool `xml:"pageOverThenDown,attr"` - SubtotalHiddenItems bool `xml:"subtotalHiddenItems,attr"` - RowGrandTotals bool `xml:"rowGrandTotals,attr"` - ColGrandTotals bool `xml:"colGrandTotals,attr"` - FieldPrintTitles bool `xml:"fieldPrintTitles,attr"` - ItemPrintTitles bool `xml:"itemPrintTitles,attr"` - MergeItem bool `xml:"mergeItem,attr"` - ShowDropZones bool `xml:"showDropZones,attr"` - CreatedVersion int `xml:"createdVersion,attr"` - Indent int `xml:"indent,attr"` - ShowEmptyRow bool `xml:"showEmptyRow,attr"` - ShowEmptyCol bool `xml:"showEmptyCol,attr"` - ShowHeaders bool `xml:"showHeaders,attr"` - Compact bool `xml:"compact,attr"` - Outline bool `xml:"outline,attr"` - OutlineData bool `xml:"outlineData,attr"` - CompactData bool `xml:"compactData,attr"` - Published bool `xml:"published,attr"` - GridDropZones bool `xml:"gridDropZones,attr"` - Immersive bool `xml:"immersive,attr"` - MultipleFieldFilters bool `xml:"multipleFieldFilters,attr"` - ChartFormat int `xml:"chartFormat,attr"` - RowHeaderCaption string `xml:"rowHeaderCaption,attr"` - ColHeaderCaption string `xml:"colHeaderCaption,attr"` - FieldListSortAscending bool `xml:"fieldListSortAscending,attr"` - MdxSubqueries bool `xml:"mdxSubqueries,attr"` - CustomListSort bool `xml:"customListSort,attr"` - Location *xlsxLocation `xml:"location"` - PivotFields *xlsxPivotFields `xml:"pivotFields"` - RowFields *xlsxRowFields `xml:"rowFields"` - RowItems *xlsxRowItems `xml:"rowItems"` - ColFields *xlsxColFields `xml:"colFields"` - ColItems *xlsxColItems `xml:"colItems"` - PageFields *xlsxPageFields `xml:"pageFields"` - DataFields *xlsxDataFields `xml:"dataFields"` - ConditionalFormats *xlsxConditionalFormats `xml:"conditionalFormats"` - PivotTableStyleInfo *xlsxPivotTableStyleInfo `xml:"pivotTableStyleInfo"` + XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main pivotTableDefinition"` + Name string `xml:"name,attr"` + CacheID int `xml:"cacheId,attr"` + ApplyNumberFormats bool `xml:"applyNumberFormats,attr,omitempty"` + ApplyBorderFormats bool `xml:"applyBorderFormats,attr,omitempty"` + ApplyFontFormats bool `xml:"applyFontFormats,attr,omitempty"` + ApplyPatternFormats bool `xml:"applyPatternFormats,attr,omitempty"` + ApplyAlignmentFormats bool `xml:"applyAlignmentFormats,attr,omitempty"` + ApplyWidthHeightFormats bool `xml:"applyWidthHeightFormats,attr,omitempty"` + DataOnRows bool `xml:"dataOnRows,attr,omitempty"` + DataPosition int `xml:"dataPosition,attr,omitempty"` + DataCaption string `xml:"dataCaption,attr"` + GrandTotalCaption string `xml:"grandTotalCaption,attr,omitempty"` + ErrorCaption string `xml:"errorCaption,attr,omitempty"` + ShowError bool `xml:"showError,attr,omitempty"` + MissingCaption string `xml:"missingCaption,attr,omitempty"` + ShowMissing bool `xml:"showMissing,attr,omitempty"` + PageStyle string `xml:"pageStyle,attr,omitempty"` + PivotTableStyle string `xml:"pivotTableStyle,attr,omitempty"` + VacatedStyle string `xml:"vacatedStyle,attr,omitempty"` + Tag string `xml:"tag,attr,omitempty"` + UpdatedVersion int `xml:"updatedVersion,attr"` + MinRefreshableVersion int `xml:"minRefreshableVersion,attr"` + AsteriskTotals bool `xml:"asteriskTotals,attr,omitempty"` + ShowItems bool `xml:"showItems,attr,omitempty"` + EditData bool `xml:"editData,attr,omitempty"` + DisableFieldList bool `xml:"disableFieldList,attr,omitempty"` + ShowCalcMbrs bool `xml:"showCalcMbrs,attr,omitempty"` + VisualTotals bool `xml:"visualTotals,attr,omitempty"` + ShowMultipleLabel bool `xml:"showMultipleLabel,attr,omitempty"` + ShowDataDropDown bool `xml:"showDataDropDown,attr,omitempty"` + ShowDrill bool `xml:"showDrill,attr,omitempty"` + PrintDrill bool `xml:"printDrill,attr,omitempty"` + ShowMemberPropertyTips bool `xml:"showMemberPropertyTips,attr,omitempty"` + ShowDataTips bool `xml:"showDataTips,attr,omitempty"` + EnableWizard bool `xml:"enableWizard,attr,omitempty"` + EnableDrill bool `xml:"enableDrill,attr,omitempty"` + EnableFieldProperties bool `xml:"enableFieldProperties,attr,omitempty"` + PreserveFormatting bool `xml:"preserveFormatting,attr,omitempty"` + UseAutoFormatting bool `xml:"useAutoFormatting,attr"` + PageWrap int `xml:"pageWrap,attr,omitempty"` + PageOverThenDown bool `xml:"pageOverThenDown,attr,omitempty"` + SubtotalHiddenItems bool `xml:"subtotalHiddenItems,attr,omitempty"` + RowGrandTotals bool `xml:"rowGrandTotals,attr,omitempty"` + ColGrandTotals bool `xml:"colGrandTotals,attr,omitempty"` + FieldPrintTitles bool `xml:"fieldPrintTitles,attr,omitempty"` + ItemPrintTitles bool `xml:"itemPrintTitles,attr"` + MergeItem bool `xml:"mergeItem,attr,omitempty"` + ShowDropZones bool `xml:"showDropZones,attr,omitempty"` + CreatedVersion int `xml:"createdVersion,attr"` + Indent int `xml:"indent,attr,omitempty"` + ShowEmptyRow bool `xml:"showEmptyRow,attr,omitempty"` + ShowEmptyCol bool `xml:"showEmptyCol,attr,omitempty"` + ShowHeaders bool `xml:"showHeaders,attr,omitempty"` + Compact bool `xml:"compact,attr,omitempty"` + Outline bool `xml:"outline,attr,omitempty"` + OutlineData bool `xml:"outlineData,attr,omitempty"` + CompactData bool `xml:"compactData,attr,omitempty"` + Published bool `xml:"published,attr,omitempty"` + GridDropZones bool `xml:"gridDropZones,attr"` + Immersive bool `xml:"immersive,attr,omitempty"` + MultipleFieldFilters bool `xml:"multipleFieldFilters,attr,omitempty"` + ChartFormat int `xml:"chartFormat,attr,omitempty"` + RowHeaderCaption string `xml:"rowHeaderCaption,attr,omitempty"` + ColHeaderCaption string `xml:"colHeaderCaption,attr,omitempty"` + FieldListSortAscending bool `xml:"fieldListSortAscending,attr,omitempty"` + MdxSubqueries bool `xml:"mdxSubqueries,attr,omitempty"` + CustomListSort bool `xml:"customListSort,attr,omitempty"` + Location *xlsxLocation `xml:"location"` + PivotFields *xlsxPivotFields `xml:"pivotFields"` + RowFields *xlsxRowFields `xml:"rowFields"` + RowItems *xlsxRowItems `xml:"rowItems"` + ColFields *xlsxColFields `xml:"colFields"` + ColItems *xlsxColItems `xml:"colItems"` + PageFields *xlsxPageFields `xml:"pageFields"` + DataFields *xlsxDataFields `xml:"dataFields"` + ConditionalFormats *xlsxConditionalFormats `xml:"conditionalFormats"` + PivotTableStyleInfo *xlsxPivotTableStyleInfo `xml:"pivotTableStyleInfo"` } // xlsxLocation represents location information for the PivotTable. diff --git a/xmlWorkbook.go b/xmlWorkbook.go index 8150e29..765563b 100644 --- a/xmlWorkbook.go +++ b/xmlWorkbook.go @@ -11,14 +11,14 @@ package excelize import "encoding/xml" -// xmlxWorkbookRels contains xmlxWorkbookRelations which maps sheet id and sheet XML. -type xlsxWorkbookRels struct { - XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"` - Relationships []xlsxWorkbookRelation `xml:"Relationship"` +// xlsxRelationships describe references from parts to other internal resources in the package or to external resources. +type xlsxRelationships struct { + XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"` + Relationships []xlsxRelationship `xml:"Relationship"` } -// xmlxWorkbookRelation maps sheet id and xl/worksheets/_rels/sheet%d.xml.rels -type xlsxWorkbookRelation struct { +// xlsxRelationship contains relations which maps id and XML. +type xlsxRelationship struct { ID string `xml:"Id,attr"` Target string `xml:",attr"` Type string `xml:",attr"`