numbers-parser 4.8.0__py3-none-any.whl → 4.9.0__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.
- numbers_parser/cell.py +110 -13
- numbers_parser/cell_storage.py +48 -12
- numbers_parser/constants.py +109 -6
- numbers_parser/containers.py +2 -2
- numbers_parser/document.py +303 -136
- numbers_parser/file.py +9 -4
- numbers_parser/model.py +104 -7
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/METADATA +96 -74
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/RECORD +12 -12
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/LICENSE.rst +0 -0
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/WHEEL +0 -0
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/entry_points.txt +0 -0
numbers_parser/file.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import re
|
|
3
4
|
from io import BytesIO
|
|
4
5
|
from sys import version_info
|
|
5
6
|
from zipfile import BadZipFile, ZipFile
|
|
@@ -32,11 +33,15 @@ def read_numbers_file(path, file_handler, object_handler=None):
|
|
|
32
33
|
read_numbers_file(filepath, file_handler, object_handler)
|
|
33
34
|
else:
|
|
34
35
|
f = open(filepath, "rb")
|
|
35
|
-
if filename.endswith(".iwa"):
|
|
36
|
-
blob = f.read()
|
|
37
|
-
extract_iwa_archives(blob, filepath, file_handler, object_handler)
|
|
38
36
|
blob = f.read()
|
|
39
|
-
|
|
37
|
+
if filename.endswith(".iwa"):
|
|
38
|
+
package_filepath = re.sub(r".*\.numbers/*", "", filepath)
|
|
39
|
+
extract_iwa_archives(blob, package_filepath, file_handler, object_handler)
|
|
40
|
+
else:
|
|
41
|
+
package_filepath = os.path.join(
|
|
42
|
+
re.sub(r".*\.numbers/*", "", path), filename
|
|
43
|
+
)
|
|
44
|
+
file_handler(package_filepath, blob)
|
|
40
45
|
else:
|
|
41
46
|
try:
|
|
42
47
|
zipf = open_zipfile(path)
|
numbers_parser/model.py
CHANGED
|
@@ -2,6 +2,7 @@ import math
|
|
|
2
2
|
import re
|
|
3
3
|
from array import array
|
|
4
4
|
from collections import defaultdict
|
|
5
|
+
from hashlib import sha1
|
|
5
6
|
from struct import pack
|
|
6
7
|
from typing import Dict, List, Tuple, Union
|
|
7
8
|
from warnings import warn
|
|
@@ -34,7 +35,6 @@ from numbers_parser.cell import (
|
|
|
34
35
|
TextCell,
|
|
35
36
|
VerticalJustification,
|
|
36
37
|
xl_col_to_name,
|
|
37
|
-
xl_range,
|
|
38
38
|
xl_rowcol_to_cell,
|
|
39
39
|
)
|
|
40
40
|
from numbers_parser.cell_storage import CellStorage
|
|
@@ -56,6 +56,7 @@ from numbers_parser.constants import (
|
|
|
56
56
|
FORMAT_TYPE_MAP,
|
|
57
57
|
MAX_TILE_SIZE,
|
|
58
58
|
PACKAGE_ID,
|
|
59
|
+
CellInteractionType,
|
|
59
60
|
FormatType,
|
|
60
61
|
)
|
|
61
62
|
from numbers_parser.containers import ObjectStore
|
|
@@ -118,11 +119,6 @@ class MergeCells:
|
|
|
118
119
|
def rect(self, row_col: Tuple) -> Tuple:
|
|
119
120
|
return self._references[row_col].rect
|
|
120
121
|
|
|
121
|
-
def merge_cell_names(self):
|
|
122
|
-
return [
|
|
123
|
-
xl_range(*v.rect) for k, v in self._references.items() if self.is_merge_reference(k)
|
|
124
|
-
]
|
|
125
|
-
|
|
126
122
|
def merge_cells(self):
|
|
127
123
|
return [k for k, v in self._references.items() if self.is_merge_anchor(k)]
|
|
128
124
|
|
|
@@ -225,6 +221,7 @@ class _NumbersModel(Cacheable):
|
|
|
225
221
|
self._table_formats = DataLists(self, "format_table", "format")
|
|
226
222
|
self._table_styles = DataLists(self, "styleTable", "reference")
|
|
227
223
|
self._table_strings = DataLists(self, "stringTable", "string")
|
|
224
|
+
self._control_specs = DataLists(self, "control_cell_spec_table", "cell_spec")
|
|
228
225
|
self._table_data = {}
|
|
229
226
|
self._styles = None
|
|
230
227
|
self._custom_formats = None
|
|
@@ -368,6 +365,64 @@ class _NumbersModel(Cacheable):
|
|
|
368
365
|
format = TSKArchives.FormatStructArchive(**attrs)
|
|
369
366
|
return self._table_formats.lookup_key(table_id, format)
|
|
370
367
|
|
|
368
|
+
def cell_popup_model(self, parent_id: int, format: Formatting):
|
|
369
|
+
tsce_items = [{"cell_value_type": "NIL_TYPE"}]
|
|
370
|
+
for item in format.popup_values:
|
|
371
|
+
if isinstance(item, str):
|
|
372
|
+
tsce_items.append(
|
|
373
|
+
{
|
|
374
|
+
"cell_value_type": "STRING_TYPE",
|
|
375
|
+
"string_value": {
|
|
376
|
+
"value": item,
|
|
377
|
+
"format": {"format_type": FormatType.TEXT},
|
|
378
|
+
},
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
else:
|
|
382
|
+
tsce_items.append(
|
|
383
|
+
{
|
|
384
|
+
"cell_value_type": "NUMBER_TYPE",
|
|
385
|
+
"number_value": {
|
|
386
|
+
"value": item,
|
|
387
|
+
"format": {"format_type": FormatType.DECIMAL},
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
popup_menu_id, _ = self.objects.create_object_from_dict(
|
|
392
|
+
f"Index/Tables/DataList-{parent_id}",
|
|
393
|
+
{"tsce_item": tsce_items},
|
|
394
|
+
TSTArchives.PopUpMenuModel,
|
|
395
|
+
True,
|
|
396
|
+
)
|
|
397
|
+
return popup_menu_id
|
|
398
|
+
|
|
399
|
+
def control_cell_archive(self, table_id: int, format_type: FormattingType, format: Formatting):
|
|
400
|
+
"""Create control cell archive from a Formatting spec and return the table format ID"""
|
|
401
|
+
if format_type == FormattingType.TICKBOX:
|
|
402
|
+
cell_spec = TSTArchives.CellSpecArchive(interaction_type=CellInteractionType.TOGGLE)
|
|
403
|
+
elif format_type == FormattingType.RATING:
|
|
404
|
+
cell_spec = TSTArchives.CellSpecArchive(
|
|
405
|
+
interaction_type=CellInteractionType.RATING,
|
|
406
|
+
range_control_min=0.0,
|
|
407
|
+
range_control_max=5.0,
|
|
408
|
+
range_control_inc=1.0,
|
|
409
|
+
)
|
|
410
|
+
elif format_type == FormattingType.SLIDER:
|
|
411
|
+
cell_spec = TSTArchives.CellSpecArchive(
|
|
412
|
+
interaction_type=CellInteractionType.SLIDER,
|
|
413
|
+
range_control_min=format.minimum,
|
|
414
|
+
range_control_max=format.maximum,
|
|
415
|
+
range_control_inc=format.increment,
|
|
416
|
+
)
|
|
417
|
+
else: # POPUP
|
|
418
|
+
popup_id = self.cell_popup_model(self._control_specs.id(table_id), format)
|
|
419
|
+
cell_spec = TSTArchives.CellSpecArchive(
|
|
420
|
+
interaction_type=CellInteractionType.POPUP,
|
|
421
|
+
chooser_control_popup_model=TSPMessages.Reference(identifier=popup_id),
|
|
422
|
+
chooser_control_start_w_first=not (format.allow_none),
|
|
423
|
+
)
|
|
424
|
+
return self._control_specs.lookup_key(table_id, cell_spec)
|
|
425
|
+
|
|
371
426
|
def add_custom_decimal_format_archive(self, format: CustomFormatting) -> None:
|
|
372
427
|
"""Create a custom format from the format spec"""
|
|
373
428
|
integer_format = format.integer_format
|
|
@@ -1482,12 +1537,35 @@ class _NumbersModel(Cacheable):
|
|
|
1482
1537
|
+ str(cell.style.bg_color.g)
|
|
1483
1538
|
+ str(cell.style.bg_color.b)
|
|
1484
1539
|
)
|
|
1540
|
+
if cell._style.bg_image is not None:
|
|
1541
|
+
fingerprint += cell._style.bg_image.filename
|
|
1485
1542
|
if fingerprint not in cell_styles:
|
|
1486
1543
|
cell_styles[fingerprint] = self.add_cell_style(cell._style)
|
|
1487
1544
|
cell._style._cell_style_obj_id = cell_styles[fingerprint]
|
|
1488
1545
|
|
|
1489
1546
|
def add_cell_style(self, style: Style) -> int:
|
|
1490
|
-
if style.
|
|
1547
|
+
if style.bg_image is not None:
|
|
1548
|
+
datas = self.objects[PACKAGE_ID].datas
|
|
1549
|
+
image_id = self.next_image_identifier()
|
|
1550
|
+
datas.append(
|
|
1551
|
+
TSPArchiveMessages.DataInfo(
|
|
1552
|
+
identifier=image_id,
|
|
1553
|
+
digest=sha1(style.bg_image.data).digest(),
|
|
1554
|
+
preferred_file_name=style.bg_image.filename,
|
|
1555
|
+
file_name=style.bg_image.filename,
|
|
1556
|
+
materialized_length=len(style.bg_image.data),
|
|
1557
|
+
)
|
|
1558
|
+
)
|
|
1559
|
+
color_attrs = {
|
|
1560
|
+
"cell_fill": {
|
|
1561
|
+
"image": {
|
|
1562
|
+
"technique": "ScaleToFill",
|
|
1563
|
+
"imagedata": {"identifier": image_id},
|
|
1564
|
+
"interpretsUntaggedImageDataAsGeneric": False,
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
elif style.bg_color is not None:
|
|
1491
1569
|
color_attrs = {
|
|
1492
1570
|
"cell_fill": {
|
|
1493
1571
|
"color": {
|
|
@@ -1536,6 +1614,7 @@ class _NumbersModel(Cacheable):
|
|
|
1536
1614
|
style=TSPMessages.Reference(identifier=cell_style_id),
|
|
1537
1615
|
)
|
|
1538
1616
|
)
|
|
1617
|
+
|
|
1539
1618
|
return cell_style_id
|
|
1540
1619
|
|
|
1541
1620
|
def text_style_object_id(self, cell_storage) -> int:
|
|
@@ -1704,6 +1783,10 @@ class _NumbersModel(Cacheable):
|
|
|
1704
1783
|
flags |= 0x200
|
|
1705
1784
|
length += 4
|
|
1706
1785
|
storage += pack("<i", cell._storage.formula_id)
|
|
1786
|
+
if cell._storage.control_id is not None:
|
|
1787
|
+
flags |= 0x400
|
|
1788
|
+
length += 4
|
|
1789
|
+
storage += pack("<i", cell._storage.control_id)
|
|
1707
1790
|
if cell._storage.suggest_id is not None:
|
|
1708
1791
|
flags |= 0x1000
|
|
1709
1792
|
length += 4
|
|
@@ -2209,6 +2292,20 @@ class _NumbersModel(Cacheable):
|
|
|
2209
2292
|
stroke_layer.stroke_runs.append(self.create_stroke(origin, length, border_value))
|
|
2210
2293
|
layer_ids.append(TSPMessages.Reference(identifier=stroke_layer_id))
|
|
2211
2294
|
|
|
2295
|
+
def store_image(self, data: bytes, filename: str) -> None:
|
|
2296
|
+
"""Store image data in the file store."""
|
|
2297
|
+
stored_filename = f"Data/{filename}"
|
|
2298
|
+
if stored_filename in self.objects.file_store:
|
|
2299
|
+
raise IndexError(f"{filename}: image already exists in document")
|
|
2300
|
+
self.objects.file_store[stored_filename] = data
|
|
2301
|
+
|
|
2302
|
+
def next_image_identifier(self):
|
|
2303
|
+
"""Return the next available ID in the list of images in the document."""
|
|
2304
|
+
datas = self.objects[PACKAGE_ID].datas
|
|
2305
|
+
image_ids = [x.identifier for x in datas]
|
|
2306
|
+
# datas never appears to be an empty list (default themes include images)
|
|
2307
|
+
return max(image_ids) + 1
|
|
2308
|
+
|
|
2212
2309
|
|
|
2213
2310
|
def rgb(obj) -> RGB:
|
|
2214
2311
|
"""Convert a TSPArchives.Color into an RGB tuple."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: numbers-parser
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.9.0
|
|
4
4
|
Summary: Read and write Apple Numbers spreadsheets
|
|
5
5
|
Home-page: https://github.com/masaccio/numbers-parser
|
|
6
6
|
License: MIT
|
|
@@ -57,14 +57,16 @@ For Intel Macs:
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
brew install snappy python3
|
|
60
|
-
CPPFLAGS="-I/usr/local/include -L/usr/local/lib"
|
|
60
|
+
CPPFLAGS="-I/usr/local/include -L/usr/local/lib" \
|
|
61
|
+
python3 -m pip install python-snappy
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
For Apple Silicon Macs:
|
|
64
65
|
|
|
65
66
|
```bash
|
|
66
67
|
brew install snappy python3
|
|
67
|
-
CPPFLAGS="-I/opt/homebrew/include -L/opt/homebrew/lib"
|
|
68
|
+
CPPFLAGS="-I/opt/homebrew/include -L/opt/homebrew/lib" \
|
|
69
|
+
python3 -m pip install python-snappy
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
For Linux (your package manager may be different):
|
|
@@ -78,10 +80,10 @@ binary libraries compiled by [Christoph Gohlke](https://www.lfd.uci.edu/~gohlke/
|
|
|
78
80
|
version for your installation. For example for python 3.11:
|
|
79
81
|
|
|
80
82
|
```text
|
|
81
|
-
|
|
83
|
+
pip install python_snappy-0.6.1-cp311-cp311-win_amd64.whl
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
## Quick Start
|
|
85
87
|
|
|
86
88
|
Reading documents:
|
|
87
89
|
|
|
@@ -121,7 +123,7 @@ the column values.
|
|
|
121
123
|
'Debit'
|
|
122
124
|
```
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
### Cell Data
|
|
125
127
|
|
|
126
128
|
Cells are objects with a common base class of `Cell`. All cell types
|
|
127
129
|
have a property `value` which returns the contents of the cell as a
|
|
@@ -129,17 +131,17 @@ python datatype. `numbers-parser` uses
|
|
|
129
131
|
[pendulum](https://pendulum.eustace.io) instead of python’s builtin
|
|
130
132
|
types. Available cell types are:
|
|
131
133
|
|
|
132
|
-
| Cell type
|
|
133
|
-
|
|
134
|
-
|
|
|
135
|
-
| TextCell
|
|
136
|
-
|
|
|
137
|
-
| EmptyCell
|
|
138
|
-
| BoolCell
|
|
139
|
-
| DateCell
|
|
140
|
-
|
|
|
141
|
-
| ErrorCell
|
|
142
|
-
|
|
|
134
|
+
| Cell type | value type | Additional properties |
|
|
135
|
+
|--------------|---------------------|--------------------------------------------------------------------------------------------------------|
|
|
136
|
+
| NumberCell | `float` | |
|
|
137
|
+
| TextCell | `str` | |
|
|
138
|
+
| RichTextCell | `str` | See [Rich text](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.RichTextCell) |
|
|
139
|
+
| EmptyCell | `None` | |
|
|
140
|
+
| BoolCell | `bool` | |
|
|
141
|
+
| DateCell | `pendulum.datetime` | |
|
|
142
|
+
| DurationCell | `pendulum.duration` | |
|
|
143
|
+
| ErrorCell | `None` | |
|
|
144
|
+
| MergedCell | `None` | See [Merged cells](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.MergedCell) |
|
|
143
145
|
|
|
144
146
|
Cell references can be either zero-offset row/column integers or an
|
|
145
147
|
Excel/Numbers A1 notation. Where cell values are not `None` the
|
|
@@ -161,7 +163,7 @@ any kind `ErrorCell`.
|
|
|
161
163
|
'£1,234.50'
|
|
162
164
|
```
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
### Pandas Support
|
|
165
167
|
|
|
166
168
|
Since the return value of `rows()` is a list of lists, you can pass
|
|
167
169
|
this directly to pandas. Assuming you have a Numbers table with a single
|
|
@@ -178,15 +180,15 @@ data = tables[0].rows(values_only=True)
|
|
|
178
180
|
df = pd.DataFrame(data[1:], columns=data[0])
|
|
179
181
|
```
|
|
180
182
|
|
|
181
|
-
|
|
183
|
+
### Writing Numbers Documents
|
|
182
184
|
|
|
183
185
|
Whilst support for writing numbers files has been stable since version
|
|
184
186
|
3.4.0, you are highly recommended not to overwrite working Numbers files
|
|
185
187
|
and instead save data to a new file.
|
|
186
188
|
|
|
187
189
|
Cell values are written using
|
|
188
|
-
[Table.write()](https://masaccio.github.io/numbers-parser
|
|
189
|
-
|
|
190
|
+
[Table.write()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.write) and
|
|
191
|
+
`numbers-parser` will automatically create empty rows and columns
|
|
190
192
|
for any cell references that are out of range of the current table.
|
|
191
193
|
|
|
192
194
|
```python
|
|
@@ -199,12 +201,9 @@ table.write("B7", datetime(2020, 12, 25))
|
|
|
199
201
|
doc.save("new-sheet.numbers")
|
|
200
202
|
```
|
|
201
203
|
|
|
202
|
-
Additional tables and worksheets can be added to a `Document` before
|
|
203
|
-
|
|
204
|
-
[
|
|
205
|
-
and
|
|
206
|
-
[Sheet.add_table()](https://masaccio.github.io/numbers-parser/#numbers_parser.Sheet.add_table)
|
|
207
|
-
respectively:
|
|
204
|
+
Additional tables and worksheets can be added to a `Document` before saving using
|
|
205
|
+
[Document.add_sheet()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_sheet) and
|
|
206
|
+
[Sheet.add_table()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Sheet.add_table) respectively:
|
|
208
207
|
|
|
209
208
|
```python
|
|
210
209
|
doc = Document()
|
|
@@ -217,37 +216,48 @@ table.write(1, 3, 3000)
|
|
|
217
216
|
doc.save("sheet.numbers")
|
|
218
217
|
```
|
|
219
218
|
|
|
220
|
-
|
|
219
|
+
### Styles
|
|
221
220
|
|
|
222
221
|
`numbers_parser` currently only supports paragraph styles and cell
|
|
223
|
-
styles. The following
|
|
222
|
+
styles. The following styles are supported:
|
|
224
223
|
|
|
225
224
|
- font attributes: bold, italic, underline, strikethrough
|
|
226
225
|
- font selection and size
|
|
227
226
|
- text foreground color
|
|
228
227
|
- horizontal and vertical alignment
|
|
229
228
|
- cell background color
|
|
229
|
+
- cell background images
|
|
230
230
|
- cell indents (first line, left, right, and text inset)
|
|
231
231
|
|
|
232
232
|
Numbers conflates style attributes that can be stored in paragraph
|
|
233
233
|
styles (the style menu in the text panel) with the settings that are
|
|
234
234
|
available on the Style tab of the Text panel. Some attributes in Numbers
|
|
235
|
-
are not applied to new cells when a style is applied.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
object. When a document is saved, the attributes
|
|
239
|
-
paragraph style are applied to each cell that includes it.
|
|
235
|
+
are not applied to new cells when a style is applied.
|
|
236
|
+
|
|
237
|
+
To keep the API simple, `numbers-parser` packs all styling into a single
|
|
238
|
+
[Style](https://masaccio.github.io/numbers-parser/api/style.html) object. When a document is saved, the attributes
|
|
239
|
+
not stored in a paragraph style are applied to each cell that includes it.
|
|
240
240
|
|
|
241
241
|
Styles are read from cells using the
|
|
242
|
-
[Cell.style](https://masaccio.github.io/numbers-parser
|
|
243
|
-
|
|
244
|
-
[Document.add_style](https://masaccio.github.io/numbers-parser
|
|
242
|
+
[Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style) property and you can
|
|
243
|
+
add new styles with
|
|
244
|
+
[Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
```python
|
|
247
|
+
red_text = doc.add_style(
|
|
248
|
+
name="Red Text",
|
|
249
|
+
font_name="Lucida Grande",
|
|
250
|
+
font_color=RGB(230, 25, 25),
|
|
251
|
+
font_size=14.0,
|
|
252
|
+
bold=True,
|
|
253
|
+
italic=True,
|
|
254
|
+
alignment=Alignment("right", "top"),
|
|
255
|
+
)
|
|
256
|
+
table.write("B2", "Red", style=red_text)
|
|
257
|
+
table.set_cell_style("C2", red_text)
|
|
258
|
+
```
|
|
249
259
|
|
|
250
|
-
|
|
260
|
+
### Cell Data Formatting
|
|
251
261
|
|
|
252
262
|
Numbers has two different cell formatting types: data formats and custom
|
|
253
263
|
formats.
|
|
@@ -260,23 +270,37 @@ internally by the package. Changing a data format for cell has no impact
|
|
|
260
270
|
on any other cells.
|
|
261
271
|
|
|
262
272
|
Cell formats are changed using
|
|
263
|
-
[Table.set_cell_formatting](https://masaccio.github.io/numbers-parser
|
|
273
|
+
[Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
|
|
264
274
|
|
|
265
275
|
```python
|
|
266
|
-
table.set_cell_formatting(
|
|
267
|
-
|
|
276
|
+
table.set_cell_formatting(
|
|
277
|
+
"C1",
|
|
278
|
+
"date",
|
|
279
|
+
date_time_format="EEEE, d MMMM yyyy"
|
|
280
|
+
)
|
|
281
|
+
table.set_cell_formatting(
|
|
282
|
+
0,
|
|
283
|
+
4,
|
|
284
|
+
"number",
|
|
285
|
+
decimal_places=3,
|
|
286
|
+
negative_style=NegativeNumberStyle.RED
|
|
287
|
+
)
|
|
268
288
|
```
|
|
269
289
|
|
|
270
290
|
Custom formats are shared across a Document and can be applied to
|
|
271
291
|
multiple cells in multiple tables. Editing a custom format changes the
|
|
272
292
|
appearance of data in all cells that share that format. You must first
|
|
273
293
|
add a custom format to the document using
|
|
274
|
-
[Document.add_custom_format](https://masaccio.github.io/numbers-parser
|
|
294
|
+
[Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format)
|
|
275
295
|
before assigning it to cells using
|
|
276
|
-
[Table.set_cell_formatting](https://masaccio.github.io/numbers-parser
|
|
296
|
+
[Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
|
|
277
297
|
|
|
278
298
|
```python
|
|
279
|
-
long_date = doc.add_custom_format(
|
|
299
|
+
long_date = doc.add_custom_format(
|
|
300
|
+
name="Long Date",
|
|
301
|
+
type="date",
|
|
302
|
+
date_time_format="EEEE, d MMMM yyyy"
|
|
303
|
+
)
|
|
280
304
|
table.set_cell_formatting("C1", "custom", format=long_date)
|
|
281
305
|
```
|
|
282
306
|
|
|
@@ -287,7 +311,7 @@ example, US dollars are referred to as `US$` whereas Euros and British
|
|
|
287
311
|
Pounds are referred to using their symbols of `€` and `£`
|
|
288
312
|
respectively.
|
|
289
313
|
|
|
290
|
-
|
|
314
|
+
### Borders
|
|
291
315
|
|
|
292
316
|
`numbers-parser` supports reading and writing cell borders, though the
|
|
293
317
|
interface for each differs. Individual cells can have each of their four
|
|
@@ -296,13 +320,11 @@ table to allow for drawing borders across multiple cells. Setting the
|
|
|
296
320
|
border of merged cells is not possible unless the edge of the cells is
|
|
297
321
|
at the end of the merged region.
|
|
298
322
|
|
|
299
|
-
Borders are represented using the
|
|
300
|
-
|
|
301
|
-
class that can be initialized with line width, color and line style. The
|
|
323
|
+
Borders are represented using the [Border](https://masaccio.github.io/numbers-parser/api/border.html) class
|
|
324
|
+
that can be initialized with line width, color and line style. The
|
|
302
325
|
current state of a cell border is read using the
|
|
303
|
-
[Cell.border](https://masaccio.github.io/numbers-parser
|
|
304
|
-
|
|
305
|
-
[Table.set_cell_border](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_border)
|
|
326
|
+
[Cell.border](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.border) property
|
|
327
|
+
and [Table.set_cell_border()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_border)
|
|
306
328
|
sets the border for a cell edge or a range of cells.
|
|
307
329
|
|
|
308
330
|
## API
|
|
@@ -319,25 +341,30 @@ format, iterating through all the spreadsheets passed on the
|
|
|
319
341
|
command-line.
|
|
320
342
|
|
|
321
343
|
```text
|
|
322
|
-
usage: cat-numbers [-h] [-T | -S | -b] [-V] [--
|
|
323
|
-
|
|
344
|
+
usage: cat-numbers [-h] [-T | -S | -b] [-V] [--formulas] [--formatting]
|
|
345
|
+
[-s SHEET] [-t TABLE] [--debug]
|
|
346
|
+
[document ...]
|
|
324
347
|
|
|
325
348
|
Export data from Apple Numbers spreadsheet tables
|
|
326
349
|
|
|
327
350
|
positional arguments:
|
|
328
|
-
document
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
-h, --help
|
|
332
|
-
-T, --list-tables
|
|
333
|
-
-S, --list-sheets
|
|
334
|
-
-b, --brief
|
|
351
|
+
document Document(s) to export
|
|
352
|
+
|
|
353
|
+
options:
|
|
354
|
+
-h, --help show this help message and exit
|
|
355
|
+
-T, --list-tables List the names of tables and exit
|
|
356
|
+
-S, --list-sheets List the names of sheets and exit
|
|
357
|
+
-b, --brief Don't prefix data rows with name of sheet/table
|
|
358
|
+
(default: false)
|
|
335
359
|
-V, --version
|
|
336
|
-
--
|
|
337
|
-
--
|
|
338
|
-
|
|
339
|
-
-s SHEET, --sheet SHEET
|
|
340
|
-
|
|
360
|
+
--formulas Dump formulas instead of formula results
|
|
361
|
+
--formatting Dump formatted cells (durations) as they appear
|
|
362
|
+
in Numbers
|
|
363
|
+
-s SHEET, --sheet SHEET
|
|
364
|
+
Names of sheet(s) to include in export
|
|
365
|
+
-t TABLE, --table TABLE
|
|
366
|
+
Names of table(s) to include in export
|
|
367
|
+
--debug Enable debug logging
|
|
341
368
|
```
|
|
342
369
|
|
|
343
370
|
Note: `--formatting` will return different capitalization for 12-hour
|
|
@@ -358,10 +385,6 @@ Current known limitations of `numbers-parser` are:
|
|
|
358
385
|
worksheet which does not take into account title or caption size
|
|
359
386
|
- New sheets insert tables with formats copied from the first table in
|
|
360
387
|
the previous sheet rather than default table formats
|
|
361
|
-
- Creating custom cell formats and cell data formats is experimental
|
|
362
|
-
and not all formats are supported. See
|
|
363
|
-
[Table.set_cell_formatting](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_formatting)
|
|
364
|
-
for more details.
|
|
365
388
|
- Due to a limitation in Python’s
|
|
366
389
|
[ZipFile](https://docs.python.org/3/library/zipfile.html), Python
|
|
367
390
|
versions older than 3.11 do not support image filenames with UTF-8
|
|
@@ -373,6 +396,5 @@ Current known limitations of `numbers-parser` are:
|
|
|
373
396
|
|
|
374
397
|
## License
|
|
375
398
|
|
|
376
|
-
All code in this repository is licensed under the [MIT
|
|
377
|
-
License](https://github.com/masaccio/numbers-parser/blob/master/LICENSE.rst)
|
|
399
|
+
All code in this repository is licensed under the [MIT License](https://github.com/masaccio/numbers-parser/blob/master/LICENSE.rst).
|
|
378
400
|
|
|
@@ -2,16 +2,16 @@ numbers_parser/__init__.py,sha256=hyoQ4x-1E8yDBMR128kZhSDLCfrnjNdz4_dEpKlekk4,19
|
|
|
2
2
|
numbers_parser/_cat_numbers.py,sha256=-HboBJT11Vjcr8sjLZl7Z6qAapnPEc_kFYq6PTqON20,4619
|
|
3
3
|
numbers_parser/_unpack_numbers.py,sha256=zfpBOfM92rMHSRQVR4tExf0fWI2Lbbb6WrwQPoq4gMo,5689
|
|
4
4
|
numbers_parser/bullets.py,sha256=OnVVMPjhTDrC-ncw52Gb00UEXNmn2Rvd3xi7lfqW3hk,2616
|
|
5
|
-
numbers_parser/cell.py,sha256=
|
|
6
|
-
numbers_parser/cell_storage.py,sha256=
|
|
7
|
-
numbers_parser/constants.py,sha256=
|
|
8
|
-
numbers_parser/containers.py,sha256=
|
|
5
|
+
numbers_parser/cell.py,sha256=YfHs1xxVSNY_oEodTN3IhqdO5tUhdgzZz5Z0m10dtpw,38457
|
|
6
|
+
numbers_parser/cell_storage.py,sha256=jaGpleFg8xWPT3U-O4bczAMYFR54t3x920UBKcrUiOQ,34354
|
|
7
|
+
numbers_parser/constants.py,sha256=BJGNz0ZZCs5xfAXlzLox4tvKOHIpDMXgJQ6i5yvdr4A,9606
|
|
8
|
+
numbers_parser/containers.py,sha256=yR_T2yF5QiVj7Dg22nCMLvo___Xrec3j8kitbxiaWyU,4220
|
|
9
9
|
numbers_parser/currencies.py,sha256=8k4a3WKmDoHeurkDICymHX13N7ManHSTaka_JNXCZYA,3767
|
|
10
10
|
numbers_parser/data/empty.numbers,sha256=8JOp035V-p2ff9_Wao7mLcYvb6_if6O2cus_esjVA9k,90316
|
|
11
|
-
numbers_parser/document.py,sha256=
|
|
11
|
+
numbers_parser/document.py,sha256=JmGyafm10FXnS5zmXkMTRVvYM-KnCKtcmbNCwMDyuSU,55333
|
|
12
12
|
numbers_parser/exceptions.py,sha256=G8dASUQZI8ksHYRVfdGWJzgsJD5CBpcZvmDJUZTqT-c,670
|
|
13
13
|
numbers_parser/experimental.py,sha256=WARjTa-2ePb8Ga8Q6oDP6EJCs12ofLRF2YpwzUu66ZI,374
|
|
14
|
-
numbers_parser/file.py,sha256=
|
|
14
|
+
numbers_parser/file.py,sha256=buNbZRzQCIlr7H4JxwTh2_eh7oDA2fGH5ZiFpIEVHoo,4200
|
|
15
15
|
numbers_parser/formula.py,sha256=JRsG0L21wS70oJ-FB46Amyoy-sKizWb-iUhSXUcVJ-U,10572
|
|
16
16
|
numbers_parser/generated/TNArchives_pb2.py,sha256=txkTtPHvdXVvv7zO1dHCxxnixaFulK7hJVLQrH3cIJc,16007
|
|
17
17
|
numbers_parser/generated/TNArchives_sos_pb2.py,sha256=AYI1X5t5Gb4l941jXlHEY0v97ToIze0bSYBR7KQmS0A,1215
|
|
@@ -50,11 +50,11 @@ numbers_parser/generated/fontmap.py,sha256=pqc1HwwTr3UbFMmhUaHJg1dX5-3pXbyhfS2bk
|
|
|
50
50
|
numbers_parser/generated/functionmap.py,sha256=VdZo0ERMYONcrnJFwABcSCHb8pjA4wY2ogt8Janz57M,6082
|
|
51
51
|
numbers_parser/iwafile.py,sha256=MuFIlB_hdXTTZflxoX_ZvA_68OaJkmRQ4eJ2UAiCKXQ,11833
|
|
52
52
|
numbers_parser/mapping.py,sha256=in8W3S8DmTcPefFaxnATLw0FQ4YnFsnAE-cl5dljzJE,32215
|
|
53
|
-
numbers_parser/model.py,sha256=
|
|
53
|
+
numbers_parser/model.py,sha256=qvU0RRF4YgUcfhtRlDdRP09gl8rUY0GNgeHpOTJYvHk,103312
|
|
54
54
|
numbers_parser/numbers_cache.py,sha256=1ghEBghQAYFpPiEeOtb74i016mXc039v1pOubbqvaLs,1141
|
|
55
55
|
numbers_parser/numbers_uuid.py,sha256=-LeAj_ULC0va57pEmyegGY0xXqkNNjyuLukCaiQJhOk,2642
|
|
56
|
-
numbers_parser-4.
|
|
57
|
-
numbers_parser-4.
|
|
58
|
-
numbers_parser-4.
|
|
59
|
-
numbers_parser-4.
|
|
60
|
-
numbers_parser-4.
|
|
56
|
+
numbers_parser-4.9.0.dist-info/LICENSE.rst,sha256=8vTa1-5KSdHrTpU9rlheO5005EWReEPMpjV7BjSaMc4,1050
|
|
57
|
+
numbers_parser-4.9.0.dist-info/METADATA,sha256=RWBbYXp1YHBV7Jj4Q6C_RwXIA_-JEi55l6o1A7G9r4w,16152
|
|
58
|
+
numbers_parser-4.9.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
59
|
+
numbers_parser-4.9.0.dist-info/entry_points.txt,sha256=V91uB9vBPxf3eCY1h-0syv21imYCT0MJfMxf87DmwIk,115
|
|
60
|
+
numbers_parser-4.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|