1
0
mirror of https://github.com/fumiama/go-docx.git synced 2026-06-11 11:04:54 +08:00

feat: add drawing inline marshal

This commit is contained in:
源文雨
2023-02-08 21:25:09 +08:00
parent 362dfefee9
commit ae07360d5e
12 changed files with 627 additions and 77 deletions

View File

@@ -34,7 +34,7 @@ func (p *Paragraph) AddLink(text string, link string) *Hyperlink {
}, },
} }
p.Data = append(p.Data, ParagraphChild{Link: hyperlink}) p.Children = append(p.Children, ParagraphChild{Link: hyperlink})
return hyperlink return hyperlink
} }

View File

@@ -3,19 +3,11 @@ package docxlib
// AddParagraph adds a new paragraph // AddParagraph adds a new paragraph
func (f *Docx) AddParagraph() *Paragraph { func (f *Docx) AddParagraph() *Paragraph {
p := &Paragraph{ p := &Paragraph{
Data: make([]ParagraphChild, 0, 64), Children: make([]ParagraphChild, 0, 64),
file: f, file: f,
} }
f.Document.Body.Paragraphs = append(f.Document.Body.Paragraphs, p) f.Document.Body.Paragraphs = append(f.Document.Body.Paragraphs, p)
return p return p
} }
func (f *Docx) Paragraphs() []*Paragraph {
return f.Document.Body.Paragraphs
}
func (p *Paragraph) Children() (ret []ParagraphChild) {
return p.Data
}

View File

@@ -29,7 +29,7 @@ func (p *Paragraph) AddText(text string) *Run {
RunProperties: &RunProperties{}, RunProperties: &RunProperties{},
} }
p.Data = append(p.Data, ParagraphChild{Run: run}) p.Children = append(p.Children, ParagraphChild{Run: run})
return run return run
} }

View File

@@ -6,7 +6,6 @@ import (
"os" "os"
"github.com/fumiama/docxlib" "github.com/fumiama/docxlib"
"github.com/golang/glog"
) )
var fileLocation *string var fileLocation *string
@@ -31,11 +30,16 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
for _, para := range doc.Paragraphs() { for _, para := range doc.Document.Body.Paragraphs {
glog.Infoln("There is a new paragraph", para) fmt.Println("New paragraph")
for _, child := range para.Children() { for _, child := range para.Children {
if child.Run != nil && child.Run.Text != nil { if child.Run != nil {
fmt.Printf("\tWe've found a new run with the text ->%s\n", child.Run.Text.Text) if child.Run.Text != nil {
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.DistT) // TODO: replace to refid
}
} }
if child.Link != nil { if child.Link != nil {
id := child.Link.ID id := child.Link.ID
@@ -49,6 +53,7 @@ func main() {
} }
} }
fmt.Print("End of paragraph\n\n")
} }
fmt.Println("End of main") fmt.Println("End of main")
} }

View File

@@ -52,10 +52,15 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
for _, para := range doc.Paragraphs() { for _, para := range doc.Document.Body.Paragraphs {
for _, child := range para.Children() { for _, child := range para.Children {
if child.Run != nil { if child.Run != nil {
fmt.Printf("\tWe've found a new run with the text ->%s\n", child.Run.Text.Text) if child.Run.Text != nil {
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.DistT) // TODO: replace to refid
}
} }
if child.Link != nil { if child.Link != nil {
id := child.Link.ID id := child.Link.ID

2
go.mod
View File

@@ -1,5 +1,3 @@
module github.com/fumiama/docxlib module github.com/fumiama/docxlib
go 1.16 go 1.16
require github.com/golang/glog v0.0.0-20210429001901-424d2337a529

2
go.sum
View File

@@ -1,2 +0,0 @@
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=

View File

@@ -4,8 +4,6 @@ import (
"archive/zip" "archive/zip"
"encoding/xml" "encoding/xml"
"strings" "strings"
"github.com/golang/glog"
) )
// This receives a zip file writer (word documents are a zip with multiple xml inside) // This receives a zip file writer (word documents are a zip with multiple xml inside)
@@ -49,7 +47,6 @@ func marshal(data interface{}) (out string, err error) {
sb.WriteString(xml.Header) sb.WriteString(xml.Header)
err = xml.NewEncoder(&sb).Encode(data) err = xml.NewEncoder(&sb).Encode(data)
if err != nil { if err != nil {
glog.Errorln("Error marshalling", err)
return return
} }
out = sb.String() out = sb.String()

File diff suppressed because one or more lines are too long

View File

@@ -3,8 +3,6 @@ package docxlib
import ( import (
"encoding/xml" "encoding/xml"
"io" "io"
"github.com/golang/glog"
) )
type ParagraphChild struct { type ParagraphChild struct {
@@ -14,8 +12,8 @@ type ParagraphChild struct {
} }
type Paragraph struct { type Paragraph struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main p"` XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main p"`
Data []ParagraphChild Children []ParagraphChild
file *Docx file *Docx
} }
@@ -47,8 +45,7 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var value Run var value Run
d.DecodeElement(&value, &start) d.DecodeElement(&value, &start)
elem.Run = &value elem.Run = &value
if value.InstrText == "" && value.Text == nil { if value.InstrText == "" && value.Text == nil && value.Drawing == nil {
glog.V(0).Infof("Empty run, we ignore")
continue continue
} }
case "rPr": case "rPr":
@@ -62,7 +59,7 @@ func (p *Paragraph) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
} }
} }
*p = Paragraph{Data: children} p.Children = children
return nil return nil
} }

View File

@@ -9,29 +9,45 @@ const (
HYPERLINK_STYLE = "a1" HYPERLINK_STYLE = "a1"
) )
// A Run is part of a paragraph that has its own style. It could be // Run is part of a paragraph that has its own style. It could be
// a piece of text in bold, or a link // a piece of text in bold, or a link
type Run struct { type Run struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main r,omitempty"` XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main r,omitempty"`
RunProperties *RunProperties `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr,omitempty"` RunProperties *RunProperties `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr,omitempty"`
InstrText string `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main instrText,omitempty"` InstrText string `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main instrText,omitempty"`
Text *Text Text *Text
Drawing *Drawing
} }
// The Text object contains the actual text // Text object contains the actual text
type Text struct { type Text struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main t"` XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main t"`
XMLSpace string `xml:"xml:space,attr,omitempty"` XMLSpace string `xml:"xml:space,attr,omitempty"`
Text string `xml:",chardata"` Text string `xml:",chardata"`
} }
// The hyperlink element contains links // Hyperlink element contains links
type Hyperlink struct { type Hyperlink struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main hyperlink,omitempty"` XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main hyperlink,omitempty"`
ID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr"` ID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr"`
Run Run Run Run
} }
// Drawing element contains photos
type Drawing struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main drawing,omitempty"`
Inline *WPInline
}
// WPInline wp:inline
type WPInline struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing inline,omitempty"`
DistT string `xml:"wp:distT,attr"`
DistB string `xml:"wp:distB,attr"`
DistL string `xml:"wp:distL,attr"`
DistR string `xml:"wp:distR,attr"`
}
// RunProperties encapsulates visual properties of a run // RunProperties encapsulates visual properties of a run
type RunProperties struct { type RunProperties struct {
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr,omitempty"` XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr,omitempty"`
@@ -67,7 +83,6 @@ type Size struct {
} }
func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var elem Run
for { for {
t, err := d.Token() t, err := d.Token()
if err == io.EOF { if err == io.EOF {
@@ -80,28 +95,30 @@ func (r *Run) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
case "rPr": case "rPr":
var value RunProperties var value RunProperties
d.DecodeElement(&value, &start) d.DecodeElement(&value, &start)
elem.RunProperties = &value r.RunProperties = &value
case "instrText": case "instrText":
var value string var value string
d.DecodeElement(&value, &start) d.DecodeElement(&value, &start)
elem.InstrText = value r.InstrText = value
case "t": case "t":
var value Text var value Text
d.DecodeElement(&value, &start) d.DecodeElement(&value, &start)
elem.Text = &value r.Text = &value
case "drawing":
var value Drawing
d.DecodeElement(&value, &start)
r.Drawing = &value
default: default:
continue continue
} }
} }
} }
*r = elem
return nil return nil
} }
func (r *Text) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { func (r *Text) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var elem Text
for { for {
t, err := d.Token() t, err := d.Token()
if err == io.EOF { if err == io.EOF {
@@ -110,16 +127,14 @@ func (r *Text) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
switch tt := t.(type) { switch tt := t.(type) {
case xml.CharData: case xml.CharData:
elem.Text = string(tt) // implicitly copy r.Text = string(tt) // implicitly copy
} }
} }
*r = elem
return nil return nil
} }
func (r *Hyperlink) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { func (r *Hyperlink) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var elem Hyperlink
for { for {
t, err := d.Token() t, err := d.Token()
if err == io.EOF { if err == io.EOF {
@@ -129,19 +144,17 @@ func (r *Hyperlink) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
switch tt := t.(type) { switch tt := t.(type) {
case xml.StartElement: case xml.StartElement:
if tt.Name.Local == "r" { if tt.Name.Local == "r" {
d.DecodeElement(&elem.Run, &start) d.DecodeElement(&r.Run, &start)
} else { } else {
continue continue
} }
} }
} }
*r = elem
return nil return nil
} }
func (r *RunStyle) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { func (r *Drawing) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var elem RunStyle
for { for {
t, err := d.Token() t, err := d.Token()
if err == io.EOF { if err == io.EOF {
@@ -150,11 +163,57 @@ func (r *RunStyle) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
switch tt := t.(type) { switch tt := t.(type) {
case xml.StartElement: case xml.StartElement:
elem.Val = getAtt(tt.Attr, "val") switch tt.Name.Local {
case "inline":
r.Inline = new(WPInline)
r.Inline.DistT = getAtt(tt.Attr, "distT")
r.Inline.DistB = getAtt(tt.Attr, "distB")
r.Inline.DistL = getAtt(tt.Attr, "distL")
r.Inline.DistR = getAtt(tt.Attr, "distR")
d.DecodeElement(r.Inline, &start)
default:
continue
}
}
}
return nil
}
func (r *WPInline) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
t, err := d.Token()
if err == io.EOF {
break
}
switch tt := t.(type) {
case xml.StartElement:
switch tt.Name.Local {
case "inline":
default:
continue
}
}
}
return nil
}
func (r *RunStyle) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
t, err := d.Token()
if err == io.EOF {
break
}
switch tt := t.(type) {
case xml.StartElement:
r.Val = getAtt(tt.Attr, "val")
} }
} }
*r = elem
return nil return nil
} }

View File

@@ -5,8 +5,6 @@ import (
"archive/zip" "archive/zip"
"encoding/xml" "encoding/xml"
"io" "io"
"github.com/golang/glog"
) )
// This receives a zip file (word documents are a zip with multiple xml inside) // This receives a zip file (word documents are a zip with multiple xml inside)
@@ -36,10 +34,8 @@ func unpack(zipReader *zip.Reader) (docx *Docx, err error) {
func processDoc(file *zip.File, doc *Document) error { func processDoc(file *zip.File, doc *Document) error {
filebytes, err := readZipFile(file) filebytes, err := readZipFile(file)
if err != nil { if err != nil {
glog.Errorln("Error reading from internal zip file")
return err return err
} }
glog.V(0).Infoln("Doc:", string(filebytes))
doc.XMLW = XMLNS_W doc.XMLW = XMLNS_W
doc.XMLR = XMLNS_R doc.XMLR = XMLNS_R
@@ -47,10 +43,8 @@ func processDoc(file *zip.File, doc *Document) error {
doc.XMLName.Local = "document" doc.XMLName.Local = "document"
err = xml.Unmarshal(filebytes, doc) err = xml.Unmarshal(filebytes, doc)
if err != nil { if err != nil {
glog.Errorln("Error unmarshalling doc", string(filebytes))
return err return err
} }
glog.V(0).Infoln("Paragraph", doc.Body.Paragraphs)
return nil return nil
} }
@@ -58,15 +52,12 @@ func processDoc(file *zip.File, doc *Document) error {
func processRelations(file *zip.File, rels *Relationships) error { func processRelations(file *zip.File, rels *Relationships) error {
filebytes, err := readZipFile(file) filebytes, err := readZipFile(file)
if err != nil { if err != nil {
glog.Errorln("Error reading from internal zip file")
return err return err
} }
glog.V(0).Infoln("Relations:", string(filebytes))
rels.Xmlns = XMLNS_R rels.Xmlns = XMLNS_R
err = xml.Unmarshal(filebytes, rels) err = xml.Unmarshal(filebytes, rels)
if err != nil { if err != nil {
glog.Errorln("Error unmarshalling relationships")
return err return err
} }
return nil return nil