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,337 @@
1
+ """
2
+ Minimal CFB (Compound File Binary) Writer
3
+
4
+ This module provides a minimal CFB file writer for creating encrypted XLSX files.
5
+ CFB format is also known as OLE (Object Linking and Embedding) format.
6
+
7
+ Based on MS-CFB specification.
8
+ """
9
+
10
+ import struct
11
+ import io
12
+
13
+
14
+ class MinimalCFBWriter:
15
+ """
16
+ Minimal CFB file writer for encrypted Office documents.
17
+
18
+ This implementation creates a simple CFB file with just two streams:
19
+ - EncryptionInfo
20
+ - EncryptedPackage
21
+ """
22
+
23
+ # CFB Constants
24
+ HEADER_SIGNATURE = 0xE11AB1A1E011CFD0
25
+ MINOR_VERSION = 0x003E
26
+ MAJOR_VERSION_3 = 0x0003 # Version 3 (512-byte sectors)
27
+ MAJOR_VERSION_4 = 0x0004 # Version 4 (4096-byte sectors)
28
+ BYTE_ORDER = 0xFFFE
29
+ SECTOR_SIZE_V3 = 512
30
+ SECTOR_SIZE_V4 = 4096
31
+ MINI_SECTOR_SIZE = 64
32
+ DIFSECT = 0xFFFFFFFC
33
+ FATSECT = 0xFFFFFFFD
34
+ ENDOFCHAIN = 0xFFFFFFFE
35
+ FREESECT = 0xFFFFFFFF
36
+
37
+ def __init__(self, sector_size=512):
38
+ """
39
+ Initialize CFB writer.
40
+
41
+ Args:
42
+ sector_size: Sector size (512 or 4096 bytes)
43
+ """
44
+ self.sector_size = sector_size
45
+ self.major_version = self.MAJOR_VERSION_3 if sector_size == 512 else self.MAJOR_VERSION_4
46
+ self.streams = []
47
+
48
+ def add_stream(self, name, data):
49
+ """
50
+ Add a stream to the CFB file.
51
+
52
+ Args:
53
+ name: Stream name
54
+ data: Stream data bytes
55
+ """
56
+ self.streams.append((name, data))
57
+
58
+ def write(self, file_path):
59
+ """
60
+ Write CFB file.
61
+
62
+ Args:
63
+ file_path: Output file path
64
+ """
65
+ # Build CFB structure
66
+ header = self._build_header()
67
+ fat = self._build_fat()
68
+ directory = self._build_directory()
69
+ data_sectors = self._build_data_sectors()
70
+
71
+ # Write to file
72
+ with open(file_path, 'wb') as f:
73
+ f.write(header)
74
+ f.write(fat)
75
+ f.write(directory)
76
+ f.write(data_sectors)
77
+
78
+ def _build_header(self):
79
+ """Build CFB header (512 bytes)."""
80
+ header = io.BytesIO()
81
+
82
+ # Signature (8 bytes)
83
+ header.write(struct.pack('<Q', self.HEADER_SIGNATURE))
84
+
85
+ # CLSID (16 bytes) - all zeros
86
+ header.write(b'\x00' * 16)
87
+
88
+ # Minor version (2 bytes)
89
+ header.write(struct.pack('<H', self.MINOR_VERSION))
90
+
91
+ # Major version (2 bytes)
92
+ header.write(struct.pack('<H', self.major_version))
93
+
94
+ # Byte order (2 bytes)
95
+ header.write(struct.pack('<H', self.BYTE_ORDER))
96
+
97
+ # Sector size power (2 bytes): 9 for 512, 12 for 4096
98
+ sector_shift = 9 if self.sector_size == 512 else 12
99
+ header.write(struct.pack('<H', sector_shift))
100
+
101
+ # Mini sector size power (2 bytes): always 6 (64 bytes)
102
+ header.write(struct.pack('<H', 6))
103
+
104
+ # Reserved (6 bytes)
105
+ header.write(b'\x00' * 6)
106
+
107
+ # Total sectors (4 bytes) - 0 for version 3
108
+ header.write(struct.pack('<I', 0))
109
+
110
+ # FAT sectors (4 bytes)
111
+ fat_sectors = self._calculate_fat_sectors()
112
+ header.write(struct.pack('<I', fat_sectors))
113
+
114
+ # First directory sector (4 bytes)
115
+ header.write(struct.pack('<I', fat_sectors)) # Right after FAT
116
+
117
+ # Transaction signature (4 bytes)
118
+ header.write(struct.pack('<I', 0))
119
+
120
+ # Mini stream cutoff size (4 bytes) - 4096
121
+ header.write(struct.pack('<I', 4096))
122
+
123
+ # First mini FAT sector (4 bytes) - ENDOFCHAIN (no mini stream)
124
+ header.write(struct.pack('<I', self.ENDOFCHAIN))
125
+
126
+ # Number of mini FAT sectors (4 bytes)
127
+ header.write(struct.pack('<I', 0))
128
+
129
+ # First DIFAT sector (4 bytes) - ENDOFCHAIN
130
+ header.write(struct.pack('<I', self.ENDOFCHAIN))
131
+
132
+ # Number of DIFAT sectors (4 bytes)
133
+ header.write(struct.pack('<I', 0))
134
+
135
+ # DIFAT array (436 bytes for 109 entries)
136
+ # First FAT sector is at sector 0
137
+ for i in range(109):
138
+ if i == 0:
139
+ header.write(struct.pack('<I', 0)) # FAT at sector 0
140
+ else:
141
+ header.write(struct.pack('<I', self.FREESECT))
142
+
143
+ # Pad to 512 bytes
144
+ data = header.getvalue()
145
+ if len(data) < 512:
146
+ data += b'\x00' * (512 - len(data))
147
+
148
+ return data[:512]
149
+
150
+ def _calculate_fat_sectors(self):
151
+ """Calculate number of FAT sectors needed."""
152
+ # We need:
153
+ # - 1 FAT sector
154
+ # - 1 directory sector
155
+ # - N data sectors for streams
156
+
157
+ total_data = sum(len(data) for _, data in self.streams)
158
+ data_sectors = (total_data + self.sector_size - 1) // self.sector_size
159
+
160
+ # For simplicity, allocate 1 FAT sector (can hold 128 entries for 512-byte sectors)
161
+ return 1
162
+
163
+ def _build_fat(self):
164
+ """Build File Allocation Table."""
165
+ entries_per_sector = self.sector_size // 4
166
+ fat = io.BytesIO()
167
+
168
+ # FAT entries:
169
+ # Sector 0: FAT sector itself (FATSECT)
170
+ fat.write(struct.pack('<I', self.FATSECT))
171
+
172
+ # Sector 1: Directory sector (ENDOFCHAIN)
173
+ fat.write(struct.pack('<I', self.ENDOFCHAIN))
174
+
175
+ # Build separate chains for each stream
176
+ current_sector = 2
177
+ for stream_idx, (name, data) in enumerate(self.streams):
178
+ sectors_needed = (len(data) + self.sector_size - 1) // self.sector_size
179
+
180
+ # Create chain for this stream
181
+ for i in range(sectors_needed):
182
+ if i == sectors_needed - 1:
183
+ # Last sector of this stream
184
+ fat.write(struct.pack('<I', self.ENDOFCHAIN))
185
+ else:
186
+ # Point to next sector in chain
187
+ fat.write(struct.pack('<I', current_sector + i + 1))
188
+
189
+ current_sector += sectors_needed
190
+
191
+ # Fill rest of FAT sector with FREESECT
192
+ total_data_sectors = sum((len(d) + self.sector_size - 1) // self.sector_size
193
+ for _, d in self.streams)
194
+ entries_written = 2 + total_data_sectors
195
+ for i in range(entries_written, entries_per_sector):
196
+ fat.write(struct.pack('<I', self.FREESECT))
197
+
198
+ return fat.getvalue()
199
+
200
+ def _build_directory(self):
201
+ """Build directory entries."""
202
+ directory = io.BytesIO()
203
+
204
+ # Root entry (entry 0)
205
+ root_entry = self._create_directory_entry(
206
+ name="Root Entry",
207
+ obj_type=5, # Root storage
208
+ color=1, # Black
209
+ left_sibling=0xFFFFFFFF,
210
+ right_sibling=0xFFFFFFFF,
211
+ child_did=1 if len(self.streams) > 0 else 0xFFFFFFFF,
212
+ clsid=b'\x00' * 16,
213
+ state_bits=0,
214
+ creation_time=0,
215
+ modified_time=0,
216
+ starting_sector=0xFFFFFFFE, # ENDOFCHAIN
217
+ stream_size=0
218
+ )
219
+ directory.write(root_entry)
220
+
221
+ # Stream entries
222
+ # For simplicity with 2 streams, create simple red-black tree:
223
+ # Stream 0 (EncryptionInfo) - entry 1, left child of root
224
+ # Stream 1 (EncryptedPackage) - entry 2, right sibling of stream 0
225
+ for i, (name, data) in enumerate(self.streams):
226
+ # Calculate starting sector (after FAT and directory)
227
+ if i == 0:
228
+ starting_sector = 2 # After FAT (0) and directory (1)
229
+ else:
230
+ prev_data = sum(len(d) for _, d in self.streams[:i])
231
+ prev_sectors = (prev_data + self.sector_size - 1) // self.sector_size
232
+ starting_sector = 2 + prev_sectors
233
+
234
+ # Set up tree structure for exactly 2 streams
235
+ if i == 0:
236
+ # First stream: left child of root, right sibling is second stream
237
+ left_sib = 0xFFFFFFFF
238
+ right_sib = 2 if len(self.streams) > 1 else 0xFFFFFFFF
239
+ elif i == 1:
240
+ # Second stream: right sibling of first stream
241
+ left_sib = 0xFFFFFFFF
242
+ right_sib = 0xFFFFFFFF
243
+ else:
244
+ left_sib = 0xFFFFFFFF
245
+ right_sib = 0xFFFFFFFF
246
+
247
+ entry = self._create_directory_entry(
248
+ name=name,
249
+ obj_type=2, # Stream
250
+ color=1, # Black
251
+ left_sibling=left_sib,
252
+ right_sibling=right_sib,
253
+ child_did=0xFFFFFFFF,
254
+ clsid=b'\x00' * 16,
255
+ state_bits=0,
256
+ creation_time=0,
257
+ modified_time=0,
258
+ starting_sector=starting_sector,
259
+ stream_size=len(data)
260
+ )
261
+ directory.write(entry)
262
+
263
+ # Fill rest of directory sector
264
+ entries_per_sector = self.sector_size // 128
265
+ entries_written = 1 + len(self.streams)
266
+ for i in range(entries_written, entries_per_sector):
267
+ directory.write(b'\xFF' * 128)
268
+
269
+ return directory.getvalue()
270
+
271
+ def _create_directory_entry(self, name, obj_type, color, left_sibling, right_sibling,
272
+ child_did, clsid, state_bits, creation_time, modified_time,
273
+ starting_sector, stream_size):
274
+ """Create a directory entry (128 bytes)."""
275
+ entry = io.BytesIO()
276
+
277
+ # Name (64 bytes): UTF-16LE, null-terminated
278
+ name_bytes = name.encode('utf-16le')
279
+ if len(name_bytes) > 62:
280
+ name_bytes = name_bytes[:62]
281
+ entry.write(name_bytes)
282
+ entry.write(b'\x00' * (64 - len(name_bytes)))
283
+
284
+ # Name length (2 bytes): including null terminator
285
+ entry.write(struct.pack('<H', len(name_bytes) + 2))
286
+
287
+ # Object type (1 byte)
288
+ entry.write(struct.pack('<B', obj_type))
289
+
290
+ # Color (1 byte)
291
+ entry.write(struct.pack('<B', color))
292
+
293
+ # Left sibling DID (4 bytes)
294
+ entry.write(struct.pack('<I', left_sibling))
295
+
296
+ # Right sibling DID (4 bytes)
297
+ entry.write(struct.pack('<I', right_sibling))
298
+
299
+ # Child DID (4 bytes)
300
+ entry.write(struct.pack('<I', child_did))
301
+
302
+ # CLSID (16 bytes)
303
+ entry.write(clsid)
304
+
305
+ # State bits (4 bytes)
306
+ entry.write(struct.pack('<I', state_bits))
307
+
308
+ # Creation time (8 bytes)
309
+ entry.write(struct.pack('<Q', creation_time))
310
+
311
+ # Modified time (8 bytes)
312
+ entry.write(struct.pack('<Q', modified_time))
313
+
314
+ # Starting sector (4 bytes)
315
+ entry.write(struct.pack('<I', starting_sector))
316
+
317
+ # Stream size (8 bytes)
318
+ entry.write(struct.pack('<Q', stream_size))
319
+
320
+ data = entry.getvalue()
321
+ assert len(data) == 128
322
+ return data
323
+
324
+ def _build_data_sectors(self):
325
+ """Build data sectors for all streams."""
326
+ data = io.BytesIO()
327
+
328
+ for name, stream_data in self.streams:
329
+ # Write stream data
330
+ data.write(stream_data)
331
+
332
+ # Pad to sector boundary
333
+ padding = (self.sector_size - (len(stream_data) % self.sector_size)) % self.sector_size
334
+ if padding > 0:
335
+ data.write(b'\x00' * padding)
336
+
337
+ return data.getvalue()