Skip to content

Commit

Permalink
Add 3 new functions: merge_cell, open_reader and set_sheet_background
Browse files Browse the repository at this point in the history
- Update unit tests and docs for the function
- Move some markdown files into .github directory
  • Loading branch information
xuri committed Dec 17, 2024
1 parent aa677fe commit 27b7ea2
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 41 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
142 changes: 115 additions & 27 deletions excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ def c_value_to_py(ctypes_instance, py_instance):
for i in range(l):
py_list.append(
c_value_to_py(
c_array[i], get_args(py_field_args[0])[0]()
c_array[i],
get_args(py_field_args[0])[0](),
),
)
setattr(py_instance, py_field_name, py_list)
Expand Down Expand Up @@ -419,7 +420,8 @@ def py_value_to_c_interface(py_value):
bool: lambda: Interface(type=4, boolean=py_value),
datetime: lambda: Interface(type=5, integer=int(py_value.timestamp())),
date: lambda: Interface(
type=5, integer=int(datetime.combine(py_value, time.min).timestamp())
type=5,
integer=int(datetime.combine(py_value, time.min).timestamp()),
),
}
interface = type_mappings.get(type(py_value), lambda: Interface())()
Expand Down Expand Up @@ -699,7 +701,8 @@ def add_pivot_table(self, opts: Optional[PivotTableOptions]) -> Optional[Excepti
"""
lib.AddPivotTable.restype = c_char_p
err = lib.AddPivotTable(
self.file_index, byref(py_value_to_c(opts, types_go._PivotTableOptions()))
self.file_index,
byref(py_value_to_c(opts, types_go._PivotTableOptions())),
).decode(ENCODE)
return None if err == "" else Exception(err)

Expand Down Expand Up @@ -1126,7 +1129,9 @@ def get_active_sheet_index(self) -> int:
res = lib.GetActiveSheetIndex(self.file_index)
return res

def get_app_props(self) -> Tuple[Optional[AppProperties], Optional[Exception]]:
def get_app_props(
self,
) -> Tuple[Optional[AppProperties], Optional[Exception]]:
"""
Get document application properties.
Expand All @@ -1138,7 +1143,7 @@ def get_app_props(self) -> Tuple[Optional[AppProperties], Optional[Exception]]:
lib.GetAppProps.restype = types_go._GetAppPropsResult
res = lib.GetAppProps(self.file_index)
err = res.err.decode(ENCODE)
return c_value_to_py(res.opts, AppProperties()) if err == "" else None, (
return (c_value_to_py(res.opts, AppProperties()) if err == "" else None), (
None if err == "" else Exception(err)
)

Expand Down Expand Up @@ -1271,6 +1276,50 @@ def get_rows(

return rows, None if err == "" else Exception(err)

def get_style(self, style_id: int) -> Tuple[Optional[Style], Optional[Exception]]:
"""
Get style definition by given style index.
Args:
style_id (int): The style ID
Returns:
Tuple[Optional[Style], Optional[Exception]]: A tuple containing the
Style object if found, otherwise None, and an Exception object if an
error occurred, otherwise None.
"""
lib.GetStyle.restype = types_go._GetStyleResult
res = lib.GetStyle(self.file_index, c_int(style_id))
err = res.err.decode(ENCODE)
if err == "":
return c_value_to_py(res.style, Style()), None
return None, Exception(err)

def merge_cell(
self, sheet: str, top_left_cell: str, bottom_right_cell: str
) -> Optional[Exception]:
"""
Merge cells by given range reference and sheet name. Merging cells only
keeps the upper-left cell value, and discards the other values.
Args:
sheet (str): The worksheet name
top_left_cell (str): The top-left cell reference
bottom_right_cell (str): The right-bottom cell reference
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
"""
lib.MergeCell.restype = c_char_p
err = lib.MergeCell(
self.file_index,
sheet.encode(ENCODE),
top_left_cell.encode(ENCODE),
bottom_right_cell.encode(ENCODE),
).decode(ENCODE)
return None if err == "" else Exception(err)

def new_sheet(self, sheet: str) -> Tuple[int, Optional[Exception]]:
"""
Create a new sheet by given a worksheet name and returns the index of
Expand Down Expand Up @@ -1309,25 +1358,6 @@ def new_style(self, style: Style) -> Tuple[int, Optional[Exception]]:
err = res.err.decode(ENCODE)
return res.style, None if err == "" else Exception(err)

def get_style(self, style_id: int) -> Tuple[Optional[Style], Optional[Exception]]:
"""
Get style definition by given style index.
Args:
style_id (int): The style ID
Returns:
Tuple[Optional[Style], Optional[Exception]]: A tuple containing the
Style object if found, otherwise None, and an Exception object if an
error occurred, otherwise None.
"""
lib.GetStyle.restype = types_go._GetStyleResult
res = lib.GetStyle(self.file_index, c_int(style_id))
err = res.err.decode(ENCODE)
if err == "":
return c_value_to_py(res.style, Style()), None
return None, Exception(err)

def set_active_sheet(self, index: int) -> Optional[Exception]:
"""
Set the default active sheet of the workbook by a given index. Note that
Expand Down Expand Up @@ -1384,7 +1414,12 @@ def set_cell_formula(
return None if err == "" else Exception(err)

def set_cell_hyperlink(
self, sheet: str, cell: str, link: str, link_type: str, *opts: HyperlinkOpts
self,
sheet: str,
cell: str,
link: str,
link_type: str,
*opts: HyperlinkOpts,
) -> Optional[Exception]:
"""
Set cell hyperlink by given worksheet name and link URL address. The
Expand Down Expand Up @@ -1449,7 +1484,11 @@ def set_cell_hyperlink(
return None if err == "" else Exception(err)

def set_cell_style(
self, sheet: str, top_left_cell: str, bottom_right_cell: str, style_id: int
self,
sheet: str,
top_left_cell: str,
bottom_right_cell: str,
style_id: int,
) -> Optional[Exception]:
"""
Add style attribute for cells by given worksheet name, range reference
Expand Down Expand Up @@ -1479,7 +1518,10 @@ def set_cell_style(
return None if err == "" else Exception(err)

def set_cell_value(
self, sheet: str, cell: str, value: Union[None, int, str, bool, datetime, date]
self,
sheet: str,
cell: str,
value: Union[None, int, str, bool, datetime, date],
) -> Optional[Exception]:
"""
Set the value of a cell. The specified coordinates should not be in the
Expand Down Expand Up @@ -1511,6 +1553,28 @@ def set_cell_value(
).decode(ENCODE)
return None if err == "" else Exception(err)

def set_sheet_background(self, sheet: str, picture: str) -> Optional[Exception]:
"""
Set background picture by given worksheet name and file path. Supported
image types: BMP, EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF,
and WMZ.
Args:
sheet (str): The worksheet name
picture (str): The image file path
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
"""
lib.SetSheetBackground.restype = c_char_p
err = lib.SetSheetBackground(
self.file_index,
sheet.encode(ENCODE),
picture.encode(ENCODE),
).decode(ENCODE)
return None if err == "" else Exception(err)

def set_sheet_background_from_bytes(
self, sheet: str, extension: str, picture: bytes
) -> Optional[Exception]:
Expand Down Expand Up @@ -1683,3 +1747,27 @@ def open_file(
if err == "":
return File(res.idx), None
return None, Exception(err)


def open_reader(
buffer: bytes, *opts: Options
) -> Tuple[Optional[File], Optional[Exception]]:
"""
Read data stream from bytes and return a populated spreadsheet file.
Args:
buffer (bytes): The contents buffer of the file
*opts (Options): Optional parameters for opening the file.
Returns:
Tuple[Optional[File], Optional[Exception]]: A tuple containing a File
object if successful, or None and an Exception if an error occurred.
"""
lib.OpenReader.restype, options = types_go._OptionsResult, None
if len(opts) > 0:
options = byref(py_value_to_c(opts[0], types_go._Options()))
res = lib.OpenReader(cast(buffer, POINTER(c_ubyte)), len(buffer), options)
err = res.err.decode(ENCODE)
if err == "":
return File(res.idx), None
return None, Exception(err)
61 changes: 61 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package main
import "C"

import (
"bytes"
"errors"
"reflect"
"sync"
Expand Down Expand Up @@ -1072,6 +1073,22 @@ func GetStyle(idx, styleID int) C.struct_GetStyleResult {
return C.struct_GetStyleResult{style: cVal.Elem().Interface().(C.struct_Style), err: C.CString(errNil)}
}

// MergeCell provides a function to merge cells by given range reference and
// sheet name. Merging cells only keeps the upper-left cell value, and
// discards the other values.
//
//export MergeCell
func MergeCell(idx int, sheet, topLeftCell, bottomRightCell *C.char) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString("")
}
if err := f.(*excelize.File).MergeCell(C.GoString(sheet), C.GoString(topLeftCell), C.GoString(bottomRightCell)); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// NewFile provides a function to create new file by default template.
//
//export NewFile
Expand Down Expand Up @@ -1153,6 +1170,34 @@ func OpenFile(filename *C.char, opts *C.struct_Options) C.struct_OptionsResult {
return C.struct_OptionsResult{idx: C.int(idx), err: C.CString(errNil)}
}

// OpenReader read data stream from io.Reader and return a populated spreadsheet
// file.
//
//export OpenReader
func OpenReader(b *C.uchar, bLen C.int, opts *C.struct_Options) C.struct_OptionsResult {
var options excelize.Options
if opts != nil {
goVal, err := cValueToGo(reflect.ValueOf(*opts), reflect.TypeOf(excelize.Options{}))
if err != nil {
return C.struct_OptionsResult{idx: C.int(-1), err: C.CString(err.Error())}
}
options = goVal.Elem().Interface().(excelize.Options)
}
buf := C.GoBytes(unsafe.Pointer(b), bLen)
f, err := excelize.OpenReader(bytes.NewReader(buf), options)
if err != nil {
return C.struct_OptionsResult{idx: C.int(-1), err: C.CString(err.Error())}
}
var idx int
files.Range(func(_, _ interface{}) bool {
idx++
return true
})
idx++
files.Store(idx, f)
return C.struct_OptionsResult{idx: C.int(idx), err: C.CString(errNil)}
}

// Save provides a function to override the spreadsheet with origin path.
//
//export Save
Expand Down Expand Up @@ -1325,6 +1370,22 @@ func SetCellValue(idx int, sheet, cell *C.char, value *C.struct_Interface) *C.ch
return C.CString(errNil)
}

// SetSheetBackground provides a function to set background picture by given
// worksheet name and file path. Supported image types: BMP, EMF, EMZ, GIF,
// JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.
//
//export SetSheetBackground
func SetSheetBackground(idx int, sheet, picture *C.char) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).SetSheetBackground(C.GoString(sheet), C.GoString(picture)); err != nil {
C.CString(err.Error())
}
return C.CString(errNil)
}

// SetSheetBackgroundFromBytes provides a function to set background picture by
// given worksheet name, extension name and image data. Supported image types:
// BMP, EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.
Expand Down
Loading

0 comments on commit 27b7ea2

Please sign in to comment.