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:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
12
apipara.go
12
apipara.go
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
2
go.mod
@@ -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
2
go.sum
@@ -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=
|
|
||||||
|
|||||||
3
pack.go
3
pack.go
@@ -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
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
95
structrun.go
95
structrun.go
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user