diff --git a/apidrawing.go b/apidrawing.go index 3219b88..34100cb 100644 --- a/apidrawing.go +++ b/apidrawing.go @@ -83,7 +83,7 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) { Drawing: d, RunProperties: &RunProperties{}, } - p.Children = append(p.Children, ParagraphChild{Run: run}) + p.Children = append(p.Children, run) return run, nil } @@ -191,7 +191,7 @@ func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) { Drawing: d, RunProperties: &RunProperties{}, } - p.Children = append(p.Children, ParagraphChild{Run: run}) + p.Children = append(p.Children, run) return run, nil } diff --git a/apilink.go b/apilink.go index 8178a33..fb6727c 100644 --- a/apilink.go +++ b/apilink.go @@ -19,7 +19,7 @@ func (p *Paragraph) AddLink(text string, link string) *Hyperlink { }, } - p.Children = append(p.Children, ParagraphChild{Link: hyperlink}) + p.Children = append(p.Children, hyperlink) return hyperlink } diff --git a/apipara.go b/apipara.go index 4151631..e7b9407 100644 --- a/apipara.go +++ b/apipara.go @@ -3,7 +3,7 @@ package docxlib // AddParagraph adds a new paragraph func (f *Docx) AddParagraph() *Paragraph { p := &Paragraph{ - Children: make([]ParagraphChild, 0, 64), + Children: make([]interface{}, 0, 64), file: f, } diff --git a/apitext.go b/apitext.go index 3442f6d..d982862 100644 --- a/apitext.go +++ b/apitext.go @@ -10,7 +10,7 @@ func (p *Paragraph) AddTab() *Run { XMLName xml.Name "xml:\"w:tab,omitempty\"" }{{}}, } - p.Children = append(p.Children, ParagraphChild{Run: run}) + p.Children = append(p.Children, run) return run } @@ -29,7 +29,7 @@ func (p *Paragraph) AddText(text string) *Run { RunProperties: &RunProperties{}, } - p.Children = append(p.Children, ParagraphChild{Run: run}) + p.Children = append(p.Children, run) return run } diff --git a/cmd/main/main.go b/cmd/main/main.go index 528e043..bf3f45e 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -96,29 +96,28 @@ func main() { for _, para := range doc.Document.Body.Paragraphs { fmt.Println("New paragraph") for _, child := range para.Children { - if child.Run != nil { - if child.Run.Text != nil { - fmt.Printf("\tWe've found a new run with the text ->%s\n", child.Run.Text.Text) + switch o := child.(type) { + case *docxlib.Run: + if o.Text != nil { + fmt.Printf("\tWe've found a new run with the text ->%s\n", o.Text.Text) } - if child.Run.Drawing != nil { - if child.Run.Drawing.Inline != nil { - fmt.Printf("\tWe've found a new run with the inline drawing ->%s\n", child.Run.Drawing.Inline.DocPr.Name) + if o.Drawing != nil { + if o.Drawing.Inline != nil { + fmt.Printf("\tWe've found a new run with the inline drawing ->%s\n", o.Drawing.Inline.DocPr.Name) } - if child.Run.Drawing.Anchor != nil { - fmt.Printf("\tWe've found a new run with the anchor drawing ->%s\n", child.Run.Drawing.Anchor.DocPr.Name) + if o.Drawing.Anchor != nil { + fmt.Printf("\tWe've found a new run with the anchor drawing ->%s\n", o.Drawing.Anchor.DocPr.Name) } } - } - if child.Link != nil { - id := child.Link.ID - text := child.Link.Run.InstrText + case *docxlib.Hyperlink: + id := o.ID + text := o.Run.InstrText link, err := doc.ReferTarget(id) if err != nil { fmt.Printf("\tWe found a link with id %s and text %s without target\n", id, text) } else { fmt.Printf("\tWe've found a new hyperlink with ref %s and the text %s\n", link, text) } - } } fmt.Print("End of paragraph\n\n") diff --git a/structdoc_test.go b/structdoc_test.go index 3e76fa4..91e917b 100644 --- a/structdoc_test.go +++ b/structdoc_test.go @@ -37,10 +37,10 @@ func TestUnmarshalPlainStructure(t *testing.T) { t.Fatalf("We were not able to parse paragraph %d", i) } for _, child := range p.Children { - if child.Link == nil && child.Properties == nil && child.Run == nil { + if child == nil { t.Fatalf("There are Paragraph children with all fields nil") } - if child.Link != nil && child.Link.ID == "" { + if o, ok := child.(*Hyperlink); ok && o.ID == "" { t.Fatalf("We have a link without ID") } } @@ -48,510 +48,6 @@ func TestUnmarshalPlainStructure(t *testing.T) { } } -const drawing_doc = ` - - - - - - - - - - - - - 直接粘贴 - - - - - - - - - inline - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 一行2个 - - - inline - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 一行2个组合 - - - inline - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 一个 浮于上方 - - - - - - 右侧对齐 - - - - - - - - - 11.32cm - - - - - - - - - 23.73cm - - - - - - - - - - - - - - - - - 2935605 - - - 97790 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - 0 - - - - - - - - - - - - -` - -func TestUnmarshalDrawingStructure(t *testing.T) { - doc := Document{ - XMLW: XMLNS_W, - XMLR: XMLNS_R, - XMLWP: XMLNS_WP, - // XMLWP14: XMLNS_WP14, - XMLName: xml.Name{Space: XMLNS_W, Local: "document"}} - err := xml.Unmarshal(StringToBytes(drawing_doc), &doc) - if err != nil { - t.Fatal(err) - } - if len(doc.Body.Paragraphs) != 8 { - t.Fatalf("We expected %d paragraphs, we got %d", 8, len(doc.Body.Paragraphs)) - } - for i, p := range doc.Body.Paragraphs { - if len(p.Children) == 0 { - t.Fatalf("We were not able to parse paragraph %d", i) - } - for j, child := range p.Children { - if child.Link == nil && child.Properties == nil && child.Run == nil { - t.Fatalf("There are Paragraph children with all fields nil") - } - if child.Run != nil && child.Run.Text == nil && child.Run.InstrText == "" && child.Run.Drawing == nil { - t.Fatalf("We have a run with no text and drawing") - } - if child.Link != nil && child.Link.ID == "" { - t.Fatalf("We have a link without ID") - } - if child.Run != nil && child.Run.Drawing != nil { - t.Log("fild drawing at aragraph", i, ", child", j) - if child.Run.Drawing.Inline != nil { - if child.Run.Drawing.Inline.DistT != 0 || child.Run.Drawing.Inline.DistB != 1 || child.Run.Drawing.Inline.DistL != 2 || child.Run.Drawing.Inline.DistR != 3 { - t.Fatal("unexpected inline dist") - } - } - } - } - } -} - func TestInlineDrawingStructure(t *testing.T) { w := NewA4() // add new paragraph @@ -563,6 +59,8 @@ func TestInlineDrawingStructure(t *testing.T) { 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() diff --git a/structdrawing.go b/structdrawing.go index 7402bb1..69a8fe8 100644 --- a/structdrawing.go +++ b/structdrawing.go @@ -429,7 +429,7 @@ func (p *PICPic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type PICNonVisualPicProperties struct { XMLName xml.Name `xml:"pic:nvPicPr,omitempty"` NonVisualDrawingProperties PICNonVisualDrawingProperties - CNvPicPr struct{} `xml:"pic:cNvPicPr"` + CNvPicPr PicCNvPicPr } func (p *PICNonVisualPicProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { @@ -448,6 +448,8 @@ func (p *PICNonVisualPicProperties) UnmarshalXML(d *xml.Decoder, start xml.Start case "cNvPr": p.NonVisualDrawingProperties.ID = getAtt(tt.Attr, "id") p.NonVisualDrawingProperties.Name = getAtt(tt.Attr, "name") + case "cNvPicPr": + d.DecodeElement(&p.CNvPicPr, &tt) default: continue } @@ -457,6 +459,48 @@ func (p *PICNonVisualPicProperties) UnmarshalXML(d *xml.Decoder, start xml.Start return nil } +// PicCNvPicPr represents the non-visual properties of a picture. +type PicCNvPicPr struct { + XMLName xml.Name `xml:"pic:cNvPicPr,omitempty"` + Locks *APicLocks +} + +func (p *PicCNvPicPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + // Loop through XML tokens + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + // Switch based on token type + switch tt := t.(type) { + case xml.StartElement: + switch tt.Name.Local { + case "picLocks": + var value APicLocks + value.NoChangeAspect, err = strconv.Atoi(getAtt(tt.Attr, "noChangeAspect")) + if err != nil { + return err + } + p.Locks = &value + default: + continue + } + } + } + return nil +} + +// APicLocks represents the locks applied to a picture. +type APicLocks struct { + XMLName xml.Name `xml:"a:picLocks,omitempty"` + NoChangeAspect int `xml:"noChangeAspect,attr"` +} + // PICNonVisualDrawingProperties represents the non-visual drawing properties of a picture in a Word document. type PICNonVisualDrawingProperties struct { XMLName xml.Name `xml:"pic:cNvPr,omitempty"` diff --git a/structpara.go b/structpara.go index 2806518..effe6b4 100644 --- a/structpara.go +++ b/structpara.go @@ -35,16 +35,10 @@ func (p *ParagraphProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen } -type ParagraphChild struct { - Link *Hyperlink `xml:"w:hyperlink,omitempty"` - Run *Run `xml:"w:r,omitempty"` - Properties *RunProperties `xml:"w:rPr,omitempty"` -} - type Paragraph struct { XMLName xml.Name `xml:"w:p,omitempty"` Properties *ParagraphProperties - Children []ParagraphChild // Children will generate an unnecessary tag ... and we skip it by a self-defined xml.Marshaler + Children []interface{} // Children will generate an unnecessary tag ... and we skip it by a self-defined xml.Marshaler file *Docx } @@ -52,10 +46,10 @@ type Paragraph struct { func (p *Paragraph) String() string { sb := strings.Builder{} for _, c := range p.Children { - switch { - case c.Link != nil: - id := c.Link.ID - text := c.Link.Run.InstrText + switch o := c.(type) { + case *Hyperlink: + id := o.ID + text := o.Run.InstrText link, err := p.file.ReferTarget(id) sb.WriteString(text) sb.WriteByte('(') @@ -65,9 +59,9 @@ func (p *Paragraph) String() string { sb.WriteString(link) } sb.WriteByte(')') - case c.Run != nil: + case *Run: sb.WriteString("run") //TODO: implement - case c.Properties != nil: + case *RunProperties: sb.WriteString("prop") //TODO: implement default: continue @@ -88,16 +82,7 @@ func (p *Paragraph) MarshalXML(e *xml.Encoder, start xml.StartElement) error { } } for _, c := range p.Children { - switch { - case c.Link != nil: - err = e.Encode(c.Link) - case c.Run != nil: - err = e.Encode(c.Run) - case c.Properties != nil: - err = e.Encode(c.Properties) - default: - continue - } + e.Encode(c) if err != nil { return err } @@ -106,7 +91,7 @@ func (p *Paragraph) MarshalXML(e *xml.Encoder, start xml.StartElement) error { } func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - children := make([]ParagraphChild, 0, 64) + children := make([]interface{}, 0, 64) for { t, err := d.Token() if err == io.EOF { @@ -117,7 +102,7 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { } switch tt := t.(type) { case xml.StartElement: - var elem ParagraphChild + var elem interface{} switch tt.Name.Local { case "hyperlink": var value Hyperlink @@ -130,15 +115,15 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { if anchor != "" { value.ID = anchor } - elem.Link = &value + elem = &value case "r": var value Run d.DecodeElement(&value, &tt) - elem.Run = &value + elem = &value case "rPr": var value RunProperties d.DecodeElement(&value, &tt) - elem.Properties = &value + elem = &value case "pPr": var value ParagraphProperties d.DecodeElement(&value, &tt) diff --git a/structrun.go b/structrun.go index 396bf67..aab8573 100644 --- a/structrun.go +++ b/structrun.go @@ -10,7 +10,7 @@ import ( type Run struct { XMLName xml.Name `xml:"w:r,omitempty"` RunProperties *RunProperties `xml:"w:rPr,omitempty"` - FrontTab []struct { + FrontTab []struct { // TODO: replace with variable []RunChild XMLName xml.Name `xml:"w:tab,omitempty"` } InstrText string `xml:"w:instrText,omitempty"`