numbers-parser 4.8.1__tar.gz → 4.9.0__tar.gz

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 (59) hide show
  1. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/PKG-INFO +23 -28
  2. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/README.md +22 -27
  3. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/pyproject.toml +1 -1
  4. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/cell.py +80 -13
  5. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/cell_storage.py +48 -12
  6. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/constants.py +103 -6
  7. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/containers.py +2 -2
  8. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/document.py +193 -69
  9. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/file.py +9 -4
  10. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/model.py +104 -1
  11. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/LICENSE.rst +0 -0
  12. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/__init__.py +0 -0
  13. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/_cat_numbers.py +0 -0
  14. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/_unpack_numbers.py +0 -0
  15. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/bullets.py +0 -0
  16. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/currencies.py +0 -0
  17. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/data/empty.numbers +0 -0
  18. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/exceptions.py +0 -0
  19. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/experimental.py +0 -0
  20. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/formula.py +0 -0
  21. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TNArchives_pb2.py +0 -0
  22. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TNArchives_sos_pb2.py +0 -0
  23. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TNCommandArchives_pb2.py +0 -0
  24. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TNCommandArchives_sos_pb2.py +0 -0
  25. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSAArchives_pb2.py +0 -0
  26. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSAArchives_sos_pb2.py +0 -0
  27. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSACommandArchives_sos_pb2.py +0 -0
  28. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCEArchives_pb2.py +0 -0
  29. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCH3DArchives_pb2.py +0 -0
  30. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCHArchives_Common_pb2.py +0 -0
  31. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCHArchives_GEN_pb2.py +0 -0
  32. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCHArchives_pb2.py +0 -0
  33. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCHArchives_sos_pb2.py +0 -0
  34. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCHCommandArchives_pb2.py +0 -0
  35. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSCHPreUFFArchives_pb2.py +0 -0
  36. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSDArchives_pb2.py +0 -0
  37. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSDArchives_sos_pb2.py +0 -0
  38. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSDCommandArchives_pb2.py +0 -0
  39. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSKArchives_pb2.py +0 -0
  40. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSKArchives_sos_pb2.py +0 -0
  41. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSPArchiveMessages_pb2.py +0 -0
  42. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSPDatabaseMessages_pb2.py +0 -0
  43. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSPMessages_pb2.py +0 -0
  44. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSSArchives_pb2.py +0 -0
  45. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSSArchives_sos_pb2.py +0 -0
  46. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSTArchives_pb2.py +0 -0
  47. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSTArchives_sos_pb2.py +0 -0
  48. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSTCommandArchives_pb2.py +0 -0
  49. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSTStylePropertyArchiving_pb2.py +0 -0
  50. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSWPArchives_pb2.py +0 -0
  51. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSWPArchives_sos_pb2.py +0 -0
  52. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/TSWPCommandArchives_pb2.py +0 -0
  53. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/__init__.py +0 -0
  54. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/fontmap.py +0 -0
  55. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/generated/functionmap.py +0 -0
  56. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/iwafile.py +0 -0
  57. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/mapping.py +0 -0
  58. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/numbers_cache.py +0 -0
  59. {numbers_parser-4.8.1 → numbers_parser-4.9.0}/src/numbers_parser/numbers_uuid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: numbers-parser
3
- Version: 4.8.1
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
@@ -187,8 +187,8 @@ Whilst support for writing numbers files has been stable since version
187
187
  and instead save data to a new file.
188
188
 
189
189
  Cell values are written using
190
- [Table.write()](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.write)
191
- and `numbers-parser` will automatically create empty rows and columns
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
192
192
  for any cell references that are out of range of the current table.
193
193
 
194
194
  ```python
@@ -201,12 +201,9 @@ table.write("B7", datetime(2020, 12, 25))
201
201
  doc.save("new-sheet.numbers")
202
202
  ```
203
203
 
204
- Additional tables and worksheets can be added to a `Document` before
205
- saving using
206
- [Document.add_sheet()](https://masaccio.github.io/numbers-parser/#numbers_parser.Document.add_sheet)
207
- and
208
- [Sheet.add_table()](https://masaccio.github.io/numbers-parser/#numbers_parser.Sheet.add_table)
209
- 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:
210
207
 
211
208
  ```python
212
209
  doc = Document()
@@ -222,27 +219,28 @@ doc.save("sheet.numbers")
222
219
  ### Styles
223
220
 
224
221
  `numbers_parser` currently only supports paragraph styles and cell
225
- styles. The following paragraph styles are supported:
222
+ styles. The following styles are supported:
226
223
 
227
224
  - font attributes: bold, italic, underline, strikethrough
228
225
  - font selection and size
229
226
  - text foreground color
230
227
  - horizontal and vertical alignment
231
228
  - cell background color
229
+ - cell background images
232
230
  - cell indents (first line, left, right, and text inset)
233
231
 
234
232
  Numbers conflates style attributes that can be stored in paragraph
235
233
  styles (the style menu in the text panel) with the settings that are
236
234
  available on the Style tab of the Text panel. Some attributes in Numbers
237
- are not applied to new cells when a style is applied. To keep the API
238
- simple, `numbers-parser` packs all styling into a single
239
- [Style](https://masaccio.github.io/numbers-parser/api/style.html)
240
- object. When a document is saved, the attributes not stored in a
241
- 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.
242
240
 
243
241
  Styles are read from cells using the
244
- [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style)
245
- property and you can add new styles with
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
246
244
  [Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
247
245
 
248
246
  ```python
@@ -272,7 +270,7 @@ internally by the package. Changing a data format for cell has no impact
272
270
  on any other cells.
273
271
 
274
272
  Cell formats are changed using
275
- [Table.set_cell_formatting](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_formatting):
273
+ [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
276
274
 
277
275
  ```python
278
276
  table.set_cell_formatting(
@@ -293,9 +291,9 @@ Custom formats are shared across a Document and can be applied to
293
291
  multiple cells in multiple tables. Editing a custom format changes the
294
292
  appearance of data in all cells that share that format. You must first
295
293
  add a custom format to the document using
296
- [Document.add_custom_format](https://masaccio.github.io/numbers-parser/#numbers_parser.Document.add_custom_format)
294
+ [Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format)
297
295
  before assigning it to cells using
298
- [Table.set_cell_formatting](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_formatting):
296
+ [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
299
297
 
300
298
  ```python
301
299
  long_date = doc.add_custom_format(
@@ -322,13 +320,11 @@ table to allow for drawing borders across multiple cells. Setting the
322
320
  border of merged cells is not possible unless the edge of the cells is
323
321
  at the end of the merged region.
324
322
 
325
- Borders are represented using the
326
- [Border](https://masaccio.github.io/numbers-parser/#numbers_parser.Border)
327
- 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
328
325
  current state of a cell border is read using the
329
- [Cell.border](https://masaccio.github.io/numbers-parser/#numbers_parser.Cell.border)
330
- property. The
331
- [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)
332
328
  sets the border for a cell edge or a range of cells.
333
329
 
334
330
  ## API
@@ -400,6 +396,5 @@ Current known limitations of `numbers-parser` are:
400
396
 
401
397
  ## License
402
398
 
403
- All code in this repository is licensed under the [MIT
404
- 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).
405
400
 
@@ -157,8 +157,8 @@ Whilst support for writing numbers files has been stable since version
157
157
  and instead save data to a new file.
158
158
 
159
159
  Cell values are written using
160
- [Table.write()](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.write)
161
- and `numbers-parser` will automatically create empty rows and columns
160
+ [Table.write()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.write) and
161
+ `numbers-parser` will automatically create empty rows and columns
162
162
  for any cell references that are out of range of the current table.
163
163
 
164
164
  ```python
@@ -171,12 +171,9 @@ table.write("B7", datetime(2020, 12, 25))
171
171
  doc.save("new-sheet.numbers")
172
172
  ```
173
173
 
174
- Additional tables and worksheets can be added to a `Document` before
175
- saving using
176
- [Document.add_sheet()](https://masaccio.github.io/numbers-parser/#numbers_parser.Document.add_sheet)
177
- and
178
- [Sheet.add_table()](https://masaccio.github.io/numbers-parser/#numbers_parser.Sheet.add_table)
179
- respectively:
174
+ Additional tables and worksheets can be added to a `Document` before saving using
175
+ [Document.add_sheet()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_sheet) and
176
+ [Sheet.add_table()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Sheet.add_table) respectively:
180
177
 
181
178
  ```python
182
179
  doc = Document()
@@ -192,27 +189,28 @@ doc.save("sheet.numbers")
192
189
  ### Styles
193
190
 
194
191
  `numbers_parser` currently only supports paragraph styles and cell
195
- styles. The following paragraph styles are supported:
192
+ styles. The following styles are supported:
196
193
 
197
194
  - font attributes: bold, italic, underline, strikethrough
198
195
  - font selection and size
199
196
  - text foreground color
200
197
  - horizontal and vertical alignment
201
198
  - cell background color
199
+ - cell background images
202
200
  - cell indents (first line, left, right, and text inset)
203
201
 
204
202
  Numbers conflates style attributes that can be stored in paragraph
205
203
  styles (the style menu in the text panel) with the settings that are
206
204
  available on the Style tab of the Text panel. Some attributes in Numbers
207
- are not applied to new cells when a style is applied. To keep the API
208
- simple, `numbers-parser` packs all styling into a single
209
- [Style](https://masaccio.github.io/numbers-parser/api/style.html)
210
- object. When a document is saved, the attributes not stored in a
211
- paragraph style are applied to each cell that includes it.
205
+ are not applied to new cells when a style is applied.
206
+
207
+ To keep the API simple, `numbers-parser` packs all styling into a single
208
+ [Style](https://masaccio.github.io/numbers-parser/api/style.html) object. When a document is saved, the attributes
209
+ not stored in a paragraph style are applied to each cell that includes it.
212
210
 
213
211
  Styles are read from cells using the
214
- [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style)
215
- property and you can add new styles with
212
+ [Cell.style](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.style) property and you can
213
+ add new styles with
216
214
  [Document.add_style](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_style).
217
215
 
218
216
  ```python
@@ -242,7 +240,7 @@ internally by the package. Changing a data format for cell has no impact
242
240
  on any other cells.
243
241
 
244
242
  Cell formats are changed using
245
- [Table.set_cell_formatting](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_formatting):
243
+ [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
246
244
 
247
245
  ```python
248
246
  table.set_cell_formatting(
@@ -263,9 +261,9 @@ Custom formats are shared across a Document and can be applied to
263
261
  multiple cells in multiple tables. Editing a custom format changes the
264
262
  appearance of data in all cells that share that format. You must first
265
263
  add a custom format to the document using
266
- [Document.add_custom_format](https://masaccio.github.io/numbers-parser/#numbers_parser.Document.add_custom_format)
264
+ [Document.add_custom_format()](https://masaccio.github.io/numbers-parser/api/document.html#numbers_parser.Document.add_custom_format)
267
265
  before assigning it to cells using
268
- [Table.set_cell_formatting](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_formatting):
266
+ [Table.set_cell_formatting()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_formatting):
269
267
 
270
268
  ```python
271
269
  long_date = doc.add_custom_format(
@@ -292,13 +290,11 @@ table to allow for drawing borders across multiple cells. Setting the
292
290
  border of merged cells is not possible unless the edge of the cells is
293
291
  at the end of the merged region.
294
292
 
295
- Borders are represented using the
296
- [Border](https://masaccio.github.io/numbers-parser/#numbers_parser.Border)
297
- class that can be initialized with line width, color and line style. The
293
+ Borders are represented using the [Border](https://masaccio.github.io/numbers-parser/api/border.html) class
294
+ that can be initialized with line width, color and line style. The
298
295
  current state of a cell border is read using the
299
- [Cell.border](https://masaccio.github.io/numbers-parser/#numbers_parser.Cell.border)
300
- property. The
301
- [Table.set_cell_border](https://masaccio.github.io/numbers-parser/#numbers_parser.Table.set_cell_border)
296
+ [Cell.border](https://masaccio.github.io/numbers-parser/api/cells.html#numbers_parser.Cell.border) property
297
+ and [Table.set_cell_border()](https://masaccio.github.io/numbers-parser/api/table.html#numbers_parser.Table.set_cell_border)
302
298
  sets the border for a cell edge or a range of cells.
303
299
 
304
300
  ## API
@@ -370,5 +366,4 @@ Current known limitations of `numbers-parser` are:
370
366
 
371
367
  ## License
372
368
 
373
- All code in this repository is licensed under the [MIT
374
- License](https://github.com/masaccio/numbers-parser/blob/master/LICENSE.rst)
369
+ All code in this repository is licensed under the [MIT License](https://github.com/masaccio/numbers-parser/blob/master/LICENSE.rst).
@@ -12,7 +12,7 @@ name = "numbers-parser"
12
12
  packages = [{include = "numbers_parser", from = "src"}]
13
13
  readme = "README.md"
14
14
  repository = "https://github.com/masaccio/numbers-parser"
15
- version = "4.8.1"
15
+ version = "4.9.0"
16
16
 
17
17
  [tool.poetry.scripts]
18
18
  cat-numbers = "numbers_parser._cat_numbers:main"
@@ -1,9 +1,10 @@
1
1
  import re
2
2
  from collections import namedtuple
3
- from dataclasses import dataclass
3
+ from dataclasses import dataclass, field
4
4
  from datetime import datetime as builtin_datetime
5
5
  from datetime import timedelta as builtin_timedelta
6
6
  from enum import IntEnum
7
+ from os.path import basename
7
8
  from typing import Any, List, Tuple, Union
8
9
  from warnings import warn
9
10
 
@@ -27,6 +28,7 @@ from numbers_parser.constants import (
27
28
  EMPTY_STORAGE_BUFFER,
28
29
  MAX_BASE,
29
30
  MAX_SIGNIFICANT_DIGITS,
31
+ ControlFormattingType,
30
32
  CustomFormattingType,
31
33
  FormattingType,
32
34
  FormatType,
@@ -75,18 +77,43 @@ __all__ = [
75
77
 
76
78
 
77
79
  class BackgroundImage:
78
- def __init__(self, image_data: bytes = None, filename: str = None):
79
- self._data = image_data
80
- self._filename = filename
80
+ """
81
+ A named document style that can be applied to cells.
82
+
83
+ .. code-block:: python
84
+
85
+ fh = open("cats.png", mode="rb")
86
+ image_data = fh.read()
87
+ cats_bg = doc.add_style(
88
+ name="Cats",
89
+ bg_image=BackgroundImage(image_data, "cats.png")
90
+ )
91
+ table.write(0, 0, "❤️ cats", style=cats_bg)
92
+
93
+ Currently only standard image files and not 'advanced' image fills are
94
+ supported. Tiling and scaling is not reported back and cannot be changed
95
+ when saving new cells.
96
+
97
+ Parameters
98
+ ----------
99
+ data: bytes
100
+ Raw image data for a cell background image.
101
+ filename: str
102
+ Path to the image file.
103
+ """
104
+
105
+ def __init__(self, data: bytes = None, filename: str = None):
106
+ self._data = data
107
+ self._filename = basename(filename)
81
108
 
82
109
  @property
83
110
  def data(self) -> bytes:
84
- """The background image as byts for a cell, or None if no image."""
111
+ """bytes: The background image as bytes for a cell, or None if no image."""
85
112
  return self._data
86
113
 
87
114
  @property
88
115
  def filename(self) -> str:
89
- """The image filename for a cell, or None if no image."""
116
+ """str: The image filename for a cell, or None if no image."""
90
117
  return self._filename
91
118
 
92
119
 
@@ -184,6 +211,8 @@ class Style:
184
211
  ------
185
212
  TypeError:
186
213
  If arguments do not match the specified type or for objects have invalid arguments
214
+ IndexError:
215
+ If an image filename already exists in document
187
216
  """
188
217
 
189
218
  alignment: Alignment = DEFAULT_ALIGNMENT_CLASS # : horizontal and vertical alignment
@@ -230,6 +259,7 @@ class Style:
230
259
  return [
231
260
  "alignment",
232
261
  "bg_color",
262
+ "bg_image",
233
263
  "first_indent",
234
264
  "left_indent",
235
265
  "right_indent",
@@ -273,9 +303,9 @@ class Style:
273
303
  if not isinstance(self.font_name, str):
274
304
  raise TypeError("font name must be a string")
275
305
 
276
- for field in ["bold", "italic", "underline", "strikethrough"]:
277
- if not isinstance(getattr(self, field), bool):
278
- raise TypeError(f"{field} argument must be boolean")
306
+ for attr in ["bold", "italic", "underline", "strikethrough"]:
307
+ if not isinstance(getattr(self, attr), bool):
308
+ raise TypeError(f"{attr} argument must be boolean")
279
309
 
280
310
  def __setattr__(self, name: str, value: Any) -> None:
281
311
  """Detect changes to cell styles and flag the style for
@@ -575,8 +605,14 @@ class Cell(Cacheable):
575
605
  else:
576
606
  raise ValueError("Can't determine cell type from type " + type(value).__name__)
577
607
 
578
- def _set_formatting(self, format_id: int, format_type: FormattingType) -> None:
579
- self._storage._set_formatting(format_id, format_type)
608
+ def _set_formatting(
609
+ self,
610
+ format_id: int,
611
+ format_type: FormattingType,
612
+ control_id: int = None,
613
+ is_currency: bool = False,
614
+ ) -> None:
615
+ self._storage.set_formatting(format_id, format_type, control_id, is_currency)
580
616
 
581
617
  def __init__(self, row: int, col: int, value):
582
618
  self._value = value
@@ -705,7 +741,32 @@ class Cell(Cacheable):
705
741
 
706
742
  @property
707
743
  def formatted_value(self) -> str:
708
- """str: The formatted value of the cell as it appears in Numbers."""
744
+ """
745
+ str: The formatted value of the cell as it appears in Numbers.
746
+
747
+ Interactive elements are converted into a suitable text format where
748
+ supported, or as their number values where there is no suitable
749
+ visual representation. Currently supported mappings are:
750
+
751
+ * Checkboxes are U+2610 (Ballow Box) or U+2611 (Ballot Box with Check)
752
+ * Ratings are their star value represented using (U+2605) (Black Star)
753
+
754
+ .. code-block:: python
755
+
756
+ >>> table = doc.sheets[0].tables[0]
757
+ >>> table.cell(0,0).value
758
+ False
759
+ >>> table.cell(0,0).formatted_value
760
+ '☐'
761
+ >>> table.cell(0,1).value
762
+ True
763
+ >>> table.cell(0,1).formatted_value
764
+ '☑'
765
+ >>> table.cell(1,1).value
766
+ 3.0
767
+ >>> table.cell(1,1).formatted_value
768
+ '★★★'
769
+ """
709
770
  if self._storage is None:
710
771
  return ""
711
772
  else:
@@ -1079,16 +1140,22 @@ def xl_col_to_name(col, col_abs=False):
1079
1140
 
1080
1141
  @dataclass()
1081
1142
  class Formatting:
1082
- type: FormattingType = FormattingType.NUMBER
1143
+ allow_none: bool = False
1083
1144
  base_places: int = 0
1084
1145
  base_use_minus_sign: bool = True
1085
1146
  base: int = 10
1147
+ control_format: ControlFormattingType = ControlFormattingType.NUMBER
1086
1148
  currency_code: str = "GBP"
1087
1149
  date_time_format: str = DEFAULT_DATETIME_FORMAT
1088
1150
  decimal_places: int = None
1089
1151
  fraction_accuracy: FractionAccuracy = FractionAccuracy.THREE
1152
+ increment: float = 1.0
1153
+ maximum: float = 100.0
1154
+ minimum: float = 1.0
1155
+ popup_values: List[str] = field(default_factory=lambda: ["Item 1"])
1090
1156
  negative_style: NegativeNumberStyle = NegativeNumberStyle.MINUS
1091
1157
  show_thousands_separator: bool = False
1158
+ type: FormattingType = FormattingType.NUMBER
1092
1159
  use_accounting_style: bool = False
1093
1160
  _format_id = None
1094
1161
 
@@ -11,6 +11,8 @@ from pendulum import datetime, duration
11
11
 
12
12
  from numbers_parser import __name__ as numbers_parser_name
13
13
  from numbers_parser.constants import (
14
+ CHECKBOX_FALSE_VALUE,
15
+ CHECKBOX_TRUE_VALUE,
14
16
  CURRENCY_CELL_TYPE,
15
17
  CUSTOM_TEXT_PLACEHOLDER,
16
18
  DATETIME_FIELD_MAP,
@@ -21,6 +23,7 @@ from numbers_parser.constants import (
21
23
  SECONDS_IN_DAY,
22
24
  SECONDS_IN_HOUR,
23
25
  SECONDS_IN_WEEK,
26
+ STAR_RATING_VALUE,
24
27
  CellPadding,
25
28
  CellType,
26
29
  CustomFormattingType,
@@ -60,7 +63,7 @@ class CellStorage(Cacheable):
60
63
  # "cond_style_id",
61
64
  # "cond_rule_style_id",
62
65
  "formula_id",
63
- # "control_id",
66
+ "control_id",
64
67
  "formula_error_id",
65
68
  "suggest_id",
66
69
  "num_format_id",
@@ -95,7 +98,7 @@ class CellStorage(Cacheable):
95
98
  # self.cond_style_id = None
96
99
  # self.cond_rule_style_id = None
97
100
  self.formula_id = None
98
- # self.control_id = None
101
+ self.control_id = None
99
102
  self.formula_error_id = None
100
103
  self.suggest_id = None
101
104
  self.num_format_id = None
@@ -148,9 +151,9 @@ class CellStorage(Cacheable):
148
151
  if flags & 0x200:
149
152
  self.formula_id = unpack("<i", buffer[offset : offset + 4])[0]
150
153
  offset += 4
151
- # if flags & 0x400:
152
- # self.control_id = unpack("<i", buffer[offset : offset + 4])[0]
153
- # offset += 4
154
+ if flags & 0x400:
155
+ self.control_id = unpack("<i", buffer[offset : offset + 4])[0]
156
+ offset += 4
154
157
  # if flags & 0x800:
155
158
  # self.formula_error_id = unpack("<i", buffer[offset : offset + 4])[0]
156
159
  # offset += 4
@@ -158,7 +161,7 @@ class CellStorage(Cacheable):
158
161
  self.suggest_id = unpack("<i", buffer[offset : offset + 4])[0]
159
162
  offset += 4
160
163
  # Skip unused flags
161
- offset += 4 * bin(flags & 0xD00).count("1")
164
+ offset += 4 * bin(flags & 0x900).count("1")
162
165
  #
163
166
  if flags & 0x2000:
164
167
  self.num_format_id = unpack("<i", buffer[offset : offset + 4])[0]
@@ -300,13 +303,15 @@ class CellStorage(Cacheable):
300
303
  format = self.model.table_format(self.table_id, self.text_format_id)
301
304
  elif self.currency_format_id is not None:
302
305
  format = self.model.table_format(self.table_id, self.currency_format_id)
306
+ elif self.bool_format_id is not None and self.type == CellType.BOOL:
307
+ format = self.model.table_format(self.table_id, self.bool_format_id)
303
308
  elif self.num_format_id is not None:
304
309
  format = self.model.table_format(self.table_id, self.num_format_id)
305
- elif self.bool_format_id is not None:
306
- format = self.model.table_format(self.table_id, self.bool_format_id)
307
310
  else:
308
311
  return str(self.value)
309
312
 
313
+ debug("custom_format: @[%d,%d]: format_type=%s, ", self.row, self.col, format.format_type)
314
+
310
315
  if format.HasField("custom_uid"):
311
316
  format_uuid = NumbersUUID(format.custom_uid).hex
312
317
  format_map = self.model.custom_format_map()
@@ -336,6 +341,10 @@ class CellStorage(Cacheable):
336
341
  return format_fraction(self.d128, format)
337
342
  elif format.format_type == FormatType.SCIENTIFIC:
338
343
  return format_scientific(self.d128, format)
344
+ elif format.format_type == FormatType.CHECKBOX:
345
+ return CHECKBOX_TRUE_VALUE if self.value else CHECKBOX_FALSE_VALUE
346
+ elif format.format_type == FormatType.RATING:
347
+ return STAR_RATING_VALUE * int(self.d128)
339
348
  else:
340
349
  formatted_value = str(self.value)
341
350
  return formatted_value
@@ -362,6 +371,14 @@ class CellStorage(Cacheable):
362
371
 
363
372
  def duration_format(self) -> str:
364
373
  format = self.model.table_format(self.table_id, self.duration_format_id)
374
+ debug(
375
+ "duration_format: @[%d,%d]: table_id=%d, duration_format_id=%d, duration_style=%s",
376
+ self.row,
377
+ self.col,
378
+ self.table_id,
379
+ self.duration_format_id,
380
+ format.duration_style,
381
+ )
365
382
 
366
383
  duration_style = format.duration_style
367
384
  unit_largest = format.duration_unit_largest
@@ -431,15 +448,34 @@ class CellStorage(Cacheable):
431
448
 
432
449
  return duration_str
433
450
 
434
- def _set_formatting(
435
- self, format_id: int, format_type: Union[FormattingType, CustomFormattingType]
451
+ def set_formatting(
452
+ self,
453
+ format_id: int,
454
+ format_type: Union[FormattingType, CustomFormattingType],
455
+ control_id: int = None,
456
+ is_currency: bool = False,
436
457
  ) -> None:
458
+ self.is_currency = is_currency
437
459
  if format_type == FormattingType.CURRENCY:
438
460
  self.currency_format_id = format_id
439
- self.is_currency = True
461
+ elif format_type == FormattingType.TICKBOX:
462
+ self.bool_format_id = format_id
463
+ self.control_id = control_id
464
+ elif format_type == FormattingType.RATING:
465
+ self.num_format_id = format_id
466
+ self.control_id = control_id
467
+ elif format_type in [FormattingType.SLIDER, FormattingType.STEPPER]:
468
+ if is_currency:
469
+ self.currency_format_id = format_id
470
+ else:
471
+ self.num_format_id = format_id
472
+ self.control_id = control_id
473
+ elif format_type == FormattingType.POPUP:
474
+ self.text_format_id = format_id
475
+ self.control_id = control_id
440
476
  elif format_type in [FormattingType.DATETIME, CustomFormattingType.DATETIME]:
441
477
  self.date_format_id = format_id
442
- elif format_type == CustomFormattingType.TEXT:
478
+ elif format_type in [FormattingType.TEXT, CustomFormattingType.TEXT]:
443
479
  self.text_format_id = format_id
444
480
  else:
445
481
  self.num_format_id = format_id