mirror of
https://github.com/fumiama/go-docx.git
synced 2026-06-05 07:40:24 +08:00
add AddShape api
This commit is contained in:
@@ -35,9 +35,9 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
idn := int(atomic.AddUintptr(&p.file.imageID, 1))
|
idn := int(atomic.AddUintptr(&p.file.docID, 1))
|
||||||
id := strconv.Itoa(idn)
|
id := strconv.Itoa(int(p.file.IncreaseID("图片")))
|
||||||
rid := p.file.addImage(Media{Name: "image" + id + "." + format, Data: pic})
|
rid := p.file.addImage(format, pic)
|
||||||
w, h := int64(sz.Width), int64(sz.Height)
|
w, h := int64(sz.Width), int64(sz.Height)
|
||||||
if float64(w)/float64(h) > 1.2 {
|
if float64(w)/float64(h) > 1.2 {
|
||||||
h = A4_EMU_MAX_WIDTH * h / w
|
h = A4_EMU_MAX_WIDTH * h / w
|
||||||
@@ -61,7 +61,7 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) {
|
|||||||
Name: "图片 " + id,
|
Name: "图片 " + id,
|
||||||
},
|
},
|
||||||
CNvGraphicFramePr: &WPCNvGraphicFramePr{
|
CNvGraphicFramePr: &WPCNvGraphicFramePr{
|
||||||
Locks: &AGraphicFrameLocks{
|
Locks: AGraphicFrameLocks{
|
||||||
NoChangeAspect: 1,
|
NoChangeAspect: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -136,9 +136,9 @@ func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
idn := int(atomic.AddUintptr(&p.file.imageID, 1))
|
idn := int(atomic.AddUintptr(&p.file.docID, 1))
|
||||||
id := strconv.Itoa(idn)
|
id := strconv.Itoa(int(p.file.IncreaseID("图片")))
|
||||||
rid := p.file.addImage(Media{Name: "image" + id + "." + format, Data: pic})
|
rid := p.file.addImage(format, pic)
|
||||||
w, h := int64(sz.Width), int64(sz.Height)
|
w, h := int64(sz.Width), int64(sz.Height)
|
||||||
if float64(w)/float64(h) > 1.2 {
|
if float64(w)/float64(h) > 1.2 {
|
||||||
h = A4_EMU_MAX_WIDTH * h / w
|
h = A4_EMU_MAX_WIDTH * h / w
|
||||||
@@ -171,7 +171,7 @@ func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) {
|
|||||||
Name: "图片 " + id,
|
Name: "图片 " + id,
|
||||||
},
|
},
|
||||||
CNvGraphicFramePr: &WPCNvGraphicFramePr{
|
CNvGraphicFramePr: &WPCNvGraphicFramePr{
|
||||||
Locks: &AGraphicFrameLocks{
|
Locks: AGraphicFrameLocks{
|
||||||
NoChangeAspect: 1,
|
NoChangeAspect: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
73
apishape.go
Normal file
73
apishape.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package docx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddShape adds wsp named drawing to paragraph
|
||||||
|
func (p *Paragraph) AddShape(w, h int64, name, bwMode, prst string, elems []interface{}) (*Run, error) {
|
||||||
|
idn := int(atomic.AddUintptr(&p.file.docID, 1))
|
||||||
|
id := strconv.Itoa(int(p.file.IncreaseID(name)))
|
||||||
|
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: name + " " + id,
|
||||||
|
},
|
||||||
|
CNvGraphicFramePr: &WPCNvGraphicFramePr{},
|
||||||
|
Graphic: &AGraphic{
|
||||||
|
XMLA: XMLNS_DRAWINGML_MAIN,
|
||||||
|
GraphicData: &AGraphicData{
|
||||||
|
URI: XMLNS_WPS,
|
||||||
|
Shape: &WPSWordprocessingShape{
|
||||||
|
CNvCnPr: &WPSCNvCnPr{
|
||||||
|
ConnShapeLocks: &struct{}{},
|
||||||
|
},
|
||||||
|
SpPr: &WPSSpPr{
|
||||||
|
BWMode: bwMode,
|
||||||
|
|
||||||
|
Xfrm: AXfrm{
|
||||||
|
Ext: AExt{
|
||||||
|
CX: w,
|
||||||
|
CY: h,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PrstGeom: APrstGeom{
|
||||||
|
Prst: prst,
|
||||||
|
},
|
||||||
|
NoFill: &struct{}{},
|
||||||
|
Elems: elems,
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -123,6 +123,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
tbl2.TableRows[0].TableCells[0].Shade("clear", "auto", "E7E6E6")
|
tbl2.TableRows[0].TableCells[0].Shade("clear", "auto", "E7E6E6")
|
||||||
|
|
||||||
|
p := w.AddParagraph().Justification("center")
|
||||||
|
p.AddText("测试 AutoShape w:ln").Size("44")
|
||||||
|
p.AddShape(808355, 238760, "AutoShape", "auto", "straightConnector1", []interface{}{
|
||||||
|
&docx.ALine{
|
||||||
|
W: 9525,
|
||||||
|
SolidFill: &docx.ASolidFill{SrgbClr: &docx.ASrgbClr{Val: "000000"}},
|
||||||
|
Round: &struct{}{},
|
||||||
|
HeadEnd: &struct{}{},
|
||||||
|
TailEnd: &struct{}{},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
f, err := os.Create(*fileLocation)
|
f, err := os.Create(*fileLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
8
docx.go
8
docx.go
@@ -27,6 +27,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Docx is the structure that allow to access the internal represntation
|
// Docx is the structure that allow to access the internal represntation
|
||||||
@@ -39,8 +40,11 @@ type Docx struct {
|
|||||||
media []Media
|
media []Media
|
||||||
mediaNameIdx map[string]int
|
mediaNameIdx map[string]int
|
||||||
|
|
||||||
rID uintptr
|
rID uintptr
|
||||||
imageID uintptr
|
imageID uintptr
|
||||||
|
docID uintptr
|
||||||
|
slowIDs map[string]uintptr
|
||||||
|
slowIDsMu sync.Mutex
|
||||||
|
|
||||||
template string
|
template string
|
||||||
tmplfs fs.FS
|
tmplfs fs.FS
|
||||||
|
|||||||
1
empty.go
1
empty.go
@@ -74,6 +74,7 @@ func newEmptyA4File() *Docx {
|
|||||||
media: make([]Media, 0, 64),
|
media: make([]Media, 0, 64),
|
||||||
mediaNameIdx: make(map[string]int, 64),
|
mediaNameIdx: make(map[string]int, 64),
|
||||||
rID: 5,
|
rID: 5,
|
||||||
|
slowIDs: make(map[string]uintptr, 64),
|
||||||
template: "a4",
|
template: "a4",
|
||||||
tmpfslst: []string{
|
tmpfslst: []string{
|
||||||
"_rels/.rels",
|
"_rels/.rels",
|
||||||
|
|||||||
10
id.go
Normal file
10
id.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
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.slowIDsMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
8
image.go
8
image.go
@@ -20,8 +20,14 @@
|
|||||||
|
|
||||||
package docx
|
package docx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
// addImage add image to docx and return its rId
|
// addImage add image to docx and return its rId
|
||||||
func (f *Docx) addImage(m Media) string {
|
func (f *Docx) addImage(format string, data []byte) string {
|
||||||
|
m := Media{Name: "image" + strconv.Itoa(int(atomic.AddUintptr(&f.imageID, 1))) + "." + format, Data: data}
|
||||||
f.addMedia(m)
|
f.addMedia(m)
|
||||||
return f.addImageRelation(m)
|
return f.addImageRelation(m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ func (r *WPDocPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|||||||
// WPCNvGraphicFramePr represents the non-visual properties of a graphic frame.
|
// WPCNvGraphicFramePr represents the non-visual properties of a graphic frame.
|
||||||
type WPCNvGraphicFramePr struct {
|
type WPCNvGraphicFramePr struct {
|
||||||
XMLName xml.Name `xml:"wp:cNvGraphicFramePr,omitempty"`
|
XMLName xml.Name `xml:"wp:cNvGraphicFramePr,omitempty"`
|
||||||
Locks *AGraphicFrameLocks
|
Locks AGraphicFrameLocks
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML ...
|
// UnmarshalXML ...
|
||||||
@@ -329,20 +329,14 @@ func (w *WPCNvGraphicFramePr) UnmarshalXML(d *xml.Decoder, start xml.StartElemen
|
|||||||
if tt, ok := t.(xml.StartElement); ok {
|
if tt, ok := t.(xml.StartElement); ok {
|
||||||
switch tt.Name.Local {
|
switch tt.Name.Local {
|
||||||
case "graphicFrameLocks":
|
case "graphicFrameLocks":
|
||||||
var value AGraphicFrameLocks
|
|
||||||
err = d.DecodeElement(&value, &tt)
|
|
||||||
if err != nil && !strings.HasPrefix(err.Error(), "expected") {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := getAtt(tt.Attr, "noChangeAspect")
|
v := getAtt(tt.Attr, "noChangeAspect")
|
||||||
if v == "" {
|
if v == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
value.NoChangeAspect, err = strconv.Atoi(v)
|
w.Locks.NoChangeAspect, err = strconv.Atoi(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.Locks = &value
|
|
||||||
default:
|
default:
|
||||||
err = d.Skip() // skip unsupported tags
|
err = d.Skip() // skip unsupported tags
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -358,7 +352,7 @@ func (w *WPCNvGraphicFramePr) UnmarshalXML(d *xml.Decoder, start xml.StartElemen
|
|||||||
// AGraphicFrameLocks represents the locks applied to a graphic frame.
|
// AGraphicFrameLocks represents the locks applied to a graphic frame.
|
||||||
type AGraphicFrameLocks struct {
|
type AGraphicFrameLocks struct {
|
||||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/main graphicFrameLocks,omitempty"`
|
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/main graphicFrameLocks,omitempty"`
|
||||||
NoChangeAspect int `xml:"noChangeAspect,attr"`
|
NoChangeAspect int `xml:"noChangeAspect,attr,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AGraphic represents a graphic in a Word document.
|
// AGraphic represents a graphic in a Word document.
|
||||||
@@ -414,6 +408,7 @@ type AGraphicData struct {
|
|||||||
XMLName xml.Name `xml:"a:graphicData,omitempty"`
|
XMLName xml.Name `xml:"a:graphicData,omitempty"`
|
||||||
URI string `xml:"uri,attr"`
|
URI string `xml:"uri,attr"`
|
||||||
Pic *PICPic
|
Pic *PICPic
|
||||||
|
Shape *WPSWordprocessingShape
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML ...
|
// UnmarshalXML ...
|
||||||
@@ -437,6 +432,13 @@ func (a *AGraphicData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro
|
|||||||
}
|
}
|
||||||
value.XMLPIC = getAtt(tt.Attr, "pic")
|
value.XMLPIC = getAtt(tt.Attr, "pic")
|
||||||
a.Pic = &value
|
a.Pic = &value
|
||||||
|
case "wsp":
|
||||||
|
var value WPSWordprocessingShape
|
||||||
|
err = d.DecodeElement(&value, &tt)
|
||||||
|
if err != nil && !strings.HasPrefix(err.Error(), "expected") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.Shape = &value
|
||||||
default:
|
default:
|
||||||
err = d.Skip() // skip unsupported tags
|
err = d.Skip() // skip unsupported tags
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ type WPSSpPr struct {
|
|||||||
Xfrm AXfrm
|
Xfrm AXfrm
|
||||||
PrstGeom APrstGeom
|
PrstGeom APrstGeom
|
||||||
NoFill *struct{} `xml:"a:noFill,omitempty"`
|
NoFill *struct{} `xml:"a:noFill,omitempty"`
|
||||||
Ln *ALine
|
Elems []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML ...
|
// UnmarshalXML ...
|
||||||
@@ -132,11 +132,12 @@ func (w *WPSSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|||||||
case "noFill":
|
case "noFill":
|
||||||
w.NoFill = &struct{}{}
|
w.NoFill = &struct{}{}
|
||||||
case "ln":
|
case "ln":
|
||||||
w.Ln = &ALine{}
|
var ln ALine
|
||||||
err = d.DecodeElement(&w.Ln, &tt)
|
err = d.DecodeElement(&ln, &tt)
|
||||||
if err != nil && !strings.HasPrefix(err.Error(), "expected") {
|
if err != nil && !strings.HasPrefix(err.Error(), "expected") {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
w.Elems = append(w.Elems, &ln)
|
||||||
default:
|
default:
|
||||||
err = d.Skip() // skip unsupported tags
|
err = d.Skip() // skip unsupported tags
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
77
structshape_test.go
Normal file
77
structshape_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package docx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"hash/crc64"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShapeStructure(t *testing.T) {
|
||||||
|
w := NewA4()
|
||||||
|
// add new paragraph
|
||||||
|
para1 := w.AddParagraph()
|
||||||
|
// add text
|
||||||
|
para1.AddText("test shape")
|
||||||
|
para1.AddShape(808355, 238760, "AutoShape", "auto", "straightConnector1", []interface{}{
|
||||||
|
&ALine{
|
||||||
|
W: 9525,
|
||||||
|
SolidFill: &ASolidFill{SrgbClr: &ASrgbClr{Val: "000000"}},
|
||||||
|
Round: &struct{}{},
|
||||||
|
HeadEnd: &struct{}{},
|
||||||
|
TailEnd: &struct{}{},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
f, err := os.Create("TestMarshalShapeStructure.xml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = marshaller{data: &w.Document}.WriteTo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = f.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
w = NewA4()
|
||||||
|
err = xml.NewDecoder(f).Decode(&w.Document)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
f1, err := os.Create("TestUnmarshalShapeStructure.xml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
_, err = marshaller{data: &w.Document}.WriteTo(f1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = f.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = f1.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
h := crc64.New(crc64.MakeTable(crc64.ECMA))
|
||||||
|
_, err = io.Copy(h, f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
crc1 := h.Sum64()
|
||||||
|
h.Reset()
|
||||||
|
_, err = io.Copy(h, f1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
crc2 := h.Sum64()
|
||||||
|
if crc1 != crc2 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import (
|
|||||||
func unpack(zipReader *zip.Reader) (docx *Docx, err error) {
|
func unpack(zipReader *zip.Reader) (docx *Docx, err error) {
|
||||||
docx = new(Docx)
|
docx = new(Docx)
|
||||||
docx.mediaNameIdx = make(map[string]int, 64)
|
docx.mediaNameIdx = make(map[string]int, 64)
|
||||||
|
docx.slowIDs = make(map[string]uintptr, 64)
|
||||||
docx.tmplfs = zipReader
|
docx.tmplfs = zipReader
|
||||||
docx.tmpfslst = make([]string, 0, 64)
|
docx.tmpfslst = make([]string, 0, 64)
|
||||||
for _, f := range zipReader.File {
|
for _, f := range zipReader.File {
|
||||||
@@ -88,6 +89,7 @@ func (f *Docx) parseDocument(file *zip.File) error {
|
|||||||
f.Document.XMLName.Local = "document"
|
f.Document.XMLName.Local = "document"
|
||||||
|
|
||||||
f.Document.Body.file = f
|
f.Document.Body.file = f
|
||||||
|
//TODO: find last docID
|
||||||
err = xml.NewDecoder(zf).Decode(&f.Document)
|
err = xml.NewDecoder(zf).Decode(&f.Document)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user