From d70a7048045c8354579cfbc2a4a0cb74108e2c82 Mon Sep 17 00:00:00 2001 From: oliver Date: Thu, 2 Oct 2025 08:39:34 +0800 Subject: [PATCH 1/3] Add SetAppProps binding and tests --- excelize.py | 21 +++++++++++++++++++++ main.go | 19 +++++++++++++++++++ test_excelize.py | 17 ++++++++++++++--- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/excelize.py b/excelize.py index 33fa36e..c5196e7 100644 --- a/excelize.py +++ b/excelize.py @@ -4009,6 +4009,27 @@ def set_active_sheet(self, index: int) -> None: if err != "": raise RuntimeError(err) + def set_app_props(self, app_properties: AppProperties) -> None: + """ + Set document application properties. + + Args: + app_properties (AppProperties): The application properties + + Returns: + None: Return None if no error occurred, otherwise raise a + RuntimeError with the message. + """ + prepare_args( + [app_properties], + [argsRule("app_properties", [AppProperties])], + ) + lib.SetAppProps.restype = c_char_p + options = py_value_to_c(app_properties, types_go._AppProperties()) + err = lib.SetAppProps(self.file_index, byref(options)).decode(ENCODE) + if err != "": + raise RuntimeError(err) + def set_cell_bool(self, sheet: str, cell: str, value: bool) -> None: """ Set bool type value of a cell by given worksheet name, cell reference diff --git a/main.go b/main.go index 64896a7..f97d339 100644 --- a/main.go +++ b/main.go @@ -2091,6 +2091,25 @@ func SetActiveSheet(idx, index int) *C.char { return C.CString(emptyString) } +// SetAppProps provides a function to set document application properties. +// +//export SetAppProps +func SetAppProps(idx int, opts *C.struct_AppProperties) *C.char { + f, ok := files.Load(idx) + if !ok { + return C.CString(errFilePtr) + } + goVal, err := cValueToGo(reflect.ValueOf(*opts), reflect.TypeOf(excelize.AppProperties{})) + if err != nil { + return C.CString(err.Error()) + } + appProps := goVal.Elem().Interface().(excelize.AppProperties) + if err := f.(*excelize.File).SetAppProps(&appProps); err != nil { + return C.CString(err.Error()) + } + return C.CString(emptyString) +} + // SetCellBool provides a function to set bool type value of a cell by given // worksheet name, cell reference and cell value. // diff --git a/test_excelize.py b/test_excelize.py index 0eb1e59..2e319d9 100644 --- a/test_excelize.py +++ b/test_excelize.py @@ -80,11 +80,22 @@ def test_open_file(self): str(context.exception), "expected type str for argument 'filename', but got int", ) - + def test_app_props(self): f = excelize.new_file() - props = f.get_app_props() - self.assertEqual(props.application, "Go Excelize") + expected = excelize.AppProperties( + application="excelize-py", + scale_crop=False, + doc_security=0, + company="Excelize", + links_up_to_date=False, + hyperlinks_changed=False, + app_version="1.0.0", + ) + self.assertIsNone(f.set_app_props(expected)) + self.assertEqual(f.get_app_props(), expected) + self.assertIsNone(f.save_as(os.path.join("test", "TestAppProps.xlsx"))) + self.assertIsNone(f.close()) def test_default_font(self): f = excelize.new_file() From 6408ce8455baa7d5b9379b04ed8a7c22893f478a Mon Sep 17 00:00:00 2001 From: oliver Date: Thu, 2 Oct 2025 09:26:38 +0800 Subject: [PATCH 2/3] add-test --- test_excelize.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_excelize.py b/test_excelize.py index 2e319d9..736e018 100644 --- a/test_excelize.py +++ b/test_excelize.py @@ -96,6 +96,8 @@ def test_app_props(self): self.assertEqual(f.get_app_props(), expected) self.assertIsNone(f.save_as(os.path.join("test", "TestAppProps.xlsx"))) self.assertIsNone(f.close()) + with self.assertRaises(RuntimeError): + f.set_app_props(expected) def test_default_font(self): f = excelize.new_file() From 3dd1db2922602ecd5fcb4bd2ab6efd3729ca7de7 Mon Sep 17 00:00:00 2001 From: oliver Date: Sat, 4 Oct 2025 11:07:58 +0800 Subject: [PATCH 3/3] fix code review issue --- excelize.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++- test_excelize.py | 19 +++++++++-------- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/excelize.py b/excelize.py index c5196e7..10346fa 100644 --- a/excelize.py +++ b/excelize.py @@ -4011,7 +4011,38 @@ def set_active_sheet(self, index: int) -> None: def set_app_props(self, app_properties: AppProperties) -> None: """ - Set document application properties. + Set document application properties. The properties that can be set are: + + application: The name of the application that created this document. + + scale_crop: Indicates the display mode of the document thumbnail. Set + this element to `true` to enable scaling of the document thumbnail to + the display. Set this element to `false` to enable cropping of the + document thumbnail to show only sections that will fit the display. + + doc_security: Security level of a document as a numeric value. Document + security is defined as: + 1 - Document is password protected. + 2 - Document is recommended to be opened as read-only. + 3 - Document is enforced to be opened as read-only. + 4 - Document is locked for annotation. + + company: The name of a company associated with the document. + + links_up_to_date: Indicates whether hyperlinks in a document are + up-to-date. Set this element to `true` to indicate that hyperlinks are + updated. Set this element to `false` to indicate that hyperlinks are + outdated. + + hyperlinks_changed: Specifies that one or more hyperlinks in this part + were updated exclusively in this part by a producer. The next producer + to open this document shall update the hyperlink relationships with the + new hyperlinks specified in this part. + + app_version: Specifies the version of the application which produced + this document. The content of this element shall be of the form XX.YYYY + where X and Y represent numerical values, or the document shall be + considered non-conformant. Args: app_properties (AppProperties): The application properties @@ -4019,6 +4050,26 @@ def set_app_props(self, app_properties: AppProperties) -> None: Returns: None: Return None if no error occurred, otherwise raise a RuntimeError with the message. + + Example: + For example: + + ```python + try: + f.set_app_props( + excelize.AppProperties( + application: "Microsoft Excel", + scale_crop: true, + doc_security: 3, + company: "Company Name", + links_up_to_date: true, + hyperlinks_changed: true, + app_version: "16.0000", + ) + ) + except (RuntimeError, TypeError) as err: + print(err) + ``` """ prepare_args( [app_properties], diff --git a/test_excelize.py b/test_excelize.py index 736e018..23c0f7f 100644 --- a/test_excelize.py +++ b/test_excelize.py @@ -80,24 +80,18 @@ def test_open_file(self): str(context.exception), "expected type str for argument 'filename', but got int", ) - + def test_app_props(self): f = excelize.new_file() expected = excelize.AppProperties( application="excelize-py", - scale_crop=False, - doc_security=0, company="Excelize", - links_up_to_date=False, - hyperlinks_changed=False, app_version="1.0.0", - ) + ) self.assertIsNone(f.set_app_props(expected)) self.assertEqual(f.get_app_props(), expected) self.assertIsNone(f.save_as(os.path.join("test", "TestAppProps.xlsx"))) self.assertIsNone(f.close()) - with self.assertRaises(RuntimeError): - f.set_app_props(expected) def test_default_font(self): f = excelize.new_file() @@ -898,6 +892,15 @@ def test_none_file_pointer(self): str(context.exception), "expected type int for argument 'index', but got str", ) + with self.assertRaises(RuntimeError) as context: + f.set_app_props(excelize.AppProperties()) + self.assertEqual(str(context.exception), expected) + with self.assertRaises(TypeError) as context: + f.set_app_props(0) + self.assertEqual( + str(context.exception), + "expected type AppProperties for argument 'app_properties', but got int", + ) with self.assertRaises(RuntimeError) as context: f.set_default_font("") self.assertEqual(str(context.exception), expected)