Skip to content

Commit da46454

Browse files
authored
Add 2 new functions: get_cell_rich_text and set_doc_props (#9)
- Simplify code for the c_value_to_py function - Set zero value for C NULL pointer to void garbage memory address value - Update unit tests - Update example in README - Upgrade the dependencies package version
1 parent 42fa2b2 commit da46454

File tree

10 files changed

+326
-103
lines changed

10 files changed

+326
-103
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ chart = excelize.Chart(
129129
],
130130
title=[excelize.RichTextRun(text="Fruit 3D Clustered Column Chart")],
131131
)
132-
f.add_chart("Sheet1", "E1", chart)
132+
err = f.add_chart("Sheet1", "E1", chart)
133+
if err:
134+
print(err)
133135
# Save spreadsheet by the given path.
134136
err = f.save_as("Book1.xlsx")
135137
if err:

README_zh.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ chart = excelize.Chart(
129129
],
130130
title=[excelize.RichTextRun(text="Fruit 3D Clustered Column Chart")],
131131
)
132-
f.add_chart("Sheet1", "E1", chart)
132+
err = f.add_chart("Sheet1", "E1", chart)
133+
if err:
134+
print(err)
133135
# 根据指定路径保存文件
134136
err = f.save_as("Book1.xlsx")
135137
if err:

excelize.py

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@ def c_value_to_py(ctypes_instance, py_instance):
179179
if value:
180180
if any(is_py_primitive_type(arg) for arg in py_field_args):
181181
# Pointer of the Go basic data type, for example: *string
182-
value = getattr(ctypes_instance, c_field_name)
183182
setattr(
184183
py_instance,
185184
py_field_name,
@@ -1482,6 +1481,29 @@ def get_cell_style(self, sheet: str, cell: str) -> Tuple[int, Optional[Exception
14821481
err = res.err.decode(ENCODE)
14831482
return res.val, None if err == "" else Exception(err)
14841483

1484+
def get_cell_rich_text(
1485+
self, sheet: str, cell: str
1486+
) -> Tuple[List[RichTextRun], Optional[Exception]]:
1487+
"""
1488+
Get rich text of cell by given worksheet and cell reference.
1489+
1490+
Args:
1491+
sheet (str): The worksheet name
1492+
cell (str): The cell reference
1493+
1494+
Returns:
1495+
Tuple[List[RichTextRun], Optional[Exception]]: A tuple containing
1496+
the rich text runs and an exception if an error occurred, otherwise
1497+
None.
1498+
"""
1499+
lib.GetCellRichText.restype = types_go._GetCellRichTextResult
1500+
res = lib.GetCellRichText(
1501+
self.file_index, sheet.encode(ENCODE), cell.encode(ENCODE)
1502+
)
1503+
runs = c_value_to_py(res, GetCellRichTextResult()).runs
1504+
err = res.Err.decode(ENCODE)
1505+
return runs if runs else [], None if err == "" else Exception(err)
1506+
14851507
def get_cell_value(
14861508
self, sheet: str, cell: str, *opts: Options
14871509
) -> Tuple[str, Optional[Exception]]:
@@ -1703,7 +1725,7 @@ def get_tables(self, sheet: str) -> Tuple[List[Table], Optional[Exception]]:
17031725
res = lib.GetTables(self.file_index, sheet.encode(ENCODE))
17041726
tables = c_value_to_py(res, GetTablesResult()).tables
17051727
err = res.Err.decode(ENCODE)
1706-
return tables, None if err == "" else Exception(err)
1728+
return tables if tables else [], None if err == "" else Exception(err)
17071729

17081730
def insert_cols(self, sheet: str, col: str, n: int) -> Optional[Exception]:
17091731
"""
@@ -2119,8 +2141,8 @@ def set_active_sheet(self, index: int) -> Optional[Exception]:
21192141

21202142
def set_cell_bool(self, sheet: str, cell: str, value: bool) -> Optional[Exception]:
21212143
"""
2122-
Set bool type value of a cell by given worksheet name, cell reference and
2123-
cell value.
2144+
Set bool type value of a cell by given worksheet name, cell reference
2145+
and cell value.
21242146
21252147
Args:
21262148
sheet (str): The worksheet name
@@ -2717,6 +2739,46 @@ def set_defined_name(self, defined_name: DefinedName) -> Optional[Exception]:
27172739
err = lib.SetDefinedName(self.file_index, byref(options)).decode(ENCODE)
27182740
return None if err == "" else Exception(err)
27192741

2742+
def set_doc_props(self, doc_properties: DocProperties) -> Optional[Exception]:
2743+
"""
2744+
Set document core properties.
2745+
2746+
Args:
2747+
doc_properties (DocProperties): The doc properties
2748+
2749+
Returns:
2750+
Optional[Exception]: Returns None if no error occurred,
2751+
otherwise returns an Exception with the message.
2752+
2753+
Example:
2754+
For example:
2755+
2756+
.. code-block:: python
2757+
2758+
err = f.set_doc_props(
2759+
excelize.DocProperties(
2760+
category="category",
2761+
content_status="Draft",
2762+
created="2019-06-04T22:00:10Z",
2763+
creator="Go Excelize",
2764+
description="This file created by Go Excelize",
2765+
identifier="xlsx",
2766+
keywords="Spreadsheet",
2767+
last_modified_by="Go Author",
2768+
modified="2019-06-04T22:00:10Z",
2769+
revision="0",
2770+
subject="Test Subject",
2771+
title="Test Title",
2772+
language="en-US",
2773+
version="1.0.0",
2774+
)
2775+
)
2776+
"""
2777+
lib.SetDocProps.restype = c_char_p
2778+
options = py_value_to_c(doc_properties, types_go._DocProperties())
2779+
err = lib.SetDocProps(self.file_index, byref(options)).decode(ENCODE)
2780+
return None if err == "" else Exception(err)
2781+
27202782
def set_header_footer(
27212783
self, sheet: str, opts: HeaderFooterOptions
27222784
) -> Optional[Exception]:

go.mod

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

55
require (
6-
github.com/xuri/excelize/v2 v2.9.1-0.20250105013731-af422e17009b
6+
github.com/xuri/excelize/v2 v2.9.1-0.20250207010127-05ed940040c1
77
golang.org/x/image v0.23.0
88
)
99

1010
require (
1111
github.com/richardlehane/mscfb v1.0.4 // indirect
1212
github.com/richardlehane/msoleps v1.0.4 // indirect
13-
github.com/tiendc/go-deepcopy v1.2.0 // indirect
13+
github.com/tiendc/go-deepcopy v1.2.2 // indirect
1414
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 // indirect
15-
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
16-
golang.org/x/crypto v0.31.0 // indirect
17-
golang.org/x/net v0.33.0 // indirect
18-
golang.org/x/text v0.21.0 // indirect
15+
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 // indirect
16+
golang.org/x/crypto v0.32.0 // indirect
17+
golang.org/x/net v0.34.0 // indirect
18+
golang.org/x/text v0.22.0 // indirect
1919
)

go.sum

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,24 @@ github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK
88
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
99
github.com/tiendc/go-deepcopy v1.2.0 h1:6vCCs+qdLQHzFqY1fcPirsAWOmrLbuccilfp8UzD1Qo=
1010
github.com/tiendc/go-deepcopy v1.2.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
11+
github.com/tiendc/go-deepcopy v1.2.2 h1:UxMUNHTh2lWbnfic8asYI3YtE5e85XGZYUgl2pZKsVY=
12+
github.com/tiendc/go-deepcopy v1.2.2/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
1113
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 h1:8m6DWBG+dlFNbx5ynvrE7NgI+Y7OlZVMVTpayoW+rCc=
1214
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
13-
github.com/xuri/excelize/v2 v2.9.1-0.20250105013731-af422e17009b h1:IgzLFLfCA10Lu5Ac9XgC8wBvPAVqHe2ilgIA8nouiPc=
14-
github.com/xuri/excelize/v2 v2.9.1-0.20250105013731-af422e17009b/go.mod h1:NBRx6e5FHFx4mHLiYG1QBONNvNNSs/wrtzS+h56/A6k=
15-
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
16-
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
17-
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
18-
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
15+
github.com/xuri/excelize/v2 v2.9.1-0.20250125020127-b19e5940b84e h1:5V9lcKkqTSO66lJhwYeANB9GJkSEGosawlR5cMpvmQY=
16+
github.com/xuri/excelize/v2 v2.9.1-0.20250125020127-b19e5940b84e/go.mod h1:QPDLfWgowGnzIqBhd8g4iNoymgms2UL7Ok/Qm/NSPsg=
17+
github.com/xuri/excelize/v2 v2.9.1-0.20250207010127-05ed940040c1 h1:K+zyRkNaXhO7LeXDSG/v1QtKZvc/wa+Q944zjSDFfRs=
18+
github.com/xuri/excelize/v2 v2.9.1-0.20250207010127-05ed940040c1/go.mod h1:QPDLfWgowGnzIqBhd8g4iNoymgms2UL7Ok/Qm/NSPsg=
19+
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 h1:hOh7aVDrvGJRxzXrQbDY8E+02oaI//5cHL+97oYpEPw=
20+
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
21+
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
22+
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
1923
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
2024
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
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=
25+
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
26+
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
2327
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
2428
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
29+
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
30+
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
2531
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

main.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ func goValueToC(goVal, cVal reflect.Value) (reflect.Value, error) {
368368
ptrVal := reflect.NewAt(v.Type(), cValPtr).Elem()
369369
ptrVal.Set(v)
370370
c.FieldByName(field.Name).Set(ptrVal.Addr())
371+
} else {
372+
c.FieldByName(field.Name).Set(reflect.Zero(cField.Type))
371373
}
372374
}
373375
case reflect.Struct:
@@ -1055,6 +1057,31 @@ func GetCellHyperLink(idx int, sheet, cell *C.char) C.struct_GetCellHyperLinkRes
10551057
return C.struct_GetCellHyperLinkResult{link: C._Bool(link), target: C.CString(target), err: C.CString(emptyString)}
10561058
}
10571059

1060+
// GetCellRichText provides a function to get rich text of cell by given
1061+
// worksheet and cell reference.
1062+
//
1063+
//export GetCellRichText
1064+
func GetCellRichText(idx int, sheet, cell *C.char) C.struct_GetCellRichTextResult {
1065+
f, ok := files.Load(idx)
1066+
if !ok {
1067+
return C.struct_GetCellRichTextResult{Err: C.CString(errFilePtr)}
1068+
}
1069+
runs, err := f.(*excelize.File).GetCellRichText(C.GoString(sheet), C.GoString(cell))
1070+
if err != nil {
1071+
return C.struct_GetCellRichTextResult{Err: C.CString(err.Error())}
1072+
}
1073+
cArray := C.malloc(C.size_t(len(runs)) * C.size_t(unsafe.Sizeof(C.struct_RichTextRun{})))
1074+
cStructArray := (*[1 << 30]C.struct_RichTextRun)(cArray)[:len(runs):len(runs)]
1075+
for i, r := range runs {
1076+
cVal, err := goValueToC(reflect.ValueOf(r), reflect.ValueOf(&C.struct_RichTextRun{}))
1077+
if err != nil {
1078+
return C.struct_GetCellRichTextResult{Err: C.CString(err.Error())}
1079+
}
1080+
cStructArray[i] = cVal.Elem().Interface().(C.struct_RichTextRun)
1081+
}
1082+
return C.struct_GetCellRichTextResult{RunsLen: C.int(len(runs)), Runs: (*C.struct_RichTextRun)(cArray), Err: C.CString(emptyString)}
1083+
}
1084+
10581085
// GetCellStyle provides a function to get cell style index by given worksheet
10591086
// name and cell reference. This function is concurrency safe.
10601087
//
@@ -1897,7 +1924,7 @@ func SetCellHyperLink(idx int, sheet, cell, link, linkType *C.char, opts *C.stru
18971924
// worksheet name, cell reference and cell value.
18981925
//
18991926
//export SetCellInt
1900-
func SetCellInt(idx int, sheet, cell *C.char, value int) *C.char {
1927+
func SetCellInt(idx int, sheet, cell *C.char, value int64) *C.char {
19011928
f, ok := files.Load(idx)
19021929
if !ok {
19031930
return C.CString(errFilePtr)
@@ -2109,6 +2136,26 @@ func SetDefinedName(idx int, definedName *C.struct_DefinedName) *C.char {
21092136
return C.CString(emptyString)
21102137
}
21112138

2139+
// SetDocProps provides a function to set document core properties.
2140+
//
2141+
//export SetDocProps
2142+
func SetDocProps(idx int, docProperties *C.struct_DocProperties) *C.char {
2143+
var options excelize.DocProperties
2144+
goVal, err := cValueToGo(reflect.ValueOf(*docProperties), reflect.TypeOf(excelize.DocProperties{}))
2145+
if err != nil {
2146+
return C.CString(err.Error())
2147+
}
2148+
options = goVal.Elem().Interface().(excelize.DocProperties)
2149+
f, ok := files.Load(idx)
2150+
if !ok {
2151+
return C.CString(errFilePtr)
2152+
}
2153+
if err := f.(*excelize.File).SetDocProps(&options); err != nil {
2154+
return C.CString(err.Error())
2155+
}
2156+
return C.CString(emptyString)
2157+
}
2158+
21122159
// SetHeaderFooter provides a function to set headers and footers by given
21132160
// worksheet name and the control characters.
21142161
//

0 commit comments

Comments
 (0)