Skip to content

Commit dc21761

Browse files
committed
Add 4 new functions: add_picture_from_bytes, add_vba_project, set_col_outline_level and set_defined_name
- Update unit tests and docs for the function - Upgrade the dependencies package version - Made unit test save workbook in the test folder - The cValueToGo and py_value_to_c functions support convert byte array data type (Go: []byte and Python: bytes)
1 parent eeafd90 commit dc21761

10 files changed

+299
-33
lines changed

.gitignore

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ __pycache__
44
*.dll
55
*.dylib
66
*.so
7-
*.xlsx
8-
*.xml
97
dist/
8+
test/
109
excelize.egg-info/
1110
libexcelize.*.h
12-
Makefile
11+
Makefile

excelize.py

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ def py_value_to_c(py_instance, ctypes_instance):
313313
py_value_to_c(getattr(py_instance, py_field_name), c_type()),
314314
)
315315
else:
316-
if get_origin(py_field_args[0]) is not list:
316+
arg_type = py_field_args[0]
317+
if get_origin(arg_type) is not list and arg_type is not bytes:
317318
# Pointer of the Go data type, for example: *excelize.Options or *string
318319
value = getattr(py_instance, py_field_name)
319320
c_type = get_c_field_type(ctypes_instance, c_field_name)._type_
@@ -335,7 +336,15 @@ def py_value_to_c(py_instance, ctypes_instance):
335336
else:
336337
# The Go data type array, for example:
337338
# []*excelize.Options, []excelize.Options, []string, []*string
338-
py_field_type = get_args(py_field_args[0])[0]
339+
if arg_type is bytes: # []byte
340+
c_type = get_c_field_type(ctypes_instance, c_field_name)._type_
341+
value = getattr(py_instance, py_field_name)
342+
ctypes_instance.__setattr__(
343+
c_field_name, cast(value, POINTER(c_ubyte))
344+
)
345+
ctypes_instance.__setattr__(c_field_name + "Len", c_int(len(value)))
346+
continue
347+
py_field_type = get_args(arg_type)[0]
339348
if type(None) not in get_args(py_field_type):
340349
# The Go data type array, for example: []excelize.Options or []string
341350
c_type = get_c_field_type(ctypes_instance, c_field_name)._type_
@@ -616,6 +625,36 @@ def add_picture(
616625
).decode(ENCODE)
617626
return None if err == "" else Exception(err)
618627

628+
def add_picture_from_bytes(
629+
self, sheet: str, cell: str, picture: Picture
630+
) -> Optional[Exception]:
631+
"""
632+
Add picture in a sheet by given picture format set (such as offset,
633+
scale, aspect ratio setting and print settings), file base name,
634+
extension name and file bytes, supported image types: EMF, EMZ, GIF,
635+
JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ. Note that this function
636+
only supports adding pictures placed over the cells currently, and
637+
doesn't support adding pictures placed in cells or creating the Kingsoft
638+
WPS Office embedded image cells
639+
640+
Args:
641+
sheet (str): The worksheet name
642+
extension (str): The image extension
643+
picture (Picture): The picture options
644+
645+
Returns:
646+
Optional[Exception]: Returns None if no error occurred,
647+
otherwise returns an Exception with the message.
648+
"""
649+
lib.AddPictureFromBytes.restype = c_char_p
650+
err = lib.AddPictureFromBytes(
651+
self.file_index,
652+
sheet.encode(ENCODE),
653+
cell.encode(ENCODE),
654+
byref(py_value_to_c(picture, types_go._Picture())),
655+
).decode(ENCODE)
656+
return None if err == "" else Exception(err)
657+
619658
def add_pivot_table(self, opts: Optional[PivotTableOptions]) -> Optional[Exception]:
620659
"""
621660
Add pivot table by given pivot table options. Note that the same fields
@@ -880,6 +919,26 @@ def add_table(self, sheet: str, table: Table) -> Optional[Exception]:
880919
).decode(ENCODE)
881920
return None if err == "" else Exception(err)
882921

922+
def add_vba_project(self, file: bytes) -> Optional[Exception]:
923+
"""
924+
Add vbaProject.bin file which contains functions and/or macros. The file
925+
extension should be XLSM or XLTM.
926+
927+
Args:
928+
file (bytes): The contents buffer of the file
929+
930+
Returns:
931+
Optional[Exception]: Returns None if no error occurred,
932+
otherwise returns an Exception with the message.
933+
"""
934+
lib.AddVBAProject.restype = c_char_p
935+
err = lib.AddVBAProject(
936+
self.file_index,
937+
cast(file, POINTER(c_ubyte)),
938+
len(file),
939+
).decode(ENCODE)
940+
return None if err == "" else Exception(err)
941+
883942
def auto_filter(
884943
self,
885944
sheet: str,
@@ -1572,6 +1631,64 @@ def set_cell_value(
15721631
).decode(ENCODE)
15731632
return None if err == "" else Exception(err)
15741633

1634+
def set_col_outline_level(
1635+
self, sheet: str, col: str, level: int
1636+
) -> Optional[Exception]:
1637+
"""
1638+
Set outline level of a single column by given worksheet name and column
1639+
name.
1640+
1641+
Args:
1642+
sheet (str): The worksheet name
1643+
col (str): The column name
1644+
level (int): The out level, acceptable value from 1 to 7
1645+
1646+
Returns:
1647+
Optional[Exception]: Returns None if no error occurred,
1648+
otherwise returns an Exception with the message.
1649+
1650+
Example:
1651+
For example, set outline level of column D in Sheet1 to 2:
1652+
1653+
.. code-block:: python
1654+
1655+
err = f.set_col_outline_level("Sheet1", "D", 2)
1656+
"""
1657+
lib.SetColOutlineLevel.restype = c_char_p
1658+
err = lib.SetColOutlineLevel(
1659+
self.file_index, sheet.encode(ENCODE), col.encode(ENCODE), level
1660+
).decode(ENCODE)
1661+
return None if err == "" else Exception(err)
1662+
1663+
def set_defined_name(self, defined_name: DefinedName) -> Optional[Exception]:
1664+
"""
1665+
Set the defined names of the workbook or worksheet. If not specified
1666+
scope, the default scope is workbook.
1667+
1668+
Args:
1669+
defined_name (DefinedName): The defined name options
1670+
1671+
Returns:
1672+
Optional[Exception]: Returns None if no error occurred,
1673+
otherwise returns an Exception with the message.
1674+
1675+
Example:
1676+
For example, create a table of A1:D5 on Sheet1:
1677+
1678+
.. code-block:: python
1679+
1680+
err = f.set_defined_name(excelize.DefinedName(
1681+
name="Amount",
1682+
refers_to="Sheet1!$A$2:$D$5",
1683+
comment="defined name comment",
1684+
scope="Sheet2",
1685+
))
1686+
"""
1687+
lib.SetDefinedName.restype = c_char_p
1688+
options = py_value_to_c(defined_name, types_go._DefinedName())
1689+
err = lib.SetDefinedName(self.file_index, byref(options)).decode(ENCODE)
1690+
return None if err == "" else Exception(err)
1691+
15751692
def set_sheet_background(self, sheet: str, picture: str) -> Optional[Exception]:
15761693
"""
15771694
Set background picture by given worksheet name and file path. Supported

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/xuri/excelize-py
33
go 1.20
44

55
require (
6-
github.com/xuri/excelize/v2 v2.9.1-0.20241208033954-3ca60f8d2382
6+
github.com/xuri/excelize/v2 v2.9.1-0.20241221071117-9934bf5c8634
77
golang.org/x/image v0.23.0
88
)
99

@@ -14,6 +14,6 @@ require (
1414
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 // indirect
1515
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
1616
golang.org/x/crypto v0.31.0 // indirect
17-
golang.org/x/net v0.32.0 // indirect
17+
golang.org/x/net v0.33.0 // indirect
1818
golang.org/x/text v0.21.0 // indirect
1919
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ github.com/tiendc/go-deepcopy v1.2.0 h1:6vCCs+qdLQHzFqY1fcPirsAWOmrLbuccilfp8UzD
1010
github.com/tiendc/go-deepcopy v1.2.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
1111
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 h1:8m6DWBG+dlFNbx5ynvrE7NgI+Y7OlZVMVTpayoW+rCc=
1212
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
13-
github.com/xuri/excelize/v2 v2.9.1-0.20241208033954-3ca60f8d2382 h1:51czxsTwHg9JHFIiKuookS9dUtLUeCL7xoXTqF6BI6A=
14-
github.com/xuri/excelize/v2 v2.9.1-0.20241208033954-3ca60f8d2382/go.mod h1:wFKkKpd4PMjXhJVhlEnMxKAxFlT+wMhayUoEMp1LR1g=
13+
github.com/xuri/excelize/v2 v2.9.1-0.20241221071117-9934bf5c8634 h1:GDmRYes16eUdzi5a4Iv2nqSn3jZUch8DNttR/qNe1NU=
14+
github.com/xuri/excelize/v2 v2.9.1-0.20241221071117-9934bf5c8634/go.mod h1:NBRx6e5FHFx4mHLiYG1QBONNvNNSs/wrtzS+h56/A6k=
1515
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
1616
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
1717
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
1818
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
1919
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
2020
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
21-
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
22-
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
21+
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
22+
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
2323
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
2424
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
2525
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

main.go

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,13 @@ func cValueToGo(cVal reflect.Value, goType reflect.Type) (reflect.Value, error)
269269
// The Go data type array, for example: []excelize.Options or []string
270270
subEle := ele
271271
cArrayLen := int(cVal.FieldByName(field.Name + "Len").Int())
272+
if subEle.Kind() == reflect.Uint8 { // []byte
273+
buf := C.GoBytes(unsafe.Pointer(cArray.Interface().(*C.uchar)), C.int(cArrayLen))
274+
s.Field(resultFieldIdx).Set(reflect.ValueOf(buf))
275+
continue
276+
}
272277
cArray = cToGoArray(cArray, cArrayLen)
273278
for i := 0; i < cArray.Len(); i++ {
274-
if subEle.Kind() == reflect.Uint8 { // []byte
275-
break
276-
}
277279
if goBaseTypes[subEle.Kind()] {
278280
// The Go basic data type array, for example: []string
279281
v, err := cToGoBaseType(cArray.Index(i), subEle.Kind())
@@ -558,6 +560,31 @@ func AddPicture(idx int, sheet, cell, name *C.char, opts *C.struct_GraphicOption
558560
return C.CString(errNil)
559561
}
560562

563+
// AddPictureFromBytes provides the method to add picture in a sheet by given
564+
// picture format set (such as offset, scale, aspect ratio setting and print
565+
// settings), file base name, extension name and file bytes, supported image
566+
// types: EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ. Note that
567+
// this function only supports adding pictures placed over the cells currently,
568+
// and doesn't support adding pictures placed in cells or creating the Kingsoft
569+
// WPS Office embedded image cells
570+
//
571+
//export AddPictureFromBytes
572+
func AddPictureFromBytes(idx int, sheet, cell *C.char, pic *C.struct_Picture) *C.char {
573+
f, ok := files.Load(idx)
574+
if !ok {
575+
return C.CString(errFilePtr)
576+
}
577+
goVal, err := cValueToGo(reflect.ValueOf(*pic), reflect.TypeOf(excelize.Picture{}))
578+
if err != nil {
579+
return C.CString(err.Error())
580+
}
581+
options := goVal.Elem().Interface().(excelize.Picture)
582+
if err := f.(*excelize.File).AddPictureFromBytes(C.GoString(sheet), C.GoString(cell), &options); err != nil {
583+
C.CString(err.Error())
584+
}
585+
return C.CString(errNil)
586+
}
587+
561588
// AddPivotTable provides the method to add pivot table by given pivot table
562589
// options. Note that the same fields can not in Columns, Rows and Filter
563590
// fields at the same time.
@@ -667,6 +694,22 @@ func AddTable(idx int, sheet *C.char, table *C.struct_Table) *C.char {
667694
return C.CString(errNil)
668695
}
669696

697+
// AddVBAProject provides the method to add vbaProject.bin file which contains
698+
// functions and/or macros. The file extension should be XLSM or XLTM.
699+
//
700+
//export AddVBAProject
701+
func AddVBAProject(idx int, file *C.uchar, fileLen C.int) *C.char {
702+
f, ok := files.Load(idx)
703+
if !ok {
704+
return C.CString(errFilePtr)
705+
}
706+
buf := C.GoBytes(unsafe.Pointer(file), fileLen)
707+
if err := f.(*excelize.File).AddVBAProject(buf); err != nil {
708+
C.CString(err.Error())
709+
}
710+
return C.CString(errNil)
711+
}
712+
670713
// AutoFilter provides the method to add auto filter in a worksheet by given
671714
// worksheet name, range reference and settings. An auto filter in Excel is a
672715
// way of filtering a 2D range of data based on some simple criteria.
@@ -1385,6 +1428,43 @@ func SetCellValue(idx int, sheet, cell *C.char, value *C.struct_Interface) *C.ch
13851428
return C.CString(errNil)
13861429
}
13871430

1431+
// SetColOutlineLevel provides a function to set outline level of a single
1432+
// column by given worksheet name and column name. The value of parameter
1433+
// 'level' is 1-7.
1434+
//
1435+
//export SetColOutlineLevel
1436+
func SetColOutlineLevel(idx int, sheet, col *C.char, level int) *C.char {
1437+
f, ok := files.Load(idx)
1438+
if !ok {
1439+
return C.CString(errFilePtr)
1440+
}
1441+
if err := f.(*excelize.File).SetColOutlineLevel(C.GoString(sheet), C.GoString(col), uint8(level)); err != nil {
1442+
return C.CString(err.Error())
1443+
}
1444+
return C.CString(errNil)
1445+
}
1446+
1447+
// SetDefinedName provides a function to set the defined names of the workbook
1448+
// or worksheet. If not specified scope, the default scope is workbook.
1449+
//
1450+
//export SetDefinedName
1451+
func SetDefinedName(idx int, definedName *C.struct_DefinedName) *C.char {
1452+
var df excelize.DefinedName
1453+
goVal, err := cValueToGo(reflect.ValueOf(*definedName), reflect.TypeOf(excelize.DefinedName{}))
1454+
if err != nil {
1455+
return C.CString(err.Error())
1456+
}
1457+
df = goVal.Elem().Interface().(excelize.DefinedName)
1458+
f, ok := files.Load(idx)
1459+
if !ok {
1460+
return C.CString(errFilePtr)
1461+
}
1462+
if err := f.(*excelize.File).SetDefinedName(&df); err != nil {
1463+
return C.CString(err.Error())
1464+
}
1465+
return C.CString(errNil)
1466+
}
1467+
13881468
// SetSheetBackground provides a function to set background picture by given
13891469
// worksheet name and file path. Supported image types: BMP, EMF, EMZ, GIF,
13901470
// JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.

test/vbaProject.bin

16 KB
Binary file not shown.

0 commit comments

Comments
 (0)