diff --git a/apidrawing.go b/apidrawing.go index b496d13..3219b88 100644 --- a/apidrawing.go +++ b/apidrawing.go @@ -15,9 +15,10 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) { if err != nil { return nil, err } - id := strconv.Itoa(int(atomic.AddUintptr(&p.file.imageId, 1))) + idn := int(atomic.AddUintptr(&p.file.imageId, 1)) + id := strconv.Itoa(idn) rId := p.file.addImage(Media{Name: "image" + id + "." + format, Data: pic}) - w, h := sz.Width, sz.Height + w, h := int64(sz.Width), int64(sz.Height) if float64(w)/float64(h) > 1.2 { h = A4_EMU_MAX_WIDTH * h / w w = A4_EMU_MAX_WIDTH @@ -36,7 +37,7 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) { }, EffectExtent: &WPEffectExtent{}, DocPr: &WPDocPr{ - ID: id, + ID: idn, Name: "图片 " + id, }, CNvGraphicFramePr: &WPCNvGraphicFramePr{ @@ -96,7 +97,7 @@ func (p *Paragraph) AddInlineDrawingFrom(file string) (*Run, error) { } // Size of the inline drawing by EMU -func (in *WPInline) Size(w, h int) { +func (in *WPInline) Size(w, h int64) { if in.Extent != nil { in.Extent.CX = w in.Extent.CY = h @@ -106,3 +107,111 @@ func (in *WPInline) Size(w, h int) { in.Graphic.GraphicData.Pic.SpPr.Xfrm.Ext.CY = h } } + +// AddAnchorDrawing adds inline drawing to paragraph +func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) { + sz, format, err := imgsz.DecodeSize(bytes.NewReader(pic)) + if err != nil { + return nil, err + } + idn := int(atomic.AddUintptr(&p.file.imageId, 1)) + id := strconv.Itoa(idn) + rId := p.file.addImage(Media{Name: "image" + id + "." + format, Data: pic}) + w, h := int64(sz.Width), int64(sz.Height) + if float64(w)/float64(h) > 1.2 { + h = A4_EMU_MAX_WIDTH * h / w + w = A4_EMU_MAX_WIDTH + } else { + h = A4_EMU_MAX_WIDTH * h / w / 2 + w = A4_EMU_MAX_WIDTH / 2 + } + d := &Drawing{ + Anchor: &WPAnchor{ + LayoutInCell: 1, + AllowOverlap: 1, + + SimplePosXY: &WPSimplePos{}, + PositionH: &WPPositionH{ + RelativeFrom: "column", + }, + PositionV: &WPPositionV{ + RelativeFrom: "paragraph", + }, + + Extent: &WPExtent{ + CX: w, + CY: h, + }, + EffectExtent: &WPEffectExtent{}, + WrapNone: &struct{}{}, + DocPr: &WPDocPr{ + ID: idn, + Name: "图片 " + id, + }, + CNvGraphicFramePr: &WPCNvGraphicFramePr{ + Locks: &AGraphicFrameLocks{ + NoChangeAspect: 1, + }, + }, + Graphic: &AGraphic{ + XMLA: XMLNS_DRAWINGML_MAIN, + GraphicData: &AGraphicData{ + URI: XMLNS_PICTURE, + Pic: &PICPic{ + XMLPIC: XMLNS_DRAWINGML_PICTURE, + NonVisualPicProperties: &PICNonVisualPicProperties{ + NonVisualDrawingProperties: PICNonVisualDrawingProperties{ + ID: id, + Name: "图片 " + id, + }, + }, + BlipFill: &PICBlipFill{ + Blip: ABlip{ + Embed: rId, + Cstate: "print", + }, + }, + SpPr: &PICSpPr{ + Xfrm: AXfrm{ + Ext: AExt{ + CX: w, + CY: h, + }, + }, + PrstGeom: APrstGeom{ + Prst: "rect", + }, + }, + }, + }, + }, + }, + } + run := &Run{ + Drawing: d, + RunProperties: &RunProperties{}, + } + p.Children = append(p.Children, ParagraphChild{Run: run}) + return run, nil +} + +// AddInlineDrawingFrom adds drawing from file to paragraph +func (p *Paragraph) AddAnchorDrawingFrom(file string) (*Run, error) { + data, err := os.ReadFile(file) + if err != nil { + return nil, err + } + return p.AddAnchorDrawing(data) +} + +// Size of the anchor drawing by EMU +func (a *WPAnchor) Size(w, h int64) { + if a.Extent != nil { + a.Extent.CX = w + a.Extent.CY = h + } + if a.Graphic != nil && a.Graphic.GraphicData != nil && a.Graphic.GraphicData.Pic != nil && a.Graphic.GraphicData.Pic.SpPr != nil { + a.Graphic.GraphicData.Pic.SpPr.Xfrm.Ext.CX = w + a.Graphic.GraphicData.Pic.SpPr.Xfrm.Ext.CY = h + } +} diff --git a/cmd/main/main.go b/cmd/main/main.go index f178bd5..528e043 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -20,6 +20,14 @@ func main() { w := docxlib.NewA4() // add new paragraph para1 := w.AddParagraph().Justification("distribute") + r, err := para1.AddAnchorDrawingFrom("testdata/fumiama.JPG") + if err != nil { + panic(err) + } + r.Drawing.Anchor.Size(r.Drawing.Anchor.Extent.CX/4, r.Drawing.Anchor.Extent.CY/4) + r.Drawing.Anchor.BehindDoc = 1 + r.Drawing.Anchor.PositionH.PosOffset = r.Drawing.Anchor.Extent.CX + r.Drawing.Anchor.Graphic.GraphicData.Pic.BlipFill.Blip.AlphaModFix = &docxlib.AAlphaModFix{Amount: 50000} // add text para1.AddText("test") para1.AddText("test font size").Size("44") @@ -36,7 +44,7 @@ func main() { para3.AddText("一行2个 inline").Size("44") para4 := w.AddParagraph().Justification("center") - r, err := para4.AddInlineDrawingFrom("testdata/fumiama.JPG") + r, err = para4.AddInlineDrawingFrom("testdata/fumiama.JPG") if err != nil { panic(err) } @@ -93,7 +101,12 @@ func main() { fmt.Printf("\tWe've found a new run with the text ->%s\n", child.Run.Text.Text) } if child.Run.Drawing != nil { - fmt.Printf("\tWe've found a new run with the drawing ->%s\n", child.Run.Drawing.Inline.DocPr.Name) // TODO: replace to refid + 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 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 child.Link != nil { diff --git a/structdoc.go b/structdoc.go index 286b111..771e52c 100644 --- a/structdoc.go +++ b/structdoc.go @@ -60,7 +60,7 @@ func (doc *Document) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error case "body": case "p": var value Paragraph - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) if len(value.Children) > 0 { value.file = doc.file doc.Body.Paragraphs = append(doc.Body.Paragraphs, &value) diff --git a/structdoc_test.go b/structdoc_test.go index f647a69..3e76fa4 100644 --- a/structdoc_test.go +++ b/structdoc_test.go @@ -111,7 +111,7 @@ const drawing_doc = ` - + @@ -175,7 +175,7 @@ const drawing_doc = ` - + @@ -221,7 +221,7 @@ const drawing_doc = ` - + @@ -288,7 +288,7 @@ const drawing_doc = ` - + @@ -447,7 +447,7 @@ const drawing_doc = ` - + 2935605 @@ -543,34 +543,26 @@ func TestUnmarshalDrawingStructure(t *testing.T) { if child.Run != nil && child.Run.Drawing != nil { t.Log("fild drawing at aragraph", i, ", child", j) if child.Run.Drawing.Inline != nil { - /*anchor := "mock-anchor-p" + string(rune('0'+i)) + "-c" + string(rune('0'+j)) - edit := "mock-edit-p" + string(rune('0'+i)) + "-c" + string(rune('0'+j)) - if anchor != child.Run.Drawing.Inline.AnchorID { - t.Fatal("expect", anchor, "but got", child.Run.Drawing.Inline.AnchorID) + 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") } - if edit != child.Run.Drawing.Inline.EditID { - t.Fatal("expect", edit, "but got", child.Run.Drawing.Inline.EditID) - }*/ - if child.Run.Drawing.Inline.Graphic != nil && child.Run.Drawing.Inline.Graphic.GraphicData != nil { - t.Log(child.Run.Drawing.Inline.Graphic.GraphicData.URI) - if child.Run.Drawing.Inline.Graphic.GraphicData.Pic != nil { - t.Log(child.Run.Drawing.Inline.Graphic.GraphicData.Pic.NonVisualPicProperties.NonVisualDrawingProperties.ID, child.Run.Drawing.Inline.Graphic.GraphicData.Pic.BlipFill.Blip.Embed) - } - } - } } } } } -func TestMarshalDrawingStructure(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} para2 := w.AddParagraph().Justification("center") para2.AddInlineDrawingFrom("testdata/fumiama.JPG") para2.AddTab().AddTab().AppendTab().AppendTab() @@ -579,7 +571,7 @@ func TestMarshalDrawingStructure(t *testing.T) { para3 := w.AddParagraph() para3.AddInlineDrawingFrom("testdata/fumiamayoko.png") - f, err := os.Create("TestMarshalDrawingStructure_Marshal.xml") + f, err := os.Create("TestMarshalInlineDrawingStructure.xml") if err != nil { t.Fatal(err) } @@ -597,7 +589,7 @@ func TestMarshalDrawingStructure(t *testing.T) { if err != nil { t.Fatal(err) } - f1, err := os.Create("TestMarshalDrawingStructure_Unmarshal.xml") + f1, err := os.Create("TestUnmarshalInlineDrawingStructure.xml") if err != nil { t.Fatal(err) } @@ -628,8 +620,5 @@ func TestMarshalDrawingStructure(t *testing.T) { md52 := h.Sum64() if md51 != md52 { t.Fail() - } /* else { - _ = os.Remove("TestMarshalDrawingStructure_Marshal.xml") - _ = os.Remove("TestMarshalDrawingStructure_Unmarshal.xml") - }*/ + } } diff --git a/structdrawing.go b/structdrawing.go index 7d49921..7402bb1 100644 --- a/structdrawing.go +++ b/structdrawing.go @@ -20,6 +20,7 @@ const ( type Drawing struct { XMLName xml.Name `xml:"w:drawing,omitempty"` Inline *WPInline + Anchor *WPAnchor } func (r *Drawing) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { @@ -37,25 +38,10 @@ func (r *Drawing) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "inline": r.Inline = new(WPInline) - r.Inline.DistT, err = strconv.Atoi(getAtt(tt.Attr, "distT")) - if err != nil { - return err - } - r.Inline.DistB, err = strconv.Atoi(getAtt(tt.Attr, "distB")) - if err != nil { - return err - } - r.Inline.DistL, err = strconv.Atoi(getAtt(tt.Attr, "distL")) - if err != nil { - return err - } - r.Inline.DistR, err = strconv.Atoi(getAtt(tt.Attr, "distR")) - if err != nil { - return err - } - // r.Inline.AnchorID = getAtt(tt.Attr, "anchorId") - // r.Inline.EditID = getAtt(tt.Attr, "editId") - d.DecodeElement(r.Inline, &start) + d.DecodeElement(r.Inline, &tt) + case "anchor": + r.Anchor = new(WPAnchor) + d.DecodeElement(r.Anchor, &tt) default: continue } @@ -69,10 +55,10 @@ func (r *Drawing) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // WPInline wp:inline type WPInline struct { XMLName xml.Name `xml:"wp:inline,omitempty"` - DistT int `xml:"distT,attr"` - DistB int `xml:"distB,attr"` - DistL int `xml:"distL,attr"` - DistR int `xml:"distR,attr"` + DistT int64 `xml:"distT,attr"` + DistB int64 `xml:"distB,attr"` + DistL int64 `xml:"distL,attr"` + DistR int64 `xml:"distR,attr"` // AnchorID string `xml:"wp14:anchorId,attr,omitempty"` // EditID string `xml:"wp14:editId,attr,omitempty"` @@ -83,7 +69,33 @@ type WPInline struct { Graphic *AGraphic } -func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { +func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "distT": + r.DistT, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return + } + case "distB": + r.DistB, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return + } + case "distL": + r.DistL, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return + } + case "distR": + r.DistR, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return + } + default: + // ignore other attributes + } + } for { t, err := d.Token() if err == io.EOF { @@ -98,46 +110,42 @@ func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "extent": r.Extent = new(WPExtent) - r.Extent.CX, err = strconv.Atoi(getAtt(tt.Attr, "cx")) + r.Extent.CX, err = strconv.ParseInt(getAtt(tt.Attr, "cx"), 10, 64) if err != nil { return err } - r.Extent.CY, err = strconv.Atoi(getAtt(tt.Attr, "cy")) + r.Extent.CY, err = strconv.ParseInt(getAtt(tt.Attr, "cy"), 10, 64) if err != nil { return err } case "effectExtent": r.EffectExtent = new(WPEffectExtent) - r.EffectExtent.L, err = strconv.Atoi(getAtt(tt.Attr, "l")) + r.EffectExtent.L, err = strconv.ParseInt(getAtt(tt.Attr, "l"), 10, 64) if err != nil { return err } - r.EffectExtent.T, err = strconv.Atoi(getAtt(tt.Attr, "t")) + r.EffectExtent.T, err = strconv.ParseInt(getAtt(tt.Attr, "t"), 10, 64) if err != nil { return err } - r.EffectExtent.R, err = strconv.Atoi(getAtt(tt.Attr, "r")) + r.EffectExtent.R, err = strconv.ParseInt(getAtt(tt.Attr, "r"), 10, 64) if err != nil { return err } - r.EffectExtent.B, err = strconv.Atoi(getAtt(tt.Attr, "b")) + r.EffectExtent.B, err = strconv.ParseInt(getAtt(tt.Attr, "b"), 10, 64) if err != nil { return err } case "docPr": r.DocPr = new(WPDocPr) - r.DocPr.ID = getAtt(tt.Attr, "id") - r.DocPr.Name = getAtt(tt.Attr, "name") - r.DocPr.Macro = getAtt(tt.Attr, "macro") - r.DocPr.Hidden = getAtt(tt.Attr, "hidden") + d.DecodeElement(r.DocPr, &tt) case "cNvGraphicFramePr": var value WPCNvGraphicFramePr - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) r.CNvGraphicFramePr = &value case "graphic": var value AGraphic - d.DecodeElement(&value, &start) - value.XMLA = getAtt(tt.Attr, "a") + d.DecodeElement(&value, &tt) r.Graphic = &value default: continue @@ -154,26 +162,106 @@ func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // CX CY 's unit is English Metric Units, which is 1/914400 inch type WPExtent struct { XMLName xml.Name `xml:"wp:extent,omitempty"` - CX int `xml:"cx,attr"` - CY int `xml:"cy,attr"` + CX int64 `xml:"cx,attr"` + CY int64 `xml:"cy,attr"` +} + +func (r *WPExtent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var err error + for _, attr := range start.Attr { + switch attr.Name.Local { + case "cx": + r.CX, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "cy": + r.CY, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + } + } + // Consume the end element + _, err = d.Token() + if err != nil { + return err + } + return nil } // WPEffectExtent represents the effect extent of a drawing in a Word document. type WPEffectExtent struct { XMLName xml.Name `xml:"wp:effectExtent,omitempty"` - L int `xml:"l,attr"` - T int `xml:"t,attr"` - R int `xml:"r,attr"` - B int `xml:"b,attr"` + L int64 `xml:"l,attr"` + T int64 `xml:"t,attr"` + R int64 `xml:"r,attr"` + B int64 `xml:"b,attr"` +} + +func (r *WPEffectExtent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var err error + for _, attr := range start.Attr { + switch attr.Name.Local { + case "l": + r.L, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "t": + r.T, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "r": + r.R, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "b": + r.B, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + } + } + // Consume the end element + _, err = d.Token() + if err != nil { + return err + } + return nil } // WPDocPr represents the document properties of a drawing in a Word document. type WPDocPr struct { XMLName xml.Name `xml:"wp:docPr,omitempty"` - ID string `xml:"id,attr"` + ID int `xml:"id,attr"` Name string `xml:"name,attr,omitempty"` - Macro string `xml:"macro,attr,omitempty"` - Hidden string `xml:"hidden,attr,omitempty"` +} + +func (r *WPDocPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "id": + id, err := strconv.Atoi(attr.Value) + if err != nil { + return err + } + r.ID = id + case "name": + r.Name = attr.Value + + default: + // ignore other attributes + } + } + // Consume the end element + _, err := d.Token() + if err != nil { + return err + } + return nil } // WPCNvGraphicFramePr represents the non-visual properties of a graphic frame. @@ -197,7 +285,7 @@ func (w *WPCNvGraphicFramePr) UnmarshalXML(d *xml.Decoder, start xml.StartElemen switch tt.Name.Local { case "graphicFrameLocks": var value AGraphicFrameLocks - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) value.NoChangeAspect, err = strconv.Atoi(getAtt(tt.Attr, "noChangeAspect")) if err != nil { return err @@ -226,6 +314,14 @@ type AGraphic struct { } func (a *AGraphic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "a": + a.XMLA = attr.Value + default: + // ignore other attributes + } + } for { t, err := d.Token() if err == io.EOF { @@ -240,7 +336,7 @@ func (a *AGraphic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "graphicData": var value AGraphicData - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) value.URI = getAtt(tt.Attr, "uri") a.GraphicData = &value default: @@ -274,7 +370,7 @@ func (a *AGraphicData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro switch tt.Name.Local { case "pic": var value PICPic - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) value.XMLPIC = getAtt(tt.Attr, "pic") a.Pic = &value default: @@ -310,15 +406,15 @@ func (p *PICPic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "nvPicPr": var value PICNonVisualPicProperties - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) p.NonVisualPicProperties = &value case "blipFill": var value PICBlipFill - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) p.BlipFill = &value case "spPr": var value PICSpPr - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) p.SpPr = &value default: continue @@ -389,10 +485,9 @@ func (p *PICBlipFill) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error case xml.StartElement: switch tt.Name.Local { case "blip": - p.Blip.Embed = getAtt(tt.Attr, "embed") - p.Blip.Cstate = getAtt(tt.Attr, "cstate") + d.DecodeElement(&p.Blip, &tt) case "stretch": - d.DecodeElement(&p.Stretch, &start) + d.DecodeElement(&p.Stretch, &tt) default: continue } @@ -404,9 +499,54 @@ func (p *PICBlipFill) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error // ABlip represents the blip of a picture in a Word document. type ABlip struct { - XMLName xml.Name `xml:"a:blip,omitempty"` - Embed string `xml:"r:embed,attr"` - Cstate string `xml:"cstate,attr"` + XMLName xml.Name `xml:"a:blip,omitempty"` + Embed string `xml:"r:embed,attr"` + Cstate string `xml:"cstate,attr"` + AlphaModFix *AAlphaModFix +} + +func (a *ABlip) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "embed": + a.Embed = attr.Value + case "cstate": + a.Cstate = attr.Value + default: + // ignore other attributes + } + } + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + switch tt := t.(type) { + case xml.StartElement: + switch tt.Name.Local { + case "alphaModFix": + var value AAlphaModFix + value.Amount, err = strconv.Atoi(getAtt(tt.Attr, "amt")) + if err != nil { + return err + } + a.AlphaModFix = &value + default: + continue + } + } + + } + return nil +} + +type AAlphaModFix struct { + XMLName xml.Name `xml:"a:alphaModFix,omitempty"` + Amount int `xml:"amt,attr"` } // AStretch ... @@ -442,9 +582,9 @@ func (p *PICSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { case xml.StartElement: switch tt.Name.Local { case "xfrm": - d.DecodeElement(&p.Xfrm, &start) + d.DecodeElement(&p.Xfrm, &tt) case "prstGeom": - d.DecodeElement(&p.PrstGeom, &start) + d.DecodeElement(&p.PrstGeom, &tt) p.PrstGeom.Prst = getAtt(tt.Attr, "prst") default: continue @@ -459,11 +599,35 @@ func (p *PICSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // which describes the position and size of a shape. type AXfrm struct { XMLName xml.Name `xml:"a:xfrm,omitempty"` + Rot int64 `xml:"rot,attr,omitempty"` + FlipH int `xml:"flipH,attr,omitempty"` + FlipV int `xml:"flipV,attr,omitempty"` Off AOff Ext AExt } -func (a *AXfrm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { +func (a *AXfrm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "rot": + a.Rot, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "flipH": + a.FlipH, err = strconv.Atoi(attr.Value) + if err != nil { + return err + } + case "flipV": + a.FlipV, err = strconv.Atoi(attr.Value) + if err != nil { + return err + } + default: + // ignore other attributes + } + } for { t, err := d.Token() if err == io.EOF { @@ -477,20 +641,20 @@ func (a *AXfrm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { case xml.StartElement: switch tt.Name.Local { case "off": - a.Off.X, err = strconv.Atoi(getAtt(tt.Attr, "x")) + a.Off.X, err = strconv.ParseInt(getAtt(tt.Attr, "x"), 10, 64) if err != nil { return err } - a.Off.Y, err = strconv.Atoi(getAtt(tt.Attr, "y")) + a.Off.Y, err = strconv.ParseInt(getAtt(tt.Attr, "y"), 10, 64) if err != nil { return err } case "ext": - a.Ext.CX, err = strconv.Atoi(getAtt(tt.Attr, "cx")) + a.Ext.CX, err = strconv.ParseInt(getAtt(tt.Attr, "cx"), 10, 64) if err != nil { return err } - a.Ext.CY, err = strconv.Atoi(getAtt(tt.Attr, "cy")) + a.Ext.CY, err = strconv.ParseInt(getAtt(tt.Attr, "cy"), 10, 64) if err != nil { return err } @@ -507,16 +671,16 @@ func (a *AXfrm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // which describes the offset of a shape from its original position. type AOff struct { XMLName xml.Name `xml:"a:off,omitempty"` - X int `xml:"x,attr"` - Y int `xml:"y,attr"` + X int64 `xml:"x,attr"` + Y int64 `xml:"y,attr"` } // AExt is a struct representing the element in OpenXML, // which describes the size of a shape. type AExt struct { XMLName xml.Name `xml:"a:ext,omitempty"` - CX int `xml:"cx,attr"` - CY int `xml:"cy,attr"` + CX int64 `xml:"cx,attr"` + CY int64 `xml:"cy,attr"` } // APrstGeom is a struct representing the element in OpenXML, @@ -566,3 +730,233 @@ func (a *AAvLst) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error return nil } + +// WPAnchor wp:anchor +type WPAnchor struct { + XMLName xml.Name `xml:"wp:anchor,omitempty"` + DistT int64 `xml:"distT,attr"` + DistB int64 `xml:"distB,attr"` + DistL int64 `xml:"distL,attr"` + DistR int64 `xml:"distR,attr"` + SimplePos int `xml:"simplePos,attr"` + RelativeHeight int `xml:"relativeHeight,attr"` + BehindDoc int `xml:"behindDoc,attr"` + Locked int `xml:"locked,attr"` + LayoutInCell int `xml:"layoutInCell,attr"` + AllowOverlap int `xml:"allowOverlap,attr"` + + SimplePosXY *WPSimplePos + PositionH *WPPositionH + PositionV *WPPositionV + Extent *WPExtent + EffectExtent *WPEffectExtent + WrapNone *struct{} `xml:"wp:wrapNone,omitempty"` + WrapSquare *WPWrapSquare + DocPr *WPDocPr + CNvGraphicFramePr *WPCNvGraphicFramePr + Graphic *AGraphic +} + +func (r *WPAnchor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, tt := range start.Attr { + switch tt.Name.Local { + case "distT": + r.DistT, err = strconv.ParseInt(tt.Value, 10, 64) + if err != nil { + return err + } + case "distB": + r.DistB, err = strconv.ParseInt(tt.Value, 10, 64) + if err != nil { + return err + } + case "distL": + r.DistL, err = strconv.ParseInt(tt.Value, 10, 64) + if err != nil { + return err + } + case "distR": + r.DistR, err = strconv.ParseInt(tt.Value, 10, 64) + if err != nil { + return err + } + case "simplePos": + r.SimplePos, err = strconv.Atoi(tt.Value) + if err != nil { + return err + } + case "relativeHeight": + r.RelativeHeight, err = strconv.Atoi(tt.Value) + if err != nil { + return err + } + case "behindDoc": + r.BehindDoc, err = strconv.Atoi(tt.Value) + if err != nil { + return err + } + case "locked": + r.Locked, err = strconv.Atoi(tt.Value) + if err != nil { + return err + } + case "layoutInCell": + r.LayoutInCell, err = strconv.Atoi(tt.Value) + if err != nil { + return err + } + case "allowOverlap": + r.AllowOverlap, err = strconv.Atoi(tt.Value) + if err != nil { + return err + } + } + } + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + switch tt := t.(type) { + case xml.StartElement: + switch tt.Name.Local { + case "simplePos": + r.SimplePosXY = new(WPSimplePos) + r.SimplePosXY.X, err = strconv.ParseInt(getAtt(tt.Attr, "x"), 10, 64) + if err != nil { + return err + } + r.SimplePosXY.Y, err = strconv.ParseInt(getAtt(tt.Attr, "y"), 10, 64) + if err != nil { + return err + } + case "positionH": + r.PositionH = new(WPPositionH) + // r.PositionH.RelativeFrom = getAtt(tt.Attr, "relativeFrom") + d.DecodeElement(&r.PositionH, &tt) + case "positionV": + r.PositionV = new(WPPositionV) + // r.PositionV.RelativeFrom = getAtt(tt.Attr, "relativeFrom") + d.DecodeElement(&r.PositionV, &tt) + case "extent": + r.Extent = new(WPExtent) + d.DecodeElement(&r.Extent, &tt) + case "effectExtent": + r.EffectExtent = new(WPEffectExtent) + d.DecodeElement(&r.EffectExtent, &tt) + case "wrapNone": + r.WrapNone = &struct{}{} + case "wrapSquare": + r.WrapSquare = new(WPWrapSquare) + r.WrapSquare.WrapText = getAtt(tt.Attr, "wrapText") + case "docPr": + r.DocPr = new(WPDocPr) + d.DecodeElement(r.DocPr, &tt) + case "cNvGraphicFramePr": + r.CNvGraphicFramePr = new(WPCNvGraphicFramePr) + d.DecodeElement(r.CNvGraphicFramePr, &tt) + case "graphic": + r.Graphic = new(AGraphic) + d.DecodeElement(&r.Graphic, &tt) + default: + continue + } + } + } + return nil +} + +// WPSimplePos represents the position of an object in a Word document. +type WPSimplePos struct { + XMLName xml.Name `xml:"wp:simplePos,omitempty"` + X int64 `xml:"x,attr"` + Y int64 `xml:"y,attr"` +} + +// WPPositionH represents the horizontal position of an object in a Word document. +type WPPositionH struct { + XMLName xml.Name `xml:"wp:positionH,omitempty"` + RelativeFrom string `xml:"relativeFrom,attr"` + PosOffset int64 `xml:"wp:posOffset"` +} + +func (r *WPPositionH) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "relativeFrom": + r.RelativeFrom = attr.Value + } + } + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + switch tt := t.(type) { + case xml.StartElement: + switch tt.Name.Local { + case "posOffset": + err = d.DecodeElement(&r.PosOffset, &tt) + if err != nil { + return err + } + default: + continue + } + } + } + return nil +} + +// WPPositionV represents the vertical position of an object in a Word document. +type WPPositionV struct { + XMLName xml.Name `xml:"wp:positionV,omitempty"` + RelativeFrom string `xml:"relativeFrom,attr"` + PosOffset int64 `xml:"wp:posOffset"` +} + +func (r *WPPositionV) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "relativeFrom": + r.RelativeFrom = attr.Value + } + } + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + switch tt := t.(type) { + case xml.StartElement: + switch tt.Name.Local { + case "posOffset": + err = d.DecodeElement(&r.PosOffset, &tt) + if err != nil { + return err + } + default: + continue + } + } + } + return nil +} + +// WPWrapSquare represents the square wrapping of an object in a Word document. +type WPWrapSquare struct { + XMLName xml.Name `xml:"wp:wrapSquare,omitempty"` + WrapText string `xml:"wrapText,attr"` +} diff --git a/structlink.go b/structlink.go index 0f63296..4720fb8 100644 --- a/structlink.go +++ b/structlink.go @@ -25,7 +25,7 @@ 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, &start) + d.DecodeElement(&r.Run, &tt) } else { continue } diff --git a/structpara.go b/structpara.go index 6b5887a..2806518 100644 --- a/structpara.go +++ b/structpara.go @@ -121,7 +121,7 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "hyperlink": var value Hyperlink - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) id := getAtt(tt.Attr, "id") anchor := getAtt(tt.Attr, "anchor") if id != "" { @@ -133,15 +133,15 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { elem.Link = &value case "r": var value Run - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) elem.Run = &value case "rPr": var value RunProperties - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) elem.Properties = &value case "pPr": var value ParagraphProperties - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) p.Properties = &value continue default: diff --git a/structrun.go b/structrun.go index cefca80..396bf67 100644 --- a/structrun.go +++ b/structrun.go @@ -36,19 +36,19 @@ func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "rPr": var value RunProperties - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) r.RunProperties = &value case "instrText": var value string - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) r.InstrText = value case "t": var value Text - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) r.Text = &value case "drawing": var value Drawing - d.DecodeElement(&value, &start) + d.DecodeElement(&value, &tt) r.Drawing = &value case "tab": if r.InstrText == "" && r.Text == nil && r.Drawing == nil {