Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/styles/named_styles.py: 47%
140 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1# Copyright (c) 2010-2022 openpyxl
3from openpyxl.compat import safe_string
5from openpyxl.descriptors import (
6 Typed,
7 Integer,
8 Bool,
9 String,
10 Sequence,
11)
12from openpyxl.descriptors.excel import ExtensionList
13from openpyxl.descriptors.serialisable import Serialisable
15from .fills import PatternFill, Fill
16from .fonts import Font
17from .borders import Border
18from .alignment import Alignment
19from .protection import Protection
20from .numbers import (
21 NumberFormatDescriptor,
22 BUILTIN_FORMATS_MAX_SIZE,
23 BUILTIN_FORMATS_REVERSE,
24)
25from .cell_style import (
26 StyleArray,
27 CellStyle,
28)
31class NamedStyle(Serialisable):
33 """
34 Named and editable styles
35 """
37 font = Typed(expected_type=Font)
38 fill = Typed(expected_type=Fill)
39 border = Typed(expected_type=Border)
40 alignment = Typed(expected_type=Alignment)
41 number_format = NumberFormatDescriptor()
42 protection = Typed(expected_type=Protection)
43 builtinId = Integer(allow_none=True)
44 hidden = Bool(allow_none=True)
45 xfId = Integer(allow_none=True)
46 name = String()
47 _wb = None
48 _style = StyleArray()
51 def __init__(self,
52 name="Normal",
53 font=Font(),
54 fill=PatternFill(),
55 border=Border(),
56 alignment=Alignment(),
57 number_format=None,
58 protection=Protection(),
59 builtinId=None,
60 hidden=False,
61 xfId=None,
62 ):
63 self.name = name
64 self.font = font
65 self.fill = fill
66 self.border = border
67 self.alignment = alignment
68 self.number_format = number_format
69 self.protection = protection
70 self.builtinId = builtinId
71 self.hidden = hidden
72 self._wb = None
73 self._style = StyleArray()
76 def __setattr__(self, attr, value):
77 super(NamedStyle, self).__setattr__(attr, value)
78 if getattr(self, '_wb', None) and attr in ( 78 ↛ 81line 78 didn't jump to line 81, because the condition on line 78 was never true
79 'font', 'fill', 'border', 'alignment', 'number_format', 'protection',
80 ):
81 self._recalculate()
84 def __iter__(self):
85 for key in ('name', 'builtinId', 'hidden', 'xfId'):
86 value = getattr(self, key, None)
87 if value is not None:
88 yield key, safe_string(value)
91 @property
92 def xfId(self):
93 """
94 Index of the style in the list of named styles
95 """
96 return self._style.xfId
99 def _set_index(self, idx):
100 """
101 Allow the containing list to set the index
102 """
103 self._style.xfId = idx
106 def bind(self, wb):
107 """
108 Bind a named style to a workbook
109 """
110 self._wb = wb
111 self._recalculate()
114 def _recalculate(self):
115 self._style.fontId = self._wb._fonts.add(self.font)
116 self._style.borderId = self._wb._borders.add(self.border)
117 self._style.fillId = self._wb._fills.add(self.fill)
118 self._style.protectionId = self._wb._protections.add(self.protection)
119 self._style.alignmentId = self._wb._alignments.add(self.alignment)
120 fmt = self.number_format
121 if fmt in BUILTIN_FORMATS_REVERSE:
122 fmt = BUILTIN_FORMATS_REVERSE[fmt]
123 else:
124 fmt = self._wb._number_formats.add(self.number_format) + (
125 BUILTIN_FORMATS_MAX_SIZE)
126 self._style.numFmtId = fmt
129 def as_tuple(self):
130 """Return a style array representing the current style"""
131 return self._style
134 def as_xf(self):
135 """
136 Return equivalent XfStyle
137 """
138 xf = CellStyle.from_array(self._style)
139 xf.xfId = None
140 xf.pivotButton = None
141 xf.quotePrefix = None
142 if self.alignment != Alignment():
143 xf.alignment = self.alignment
144 if self.protection != Protection():
145 xf.protection = self.protection
146 return xf
149 def as_name(self):
150 """
151 Return relevant named style
153 """
154 named = _NamedCellStyle(
155 name=self.name,
156 builtinId=self.builtinId,
157 hidden=self.hidden,
158 xfId=self.xfId
159 )
160 return named
163class NamedStyleList(list):
164 """
165 Named styles are editable and can be applied to multiple objects
167 As only the index is stored in referencing objects the order mus
168 be preserved.
169 """
171 @property
172 def names(self):
173 return [s.name for s in self]
176 def __getitem__(self, key):
177 if isinstance(key, int):
178 return super(NamedStyleList, self).__getitem__(key)
180 names = self.names
181 if key not in names:
182 raise KeyError("No named style with the name{0} exists".format(key))
184 for idx, name in enumerate(names):
185 if name == key:
186 return self[idx]
189 def append(self, style):
190 if not isinstance(style, NamedStyle):
191 raise TypeError("""Only NamedStyle instances can be added""")
192 elif style.name in self.names:
193 raise ValueError("""Style {0} exists already""".format(style.name))
194 style._set_index(len(self))
195 super(NamedStyleList, self).append(style)
198class _NamedCellStyle(Serialisable):
200 """
201 Pointer-based representation of named styles in XML
202 xfId refers to the corresponding CellStyleXfs
204 Not used in client code.
205 """
207 tagname = "cellStyle"
209 name = String()
210 xfId = Integer()
211 builtinId = Integer(allow_none=True)
212 iLevel = Integer(allow_none=True)
213 hidden = Bool(allow_none=True)
214 customBuiltin = Bool(allow_none=True)
215 extLst = Typed(expected_type=ExtensionList, allow_none=True)
217 __elements__ = ()
220 def __init__(self,
221 name=None,
222 xfId=None,
223 builtinId=None,
224 iLevel=None,
225 hidden=None,
226 customBuiltin=None,
227 extLst=None,
228 ):
229 self.name = name
230 self.xfId = xfId
231 self.builtinId = builtinId
232 self.iLevel = iLevel
233 self.hidden = hidden
234 self.customBuiltin = customBuiltin
237class _NamedCellStyleList(Serialisable):
238 """
239 Container for named cell style objects
241 Not used in client code
242 """
244 tagname = "cellStyles"
246 count = Integer(allow_none=True)
247 cellStyle = Sequence(expected_type=_NamedCellStyle)
249 __attrs__ = ("count",)
251 def __init__(self,
252 count=None,
253 cellStyle=(),
254 ):
255 self.cellStyle = cellStyle
258 @property
259 def count(self):
260 return len(self.cellStyle)
263 @property
264 def names(self):
265 """
266 Convert to NamedStyle objects and remove duplicates.
268 In theory the highest xfId wins but in practice they are duplicates
269 so it doesn't matter.
270 """
272 def sort_fn(v):
273 return v.xfId
275 styles = []
276 names = set()
278 for ns in sorted(self.cellStyle, key=sort_fn):
279 if ns.name in names:
280 continue
282 style = NamedStyle(
283 name=ns.name,
284 hidden=ns.hidden,
285 builtinId = ns.builtinId
286 )
287 names.add(ns.name)
288 style._set_index(len(styles)) # assign xfId
289 styles.append(style)
291 return NamedStyleList(styles)