Skip to content

Commit

Permalink
Add 4 new functions: protect_sheet, protect_workbook, move_sheet and …
Browse files Browse the repository at this point in the history
…set_row_height

- Update unit tests and docs for the function
  • Loading branch information
xuri committed Dec 26, 2024
1 parent 3da5bc3 commit 8e22bff
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 3 deletions.
134 changes: 134 additions & 0 deletions excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,104 @@ 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 protect_sheet(
self, sheet: str, opts: SheetProtectionOptions
) -> Optional[Exception]:
"""
Prevent other users from accidentally or deliberately changing, moving,
or deleting data in a worksheet. The optional field AlgorithmName
specified hash algorithm, support XOR, MD4, MD5, SHA-1, SHA2-56,
SHA-384, and SHA-512 currently, if no hash algorithm specified, will be
using the XOR algorithm as default.
Args:
sheet (str): The worksheet name
opts (SheetProtectionOptions): The sheet protection options
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
Example:
For example, protect Sheet1 with protection settings:
.. code-block:: python
err = f.protect_sheet("Sheet1", excelize.SheetProtectionOptions(
algorithm_name="SHA-512",
password="password",
select_locked_cells=True,
select_unlocked_cells=True,
edit_scenarios=True,
))
"""
lib.ProtectSheet.restype = c_char_p
options = py_value_to_c(opts, types_go._SheetProtectionOptions())
err = lib.ProtectSheet(
self.file_index, sheet.encode(ENCODE), byref(options)
).decode(ENCODE)
return None if err == "" else Exception(err)

def protect_workbook(self, opts: WorkbookProtectionOptions) -> Optional[Exception]:
"""
Prevent other users from viewing hidden worksheets, adding, moving,
deleting, or hiding worksheets, and renaming worksheets in a workbook.
The optional field AlgorithmName specified hash algorithm, support XOR,
MD4, MD5, SHA-1, SHA2-56, SHA-384, and SHA-512 currently, if no hash
algorithm specified, will be using the XOR algorithm as default. The
generated workbook only works on Microsoft Office 2007 and later.
Args:
opts (WorkbookProtectionOptions): The workbook protection options
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
Example:
For example, protect workbook with protection settings:
.. code-block:: python
err = f.protect_workbook(excelize.WorkbookProtectionOptions(
password="password",
lock_structure=True,
))
"""
lib.ProtectWorkbook.restype = c_char_p
options = py_value_to_c(opts, types_go._WorkbookProtectionOptions())
err = lib.ProtectWorkbook(self.file_index, byref(options)).decode(ENCODE)
return None if err == "" else Exception(err)

def remove_col(self, sheet: str, col: str) -> Optional[Exception]:
"""
Remove single column by given worksheet name and column index.
Use this method with caution, which will affect changes in references
such as formulas, charts, and so on. If there is any referenced value of
the worksheet, it will cause a file error when you open it. The excelize
only partially updates these references currently.
Args:
sheet (str): The worksheet name
col (str): The column name
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
Example:
For example, remove column C in Sheet1:
.. code-block:: python
err = f.remove_col("Sheet1", "C")
"""
lib.RemoveCol.restype = c_char_p
err = lib.RemoveCol(
self.file_index, sheet.encode(ENCODE), col.encode(ENCODE)
).decode(ENCODE)
return None if err == "" else 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 @@ -1666,6 +1764,12 @@ def set_cell_rich_text(
.. code-block:: python
f = excelize.new_file()
err = f.set_row_height("Sheet1", 1, 35)
if err:
print(err)
err = f.set_col_width("Sheet1", "A", "A", 44)
if err:
print(err)
err = f.set_cell_rich_text(
"Sheet1",
"A1",
Expand Down Expand Up @@ -2078,6 +2182,36 @@ def set_defined_name(self, defined_name: DefinedName) -> Optional[Exception]:
err = lib.SetDefinedName(self.file_index, byref(options)).decode(ENCODE)
return None if err == "" else Exception(err)

def set_row_height(
self, sheet: str, row: int, height: float
) -> Optional[Exception]:
"""
Set the height of a single row. If the value of height is 0, will hide
the specified row, if the value of height is -1, will unset the custom
row height.
Args:
sheet (str): The worksheet name
row (int): The row number
height (float): The row height
Returns:
Optional[Exception]: Returns None if no error occurred,
otherwise returns an Exception with the message.
Example:
For example, set the height of the first row in Sheet1:
.. code-block:: python
err = f.set_row_height("Sheet1", 1, 50)
"""
lib.SetRowHeight.restype = c_char_p
err = lib.SetRowHeight(
self.file_index, sheet.encode(ENCODE), c_int(row), c_double(height)
).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
Expand Down
81 changes: 81 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,71 @@ func OpenReader(b *C.uchar, bLen C.int, opts *C.struct_Options) C.struct_Options
return C.struct_OptionsResult{idx: C.int(idx), err: C.CString(errNil)}
}

// ProtectSheet provides a function to prevent other users from accidentally or
// deliberately changing, moving, or deleting data in a worksheet. The
// optional field AlgorithmName specified hash algorithm, support XOR, MD4,
// MD5, SHA-1, SHA2-56, SHA-384, and SHA-512 currently, if no hash algorithm
// specified, will be using the XOR algorithm as default.
//
//export ProtectSheet
func ProtectSheet(idx int, sheet *C.char, opts *C.struct_SheetProtectionOptions) *C.char {
var options excelize.SheetProtectionOptions
goVal, err := cValueToGo(reflect.ValueOf(*opts), reflect.TypeOf(excelize.SheetProtectionOptions{}))
if err != nil {
return C.CString(err.Error())
}
options = goVal.Elem().Interface().(excelize.SheetProtectionOptions)
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).ProtectSheet(C.GoString(sheet), &options); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// ProtectWorkbook provides a function to prevent other users from viewing
// hidden worksheets, adding, moving, deleting, or hiding worksheets, and
// renaming worksheets in a workbook. The optional field AlgorithmName
// specified hash algorithm, support XOR, MD4, MD5, SHA-1, SHA2-56, SHA-384,
// and SHA-512 currently, if no hash algorithm specified, will be using the XOR
// algorithm as default. The generated workbook only works on Microsoft Office
// 2007 and later.
//
//export ProtectWorkbook
func ProtectWorkbook(idx int, opts *C.struct_WorkbookProtectionOptions) *C.char {
var options excelize.WorkbookProtectionOptions
goVal, err := cValueToGo(reflect.ValueOf(*opts), reflect.TypeOf(excelize.WorkbookProtectionOptions{}))
if err != nil {
return C.CString(err.Error())
}
options = goVal.Elem().Interface().(excelize.WorkbookProtectionOptions)
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).ProtectWorkbook(&options); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// RemoveCol provides a function to remove single column by given worksheet
// name and column index.
//
//export RemoveCol
func RemoveCol(idx int, sheet, col *C.char) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).RemoveCol(C.GoString(sheet), C.GoString(col)); err != nil {
return C.CString(err.Error())
}
return C.CString(errNil)
}

// Save provides a function to override the spreadsheet with origin path.
//
//export Save
Expand Down Expand Up @@ -1650,6 +1715,22 @@ func SetDefinedName(idx int, definedName *C.struct_DefinedName) *C.char {
return C.CString(errNil)
}

// SetRowHeight provides a function to set the height of a single row. If the
// value of height is 0, will hide the specified row, if the value of height is
// -1, will unset the custom row height.
//
//export SetRowHeight
func SetRowHeight(idx int, sheet *C.char, row int, height float64) *C.char {
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).SetRowHeight(C.GoString(sheet), row, height); err != nil {
C.CString(err.Error())
}
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.
Expand Down
23 changes: 22 additions & 1 deletion test_excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,28 @@ def test_style(self):
["Hello"],
],
)

self.assertIsNone(
f.protect_sheet(
"Sheet1",
excelize.SheetProtectionOptions(
algorithm_name="SHA-512",
password="password",
select_locked_cells=True,
select_unlocked_cells=True,
edit_scenarios=True,
),
)
)
self.assertIsNone(
f.protect_workbook(
excelize.WorkbookProtectionOptions(
password="password",
lock_structure=True,
)
)
)
self.assertIsNone(f.move_sheet("Sheet2", "Sheet1"))
self.assertIsNone(f.remove_col("Sheet1", "Z"))
self.assertIsNone(f.ungroup_sheets())
self.assertIsNone(f.update_linked_value())
self.assertIsNone(f.save())
Expand Down Expand Up @@ -720,6 +740,7 @@ def test_cell_hyperlink(self):

def test_cell_rich_text(self):
f = excelize.new_file()
self.assertIsNone(f.set_row_height("Sheet1", 1, 35))
self.assertIsNone(f.set_col_width("Sheet1", "A", "A", 44))
self.assertIsNone(
f.set_cell_rich_text(
Expand Down
22 changes: 22 additions & 0 deletions types_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,28 @@ struct Shape
struct RichTextRun *Paragraph;
};

// SheetProtectionOptions directly maps the settings of worksheet protection.
struct SheetProtectionOptions
{
char *AlgorithmName;
bool AutoFilter;
bool DeleteColumns;
bool DeleteRows;
bool EditObjects;
bool EditScenarios;
bool FormatCells;
bool FormatColumns;
bool FormatRows;
bool InsertColumns;
bool InsertHyperlinks;
bool InsertRows;
char *Password;
bool PivotTables;
bool SelectLockedCells;
bool SelectUnlockedCells;
bool Sort;
};

// SlicerOptions represents the settings of the slicer.
struct SlicerOptions
{
Expand Down
22 changes: 22 additions & 0 deletions types_go.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,28 @@ class _Shape(Structure):
]


class _SheetProtectionOptions(Structure):
_fields_ = [
("AlgorithmName", c_char_p),
("AutoFilter", c_bool),
("DeleteColumns", c_bool),
("DeleteRows", c_bool),
("EditObjects", c_bool),
("EditScenarios", c_bool),
("FormatCells", c_bool),
("FormatColumns", c_bool),
("FormatRows", c_bool),
("InsertColumns", c_bool),
("InsertHyperlinks", c_bool),
("InsertRows", c_bool),
("Password", c_char_p),
("PivotTables", c_bool),
("SelectLockedCells", c_bool),
("SelectUnlockedCells", c_bool),
("Sort", c_bool),
]


class _SlicerOptions(Structure):
_fields_ = [
("Name", c_char_p),
Expand Down
25 changes: 23 additions & 2 deletions types_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,27 @@ class Shape:
paragraph: Optional[List[RichTextRun]] = None


@dataclass
class SheetProtectionOptions:
algorithm_name: str = ""
auto_filter: bool = False
delete_columns: bool = False
delete_rows: bool = False
edit_objects: bool = False
edit_scenarios: bool = False
format_cells: bool = False
format_columns: bool = False
format_rows: bool = False
insert_columns: bool = False
insert_hyperlinks: bool = False
insert_rows: bool = False
password: str = ""
pivot_tables: bool = False
select_locked_cells: bool = False
select_unlocked_cells: bool = False
sort: bool = False


@dataclass
class SlicerOptions:
name: str = ""
Expand Down Expand Up @@ -636,5 +657,5 @@ class WorkbookPropsOptions:
class WorkbookProtectionOptions:
algorithm_name: str = ""
password: str = ""
lock_structure: Optional[bool] = None
lock_windows: Optional[bool] = None
lock_structure: bool = False
lock_windows: bool = False

0 comments on commit 8e22bff

Please sign in to comment.