diff --git a/apipara.go b/apipara.go index e7b9407..4627443 100644 --- a/apipara.go +++ b/apipara.go @@ -2,14 +2,14 @@ package docxlib // AddParagraph adds a new paragraph func (f *Docx) AddParagraph() *Paragraph { - p := &Paragraph{ + f.Document.Body.mu.Lock() + defer f.Document.Body.mu.Unlock() + f.Document.Body.Paragraphs = append(f.Document.Body.Paragraphs, Paragraph{ Children: make([]interface{}, 0, 64), file: f, - } + }) - f.Document.Body.Paragraphs = append(f.Document.Body.Paragraphs, p) - - return p + return &f.Document.Body.Paragraphs[len(f.Document.Body.Paragraphs)-1] } // Justification allows to set para's horizonal alignment diff --git a/empty.go b/empty.go index 6d0bf12..eaa511b 100644 --- a/empty.go +++ b/empty.go @@ -15,16 +15,13 @@ func newEmptyA4File() *Docx { XMLR: XMLNS_R, XMLWP: XMLNS_WP, // XMLWP14: XMLNS_WP14, - Body: &Body{ - XMLName: xml.Name{ - Space: "w", - }, - Paragraphs: make([]*Paragraph, 0, 64), + Body: Body{ + Paragraphs: make([]Paragraph, 0, 64), }, }, DocRelation: Relationships{ Xmlns: XMLNS_REL, - Relationships: []*Relationship{ + Relationship: []Relationship{ { ID: "rId1", Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles`, @@ -69,6 +66,6 @@ func newEmptyA4File() *Docx { }, buf: bytes.NewBuffer(make([]byte, 0, 1024*1024*4)), } - docx.Document.file = docx + docx.Document.Body.file = docx return docx } diff --git a/link.go b/link.go index 64325bb..6e37be7 100644 --- a/link.go +++ b/link.go @@ -17,14 +17,14 @@ var ( // // this func is not thread-safe func (f *Docx) addLinkRelation(link string) string { - rel := &Relationship{ + rel := Relationship{ ID: "rId" + strconv.Itoa(int(atomic.AddUintptr(&f.rId, 1))), Type: REL_HYPERLINK, Target: link, TargetMode: REL_TARGETMODE, } - f.DocRelation.Relationships = append(f.DocRelation.Relationships, rel) + f.DocRelation.Relationship = append(f.DocRelation.Relationship, rel) return rel.ID } @@ -33,13 +33,13 @@ func (f *Docx) addLinkRelation(link string) string { // // this func is not thread-safe func (f *Docx) addImageRelation(m Media) string { - rel := &Relationship{ + rel := Relationship{ ID: "rId" + strconv.Itoa(int(atomic.AddUintptr(&f.rId, 1))), Type: REL_IMAGE, Target: "media/" + m.Name, } - f.DocRelation.Relationships = append(f.DocRelation.Relationships, rel) + f.DocRelation.Relationship = append(f.DocRelation.Relationship, rel) return rel.ID } @@ -48,7 +48,7 @@ func (f *Docx) addImageRelation(m Media) string { func (f *Docx) ReferTarget(id string) (string, error) { f.DocRelation.mu.RLock() defer f.DocRelation.mu.RUnlock() - for _, a := range f.DocRelation.Relationships { + for _, a := range f.DocRelation.Relationship { if a.ID == id { return a.Target, nil } @@ -60,7 +60,7 @@ func (f *Docx) ReferTarget(id string) (string, error) { func (f *Docx) ReferID(target string) (string, error) { f.DocRelation.mu.RLock() defer f.DocRelation.mu.RUnlock() - for _, a := range f.DocRelation.Relationships { + for _, a := range f.DocRelation.Relationship { if a.Target == target { return a.ID, nil } diff --git a/pack.go b/pack.go index 343b5da..0699933 100644 --- a/pack.go +++ b/pack.go @@ -31,7 +31,7 @@ func (f *Docx) pack(zipWriter *zip.Writer) (err error) { } files["word/_rels/document.xml.rels"] = marshaller{data: &f.DocRelation} - files["word/document.xml"] = marshaller{data: f.Document} + files["word/document.xml"] = marshaller{data: &f.Document} for _, m := range f.media { files[m.String()] = bytes.NewReader(m.Data) diff --git a/structdoc.go b/structdoc.go index 771e52c..3416386 100644 --- a/structdoc.go +++ b/structdoc.go @@ -3,6 +3,8 @@ package docxlib import ( "encoding/xml" "io" + "strings" + "sync" ) const ( @@ -24,27 +26,13 @@ func getAtt(atts []xml.Attr, name string) string { } type Body struct { - XMLName xml.Name `xml:"w:body"` - Paragraphs []*Paragraph `xml:"w:p,omitempty"` -} - -type Document struct { - XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main document"` - XMLW string `xml:"xmlns:w,attr"` // cannot be unmarshalled in - XMLR string `xml:"xmlns:r,attr,omitempty"` // cannot be unmarshalled in - XMLWP string `xml:"xmlns:wp,attr,omitempty"` // cannot be unmarshalled in - XMLWP14 string `xml:"xmlns:wp14,attr,omitempty"` // cannot be unmarshalled in - Body *Body + mu sync.Mutex + Paragraphs []Paragraph `xml:"w:p,omitempty"` file *Docx } -func (doc *Document) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - if doc.Body == nil { - doc.Body = &Body{ - Paragraphs: make([]*Paragraph, 0, 64), - } - } +func (b *Body) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for { t, err := d.Token() if err == io.EOF { @@ -56,19 +44,59 @@ func (doc *Document) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error switch tt := t.(type) { case xml.StartElement: - switch tt.Name.Local { - case "body": - case "p": + if tt.Name.Local == "p" { var value Paragraph - d.DecodeElement(&value, &tt) - if len(value.Children) > 0 { - value.file = doc.file - doc.Body.Paragraphs = append(doc.Body.Paragraphs, &value) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + if len(value.Children) > 0 { + value.file = b.file + b.mu.Lock() + b.Paragraphs = append(b.Paragraphs, value) + b.mu.Unlock() } - default: - d.Skip() continue } + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + } + + } + return nil + +} + +type Document struct { + XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main document"` + XMLW string `xml:"xmlns:w,attr"` // cannot be unmarshalled in + XMLR string `xml:"xmlns:r,attr,omitempty"` // cannot be unmarshalled in + XMLWP string `xml:"xmlns:wp,attr,omitempty"` // cannot be unmarshalled in + // XMLWP14 string `xml:"xmlns:wp14,attr,omitempty"` // cannot be unmarshalled in + + Body Body `xml:"w:body"` +} + +func (doc *Document) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + switch tt := t.(type) { + case xml.StartElement: + if tt.Name.Local == "body" { + err = d.DecodeElement(&doc.Body, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + } } } diff --git a/structdoc_test.go b/structdoc_test.go index 91e917b..68f8ee7 100644 --- a/structdoc_test.go +++ b/structdoc_test.go @@ -2,9 +2,6 @@ package docxlib import ( "encoding/xml" - "hash/crc64" - "io" - "os" "testing" ) @@ -47,76 +44,3 @@ func TestUnmarshalPlainStructure(t *testing.T) { } } } - -func TestInlineDrawingStructure(t *testing.T) { - w := NewA4() - // add new paragraph - para1 := w.AddParagraph() - // add text - para1.AddText("直接粘贴 inline").AddTab() - r, err := para1.AddAnchorDrawingFrom("testdata/fumiama.JPG") - if err != nil { - t.Fatal(err) - } - r.Drawing.Anchor.Graphic.GraphicData.Pic.BlipFill.Blip.AlphaModFix = &AAlphaModFix{Amount: 50000} - r.Drawing.Anchor.Graphic.GraphicData.Pic.NonVisualPicProperties.CNvPicPr.Locks = &APicLocks{NoChangeAspect: 1} - r.Drawing.Anchor.Graphic.GraphicData.Pic.SpPr.Xfrm.Rot = 50000 - para2 := w.AddParagraph().Justification("center") - para2.AddInlineDrawingFrom("testdata/fumiama.JPG") - para2.AddTab().AddTab().AppendTab().AppendTab() - para2.AddInlineDrawingFrom("testdata/fumiama2x.webp") - - para3 := w.AddParagraph() - para3.AddInlineDrawingFrom("testdata/fumiamayoko.png") - - f, err := os.Create("TestMarshalInlineDrawingStructure.xml") - if err != nil { - t.Fatal(err) - } - defer f.Close() - _, err = marshaller{data: w.Document}.WriteTo(f) - if err != nil { - t.Fatal(err) - } - _, err = f.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - w = NewA4() - err = xml.NewDecoder(f).Decode(&w.Document) - if err != nil { - t.Fatal(err) - } - f1, err := os.Create("TestUnmarshalInlineDrawingStructure.xml") - if err != nil { - t.Fatal(err) - } - defer f1.Close() - _, err = marshaller{data: w.Document}.WriteTo(f1) - if err != nil { - t.Fatal(err) - } - _, err = f.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - _, err = f1.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - h := crc64.New(crc64.MakeTable(crc64.ECMA)) - _, err = io.Copy(h, f) - if err != nil { - t.Fatal(err) - } - md51 := h.Sum64() - h.Reset() - _, err = io.Copy(h, f1) - if err != nil { - t.Fatal(err) - } - md52 := h.Sum64() - if md51 != md52 { - t.Fail() - } -} diff --git a/structdrawing.go b/structdrawing.go index 69a8fe8..51959d8 100644 --- a/structdrawing.go +++ b/structdrawing.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "io" "strconv" + "strings" ) const ( @@ -38,10 +39,16 @@ func (r *Drawing) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "inline": r.Inline = new(WPInline) - d.DecodeElement(r.Inline, &tt) + err = d.DecodeElement(r.Inline, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "anchor": r.Anchor = new(WPAnchor) - d.DecodeElement(r.Anchor, &tt) + err = d.DecodeElement(r.Anchor, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } default: continue } @@ -138,14 +145,23 @@ func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err err } case "docPr": r.DocPr = new(WPDocPr) - d.DecodeElement(r.DocPr, &tt) + err = d.DecodeElement(r.DocPr, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "cNvGraphicFramePr": var value WPCNvGraphicFramePr - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } r.CNvGraphicFramePr = &value case "graphic": var value AGraphic - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } r.Graphic = &value default: continue @@ -285,7 +301,10 @@ func (w *WPCNvGraphicFramePr) UnmarshalXML(d *xml.Decoder, start xml.StartElemen switch tt.Name.Local { case "graphicFrameLocks": var value AGraphicFrameLocks - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } value.NoChangeAspect, err = strconv.Atoi(getAtt(tt.Attr, "noChangeAspect")) if err != nil { return err @@ -336,7 +355,10 @@ func (a *AGraphic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "graphicData": var value AGraphicData - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } value.URI = getAtt(tt.Attr, "uri") a.GraphicData = &value default: @@ -370,7 +392,10 @@ func (a *AGraphicData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro switch tt.Name.Local { case "pic": var value PICPic - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } value.XMLPIC = getAtt(tt.Attr, "pic") a.Pic = &value default: @@ -406,15 +431,24 @@ func (p *PICPic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "nvPicPr": var value PICNonVisualPicProperties - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } p.NonVisualPicProperties = &value case "blipFill": var value PICBlipFill - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } p.BlipFill = &value case "spPr": var value PICSpPr - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } p.SpPr = &value default: continue @@ -449,7 +483,10 @@ func (p *PICNonVisualPicProperties) UnmarshalXML(d *xml.Decoder, start xml.Start p.NonVisualDrawingProperties.ID = getAtt(tt.Attr, "id") p.NonVisualDrawingProperties.Name = getAtt(tt.Attr, "name") case "cNvPicPr": - d.DecodeElement(&p.CNvPicPr, &tt) + err = d.DecodeElement(&p.CNvPicPr, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } default: continue } @@ -529,9 +566,15 @@ func (p *PICBlipFill) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error case xml.StartElement: switch tt.Name.Local { case "blip": - d.DecodeElement(&p.Blip, &tt) + err = d.DecodeElement(&p.Blip, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "stretch": - d.DecodeElement(&p.Stretch, &tt) + err = d.DecodeElement(&p.Stretch, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } default: continue } @@ -626,9 +669,15 @@ func (p *PICSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { case xml.StartElement: switch tt.Name.Local { case "xfrm": - d.DecodeElement(&p.Xfrm, &tt) + err = d.DecodeElement(&p.Xfrm, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "prstGeom": - d.DecodeElement(&p.PrstGeom, &tt) + err = d.DecodeElement(&p.PrstGeom, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } p.PrstGeom.Prst = getAtt(tt.Attr, "prst") default: continue @@ -881,17 +930,29 @@ func (r *WPAnchor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err err case "positionH": r.PositionH = new(WPPositionH) // r.PositionH.RelativeFrom = getAtt(tt.Attr, "relativeFrom") - d.DecodeElement(&r.PositionH, &tt) + err = d.DecodeElement(&r.PositionH, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "positionV": r.PositionV = new(WPPositionV) // r.PositionV.RelativeFrom = getAtt(tt.Attr, "relativeFrom") - d.DecodeElement(&r.PositionV, &tt) + err = d.DecodeElement(&r.PositionV, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "extent": r.Extent = new(WPExtent) - d.DecodeElement(&r.Extent, &tt) + err = d.DecodeElement(&r.Extent, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "effectExtent": r.EffectExtent = new(WPEffectExtent) - d.DecodeElement(&r.EffectExtent, &tt) + err = d.DecodeElement(&r.EffectExtent, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "wrapNone": r.WrapNone = &struct{}{} case "wrapSquare": @@ -899,13 +960,22 @@ func (r *WPAnchor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err err r.WrapSquare.WrapText = getAtt(tt.Attr, "wrapText") case "docPr": r.DocPr = new(WPDocPr) - d.DecodeElement(r.DocPr, &tt) + err = d.DecodeElement(r.DocPr, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "cNvGraphicFramePr": r.CNvGraphicFramePr = new(WPCNvGraphicFramePr) - d.DecodeElement(r.CNvGraphicFramePr, &tt) + err = d.DecodeElement(r.CNvGraphicFramePr, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "graphic": r.Graphic = new(AGraphic) - d.DecodeElement(&r.Graphic, &tt) + err = d.DecodeElement(&r.Graphic, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } default: continue } @@ -949,7 +1019,7 @@ func (r *WPPositionH) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error switch tt.Name.Local { case "posOffset": err = d.DecodeElement(&r.PosOffset, &tt) - if err != nil { + if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } default: @@ -988,7 +1058,7 @@ func (r *WPPositionV) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error switch tt.Name.Local { case "posOffset": err = d.DecodeElement(&r.PosOffset, &tt) - if err != nil { + if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } default: diff --git a/structdrawing_test.go b/structdrawing_test.go new file mode 100644 index 0000000..bf9d305 --- /dev/null +++ b/structdrawing_test.go @@ -0,0 +1,82 @@ +package docxlib + +import ( + "encoding/xml" + "hash/crc64" + "io" + "os" + "testing" +) + +func TestDrawingStructure(t *testing.T) { + w := NewA4() + // add new paragraph + para1 := w.AddParagraph() + // add text + para1.AddText("直接粘贴 inline").AddTab() + r, err := para1.AddAnchorDrawingFrom("testdata/fumiama.JPG") + if err != nil { + t.Fatal(err) + } + r.Drawing.Anchor.Graphic.GraphicData.Pic.BlipFill.Blip.AlphaModFix = &AAlphaModFix{Amount: 50000} + r.Drawing.Anchor.Graphic.GraphicData.Pic.NonVisualPicProperties.CNvPicPr.Locks = &APicLocks{NoChangeAspect: 1} + r.Drawing.Anchor.Graphic.GraphicData.Pic.SpPr.Xfrm.Rot = 50000 + para2 := w.AddParagraph().Justification("center") + para2.AddInlineDrawingFrom("testdata/fumiama.JPG") + para2.AddTab().AddTab().AppendTab().AppendTab() + para2.AddInlineDrawingFrom("testdata/fumiama2x.webp") + + para3 := w.AddParagraph() + para3.AddInlineDrawingFrom("testdata/fumiamayoko.png") + + f, err := os.Create("TestMarshalInlineDrawingStructure.xml") + if err != nil { + t.Fatal(err) + } + defer f.Close() + _, err = marshaller{data: &w.Document}.WriteTo(f) + if err != nil { + t.Fatal(err) + } + _, err = f.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + w = NewA4() + err = xml.NewDecoder(f).Decode(&w.Document) + if err != nil { + t.Fatal(err) + } + f1, err := os.Create("TestUnmarshalInlineDrawingStructure.xml") + if err != nil { + t.Fatal(err) + } + defer f1.Close() + _, err = marshaller{data: &w.Document}.WriteTo(f1) + if err != nil { + t.Fatal(err) + } + _, err = f.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + _, err = f1.Seek(0, io.SeekStart) + if err != nil { + t.Fatal(err) + } + h := crc64.New(crc64.MakeTable(crc64.ECMA)) + _, err = io.Copy(h, f) + if err != nil { + t.Fatal(err) + } + crc1 := h.Sum64() + h.Reset() + _, err = io.Copy(h, f1) + if err != nil { + t.Fatal(err) + } + crc2 := h.Sum64() + if crc1 != crc2 { + t.Fail() + } +} diff --git a/structlink.go b/structlink.go index 4720fb8..14576f4 100644 --- a/structlink.go +++ b/structlink.go @@ -3,6 +3,7 @@ package docxlib import ( "encoding/xml" "io" + "strings" ) // Hyperlink element contains links @@ -25,9 +26,10 @@ func (r *Hyperlink) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt := t.(type) { case xml.StartElement: if tt.Name.Local == "r" { - d.DecodeElement(&r.Run, &tt) - } else { - continue + err = d.DecodeElement(&r.Run, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } } } diff --git a/structpara.go b/structpara.go index 41936df..a7c512b 100644 --- a/structpara.go +++ b/structpara.go @@ -36,7 +36,7 @@ func (p *ParagraphProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen } type Paragraph struct { - XMLName xml.Name `xml:"w:p,omitempty"` + // XMLName xml.Name `xml:"w:p,omitempty"` Properties *ParagraphProperties Children []interface{} // Children will generate an unnecessary tag ... and we skip it by a self-defined xml.Marshaler @@ -106,7 +106,10 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "hyperlink": var value Hyperlink - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } id := getAtt(tt.Attr, "id") anchor := getAtt(tt.Attr, "anchor") if id != "" { @@ -118,15 +121,24 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { elem = &value case "r": var value Run - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } elem = &value case "rPr": var value RunProperties - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } elem = &value case "pPr": var value ParagraphProperties - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } p.Properties = &value continue default: diff --git a/structrel.go b/structrel.go index e43616b..aff043f 100644 --- a/structrel.go +++ b/structrel.go @@ -1,7 +1,6 @@ package docxlib import ( - "encoding/xml" "sync" ) @@ -14,16 +13,14 @@ const ( ) type Relationships struct { - mu sync.RWMutex - XMLName xml.Name `xml:"Relationships"` - Xmlns string `xml:"xmlns,attr"` - Relationships []*Relationship `xml:"Relationship"` + mu sync.RWMutex + Xmlns string `xml:"xmlns,attr"` + Relationship []Relationship } type Relationship struct { - XMLName xml.Name `xml:"Relationship"` - ID string `xml:"Id,attr"` - Type string `xml:"Type,attr"` - Target string `xml:"Target,attr"` - TargetMode string `xml:"TargetMode,attr,omitempty"` + ID string `xml:"Id,attr"` + Type string `xml:"Type,attr"` + Target string `xml:"Target,attr"` + TargetMode string `xml:"TargetMode,attr,omitempty"` } diff --git a/structrel_test.go b/structrel_test.go new file mode 100644 index 0000000..34b51db --- /dev/null +++ b/structrel_test.go @@ -0,0 +1,55 @@ +package docxlib + +import ( + "crypto/md5" + "encoding/hex" + "io" + "os" + "testing" +) + +func TestRelationships(t *testing.T) { + rel := Relationships{ + Xmlns: XMLNS_REL, + Relationship: []Relationship{ + { + ID: "rId1", + Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles`, + Target: "styles.xml", + }, + { + ID: "rId2", + Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme`, + Target: "theme/theme1.xml", + }, + { + ID: "rId3", + Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable`, + Target: "fontTable.xml", + }, + { + ID: "rId4", + Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings`, + Target: "settings.xml", + }, + { + ID: "rId5", + Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings`, + Target: "webSettings.xml", + }, + }, + } + f, err := os.Create("TestRelationships.xml") + if err != nil { + t.Fatal(err) + } + h := md5.New() + _, err = io.Copy(io.MultiWriter(f, h), marshaller{data: &rel}) + if err != nil { + t.Fatal(err) + } + m := hex.EncodeToString(h.Sum(make([]byte, 0, 16))) + if m != "c75af73ef6cc9536a193669c4a3605c3" { + t.Fatal("real md5:", m) + } +} diff --git a/structrun.go b/structrun.go index bee1dac..ce5f5e7 100644 --- a/structrun.go +++ b/structrun.go @@ -3,6 +3,7 @@ package docxlib import ( "encoding/xml" "io" + "strings" ) // Run is part of a paragraph that has its own style. It could be @@ -36,19 +37,31 @@ func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "rPr": var value RunProperties - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } r.RunProperties = &value case "instrText": var value string - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } r.InstrText = value case "t": var value Text - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } r.Text = &value case "drawing": var value Drawing - d.DecodeElement(&value, &tt) + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } r.Drawing = &value case "tab": if r.InstrText == "" && r.Text == nil && r.Drawing == nil { @@ -159,6 +172,6 @@ type Size struct { // both:两端对齐。 // distribute:分散对齐。 type Justification struct { - XMLName xml.Name `xml:"w:jc"` - Val string `xml:"w:val,attr"` + // XMLName xml.Name `xml:"w:jc"` + Val string `xml:"w:val,attr"` }