gooxml: rework how filenames are calculated

This is needed for pivot tables, split out from that branch
so any further changes to filename handling will be easier in
master.
This commit is contained in:
Todd 2017-09-28 17:05:38 -05:00
parent 11edbae6be
commit 89b1416b8f
11 changed files with 177 additions and 188 deletions

13
algo/strings.go Normal file
View File

@ -0,0 +1,13 @@
package algo
func RepeatString(s string, cnt int) string {
if cnt <= 0 {
return ""
}
buf := make([]byte, len(s)*cnt)
sb := []byte(s)
for i := 0; i < cnt; i++ {
copy(buf[i:], sb)
}
return string(buf)
}

View File

@ -57,8 +57,8 @@ func (r Relationships) FindRIDForN(i int, t string) string {
// AddAutoRelationship adds a relationship with an automatically generated
// filename based off of the type. It should be preferred over AddRelationship
// to ensure consistent filenames are maintained.
func (r Relationships) AddAutoRelationship(dt gooxml.DocType, idx int, ctype string) Relationship {
return r.AddRelationship(gooxml.RelativeFilename(dt, ctype, idx), ctype)
func (r Relationships) AddAutoRelationship(dt gooxml.DocType, src string, idx int, ctype string) Relationship {
return r.AddRelationship(gooxml.RelativeFilename(dt, src, ctype, idx), ctype)
}
// AddRelationship adds a relationship.

View File

@ -67,7 +67,7 @@ func New() *Document {
d.ContentTypes.AddOverride("/word/settings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml")
d.Rels = common.NewRelationships()
d.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeDocument, gooxml.CorePropertiesType, 0), gooxml.CorePropertiesType)
d.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeDocument, "", gooxml.CorePropertiesType, 0), gooxml.CorePropertiesType)
d.Rels.AddRelationship("docProps/app.xml", gooxml.ExtendedPropertiesType)
d.Rels.AddRelationship("word/document.xml", gooxml.OfficeDocumentType)
@ -370,8 +370,8 @@ 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(zippkg.Target{Path: gooxml.ContentTypesFilename, Ifc: doc.ContentTypes.X()})
decMap.AddTarget(zippkg.Target{Path: gooxml.BaseRelsFilename, Ifc: doc.Rels.X()})
decMap.AddTarget(gooxml.ContentTypesFilename, doc.ContentTypes.X(), "", 0)
decMap.AddTarget(gooxml.BaseRelsFilename, doc.Rels.X(), "", 0)
if err := decMap.Decode(files); err != nil {
return nil, err
}
@ -490,18 +490,18 @@ func (doc *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ str
switch typ {
case gooxml.OfficeDocumentType:
doc.x = wml.NewDocument()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.x})
decMap.AddTarget(target, doc.x, typ, 0)
// look for the document relationships file as well
decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: doc.docRels.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(zippkg.RelationsPathFor(target), doc.docRels.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.CorePropertiesType:
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.CoreProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.CoreProperties.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.ExtendedPropertiesType:
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.AppProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.AppProperties.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.ThumbnailType:
// read our thumbnail
@ -524,56 +524,56 @@ func (doc *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ str
}
case gooxml.SettingsType:
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.Settings.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.Settings.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.NumberingType:
doc.Numbering = NewNumbering()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.Numbering.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.Numbering.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.StylesType:
doc.Styles.Clear()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.Styles.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.Styles.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.HeaderType:
hdr := wml.NewHdr()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: hdr, Index: uint32(len(doc.headers))})
decMap.AddTarget(target, hdr, typ, uint32(len(doc.headers)))
doc.headers = append(doc.headers, hdr)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.headers))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(doc.headers))
case gooxml.FooterType:
ftr := wml.NewFtr()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: ftr, Index: uint32(len(doc.footers))})
decMap.AddTarget(target, ftr, typ, uint32(len(doc.footers)))
doc.footers = append(doc.footers, ftr)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.footers))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(doc.footers))
case gooxml.ThemeType:
thm := dml.NewTheme()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: thm, Index: uint32(len(doc.themes))})
decMap.AddTarget(target, thm, typ, uint32(len(doc.themes)))
doc.themes = append(doc.themes, thm)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.themes))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(doc.themes))
case gooxml.WebSettingsType:
doc.webSettings = wml.NewWebSettings()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.webSettings})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.webSettings, typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.FontTableType:
doc.fontTable = wml.NewFonts()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.fontTable})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.fontTable, typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.EndNotesType:
doc.endNotes = wml.NewEndnotes()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.endNotes})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.endNotes, typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.FootNotesType:
doc.footNotes = wml.NewFootnotes()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: doc.footNotes})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, doc.footNotes, typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.ImageType:
for i, f := range files {
@ -594,7 +594,7 @@ func (doc *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ str
files[i] = nil
}
}
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(doc.Images))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(doc.Images))
default:
log.Printf("unsupported relationship type: %s tgt: %s", typ, target)
}

View File

@ -10,6 +10,9 @@ package gooxml
import (
"fmt"
"log"
"strings"
"baliance.com/gooxml/algo"
)
// Common filenames used in zip packages.
@ -29,83 +32,36 @@ const (
DocTypePresentation
)
// RelativeFilename returns a filename relative to where it is normally
// referenced from a relationships file. Index is used in some cases for files
// which there may be more than one of (e.g. worksheets/drawings/charts)
func RelativeFilename(dt DocType, typ string, index int) string {
switch typ {
case CorePropertiesType:
return "docProps/core.xml"
case ExtendedPropertiesType:
return "docProps/app.xml"
case ThumbnailType:
return "docProps/thumbnail.jpeg"
case StylesType:
return "styles.xml"
case ChartType, ChartContentType:
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 TableType, TableContentType:
return fmt.Sprintf("../tables/table%d.xml", index)
case ThemeType, ThemeContentType:
return fmt.Sprintf("theme/theme%d.xml", index)
case OfficeDocumentType:
switch dt {
case DocTypeSpreadsheet:
return "xl/workbook.xml"
case DocTypeDocument:
return "word/document.xml"
default:
log.Printf("unsupported type %s pair and %v", typ, dt)
}
case ImageType:
switch dt {
case DocTypeSpreadsheet:
return fmt.Sprintf("media/image%d.png", index)
case DocTypeDocument:
return fmt.Sprintf("media/image%d.png", index)
default:
log.Printf("unsupported type %s pair and %v", typ, dt)
}
// SML
case WorksheetType, WorksheetContentType:
return fmt.Sprintf("worksheets/sheet%d.xml", index)
case SharedStingsType, SharedStringsContentType:
return "sharedStrings.xml"
// WML
case FontTableType:
return "fontTable.xml"
case EndNotesType:
return "endnotes.xml"
case FootNotesType:
return "footnotes.xml"
case NumberingType:
return "numbering.xml"
case WebSettingsType:
return "webSettings.xml"
case SettingsType:
return "settings.xml"
case HeaderType:
return fmt.Sprintf("header%d.xml", index)
case FooterType:
return fmt.Sprintf("footer%d.xml", index)
default:
log.Printf("unsupported type %s", typ)
// RelativeFilename returns a filename relative to the source file referenced
// from a relationships file. Index is used in some cases for files which there
// may be more than one of (e.g. worksheets/drawings/charts)
func RelativeFilename(dt DocType, relToTyp, typ string, index int) string {
orig := AbsoluteFilename(dt, typ, index)
if relToTyp == "" {
return orig
}
return ""
relTo := AbsoluteFilename(dt, relToTyp, index)
relToSp := strings.Split(relTo, "/")
origSp := strings.Split(orig, "/")
// determine how many segments match
matching := 0
for i := 0; i < len(relToSp); i++ {
if relToSp[i] == origSp[i] {
matching++
}
if i+1 == len(origSp) {
break
}
}
relToSp = relToSp[matching:]
origSp = origSp[matching:]
nm := len(relToSp) - 1
if nm > 0 {
return algo.RepeatString("../", nm) + strings.Join(origSp, "/")
}
return strings.Join(origSp, "/")
}
// AbsoluteFilename returns the full path to a file from the root of the zip
@ -184,6 +140,15 @@ func AbsoluteFilename(dt DocType, typ string, index int) string {
log.Printf("unsupported type %s pair and %v", typ, dt)
}
case ImageType:
switch dt {
case DocTypeDocument:
return fmt.Sprintf("word/media/image%d.png", index)
case DocTypeSpreadsheet:
return fmt.Sprintf("xl/media/image%d.png", index)
default:
log.Printf("unsupported type %s pair and %v", typ, dt)
}
// SML
case WorksheetType, WorksheetContentType:
return fmt.Sprintf("xl/worksheets/sheet%d.xml", index)

View File

@ -14,31 +14,26 @@ func TestWMLFilenames(t *testing.T) {
td := []struct {
Idx int
Type string
ExpRel string
ExpAbs string
}{
{0, gooxml.CorePropertiesType, "docProps/core.xml", "docProps/core.xml"},
{0, gooxml.ExtendedPropertiesType, "docProps/app.xml", "docProps/app.xml"},
{0, gooxml.ThumbnailType, "docProps/thumbnail.jpeg", "docProps/thumbnail.jpeg"},
{0, gooxml.StylesType, "styles.xml", "word/styles.xml"},
{0, gooxml.CorePropertiesType, "docProps/core.xml"},
{0, gooxml.ExtendedPropertiesType, "docProps/app.xml"},
{0, gooxml.ThumbnailType, "docProps/thumbnail.jpeg"},
{0, gooxml.StylesType, "word/styles.xml"},
{0, gooxml.OfficeDocumentType, "word/document.xml", "word/document.xml"},
{0, gooxml.FontTableType, "fontTable.xml", "word/fontTable.xml"},
{0, gooxml.EndNotesType, "endnotes.xml", "word/endnotes.xml"},
{0, gooxml.FootNotesType, "footnotes.xml", "word/footnotes.xml"},
{0, gooxml.NumberingType, "numbering.xml", "word/numbering.xml"},
{0, gooxml.WebSettingsType, "webSettings.xml", "word/webSettings.xml"},
{0, gooxml.SettingsType, "settings.xml", "word/settings.xml"},
{23, gooxml.HeaderType, "header23.xml", "word/header23.xml"},
{15, gooxml.FooterType, "footer15.xml", "word/footer15.xml"},
{1, gooxml.ThemeType, "theme/theme1.xml", "word/theme/theme1.xml"},
{0, gooxml.OfficeDocumentType, "word/document.xml"},
{0, gooxml.FontTableType, "word/fontTable.xml"},
{0, gooxml.EndNotesType, "word/endnotes.xml"},
{0, gooxml.FootNotesType, "word/footnotes.xml"},
{0, gooxml.NumberingType, "word/numbering.xml"},
{0, gooxml.WebSettingsType, "word/webSettings.xml"},
{0, gooxml.SettingsType, "word/settings.xml"},
{23, gooxml.HeaderType, "word/header23.xml"},
{15, gooxml.FooterType, "word/footer15.xml"},
{1, gooxml.ThemeType, "word/theme/theme1.xml"},
}
for _, tc := range td {
rel := gooxml.RelativeFilename(gooxml.DocTypeDocument, tc.Type, tc.Idx)
abs := gooxml.AbsoluteFilename(gooxml.DocTypeDocument, tc.Type, tc.Idx)
if rel != tc.ExpRel {
t.Errorf("expected relative filename of %s for document %s/%d, got %s", tc.ExpRel, tc.Type, tc.Idx, rel)
}
if abs != tc.ExpAbs {
t.Errorf("expected absolute filename of %s for document %s/%d, got %s", tc.ExpAbs, tc.Type, tc.Idx, abs)
}
@ -49,30 +44,26 @@ func TestSMLFilenames(t *testing.T) {
td := []struct {
Idx int
Type string
ExpRel string
ExpAbs string
}{
{0, gooxml.CorePropertiesType, "docProps/core.xml", "docProps/core.xml"},
{0, gooxml.ExtendedPropertiesType, "docProps/app.xml", "docProps/app.xml"},
{0, gooxml.ThumbnailType, "docProps/thumbnail.jpeg", "docProps/thumbnail.jpeg"},
{0, gooxml.StylesType, "styles.xml", "xl/styles.xml"},
{0, gooxml.CorePropertiesType, "docProps/core.xml"},
{0, gooxml.ExtendedPropertiesType, "docProps/app.xml"},
{0, gooxml.ThumbnailType, "docProps/thumbnail.jpeg"},
{0, gooxml.StylesType, "xl/styles.xml"},
{0, gooxml.OfficeDocumentType, "xl/workbook.xml", "xl/workbook.xml"},
{15, gooxml.ChartType, "../charts/chart15.xml", "xl/charts/chart15.xml"},
{12, gooxml.DrawingType, "../drawings/drawing12.xml", "xl/drawings/drawing12.xml"},
{13, gooxml.TableType, "../tables/table13.xml", "xl/tables/table13.xml"},
{2, gooxml.CommentsType, "../comments2.xml", "xl/comments2.xml"},
{15, gooxml.WorksheetType, "worksheets/sheet15.xml", "xl/worksheets/sheet15.xml"},
{2, gooxml.VMLDrawingType, "../drawings/vmlDrawing2.vml", "xl/drawings/vmlDrawing2.vml"},
{0, gooxml.SharedStingsType, "sharedStrings.xml", "xl/sharedStrings.xml"},
{1, gooxml.ThemeType, "theme/theme1.xml", "xl/theme/theme1.xml"},
{0, gooxml.OfficeDocumentType, "xl/workbook.xml"},
{15, gooxml.ChartType, "xl/charts/chart15.xml"},
{12, gooxml.DrawingType, "xl/drawings/drawing12.xml"},
{13, gooxml.TableType, "xl/tables/table13.xml"},
{2, gooxml.CommentsType, "xl/comments2.xml"},
{15, gooxml.WorksheetType, "xl/worksheets/sheet15.xml"},
{2, gooxml.VMLDrawingType, "xl/drawings/vmlDrawing2.vml"},
{0, gooxml.SharedStingsType, "xl/sharedStrings.xml"},
{1, gooxml.ThemeType, "xl/theme/theme1.xml"},
{2, gooxml.ImageType, "xl/media/image2.png"},
}
for _, tc := range td {
rel := gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, tc.Type, tc.Idx)
abs := gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, tc.Type, tc.Idx)
if rel != tc.ExpRel {
t.Errorf("expected relative filename of %s for document %s/%d, got %s", tc.ExpRel, tc.Type, tc.Idx, rel)
}
if abs != tc.ExpAbs {
t.Errorf("expected absolute filename of %s for document %s/%d, got %s", tc.ExpAbs, tc.Type, tc.Idx, abs)
}

View File

@ -45,10 +45,11 @@ func (d Drawing) AddChart(at AnchorType) (chart.Chart, Anchor) {
d.wb.ContentTypes.AddOverride(fn, gooxml.ChartContentType)
var chartID string
// add relationship from drawing to the chart
for i, dr := range d.wb.drawings {
if dr == d.x {
fn := gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.ChartType, len(d.wb.charts))
fn := gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.DrawingType, gooxml.ChartType, len(d.wb.charts))
rel := d.wb.drawingRels[i].AddRelationship(fn, gooxml.ChartType)
chartID = rel.ID()
break

View File

@ -24,10 +24,11 @@ func New() *Workbook {
wb.Rels = common.NewRelationships()
wb.wbRels = common.NewRelationships()
wb.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.ExtendedPropertiesType, 0), gooxml.ExtendedPropertiesType)
wb.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.CorePropertiesType, 0), gooxml.CorePropertiesType)
wb.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.OfficeDocumentType, 0), gooxml.OfficeDocumentType)
wb.wbRels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.StylesType, 0), gooxml.StylesType)
wb.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, "", gooxml.ExtendedPropertiesType, 0), gooxml.ExtendedPropertiesType)
wb.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, "", gooxml.CorePropertiesType, 0), gooxml.CorePropertiesType)
wb.Rels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, "", gooxml.OfficeDocumentType, 0), gooxml.OfficeDocumentType)
wb.wbRels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.OfficeDocumentType, gooxml.StylesType, 0), gooxml.StylesType)
wb.ContentTypes = common.NewContentTypes()
wb.ContentTypes.AddDefault("vml", gooxml.VMLDrawingContentType)
@ -36,7 +37,7 @@ func New() *Workbook {
wb.SharedStrings = NewSharedStrings()
wb.ContentTypes.AddOverride(gooxml.AbsoluteFilename(gooxml.DocTypeSpreadsheet, gooxml.SharedStingsType, 0), gooxml.SharedStringsContentType)
wb.wbRels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.SharedStingsType, 0), gooxml.SharedStingsType)
wb.wbRels.AddRelationship(gooxml.RelativeFilename(gooxml.DocTypeSpreadsheet, gooxml.OfficeDocumentType, gooxml.SharedStingsType, 0), gooxml.SharedStingsType)
return wb
}

View File

@ -37,8 +37,8 @@ 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(zippkg.Target{Path: gooxml.ContentTypesFilename, Ifc: wb.ContentTypes.X()})
decMap.AddTarget(zippkg.Target{Path: gooxml.BaseRelsFilename, Ifc: wb.Rels.X()})
decMap.AddTarget(gooxml.ContentTypesFilename, wb.ContentTypes.X(), "", 0)
decMap.AddTarget(gooxml.BaseRelsFilename, wb.Rels.X(), "", 0)
if err := decMap.Decode(files); err != nil {
return nil, err
}

View File

@ -28,6 +28,10 @@ type Sheet struct {
x *sml.Worksheet
}
func (s Sheet) IsValid() bool {
return s.x != nil
}
// X returns the inner wrapped XML type.
func (s Sheet) X() *sml.Worksheet {
return s.x
@ -232,11 +236,12 @@ func (s Sheet) SetDrawing(d Drawing) {
break
}
}
// add relationship from drawing to the sheet
var drawingID string
for i, dr := range d.wb.drawings {
if dr == d.x {
rel := rel.AddAutoRelationship(gooxml.DocTypeSpreadsheet, i+1, gooxml.DrawingType)
rel := rel.AddAutoRelationship(gooxml.DocTypeSpreadsheet, gooxml.WorksheetType, i+1, gooxml.DrawingType)
drawingID = rel.ID()
break
}
@ -453,12 +458,12 @@ func (s Sheet) Comments() Comments {
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.xwsRels[i].AddAutoRelationship(gooxml.DocTypeSpreadsheet, gooxml.WorksheetType, 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)
vmlID := s.w.xwsRels[i].AddAutoRelationship(gooxml.DocTypeSpreadsheet, gooxml.WorksheetType, 1, gooxml.VMLDrawingType)
if s.x.LegacyDrawing == nil {
s.x.LegacyDrawing = sml.NewCT_LegacyDrawing()
}

View File

@ -83,8 +83,9 @@ func (wb *Workbook) AddSheet() Sheet {
wb.comments = append(wb.comments, nil)
dt := gooxml.DocTypeSpreadsheet
// update the references
rid := wb.wbRels.AddAutoRelationship(dt, len(wb.x.Sheets.Sheet), gooxml.WorksheetType)
rid := wb.wbRels.AddAutoRelationship(dt, gooxml.OfficeDocumentType, len(wb.x.Sheets.Sheet), gooxml.WorksheetType)
rs.IdAttr = rid.ID()
// add the content type
@ -280,28 +281,28 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
switch typ {
case gooxml.OfficeDocumentType:
wb.x = sml.NewWorkbook()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.x})
decMap.AddTarget(target, wb.x, typ, 0)
// look for the workbook relationships file as well
wb.wbRels = common.NewRelationships()
decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: wb.wbRels.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(zippkg.RelationsPathFor(target), wb.wbRels.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.CorePropertiesType:
decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.CoreProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, wb.CoreProperties.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.ExtendedPropertiesType:
decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.AppProperties.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, wb.AppProperties.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.WorksheetType:
ws := sml.NewWorksheet()
idx := uint32(len(wb.xws))
wb.xws = append(wb.xws, ws)
decMap.AddTarget(zippkg.Target{Path: target, Ifc: ws, Index: idx})
decMap.AddTarget(target, ws, typ, idx)
// look for worksheet rels
wksRel := common.NewRelationships()
decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: wksRel.X(), Index: idx})
decMap.AddTarget(zippkg.RelationsPathFor(target), wksRel.X(), typ, 0)
wb.xwsRels = append(wb.xwsRels, wksRel)
// add a comments placeholder that will be replaced if we see a comments
@ -310,23 +311,23 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
// fix the relationship target so it points to where we'll save
// the worksheet
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.xws))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(wb.xws))
case gooxml.StylesType:
wb.StyleSheet = NewStyleSheet(wb)
decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.StyleSheet.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, wb.StyleSheet.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.ThemeType:
thm := dml.NewTheme()
wb.themes = append(wb.themes, thm)
decMap.AddTarget(zippkg.Target{Path: target, Ifc: thm})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.themes))
decMap.AddTarget(target, thm, typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(wb.themes))
case gooxml.SharedStingsType:
wb.SharedStrings = NewSharedStrings()
decMap.AddTarget(zippkg.Target{Path: target, Ifc: wb.SharedStrings.X()})
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
decMap.AddTarget(target, wb.SharedStrings.X(), typ, 0)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, 0)
case gooxml.ThumbnailType:
// read our thumbnail
@ -367,43 +368,43 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
files[i] = nil
}
}
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(wb.Images))
case gooxml.DrawingType:
drawing := sd.NewWsDr()
idx := uint32(len(wb.drawings))
decMap.AddTarget(zippkg.Target{Path: target, Ifc: drawing, Index: idx})
decMap.AddTarget(target, drawing, typ, idx)
wb.drawings = append(wb.drawings, drawing)
drel := common.NewRelationships()
decMap.AddTarget(zippkg.Target{Path: zippkg.RelationsPathFor(target), Ifc: drel.X(), Index: idx})
decMap.AddTarget(zippkg.RelationsPathFor(target), drel.X(), typ, idx)
wb.drawingRels = append(wb.drawingRels, drel)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.drawings))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, 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})
decMap.AddTarget(target, vd, typ, 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))
decMap.AddTarget(target, wb.comments[src.Index], typ, src.Index)
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(wb.comments))
case gooxml.ChartType:
chart := crt.NewChartSpace()
idx := uint32(len(wb.charts))
decMap.AddTarget(zippkg.Target{Path: target, Ifc: chart, Index: idx})
decMap.AddTarget(target, chart, typ, idx)
wb.charts = append(wb.charts, chart)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(wb.charts))
case gooxml.TableType:
tbl := sml.NewTable()
idx := uint32(len(wb.tables))
decMap.AddTarget(zippkg.Target{Path: target, Ifc: tbl, Index: idx})
decMap.AddTarget(target, tbl, typ, idx)
wb.tables = append(wb.tables, tbl)
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
rel.TargetAttr = gooxml.RelativeFilename(dt, src.Typ, typ, len(wb.tables))
default:
log.Printf("unsupported relationship %s %s", target, typ)
}
@ -551,3 +552,12 @@ func (wb *Workbook) Protection() WorkbookProtection {
}
return WorkbookProtection{wb.x.WorkbookProtection}
}
func (wb *Workbook) GetSheet(name string) Sheet {
for _, s := range wb.Sheets() {
if s.Name() == name {
return s
}
}
return Sheet{}
}

View File

@ -45,19 +45,22 @@ func (d *DecodeMap) SetOnNewRelationshipFunc(fn OnNewRelationshipFunc) {
type Target struct {
Path string
Typ 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(tgt Target) {
// unmarshaled to. filePath is the absolute path to the target, ifc is the
// object to decode into, sourceFileType is the type of file that the reference
// was discovered in, and index is the index of the source file type.
func (d *DecodeMap) AddTarget(filePath string, ifc interface{}, sourceFileType string, idx uint32) {
if d.pathsToIfcs == nil {
d.pathsToIfcs = make(map[string]Target)
d.basePaths = make(map[*relationships.Relationships]string)
}
d.pathsToIfcs[filepath.Clean(tgt.Path)] = tgt
d.pathsToIfcs[filepath.Clean(filePath)] = Target{Path: filePath, Typ: sourceFileType, Ifc: ifc, Index: idx}
}
// Decode loops decoding targets registered with AddTarget and calling th