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

add AddShape api

This commit is contained in:
源文雨
2023-02-27 16:12:56 +08:00
parent c8aae913f2
commit d19032acad
11 changed files with 211 additions and 23 deletions

View File

@@ -35,9 +35,9 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) {
if err != nil {
return nil, err
}
idn := int(atomic.AddUintptr(&p.file.imageID, 1))
id := strconv.Itoa(idn)
rid := p.file.addImage(Media{Name: "image" + id + "." + format, Data: pic})
idn := int(atomic.AddUintptr(&p.file.docID, 1))
id := strconv.Itoa(int(p.file.IncreaseID("图片")))
rid := p.file.addImage(format, pic)
w, h := int64(sz.Width), int64(sz.Height)
if float64(w)/float64(h) > 1.2 {
h = A4_EMU_MAX_WIDTH * h / w
@@ -61,7 +61,7 @@ func (p *Paragraph) AddInlineDrawing(pic []byte) (*Run, error) {
Name: "图片 " + id,
},
CNvGraphicFramePr: &WPCNvGraphicFramePr{
Locks: &AGraphicFrameLocks{
Locks: AGraphicFrameLocks{
NoChangeAspect: 1,
},
},
@@ -136,9 +136,9 @@ func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) {
if err != nil {
return nil, err
}
idn := int(atomic.AddUintptr(&p.file.imageID, 1))
id := strconv.Itoa(idn)
rid := p.file.addImage(Media{Name: "image" + id + "." + format, Data: pic})
idn := int(atomic.AddUintptr(&p.file.docID, 1))
id := strconv.Itoa(int(p.file.IncreaseID("图片")))
rid := p.file.addImage(format, pic)
w, h := int64(sz.Width), int64(sz.Height)
if float64(w)/float64(h) > 1.2 {
h = A4_EMU_MAX_WIDTH * h / w
@@ -171,7 +171,7 @@ func (p *Paragraph) AddAnchorDrawing(pic []byte) (*Run, error) {
Name: "图片 " + id,
},
CNvGraphicFramePr: &WPCNvGraphicFramePr{
Locks: &AGraphicFrameLocks{
Locks: AGraphicFrameLocks{
NoChangeAspect: 1,
},
},

73
apishape.go Normal file
View 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
}

View File

@@ -123,6 +123,18 @@ func main() {
}
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)
if err != nil {
panic(err)

View File

@@ -27,6 +27,7 @@ import (
"bytes"
"io"
"io/fs"
"sync"
)
// Docx is the structure that allow to access the internal represntation
@@ -39,8 +40,11 @@ type Docx struct {
media []Media
mediaNameIdx map[string]int
rID uintptr
imageID uintptr
rID uintptr
imageID uintptr
docID uintptr
slowIDs map[string]uintptr
slowIDsMu sync.Mutex
template string
tmplfs fs.FS

View File

@@ -74,6 +74,7 @@ func newEmptyA4File() *Docx {
media: make([]Media, 0, 64),
mediaNameIdx: make(map[string]int, 64),
rID: 5,
slowIDs: make(map[string]uintptr, 64),
template: "a4",
tmpfslst: []string{
"_rels/.rels",

10
id.go Normal file
View 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
}

View File

@@ -20,8 +20,14 @@
package docx
import (
"strconv"
"sync/atomic"
)
// 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)
return f.addImageRelation(m)
}

View File

@@ -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.
type WPCNvGraphicFramePr struct {
XMLName xml.Name `xml:"wp:cNvGraphicFramePr,omitempty"`
Locks *AGraphicFrameLocks
Locks AGraphicFrameLocks
}
// UnmarshalXML ...
@@ -329,20 +329,14 @@ func (w *WPCNvGraphicFramePr) UnmarshalXML(d *xml.Decoder, start xml.StartElemen
if tt, ok := t.(xml.StartElement); ok {
switch tt.Name.Local {
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")
if v == "" {
continue
}
value.NoChangeAspect, err = strconv.Atoi(v)
w.Locks.NoChangeAspect, err = strconv.Atoi(v)
if err != nil {
return err
}
w.Locks = &value
default:
err = d.Skip() // skip unsupported tags
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.
type AGraphicFrameLocks struct {
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.
@@ -414,6 +408,7 @@ type AGraphicData struct {
XMLName xml.Name `xml:"a:graphicData,omitempty"`
URI string `xml:"uri,attr"`
Pic *PICPic
Shape *WPSWordprocessingShape
}
// UnmarshalXML ...
@@ -437,6 +432,13 @@ 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
err = d.DecodeElement(&value, &tt)
if err != nil && !strings.HasPrefix(err.Error(), "expected") {
return err
}
a.Shape = &value
default:
err = d.Skip() // skip unsupported tags
if err != nil {

View File

@@ -98,7 +98,7 @@ type WPSSpPr struct {
Xfrm AXfrm
PrstGeom APrstGeom
NoFill *struct{} `xml:"a:noFill,omitempty"`
Ln *ALine
Elems []interface{}
}
// UnmarshalXML ...
@@ -132,11 +132,12 @@ func (w *WPSSpPr) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
case "noFill":
w.NoFill = &struct{}{}
case "ln":
w.Ln = &ALine{}
err = d.DecodeElement(&w.Ln, &tt)
var ln ALine
err = d.DecodeElement(&ln, &tt)
if err != nil && !strings.HasPrefix(err.Error(), "expected") {
return err
}
w.Elems = append(w.Elems, &ln)
default:
err = d.Skip() // skip unsupported tags
if err != nil {

77
structshape_test.go Normal file
View 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()
}
}

View File

@@ -39,6 +39,7 @@ import (
func unpack(zipReader *zip.Reader) (docx *Docx, err error) {
docx = new(Docx)
docx.mediaNameIdx = make(map[string]int, 64)
docx.slowIDs = make(map[string]uintptr, 64)
docx.tmplfs = zipReader
docx.tmpfslst = make([]string, 0, 64)
for _, f := range zipReader.File {
@@ -88,6 +89,7 @@ func (f *Docx) parseDocument(file *zip.File) error {
f.Document.XMLName.Local = "document"
f.Document.Body.file = f
//TODO: find last docID
err = xml.NewDecoder(zf).Decode(&f.Document)
return err
}