aspose-cells-foss 25.12.1__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 (53) hide show
  1. aspose/__init__.py +14 -0
  2. aspose/cells/__init__.py +31 -0
  3. aspose/cells/cell.py +350 -0
  4. aspose/cells/constants.py +44 -0
  5. aspose/cells/converters/__init__.py +13 -0
  6. aspose/cells/converters/csv_converter.py +55 -0
  7. aspose/cells/converters/json_converter.py +46 -0
  8. aspose/cells/converters/markdown_converter.py +453 -0
  9. aspose/cells/drawing/__init__.py +17 -0
  10. aspose/cells/drawing/anchor.py +172 -0
  11. aspose/cells/drawing/collection.py +233 -0
  12. aspose/cells/drawing/image.py +338 -0
  13. aspose/cells/formats.py +80 -0
  14. aspose/cells/formula/__init__.py +10 -0
  15. aspose/cells/formula/evaluator.py +360 -0
  16. aspose/cells/formula/functions.py +433 -0
  17. aspose/cells/formula/tokenizer.py +340 -0
  18. aspose/cells/io/__init__.py +27 -0
  19. aspose/cells/io/csv/__init__.py +8 -0
  20. aspose/cells/io/csv/reader.py +88 -0
  21. aspose/cells/io/csv/writer.py +98 -0
  22. aspose/cells/io/factory.py +138 -0
  23. aspose/cells/io/interfaces.py +48 -0
  24. aspose/cells/io/json/__init__.py +8 -0
  25. aspose/cells/io/json/reader.py +126 -0
  26. aspose/cells/io/json/writer.py +119 -0
  27. aspose/cells/io/md/__init__.py +8 -0
  28. aspose/cells/io/md/reader.py +161 -0
  29. aspose/cells/io/md/writer.py +334 -0
  30. aspose/cells/io/models.py +64 -0
  31. aspose/cells/io/xlsx/__init__.py +9 -0
  32. aspose/cells/io/xlsx/constants.py +312 -0
  33. aspose/cells/io/xlsx/image_writer.py +311 -0
  34. aspose/cells/io/xlsx/reader.py +284 -0
  35. aspose/cells/io/xlsx/writer.py +931 -0
  36. aspose/cells/plugins/__init__.py +6 -0
  37. aspose/cells/plugins/docling_backend/__init__.py +7 -0
  38. aspose/cells/plugins/docling_backend/backend.py +535 -0
  39. aspose/cells/plugins/markitdown_plugin/__init__.py +15 -0
  40. aspose/cells/plugins/markitdown_plugin/plugin.py +128 -0
  41. aspose/cells/range.py +210 -0
  42. aspose/cells/style.py +287 -0
  43. aspose/cells/utils/__init__.py +54 -0
  44. aspose/cells/utils/coordinates.py +68 -0
  45. aspose/cells/utils/exceptions.py +43 -0
  46. aspose/cells/utils/validation.py +102 -0
  47. aspose/cells/workbook.py +352 -0
  48. aspose/cells/worksheet.py +670 -0
  49. aspose_cells_foss-25.12.1.dist-info/METADATA +189 -0
  50. aspose_cells_foss-25.12.1.dist-info/RECORD +53 -0
  51. aspose_cells_foss-25.12.1.dist-info/WHEEL +5 -0
  52. aspose_cells_foss-25.12.1.dist-info/entry_points.txt +2 -0
  53. aspose_cells_foss-25.12.1.dist-info/top_level.txt +1 -0
aspose/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ Aspose.Cells.Python - Modern Excel processing library
3
+
4
+ Open source Excel processing library from Aspose.org providing Pythonic API
5
+ for Excel file manipulation with comprehensive data structures, conversion
6
+ capabilities, and extensible plugin ecosystem.
7
+
8
+ Part of the Aspose.org open source ecosystem.
9
+ """
10
+
11
+ from .cells import Workbook, FileFormat
12
+
13
+ __version__ = "1.0.0"
14
+ __all__ = ["Workbook", "FileFormat"]
@@ -0,0 +1,31 @@
1
+ """
2
+ Core Excel processing module providing workbook, worksheet, and cell management.
3
+
4
+ Part of Aspose.Cells.Python - an open source Excel processing library from Aspose.org.
5
+ """
6
+
7
+ from .workbook import Workbook
8
+ from .worksheet import Worksheet
9
+ from .cell import Cell
10
+ from .range import Range
11
+ from .formats import FileFormat, ConversionOptions, CellValue
12
+ from .style import Style, Font, Fill
13
+ from .drawing import Image, ImageFormat, Anchor, AnchorType, ImageCollection
14
+
15
+ __all__ = [
16
+ "Workbook",
17
+ "Worksheet",
18
+ "Cell",
19
+ "Range",
20
+ "FileFormat",
21
+ "ConversionOptions",
22
+ "CellValue",
23
+ "Style",
24
+ "Font",
25
+ "Fill",
26
+ "Image",
27
+ "ImageFormat",
28
+ "Anchor",
29
+ "AnchorType",
30
+ "ImageCollection"
31
+ ]
aspose/cells/cell.py ADDED
@@ -0,0 +1,350 @@
1
+ """
2
+ Cell implementation with value management and styling capabilities.
3
+ """
4
+
5
+ from typing import Optional, TYPE_CHECKING
6
+ from datetime import datetime
7
+
8
+ from .formats import CellValue
9
+ from .style import Style, Font, Fill, Border, Alignment
10
+ from .utils import (
11
+ infer_data_type,
12
+ convert_value,
13
+ tuple_to_coordinate,
14
+ CellValueError
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from .worksheet import Worksheet
19
+
20
+
21
+ class Cell:
22
+ """Individual Excel cell with value, type, and styling management."""
23
+
24
+ def __init__(self, worksheet: 'Worksheet', row: int, column: int, value: CellValue = None):
25
+ # Validate input parameters
26
+ if not isinstance(row, int) or row < 1:
27
+ raise ValueError(f"Row must be a positive integer, got: {row}")
28
+ if not isinstance(column, int) or column < 1:
29
+ raise ValueError(f"Column must be a positive integer, got: {column}")
30
+
31
+ self._worksheet = worksheet
32
+ self._row = row
33
+ self._column = column
34
+ self._value = value
35
+ self._data_type: Optional[str] = None
36
+ self._style: Optional[Style] = None
37
+ self._number_format: str = "General"
38
+ self._hyperlink: Optional[str] = None
39
+ self._comment: Optional[str] = None
40
+ self._formula: Optional[str] = None # Store original formula
41
+ self._calculated_value: Optional[CellValue] = None # Store calculated result
42
+
43
+ if value is not None:
44
+ self.value = value
45
+
46
+ @property
47
+ def row(self) -> int:
48
+ """Row number (1-based)."""
49
+ return self._row
50
+
51
+ @property
52
+ def column(self) -> int:
53
+ """Column number (1-based)."""
54
+ return self._column
55
+
56
+ @property
57
+ def coordinate(self) -> str:
58
+ """Excel coordinate (e.g., 'A1')."""
59
+ return tuple_to_coordinate(self._row, self._column)
60
+
61
+ @property
62
+ def worksheet(self) -> 'Worksheet':
63
+ """Parent worksheet."""
64
+ return self._worksheet
65
+
66
+ @property
67
+ def value(self) -> CellValue:
68
+ """Cell value."""
69
+ return self._value
70
+
71
+ @value.setter
72
+ def value(self, val: CellValue):
73
+ """Set cell value with automatic type inference."""
74
+ self._value = val
75
+ self._data_type = infer_data_type(val)
76
+
77
+ # Update worksheet bounds
78
+ if hasattr(self._worksheet, '_update_bounds'):
79
+ self._worksheet._update_bounds(self._row, self._column)
80
+
81
+ @property
82
+ def data_type(self) -> Optional[str]:
83
+ """Inferred data type."""
84
+ return self._data_type
85
+
86
+ @property
87
+ def number_format(self) -> str:
88
+ """Number format string."""
89
+ return self._number_format
90
+
91
+ @number_format.setter
92
+ def number_format(self, value: str):
93
+ """Set number format."""
94
+ self._number_format = value
95
+
96
+ @property
97
+ def font(self) -> Font:
98
+ """Font styling (creates style if needed)."""
99
+ if self._style is None:
100
+ self._style = Style()
101
+ return self._style.font
102
+
103
+ @property
104
+ def fill(self) -> Fill:
105
+ """Fill styling (creates style if needed)."""
106
+ if self._style is None:
107
+ self._style = Style()
108
+ return self._style.fill
109
+
110
+ @property
111
+ def border(self) -> Border:
112
+ """Border styling (creates style if needed)."""
113
+ if self._style is None:
114
+ self._style = Style()
115
+ return self._style.border
116
+
117
+ @property
118
+ def alignment(self) -> Alignment:
119
+ """Alignment styling (creates style if needed)."""
120
+ if self._style is None:
121
+ self._style = Style()
122
+ return self._style.alignment
123
+
124
+ @property
125
+ def style(self) -> Style:
126
+ """Complete style object (creates if needed)."""
127
+ if self._style is None:
128
+ self._style = Style()
129
+ return self._style
130
+
131
+ @style.setter
132
+ def style(self, value: Style):
133
+ """Set complete style."""
134
+ self._style = value
135
+
136
+ @property
137
+ def hyperlink(self) -> Optional[str]:
138
+ """Hyperlink URL."""
139
+ return self._hyperlink
140
+
141
+ @hyperlink.setter
142
+ def hyperlink(self, value: Optional[str]):
143
+ """Set hyperlink URL."""
144
+ self._hyperlink = value
145
+
146
+ @property
147
+ def comment(self) -> Optional[str]:
148
+ """Cell comment text."""
149
+ return self._comment
150
+
151
+ @comment.setter
152
+ def comment(self, value: Optional[str]):
153
+ """Set cell comment."""
154
+ self._comment = value
155
+
156
+ def as_str(self, default: str = "") -> str:
157
+ """Convert value to string."""
158
+ return convert_value(self._value, 'string', default)
159
+
160
+ def as_int(self, default: int = 0) -> int:
161
+ """Convert value to integer."""
162
+ return convert_value(self._value, 'int', default)
163
+
164
+ def as_float(self, default: float = 0.0) -> float:
165
+ """Convert value to float."""
166
+ return convert_value(self._value, 'float', default)
167
+
168
+ def as_bool(self, default: bool = False) -> bool:
169
+ """Convert value to boolean."""
170
+ return convert_value(self._value, 'bool', default)
171
+
172
+ def is_numeric(self) -> bool:
173
+ """Check if cell contains numeric value."""
174
+ return self._data_type == 'number'
175
+
176
+ def is_date(self) -> bool:
177
+ """Check if cell contains date value."""
178
+ return self._data_type == 'date' or isinstance(self._value, datetime)
179
+
180
+ def is_formula(self) -> bool:
181
+ """Check if cell contains formula."""
182
+ return self._data_type == 'formula'
183
+
184
+ def is_empty(self) -> bool:
185
+ """Check if cell is empty."""
186
+ return self._value is None or self._data_type == 'empty'
187
+
188
+ def clear(self):
189
+ """Clear cell value and formatting."""
190
+ self._value = None
191
+ self._data_type = 'empty'
192
+ self._style = None
193
+ self._number_format = "General"
194
+ self._hyperlink = None
195
+ self._comment = None
196
+ self._formula = None
197
+ self._calculated_value = None
198
+
199
+ def set_formula(self, formula: str, calculated_value: CellValue = None):
200
+ """Set cell formula (handles = prefix automatically)."""
201
+ if not formula.startswith('='):
202
+ formula = '=' + formula
203
+ self._formula = formula
204
+ self.value = formula
205
+ self._data_type = 'formula'
206
+
207
+ # Always ensure calculated value is set
208
+ if calculated_value is not None:
209
+ self._calculated_value = calculated_value
210
+ else:
211
+ # Always recalculate when setting a new formula
212
+ self._calculated_value = self._get_basic_formula_result(formula)
213
+
214
+ @property
215
+ def formula(self) -> Optional[str]:
216
+ """Get the original formula if this is a formula cell."""
217
+ return self._formula
218
+
219
+ @property
220
+ def calculated_value(self) -> CellValue:
221
+ """Get the calculated result of a formula, or the cell value if not a formula."""
222
+ if self.is_formula() and self._calculated_value is not None:
223
+ return self._calculated_value
224
+ return self._value
225
+
226
+ @property
227
+ def display_value(self) -> str:
228
+ """Get the display value (what should be shown to users)."""
229
+ if self.is_formula():
230
+ # For formulas, prefer calculated value over raw formula
231
+ if self._calculated_value is not None:
232
+ return str(self._calculated_value)
233
+ else:
234
+ return str(self._value) # Fallback to formula text
235
+ return str(self._value) if self._value is not None else ""
236
+
237
+ def get_value(self, mode: str = 'display') -> CellValue:
238
+ """
239
+ Get cell value in different modes.
240
+
241
+ Args:
242
+ mode: 'display' (calculated/display value), 'formula' (raw formula), 'raw' (raw value)
243
+
244
+ Returns:
245
+ Cell value based on the requested mode
246
+ """
247
+ if mode == 'formula' and self.is_formula():
248
+ return self._formula or self._value
249
+ elif mode == 'display':
250
+ return self.calculated_value
251
+ elif mode == 'raw':
252
+ return self._value
253
+ else:
254
+ return self.calculated_value # Default to display mode
255
+
256
+ def has_hyperlink(self) -> bool:
257
+ """Check if cell has a hyperlink."""
258
+ return self._hyperlink is not None and self._hyperlink.strip() != ""
259
+
260
+ def get_markdown_link(self, text: Optional[str] = None) -> str:
261
+ """Get markdown formatted link if cell has hyperlink."""
262
+ if not self.has_hyperlink():
263
+ return text or self.display_value
264
+
265
+ link_text = text or self.display_value
266
+ if not link_text:
267
+ link_text = self._hyperlink
268
+
269
+ return f"[{link_text}]({self._hyperlink})"
270
+
271
+ def set_hyperlink(self, url: str, display_text: Optional[str] = None):
272
+ """Set hyperlink with optional display text."""
273
+ self._hyperlink = url
274
+ if display_text is not None:
275
+ self.value = display_text
276
+
277
+ def copy_from(self, other: 'Cell'):
278
+ """Copy value and style from another cell."""
279
+ self.value = other.value
280
+ if other._style:
281
+ self._style = other._style.copy()
282
+ self._number_format = other._number_format
283
+ self._hyperlink = other._hyperlink
284
+ self._comment = other._comment
285
+ self._formula = other._formula
286
+ self._calculated_value = other._calculated_value
287
+
288
+ def __str__(self) -> str:
289
+ """String representation."""
290
+ return f"Cell({self.coordinate}={self._value})"
291
+
292
+ def _get_basic_formula_result(self, formula: str) -> CellValue:
293
+ """Get calculated result using the formula engine."""
294
+ try:
295
+ from .formula import FormulaEvaluator
296
+ evaluator = FormulaEvaluator(self._worksheet)
297
+ return evaluator.evaluate(formula, self.coordinate)
298
+ except Exception:
299
+ # Fallback to simple evaluation
300
+ return self._simple_formula_fallback(formula)
301
+
302
+ def _simple_formula_fallback(self, formula: str) -> CellValue:
303
+ """Simple fallback for basic formulas when engine fails."""
304
+ formula_upper = formula.upper().strip()
305
+
306
+ # Remove = prefix
307
+ if formula_upper.startswith('='):
308
+ formula_upper = formula_upper[1:]
309
+
310
+ # Handle very basic cases
311
+ if formula_upper.startswith(('SUM', 'COUNT', 'AVERAGE', 'MAX', 'MIN')):
312
+ return 0
313
+ elif formula_upper.startswith(('NOW', 'TODAY')):
314
+ return "2024-01-01"
315
+ elif formula_upper.startswith('TRUE'):
316
+ return True
317
+ elif formula_upper.startswith('FALSE'):
318
+ return False
319
+ elif all(c in '0123456789+-*/.() ' for c in formula_upper):
320
+ try:
321
+ # Use safe expression evaluation instead of eval
322
+ import ast
323
+ node = ast.parse(formula_upper, mode='eval')
324
+ if self._is_safe_expression(node):
325
+ return eval(compile(node, '<string>', 'eval'))
326
+ else:
327
+ return 0
328
+ except (ValueError, SyntaxError, TypeError):
329
+ return 0
330
+ else:
331
+ return 0
332
+
333
+ def _is_safe_expression(self, node) -> bool:
334
+ """Check if AST node contains only safe mathematical operations."""
335
+ import ast
336
+
337
+ allowed_nodes = (
338
+ ast.Expression, ast.BinOp, ast.UnaryOp, ast.Constant, ast.Num,
339
+ ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod, ast.Pow,
340
+ ast.USub, ast.UAdd
341
+ )
342
+
343
+ for child in ast.walk(node):
344
+ if not isinstance(child, allowed_nodes):
345
+ return False
346
+ return True
347
+
348
+ def __repr__(self) -> str:
349
+ """Debug representation."""
350
+ return f"Cell({self.coordinate}, row={self._row}, col={self._column}, value={self._value!r}, type={self._data_type})"
@@ -0,0 +1,44 @@
1
+ """
2
+ Constants for Excel file format specifications and limits.
3
+ """
4
+
5
+ # Excel worksheet limits
6
+ MAX_SHEET_NAME_LENGTH = 31
7
+ MAX_ROWS = 1048576 # Excel 2007+ limit
8
+ MAX_COLUMNS = 16384 # Excel 2007+ limit (XFD column)
9
+ MAX_CELL_CONTENT_LENGTH = 32767 # Maximum characters in a cell
10
+
11
+ # Column width limits
12
+ MIN_COLUMN_WIDTH = 0.0
13
+ MAX_COLUMN_WIDTH = 255.0
14
+ DEFAULT_COLUMN_WIDTH = 8.43
15
+
16
+ # Row height limits
17
+ MIN_ROW_HEIGHT = 0.0
18
+ MAX_ROW_HEIGHT = 409.5
19
+ DEFAULT_ROW_HEIGHT = 15.0
20
+
21
+ # Style limits
22
+ MAX_FONT_SIZE = 409
23
+ MIN_FONT_SIZE = 1
24
+ DEFAULT_FONT_SIZE = 11
25
+
26
+ # File format extensions
27
+ EXCEL_EXTENSIONS = {'.xlsx', '.xls', '.xlsm', '.xlsb'}
28
+ CSV_EXTENSIONS = {'.csv', '.tsv'}
29
+ TEXT_EXTENSIONS = {'.txt', '.tab'}
30
+
31
+ # Invalid characters for sheet names
32
+ INVALID_SHEET_NAME_CHARS = ['\\', '/', '?', '*', '[', ']', ':']
33
+
34
+ # Excel date constants
35
+ EXCEL_EPOCH_DATE = "1900-01-01"
36
+ EXCEL_EPOCH_DATETIME = "1900-01-01T00:00:00"
37
+
38
+ # Cell reference patterns
39
+ CELL_REF_PATTERN = r'^[A-Z]{1,3}[1-9]\d*$'
40
+ RANGE_REF_PATTERN = r'^[A-Z]{1,3}[1-9]\d*:[A-Z]{1,3}[1-9]\d*$'
41
+
42
+ # Default values
43
+ DEFAULT_SHEET_NAME = "Sheet"
44
+ DEFAULT_WORKBOOK_NAME = "Workbook"
@@ -0,0 +1,13 @@
1
+ """
2
+ Data conversion modules for exporting Excel data to various formats.
3
+ """
4
+
5
+ from .json_converter import JsonConverter
6
+ from .csv_converter import CsvConverter
7
+ from .markdown_converter import MarkdownConverter
8
+
9
+ __all__ = [
10
+ "JsonConverter",
11
+ "CsvConverter",
12
+ "MarkdownConverter"
13
+ ]
@@ -0,0 +1,55 @@
1
+ """
2
+ CSV converter for exporting Excel data to CSV format.
3
+ """
4
+
5
+ import csv
6
+ import io
7
+ from typing import Optional, TYPE_CHECKING
8
+ from ..io.csv import CsvWriter
9
+
10
+ if TYPE_CHECKING:
11
+ from ..workbook import Workbook
12
+
13
+
14
+ class CsvConverter:
15
+ """Convert Excel workbook data to CSV format."""
16
+
17
+ def __init__(self):
18
+ self._writer = CsvWriter()
19
+
20
+ def convert_workbook(self, workbook: 'Workbook', **kwargs) -> str:
21
+ """Convert active worksheet to CSV string."""
22
+ sheet_name = kwargs.get('sheet_name')
23
+ delimiter = kwargs.get('delimiter', ',')
24
+ quotechar = kwargs.get('quotechar', '"')
25
+
26
+ # Get target worksheet
27
+ if sheet_name and sheet_name in workbook._worksheets:
28
+ worksheet = workbook._worksheets[sheet_name]
29
+ else:
30
+ worksheet = workbook.active
31
+
32
+ if not worksheet or not worksheet._cells:
33
+ return ""
34
+
35
+ # Convert worksheet to data
36
+ data = self._writer._worksheet_to_data(worksheet)
37
+
38
+ if not data:
39
+ return ""
40
+
41
+ # Create CSV in memory
42
+ output = io.StringIO()
43
+ writer = csv.writer(output, delimiter=delimiter, quotechar=quotechar,
44
+ quoting=csv.QUOTE_MINIMAL)
45
+
46
+ # Write data rows
47
+ for row_data in data:
48
+ formatted_row = []
49
+ for cell in row_data:
50
+ formatted_row.append(self._writer._format_cell_value(cell))
51
+ writer.writerow(formatted_row)
52
+
53
+ csv_content = output.getvalue()
54
+ output.close()
55
+ return csv_content
@@ -0,0 +1,46 @@
1
+ """
2
+ JSON converter for exporting Excel data to JSON format.
3
+ """
4
+
5
+ import json
6
+ from typing import Dict, List, Optional, Union, TYPE_CHECKING
7
+ from ..io.json import JsonWriter
8
+
9
+ if TYPE_CHECKING:
10
+ from ..workbook import Workbook
11
+
12
+
13
+ class JsonConverter:
14
+ """Convert Excel workbook data to JSON format."""
15
+
16
+ def __init__(self):
17
+ self._writer = JsonWriter()
18
+
19
+ def convert_workbook(self, workbook: 'Workbook', **kwargs) -> str:
20
+ """Convert entire workbook to JSON string."""
21
+ pretty_print = kwargs.get('pretty_print', False)
22
+ include_empty_cells = kwargs.get('include_empty_cells', False)
23
+ all_sheets = kwargs.get('all_sheets', False)
24
+ sheet_name = kwargs.get('sheet_name')
25
+
26
+ if sheet_name:
27
+ # Export specific sheet
28
+ if sheet_name in workbook._worksheets:
29
+ worksheet = workbook._worksheets[sheet_name]
30
+ result = self._writer._convert_worksheet(worksheet, include_empty_cells)
31
+ else:
32
+ result = []
33
+ elif all_sheets:
34
+ # Export all sheets with sheet names as keys
35
+ result = {}
36
+ for name, worksheet in workbook._worksheets.items():
37
+ sheet_data = self._writer._convert_worksheet(worksheet, include_empty_cells)
38
+ result[name] = sheet_data
39
+ else:
40
+ # Export only active sheet as simple list
41
+ result = self._writer._convert_worksheet(workbook.active, include_empty_cells)
42
+
43
+ if pretty_print:
44
+ return json.dumps(result, indent=2, ensure_ascii=False)
45
+ else:
46
+ return json.dumps(result, ensure_ascii=False)