From 8d5685b237206bbf7aa73c79b1686b1894d87404 Mon Sep 17 00:00:00 2001 From: zhexiao Date: Wed, 9 Oct 2019 17:46:11 +0800 Subject: [PATCH 1/7] get inline image from Run --- document/document.go | 8 ++++++++ document/run.go | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/document/document.go b/document/document.go index 694bf818c1..11d0db023e 100644 --- a/document/document.go +++ b/document/document.go @@ -839,6 +839,14 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin return err } iref = common.MakeImageRef(img, &d.DocBase, d.docRels) + + //name: zhexiao + //date: 2019-10-09 + //ImageRef add relID + //================================start + iref.SetRelID(rel.IdAttr) + //================================end + d.Images = append(d.Images, iref) files[i] = nil } diff --git a/document/run.go b/document/run.go index b467cc5c2c..d8a641f12e 100644 --- a/document/run.go +++ b/document/run.go @@ -133,6 +133,26 @@ func (r Run) AddPageBreak() { ic.Br.TypeAttr = wml.ST_BrTypePage } +//name: zhexiao +//date: 2019-10-09 +//get inline image from document xml +//================================start +func (r Run) DrawingInline() []InlineDrawing { + var ret []InlineDrawing + for _, ic := range r.x.EG_RunInnerContent { + if ic.Drawing == nil { + continue + } + + for _, inl := range ic.Drawing.Inline { + ret = append(ret, InlineDrawing{r.d, inl}) + } + } + return ret +} + +//=================================end + // DrawingAnchored returns a slice of AnchoredDrawings. func (r Run) DrawingAnchored() []AnchoredDrawing { ret := []AnchoredDrawing{} From a5cddfaf1ec68e724a0d80556631aec6520f051a Mon Sep 17 00:00:00 2001 From: zhexiao Date: Thu, 10 Oct 2019 17:35:05 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AF=B9word=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=B8=ADoleObject=E5=85=AC=E5=BC=8F=E7=9A=84=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=EF=BC=81=EF=BC=81=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- document/oleobject.go | 29 ++++++++ document/run.go | 67 ++++++++++++----- schema/soo/wml/CT_Imagedata.go | 95 ++++++++++++++++++++++++ schema/soo/wml/CT_Object.go | 29 +++++++- schema/soo/wml/CT_OleObject.go | 128 ++++++++++++++++++++++++++++++++ schema/soo/wml/CT_Shape.go | 130 +++++++++++++++++++++++++++++++++ 6 files changed, 457 insertions(+), 21 deletions(-) create mode 100644 document/oleobject.go create mode 100644 schema/soo/wml/CT_Imagedata.go create mode 100644 schema/soo/wml/CT_OleObject.go create mode 100644 schema/soo/wml/CT_Shape.go diff --git a/document/oleobject.go b/document/oleobject.go new file mode 100644 index 0000000000..5d3c9c71c4 --- /dev/null +++ b/document/oleobject.go @@ -0,0 +1,29 @@ +//name: zhexiao +//date: 2019-10-10 +//OLEObject struct +//================================start +package document + +import "github.com/unidoc/unioffice/schema/soo/wml" + +// InlineDrawing is an inlined image within a run. +type OleObject struct { + oleobject *wml.CT_OleObject + shape *wml.CT_Shape +} + +func (o OleObject) Shape() *wml.CT_Shape { + return o.shape +} + +func (o OleObject) OleObject() *wml.CT_OleObject { + return o.oleobject +} + +func (o OleObject) OleRid() string { + return *o.oleobject.IdAttr +} + +func (o OleObject) ImagedataRid() string { + return *o.shape.Imagedata.IdAttr +} diff --git a/document/run.go b/document/run.go index d8a641f12e..17c3804391 100644 --- a/document/run.go +++ b/document/run.go @@ -133,26 +133,6 @@ func (r Run) AddPageBreak() { ic.Br.TypeAttr = wml.ST_BrTypePage } -//name: zhexiao -//date: 2019-10-09 -//get inline image from document xml -//================================start -func (r Run) DrawingInline() []InlineDrawing { - var ret []InlineDrawing - for _, ic := range r.x.EG_RunInnerContent { - if ic.Drawing == nil { - continue - } - - for _, inl := range ic.Drawing.Inline { - ret = append(ret, InlineDrawing{r.d, inl}) - } - } - return ret -} - -//=================================end - // DrawingAnchored returns a slice of AnchoredDrawings. func (r Run) DrawingAnchored() []AnchoredDrawing { ret := []AnchoredDrawing{} @@ -301,3 +281,50 @@ func (r Run) AddDrawingInline(img common.ImageRef) (InlineDrawing, error) { return inline, nil } + +//name: zhexiao +//date: 2019-10-09 +//get inline image from document xml +//================================start +func (r Run) DrawingInline() []InlineDrawing { + var ret []InlineDrawing + for _, ic := range r.x.EG_RunInnerContent { + if ic.Drawing == nil { + continue + } + + for _, inl := range ic.Drawing.Inline { + ret = append(ret, InlineDrawing{r.d, inl}) + } + } + return ret +} + +//=================================end + +//name: zhexiao +//date: 2019-10-10 +//get OLEObject from document xml +//================================start +func (r Run) OleObjects() []OleObject { + var ret []OleObject + for _, ic := range r.x.EG_RunInnerContent { + if ic.Object == nil { + continue + } + + //读取对象 + oleObject := ic.Object.OleObject + shape := ic.Object.Shape + + if oleObject == nil || shape == nil { + continue + } + + ret = append(ret, OleObject{oleobject: oleObject, shape: shape}) + } + + return ret +} + +//=================================end diff --git a/schema/soo/wml/CT_Imagedata.go b/schema/soo/wml/CT_Imagedata.go new file mode 100644 index 0000000000..55ee85e70a --- /dev/null +++ b/schema/soo/wml/CT_Imagedata.go @@ -0,0 +1,95 @@ +//name: zhexiao +//date: 2019-10-10 +//create object shape imagedata for document xml +//================================start +package wml + +import ( + "encoding/xml" + "fmt" + "github.com/unidoc/unioffice" +) + +type CT_Imagedata struct { + //r:id + IdAttr *string + + TitleAttr *string +} + +func NewCT_Imagedata() *CT_Imagedata { + ret := &CT_Imagedata{} + return ret +} + +func (m *CT_Imagedata) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if m.IdAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "r:id"}, + Value: fmt.Sprintf("%v", *m.IdAttr)}) + } + + if m.TitleAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "title"}, + Value: fmt.Sprintf("%v", *m.TitleAttr)}) + } + + _ = e.EncodeToken(start) + _ = e.EncodeToken(xml.EndElement{Name: start.Name}) + return nil +} + +func (m *CT_Imagedata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + // initialize to default + for _, attr := range start.Attr { + if attr.Name.Space == "http://schemas.openxmlformats.org/officeDocument/2006/relationships" && attr.Name.Local == "id" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.IdAttr = &parsed + continue + } + + if attr.Name.Space == "urn:schemas-microsoft-com:office:office" && attr.Name.Local == "title" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.TitleAttr = &parsed + continue + } + } +lCT_Imagedata: + for { + tok, err := d.Token() + if err != nil { + return err + } + switch el := tok.(type) { + case xml.StartElement: + switch el.Name { + default: + unioffice.Log("skipping unsupported element on CT_Imagedata %v", el.Name) + if err := d.Skip(); err != nil { + return err + } + } + case xml.EndElement: + break lCT_Imagedata + case xml.CharData: + } + } + return nil +} + +// Validate validates the CT_OleObject and its children +func (m *CT_Imagedata) Validate() error { + return m.ValidateWithPath("CT_Imagedata") +} + +// ValidateWithPath validates the CT_OleObject and its children, prefixing error messages with path +func (m *CT_Imagedata) ValidateWithPath(path string) error { + return nil +} + +//================================end diff --git a/schema/soo/wml/CT_Object.go b/schema/soo/wml/CT_Object.go index 1171a568ed..969e0cdfb5 100644 --- a/schema/soo/wml/CT_Object.go +++ b/schema/soo/wml/CT_Object.go @@ -12,7 +12,6 @@ package wml import ( "encoding/xml" "fmt" - "github.com/unidoc/unioffice" "github.com/unidoc/unioffice/schema/soo/ofc/sharedTypes" ) @@ -24,6 +23,14 @@ type CT_Object struct { DyaOrigAttr *sharedTypes.ST_TwipsMeasure Drawing *CT_Drawing Choice *CT_ObjectChoice + + //name: zhexiao + //date: 2019-10-10 + //add OLEObject and shape + //================================start + OleObject *CT_OleObject + Shape *CT_Shape + //================================end } func NewCT_Object() *CT_Object { @@ -111,6 +118,26 @@ lCT_Object: if err := d.DecodeElement(&m.Choice.Movie, &el); err != nil { return err } + + //name: zhexiao + //date: 2019-10-10 + //add shape and OLEObject + //================================start + case xml.Name{Space: "urn:schemas-microsoft-com:vml", Local: "shape"}: + m.Shape = NewCT_Shape() + if err := d.DecodeElement(&m.Shape, &el); err != nil { + return err + } + //fmt.Printf("%#v \n", m.Shape) + + case xml.Name{Space: "urn:schemas-microsoft-com:office:office", Local: "OLEObject"}: + m.OleObject = NewCT_OleObject() + if err := d.DecodeElement(&m.OleObject, &el); err != nil { + return err + } + //fmt.Printf("%#v \n", m.OleObject) + //================================end + default: unioffice.Log("skipping unsupported element on CT_Object %v", el.Name) if err := d.Skip(); err != nil { diff --git a/schema/soo/wml/CT_OleObject.go b/schema/soo/wml/CT_OleObject.go new file mode 100644 index 0000000000..a7ac28a1ca --- /dev/null +++ b/schema/soo/wml/CT_OleObject.go @@ -0,0 +1,128 @@ +//name: zhexiao +//date: 2019-10-10 +//create object OLEObject for document xml +//================================start +package wml + +import ( + "encoding/xml" + "fmt" + "github.com/unidoc/unioffice" +) + +type CT_OleObject struct { + TypeAttr *string + + // Embedded Object ProgId + ProgIdAttr *string + + // Shape Id + ShapeIdAttr *string + + //r:id + IdAttr *string +} + +func NewCT_OleObject() *CT_OleObject { + ret := &CT_OleObject{} + return ret +} + +func (m *CT_OleObject) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if m.TypeAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "Type"}, + Value: fmt.Sprintf("%v", *m.TypeAttr)}) + } + + if m.ProgIdAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "ProgID"}, + Value: fmt.Sprintf("%v", *m.ProgIdAttr)}) + } + + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "ShapeID"}, + Value: fmt.Sprintf("%v", m.ShapeIdAttr)}) + + if m.IdAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "r:id"}, + Value: fmt.Sprintf("%v", *m.IdAttr)}) + } + + _ = e.EncodeToken(start) + _ = e.EncodeToken(xml.EndElement{Name: start.Name}) + return nil +} + +func (m *CT_OleObject) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + // initialize to default + for _, attr := range start.Attr { + if attr.Name.Local == "Type" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.TypeAttr = &parsed + continue + } + + if attr.Name.Local == "ProgID" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.ProgIdAttr = &parsed + continue + } + + if attr.Name.Local == "ShapeID" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.ShapeIdAttr = &parsed + continue + } + + if attr.Name.Space == "http://schemas.openxmlformats.org/officeDocument/2006/relationships" && attr.Name.Local == "id" || + attr.Name.Space == "http://purl.oclc.org/ooxml/officeDocument/relationships" && attr.Name.Local == "id" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.IdAttr = &parsed + continue + } + } +lCT_OleObject: + for { + tok, err := d.Token() + if err != nil { + return err + } + switch el := tok.(type) { + case xml.StartElement: + switch el.Name { + default: + unioffice.Log("skipping unsupported element on CT_OleObject %v", el.Name) + if err := d.Skip(); err != nil { + return err + } + } + case xml.EndElement: + break lCT_OleObject + case xml.CharData: + } + } + return nil +} + +// Validate validates the CT_OleObject and its children +func (m *CT_OleObject) Validate() error { + return m.ValidateWithPath("CT_OleObject") +} + +// ValidateWithPath validates the CT_OleObject and its children, prefixing error messages with path +func (m *CT_OleObject) ValidateWithPath(path string) error { + return nil +} + +//================================end diff --git a/schema/soo/wml/CT_Shape.go b/schema/soo/wml/CT_Shape.go new file mode 100644 index 0000000000..201fe6a18a --- /dev/null +++ b/schema/soo/wml/CT_Shape.go @@ -0,0 +1,130 @@ +//name: zhexiao +//date: 2019-10-10 +//create object shape for document xml +//================================start +package wml + +import ( + "encoding/xml" + "fmt" + "github.com/unidoc/unioffice" +) + +type CT_Shape struct { + IdAttr *string + TypeAttr *string + StyleAttr *string + OleAttr *string + + Imagedata *CT_Imagedata +} + +func NewCT_Shape() *CT_Shape { + ret := &CT_Shape{} + return ret +} + +func (m *CT_Shape) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if m.TypeAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "type"}, + Value: fmt.Sprintf("%v", *m.TypeAttr)}) + } + + if m.IdAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "id"}, + Value: fmt.Sprintf("%v", *m.IdAttr)}) + } + + if m.StyleAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "style"}, + Value: fmt.Sprintf("%v", *m.StyleAttr)}) + } + + if m.OleAttr != nil { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "o:ole"}, + Value: fmt.Sprintf("%v", *m.OleAttr)}) + } + + _ = e.EncodeToken(start) + _ = e.EncodeToken(xml.EndElement{Name: start.Name}) + return nil +} + +func (m *CT_Shape) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + // initialize to default + for _, attr := range start.Attr { + if attr.Name.Local == "id" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.IdAttr = &parsed + continue + } + + if attr.Name.Local == "type" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.TypeAttr = &parsed + continue + } + + if attr.Name.Local == "style" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.StyleAttr = &parsed + continue + } + + if attr.Name.Space == "urn:schemas-microsoft-com:office:office" && attr.Name.Local == "ole" { + parsed, err := attr.Value, error(nil) + if err != nil { + return err + } + m.OleAttr = &parsed + continue + } + } +lCT_Shape: + for { + tok, err := d.Token() + if err != nil { + return err + } + switch el := tok.(type) { + case xml.StartElement: + switch el.Name { + case xml.Name{Space: "urn:schemas-microsoft-com:vml", Local: "imagedata"}: + m.Imagedata = NewCT_Imagedata() + if err := d.DecodeElement(&m.Imagedata, &el); err != nil { + return err + } + default: + unioffice.Log("skipping unsupported element on CT_Shape %v", el.Name) + if err := d.Skip(); err != nil { + return err + } + } + case xml.EndElement: + break lCT_Shape + case xml.CharData: + } + } + return nil +} + +// Validate validates the CT_OleObject and its children +func (m *CT_Shape) Validate() error { + return m.ValidateWithPath("CT_Shape") +} + +// ValidateWithPath validates the CT_OleObject and its children, prefixing error messages with path +func (m *CT_Shape) ValidateWithPath(path string) error { + return nil +} + +//================================end From b94216a54929c9756a03f4db7c54428d07131fb0 Mon Sep 17 00:00:00 2001 From: zhexiao Date: Fri, 11 Oct 2019 15:23:51 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AF=B9=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=B8=AD=E5=85=AC=E5=BC=8F=E3=80=81=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- document/document.go | 56 ++++++++++++++++++++++++++++++++-- document/oleobject.go | 31 +++++++++++++++++-- document/run.go | 8 ++--- schema/soo/wml/CT_Imagedata.go | 4 +-- schema/soo/wml/CT_Object.go | 8 ++--- schema/soo/wml/CT_OleObject.go | 4 +-- schema/soo/wml/CT_Shape.go | 4 +-- schemas.go | 7 +++++ 8 files changed, 103 insertions(+), 19 deletions(-) diff --git a/document/document.go b/document/document.go index 11d0db023e..6ffc3b97e7 100644 --- a/document/document.go +++ b/document/document.go @@ -56,6 +56,15 @@ type Document struct { fontTable *wml.Fonts endNotes *wml.Endnotes footNotes *wml.Footnotes + + //name: zhexiao(肖哲) + //date: 2019-10-09 + //编写doc数据结构对OLEobject类型的支持,一个公式路径,一个是公式自带的wmf图片路径 + //================================start + OleObjectPaths []OleObjectPath + OleObjectWmfPath []OleObjectWmfPath + //================================end + } // New constructs an empty document that content can be added to. @@ -829,20 +838,37 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin if f == nil { continue } + if f.Name == target { path, err := zippkg.ExtractToDiskTmp(f, d.TmpPath) if err != nil { return err } + + //name: zhexiao(肖哲) + //date: 2019-10-09 + //处理公式自带的wmf图片 + //================================start + if strings.Contains(rel.TargetAttr, ".wmf") { + d.OleObjectWmfPath = append(d.OleObjectWmfPath, OleObjectWmfPath{ + rid: rel.IdAttr, + path: path, + }) + + continue + } + //================================end + img, err := common.ImageFromFile(path) if err != nil { return err } + iref = common.MakeImageRef(img, &d.DocBase, d.docRels) - //name: zhexiao + //name: zhexiao(肖哲) //date: 2019-10-09 - //ImageRef add relID + //BUG修复:新增图片的relID //================================start iref.SetRelID(rel.IdAttr) //================================end @@ -859,6 +885,32 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin rel.TargetAttr = rel.TargetAttr[0:len(rel.TargetAttr)-len(newExt)] + ext } + //name: zhexiao(肖哲) + //date: 2019-10-09 + //对文档公式数据的解析 + //================================start + case unioffice.OleObjectType: + for _, f := range files { + if f == nil { + continue + } + + //fmt.Printf("%#v \n", rel) + + if f.Name == target { + path, err := zippkg.ExtractToDiskTmp(f, d.TmpPath) + if err != nil { + return err + } + + d.OleObjectPaths = append(d.OleObjectPaths, OleObjectPath{ + rid: rel.IdAttr, + path: path, + }) + } + } + //================================end + default: unioffice.Log("unsupported relationship type: %s tgt: %s", typ, target) } diff --git a/document/oleobject.go b/document/oleobject.go index 5d3c9c71c4..dea47d6a04 100644 --- a/document/oleobject.go +++ b/document/oleobject.go @@ -1,12 +1,21 @@ -//name: zhexiao +//name: zhexiao(肖哲) //date: 2019-10-10 -//OLEObject struct +//一些与公式相关的数据结构体 //================================start package document import "github.com/unidoc/unioffice/schema/soo/wml" -// InlineDrawing is an inlined image within a run. +type OleObjectPath struct { + rid string + path string +} + +type OleObjectWmfPath struct { + rid string + path string +} + type OleObject struct { oleobject *wml.CT_OleObject shape *wml.CT_Shape @@ -27,3 +36,19 @@ func (o OleObject) OleRid() string { func (o OleObject) ImagedataRid() string { return *o.shape.Imagedata.IdAttr } + +func (o OleObjectPath) Rid() string { + return o.rid +} + +func (o OleObjectPath) Path() string { + return o.path +} + +func (o OleObjectWmfPath) Rid() string { + return o.rid +} + +func (o OleObjectWmfPath) Path() string { + return o.path +} diff --git a/document/run.go b/document/run.go index 17c3804391..384e2d9a3e 100644 --- a/document/run.go +++ b/document/run.go @@ -282,9 +282,9 @@ func (r Run) AddDrawingInline(img common.ImageRef) (InlineDrawing, error) { return inline, nil } -//name: zhexiao +//name: zhexiao(肖哲) //date: 2019-10-09 -//get inline image from document xml +//从doc xml中读取inline类型的图片 //================================start func (r Run) DrawingInline() []InlineDrawing { var ret []InlineDrawing @@ -302,9 +302,9 @@ func (r Run) DrawingInline() []InlineDrawing { //=================================end -//name: zhexiao +//name: zhexiao(肖哲) //date: 2019-10-10 -//get OLEObject from document xml +//从doc xml中读取ole公式对象数据 //================================start func (r Run) OleObjects() []OleObject { var ret []OleObject diff --git a/schema/soo/wml/CT_Imagedata.go b/schema/soo/wml/CT_Imagedata.go index 55ee85e70a..b9f942cc8e 100644 --- a/schema/soo/wml/CT_Imagedata.go +++ b/schema/soo/wml/CT_Imagedata.go @@ -1,6 +1,6 @@ -//name: zhexiao +//name: zhexiao(肖哲) //date: 2019-10-10 -//create object shape imagedata for document xml +//新增CT_Shape下面的imagedata解析 //================================start package wml diff --git a/schema/soo/wml/CT_Object.go b/schema/soo/wml/CT_Object.go index 969e0cdfb5..34763df149 100644 --- a/schema/soo/wml/CT_Object.go +++ b/schema/soo/wml/CT_Object.go @@ -24,9 +24,9 @@ type CT_Object struct { Drawing *CT_Drawing Choice *CT_ObjectChoice - //name: zhexiao + //name: zhexiao(肖哲) //date: 2019-10-10 - //add OLEObject and shape + //新增CT_Object对OLEObject和Shape属性的支持 //================================start OleObject *CT_OleObject Shape *CT_Shape @@ -119,9 +119,9 @@ lCT_Object: return err } - //name: zhexiao + //name: zhexiao(肖哲) //date: 2019-10-10 - //add shape and OLEObject + //解析Shape和Oleobject属性数据 //================================start case xml.Name{Space: "urn:schemas-microsoft-com:vml", Local: "shape"}: m.Shape = NewCT_Shape() diff --git a/schema/soo/wml/CT_OleObject.go b/schema/soo/wml/CT_OleObject.go index a7ac28a1ca..90644ccdf1 100644 --- a/schema/soo/wml/CT_OleObject.go +++ b/schema/soo/wml/CT_OleObject.go @@ -1,6 +1,6 @@ -//name: zhexiao +//name: zhexiao(肖哲) //date: 2019-10-10 -//create object OLEObject for document xml +//新增CT_Object下面的OleObject解析 //================================start package wml diff --git a/schema/soo/wml/CT_Shape.go b/schema/soo/wml/CT_Shape.go index 201fe6a18a..d422d849a0 100644 --- a/schema/soo/wml/CT_Shape.go +++ b/schema/soo/wml/CT_Shape.go @@ -1,6 +1,6 @@ -//name: zhexiao +//name: zhexiao(肖哲) //date: 2019-10-10 -//create object shape for document xml +//新增CT_Object下面的Shape解析 //================================start package wml diff --git a/schemas.go b/schemas.go index af028e6c3a..62227462fa 100644 --- a/schemas.go +++ b/schemas.go @@ -62,6 +62,13 @@ const ( CustomPropertiesType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" CustomXMLType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml" + //name: zhexiao(肖哲) + //date: 2019-10-10 + //新增文档初始化解析对OleObject的处理 + //================================start + OleObjectType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject" + //================================end + // SML WorksheetType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" WorksheetContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" From 05ce52bb981496ecf7236a1523053823d373148b Mon Sep 17 00:00:00 2001 From: zhexiao Date: Tue, 15 Oct 2019 10:56:57 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=B0=BA=E5=AF=B8=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schema/soo/dml/CT_PositiveSize2D.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/schema/soo/dml/CT_PositiveSize2D.go b/schema/soo/dml/CT_PositiveSize2D.go index f9537c27ed..3a6f3b6cae 100644 --- a/schema/soo/dml/CT_PositiveSize2D.go +++ b/schema/soo/dml/CT_PositiveSize2D.go @@ -93,3 +93,32 @@ func (m *CT_PositiveSize2D) ValidateWithPath(path string) error { } return nil } + +//name: zhexiao(肖哲) +//date: 2019-10-15 +//计算图片的宽度和长度 +//在Open XML里使用了English Metric Units(EMUs)来作为度量单位,比如 +//https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.wordprocessing.extent?view=openxml-2.8.1 +//================================start +type EmuSize struct { + Width string + Height string +} + +const ( + mm = 36000 + cm = 360000 + in = 914400 + pt = 12700 + pc = 152400 + pi = 152400 +) + +//这里只计算返回的单位为PT的大小 +func (m *CT_PositiveSize2D) Size() EmuSize { + return EmuSize{ + Width: fmt.Sprintf("%spt", strconv.FormatInt(m.CxAttr / pt, 10)), + Height: fmt.Sprintf("%spt", strconv.FormatInt(m.CyAttr / pt, 10))} +} + +//================================end From 9a043bdb3104a9820bac143cfbbdc5367f802cbe Mon Sep 17 00:00:00 2001 From: zhexiao Date: Fri, 20 Dec 2019 15:59:11 +0800 Subject: [PATCH 5/7] update code by follow the developer ruler --- document/document.go | 23 ----------------------- document/oleobject.go | 10 ++++++---- document/run.go | 12 ------------ go.mod | 2 ++ schema/soo/dml/CT_PositiveSize2D.go | 14 ++++---------- schema/soo/wml/CT_Imagedata.go | 12 ++++++------ schema/soo/wml/CT_Object.go | 12 ------------ schema/soo/wml/CT_OleObject.go | 12 ++++++------ schema/soo/wml/CT_Shape.go | 12 ++++++------ schemas.go | 8 +------- 10 files changed, 31 insertions(+), 86 deletions(-) diff --git a/document/document.go b/document/document.go index 24fa79375d..a01fd70b02 100644 --- a/document/document.go +++ b/document/document.go @@ -57,14 +57,8 @@ type Document struct { endNotes *wml.Endnotes footNotes *wml.Footnotes - //name: zhexiao(肖哲) - //date: 2019-10-09 - //编写doc数据结构对OLEobject类型的支持,一个公式路径,一个是公式自带的wmf图片路径 - //================================start OleObjectPaths []OleObjectPath OleObjectWmfPath []OleObjectWmfPath - //================================end - } // New constructs an empty document that content can be added to. @@ -874,10 +868,6 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin return err } - //name: zhexiao(肖哲) - //date: 2019-10-09 - //处理公式自带的wmf图片 - //================================start if strings.Contains(rel.TargetAttr, ".wmf") { d.OleObjectWmfPath = append(d.OleObjectWmfPath, OleObjectWmfPath{ rid: rel.IdAttr, @@ -886,7 +876,6 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin continue } - //================================end img, err := common.ImageFromFile(path) if err != nil { @@ -894,13 +883,7 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin } iref = common.MakeImageRef(img, &d.DocBase, d.docRels) - - //name: zhexiao(肖哲) - //date: 2019-10-09 - //BUG修复:新增图片的relID - //================================start iref.SetRelID(rel.IdAttr) - //================================end d.Images = append(d.Images, iref) files[i] = nil @@ -914,10 +897,6 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin rel.TargetAttr = rel.TargetAttr[0:len(rel.TargetAttr)-len(newExt)] + ext } - //name: zhexiao(肖哲) - //date: 2019-10-09 - //对文档公式数据的解析 - //================================start case unioffice.OleObjectType: for _, f := range files { if f == nil { @@ -938,8 +917,6 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin }) } } - //================================end - default: unioffice.Log("unsupported relationship type: %s tgt: %s", typ, target) } diff --git a/document/oleobject.go b/document/oleobject.go index dea47d6a04..a14cd33f72 100644 --- a/document/oleobject.go +++ b/document/oleobject.go @@ -1,7 +1,9 @@ -//name: zhexiao(肖哲) -//date: 2019-10-10 -//一些与公式相关的数据结构体 -//================================start +// Copyright 2017 FoxyUtils ehf. All rights reserved. +// +// Use of this source code is governed by the terms of the Affero GNU General +// Public License version 3.0 as published by the Free Software Foundation and +// appearing in the file LICENSE included in the packaging of this file. A +// commercial license can be purchased by contacting sales@baliance.com. package document import "github.com/unidoc/unioffice/schema/soo/wml" diff --git a/document/run.go b/document/run.go index 384e2d9a3e..8ad3ac9fc7 100644 --- a/document/run.go +++ b/document/run.go @@ -282,10 +282,6 @@ func (r Run) AddDrawingInline(img common.ImageRef) (InlineDrawing, error) { return inline, nil } -//name: zhexiao(肖哲) -//date: 2019-10-09 -//从doc xml中读取inline类型的图片 -//================================start func (r Run) DrawingInline() []InlineDrawing { var ret []InlineDrawing for _, ic := range r.x.EG_RunInnerContent { @@ -300,12 +296,6 @@ func (r Run) DrawingInline() []InlineDrawing { return ret } -//=================================end - -//name: zhexiao(肖哲) -//date: 2019-10-10 -//从doc xml中读取ole公式对象数据 -//================================start func (r Run) OleObjects() []OleObject { var ret []OleObject for _, ic := range r.x.EG_RunInnerContent { @@ -326,5 +316,3 @@ func (r Run) OleObjects() []OleObject { return ret } - -//=================================end diff --git a/go.mod b/go.mod index 234a1742f8..8867b93fe8 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,3 @@ module github.com/unidoc/unioffice + +go 1.13 diff --git a/schema/soo/dml/CT_PositiveSize2D.go b/schema/soo/dml/CT_PositiveSize2D.go index 3a6f3b6cae..bd81581444 100644 --- a/schema/soo/dml/CT_PositiveSize2D.go +++ b/schema/soo/dml/CT_PositiveSize2D.go @@ -94,12 +94,8 @@ func (m *CT_PositiveSize2D) ValidateWithPath(path string) error { return nil } -//name: zhexiao(肖哲) -//date: 2019-10-15 -//计算图片的宽度和长度 -//在Open XML里使用了English Metric Units(EMUs)来作为度量单位,比如 -//https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.wordprocessing.extent?view=openxml-2.8.1 -//================================start +//calculate image width and height +//convention: https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.drawing.wordprocessing.extent?view=openxml-2.8.1 type EmuSize struct { Width string Height string @@ -117,8 +113,6 @@ const ( //这里只计算返回的单位为PT的大小 func (m *CT_PositiveSize2D) Size() EmuSize { return EmuSize{ - Width: fmt.Sprintf("%spt", strconv.FormatInt(m.CxAttr / pt, 10)), - Height: fmt.Sprintf("%spt", strconv.FormatInt(m.CyAttr / pt, 10))} + Width: fmt.Sprintf("%spt", strconv.FormatInt(m.CxAttr/pt, 10)), + Height: fmt.Sprintf("%spt", strconv.FormatInt(m.CyAttr/pt, 10))} } - -//================================end diff --git a/schema/soo/wml/CT_Imagedata.go b/schema/soo/wml/CT_Imagedata.go index b9f942cc8e..865d2ba7b3 100644 --- a/schema/soo/wml/CT_Imagedata.go +++ b/schema/soo/wml/CT_Imagedata.go @@ -1,7 +1,9 @@ -//name: zhexiao(肖哲) -//date: 2019-10-10 -//新增CT_Shape下面的imagedata解析 -//================================start +// Copyright 2017 FoxyUtils ehf. All rights reserved. +// +// Use of this source code is governed by the terms of the Affero GNU General +// Public License version 3.0 as published by the Free Software Foundation and +// appearing in the file LICENSE included in the packaging of this file. A +// commercial license can be purchased by contacting sales@baliance.com. package wml import ( @@ -91,5 +93,3 @@ func (m *CT_Imagedata) Validate() error { func (m *CT_Imagedata) ValidateWithPath(path string) error { return nil } - -//================================end diff --git a/schema/soo/wml/CT_Object.go b/schema/soo/wml/CT_Object.go index 34763df149..142eb083a5 100644 --- a/schema/soo/wml/CT_Object.go +++ b/schema/soo/wml/CT_Object.go @@ -24,13 +24,8 @@ type CT_Object struct { Drawing *CT_Drawing Choice *CT_ObjectChoice - //name: zhexiao(肖哲) - //date: 2019-10-10 - //新增CT_Object对OLEObject和Shape属性的支持 - //================================start OleObject *CT_OleObject Shape *CT_Shape - //================================end } func NewCT_Object() *CT_Object { @@ -119,24 +114,17 @@ lCT_Object: return err } - //name: zhexiao(肖哲) - //date: 2019-10-10 - //解析Shape和Oleobject属性数据 - //================================start case xml.Name{Space: "urn:schemas-microsoft-com:vml", Local: "shape"}: m.Shape = NewCT_Shape() if err := d.DecodeElement(&m.Shape, &el); err != nil { return err } - //fmt.Printf("%#v \n", m.Shape) case xml.Name{Space: "urn:schemas-microsoft-com:office:office", Local: "OLEObject"}: m.OleObject = NewCT_OleObject() if err := d.DecodeElement(&m.OleObject, &el); err != nil { return err } - //fmt.Printf("%#v \n", m.OleObject) - //================================end default: unioffice.Log("skipping unsupported element on CT_Object %v", el.Name) diff --git a/schema/soo/wml/CT_OleObject.go b/schema/soo/wml/CT_OleObject.go index 90644ccdf1..6915e80f02 100644 --- a/schema/soo/wml/CT_OleObject.go +++ b/schema/soo/wml/CT_OleObject.go @@ -1,7 +1,9 @@ -//name: zhexiao(肖哲) -//date: 2019-10-10 -//新增CT_Object下面的OleObject解析 -//================================start +// Copyright 2017 FoxyUtils ehf. All rights reserved. +// +// Use of this source code is governed by the terms of the Affero GNU General +// Public License version 3.0 as published by the Free Software Foundation and +// appearing in the file LICENSE included in the packaging of this file. A +// commercial license can be purchased by contacting sales@baliance.com. package wml import ( @@ -124,5 +126,3 @@ func (m *CT_OleObject) Validate() error { func (m *CT_OleObject) ValidateWithPath(path string) error { return nil } - -//================================end diff --git a/schema/soo/wml/CT_Shape.go b/schema/soo/wml/CT_Shape.go index d422d849a0..ac6c845368 100644 --- a/schema/soo/wml/CT_Shape.go +++ b/schema/soo/wml/CT_Shape.go @@ -1,7 +1,9 @@ -//name: zhexiao(肖哲) -//date: 2019-10-10 -//新增CT_Object下面的Shape解析 -//================================start +// Copyright 2017 FoxyUtils ehf. All rights reserved. +// +// Use of this source code is governed by the terms of the Affero GNU General +// Public License version 3.0 as published by the Free Software Foundation and +// appearing in the file LICENSE included in the packaging of this file. A +// commercial license can be purchased by contacting sales@baliance.com. package wml import ( @@ -126,5 +128,3 @@ func (m *CT_Shape) Validate() error { func (m *CT_Shape) ValidateWithPath(path string) error { return nil } - -//================================end diff --git a/schemas.go b/schemas.go index 62227462fa..28b100fb4a 100644 --- a/schemas.go +++ b/schemas.go @@ -61,13 +61,7 @@ const ( CorePropertiesType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" CustomPropertiesType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" CustomXMLType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml" - - //name: zhexiao(肖哲) - //date: 2019-10-10 - //新增文档初始化解析对OleObject的处理 - //================================start - OleObjectType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject" - //================================end + OleObjectType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject" // SML WorksheetType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" From 28f06104100c242bd8b2a539fb222123e0b8d373 Mon Sep 17 00:00:00 2001 From: zhexiao Date: Fri, 20 Dec 2019 16:01:56 +0800 Subject: [PATCH 6/7] update code by follow the developer ruler --- document/document.go | 2 -- schema/soo/dml/CT_PositiveSize2D.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/document/document.go b/document/document.go index a01fd70b02..0656afcb93 100644 --- a/document/document.go +++ b/document/document.go @@ -903,8 +903,6 @@ func (d *Document) onNewRelationship(decMap *zippkg.DecodeMap, target, typ strin continue } - //fmt.Printf("%#v \n", rel) - if f.Name == target { path, err := zippkg.ExtractToDiskTmp(f, d.TmpPath) if err != nil { diff --git a/schema/soo/dml/CT_PositiveSize2D.go b/schema/soo/dml/CT_PositiveSize2D.go index bd81581444..ce1d52078a 100644 --- a/schema/soo/dml/CT_PositiveSize2D.go +++ b/schema/soo/dml/CT_PositiveSize2D.go @@ -110,7 +110,7 @@ const ( pi = 152400 ) -//这里只计算返回的单位为PT的大小 +//return width and height by using pt func (m *CT_PositiveSize2D) Size() EmuSize { return EmuSize{ Width: fmt.Sprintf("%spt", strconv.FormatInt(m.CxAttr/pt, 10)), From 01c74eedc78e77ea5f6a6aa11272a514ed089998 Mon Sep 17 00:00:00 2001 From: zhexiao Date: Fri, 20 Dec 2019 16:59:10 +0800 Subject: [PATCH 7/7] add oleObejct test example --- _examples/document/oleobject/main.go | 79 ++++++++++++++++++++ _examples/document/oleobject/oleobject.docx | Bin 0 -> 21491 bytes 2 files changed, 79 insertions(+) create mode 100644 _examples/document/oleobject/main.go create mode 100644 _examples/document/oleobject/oleobject.docx diff --git a/_examples/document/oleobject/main.go b/_examples/document/oleobject/main.go new file mode 100644 index 0000000000..ba04275106 --- /dev/null +++ b/_examples/document/oleobject/main.go @@ -0,0 +1,79 @@ +// oleObject using demo + +// $ cd unioffice/_examples/document/oleobject +// $ go run main.go + +// print: +//=============oleObject info=========== +//Data path = C:\Users\ADMINI~1\AppData\Local\Temp\gooxml-docx131058703\zz524008729 +//Wmf path = C:\Users\ADMINI~1\AppData\Local\Temp\gooxml-docx131058703\zz731151394 +//===============end============= +//=============oleObject info=========== +//Data path = C:\Users\ADMINI~1\AppData\Local\Temp\gooxml-docx131058703\zz304145587 +//Wmf path = C:\Users\ADMINI~1\AppData\Local\Temp\gooxml-docx131058703\zz059105188 +//===============end============= +//=============oleObject info=========== +//Data path = C:\Users\ADMINI~1\AppData\Local\Temp\gooxml-docx131058703\zz462161757 +//Wmf path = C:\Users\ADMINI~1\AppData\Local\Temp\gooxml-docx131058703\zz101524598 +//===============end============= +// + +// Comments: +// I developed a project can do convert the oleObject file to LaTeX data +// project path: https://github.com/zhexiao/mtef-go + +// code Demo: +//import "github.com/zhexiao/mtef-go/eqn" +//latexData := eqn.Convert(path) + +package main + +import ( + "fmt" + "github.com/unidoc/unioffice/document" + "log" +) + +func main() { + filepath := "oleobject.docx" + + doc, err := document.Open(filepath) + if err != nil { + log.Panicf("Open file failed, err=%s", err) + } + + //save oleObject data rid/filepath relationship + oleObjectDataMap := make(map[string]string) + for _, oleData := range doc.OleObjectPaths { + path := oleData.Path() + rid := oleData.Rid() + + oleObjectDataMap[rid] = path + } + + //save oleObject wmf rid/filepath relationship + oleObjectWmfMap := make(map[string]string) + for _, oleData := range doc.OleObjectWmfPath { + path := oleData.Path() + rid := oleData.Rid() + + oleObjectWmfMap[rid] = path + } + + // read the oleObject file information from the word data + for _, paragraph := range doc.Paragraphs() { + for _, run := range paragraph.Runs() { + if run.OleObjects() != nil { + for _, ole := range run.OleObjects() { + dataRid := ole.OleRid() + wmfRid := ole.ImagedataRid() + + fmt.Println("=============oleObject info===========") + fmt.Printf("Data path = %s \n", oleObjectDataMap[dataRid]) + fmt.Printf("Wmf path = %s \n", oleObjectWmfMap[wmfRid]) + fmt.Println("===============end=============") + } + } + } + } +} diff --git a/_examples/document/oleobject/oleobject.docx b/_examples/document/oleobject/oleobject.docx new file mode 100644 index 0000000000000000000000000000000000000000..ed8e4ba40ebba723b66b21237b301ab9fcc3231d GIT binary patch literal 21491 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