55 "encoding/xml"
66 "fmt"
77 "io"
8+ "strconv"
89 "strings"
910 "time"
1011
@@ -19,12 +20,14 @@ type File struct {
1920 metadata * oxml.DocumentMeta
2021 docStyles * oxml.DocumentStyles
2122 rawFiles map [string ][]byte
23+ images map [string ][]byte
2224 path string
2325 closed bool
2426 autoRecalc bool
2527 contentStyles map [string ]oxml.Style
2628 namedRanges []namedRange
2729 autoFilters []autoFilter
30+ nextImageID int
2831}
2932
3033type sheet struct {
@@ -69,6 +72,18 @@ type cell struct {
6972 comment * Comment
7073 styleName string
7174 validationName string
75+ images []* imageFrame
76+ }
77+
78+ type imageFrame struct {
79+ name string
80+ href string
81+ width float64
82+ height float64
83+ offsetX float64
84+ offsetY float64
85+ format string
86+ data []byte
7287}
7388
7489type mergeRange struct {
@@ -81,6 +96,7 @@ func NewFile() *File {
8196 sheets : make ([]* sheet , 0 ),
8297 styles : newStyleManager (),
8398 rawFiles : make (map [string ][]byte ),
99+ images : make (map [string ][]byte ),
84100 metadata : & oxml.DocumentMeta {
85101 Meta : oxml.Meta {
86102 Generator : "goods" ,
@@ -136,6 +152,7 @@ func parseZipResult(result *ozip.ReadResult) (*File, error) {
136152 sheets : make ([]* sheet , 0 ),
137153 styles : newStyleManager (),
138154 rawFiles : make (map [string ][]byte ),
155+ images : make (map [string ][]byte ),
139156 metadata : & oxml.DocumentMeta {
140157 Meta : oxml.Meta {Generator : "goods" },
141158 },
@@ -146,7 +163,11 @@ func parseZipResult(result *ozip.ReadResult) (*File, error) {
146163 switch name {
147164 case "mimetype" , "content.xml" , "styles.xml" , "meta.xml" , "META-INF/manifest.xml" , "settings.xml" :
148165 default :
149- f .rawFiles [name ] = data
166+ if strings .HasPrefix (name , "Pictures/" ) {
167+ f .images [name ] = data
168+ } else {
169+ f .rawFiles [name ] = data
170+ }
150171 }
151172 }
152173
@@ -201,9 +222,26 @@ type xmlTableCell struct {
201222 NumberColumnsSpanned int `xml:"number-columns-spanned,attr"`
202223 NumberRowsSpanned int `xml:"number-rows-spanned,attr"`
203224 Annotations []xmlAnnotation `xml:"annotation"`
225+ Frames []xmlDrawFrame `xml:"frame"`
204226 Paragraphs []xmlParagraph `xml:"p"`
205227}
206228
229+ type xmlDrawFrame struct {
230+ XMLName xml.Name `xml:"frame"`
231+ Name string `xml:"name,attr"`
232+ Width string `xml:"width,attr"`
233+ Height string `xml:"height,attr"`
234+ X string `xml:"x,attr"`
235+ Y string `xml:"y,attr"`
236+ EndCellAddress string `xml:"end-cell-address,attr"`
237+ Image * xmlDrawImage `xml:"image"`
238+ }
239+
240+ type xmlDrawImage struct {
241+ XMLName xml.Name `xml:"image"`
242+ Href string `xml:"http://www.w3.org/1999/xlink href,attr"`
243+ }
244+
207245type xmlTableRow struct {
208246 XMLName xml.Name `xml:"table-row"`
209247 StyleName string `xml:"style-name,attr"`
@@ -597,6 +635,7 @@ func parseXMLRowCells(s *sheet, rowIdx int, xmlCells []xmlTableCell) bool {
597635 comment : c .comment ,
598636 styleName : c .styleName ,
599637 validationName : c .validationName ,
638+ images : c .images ,
600639 }
601640 r .cells [colIdx + rep ] = newCell
602641 if colIdx + rep > s .maxCol {
@@ -679,8 +718,9 @@ func convertXMLCell(xc *xmlTableCell) *cell {
679718 hasAnnotation := len (xc .Annotations ) > 0
680719 hasValidation := xc .ContentValidationName != ""
681720 hasStyle := xc .StyleName != ""
721+ hasFrame := len (xc .Frames ) > 0
682722
683- if xc .ValueType == "" && len (xc .Paragraphs ) == 0 && xc .Formula == "" && ! hasAnnotation && ! hasValidation && ! hasStyle {
723+ if xc .ValueType == "" && len (xc .Paragraphs ) == 0 && xc .Formula == "" && ! hasAnnotation && ! hasValidation && ! hasStyle && ! hasFrame {
684724 return nil
685725 }
686726
@@ -705,6 +745,25 @@ func convertXMLCell(xc *xmlTableCell) *cell {
705745 }
706746 }
707747
748+ for _ , fr := range xc .Frames {
749+ if fr .Image == nil || fr .Image .Href == "" {
750+ continue
751+ }
752+ format := ""
753+ if i := strings .LastIndex (fr .Image .Href , "." ); i >= 0 {
754+ format = fr .Image .Href [i + 1 :]
755+ }
756+ c .images = append (c .images , & imageFrame {
757+ name : fr .Name ,
758+ href : fr .Image .Href ,
759+ width : parseCmValue (fr .Width ),
760+ height : parseCmValue (fr .Height ),
761+ offsetX : parseCmValue (fr .X ),
762+ offsetY : parseCmValue (fr .Y ),
763+ format : format ,
764+ })
765+ }
766+
708767 parseXMLCellValue (xc , c )
709768
710769 if xc .Formula != "" {
@@ -715,6 +774,19 @@ func convertXMLCell(xc *xmlTableCell) *cell {
715774 return c
716775}
717776
777+ func parseCmValue (s string ) float64 {
778+ if s == "" {
779+ return 0
780+ }
781+ s = strings .TrimSuffix (s , "cm" )
782+ s = strings .TrimSpace (s )
783+ f , err := strconv .ParseFloat (s , 64 )
784+ if err != nil {
785+ return 0
786+ }
787+ return f
788+ }
789+
718790func parseXMLCellValue (xc * xmlTableCell , c * cell ) {
719791 switch xc .ValueType {
720792 case "float" , "currency" , "percentage" :
@@ -1309,6 +1381,24 @@ func buildXMLCell(c *cell, sm *styleManager, autoStyles *[]oxml.Style) oxml.Tabl
13091381 }
13101382 }
13111383
1384+ for _ , img := range c .images {
1385+ frame := oxml.DrawFrame {
1386+ Name : img .name ,
1387+ ZIndex : "0" ,
1388+ Width : fmt .Sprintf ("%.4fcm" , img .width ),
1389+ Height : fmt .Sprintf ("%.4fcm" , img .height ),
1390+ X : fmt .Sprintf ("%.4fcm" , img .offsetX ),
1391+ Y : fmt .Sprintf ("%.4fcm" , img .offsetY ),
1392+ Image : & oxml.DrawImage {
1393+ Href : img .href ,
1394+ Type : "simple" ,
1395+ Show : "embed" ,
1396+ Actuate : "onLoad" ,
1397+ },
1398+ }
1399+ xmlCell .Frames = append (xmlCell .Frames , frame )
1400+ }
1401+
13121402 if c .colSpan > 1 {
13131403 xmlCell .NumberColumnsSpanned = c .colSpan
13141404 }
@@ -1426,6 +1516,16 @@ func (f *File) marshalStyles() ([]byte, error) {
14261516
14271517func (f * File ) marshalManifest () ([]byte , error ) {
14281518 m := oxml .DefaultManifest ()
1519+ for href := range f .images {
1520+ ext := ""
1521+ if i := strings .LastIndex (href , "." ); i >= 0 {
1522+ ext = href [i + 1 :]
1523+ }
1524+ m .FileEntries = append (m .FileEntries , oxml.FileEntry {
1525+ FullPath : href ,
1526+ MediaType : imageMimeType (ext ),
1527+ })
1528+ }
14291529 var buf bytes.Buffer
14301530 if err := oxml .WriteManifestXML (& buf , & m ); err != nil {
14311531 return nil , err
0 commit comments