aspose-cells-foss 25.12.1__py3-none-any.whl → 26.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. aspose_cells/__init__.py +88 -0
  2. aspose_cells/auto_filter.py +527 -0
  3. aspose_cells/cell.py +483 -0
  4. aspose_cells/cell_value_handler.py +319 -0
  5. aspose_cells/cells.py +779 -0
  6. aspose_cells/cfb_handler.py +445 -0
  7. aspose_cells/cfb_writer.py +659 -0
  8. aspose_cells/cfb_writer_minimal.py +337 -0
  9. aspose_cells/comment_xml.py +475 -0
  10. aspose_cells/conditional_format.py +1185 -0
  11. aspose_cells/csv_handler.py +690 -0
  12. aspose_cells/data_validation.py +911 -0
  13. aspose_cells/document_properties.py +356 -0
  14. aspose_cells/encryption_crypto.py +247 -0
  15. aspose_cells/encryption_params.py +138 -0
  16. aspose_cells/hyperlink.py +372 -0
  17. aspose_cells/json_handler.py +185 -0
  18. aspose_cells/markdown_handler.py +583 -0
  19. aspose_cells/shared_strings.py +101 -0
  20. aspose_cells/style.py +841 -0
  21. aspose_cells/workbook.py +499 -0
  22. aspose_cells/workbook_hash_password.py +68 -0
  23. aspose_cells/workbook_properties.py +712 -0
  24. aspose_cells/worksheet.py +570 -0
  25. aspose_cells/worksheet_properties.py +1239 -0
  26. aspose_cells/xlsx_encryptor.py +403 -0
  27. aspose_cells/xml_autofilter_loader.py +195 -0
  28. aspose_cells/xml_autofilter_saver.py +173 -0
  29. aspose_cells/xml_conditional_format_loader.py +215 -0
  30. aspose_cells/xml_conditional_format_saver.py +351 -0
  31. aspose_cells/xml_datavalidation_loader.py +239 -0
  32. aspose_cells/xml_datavalidation_saver.py +245 -0
  33. aspose_cells/xml_hyperlink_handler.py +323 -0
  34. aspose_cells/xml_loader.py +986 -0
  35. aspose_cells/xml_properties_loader.py +512 -0
  36. aspose_cells/xml_properties_saver.py +607 -0
  37. aspose_cells/xml_saver.py +1306 -0
  38. aspose_cells_foss-26.2.2.dist-info/METADATA +190 -0
  39. aspose_cells_foss-26.2.2.dist-info/RECORD +41 -0
  40. {aspose_cells_foss-25.12.1.dist-info → aspose_cells_foss-26.2.2.dist-info}/WHEEL +1 -1
  41. aspose_cells_foss-26.2.2.dist-info/top_level.txt +1 -0
  42. aspose/__init__.py +0 -14
  43. aspose/cells/__init__.py +0 -31
  44. aspose/cells/cell.py +0 -350
  45. aspose/cells/constants.py +0 -44
  46. aspose/cells/converters/__init__.py +0 -13
  47. aspose/cells/converters/csv_converter.py +0 -55
  48. aspose/cells/converters/json_converter.py +0 -46
  49. aspose/cells/converters/markdown_converter.py +0 -453
  50. aspose/cells/drawing/__init__.py +0 -17
  51. aspose/cells/drawing/anchor.py +0 -172
  52. aspose/cells/drawing/collection.py +0 -233
  53. aspose/cells/drawing/image.py +0 -338
  54. aspose/cells/formats.py +0 -80
  55. aspose/cells/formula/__init__.py +0 -10
  56. aspose/cells/formula/evaluator.py +0 -360
  57. aspose/cells/formula/functions.py +0 -433
  58. aspose/cells/formula/tokenizer.py +0 -340
  59. aspose/cells/io/__init__.py +0 -27
  60. aspose/cells/io/csv/__init__.py +0 -8
  61. aspose/cells/io/csv/reader.py +0 -88
  62. aspose/cells/io/csv/writer.py +0 -98
  63. aspose/cells/io/factory.py +0 -138
  64. aspose/cells/io/interfaces.py +0 -48
  65. aspose/cells/io/json/__init__.py +0 -8
  66. aspose/cells/io/json/reader.py +0 -126
  67. aspose/cells/io/json/writer.py +0 -119
  68. aspose/cells/io/md/__init__.py +0 -8
  69. aspose/cells/io/md/reader.py +0 -161
  70. aspose/cells/io/md/writer.py +0 -334
  71. aspose/cells/io/models.py +0 -64
  72. aspose/cells/io/xlsx/__init__.py +0 -9
  73. aspose/cells/io/xlsx/constants.py +0 -312
  74. aspose/cells/io/xlsx/image_writer.py +0 -311
  75. aspose/cells/io/xlsx/reader.py +0 -284
  76. aspose/cells/io/xlsx/writer.py +0 -931
  77. aspose/cells/plugins/__init__.py +0 -6
  78. aspose/cells/plugins/docling_backend/__init__.py +0 -7
  79. aspose/cells/plugins/docling_backend/backend.py +0 -535
  80. aspose/cells/plugins/markitdown_plugin/__init__.py +0 -15
  81. aspose/cells/plugins/markitdown_plugin/plugin.py +0 -128
  82. aspose/cells/range.py +0 -210
  83. aspose/cells/style.py +0 -287
  84. aspose/cells/utils/__init__.py +0 -54
  85. aspose/cells/utils/coordinates.py +0 -68
  86. aspose/cells/utils/exceptions.py +0 -43
  87. aspose/cells/utils/validation.py +0 -102
  88. aspose/cells/workbook.py +0 -352
  89. aspose/cells/worksheet.py +0 -670
  90. aspose_cells_foss-25.12.1.dist-info/METADATA +0 -189
  91. aspose_cells_foss-25.12.1.dist-info/RECORD +0 -53
  92. aspose_cells_foss-25.12.1.dist-info/entry_points.txt +0 -2
  93. aspose_cells_foss-25.12.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,173 @@
1
+ """
2
+ Aspose.Cells for Python - XML AutoFilter Saver Module
3
+
4
+ This module provides the AutoFilterXMLWriter class which handles saving
5
+ autofilter data to XML format according to ECMA-376 specification.
6
+
7
+ ECMA-376 Section 18.3.1.2 defines the autoFilter element structure.
8
+ """
9
+
10
+
11
+ class AutoFilterXMLWriter:
12
+ """
13
+ Handles writing autofilter data to XML format for .xlsx files.
14
+
15
+ The AutoFilterXMLWriter class is responsible for creating the XML
16
+ representation of autofilter settings including filter columns,
17
+ filter values, custom filters, color filters, dynamic filters,
18
+ top10 filters, and sort state.
19
+
20
+ Examples:
21
+ >>> writer = AutoFilterXMLWriter()
22
+ >>> xml = writer.format_auto_filter_xml(worksheet.auto_filter)
23
+ """
24
+
25
+ def __init__(self, escape_xml_func):
26
+ """
27
+ Initializes a new instance of the AutoFilterXMLWriter class.
28
+
29
+ Args:
30
+ escape_xml_func: A function to escape XML special characters.
31
+ """
32
+ self._escape_xml = escape_xml_func
33
+
34
+ def format_auto_filter_xml(self, auto_filter):
35
+ """
36
+ Formats auto filter settings as XML according to ECMA-376 specification.
37
+
38
+ ECMA-376 Section 18.3.1.2 defines the autoFilter element structure:
39
+ - autoFilter: Main element with ref attribute (filter range)
40
+ - filterColumn: Represents filter settings for a column (colId attribute)
41
+ - filters: Contains filter values
42
+ - filter: Individual filter value (val attribute)
43
+ - customFilters: Contains custom filter criteria
44
+ - customFilter: Custom filter criterion (operator and val attributes)
45
+ - colorFilter: Color filter settings
46
+ - dynamicFilter: Dynamic filter settings (type and val attributes)
47
+ - top10: Top 10 filter settings (top, percent, and val attributes)
48
+ - sortState: Sort state settings (columnOffset and order attributes)
49
+
50
+ Args:
51
+ auto_filter (AutoFilter): The AutoFilter object to format.
52
+
53
+ Returns:
54
+ str: XML representation of the auto filter settings.
55
+ """
56
+ xml = f' <autoFilter ref="{auto_filter.range}">\n'
57
+
58
+ # Write filter columns
59
+ for col_id, filter_col in sorted(auto_filter.filter_columns.items()):
60
+ xml += f' <filterColumn colId="{col_id}"'
61
+
62
+ # Add hiddenButton attribute if filter button is hidden
63
+ if not filter_col.filter_button:
64
+ xml += ' hiddenButton="1"'
65
+
66
+ # Check if this column has any filters
67
+ has_filters = (len(filter_col.filters) > 0 or
68
+ len(filter_col.custom_filters) > 0 or
69
+ filter_col.color_filter is not None or
70
+ filter_col.dynamic_filter is not None or
71
+ filter_col.top10_filter is not None)
72
+
73
+ if not has_filters:
74
+ xml += '/>\n'
75
+ continue
76
+
77
+ xml += '>\n'
78
+
79
+ # Write filters (value filters)
80
+ if len(filter_col.filters) > 0:
81
+ xml += f' <filters>\n'
82
+ for filter_value in filter_col.filters:
83
+ escaped_value = self._escape_xml(str(filter_value))
84
+ xml += f' <filter val="{escaped_value}"/>\n'
85
+ xml += f' </filters>\n'
86
+
87
+ # Write custom filters
88
+ if len(filter_col.custom_filters) > 0:
89
+ xml += f' <customFilters>\n'
90
+ for operator, value in filter_col.custom_filters:
91
+ escaped_value = self._escape_xml(str(value))
92
+ xml += f' <customFilter operator="{operator}" val="{escaped_value}"/>\n'
93
+ xml += f' </customFilters>\n'
94
+
95
+ # Write color filter
96
+ if filter_col.color_filter is not None:
97
+ color_data = filter_col.color_filter
98
+ cell_color_attr = ' cellColor="1"' if color_data['cell_color'] else ' cellColor="0"'
99
+ xml += f' <colorFilter rgb="{color_data["color"]}"{cell_color_attr}/>\n'
100
+
101
+ # Write dynamic filter
102
+ if filter_col.dynamic_filter is not None:
103
+ dynamic_data = filter_col.dynamic_filter
104
+ xml += f' <dynamicFilter type="{dynamic_data["type"]}"'
105
+ if dynamic_data['value'] is not None:
106
+ escaped_value = self._escape_xml(str(dynamic_data['value']))
107
+ xml += f' val="{escaped_value}"'
108
+ xml += '/>\n'
109
+
110
+ # Write top10 filter
111
+ if filter_col.top10_filter is not None:
112
+ top10_data = filter_col.top10_filter
113
+ top_attr = ' top="1"' if top10_data['top'] else ' top="0"'
114
+ percent_attr = ' percent="1"' if top10_data['percent'] else ' percent="0"'
115
+ xml += f' <top10{top_attr}{percent_attr} val="{top10_data["val"]}"/>\n'
116
+
117
+ xml += f' </filterColumn>\n'
118
+
119
+ # Write sort state (ECMA-376 Section 18.3.1.92)
120
+ if auto_filter.sort_state is not None and auto_filter.range is not None:
121
+ sort_data = auto_filter.sort_state
122
+ # Calculate sortState ref and sortCondition ref from autoFilter range
123
+ sort_state_ref, sort_condition_ref = self._calculate_sort_refs(
124
+ auto_filter.range, sort_data.get('column_index', 0)
125
+ )
126
+ xml += f' <sortState ref="{sort_state_ref}">\n'
127
+ # Build sortCondition with ref attribute
128
+ descending_attr = ' descending="1"' if sort_data.get('descending', False) else ''
129
+ xml += f' <sortCondition ref="{sort_condition_ref}"{descending_attr}/>\n'
130
+ xml += f' </sortState>\n'
131
+
132
+ xml += f' </autoFilter>\n'
133
+
134
+ return xml
135
+
136
+ def _calculate_sort_refs(self, filter_range, column_index):
137
+ """
138
+ Calculates sortState ref and sortCondition ref from autoFilter range.
139
+
140
+ According to ECMA-376:
141
+ - sortState ref: The data range being sorted (excludes header row)
142
+ - sortCondition ref: The specific column range being sorted
143
+
144
+ Args:
145
+ filter_range (str): The autoFilter range (e.g., "A1:D10")
146
+ column_index (int): Zero-based column index within the filter range
147
+
148
+ Returns:
149
+ tuple: (sort_state_ref, sort_condition_ref)
150
+ """
151
+ from .cells import Cells
152
+
153
+ # Parse the filter range
154
+ if ':' in filter_range:
155
+ start_ref, end_ref = filter_range.split(':')
156
+ else:
157
+ start_ref = end_ref = filter_range
158
+
159
+ start_row, start_col = Cells.coordinate_from_string(start_ref)
160
+ end_row, end_col = Cells.coordinate_from_string(end_ref)
161
+
162
+ # sortState ref excludes header row (start from row after header)
163
+ data_start_row = start_row + 1
164
+ start_col_letter = Cells.column_letter_from_index(start_col)
165
+ end_col_letter = Cells.column_letter_from_index(end_col)
166
+ sort_state_ref = f"{start_col_letter}{data_start_row}:{end_col_letter}{end_row}"
167
+
168
+ # sortCondition ref is for the specific column being sorted
169
+ sort_col = start_col + column_index
170
+ sort_col_letter = Cells.column_letter_from_index(sort_col)
171
+ sort_condition_ref = f"{sort_col_letter}{data_start_row}:{sort_col_letter}{end_row}"
172
+
173
+ return sort_state_ref, sort_condition_ref
@@ -0,0 +1,215 @@
1
+ """
2
+ Aspose.Cells for Python - XML Conditional Formatting Loader Module
3
+
4
+ This module provides the ConditionalFormatXMLLoader class which handles loading
5
+ conditional formatting data from XML format according to ECMA-376 specification.
6
+
7
+ ECMA-376 Section 18.3.1.18 defines the conditionalFormatting element structure.
8
+ """
9
+
10
+
11
+ class ConditionalFormatXMLLoader:
12
+ """
13
+ Handles loading conditional formatting data from XML format for .xlsx files.
14
+
15
+ The ConditionalFormatXMLLoader class is responsible for parsing the XML
16
+ representation of conditional formatting rules including cell value rules,
17
+ text rules, date rules, formula rules, color scales, data bars, icon sets,
18
+ and more.
19
+
20
+ Examples:
21
+ >>> loader = ConditionalFormatXMLLoader(namespaces, workbook)
22
+ >>> loader.load_conditional_formatting(worksheet, worksheet_root)
23
+ """
24
+
25
+ def __init__(self, namespaces, workbook):
26
+ """
27
+ Initializes a new instance of the ConditionalFormatXMLLoader class.
28
+
29
+ Args:
30
+ namespaces: XML namespaces dictionary for parsing.
31
+ workbook: The workbook instance for accessing dxf styles.
32
+ """
33
+ self.ns = namespaces
34
+ self.workbook = workbook
35
+
36
+ def load_conditional_formatting(self, worksheet, worksheet_root):
37
+ """
38
+ Loads conditional formatting from worksheet XML according to ECMA-376 specification.
39
+
40
+ ECMA-376 Section 18.3.1.18 defines the conditionalFormatting element and its children:
41
+ - conditionalFormatting: Main element with sqref attribute
42
+ - cfRule: Individual conditional formatting rule
43
+
44
+ Args:
45
+ worksheet (Worksheet): The worksheet object to load data into.
46
+ worksheet_root: The XML root element of the worksheet.
47
+ """
48
+ from .conditional_format import ConditionalFormat
49
+
50
+ # Find all conditionalFormatting elements
51
+ cf_elements = worksheet_root.findall('.//main:conditionalFormatting', namespaces=self.ns)
52
+
53
+ for cf_elem in cf_elements:
54
+ sqref = cf_elem.get('sqref')
55
+ if not sqref:
56
+ continue
57
+
58
+ # Load each cfRule within this conditionalFormatting element
59
+ for rule_elem in cf_elem.findall('main:cfRule', namespaces=self.ns):
60
+ cf = ConditionalFormat()
61
+ cf._range = sqref
62
+
63
+ # Parse rule attributes
64
+ rule_type = rule_elem.get('type')
65
+ cf._type = rule_type
66
+
67
+ # Parse priority
68
+ priority = rule_elem.get('priority')
69
+ if priority:
70
+ cf._priority = int(priority)
71
+
72
+ # Parse stopIfTrue
73
+ stop_if_true = rule_elem.get('stopIfTrue')
74
+ cf._stop_if_true = stop_if_true == '1'
75
+
76
+ # Parse dxfId for loading formatting later
77
+ dxf_id = rule_elem.get('dxfId')
78
+ if dxf_id:
79
+ cf._dxf_id = int(dxf_id)
80
+
81
+ # Parse operator for cellIs type
82
+ operator = rule_elem.get('operator')
83
+ if operator:
84
+ cf._operator = operator
85
+
86
+ # Parse text attribute for text-based rules
87
+ text = rule_elem.get('text')
88
+ if text:
89
+ cf._formula1 = text
90
+
91
+ # Parse timePeriod attribute
92
+ time_period = rule_elem.get('timePeriod')
93
+ if time_period:
94
+ cf._operator = time_period
95
+
96
+ # Parse top10 attributes
97
+ if rule_type == 'top10':
98
+ bottom = rule_elem.get('bottom')
99
+ cf._top = bottom != '1'
100
+ percent = rule_elem.get('percent')
101
+ cf._percent = percent == '1'
102
+ rank = rule_elem.get('rank')
103
+ if rank:
104
+ cf._rank = int(rank)
105
+
106
+ # Parse aboveAverage attributes
107
+ if rule_type == 'aboveAverage':
108
+ above_average = rule_elem.get('aboveAverage', '1')
109
+ cf._above = above_average != '0'
110
+ std_dev = rule_elem.get('stdDev')
111
+ if std_dev:
112
+ cf._std_dev = int(std_dev)
113
+
114
+ # Parse formula elements based on rule type
115
+ formula_elems = rule_elem.findall('main:formula', namespaces=self.ns)
116
+ if rule_type == 'expression':
117
+ # Expression rules store formula in _formula property
118
+ if len(formula_elems) > 0 and formula_elems[0].text:
119
+ cf._formula = formula_elems[0].text
120
+ elif rule_type == 'cellIs':
121
+ # Cell value rules use _formula1 and _formula2
122
+ if len(formula_elems) > 0 and formula_elems[0].text:
123
+ cf._formula1 = formula_elems[0].text
124
+ if len(formula_elems) > 1 and formula_elems[1].text:
125
+ cf._formula2 = formula_elems[1].text
126
+ elif rule_type in ('containsText', 'notContainsText', 'beginsWith', 'endsWith'):
127
+ # Text rules: the text attribute is already parsed above
128
+ # The formula element contains the Excel formula, not the text value
129
+ # We use the text attribute value which was set earlier
130
+ pass
131
+ else:
132
+ # Default: store in _formula1
133
+ if len(formula_elems) > 0 and formula_elems[0].text:
134
+ cf._formula1 = formula_elems[0].text
135
+ if len(formula_elems) > 1 and formula_elems[1].text:
136
+ cf._formula2 = formula_elems[1].text
137
+
138
+ # Parse colorScale element
139
+ color_scale_elem = rule_elem.find('main:colorScale', namespaces=self.ns)
140
+ if color_scale_elem is not None:
141
+ self._load_color_scale(cf, color_scale_elem)
142
+
143
+ # Parse dataBar element
144
+ data_bar_elem = rule_elem.find('main:dataBar', namespaces=self.ns)
145
+ if data_bar_elem is not None:
146
+ self._load_data_bar(cf, data_bar_elem)
147
+
148
+ # Parse iconSet element
149
+ icon_set_elem = rule_elem.find('main:iconSet', namespaces=self.ns)
150
+ if icon_set_elem is not None:
151
+ self._load_icon_set(cf, icon_set_elem)
152
+
153
+ # Add to worksheet's conditional formats
154
+ worksheet.conditional_formats._formats.append(cf)
155
+
156
+ # Load dxf formatting and apply to conditional formats
157
+ self._apply_dxf_to_conditional_formats(worksheet)
158
+
159
+ def _load_color_scale(self, cf, color_scale_elem):
160
+ """Loads colorScale element data into conditional format."""
161
+ # Get cfvo elements to determine if 2-color or 3-color
162
+ cfvo_elems = color_scale_elem.findall('main:cfvo', namespaces=self.ns)
163
+ cf._color_scale_type = '3-color' if len(cfvo_elems) >= 3 else '2-color'
164
+
165
+ # Get color elements
166
+ color_elems = color_scale_elem.findall('main:color', namespaces=self.ns)
167
+ if len(color_elems) >= 1:
168
+ cf._min_color = color_elems[0].get('rgb')
169
+ if len(color_elems) >= 3:
170
+ cf._mid_color = color_elems[1].get('rgb')
171
+ cf._max_color = color_elems[2].get('rgb')
172
+ elif len(color_elems) >= 2:
173
+ cf._max_color = color_elems[1].get('rgb')
174
+
175
+ def _load_data_bar(self, cf, data_bar_elem):
176
+ """Loads dataBar element data into conditional format."""
177
+ # Get color element
178
+ color_elem = data_bar_elem.find('main:color', namespaces=self.ns)
179
+ if color_elem is not None:
180
+ cf._bar_color = color_elem.get('rgb')
181
+
182
+ def _load_icon_set(self, cf, icon_set_elem):
183
+ """Loads iconSet element data into conditional format."""
184
+ cf._icon_set_type = icon_set_elem.get('iconSet', '3TrafficLights1')
185
+ cf._reverse_icons = icon_set_elem.get('reverse') == '1'
186
+ cf._show_icon_only = icon_set_elem.get('showValue') == '0'
187
+
188
+ def _apply_dxf_to_conditional_formats(self, worksheet):
189
+ """Applies differential formatting (dxf) to conditional formats."""
190
+ # dxf styles are stored in workbook._dxf_styles after loading styles.xml
191
+ if not hasattr(self.workbook, '_dxf_styles') or not self.workbook._dxf_styles:
192
+ return
193
+
194
+ for cf in worksheet.conditional_formats:
195
+ if hasattr(cf, '_dxf_id') and cf._dxf_id is not None:
196
+ if cf._dxf_id < len(self.workbook._dxf_styles):
197
+ dxf_data = self.workbook._dxf_styles[cf._dxf_id]
198
+ self._apply_dxf_data_to_cf(cf, dxf_data)
199
+
200
+ def _apply_dxf_data_to_cf(self, cf, dxf_data):
201
+ """Applies dxf data to a conditional format."""
202
+ if 'font' in dxf_data:
203
+ font = dxf_data['font']
204
+ cf._font.bold = font.get('bold', False)
205
+ cf._font.italic = font.get('italic', False)
206
+ cf._font.underline = font.get('underline', False)
207
+ cf._font.strikethrough = font.get('strikethrough', False)
208
+ if 'color' in font:
209
+ cf._font.color = font['color']
210
+
211
+ if 'fill' in dxf_data:
212
+ fill = dxf_data['fill']
213
+ cf._fill.pattern_type = fill.get('pattern_type', 'solid')
214
+ cf._fill.foreground_color = fill.get('fg_color', 'FFFFFFFF')
215
+ cf._fill.background_color = fill.get('bg_color', 'FFFFFFFF')