mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
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:
parent
11edbae6be
commit
89b1416b8f
13
algo/strings.go
Normal file
13
algo/strings.go
Normal 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)
|
||||
}
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
117
filenames.go
117
filenames.go
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user