diff --git a/README.md b/README.md index 75afb90..ebb4033 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ finally: With Excelize chart generation and management is as easy as a few lines of code. You can build charts based on data in your worksheet or generate charts without any data in your worksheet at all. -

Excelize

+

Excelize

```python import excelize diff --git a/README_zh.md b/README_zh.md index 9c985e0..1edea1a 100644 --- a/README_zh.md +++ b/README_zh.md @@ -89,7 +89,7 @@ finally: 使用 Excelize 生成图表十分简单,仅需几行代码。您可以根据工作表中的已有数据构建图表,或向工作表中添加数据并创建图表。 -

使用 Excelize 在 Excel 电子表格文档中创建图表

+

使用 Excelize 在 Excel 电子表格文档中创建图表

```python import excelize diff --git a/excelize.py b/excelize.py index 382369a..a14a949 100644 --- a/excelize.py +++ b/excelize.py @@ -13,7 +13,7 @@ from dataclasses import fields from datetime import datetime, date, time from enum import Enum -from typing import Tuple, get_args, get_origin, List, Optional, Union +from typing import Tuple, get_args, get_origin, Dict, List, Optional, Union from ctypes import ( byref, c_bool, @@ -1692,14 +1692,30 @@ def get_col_width(self, sheet: str, col: str) -> float: ``` """ lib.GetColWidth.restype = types_go._Float64ErrorResult - res = lib.GetColWidth( - self.file_index, sheet.encode(ENCODE), col.encode(ENCODE) - ) + res = lib.GetColWidth(self.file_index, sheet.encode(ENCODE), col.encode(ENCODE)) err = res.err.decode(ENCODE) if not err: return res.val raise RuntimeError(err) + def get_comments(self, sheet: str) -> List[Comment]: + """ + GetComments retrieves all comments in a worksheet by given worksheet + name. + + Returns: + List[Comment]: Return the comment list if no error occurred, + otherwise raise a RuntimeError with the message. + """ + lib.GetComments.restype = types_go._GetCommentsResult + res = lib.GetComments(self.file_index, sheet.encode(ENCODE)) + result = c_value_to_py(res, GetCommentsResult()) + if res.Err: + err = res.Err.decode(ENCODE) + if err: + raise RuntimeError(err) + return result.comments if result and result.comments else [] + def get_default_font(self) -> str: """ Get the default font name currently set in the workbook. The spreadsheet @@ -1827,6 +1843,38 @@ def get_sheet_index(self, sheet: str) -> int: return res.val raise RuntimeError(err) + def get_sheet_list(self) -> List[str]: + """ + GetSheetList provides a function to get worksheets, chart sheets, and + dialog sheets name list of the workbook. + + Returns: + List[str]: Return the sheet name list if no error occurred, + otherwise return an empty list. + """ + lib.GetSheetList.restype = types_go._StringArrayErrorResult + res = lib.GetSheetList(self.file_index) + arr = c_value_to_py(res, StringArrayErrorResult()).arr + return arr if arr else [] + + def get_sheet_map(self) -> Dict[int, str]: + """ + GetSheetMap provides a function to get worksheets, chart sheets, dialog + sheets ID, and name maps of the workbook. + + Returns: + dict[int, str]: Return the sheet ID and name map if no error + occurred, otherwise return an empty dictionary. + """ + lib.GetSheetMap.restype = types_go._GetSheetMapResult + sheet_map = dict() + res = lib.GetSheetMap(self.file_index) + result = c_value_to_py(res, GetSheetMapResult()).arr + if result: + for item in result: + sheet_map[item.k] = item.v + return sheet_map + def get_sheet_name(self, sheet: int) -> str: """ Get the sheet name of the workbook by the given sheet index. @@ -3744,9 +3792,7 @@ def open_file(filename: str, *opts: Options) -> File: raise RuntimeError(err) -def open_reader( - buffer: bytes, *opts: Options -) -> Optional[File]: +def open_reader(buffer: bytes, *opts: Options) -> Optional[File]: """ Read data stream from bytes and return a populated spreadsheet file. diff --git a/main.go b/main.go index 7c81e9e..55781a0 100644 --- a/main.go +++ b/main.go @@ -548,9 +548,10 @@ func AddFormControl(idx int, sheet *C.char, opts *C.struct_FormControl) *C.char return C.CString(emptyString) } -// Add picture in a sheet by given picture format set (such as offset, scale, -// aspect ratio setting and print settings) and file path, supported image -// types: BMP, EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ. +// AddPicture add picture in a sheet by given picture format set (such as +// offset, scale, aspect ratio setting and print settings) and file path, +// supported image types: BMP, EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, +// WMF, and WMZ. // //export AddPicture func AddPicture(idx int, sheet, cell, name *C.char, opts *C.struct_GraphicOptions) *C.char { @@ -1189,6 +1190,29 @@ func GetColWidth(idx int, sheet, col *C.char) C.struct_Float64ErrorResult { return C.struct_Float64ErrorResult{val: C.double(val), err: C.CString(emptyString)} } +// GetComments retrieves all comments in a worksheet by given worksheet name. +// +//export GetComments +func GetComments(idx int, sheet *C.char) C.struct_GetCommentsResult { + f, ok := files.Load(idx) + if !ok { + return C.struct_GetCommentsResult{Err: C.CString(errFilePtr)} + } + comments, err := f.(*excelize.File).GetComments(C.GoString(sheet)) + if err != nil { + return C.struct_GetCommentsResult{Err: C.CString(err.Error())} + } + cArray := C.malloc(C.size_t(len(comments)) * C.size_t(unsafe.Sizeof(C.struct_Comment{}))) + for i, r := range comments { + cVal, err := goValueToC(reflect.ValueOf(r), reflect.ValueOf(&C.struct_Comment{})) + if err != nil { + return C.struct_GetCommentsResult{Err: C.CString(err.Error())} + } + *(*C.struct_Comment)(unsafe.Pointer(uintptr(unsafe.Pointer(cArray)) + uintptr(i)*unsafe.Sizeof(C.struct_Comment{}))) = cVal.Elem().Interface().(C.struct_Comment) + } + return C.struct_GetCommentsResult{CommentsLen: C.int(len(comments)), Comments: (*C.struct_Comment)(cArray), Err: C.CString(emptyString)} +} + // GetDefaultFont provides the default font name currently set in the // workbook. The spreadsheet generated by excelize default font is Calibri. // @@ -1302,6 +1326,56 @@ func GetSheetIndex(idx int, sheet *C.char) C.struct_IntErrorResult { return C.struct_IntErrorResult{val: C.int(idx), err: C.CString(emptyString)} } +// GetSheetList provides a function to get worksheets, chart sheets, and +// dialog sheets name list of the workbook. +// +//export GetSheetList +func GetSheetList(idx int) C.struct_StringArrayErrorResult { + f, ok := files.Load(idx) + if !ok { + return C.struct_StringArrayErrorResult{Err: C.CString(errFilePtr)} + } + result := f.(*excelize.File).GetSheetList() + cArray := C.malloc(C.size_t(len(result)) * C.size_t(unsafe.Sizeof(uintptr(0)))) + for i, v := range result { + *(*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(cArray)) + uintptr(i)*unsafe.Sizeof(uintptr(0)))) = unsafe.Pointer(C.CString(v)) + } + return C.struct_StringArrayErrorResult{ArrLen: C.int(len(result)), Arr: (**C.char)(cArray), Err: C.CString(emptyString)} +} + +// GetSheetMap provides a function to get worksheets, chart sheets, dialog +// sheets ID and name map of the workbook. +// +//export GetSheetMap +func GetSheetMap(idx int) C.struct_GetSheetMapResult { + type IntStringResult struct { + K int + V string + } + type GetSheetMapResult struct { + Arr []IntStringResult + Err string + } + var result GetSheetMapResult + f, ok := files.Load(idx) + if !ok { + return C.struct_GetSheetMapResult{ + Err: C.CString(errFilePtr), + } + } + for k, v := range f.(*excelize.File).GetSheetMap() { + result.Arr = append(result.Arr, IntStringResult{K: k, V: v}) + } + + cVal, err := goValueToC(reflect.ValueOf(result), reflect.ValueOf(&C.struct_GetSheetMapResult{})) + if err != nil { + return C.struct_GetSheetMapResult{Err: C.CString(err.Error())} + } + ret := cVal.Elem().Interface().(C.struct_GetSheetMapResult) + ret.Err = C.CString(emptyString) + return ret +} + // GetSheetName provides a function to get the sheet name by the given worksheet index. // If the given worksheet index is invalid, it will return an error. // diff --git a/test_excelize.py b/test_excelize.py index 1fc46b0..38b3b95 100644 --- a/test_excelize.py +++ b/test_excelize.py @@ -337,6 +337,8 @@ def test_style(self): with self.assertRaises(RuntimeError) as context: _ = f.get_sheet_index("") self.assertEqual(str(context.exception), "the sheet name can not be blank") + self.assertEqual(f.get_sheet_list(), ["Sheet1", "Sheet2"]) + self.assertEqual(len(f.get_sheet_map()), 2) self.assertEqual(f.get_sheet_name(index), "Sheet2") self.assertIsNone(f.set_col_outline_level("Sheet1", "D", 2)) @@ -735,6 +737,15 @@ def test_comment(self): comment, ) ) + comments = f.get_comments("Sheet1") + self.assertEqual(len(comments), 1) + self.assertEqual(comments[0].cell, "A5") + self.assertEqual(comments[0].author, "Excelize") + self.assertEqual(comments[0].paragraph[0].text, comment.paragraph[0].text) + self.assertEqual(comments[0].paragraph[1].text, comment.paragraph[1].text) + with self.assertRaises(RuntimeError) as context: + f.get_comments("SheetN") + self.assertEqual(str(context.exception), "sheet SheetN does not exist") with self.assertRaises(RuntimeError) as context: f.add_comment("SheetN", comment) self.assertEqual(str(context.exception), "sheet SheetN does not exist") diff --git a/types_c.h b/types_c.h index 463ac9c..9962303 100644 --- a/types_c.h +++ b/types_c.h @@ -741,6 +741,11 @@ struct StringArrayErrorResult char *Err; }; +struct IntStringResult { + int K; + char *V; +}; + struct GetCellHyperLinkResult { bool link; @@ -799,3 +804,15 @@ struct GetWorkbookPropsResult struct WorkbookPropsOptions opts; char *err; }; + +struct GetSheetMapResult { + int ArrLen; + struct IntStringResult *Arr; + char *Err; +}; + +struct GetCommentsResult { + int CommentsLen; + struct Comment *Comments; + char *Err; +}; diff --git a/types_go.py b/types_go.py index 7ece669..ab079ab 100644 --- a/types_go.py +++ b/types_go.py @@ -704,12 +704,21 @@ class _BoolErrorResult(Structure): ("err", c_char_p), ] + class _Float64ErrorResult(Structure): _fields_ = [ ("val", c_double), ("err", c_char_p), ] + +class _IntStringResult(Structure): + _fields_ = [ + ("K", c_int), + ("V", c_char_p), + ] + + class _StringArrayErrorResult(Structure): _fields_ = [ ("ArrLen", c_int), @@ -784,3 +793,19 @@ class _GetWorkbookPropsResult(Structure): ("opts", _WorkbookPropsOptions), ("err", c_char_p), ] + + +class _GetSheetMapResult(Structure): + _fields_ = [ + ("ArrLen", c_int), + ("Arr", POINTER(_IntStringResult)), + ("Err", c_char_p), + ] + + +class _GetCommentsResult(Structure): + _fields_ = [ + ("CommentsLen", c_int), + ("Comments", POINTER(_Comment)), + ("Err", c_char_p), + ] diff --git a/types_py.py b/types_py.py index 5997326..5a05612 100644 --- a/types_py.py +++ b/types_py.py @@ -792,7 +792,25 @@ class WorkbookProtectionOptions: lock_windows: bool = False +@dataclass +class IntStringResult: + k: int = 0 + v: str = "" + + @dataclass class StringArrayErrorResult: arr: Optional[List[str]] = None err: str = "" + + +@dataclass +class GetSheetMapResult: + arr: Optional[List[IntStringResult]] = None + err: str = "" + + +@dataclass +class GetCommentsResult: + comments: Optional[List[Comment]] = None + err: str = ""