numbers-parser 4.10.1__py3-none-any.whl → 4.10.3__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/model.py CHANGED
@@ -1,4 +1,3 @@
1
- import math
2
1
  import re
3
2
  from array import array
4
3
  from collections import defaultdict
@@ -6,7 +5,6 @@ from hashlib import sha1
6
5
  from pathlib import Path
7
6
  from struct import pack
8
7
  from typing import Dict, List, Tuple, Union
9
- from warnings import warn
10
8
 
11
9
  from numbers_parser.bullets import (
12
10
  BULLET_CONVERSION,
@@ -16,32 +14,24 @@ from numbers_parser.bullets import (
16
14
  from numbers_parser.cell import (
17
15
  RGB,
18
16
  Alignment,
19
- BoolCell,
20
17
  Border,
21
18
  BorderType,
19
+ Cell,
22
20
  CustomFormatting,
23
- DateCell,
24
- DurationCell,
25
- EmptyCell,
26
21
  Formatting,
27
22
  FormattingType,
28
23
  HorizontalJustification,
29
24
  MergeAnchor,
30
25
  MergedCell,
31
26
  MergeReference,
32
- NumberCell,
33
27
  PaddingType,
34
- RichTextCell,
35
28
  Style,
36
- TextCell,
37
29
  VerticalJustification,
38
30
  xl_col_to_name,
39
31
  xl_rowcol_to_cell,
40
32
  )
41
- from numbers_parser.cell_storage import CellStorage
42
33
  from numbers_parser.constants import (
43
34
  ALLOWED_FORMATTING_PARAMETERS,
44
- CURRENCY_CELL_TYPE,
45
35
  CUSTOM_FORMAT_TYPE_MAP,
46
36
  CUSTOM_TEXT_PLACEHOLDER,
47
37
  DEFAULT_COLUMN_WIDTH,
@@ -53,15 +43,15 @@ from numbers_parser.constants import (
53
43
  DEFAULT_TEXT_WRAP,
54
44
  DEFAULT_TILE_SIZE,
55
45
  DOCUMENT_ID,
56
- EPOCH,
57
46
  FORMAT_TYPE_MAP,
58
47
  MAX_TILE_SIZE,
59
48
  PACKAGE_ID,
60
49
  CellInteractionType,
61
50
  FormatType,
51
+ OwnerKind,
62
52
  )
63
53
  from numbers_parser.containers import ObjectStore
64
- from numbers_parser.exceptions import UnsupportedError, UnsupportedWarning
54
+ from numbers_parser.exceptions import UnsupportedError
65
55
  from numbers_parser.formula import TableFormulas
66
56
  from numbers_parser.generated import TNArchives_pb2 as TNArchives
67
57
  from numbers_parser.generated import TSCEArchives_pb2 as TSCEArchives
@@ -106,9 +96,11 @@ class MergeCells:
106
96
  self._references[(row, col)] = MergeAnchor(size)
107
97
 
108
98
  def is_merge_reference(self, row_col: Tuple) -> bool:
99
+ # defaultdict will default this to False for missing entries
109
100
  return isinstance(self._references[row_col], MergeReference)
110
101
 
111
102
  def is_merge_anchor(self, row_col: Tuple) -> bool:
103
+ # defaultdict will default this to False for missing entries
112
104
  return isinstance(self._references[row_col], MergeAnchor)
113
105
 
114
106
  def get(self, row_col: Tuple) -> Union[MergeAnchor, MergeReference]:
@@ -123,9 +115,6 @@ class MergeCells:
123
115
  def merge_cells(self):
124
116
  return [k for k, v in self._references.items() if self.is_merge_anchor(k)]
125
117
 
126
- def __len__(self):
127
- return len(self._references.keys())
128
-
129
118
 
130
119
  class DataLists(Cacheable):
131
120
  """Model for TST.DataList with caching and key generation for new values."""
@@ -226,6 +215,7 @@ class _NumbersModel(Cacheable):
226
215
  self._control_specs = DataLists(self, "control_cell_spec_table", "cell_spec")
227
216
  self._table_data = {}
228
217
  self._styles = None
218
+ self._images = {}
229
219
  self._custom_formats = None
230
220
  self._custom_format_archives = None
231
221
  self._custom_format_ids = None
@@ -603,6 +593,7 @@ class _NumbersModel(Cacheable):
603
593
  formula_owner_ids = self.find_refs("FormulaOwnerDependenciesArchive")
604
594
  for dependency_id in formula_owner_ids: # pragma: no branch
605
595
  obj = self.objects[dependency_id]
596
+ # if obj.owner_kind == OwnerKind.HAUNTED_OWNER:
606
597
  if obj.HasField("base_owner_uid") and obj.HasField(
607
598
  "formula_owner_uid"
608
599
  ): # pragma: no branch
@@ -653,17 +644,20 @@ class _NumbersModel(Cacheable):
653
644
  owner_id_map = self.owner_id_map()
654
645
  table_base_id = self.table_base_id(table_id)
655
646
 
656
- range_table_ids = self.find_refs("RangePrecedentsTileArchive")
657
- for range_id in range_table_ids:
658
- o = self.objects[range_id]
659
- to_owner_id = o.to_owner_id
660
- if owner_id_map[to_owner_id] == table_base_id:
661
- for from_to_range in o.from_to_range:
662
- rect = from_to_range.refers_to_rect
663
- row_start = rect.origin.row
664
- row_end = row_start + rect.size.num_rows - 1
665
- col_start = rect.origin.column
666
- col_end = col_start + rect.size.num_columns - 1
647
+ formula_table_ids = self.find_refs("FormulaOwnerDependenciesArchive")
648
+ for formula_id in formula_table_ids:
649
+ dependencies = self.objects[formula_id]
650
+ if dependencies.owner_kind != OwnerKind.MERGE_OWNER:
651
+ continue
652
+ for record in dependencies.range_dependencies.back_dependency:
653
+ to_owner_id = record.internal_range_reference.owner_id
654
+ if owner_id_map[to_owner_id] == table_base_id:
655
+ record_range = record.internal_range_reference.range
656
+ row_start = record_range.top_left_row
657
+ row_end = record_range.bottom_right_row
658
+ col_start = record_range.top_left_column
659
+ col_end = record_range.bottom_right_column
660
+
667
661
  size = (row_end - row_start + 1, col_end - col_start + 1)
668
662
  for row in range(row_start, row_end + 1):
669
663
  for col in range(col_start, col_end + 1):
@@ -853,8 +847,6 @@ class _NumbersModel(Cacheable):
853
847
 
854
848
  def recalculate_merged_cells(self, table_id: int):
855
849
  merge_cells = self.merge_cells(table_id)
856
- if len(merge_cells) == 0:
857
- return
858
850
 
859
851
  merge_map_id, merge_map = self.objects.create_object_from_dict(
860
852
  "CalculationEngine", {}, TSTArchives.MergeRegionMapArchive
@@ -884,7 +876,7 @@ class _NumbersModel(Cacheable):
884
876
  current_offset = 0
885
877
 
886
878
  for col in range(len(data[row])):
887
- buffer = self.pack_cell_storage(table_id, data, row, col)
879
+ buffer = data[row][col]._to_buffer()
888
880
  if buffer is not None:
889
881
  cell_storage += buffer
890
882
  # Always use wide offsets
@@ -1233,8 +1225,6 @@ class _NumbersModel(Cacheable):
1233
1225
  )
1234
1226
  )
1235
1227
 
1236
- data = [[EmptyCell(row, col) for col in range(num_cols)] for row in range(num_rows)]
1237
-
1238
1228
  row_headers_id, _ = self.objects.create_object_from_dict(
1239
1229
  "Index/Tables/HeaderStorageBucket-{}",
1240
1230
  {"bucketHashFunction": 1},
@@ -1248,6 +1238,10 @@ class _NumbersModel(Cacheable):
1248
1238
  TSPMessages.Reference(identifier=row_headers_id)
1249
1239
  )
1250
1240
 
1241
+ data = [
1242
+ [Cell._empty_cell(table_model_id, row, col, self) for col in range(num_cols)]
1243
+ for row in range(num_rows)
1244
+ ]
1251
1245
  self.recalculate_table_data(table_model_id, data)
1252
1246
 
1253
1247
  table_info_id, table_info = self.objects.create_object_from_dict(
@@ -1546,17 +1540,22 @@ class _NumbersModel(Cacheable):
1546
1540
 
1547
1541
  def add_cell_style(self, style: Style) -> int:
1548
1542
  if style.bg_image is not None:
1549
- datas = self.objects[PACKAGE_ID].datas
1550
- image_id = self.next_image_identifier()
1551
- datas.append(
1552
- TSPArchiveMessages.DataInfo(
1553
- identifier=image_id,
1554
- digest=sha1(style.bg_image.data).digest(),
1555
- preferred_file_name=style.bg_image.filename,
1556
- file_name=style.bg_image.filename,
1557
- materialized_length=len(style.bg_image.data),
1543
+ digest = sha1(style.bg_image.data).digest()
1544
+ if digest in self._images:
1545
+ image_id = self._images[digest]
1546
+ else:
1547
+ datas = self.objects[PACKAGE_ID].datas
1548
+ image_id = self.next_image_identifier()
1549
+ datas.append(
1550
+ TSPArchiveMessages.DataInfo(
1551
+ identifier=image_id,
1552
+ digest=digest,
1553
+ preferred_file_name=style.bg_image.filename,
1554
+ file_name=style.bg_image.filename,
1555
+ materialized_length=len(style.bg_image.data),
1556
+ )
1558
1557
  )
1559
- )
1558
+ self._images[digest] = image_id
1560
1559
  color_attrs = {
1561
1560
  "cell_fill": {
1562
1561
  "image": {
@@ -1618,16 +1617,16 @@ class _NumbersModel(Cacheable):
1618
1617
 
1619
1618
  return cell_style_id
1620
1619
 
1621
- def text_style_object_id(self, cell_storage) -> int:
1622
- if cell_storage.text_style_id is None:
1620
+ def text_style_object_id(self, cell: Cell) -> int:
1621
+ if cell._text_style_id is None:
1623
1622
  return None
1624
- entry = self._table_styles.lookup_value(cell_storage.table_id, cell_storage.text_style_id)
1623
+ entry = self._table_styles.lookup_value(cell._table_id, cell._text_style_id)
1625
1624
  return entry.reference.identifier
1626
1625
 
1627
- def cell_style_object_id(self, cell_storage) -> int:
1628
- if cell_storage.cell_style_id is None:
1626
+ def cell_style_object_id(self, cell: Cell) -> int:
1627
+ if cell._cell_style_id is None:
1629
1628
  return None
1630
- entry = self._table_styles.lookup_value(cell_storage.table_id, cell_storage.cell_style_id)
1629
+ entry = self._table_styles.lookup_value(cell._table_id, cell._cell_style_id)
1631
1630
  return entry.reference.identifier
1632
1631
 
1633
1632
  def custom_style_name(self) -> str:
@@ -1683,166 +1682,10 @@ class _NumbersModel(Cacheable):
1683
1682
  else:
1684
1683
  return "Custom Format 1"
1685
1684
 
1686
- def pack_cell_storage( # noqa: PLR0912, PLR0915
1687
- self, table_id: int, data: List, row: int, col: int
1688
- ) -> bytearray:
1689
- """Create a storage buffer for a cell using v5 (modern) layout."""
1690
- cell = data[row][col]
1691
- if cell._style is not None:
1692
- if cell._style._text_style_obj_id is not None:
1693
- cell._storage.text_style_id = self._table_styles.lookup_key(
1694
- cell._table_id,
1695
- TSPMessages.Reference(identifier=cell._style._text_style_obj_id),
1696
- )
1697
- self.add_component_reference(
1698
- cell._style._text_style_obj_id,
1699
- parent_id=self._table_styles.id(cell._table_id),
1700
- )
1701
-
1702
- if cell._style._cell_style_obj_id is not None:
1703
- cell._storage.cell_style_id = self._table_styles.lookup_key(
1704
- cell._table_id,
1705
- TSPMessages.Reference(identifier=cell._style._cell_style_obj_id),
1706
- )
1707
- self.add_component_reference(
1708
- cell._style._cell_style_obj_id,
1709
- parent_id=self._table_styles.id(cell._table_id),
1710
- )
1711
-
1712
- length = 12
1713
- if isinstance(cell, NumberCell):
1714
- flags = 1
1715
- length += 16
1716
- if cell._storage.is_currency:
1717
- cell_type = CURRENCY_CELL_TYPE
1718
- else:
1719
- cell_type = TSTArchives.numberCellType
1720
- value = pack_decimal128(cell.value)
1721
- elif isinstance(cell, TextCell):
1722
- flags = 8
1723
- length += 4
1724
- cell_type = TSTArchives.textCellType
1725
- value = pack("<i", self.table_string_key(table_id, cell.value))
1726
- elif isinstance(cell, DateCell):
1727
- flags = 4
1728
- length += 8
1729
- cell_type = TSTArchives.dateCellType
1730
- date_delta = cell.value - EPOCH
1731
- value = pack("<d", float(date_delta.total_seconds()))
1732
- elif isinstance(cell, BoolCell):
1733
- flags = 2
1734
- length += 8
1735
- cell_type = TSTArchives.boolCellType
1736
- value = pack("<d", float(cell.value))
1737
- elif isinstance(cell, DurationCell):
1738
- flags = 2
1739
- length += 8
1740
- cell_type = TSTArchives.durationCellType
1741
- value = value = pack("<d", float(cell.value.total_seconds()))
1742
- elif isinstance(cell, EmptyCell):
1743
- if cell._style is not None or cell._storage is not None:
1744
- flags = 0
1745
- cell_type = TSTArchives.emptyCellValueType
1746
- value = b""
1747
- else:
1748
- return None
1749
- elif isinstance(cell, MergedCell):
1750
- return None
1751
- elif isinstance(cell, RichTextCell):
1752
- flags = 0
1753
- length += 4
1754
- cell_type = TSTArchives.automaticCellType
1755
- value = pack("<i", cell._storage.rich_id)
1756
- else:
1757
- data_type = type(cell).__name__
1758
- table_name = self.table_name(table_id)
1759
- warn(
1760
- f"@{table_name}:[{row},{col}]: unsupported data type {data_type} for save",
1761
- UnsupportedWarning,
1762
- stacklevel=1,
1763
- )
1764
- return None
1765
-
1766
- storage = bytearray(12)
1767
- storage[0] = 5
1768
- storage[1] = cell_type
1769
- storage += value
1770
-
1771
- if cell._storage.rich_id is not None:
1772
- flags |= 0x10
1773
- length += 4
1774
- storage += pack("<i", cell._storage.rich_id)
1775
- if cell._storage.cell_style_id is not None:
1776
- flags |= 0x20
1777
- length += 4
1778
- storage += pack("<i", cell._storage.cell_style_id)
1779
- if cell._storage.text_style_id is not None:
1780
- flags |= 0x40
1781
- length += 4
1782
- storage += pack("<i", cell._storage.text_style_id)
1783
- if cell._storage.formula_id is not None:
1784
- flags |= 0x200
1785
- length += 4
1786
- storage += pack("<i", cell._storage.formula_id)
1787
- if cell._storage.control_id is not None:
1788
- flags |= 0x400
1789
- length += 4
1790
- storage += pack("<i", cell._storage.control_id)
1791
- if cell._storage.suggest_id is not None:
1792
- flags |= 0x1000
1793
- length += 4
1794
- storage += pack("<i", cell._storage.suggest_id)
1795
- if cell._storage.num_format_id is not None:
1796
- flags |= 0x2000
1797
- length += 4
1798
- storage += pack("<i", cell._storage.num_format_id)
1799
- storage[6] |= 1
1800
- # storage[6:8] = pack("<h", 1)
1801
- if cell._storage.currency_format_id is not None:
1802
- flags |= 0x4000
1803
- length += 4
1804
- storage += pack("<i", cell._storage.currency_format_id)
1805
- storage[6] |= 2
1806
- if cell._storage.date_format_id is not None:
1807
- flags |= 0x8000
1808
- length += 4
1809
- storage += pack("<i", cell._storage.date_format_id)
1810
- storage[6] |= 8
1811
- if cell._storage.duration_format_id is not None:
1812
- flags |= 0x10000
1813
- length += 4
1814
- storage += pack("<i", cell._storage.duration_format_id)
1815
- storage[6] |= 4
1816
- if cell._storage.text_format_id is not None:
1817
- flags |= 0x20000
1818
- length += 4
1819
- storage += pack("<i", cell._storage.text_format_id)
1820
- if cell._storage.bool_format_id is not None:
1821
- flags |= 0x40000
1822
- length += 4
1823
- storage += pack("<i", cell._storage.bool_format_id)
1824
- storage[6] |= 0x20
1825
- if cell._storage.string_id is not None:
1826
- storage[6] |= 0x80
1827
-
1828
- storage[8:12] = pack("<i", flags)
1829
- if len(storage) < 32:
1830
- storage += bytearray(32 - length)
1831
-
1832
- return storage[0:length]
1833
-
1834
1685
  @cache()
1835
1686
  def table_formulas(self, table_id: int):
1836
1687
  return TableFormulas(self, table_id)
1837
1688
 
1838
- @cache(num_args=3)
1839
- def table_cell_decode(self, table_id: int, row: int, col: int) -> Dict:
1840
- buffer = self.storage_buffer(table_id, row, col)
1841
- if buffer is None:
1842
- return None
1843
-
1844
- return CellStorage(self, table_id, buffer, row, col)
1845
-
1846
1689
  @cache(num_args=2)
1847
1690
  def table_rich_text(self, table_id: int, string_key: int) -> Dict:
1848
1691
  """Extract bullets and hyperlinks from a rich text data cell."""
@@ -1934,41 +1777,41 @@ class _NumbersModel(Cacheable):
1934
1777
  "hyperlinks": hyperlinks,
1935
1778
  }
1936
1779
 
1937
- def cell_text_style(self, cell_storage: object) -> object:
1780
+ def cell_text_style(self, cell: Cell) -> object:
1938
1781
  """Return the text style object for the cell or, if none
1939
1782
  is defined, the default header, footer or body style.
1940
1783
  """
1941
- if cell_storage.text_style_id is not None:
1942
- return self.table_style(cell_storage.table_id, cell_storage.text_style_id)
1784
+ if cell._text_style_id is not None:
1785
+ return self.table_style(cell._table_id, cell._text_style_id)
1943
1786
 
1944
- table_model = self.objects[cell_storage.table_id]
1945
- if cell_storage.row in range(table_model.number_of_header_rows):
1787
+ table_model = self.objects[cell._table_id]
1788
+ if cell.row in range(table_model.number_of_header_rows):
1946
1789
  return self.objects[table_model.header_row_text_style.identifier]
1947
- elif cell_storage.col in range(table_model.number_of_header_columns):
1790
+ elif cell.col in range(table_model.number_of_header_columns):
1948
1791
  return self.objects[table_model.header_column_text_style.identifier]
1949
1792
  elif table_model.number_of_footer_rows > 0:
1950
1793
  start_row_num = table_model.number_of_rows - table_model.number_of_footer_rows
1951
1794
  end_row_num = start_row_num + table_model.number_of_footer_rows
1952
- if cell_storage.row in range(start_row_num, end_row_num):
1795
+ if cell.row in range(start_row_num, end_row_num):
1953
1796
  return self.objects[table_model.footer_row_text_style.identifier]
1954
1797
  return self.objects[table_model.body_text_style.identifier]
1955
1798
 
1956
- def cell_alignment(self, cell_storage: object) -> Alignment:
1957
- style = self.cell_text_style(cell_storage)
1799
+ def cell_alignment(self, cell: Cell) -> Alignment:
1800
+ style = self.cell_text_style(cell)
1958
1801
  horizontal = HorizontalJustification(self.para_property(style, "alignment"))
1959
1802
 
1960
- if cell_storage.cell_style_id is None:
1803
+ if cell._cell_style_id is None:
1961
1804
  vertical = VerticalJustification.TOP
1962
1805
  else:
1963
- style = self.table_style(cell_storage.table_id, cell_storage.cell_style_id)
1806
+ style = self.table_style(cell._table_id, cell._cell_style_id)
1964
1807
  vertical = VerticalJustification(self.cell_property(style, "vertical_alignment"))
1965
1808
  return Alignment(horizontal, vertical)
1966
1809
 
1967
- def cell_bg_color(self, cell_storage: object) -> Union[Tuple, List[Tuple]]:
1968
- if cell_storage.cell_style_id is None:
1810
+ def cell_bg_color(self, cell: Cell) -> Union[Tuple, List[Tuple]]:
1811
+ if cell._cell_style_id is None:
1969
1812
  return None
1970
1813
 
1971
- style = self.table_style(cell_storage.table_id, cell_storage.cell_style_id)
1814
+ style = self.table_style(cell._table_id, cell._cell_style_id)
1972
1815
  cell_properties = style.cell_properties.cell_fill
1973
1816
 
1974
1817
  if cell_properties.HasField("color"):
@@ -2006,67 +1849,67 @@ class _NumbersModel(Cacheable):
2006
1849
  else:
2007
1850
  return getattr(style.cell_properties, field)
2008
1851
 
2009
- def cell_is_bold(self, obj: object) -> bool:
2010
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1852
+ def cell_is_bold(self, obj: Union[Cell, object]) -> bool:
1853
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2011
1854
  return self.char_property(style, "bold")
2012
1855
 
2013
- def cell_is_italic(self, obj: object) -> bool:
2014
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1856
+ def cell_is_italic(self, obj: Union[Cell, object]) -> bool:
1857
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2015
1858
  return self.char_property(style, "italic")
2016
1859
 
2017
- def cell_is_underline(self, obj: object) -> bool:
2018
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1860
+ def cell_is_underline(self, obj: Union[Cell, object]) -> bool:
1861
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2019
1862
  underline = self.char_property(style, "underline")
2020
1863
  return underline != CharacterStyle.UnderlineType.kNoUnderline
2021
1864
 
2022
- def cell_is_strikethrough(self, obj: object) -> bool:
2023
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1865
+ def cell_is_strikethrough(self, obj: Union[Cell, object]) -> bool:
1866
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2024
1867
  strikethru = self.char_property(style, "strikethru")
2025
1868
  return strikethru != CharacterStyle.StrikethruType.kNoStrikethru
2026
1869
 
2027
- def cell_style_name(self, obj: object) -> bool:
2028
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1870
+ def cell_style_name(self, obj: Union[Cell, object]) -> bool:
1871
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2029
1872
  return style.super.name
2030
1873
 
2031
- def cell_font_color(self, obj: object) -> Tuple:
2032
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1874
+ def cell_font_color(self, obj: Union[Cell, object]) -> Tuple:
1875
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2033
1876
  return rgb(self.char_property(style, "font_color"))
2034
1877
 
2035
- def cell_font_size(self, obj: object) -> float:
2036
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1878
+ def cell_font_size(self, obj: Union[Cell, object]) -> float:
1879
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2037
1880
  return self.char_property(style, "font_size")
2038
1881
 
2039
- def cell_font_name(self, obj: object) -> str:
2040
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1882
+ def cell_font_name(self, obj: Union[Cell, object]) -> str:
1883
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2041
1884
  font_name = self.char_property(style, "font_name")
2042
1885
  return FONT_NAME_TO_FAMILY[font_name]
2043
1886
 
2044
- def cell_first_indent(self, obj: object) -> float:
2045
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1887
+ def cell_first_indent(self, obj: Union[Cell, object]) -> float:
1888
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2046
1889
  return self.para_property(style, "first_line_indent")
2047
1890
 
2048
- def cell_left_indent(self, obj: object) -> float:
2049
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1891
+ def cell_left_indent(self, obj: Union[Cell, object]) -> float:
1892
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2050
1893
  return self.para_property(style, "left_indent")
2051
1894
 
2052
- def cell_right_indent(self, obj: object) -> float:
2053
- style = self.cell_text_style(obj) if isinstance(obj, CellStorage) else obj
1895
+ def cell_right_indent(self, obj: Union[Cell, object]) -> float:
1896
+ style = self.cell_text_style(obj) if isinstance(obj, Cell) else obj
2054
1897
  return self.para_property(style, "right_indent")
2055
1898
 
2056
- def cell_text_inset(self, cell_storage: CellStorage) -> float:
2057
- if cell_storage.cell_style_id is None:
1899
+ def cell_text_inset(self, cell: Cell) -> float:
1900
+ if cell._cell_style_id is None:
2058
1901
  return DEFAULT_TEXT_INSET
2059
1902
  else:
2060
- style = self.table_style(cell_storage.table_id, cell_storage.cell_style_id)
1903
+ style = self.table_style(cell._table_id, cell._cell_style_id)
2061
1904
  padding = self.cell_property(style, "padding")
2062
1905
  # Padding is always identical (only one UI setting)
2063
1906
  return padding.left
2064
1907
 
2065
- def cell_text_wrap(self, cell_storage: CellStorage) -> float:
2066
- if cell_storage.cell_style_id is None:
1908
+ def cell_text_wrap(self, cell: Cell) -> float:
1909
+ if cell._cell_style_id is None:
2067
1910
  return DEFAULT_TEXT_WRAP
2068
1911
  else:
2069
- style = self.table_style(cell_storage.table_id, cell_storage.cell_style_id)
1912
+ style = self.table_style(cell._table_id, cell._cell_style_id)
2070
1913
  return self.cell_property(style, "text_wrap")
2071
1914
 
2072
1915
  def stroke_type(self, stroke_run: object) -> str:
@@ -2418,23 +2261,6 @@ def clear_field_container(obj):
2418
2261
  _ = obj.pop()
2419
2262
 
2420
2263
 
2421
- def pack_decimal128(value: float) -> bytearray:
2422
- buffer = bytearray(16)
2423
- exp = math.floor(math.log10(math.e) * math.log(abs(value))) if value != 0.0 else 0
2424
- exp += 0x1820 - 16
2425
- mantissa = abs(int(value / math.pow(10, exp - 0x1820)))
2426
- buffer[15] |= exp >> 7
2427
- buffer[14] |= (exp & 0x7F) << 1
2428
- i = 0
2429
- while mantissa >= 1:
2430
- buffer[i] = mantissa & 0xFF
2431
- i += 1
2432
- mantissa = int(mantissa / 256)
2433
- if value < 0:
2434
- buffer[15] |= 0x80
2435
- return buffer
2436
-
2437
-
2438
2264
  def field_references(obj: object) -> dict:
2439
2265
  """Return a dict of all fields in an object that are references to other objects."""
2440
2266
  return {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: numbers-parser
3
- Version: 4.10.1
3
+ Version: 4.10.3
4
4
  Summary: Read and write Apple Numbers spreadsheets
5
5
  Home-page: https://github.com/masaccio/numbers-parser
6
6
  License: MIT
@@ -19,7 +19,7 @@ Requires-Dist: compact-json (>=1.1.3,<2.0.0)
19
19
  Requires-Dist: enum-tools (>=0.11.0,<0.12.0)
20
20
  Requires-Dist: importlib-resources (>=6.1.1,<7.0.0)
21
21
  Requires-Dist: pendulum (>=3.0,<4.0)
22
- Requires-Dist: protobuf (>=4.21.1,<5.0.0)
22
+ Requires-Dist: protobuf
23
23
  Requires-Dist: python-snappy (>=0.6.1,<0.7.0)
24
24
  Requires-Dist: regex (>=2022.9.13,<2023.0.0)
25
25
  Requires-Dist: roman (>=3.3,<4.0)
@@ -37,7 +37,7 @@ Description-Content-Type: text/markdown
37
37
  generated by Numbers version 10.3, and up with the latest tested version being 13.2
38
38
  (current as of September 2023).
39
39
 
40
- It supports and is tested against Python versions from 3.8 onwards. It is not compatible
40
+ It supports and is tested against Python versions from 3.9 onwards. It is not compatible
41
41
  with earlier versions of Python.
42
42
 
43
43
  ## Installation
@@ -376,24 +376,27 @@ cannot return lower-case versions of AM/PM.
376
376
 
377
377
  ## Limitations
378
378
 
379
- Current known limitations of `numbers-parser` are:
379
+ Current known limitations of `numbers-parser` which may be implemented in the future are:
380
380
 
381
- - Formulas cannot be written to a document
382
381
  - Table styles that allow new tables to adopt a style across the whole
383
- table are not planned.
382
+ table are not suppported
384
383
  - Creating cells of type `BulletedTextCell` is not supported
385
384
  - New tables are inserted with a fixed offset below the last table in a
386
385
  worksheet which does not take into account title or caption size
386
+ - Formulas cannot be written to a document
387
+ - Pivot tables are unsupported and saving a document with a pivot table issues
388
+ a UnsupportedWarning.
389
+
390
+ The following limitations are expected to always remain:
391
+
387
392
  - New sheets insert tables with formats copied from the first table in
388
393
  the previous sheet rather than default table formats
389
394
  - Due to a limitation in Python’s
390
395
  [ZipFile](https://docs.python.org/3/library/zipfile.html), Python
391
- versions older than 3.11 do not support image filenames with UTF-8
392
- characters (see [issue
393
- 69](https://github.com/masaccio/numbers-parser/issues/69)).
394
- [Cell.style.bg_image](https://masaccio.github.io/numbers-parser/#numbers_parser.Style)
395
- returns `None` for such files and issues a `RuntimeWarning`.
396
- - Pivot tables are unsupported, but re-saving a document is believed to work. Saving a document with a pivot table issues a UnsupportedWarning.
396
+ versions older than 3.11 do not support image filenames with UTF-8 characters
397
+ [Cell.add_style.bg_image()](https://masaccio.github.io/numbers-parser/api/sheet.html#numbers_parser.Style) returns
398
+ `None` for such files and issues a `RuntimeWarning`
399
+ (see [issue 69](https://github.com/masaccio/numbers-parser/ssues/69) for details).
397
400
 
398
401
  ## License
399
402
 
@@ -1,14 +1,13 @@
1
- numbers_parser/__init__.py,sha256=8quRZ78ii_WDvog0ElZIWGL8pNq6W5Ab8MaJ4eZTm7Y,1943
1
+ numbers_parser/__init__.py,sha256=w0ZhMcFpVol1VwjyYHjvqBmshvsMDfLIyFX-GmpOpMQ,1410
2
2
  numbers_parser/_cat_numbers.py,sha256=-HboBJT11Vjcr8sjLZl7Z6qAapnPEc_kFYq6PTqON20,4619
3
3
  numbers_parser/_unpack_numbers.py,sha256=U5SL16uNNe0BvYtUtIYJT-t3RN36Q8U-8UTr7lnOylw,7040
4
4
  numbers_parser/bullets.py,sha256=OnVVMPjhTDrC-ncw52Gb00UEXNmn2Rvd3xi7lfqW3hk,2616
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=htdFCXNmCisSjcPq4X7s70Pi4FbnW98O0b2hhMnA2Sk,9781
8
- numbers_parser/containers.py,sha256=FcNXbFM_VOe1XCT_WAuFOJwE47h6961_zv2_6OIsVoo,4749
5
+ numbers_parser/cell.py,sha256=q8gDmrncUpZTNx-XGfTRDToDVFudSkOMhDLMOEJU5ss,75511
6
+ numbers_parser/constants.py,sha256=gROxeu8wDj-a96rWy2bR_c8CoF0RagqWI3V4SE6SN4M,9908
7
+ numbers_parser/containers.py,sha256=EnHcsmHIFLsRiZsjhqMcJL20k6sHMsd5neVDFNUDuwc,4751
9
8
  numbers_parser/currencies.py,sha256=8k4a3WKmDoHeurkDICymHX13N7ManHSTaka_JNXCZYA,3767
10
9
  numbers_parser/data/empty.numbers,sha256=8JOp035V-p2ff9_Wao7mLcYvb6_if6O2cus_esjVA9k,90316
11
- numbers_parser/document.py,sha256=Aj26VBmR2yQ16zD4d_BQzzuWrHlkmi2r2RJG67D07Ao,56748
10
+ numbers_parser/document.py,sha256=34BegFJnEONgaAJbKG5zlaiJ4RnaJmQw3Zbw54SM8YM,56813
12
11
  numbers_parser/exceptions.py,sha256=G8dASUQZI8ksHYRVfdGWJzgsJD5CBpcZvmDJUZTqT-c,670
13
12
  numbers_parser/experimental.py,sha256=WARjTa-2ePb8Ga8Q6oDP6EJCs12ofLRF2YpwzUu66ZI,374
14
13
  numbers_parser/formula.py,sha256=JRsG0L21wS70oJ-FB46Amyoy-sKizWb-iUhSXUcVJ-U,10572
@@ -50,11 +49,11 @@ numbers_parser/generated/functionmap.py,sha256=VdZo0ERMYONcrnJFwABcSCHb8pjA4wY2o
50
49
  numbers_parser/iwafile.py,sha256=xHDBZWd5EEaPHonlAAl4OYmmaXg_Nw4H0OmItt91URg,11861
51
50
  numbers_parser/iwork.py,sha256=U0Irxt-3v3vFP_feCuYMDxWUgqeq_YHMCfw1tsDpUcM,8947
52
51
  numbers_parser/mapping.py,sha256=in8W3S8DmTcPefFaxnATLw0FQ4YnFsnAE-cl5dljzJE,32215
53
- numbers_parser/model.py,sha256=6eLkfo78Ogzcum8QufsuynzUf0mkCCp024rSBK7QO00,103436
52
+ numbers_parser/model.py,sha256=0DKFurS9DSLwdIvij4Zz5CjDDvlYC4ZlN2DnoxW4nIU,97058
54
53
  numbers_parser/numbers_cache.py,sha256=1ghEBghQAYFpPiEeOtb74i016mXc039v1pOubbqvaLs,1141
55
54
  numbers_parser/numbers_uuid.py,sha256=-LeAj_ULC0va57pEmyegGY0xXqkNNjyuLukCaiQJhOk,2642
56
- numbers_parser-4.10.1.dist-info/LICENSE.rst,sha256=8vTa1-5KSdHrTpU9rlheO5005EWReEPMpjV7BjSaMc4,1050
57
- numbers_parser-4.10.1.dist-info/METADATA,sha256=c6-vpgtOUUFmCdKasNJqwrMyN9lTHzfOcKJSzPepXGM,16198
58
- numbers_parser-4.10.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
59
- numbers_parser-4.10.1.dist-info/entry_points.txt,sha256=V91uB9vBPxf3eCY1h-0syv21imYCT0MJfMxf87DmwIk,115
60
- numbers_parser-4.10.1.dist-info/RECORD,,
55
+ numbers_parser-4.10.3.dist-info/LICENSE.rst,sha256=8vTa1-5KSdHrTpU9rlheO5005EWReEPMpjV7BjSaMc4,1050
56
+ numbers_parser-4.10.3.dist-info/METADATA,sha256=CNp5aHpYLS0CehOLWXXE-vE4hl3w6qVsaEEZagB6RCE,16267
57
+ numbers_parser-4.10.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
58
+ numbers_parser-4.10.3.dist-info/entry_points.txt,sha256=V91uB9vBPxf3eCY1h-0syv21imYCT0MJfMxf87DmwIk,115
59
+ numbers_parser-4.10.3.dist-info/RECORD,,