@@ -16,6 +16,8 @@ import (
16
16
"encoding/xml"
17
17
"io"
18
18
"reflect"
19
+ "strconv"
20
+ "time"
19
21
)
20
22
21
23
// SetAppProps provides a function to set document application properties. The
@@ -206,6 +208,123 @@ func (f *File) SetDocProps(docProperties *DocProperties) error {
206
208
return err
207
209
}
208
210
211
+ // SetDocCustomProps provides a function to set excel custom properties, support string, bool, float64,
212
+ // time.DateTime four types.
213
+ func (f * File ) SetDocCustomProps (name string , value interface {}) error {
214
+ customProps := new (xlsxCustomProperties )
215
+
216
+ // find existing custom properties
217
+ if err := f .xmlNewDecoder (bytes .NewReader (namespaceStrictToTransitional (f .readXML (defaultXMLPathDocPropsCustom )))).
218
+ Decode (customProps ); err != nil && err != io .EOF {
219
+ return err
220
+ }
221
+
222
+ props := customProps .Props
223
+ existingPropertyMap := make (map [string ]xlsxCustomProperty )
224
+ maxPID := 1 // pid from 2
225
+ for _ , prop := range props {
226
+ pid , err := strconv .Atoi (prop .PID )
227
+ if err == nil && pid > maxPID {
228
+ maxPID = pid
229
+ }
230
+
231
+ existingPropertyMap [prop .Name ] = prop
232
+ }
233
+
234
+ // different custom property value type setter function
235
+ var setValueFunc func (* xlsxCustomProperty ) error
236
+ switch v := value .(type ) {
237
+ case float64 :
238
+ setValueFunc = func (property * xlsxCustomProperty ) error {
239
+ property .Number = & NumberValue {Number : v }
240
+ return nil
241
+ }
242
+ case bool :
243
+ setValueFunc = func (property * xlsxCustomProperty ) error {
244
+ property .Bool = & BoolValue {Bool : v }
245
+ return nil
246
+ }
247
+ case string :
248
+ setValueFunc = func (property * xlsxCustomProperty ) error {
249
+ property .Text = & TextValue {Text : v }
250
+ return nil
251
+ }
252
+ case time.Time :
253
+ setValueFunc = func (property * xlsxCustomProperty ) error {
254
+ property .DateTime = & FileTimeValue {
255
+ DateTime : v .Format (time .RFC3339 ),
256
+ }
257
+ return nil
258
+ }
259
+ default :
260
+ setValueFunc = func (_ * xlsxCustomProperty ) error {
261
+ return ErrUnsupportedCustomPropertyDataType
262
+ }
263
+ }
264
+
265
+ // update existing custom properties
266
+ if existingProperty , ok := existingPropertyMap [name ]; ok {
267
+ if err := setValueFunc (& existingProperty ); err != nil {
268
+ return err
269
+ }
270
+ } else {
271
+ // add new custom property
272
+ newProperty := xlsxCustomProperty {
273
+ FmtID : CustomPropertiesFMTID ,
274
+ PID : strconv .FormatInt (int64 (maxPID + 1 ), 10 ), // max pid plus 1 to create a new unique pid
275
+ Name : name ,
276
+ }
277
+
278
+ if err := setValueFunc (& newProperty ); err != nil {
279
+ return err
280
+ }
281
+
282
+ props = append (props , newProperty )
283
+ }
284
+
285
+ newCustomProps := & xlsxCustomProperties {
286
+ Vt : NameSpaceDocumentPropertiesVariantTypes .Value ,
287
+ Props : props ,
288
+ }
289
+
290
+ output , err := xml .Marshal (newCustomProps )
291
+ f .saveFileList (defaultXMLPathDocPropsCustom , output )
292
+
293
+ // set custom properties if necessary
294
+ _ = f .addRels (defaultXMLRels , SourceRelationshipCustomProperties , defaultXMLPathDocPropsCustom , "" )
295
+
296
+ // set content type if necessary
297
+ _ = f .setContentTypes ("/" + defaultXMLPathDocPropsCustom , ContentTypeCustomProperties )
298
+
299
+ return err
300
+ }
301
+
302
+ // GetDocCustomProps provides a function to get document custom properties, supported string, bool, float64,
303
+ // time.DateTime four types. If the custom property is not set, it will return an empty map, and if the custom property
304
+ // value is invalid format, it returns as if the custom property does not exist.
305
+ func (f * File ) GetDocCustomProps () (kv map [string ]interface {}, err error ) {
306
+ custom := new (xlsxCustomProperties )
307
+
308
+ if err = f .xmlNewDecoder (bytes .NewReader (namespaceStrictToTransitional (f .readXML (defaultXMLPathDocPropsCustom )))).
309
+ Decode (custom ); err != nil && err != io .EOF {
310
+ return
311
+ }
312
+
313
+ kv = make (map [string ]interface {})
314
+ if custom == nil || len (custom .Props ) == 0 {
315
+ return kv , nil
316
+ }
317
+
318
+ for _ , prop := range custom .Props {
319
+ propertyValue := prop .getPropertyValue ()
320
+ if propertyValue != nil {
321
+ kv [prop .Name ] = propertyValue
322
+ }
323
+ }
324
+
325
+ return kv , nil
326
+ }
327
+
209
328
// GetDocProps provides a function to get document core properties.
210
329
func (f * File ) GetDocProps () (ret * DocProperties , err error ) {
211
330
core := new (decodeCoreProperties )
0 commit comments