diff --git a/cmd/main/main.go b/cmd/main/main.go index 89a04ce..cdf9402 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -130,8 +130,8 @@ func main() { W: 9525, SolidFill: &docx.ASolidFill{SrgbClr: &docx.ASrgbClr{Val: "000000"}}, Round: &struct{}{}, - HeadEnd: &struct{}{}, - TailEnd: &struct{}{}, + HeadEnd: &docx.AHeadEnd{}, + TailEnd: &docx.ATailEnd{}, }, }) diff --git a/empty.go b/empty.go index 340f679..0d674c5 100644 --- a/empty.go +++ b/empty.go @@ -35,6 +35,7 @@ func newEmptyA4File() *Docx { XMLR: XMLNS_R, XMLWP: XMLNS_WP, XMLWPS: XMLNS_WPS, + XMLWPC: XMLNS_WPC, // XMLMC: XMLNS_MC, // XMLWP14: XMLNS_WP14, Body: Body{ diff --git a/id.go b/id.go index 2721561..005e92a 100644 --- a/id.go +++ b/id.go @@ -2,7 +2,7 @@ package docx func (f *Docx) IncreaseID(name string) (n uintptr) { f.slowIDsMu.Lock() - n, _ = f.slowIDs[name] //nolint: go-staticcheck + n = f.slowIDs[name] n++ f.slowIDs[name] = n f.slowIDsMu.Unlock() diff --git a/structcanvas.go b/structcanvas.go new file mode 100644 index 0000000..ada991e --- /dev/null +++ b/structcanvas.go @@ -0,0 +1,127 @@ +package docx + +import ( + "encoding/xml" + "io" + "strings" +) + +type WordprocessingCanvas struct { + XMLName xml.Name `xml:"wpc:wpc,omitempty"` + Background *WPCBackground + Whole *WPCWhole + + Items []interface{} +} + +// UnmarshalXML ... +func (c *WordprocessingCanvas) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err 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 "bg": + c.Background = new(WPCBackground) + err = d.DecodeElement(c.Background, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + case "whole": + c.Whole = new(WPCWhole) + err = d.DecodeElement(c.Whole, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + case "wsp": + var value WPSWordprocessingShape + err = d.DecodeElement(&value, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + c.Items = append(c.Items, &value) + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} + +type WPCBackground struct { + XMLName xml.Name `xml:"wpc:bg,omitempty"` + NoFill *struct{} `xml:"a:noFill,omitempty"` +} + +// UnmarshalXML ... +func (b *WPCBackground) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err 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 "noFill": + b.NoFill = &struct{}{} + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} + +type WPCWhole struct { + XMLName xml.Name `xml:"wpc:whole,omitempty"` + Line *ALine +} + +// UnmarshalXML ... +func (w *WPCWhole) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err 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 "ln": + w.Line = new(ALine) + err = d.DecodeElement(w.Line, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } + default: + err = d.Skip() // skip unsupported tags + if err != nil { + return err + } + continue + } + } + } + return nil +} diff --git a/structdoc.go b/structdoc.go index 28d4ddb..8ae78af 100644 --- a/structdoc.go +++ b/structdoc.go @@ -32,6 +32,7 @@ const ( XMLNS_R = `http://schemas.openxmlformats.org/officeDocument/2006/relationships` XMLNS_WP = `http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing` XMLNS_WPS = `http://schemas.microsoft.com/office/word/2010/wordprocessingShape` + XMLNS_WPC = `http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas` // XMLNS_MC = `http://schemas.openxmlformats.org/markup-compatibility/2006` // XMLNS_WP14 = `http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing` @@ -101,6 +102,7 @@ type Document struct { XMLR string `xml:"xmlns:r,attr,omitempty"` // cannot be unmarshalled in XMLWP string `xml:"xmlns:wp,attr,omitempty"` // cannot be unmarshalled in XMLWPS string `xml:"xmlns:wps,attr,omitempty"` // cannot be unmarshalled in + XMLWPC string `xml:"xmlns:wpc,attr,omitempty"` // cannot be unmarshalled in // XMLMC string `xml:"xmlns:mc,attr,omitempty"` // cannot be unmarshalled in // XMLWP14 string `xml:"xmlns:wp14,attr,omitempty"` // cannot be unmarshalled in diff --git a/structshape.go b/structshape.go index b6770ec..46301ec 100644 --- a/structshape.go +++ b/structshape.go @@ -152,13 +152,18 @@ func (w *WPSSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // ALine represents a line element in a Word document. type ALine struct { - XMLName xml.Name `xml:"a:ln,omitempty"` - W int64 `xml:"w,attr"` + XMLName xml.Name `xml:"a:ln,omitempty"` + W int64 `xml:"w,attr"` + Cap string `xml:"cap,attr,omitempty"` + Compound string `xml:"cmpd,attr,omitempty"` + Align string `xml:"algn,attr,omitempty"` SolidFill *ASolidFill + PrstDash *APrstDash + Miter *AMiter Round *struct{} `xml:"a:round,omitempty"` - HeadEnd *struct{} `xml:"a:headEnd,omitempty"` - TailEnd *struct{} `xml:"a:tailEnd,omitempty"` + HeadEnd *AHeadEnd + TailEnd *ATailEnd } // UnmarshalXML ... @@ -170,6 +175,12 @@ func (l *ALine) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) if err != nil { return err } + case "cap": + l.Cap = attr.Value + case "cmpd": + l.Compound = attr.Value + case "algn": + l.Align = attr.Value default: // ignore other attributes } @@ -191,12 +202,28 @@ func (l *ALine) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) if err != nil && !strings.HasPrefix(err.Error(), "expected") { return err } + case "prstDash": + var value APrstDash + value.Val = getAtt(tt.Attr, "val") + l.PrstDash = &value + case "miter": + var value AMiter + value.Limit = getAtt(tt.Attr, "lim") + l.Miter = &value case "round": l.Round = &struct{}{} case "headEnd": - l.HeadEnd = &struct{}{} + l.HeadEnd = new(AHeadEnd) + err = d.DecodeElement(l.HeadEnd, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } case "tailEnd": - l.TailEnd = &struct{}{} + l.TailEnd = new(ATailEnd) + err = d.DecodeElement(l.TailEnd, &tt) + if err != nil && !strings.HasPrefix(err.Error(), "expected") { + return err + } default: err = d.Skip() // skip unsupported tags if err != nil { @@ -258,6 +285,72 @@ type ASrgbClr struct { Val string `xml:"val,attr"` } +// APrstDash ... +type APrstDash struct { + XMLName xml.Name `xml:"a:prstDash,omitempty"` + Val string `xml:"val,attr"` +} + +// AMiter ... +type AMiter struct { + XMLName xml.Name `xml:"a:miter,omitempty"` + Limit string `xml:"lim,attr"` +} + +// AHeadEnd ... +type AHeadEnd struct { + XMLName xml.Name `xml:"a:headEnd,omitempty"` + Type string `xml:"type,attr,omitempty"` + W string `xml:"w,attr,omitempty"` + Len string `xml:"len,attr,omitempty"` +} + +// UnmarshalXML ... +func (r *AHeadEnd) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "type": + r.Type = attr.Value + case "w": + r.W = attr.Value + case "len": + r.Len = attr.Value + default: + // ignore other attributes + } + } + // Consume the end element + _, err := d.Token() + return err +} + +// ATailEnd ... +type ATailEnd struct { + XMLName xml.Name `xml:"a:tailEnd,omitempty"` + Type string `xml:"type,attr,omitempty"` + W string `xml:"w,attr,omitempty"` + Len string `xml:"len,attr,omitempty"` +} + +// UnmarshalXML ... +func (r *ATailEnd) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + for _, attr := range start.Attr { + switch attr.Name.Local { + case "type": + r.Type = attr.Value + case "w": + r.W = attr.Value + case "len": + r.Len = attr.Value + default: + // ignore other attributes + } + } + // Consume the end element + _, err := d.Token() + return err +} + // WPSBodyPr represents the body properties for a WordprocessingML DrawingML shape. type WPSBodyPr struct { XMLName xml.Name `xml:"wps:bodyPr,omitempty"` diff --git a/structshape_test.go b/structshape_test.go index 1b9a8bf..5f8eb7e 100644 --- a/structshape_test.go +++ b/structshape_test.go @@ -19,8 +19,8 @@ func TestShapeStructure(t *testing.T) { W: 9525, SolidFill: &ASolidFill{SrgbClr: &ASrgbClr{Val: "000000"}}, Round: &struct{}{}, - HeadEnd: &struct{}{}, - TailEnd: &struct{}{}, + HeadEnd: &AHeadEnd{}, + TailEnd: &ATailEnd{}, }, }) diff --git a/unpack.go b/unpack.go index 5e70e81..a955da2 100644 --- a/unpack.go +++ b/unpack.go @@ -84,6 +84,7 @@ func (f *Docx) parseDocument(file *zip.File) error { f.Document.XMLWP = XMLNS_WP // f.Document.XMLMC = XMLNS_MC f.Document.XMLWPS = XMLNS_WPS + f.Document.XMLWPC = XMLNS_WPC // f.Document.XMLWP14 = XMLNS_WP14 f.Document.XMLName.Space = XMLNS_W f.Document.XMLName.Local = "document"