1from __future__ import absolute_import 2# Copyright (c) 2010-2019 openpyxl 3 4from array import array 5 6from openpyxl.descriptors.serialisable import Serialisable 7from openpyxl.descriptors import ( 8 Typed, 9 Float, 10 Bool, 11 Integer, 12 Sequence, 13) 14from openpyxl.descriptors.excel import ExtensionList 15from openpyxl.utils.indexed_list import IndexedList 16 17 18from .alignment import Alignment 19from .protection import Protection 20 21 22class ArrayDescriptor(object): 23 24 def __init__(self, key): 25 self.key = key 26 27 def __get__(self, instance, cls): 28 return instance[self.key] 29 30 def __set__(self, instance, value): 31 instance[self.key] = value 32 33 34class StyleArray(array): 35 """ 36 Simplified named tuple with an array 37 """ 38 39 __slots__ = () 40 tagname = 'xf' 41 42 fontId = ArrayDescriptor(0) 43 fillId = ArrayDescriptor(1) 44 borderId = ArrayDescriptor(2) 45 numFmtId = ArrayDescriptor(3) 46 protectionId = ArrayDescriptor(4) 47 alignmentId = ArrayDescriptor(5) 48 pivotButton = ArrayDescriptor(6) 49 quotePrefix = ArrayDescriptor(7) 50 xfId = ArrayDescriptor(8) 51 52 53 def __new__(cls, args=[0]*9): 54 return array.__new__(cls, 'i', args) 55 56 57 def __hash__(self): 58 return hash(tuple(self)) 59 60 61 def __copy__(self): 62 return StyleArray((self)) 63 64 65 def __deepcopy__(self, memo): 66 return StyleArray((self)) 67 68 69class CellStyle(Serialisable): 70 71 tagname = "xf" 72 73 numFmtId = Integer() 74 fontId = Integer() 75 fillId = Integer() 76 borderId = Integer() 77 xfId = Integer(allow_none=True) 78 quotePrefix = Bool(allow_none=True) 79 pivotButton = Bool(allow_none=True) 80 applyNumberFormat = Bool(allow_none=True) 81 applyFont = Bool(allow_none=True) 82 applyFill = Bool(allow_none=True) 83 applyBorder = Bool(allow_none=True) 84 applyAlignment = Bool(allow_none=True) 85 applyProtection = Bool(allow_none=True) 86 alignment = Typed(expected_type=Alignment, allow_none=True) 87 protection = Typed(expected_type=Protection, allow_none=True) 88 extLst = Typed(expected_type=ExtensionList, allow_none=True) 89 90 __elements__ = ('alignment', 'protection') 91 __attrs__ = ("numFmtId", "fontId", "fillId", "borderId", 92 "applyAlignment", "applyProtection", "pivotButton", "quotePrefix", "xfId") 93 94 def __init__(self, 95 numFmtId=0, 96 fontId=0, 97 fillId=0, 98 borderId=0, 99 xfId=None, 100 quotePrefix=None, 101 pivotButton=None, 102 applyNumberFormat=None, 103 applyFont=None, 104 applyFill=None, 105 applyBorder=None, 106 applyAlignment=None, 107 applyProtection=None, 108 alignment=None, 109 protection=None, 110 extLst=None, 111 ): 112 self.numFmtId = numFmtId 113 self.fontId = fontId 114 self.fillId = fillId 115 self.borderId = borderId 116 self.xfId = xfId 117 self.quotePrefix = quotePrefix 118 self.pivotButton = pivotButton 119 self.applyNumberFormat = applyNumberFormat 120 self.applyFont = applyFont 121 self.applyFill = applyFill 122 self.applyBorder = applyBorder 123 self.alignment = alignment 124 self.protection = protection 125 126 127 def to_array(self): 128 """ 129 Convert to StyleArray 130 """ 131 style = StyleArray() 132 for k in ("fontId", "fillId", "borderId", "numFmtId", "pivotButton", 133 "quotePrefix", "xfId"): 134 v = getattr(self, k, 0) 135 if v is not None: 136 setattr(style, k, v) 137 return style 138 139 140 @classmethod 141 def from_array(cls, style): 142 """ 143 Convert from StyleArray 144 """ 145 return cls(numFmtId=style.numFmtId, fontId=style.fontId, 146 fillId=style.fillId, borderId=style.borderId, xfId=style.xfId, 147 quotePrefix=style.quotePrefix, pivotButton=style.pivotButton,) 148 149 150 @property 151 def applyProtection(self): 152 return self.protection is not None or None 153 154 155 @property 156 def applyAlignment(self): 157 return self.alignment is not None or None 158 159 160class CellStyleList(Serialisable): 161 162 tagname = "cellXfs" 163 164 __attrs__ = ("count",) 165 166 count = Integer(allow_none=True) 167 xf = Sequence(expected_type=CellStyle) 168 alignment = Sequence(expected_type=Alignment) 169 protection = Sequence(expected_type=Protection) 170 171 __elements__ = ('xf',) 172 173 def __init__(self, 174 count=None, 175 xf=(), 176 ): 177 self.xf = xf 178 179 180 @property 181 def count(self): 182 return len(self.xf) 183 184 185 def __getitem__(self, idx): 186 return self.xf[idx] 187 188 189 def _to_array(self): 190 """ 191 Extract protection and alignments, convert to style array 192 """ 193 self.prots = IndexedList([Protection()]) 194 self.alignments = IndexedList([Alignment()]) 195 styles = [] # allow duplicates 196 for xf in self.xf: 197 style = xf.to_array() 198 if xf.alignment is not None: 199 style.alignmentId = self.alignments.add(xf.alignment) 200 if xf.protection is not None: 201 style.protectionId = self.prots.add(xf.protection) 202 styles.append(style) 203 return IndexedList(styles) 204