From 64b3464cbf43cb4e2fdea1fcd8e780cb809f4cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 2 Mar 2023 18:07:53 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=8C=E5=96=84=20table=20?= =?UTF-8?q?canvas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apidrawing.go | 4 +- apishape.go | 83 ++++++- cmd/main/main.go | 15 +- empty.go | 14 +- id.go | 20 ++ structcanvas.go | 25 +- structdrawing.go | 41 +++- structeffects.go | 64 +++++- structpara.go | 93 ++++++-- structrel_test.go | 12 +- structrun.go | 35 ++- structshape.go | 441 +++++++++++++++++++++++++++++++++++- structshape_test.go | 38 +++- structtable.go | 8 +- xml/a4/[Content_Types].xml | 2 - xml/a4/word/settings.xml | 56 ----- xml/a4/word/webSettings.xml | 11 - 17 files changed, 806 insertions(+), 156 deletions(-) delete mode 100755 xml/a4/word/settings.xml delete mode 100755 xml/a4/word/webSettings.xml diff --git a/apidrawing.go b/apidrawing.go index e20614e..90015ee 100644 --- a/apidrawing.go +++ b/apidrawing.go @@ -69,7 +69,7 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) { XMLA: XMLNS_DRAWINGML_MAIN, GraphicData: &AGraphicData{ URI: XMLNS_PICTURE, - Pic: &PICPic{ + Pic: &Picture{ XMLPIC: XMLNS_DRAWINGML_PICTURE, NonVisualPicProperties: &PICNonVisualPicProperties{ NonVisualDrawingProperties: PICNonVisualDrawingProperties{ @@ -179,7 +179,7 @@ func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) { XMLA: XMLNS_DRAWINGML_MAIN, GraphicData: &AGraphicData{ URI: XMLNS_PICTURE, - Pic: &PICPic{ + Pic: &Picture{ XMLPIC: XMLNS_DRAWINGML_PICTURE, NonVisualPicProperties: &PICNonVisualPicProperties{ NonVisualDrawingProperties: PICNonVisualDrawingProperties{ diff --git a/apishape.go b/apishape.go index 9507111..2918aa3 100644 --- a/apishape.go +++ b/apishape.go @@ -1,3 +1,23 @@ +/* + Copyright (c) 2020 gingfrederik + Copyright (c) 2021 Gonzalo Fernandez-Victorio + Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com) + Copyright (c) 2023 Fumiama Minamoto (源文雨) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package docx import ( @@ -5,8 +25,63 @@ import ( "sync/atomic" ) -// AddShape adds wsp named drawing to paragraph -func (p *Paragraph) AddShape(w, h int64, name, bwMode, prst string, elems []interface{}) (*Run, error) { +// AddInlineShape adds wsp named drawing to paragraph +func (p *Paragraph) AddInlineShape(w, h int64, name, bwMode, prst string, ln *ALine) (*Run, error) { + idn := int(atomic.AddUintptr(&p.file.docID, 1)) + id := strconv.Itoa(int(p.file.IncreaseID(name))) + d := &Drawing{ + Inline: &WPInline{ + Extent: &WPExtent{ + CX: w, + CY: h, + }, + EffectExtent: &WPEffectExtent{}, + DocPr: &WPDocPr{ + ID: idn, + Name: name + " " + id, + }, + CNvGraphicFramePr: &WPCNvGraphicFramePr{}, + Graphic: &AGraphic{ + XMLA: XMLNS_DRAWINGML_MAIN, + GraphicData: &AGraphicData{ + URI: XMLNS_WPS, + Shape: &WordprocessingShape{ + CNvCnPr: &WPSCNvCnPr{ + ConnShapeLocks: &struct{}{}, + }, + SpPr: &WPSSpPr{ + BWMode: bwMode, + + Xfrm: AXfrm{ + Ext: AExt{ + CX: w, + CY: h, + }, + }, + PrstGeom: APrstGeom{ + Prst: prst, + }, + NoFill: &struct{}{}, + Line: ln, + }, + BodyPr: &WPSBodyPr{}, + }, + }, + }, + }, + } + c := make([]interface{}, 1, 64) + c[0] = d + run := &Run{ + RunProperties: &RunProperties{}, + Children: c, + } + p.Children = append(p.Children, run) + return run, nil +} + +// AddAnchorShape adds wsp named drawing to paragraph +func (p *Paragraph) AddAnchorShape(w, h int64, name, bwMode, prst string, ln *ALine) (*Run, error) { idn := int(atomic.AddUintptr(&p.file.docID, 1)) id := strconv.Itoa(int(p.file.IncreaseID(name))) d := &Drawing{ @@ -37,7 +112,7 @@ func (p *Paragraph) AddShape(w, h int64, name, bwMode, prst string, elems []inte XMLA: XMLNS_DRAWINGML_MAIN, GraphicData: &AGraphicData{ URI: XMLNS_WPS, - Shape: &WPSWordprocessingShape{ + Shape: &WordprocessingShape{ CNvCnPr: &WPSCNvCnPr{ ConnShapeLocks: &struct{}{}, }, @@ -54,7 +129,7 @@ func (p *Paragraph) AddShape(w, h int64, name, bwMode, prst string, elems []inte Prst: prst, }, NoFill: &struct{}{}, - Elems: elems, + Line: ln, }, BodyPr: &WPSBodyPr{}, }, diff --git a/cmd/main/main.go b/cmd/main/main.go index cdf9402..d6a391d 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -125,7 +125,7 @@ func main() { p := w.AddParagraph().Justification("center") p.AddText("测试 AutoShape w:ln").Size("44") - p.AddShape(808355, 238760, "AutoShape", "auto", "straightConnector1", []interface{}{ + p.AddAnchorShape(808355, 238760, "AutoShape", "auto", "straightConnector1", &docx.ALine{ W: 9525, SolidFill: &docx.ASolidFill{SrgbClr: &docx.ASrgbClr{Val: "000000"}}, @@ -133,7 +133,16 @@ func main() { HeadEnd: &docx.AHeadEnd{}, TailEnd: &docx.ATailEnd{}, }, - }) + ) + p.AddInlineShape(808355, 238760, "AutoShape", "auto", "straightConnector1", + &docx.ALine{ + W: 9525, + SolidFill: &docx.ASolidFill{SrgbClr: &docx.ASrgbClr{Val: "000000"}}, + Round: &struct{}{}, + HeadEnd: &docx.AHeadEnd{}, + TailEnd: &docx.ATailEnd{}, + }, + ) f, err := os.Create(*fileLocation) if err != nil { @@ -193,7 +202,7 @@ func main() { if len(c.Paragraphs) > 0 && len(c.Paragraphs[0].Children) > 0 { fmt.Printf("<%d> %v\t", y, &c.Paragraphs[0]) } else { - fmt.Print("\t") + fmt.Printf("<%d> \t\t", y) } } fmt.Print("\n") diff --git a/empty.go b/empty.go index 0d674c5..6f7451f 100644 --- a/empty.go +++ b/empty.go @@ -60,21 +60,11 @@ func newEmptyA4File() *Docx { 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", - }, }, }, media: make([]Media, 0, 64), mediaNameIdx: make(map[string]int, 64), - rID: 5, + rID: 3, slowIDs: make(map[string]uintptr, 64), template: "a4", tmpfslst: []string{ @@ -83,9 +73,7 @@ func newEmptyA4File() *Docx { "docProps/core.xml", "word/theme/theme1.xml", "word/fontTable.xml", - "word/settings.xml", "word/styles.xml", - "word/webSettings.xml", "[Content_Types].xml", }, buf: bytes.NewBuffer(make([]byte, 0, 1024*1024)), diff --git a/id.go b/id.go index 005e92a..fb0bd60 100644 --- a/id.go +++ b/id.go @@ -1,3 +1,23 @@ +/* + Copyright (c) 2020 gingfrederik + Copyright (c) 2021 Gonzalo Fernandez-Victorio + Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com) + Copyright (c) 2023 Fumiama Minamoto (源文雨) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package docx func (f *Docx) IncreaseID(name string) (n uintptr) { diff --git a/structcanvas.go b/structcanvas.go index ada991e..bd93cbf 100644 --- a/structcanvas.go +++ b/structcanvas.go @@ -1,3 +1,23 @@ +/* + Copyright (c) 2020 gingfrederik + Copyright (c) 2021 Gonzalo Fernandez-Victorio + Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com) + Copyright (c) 2023 Fumiama Minamoto (源文雨) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package docx import ( @@ -12,6 +32,8 @@ type WordprocessingCanvas struct { Whole *WPCWhole Items []interface{} + + file *Docx } // UnmarshalXML ... @@ -40,7 +62,8 @@ func (c *WordprocessingCanvas) UnmarshalXML(d *xml.Decoder, start xml.StartEleme return err } case "wsp": - var value WPSWordprocessingShape + var value WordprocessingShape + value.file = c.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err diff --git a/structdrawing.go b/structdrawing.go index 87151d4..30ec665 100644 --- a/structdrawing.go +++ b/structdrawing.go @@ -44,6 +44,8 @@ type Drawing struct { XMLName xml.Name `xml:"w:drawing,omitempty"` Inline *WPInline Anchor *WPAnchor + + file *Docx } // UnmarshalXML ... @@ -61,12 +63,14 @@ func (r *Drawing) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "inline": r.Inline = new(WPInline) + r.Inline.file = r.file err = d.DecodeElement(r.Inline, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } case "anchor": r.Anchor = new(WPAnchor) + r.Anchor.file = r.file err = d.DecodeElement(r.Anchor, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err @@ -105,6 +109,8 @@ type WPInline struct { DocPr *WPDocPr CNvGraphicFramePr *WPCNvGraphicFramePr Graphic *AGraphic + + file *Docx } // UnmarshalXML ... @@ -191,6 +197,7 @@ func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err err r.CNvGraphicFramePr = &value case "graphic": var value AGraphic + value.file = r.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err @@ -360,6 +367,8 @@ type AGraphic struct { XMLName xml.Name `xml:"a:graphic,omitempty"` XMLA string `xml:"xmlns:a,attr,omitempty"` GraphicData *AGraphicData + + file *Docx } // UnmarshalXML ... @@ -385,6 +394,7 @@ func (a *AGraphic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "graphicData": var value AGraphicData + value.file = a.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err @@ -407,8 +417,11 @@ func (a *AGraphic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { type AGraphicData struct { XMLName xml.Name `xml:"a:graphicData,omitempty"` URI string `xml:"uri,attr"` - Pic *PICPic - Shape *WPSWordprocessingShape + Pic *Picture + Shape *WordprocessingShape + Canvas *WordprocessingCanvas + + file *Docx } // UnmarshalXML ... @@ -425,7 +438,7 @@ func (a *AGraphicData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro if tt, ok := t.(xml.StartElement); ok { switch tt.Name.Local { case "pic": - var value PICPic + var value Picture err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err @@ -433,12 +446,21 @@ func (a *AGraphicData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro value.XMLPIC = getAtt(tt.Attr, "pic") a.Pic = &value case "wsp": - var value WPSWordprocessingShape + var value WordprocessingShape + value.file = a.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } a.Shape = &value + case "wpc": + var value WordprocessingCanvas + value.file = a.file + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + a.Canvas = &value default: err = d.Skip() // skip unsupported tags if err != nil { @@ -451,8 +473,8 @@ func (a *AGraphicData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro return nil } -// PICPic represents a picture in a Word document. -type PICPic struct { +// Picture represents a picture in a Word document. +type Picture struct { XMLName xml.Name `xml:"pic:pic,omitempty"` XMLPIC string `xml:"xmlns:pic,attr,omitempty"` NonVisualPicProperties *PICNonVisualPicProperties @@ -461,7 +483,7 @@ type PICPic struct { } // UnmarshalXML ... -func (p *PICPic) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { +func (p *Picture) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for { t, err := d.Token() if err == io.EOF { @@ -649,7 +671,7 @@ func (p *PICBlipFill) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error type ABlip struct { XMLName xml.Name `xml:"a:blip,omitempty"` Embed string `xml:"r:embed,attr"` - Cstate string `xml:"cstate,attr"` + Cstate string `xml:"cstate,attr,omitempty"` AlphaModFix *AAlphaModFix } @@ -934,6 +956,8 @@ type WPAnchor struct { DocPr *WPDocPr CNvGraphicFramePr *WPCNvGraphicFramePr Graphic *AGraphic + + file *Docx } // UnmarshalXML ... @@ -1061,6 +1085,7 @@ func (r *WPAnchor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err err } case "graphic": r.Graphic = new(AGraphic) + r.Graphic.file = r.file err = d.DecodeElement(&r.Graphic, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err diff --git a/structeffects.go b/structeffects.go index abb5062..2dc0ad8 100644 --- a/structeffects.go +++ b/structeffects.go @@ -1,3 +1,23 @@ +/* + Copyright (c) 2020 gingfrederik + Copyright (c) 2021 Gonzalo Fernandez-Victorio + Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com) + Copyright (c) 2023 Fumiama Minamoto (源文雨) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package docx import "encoding/xml" @@ -27,6 +47,12 @@ type Size struct { Val string `xml:"w:val,attr"` } +// SizeCs contains the cs font size +type SizeCs struct { + XMLName xml.Name `xml:"w:szCs,omitempty"` + Val string `xml:"w:val,attr"` +} + // Bold ... type Bold struct { XMLName xml.Name `xml:"w:b,omitempty"` @@ -52,7 +78,7 @@ type Highlight struct { // Kern ... type Kern struct { XMLName xml.Name `xml:"w:kern,omitempty"` - Val int64 `xml:"w:val,attr,omitempty"` + Val int64 `xml:"w:val,attr"` } // Justification contains the way of the horizonal alignment @@ -68,6 +94,18 @@ type Justification struct { Val string `xml:"w:val,attr"` } +// TextAlignment ... +type TextAlignment struct { + XMLName xml.Name `xml:"w:textAlignment,omitempty"` + Val string `xml:"w:val,attr"` +} + +// VertAlign ... +type VertAlign struct { + XMLName xml.Name `xml:"w:vertAlign,omitempty"` + Val string `xml:"w:val,attr"` +} + // Shade is an element that represents a shading pattern applied to a document element. type Shade struct { XMLName xml.Name `xml:"w:shd,omitempty"` @@ -100,3 +138,27 @@ func (s *Shade) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { _, err := d.Token() return err } + +// AdjustRightInd ... +type AdjustRightInd struct { + XMLName xml.Name `xml:"w:adjustRightInd,omitempty"` + Val int `xml:"w:val,attr"` +} + +// SnapToGrid ... +type SnapToGrid struct { + XMLName xml.Name `xml:"w:snapToGrid,omitempty"` + Val int `xml:"w:val,attr"` +} + +// Kinsoku ... +type Kinsoku struct { + XMLName xml.Name `xml:"w:kinsoku,omitempty"` + Val int `xml:"w:val,attr"` +} + +// OverflowPunct ... +type OverflowPunct struct { + XMLName xml.Name `xml:"w:overflowPunct,omitempty"` + Val int `xml:"w:val,attr"` +} diff --git a/structpara.go b/structpara.go index d4aa430..9805f03 100644 --- a/structpara.go +++ b/structpara.go @@ -31,10 +31,18 @@ import ( // ParagraphProperties type ParagraphProperties struct { - XMLName xml.Name `xml:"w:pPr,omitempty"` - Justification *Justification - Shade *Shade - Kern *Kern + XMLName xml.Name `xml:"w:pPr,omitempty"` + Justification *Justification + Shade *Shade + Kern *Kern + Style *Style + TextAlignment *TextAlignment + AdjustRightInd *AdjustRightInd + SnapToGrid *SnapToGrid + Kinsoku *Kinsoku + OverflowPunct *OverflowPunct + + RunProperties *RunProperties } // UnmarshalXML ... @@ -69,6 +77,61 @@ func (p *ParagraphProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen return err } p.Kern = &value + case "rPr": + var value RunProperties + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + p.RunProperties = &value + case "pStyle": + p.Style = &Style{Val: getAtt(tt.Attr, "val")} + case "textAlignment": + p.TextAlignment = &TextAlignment{Val: getAtt(tt.Attr, "val")} + case "adjustRightInd": + var value AdjustRightInd + v := getAtt(tt.Attr, "val") + if v == "" { + continue + } + value.Val, err = strconv.Atoi(v) + if err != nil { + return err + } + p.AdjustRightInd = &value + case "snapToGrid": + var value SnapToGrid + v := getAtt(tt.Attr, "val") + if v == "" { + continue + } + value.Val, err = strconv.Atoi(v) + if err != nil { + return err + } + p.SnapToGrid = &value + case "kinsoku": + var value Kinsoku + v := getAtt(tt.Attr, "val") + if v == "" { + continue + } + value.Val, err = strconv.Atoi(v) + if err != nil { + return err + } + p.Kinsoku = &value + case "overflowPunct": + var value OverflowPunct + v := getAtt(tt.Attr, "val") + if v == "" { + continue + } + value.Val, err = strconv.Atoi(v) + if err != nil { + return err + } + p.OverflowPunct = &value default: err = d.Skip() // skip unsupported tags if err != nil { @@ -177,27 +240,6 @@ func (p *Paragraph) String() string { return sb.String() } -// MarshalXML ... -func (p *Paragraph) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - err := e.EncodeToken(start) - if err != nil { - return err - } - if p.Properties != nil { - err = e.Encode(p.Properties) - if err != nil { - return err - } - } - for _, c := range p.Children { - err = e.Encode(c) - if err != nil { - return err - } - } - return e.EncodeToken(start.End()) -} - // UnmarshalXML ... func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for _, attr := range start.Attr { @@ -243,6 +285,7 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { elem = &value case "r": var value Run + value.file = p.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err diff --git a/structrel_test.go b/structrel_test.go index dcc2716..b4c1d41 100644 --- a/structrel_test.go +++ b/structrel_test.go @@ -47,16 +47,6 @@ func TestRelationships(t *testing.T) { 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") @@ -69,7 +59,7 @@ func TestRelationships(t *testing.T) { t.Fatal(err) } m := hex.EncodeToString(h.Sum(make([]byte, 0, 16))) - if m != "c75af73ef6cc9536a193669c4a3605c3" { + if m != "62c753dc14365fce007fc4c7c3bd0c82" { t.Fatal("real md5:", m) } } diff --git a/structrun.go b/structrun.go index bda46b7..095f835 100644 --- a/structrun.go +++ b/structrun.go @@ -31,16 +31,27 @@ import ( // a piece of text in bold, or a link type Run struct { XMLName xml.Name `xml:"w:r,omitempty"` + RsidRPr string `xml:"w:rsidRPr,attr,omitempty"` RunProperties *RunProperties `xml:"w:rPr,omitempty"` InstrText string `xml:"w:instrText,omitempty"` Children []interface{} + + file *Docx } // UnmarshalXML ... func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "rsidRPr": + r.RsidRPr = attr.Value + default: + // ignore other attributes + } + } for { t, err := d.Token() if err == io.EOF { @@ -91,6 +102,7 @@ func (r *Run) parse(d *xml.Decoder, tt xml.StartElement) (child interface{}, err child = &value case "drawing": var value Drawing + value.file = r.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return nil, err @@ -112,8 +124,14 @@ func (r *Run) parse(d *xml.Decoder, tt xml.StartElement) (child interface{}, err if ttt, ok := tok.(xml.StartElement); ok && ttt.Name.Local == "Choice" { for _, attr := range ttt.Attr { if attr.Name.Local == "Requires" { - if attr.Value == "wps" { - child, err = r.parse(d, ttt) + if attr.Value == "wps" || attr.Value == "wpc" { + tok, err = d.Token() // go into choice + if err != nil { + return nil, err + } + if ttt, ok := tok.(xml.StartElement); ok { + child, err = r.parse(d, ttt) + } break altcont } break @@ -141,15 +159,18 @@ type RunProperties struct { XMLName xml.Name `xml:"w:rPr,omitempty"` Fonts *RunFonts Bold *Bold + ICs *struct{} `xml:"w:iCs,omitempty"` Italic *Italic Underline *Underline Highlight *Highlight Color *Color Size *Size + SizeCs *SizeCs RunStyle *RunStyle Style *Style Shade *Shade Kern *Kern + VertAlign *VertAlign } // UnmarshalXML ... @@ -174,6 +195,8 @@ func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err r.Fonts = &value case "b": r.Bold = &Bold{} + case "iCs": + r.ICs = &struct{}{} case "i": r.Italic = &Italic{} case "u": @@ -192,6 +215,10 @@ func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err var value Size value.Val = getAtt(tt.Attr, "val") r.Size = &value + case "szCs": + var value SizeCs + value.Val = getAtt(tt.Attr, "val") + r.SizeCs = &value case "rStyle": var value RunStyle value.Val = getAtt(tt.Attr, "val") @@ -218,6 +245,10 @@ func (r *RunProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err return err } r.Kern = &value + case "vertAlign": + var value VertAlign + value.Val = getAtt(tt.Attr, "val") + r.VertAlign = &value default: err = d.Skip() // skip unsupported tags if err != nil { diff --git a/structshape.go b/structshape.go index 46301ec..72329ae 100644 --- a/structshape.go +++ b/structshape.go @@ -1,3 +1,23 @@ +/* + Copyright (c) 2020 gingfrederik + Copyright (c) 2021 Gonzalo Fernandez-Victorio + Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com) + Copyright (c) 2023 Fumiama Minamoto (源文雨) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package docx import ( @@ -7,16 +27,21 @@ import ( "strings" ) -// WPSWordprocessingShape is a container for a WordprocessingML DrawingML shape. -type WPSWordprocessingShape struct { +// WordprocessingShape is a container for a WordprocessingML DrawingML shape. +type WordprocessingShape struct { XMLName xml.Name `xml:"wps:wsp,omitempty"` + CNvPr *WPSCNvPr CNvCnPr *WPSCNvCnPr + CNvSpPr *WPSCNvSpPr SpPr *WPSSpPr + TextBox *WPSTextBox BodyPr *WPSBodyPr + + file *Docx } // UnmarshalXML ... -func (w *WPSWordprocessingShape) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { +func (w *WordprocessingShape) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for { t, err := d.Token() if err == io.EOF { @@ -28,18 +53,38 @@ func (w *WPSWordprocessingShape) UnmarshalXML(d *xml.Decoder, start xml.StartEle if tt, ok := t.(xml.StartElement); ok { switch tt.Name.Local { + case "cNvPr": + w.CNvPr = new(WPSCNvPr) + err = d.DecodeElement(w.CNvPr, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "cNvCnPr": w.CNvCnPr = new(WPSCNvCnPr) err = d.DecodeElement(w.CNvCnPr, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } + case "cNvSpPr": + w.CNvSpPr = new(WPSCNvSpPr) + err = d.DecodeElement(w.CNvSpPr, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "spPr": w.SpPr = new(WPSSpPr) err = d.DecodeElement(w.SpPr, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } + case "txbx": + var value WPSTextBox + value.file = w.file + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + w.TextBox = &value case "bodyPr": w.BodyPr = new(WPSBodyPr) err = d.DecodeElement(w.BodyPr, &tt) @@ -57,6 +102,33 @@ func (w *WPSWordprocessingShape) UnmarshalXML(d *xml.Decoder, start xml.StartEle return nil } +// WPSCNvPr is an element that represents the non-visual properties of a content control. +type WPSCNvPr struct { + XMLName xml.Name `xml:"wps:cNvPr,omitempty"` + ID int `xml:"id,attr"` + Name string `xml:"name,attr"` +} + +// UnmarshalXML ... +func (r *WPSCNvPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "id": + r.ID, err = strconv.Atoi(attr.Value) + if err != nil { + return + } + case "name": + r.Name = attr.Value + default: + // ignore other attributes + } + } + // Consume the end element + _, err = d.Token() + return +} + // WPSCNvCnPr represents the non-visual drawing properties of a connector. type WPSCNvCnPr struct { XMLName xml.Name `xml:"wps:cNvCnPr,omitempty"` @@ -90,15 +162,76 @@ func (w *WPSCNvCnPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error return nil } +// WPSCNvSpPr represents the non-visual properties of a WordArt object. +type WPSCNvSpPr struct { + XMLName xml.Name `xml:"wps:cNvSpPr,omitempty"` + TxBox int `xml:"txBox,attr,omitempty"` + + SPLocks *ASPLocks +} + +// UnmarshalXML ... +func (w *WPSCNvSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "txBox": + w.TxBox, err = strconv.Atoi(attr.Value) + if err != nil { + return err + } + default: + // ignore other attributes + } + } + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + if tt, ok := t.(xml.StartElement); ok { + switch tt.Name.Local { + case "spLocks": + var value ASPLocks + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + w.SPLocks = &value + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} + +// ASPLocks represents the locks applied to a shape. +type ASPLocks struct { + XMLName xml.Name `xml:"a:spLocks,omitempty"` +} + // WPSSpPr is a container element that represents the visual properties of a shape. type WPSSpPr struct { XMLName xml.Name `xml:"wps:spPr,omitempty"` BWMode string `xml:"bwMode,attr"` - Xfrm AXfrm - PrstGeom APrstGeom - NoFill *struct{} `xml:"a:noFill,omitempty"` - Elems []interface{} + Xfrm AXfrm + PrstGeom APrstGeom + SolidFill *ASolidFill + BlipFill *ABlipFill + NoFill *struct{} `xml:"a:noFill,omitempty"` + Line *ALine + + EffectList struct{} `xml:"a:effectLst"` + ExtList struct{} `xml:"a:extLst"` } // UnmarshalXML ... @@ -129,6 +262,20 @@ func (w *WPSSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { } case "prstGeom": w.PrstGeom.Prst = getAtt(tt.Attr, "prst") + case "solidFill": + var value ASolidFill + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + w.SolidFill = &value + case "blipFill": + var value ABlipFill + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + w.BlipFill = &value case "noFill": w.NoFill = &struct{}{} case "ln": @@ -137,7 +284,7 @@ func (w *WPSSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } - w.Elems = append(w.Elems, &ln) + w.Line = &ln default: err = d.Skip() // skip unsupported tags if err != nil { @@ -150,14 +297,137 @@ func (w *WPSSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { return nil } +// ABlipFill represents a fill that contains a reference to an image. +type ABlipFill struct { + XMLName xml.Name `xml:"a:blipFill,omitempty"` + DPI int `xml:"dpi,attr"` + RotWithShape int `xml:"rotWithShape,attr"` + + Blip *ABlip + SrcRect *ASrcRect + Tile *ATile +} + +// UnmarshalXML ... +func (r *ABlipFill) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "dpi": + r.DPI, err = strconv.Atoi(attr.Value) + if err != nil { + return err + } + case "rotWithShape": + r.RotWithShape, err = strconv.Atoi(attr.Value) + if err != nil { + return err + } + default: + // ignore other attributes + } + } + + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + if tt, ok := t.(xml.StartElement); ok { + switch tt.Name.Local { + case "blip": + var value ABlip + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + r.Blip = &value + case "srcRect": + r.SrcRect = new(ASrcRect) + case "tile": + var value ATile + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + r.Tile = &value + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} + +// ASrcRect represents the source rectangle of a tiled image fill. +type ASrcRect struct { + XMLName xml.Name `xml:"a:srcRect,omitempty"` +} + +// ATile represents the tiling information of a fill or border +type ATile struct { + XMLName xml.Name `xml:"a:tile,omitempty"` + TX int64 `xml:"tx,attr"` + TY int64 `xml:"ty,attr"` + SX int64 `xml:"sx,attr"` + SY int64 `xml:"sy,attr"` + Flip string `xml:"flip,attr"` + Algn string `xml:"algn,attr"` +} + +// UnmarshalXML ... +func (t *ATile) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "tx": + t.TX, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "ty": + t.TY, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "sx": + t.SX, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "sy": + t.SY, err = strconv.ParseInt(attr.Value, 10, 64) + if err != nil { + return err + } + case "flip": + t.Flip = attr.Value + case "algn": + t.Algn = attr.Value + default: + // ignore other attributes + } + } + // Consume the end element + _, err = d.Token() + return err +} + // ALine represents a line element in a Word document. type ALine struct { XMLName xml.Name `xml:"a:ln,omitempty"` - W int64 `xml:"w,attr"` + W int64 `xml:"w,attr,omitempty"` Cap string `xml:"cap,attr,omitempty"` Compound string `xml:"cmpd,attr,omitempty"` Align string `xml:"algn,attr,omitempty"` + NoFill *struct{} `xml:"a:noFill,omitempty"` SolidFill *ASolidFill PrstDash *APrstDash Miter *AMiter @@ -196,6 +466,8 @@ func (l *ALine) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) if tt, ok := t.(xml.StartElement); ok { switch tt.Name.Local { + case "noFill": + l.NoFill = &struct{}{} case "solidFill": l.SolidFill = new(ASolidFill) err = d.DecodeElement(l.SolidFill, &tt) @@ -351,7 +623,156 @@ func (r *ATailEnd) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { return err } +// WPSTextBox ... +type WPSTextBox struct { + XMLName xml.Name `xml:"wps:txbx,omitempty"` + Content *WTextBoxContent + + file *Docx +} + +// UnmarshalXML ... +func (b *WPSTextBox) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + if tt, ok := t.(xml.StartElement); ok { + switch tt.Name.Local { + case "txbxContent": + var value WTextBoxContent + value.file = b.file + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + b.Content = &value + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} + +// WTextBoxContent ... +type WTextBoxContent struct { + XMLName xml.Name `xml:"w:txbxContent,omitempty"` + Paragraphs []Paragraph `xml:"w:p,omitempty"` + + file *Docx +} + +// UnmarshalXML ... +func (c *WTextBoxContent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + if tt, ok := t.(xml.StartElement); ok { + switch tt.Name.Local { + case "p": + var value Paragraph + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + if len(value.Children) > 0 { + value.file = c.file + c.Paragraphs = append(c.Paragraphs, value) + } + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} + // WPSBodyPr represents the body properties for a WordprocessingML DrawingML shape. type WPSBodyPr struct { - XMLName xml.Name `xml:"wps:bodyPr,omitempty"` + XMLName xml.Name `xml:"wps:bodyPr,omitempty"` + Rot int `xml:"rot,attr,omitempty"` + Vert string `xml:"vert,attr,omitempty"` + Wrap string `xml:"wrap,attr,omitempty"` + LIns int64 `xml:"lIns,attr,omitempty"` + TIns int64 `xml:"tIns,attr,omitempty"` + RIns int64 `xml:"rIns,attr,omitempty"` + BIns int64 `xml:"bIns,attr,omitempty"` + Anchor string `xml:"anchor,attr,omitempty"` + AnchorCtr int `xml:"anchorCtr,attr,omitempty"` + Upright int `xml:"upright,attr,omitempty"` + + NoAutofit *struct{} `xml:"a:noAutofit,omitempty"` +} + +// UnmarshalXML ... +func (r *WPSBodyPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "rot": + r.Rot, _ = strconv.Atoi(attr.Value) + case "vert": + r.Vert = attr.Value + case "wrap": + r.Wrap = attr.Value + case "lIns": + r.LIns, _ = strconv.ParseInt(attr.Value, 10, 64) + case "tIns": + r.TIns, _ = strconv.ParseInt(attr.Value, 10, 64) + case "rIns": + r.RIns, _ = strconv.ParseInt(attr.Value, 10, 64) + case "bIns": + r.BIns, _ = strconv.ParseInt(attr.Value, 10, 64) + case "anchor": + r.Anchor = attr.Value + case "anchorCtr": + r.AnchorCtr, _ = strconv.Atoi(attr.Value) + case "upright": + r.Upright, _ = strconv.Atoi(attr.Value) + default: + // ignore other attributes + } + } + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return err + } + + if tt, ok := t.(xml.StartElement); ok { + switch tt.Name.Local { + case "noAutofit": + r.NoAutofit = &struct{}{} + default: + err = d.Skip() // skip unsupported elements + if err != nil { + return err + } + continue + } + } + } + return nil } diff --git a/structshape_test.go b/structshape_test.go index 5f8eb7e..d70fad5 100644 --- a/structshape_test.go +++ b/structshape_test.go @@ -1,3 +1,23 @@ +/* + Copyright (c) 2020 gingfrederik + Copyright (c) 2021 Gonzalo Fernandez-Victorio + Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com) + Copyright (c) 2023 Fumiama Minamoto (源文雨) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package docx import ( @@ -13,8 +33,8 @@ func TestShapeStructure(t *testing.T) { // add new paragraph para1 := w.AddParagraph() // add text - para1.AddText("test shape") - para1.AddShape(808355, 238760, "AutoShape", "auto", "straightConnector1", []interface{}{ + para1.AddText("test anchor shape") + para1.AddAnchorShape(808355, 238760, "AutoShape", "auto", "straightConnector1", &ALine{ W: 9525, SolidFill: &ASolidFill{SrgbClr: &ASrgbClr{Val: "000000"}}, @@ -22,7 +42,19 @@ func TestShapeStructure(t *testing.T) { HeadEnd: &AHeadEnd{}, TailEnd: &ATailEnd{}, }, - }) + ) + + para2 := w.AddParagraph() + para2.AddText("test inline shape") + para2.AddInlineShape(808355, 238760, "AutoShape", "auto", "straightConnector1", + &ALine{ + W: 9525, + SolidFill: &ASolidFill{SrgbClr: &ASrgbClr{Val: "000000"}}, + Round: &struct{}{}, + HeadEnd: &AHeadEnd{}, + TailEnd: &ATailEnd{}, + }, + ) f, err := os.Create("TestMarshalShapeStructure.xml") if err != nil { diff --git a/structtable.go b/structtable.go index 7590c88..10e1b25 100644 --- a/structtable.go +++ b/structtable.go @@ -51,11 +51,11 @@ func (t *WTable) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { switch tt.Name.Local { case "tr": var value WTableRow + value.file = t.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } - value.file = t.file t.TableRows = append(t.TableRows, &value) case "tblPr": t.TableProperties = new(WTableProperties) @@ -421,11 +421,11 @@ func (w *WTableRow) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { } case "tc": var value WTableCell + value.file = w.file err = d.DecodeElement(&value, &tt) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } - value.file = w.file w.TableCells = append(w.TableCells, &value) default: err = d.Skip() // skip unsupported tags @@ -504,7 +504,7 @@ func (t *WTableRowProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElemen // WTableRowHeight represents the height of a row within a table. type WTableRowHeight struct { XMLName xml.Name `xml:"w:trHeight,omitempty"` - Rule string `xml:"w:hRule,omitempty"` + Rule string `xml:"w:hRule,attr,omitempty"` Val int64 `xml:"w:val,attr"` } @@ -565,9 +565,9 @@ type WTableCellProperties struct { TableCellWidth *WTableCellWidth VMerge *WvMerge GridSpan *WGridSpan - VAlign *WVerticalAlignment TableBorders *WTableBorders `xml:"w:tcBorders"` Shade *Shade + VAlign *WVerticalAlignment } // UnmarshalXML ... diff --git a/xml/a4/[Content_Types].xml b/xml/a4/[Content_Types].xml index b3a964c..3ea9380 100644 --- a/xml/a4/[Content_Types].xml +++ b/xml/a4/[Content_Types].xml @@ -7,8 +7,6 @@ - - diff --git a/xml/a4/word/settings.xml b/xml/a4/word/settings.xml deleted file mode 100755 index 599386d..0000000 --- a/xml/a4/word/settings.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/xml/a4/word/webSettings.xml b/xml/a4/word/webSettings.xml deleted file mode 100755 index 6c0860b..0000000 --- a/xml/a4/word/webSettings.xml +++ /dev/null @@ -1,11 +0,0 @@ - -