PageRenderTime 63ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/tablib/packages/openpyxl/style.py

https://github.com/DanLipsitt/tablib
Python | 392 lines | 293 code | 57 blank | 42 comment | 7 complexity | f3216c8495818665db37bb778a7d4153 MD5 | raw file
  1. # file openpyxl/style.py
  2. # Copyright (c) 2010 openpyxl
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in
  12. # all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. # THE SOFTWARE.
  21. #
  22. # @license: http://www.opensource.org/licenses/mit-license.php
  23. # @author: Eric Gazoni
  24. """Style and formatting option tracking."""
  25. # Python stdlib imports
  26. import re
  27. try:
  28. from hashlib import md5
  29. except ImportError:
  30. from md5 import md5
  31. class HashableObject(object):
  32. """Define how to hash property classes."""
  33. __fields__ = None
  34. __leaf__ = False
  35. def __repr__(self):
  36. return ':'.join([repr(getattr(self, x)) for x in self.__fields__])
  37. def __hash__(self):
  38. # return int(md5(repr(self)).hexdigest(), 16)
  39. return hash(repr(self))
  40. class Color(HashableObject):
  41. """Named colors for use in styles."""
  42. BLACK = 'FF000000'
  43. WHITE = 'FFFFFFFF'
  44. RED = 'FFFF0000'
  45. DARKRED = 'FF800000'
  46. BLUE = 'FF0000FF'
  47. DARKBLUE = 'FF000080'
  48. GREEN = 'FF00FF00'
  49. DARKGREEN = 'FF008000'
  50. YELLOW = 'FFFFFF00'
  51. DARKYELLOW = 'FF808000'
  52. __fields__ = ('index',)
  53. __slots__ = __fields__
  54. __leaf__ = True
  55. def __init__(self, index):
  56. super(Color, self).__init__()
  57. self.index = index
  58. class Font(HashableObject):
  59. """Font options used in styles."""
  60. UNDERLINE_NONE = 'none'
  61. UNDERLINE_DOUBLE = 'double'
  62. UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting'
  63. UNDERLINE_SINGLE = 'single'
  64. UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting'
  65. __fields__ = ('name',
  66. 'size',
  67. 'bold',
  68. 'italic',
  69. 'superscript',
  70. 'subscript',
  71. 'underline',
  72. 'strikethrough',
  73. 'color')
  74. __slots__ = __fields__
  75. def __init__(self):
  76. super(Font, self).__init__()
  77. self.name = 'Calibri'
  78. self.size = 11
  79. self.bold = False
  80. self.italic = False
  81. self.superscript = False
  82. self.subscript = False
  83. self.underline = self.UNDERLINE_NONE
  84. self.strikethrough = False
  85. self.color = Color(Color.BLACK)
  86. class Fill(HashableObject):
  87. """Area fill patterns for use in styles."""
  88. FILL_NONE = 'none'
  89. FILL_SOLID = 'solid'
  90. FILL_GRADIENT_LINEAR = 'linear'
  91. FILL_GRADIENT_PATH = 'path'
  92. FILL_PATTERN_DARKDOWN = 'darkDown'
  93. FILL_PATTERN_DARKGRAY = 'darkGray'
  94. FILL_PATTERN_DARKGRID = 'darkGrid'
  95. FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal'
  96. FILL_PATTERN_DARKTRELLIS = 'darkTrellis'
  97. FILL_PATTERN_DARKUP = 'darkUp'
  98. FILL_PATTERN_DARKVERTICAL = 'darkVertical'
  99. FILL_PATTERN_GRAY0625 = 'gray0625'
  100. FILL_PATTERN_GRAY125 = 'gray125'
  101. FILL_PATTERN_LIGHTDOWN = 'lightDown'
  102. FILL_PATTERN_LIGHTGRAY = 'lightGray'
  103. FILL_PATTERN_LIGHTGRID = 'lightGrid'
  104. FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal'
  105. FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis'
  106. FILL_PATTERN_LIGHTUP = 'lightUp'
  107. FILL_PATTERN_LIGHTVERTICAL = 'lightVertical'
  108. FILL_PATTERN_MEDIUMGRAY = 'mediumGray'
  109. __fields__ = ('fill_type',
  110. 'rotation',
  111. 'start_color',
  112. 'end_color')
  113. __slots__ = __fields__
  114. def __init__(self):
  115. super(Fill, self).__init__()
  116. self.fill_type = self.FILL_NONE
  117. self.rotation = 0
  118. self.start_color = Color(Color.WHITE)
  119. self.end_color = Color(Color.BLACK)
  120. class Border(HashableObject):
  121. """Border options for use in styles."""
  122. BORDER_NONE = 'none'
  123. BORDER_DASHDOT = 'dashDot'
  124. BORDER_DASHDOTDOT = 'dashDotDot'
  125. BORDER_DASHED = 'dashed'
  126. BORDER_DOTTED = 'dotted'
  127. BORDER_DOUBLE = 'double'
  128. BORDER_HAIR = 'hair'
  129. BORDER_MEDIUM = 'medium'
  130. BORDER_MEDIUMDASHDOT = 'mediumDashDot'
  131. BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot'
  132. BORDER_MEDIUMDASHED = 'mediumDashed'
  133. BORDER_SLANTDASHDOT = 'slantDashDot'
  134. BORDER_THICK = 'thick'
  135. BORDER_THIN = 'thin'
  136. __fields__ = ('border_style',
  137. 'color')
  138. __slots__ = __fields__
  139. def __init__(self):
  140. super(Border, self).__init__()
  141. self.border_style = self.BORDER_NONE
  142. self.color = Color(Color.BLACK)
  143. class Borders(HashableObject):
  144. """Border positioning for use in styles."""
  145. DIAGONAL_NONE = 0
  146. DIAGONAL_UP = 1
  147. DIAGONAL_DOWN = 2
  148. DIAGONAL_BOTH = 3
  149. __fields__ = ('left',
  150. 'right',
  151. 'top',
  152. 'bottom',
  153. 'diagonal',
  154. 'diagonal_direction',
  155. 'all_borders',
  156. 'outline',
  157. 'inside',
  158. 'vertical',
  159. 'horizontal')
  160. __slots__ = __fields__
  161. def __init__(self):
  162. super(Borders, self).__init__()
  163. self.left = Border()
  164. self.right = Border()
  165. self.top = Border()
  166. self.bottom = Border()
  167. self.diagonal = Border()
  168. self.diagonal_direction = self.DIAGONAL_NONE
  169. self.all_borders = Border()
  170. self.outline = Border()
  171. self.inside = Border()
  172. self.vertical = Border()
  173. self.horizontal = Border()
  174. class Alignment(HashableObject):
  175. """Alignment options for use in styles."""
  176. HORIZONTAL_GENERAL = 'general'
  177. HORIZONTAL_LEFT = 'left'
  178. HORIZONTAL_RIGHT = 'right'
  179. HORIZONTAL_CENTER = 'center'
  180. HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous'
  181. HORIZONTAL_JUSTIFY = 'justify'
  182. VERTICAL_BOTTOM = 'bottom'
  183. VERTICAL_TOP = 'top'
  184. VERTICAL_CENTER = 'center'
  185. VERTICAL_JUSTIFY = 'justify'
  186. __fields__ = ('horizontal',
  187. 'vertical',
  188. 'text_rotation',
  189. 'wrap_text',
  190. 'shrink_to_fit',
  191. 'indent')
  192. __slots__ = __fields__
  193. __leaf__ = True
  194. def __init__(self):
  195. super(Alignment, self).__init__()
  196. self.horizontal = self.HORIZONTAL_GENERAL
  197. self.vertical = self.VERTICAL_BOTTOM
  198. self.text_rotation = 0
  199. self.wrap_text = False
  200. self.shrink_to_fit = False
  201. self.indent = 0
  202. class NumberFormat(HashableObject):
  203. """Numer formatting for use in styles."""
  204. FORMAT_GENERAL = 'General'
  205. FORMAT_TEXT = '@'
  206. FORMAT_NUMBER = '0'
  207. FORMAT_NUMBER_00 = '0.00'
  208. FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00'
  209. FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-'
  210. FORMAT_PERCENTAGE = '0%'
  211. FORMAT_PERCENTAGE_00 = '0.00%'
  212. FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd'
  213. FORMAT_DATE_YYYYMMDD = 'yy-mm-dd'
  214. FORMAT_DATE_DDMMYYYY = 'dd/mm/yy'
  215. FORMAT_DATE_DMYSLASH = 'd/m/y'
  216. FORMAT_DATE_DMYMINUS = 'd-m-y'
  217. FORMAT_DATE_DMMINUS = 'd-m'
  218. FORMAT_DATE_MYMINUS = 'm-y'
  219. FORMAT_DATE_XLSX14 = 'mm-dd-yy'
  220. FORMAT_DATE_XLSX15 = 'd-mmm-yy'
  221. FORMAT_DATE_XLSX16 = 'd-mmm'
  222. FORMAT_DATE_XLSX17 = 'mmm-yy'
  223. FORMAT_DATE_XLSX22 = 'm/d/yy h:mm'
  224. FORMAT_DATE_DATETIME = 'd/m/y h:mm'
  225. FORMAT_DATE_TIME1 = 'h:mm AM/PM'
  226. FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM'
  227. FORMAT_DATE_TIME3 = 'h:mm'
  228. FORMAT_DATE_TIME4 = 'h:mm:ss'
  229. FORMAT_DATE_TIME5 = 'mm:ss'
  230. FORMAT_DATE_TIME6 = 'h:mm:ss'
  231. FORMAT_DATE_TIME7 = 'i:s.S'
  232. FORMAT_DATE_TIME8 = 'h:mm:ss@'
  233. FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd@'
  234. FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-'
  235. FORMAT_CURRENCY_USD = '$#,##0_-'
  236. FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-'
  237. _BUILTIN_FORMATS = {
  238. 0: 'General',
  239. 1: '0',
  240. 2: '0.00',
  241. 3: '#,##0',
  242. 4: '#,##0.00',
  243. 9: '0%',
  244. 10: '0.00%',
  245. 11: '0.00E+00',
  246. 12: '# ?/?',
  247. 13: '# ??/??',
  248. 14: 'mm-dd-yy',
  249. 15: 'd-mmm-yy',
  250. 16: 'd-mmm',
  251. 17: 'mmm-yy',
  252. 18: 'h:mm AM/PM',
  253. 19: 'h:mm:ss AM/PM',
  254. 20: 'h:mm',
  255. 21: 'h:mm:ss',
  256. 22: 'm/d/yy h:mm',
  257. 37: '#,##0 (#,##0)',
  258. 38: '#,##0 [Red](#,##0)',
  259. 39: '#,##0.00(#,##0.00)',
  260. 40: '#,##0.00[Red](#,##0.00)',
  261. 41: '_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)',
  262. 42: '_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)',
  263. 43: '_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)',
  264. 44: '_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)',
  265. 45: 'mm:ss',
  266. 46: '[h]:mm:ss',
  267. 47: 'mmss.0',
  268. 48: '##0.0E+0',
  269. 49: '@', }
  270. _BUILTIN_FORMATS_REVERSE = dict(
  271. [(value, key) for key, value in _BUILTIN_FORMATS.items()])
  272. __fields__ = ('_format_code',
  273. '_format_index')
  274. __slots__ = __fields__
  275. __leaf__ = True
  276. DATE_INDICATORS = 'dmyhs'
  277. def __init__(self):
  278. super(NumberFormat, self).__init__()
  279. self._format_code = self.FORMAT_GENERAL
  280. self._format_index = 0
  281. def _set_format_code(self, format_code = FORMAT_GENERAL):
  282. """Setter for the format_code property."""
  283. self._format_code = format_code
  284. self._format_index = self.builtin_format_id(format = format_code)
  285. def _get_format_code(self):
  286. """Getter for the format_code property."""
  287. return self._format_code
  288. format_code = property(_get_format_code, _set_format_code)
  289. def builtin_format_code(self, index):
  290. """Return one of the standard format codes by index."""
  291. return self._BUILTIN_FORMATS[index]
  292. def is_builtin(self, format = None):
  293. """Check if a format code is a standard format code."""
  294. if format is None:
  295. format = self._format_code
  296. return format in self._BUILTIN_FORMATS.values()
  297. def builtin_format_id(self, format):
  298. """Return the id of a standard style."""
  299. return self._BUILTIN_FORMATS_REVERSE.get(format, None)
  300. def is_date_format(self, format = None):
  301. """Check if the number format is actually representing a date."""
  302. if format is None:
  303. format = self._format_code
  304. return any([x in format for x in self.DATE_INDICATORS])
  305. class Protection(HashableObject):
  306. """Protection options for use in styles."""
  307. PROTECTION_INHERIT = 'inherit'
  308. PROTECTION_PROTECTED = 'protected'
  309. PROTECTION_UNPROTECTED = 'unprotected'
  310. __fields__ = ('locked',
  311. 'hidden')
  312. __slots__ = __fields__
  313. __leaf__ = True
  314. def __init__(self):
  315. super(Protection, self).__init__()
  316. self.locked = self.PROTECTION_INHERIT
  317. self.hidden = self.PROTECTION_INHERIT
  318. class Style(HashableObject):
  319. """Style object containing all formatting details."""
  320. __fields__ = ('font',
  321. 'fill',
  322. 'borders',
  323. 'alignment',
  324. 'number_format',
  325. 'protection')
  326. __slots__ = __fields__
  327. def __init__(self):
  328. super(Style, self).__init__()
  329. self.font = Font()
  330. self.fill = Fill()
  331. self.borders = Borders()
  332. self.alignment = Alignment()
  333. self.number_format = NumberFormat()
  334. self.protection = Protection()
  335. DEFAULTS = Style()