diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py new file mode 100644 index 00000000..221d333b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py @@ -0,0 +1,282 @@ +# Copyright (c) 2010-2024 openpyxl + +from openpyxl.compat import safe_string + +from openpyxl.descriptors import ( + Typed, + Integer, + Bool, + String, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + +from .fills import PatternFill, Fill +from .fonts import Font +from .borders import Border +from .alignment import Alignment +from .protection import Protection +from .numbers import ( + NumberFormatDescriptor, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, +) +from .cell_style import ( + StyleArray, + CellStyle, +) + + +class NamedStyle(Serialisable): + + """ + Named and editable styles + """ + + font = Typed(expected_type=Font) + fill = Typed(expected_type=Fill) + border = Typed(expected_type=Border) + alignment = Typed(expected_type=Alignment) + number_format = NumberFormatDescriptor() + protection = Typed(expected_type=Protection) + builtinId = Integer(allow_none=True) + hidden = Bool(allow_none=True) + name = String() + _wb = None + _style = StyleArray() + + + def __init__(self, + name="Normal", + font=None, + fill=None, + border=None, + alignment=None, + number_format=None, + protection=None, + builtinId=None, + hidden=False, + ): + self.name = name + self.font = font or Font() + self.fill = fill or PatternFill() + self.border = border or Border() + self.alignment = alignment or Alignment() + self.number_format = number_format + self.protection = protection or Protection() + self.builtinId = builtinId + self.hidden = hidden + self._wb = None + self._style = StyleArray() + + + def __setattr__(self, attr, value): + super().__setattr__(attr, value) + if getattr(self, '_wb', None) and attr in ( + 'font', 'fill', 'border', 'alignment', 'number_format', 'protection', + ): + self._recalculate() + + + def __iter__(self): + for key in ('name', 'builtinId', 'hidden', 'xfId'): + value = getattr(self, key, None) + if value is not None: + yield key, safe_string(value) + + + def bind(self, wb): + """ + Bind a named style to a workbook + """ + self._wb = wb + self._recalculate() + + + def _recalculate(self): + self._style.fontId = self._wb._fonts.add(self.font) + self._style.borderId = self._wb._borders.add(self.border) + self._style.fillId = self._wb._fills.add(self.fill) + self._style.protectionId = self._wb._protections.add(self.protection) + self._style.alignmentId = self._wb._alignments.add(self.alignment) + fmt = self.number_format + if fmt in BUILTIN_FORMATS_REVERSE: + fmt = BUILTIN_FORMATS_REVERSE[fmt] + else: + fmt = self._wb._number_formats.add(self.number_format) + ( + BUILTIN_FORMATS_MAX_SIZE) + self._style.numFmtId = fmt + + + def as_tuple(self): + """Return a style array representing the current style""" + return self._style + + + def as_xf(self): + """ + Return equivalent XfStyle + """ + xf = CellStyle.from_array(self._style) + xf.xfId = None + xf.pivotButton = None + xf.quotePrefix = None + if self.alignment != Alignment(): + xf.alignment = self.alignment + if self.protection != Protection(): + xf.protection = self.protection + return xf + + + def as_name(self): + """ + Return relevant named style + + """ + named = _NamedCellStyle( + name=self.name, + builtinId=self.builtinId, + hidden=self.hidden, + xfId=self._style.xfId + ) + return named + + +class NamedStyleList(list): + """ + Named styles are editable and can be applied to multiple objects + + As only the index is stored in referencing objects the order mus + be preserved. + + Returns a list of NamedStyles + """ + + def __init__(self, iterable=()): + """ + Allow a list of named styles to be passed in and index them. + """ + + for idx, s in enumerate(iterable, len(self)): + s._style.xfId = idx + super().__init__(iterable) + + + @property + def names(self): + return [s.name for s in self] + + + def __getitem__(self, key): + if isinstance(key, int): + return super().__getitem__(key) + + + for idx, name in enumerate(self.names): + if name == key: + return self[idx] + + raise KeyError("No named style with the name{0} exists".format(key)) + + def append(self, style): + if not isinstance(style, NamedStyle): + raise TypeError("""Only NamedStyle instances can be added""") + elif style.name in self.names: # hotspot + raise ValueError("""Style {0} exists already""".format(style.name)) + style._style.xfId = (len(self)) + super().append(style) + + +class _NamedCellStyle(Serialisable): + + """ + Pointer-based representation of named styles in XML + xfId refers to the corresponding CellStyleXfs + + Not used in client code. + """ + + tagname = "cellStyle" + + name = String() + xfId = Integer() + builtinId = Integer(allow_none=True) + iLevel = Integer(allow_none=True) + hidden = Bool(allow_none=True) + customBuiltin = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + + def __init__(self, + name=None, + xfId=None, + builtinId=None, + iLevel=None, + hidden=None, + customBuiltin=None, + extLst=None, + ): + self.name = name + self.xfId = xfId + self.builtinId = builtinId + self.iLevel = iLevel + self.hidden = hidden + self.customBuiltin = customBuiltin + + +class _NamedCellStyleList(Serialisable): + """ + Container for named cell style objects + + Not used in client code + """ + + tagname = "cellStyles" + + count = Integer(allow_none=True) + cellStyle = Sequence(expected_type=_NamedCellStyle) + + __attrs__ = ("count",) + + def __init__(self, + count=None, + cellStyle=(), + ): + self.cellStyle = cellStyle + + + @property + def count(self): + return len(self.cellStyle) + + + def remove_duplicates(self): + """ + Some applications contain duplicate definitions either by name or + referenced style. + + As the references are 0-based indices, styles are sorted by + index. + + Returns a list of style references with duplicates removed + """ + + def sort_fn(v): + return v.xfId + + styles = [] + names = set() + ids = set() + + for ns in sorted(self.cellStyle, key=sort_fn): + if ns.xfId in ids or ns.name in names: # skip duplicates + continue + ids.add(ns.xfId) + names.add(ns.name) + + styles.append(ns) + + return styles |