# Copyright (c) 2010-2024 fastpyxl
from array import array
from fastpyxl.compat import safe_string
from fastpyxl.typed_serialisable.base import Serialisable
from fastpyxl.typed_serialisable.fields import Field
from fastpyxl.descriptors.excel import ExtensionList
from fastpyxl.utils.indexed_list import IndexedList
from .alignment import Alignment
from .protection import Protection
[docs]
class ArrayDescriptor:
def __init__(self, key):
self.key = key
def __get__(self, instance, cls):
return instance[self.key]
def __set__(self, instance, value):
instance[self.key] = value
[docs]
class StyleArray(array):
"""
Simplified named tuple with an array
"""
__slots__ = ('_hash',)
tagname = 'xf'
fontId = ArrayDescriptor(0)
fillId = ArrayDescriptor(1)
borderId = ArrayDescriptor(2)
numFmtId = ArrayDescriptor(3)
protectionId = ArrayDescriptor(4)
alignmentId = ArrayDescriptor(5)
pivotButton = ArrayDescriptor(6)
quotePrefix = ArrayDescriptor(7)
xfId = ArrayDescriptor(8)
def __new__(cls, args=[0]*9):
obj = array.__new__(cls, 'i', args)
obj._hash = None
return obj
def __setitem__(self, index, value):
self._hash = None
array.__setitem__(self, index, value)
def __hash__(self):
h = self._hash
if h is None:
h = hash(tuple(self))
self._hash = h
return h
def __copy__(self):
return StyleArray((self))
def __deepcopy__(self, memo):
return StyleArray((self))
[docs]
class CellStyle(Serialisable):
tagname = "xf"
numFmtId: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
fontId: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
fillId: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
borderId: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
xfId: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
quotePrefix: bool | None = Field.attribute(expected_type=bool, allow_none=True, default=None)
pivotButton: bool | None = Field.attribute(expected_type=bool, allow_none=True, default=None)
alignment: Alignment | None = Field.element(expected_type=Alignment, allow_none=True, default=None)
protection: Protection | None = Field.element(expected_type=Protection, allow_none=True, default=None)
extLst: ExtensionList | None = Field.element(expected_type=ExtensionList, allow_none=True, default=None)
xml_order = ("alignment", "protection")
def __init__(self,
numFmtId=0,
fontId=0,
fillId=0,
borderId=0,
xfId=None,
quotePrefix=None,
pivotButton=None,
applyNumberFormat=None,
applyFont=None,
applyFill=None,
applyBorder=None,
applyAlignment=None,
applyProtection=None,
alignment=None,
protection=None,
extLst=None,
):
self.numFmtId = numFmtId
self.fontId = fontId
self.fillId = fillId
self.borderId = borderId
self.xfId = xfId
self.quotePrefix = quotePrefix
self.pivotButton = pivotButton
self.applyNumberFormat = applyNumberFormat
self.applyFont = applyFont
self.applyFill = applyFill
self.applyBorder = applyBorder
self.alignment = alignment
self.protection = protection
self.extLst = extLst
[docs]
def to_array(self):
"""
Convert to StyleArray
"""
style = StyleArray()
for idx, v in (
(0, self.fontId), (1, self.fillId), (2, self.borderId),
(3, self.numFmtId), (6, self.pivotButton),
(7, self.quotePrefix), (8, self.xfId),
):
if v is not None:
style[idx] = v
return style
[docs]
@classmethod
def from_array(cls, style):
"""
Convert from StyleArray
"""
return cls(numFmtId=style.numFmtId, fontId=style.fontId,
fillId=style.fillId, borderId=style.borderId, xfId=style.xfId,
quotePrefix=style.quotePrefix, pivotButton=style.pivotButton,)
@property
def applyProtection(self):
return self.protection is not None or None
@property
def applyAlignment(self):
return self.alignment is not None or None
def __iter__(self):
attrs = (
("numFmtId", self.numFmtId),
("fontId", self.fontId),
("fillId", self.fillId),
("borderId", self.borderId),
("applyAlignment", self.applyAlignment),
("applyProtection", self.applyProtection),
("pivotButton", self.pivotButton),
("quotePrefix", self.quotePrefix),
("xfId", self.xfId),
)
for key, value in attrs:
if value is None:
continue
yield key, safe_string(value)
[docs]
class CellStyleList(Serialisable):
tagname = "cellXfs"
xf: list[CellStyle] = Field.sequence(expected_type=CellStyle, default=list)
def __init__(self,
count=None,
xf=(),
):
self.xf = list(xf)
@property
def count(self):
return len(self.xf)
def __iter__(self):
yield "count", safe_string(self.count)
def __getitem__(self, idx):
try:
return self.xf[idx]
except IndexError:
print((f"{idx} is out of range"))
return self.xf[idx]
def _to_array(self):
"""
Extract protection and alignments, convert to style array
"""
self.prots = IndexedList([Protection()])
self.alignments = IndexedList([Alignment()])
styles = [] # allow duplicates
for xf in self.xf:
style = xf.to_array()
if xf.alignment is not None:
style.alignmentId = self.alignments.add(xf.alignment)
if xf.protection is not None:
style.protectionId = self.prots.add(xf.protection)
styles.append(style)
return IndexedList(styles)