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