@@ -14,6 +14,7 @@ package excelize
14
14
import (
15
15
"bytes"
16
16
"encoding/xml"
17
+ "fmt"
17
18
"image"
18
19
"io"
19
20
"os"
@@ -1018,3 +1019,155 @@ func (f *File) getDispImages(sheet, cell string) ([]Picture, error) {
1018
1019
}
1019
1020
return pics , err
1020
1021
}
1022
+
1023
+ // EmbeddedObjectOptions defines the format set of embedded object.
1024
+ type EmbeddedObjectOptions struct {
1025
+ AltText string
1026
+ PrintObject * bool
1027
+ Locked * bool
1028
+ ObjectType string // Default is "Package"
1029
+ }
1030
+
1031
+ // AddEmbeddedObject provides a method to embed a file as an object in a cell.
1032
+ // The embedded object will be accessible through Excel's EMBED formula.
1033
+ // Supported object types include "Package" for general files. For example:
1034
+ //
1035
+ // package main
1036
+ //
1037
+ // import (
1038
+ // "fmt"
1039
+ // "os"
1040
+ //
1041
+ // "github.com/xuri/excelize/v2"
1042
+ // )
1043
+ //
1044
+ // func main() {
1045
+ // f := excelize.NewFile()
1046
+ // defer func() {
1047
+ // if err := f.Close(); err != nil {
1048
+ // fmt.Println(err)
1049
+ // }
1050
+ // }()
1051
+ //
1052
+ // // Read file to embed
1053
+ // file, err := os.ReadFile("document.pdf")
1054
+ // if err != nil {
1055
+ // fmt.Println(err)
1056
+ // return
1057
+ // }
1058
+ //
1059
+ // // Add embedded object
1060
+ // if err := f.AddEmbeddedObject("Sheet1", "A1", "document.pdf", file,
1061
+ // &excelize.EmbeddedObjectOptions{
1062
+ // ObjectType: "Package",
1063
+ // AltText: "Embedded PDF Document",
1064
+ // }); err != nil {
1065
+ // fmt.Println(err)
1066
+ // return
1067
+ // }
1068
+ //
1069
+ // if err := f.SaveAs("Book1.xlsx"); err != nil {
1070
+ // fmt.Println(err)
1071
+ // }
1072
+ // }
1073
+ func (f * File ) AddEmbeddedObject (sheet , cell , filename string , file []byte , opts * EmbeddedObjectOptions ) error {
1074
+ if opts == nil {
1075
+ opts = & EmbeddedObjectOptions {
1076
+ ObjectType : "Package" ,
1077
+ PrintObject : boolPtr (true ),
1078
+ Locked : boolPtr (true ),
1079
+ }
1080
+ }
1081
+ if opts .ObjectType == "" {
1082
+ opts .ObjectType = "Package"
1083
+ }
1084
+ if opts .PrintObject == nil {
1085
+ opts .PrintObject = boolPtr (true )
1086
+ }
1087
+ if opts .Locked == nil {
1088
+ opts .Locked = boolPtr (true )
1089
+ }
1090
+
1091
+ // Add embedded object to package
1092
+ objPath := f .addEmbeddedObject (file , filename )
1093
+
1094
+ // Add relationships
1095
+ sheetXMLPath , _ := f .getSheetXMLPath (sheet )
1096
+ sheetRels := "xl/worksheets/_rels/" + strings .TrimPrefix (sheetXMLPath , "xl/worksheets/" ) + ".rels"
1097
+ rID := f .addRels (sheetRels , SourceRelationshipOLEObject , "../" + objPath , "" )
1098
+
1099
+ // Set EMBED formula in cell
1100
+ formula := fmt .Sprintf ("EMBED(\" %s\" ,\" \" )" , opts .ObjectType )
1101
+ if err := f .SetCellFormula (sheet , cell , formula ); err != nil {
1102
+ return err
1103
+ }
1104
+
1105
+ // Add OLE object to worksheet
1106
+ ws , err := f .workSheetReader (sheet )
1107
+ if err != nil {
1108
+ return err
1109
+ }
1110
+
1111
+ if ws .OleObjects == nil {
1112
+ ws .OleObjects = & xlsxInnerXML {}
1113
+ }
1114
+
1115
+ // Create OLE object XML content
1116
+ oleObjectXML := fmt .Sprintf (`<oleObject progId="Package" dvAspect="DVASPECT_ICON" link="false" oleUpdate="OLEUPDATE_ONCALL" autoLoad="false" shapeId="1025" r:id="rId%d"/>` , rID )
1117
+ if ws .OleObjects .Content == "" {
1118
+ ws .OleObjects .Content = oleObjectXML
1119
+ } else {
1120
+ ws .OleObjects .Content += oleObjectXML
1121
+ }
1122
+
1123
+ // Add content type for embedded object
1124
+ return f .addContentTypePartEmbeddedObject ()
1125
+ }
1126
+
1127
+ // addEmbeddedObject adds embedded object file to the package and returns the path.
1128
+ func (f * File ) addEmbeddedObject (file []byte , filename string ) string {
1129
+ count := f .countEmbeddedObjects ()
1130
+ objPath := "embeddings/oleObject" + strconv .Itoa (count + 1 ) + ".bin"
1131
+ f .Pkg .Store ("xl/" + objPath , file )
1132
+ return objPath
1133
+ }
1134
+
1135
+ // countEmbeddedObjects counts the number of embedded objects in the package.
1136
+ func (f * File ) countEmbeddedObjects () int {
1137
+ count := 0
1138
+ f .Pkg .Range (func (k , v interface {}) bool {
1139
+ if strings .Contains (k .(string ), "xl/embeddings/oleObject" ) {
1140
+ count ++
1141
+ }
1142
+ return true
1143
+ })
1144
+ return count
1145
+ }
1146
+
1147
+ // addContentTypePartEmbeddedObject adds content type for embedded objects.
1148
+ func (f * File ) addContentTypePartEmbeddedObject () error {
1149
+ content , err := f .contentTypesReader ()
1150
+ if err != nil {
1151
+ return err
1152
+ }
1153
+ content .mu .Lock ()
1154
+ defer content .mu .Unlock ()
1155
+
1156
+ // Check if bin extension already exists
1157
+ var binExists bool
1158
+ for _ , v := range content .Defaults {
1159
+ if v .Extension == "bin" {
1160
+ binExists = true
1161
+ break
1162
+ }
1163
+ }
1164
+
1165
+ if ! binExists {
1166
+ content .Defaults = append (content .Defaults , xlsxDefault {
1167
+ Extension : "bin" ,
1168
+ ContentType : ContentTypeOLEObject ,
1169
+ })
1170
+ }
1171
+
1172
+ return nil
1173
+ }
0 commit comments