diff --git a/_examples/spreadsheet/comments/comments.xlsx b/_examples/spreadsheet/comments/comments.xlsx
new file mode 100644
index 00000000..01f2d0db
Binary files /dev/null and b/_examples/spreadsheet/comments/comments.xlsx differ
diff --git a/_examples/spreadsheet/comments/main.go b/_examples/spreadsheet/comments/main.go
new file mode 100644
index 00000000..83a892c3
--- /dev/null
+++ b/_examples/spreadsheet/comments/main.go
@@ -0,0 +1,23 @@
+// Copyright 2017 Baliance. All rights reserved.
+package main
+
+import (
+ "log"
+
+ "baliance.com/gooxml/spreadsheet"
+)
+
+func main() {
+ ss := spreadsheet.New()
+ sheet := ss.AddSheet()
+
+ sheet.Cell("A1").SetString("Hello World!")
+ sheet.Comments().AddCommentWithStyle("A1", "Gopher", "This looks interesting.")
+ sheet.Comments().AddCommentWithStyle("C10", "Gopher", "This is a different comment.")
+
+ if err := ss.Validate(); err != nil {
+ log.Fatalf("error validating sheet: %s", err)
+ }
+
+ ss.SaveToFile("comments.xlsx")
+}
diff --git a/creator_test.go b/creator_test.go
index e501a55d..1a259597 100644
--- a/creator_test.go
+++ b/creator_test.go
@@ -69,8 +69,7 @@ func TestRawEncode(t *testing.T) {
end := strings.LastIndex(xmlStr, "")
gotRaw := xmlStr[beg+20 : end]
-
- exp := ""
+ exp := ""
if gotRaw != exp {
t.Errorf("expected\n%q\ngot\n%q\n", exp, gotRaw)
}
diff --git a/document/document.go b/document/document.go
index 00ea6c0b..1632c45d 100644
--- a/document/document.go
+++ b/document/document.go
@@ -329,9 +329,11 @@ func Read(r io.ReaderAt, size int64) (*Document, error) {
decMap := zippkg.DecodeMap{}
decMap.SetOnNewRelationshipFunc(doc.onNewRelationship)
// we should discover all contents by starting with these two files
- decMap.AddTarget(gooxml.ContentTypesFilename, doc.ContentTypes.X())
- decMap.AddTarget(gooxml.BaseRelsFilename, doc.Rels.X())
- decMap.Decode(files)
+ decMap.AddTarget(zippkg.Target{Path: gooxml.ContentTypesFilename, Ifc: doc.ContentTypes.X()})
+ decMap.AddTarget(zippkg.Target{Path: gooxml.BaseRelsFilename, Ifc: doc.Rels.X()})
+ if err := decMap.Decode(files); err != nil {
+ return nil, err
+ }
for _, f := range files {
if f == nil {
@@ -467,23 +469,23 @@ func (d *Document) FormFields() []FormField {
return ret
}
-func (doc *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship) error {
+func (doc *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship, src zippkg.Target) error {
dt := gooxml.DocTypeDocument
switch typ {
case gooxml.OfficeDocumentType:
doc.x = wml.NewDocument()
- decMap.AddTarget(target, doc.x)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.x})
// look for the document relationships file as well
- decMap.AddTarget(zippkg.RelationsPathFor(target), doc.docRels.X())
+ decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: doc.docRels.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.CorePropertiesType:
- decMap.AddTarget(target, doc.CoreProperties.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.CoreProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.ExtendedPropertiesType:
- decMap.AddTarget(target, doc.AppProperties.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.AppProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.ThumbnailType:
@@ -507,55 +509,55 @@ func (doc *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ str
}
case gooxml.SettingsType:
- decMap.AddTarget(target, doc.Settings.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.Settings.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.NumberingType:
doc.Numbering = NewNumbering()
- decMap.AddTarget(target, doc.Numbering.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.Numbering.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.StylesType:
doc.Styles.Clear()
- decMap.AddTarget(target, doc.Styles.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.Styles.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.HeaderType:
hdr := wml.NewHdr()
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: hdr, Index: uint32(len(doc.headers))})
doc.headers = append(doc.headers, hdr)
- decMap.AddTarget(target, hdr)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.headers))
case gooxml.FooterType:
ftr := wml.NewFtr()
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: ftr, Index: uint32(len(doc.footers))})
doc.footers = append(doc.footers, ftr)
- decMap.AddTarget(target, ftr)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.footers))
case gooxml.ThemeType:
thm := dml.NewTheme()
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: thm, Index: uint32(len(doc.themes))})
doc.themes = append(doc.themes, thm)
- decMap.AddTarget(target, thm)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.themes))
case gooxml.WebSettingsType:
doc.webSettings = wml.NewWebSettings()
- decMap.AddTarget(target, doc.webSettings)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.webSettings})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.FontTableType:
doc.fontTable = wml.NewFonts()
- decMap.AddTarget(target, doc.fontTable)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.fontTable})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.EndNotesType:
doc.endNotes = wml.NewEndnotes()
- decMap.AddTarget(target, doc.endNotes)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.endNotes})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.FootNotesType:
doc.footNotes = wml.NewFootnotes()
- decMap.AddTarget(target, doc.footNotes)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.footNotes})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.ImageType:
diff --git a/filenames.go b/filenames.go
index ce2a0ce8..1509b700 100644
--- a/filenames.go
+++ b/filenames.go
@@ -47,6 +47,11 @@ func RelativeFilename(dt DocType, typ string, index int) string {
return fmt.Sprintf("../charts/chart%d.xml", index)
case DrawingType, DrawingContentType:
return fmt.Sprintf("../drawings/drawing%d.xml", index)
+ case CommentsType, CommentsContentType:
+ return fmt.Sprintf("../comments%d.xml", index)
+
+ case VMLDrawingType, VMLDrawingContentType:
+ return fmt.Sprintf("../drawings/vmlDrawing%d.vml", index)
case ThemeType, ThemeContentType:
return fmt.Sprintf("theme/theme%d.xml", index)
@@ -128,6 +133,8 @@ func AbsoluteFilename(dt DocType, typ string, index int) string {
return "xl/styles.xml"
case DocTypeDocument:
return "word/styles.xml"
+ default:
+ log.Printf("unsupported type %s pair and %v", typ, dt)
}
case ChartType, ChartContentType:
@@ -145,6 +152,23 @@ func AbsoluteFilename(dt DocType, typ string, index int) string {
default:
log.Printf("unsupported type %s pair and %v", typ, dt)
}
+
+ case CommentsType, CommentsContentType:
+ switch dt {
+ case DocTypeSpreadsheet:
+ return fmt.Sprintf("xl/comments%d.xml", index)
+ default:
+ log.Printf("unsupported type %s pair and %v", typ, dt)
+ }
+
+ case VMLDrawingType, VMLDrawingContentType:
+ switch dt {
+ case DocTypeSpreadsheet:
+ return fmt.Sprintf("xl/drawings/vmlDrawing%d.vml", index)
+ default:
+ log.Fatalf("unsupported type %s pair and %v", typ, dt)
+ }
+
// SML
case WorksheetType, WorksheetContentType:
return fmt.Sprintf("xl/worksheets/sheet%d.xml", index)
diff --git a/optional.go b/optional.go
index 354ae19a..4d691581 100644
--- a/optional.go
+++ b/optional.go
@@ -9,6 +9,12 @@ package gooxml
import "fmt"
+// Float32 returns a copy of v as a pointer.
+func Float32(v float32) *float32 {
+ x := v
+ return &x
+}
+
// Float64 returns a copy of v as a pointer.
func Float64(v float64) *float64 {
x := v
diff --git a/schemas.go b/schemas.go
index 38ad30dd..a7605919 100644
--- a/schemas.go
+++ b/schemas.go
@@ -10,20 +10,20 @@ package gooxml
// Consts for content types used throughout the package
const (
// Common
- OfficeDocumentType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
- StylesType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
- ThemeType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
- ThemeContentType = "application/vnd.openxmlformats-officedocument.theme+xml"
- SettingsType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"
- ImageType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
- CommentsType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
- ThumbnailType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"
- DrawingType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
- DrawingContentType = "application/vnd.openxmlformats-officedocument.drawing+xml"
- ChartType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
- ChartContentType = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
- HyperLinkType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
-
+ OfficeDocumentType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
+ StylesType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
+ ThemeType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
+ ThemeContentType = "application/vnd.openxmlformats-officedocument.theme+xml"
+ SettingsType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"
+ ImageType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
+ CommentsType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
+ CommentsContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
+ ThumbnailType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"
+ DrawingType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
+ DrawingContentType = "application/vnd.openxmlformats-officedocument.drawing+xml"
+ ChartType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
+ ChartContentType = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"
+ HyperLinkType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
ExtendedPropertiesType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"
CorePropertiesType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
@@ -50,4 +50,8 @@ const (
SlideMasterContentType = "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"
SlideLayoutType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout"
SlideLayoutContentType = "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"
+
+ // VML
+ VMLDrawingType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
+ VMLDrawingContentType = "application/vnd.openxmlformats-officedocument.vmlDrawing"
)
diff --git a/spreadsheet/comment.go b/spreadsheet/comment.go
new file mode 100644
index 00000000..1fd58116
--- /dev/null
+++ b/spreadsheet/comment.go
@@ -0,0 +1,50 @@
+// Copyright 2017 Baliance. All rights reserved.
+//
+// Use of this source code is governed by the terms of the Affero GNU General
+// Public License version 3.0 as published by the Free Software Foundation and
+// appearing in the file LICENSE included in the packaging of this file. A
+// commercial license can be purchased by contacting sales@baliance.com.
+
+package spreadsheet
+
+import sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
+
+// Comment is a single comment within a sheet.
+type Comment struct {
+ w *Workbook
+ x *sml.CT_Comment
+ cmts *sml.Comments
+}
+
+// X returns the inner wrapped XML type.
+func (c Comment) X() *sml.CT_Comment {
+ return c.x
+}
+
+// CellReference returns the cell reference within a sheet that a comment refers
+// to (e.g. "A1")
+func (c Comment) CellReference() string {
+ return c.x.RefAttr
+}
+
+// SetCellReference sets the cell reference within a sheet that a comment refers
+// to (e.g. "A1")
+func (c Comment) SetCellReference(cellRef string) {
+ c.x.RefAttr = cellRef
+}
+
+// Author returns the author of the comment
+func (c Comment) Author() string {
+ if c.x.AuthorIdAttr < uint32(len(c.cmts.Authors.Author)) {
+ return c.cmts.Authors.Author[c.x.AuthorIdAttr]
+ }
+ return ""
+}
+
+// SetAuthor sets the author of the comment. If the comment body contains the
+// author's name (as is the case with Excel and Comments.AddCommentWithStyle, it
+// will not be changed). This method only changes the metadata author of the
+// comment.
+func (c Comment) SetAuthor(author string) {
+ c.x.AuthorIdAttr = Comments{c.w, c.cmts}.getOrCreateAuthor(author)
+}
diff --git a/spreadsheet/comments.go b/spreadsheet/comments.go
new file mode 100644
index 00000000..fb74f470
--- /dev/null
+++ b/spreadsheet/comments.go
@@ -0,0 +1,85 @@
+// Copyright 2017 Baliance. All rights reserved.
+//
+// Use of this source code is governed by the terms of the Affero GNU General
+// Public License version 3.0 as published by the Free Software Foundation and
+// appearing in the file LICENSE included in the packaging of this file. A
+// commercial license can be purchased by contacting sales@baliance.com.
+
+package spreadsheet
+
+import (
+ "baliance.com/gooxml/color"
+ sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
+ "baliance.com/gooxml/vmldrawing"
+)
+
+// Comments is the container for comments for a single sheet.
+type Comments struct {
+ w *Workbook
+ x *sml.Comments
+}
+
+// MakeComments constructs a new Comments wrapper.
+func MakeComments(w *Workbook, x *sml.Comments) Comments {
+ return Comments{w, x}
+}
+
+// X returns the inner wrapped XML type.
+func (c Comments) X() *sml.Comments {
+ return c.x
+}
+
+// Comments returns the list of comments for this sheet
+func (c Comments) Comments() []Comment {
+ ret := []Comment{}
+ for _, cmt := range c.x.CommentList.Comment {
+ ret = append(ret, Comment{c.w, cmt, c.x})
+ }
+ return ret
+}
+
+func (c Comments) getOrCreateAuthor(author string) uint32 {
+ for i, knownAuthor := range c.x.Authors.Author {
+ if knownAuthor == author {
+ return uint32(i)
+ }
+ }
+
+ // didn't find the author, so add a new one
+ authIdx := uint32(len(c.x.Authors.Author))
+ c.x.Authors.Author = append(c.x.Authors.Author, author)
+ return authIdx
+}
+
+// AddComment adds a new comment and returns a RichText which will contain the
+// styled comment text.
+func (c Comments) AddComment(cellRef string, author string) RichText {
+
+ cmt := sml.NewCT_Comment()
+ c.x.CommentList.Comment = append(c.x.CommentList.Comment, cmt)
+ cmt.RefAttr = cellRef
+ cmt.AuthorIdAttr = c.getOrCreateAuthor(author)
+ cmt.Text = sml.NewCT_Rst()
+ return RichText{cmt.Text}
+}
+
+// AddCommentWithStyle adds a new comment styled in a default way
+func (c Comments) AddCommentWithStyle(cellRef string, author string, comment string) {
+ rt := c.AddComment(cellRef, author)
+ run := rt.AddRun()
+ run.SetBold(true)
+ run.SetSize(10)
+ run.SetColor(color.Black)
+ run.SetFont("Calibri")
+ run.SetText(author + ":")
+
+ run = rt.AddRun()
+ run.SetSize(10)
+ run.SetFont("Calibri")
+ run.SetColor(color.Black)
+ run.SetText("\r\n" + comment + "\r\n")
+
+ col, rowIdx, _ := ParseCellReference(cellRef)
+ colIdx := ColumnToIndex(col)
+ c.w.vmlDrawings[0].Shape = append(c.w.vmlDrawings[0].Shape, vmldrawing.NewCommentShape(int64(colIdx), int64(rowIdx-1)))
+}
diff --git a/spreadsheet/comments_test.go b/spreadsheet/comments_test.go
new file mode 100644
index 00000000..a54f40e0
--- /dev/null
+++ b/spreadsheet/comments_test.go
@@ -0,0 +1,79 @@
+// Copyright 2017 Baliance. All rights reserved.
+//
+// Use of this source code is governed by the terms of the Affero GNU General
+// Public License version 3.0 as published by the Free Software Foundation and
+// appearing in the file LICENSE included in the packaging of this file. A
+// commercial license can be purchased by contacting sales@baliance.com.
+
+package spreadsheet_test
+
+import (
+ "testing"
+
+ "baliance.com/gooxml/spreadsheet"
+)
+
+func TestComments(t *testing.T) {
+ wb := spreadsheet.New()
+ sheet := wb.AddSheet()
+ c := sheet.Comments()
+
+ expRef := "A1"
+ expAuth := "John Doe"
+ c.AddCommentWithStyle(expRef, expAuth, "This is my comment")
+ if c.X().Authors == nil {
+ t.Fatalf("author should be non-nil")
+ }
+ if len(c.X().Authors.Author) != 1 {
+ t.Errorf("expected one author, got %v", c.X().Authors.Author)
+ }
+
+ auth := c.X().Authors.Author[0]
+ if auth != expAuth {
+ t.Errorf("expected author = %s, got %s", expAuth, auth)
+ }
+ if c.X().CommentList == nil {
+ t.Fatalf("commentlist should be non-nil")
+ }
+ if len(c.X().CommentList.Comment) != 1 {
+ t.Errorf("expected one comment, got %v", c.X().CommentList.Comment)
+ }
+ cmt := c.X().CommentList.Comment[0]
+ if cmt.AuthorIdAttr != 0 {
+ t.Errorf("expected author ID = 0, got %d", cmt.AuthorIdAttr)
+ }
+ if cmt.RefAttr != expRef {
+ t.Errorf("expected ref = %s, got %s", expRef, cmt.RefAttr)
+ }
+}
+
+func TestCommentsReusesAuthorIDs(t *testing.T) {
+ wb := spreadsheet.New()
+ sheet := wb.AddSheet()
+ c := sheet.Comments()
+
+ c.AddCommentWithStyle("A1", "foo", "This is my comment")
+ if c.X().Authors == nil {
+ t.Fatalf("author should be non-nil")
+ }
+ if len(c.X().Authors.Author) != 1 {
+ t.Errorf("expected one author, got %v", c.X().Authors.Author)
+ }
+
+ c.AddCommentWithStyle("B1", "foo", "This is another comment")
+ if c.X().Authors == nil {
+ t.Fatalf("author should be non-nil")
+ }
+ if len(c.X().Authors.Author) != 1 {
+ t.Errorf("expected one author, got %v", c.X().Authors.Author)
+ }
+
+ c.AddCommentWithStyle("C1", "bar", "This is the last comment")
+ if c.X().Authors == nil {
+ t.Fatalf("author should be non-nil")
+ }
+ if len(c.X().Authors.Author) != 2 {
+ t.Errorf("expected two authors, got %v", c.X().Authors.Author)
+ }
+
+}
diff --git a/spreadsheet/new.go b/spreadsheet/new.go
index f706986d..6a0b3b38 100644
--- a/spreadsheet/new.go
+++ b/spreadsheet/new.go
@@ -30,8 +30,9 @@ func New() *Workbook {
wb.wbRels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.StylesType, 0), gooxml.StylesType)
wb.ContentTypes = common.NewContentTypes()
+ wb.ContentTypes.AddDefault("vml", gooxml.VMLDrawingContentType)
wb.ContentTypes.AddOverride(gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, gooxml.OfficeDocumentType, 0), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml")
- wb.ContentTypes.AddOverride(gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, gooxml.StylesType, 0), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml")
+ wb.ContentTypes.AddOverride(gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, gooxml.StylesType, 0), gooxml.SMLStyleSheetContentType)
wb.SharedStrings = NewSharedStrings()
wb.ContentTypes.AddOverride(gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, gooxml.SharedStingsType, 0), gooxml.SharedStringsContentType)
diff --git a/spreadsheet/read.go b/spreadsheet/read.go
index 0e4b40a6..7f3c59a2 100644
--- a/spreadsheet/read.go
+++ b/spreadsheet/read.go
@@ -37,9 +37,11 @@ func Read(r io.ReaderAt, size int64) (*Workbook, error) {
decMap := zippkg.DecodeMap{}
decMap.SetOnNewRelationshipFunc(wb.onNewRelationship)
// we should discover all contents by starting with these two files
- decMap.AddTarget(gooxml.ContentTypesFilename, wb.ContentTypes.X())
- decMap.AddTarget(gooxml.BaseRelsFilename, wb.Rels.X())
- decMap.Decode(files)
+ decMap.AddTarget(zippkg.Target{Path: gooxml.ContentTypesFilename, Ifc: wb.ContentTypes.X()})
+ decMap.AddTarget(zippkg.Target{Path: gooxml.BaseRelsFilename, Ifc: wb.Rels.X()})
+ if err := decMap.Decode(files); err != nil {
+ return nil, err
+ }
// etra files are things we don't handle yet, or files that happened to have
// been in the zip before. We just round-trip them.
diff --git a/spreadsheet/sheet.go b/spreadsheet/sheet.go
index 0c91aafe..60a6515a 100644
--- a/spreadsheet/sheet.go
+++ b/spreadsheet/sheet.go
@@ -16,6 +16,7 @@ import (
"baliance.com/gooxml"
"baliance.com/gooxml/common"
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
+ "baliance.com/gooxml/vmldrawing"
)
// Sheet is a single sheet within a workbook.
@@ -374,3 +375,29 @@ func (s Sheet) Column(idx uint32) Column {
colSet.Col = append(colSet.Col, col)
return Column{col}
}
+
+// Comments returns the comments for a sheet.
+func (s Sheet) Comments() Comments {
+ for i, wks := range s.w.xws {
+ if wks == s.x {
+ if s.w.comments[i] == nil {
+ s.w.comments[i] = sml.NewComments()
+ s.w.xwsRels[i].AddAutoRelationship(gooxml.DocTypeSpreadsheet, i+1, gooxml.CommentsType)
+ s.w.ContentTypes.AddOverride(gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, gooxml.CommentsType, i+1), gooxml.CommentsContentType)
+ }
+ if len(s.w.vmlDrawings) == 0 {
+ s.w.vmlDrawings = append(s.w.vmlDrawings, vmldrawing.NewCommentDrawing())
+ vmlID := s.w.xwsRels[i].AddAutoRelationship(gooxml.DocTypeSpreadsheet, 1, gooxml.VMLDrawingType)
+ if s.x.LegacyDrawing == nil {
+ s.x.LegacyDrawing = sml.NewCT_LegacyDrawing()
+ }
+ s.x.LegacyDrawing.IdAttr = vmlID.ID()
+ }
+ return Comments{s.w, s.w.comments[i]}
+ }
+ }
+
+ log.Printf("attempted to access comments for non-existent sheet")
+ // should never occur
+ return Comments{}
+}
diff --git a/spreadsheet/testdata/comments.xlsx b/spreadsheet/testdata/comments.xlsx
new file mode 100644
index 00000000..add7c103
Binary files /dev/null and b/spreadsheet/testdata/comments.xlsx differ
diff --git a/spreadsheet/testdata/simple-1.xlsx b/spreadsheet/testdata/simple-1.xlsx
index 0612fa6c..b0e08128 100644
Binary files a/spreadsheet/testdata/simple-1.xlsx and b/spreadsheet/testdata/simple-1.xlsx differ
diff --git a/spreadsheet/testdata/simple-2.xlsx b/spreadsheet/testdata/simple-2.xlsx
index a4f449a7..e7713b0a 100644
Binary files a/spreadsheet/testdata/simple-2.xlsx and b/spreadsheet/testdata/simple-2.xlsx differ
diff --git a/spreadsheet/workbook.go b/spreadsheet/workbook.go
index 12fd4c4e..883c4c2e 100644
--- a/spreadsheet/workbook.go
+++ b/spreadsheet/workbook.go
@@ -19,6 +19,7 @@ import (
"baliance.com/gooxml"
"baliance.com/gooxml/common"
+ "baliance.com/gooxml/vmldrawing"
"baliance.com/gooxml/zippkg"
dml "baliance.com/gooxml/schema/schemas.openxmlformats.org/drawingml"
@@ -36,12 +37,14 @@ type Workbook struct {
StyleSheet StyleSheet
SharedStrings SharedStrings
+ comments []*sml.Comments
xws []*sml.Worksheet
xwsRels []common.Relationships
wbRels common.Relationships
themes []*dml.Theme
drawings []*sd.WsDr
drawingRels []common.Relationships
+ vmlDrawings []*vmldrawing.Container
charts []*crt.ChartSpace
}
@@ -71,9 +74,12 @@ func (wb *Workbook) AddSheet() Sheet {
ws.Dimension.RefAttr = "A1"
wb.xws = append(wb.xws, ws)
wsRel := common.NewRelationships()
+
wb.xwsRels = append(wb.xwsRels, wsRel)
ws.SheetData = sml.NewCT_SheetData()
+ wb.comments = append(wb.comments, nil)
+
dt := gooxml.DocTypeSpreadsheet
// update the references
rid := wb.wbRels.AddAutoRelationship(dt, len(wb.x.Sheets.Sheet), gooxml.WorksheetType)
@@ -176,9 +182,20 @@ func (wb *Workbook) Save(w io.Writer) error {
zippkg.MarshalXML(z, zippkg.RelationsPathFor(fn), wb.drawingRels[i].X())
}
}
+ for i, drawing := range wb.vmlDrawings {
+ zippkg.MarshalXML(z, gooxml.AbsoluteFilename(dt, gooxml.VMLDrawingType, i+1), drawing)
+ // never seen relationships for a VML drawing yet
+ }
+
if err := zippkg.MarshalXML(z, gooxml.ContentTypesFilename, wb.ContentTypes.X()); err != nil {
return err
}
+ for i, cmt := range wb.comments {
+ if cmt == nil {
+ continue
+ }
+ zippkg.MarshalXML(z, gooxml.AbsoluteFilename(dt, gooxml.CommentsType, i+1), cmt)
+ }
if err := wb.WriteExtraFiles(z); err != nil {
return err
@@ -237,52 +254,58 @@ func (wb Workbook) SheetCount() int {
return len(wb.xws)
}
-func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship) error {
+func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship, src zippkg.Target) error {
dt := gooxml.DocTypeSpreadsheet
switch typ {
case gooxml.OfficeDocumentType:
wb.x = sml.NewWorkbook()
- decMap.AddTarget(target, wb.x)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.x})
// look for the workbook relationships file as well
wb.wbRels = common.NewRelationships()
- decMap.AddTarget(zippkg.RelationsPathFor(target), wb.wbRels.X())
+ decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: wb.wbRels.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.CorePropertiesType:
- decMap.AddTarget(target, wb.CoreProperties.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.CoreProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.ExtendedPropertiesType:
- decMap.AddTarget(target, wb.AppProperties.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.AppProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.WorksheetType:
ws := sml.NewWorksheet()
+ idx := uint32(len(wb.xws))
wb.xws = append(wb.xws, ws)
- decMap.AddTarget(target, ws)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: ws, Index: idx})
// look for worksheet rels
wksRel := common.NewRelationships()
- decMap.AddTarget(zippkg.RelationsPathFor(target), wksRel.X())
+ decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: wksRel.X(), Index: idx})
wb.xwsRels = append(wb.xwsRels, wksRel)
+
+ // add a comments placeholder that will be replaced if we see a comments
+ // relationship for the current sheet
+ wb.comments = append(wb.comments, nil)
+
// fix the relationship target so it points to where we'll save
// the worksheet
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.xws))
case gooxml.StylesType:
wb.StyleSheet = NewStyleSheet(wb)
- decMap.AddTarget(target, wb.StyleSheet.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.StyleSheet.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.ThemeType:
thm := dml.NewTheme()
wb.themes = append(wb.themes, thm)
- decMap.AddTarget(target, thm)
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: thm})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.themes))
case gooxml.SharedStingsType:
wb.SharedStrings = NewSharedStrings()
- decMap.AddTarget(target, wb.SharedStrings.X())
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.SharedStrings.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
case gooxml.ThumbnailType:
@@ -307,17 +330,30 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
case gooxml.DrawingType:
drawing := sd.NewWsDr()
- decMap.AddTarget(target, drawing)
+ idx := uint32(len(wb.drawings))
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: drawing, Index: idx})
wb.drawings = append(wb.drawings, drawing)
drel := common.NewRelationships()
- decMap.AddTarget(zippkg.RelationsPathFor(target), drel.X())
+ decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: drel.X(), Index: idx})
wb.drawingRels = append(wb.drawingRels, drel)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.drawings))
+ case gooxml.VMLDrawingType:
+ vd := vmldrawing.NewContainer()
+ idx := uint32(len(wb.vmlDrawings))
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: vd, Index: idx})
+ wb.vmlDrawings = append(wb.vmlDrawings, vd)
+
+ case gooxml.CommentsType:
+ wb.comments[src.Index] = sml.NewComments()
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.comments[src.Index], Index: src.Index})
+ rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.comments))
+
case gooxml.ChartType:
chart := crt.NewChartSpace()
- decMap.AddTarget(target, chart)
+ idx := uint32(len(wb.charts))
+ decMap.AddTarget(zippkg.Target{Path: target, Ifc: chart, Index: idx})
wb.charts = append(wb.charts, chart)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
diff --git a/spreadsheet/workbook_test.go b/spreadsheet/workbook_test.go
index 178d578d..c0211be6 100644
--- a/spreadsheet/workbook_test.go
+++ b/spreadsheet/workbook_test.go
@@ -208,6 +208,7 @@ func TestAddDefinedName(t *testing.T) {
t.Errorf("expected content = %s, got %s", ref, dn.Content())
}
}
+
func ExampleWorkbook_AddDefinedName() {
wb := spreadsheet.New()
sheet := wb.AddSheet()
@@ -216,3 +217,19 @@ func ExampleWorkbook_AddDefinedName() {
fmt.Printf("%s refers to %s", productNames.Name(), productNames.Content())
// Output: ProductNames refers to 'Sheet 1'!$A$2:$A$6
}
+
+func TestOpenComments(t *testing.T) {
+ wb, err := spreadsheet.Open("./testdata/comments.xlsx")
+ if err != nil {
+ t.Fatalf("error opening workbook: %s", err)
+ }
+
+ sheet := wb.Sheets()[0]
+ if len(sheet.Comments().Comments()) != 1 {
+ t.Fatalf("sheet should have returned 1 existing comments")
+ }
+ cmt := sheet.Comments().Comments()[0]
+ if cmt.Author() != "John Doe" {
+ t.Errorf("error reading comment author")
+ }
+}
diff --git a/testhelper/compare.go b/testhelper/compare.go
index 1cad5c46..955a9e51 100644
--- a/testhelper/compare.go
+++ b/testhelper/compare.go
@@ -13,6 +13,7 @@ import (
"bytes"
"encoding/xml"
"flag"
+ "fmt"
"io/ioutil"
"log"
"os"
@@ -139,6 +140,8 @@ func compareFiles(exp, got *zip.File) func(t *testing.T) {
gotAll, _ := ioutil.ReadAll(gf)
if !bytes.Equal(expAll, gotAll) {
dumpXmlDiff(t, expAll, gotAll)
+ fmt.Println(string(expAll))
+ fmt.Println(string(gotAll))
t.Errorf("mismatched contents %d vs %d", len(expAll), len(gotAll))
}
@@ -181,19 +184,19 @@ func dumpXmlDiff(t *testing.T, exp, got []byte) {
xmlIndentFile(expF)
xmlIndentFile(gotF)
- a := exec.Command("diff", "-u", expF, gotF)
- outp, err := a.StdoutPipe()
+ diff := exec.Command("diff", "-u", expF, gotF)
+ outp, err := diff.StdoutPipe()
if err != nil {
t.Fatalf("error running xmlindent: %s", err)
}
defer outp.Close()
- errp, err := a.StderrPipe()
+ errp, err := diff.StderrPipe()
if err != nil {
t.Fatalf("error running xmlindent: %s", err)
}
defer errp.Close()
- if err := a.Start(); err != nil {
+ if err := diff.Start(); err != nil {
t.Fatalf("error string xmlindent: %s", err)
}
scanner := bufio.NewScanner(outp)
@@ -201,7 +204,7 @@ func dumpXmlDiff(t *testing.T, exp, got []byte) {
log.Println(scanner.Text())
}
- if err := a.Wait(); err != nil {
+ if err := diff.Wait(); err != nil {
errOutput, _ := ioutil.ReadAll(errp)
t.Fatalf("error waiting on xmlindent: %s [%s]", string(errOutput), err)
}
diff --git a/vmldrawing/commentdrawing.go b/vmldrawing/commentdrawing.go
new file mode 100644
index 00000000..c87944f0
--- /dev/null
+++ b/vmldrawing/commentdrawing.go
@@ -0,0 +1,96 @@
+// Copyright 2017 Baliance. All rights reserved.
+//
+// Use of this source code is governed by the terms of the Affero GNU General
+// Public License version 3.0 as published by the Free Software Foundation and
+// appearing in the file LICENSE included in the packaging of this file. A
+// commercial license can be purchased by contacting sales@baliance.com.
+
+package vmldrawing
+
+import (
+ "fmt"
+
+ "baliance.com/gooxml"
+ "baliance.com/gooxml/schema/urn/schemas_microsoft_com/office/excel"
+
+ st "baliance.com/gooxml/schema/schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
+ vml "baliance.com/gooxml/schema/urn/schemas_microsoft_com/vml"
+)
+
+// NewCommentDrawing constructs a new comment drawing.
+func NewCommentDrawing() *Container {
+ c := NewContainer()
+ c.Layout = vml.NewOfcShapelayout()
+ c.Layout.ExtAttr = vml.ST_ExtEdit
+ c.Layout.Idmap = vml.NewOfcCT_IdMap()
+ c.Layout.Idmap.DataAttr = gooxml.String("1")
+ c.Layout.Idmap.ExtAttr = vml.ST_ExtEdit
+
+ c.ShapeType = vml.NewShapetype()
+ c.ShapeType.IdAttr = gooxml.String("_x0000_t202")
+ c.ShapeType.CoordsizeAttr = gooxml.String("21600,21600")
+ c.ShapeType.SptAttr = gooxml.Float32(202)
+ c.ShapeType.PathAttr = gooxml.String("m0,0l0,21600,21600,21600,21600,0xe")
+
+ se := vml.NewEG_ShapeElements()
+ c.ShapeType.EG_ShapeElements = append(c.ShapeType.EG_ShapeElements, se)
+ se.Path = vml.NewPath()
+ se.Path.GradientshapeokAttr = st.ST_TrueFalseT
+ se.Path.ConnecttypeAttr = vml.OfcST_ConnectTypeRect
+
+ return c
+}
+
+// NewCommentShape creates a new comment shape for a given cell index. The
+// indices here are zero based.
+func NewCommentShape(col, row int64) *vml.Shape {
+ shape := vml.NewShape()
+ shape.IdAttr = gooxml.String(fmt.Sprintf("cs_%d_%d", col, row))
+ shape.TypeAttr = gooxml.String("#_x0000_t202")
+ // visibility of the comment box is controlled by this visibility style
+ shape.StyleAttr = gooxml.String("position:absolute;margin-left:80pt;margin-top:2pt;width:104pt;height:76pt;z-index:1;visibility:hidden")
+ shape.FillcolorAttr = gooxml.String("#fbf6d6")
+ shape.StrokecolorAttr = gooxml.String("#edeaa1")
+ //shape.InsetmodeAttr = vml.OfcST_InsetModeAuto
+
+ fill := vml.NewEG_ShapeElements()
+ fill.Fill = vml.NewFill()
+ fill.Fill.Color2Attr = gooxml.String("#fbfe82")
+ fill.Fill.AngleAttr = gooxml.Float64(-180)
+ fill.Fill.TypeAttr = vml.ST_FillTypeGradient
+ fill.Fill.Fill = vml.NewOfcFill()
+ fill.Fill.Fill.ExtAttr = vml.ST_ExtView
+ fill.Fill.Fill.TypeAttr = vml.OfcST_FillTypeGradientUnscaled
+
+ shape.EG_ShapeElements = append(shape.EG_ShapeElements, fill)
+
+ shadow := vml.NewEG_ShapeElements()
+ shadow.Shadow = vml.NewShadow()
+ shadow.Shadow.OnAttr = st.ST_TrueFalseT
+ shadow.Shadow.ObscuredAttr = st.ST_TrueFalseT
+ shape.EG_ShapeElements = append(shape.EG_ShapeElements, shadow)
+
+ fpath := vml.NewEG_ShapeElements()
+ fpath.Path = vml.NewPath()
+ fpath.Path.ConnecttypeAttr = vml.OfcST_ConnectTypeNone
+ shape.EG_ShapeElements = append(shape.EG_ShapeElements, fpath)
+
+ tb := vml.NewEG_ShapeElements()
+ tb.Textbox = vml.NewTextbox()
+ tb.Textbox.StyleAttr = gooxml.String("mso-direction-alt:auto")
+ // TODO: add div?
+ shape.EG_ShapeElements = append(shape.EG_ShapeElements, tb)
+
+ cd := vml.NewEG_ShapeElements()
+ cd.ClientData = excel.NewClientData()
+ cd.ClientData.ObjectTypeAttr = excel.ST_ObjectTypeNote
+ cd.ClientData.MoveWithCells = st.ST_TrueFalseBlankT
+ cd.ClientData.SizeWithCells = st.ST_TrueFalseBlankT
+ cd.ClientData.Anchor = gooxml.String("1, 15, 0, 2, 2, 54, 5, 3")
+ cd.ClientData.AutoFill = st.ST_TrueFalseBlankFalse
+ cd.ClientData.Row = gooxml.Int64(row)
+ cd.ClientData.Column = gooxml.Int64(col)
+ shape.EG_ShapeElements = append(shape.EG_ShapeElements, cd)
+
+ return shape
+}
diff --git a/vmldrawing/container.go b/vmldrawing/container.go
new file mode 100644
index 00000000..36e063d7
--- /dev/null
+++ b/vmldrawing/container.go
@@ -0,0 +1,80 @@
+// Copyright 2017 Baliance. All rights reserved.
+//
+// Use of this source code is governed by the terms of the Affero GNU General
+// Public License version 3.0 as published by the Free Software Foundation and
+// appearing in the file LICENSE included in the packaging of this file. A
+// commercial license can be purchased by contacting sales@baliance.com.
+
+package vmldrawing
+
+import (
+ "encoding/xml"
+
+ "baliance.com/gooxml/schema/urn/schemas_microsoft_com/vml"
+)
+
+type Container struct {
+ Layout *vml.OfcShapelayout
+ ShapeType *vml.Shapetype
+ Shape []*vml.Shape
+}
+
+func NewContainer() *Container {
+ return &Container{}
+}
+
+func (c *Container) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:v"}, Value: "urn:schemas-microsoft-com:vml"})
+ start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:o"}, Value: "urn:schemas-microsoft-com:office:office"})
+ start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:x"}, Value: "urn:schemas-microsoft-com:office:excel"})
+ start.Name.Local = "xml"
+ e.EncodeToken(start)
+ if c.Layout != nil {
+ se := xml.StartElement{Name: xml.Name{Local: "o:shapelayout"}}
+ e.EncodeElement(c.Layout, se)
+ }
+ if c.ShapeType != nil {
+ se := xml.StartElement{Name: xml.Name{Local: "v:shapetype"}}
+ e.EncodeElement(c.ShapeType, se)
+ }
+ for _, s := range c.Shape {
+ se := xml.StartElement{Name: xml.Name{Local: "v:shape"}}
+ e.EncodeElement(s, se)
+ }
+ return e.EncodeToken(xml.EndElement{Name: start.Name})
+}
+
+func (c *Container) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ c.Shape = nil
+outer:
+ for {
+ tok, err := d.Token()
+ if err != nil {
+ return err
+ }
+ switch el := tok.(type) {
+ case xml.StartElement:
+ switch el.Name.Local {
+ case "shapelayout":
+ c.Layout = vml.NewOfcShapelayout()
+ if err := d.DecodeElement(c.Layout, &el); err != nil {
+ return err
+ }
+ case "shapetype":
+ c.ShapeType = vml.NewShapetype()
+ if err := d.DecodeElement(c.ShapeType, &el); err != nil {
+ return err
+ }
+ case "shape":
+ shp := vml.NewShape()
+ if err := d.DecodeElement(shp, &el); err != nil {
+ return err
+ }
+ c.Shape = append(c.Shape, shp)
+ }
+ case xml.EndElement:
+ break outer
+ }
+ }
+ return nil
+}
diff --git a/zippkg/decodemap.go b/zippkg/decodemap.go
index 54af8853..d48315a7 100644
--- a/zippkg/decodemap.go
+++ b/zippkg/decodemap.go
@@ -26,32 +26,38 @@ import (
// target doesn't match where gooxml will write the file (e.g. read in
// 'xl/worksheets/MyWorksheet.xml' and we'll write out
// 'xl/worksheets/sheet1.xml')
-type OnNewRelationshipFunc func(decMap *DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship) error
+type OnNewRelationshipFunc func(decMap *DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship, src Target) error
// DecodeMap is used to walk a tree of relationships, decoding files and passing
// control back to the document.
type DecodeMap struct {
- pathsToIfcs map[string]interface{}
+ pathsToIfcs map[string]Target
basePaths map[*relationships.Relationships]string
- rels []*relationships.Relationships
- decFunc OnNewRelationshipFunc
+ rels []Target
+ decodeFunc OnNewRelationshipFunc
}
// SetOnNewRelationshipFunc sets the function to be called when a new
// relationship has been discovered.
func (d *DecodeMap) SetOnNewRelationshipFunc(fn OnNewRelationshipFunc) {
- d.decFunc = fn
+ d.decodeFunc = fn
+}
+
+type Target struct {
+ Path string
+ Ifc interface{}
+ Index uint32
}
// AddTarget allows documents to register decode targets. Path is a path that
// will be found in the zip file and ifc is an XML element that the file will be
// unmarshaled to.
-func (d *DecodeMap) AddTarget(path string, ifc interface{}) {
+func (d *DecodeMap) AddTarget(tgt Target) {
if d.pathsToIfcs == nil {
- d.pathsToIfcs = make(map[string]interface{})
+ d.pathsToIfcs = make(map[string]Target)
d.basePaths = make(map[*relationships.Relationships]string)
}
- d.pathsToIfcs[filepath.Clean(path)] = ifc
+ d.pathsToIfcs[filepath.Clean(tgt.Path)] = tgt
}
// Decode loops decoding targets registered with AddTarget and calling th
@@ -62,11 +68,12 @@ func (d *DecodeMap) Decode(files []*zip.File) error {
// if we've loaded any relationships files, notify the document so it
// can create elements to receive the decoded version
for len(d.rels) > 0 {
- relFile := d.rels[len(d.rels)-1]
+ relSource := d.rels[len(d.rels)-1]
d.rels = d.rels[0 : len(d.rels)-1]
- for _, r := range relFile.Relationship {
- bp, _ := d.basePaths[relFile]
- d.decFunc(d, bp+r.TargetAttr, r.TypeAttr, files, r)
+ relRaw := relSource.Ifc.(*relationships.Relationships)
+ for _, r := range relRaw.Relationship {
+ bp, _ := d.basePaths[relRaw]
+ d.decodeFunc(d, bp+r.TargetAttr, r.TypeAttr, files, r, relSource)
}
}
@@ -74,19 +81,20 @@ func (d *DecodeMap) Decode(files []*zip.File) error {
if f == nil {
continue
}
+
// if there is a registered target for the file
if dest, ok := d.pathsToIfcs[f.Name]; ok {
delete(d.pathsToIfcs, f.Name)
// decode to the target and mark the file as nil so we'll skip
// it later
- if err := Decode(f, dest); err != nil {
+ if err := Decode(f, dest.Ifc); err != nil {
return err
}
files[i] = nil
// we decoded a relationships file, so we need to traverse it
- if drel, ok := dest.(*relationships.Relationships); ok {
- d.rels = append(d.rels, drel)
+ if drel, ok := dest.Ifc.(*relationships.Relationships); ok {
+ d.rels = append(d.rels, dest)
// find the path that any files mentioned in the
// relationships file are relative to
basePath, _ := filepath.Split(filepath.Clean(f.Name + "/../"))