cell_style.py 5.2 KB

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